Spec Studio

Gherkin view renders any Spec as a disposable read shape

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

Changed since the base graph base b5e0a8b: behavior.rules[4]; behavior.rules[6]; rule.portablePublication; rule.safeCleanup

Base input: schema 0.12.0, 456 Specs.

Packs: pack:self-hosting-v1

Design: no design entries · no pinned declarations · 3 decisions

Findings: no errors, no warnings

specs/consumers/gherkin-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 a generated Gherkin-shaped page for reading any Spec, without making it a carrier, changing the default carrier or claiming conversion back to the authored Spec.
Behavior
rule line 23 · specs/consumers/gherkin-view.sdp.md:23
renderGherkinView is a pure Reader -> pages projection with no filesystem or clock access; equal reader data produces byte-identical pages.
rule line 24 · specs/consumers/gherkin-view.sdp.md:24
Every Spec renders as one Gherkin-shaped page plus one deterministic index. Pages use spec/<id body>.feature.md, with the spec: namespace removed from the id. Packs are not projected. The projection never uses .sdp.gherkin and never claims round-trip parity.
rule line 25 · specs/consumers/gherkin-view.sdp.md:25
Each page says it is generated and disposable. A page for a kind the Gherkin carrier does not carry has a # LOSSY: comment in its header giving that kind's reason, and content Gherkin cannot carry honestly is marked the same way rather than invented as structure.
rule line 26 · specs/consumers/gherkin-view.sdp.md:26
Description prose lands only on the narrative and keyed description bullets under the prose-ownership decision, spec:decisions.prose-ownership; the projection never emits DocStrings, DataTables, Scenario Outlines, Examples tables, backgrounds, star steps or leading conjunctions.
rule line 27 · specs/consumers/gherkin-view.sdp.md:27
Every emitted record is ordered by deterministic code-unit order independent of graph input order. Hostile characters are escaped so they cannot close a fence or invent a DocString, and every line terminator, CR, LF, U+2028 or U+2029, folds to one space, so a page never shows a line the graph does not state.
rule line 28 · specs/consumers/gherkin-view.sdp.md:28
After the shared build, publication owns only generated/gherkin/ and uses the explicit sdp gherkin 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/gherkin-view.sdp.md:29
A Gherkin-view run writes its complete page set to generated/gherkin.tmp/, removes the prior Gherkin-view 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 Gherkin-view root it cannot certify when its output parent remains safe.
rule line 30 · specs/consumers/gherkin-view.sdp.md:30
sdp gherkin --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/gherkin-view.sdp.md:31
When extraction succeeds but graph validation reports errors, the Gherkin view still publishes the labelled diagnostic projection and returns the nonzero validation exit code.
rule line 32 · specs/consumers/gherkin-view.sdp.md:32
The projection adds no Gherkin-specific Reader accessors and confers nothing back into the graph.
rule.portablePublication line 35 · specs/consumers/gherkin-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/gherkin-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

10 unkeyed rules.

Example space

