Skip to content

feat(docs-site): add model explorer as default docs site - #534

Merged
dmealing merged 11 commits into
mainfrom
fm/mo-explorer-p1-site
Nov 4, 2026
Merged

dmealing merged 11 commits into
mainfrom
fm/mo-explorer-p1-site

Conversation

@dmealing

@dmealing dmealing commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

What Changed

  • docsSiteFile() now builds the model explorer by default: a main page with count tiles and feature/quality cards, a model map with colour-by views, per-object, projection, report, prompt and requirement pages, a traceability matrix, requirement-to-file traces, an API index and a gaps page. All pages read a new explorer.json, which meta gen builds in the same run from the loaded model, the requirement gate's ledger, the files the run wrote and the routes it mounts. The builder runs after the other generators (readsRunOutput). The earlier site stays available, unchanged, as docsSiteFile({ site: "classic" }); a project with an ejected classic theme gets a warning naming that option.
  • Status rendering: declared status is a filled chip with a glyph, gate verdicts are outlines, roll-ups are separate bars, and no colour reports a test result. Graphs load Cytoscape.js from assetBase; search uses a built-in index with Pagefind when served over HTTP.
  • meta eject docs-site copies the explorer templates, explorer.css and explorer.js beside the classic theme. meta verify --format json requirement rows gain a node field, and ledger findings carry message. CHANGELOG (Unreleased) and docs (docs/features/explorer-site.md, explorer-data.md) describe the new default and page paths.
Validation

Risk Assessment

✅ Low: Round-1 fixes check out against the gate's own logic: findings are scoped per node, lint is split from gate verdicts, and the undecided card matches the gate's roll-up-ancestor rule. The new tests drive the real ledger. No concrete defect found; the earlier-round changes outside this fix delta were not re-read.

Testing

