Spec Studio

Mermaid renders bounded one-hop and Pack diagrams without becoming a graph browser

spec:consumers.mermaid-viewUse Case / Behavior · featureready floor ready

Packs: pack:self-hosting-v1

Design: no design entries · no pinned declarations · 1 decision

Findings: no errors, no warnings

specs/consumers/mermaid-view.sdp.md

No open questions and no blocking questions; implementation binding present, verifier binding present, and expected-outcome oracle none.

Intent
outcome
Give maintainers disposable, deterministic Mermaid diagrams of each Spec's one-hop neighborhood and each Pack's membership without ever projecting the whole graph or inventing a second truth store.
Behavior
rule line 19 · specs/consumers/mermaid-view.sdp.md:19
renderMermaid is a pure Reader -> pages projection with no filesystem or clock access; equal reader data produces byte-identical pages.
rule line 20 · specs/consumers/mermaid-view.sdp.md:20
The page set is one page per Spec, selecting that Spec and every node one edge away, code units and test anchors included, one page per Pack selecting the Pack and its members, and one deterministic index that links them. A selected diagram is rendered or refused under the hard bounds; the projection never emits a whole-graph diagram.
rule line 21 · specs/consumers/mermaid-view.sdp.md:21
Machine node tokens are injective encodings of the full graph ID. Titles and other display text never become machine tokens.
rule line 22 · specs/consumers/mermaid-view.sdp.md:22
Visible labels are escaped for Mermaid's own syntax, apart from the Design Review's Markdown escaping, so no label can close or break a diagram.
rule line 23 · specs/consumers/mermaid-view.sdp.md:23
Every emitted record — pages, node declarations, edge declarations, index rows — is ordered by deterministic code-unit order independent of graph input order.
rule line 24 · specs/consumers/mermaid-view.sdp.md:24
An unresolved relation or edge target renders as an explicit unresolved placeholder node rather than disappearing or being invented.
rule line 25 · specs/consumers/mermaid-view.sdp.md:25
A cycle through the root renders as its ordinary edges; the projection computes no transitive closure and no layout.
rule line 26 · specs/consumers/mermaid-view.sdp.md:26
Disconnected neighborhoods and foreign edge types remain visible when they appear in the selected one-hop or Pack slice; absence of a neighbor is honest silence, not a synthetic hub.
rule line 27 · specs/consumers/mermaid-view.sdp.md:27
Hard bounds are exact: maxNodesPerDiagram = 64 and maxEdgesPerDiagram = 128. A token collision or an overflow of either bound refuses the affected diagram with a deterministic refusal that names the bound, while every in-bound diagram still publishes and the command exits 0. The projection never silently truncates, shards partially, drops edges to fit, or aborts the whole page set because one diagram overflowed.
rule line 28 · specs/consumers/mermaid-view.sdp.md:28
After the shared build, publication owns only generated/mermaid/ and uses the explicit sdp mermaid surface. It is not a child of or an extra write inside Design Review's transaction, and it shares no publication bus or hidden side channel with other projections.
rule line 29 · specs/consumers/mermaid-view.sdp.md:29
A Mermaid run writes its complete page set to generated/mermaid.tmp/, removes the prior Mermaid root, and renames the temporary root into place. The shared build invalidates this root and all sibling projection roots before extraction, as spec:consumers.wholesale-view-rewrite#rule.buildInvalidates states, so failure leaves honest absence rather than stale output that looks current. A failed publish removes any live or temporary Mermaid root it cannot certify when its output parent remains safe.
rule line 30 · specs/consumers/mermaid-view.sdp.md:30
sdp mermaid --check-clean renders an independent twin, refuses divergent renders, and compares the current generated root with the new render. Missing or drifted output returns nonzero and is removed; clean output is replaced wholesale with byte-identical content.
rule line 31 · specs/consumers/mermaid-view.sdp.md:31
When extraction succeeds but graph validation reports errors, Mermaid still publishes the labelled diagnostic projection and returns the nonzero validation exit code.
rule line 32 · specs/consumers/mermaid-view.sdp.md:32
The projection adds no Mermaid-specific Reader accessors, maintains no projection-owned taxonomy list, and confers nothing back into the graph.
rule.portablePublication line 35 · specs/consumers/mermaid-view.sdp.md:35
A case-insensitive page-path collision refuses the publisher's whole page set, names both ids and paths, removes its stale live and temporary roots when their parents are safe, and exits 1 regardless of validation's exit code. Renderers retain their page names and bytes.

no example exercises it

rule.safeCleanup line 36 · specs/consumers/mermaid-view.sdp.md:36
Publication and failure cleanup recheck the physical output parent before each mutation; an escaping, unresolvable or linked generated/ parent is refused without cleanup through it.

no example exercises it

14 unkeyed rules.

Example space

Given a graph containing Specs, Packs, one-hop relations, an unresolved target, a cycle, and a neighborhood within the stated bounds
When the Mermaid projection renders and publishes through the explicit mermaid command
Then each Spec page holds only that Spec's one-hop neighborhood
Then each Pack page holds only that Pack and its members
Then the index links every diagram deterministically
Then machine tokens remain injective full-ID encodings
Then hostile label characters cannot close or break Mermaid syntax
Then an unresolved target renders as an explicit placeholder
Then a colliding token or a diagram past maxNodesPerDiagram = 64 or maxEdgesPerDiagram = 128 is refused by name while every in-bound diagram still publishes
Then generated/mermaid/ is the only current Mermaid root
Then a clean independent render is byte-identical
Then validation errors label the index as a diagnostic projection
Then no whole-graph diagram is emitted
Verification

Verifier bindings

  • test:protocol.cli-write-boundaries · anchored · test anchor binding present · test/cli-write-boundaries.test.ts:292

    External run evidence from test/cli-write-boundaries.test.ts

    Test anchors: test:protocol.cli-write-boundaries. Specs verified by those bindings: spec:consumers.design-review, spec:consumers.gherkin-view, spec:consumers.mermaid-view, spec:consumers.projections-model, spec:consumers.wholesale-view-rewrite, spec:extraction.build-pipeline.

    Run 2: external run evidence from vitest 4.1.10

    Build: CI 439 · Branch: docs/articles-refresh-1 · Commit: 61576184ea9f39b7f91c51fef5ff991b0bf2d0e6 · Start: 2026-11-04T07:59:39.962Z · Stop: 2026-11-04T08:03:39.928Z

    NameSuiteResultDuration
    replaces a stale 'symbolic' link at 'graph.json' without touching its targetbuild write boundariespassed54.47 ms
    replaces a stale 'hard' link at 'graph.json' without touching its targetbuild write boundariespassed16.32 ms
    replaces a stale 'symbolic' link at 'imports.json' without touching its targetbuild write boundariespassed11.53 ms
    replaces a stale 'hard' link at 'imports.json' without touching its targetbuild write boundariespassed10.36 ms
    replaces a stale 'symbolic' link at 'registrars.json' without touching its targetbuild write boundariespassed27.5 ms
    replaces a stale 'hard' link at 'registrars.json' without touching its targetbuild write boundariespassed17.97 ms
    routes a successful build's ordinary removals and writes through the filesystem hooksbuild write boundariespassed23.41 ms
    names an unresolvable output parent when generated is a dangling linkbuild write boundariespassed1.05 ms
    refuses a staging link planted after stale removalbuild write boundariespassed5.06 ms
    build refuses an escaping generated parent before writing or cleanupbuild write boundariespassed7 ms
    view refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.66 ms
    census refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.94 ms
    mermaid refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.96 ms
    gherkin refuses an escaping generated parent before writing or cleanupbuild write boundariespassed6.66 ms
    studio refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.54 ms
    generate refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.54 ms
    vault refuses an escaping generated parent before writing or cleanupbuild write boundariespassed5.19 ms
    stamp refuses an escaping generated parent before writing or cleanupbuild write boundariespassed5.45 ms
    keeps the named root in ordinary command outputexplicit symlinked watch rootpassed5.72 ms
    validate starts and reruns through the named rootexplicit symlinked watch rootpassed29.85 ms
    studio starts and reruns through the named rootexplicit symlinked watch rootpassed62.13 ms
    vault starts and reruns through the named rootexplicit symlinked watch rootpassed29.55 ms
    view refuses case-colliding pages and removes stale outputportable projection publicationpassed9.55 ms
    mermaid refuses case-colliding pages and removes stale outputportable projection publicationpassed8.21 ms
    gherkin refuses case-colliding pages and removes stale outputportable projection publicationpassed8.19 ms
    stamp replaces a symbolic link without modifying its targetpublication boundary regressionspassed4.57 ms
    stamp replaces a hard link without modifying its targetpublication boundary regressionspassed1.67 ms
    a stamp that fails after staging leaves no staging file in the page setpublication boundary regressionspassed1.34 ms
    view recovery preserves outside output after a parent swappublication boundary regressionspassed4.57 ms
    census recovery preserves outside output after a parent swappublication boundary regressionspassed4.7 ms
    mermaid recovery preserves outside output after a parent swappublication boundary regressionspassed4.94 ms
    gherkin recovery preserves outside output after a parent swappublication boundary regressionspassed5.11 ms
    studio recovery preserves outside output after a parent swappublication boundary regressionspassed4.7 ms
    vault recovery preserves outside output after a parent swappublication boundary regressionspassed4.45 ms
    generate recovery preserves outside output after a parent swappublication boundary regressionspassed6.03 ms
    build refuses inside-link generated identitypublication boundary regressionspassed4.61 ms
    build refuses replaced-directory generated identitypublication boundary regressionspassed2.86 ms
    vault refuses a default vault linked outside the root but allows an explicit outside vaultpublication boundary regressionspassed6.69 ms
    generate refuses a default vault linked outside the root but allows an explicit outside vaultpublication boundary regressionspassed19.16 ms
    vault holds a default vault linked inside the root to the placement rules where it resolvespublication boundary regressionspassed12.3 ms
    generate holds a default vault linked inside the root to the placement rules where it resolvespublication boundary regressionspassed29.22 ms
    projection suite refuses a linked generated parent (check-clean: false)publication boundary regressionspassed40.27 ms
    projection suite refuses a linked generated parent (check-clean: true)publication boundary regressionspassed39.73 ms
    package artifacts unlink stale staging links and refuse linked recipe directoriespublication boundary regressionspassed1.68 ms
    package artifacts refuse a linked recipe directory before stale cleanupnot recordedpassed0.78 ms
    watch leaves outside bytes unchanged after direct-empty parent swapnot recordedpassed11.63 ms
    watch leaves outside bytes unchanged after direct-managed parent swapnot recordedpassed15.51 ms
    watch leaves outside bytes unchanged after checkout-empty parent swapnot recordedpassed134.97 ms
    watch leaves outside bytes unchanged after checkout-managed parent swapnot recordedpassed116.97 ms
    watch leaves outside bytes unchanged after studio-direct parent swapnot recordedpassed23.31 ms
  • test:protocol.mermaid-view · verifies bounded deterministic Mermaid projection and publication · anchored · test anchor binding present · test/mermaid-render.test.ts:20

    External run evidence from test/mermaid-render.test.ts

    Test anchors: test:protocol.mermaid-view. Specs verified by those bindings: spec:consumers.mermaid-view.

    Run 2: external run evidence from vitest 4.1.10

    Build: CI 439 · Branch: docs/articles-refresh-1 · Commit: 61576184ea9f39b7f91c51fef5ff991b0bf2d0e6 · Start: 2026-11-04T07:59:39.962Z · Stop: 2026-11-04T08:03:39.928Z

    NameSuiteResultDuration
    renders every checkout-v1 Spec, Pack, and index page as the reviewed goldenthe bounded Mermaid projectionpassed155.97 ms
    is byte-identical under node and edge permutation with code-unit sorted recordsthe bounded Mermaid projectionpassed2.07 ms
    labels the index as diagnostic when validation errors are presentthe bounded Mermaid projectionpassed1.93 ms
    escapes hostile labels without changing injective full-ID machine tokensthe bounded Mermaid projectionpassed1.22 ms
    refuses a diagram whose neighbor has a duplicated id and names that neighborthe bounded Mermaid projectionpassed1.86 ms
    names each duplicated root in its own refusalthe bounded Mermaid projectionpassed0.96 ms
    keeps one-hop cycles, disconnected Pack members, foreign edges, and dangling placeholders without emitting a whole graphthe bounded Mermaid projectionpassed1.6 ms
    withholds only the affected diagram when identities collide or a bound overflowsthe bounded Mermaid projectionpassed5.75 ms
    accepts diagrams exactly at the 64-node and 128-edge boundsthe bounded Mermaid projectionpassed5.84 ms
    withholds an oversized Pack and still renders every in-bound Spec diagramthe bounded Mermaid projectionpassed14.88 ms

Example space

None.

Bound points

No bound example points recorded.

Oracle binding: none

Coverage

Unbound

None.

Design view

Requirement drawing

  • Declared verifying examples: no declared verifying example.
Requirement drawing for spec:consumers.mermaid-view
The diagram as text
  • spec:consumers.mermaid-view: Mermaid renders bounded one-hop and Pack diagrams without becoming a graph browser · declared Spec
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores · declared Spec
  • impl:protocol.mermaid-view: renders bounded Spec one-hop and Pack membership diagrams · anchored code · src/projections/mermaid.ts:14
  • impl:protocol.mermaid-view-cli: publishes Mermaid through the explicit sdp mermaid surface · anchored code · src/cli/mermaid-command.ts:74
  • test:protocol.cli-write-boundaries: test:protocol.cli-write-boundaries · anchored test binding · binding present · test/cli-write-boundaries.test.ts:292
  • test:protocol.mermaid-view: verifies bounded deterministic Mermaid projection and publication · anchored test binding · binding present · test/mermaid-render.test.ts:20
  • spec:consumers.mermaid-view refines spec:consumers.projections-model · declared
  • impl:protocol.mermaid-view satisfies spec:consumers.mermaid-view · anchored
  • impl:protocol.mermaid-view-cli satisfies spec:consumers.mermaid-view · anchored
  • test:protocol.cli-write-boundaries verifies spec:consumers.mermaid-view · anchored
  • test:protocol.mermaid-view verifies spec:consumers.mermaid-view · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred and chosen syntax: requirementDiagram; exact refines, satisfies and verifies, with claim words in nodes and per-edge comments.

Decisions

  • The shipped projections stay frozen

    Re-specifying the shipped Design Review, census, Mermaid, or Gherkin projection is refused. These four projections are the ruled read surfaces over the one graph. A later decision Spec that supersedes this one replaces the ruling whole; a decision Spec that refines this one narrows it for one projection or one rendering and leaves the rest frozen. Either passes the ADR three-part test.

Runtime
  • component:protocol.cli · entry points, shared build and publication · src/cli/build-command.ts:31 · layer edge · context protocol

    Uses

    • component:protocol.adapters · external runtime and source adapters · src/adapters/vitest.ts:23 · layer adapter · context protocol
    • component:protocol.codegen · contracts and registrar output · src/codegen/contracts.ts:671 · layer application · context protocol
    • component:protocol.extract · the carrier and source pipeline · src/extract/index.ts:197 · layer application · context protocol
    • component:protocol.graph · the one graph and its semantic decisions · src/graph/schema.ts:34 · layer domain · context protocol
    • component:protocol.import · canonical conversion with a fidelity boundary · src/import/import.ts:45 · layer application · context protocol
    • component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · context protocol
    • component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol
    • component:protocol.reader · the typed graph contract and body evaluator · src/reader/reader.ts:697 · layer application · context protocol
    • component:protocol.validate · one finding currency and ordered checks · src/validate/validators.ts:76 · layer domain · context protocol
  • component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol

    Uses

    • component:protocol.extract · the carrier and source pipeline · src/extract/index.ts:197 · layer application · context protocol
    • component:protocol.graph · the one graph and its semantic decisions · src/graph/schema.ts:34 · layer domain · context protocol
    • component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · context protocol
    • component:protocol.notation · the leaf step codec · src/notation/slots.ts:263 · layer domain · context protocol
    • component:protocol.reader · the typed graph contract and body evaluator · src/reader/reader.ts:697 · layer application · context protocol
    • component:protocol.validate · one finding currency and ordered checks · src/validate/validators.ts:76 · layer domain · context protocol
  • impl:protocol.mermaid-view · renders bounded Spec one-hop and Pack membership diagrams · src/projections/mermaid.ts:14

    Component: component:protocol.projections · read-only output, layout and owned publication · src/projections/design-review.ts:29 · layer edge · context protocol

    Uses

    None.

  • impl:protocol.mermaid-view-cli · publishes Mermaid through the explicit sdp mermaid surface · src/cli/mermaid-command.ts:74

    Component: component:protocol.cli · entry points, shared build and publication · src/cli/build-command.ts:31 · layer edge · context protocol

    Uses

    None.