Given a graph containing behavior, example, and refused-kind Specs with hostile titles
When the Gherkin-view projection renders and publishes through the explicit gherkin command
Then each Spec page is a visibly generated Gherkin-shaped read
Then refused-kind pages carry the per-kind lie-reason as lossy commentary
Then a clean independent render is byte-identical
Then hostile characters cannot close a fence or invent a DocString
Then generated/gherkin/ is the only current Gherkin-view root
Then validation errors label the index as a diagnostic projection
Then no page uses the .sdp.gherkin suffix
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 1: external run evidence from vitest 4.1.10

    Build: CI 433 · Branch: fix/second-review-pass · Commit: 6375b6e431a7cf885a49befec76d7876cd3e538c · Start: 2026-11-04T07:12:23.076Z · Stop: 2026-11-04T07:15:49.695Z

    NameSuiteResultDuration
    replaces a stale 'symbolic' link at 'graph.json' without touching its targetbuild write boundariespassed67.45 ms
    replaces a stale 'hard' link at 'graph.json' without touching its targetbuild write boundariespassed13.17 ms
    replaces a stale 'symbolic' link at 'imports.json' without touching its targetbuild write boundariespassed18.95 ms
    replaces a stale 'hard' link at 'imports.json' without touching its targetbuild write boundariespassed10.38 ms
    replaces a stale 'symbolic' link at 'registrars.json' without touching its targetbuild write boundariespassed19.24 ms
    replaces a stale 'hard' link at 'registrars.json' without touching its targetbuild write boundariespassed12.84 ms
    routes a successful build's ordinary removals and writes through the filesystem hooksbuild write boundariespassed19.14 ms
    names an unresolvable output parent when generated is a dangling linkbuild write boundariespassed1.24 ms
    refuses a staging link planted after stale removalbuild write boundariespassed4.94 ms
    build refuses an escaping generated parent before writing or cleanupbuild write boundariespassed5.03 ms
    view refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.29 ms
    census refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.86 ms
    mermaid refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.19 ms
    gherkin refuses an escaping generated parent before writing or cleanupbuild write boundariespassed6.19 ms
    studio refuses an escaping generated parent before writing or cleanupbuild write boundariespassed4.04 ms
    generate refuses an escaping generated parent before writing or cleanupbuild write boundariespassed3.87 ms
    vault refuses an escaping generated parent before writing or cleanupbuild write boundariespassed5.04 ms
    stamp refuses an escaping generated parent before writing or cleanupbuild write boundariespassed11.13 ms
    keeps the named root in ordinary command outputexplicit symlinked watch rootpassed5.6 ms
    validate starts and reruns through the named rootexplicit symlinked watch rootpassed17.86 ms
    studio starts and reruns through the named rootexplicit symlinked watch rootpassed88.19 ms
    vault starts and reruns through the named rootexplicit symlinked watch rootpassed35.65 ms
    view refuses case-colliding pages and removes stale outputportable projection publicationpassed10.23 ms
    mermaid refuses case-colliding pages and removes stale outputportable projection publicationpassed7.15 ms
    gherkin refuses case-colliding pages and removes stale outputportable projection publicationpassed10.32 ms
    stamp replaces a symbolic link without modifying its targetpublication boundary regressionspassed4.1 ms
    stamp replaces a hard link without modifying its targetpublication boundary regressionspassed3.74 ms
    a stamp that fails after staging leaves no staging file in the page setpublication boundary regressionspassed1.83 ms
    view recovery preserves outside output after a parent swappublication boundary regressionspassed6.23 ms
    census recovery preserves outside output after a parent swappublication boundary regressionspassed6.21 ms
    mermaid recovery preserves outside output after a parent swappublication boundary regressionspassed5.59 ms
    gherkin recovery preserves outside output after a parent swappublication boundary regressionspassed5.64 ms
    studio recovery preserves outside output after a parent swappublication boundary regressionspassed5.11 ms
    vault recovery preserves outside output after a parent swappublication boundary regressionspassed5 ms
    generate recovery preserves outside output after a parent swappublication boundary regressionspassed6.75 ms
    build refuses inside-link generated identitypublication boundary regressionspassed2.93 ms
    build refuses replaced-directory generated identitypublication boundary regressionspassed2.58 ms
    vault refuses a default vault linked outside the root but allows an explicit outside vaultpublication boundary regressionspassed9.03 ms
    generate refuses a default vault linked outside the root but allows an explicit outside vaultpublication boundary regressionspassed23.26 ms
    vault holds a default vault linked inside the root to the placement rules where it resolvespublication boundary regressionspassed11.29 ms
    generate holds a default vault linked inside the root to the placement rules where it resolvespublication boundary regressionspassed28.31 ms
    projection suite refuses a linked generated parent (check-clean: false)publication boundary regressionspassed65.78 ms
    projection suite refuses a linked generated parent (check-clean: true)publication boundary regressionspassed47.87 ms
    package artifacts unlink stale staging links and refuse linked recipe directoriespublication boundary regressionspassed1.5 ms
    package artifacts refuse a linked recipe directory before stale cleanupnot recordedpassed0.78 ms
    watch leaves outside bytes unchanged after direct-empty parent swapnot recordedpassed12.49 ms
    watch leaves outside bytes unchanged after direct-managed parent swapnot recordedpassed15.69 ms
    watch leaves outside bytes unchanged after checkout-empty parent swapnot recordedpassed77.25 ms
    watch leaves outside bytes unchanged after checkout-managed parent swapnot recordedpassed117.05 ms
    watch leaves outside bytes unchanged after studio-direct parent swapnot recordedpassed12.8 ms
  • test:protocol.gherkin-view · verifies the generated Gherkin-shaped READ projection · anchored · test anchor binding present · test/gherkin-view.test.ts:20

    External run evidence from test/gherkin-view.test.ts

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

    Run 1: external run evidence from vitest 4.1.10

    Build: CI 433 · Branch: fix/second-review-pass · Commit: 6375b6e431a7cf885a49befec76d7876cd3e538c · Start: 2026-11-04T07:12:23.076Z · Stop: 2026-11-04T07:15:49.695Z

    NameSuiteResultDuration
    double-renders byte-identically and sorts independently of input orderthe generated Gherkin-shaped READ projectionpassed3.92 ms
    labels the index as diagnostic when validation errors are presentthe generated Gherkin-shaped READ projectionpassed0.51 ms
    marks refused kinds with the ruled lie-reason and never uses .sdp.gherkinthe generated Gherkin-shaped READ projectionpassed0.51 ms
    marks escaped relation reasons as lossy commentary in relation order without changing tagsthe generated Gherkin-shaped READ projectionpassed6.13 ms
    marks every populated section that a canonical Gherkin shape cannot representthe generated Gherkin-shaped READ projectionpassed0.71 ms
    escapes hostile characters so they cannot close a fence or invent a DocStringthe generated Gherkin-shaped READ projectionpassed0.42 ms
    folds CR to one space so no page line is inventedthe generated Gherkin-shaped READ projectionpassed0.56 ms
    folds CRLF to one space so no page line is inventedthe generated Gherkin-shaped READ projectionpassed0.51 ms
    folds U+2028 to one space so no page line is inventedthe generated Gherkin-shaped READ projectionpassed0.54 ms
    folds U+2029 to one space so no page line is inventedthe generated Gherkin-shaped READ projectionpassed0.42 ms
    renders the checkout-v1 corpus as a disposable generated viewthe generated Gherkin-shaped READ projectionpassed196.76 ms
    publishes diagnostic output for validation errors and returns nonzerosdp gherkin publicationpassed32.78 ms
    surfaces the explicit command in help and names Gherkin on bad inputsdp gherkin publicationpassed0.49 ms
    publishes only generated/gherkin wholesale and regenerates byte-identically without tmp residuesdp gherkin publicationpassed34.96 ms
    passes --check-clean only when twin renders and the published bytes agreesdp gherkin publicationpassed46.02 ms
    refuses perturbed published bytes and removes both live and temporary rootssdp gherkin publicationpassed41.71 ms
    refuses divergent --check-clean twins and removes both publication rootssdp gherkin publicationpassed64.21 ms
    removes planted stale live and tmp output when the authored corpus failssdp gherkin publicationpassed19.27 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.gherkin-view
