Spec Studio

The rule-key proposal keys every unkeyed rule as a change proposal

spec:consumers.upgrade-kit.rule-key-proposalUse Case / Behavior · storyready floor ready

Packs: pack:adopter-speed-v1, pack:self-hosting-v1

Design: 9 design entries · 6 pinned declarations · no decisions

Findings: no errors, no warnings

specs/consumers/upgrade-kit.rule-key-proposal.sdp.md

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

A key is the one capability a machine can add without an author's judgment: it addresses a rule and changes nothing the rule says. The proposal coins each key from the words of its rule, so the same corpus gives the same keys, and a person reads and may rename a key before an agent applies the change. A key coined from words is plainer than one an author would choose, which is why it travels as a proposal a person reviews rather than as an edit.

Intent
problem
Keying every rule of a large corpus by hand is slow and uneven, and an agent asked to do it in one pass edits carriers with no reviewable record of what it intends.
outcome
Write one change proposal that adds a key to each unkeyed rule and flow entry in a scope, so an agent applies it after the proposal check and the corpus validates with no error.
Behavior
rule.proposesKeys line 25 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:25
The rule-key proposal, recipe 48, writes one change proposal that adds a key to each unkeyed rule and flow entry in its scope, in the shape the change-proposal contract states, and edits nothing.

spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · enabled

rule.keepsText line 26 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:26
Each change adds only the key marker to its entry and keeps the entry's text, so keying a rule changes no rule's meaning and no frozen projection's text.

spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · enabled

rule.coinedKey line 27 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:27
Each key is coined from its entry's text by fixed steps, so the same graph and parameters give the same keys.

spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · enabled, spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · enabled

rule.uniqueKey line 28 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:28
A coined key is lower camel on the rule-key grammar, unique among the rule and flow keys of its Spec, the keys it already carries and the keys coined before it, and never a name the carrier refuses as a key.

spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · enabled

rule.unkeyable line 29 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:29
An entry whose text yields no word to coin from is listed apart, with its Spec, entry and line, and gets no change; the recipe never invents a key from nothing.

spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · enabled

rule.applyAfterCheck line 30 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:30
An agent reads the proposal check per change: a change whose scope moved since the named commit goes to a person, and the remaining changes are applied as ordinary carrier edits, so one moved scope never holds back the whole proposal. A person may rename a coined key before it is applied. The proposal check grants no approval and edits nothing; conformance and honesty checks remain the only gate.

spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · enabled

0 unkeyed rules.

Verifying examples

Two rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed · spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · ready floor ready

Executable Spec

Given a probe Spec whose unkeyed rules read {rules: "A part fits the budget. / Every part fits the budget again. / Ο κανόνας ισχύει."}
When the rule-key proposal runs for it at a commit and the proposal check reads it against the graph of that commit
Then the proposal adds the keys {keys: "partFitsBudget, partFitsBudget2"}
Then the proposal leaves {unkeyable: 1} entries unkeyed
Then the proposal check reports {moved: 0} moved changes and {problems: 0} schema problems

Open example

A rule-key proposal passes the proposal check and validates once applied · spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · ready floor ready

Executable Spec

Given a probe Spec whose unkeyed rules read {rules: "The bundle is composed on each invocation. / Each part fits the budget."}
When the rule-key proposal runs for it at a commit and the proposal check reads it against the graph of that commit
Then the proposal adds the keys {keys: "bundleComposedInvocation, partFitsBudget"}
Then the proposal leaves {unkeyable: 0} entries unkeyed
Then the proposal check reports {moved: 0} moved changes and {problems: 0} schema problems
Then the probe validates with {errors: 0} errors once an agent applies the proposal

Open example

Example space

