Spec Studio

Stable IDs are the Protocol's durable join key

spec:model.stable-idsBusiness Rule · storyready floor ready

Packs: pack:self-hosting-v1

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

Findings: no errors, no warnings

specs/model/stable-ids.sdp.md

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

Intent
outcome
Keep intent, bindings, and graph nodes connected through names that survive code refactoring.
Rule
rule line 16 · specs/model/stable-ids.sdp.md:16
A Protocol ID is stable, unique, namespaced, human-readable, and the only binding between intent and code.
rule.idGrammar line 17 · specs/model/stable-ids.sdp.md:17
An ID has a lowercase namespace followed by a colon and a dotted path whose segments admit mixed case. An optional single # suffix is an entry address, <section>.<key>, with section design, ui, question, rule or term and a lower-camel ASCII key; it is admitted only in the spec: namespace and never belongs to an identity. Referential-integrity checks reject malformed or unresolved references.

spec:model.stable-ids.malformed-refusal · enabled, spec:model.stable-ids.namespaced-round-trip · enabled, spec:model.stable-ids.unsectioned-address-refused · enabled

rule line 18 · specs/model/stable-ids.sdp.md:18
IDs carry no history: a rename is a repository edit recorded by git rather than graph-resident bookkeeping.
rule line 19 · specs/model/stable-ids.sdp.md:19
The Spec and Spec-reference builders admit spec:, the Pack builder pack:, the code-anchor builder impl:, api: and component:, the test-anchor builder test:, and the oracle-anchor builder oracle:. These are the namespaces of the flavored authored identities and targets; the grammar itself admits any lowercase namespace.
rule line 20 · specs/model/stable-ids.sdp.md:20
doc: is reserved for a genuinely external document a decision Spec links to; in-system decisions are Specs. Authored relations and the Spec-reference builder accept spec: targets, so the doc: relation-target reservation is a named deferral rather than a landed relation target.

4 unkeyed rules.

Verifying examples

An uppercase namespace is refused with its reason named · spec:model.stable-ids.malformed-refusal · ready floor ready

Executable Spec

Given the authored identifier {identifier: "Spec:orders.create-order"}
When the identifier is parsed
Then parsing {outcome: "is refused"}
Then the refusal names the reason {reason: "namespace must be lowercase"}

Open example

A namespaced dotted path with a sub-part survives parsing unchanged · spec:model.stable-ids.namespaced-round-trip · ready floor ready

Executable Spec

Given the authored identifier {identifier: "spec:orders.create-order#design.validCart"}
When the identifier is parsed
Then parsing {outcome: "resolves"}
Then reformatting the parsed parts restores {restored: "spec:orders.create-order#design.validCart"}

Open example

A # sub-part without its section is refused with its reason named · spec:model.stable-ids.unsectioned-address-refused · ready floor ready

Executable Spec

Given the authored identifier {identifier: "spec:orders.create-order#valid-cart"}
When the identifier is parsed
Then parsing {outcome: "is refused"}
Then the refusal names the reason {reason: "entry address must be <section>.<key> with section design, ui, question, rule, or term"}

Open example

Example space

Given the authored identifier {identifier:string}
When the identifier is parsed
Then parsing {outcome:"resolves"|"is refused"}
Then reformatting the parsed parts restores {restored:string}
Then the refusal names the reason {reason:string}
Verification

Verifier bindings

  • spec:model.stable-ids.malformed-refusal · An uppercase namespace is refused with its reason named · declared · example enabled · specs/model/stable-ids.malformed-refusal.sdp.md

    External run evidence from test/self-hosting-model.test.ts

    Test anchors: test:protocol.anchors.lookalike-refusal, test:protocol.anchors.physical-identity, test:protocol.stable-ids.malformed-refusal, test:protocol.stable-ids.namespaced-round-trip, test:protocol.stable-ids.unsectioned-address-refused. Specs verified by those bindings: spec:model.anchors, spec:model.anchors.lookalike-refusal, spec:model.anchors.physical-identity, spec:model.stable-ids, spec:model.stable-ids.malformed-refusal, spec:model.stable-ids.namespaced-round-trip, spec:model.stable-ids.unsectioned-address-refused.

    Run 1: external run evidence from vitest 4.1.10

    Build: CI 380 · Branch: feature/roadmap-and-slices-v0 · Commit: 34909918bf0be0e849959644401797191cae379f · Start: 2026-10-22T17:40:31.456Z · Stop: 2026-10-22T17:42:16.171Z

    NameSuiteResultDuration
    executes the bound example against a fresh worldA namespaced dotted path with a sub-part survives parsing unchanged (spec:model.stable-ids.namespaced-round-trip)passed3.45 ms
    executes the bound example against a fresh worldAn uppercase namespace is refused with its reason named (spec:model.stable-ids.malformed-refusal)passed0.43 ms
    executes the bound example against a fresh worldA `#` sub-part without its section is refused with its reason named (spec:model.stable-ids.unsectioned-address-refused)passed0.31 ms
    executes the bound example against a fresh worldA consumer-local lookalike builder mints no anchor and no finding (spec:model.anchors.lookalike-refusal)passed20.56 ms
    executes the bound example against a fresh worldA deep relative import that resolves to the Protocol builders is trusted (spec:model.anchors.physical-identity)passed7.87 ms
  • spec:model.stable-ids.namespaced-round-trip · A namespaced dotted path with a sub-part survives parsing unchanged · declared · example enabled · specs/model/stable-ids.namespaced-round-trip.sdp.md
  • spec:model.stable-ids.unsectioned-address-refused · A `#` sub-part without its section is refused with its reason named · declared · example enabled · specs/model/stable-ids.unsectioned-address-refused.sdp.md

Example space

  • identifier · string · declared by the authored identifier {identifier}

Bound points

Exampleidentifier
spec:model.stable-ids.malformed-refusal"Spec:orders.create-order"
spec:model.stable-ids.namespaced-round-trip"spec:orders.create-order#design.validCart"
spec:model.stable-ids.unsectioned-address-refused"spec:orders.create-order#valid-cart"

Oracle binding: none

Coverage

identifier: "Spec:orders.create-order" · spec:model.stable-ids.malformed-refusal; "spec:orders.create-order#design.validCart" · spec:model.stable-ids.namespaced-round-trip; "spec:orders.create-order#valid-cart" · spec:model.stable-ids.unsectioned-address-refused · not enumerable

Unbound

  • identifier: None
Design view

Requirement drawing