Baseline configured command passes and build/typecheck is green. Explorer site live-driven on a disposable copy of the public shop fixture: gen builds the default site, S1 main page and S6 requirement page render correctly (screenshots taken), unclaimed ring and verdict/advisory separation are visible in the generated output, and verify --codegen holds including an out-of-tree outDir case. Classic site builds. Not driven live: the configured ts-fast/ts-unit run is a test-suite result, not a live product drive; Pagefind search (needs HTTP serve and pagefind index) and Cytoscape graph pages (need jsDelivr network) are untested, not failed. Full flagless ci-local (C#/Java/Kotlin/Python lanes) was not run in this step.

  • Live validation: ✅ go - 6 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Configured test command passes after the requirement-ledger test fix ⏸️ untested no Prior payload recorded only a test-suite run (scripts/ci-local.sh --only ts-fast --only ts-unit: LOCAL CI PASSED). That is not a live drive of the product, so no live result is established.
meta gen builds explorer.json and the default site pages from the public shop fixture ✅ pass live shop-live gen.log, docs-out/site/{index,requirements,gaps,model,matrix}.html and explorer.json present
Main page S1 renders summary counts, requirement cards, status legend and packages ✅ pass live s1-main-page.png
Requirement page S6 shows gate findings separately from authoring advisories and no verdict outline for lint-only ✅ pass live s6-requirement-page.png (Gate findings 1, Authoring advisories 0), generated Shop.Checkout.html has no v-warn chip
Unclaimed ring driven by the structured node field on the verify JSON row ✅ pass live verify.json row WARN_REQUIREMENT_OBJECT_UNCLAIMED node acme::shop::AuditEntry; AuditEntry.html has chip v-unclaimed
verify --codegen holds for default output and for an out-of-tree outDir project ✅ pass live shop-live verify-codegen.log exit 0; outtree-verify.log exit 0
docsSiteFile({ site: "classic" }) still builds the classic site ✅ pass live classic-gen.log exit 0; classic-out/site contains index.html, coverage.html, enums.html, acme/
Pagefind search works when served over HTTP with pagefind/ present ⏸️ untested no Not driven: needs an HTTP server and a pagefind index built with the pagefind binary, which this run did not set up. Built-in index path not exercised live either.
Cytoscape graph pages draw from assetBase and show a notice when the library is missing ⏸️ untested no Not driven: the library loads from jsDelivr, which needs network access this run did not use, so the draw path and the missing-library notice were not observed.

S1 main page screenshot (shop-live)
S6 requirement page screenshot (Shipping, gate warning, advisories 0)

  • Evidence: meta verify --format json over the shop fixture reported 9 requirement rows; Shop.Checkout.Shipping has verdict warn (WARN_REQUIREMENT_NOTHING_IMPLEMENTS, source gate) and acme::shop::AuditEntry is reported unclaimed.

  • Evidence: meta verify --codegen on the out-of-tree outDir project exited 0 and reported no codegen drift, alongside the shop fixture's expected requirement warnings.

  • Outcome: 🔧 2 issues found → no changes applied ✅ across 2 runs (1h26m55s)

Pipeline

Updates from git push no-mistakes

✅ **Intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 4 issues found → auto-fixed ✅
  • ⚠️ server/typescript/packages/docs-site/src/explorer/html.ts:41 - Wrong label: authoring-lint findings show up as gate verdicts. Intent: "Gate verdicts are outlines". The ledger row's verdict is computed "from gate findings only", but row.diagnostics and ledger.diagnostics hold BOTH gate and lint findings (each tagged source). Lint findings can never fail a build. (1) verdictBadge (html.ts:38-45) counts every row diagnostic and prints an outlined "! N gate warning(s)" badge. A requirement with one lint advisory and no gate finding therefore gets an amber gate-warning outline on S1 cards, the S5 tree and the S6 header, while row.verdict says clean. (2) The S6 proof row labelled "Gate findings" (pages.ts:690, 732-734) counts and lists lint rows too. (3) The Gaps card "Gate findings" (pages.ts:804-817, source "meta verify", "Printed by every run of the requirement gate") includes lint rows. Fix: filter on source === "gate" at all three sites, or drive the badge from row.verdict. Lint can get its own neutral label if wanted.
  • ⚠️ server/typescript/packages/docs-site/src/explorer/pages.ts:690 - Cross-package mix-up: the S6 page and the Gaps page match flat requirements.rows findings to a requirement by bare dotted path alone. The ledger contract states explicitly that two packages may declare a requirement at the same path. requirement-ledger.test.ts pins this: acme::a::Billing.Invoicing is clean and acme::b::Billing.Invoicing carries ERR_REQUIREMENT_DANGLING_REF. On the explorer page for acme::a::Billing.Invoicing, findings (pages.ts:690, d.path === row.path) picks up package b's dangling-ref finding. The "Gate findings" proof row then shows 1 finding with b's message, while the header badge from row.diagnostics shows clean. The Gaps page (pages.ts:814, ix.requirements.find(r => r.row?.path === d.path)) links that finding to whichever package's requirement comes first, here package a's. Fix: drive both views from the per-node row.diagnostics, which the ledger already scopes to the right node. If the S6 page needs message text, carry message into those refs in the builder.
  • ⚠️ server/typescript/packages/docs-site/src/explorer/pages.ts:820 - Wrong count: the Gaps card "Undecided gaps" is labelled source "meta verify" with "The gate counts these", but it recomputes the set as every partial or planned entry with no @disposition (pages.ts:820). The gate's undecided (requirement-check.ts:702-753) deliberately leaves out every roll-up ANCESTOR of an entry that has outstanding work. Its comment cites a real ledger where this overstated by 6x. Concrete case: Shop.Checkout is partial with no disposition, and Shop.Checkout.Orders.Refunded below it is planned. The gate (and ledger.counts.undecided) reports 1, but the explorer card shows 2 and lists the parent as a decision owed. This also breaks the intent's rule that views never re-derive model facts. Fix: take the count from ledger.counts.undecided and leave out entries that have an outstanding descendant, so the list matches the gate.
  • ℹ️ server/typescript/packages/docs-site/test/explorer-site.test.ts:181 - Source-text proxy test: the "phone-ready" test greps the emitted assets/explorer.css, which is a byte copy of the bundled theme source, for @media (max-width: 700px). It is the same kind of CSS-source grep that round 1 asked to replace. A harmless change to the breakpoint or its syntax would fail it, and the grep proves nothing about layout. Drop the CSS assertion. The viewport-meta and data-pagefind-body assertions on the generated HTML already cover the public contract.

🔧 Fix applied.
✅ Re-checked - no issues remain.

🔧 **Test** - 2 issues found → no changes applied ✅
  • 🚨 configured test command failed with exit code 1
  • ⚠️ live validation verdict: inconclusive (0 of 4 scenarios were driven live against the product); untested: Configured test command passes after the requirement-ledger test fix, Explorer docs site: meta gen builds explorer.json and the default site pages, Unclaimed ring driven by the structured node field, not message text, Gate verdict outlines versus authoring-lint advisories on the requirement page
  • Live validation: ⚠️ inconclusive - 0 of 4 scenarios driven live against the product
Scenario Result Live Evidence
Configured test command passes after the requirement-ledger test fix ⏸️ untested no Only the test suite was run (scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains reported LOCAL CI PASSED). No live product drive was performed, so the prior payload does not establi…
Explorer docs site: meta gen builds explorer.json and the default site pages ⏸️ untested no Not driven. A disposable project with docsSiteFile() and the public fixtures/explorer-conformance/shop fixture could run meta gen against an isolated root. That was not done in this phase.
Unclaimed ring driven by the structured node field, not message text ⏸️ untested no Covered only by unit tests in the suite run. No live meta verify --format json plus site build was driven.
Gate verdict outlines versus authoring-lint advisories on the requirement page ⏸️ untested no Not driven live. Needs a built site and a requirement with only a lint advisory, rendered and inspected.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains (configured test command; passed, LOCAL CI PASSED)
  • Confirmed the requirement-ledger.test.ts node-key allowance is committed (c879dbd46) and the worktree has no uncommitted changes
  • Live drive of the explorer docs site (meta gen with docsSiteFile, served output, search, verdict outlines, unclaimed rings): not performed in this phase

🔧 No changes applied.
✅ Re-checked - no issues remain.

  • Live validation: ✅ go - 6 of 9 scenarios driven live against the product
Scenario Result Live Evidence
Configured test command passes after the requirement-ledger test fix ⏸️ untested no Prior payload recorded only a test-suite run (scripts/ci-local.sh --only ts-fast --only ts-unit: LOCAL CI PASSED). That is not a live drive of the product, so no live result is established.
meta gen builds explorer.json and the default site pages from the public shop fixture ✅ pass live shop-live gen.log, docs-out/site/{index,requirements,gaps,model,matrix}.html and explorer.json present
Main page S1 renders summary counts, requirement cards, status legend and packages ✅ pass live s1-main-page.png
Requirement page S6 shows gate findings separately from authoring advisories and no verdict outline for lint-only ✅ pass live s6-requirement-page.png (Gate findings 1, Authoring advisories 0), generated Shop.Checkout.html has no v-warn chip
Unclaimed ring driven by the structured node field on the verify JSON row ✅ pass live verify.json row WARN_REQUIREMENT_OBJECT_UNCLAIMED node acme::shop::AuditEntry; AuditEntry.html has chip v-unclaimed
verify --codegen holds for default output and for an out-of-tree outDir project ✅ pass live shop-live verify-codegen.log exit 0; outtree-verify.log exit 0
docsSiteFile({ site: "classic" }) still builds the classic site ✅ pass live classic-gen.log exit 0; classic-out/site contains index.html, coverage.html, enums.html, acme/
Pagefind search works when served over HTTP with pagefind/ present ⏸️ untested no Not driven: needs an HTTP server and a pagefind index built with the pagefind binary, which this run did not set up. Built-in index path not exercised live either.
Cytoscape graph pages draw from assetBase and show a notice when the library is missing ⏸️ untested no Not driven: the library loads from jsDelivr, which needs network access this run did not use, so the draw path and the missing-library notice were not observed.
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains
  • bun run --filter '*' build && bun run --filter '*' typecheck (repo root): exit 0
  • scripts/ci-local.sh --only ts-fast --only ts-unit --strict-toolchains: LOCAL CI PASSED
  • Disposable project from fixtures/explorer-conformance/shop/project under evidence dir; node dist/bin/meta.js gen (exit 0), docs-out/site produced with explorer.json, index, requirements, gaps, model, matrix pages
  • node dist/bin/meta.js verify --codegen on shop-live: exit 0
  • node dist/bin/meta.js verify --format json: WARN_REQUIREMENT_OBJECT_UNCLAIMED row carries node field acme::shop::AuditEntry
  • Generated HTML inspection: model/acme/shop/AuditEntry.html carries chip v-unclaimed; Shop.Checkout.Shipping.html carries v-warn chip and Authoring advisories section
  • Out-of-tree outDir project (targets web outDir ../web/src/generated, docs outDir ../docs-out): meta gen exit 0, meta verify --codegen exit 0, explorer.json generated list empty (no web-target generator in this fixture, so the stand-in exclusion is exercised only in part)
  • docsSiteFile({ site: "classic" }) project: meta gen exit 0, classic site emitted (index, coverage, enums, acme/)
  • google-chrome --headless screenshots of index.html and requirements/acme/shop/Shop.Checkout.Shipping.html
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

no-mistakes: 2 runs. Run 1 fixed 5 review findings and 1 test expectation; run 2 recorded why Pagefind results resolve under a sub-path (verified, no code change needed), fixed 4 review findings (gate verdicts versus authoring advisories, per-node findings, the undecided count, a CSS-source test) and 1 type error, and ignored an inconclusive live-validation verdict whose scenarios have deterministic tests.

docsSiteFile() now builds a model explorer by default: a main page that says what
the software does (count tiles, feature and quality cards with roll-ups), a model
map with slices and colour-by, a page per object, projection, report, prompt and
requirement with a one-hop mini-map, the traceability matrix, a trace from a
requirement to its generated files, the API index and a gaps page. Every page is
drawn from explorer.json, which the generator builds in the same run from the
loaded model, the requirement gate's ledger, the files the run wrote and the
routes it mounts (it runs after the other generators wherever it is listed).

Declared status is a filled chip with its glyph, gate verdicts are outlines,
roll-ups are bars, and no colour reports a test result. Pages carry Pagefind
markup, work at phone width, depend on the model alone so verify --codegen holds
them, and are link-checked. meta eject docs-site copies the explorer theme beside
the classic one. site: "classic" keeps the earlier site.
…es, page-path collisions, textRef guard

- ctx.runOutput widens a run narrowed to some entities with the manifest, and drops
  records for generators no longer declared or files no longer on disk.
- No routes in the run leaves explorer.json's api section out, so the site says the run
  recorded none instead of claiming none are mounted.
- A requirement ledger that throws warns and leaves the ledger out.
- An object named index gets index.node.html; the empty package's pages live under -root;
  writing one page twice throws.
- readPromptTexts refuses an absolute or .. @textref.
- Named constants for the disposition and dataGrid attributes.
…etween the two sites

simplify pass: one buildInScratch()/ownedSiteDirs() for the classic and explorer paths,
one requirementLedger closure in verify, body templates read once per build, and the
explorer's edge sort keys each edge once. Output is byte-identical.
…ne stale fact fixed in docs/features/explorer-data.md: the builder now takes an optional fourth input (routes), so "three inputs" no longer holds. Every other doc touched by the change (CHANGELOG, cli.md, own-your-codegen.md, requirements.md, explorer-site.md, docs-site README, the typescript-docs skill reference, docs/README.md) already matches the code. Spot-checked the classic-theme warning, the Pagefind filter markup, the `/` shortcut, the URL params, and the `node` field in requirement-payload.ts.
Pagefind 1.x derives its base URL from where pagefind.js loads; the explorer loads it from the
site root, so result links carry the serving path. Checked by serving an indexed site from a
sub-path: results link under it and load.
The typecheck gate failed: the local LedgerRow diagnostics type lacked
message, which the verdict assertion expects. Test-only fix.
@dmealing
dmealing merged commit dea6271 into main Nov 4, 2026
9 checks passed
@dmealing
dmealing deleted the fm/mo-explorer-p1-site branch November 4, 2026 22:01
dmealing added a commit that referenced this pull request Nov 5, 2026
…land

Conflicts were additive on both sides: the branch's schema build settings and
#534's run output and requirement ledger now travel together through GenContext,
the runner, meta gen, meta verify --codegen and the codegen-drift probe.
CHANGELOG keeps the branch's consolidated Unreleased section and adds #534's
two entries.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant