✅ fresh
last synced 2026-06-17T22:39:35.514075+00:00 · coverage 86% (
application)Validation by Beadloom
doc_sync— same source assync-check.
Application
Use-case orchestration layer. Sits between the interface layer (services/tui) and the domain layer (context_oracle, doc_sync, graph, onboarding). Its modules coordinate multiple domains plus infrastructure to fulfil a use case and hold no business rules of their own.
Layer order: services → application → domains → infrastructure. The application layer may depend on domains and infrastructure (legal top-down); it is never depended upon by a lower layer. Extracting these orchestrators out of infrastructure is what lets infrastructure stay domain-agnostic and restores the DDD Dependency Rule.
Features
Each feature has its own SPEC.md:
- Reindex — full + incremental index rebuild (
beadloom reindex). - Doctor — graph/data integrity checks (
beadloom doctor). - Debt Report — weighted architecture-debt score.
- Watcher — file-watch auto-reindex.
- Site Generation — the VitePress site generator (
beadloom docs site). - CI Gate — the unified
beadloom cienforcement gate.
Specification
Modules
- reindex/ — package (decomposed by cohesion in BDL-059 S4 into
models,rules_loader,indexing,enrichment,sync_state,change_detection,full,incremental; the package__init__re-exports the stable public + back-compat surface).reindex(root)performs full reindex: snapshot sync baselines → drop tables → create schema → load graph YAML → store deep config → index docs → index code → resolve imports → load rules → map tests → analyze git activity → extract API routes → build sync state (with preserved symbol hashes) → populate FTS5 → clear bundle cache → take health snapshot → populate file index → store parser fingerprint.incremental_reindex(root)updates only changed files; detects parser availability changes via fingerprint comparison and graph YAML changes via_graph_yaml_changed(), triggering full reindex when needed. Backfillssymbols_indexedfrom the live DB (totalcode_symbolscount) so the result reports the true symbol total, not just the per-run delta (mirroring the #88 nodes/edges backfill). - doctor.py —
run_checks(conn, *, project_root=None)validates graph health with DB checks (empty summaries, unlinked docs, nodes without docs, isolated nodes, symbol drift, stale sync entries, source coverage gaps) plus an optional "Agent Instructions" check whenproject_rootis provided, comparing CLAUDE.md/AGENTS.md factual claims (version, packages, CLI commands, MCP tool count) against runtime truth. - debt_report/ — package (decomposed by cohesion in BDL-059 S4 into
models,config,collect,scoring,trend,render; the package__init__re-exports the public surface).collect_debt_data()aggregates architecture health signals from lint, sync-check, doctor, git activity, and test mapper.compute_debt_score()applies a weighted formula producing a 0-100 debt score with category breakdown, severity classification, and per-node top offenders.format_debt_report()/format_debt_json()render the report.compute_debt_trend()compares against the last graph snapshot. - watcher.py —
watch()monitors project files (graph YAML, docs, source) and auto-triggers reindex on changes usingwatchfiles. Graph changes trigger full reindex; other changes trigger incremental.WatchEventfrozen dataclass captures per-event metadata.DEFAULT_DEBOUNCE_MSconstant (500ms). - site.py —
generate_site(conn, out_dir, *, project_root, federated=None, now_ts=None)is thedocs siteuse-case: it reads the indexed graph read-only and writes a VitePress content tree underout_dir(defaultsite/) — anindex.mdAbout home page rendered from the projectREADME.mdviasite_about.render_about(link-rebased; falls back to the architecture overview body when noREADME.md), aru/index.mdRU About page fromREADME.ru.md(omitted when absent; both About pages get the in-page bilingual cross-link/↔/ru/viacross_link_routes), anarchitecture.mdarchitecture overview (counts + top-level C4/Mermaid + a read-only health summary — the body that used to live atindex.md, BDL-046), adocs/index.mdDocumentation overview (BDL-046 BEAD-11: a short intro + one## <Group>heading per top-level docs group — Domains / Services / Guides / General — each followed by a single sentence that names its members as inline human-labelled TEXT, no link wall, since the full navigable tree is already the expanded Documentation sidebar), one page per node (delegated tosite_pages.py), the metrics dashboard (dashboard.md+dashboard.data.json, delegated to thesite_dashboard/package), the 🌟 landscape map (landscape.md, delegated tosite_landscape.py), and.vitepress/config.generated.mjs(nav/sidebar). Before building the dashboard it backfills structural trend history fromgraph_snapshotsand records this run's honest metrics point (site_metrics_history.append_metrics_point) so the emitted trend series includes "now";now_tsis the injected ISO timestamp for that point (deterministic in tests; defaults to the current UTC instant in production — the only wall-clock read, and it lands solely in the append-only history store, never in the diffed dashboard fields). Beadloom produces, VitePress renders. Output is deterministic (sorted, stable frontmatter, no wall-clock in the diffed output) and never writes into the sourcedocs/. Returns a frozenSiteResultlisting every written path. Reusesgraph/c4.py(map_to_c4/filter_c4_nodes/render_c4_mermaid) for diagrams; reimplements no graph logic. Every emitted Markdown page is run through the Mermaid structural guard (site_mermaid_guard.validate_mermaid) before writing — a structurally broken diagram raisesMermaidValidationErrorand fails generation (closing the "build green ≠ renders ok" gap) instead of shipping a page that crashes the browser render. - site_pages.py — per-node page rendering for
site.py(split out to stay under the domain-size limit).render_all_pages(conn)returns sortedNodePages; each page has summary, source, public symbols, a Relationships section, linked hand-written docs (rooted at/docs/so they resolve to the published copy undersite/docs/…), and an embedded scoped C4/Mermaid diagram. The Relationships section renders OUTGOINGpart_of/depends_on/usesedges as Markdown links to other node pages, then INCOMING relationships: Used by — the sorted, deduped union of incominguses+depends_onconsumers (who consumes this node; no separate "Depended on by" section) — and Parts — incomingpart_ofchild nodes. Incoming refs are link-safe (a ref with a generated page links to it, one without renders as plain text — never a dead link); self-edges are skipped; an incoming section with no entries is omitted (a leaf shows neither). Deterministic (sorted). - site_nav.py — the generated VitePress nav/sidebar tree builders for
site.py(split out to keep the generator small).render_nav_config(conn, project_root)emits the full.vitepress/config.generated.mjsmodule exporting onlynav+sidebar(BDL-046 BEAD-11 dropped VitePresslocales— its global/x↔/ru/xmapping translated the whole menu and 404'd off/ru/— so there is a single shared EN sidebar and nonavRu/sidebarRu/render_sidebar_ru). Top nav is empty (render_nav→[]; BDL-046) — the VitePress default theme still renders the appearance toggle and local search regardless. The sidebar (render_sidebar(conn, *, docs_root, has_getting_started)) is a single ordered, link-safe tree: About (/) · Getting Started (/docs/getting-started, emitted only if that page exists) · Dashboard (flat) · Architecture · Landscape map (flat) · Documentation. The Architecture group iscollapsed: trueand apart_of-nested tree (service root → domains → features) with human-readable labels viahuman_label(context-oracle→Context Oracle), roots being nodes with no realpart_ofparent (aroot part_of rootself-edge is ignored so the root service isn't dropped); an "Architecture overview" entry stays on top and links to/architecture(the overview page). The Documentation group iscollapsed: false(expanded) and mirrors thedocs/directory tree (render_documentation_group_from_dir(docs_dir, *, collapsed)) as a nested, collapsible structure (each subdir a group, each.mda leaf link rooted at/docs/), led by an Overview link. Dashboard + Landscape map are plain{ text, link }entries (not one-child groups). Deterministic (sorted, byte-stable); no dead nav links. - site_about.py — the README→About page transform (BDL-046).
render_about(readme_text, *, published_doc_slugs, repo_url, cross_link_routes=None)turns a project README's Markdown into the VitePress About/home page body by rebasing links (prose untouched, pure, deterministic, no I/O): adocs/<x>.mdlink whose slug is inpublished_doc_slugs→ the extension-less site link/docs/<x>; aREADME.md/README.ru.mdcross-link → if its lowercased basename is incross_link_routes(a basename→route map, e.g.{"readme.ru.md": "/ru/", "readme.md": "/"}), the link target is rewritten to that route (visible text kept) — this is the in-page bilingual About toggle that replaced the dropped locale switcher (BDL-046 BEAD-11); when no map is given the cross-link is dropped (text kept, back-compat); any other internal/relative target → an absolute GitHub URL{repo_url}/blob/main/<path>; already-absolute URLs, shields.io badges, and pure anchors are left untouched; the same rules apply to image targets; links inside code spans / fenced blocks are never rewritten. This lets the rewritten README be the bilingual front-door page (EN/, RU/ru/) without a hand-maintained duplicate. - site_dashboard/ — package (decomposed by cohesion in BDL-059 S4 into
_common,gate_metrics,ai_activity,recommendations,alerts,status_cards,assemble; the package__init__re-exports the public surface). Showcase A, the AaC/DocAsCode metrics dashboard.build_dashboard_data(conn, *, project_root, federated=None)returns a deterministic, JSON-safe dict andrender_dashboard_md(data)renders the human page from that same dict (the front-end never invents a figure). Honest by construction: every number comes from the SAME code path as its gate —lint(count + severity breakdown viagraph/linter.lint),debt(debt_report.compute_debt_score+compute_debt_trend, serialized viaformat_debt_json),docs(coverage % +sync_statefreshness % + stale count, read-only),doctor(doctor.run_checkspass/fail summary), and an optionalfederatedrollup (per-service edge-verdict health + contract-verdict counts) reusing thefederateoutput verbatim. It also emitstrends— the recorded time-series fromsite_metrics_history.read_history(sorted byts; ONLY real recorded points — no interpolation, no fabricated samples; sparse at first is correct) —ai_techwriter— the honest "AI tech-writer activity" section (G9) read independently from the append-only run-record store.beadloom/ai_techwriter_runs.jsonthe CI harness emits (absent/empty/corrupt → an empty-but-present section, never an error):runs[]sorted bytswith per-run + cumulative docs-refreshed and input/output token spend (ONLY real recorded runs — same no-interpolation contract astrends),totals, and acost_estimate({usd, rate_usd_per_1m, is_estimate=True, label "est. @ $X/1M tokens"}) — token counts are FACTS from each record while the dollar figure is a clearly-labeled ESTIMATE at the configured_USD_PER_1M_TOKENSrate, never a hard cost (rendered by theAiTechwriterActivitywidget) — andrecommendations— a prioritized, actionable list built from the EXISTING gate data (one item per lint violation, BREAKING/DRIFT contract risks from the--federatedartifact, stale docs fromsync_state, and worst-debt nodes fromdebt_reporttop offenders); each item is{kind, severity, target, message, link}, severity-ordered (errors first) with deterministic tie-breaks, so the panel is honest by construction. For a critical-first UX it additionally emitsalerts— the attention-banner problems ({kind, severity, message}) shown IFF there is something wrong (BREAKING contracts →critical, DRIFT contracts / lint errors / doctor errors →error, stale docs / high-debt →warn/error), severity-ordered (BREAKING leads) with deterministic tie-breaks; an empty list is the all-clear state — andstatus_cards— one threshold-colored card per metric group ({group, label, status, value, detail}withstatus∈ok/warn/error, the severity computed deterministically in Python so the front-end only paints the color).render_dashboard_mdemits only the page title + a short intro + the<ClientOnly>component mounts (no per-metric text dump, no<noscript>fallback) — the cards/widgets are the single presentation surface and read the honest figures fromdashboard.data.json(build_dashboard_data, unchanged). - site_landscape.py — Showcase B, the 🌟 cross-repo landscape map.
build_landscape_data(conn=None, *, federated=None)returns a deterministic, JSON-safe dict (scope/nodes/edges) andrender_landscape_md(data, *, pages=None)renders a Mermaid diagram from it (never hand-drawn). With afederated.json(the F2federatehub output) nodes are the satellites and edges are the cross-repo links carrying the hub'sContractVerdict-style verdict verbatim; without it the map is the LOCAL contract graph —_local_landscapereads the repo's ownproduces/consumesedges, reconciles them bycontract_keyintograph.contracts.Contracts, classifies each to aContractVerdict, and renders one edge per producer→consumer coloured by that verdict (Beadloom's own site emits a singlebeadloom → vitepress-siteCONFIRMED edge; a repo with no contracts → an empty map). This is the real contract reality, not the structuraldepends_on/usesarch (which stays in the C4 overview). Edges are labelled by their verdict; a MermaidclassDefhealth overlay colours nodes (green = healthy, red = broken, grey = external/expected) and broken edges get a redlinkStyle. Clicks are page-aware: a node emitsclick <id> "/<dir>/<ref>"ONLY whenpages(fromexisting_page_urls(conn)) has a real generated page for it — a node with no page (asitenode, a foreign federated repo) renders without a click, so the map never links to a dead page. Every Mermaid id is prefixed (n_<sanitized>) so it can never collide with a reserved keyword (a node namedgraphbecomesn_graph— the label and click route keep the real ref). Thin slice = Mermaid only (no JS graph library). - site_mermaid_guard.py — the generation-time Mermaid validity guard (targeted structural validators, NOT a full parser).
validate_mermaid(text)returns a list ofMermaidIssuefor the two F4 render bug classes: (1) a flowchart/graphnode id that equals a reserved Mermaid keyword or has an illegal charset; (2) a C4Rel(a, b, …)whose endpoint is not a declaredContainer/Component/Person/System*node (a Rel to the boundary/root crashesdrawRels). An extensible validator registry; deterministic (issues in source order).site.generate_sitecalls it on every emitted diagram and raises on any issue. - site_metrics_history.py — the metrics-history append-store backing honest dashboard trends. A tiny additive JSON log at
.beadloom/metrics_history.jsonofMetricsPoints (ts,lint_violations,debt_score,coverage_pct,sync_pct,nodes,edges,symbols).append_metrics_point(project_root, point)records one point perdocs siterun (thetsis supplied by the caller — nevernow()inside this module — so tests are deterministic; appending an existingtsoverwrites that point so a re-run does not double-count);read_history(project_root)returns the series sorted byts(only real recorded points, never an interpolated one);backfill_structural_history(conn, project_root)seeds structural counts (nodes/edges/symbols) from the existinggraph_snapshotshistory so the structural trend isn't empty on day one (idempotent; never overwrites a richer recorded point). Additive append-state, NOT a versioned artifact — no schema bump. - site_published.py — Showcase C, the published validated documentation.
publish_docs(conn, out_dir, *, project_root)copies the REALdocs/**tree intoout_dir/docs/…preserving structure (the source of truth, rendered as-is) and injects a per-doc validation badge into the COPY only — the sourcedocs/is NEVER mutated (no AI prose-rewriting; that is the deferred F4.1). A generateddocs/index.mdlanding page (sorted links to every published doc) is also emitted so the/docs/nav target resolves.build_published_docs(conn, *, project_root)returns the deterministic per-doc inputs (PublishedDoc:status/reason/synced_at/ref_id/coverage_pct); the status comes from thedoc_syncengine viacheck_sync— the SAME code pathbeadloom sync-checkruns — so a doc the gate calls stale showsstaleon the site. The badge head is✅ fresh/⚠️ stale — <reason>for tracked docs; a doc tracked by NO doc-code pair is badged neutrally as📘 reference — overview/guide, not tied to a code symbol(an overview/guide is not a defect, so it is NOT called "untracked").inject_badge(prose, badge_body)wraps the badge between the stable<!-- beadloom:badge-start -->/-end -->markers so regeneration overwrites ONLY the badge region and leaves the authored prose byte-for-byte intact;render_published_doc(doc, prose)renders the badged Markdown. Fresh/stale badges showlast synced(the storedsync_state.synced_at, not wall-clock → deterministic) and the owning node's read-only source-coverage %; the reference (untracked) badge deliberately omits the coverage % line — that figure is the node's source coverage, unrelated to the prose, and printing it next to a not-tracked doc reads as a contradiction. - active_table.py — shared ACTIVE.md bead-status table parser/updater + the pure reconcile-from-bd core (BDL-053).
split_table_row/is_separator_cellsare the markdown row primitives;set_active_table_status(path, bead_id, status)flips one bead's Status cell by whole-token bead-id match (the extracted MCP S4 behaviour, byte-identical —services/mcp_server.pyre-exports them for back-compat);bd_status_to_cell(bd_status)is the documentedbd-status → Status-cell map (closed → ✓ done,in_progress → in progress,blocked → blocked,open/ready → ready; unknown →None);reconcile_active_tables(project_root, bd_statuses, *, epic=None)discovers ACTIVE.md files (one epic or everyfeatures/*/ACTIVE.md), locates the bead-status table'sStatuscolumn by header index (3- or 4-col), and rewrites only the cells whose state drifts from the injectedbdstatuses — preserving a richer note when the state already agrees — returning aReconcileResult(changed_files,drifted_rows) for--checkvs fix. Best-effort: never raises, touches only Status cells (prose/Progress Log/other columns byte-preserved). Classified as theactive-tablecomponent node (its own DOC.md). - gate.py —
run_ci_gate(project_root, *, fail_on, hub_exports, no_reindex)is the unified CI enforcement gate (thebeadloom ciorchestrator). It composes the existing checkers IN ORDER — reindex (unlessno_reindex) →lint --strict→sync-check→config-check(AgentConfigAsCode) →doctor(graph/data integrity; onlyERROR-severity checks fail the gate, so advisory WARNING/INFO checks never block — no false gate) → (whenhub_exportsgiven)federate --fail-on— into oneGateResultwhose.okis True only when every step passed. It ORCHESTRATES existing domain code; it reimplements no checker (the doctor step reusesdoctor.run_checks). Honesty invariants: no short-circuit (every step runs and ALL findings are collected even after an earlier failure) and no silent skip (eachGateSteprecordsPASS/FAIL/SKIP). Findings are projected to the shared agent-actionable shape{kind, rule, severity, locations, why, remediation}(reused fromgraph/linter.py) uniformly across all steps, so--format json/githubare identical regardless of which step produced a finding. - graph_reads.py — the application-layer read facade over the infrastructure graph-index repository (BDL-059 S2). Presentation code (
tui/) must not read SQLite directly (thetui-no-direct-infraboundary), so it consumes graph-index data — nodes, edges, symbols, hierarchy — through this facade, which delegates toinfrastructure/repository.py. Read-only; returns the repository's typed rows. Keeps the data-access seam in one place and the dependency direction honest (presentation → application → infrastructure). - status.py — the read-side of the
beadloom statuscommand (BDL-059 S4; moved down fromservices/cli.py).gather_status(conn, project_root)reads the index/coverage/health/trend counts plus per-kind breakdown out of the SQLite index into a frozenStatusDatavalue;compute_context_metrics(conn, nodes_count, symbols_count)builds each node's context bundle and returns the average/largest bundle token sizes and total indexed symbols. The CLIstatuscommand keeps only presentation (Rich or JSON rendering) — this module owns the queries.
API
Module src/beadloom/application/reindex/ (package; public surface re-exported from __init__):
ReindexResult— dataclass with counts,nothing_changedflag,errors, andwarningsreindex(project_root, *, docs_dir=None)->ReindexResult— full reindex with sync baseline preservationincremental_reindex(project_root, *, docs_dir=None)->ReindexResult— incremental reindex with parser fingerprint and graph YAML change detectionresolve_scan_paths(project_root)->list[str]— resolves source scan directories from config (defined ininfrastructure/scan_paths.py; re-exported here for backward-compatible import paths)
Module src/beadloom/application/doctor.py:
Severity— enum:OK,INFO,WARNING,ERRORCheck— dataclass:name,severity,descriptionrun_checks(conn, *, project_root=None)->list[Check]— runs DB validation checks plus optional agent instructions freshness check whenproject_rootis provided
Module src/beadloom/application/status.py:
StatusData— frozen dataclass: version/last-reindex, node/edge/doc/chunk/symbol counts, stale/isolated/empty-summary counts, coverage, per-kind breakdown, trends, andcontext_metricsgather_status(conn, project_root)->StatusData— read the full status payload (counts, coverage, health, trends, context metrics) from the indexcompute_context_metrics(conn, nodes_count, symbols_count)->dict— average/largest context-bundle token sizes (+ owning ref_id) and total indexed symbols
Module src/beadloom/application/debt_report/ (package; public surface re-exported from __init__):
DebtReport— frozen dataclass:debt_score(0-100),severity,categories,top_offenders,trendload_debt_weights(project_root)->DebtWeightscollect_debt_data(conn, project_root, weights=None)->DebtDatacompute_debt_score(data, weights=None)->DebtReportcompute_debt_trend(conn, current_report, project_root, weights=None)->DebtTrend | Noneformat_debt_report(report)->strformat_debt_json(report, category=None)->dict[str, Any]
Module src/beadloom/application/watcher.py:
DEFAULT_DEBOUNCE_MS— debounce constant (500ms)WatchEvent— frozen dataclass:files_changed,is_graph_change,reindex_typewatch(project_root, debounce_ms=DEFAULT_DEBOUNCE_MS, callback=None)— monitors project files viawatchfiles
Module src/beadloom/application/site.py:
SiteResult— frozen dataclass:out_dir,written(sorted tuple of every written path)MermaidValidationError— raised when a generated page fails the Mermaid guard (carriespage+issues)generate_site(conn, out_dir, *, project_root, federated=None, now_ts=None)->SiteResult— deterministic VitePress tree generator; never writes into the sourcedocs/; guards every emitted diagram. Emits the About homeindex.mdfromREADME.md(fallback: architecture overview), the architecture overview atarchitecture.md, a RU Aboutru/index.mdfromREADME.ru.md(skipped when absent; both link to each other via the in-page/↔/ru/cross-link), and adocs/index.mdDocumentation overview = intro + per-section named-members descriptions, no link wall (BDL-046 BEAD-11).now_tsis the injected ISO-8601 timestamp for the metrics-history point recorded this run (deterministic in tests; defaults to the current UTC instant in production — the only wall-clock read, landing solely in the append-only history store, never in the diffed output)
Module src/beadloom/application/site_mermaid_guard.py:
MermaidIssue— frozen dataclass:kind(reserved-id/charset/c4-rel-undeclared),messagevalidate_mermaid(text)->list[MermaidIssue]— targeted structural guard for flowchart reserved-id/charset + C4 Rel integrity (extensible, deterministic)
Module src/beadloom/application/site_dashboard/ (package; public surface re-exported from __init__):
build_dashboard_data(conn, *, project_root, federated=None)->dict— deterministic dashboard data (lint/debt/docs/doctor + optional federated rollup + critical-firstalerts+ threshold-coloredstatus_cards+trendstime-series + prioritizedrecommendations); honest by construction (reuses each gate's code path; trends are exactly the recorded points)render_dashboard_md(data)->str— renderdashboard.mdfrom the data dict: the page title + a short intro + the<ClientOnly>block mounting the banner + status cards (<AlertBanner/>/<StatusCards/>) and the committed ECharts widgets (<HealthGauges/>/<CategoryChart/>/<TrendCharts/>/<Recommendations/>, theme-registered, readingdashboard.data.json). No per-metric text dump, no<noscript>fallback — the widgets are the single presentation surface (data honesty lives indashboard.data.json)serialize_dashboard_data(data)->str— deterministic JSON (sorted keys) fordashboard.data.json
Module src/beadloom/application/site_metrics_history.py:
MetricsPoint— frozen dataclass:ts,lint_violations,debt_score,coverage_pct,sync_pct,nodes,edges,symbolshistory_path(project_root)->Path—.beadloom/metrics_history.jsonappend_metrics_point(project_root, point)— append/overwrite-by-ts and persist (injected ts; idempotent per ts)read_history(project_root)->list[MetricsPoint]— the recorded series sorted byts(only real points, no fabrication)backfill_structural_history(conn, project_root)— seed structural counts fromgraph_snapshots(idempotent; never clobbers a recorded point)
Module src/beadloom/application/site_landscape.py:
build_landscape_data(conn=None, *, federated=None)->dict— deterministic landscape-map data (scope/nodes/edges); federated when afederateartifact is given, else a single-repo contract map from the local graph (produces/consumesedges reconciled bycontract_keyintoContracts, classified to aContractVerdict; one edge per producer→consumer)render_landscape_md(data, *, pages=None)->str— renderlandscape.mdas a Mermaid diagram (verdict-labelled edges,classDefhealth overlay, clickable nodes); never hand-drawn.pagesis aref_id → existing page URLmap: a node emits aclickONLY when it has a real generated page, so the map never links to a dead URL (a node whose kind has no page directory — e.g. asitenode or a foreign federated repo — renders without a click)existing_page_urls(conn)->dict[str, str]— map every node that has a generated page (kinds with an output directory:service/domain/feature) to its absolute page URL (/<dir>/<ref>); fed torender_landscape_md(pages=…)so landscape clicks resolve to real pages
Module src/beadloom/application/site_pages.py:
NodeRow/NodePage— frozen dataclasses for a graph node and its rendered pageload_nodes(conn)->list[NodeRow];render_all_pages(conn)-> sortedlist[NodePage]
Module src/beadloom/application/site_nav.py:
human_label(ref_id)->str— title-cased, hyphen→space label (context-oracle→Context Oracle)render_architecture_group(conn)->str— the collapsed,part_of-nested Architecture sidebar group (human labels; self-edge-safe roots; "Architecture overview" →/architecture)render_documentation_group(project_root)->str— the Documentation sidebar group mirroring thedocs/tree (nested, collapsible;/docs/-rooted leaf links)render_documentation_group_from_dir(docs_dir, *, collapsed)->str— the Documentation group from a docs dir with an explicitcollapsedflag (expanded on the site)render_nav()->str— the top-nav JS array, intentionally"[]"(BDL-046; theme keeps appearance toggle + local search)render_sidebar(conn, *, docs_root, has_getting_started)->str— the full ordered, link-safe sidebar (About / Getting Started / flat Dashboard / Architecture / flat Landscape map / expanded Documentation)render_nav_config(conn, project_root)->str— the full deterministic.vitepress/config.generated.mjsmodule: exports onlynav(empty) + the single sharedsidebar. VitePresslocaleswas dropped (BDL-046 BEAD-11), so there is nonavRu/sidebarRu/render_sidebar_ru
Module src/beadloom/application/site_about.py:
render_about(readme_text, *, published_doc_slugs, repo_url, cross_link_routes=None)->str— pure, deterministic README→About transform: rebases doc links to/docs/<slug>; rewritesREADME.md/README.ru.mdcross-links to the route incross_link_routes(the in-page bilingual toggle/↔/ru/) or drops them when no map is given; rewrites other internal targets to absolute GitHub URLs; leaves absolute URLs/badges/anchors and code-span/fenced links untouched (EN/, RU/ru/front-door page)
Module src/beadloom/application/site_published.py:
BADGE_START/BADGE_END— stable markers delimiting the injected badge regionPublishedDoc— frozen dataclass:doc_path,status,reason,synced_at,ref_id,coverage_pctbuild_published_docs(conn, *, project_root)->list[PublishedDoc]— per-doc validation inputs fromcheck_sync(same source assync-check); a doc with no doc-code pair isuntrackedand rendered as a neutral📘 referencebadge (no coverage % line)inject_badge(prose, badge_body)->str— marker-delimited badge prefix; re-injection overwrites only the badge regionrender_published_doc(doc, prose)->str— badged Markdown (badge + authored prose as-is)publish_docs(conn, out_dir, *, project_root)->list[Path]— copydocs/**intoout_dir/docs/…with badges (plus a generateddocs/index.mdlanding page so the/docs/nav target resolves); never mutates the source
Module src/beadloom/application/gate.py:
GateStep— dataclass:name,passed,skipped,findings,summary;.status->PASS/FAIL/SKIPGateResult— dataclass:steps;.ok(all steps passed),.findings(all findings across steps)run_ci_gate(project_root, *, fail_on, hub_exports, no_reindex)->GateResult— composes reindex → lint → sync-check → config-check → doctor → (optional) federate; never short-circuits
Testing
Tests: tests/test_reindex.py, tests/test_reindex_config.py, tests/test_reindex_tests.py, tests/test_reindex_activity.py, tests/test_reindex_routes.py, tests/test_doctor.py, tests/test_doctor_drift.py, tests/test_doctor_instructions.py, tests/test_watcher.py, tests/test_debt_report.py, tests/test_debt_integration.py, tests/test_gate.py, tests/test_site_generator.py, tests/test_site_dashboard.py, tests/test_site_landscape.py, tests/test_site_published_docs.py, tests/test_site_nav.py