Spec Studio

Designs are compared between a base commit and the working tree at entry grain

spec:consumers.design-diffUse Case / Behavior · featureready floor ready

Packs: pack:design-iteration-v1, pack:design-management-v1, pack:self-hosting-v1

Design: 8 design entries · 4 pinned declarations · 1 decision

Findings: no errors, no warnings

specs/consumers/design-diff.sdp.md

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

The original design compared graph snapshots. Comparison concerns the curated graph a design lives in and needs no symbol identity.

Iterating on a design asks what changed since the last reading. The first adopter asks for a comparison by Spec id, section and entry key that tells changed declaration text from changed prose, moved entries, readiness edits, relation edits and new or removed examples, and for what changed since a design pass, read against the commit that pass read (libar-platform/docs/feedback/sdp-feedback-02.md, items 30 and 31). Git shows the changed lines; this comparison shows the changed design.

Intent
actor
A reviewer or an agent starting the next design pass over a Pack, holding the commit the last pass read.
problem
A line diff of the carriers shows edits but not which Specs, entries, questions and relations changed, so each pass re-reads the whole Pack to find what moved.
outcome
Show what changed in a design between a base commit and the working tree, down to the Spec, section and keyed entry, so a reviewer reads what changed since the last pass.
Behavior
rule line 28 · specs/consumers/design-diff.sdp.md:28
The design diff compares a base graph with the current graph.
rule line 29 · specs/consumers/design-diff.sdp.md:29
A comparison is a catalog recipe that takes the graph sdp build wrote at the base commit as a recipe parameter and compares it with the graph the evaluation derives from the working tree, including uncommitted edits; it adds no verb or binding.
rule line 30 · specs/consumers/design-diff.sdp.md:30
The base graph is input the caller holds, derived from the repository alone at the base commit; it never answers in the graph's name, and the comparison writes nothing.
rule line 31 · specs/consumers/design-diff.sdp.md:31
The caller always names the base. The recipe reads no record of a design pass and keeps none, and a corpus that notes which commit a pass read keeps that note outside the graph.
rule line 32 · specs/consumers/design-diff.sdp.md:32
Two graphs written by different engine versions are compared on what both carry; the result reports each side's schema version, and a difference is a diagnostic in the result, never a refusal.
rule line 33 · specs/consumers/design-diff.sdp.md:33
It reports the Specs added and removed, and for a Spec present in both graphs the changes to its title, kind, altitude, stated readiness, relations, narrative and each section.
rule line 34 · specs/consumers/design-diff.sdp.md:34
Inside the Design and UI sections, the open questions and other keyed sections apart from rules and flows, it compares keyed entries by key and reports each as added, removed, changed in value or moved in order.
rule line 35 · specs/consumers/design-diff.sdp.md:35
Rule and flow entries match first by unchanged text, preferring the same key for repeated wording, then by retained key; remaining unkeyed base entries pair in list order, including an entry reworded while adding a key. An unmatched keyed base entry is removed, and an unmatched head entry is added. Other unkeyed entries have no identity across the two graphs and compare as part of their whole section.
rule line 36 · specs/consumers/design-diff.sdp.md:36
A rule or flow with unchanged text whose key was added, removed or changed is one keyed change naming its old and new address, or none; only a text change is changed. An unchanged entry moving between rules and flows is moved; when one graph keeps no order across rules and flows, keyed entries compare by order within each list and a ruleOrder diagnostic names the Spec.
rule line 37 · specs/consumers/design-diff.sdp.md:37
It reports changes to Pack membership, member order and what a Pack's order means, and to the code units, verifiers and delivery facts bound to each Spec, kept apart from the authored changes because they derive from anchors.
rule line 38 · specs/consumers/design-diff.sdp.md:38
A comparison states facts about the two graphs and confers nothing. It grants no approval, raises no staleness flag, moves no readiness and never records that a pass happened.

11 unkeyed rules.

Design
diffRecipe line 42 · specs/consumers/design-diff.sdp.md:42
recipe 35, the design diff, takes params.base, the graph object sdp build wrote at the base commit, an optional params.pack that limits the comparison to the Specs that are members of that Pack at either commit, and params.detail; it compares params.base with the graph binding and reads nothing else.
diffShape line 43 · specs/consumers/design-diff.sdp.md:43
{ base: { schemaVersion, specs }, head: { schemaVersion, specs }, diagnostics, totals, specs: { added: [id], removed: [id], changed }, packs, bindings, leftOut }, where totals counts each list and diagnostics lists schema-version differences, parts outside the common schema, a Pack absent from both graphs, and Specs compared without order across rules and flows; base.specs and head.specs count the Specs of the comparison scope present on each side, the scope being every Spec or, with pack, the Pack's members in either graph, while the top-level specs object carries the change lists.

