Skip to content

S10 — Spec Visualization on the Docs Site ​

Overview ​

The RFC-2119 specs (specs/*.md) and the requirement traceability matrix (specs/traceability.json) are the project's source of truth, but they are only readable as raw files in the repository. This spec puts them on the VitePress docs site: a Specs section with a guidelines page (how the spec-driven workflow works), a filterable matrix table, and one browsable page per spec — all generated at build time from the files in specs/, never hand-copied, so the site cannot drift from the repository.

There is no Spec Kit (or other) standard for visualizing a spec corpus — Spec Kit defines an authoring workflow (specify → plan → tasks), not a publishing format — so this spec defines the project's own minimal presentation layer on top of the existing artifacts.

User Stories ​

  • As a contributor, I want to browse the specs and their implementation status on the docs site so I don't have to read raw markdown and JSON in the repo.
  • As a maintainer, I want to filter the matrix by status (e.g. planned) to see at a glance what is specified but not yet built.
  • As a newcomer, I want a page that explains how the spec-driven workflow works (IDs, statuses, test tags, checkers) before I make my first change.

Functional Requirements ​

Specs Section ​

  • S10-SR-01 The docs site MUST expose a Specs section reachable from the top navigation, containing at minimum: a guidelines page explaining the spec-driven workflow, a matrix page, and one page per spec file.
  • S10-SR-02 The guidelines page MUST describe: the RFC-2119 key-word convention, requirement IDs and where they are defined, the five matrix statuses and their meanings, [ID] test tags, <!-- spec:… --> documentation tags, and the checkers that enforce them (check:traceability, check:doc-traceability).

Matrix Page ​

  • S10-SR-03 The matrix page MUST render a table listing every spec, with its ID, title, kind (feature/system), and a per-status breakdown of its requirements, derived from specs/traceability.json.
  • S10-SR-04 The table MUST be filterable by requirement status and by spec kind, and searchable by free text over spec ID and title. Status and kind each MUST be presented as a multi-select dropdown (a labelled control that opens a checkbox list), so several values can be active at once. Within one dropdown the selected values combine as OR (a spec matches if it has at least one requirement in any selected status; a spec matches if its kind is any selected kind); the two dropdowns and the search box combine as AND. An empty dropdown applies no constraint for that dimension (all values pass), and the control MUST show how many values are active.
  • S10-SR-05 Each spec listed in the table MUST link to that spec's page.
  • S10-SR-09 The matrix table MUST show the advisory estimates alongside the status breakdown as columns: effort points and T-shirt size (S13), and for feature specs the customer-value score, tier and RICE ratio (S16). A spec with no such estimate MUST render an explicit placeholder rather than a blank or a zero, so "not scored" is never read as "scored low". The RICE cell MUST also carry a proportional bar scaled against the corpus's highest RICE ratio, so relative value-per-effort is readable at a glance without sorting — the matrix is the project's one RICE ranking surface (S10-SR-12).
  • S10-SR-10 Column visibility MUST be user-controllable through a multi-select dropdown listing every column except the spec id, which MUST remain visible at all times so a row is always identifiable. The control MUST show how many columns are visible, and the choice applies to the table only — it MUST NOT change which specs the filters match.
  • S10-SR-16 Every column, including the spec id, MUST be sortable by clicking its header: the first click sorts ascending, a second click on the same header sorts descending, and a third click returns the table to its unsorted (spec-id source) order. The active column and direction MUST be indicated visually and via the header's aria-sort attribute. Sorting MUST NOT change which specs the filters match, only their order, and — echoing S10-SR-09 — a row with no estimate for the sorted metric column MUST always sort after every scored row, in both directions, so "not scored" is never read as "scored low" by appearing at the top of a descending sort.
  • S10-SR-23 The matrix table MUST show a Release column: the version of the first published release that shipped that spec. It is derived at build time by joining the spec file's own git history (the same commits S10-SR-14 lists) against the repository's chore(main): release X.Y.Z commits — never hand-maintained, and never read from a hand-edited field. A spec whose file predates the oldest commit reachable in the build's checkout MUST render the same explicit "no history available" state as S10-SR-15, distinct from a spec that has history but has not shipped yet, which MUST render an explicit "unreleased" placeholder — neither case may render a blank or fall back to a version number. The column follows the same column-visibility control (S10-SR-10) and header-click sort (S10-SR-16) as every other column; a row in either placeholder state MUST always sort after every released row, in both directions, echoing S10-SR-09's "not scored" guarantee.
  • S10-SR-24 The matrix table MUST show Created and Updated columns: the date of the oldest and, respectively, newest commit in that spec file's own git history (the same commits S10-SR-14 lists), each linking to that commit on the repository host. A spec with no discoverable history in the build's checkout MUST render both as the same explicit "no history available" state as S10-SR-15 rather than a blank cell — this includes the shallow-boundary case noted there, where the reported oldest commit may not be the file's true first commit, so "Created" inherits that same caveat. Both columns follow the same column-visibility control (S10-SR-10) and header-click sort (S10-SR-16) as every other column, sorting chronologically by date, with the no-history placeholder always sorting after every dated row in both directions.
  • S10-SR-11 Each spec's sidebar entry MUST carry a compact advisory-metric indicator (T-shirt size, and RICE where one exists), so the estimates are comparable while browsing without opening each page. The indicator MUST be built from the same build-time data as the matrix, never hand-maintained.

Insights Page ​

  • S10-SR-12 The Specs section MUST include an insights page reporting corpus-level KPIs derived from the spec artifacts: requirement and spec totals, the status breakdown, documentation and test-tag coverage, total and mean effort, and the value-tier and effort-size distributions. Every figure MUST be derived at build time from specs/ (S10-NFR-01). The page MUST NOT reproduce the per-spec metric listing the matrix page already serves: ranking feature specs by RICE is the matrix's job (its RICE column sorts, per S10-SR-16, and carries the proportional bar, per S10-SR-09), and a second rendering of the same list would be two tables to keep in step.

  • S10-SR-13 The insights page MUST label the effort- and value-derived figures as advisory estimates and visually separate them from the counted facts (requirements, statuses, coverage), so a judgement is never presented as a measurement.

  • S10-SR-17 The insights page MUST present the corpus graphically, not only as numbers: the counted-facts section MUST include a per-spec stacked bar chart of requirements by status, using the same status colors as the status badges so the two read as one system; the advisory section MUST include a value-versus-effort scatter plot of the scored feature specs, with the S16 tier bands marked on the value axis and constant-RICE guide lines so value-per-effort is readable as slope; and the value-tier and effort-size distributions MUST be rendered as labelled count bars. Charts MUST be built from the same build-time data as the rest of the page (S10-NFR-01) with no new runtime dependency or chart library (S10-NFR-02). Every mark MUST expose its exact values (tooltip or visible label) so a graphic never replaces the numbers with an impression, and marks that would sit at identical coordinates MUST merge into one mark naming all of them rather than overplot.

  • S10-SR-18 The insights page MUST include an estimate-ageing chart plotting each spec's recorded evidence LOC snapshot (specs/effort.json, S13-SR-02) against its implementation size today, with the S13 LOC band boundaries drawn and the equality diagonal marked. A spec whose live LOC has crossed into a different band MUST be visually distinguished and named, because that is precisely the condition npm run check:spec-effort warns on — the chart and the checker MUST derive it from the same exported rubric helpers (computeEvidence, basePointsForLoc) rather than reimplementing the bands, so the two can never disagree.

    The chart MUST NOT instead plot effort points against implementation LOC as evidence that the rubric is calibrated: basePoints is derived from LOC by the band table, so any such correlation is circular by construction and would present a tautology as a finding. Ageing of a committed snapshot is the non-circular question, and the page MUST say so where it could mislead.

  • S10-SR-19 The insights page MUST include a demo coverage chart listing feature specs ranked by customer value and marking, for each, whether an end-to-end demo exercises it. The mapping MUST come from scripts/demo-registry.mjs — S08-SR-13's single source of truth for which demo covers which spec — never a hand-maintained list, and specs with no demo MUST be distinguishable by more than colour alone. Because the ranking is value-derived it belongs in the advisory section (S10-SR-13), and the page MUST note that demo presence itself is a counted fact while only the ordering is an estimate.

  • S10-SR-20 The insights page MUST include a test-strength chart plotting each scored feature spec's customer value (S16) against its mutation score (S18), so a valuable feature with weak tests is visible as a position rather than something to be hunted for in a table. Per-spec scores MUST be joined from mutation-score.json through the matrix's impl paths, weighted by each file's mutant count so a large file cannot be outvoted by a small one. Where mutation-score.json is absent or a spec has no mutated file, the chart MUST render an explicit not measured state (S18-SR-04) — never a zero — and MUST show the artifact's generation date beside it (S18-SR-03), because the score is a snapshot that ages.

    Line coverage MUST NOT be used as this chart's axis: the 80% gate compresses every spec into a 90–99% band, so the axis would rank nothing. That the measurement exists is not sufficient reason to plot it.

  • S10-SR-21 The insights page MUST include a requirement lifecycle chart built from the S11 stamps: for requirements that carry them, the time from specifiedAt to implementedAt, and for those still open, how long they have been waiting. Requirements with no stamp MUST be reported as predating lifecycle tracking and counted separately rather than drawn as zero-day (S11-SR-09) — a same-day requirement and an unmeasured one are different facts and MUST NOT share a mark. The chart MUST render its own empty state gracefully: the corpus begins with no tracked requirements at all, and a chart that looked broken until data accumulated would be a standing invitation to delete it.

  • S10-SR-22 The insights page MUST report defect attribution from the S19 Fixes: trailers: which specs have accumulated fixes, and how many fix commits carry no attribution. The unattributed count MUST be shown wherever the attributed counts are (S19-SR-05) — the convention starts with an empty history, so a low defect count means "not yet recorded" far more than it means "not fragile", and showing the former without the latter would invert the reading. Counts MUST come from the build-time git loader, never a committed tally.

Spec Pages ​

  • S10-SR-06 Each spec page MUST render the full content of the corresponding specs/*.md file, with relative links to sibling spec files rewritten to the sibling's docs-site page.
  • S10-SR-07 Each spec page MUST include a requirement traceability table for that spec's requirements: ID, status, implementing files (linked to the repository), and the matrix note.
  • S10-SR-08 Each spec page MUST list the documentation sections that carry that spec's <!-- spec:… --> tags (S07), showing for each: the source document, the nearest heading, and the attributed word count — linking docs-site pages to the page and nearest anchor, and repository-only documents (e.g. README.md) to the file on the repository host. The list is derived at build time from the S07 depth-metric library (S07-SR-13), never hand-maintained, and a spec with no tagged section shows its expected word count as the gap to fill.

Changes Over Time ​

  • S10-SR-14 Each spec page MUST render a changes over time section listing every commit that has touched that spec's specs/*.md file, newest first: short hash, date, and subject line, each linking to that commit on the repository host. The list MUST be derived at build time from the file's own git log history (following renames) — never a hand-maintained changelog — so it can never drift from what actually happened to the file.
  • S10-SR-15 A spec file with no history available at build time (e.g. a shallow checkout with the commit outside the fetched depth) MUST render an explicit "no history available" state rather than an empty or misleading section. Note this is not the only failure mode a shallow checkout causes: in a --depth 1 clone, git log --follow on a file the tip commit never touched can still report that tip commit as if it were part of the file's history (a shallow-boundary quirk, not a bug in this feature's own code) — silently wrong, not merely empty, and so not caught by this requirement's fallback at all. docs.yml's checkout MUST use fetch-depth: 0 for exactly this reason: it's a correctness requirement for S10-SR-14, not just a convenience for completeness.

Non-Functional Requirements ​

  • S10-NFR-01 All Specs-section content MUST be generated at build time from specs/*.md, specs/traceability.json, and (for S10-SR-14) the git history of each spec file (VitePress data loaders / dynamic routes). No spec text, requirement list, status value, or change history may be duplicated by hand into docs/ — a change to specs/ (or a new commit touching it) is reflected by the next docs build with no manual step.
  • S10-NFR-02 The section MUST build with the existing docs toolchain (VitePress, docs.yml workflow) with no new runtime services; pages are fully static and filtering runs client-side.

Out of Scope ​

  • Visualizing per-requirement test coverage (which tests carry a given [ID] tag) — the checker discovers this at verify time; surfacing it on the site is future work.
  • Editing or annotating specs from the site — the repository remains the only write path.
  • Rendering .specify/ artifacts (constitution, templates) — only specs/ is published.

Acceptance Criteria ​

  1. npm run build in docs/ succeeds and emits /specs/ (guidelines), /specs/matrix, /specs/insights, and one page per specs/[FS]*.md file.
  2. Adding a requirement to a spec and its entry to traceability.json changes the matrix table on the next build with no edit under docs/.
  3. Selecting the planned status filter hides every spec whose requirements are all implemented/manual.
  4. Clicking a spec ID in the matrix opens that spec's page, whose traceability table matches the matrix entries for that spec.
  5. npm run check:doc-traceability passes with the new pages tagged <!-- spec:S10 --> (and the guidelines page additionally tagged for the traceability conventions it documents).
  6. Unchecking a column in the matrix's column dropdown removes it from the table while the spec id column stays; the row count is unchanged.
  7. The insights page's requirement total equals the matrix's, and sorting the matrix's RICE column descending yields the same order as npm run spec-value:report; the insights page itself contains no per-spec RICE listing.
  8. Each spec page shows a "Changes over time" list of commits touching that spec's file, newest first, each linking to the commit on the repository host; a spec file with no discoverable history shows the explicit empty state instead.
  9. Clicking the "Effort" header sorts the table by effort points ascending with unscored specs last; clicking it again reverses to descending with unscored specs still last; a third click restores the original order.
  10. The insights page renders the requirements-by-status stacked chart and the value-versus-effort scatter; every scored feature appears in the scatter exactly once (co-located features share one labelled mark), and adding a requirement to traceability.json changes the status chart on the next build with no edit under docs/.
  11. Every spec the estimate-ageing chart marks as drifted is also warned about by npm run check:spec-effort, and vice versa — the two lists match because both call basePointsForLoc on the same evidence.
  12. Adding a demo to scripts/demo-registry.mjs moves that spec from the "no demo" to the "has demo" state on the next docs build, with no edit under docs/.
  13. The matrix's Release column shows, for each spec, the version of the first release whose chore(main): release X.Y.Z commit is reachable from that spec file's oldest git-history commit; a spec added after the latest release shows "unreleased", and a spec with no discoverable history shows the same "no history available" state as its spec page (S10-SR-15). Sorting by Release always places both placeholder states after every released row, in both directions.
  14. The matrix's Created and Updated columns show the oldest and newest commit dates from each spec file's git history, each linking to that commit on the repository host; a spec with no discoverable history shows "no history available" for both, sorting after every dated row in either direction.

History ​

  • 2026-07-26 — Added S10-SR-24: Created and Updated columns on the matrix, showing the oldest and newest commit dates from each spec file's own git history (S10-SR-14's commit list), each linking to that commit — reusing the same "no history available" fallback as S10-SR-15/23 rather than a blank cell.
  • 2026-07-26 — Added S10-SR-23: a Release column on the matrix showing the first published version that shipped each spec, joining the spec file's own git history (S10-SR-14) against chore(main): release X.Y.Z commits, with "unreleased" and "no history available" (S10-SR-15) as distinct explicit placeholder states rather than a blank cell.
  • 2026-07-25 — Added S10-SR-14/15: a per-spec "changes over time" section sourced from git log on the spec file itself, so browsing a spec's page shows its evolution without needing to leave the docs site for the repository's commit history.
  • 2026-07-25 — docs.yml's checkout was still the default fetch-depth: 1 (shallow) when this shipped. Verified in a real --depth 1 clone that this is worse than S10-SR-15's "no history" fallback: git log --follow on a file the tip commit never touched still reported that tip commit as the file's history — every spec page would have shown the same one build-time-irrelevant commit, not an empty/fallback state. Fixed by adding fetch-depth: 0 to docs.yml's checkout step; S10-SR-15 amended to record the failure mode so a future shallow-checkout regression is recognized.
  • 2026-07-25 — Added S10-SR-16: click-to-sort matrix columns (asc/desc/ unsorted cycle, aria-sort indicator), with unscored effort/value/RICE rows always sorting after scored ones regardless of direction.
  • 2026-07-25 — Added S10-SR-20/21: a test-strength chart over the new S18 mutation-score artifact (line coverage was tried first and rejected — the 80% gate flattens it to r ≈ 0.08 against value), and a requirement-lifecycle chart over the new S11 stamps, which starts empty by design because those stamps are deliberately not backfilled.
  • 2026-07-25 — Added S10-SR-18/19, two charts built from artifacts the repository already had but never joined: the S13 evidence snapshot against live implementation size (estimate ageing), and the S08 demo registry against the S16 value ranking (demo coverage). S10-SR-18 also records a rejected design — points-versus-LOC as a "the rubric is calibrated" chart — because basePoints is derived from LOC and the correlation is therefore circular; the drift of a committed snapshot is the question that is not.
  • 2026-07-25 — Amended S10-SR-09/12 and added S10-SR-17: the insights page's RICE ranking table duplicated the matrix once S10-SR-16 made the RICE column sortable, so the list moved out — the matrix's RICE cell gained the proportional bar the table alone had — and the insights page now carries charts instead: per-spec requirements-by-status stacked bars, a value-versus-effort scatter with S16 tier bands and constant-RICE guides, and count bars for the tier and effort-size distributions.

Requirement traceability ​

Status of each requirement in specs/traceability.json. Test coverage is auto-discovered from [ID] tags in test titles and is not listed here.

RequirementStatusImplementationNote
S10-NFR-01manualdocs/.vitepress/specs.data.ts, docs/specs/[id].paths.tsdocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-NFR-02manualdocs/.vitepress/config.tsdocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-01manualdocs/.vitepress/config.ts, docs/specs/index.md, docs/specs/matrix.md, docs/specs/[id].mddocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-02manualdocs/specs/index.mddocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-03manualdocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecMatrix.vuedocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-04manualdocs/.vitepress/theme/SpecMatrix.vuedocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-05manualdocs/.vitepress/theme/SpecMatrix.vuedocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-06manualdocs/specs/[id].paths.ts, docs/specs/[id].mddocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-07manualdocs/.vitepress/theme/SpecTraceability.vue, docs/specs/[id].mddocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-08implementeddocs/.vitepress/theme/SpecDocSections.vue, docs/.vitepress/docCoverage.data.ts, docs/specs/[id].md"Documented in" list on each spec page: heading links to page#anchor or repo blob; expected-words gap shown when untagged
S10-SR-09implementeddocs/.vitepress/theme/SpecMatrix.vue, docs/.vitepress/specs.data.tseffort (points + T-shirt), value (score + tier) and RICE columns joined from effort.json/value.json; unscored specs render an explicit placeholder, never a zero
S10-SR-10implementeddocs/.vitepress/theme/SpecMatrix.vue, docs/.vitepress/theme/SpecFilterDropdown.vueColumns multi-select reusing the filter dropdown with an "N of M" summary; spec id omitted from the options so it can never be hidden
S10-SR-11implementeddocs/.vitepress/config.ts, docs/.vitepress/theme/style.csscompact T-shirt + RICE badges injected into sidebar labels (VitePress renders them with v-html) from the same committed JSON the matrix reads
S10-SR-12implementeddocs/specs/insights.md, docs/.vitepress/theme/SpecInsights.vue, docs/.vitepress/specs.data.tscorpus KPIs: spec/requirement totals, status breakdown, test-tag and doc coverage, effort and value aggregates, RICE ranking — all build-time derived
S10-SR-13implementeddocs/.vitepress/theme/SpecInsights.vue, docs/specs/insights.mdcounted facts and advisory estimates in separate sections, advisory block carries a warning callout and dashed KPI cards
S10-SR-14manualdocs/.vitepress/specHistory.data.ts, docs/.vitepress/theme/SpecHistory.vue, docs/specs/[id].md, .github/workflows/docs.ymldocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-15manualdocs/.vitepress/specHistory.data.ts, docs/.vitepress/theme/SpecHistory.vue, .github/workflows/docs.ymldocs-site visualization requirement: verified by the VitePress build and visual review, not unit-taggable
S10-SR-16implementeddocs/.vitepress/theme/SpecMatrix.vueclick-to-sort headers cycling unsorted/asc/desc per column, aria-sort indicator; unscored metrics always sort after scored rows in either direction
S10-SR-17implementeddocs/.vitepress/theme/SpecInsights.vueinsights-page charts: status stacked bars, value-vs-effort scatter, distribution bars
S10-SR-18implementeddocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecInsights.vueestimate-ageing chart; drift derived from scripts/spec-effort.mjs exports so chart and checker agree
S10-SR-19implementeddocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecInsights.vuedemo-coverage chart sourced from scripts/demo-registry.mjs (S08-SR-13)
S10-SR-20implementeddocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecInsights.vuevalue x mutation-score quadrant; not-measured state when mutation-score.json is absent
S10-SR-21implementeddocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecInsights.vuerequirement lifecycle from S11 stamps; unstamped requirements counted apart, never drawn as zero-day
S10-SR-22implementeddocs/.vitepress/defects.data.ts, docs/.vitepress/theme/SpecInsights.vuedefect attribution panel with the unattributed count alongside
S10-SR-23implementeddocs/.vitepress/gitHistory.ts, docs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecMatrix.vueRelease column resolves each spec's oldest git-history commit against chore(main): release X.Y.Z commits; unreleased/no-history render distinct placeholders that always sort after a released row
S10-SR-24implementeddocs/.vitepress/specs.data.ts, docs/.vitepress/theme/SpecMatrix.vue, docs/.vitepress/theme/repo.tsCreated/Updated columns from the oldest/newest entry of the same per-spec git history as S10-SR-14/23, each linking to that commit via the shared REPO_COMMIT_URL; no-history rows share S10-SR-15's placeholder and sort last

Documented in ​

Documentation sections tagged spec:S10 (or one of its requirement ids) — 1245 of ~1609 expected words (77%). The expectation comes from this spec's evaluated complexity of 40.2 (26 documentable requirements — 24 SR, 2 NFR — weighted by kind and by each definition's length).

Changes over time ​

  • 92bd4212026-07-26feat(S10): add Created and Updated columns to the spec matrix
  • 6620c3a2026-07-26feat(S10): add a Release column to the spec matrix
  • efd676d2026-07-26feat(specs): attribute defects via a Fixes: commit trailer, and close CI gaps
  • c0156392026-07-26feat(docs): add requirement lifecycle stamps and publish the mutation score
  • 9d66f9b2026-07-26feat(docs): chart estimate ageing and demo coverage on the insights page
  • 862bd6a2026-07-25feat(docs): replace the insights RICE table with charts, move the bar into the matrix
  • f8b61ab2026-07-25feat(docs): make the spec matrix sortable by column
  • 4633cea2026-07-25fix(docs): fetch full git history so per-spec change history is correct
  • a647c5d2026-07-25feat(specs): per-spec change history, release-scoped demo/GIF selection, concurrent verify gate
  • 80629682026-07-25feat(docs): surface spec metrics in the matrix, sidebar and a new insights page
  • 6c0d3ba2026-07-20feat(S07): complexity-weighted documentation-depth metric + doc links on spec pages
  • f08a20e2026-07-17feat(docs): multi-select dropdown filters for the spec matrix
  • 4835f192026-07-17feat(docs): add Specs section — filterable requirement matrix and spec pages

Released under the MIT License.