Runtime observation: not tracked.

Evidence

Runtime observation: not tracked.

Build attestations: not tracked.

Deployments: not tracked.

Bills of materials: not tracked.

Impact
Relations of spec:consumers.mermaid-view
The diagram as text
  • spec:consumers.mermaid-view: Mermaid renders bounded one-hop and Pack diagrams without becoming a graph browser
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores
  • spec:consumers.wholesale-view-rewrite: Every view run rewrites the view wholesale
  • spec:decisions.shipped-projections-frozen: The shipped projections stay frozen
  • spec:consumers.mermaid-view decidedBy spec:decisions.shipped-projections-frozen · declared
  • spec:consumers.mermaid-view dependsOn spec:consumers.wholesale-view-rewrite · declared — the shared build invalidates the projection roots before this publisher renders
  • spec:consumers.mermaid-view refines spec:consumers.projections-model · declared
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: flowchart. Chosen syntax: labelled flowchart, preserving exact relations, claims and folds.

Rests on

Rested on by

No Spec rests on this Spec.

Relations

Relation and directionOther endClaimReasonExercised keys
decidedBy · outgoingThe shipped projections stay frozen
spec:decisions.shipped-projections-frozen · resolves
declaredNoneNone
dependsOn · outgoingEvery view run rewrites the view wholesale
spec:consumers.wholesale-view-rewrite · resolves
declaredthe shared build invalidates the projection roots before this publisher rendersNone
refines · outgoingProjections fan out from one graph without becoming truth stores
spec:consumers.projections-model · resolves
declaredNoneNone

Bindings

Implementation binding: present

  • impl:protocol.mermaid-view · renders bounded Spec one-hop and Pack membership diagrams · src/projections/mermaid.ts:14 · role projection · component component:protocol.projections · layer edge · context protocol
  • impl:protocol.mermaid-view-cli · publishes Mermaid through the explicit `sdp mermaid` surface · src/cli/mermaid-command.ts:74 · role publisher · component component:protocol.cli · layer edge · context protocol

Design reference: none

Verifier binding: present

  • test:protocol.cli-write-boundaries · anchored · test/cli-write-boundaries.test.ts:292 · test anchor
  • test:protocol.mermaid-view · verifies bounded deterministic Mermaid projection and publication · anchored · test/mermaid-render.test.ts:20 · test anchor

Expected-outcome oracle: none

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.