no citation recorded

changedSpecRow line 44 · specs/consumers/design-diff.sdp.md:44
{ id, parts: [part] } in the summary and { id, parts: [{ part, change, from, to }] } with detail: true; change is added, removed, changed, moved or keyed. For keyed, from and to carry the old and new entry addresses, null meaning none; other changes carry the values on each side, a rule or flow value as { list, text }. A text change while adding a key is one changed entry.

no citation recorded

diffParts line 45 · specs/consumers/design-diff.sdp.md:45
a part is title, specKind, altitude, statedReadiness, narrative, relation.<type>:<target>, a keyed entry as design.<key>, ui.<key>, question.<key>, rule.<key> or term.<term>, an unkeyed rule or flow as behavior.rules[<index>] or behavior.flows[<index>], using its head index; a removed unkeyed entry uses its base index with a (base) mark, such as behavior.rules[1] (base), so it cannot share a name with a head entry at that index; an unkeyed question list as intent.openQuestions, and any other section as its name.
movedEntry line 46 · specs/consumers/design-diff.sdp.md:46
a keyed entry whose value is unchanged and whose position among the keyed entries of its section changed is moved; an entry whose value changed is changed whatever its position. For rules and flows, unchanged text with a different key takes keyed precedence, then a list change or relative order change among retained entries is moved; anonymous entries compare order within their list. Adding or removing a key alone moves no other entry.
packRows line 47 · specs/consumers/design-diff.sdp.md:47
packs rows are { id, change, membersAdded, membersRemoved, reordered, orderChanged, orderBefore, orderAfter } for each Pack added, removed or changed in membership, member order or order meaning; orderBefore and orderAfter are "reading" or "priority", null on a side where the Pack is absent, and orderChanged is true when both sides hold the Pack and the two differ. An absent order reads as "reading" in every schema version; a value the head's engine does not know is outside the common schema, listed in diagnostics and not compared; that Pack's membership and member order still compare, its orderChanged is false, and the side holding the unknown value reads null. membersAdded and membersRemoved keep their respective manifest order.

no citation recorded

bindingRows line 48 · specs/consumers/design-diff.sdp.md:48
bindings rows are { id, implementedBy, referencedBy, verifiers, deliveryFacts }, each an { added, removed } pair of ids or fact names, kept apart from specs because they derive from anchors.

no citation recorded

baseRecipe line 49 · specs/consumers/design-diff.sdp.md:49
The catalog shows how to derive the base graph in a worktree at the base commit with the corpus's exclusions. When constant-form anchors use relative engine imports, as in this repository, run the base worktree's own engine, then pass its generated/graph.json as base and remove the worktree.
Verification

Verifier bindings

  • test:protocol.design-diff-recipe · anchored · test anchor binding present · test/iteration-recipes.test.ts:34

    External run evidence from test/iteration-recipes.test.ts

    Test anchors: test:protocol.context-bundle-recipe, test:protocol.design-diff-recipe, test:protocol.perspective-filter-recipe, test:protocol.proposal-schema-and-recipe. Specs verified by those bindings: spec:consumers.context-bundle, spec:consumers.design-diff, spec:consumers.intent-composition.proposal, spec:consumers.review-perspectives.

    Run 1: external run evidence from vitest 4.1.10

    Build: CI 391 · Branch: cursor/media-plan-and-articles-09b1 · Commit: 076cd4f1f85640d8236e5cca1bffffa68ab79a33 · Start: 2026-10-31T15:32:20.471Z · Stop: 2026-10-31T15:34:02.073Z

    NameSuiteResultDuration
    compares keyed values, keyed order and unkeyed sections while keeping bindings apartiteration loop: design diffpassed83.58 ms
    compares question fields by key, whole unkeyed questions, envelope fields and other sectionsiteration loop: design diffpassed24.03 ms
    reports added and removed Specs and Packs, filters the union of membership, and ignores source linesiteration loop: design diffpassed39.49 ms
    diagnoses required input and schema differences and compares common contentiteration loop: design diffpassed10.49 ms
    runs the documented base-worktree procedure, preserves constant anchors, and compares HEADiteration loop: design diffpassed23367.74 ms
    compares a keyed rule that becomes a flow at the same addressiteration loop: design diffpassed31.53 ms
    compares new Design entries across schema versions when the base corpus has noneiteration loop: design diffpassed26.78 ms
    diagnoses uncertain optional fields at base 0.9.0 and head 0.9.0iteration loop: design diffpassed14.73 ms
    diagnoses uncertain optional fields at base 0.9.0 and head 0.10.0iteration loop: design diffpassed18.9 ms
    diagnoses uncertain optional fields at base 0.10.0 and head 0.9.0iteration loop: design diffpassed28.09 ms
    normalizes legacy rule markers in graphs with comparable rule-key capabilityiteration loop: design diffpassed17.53 ms
    invents no order across rules and flows from a legacy baseiteration loop: design diffpassed26.36 ms
    compares a legacy base's keyed entries by order within each listiteration loop: design diffpassed21.05 ms
    reports proposal scopes outside the common schema as incomparableiteration loop: design diffpassed7.3 ms
    lists all authored reasons into and out of the planning subject in type/from/to orderrelation reasons in planning recipespassed18.61 ms
    keeps dependency reasons in context-bundle footingrelation reasons in planning recipespassed8.94 ms
    matches every declared selector, combines requirements, and keeps authored order unless prioritizediteration loop: perspective filterpassed69.31 ms
    matches layers and contexts only through named bindings and keeps anchored matches apartiteration loop: perspective filterpassed27.79 ms
    reports malformed and unsupported selectors while applying understood onesiteration loop: perspective filterpassed26.17 ms
    keeps term addresses, term lines and key maps through the section cutiteration loop: context bundlepassed29.65 ms
    keeps questions and declarations in one place and member-example steps only in their member rowiteration loop: context bundlepassed30.4 ms
    takes the declaration cut last only with addresses and keeps compact pointers and linksiteration loop: context bundlepassed91.81 ms
    cuts a pinned dotted key to its line when it has no entry addressiteration loop: context bundlepassed20.04 ms
    composes both scopes, dependency footing, declarations, example bindings and a perspective subsetiteration loop: context bundlepassed38.24 ms
    takes all six whole-mode cuts in fixed order and counts the complete returned value including Unicodeiteration loop: context bundlepassed70.24 ms
    keeps a pinned declaration whole through the sixth cut when its key contains a dotiteration loop: context bundlepassed21.59 ms
    reports invalid, missing, doubled and unknown scopes without a partial bundleiteration loop: context bundlepassed34.89 ms
    accepts term scopes and detects changed definitions through their authored keysiteration loop: proposal schema and checkpassed39.18 ms
    holds the schema and recipe to the same accepted and refused documentsiteration loop: proposal schema and checkpassed126.52 ms
    checks the complete scope union, resolves both graphs, and reports each change independentlyiteration loop: proposal schema and checkpassed15.33 ms
    agrees with recipe 35 on added, removed and changed entry values and on whole-Spec changesiteration loop: proposal schema and checkpassed13.3 ms
    excludes anchor-only changes and requires the caller's base graphiteration loop: proposal schema and checkpassed14.83 ms
    compares interleaved keyed rules and flows in their authored order and reports verifier changes apartiteration loop: comparison and budget edgespassed21.4 ms
    cuts example text once and preserves compact pointers after the sixth cutiteration loop: comparison and budget edgespassed60.02 ms
    reads the base graph and proposal from named JSON files through the real parameter seamiteration loop: comparison and budget edgespassed8.13 ms
    requires a test bound directly to an example before the bundle names it enablediteration loop: example enablementpassed17.83 ms
    compares rule order by text and other anonymous collections as a wholeiteration loop: anonymous collection orderpassed16.78 ms
    keeps addresses in each place when narrative, example, question and section text are cutiteration loop: protected entry addressespassed47.69 ms
  • test:protocol.roadmap-priority · checks Pack priority extraction, comparison and Studio change notes · anchored · test anchor binding present · test/roadmap-priority.test.ts:44

    External run evidence from test/roadmap-priority.test.ts

    Test anchors: test:protocol.roadmap-priority. Specs verified by those bindings: spec:carrier.markdown-pack-authoring, spec:consumers.design-diff, spec:consumers.spec-studio.diff-page, spec:consumers.spec-studio.pack-page, spec:extraction.pack-member-order, spec:model.pack-aggregate.

    Run 1: external run evidence from vitest 4.1.10

    Build: CI 391 · Branch: cursor/media-plan-and-articles-09b1 · Commit: 076cd4f1f85640d8236e5cca1bffffa68ab79a33 · Start: 2026-10-31T15:32:20.471Z · Stop: 2026-10-31T15:34:02.073Z

    NameSuiteResultDuration
    derives equal Markdown and TypeScript Pack nodes and edges, serializes order last, and exposes it through the readerroadmap priority manifestpassed45.69 ms
    refuses the invalid Markdown value readingroadmap priority manifestpassed1.85 ms
    refuses the invalid Markdown value arbitraryroadmap priority manifestpassed1.57 ms
    refuses the invalid Markdown value trueroadmap priority manifestpassed1.49 ms
    refuses the invalid Markdown value nullroadmap priority manifestpassed1.94 ms
    refuses the invalid Markdown value []roadmap priority manifestpassed1.71 ms
    refuses the invalid Markdown value {}roadmap priority manifestpassed1.43 ms
    refuses the invalid TypeScript value "reading"roadmap priority manifestpassed7.24 ms
    refuses the invalid TypeScript value "arbitrary"roadmap priority manifestpassed2.88 ms
    refuses the invalid TypeScript value trueroadmap priority manifestpassed3.36 ms
    refuses the invalid TypeScript value nullroadmap priority manifestpassed3.31 ms
    refuses the invalid TypeScript value []roadmap priority manifestpassed9.61 ms
    refuses the invalid TypeScript value {}roadmap priority manifestpassed3.83 ms
    refuses the invalid TypeScript value undefinedroadmap priority manifestpassed2.87 ms
    refuses the invalid TypeScript value chosenOrderroadmap priority manifestpassed4.04 ms
    names a removed Pack by its order meaning (priority true)roadmap priority design diffpassed19.39 ms
    names a removed Pack by its order meaning (priority false)roadmap priority design diffpassed17.38 ms
    names an added roadmap and preserves manifest order in its member deltaroadmap priority design diffpassed26.27 ms
    labels Pack membership by its order meaning (priority true)roadmap priority design diffpassed15.16 ms
    labels Pack membership by its order meaning (priority false)roadmap priority design diffpassed6.39 ms
    states order values when a change is not a known roadmap transitionroadmap priority design diffpassed11.93 ms
    documents the order comparison fields, default and diagnostic in the recipe contractroadmap priority design diffpassed1.3 ms
    compares a priority transition to true without a membership editroadmap priority design diffpassed10.9 ms
    compares a priority transition to false without a membership editroadmap priority design diffpassed6.76 ms
    reads an absent order as reading order in a base schema that predates the fieldroadmap priority design diffpassed14.49 ms
    marks the absent side of an added or removed roadmap as null (true)roadmap priority design diffpassed10.48 ms
    marks the absent side of an added or removed roadmap as null (false)roadmap priority design diffpassed4.24 ms
    diagnoses an unknown order at the base without reporting an order changeroadmap priority design diffpassed3.41 ms
    diagnoses an unknown order at the head without reporting an order changeroadmap priority design diffpassed4.06 ms
    still compares membership and member order when the base order is unknownroadmap priority design diffpassed5.37 ms
    still compares membership and member order when the head order is unknownroadmap priority design diffpassed9.28 ms
    keeps an order-only change in the selected Pack's comparison scoperoadmap priority design diffpassed3.1 ms
    shows no roadmap transition for a membership-only change (priority true)roadmap priority design diffpassed8.38 ms
    shows no roadmap transition for a membership-only change (priority false)roadmap priority design diffpassed7.43 ms
    shows the roadmap transition to true on the diff and Pack pagesroadmap priority design diffpassed8.78 ms
    shows the roadmap transition to false on the diff and Pack pagesroadmap priority design diffpassed17.15 ms