The diagram as text
  • spec:consumers.gherkin-view: Gherkin view renders any Spec as a disposable read shape · declared Spec
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores · declared Spec
  • impl:protocol.gherkin-kind-policy: shared Gherkin kind-refusal and loss explanations · anchored code · src/extract/gherkin-kind-honesty.ts:6
  • impl:protocol.gherkin-view: renders a generated Gherkin-shaped READ projection of any Spec · anchored code · src/projections/gherkin-view.ts:19
  • impl:protocol.gherkin-view-cli: publishes the Gherkin view through the explicit sdp gherkin surface · anchored code · src/cli/gherkin-command.ts:58
  • test:protocol.cli-write-boundaries: test:protocol.cli-write-boundaries · anchored test binding · binding present · test/cli-write-boundaries.test.ts:292
  • test:protocol.gherkin-view: verifies the generated Gherkin-shaped READ projection · anchored test binding · binding present · test/gherkin-view.test.ts:20
  • spec:consumers.gherkin-view refines spec:consumers.projections-model · declared
  • impl:protocol.gherkin-kind-policy satisfies spec:consumers.gherkin-view · anchored
  • impl:protocol.gherkin-view satisfies spec:consumers.gherkin-view · anchored
  • impl:protocol.gherkin-view-cli satisfies spec:consumers.gherkin-view · anchored
  • test:protocol.cli-write-boundaries verifies spec:consumers.gherkin-view · anchored
  • test:protocol.gherkin-view verifies spec:consumers.gherkin-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

  • Carrier universality is bounded by honest kind structure

    In order, first, Gherkin remains an honest canonical per-ID option only for behavior (Feature) and example (ordinary Scenario); the other six kinds stay Markdown because a Gherkin mapping would lie against the kind-evidence table — workflow has no distinct Gherkin root and Feature mapping erases the kind; rule collides with Rule: already consumed as inline behavior.rules; constraint needs machine-readable targets a Scenario cannot own; model needs keyed terms; decision needs context/decision/rationale/consequences (and supersedes) that Feature structure cannot distinguish; contract shares the behavior family row but Feature cannot structurally mark the kind. Second, rich content is ruled by physical form: Feature and Scenario description prose (keyed description bullets and remaining non-heading prose) is lawful only as free prose on the prose owners the prose-ownership law (MD-19) names — narrative or description on the typed owner — never a new field and never a parser-within-a-parser beyond enumerated constructs; Gherkin DocStrings and DataTables remain refused. Third, "universal" means per-ID carriers plus a generated Gherkin-shaped READ projection of any Spec (visibly generated, disposable, lossy commentary allowed for refused kinds), not one default authored carrier for everything and not a lossless codec. Fourth, the corpus default does not flip: Markdown remains the default Spec carrier; a default flip is refused before full honest round-trip exists. Fifth, Packs are out of this ruling; the Pack syntax ruling (MD-25) remains the Pack carrier law.

  • Prose belongs to typed graph owners

    Free prose is stored as a narrative or a description on its typed owner; unowned prose is refused.

  • 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.extract · the carrier and source pipeline · src/extract/index.ts:197 · layer application · context protocol

    Uses

    • 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.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.gherkin-kind-policy · shared Gherkin kind-refusal and loss explanations · src/extract/gherkin-kind-honesty.ts:6

    Component: component:protocol.extract · the carrier and source pipeline · src/extract/index.ts:197 · layer application · context protocol

    Uses

    None.

  • impl:protocol.gherkin-view · renders a generated Gherkin-shaped READ projection of any Spec · src/projections/gherkin-view.ts:19

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

    Uses

    • impl:protocol.gherkin-kind-policy · shared Gherkin kind-refusal and loss explanations · src/extract/gherkin-kind-honesty.ts:6
  • impl:protocol.gherkin-view-cli · publishes the Gherkin view through the explicit sdp gherkin surface · src/cli/gherkin-command.ts:58

    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.gherkin-view