Requirement drawing for spec:model.stable-ids
Requirement drawing for spec:model.stable-idssatisfies · anchoredrefines · declaredrefines · declaredrefines, verifies · declaredrefines, verifies · declaredrefines, verifies · declaredverifies · anchoredverifies · anchoredverifies · anchoredIntended Specsspec:decisions.checked-mentionsA Spec id written in proseis a checked mention…decisions.checked-mentionsdeclared Specspec:model.stable-idsStable IDs are theProtocol's durable join key…model.stable-idsdeclared SpecDeclared verifying examplesspec:model.stable-ids.malformed-refusalAn uppercase namespace isrefused with its reasonnamed…model.stable-ids.malformed-refusaldeclared Specspec:model.stable-ids.namespaced-round-tripA namespaced dotted pathwith a sub-part survivesparsing unchanged…model.stable-ids.namespaced-round-tripdeclared Specspec:model.stable-ids.unsectioned-address-refusedA # sub-part without itssection is refused with itsreason named…model.stable-ids.unsectioned-address-refuseddeclared SpecSatisfying or referencingunitsimpl:protocol.stable-idsstable ID grammar parser…protocol.stable-idsanchored codeTest or oracle bindingstest:protocol.stable-ids.malformed-refusalthe malformed pointverifies thelowercase-namespace refusal…protocol.stable-ids.malformed-refusalanchored test binding ·binding presenttest:protocol.stable-ids.namespaced-round-tripthe round-trip pointverifies the namespaceddotted-path grammar…protocol.stable-ids.namespaced-round-tripanchored test binding ·binding presenttest:protocol.stable-ids.unsectioned-address-refusedthe unsectioned pointverifies the entry-addresssection refusal…protocol.stable-ids.unsectioned-address-refusedanchored test binding ·binding presentOutside the selected setspec:model.core-modelThe Protocol modelsdelivery with oneenrichable Spec…model.core-modeldeclared Spec
The diagram as text
  • spec:model.stable-ids: Stable IDs are the Protocol's durable join key · declared Spec
  • spec:decisions.checked-mentions: A Spec id written in prose is a checked mention · declared Spec
  • spec:model.stable-ids.malformed-refusal: An uppercase namespace is refused with its reason named · declared Spec
  • spec:model.stable-ids.namespaced-round-trip: A namespaced dotted path with a sub-part survives parsing unchanged · declared Spec
  • spec:model.stable-ids.unsectioned-address-refused: A # sub-part without its section is refused with its reason named · declared Spec
  • spec:model.core-model: The Protocol models delivery with one enrichable Spec · declared Spec
  • impl:protocol.stable-ids: stable ID grammar parser · anchored code · src/ids.ts:173
  • test:protocol.stable-ids.malformed-refusal: the malformed point verifies the lowercase-namespace refusal · anchored test binding · binding present · test/self-hosting-model.test.ts:112
  • test:protocol.stable-ids.namespaced-round-trip: the round-trip point verifies the namespaced dotted-path grammar · anchored test binding · binding present · test/self-hosting-model.test.ts:86
  • test:protocol.stable-ids.unsectioned-address-refused: the unsectioned point verifies the entry-address section refusal · anchored test binding · binding present · test/self-hosting-model.test.ts:139
  • spec:decisions.checked-mentions refines spec:model.stable-ids · declared
  • spec:model.stable-ids refines spec:model.core-model · declared
  • spec:model.stable-ids.malformed-refusal refines spec:model.stable-ids · declared
  • spec:model.stable-ids.malformed-refusal verifies spec:model.stable-ids · declared
  • spec:model.stable-ids.namespaced-round-trip refines spec:model.stable-ids · declared
  • spec:model.stable-ids.namespaced-round-trip verifies spec:model.stable-ids · declared
  • spec:model.stable-ids.unsectioned-address-refused refines spec:model.stable-ids · declared
  • spec:model.stable-ids.unsectioned-address-refused verifies spec:model.stable-ids · declared
  • impl:protocol.stable-ids satisfies spec:model.stable-ids · anchored
  • test:protocol.stable-ids.malformed-refusal verifies spec:model.stable-ids.malformed-refusal · anchored
  • test:protocol.stable-ids.namespaced-round-trip verifies spec:model.stable-ids.namespaced-round-trip · anchored
  • test:protocol.stable-ids.unsectioned-address-refused verifies spec:model.stable-ids.unsectioned-address-refused · 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

  • A Spec id written in prose is a checked mention

    A Spec id in narrative or section text, outside gwt and gwt-vocabulary fences, is a mention. A mention that does not resolve is a conformance error. A mention of another Spec that shares no declared relation with the mentioning Spec, in either direction, is one warning per mentioning Spec and target Spec pair, whatever the number of locations; an address on the mentioning Spec's own id is checked for its entry and never warned. An entry address is a Spec id followed by #, the section name design, ui, question, rule, or term, a dot, and the entry's lower-camel ASCII key, as in spec:<id>#design.<key> or spec:<id>#question.<key>. A design or ui address resolves when the Spec exists and that section holds the key as its own key, other than description; a question address resolves when the Spec exists and one of its open questions carries that key; a term address resolves when the Spec exists and one of its Model terms carries that key. The # sub-part is reserved for the entry address in every namespace: a Spec's own id, a relation target, a Pack member or model reference, an anchor id or target, and the specId and ref builders all refuse it. A mention mints no edge, and the reader gains no join.