Design view

Requirement drawing

  • Declared verifying examples: no declared verifying example.
Requirement drawing for spec:consumers.design-diff
The diagram as text
  • spec:consumers.design-diff: Designs are compared between a base commit and the working tree at entry grain · declared Spec
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores · declared Spec
  • impl:protocol.design-diff-recipe: realizes the design diff in the shipped recipe catalog · anchored code · test/iteration-recipes.test.ts:27
  • test:protocol.design-diff-recipe: test:protocol.design-diff-recipe · anchored test binding · binding present · test/iteration-recipes.test.ts:34
  • test:protocol.roadmap-priority: checks Pack priority extraction, comparison and Studio change notes · anchored test binding · binding present · test/roadmap-priority.test.ts:44
  • spec:consumers.design-diff refines spec:consumers.projections-model · declared
  • impl:protocol.design-diff-recipe satisfies spec:consumers.design-diff · anchored
  • test:protocol.design-diff-recipe verifies spec:consumers.design-diff · anchored
  • test:protocol.roadmap-priority verifies spec:consumers.design-diff · 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 roadmap states advisory priority in the graph

    A roadmap is a Pack whose manifest states order: priority; its members' authored order is the corpus's advisory priority, first entry first. order is an optional manifest field whose one lawful value is priority, and a Pack without it keeps the reading order every Pack has. Priority is preference among bodies of work and nothing more: it carries no date, no dependency, no status and no gate, and no check, recipe or page treats a place on a roadmap as permission, obligation or sequence. Everything a roadmap shows about an entry, what it still needs and what holds it, is read from the graph each time. A corpus may keep several roadmaps, and a Spec may be listed on any number of them.