Given a probe Spec whose unkeyed rules read {rules:string}
When the rule-key proposal runs for it at a commit and the proposal check reads it against the graph of that commit
Then the proposal adds the keys {keys:string}
Then the proposal leaves {unkeyable:number} entries unkeyed
Then the proposal check reports {moved:number} moved changes and {problems:number} schema problems
Then the probe validates with {errors:number} errors once an agent applies the proposal
Design
proposalRecipe line 33 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:33
recipe 48, the rule-key proposal, takes params.commit, the commit whose graph it reads, required, and at most one scope, params.pack or params.specs, the whole corpus with neither; a missing commit, both scopes, or an id that does not resolve returns { diagnostics: [{ parameter, problem }] } and no proposal.
proposalAnswer line 34 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:34
{ scope, totals: { specs, entries, keyed, proposed, unkeyable }, proposal, unkeyable }, where scope is { corpus: true }, { pack } or { specs }, entries counts the rule and flow entries in scope, keyed those that already carry a key, proposal is the change-proposal document, or null when no entry needs a key, and unkeyable lists the entries no key could be coined for.

no citation recorded

proposalDocument line 35 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:35
{ "schema": "sdp-proposal/1", "base": "<commit>", "reason": "Key every rule and flow entry in <scope> so a review, a register row or an example can cite it.", "changes": [change] }, with <scope> the Pack id, the Spec ids joined by a comma and a space, or the corpus.

no citation recorded

keyChange line 36 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:36
{ "scope": ["<spec id>"], "request": "<request>", "text": "[#<key>] <entry text>", "reason": "Gives the entry the address <spec id>#rule.<key>." }, one per unkeyed entry, by Spec in scope order, then its rules before its flows, each in authored order; text is the entry's text as its carrier writes it once keyed, after its rule: or flow: label where it has one.

no citation recorded