Runtime
  • component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · context protocol

    Uses

    None.

  • impl:protocol.stable-ids · stable ID grammar parser · src/ids.ts:173

    Component: component:protocol.model · the authored contracts and stable ids · src/model/anchors.ts:100 · layer domain · 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:model.stable-ids

Scroll horizontally to read the whole drawing.

The diagram as text
  • spec:decisions.checked-mentions: A Spec id written in prose is a checked mention
  • spec:model.core-model: The Protocol models delivery with one enrichable Spec
  • spec:model.stable-ids: Stable IDs are the Protocol's durable join key
  • spec:model.stable-ids.malformed-refusal: An uppercase namespace is refused with its reason named
  • spec:model.stable-ids.namespaced-round-trip: A namespaced dotted path with a sub-part survives parsing unchanged
  • spec:model.stable-ids.unsectioned-address-refused: A # sub-part without its section is refused with its reason named
  • spec:model.term-addresses: A Model term may carry a key that addresses it
  • spec:model.stable-ids decidedBy spec:decisions.checked-mentions · declared
  • spec:model.stable-ids refines spec:model.core-model · declared
  • spec:model.term-addresses dependsOn spec:model.stable-ids · declared — a term address extends the Spec entry-address grammar
  • spec:decisions.checked-mentions refines spec:model.stable-ids · declared
  • spec:model.stable-ids.malformed-refusal refines spec:model.stable-ids · declared
  • spec:model.stable-ids.namespaced-round-trip refines spec:model.stable-ids · declared
  • spec:model.stable-ids.unsectioned-address-refused refines spec:model.stable-ids · declared
  • spec:model.stable-ids.malformed-refusal verifies spec:model.stable-ids · declared
  • spec:model.stable-ids.namespaced-round-trip verifies spec:model.stable-ids · declared
  • spec:model.stable-ids.unsectioned-address-refused verifies spec:model.stable-ids · 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

Distance 1

Distance 2

Distance 3

Distance 4

Distance 5

Distance 6

Distance 7

Distance 8

Distance 9

Distance 10

Relations

Relation and directionOther endClaimReasonExercised keys
decidedBy · outgoingA Spec id written in prose is a checked mention
spec:decisions.checked-mentions · resolves
declaredNoneNone
refines · outgoingThe Protocol models delivery with one enrichable Spec
spec:model.core-model · resolves
declaredNoneNone
dependsOn · incomingA Model term may carry a key that addresses it
spec:model.term-addresses · resolves
declareda term address extends the Spec entry-address grammarNone
refines · incomingA Spec id written in prose is a checked mention
spec:decisions.checked-mentions · resolves
declaredNoneNone
refines · incomingAn uppercase namespace is refused with its reason named
spec:model.stable-ids.malformed-refusal · resolves
declaredNoneNone
refines · incomingA namespaced dotted path with a sub-part survives parsing unchanged
spec:model.stable-ids.namespaced-round-trip · resolves
declaredNoneNone
refines · incomingA # sub-part without its section is refused with its reason named
spec:model.stable-ids.unsectioned-address-refused · resolves
declaredNoneNone
verifies · incomingAn uppercase namespace is refused with its reason named
spec:model.stable-ids.malformed-refusal · resolves
declaredNoneidGrammar
verifies · incomingA namespaced dotted path with a sub-part survives parsing unchanged
spec:model.stable-ids.namespaced-round-trip · resolves
declaredNoneidGrammar
verifies · incomingA # sub-part without its section is refused with its reason named
spec:model.stable-ids.unsectioned-address-refused · resolves
declaredNoneidGrammar

Bindings

Implementation binding: present

  • impl:protocol.stable-ids · stable ID grammar parser · src/ids.ts:173 · role codec · component component:protocol.model · layer domain · context protocol

Design reference: none

Verifier binding: present

Expected-outcome oracle: none

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.