Runtime
  • impl:protocol.design-diff-recipe · realizes the design diff in the shipped recipe catalog · test/iteration-recipes.test.ts:27

    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.design-diff
  • E4: constrainedBy · declared — equal sources derive equal graphs, so a difference records a source change
The diagram as text
  • spec:consumers.agent-surface.recipe-parameters: Recipes take parameters as data and ship as runnable files
  • spec:consumers.delivery-session-on-ramp: Delivery sessions route work from current graph state
  • spec:consumers.design-diff: Designs are compared between a base commit and the working tree at entry grain
  • spec:consumers.intent-composition.proposal: A change proposal is addressed, based and checked before it lands
  • spec:consumers.projections-model: Projections fan out from one graph without becoming truth stores
  • spec:consumers.spec-studio.data.inputs: The Studio reads two named inputs beside the graph, a base graph and run records
  • spec:consumers.spec-studio.diff-page: The diff page shows what changed in the design since a base graph
  • spec:decisions.roadmap-priority: A roadmap states advisory priority in the graph
  • spec:extraction.determinism: Committed source derives byte-identical output
  • spec:consumers.design-diff constrainedBy spec:extraction.determinism · declared — equal sources derive equal graphs, so a difference records a source change
  • spec:consumers.design-diff decidedBy spec:decisions.roadmap-priority · declared
  • spec:consumers.design-diff dependsOn spec:consumers.agent-surface.recipe-parameters · declared — the base graph is supplied as recipe-parameter data, including the KEY=@PATH file form
  • spec:consumers.design-diff refines spec:consumers.projections-model · declared
  • spec:consumers.delivery-session-on-ramp dependsOn spec:consumers.design-diff · declared — the design loop starts from what changed since the last pass
  • spec:consumers.intent-composition.proposal dependsOn spec:consumers.design-diff · declared
  • spec:consumers.spec-studio.data.inputs dependsOn spec:consumers.design-diff · declared — the base graph input is the design diff's base
  • spec:consumers.spec-studio.diff-page dependsOn spec:consumers.design-diff · declared — the page renders the design diff between the base graph and the graph built
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

Relations

Relation and directionOther endClaimReasonExercised keys
constrainedBy · outgoingCommitted source derives byte-identical output
spec:extraction.determinism · resolves
declaredequal sources derive equal graphs, so a difference records a source changeNone
decidedBy · outgoingA roadmap states advisory priority in the graph
spec:decisions.roadmap-priority · resolves
declaredNoneNone
dependsOn · outgoingRecipes take parameters as data and ship as runnable files
spec:consumers.agent-surface.recipe-parameters · resolves
declaredthe base graph is supplied as recipe-parameter data, including the KEY=@PATH file formNone
refines · outgoingProjections fan out from one graph without becoming truth stores
spec:consumers.projections-model · resolves
declaredNoneNone
dependsOn · incomingDelivery sessions route work from current graph state
spec:consumers.delivery-session-on-ramp · resolves
declaredthe design loop starts from what changed since the last passNone
dependsOn · incomingA change proposal is addressed, based and checked before it lands
spec:consumers.intent-composition.proposal · resolves
declaredNoneNone
dependsOn · incomingThe Studio reads two named inputs beside the graph, a base graph and run records
spec:consumers.spec-studio.data.inputs · resolves
declaredthe base graph input is the design diff's baseNone
dependsOn · incomingThe diff page shows what changed in the design since a base graph
spec:consumers.spec-studio.diff-page · resolves
declaredthe page renders the design diff between the base graph and the graph builtNone

Bindings

Implementation binding: present

  • impl:protocol.design-diff-recipe · realizes the design diff in the shipped recipe catalog · test/iteration-recipes.test.ts:27 · role projection

Design reference: none

Verifier binding: present

  • test:protocol.design-diff-recipe · anchored · test/iteration-recipes.test.ts:34 · test anchor
  • test:protocol.roadmap-priority · checks Pack priority extraction, comparison and Studio change notes · anchored · test/roadmap-priority.test.ts:44 · 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.