keyRequest line 37 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:37
Add the key marker [#<key>] and one space at the start of the text of <entry> of <spec id>, at <file> line <line>, after its rule: or flow: label where it has one., with <entry> the location table's rule[<i>] or flow[<i>] and , at <file> alone when the line is not located.

no citation recorded

keyCoining line 38 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:38
decompose the text and drop combining marks; drop the bracketed groups that open it, such as [extension]; drop an 's or ’s not followed by an ASCII letter, digit or underscore, then any remaining apostrophe; split the rest into words at every character other than an ASCII letter or digit and at each change from a lowercase letter or digit to an uppercase letter; lowercase each word; drop the stop words, unless that leaves no word; drop the leading words that do not open with a letter and every repeat of a word kept before it; join the first three words left in lower camel case, the first all lowercase and each later one with its first letter uppercased.
stopWords line 39 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:39
a, an, the, and, or, but, nor, so, then, than, if, when, while, where, of, to, in, on, at, by, for, with, from, into, onto, as, per, via, over, under, about, is, are, was, were, be, been, being, do, does, did, has, have, had, can, could, may, might, must, shall, should, will, would, it, its, this, that, these, those, there, their, they, them, which, who, whom, whose, what, each, every, any, also, only and own.

no citation recorded

keyCollision line 40 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:40
a coined key already taken in its Spec, or equal to a key the carrier refuses, takes the smallest suffix from 2 up that makes it unique.
unkeyableRow line 41 · specs/consumers/upgrade-kit.rule-key-proposal.sdp.md:41
{ spec, entry, file, line, reason: "no word to coin a key from" }, with entry the location table's rule[<i>] or flow[<i>] and line null when not located.

no citation recorded

Verification

Verifier bindings

  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · Two rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed · declared · example enabled · specs/consumers/upgrade-kit.rule-key-proposal.collision-and-unkeyable.sdp.md

    External run evidence from test/upgrade-kit.test.ts

    Test anchors: test:protocol.capability-adoption.counts-a-corpus, test:protocol.rule-key-proposal.collision-and-unkeyable, test:protocol.rule-key-proposal.proposal-applies-clean, test:protocol.upgrade-guide. Specs verified by those bindings: spec:consumers.upgrade-kit, spec:consumers.upgrade-kit.capability-adoption, spec:consumers.upgrade-kit.capability-adoption.counts-a-corpus, spec:consumers.upgrade-kit.rule-key-proposal, spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable, spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean.

    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
    applies unchanged scopes while sending only moved changes to a personupgrade kitpassed1.93 ms
    names a carrying Spec in every guide sectionupgrade kitpassed0.58 ms
    teaches the upgrade steps in the pinned orderupgrade kitpassed1.14 ms
    teaches epic reparenting and advisory roadmap adoptionupgrade kitpassed0.48 ms
    runs the guide's rules file on a clean corpus with no address parametersupgrade kitpassed26.18 ms
    names the recipes' carrying Specs and teaches rule-key proposal as the rule-key actionupgrade kitpassed1.8 ms
    ships the guide and names every reported capability's law and measurementupgrade kitpassed17.05 ms
    counts rules, reasons and unknown external perspectivesupgrade kitpassed21.17 ms
    coins collisions and lists an unkeyable entryupgrade kitpassed11.27 ms
    executes the bound example against a fresh worldCapability adoption counts keyed rules and reasoned relations (spec:consumers.upgrade-kit.capability-adoption.counts-a-corpus)passed9.23 ms
    executes the bound example against a fresh worldA rule-key proposal passes the proposal check and validates once applied (spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean)passed12.56 ms
    executes the bound example against a fresh worldTwo rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed (spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable)passed13.93 ms
    counts distinct step skeletons and vocabulary keys inherited through refinescapability adoption detailspassed15.36 ms
    counts distinct step skeletons and vocabulary keys inherited through verifiescapability adoption detailspassed17.05 ms
    counts repeated vocabulary and example steps once per skeleton within a Speccapability adoption detailspassed3.68 ms
    counts all capabilities and deduplicates shared components and repeated Pack memberscapability adoption detailspassed20.68 ms
    diagnoses detail nullcapability adoption detailspassed2.27 ms
    diagnoses detail 1capability adoption detailspassed2.16 ms
    diagnoses detail "all"capability adoption detailspassed2.08 ms
    diagnoses detail {}capability adoption detailspassed2.06 ms
    diagnoses detail "spec:missing"capability adoption detailspassed1.97 ms
    diagnoses detail truecapability adoption detailspassed1.94 ms
    drops possessives and apostrophes before coining keysrule-key proposal detailspassed2.38 ms
    preserves scope and entry order and skips existing keys and tagsrule-key proposal detailspassed12.62 ms
    diagnoses both scopes even when each resolvesrule-key proposal detailspassed2.4 ms
    diagnoses invalid parameters {}rule-key proposal detailspassed2.06 ms
    diagnoses invalid parameters {"commit":""}rule-key proposal detailspassed2.07 ms
    diagnoses invalid parameters {"commit":1}rule-key proposal detailspassed1.99 ms
    diagnoses invalid parameters {"commit":"abc","pack":"pack:missing"}rule-key proposal detailspassed1.73 ms
    diagnoses invalid parameters {"commit":"abc","specs":["spec:missing"]}rule-key proposal detailspassed2.12 ms
    diagnoses invalid parameters {"commit":"abc","specs":[],"pack":"pack:missing"}rule-key proposal detailspassed1.32 ms
    diagnoses invalid parameters {"commit":"abc","specs":"all"}rule-key proposal detailspassed1.26 ms
    drops leading digit words before checking word repeatsnot recordedpassed1.48 ms
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · A rule-key proposal passes the proposal check and validates once applied · declared · example enabled · specs/consumers/upgrade-kit.rule-key-proposal.proposal-applies-clean.sdp.md

Example space

  • rules · string · declared by a probe Spec whose unkeyed rules read {rules}

Bound points

Examplerules
spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable"A part fits the budget. / Every part fits the budget again. / Ο κανόνας ισχύει."
spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean"The bundle is composed on each invocation. / Each part fits the budget."

Oracle binding: present · oracle:protocol.rule-key-proposal · test/upgrade-kit.test.ts:398

Coverage

rules: "A part fits the budget. / Every part fits the budget again. / Ο κανόνας ισχύει." · spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable; "The bundle is composed on each invocation. / Each part fits the budget." · spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · not enumerable

Unbound

  • rules: None
Design view

Requirement drawing

Requirement drawing for spec:consumers.upgrade-kit.rule-key-proposal
Requirement drawing for spec:consumers.upgrade-kit.rule-key-proposalsatisfies · anchoredmodels · anchoredrefines · declaredrefines, verifies · declaredrefines, verifies · declaredverifies · anchoredverifies · anchoredIntended Specsspec:consumers.upgrade-kit.rule-key-proposalThe rule-key proposal keysevery unkeyed rule as achange proposal…consumers.upgrade-kit.rule-key-proposaldeclared SpecDeclared verifying examplesspec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyableTwo rules that coin one keystay apart, and a rule withno ASCII word stays unkeyed…consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyabledeclared Specspec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-cleanA rule-key proposal passesthe proposal check andvalidates once applied…consumers.upgrade-kit.rule-key-proposal.proposal-applies-cleandeclared SpecSatisfying or referencingunitsimpl:protocol.rule-key-proposal-reciperealizes the rule-keyproposal in the shippedrecipe catalog…protocol.rule-key-proposal-recipeanchored codeTest or oracle bindingsoracle:protocol.rule-key-proposaloracle:protocol.rule-key-proposal…protocol.rule-key-proposalanchored oracle bindingtest:protocol.rule-key-proposal.collision-and-unkeyabletest:protocol.rule-key-proposal.collision-and-unkeyable…protocol.rule-key-proposal.collision-and-unkeyableanchored test binding ·binding presenttest:protocol.rule-key-proposal.proposal-applies-cleantest:protocol.rule-key-proposal.proposal-applies-clean…protocol.rule-key-proposal.proposal-applies-cleananchored test binding ·binding presentOutside the selected setspec:consumers.upgrade-kitThe upgrade kit shows acorpus what it could adopt…consumers.upgrade-kitdeclared Spec
The diagram as text
  • spec:consumers.upgrade-kit.rule-key-proposal: The rule-key proposal keys every unkeyed rule as a change proposal · declared Spec
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable: Two rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed · declared Spec
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean: A rule-key proposal passes the proposal check and validates once applied · declared Spec
  • spec:consumers.upgrade-kit: The upgrade kit shows a corpus what it could adopt · declared Spec
  • impl:protocol.rule-key-proposal-recipe: realizes the rule-key proposal in the shipped recipe catalog · anchored code · test/upgrade-kit.test.ts:742
  • oracle:protocol.rule-key-proposal: oracle:protocol.rule-key-proposal · anchored oracle binding · test/upgrade-kit.test.ts:398
  • test:protocol.rule-key-proposal.collision-and-unkeyable: test:protocol.rule-key-proposal.collision-and-unkeyable · anchored test binding · binding present · test/upgrade-kit.test.ts:483
  • test:protocol.rule-key-proposal.proposal-applies-clean: test:protocol.rule-key-proposal.proposal-applies-clean · anchored test binding · binding present · test/upgrade-kit.test.ts:466
  • spec:consumers.upgrade-kit.rule-key-proposal refines spec:consumers.upgrade-kit · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable refines spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable verifies spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean refines spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean verifies spec:consumers.upgrade-kit.rule-key-proposal · declared
  • impl:protocol.rule-key-proposal-recipe satisfies spec:consumers.upgrade-kit.rule-key-proposal · anchored
  • oracle:protocol.rule-key-proposal models spec:consumers.upgrade-kit.rule-key-proposal · anchored
  • test:protocol.rule-key-proposal.collision-and-unkeyable verifies spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · anchored
  • test:protocol.rule-key-proposal.proposal-applies-clean verifies spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · anchored
Mermaid source

Copyable source; the Studio draws static SVG.

Preferred syntax: requirementDiagram. Chosen syntax: flowchart TB because models require exact labels unavailable in requirement syntax.

Decisions

No decision record named.

Runtime
  • impl:protocol.rule-key-proposal-recipe · realizes the rule-key proposal in the shipped recipe catalog · test/upgrade-kit.test.ts:742

    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.upgrade-kit.rule-key-proposal
The diagram as text
  • spec:consumers.intent-composition.proposal: A change proposal is addressed, based and checked before it lands
  • spec:consumers.upgrade-kit: The upgrade kit shows a corpus what it could adopt
  • spec:consumers.upgrade-kit.rule-key-proposal: The rule-key proposal keys every unkeyed rule as a change proposal
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable: Two rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean: A rule-key proposal passes the proposal check and validates once applied
  • spec:extraction.entry-locations: The graph records where each addressable entry is written
  • spec:model.rule-keys: A rule or flow entry may carry a key that addresses it
  • spec:consumers.upgrade-kit.rule-key-proposal dependsOn spec:consumers.intent-composition.proposal · declared — the recipe writes a change proposal in the shape the proposal check reads
  • spec:consumers.upgrade-kit.rule-key-proposal dependsOn spec:extraction.entry-locations · declared — each change names the file and line the location table records for its entry
  • spec:consumers.upgrade-kit.rule-key-proposal dependsOn spec:model.rule-keys · declared — a coined key follows the rule-key grammar and marker
  • spec:consumers.upgrade-kit.rule-key-proposal refines spec:consumers.upgrade-kit · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable refines spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean refines spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable verifies spec:consumers.upgrade-kit.rule-key-proposal · declared
  • spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean verifies spec:consumers.upgrade-kit.rule-key-proposal · 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

Relations

Relation and directionOther endClaimReasonExercised keys
dependsOn · outgoingA change proposal is addressed, based and checked before it lands
spec:consumers.intent-composition.proposal · resolves
declaredthe recipe writes a change proposal in the shape the proposal check readsNone
dependsOn · outgoingThe graph records where each addressable entry is written
spec:extraction.entry-locations · resolves
declaredeach change names the file and line the location table records for its entryNone
dependsOn · outgoingA rule or flow entry may carry a key that addresses it
spec:model.rule-keys · resolves
declareda coined key follows the rule-key grammar and markerNone
refines · outgoingThe upgrade kit shows a corpus what it could adopt
spec:consumers.upgrade-kit · resolves
declaredNoneNone
refines · incomingTwo rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed
spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · resolves
declaredNoneNone
refines · incomingA rule-key proposal passes the proposal check and validates once applied
spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · resolves
declaredNoneNone
verifies · incomingTwo rules that coin one key stay apart, and a rule with no ASCII word stays unkeyed
spec:consumers.upgrade-kit.rule-key-proposal.collision-and-unkeyable · resolves
declaredNonecoinedKey, uniqueKey, unkeyable
verifies · incomingA rule-key proposal passes the proposal check and validates once applied
spec:consumers.upgrade-kit.rule-key-proposal.proposal-applies-clean · resolves
declaredNoneproposesKeys, keepsText, coinedKey, applyAfterCheck

Bindings

Implementation binding: present

  • impl:protocol.rule-key-proposal-recipe · realizes the rule-key proposal in the shipped recipe catalog · test/upgrade-kit.test.ts:742 · role projection

Design reference: none

Verifier binding: present

Expected-outcome oracle: present

  • oracle:protocol.rule-key-proposal · test/upgrade-kit.test.ts:398

Runtime observation: not tracked.

Run results: not recorded in the graph.

Findings

None.

Composing (0)

Composing needs the script.