The diagram as text
  • spec:consumers.gherkin-view: Gherkin view renders any Spec as a disposable read shape
  • 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.carrier-universality: Carrier universality is bounded by honest kind structure
  • spec:decisions.prose-ownership: Prose belongs to typed graph owners
  • spec:decisions.shipped-projections-frozen: The shipped projections stay frozen
  • spec:consumers.gherkin-view decidedBy spec:decisions.carrier-universality · declared
  • spec:consumers.gherkin-view decidedBy spec:decisions.prose-ownership · declared — description prose stays on its typed graph owner
  • spec:consumers.gherkin-view decidedBy spec:decisions.shipped-projections-frozen · declared
  • spec:consumers.gherkin-view dependsOn spec:consumers.wholesale-view-rewrite · declared — the shared build invalidates the projection roots before this publisher renders
  • spec:consumers.gherkin-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 · outgoingCarrier universality is bounded by honest kind structure
spec:decisions.carrier-universality · resolves
declaredNoneNone
decidedBy · outgoingProse belongs to typed graph owners
spec:decisions.prose-ownership · resolves
declareddescription prose stays on its typed graph ownerNone
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.gherkin-kind-policy · shared Gherkin kind-refusal and loss explanations · src/extract/gherkin-kind-honesty.ts:6 · role decider · component component:protocol.extract · layer application · context protocol
  • impl:protocol.gherkin-view · renders a generated Gherkin-shaped READ projection of any Spec · src/projections/gherkin-view.ts:19 · role projection · component component:protocol.projections · layer edge · context protocol
  • impl:protocol.gherkin-view-cli · publishes the Gherkin view through the explicit `sdp gherkin` surface · src/cli/gherkin-command.ts:58 · 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.gherkin-view · verifies the generated Gherkin-shaped READ projection · anchored · test/gherkin-view.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.