Skip to content

fresh

last synced 2026-06-17T22:39:35.514075+00:00 · coverage 86% (application)

Validation by Beadloom doc_sync — same source as sync-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 ci enforcement 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. Backfills symbols_indexed from the live DB (total code_symbols count) so the result reports the true symbol total, not just the per-run delta (mirroring the #88 nodes/edges backfill).
  • doctor.pyrun_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 when project_root is 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.pywatch() monitors project files (graph YAML, docs, source) and auto-triggers reindex on changes using watchfiles. Graph changes trigger full reindex; other changes trigger incremental. WatchEvent frozen dataclass captures per-event metadata. DEFAULT_DEBOUNCE_MS constant (500ms).
  • site.pygenerate_site(conn, out_dir, *, project_root, federated=None, now_ts=None) is the docs site use-case: it reads the indexed graph read-only and writes a VitePress content tree under out_dir (default site/) — an index.md About home page rendered from the project README.md via site_about.render_about (link-rebased; falls back to the architecture overview body when no README.md), a ru/index.md RU About page from README.ru.md (omitted when absent; both About pages get the in-page bilingual cross-link //ru/ via cross_link_routes), an architecture.md architecture overview (counts + top-level C4/Mermaid + a read-only health summary — the body that used to live at index.md, BDL-046), a docs/index.md Documentation 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 to site_pages.py), the metrics dashboard (dashboard.md + dashboard.data.json, delegated to the site_dashboard/ package), the 🌟 landscape map (landscape.md, delegated to site_landscape.py), and .vitepress/config.generated.mjs (nav/sidebar). Before building the dashboard it backfills structural trend history from graph_snapshots and records this run's honest metrics point (site_metrics_history.append_metrics_point) so the emitted trend series includes "now"; now_ts is 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 source docs/. Returns a frozen SiteResult listing every written path. Reuses graph/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 raises MermaidValidationError and 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 sorted NodePages; each page has summary, source, public symbols, a Relationships section, linked hand-written docs (rooted at /docs/ so they resolve to the published copy under site/docs/…), and an embedded scoped C4/Mermaid diagram. The Relationships section renders OUTGOING part_of/depends_on/uses edges as Markdown links to other node pages, then INCOMING relationships: Used by — the sorted, deduped union of incoming uses+depends_on consumers (who consumes this node; no separate "Depended on by" section) — and Parts — incoming part_of child 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.mjs module exporting only nav + sidebar (BDL-046 BEAD-11 dropped VitePress locales — its global /x↔/ru/x mapping translated the whole menu and 404'd off /ru/ — so there is a single shared EN sidebar and no navRu/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 is collapsed: true and a part_of-nested tree (service root → domains → features) with human-readable labels via human_label (context-oracleContext Oracle), roots being nodes with no real part_of parent (a root part_of root self-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 is collapsed: false (expanded) and mirrors the docs/ directory tree (render_documentation_group_from_dir(docs_dir, *, collapsed)) as a nested, collapsible structure (each subdir a group, each .md a 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): a docs/<x>.md link whose slug is in published_doc_slugs → the extension-less site link /docs/<x>; a README.md/README.ru.md cross-link → if its lowercased basename is in cross_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 and render_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 via graph/linter.lint), debt (debt_report.compute_debt_score + compute_debt_trend, serialized via format_debt_json), docs (coverage % + sync_state freshness % + stale count, read-only), doctor (doctor.run_checks pass/fail summary), and an optional federated rollup (per-service edge-verdict health + contract-verdict counts) reusing the federate output verbatim. It also emits trends — the recorded time-series from site_metrics_history.read_history (sorted by ts; 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.json the CI harness emits (absent/empty/corrupt → an empty-but-present section, never an error): runs[] sorted by ts with per-run + cumulative docs-refreshed and input/output token spend (ONLY real recorded runs — same no-interpolation contract as trends), totals, and a cost_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_TOKENS rate, never a hard cost (rendered by the AiTechwriterActivity widget) — and recommendations — a prioritized, actionable list built from the EXISTING gate data (one item per lint violation, BREAKING/DRIFT contract risks from the --federated artifact, stale docs from sync_state, and worst-debt nodes from debt_report top 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 emits alerts — 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 — and status_cards — one threshold-colored card per metric group ({group, label, status, value, detail} with statusok/warn/error, the severity computed deterministically in Python so the front-end only paints the color). render_dashboard_md emits 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 from dashboard.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) and render_landscape_md(data, *, pages=None) renders a Mermaid diagram from it (never hand-drawn). With a federated.json (the F2 federate hub output) nodes are the satellites and edges are the cross-repo links carrying the hub's ContractVerdict-style verdict verbatim; without it the map is the LOCAL contract graph_local_landscape reads the repo's own produces/consumes edges, reconciles them by contract_key into graph.contracts.Contracts, classifies each to a ContractVerdict, and renders one edge per producer→consumer coloured by that verdict (Beadloom's own site emits a single beadloom → vitepress-site CONFIRMED edge; a repo with no contracts → an empty map). This is the real contract reality, not the structural depends_on/uses arch (which stays in the C4 overview). Edges are labelled by their verdict; a Mermaid classDef health overlay colours nodes (green = healthy, red = broken, grey = external/expected) and broken edges get a red linkStyle. Clicks are page-aware: a node emits click <id> "/<dir>/<ref>" ONLY when pages (from existing_page_urls(conn)) has a real generated page for it — a node with no page (a site node, 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 named graph becomes n_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 of MermaidIssue for the two F4 render bug classes: (1) a flowchart/graph node id that equals a reserved Mermaid keyword or has an illegal charset; (2) a C4 Rel(a, b, …) whose endpoint is not a declared Container/Component/Person/System* node (a Rel to the boundary/root crashes drawRels). An extensible validator registry; deterministic (issues in source order). site.generate_site calls 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.json of MetricsPoints (ts, lint_violations, debt_score, coverage_pct, sync_pct, nodes, edges, symbols). append_metrics_point(project_root, point) records one point per docs site run (the ts is supplied by the caller — never now() inside this module — so tests are deterministic; appending an existing ts overwrites that point so a re-run does not double-count); read_history(project_root) returns the series sorted by ts (only real recorded points, never an interpolated one); backfill_structural_history(conn, project_root) seeds structural counts (nodes/edges/symbols) from the existing graph_snapshots history 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 REAL docs/** tree into out_dir/docs/… preserving structure (the source of truth, rendered as-is) and injects a per-doc validation badge into the COPY only — the source docs/ is NEVER mutated (no AI prose-rewriting; that is the deferred F4.1). A generated docs/index.md landing 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 the doc_sync engine via check_sync — the SAME code path beadloom sync-check runs — so a doc the gate calls stale shows stale on 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 show last synced (the stored sync_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_cells are 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.py re-exports them for back-compat); bd_status_to_cell(bd_status) is the documented bd-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 every features/*/ACTIVE.md), locates the bead-status table's Status column by header index (3- or 4-col), and rewrites only the cells whose state drifts from the injected bd statuses — preserving a richer note when the state already agrees — returning a ReconcileResult (changed_files, drifted_rows) for --check vs fix. Best-effort: never raises, touches only Status cells (prose/Progress Log/other columns byte-preserved). Classified as the active-table component node (its own DOC.md).
  • gate.pyrun_ci_gate(project_root, *, fail_on, hub_exports, no_reindex) is the unified CI enforcement gate (the beadloom ci orchestrator). It composes the existing checkers IN ORDER — reindex (unless no_reindex) → lint --strictsync-checkconfig-check (AgentConfigAsCode) → doctor (graph/data integrity; only ERROR-severity checks fail the gate, so advisory WARNING/INFO checks never block — no false gate) → (when hub_exports given) federate --fail-on — into one GateResult whose .ok is True only when every step passed. It ORCHESTRATES existing domain code; it reimplements no checker (the doctor step reuses doctor.run_checks). Honesty invariants: no short-circuit (every step runs and ALL findings are collected even after an earlier failure) and no silent skip (each GateStep records PASS/FAIL/SKIP). Findings are projected to the shared agent-actionable shape {kind, rule, severity, locations, why, remediation} (reused from graph/linter.py) uniformly across all steps, so --format json/github are 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 (the tui-no-direct-infra boundary), so it consumes graph-index data — nodes, edges, symbols, hierarchy — through this facade, which delegates to infrastructure/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 status command (BDL-059 S4; moved down from services/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 frozen StatusData value; 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 CLI status command 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_changed flag, errors, and warnings
  • reindex(project_root, *, docs_dir=None) -> ReindexResult — full reindex with sync baseline preservation
  • incremental_reindex(project_root, *, docs_dir=None) -> ReindexResult — incremental reindex with parser fingerprint and graph YAML change detection
  • resolve_scan_paths(project_root) -> list[str] — resolves source scan directories from config (defined in infrastructure/scan_paths.py; re-exported here for backward-compatible import paths)

Module src/beadloom/application/doctor.py:

  • Severity — enum: OK, INFO, WARNING, ERROR
  • Check — dataclass: name, severity, description
  • run_checks(conn, *, project_root=None) -> list[Check] — runs DB validation checks plus optional agent instructions freshness check when project_root is 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, and context_metrics
  • gather_status(conn, project_root) -> StatusData — read the full status payload (counts, coverage, health, trends, context metrics) from the index
  • compute_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, trend
  • load_debt_weights(project_root) -> DebtWeights
  • collect_debt_data(conn, project_root, weights=None) -> DebtData
  • compute_debt_score(data, weights=None) -> DebtReport
  • compute_debt_trend(conn, current_report, project_root, weights=None) -> DebtTrend | None
  • format_debt_report(report) -> str
  • format_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_type
  • watch(project_root, debounce_ms=DEFAULT_DEBOUNCE_MS, callback=None) — monitors project files via watchfiles

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 (carries page + issues)
  • generate_site(conn, out_dir, *, project_root, federated=None, now_ts=None) -> SiteResult — deterministic VitePress tree generator; never writes into the source docs/; guards every emitted diagram. Emits the About home index.md from README.md (fallback: architecture overview), the architecture overview at architecture.md, a RU About ru/index.md from README.ru.md (skipped when absent; both link to each other via the in-page //ru/ cross-link), and a docs/index.md Documentation overview = intro + per-section named-members descriptions, no link wall (BDL-046 BEAD-11). now_ts is 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), message
  • validate_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-first alerts + threshold-colored status_cards + trends time-series + prioritized recommendations); honest by construction (reuses each gate's code path; trends are exactly the recorded points)
  • render_dashboard_md(data) -> str — render dashboard.md from 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, reading dashboard.data.json). No per-metric text dump, no <noscript> fallback — the widgets are the single presentation surface (data honesty lives in dashboard.data.json)
  • serialize_dashboard_data(data) -> str — deterministic JSON (sorted keys) for dashboard.data.json

Module src/beadloom/application/site_metrics_history.py:

  • MetricsPoint — frozen dataclass: ts, lint_violations, debt_score, coverage_pct, sync_pct, nodes, edges, symbols
  • history_path(project_root) -> Path.beadloom/metrics_history.json
  • append_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 by ts (only real points, no fabrication)
  • backfill_structural_history(conn, project_root) — seed structural counts from graph_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 a federate artifact is given, else a single-repo contract map from the local graph (produces/consumes edges reconciled by contract_key into Contracts, classified to a ContractVerdict; one edge per producer→consumer)
  • render_landscape_md(data, *, pages=None) -> str — render landscape.md as a Mermaid diagram (verdict-labelled edges, classDef health overlay, clickable nodes); never hand-drawn. pages is a ref_id → existing page URL map: a node emits a click ONLY 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. a site node 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 to render_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 page
  • load_nodes(conn) -> list[NodeRow]; render_all_pages(conn) -> sorted list[NodePage]

Module src/beadloom/application/site_nav.py:

  • human_label(ref_id) -> str — title-cased, hyphen→space label (context-oracleContext 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 the docs/ 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 explicit collapsed flag (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.mjs module: exports only nav (empty) + the single shared sidebar. VitePress locales was dropped (BDL-046 BEAD-11), so there is no navRu/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>; rewrites README.md/README.ru.md cross-links to the route in cross_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 region
  • PublishedDoc — frozen dataclass: doc_path, status, reason, synced_at, ref_id, coverage_pct
  • build_published_docs(conn, *, project_root) -> list[PublishedDoc] — per-doc validation inputs from check_sync (same source as sync-check); a doc with no doc-code pair is untracked and rendered as a neutral 📘 reference badge (no coverage % line)
  • inject_badge(prose, badge_body) -> str — marker-delimited badge prefix; re-injection overwrites only the badge region
  • render_published_doc(doc, prose) -> str — badged Markdown (badge + authored prose as-is)
  • publish_docs(conn, out_dir, *, project_root) -> list[Path] — copy docs/** into out_dir/docs/… with badges (plus a generated docs/index.md landing 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/SKIP
  • GateResult — 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