From df04577a22d01dc57f35598528a71fbf28359f0f Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Fri, 22 May 2026 23:22:39 +0000 Subject: [PATCH 01/24] Refine Rust V1 scope and orchestration plans --- ORCH_PLAN.md | 701 +++++---------------- PLAN.md | 1047 +++++++++----------------------- docs/rust_v1_contract_stack.md | 104 +++- 3 files changed, 522 insertions(+), 1330 deletions(-) diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index 383193f..64cf630 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,652 +1,245 @@ -# I6 Orchestration Plan +# I7 Orchestration Plan -Status: **authoritative execution runbook** -Milestone: **I6 Rust V1 service benchmark activation** -Plan authority: **`/home/azureuser/__Active_Code/atomize-hq/spec/PLAN.md`** -Frozen basis: **current `HEAD` of `codex/i6-service-benchmark-activation` when the run begins** -Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** -Last rewritten: **2026-05-21** +Status: **authoritative execution runbook** +Milestone: **I7 Rust V1 scope-decision closure** +Plan authority: **`/home/azureuser/__Active_Code/atomize-hq/spec/PLAN.md`** +Frozen basis: **current `HEAD` of `main` when the run begins** +Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** +Last rewritten: **2026-05-22** ## Summary - Execute from `/home/azureuser/__Active_Code/atomize-hq/spec`. - Treat `PLAN.md` as the only milestone authority. -- Treat the existing `ORCH_PLAN.md` as stale I5 context only. -- Keep the live primary checkout on `codex/i6-service-benchmark-activation` as the parent lane and canonical run-state root. -- Use dedicated worker worktrees under `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/`. -- All worker lanes use **GPT-5.4** with **`reasoning_effort=high`**. -- Cap concurrency at **2 active worker lanes**: - - Lane A runs first alone - - Lane B and conditional Lane C may overlap only after the Lane A freeze commit exists - - Lane D starts only after the post-B/C truth gate passes -- The parent agent remains the **only integrator**, **only merge authority**, and **only final acceptance authority**. +- Treat the old I6 execution shape as shipped history, not current work. +- Keep the parent agent as the only merge authority and the only final + acceptance authority. - Keep the critical path local to the parent for: - basis freeze - - queue freeze - - service-freeze record - - worker launch - - gate decisions - - merge order - - final proof wall - - final fast-forward of the parent branch + - decision freeze + - contract-ratification merge + - final I8 handoff + +I7 is lighter than I6. This milestone is mostly about forcing the last scope +decisions into checked-in authority instead of letting them float. ## Starting Truth -Observed on `codex/i6-service-benchmark-activation` at the then-current `HEAD`: - -- `cargo run -p spec-cli -- status . --format json` reports: - - `schema_version: 4` - - `scope_authority: "inventory_only"` - - `BENCH-CROSSLIB` is `active`, `passing`, and `positive_credit_cases: 0` - - `BENCH-ECOM` is `active`, `passing`, and `readability_review_status: "current"` - - `BENCH-SERVICE` is `reserved`, `accounting_status: "reserved_missing_cases"`, and `summary.total_cases: 0` -- `benchmarks/labels.json` already declares: - - `id: "BENCH-SERVICE"` - - `root: "examples/service/units"` - - `generated_root: "examples/service/src/generated"` - - `readability_scope: "supported_closure"` - - `cases: []` -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` exists and is still reserved-form. -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` does not exist. -- `examples/service/` does not exist. -- `spec-cli/tests/rust_v1_service.rs` does not exist. -- `.runs/` already exists and can host `.runs/i6/`. -- Existing reusable patterns live in: - - `examples/ecommerce/units/pricing/discount_strategy.unit.spec` - - `examples/ecommerce/units/pricing/pricing_quote.unit.spec` - - `examples/ecommerce/units/pricing/*.test.spec` - - `spec-cli/tests/rust_v1_closure.rs` - - `spec-cli/tests/fixtures/benchmarks/**` - - `spec-cli/tests/fixtures/m19/semantic_falsification_pack/units/billing/**` +Observed on `main` after the `0.15.2` landing: + +- `BENCH-ECOM` is active positive proof. +- `BENCH-SERVICE` is active positive proof. +- `BENCH-CROSSLIB` is the active companion-negative wall. +- repo-root `status . --format json` remains `inventory_only`. +- repo-root `export .` remains unsupported for this workspace shape. +- `M66` still leaves bounded generics and async/IO outside the narrow-core + shipped baseline. +- the repo now needs a checked-in answer for the final honest Rust V1 line. ## Hard Guards -- Do not widen M66 support. -- Do not reopen M68 benchmark design or command-scope mechanics. -- Do not introduce async, IO, traits, generics, lifetimes, framework-heavy authored surfaces, or cross-library service proof. -- Keep `BENCH-SERVICE` single-library under `examples/service/**`. -- Keep the positive service roster exactly: - - `billing/apply_membership_discount` - - `billing/apply_regional_fee` - - `billing/checkout_net_total` - - `billing/checkout_net_total_guarded_fee` - - `billing/discount_strategy` - - `billing/pricing_quote` -- Keep the required molecule roster exactly: - - `billing/checkout_success_flow` - - `billing/checkout_declined_discount_flow` - - `billing/discount_strategy_quote_flow` -- Treat `.unit.spec` and `.test.spec` files as authored truth. -- Treat generated Rust, passports, molecule evidence, snapshots, and readability reviews as derived or observation surfaces. -- Allow projection/core edits only if the active service benchmark exposes a real read-side truth bug. -- Keep `BENCH-ECOM` and `BENCH-CROSSLIB` green and truthful throughout I6. -- Keep repo-root `status . --format json` diagnostic-only. It must remain `scope_authority: "inventory_only"` and is not a zero-exit acceptance gate. +- Do not reopen I3.5 command-wall semantics. +- Do not redesign benchmark mechanics. +- Do not widen Rust V1 support without an explicit I7 decision packet. +- Do not treat "maybe V1 later" as a substitute for repo-backed truth. +- Keep `I8` as the final proof-run milestone. +- Do not imply a checked-in `I9` queue. ## Worktree And Branch Plan -Create the I6 worktree root once: - -```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6 -``` - -Freeze the live basis before any worker branch is created: - -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse --abbrev-ref HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse --short HEAD -``` - -Expected basis: - -- branch: `codex/i6-service-benchmark-activation` -- commit: record the current `HEAD` in `.runs/i6/basis.json` at queue freeze and use that as the run-local basis for all later drift checks +Use the live primary checkout as the parent lane basis: -Use the live primary checkout as the parent lane: - -- Parent branch: `codex/i6-service-benchmark-activation` +- Parent basis branch: `main` - Parent workspace: `/home/azureuser/__Active_Code/atomize-hq/spec` -Create Lane A first and only: - -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-a -b codex/i6-lane-a-service-root codex/i6-service-benchmark-activation -``` - -After Lane A merges and the parent records `service_freeze_commit`, create integration and Lane B from that exact commit: +Create the I7 worktree root only if parallel lanes are actually needed: ```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/int -b codex/i6-int -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-b -b codex/i6-lane-b-service-regressions +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7 ``` -Create Lane C only if the parent confirms a real read-side truth bug: +Recommended parent working branch for the live milestone after basis freeze: ```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-c -b codex/i6-lane-c-projection-fix +git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout -b codex/i7-v1-scope-closure ``` -Create Lane D only after Gate 2 passes: +Optional worker lanes after basis freeze: ```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-d -b codex/i6-lane-d-closeout codex/i6-int +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a -b codex/i7-lane-a-generics codex/i7-v1-scope-closure +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b -b codex/i7-lane-b-async codex/i7-v1-scope-closure +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c -b codex/i7-lane-c-ratify codex/i7-v1-scope-closure ``` -Final landing is parent-owned only: - -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec merge --ff-only codex/i6-int -``` +If the parent decides that no parallelism is needed, keep I7 single-lane and do +the work locally. ## Orchestration State Canonical run state lives under: -- `I6_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6` -- queue: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/tasks.json` -- session log: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/session-log.md` -- basis record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/basis.json` -- Gate 1 record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/service-freeze.json` -- Gate 2 record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/service-truth-gate.json` -- Gate 3 record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/final-acceptance.json` -- per-task state: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i6/task//` - -The parent owns all writes under `.runs/i6/`. Workers do not update orchestration state directly. - -Per-task sentinels inside `.runs/i6/task//`: +- `I7_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7` +- basis record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis.json` +- decision freeze: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/decision-freeze.json` +- final handoff: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/i8-handoff.json` +- session log: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/session-log.md` -| Sentinel | Meaning | -| --- | --- | -| `QUEUED` | task frozen, not started | -| `RUNNING` | lane or parent actively executing | -| `BLOCKED` | waiting on parent decision or dependency | -| `READY` | lane believes acceptance is satisfied | -| `MERGED` | parent merged result | -| `REJECTED` | parent bounced lane for scope or quality reasons | +Suggested task packets: -Each task directory carries: +- `i7-a0-basis-freeze` +- `i7-a1-bounded-generics-decision` +- `i7-a2-async-io-decision` +- `i7-b0-contract-ratification` +- `i7-c0-i8-handoff-freeze` -- `scope.md` -- `acceptance.md` -- `handoff.md` -- `decisions.md` +The parent owns `.runs/i7/**`. Workers do not mutate orchestration state +directly. ## Lane Map -| Lane | Branch | Worktree | Owned write set | Goal | -| --- | --- | --- | --- | --- | -| Parent | `codex/i6-service-benchmark-activation` | `/home/azureuser/__Active_Code/atomize-hq/spec` | `.runs/i6/**`, gate records, merge decisions, final fast-forward | orchestration and final authority | -| Lane A | `codex/i6-lane-a-service-root` | `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-a` | `benchmarks/labels.json`, `examples/service/**`, generated Rust under `examples/service/src/generated/**`, service passports, service molecule evidence | service scaffold, active labels, authored proof wall | -| Lane B | `codex/i6-lane-b-service-regressions` | `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-b` | `spec-cli/tests/rust_v1_service.rs`, `spec-cli/tests/fixtures/benchmarks/**` | service regression truth and benchmark fixtures | -| Lane C | `codex/i6-lane-c-projection-fix` | `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-c` | `spec-core/src/benchmark.rs`, `spec-cli/src/commands.rs` | minimal read-side truth fix only if needed | -| Lane D | `codex/i6-lane-d-closeout` | `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/lane-d` | `benchmarks/snapshots/BENCH-SERVICE.snapshot.json`, `benchmarks/reviews/BENCH-SERVICE.readability.review.json`, `README.md`, `docs/rust_v1_contract_stack.md`, `TODOS.md`, `CHANGELOG.md`, and only-if-truthfully-required closeout refreshes for existing ECOM/CROSSLIB snapshot or readability or doc surfaces | artifact/docs closeout only | -| Integration | `codex/i6-int` | `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/int` | merge-only plus minimal parent reconciliation | integration and gate execution | +| Lane | Branch | Owned write set | Goal | +| --- | --- | --- | --- | +| Parent | `codex/i7-v1-scope-closure` | `.runs/i7/**`, merge decisions, final acceptance | basis freeze, decisions, final authority | +| Lane A | `codex/i7-lane-a-generics` | decision packet drafts, supporting contract docs | bounded-generics recommendation | +| Lane B | `codex/i7-lane-b-async` | decision packet drafts, supporting contract docs | async/IO recommendation | +| Lane C | `codex/i7-lane-c-ratify` | `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, repo-facing closeout docs | ratified repo authority after parent freezes decisions | + +If I7 later admits a bounded new slice that truly requires code proof, create an +additional lane only after the parent records the decision freeze. Do not front-run +that work. ## Gate Model -### Gate 0: Basis Freeze / Queue Freeze +### Gate 0: Basis Freeze Owner: parent -Task id: `i6-a0-freeze-basis` +Task id: `i7-a0-basis-freeze` Advance only when: -- live branch is `codex/i6-service-benchmark-activation` -- live commit is the branch `HEAD` recorded in `basis.json` for this run -- `.runs/i6/` exists -- `basis.json`, `tasks.json`, and task packets exist -- frozen positive service roster and required molecules are recorded -- Lane A is the only worker branch created +- current branch and commit are recorded in `.runs/i7/basis.json` +- the shipped benchmark walls are recorded +- the current deferred rows from M66 are recorded +- the current command-wall semantics are recorded +- any worker lanes, if used, are created from the frozen basis -Reopens if: +Reopen if: -- the live parent basis drifts before worker launch -- queue ownership or frozen rosters are unclear -- any worker needs to guess write-set or gate behavior +- basis drifts before decision work starts +- a worker is launched from an unfrozen commit +- the starting claim set is still paraphrase instead of evidence -### Gate 1: Post-Lane-A Service-Freeze Gate +### Gate 1: Decision Freeze Owner: parent -Task id: `i6-a1-service-freeze` +Task id: `i7-a3-decision-freeze` Advance only when: -- `examples/service/**` exists as a real single-library benchmark root -- `benchmarks/labels.json` marks `BENCH-SERVICE` active -- labels list exactly the frozen six service units and three required molecules -- all six service units have fresh passports -- all three service molecules have fresh evidence -- the parent writes `service-freeze.json` with: - - `service_freeze_commit` - - frozen rosters - - current `label_digest` - - whether a projection bug is open +- the bounded-generics packet recommends exactly one outcome +- the async/IO packet recommends exactly one outcome +- each packet names: + - the user-value rationale + - the authored boundary + - the proof burden + - the rejected broader expansions +- the parent records both decisions in `.runs/i7/decision-freeze.json` -Reopens Lane A if: +Reopen if: -- authored service truth is incomplete, stale, invalid, or out of roster -- service root or labels drift from the frozen roster -- any claimed proof depends on widened support +- a packet still says "maybe" +- a packet quietly widens into broad support +- the proof burden is missing or unbelievable -### Gate 2: Post-B/C Service-Truth Gate +### Gate 2: Contract Ratification Owner: parent -Task id: `i6-d0-service-truth-gate` +Task id: `i7-b0-contract-ratification` Advance only when: -- Lane B is merged into `codex/i6-int` -- Lane C is either merged or explicitly skipped -- `cargo run -p spec-cli -- status examples/service/units --format json` and `cargo run -p spec-cli -- export examples/service/units` project truthful service-root benchmark state -- service-root benchmark truth is passing with full proof -- service-root fixtures and assertions in Lane B are derived from direct service-root `status` and `export` truth, not only indirect unit-test expectations -- partial service scopes emit zero positive credit -- `BENCH-ECOM` remains green/current -- `BENCH-CROSSLIB` remains green with zero positive credit +- `PLAN.md` teaches the frozen I7 story +- `ORCH_PLAN.md` teaches the frozen I7 execution shape +- `docs/rust_v1_contract_stack.md` teaches `I7 -> I8` +- repo-facing docs no longer imply a post-I6 planning vacuum +- the repo names deferred `V1.1` surfaces explicitly if they remain out -Reopens prior lanes if: +Reopen if: -- service-root truth is wrong because authored proof or labels are wrong: reopen Lane A -- service-root truth is wrong because regression tests or fixtures are missing or dishonest: reopen Lane B -- service-root truth is wrong because projection/core behavior is wrong: reopen Lane C -- any cross-lane disagreement conflicts with `PLAN.md`: bounce to the owning lane, do not resolve creatively in integration +- any checked-in doc still teaches the old I6-as-current story +- one doc says a surface is in V1 while another leaves it deferred +- `I8` is still undefined as the final proof milestone -### Gate 3: Final Acceptance Gate +### Gate 3: I8 Handoff Freeze Owner: parent -Task id: `i6-e-final-acceptance` +Task id: `i7-c0-i8-handoff-freeze` Advance only when: -- `BENCH-SERVICE` is active, passing, and gate-satisfied at service-root scope -- `BENCH-SERVICE` readability review is `current` -- committed `BENCH-SERVICE.snapshot.json` is stable on rerun -- service-root `status` and `export` remain the proof wall -- `BENCH-ECOM` and `BENCH-CROSSLIB` stay truthful -- repo-root `status . --format json` remains diagnostic inventory with `scope_authority: "inventory_only"` - -Reopens prior lanes if: - -- snapshot or readability truth drifts: reopen Lane D -- service-root proof wall drifts: reopen Lane A, B, or C based on cause -- docs overstate shipped truth: reopen Lane D - -## Conflict And Bounce-Back Rules - -- If a lane edits outside its write set, the parent rejects it. -- If a lane discovers required scope drift to the frozen positive roster or required molecule roster, it must stop, mark `BLOCKED`, and escalate to the parent. -- Lane A may not change regression suites, fixtures, snapshots, reviews, or docs. -- Lane B may not patch core behavior. If it requires `spec-core/src/benchmark.rs` or `spec-cli/src/commands.rs` changes, it must bounce to the parent and conditional Lane C. -- Lane C may not patch labels, specs, proof artifacts, tests, fixtures, snapshots, reviews, or docs. -- Lane D is closeout-only. It may not reopen service semantics, labels, tests, fixtures, or core behavior. -- Integration does not resolve creative cross-lane disagreements. It either: - - applies `PLAN.md` literally, or - - bounces the issue back to the owning lane -- If a lane believes another lane’s owned surface must change, it does not edit that surface directly. It returns a narrow parent escalation. - -## Shared Ownership And Artifact Policy - -- `PLAN.md` is the only milestone authority. -- Source service specs are authored truth: - - `examples/service/units/**/*.unit.spec` - - `examples/service/units/**/*.test.spec` -- Lane A may refresh the derived service proof surfaces required by those specs: - - `examples/service/src/generated/**` - - `examples/service/units/**/*.spec.passport.json` - - `examples/service/units/**/*.test.evidence.json` -- No lane hand-edits generated Rust, passports, molecule evidence, snapshot JSON, or readability review JSON. -- Lane D alone owns committed snapshot and readability refresh for `BENCH-SERVICE`. -- If merged I6 truth makes `BENCH-ECOM` or `BENCH-CROSSLIB` snapshot or readability or closeout docs require refresh, that refresh belongs to Lane D and nowhere else. -- Lane B owns benchmark test fixtures, including any repo-root benchmark fixture JSON that changes because `BENCH-SERVICE` becomes active. -- Repo-root `status . --format json` is always interpreted as structured inventory, never as a zero-exit proof command. +- the benchmark walls required for I8 are named explicitly +- the proof-authoritative commands are named explicitly +- the deferred `V1.1` list is named explicitly +- the parent records the target in `.runs/i7/i8-handoff.json` -## Workstream Plan +Reopen if: -### WS-PARENT-0 — basis freeze and queue freeze +- I8 still sounds like "we'll know it when we see it" +- the proof wall is not enumerable +- any deferred surface is still ambient instead of named -Task id: `i6-a0-freeze-basis` +## Execution Order -Parent actions: +1. Freeze basis locally. +2. Draft bounded-generics and async/IO packets. +3. Parent freezes the decisions. +4. Ratify checked-in docs from the frozen decision state. +5. Freeze the I8 handoff. -- confirm live branch and commit -- create `.runs/i6/` -- write `basis.json` -- write `tasks.json`, `session-log.md`, and task packets -- create Lane A only -- freeze the exact positive roster and required molecule roster in run state +Do not reverse steps 3 and 4. The docs should teach frozen decisions, not draft +debate. -Acceptance: +## Validation Wall -- Gate 0 is green -- no worker needs to guess branch names, worktree paths, write-sets, or gate rules - -### WS-A — service scaffold, labels, and proof wall - -Task id: `i6-a1-service-freeze` - -Lane A owns: - -- `benchmarks/labels.json` -- `examples/service/**` -- generated Rust under `examples/service/src/generated/**` -- service passports and service molecule evidence - -Required outcomes: - -- scaffold the service example -- author the six frozen service units -- author the three frozen molecule tests -- activate `BENCH-SERVICE` -- build generated Rust -- refresh all six unit passports -- refresh all three molecule evidence files - -Required commands: +Before final acceptance, rerun the current proof-authoritative commands: ```bash -cargo run -p spec-cli -- build examples/service/units --output examples/service/src/generated -cargo run -p spec-cli -- test examples/service/units/billing/apply_membership_discount.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/apply_regional_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total_guarded_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/pricing_quote.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_success_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_declined_discount_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy_quote_flow.test.spec -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -``` - -Hard rules: - -- no snapshot refresh -- no readability review authoring -- no test fixture edits -- no docs edits -- no core projection edits - -Acceptance: - -- Gate 1 is green -- if read-side truth is wrong, Lane A returns a narrow reproduced bug statement rather than patching core behavior itself - -### WS-PARENT-1 — post-freeze fan-out - -Task id: `i6-a2-launch-int-b-c` - -Parent actions: - -- create `codex/i6-int` and `codex/i6-lane-b-service-regressions` from `service_freeze_commit` -- create `codex/i6-lane-c-projection-fix` only if needed -- mark Lane B running -- mark Lane C running only if spawned - -Acceptance: - -- all post-freeze branches fork from the exact same `service_freeze_commit` -- active worker count never exceeds 2 - -### WS-B — service regression truth and benchmark fixtures - -Task id: `i6-b1-service-regressions` - -Lane B owns: - -- `spec-cli/tests/rust_v1_service.rs` -- `spec-cli/tests/fixtures/benchmarks/**` - -Required outcomes: - -- add a dedicated `rust_v1_service` suite -- add or refresh benchmark fixtures for: - - full service-root `status` - - full service-root `export` - - partial service-scope zero-credit behavior - - active repo-root benchmark inventory with `BENCH-SERVICE` -- prove regressions for: - - missing required molecule proof - - stale required molecule proof - - failing required molecule proof - - readability drift becoming non-current - - partial service scopes emitting zero positive credit - - preserved `BENCH-ECOM` - - preserved `BENCH-CROSSLIB` - - preserved repo-root inventory semantics - -Required commands: - -```bash -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo test -p spec-cli rust_v1_service -cargo test -p spec-cli rust_v1_closure cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- status examples/crosslib-app/units --format json -cargo run -p spec-cli -- status . --format json -``` - -Hard rules: - -- no edits to `benchmarks/labels.json` -- no edits under `examples/service/**` -- no snapshot or readability-review edits -- no docs edits -- no core behavior edits - -Acceptance: - -- lane diff stays entirely within Lane B ownership -- service-root fixtures are derived from direct `status examples/service/units --format json` and `export examples/service/units` truth -- service-root fixtures are not justified only by indirect unit-test expectations -- repo-root inventory fixtures stay truthful to active `BENCH-SERVICE` - -### WS-C — minimal projection/core fix only if needed - -Task id: `i6-c1-projection-fix` - -Lane C owns: - -- `spec-core/src/benchmark.rs` -- `spec-cli/src/commands.rs` - -Required outcomes: - -- reproduce the exact read-side truth bug -- fix only the minimum code needed for truthful service-root projection -- preserve M68 mechanics and current command-scope behavior - -Required commands: - -```bash -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -``` - -Hard rules: - -- no edits to specs, proof artifacts, labels, tests, fixtures, snapshots, reviews, or docs -- no benchmark-schema redesign -- no support expansion - -Acceptance: - -- diff stays entirely inside Lane C ownership -- bug is explained as read-side truth repair, not feature work - -### WS-INT-2 — post-B/C service-truth gate - -Task id: `i6-d0-service-truth-gate` - -Parent actions in `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i6/int`: - -- merge Lane C first if present -- merge Lane B second -- run Gate 2 before Lane D exists - -Required commands: - -```bash -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo test -p spec-cli rust_v1_service -cargo test -p spec-cli rust_v1_closure -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- status examples/crosslib-app/units --format json -``` - -Acceptance: - -- Gate 2 is green -- service-root `status` and `export` are truthful proof walls -- `readability_review_status` may still be non-current here because Lane D has not run yet - -### WS-D — artifact/docs closeout only - -Task id: `i6-d1-closeout` - -Lane D owns: - -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -- `README.md` -- `docs/rust_v1_contract_stack.md` -- `TODOS.md` -- `CHANGELOG.md` -- only-if-truthfully-required closeout refreshes for ECOM/CROSSLIB snapshot, readability, or docs surfaces - -Required outcomes: - -- refresh `BENCH-SERVICE.snapshot.json` from reserved-form to active-form -- author the first `BENCH-SERVICE.readability.review.json` -- align repo-facing docs to shipped truth only - -Required commands: - -```bash -cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE +cargo run -p spec-cli -- export examples/ecommerce/units cargo run -p spec-cli -- status examples/service/units --format json cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- status . --format json ``` -Hard rules: - -- no service semantic edits -- no label edits -- no spec edits -- no test edits -- no fixture edits -- no core behavior edits -- Lane D is artifact/docs closeout only +If I7 admits a bounded new slice into V1, append its exact proving commands +here before closing the milestone. -Acceptance: +## Final Acceptance -- `BENCH-SERVICE` snapshot is active-form -- `BENCH-SERVICE` readability review is current against final projection digest and generated file set -- docs teach the same story the CLI now projects +I7 lands only when all of these are true: -### WS-INT-3 — final acceptance and landing +- the repo has one answer on bounded generics +- the repo has one answer on async/IO +- the checked-in ladder is `I7 -> I8` +- the repo does not imply a checked-in `I9` +- the parent froze the I8 target explicitly +- the proof wall still matches the ratified claim -Task id: `i6-e-final-acceptance` +## Failure Modes -Parent actions: +Bounce the milestone if any of these happen: -- merge Lane D into `codex/i6-int` -- run Gate 3 -- if green, fast-forward the parent branch -- if red, bounce only the owning lane +- "decision packet" becomes code-scope creep +- "ratification" becomes benchmark-mechanics churn +- `V1.1` deferral is treated like failure instead of explicit product truth +- I8 is used as a bucket for unresolved I7 ambiguity -Required commands: +## Immediate Next Move -```bash -cargo run -p spec-cli -- build examples/service/units --output examples/service/src/generated -cargo run -p spec-cli -- test examples/service/units/billing/apply_membership_discount.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/apply_regional_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total_guarded_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/pricing_quote.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_success_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_declined_discount_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy_quote_flow.test.spec -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE -cargo test -p spec-cli rust_v1_service -cargo test -p spec-cli rust_v1_closure -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- status examples/crosslib-app/units --format json -cargo run -p spec-cli -- status . --format json -``` +If starting fresh from this runbook: -Acceptance: - -- Gate 3 is green -- `BENCH-SERVICE` is active, passing, gate-satisfied, and `readability_review_status: "current"` -- rerunning `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` leaves the committed snapshot stable -- service-root `status` and `export` are the proof wall -- repo-root `status . --format json` remains diagnostic inventory with `scope_authority: "inventory_only"` - -## Merge Order - -1. Parent freezes basis and queue. -2. Lane A lands on the parent branch. -3. Parent records `service_freeze_commit`. -4. Parent creates `codex/i6-int` and Lane B from `service_freeze_commit`. -5. Parent creates Lane C only if a real projection bug exists. -6. Lane C, if present, merges into `codex/i6-int` first. -7. Lane B rebases if Lane C changed truth-surface behavior, then merges into `codex/i6-int`. -8. Parent runs Gate 2. -9. Lane D branches from `codex/i6-int` only after Gate 2 passes. -10. Lane D merges into `codex/i6-int`. -11. Parent runs Gate 3. -12. Parent fast-forwards `codex/i6-service-benchmark-activation` to `codex/i6-int`. - -## Context-Control Rules - -- Parent keeps only: - - `PLAN.md` - - `.runs/i6/tasks.json` - - gate records - - the latest narrow diff summary per lane -- Each worker prompt contains only: - - owned files - - forbidden files - - relevant `PLAN.md` excerpt - - required commands - - the recorded `service_freeze_commit` when applicable -- Workers return only: - - changed files - - commands run and exit codes - - blockers - - unresolved assumptions -- Workers do not write `.runs/i6/**`. -- Close each worker immediately after merge or rejection. - -## Tests And Acceptance - -- Lane A acceptance is authored service truth plus refreshed proof. -- Lane B acceptance is direct service-root regression truth plus truthful benchmark fixtures. -- Lane C acceptance is a minimal reproduced-and-fixed read-side bug. -- Lane D acceptance is snapshot, readability, and docs closeout only. -- Final acceptance is the full service benchmark wall plus non-regression across `BENCH-ECOM`, `BENCH-CROSSLIB`, and repo-root inventory semantics. - -The service benchmark wall is closed only when all are true at once: - -- `BENCH-SERVICE` is active rather than reserved. -- the roster is exactly six positive units and three required molecules. -- all six positive units are valid with fresh proof. -- all three required molecules are valid with fresh evidence. -- service-root `status` and `export` both project passing active truth. -- service partial scopes do not launder positive credit. -- `BENCH-SERVICE` readability review is current. -- committed `BENCH-SERVICE.snapshot.json` is stable on rerun. -- `BENCH-ECOM` remains passing/current. -- `BENCH-CROSSLIB` remains passing with zero positive credit. -- repo-root `status . --format json` remains diagnostic inventory and not a zero-exit acceptance gate. - -## Assumptions - -- The primary checkout on `codex/i6-service-benchmark-activation` remains the parent workspace for the full run. -- `spec-cli/tests/fixtures/benchmarks/**` is broad enough that Lane B may own all service-root and repo-root benchmark fixture refresh required by active `BENCH-SERVICE`. -- `.runs/i6/**` is parent-owned run state and is not assumed to be a checked-in deliverable. +1. create `codex/i7-v1-scope-closure` +2. freeze the post-I6 basis into `.runs/i7/basis.json` +3. draft the two scope-decision packets +4. let the parent freeze the decisions before rewriting broader repo-facing docs diff --git a/PLAN.md b/PLAN.md index 5244d26..183e89c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,18 +1,18 @@ -# I6: Rust V1 Service Benchmark Activation Plan +# I7: Rust V1 Scope-Decision Closure Plan Status: **authoritative implementation plan** -Iteration: **I6** -Milestone family: **Rust V1 service-shaped proof closure** +Iteration: **I7** +Milestone family: **Rust V1 scope closure and contract ratification** Implementation readiness: **ready for implementation** -Plan scope: **turn `BENCH-SERVICE` from a reserved benchmark into the active service-shaped proof workload required by `M67`, without widening `M66` support rows, reopening `M68` mechanics, or admitting async/IO, generics, traits, lifetimes, or framework-heavy authored surfaces** +Plan scope: **turn the post-I6 ambiguity into checked-in repo truth by resolving bounded-generics and async/IO admission for honest Rust V1, ratifying the final pre-proof contract line, and freezing I8 as the final proof-run milestone** Base branch: **main** -Working branch: **`codex/i6-service-benchmark-activation`** -Validated on branch: **`codex/i6-service-benchmark-activation`** -Last rewritten: **2026-05-21** +Working branch: **`codex/i7-v1-scope-closure`** +Validated on branch: **`main`** +Last rewritten: **2026-05-22** Supersedes: -- the prior `I5: Rust V1 Supported-Core Closure Plan` +- the prior `I6: Rust V1 Service Benchmark Activation Plan` Locked authority inputs: @@ -21,874 +21,403 @@ Locked authority inputs: - `M66`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-213928.md` - `M67`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-220646.md` - `M68`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-225503.md` -- live repo truth run on branch `codex/i6-service-benchmark-activation` at the then-current `HEAD`: +- live repo truth on `main` after I6 landing: + - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` + - `cargo run -p spec-cli -- export examples/ecommerce/units` + - `cargo run -p spec-cli -- status examples/service/units --format json` + - `cargo run -p spec-cli -- export examples/service/units` - `cargo run -p spec-cli -- status . --format json` - - `git rev-parse --short HEAD` - - `git branch --show-current` Historical context, not authority: - `README.md` - `CHANGELOG.md` - `TODOS.md` -- `benchmarks/snapshots/*.snapshot.json` -- `benchmarks/reviews/*.readability.review.json` -- `spec-cli/tests/fixtures/m19/semantic_falsification_pack/units/billing/**` +- `/home/azureuser/.gstack/projects/atomize-hq-spec/azureuser-main-design-20260522-001238.md` Primary repo surfaces: +- `PLAN.md` +- `ORCH_PLAN.md` +- `docs/rust_v1_contract_stack.md` +- `README.md` +- `DECISIONS.md` +- `CHANGELOG.md` +- `TODOS.md` - `benchmarks/labels.json` -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -- `examples/service/**` and `examples/service/units/**` -- `spec-core/src/benchmark.rs` -- `spec-cli/src/commands.rs` -- `spec-cli/tests/cli.rs` -- `spec-cli/tests/rust_v1_closure.rs` -- `spec-cli/tests/rust_v1_service.rs` -- `spec-cli/tests/fixtures/benchmarks/**` +- `benchmarks/snapshots/*.snapshot.json` +- `benchmarks/reviews/*.readability.review.json` ## Executive Summary -I5 closed the supported-core benchmark wall. +I6 shipped the missing service proof wall. -The remaining Rust V1 proof gap is narrower now than it was when `I5` was first -drafted: the repo already proves the positive narrow-core benchmark -(`BENCH-ECOM`) and the companion-negative wall (`BENCH-CROSSLIB`), while -`BENCH-SERVICE` still exists only as a reserved placeholder with zero cases and -no readability review. +That means the repo is no longer blocked on benchmark mechanics or on whether +`BENCH-SERVICE` is real. The remaining Rust V1 pressure is now narrower and +more dangerous: bounded generics and async/IO are still floating as ambient +"maybe V1, maybe later" scope. -I6 closes exactly that gap and nothing else. +That is how V1 definitions go stale. Not because the code is unclear, but +because the last 10 percent never gets said out loud. -This plan activates `BENCH-SERVICE` as one real single-library benchmark rooted -at `examples/service/units`, backed by six supported positive cases, three -required molecule proofs, a current snapshot, a current readability review, and -a dedicated regression wall. When I6 is complete, the repo can truthfully claim -that the current Rust V1 narrow-core contract is proven by both an example-domain -workload and a service-shaped workload. +I7 closes that gap. + +This milestone does not start by widening Rust support. It starts by forcing the +scope decisions that M65 deliberately left open: + +- does bounded generics join honest Rust V1 in a bounded slice, or defer + explicitly to `V1.1`? +- does Rust V1 stay synchronous-only, or admit a bounded async/IO edge? + +The milestone is complete only when the repo can answer those questions without +guessing, the answers are checked in as authority, and `I8` is frozen as the +final proof-run milestone. ## Current Validated Truth -Observed on branch `codex/i6-service-benchmark-activation` at the then-current -`HEAD` on 2026-05-21: - -- `cargo run -p spec-cli -- status . --format json` reports: - - `schema_version: 4` - - `scope_authority: "inventory_only"` - - `BENCH-ECOM` is `active`, `passing`, and `readability_review_status: - "current"` - - `BENCH-CROSSLIB` is `active`, `passing`, and `positive_credit_cases: 0` - - `BENCH-SERVICE` is `reserved`, `reserved_missing_cases`, and has - `total_cases: 0` -- `cargo run -p spec-cli -- status . --format json` currently exits non-zero - because repo-root inventory includes intentionally non-green fixture surfaces. - For I6, repo-root status remains a diagnostic read surface; acceptance must - assert its JSON fields, not require a zero exit code. -- `benchmarks/labels.json` already declares the future service benchmark root: - - `root: "examples/service/units"` - - `generated_root: "examples/service/src/generated"` - - `readability_scope: "supported_closure"` - - `cases: []` -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` already exists, but only in - reserved form -- `benchmarks/reviews/` contains `BENCH-ECOM.readability.review.json`, but - there is no `BENCH-SERVICE.readability.review.json` -- `examples/service/` does not exist yet -- the closest already-shipped service-shape building blocks are: - - seam examples under `examples/ecommerce/units/pricing/` - - benchmark fixtures under `spec-cli/tests/fixtures/benchmarks/` - - closure regression patterns in `spec-cli/tests/rust_v1_closure.rs` -- service-shaped billing vocabulary already exists in fixture form under - `spec-cli/tests/fixtures/m19/semantic_falsification_pack/units/billing/**` -- `docs/rust_v1_contract_stack.md` still teaches the ladder only through `I4`, - so the implementation ladder needs a closeout update after the benchmark lands - -That means the benchmark system is already capable of carrying the claim. The -missing work is the benchmark payload, proof, review artifact, and regression -coverage. +Observed on `main` after the `0.15.2` landing: + +- `CHANGELOG.md` says Rust V1 now ships an active service benchmark wall. +- `README.md` teaches both benchmark-root proof walls: + - `examples/ecommerce/units` + - `examples/service/units` +- `TODOS.md` marks `M68` complete on `2026-05-18` and `M69` complete on + `2026-05-21`. +- `docs/rust_v1_contract_stack.md` previously stopped the implementation ladder + at `I6`, which made the post-I6 state feel under-authored even though the repo + had moved on. +- `M66` still treats bounded generics as a deferred row and still keeps async/IO + outside the narrow-core contract. +- `BENCH-CROSSLIB` currently exists as the active companion-negative benchmark + wall, not as a dead placeholder. + +In plain English: + +- the proof walls are real +- the narrow-core support line is real +- the final honest V1 scope line is not fully ratified yet + +That is the work. ## Step 0: Scope Challenge ### Premise correction -The problem is not "add broader Rust support." +The problem is not "finish Rust V1 by adding more Rust." The problem is: ```text -turn the reserved BENCH-SERVICE placeholder into one active, -single-library, service-shaped benchmark that proves the already-shipped -Rust V1 narrow core on a real workflow +turn the remaining post-I6 scope pressure into explicit repo-backed truth +so the final V1 claim is honest, bounded, and ready for one last proof run ``` If implementation expands beyond that sentence, it has escaped the milestone. ### Scope verdict -The complete version is still cheap here. +The complete version is still cheap here because the repo already has the proof +machinery and the benchmark walls. -I6 does not need: +I7 does not need: - a new benchmark subsystem -- new benchmark schemas -- cross-library service proof -- async or IO semantics -- new semantic-family promotion -- trait, lifetime, macro, or framework-heavy authored support +- a new command wall +- a new benchmark roster +- speculative `I9` planning +- a broad Rust-support expansion disguised as "just finishing V1" -It needs one truthful service benchmark and the regression wall that keeps it -honest. +It needs: -Scope is accepted as-is. +- one explicit bounded-generics decision +- one explicit async/IO decision +- one ratified plain-English Rust V1 line +- one checked-in handoff to `I8` -### Failure-path definition for I6 +### Default rule for unresolved pressure -`M67` requires a service-shaped failure-path workflow. For I6, that means a -business-path unhappy flow that stays inside the already-supported narrow core. +I7 must not widen support by atmosphere. -Allowed examples: +If a surface is not already proven by current repo truth and cannot be admitted +through one small, bounded, well-explained slice, it defers explicitly to +`V1.1`. -- a declined or absent discount path expressed through the supported plain sum - seam -- a guarded-fee path that clamps invalid business input into the supported - non-negative surface +That rule matters because otherwise "maybe later in this milestone" quietly +turns into "accidentally part of V1." -Disallowed examples: +### Complexity check -- network failure -- database failure -- async cancellation -- retry or fallback orchestration -- framework-managed request lifecycles +Expected primary write scope: -Those remain outside `M66` and outside I6. +- `PLAN.md` +- `ORCH_PLAN.md` +- `docs/rust_v1_contract_stack.md` +- `README.md` +- `DECISIONS.md` +- `CHANGELOG.md` +- `TODOS.md` -### Complexity check +Possible conditional write scope only if I7 explicitly admits a bounded new +slice into V1 and the parent accepts the proof burden: -Expected write scope: +- `spec-core/**` +- `spec-cli/**` +- benchmark artifacts +- example roots needed to prove the admitted slice -- `examples/service/**` -- `benchmarks/labels.json` -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -- `spec-cli/tests/rust_v1_service.rs` -- `spec-cli/tests/fixtures/benchmarks/**` -- targeted `spec-core/src/benchmark.rs` or `spec-cli/src/commands.rs` only if - activation exposes a truthful projection gap -- `README.md`, `docs/rust_v1_contract_stack.md`, `TODOS.md`, and - `CHANGELOG.md` during closeout - -If implementation starts redesigning projection, widening support rows, or -re-arguing `M66`/`M67`/`M68`, stop. That is different scope. +If implementation starts redesigning benchmark mechanics, reopening I3.5, or +silently widening M66 without a forced decision record, stop. That is different +scope. ## What Already Exists -| Sub-problem | Existing owner | I6 action | +| Sub-problem | Current owner | I7 action | | --- | --- | --- | -| positive benchmark mechanics | `benchmarks/labels.json`, `spec-core/src/benchmark.rs`, `spec-cli/src/commands.rs` | reuse unchanged unless service activation exposes a real truth bug | -| benchmark-root truth surfaces | `spec status`, `spec export`, `spec benchmark snapshot` | preserve contract, add service-root coverage | -| positive narrow-core proof shape | `BENCH-ECOM` | reuse as the primary pattern for labels, required molecules, readability, and snapshot assertions | -| companion-negative wall | `BENCH-CROSSLIB` | preserve unchanged and assert non-regression | -| readability review mechanics | `benchmarks/reviews/BENCH-ECOM.readability.review.json` | reuse the same digest and generated-file-set contract for `BENCH-SERVICE` | -| benchmark fixture pattern | `spec-cli/tests/fixtures/benchmarks/**` | add service-specific fixtures alongside the existing benchmark fixture family | -| benchmark regression pattern | `spec-cli/tests/rust_v1_closure.rs` | preserve this suite and add a dedicated `rust_v1_service` suite rather than overloading closure tests | -| service billing vocabulary | `spec-cli/tests/fixtures/m19/semantic_falsification_pack/units/billing/**` | promote only the supported positive shapes into `examples/service/units/billing/**` | -| supported seam patterns | `examples/ecommerce/units/pricing/discount_strategy.unit.spec`, `examples/ecommerce/units/pricing/pricing_quote.unit.spec`, and their molecule tests | adapt the same supported seam shape into the service domain | -| repo-root inventory semantics | current repo-root `status . --format json` behavior | preserve as diagnostic-only inventory with benchmark visibility, not as a proof-success command | +| benchmark-root proof walls | `BENCH-ECOM`, `BENCH-SERVICE`, I3.5 command wall | reuse unchanged as the proof baseline | +| companion-negative boundary | `BENCH-CROSSLIB` | preserve unless I7 records an explicit retirement decision | +| narrow-core Rust support rows | `M66` | reuse as the starting contract, not the final V1 ratification | +| benchmark and readability semantics | `M67`, `M68`, shipped repo mechanics | preserve unchanged unless a real truth bug appears | +| post-I6 milestone ambiguity | stale ladder and missing checked-in follow-on | replace with explicit `I7 -> I8` authority | ## Frozen Decisions -These are locked. I6 implements them and does not reopen them. - -1. **I6 activates `BENCH-SERVICE`.** - - The milestone is not complete while `BENCH-SERVICE` remains - `lifecycle: "reserved"`. - - The benchmark must become an active positive benchmark with real cases, - real required molecules, current proof, and a current readability review. - -2. **I6 does not widen the Rust V1 support contract.** - - `ROW-GENERIC-BOUNDED` stays deferred. - - `ROW-ASYNC-IO` stays deferred. - - no trait-authored, lifetime-heavy, or macro/framework-heavy authored - surfaces enter the benchmark - - no new support rows or interaction claims are admitted - -3. **I6 stays single-library.** - - `BENCH-SERVICE` lands under `examples/service/**` - - no `[libraries]` graph or sibling-library dependency is part of the - service benchmark - - the benchmark proves service shape, not cross-library shape - -4. **I6 uses only already-supported families and seam kinds.** - - supported function families already shipped in the repo are allowed - - supported seam examples already shipped in ecommerce are allowed - - unsupported-function or unsupported-seam cases stay fixture-only, not - positive benchmark cases - -5. **The positive service roster is fixed in this plan.** - - `billing/apply_membership_discount` - - `billing/apply_regional_fee` - - `billing/checkout_net_total` - - `billing/checkout_net_total_guarded_fee` - - `billing/discount_strategy` - - `billing/pricing_quote` - -6. **The required molecule roster is fixed in this plan.** - - `billing/checkout_success_flow` - - `billing/checkout_declined_discount_flow` - - `billing/discount_strategy_quote_flow` - -7. **Source specs remain the only authored truth for service behavior.** - - edit `.unit.spec` and `.test.spec` files by hand - - never hand-edit generated Rust, passports, molecule evidence, or snapshots - - refresh generated Rust with `spec build` - - refresh passports and molecule evidence with `spec test` - - refresh benchmark snapshot with `spec benchmark snapshot` - -8. **`BENCH-SERVICE` keeps the existing writer/reader contract.** - - source specs are authored truth - - passports and molecule evidence are proof writers - - benchmark labels are benchmark-accounting truth - - readability review is human-authored observation truth - - `status`, `export`, and `benchmark snapshot` are read-side projections - -9. **A service case that needs widened support is a failed case, not a widened contract.** - - if any proposed positive case cannot stay inside current supported rows and - interactions, replace the case - - do not widen `M66` to rescue the benchmark - -10. **`BENCH-ECOM` and `BENCH-CROSSLIB` stay green and truthful throughout I6.** - - the service landing may refresh shared fixtures only where the benchmark - roster legitimately changes - - it may not regress the already-shipped benchmark wall - -11. **Minimal diff still wins.** - - prefer promoting existing billing fixture vocabulary plus adapting shipped - seam patterns - - only touch projection code when the active service benchmark exposes a - real truth gap - -12. **Implementation finishes on the current dedicated branch.** - - work continues on `codex/i6-service-benchmark-activation` - - refresh validation truth on that branch before declaring I6 done - -## Intended Service Workload - -I6 authors exactly this service example scaffold: - -- `examples/service/Cargo.toml` -- `examples/service/README.md` -- `examples/service/src/main.rs` -- `examples/service/units/.gitignore` -- `examples/service/units/billing/apply_membership_discount.unit.spec` -- `examples/service/units/billing/apply_regional_fee.unit.spec` -- `examples/service/units/billing/checkout_net_total.unit.spec` -- `examples/service/units/billing/checkout_net_total_guarded_fee.unit.spec` -- `examples/service/units/billing/discount_strategy.unit.spec` -- `examples/service/units/billing/pricing_quote.unit.spec` -- `examples/service/units/billing/checkout_success_flow.test.spec` -- `examples/service/units/billing/checkout_declined_discount_flow.test.spec` -- `examples/service/units/billing/discount_strategy_quote_flow.test.spec` - -Concrete authored intent for each positive case: - -- `billing/apply_membership_discount` - - promoted from the M19 billing fixture pack - - supported arithmetic leaf, monotone-down nonnegative -- `billing/apply_regional_fee` - - promoted from the M19 billing fixture pack - - supported arithmetic leaf, monotone-up -- `billing/checkout_net_total` - - promoted from the M19 billing fixture pack - - supported wrapper pipeline over discount then fee -- `billing/checkout_net_total_guarded_fee` - - new guarded wrapper over the same leaf pair - - uses the already-shipped normalized-required-arg shape to clamp - `regional_rate` at zero -- `billing/discount_strategy` - - new plain `kind:sum` seam adapted from the ecommerce pattern - - variants: `none`, `percentage { rate }`, `fixed_amount { amount }` -- `billing/pricing_quote` - - new plain `kind:data` seam adapted from the ecommerce pattern - - fields: `subtotal`, `membership_rate`, `regional_rate` - - methods expose discounted subtotal and final net total - -Concrete molecule obligations: - -- `billing/checkout_success_flow` - - proves the happy-path multi-unit business flow -- `billing/checkout_declined_discount_flow` - - proves the unhappy-path service flow using supported business semantics - - this is where "failure path" becomes concrete for I6 -- `billing/discount_strategy_quote_flow` - - proves seam usage and seam-to-business coherence - -## I6 Service Closure Matrix - -| Required proof dimension | I6 proof owner | Required proof surface | -| --- | --- | --- | -| real multi-unit business workflow | `billing/checkout_net_total`, `billing/checkout_success_flow` | benchmark-root `status` + `export` + fresh passport/evidence | -| business-path unhappy flow | `billing/checkout_net_total_guarded_fee`, `billing/checkout_declined_discount_flow` | required molecule proof + benchmark gate | -| supported function rows | `billing/apply_membership_discount`, `billing/apply_regional_fee`, `billing/checkout_net_total`, `billing/checkout_net_total_guarded_fee` | fresh passports + benchmark case projections | -| supported seam rows | `billing/discount_strategy`, `billing/pricing_quote` | fresh passports + seam-focused molecule proof | -| supported seam/business interaction | `billing/discount_strategy_quote_flow` | required molecule proof + read-side projection | -| generated readability pressure | `examples/service/src/generated/**` | readability review + generated Rust inspection | -| truthful read-side projection | `BENCH-SERVICE` benchmark-root `status`, `export`, and snapshot | full-scope projections only; partial scopes remain zero-credit | -| benchmark-scoped readability | `benchmarks/reviews/BENCH-SERVICE.readability.review.json` | current digest + current generated-file set | -| preserved prior closure | `BENCH-ECOM`, `BENCH-CROSSLIB` | regression suite + repo-root inventory projection | - -## Architecture +These are locked. I7 implements them and does not reopen them casually. -```text - M66 NARROW-CORE CLAIM (UNCHANGED) - | - +----------------------+----------------------+ - | | - v v - shipped supported-core wall new service-shaped wall - (BENCH-ECOM + BENCH-CROSSLIB) (BENCH-SERVICE) - | - +--------------------------------+-------------------------------+ - | | | - v v v - positive service cases required molecule flows readability review - | | | - v v v - fresh passports fresh evidence current digest + file set - \___________________________ | _________________________/ - \ | / - v v v - benchmark projection core - | - +-----------------------------+-----------------------------+ - | | - v v - service-root status/export (proof wall) BENCH-SERVICE snapshot - | - v - repo can honestly claim service-shaped proof exists - - BENCH-ECOM and BENCH-CROSSLIB remain active and must stay green. - Repo-root status remains inventory_only even after BENCH-SERVICE activates. -``` +1. **I7 owns post-I6 scope closure.** + - I6 is shipped. + - I7 is the first checked-in milestone that answers what remains before + honest Rust V1 can be declared complete. -## Authoring and Proof Contract - -I6 must follow the repo's spec workflow rather than mutating artifacts directly. - -Implementation rules: - -- edit `.unit.spec` and `.test.spec` files, not generated Rust or proof artifacts -- use `cargo run -p spec-cli -- validate --format json` as the - first machine-readable failure surface when a spec is invalid -- use `cargo run -p spec-cli -- build examples/service/units --output examples/service/src/generated` - to regenerate the shared output tree -- use `cargo run -p spec-cli -- test ` to refresh the exact passport or - molecule evidence that changed -- treat `status` as projection and inventory; treat `test` as proof-writer -- author the readability review only after the generated file set and projection - digest are final - -This removes the only dangerous ambiguity in the implementation: which files are -authored truth versus derived truth. - -## Target Outcome - -I6 is done only when all of these are true at the same time: - -1. `BENCH-SERVICE` is no longer reserved. -2. `benchmarks/labels.json` lists exactly the six positive service cases and - three required service molecules from this plan. -3. `examples/service/units` exists as a real single-library authored workload. -4. `cargo run -p spec-cli -- status examples/service/units --format json` - exits successfully and reports: - - `benchmark_id: "BENCH-SERVICE"` - - `lifecycle: "active"` - - `accounting_status: "valid"` - - `benchmark_status: "passing"` - - `gate_status: "satisfied"` - - `readability_review_status: "current"` - - `summary.total_cases: 6` - - `summary.supported_valid_cases: 6` - - `summary.required_molecule_total: 3` - - `summary.required_molecule_status_counts.valid: 3` -5. `cargo run -p spec-cli -- export examples/service/units` projects the same - benchmark truth without inventing any new proof. -6. `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` writes an active - snapshot, not a reserved one. -7. `BENCH-ECOM` still passes with current readability truth. -8. `BENCH-CROSSLIB` still passes with zero positive credit. -9. repo-root `status . --format json` still reports `scope_authority: - "inventory_only"` and includes all three benchmarks, regardless of whether - unrelated inventory rows keep the overall command non-green. -10. closeout docs teach the same story the benchmark wall now proves. - -## Implementation Contract - -Implementation is four phases. Phases 1-3 land the benchmark truth. Phase 4 is -the closeout pass after the service wall is stable. - -### Phase 1: Service scaffold and benchmark activation - -Objective: - -- create the real service example tree and flip `BENCH-SERVICE` from reserved - to active with the exact roster frozen above - -Primary write scope: - -- `examples/service/**` -- `benchmarks/labels.json` +2. **I7 does not widen support by default.** + - bounded generics is not admitted just because it would be nice + - async/IO is not admitted just because it feels adjacent + - admission requires an explicit decision, bounded authored shape, and a + believable proof burden -Required changes: +3. **Deferred is a real outcome.** + - if bounded generics cannot be admitted cleanly, write down `V1.1` + - if async/IO cannot be admitted cleanly, write down `V1.1` + - the repo must prefer honest narrow truth over aspirational ambiguity -- scaffold `examples/service/` to mirror the single-library shape used by - `examples/ecommerce/` -- promote the positive M19 billing function fixtures into - `examples/service/units/billing/**` -- author `billing/checkout_net_total_guarded_fee.unit.spec` -- author `billing/discount_strategy.unit.spec` -- author `billing/pricing_quote.unit.spec` -- switch `BENCH-SERVICE` to `lifecycle: "active"` -- add the six positive cases and the three required molecules -- keep `required_for_v1: true` -- keep `kind: "positive"` and `readability_scope: "supported_closure"` +4. **I7 preserves the shipped proof wall unless it records a scoped change.** + - `BENCH-ECOM` stays active positive proof + - `BENCH-SERVICE` stays active positive proof + - `BENCH-CROSSLIB` stays the active companion-negative wall unless I7 + explicitly retires or demotes it with replacement rationale -Hard rules: +5. **I8 is the final proof-run milestone.** + - I7 ends by freezing what I8 must prove + - the repo should not imply a checked-in `I9` queue unless one is later + authored on purpose -- no `[libraries]` graph in the service example -- no `examples/shared-spec` dependency -- no extra positive service cases in I6 -- do not author the readability review yet +## Required Outputs -Exit criteria: +I7 is done only when all of these exist together: -- `examples/service/units` loads as a real benchmark root -- `BENCH-SERVICE` is active on purpose rather than reserved by placeholder -- the label roster matches this plan exactly +1. a checked-in bounded-generics decision +2. a checked-in async/IO decision +3. a ratified plain-English Rust V1 claim +4. an explicit `I8` handoff that names the proof wall and the deferred `V1.1` + list +5. repo-facing docs that no longer leave post-I6 state to inference -### Phase 2: Service proof wall +The exact artifact locations may vary during execution, but the repo must finish +with a maintainer-readable answer to: -Objective: +- what is in Rust V1? +- what is out of Rust V1? +- what does I8 still need to prove? -- refresh real proof for every positive service case and every required service - molecule so the active benchmark can pass truthfully +## Decision Framework -Primary write scope: +### D2: Bounded Generics -- `examples/service/units/**` -- generated proof artifacts under `examples/service/units/**` -- `examples/service/src/generated/**` +I7 must answer: -Required changes: +- is there a bounded generic slice that is part of the honest Rust V1 claim? -- author and prove `billing/checkout_success_flow.test.spec` -- author and prove `billing/checkout_declined_discount_flow.test.spec` -- author and prove `billing/discount_strategy_quote_flow.test.spec` -- run `spec test` for all six positive service units -- run `spec test` for all three service molecule files -- run `spec build examples/service/units --output examples/service/src/generated` -- confirm every positive service case is `status: "valid"` -- confirm every required service molecule is `status: "valid"` +Admit bounded generics into V1 only if all of these are true: -Hard rules: +- the slice serves the stated solo Rust backend/business-logic user directly +- the authored shape is small and nameable +- the proof burden can be expressed on top of the shipped command wall without + new benchmark mechanics +- the slice does not quietly smuggle in broader trait, lifetime, macro, or + abstraction-heavy support -- if a service case needs unsupported control flow, replace the case -- if a service failure-path idea needs async/IO or fallback, reject the idea -- if a seam shape no longer fits current support truth, adapt it to the shipped - ecommerce seam pattern instead of widening the contract +Otherwise: -Exit criteria: +- keep `ROW-GENERIC-BOUNDED` deferred +- state plainly that generics defer to `V1.1` -- `status examples/service/units --format json` can truthfully pass except for - readability review if Phase 3 has not run yet -- `examples/service/src/generated/**` is final enough for readability review +### D3: Async / IO -### Phase 3: Service benchmark projection, snapshot, readability, and regressions +I7 must answer: -Objective: +- does Rust V1 stay synchronous-only, or is there one bounded async/IO edge + worth admitting? -- make the active service benchmark visible, current, and difficult to regress +Admit async/IO into V1 only if all of these are true: -Primary write scope: +- the slice is small enough to explain in one paragraph +- it does not require framework lifecycle semantics as the new baseline +- it does not collapse the narrow-core claim into "normal backend Rust" +- it can be proven without turning I7 into a mechanics rewrite -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -- `spec-cli/tests/rust_v1_service.rs` -- `spec-cli/tests/fixtures/benchmarks/**` -- targeted `spec-core/src/benchmark.rs` or `spec-cli/src/commands.rs` only if - the active service benchmark exposes a read-side truth bug +Otherwise: -Required changes: +- keep Rust V1 explicitly synchronous-only +- state plainly that async/IO defers to `V1.1` -- refresh `BENCH-SERVICE.snapshot.json` from reserved state to active state -- author the first `BENCH-SERVICE` readability review against the final - projection digest and generated file set -- add a dedicated integration suite for service-benchmark assertions -- refresh or add benchmark fixtures for: - - service benchmark-root `status` - - service benchmark-root `export` - - service benchmark snapshot - - repo-root inventory showing active `BENCH-SERVICE` -- add regressions that prove: - - active `BENCH-SERVICE` becomes non-passing if any required molecule is - missing, stale, or failing - - active `BENCH-SERVICE` becomes non-current when the readability digest or - file set drifts - - service partial scopes emit zero positive credit - - `BENCH-ECOM` and `BENCH-CROSSLIB` remain truthful while service activates +### D4: `BENCH-CROSSLIB` role -Hard rules: +I7 does not treat this as ambient ambiguity anymore. -- do not re-open benchmark schema design -- do not merge service activation by only changing fixtures -- projection code changes are allowed only to fix truthful active-benchmark - behavior +The shipped default is: -Exit criteria: +- `BENCH-CROSSLIB` remains the companion-negative benchmark because it is still + doing useful boundary work for the current claim -- the service benchmark is active, current, snapshot-backed, and regression - protected +I7 may retire or demote it only if the repo records: -### Phase 4: Docs and closeout +- what negative-proof function it was serving +- what replaces that function +- why the final V1 proof run becomes clearer, not weaker -Objective: +## Work Phases -- align repo-facing docs and milestone bookkeeping to the now-active service - benchmark wall +### Phase 1: Basis Freeze -Primary write scope: +Capture the current post-I6 truth without embellishment: -- `README.md` -- `docs/rust_v1_contract_stack.md` -- `TODOS.md` -- `CHANGELOG.md` +- record the current benchmark-root proof commands and outputs +- record the current deferred rows from M66 +- record the currently shipped benchmark roster and roles +- record the current plain-English README and changelog teaching surfaces -Required changes: - -- update `README.md` benchmark roster so `BENCH-SERVICE` is no longer described - as reserved-only -- update `docs/rust_v1_contract_stack.md` so the implementation ladder and repo - note reflect shipped `I5` closure and active `I6` service proof work -- update `TODOS.md` to retire stale post-`M68` closure placeholders and leave - only true follow-on oceans -- record the active service benchmark landing in `CHANGELOG.md` -- if the benchmark wall truly closes the final narrow-core proof gate, say that - explicitly in docs; if not, do not overstate it - -Exit criteria: - -- docs, labels, snapshots, reviews, and CLI truth all teach the same story - -## Implementation Tasks - -Synthesized from the plan's locked decisions and risk surfaces. - -- [ ] **T1 (P1, human: ~2h / CC: ~20min)** — service scaffold — create - `examples/service/` as a single-library benchmark root - - Surfaced by: Phase 1 — `BENCH-SERVICE` cannot activate without a real root - - Files: `examples/service/**`, `benchmarks/labels.json` - - Verify: `cargo run -p spec-cli -- status examples/service/units --format json` - -- [ ] **T2 (P1, human: ~2h / CC: ~20min)** — service function roster — promote - the billing leaf and wrapper cases from the M19 fixture pack - - Surfaced by: Intended Service Workload — supported billing shapes already - exist and should be reused rather than re-invented - - Files: `examples/service/units/billing/*.unit.spec` - - Verify: `cargo run -p spec-cli -- test examples/service/units/billing/apply_membership_discount.unit.spec` - -- [ ] **T3 (P1, human: ~2h / CC: ~20min)** — service seam roster — author the - plain `discount_strategy` and `pricing_quote` seams inside the service - root - - Surfaced by: I6 Service Closure Matrix — service proof must include seam - usage, not functions only - - Files: `examples/service/units/billing/discount_strategy.unit.spec`, - `examples/service/units/billing/pricing_quote.unit.spec` - - Verify: `cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy.unit.spec` - -- [ ] **T4 (P1, human: ~90min / CC: ~15min)** — service molecule gate — author - and prove the three required service flows - - Surfaced by: Frozen Decisions 6 — required molecule roster is fixed - - Files: `examples/service/units/billing/*.test.spec` - - Verify: `cargo run -p spec-cli -- test examples/service/units/billing/checkout_success_flow.test.spec` - -- [ ] **T5 (P1, human: ~60min / CC: ~10min)** — active benchmark labels — - switch `BENCH-SERVICE` to active and populate the frozen case roster - - Surfaced by: Frozen Decisions 1 and 5 - - Files: `benchmarks/labels.json` - - Verify: `cargo run -p spec-cli -- status examples/service/units --format json` - -- [ ] **T6 (P1, human: ~2h / CC: ~20min)** — service regression wall — add a - dedicated `rust_v1_service` integration suite and service benchmark - fixtures - - Surfaced by: Phase 3 — active service truth must be hard to regress - - Files: `spec-cli/tests/rust_v1_service.rs`, - `spec-cli/tests/fixtures/benchmarks/**` - - Verify: `cargo test -p spec-cli rust_v1_service` - -- [ ] **T7 (P2, human: ~45min / CC: ~10min)** — snapshot plus readability — - refresh the active service snapshot and author the first service - readability review - - Surfaced by: Target Outcome 4 and 6 - - Files: `benchmarks/snapshots/BENCH-SERVICE.snapshot.json`, - `benchmarks/reviews/BENCH-SERVICE.readability.review.json` - - Verify: `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` - -- [ ] **T8 (P2, human: ~45min / CC: ~10min)** — docs closeout — update the repo - narrative to reflect active service proof - - Surfaced by: Phase 4 — current docs still teach the pre-I5 ladder - - Files: `README.md`, `docs/rust_v1_contract_stack.md`, `TODOS.md`, - `CHANGELOG.md` - - Verify: manual doc consistency pass against live CLI output - -## Acceptance Commands - -Phase-2 proof refresh: +Deliverable: -```bash -cargo run -p spec-cli -- build examples/service/units --output examples/service/src/generated -cargo run -p spec-cli -- test examples/service/units/billing/apply_membership_discount.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/apply_regional_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_net_total_guarded_fee.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/pricing_quote.unit.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_success_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/checkout_declined_discount_flow.test.spec -cargo run -p spec-cli -- test examples/service/units/billing/discount_strategy_quote_flow.test.spec -``` +- one basis packet that later I7 decisions can cite instead of paraphrasing from + memory -Benchmark proof wall: +### Phase 2: Bounded-Generics Decision Packet -```bash -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE -``` +Do exactly one of: -Non-regression wall: +- admit one bounded generic slice into V1 with explicit authored boundaries and + proof expectations +- defer bounded generics to `V1.1` explicitly -```bash -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- status examples/crosslib-app/units --format json -cargo test -p spec-cli rust_v1_service -cargo test -p spec-cli rust_v1_closure -cargo run -p spec-cli -- status . --format json -``` +Required contents: -Acceptance notes: +- user-value rationale +- authored boundary +- proof burden +- rejected broader expansions -- the benchmark-root commands above must exit successfully when I6 is complete -- the final repo-root `status . --format json` command is diagnostic; it must - preserve `scope_authority: "inventory_only"` and show all three benchmarks, - but it is not required to become a zero-exit proof command for this workspace +### Phase 3: Async / IO Decision Packet -## Test Strategy +Do exactly one of: -### Coverage diagram +- admit one bounded async/IO slice into V1 with explicit authored boundaries and + proof expectations +- keep Rust V1 synchronous-only and defer async/IO to `V1.1` -```text -CODE PATH COVERAGE -=========================== -[+] Service benchmark activation - | - +- benchmarks/labels.json - | +- [CHANGE] BENCH-SERVICE reserved -> active - | +- [CHANGE] add 6 service cases - | \- [CHANGE] add 3 required molecules - | - +- examples/service/units/billing/** - | +- [CHANGE] positive function units - | +- [CHANGE] seam units - | \- [CHANGE] molecule flows - | - +- benchmark projection core - | +- [TEST] active BENCH-SERVICE passes only with full current proof - | \- [TEST] partial service scope never earns positive credit - | - \- snapshot + readability - +- [CHANGE] active BENCH-SERVICE snapshot - \- [TEST] current vs stale review truth - -[+] Non-regression coverage - | - +- BENCH-ECOM - | \- [TEST] remains passing/current - | - +- BENCH-CROSSLIB - | \- [TEST] remains passing/zero-credit - | - \- repo-root inventory - \- [TEST] remains inventory_only while showing active BENCH-SERVICE - -USER FLOW COVERAGE -=========================== -[+] Maintainer service-proof flow - | - +- author service specs - +- validate failing specs with `spec validate --format json` - +- build service generated Rust - +- refresh unit passports - +- refresh molecule evidence - +- run service benchmark-root status/export/snapshot - \- confirm active current BENCH-SERVICE - -[+] Failure states - | - +- active benchmark with missing molecule proof becomes non-passing - +- service review digest drift becomes non-current - +- partial service scope cannot launder positive credit - +- attempted unsupported service shape gets rejected from the positive roster - \- prior benchmarks remain green while service activates -``` +Required contents: -### Mandatory regression tests - -1. `BENCH-SERVICE` becomes non-passing if any required service molecule proof is - missing, stale, or failing. -2. `BENCH-SERVICE` reports current readability only when both - `projection_digest` and `readability_generated_files` match the live - projection. -3. benchmark-root `status` and `export` agree on the active service benchmark - summary and case truth. -4. service namespace and single-file scopes emit zero positive supported credit. -5. repo-root `status . --format json` remains `scope_authority: - "inventory_only"` after service activation. -6. `BENCH-ECOM` still reports passing/current after the service benchmark lands. -7. `BENCH-CROSSLIB` still reports passing with `positive_credit_cases: 0` after - the service benchmark lands. -8. the unhappy-path service flow remains a supported business-path proof, not a - fallback-backed or unsupported workaround. - -### Test files - -Preferred write shape: - -- keep broad benchmark command-wall fixtures in `spec-cli/tests/cli.rs` -- keep I5 closure assertions in `spec-cli/tests/rust_v1_closure.rs` -- add a new dedicated suite `spec-cli/tests/rust_v1_service.rs` for the active - service benchmark -- keep benchmark fixture JSON under `spec-cli/tests/fixtures/benchmarks/` -- if adversarial service fixtures are needed, keep them under a dedicated - service subtree rather than mixing them into unrelated milestone packs - -## Failure Modes Registry - -| Failure mode | Why it matters | Coverage requirement | Critical gap? | -| --- | --- | --- | --- | -| service benchmark goes active with the wrong roster or empty cases | fake V1 closure | label-registry assertions plus full benchmark fixture checks | yes | -| service workload quietly depends on unsupported control flow or widened semantics | benchmark claims more than M66 allows | positive-roster review plus service integration tests | yes | -| unhappy-path flow is implemented as fallback or unsupported trickery | failure-path proof becomes dishonest | explicit unhappy-path molecule proof plus benchmark projection checks | yes | -| service readability review exists but is stale | maintainers think service code was reviewed when read-side truth disagrees | current-vs-stale review regression | yes | -| service partial scope emits positive credit | read-side anti-laundering rule regresses | namespace and single-file zero-credit regressions | yes | -| repo-root acceptance wrongly expects zero exit | implementation appears blocked even when contract is preserved | acceptance notes must treat repo-root status as diagnostic inventory | yes | -| ECOM or CROSSLIB regresses while BENCH-SERVICE activates | I6 breaks shipped proof walls | non-regression suite across all three benchmarks | yes | -| contract-stack doc stays out of sync after I6 lands | repo authority becomes confusing again | closeout doc pass | no | - -## Performance / Complexity Guardrails - -I6 should not introduce a meaningful runtime or maintenance cost increase. - -Guardrails: - -- benchmark projection remains label-driven and linear in loaded benchmark cases -- service example stays single-library and local -- no new benchmark commands are introduced -- no new artifact families are introduced -- no new semantic-family support is introduced -- service failure-path proof must stay in business semantics, not runtime - orchestration -- snapshot and review refresh remain explicit artifact updates, never hidden - side effects of `status` or `export` - -If implementation wants a new registry, a new service runtime, or a broader Rust -admission story, it is solving the wrong problem. - -## Worktree Parallelization Strategy - -I6 has real parallelization opportunities, but they have to be sequenced so the -benchmark roster freezes before tests, snapshots, and docs start depending on -it. - -### Dependency table - -| Step | Modules touched | Depends on | -| --- | --- | --- | -| service scaffold and benchmark activation | `examples/service/`, `benchmarks/` | — | -| service proof refresh | `examples/service/`, `examples/service/src/generated/` | service scaffold and benchmark activation | -| service regression suite | `spec-cli/tests/`, `spec-cli/tests/fixtures/benchmarks/` | service scaffold and benchmark activation | -| projection/core fixes if needed | `spec-core/`, `spec-cli/src/` | service scaffold and benchmark activation | -| snapshot, readability, and docs closeout | `benchmarks/snapshots/`, `benchmarks/reviews/`, `docs/`, repo root docs | service proof refresh, service regression suite, and any projection/core fixes | +- user-value rationale +- authored boundary +- proof burden +- rejected broader expansions + +### Phase 4: Contract Ratification Pass -### Parallel lanes +Update repo-facing authority surfaces so they all teach the same final pre-I8 +story: -Lane A: service scaffold and benchmark activation -> service proof refresh -(sequential, shared `examples/service/`) +- the implementation ladder in `docs/rust_v1_contract_stack.md` +- the active milestone authority in `PLAN.md` +- the active execution runbook in `ORCH_PLAN.md` +- the plain-English Rust V1 line in repo-facing docs +- the explicit deferred `V1.1` list -Lane B: service regression suite (independent after Lane A freezes the roster) +This is the part that prevents the next maintainer from having to reconstruct +our intent from changelog archaeology. -Lane C: projection/core fixes only if activation exposes a real truth bug -(independent from Lane B, but dependent on Lane A) +### Phase 5: I8 Handoff Freeze -Lane D: snapshot, readability, and docs closeout (sequential after A/B/C merge) +Before I7 closes, freeze exactly what I8 must prove: -### Execution order +- which benchmark walls are required +- which commands are authoritative +- which deferred surfaces are explicitly out of scope for V1 +- which documentation line I8 is trying to make honest -1. Launch Lane A first and freeze the benchmark roster. -2. Once Lane A has frozen `BENCH-SERVICE`, launch Lane B in parallel. -3. Launch Lane C only if Lane A or Lane B exposes a real read-side truth bug. -4. Merge A, B, and C. -5. Launch Lane D after that merge, not before. +If I8's target is still fuzzy at the end of I7, I7 is not done. -### Conflict flags +## Acceptance Checklist -- `benchmarks/labels.json` belongs to Lane A only. -- `examples/service/**` belongs to Lane A only. -- `spec-cli/tests/fixtures/benchmarks/**` belongs to Lane B until fixture shape - stabilizes; Lane D should not touch it in parallel. -- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` and - `benchmarks/reviews/BENCH-SERVICE.readability.review.json` belong to Lane D - only. -- if Lane B discovers it needs a label change, queue that change for post-merge - integration instead of editing `benchmarks/labels.json` in parallel. +I7 is complete only when all of these are true at the same time: + +- the repo has one explicit answer on bounded generics +- the repo has one explicit answer on async/IO +- the post-I6 ladder is checked in as `I7 -> I8` +- the repo no longer implies an inferred `I9` +- the proof-authoritative command wall remains the I3.5 wall +- the benchmark roster still matches the ratified claim +- the final plain-English Rust V1 line can be quoted without hand-waving + +## Validation Wall + +Minimum truth commands to run before declaring I7 closed: + +```bash +cargo run -p spec-cli -- status examples/ecommerce/units --format json +cargo run -p spec-cli -- export examples/ecommerce/units +cargo run -p spec-cli -- status examples/service/units --format json +cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- status . --format json +``` + +If I7 admits any new bounded slice into V1, it must add the exact proving +commands required for that slice before the milestone can close. ## What Success Looks Like -After I6: +After I7: -- `BENCH-SERVICE` is an active, passing, current positive benchmark -- the repo proves both example-domain and service-shaped workloads on the same - narrow-core contract -- service proof includes both a happy path and a business-path unhappy flow -- the read-side benchmark wall stays truthful under `status`, `export`, and - snapshot -- the benchmark roster still distinguishes positive proof from companion - negative proof -- the next milestone can debate bounded generics, async/IO, or broader V1.1 - scope from a fully closed proof baseline instead of from a reserved placeholder +- the repo can answer "what is honest Rust V1?" in one paragraph +- the answer names both what is included and what is deferred +- no maintainer needs to infer the post-I6 plan from external notes +- I8 is a proof run, not another scope-discovery exercise ## NOT in scope -- bounded generics admission into V1 -- async or IO admission into V1 -- trait-authored, lifetime-heavy, or macro/framework-heavy authored support -- cross-library service benchmark work -- benchmark schema redesign -- new benchmark commands or a standalone benchmark subsystem -- repository-wide command-scope redesign -- ORCH plan authoring -- broader TypeScript-lane work unrelated to Rust V1 service proof - -## Completion Summary - -- Step 0: Scope challenge accepted as service benchmark activation only -- Architecture review structure: one active service wall defined beside the - shipped ECOM and CROSSLIB walls -- Code quality structure: authored truth, proof writers, and read-side surfaces - are now separated explicitly -- Test review structure: coverage diagram, mandatory regressions, failure modes, - and acceptance semantics are all specified -- Parallelization: 4 lanes total, 2 conditional/parallel lanes, 2 sequential - dependency stages -- Unresolved decisions: none; if new ambiguity appears during implementation, - the implementation is out of plan and should stop for a scope check +- broad generics support +- framework-heavy async support +- reopening benchmark mechanics for style reasons +- reopening I3.5 command-wall semantics +- inventing an `I9` milestone to hide unresolved decisions + +## Open Questions + +These remain open until I7 resolves them explicitly: + +- Is there a bounded generic slice small enough and valuable enough to join V1? +- Is there a bounded async/IO edge small enough and valuable enough to join V1? +- Does `BENCH-CROSSLIB` still earn its benchmark slot once the final V1 line is + ratified? + +## Immediate Next Move + +Treat this document as the current authority. + +The next work is: + +1. freeze the post-I6 basis packet +2. write the bounded-generics decision packet +3. write the async/IO decision packet +4. ratify the final pre-I8 V1 line in checked-in docs + +Do not start "wrapping V1" by broadening code scope before those decisions are +repo-backed truth. diff --git a/docs/rust_v1_contract_stack.md b/docs/rust_v1_contract_stack.md index 8e45b57..578bb2b 100644 --- a/docs/rust_v1_contract_stack.md +++ b/docs/rust_v1_contract_stack.md @@ -1,22 +1,31 @@ # Rust V1 Contract Stack -This document is the repo-facing index for the Rust V1 contract stack and the -frozen I3.5 command wall. +This document is the repo-facing index for the Rust V1 contract stack, the +frozen I3.5 command wall, and the active post-I6 implementation ladder. -It exists to answer two questions quickly: +It exists to answer three questions quickly: - which artifact owns which Rust V1 claim - which command surface is proof-authoritative versus diagnostic only +- where the current I-series wrap to V1 actually stands ## Current Authority -I3.5 freezes the public contract against these in-repo authority artifacts: +I3.5 still freezes the public command contract against these in-repo authority +artifacts: - `.runs/i3_5_authority_alignment/authority-plan.snapshot.md` - `.runs/i3_5_authority_alignment/phase2-freeze.json` -Those files are the maintained repo authority for the current alignment work. -This index deliberately avoids stale local-user planning paths. +Those files remain the maintained repo authority for command-wall semantics. + +The active post-I6 milestone authority now lives in: + +- [`PLAN.md`](../PLAN.md) for the current `I7` milestone definition +- [`ORCH_PLAN.md`](../ORCH_PLAN.md) for the current `I7` execution runbook + +This index deliberately avoids local-user planning paths and records only +checked-in repo authority. ## Historical Stack Ownership @@ -54,7 +63,8 @@ Owns: - benchmark roster and benchmark roles - writer-versus-reader boundaries - readability as an observation surface -- `BENCH-SERVICE` as the required service benchmark that stays within the frozen Rust V1 support boundary +- `BENCH-SERVICE` as the required service benchmark that stays within the + frozen Rust V1 support boundary ### M68: Mechanics-Landing Implementation Contract @@ -83,7 +93,12 @@ Use this routing rule before editing or extending any artifact in the stack: The current ladder is: -- `M65-M68 -> I3 -> I3.5 -> I4 -> I6` +- `M65-M68 -> I3 -> I3.5 -> I4 -> I6 -> I7 -> I8` + +There is now checked-in authority for `I7`. + +There are currently no checked-in authoritative `I9` docs. The active wrap path +to Rust V1 is `I7` then `I8`. ### I3: Benchmark Mechanics Baseline @@ -146,21 +161,76 @@ Primary outcome: Goal: -- activate `BENCH-SERVICE` as a real single-library proof wall without widening M66 support +- activate `BENCH-SERVICE` as a real single-library proof wall without widening + M66 support Frozen outcomes: -- `examples/service/units` is the benchmark root for the service activation slice -- `BENCH-SERVICE` stays frozen to the six authored service units and three required molecule proofs -- service-root `status` and `export` become proof-authoritative alongside the existing ecommerce wall -- `BENCH-SERVICE` closeout includes a current readability review and a stable committed snapshot +- `examples/service/units` is the benchmark root for the service activation + slice +- `BENCH-SERVICE` stays frozen to the six authored service units and three + required molecule proofs +- service-root `status` and `export` become proof-authoritative alongside the + existing ecommerce wall +- `BENCH-SERVICE` closeout includes a current readability review and a stable + committed snapshot Primary outcome: -- the service benchmark is now a shipped, benchmark-root Rust V1 proof surface instead of a reserved placeholder +- the service benchmark is now a shipped, benchmark-root Rust V1 proof surface + instead of a reserved placeholder + +### I7: Rust V1 Scope-Decision Closure + +Goal: + +- resolve the remaining post-I6 scope pressure around bounded generics and + async/IO, then ratify the honest Rust V1 line without widening support by + accident + +Frozen outcomes: + +- the repo explicitly states whether bounded generics join Rust V1 in a bounded + slice or defer to `V1.1` +- the repo explicitly states whether Rust V1 stays synchronous-only or admits a + bounded async/IO edge +- the post-I6 ladder stops implying a planning vacuum after the service + benchmark landed +- `I8` is defined as the final proof-run milestone instead of an inferred + follow-on + +Primary outcome: + +- the repo can answer "what is still in Rust V1?" without guessing or relying + on untracked design notes + +### I8: Rust V1 Final Proof Run + +Goal: + +- run the final end-state proof wall against the explicit V1 contract ratified + by I7 + +Frozen outcomes: + +- one plain-English Rust V1 claim can be published honestly +- the positive and companion-negative benchmark walls still match the ratified + claim +- deferred `V1.1` surfaces are named explicitly instead of remaining ambient + pressure + +Primary outcome: + +- Rust V1 reaches a truthful repo-backed done state instead of a merely shipped + narrow-core checkpoint ## Repo Note -If you need the current execution truth, start with the authority snapshot and -freeze record under `.runs/i3_5_authority_alignment/`, then use this document -as the index that explains what each milestone owns. +If you need the command-wall truth, start with the authority snapshot and +freeze record under `.runs/i3_5_authority_alignment/`. + +If you need the active Rust V1 wrap plan, start with [`PLAN.md`](../PLAN.md) +and [`ORCH_PLAN.md`](../ORCH_PLAN.md). + +Use this document as the index that explains what each milestone owns and why +the current wrap path is `I7 -> I8`, not an inferred `I7 -> I8 -> I9`. From 9bec150c596148f5bd03f048d154c59f137bb0cf Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Fri, 22 May 2026 23:43:28 +0000 Subject: [PATCH 02/24] Refine I7 orchestration plan and execution runbook --- ORCH_PLAN.md | 712 ++++++++++++++++++++++++++++++++++++++++----------- PLAN.md | 489 +++++++++++++++++++---------------- 2 files changed, 837 insertions(+), 364 deletions(-) diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index 64cf630..173e0c2 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,210 +1,442 @@ # I7 Orchestration Plan -Status: **authoritative execution runbook** -Milestone: **I7 Rust V1 scope-decision closure** -Plan authority: **`/home/azureuser/__Active_Code/atomize-hq/spec/PLAN.md`** -Frozen basis: **current `HEAD` of `main` when the run begins** -Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** +Status: **authoritative execution runbook** +Milestone: **I7 Rust V1 scope-decision closure** +Plan authority: **[`PLAN.md`](./PLAN.md)** +Contract-stack authority: **[`docs/rust_v1_contract_stack.md`](./docs/rust_v1_contract_stack.md)** +Frozen command-wall authority: **`.runs/i3_5_authority_alignment/authority-plan.snapshot.md` and `.runs/i3_5_authority_alignment/phase2-freeze.json`** +Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** Last rewritten: **2026-05-22** ## Summary - Execute from `/home/azureuser/__Active_Code/atomize-hq/spec`. -- Treat `PLAN.md` as the only milestone authority. -- Treat the old I6 execution shape as shipped history, not current work. -- Keep the parent agent as the only merge authority and the only final - acceptance authority. +- Treat I7 as a scope-decision closure milestone, not a broad feature milestone. - Keep the critical path local to the parent for: - basis freeze + - packet acceptance - decision freeze - - contract-ratification merge + - authority merge - final I8 handoff +- Parallelize only the two independent decision packets before freeze: + - Lane A: bounded generics + - Lane B: async/IO +- Launch Lane C only after decision freeze. +- Launch Lane D only if the frozen decision admits one newly bounded slice that + still needs code proof before I8. -I7 is lighter than I6. This milestone is mostly about forcing the last scope -decisions into checked-in authority instead of letting them float. +I7 is complete only when the repo can answer, without inference: -## Starting Truth +- what Rust V1 includes +- what Rust V1 defers to `V1.1` +- what exact wall I8 must prove -Observed on `main` after the `0.15.2` landing: +## Milestone Posture -- `BENCH-ECOM` is active positive proof. -- `BENCH-SERVICE` is active positive proof. -- `BENCH-CROSSLIB` is the active companion-negative wall. -- repo-root `status . --format json` remains `inventory_only`. -- repo-root `export .` remains unsupported for this workspace shape. -- `M66` still leaves bounded generics and async/IO outside the narrow-core - shipped baseline. -- the repo now needs a checked-in answer for the final honest Rust V1 line. +- I7 closes post-I6 ambiguity; it does not reopen I3.5 command semantics. +- I3.5 remains frozen authority for the public command wall. +- I8 is the final proof-run milestone. +- There is no implied checked-in `I9`. +- `BENCH-ECOM`, `BENCH-SERVICE`, and the current `BENCH-CROSSLIB` role remain + frozen starting truth unless I7 records an explicit scoped change. +- Narrow, explicit deferral is a successful I7 outcome. + +## Parent And Worker Responsibilities + +### Parent owns + +- the frozen basis commit and basis packet +- all canonical run-state under `.runs/i7/` +- acceptance or rejection of lane packets +- the single decision-freeze record +- the single authority merge +- the final validation wall +- the final I8 handoff record + +### Workers own + +- narrow branch-local implementation or drafting work for their lane only +- short return summaries: changed files, commands run, blockers, assumptions + +### Workers must not own + +- `.runs/i7/**` in the primary checkout +- basis freeze decisions +- final scope admission decisions +- cross-lane reconciliation +- final validation or milestone closeout ## Hard Guards -- Do not reopen I3.5 command-wall semantics. +- Do not change the I3.5 command wall. - Do not redesign benchmark mechanics. -- Do not widen Rust V1 support without an explicit I7 decision packet. -- Do not treat "maybe V1 later" as a substitute for repo-backed truth. -- Keep `I8` as the final proof-run milestone. -- Do not imply a checked-in `I9` queue. +- Do not widen Rust V1 support by prose drift. +- Do not let Lane A or Lane B edit checked-in authority docs directly before + decision freeze. +- Do not merge ratification docs before the parent records the frozen decisions. +- Do not create Lane D unless the parent has already frozen one exact admitted + slice and its proof burden. +- Do not close I7 while any V1 admission still lacks exact proof commands. +- Do not imply a checked-in `I9`. -## Worktree And Branch Plan +## Worktree And Branch Layout -Use the live primary checkout as the parent lane basis: +The parent checkout is the canonical run root. - Parent basis branch: `main` +- Parent working branch: `codex/i7-v1-scope-closure` - Parent workspace: `/home/azureuser/__Active_Code/atomize-hq/spec` +- Worktree root: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7` -Create the I7 worktree root only if parallel lanes are actually needed: +Parent branch creation: ```bash +git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout main +git -C /home/azureuser/__Active_Code/atomize-hq/spec pull --ff-only +git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout -b codex/i7-v1-scope-closure mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7 ``` -Recommended parent working branch for the live milestone after basis freeze: +Worker worktrees after Gate 0 basis freeze: ```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout -b codex/i7-v1-scope-closure +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a -b codex/i7-lane-a-generics codex/i7-v1-scope-closure +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b -b codex/i7-lane-b-async codex/i7-v1-scope-closure ``` -Optional worker lanes after basis freeze: +Worker worktrees after Gate 2 decision freeze: ```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a -b codex/i7-lane-a-generics codex/i7-v1-scope-closure -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b -b codex/i7-lane-b-async codex/i7-v1-scope-closure -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c -b codex/i7-lane-c-ratify codex/i7-v1-scope-closure +DECISION_FREEZE_COMMIT="$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD)" +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c -b codex/i7-lane-c-ratify "$DECISION_FREEZE_COMMIT" +``` + +Conditional worker only if I7 admits a new bounded slice that needs code proof: + +```bash +DECISION_FREEZE_COMMIT="$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD)" +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-d -b codex/i7-lane-d-proof "$DECISION_FREEZE_COMMIT" ``` -If the parent decides that no parallelism is needed, keep I7 single-lane and do -the work locally. +Concurrency policy: + +- maximum concurrent workers before decision freeze: `2` +- maximum concurrent workers after decision freeze: `1` by default +- Lane C and Lane D may overlap only if Lane C is limited to prose that cannot + affect the final validation wall or proof-command list -## Orchestration State +## Canonical Run State -Canonical run state lives under: +The parent-owned source of truth for this milestone is: - `I7_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7` -- basis record: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis.json` -- decision freeze: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/decision-freeze.json` -- final handoff: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/i8-handoff.json` -- session log: `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/session-log.md` +- `tasks.json` at `$I7_RUN_ROOT/tasks.json` +- session log at `$I7_RUN_ROOT/session-log.md` +- basis record at `$I7_RUN_ROOT/basis.json` +- decision packet receipt log at `$I7_RUN_ROOT/packet-receipts.json` +- decision freeze at `$I7_RUN_ROOT/decision-freeze.json` +- authority merge record at `$I7_RUN_ROOT/authority-merge.json` +- final I8 handoff at `$I7_RUN_ROOT/i8-handoff.json` -Suggested task packets: +Parent also preserves raw basis evidence under: -- `i7-a0-basis-freeze` -- `i7-a1-bounded-generics-decision` -- `i7-a2-async-io-decision` -- `i7-b0-contract-ratification` -- `i7-c0-i8-handoff-freeze` +- `$I7_RUN_ROOT/basis/examples-ecommerce.status.json` +- `$I7_RUN_ROOT/basis/examples-ecommerce.export.json` +- `$I7_RUN_ROOT/basis/examples-service.status.json` +- `$I7_RUN_ROOT/basis/examples-service.export.json` +- `$I7_RUN_ROOT/basis/workspace.status.json` -The parent owns `.runs/i7/**`. Workers do not mutate orchestration state -directly. +Recommended parent-owned lane return mirrors: -## Lane Map +- `$I7_RUN_ROOT/lanes/lane-a-bounded-generics.md` +- `$I7_RUN_ROOT/lanes/lane-b-async-io.md` +- `$I7_RUN_ROOT/lanes/lane-c-ratification.md` +- `$I7_RUN_ROOT/lanes/lane-d-proof.md` -| Lane | Branch | Owned write set | Goal | -| --- | --- | --- | --- | -| Parent | `codex/i7-v1-scope-closure` | `.runs/i7/**`, merge decisions, final acceptance | basis freeze, decisions, final authority | -| Lane A | `codex/i7-lane-a-generics` | decision packet drafts, supporting contract docs | bounded-generics recommendation | -| Lane B | `codex/i7-lane-b-async` | decision packet drafts, supporting contract docs | async/IO recommendation | -| Lane C | `codex/i7-lane-c-ratify` | `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, repo-facing closeout docs | ratified repo authority after parent freezes decisions | +Workers may draft branch-local notes, but only the parent writes canonical +files under `.runs/i7/` in the primary checkout. -If I7 later admits a bounded new slice that truly requires code proof, create an -additional lane only after the parent records the decision freeze. Do not front-run -that work. +## Workstream Matrix -## Gate Model +| Workstream | Task id | Owner | Start gate | Owned write set | Deliverable | +| --- | --- | --- | --- | --- | --- | +| WS-PARENT-0 | `task/i7-p0-basis-freeze` | parent | run start | `.runs/i7/**` | frozen basis packet plus raw evidence | +| WS-A | `task/i7-a1-bounded-generics-packet` | worker lane A | Gate 0 green | worker-local packet draft only | one bounded-generics recommendation packet | +| WS-B | `task/i7-b1-async-io-packet` | worker lane B | Gate 0 green | worker-local packet draft only | one async/IO recommendation packet | +| WS-PARENT-1 | `task/i7-p1-packet-acceptance-and-decision-freeze` | parent | WS-A and WS-B returned | `.runs/i7/packet-receipts.json`, `.runs/i7/decision-freeze.json` | frozen milestone posture | +| WS-D | `task/i7-d1-conditional-proof` | worker lane D | Gate 2 admits one bounded slice | only frozen proof surfaces named in `decision-freeze.json` | exact bounded proof or honest blocker | +| WS-C | `task/i7-c1-contract-ratification` | worker lane C | Gate 2 green | checked-in authority docs only | repo-facing I7 ratification diff | +| WS-PARENT-2 | `task/i7-p2-integration-and-closeout` | parent | WS-C done and WS-D done if present | parent branch, `.runs/i7/authority-merge.json`, `.runs/i7/i8-handoff.json` | merged authority, validation rerun, I8 handoff freeze | -### Gate 0: Basis Freeze +## Approval And Gate Model -Owner: parent -Task id: `i7-a0-basis-freeze` +I7 does not need M26-style human approval pauses by default. Its approval model +is parent-gated milestone control. -Advance only when: +- Gate 0 freezes starting truth. +- Gate 1 accepts or rejects the two decision packets. +- Gate 2 freezes the single milestone posture. +- Gate 3 admits or rejects any conditional proof lane. +- Gate 4 merges ratified authority. +- Gate 5 freezes the final I8 handoff and closes the run. -- current branch and commit are recorded in `.runs/i7/basis.json` -- the shipped benchmark walls are recorded -- the current deferred rows from M66 are recorded -- the current command-wall semantics are recorded -- any worker lanes, if used, are created from the frozen basis +If a lane recommends widening Rust V1 beyond the current narrow baseline and +the parent cannot express that widening as one bounded, nameable, provable +slice using existing authority inputs, the parent halts and rejects the packet +back to that lane. -Reopen if: +## Workstream Plan -- basis drifts before decision work starts -- a worker is launched from an unfrozen commit -- the starting claim set is still paraphrase instead of evidence +### WS-PARENT-0 (`task/i7-p0-basis-freeze`) — parent only, sequential -### Gate 1: Decision Freeze +Owned write set: -Owner: parent -Task id: `i7-a3-decision-freeze` +- `.runs/i7/**` -Advance only when: +Required commands: -- the bounded-generics packet recommends exactly one outcome -- the async/IO packet recommends exactly one outcome -- each packet names: - - the user-value rationale - - the authored boundary - - the proof burden - - the rejected broader expansions -- the parent records both decisions in `.runs/i7/decision-freeze.json` +```bash +mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis +git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse --abbrev-ref HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD +cargo run -p spec-cli -- status examples/ecommerce/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-ecommerce.status.json +cargo run -p spec-cli -- export examples/ecommerce/units | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-ecommerce.export.json +cargo run -p spec-cli -- status examples/service/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-service.status.json +cargo run -p spec-cli -- export examples/service/units | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-service.export.json +cargo run -p spec-cli -- status . --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/workspace.status.json +``` -Reopen if: +Acceptance: -- a packet still says "maybe" -- a packet quietly widens into broad support -- the proof burden is missing or unbelievable +- `basis.json` records: + - `basis_branch` + - `basis_commit` + - the frozen I3.5 authority paths + - the five-command validation wall + - current `BENCH-ECOM`, `BENCH-SERVICE`, and `BENCH-CROSSLIB` starting truth + - the current bounded-generics and async/IO deferred posture from milestone + authority +- raw basis command outputs exist under `.runs/i7/basis/` +- Lane A and Lane B fork only from `basis_commit` -### Gate 2: Contract Ratification +### WS-A (`task/i7-a1-bounded-generics-packet`) — worker lane A -Owner: parent -Task id: `i7-b0-contract-ratification` +Branch and worktree: -Advance only when: +- branch: `codex/i7-lane-a-generics` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a` -- `PLAN.md` teaches the frozen I7 story -- `ORCH_PLAN.md` teaches the frozen I7 execution shape -- `docs/rust_v1_contract_stack.md` teaches `I7 -> I8` -- repo-facing docs no longer imply a post-I6 planning vacuum -- the repo names deferred `V1.1` surfaces explicitly if they remain out +Owned write set: -Reopen if: +- worker-local draft packet only: + - `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a/.runs/i7-drafts/task-i7-a1-bounded-generics.md` +- no checked-in authority docs before Gate 2 -- any checked-in doc still teaches the old I6-as-current story -- one doc says a surface is in V1 while another leaves it deferred -- `I8` is still undefined as the final proof milestone +Required commands: -### Gate 3: I8 Handoff Freeze +```bash +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a/.runs/i7-drafts +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a rev-parse HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD +``` -Owner: parent -Task id: `i7-c0-i8-handoff-freeze` +Required packet contents: -Advance only when: +- recommendation: `admit` or `defer` +- user-value rationale +- exact authored boundary +- exact proof burden +- rejected broader expansions +- exact repo surfaces that would change if admitted -- the benchmark walls required for I8 are named explicitly -- the proof-authoritative commands are named explicitly -- the deferred `V1.1` list is named explicitly -- the parent records the target in `.runs/i7/i8-handoff.json` +Acceptance: -Reopen if: +- the packet recommends exactly one outcome +- the boundary fits in one paragraph +- the proof burden is believable on top of the frozen command wall +- the packet does not quietly widen into broad trait, lifetime, macro, or + abstraction-heavy support +- the packet cites the frozen basis commit and the relevant authority inputs +- the packet returns changed files, commands run, blockers, and assumptions in a + parent-readable summary -- I8 still sounds like "we'll know it when we see it" -- the proof wall is not enumerable -- any deferred surface is still ambient instead of named +### WS-B (`task/i7-b1-async-io-packet`) — worker lane B -## Execution Order +Branch and worktree: -1. Freeze basis locally. -2. Draft bounded-generics and async/IO packets. -3. Parent freezes the decisions. -4. Ratify checked-in docs from the frozen decision state. -5. Freeze the I8 handoff. +- branch: `codex/i7-lane-b-async` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b` -Do not reverse steps 3 and 4. The docs should teach frozen decisions, not draft -debate. +Owned write set: -## Validation Wall +- worker-local draft packet only: + - `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b/.runs/i7-drafts/task-i7-b1-async-io-packet.md` +- no checked-in authority docs before Gate 2 -Before final acceptance, rerun the current proof-authoritative commands: +Required commands: ```bash +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b/.runs/i7-drafts +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b rev-parse HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD +``` + +Required packet contents: + +- recommendation: `admit` or `defer` +- user-value rationale +- exact authored boundary +- exact proof burden +- rejected broader expansions +- exact repo surfaces that would change if admitted + +Acceptance: + +- the packet recommends exactly one outcome +- the boundary does not collapse Rust V1 into normal backend Rust +- the proof burden does not require a mechanics rewrite +- the packet does not quietly import framework lifecycle semantics as baseline +- the packet cites the frozen basis commit and the relevant authority inputs +- the packet returns changed files, commands run, blockers, and assumptions in a + parent-readable summary + +### WS-PARENT-1 (`task/i7-p1-packet-acceptance-and-decision-freeze`) — parent only, sequential + +Owned write set: + +- `.runs/i7/packet-receipts.json` +- `.runs/i7/decision-freeze.json` + +Required actions: + +- read Lane A and Lane B returns against frozen basis +- compare both packets against `PLAN.md`, `docs/rust_v1_contract_stack.md`, and + the frozen I3.5 authority snapshot +- reject any packet that still says "maybe" +- reject any packet whose admitted slice cannot be named, bounded, and proven +- reject any pair of packets that disagree on shared milestone truth +- record accepted packet summaries in `packet-receipts.json` +- freeze the single final posture in `decision-freeze.json` + +`decision-freeze.json` must record: + +- `basis_commit` +- bounded-generics outcome +- async/IO outcome +- `BENCH-CROSSLIB` disposition +- whether Lane D exists +- if Lane D exists: + - admitted slice name + - owned code surfaces + - exact required proof commands + +Acceptance: + +- bounded generics has one frozen outcome +- async/IO has one frozen outcome +- there is no remaining ambient scope question for I7 +- Lane D is either explicitly absent or explicitly bounded + +### WS-D (`task/i7-d1-conditional-proof`) — conditional worker lane D + +Start condition: + +- this lane exists only if `decision-freeze.json` admits one new bounded slice + and names exact proof commands +- if `decision-freeze.json` does not name exact proof commands, Lane D does not + start and Gate 2 is still open + +Branch and worktree: + +- branch: `codex/i7-lane-d-proof` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-d` + +Owned write set: + +- only the code, tests, examples, and benchmark surfaces named in + `decision-freeze.json` + +Forbidden touches: + +- `PLAN.md` +- `ORCH_PLAN.md` +- `docs/rust_v1_contract_stack.md` +- `.runs/i7/**` +- any surface outside the admitted bounded slice + +Acceptance: + +- the admitted slice proves only the frozen boundary +- the commands named in `decision-freeze.json` actually run and are sufficient +- no additional broad support is smuggled in as proof scaffolding +- if the proof fails honestly, Lane D returns a blocker instead of widening the + slice or inventing new milestone scope + +### WS-C (`task/i7-c1-contract-ratification`) — worker lane C after Gate 2 + +Branch and worktree: + +- branch: `codex/i7-lane-c-ratify` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c` + +Owned write set: + +- `PLAN.md` +- `ORCH_PLAN.md` +- `docs/rust_v1_contract_stack.md` +- any additional repo-facing closeout docs explicitly required by the frozen + I7 decisions + +Dependencies: + +- must start from the exact `decision_freeze_commit` +- if Lane D exists, Lane C may draft prose early but the parent must not merge + Lane C until Lane D proves the final command list + +Required commands: + +```bash +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c rev-parse HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD +``` + +Acceptance: + +- all checked-in authority surfaces teach the same frozen I7 story +- `I7 -> I8` is explicit everywhere +- deferred `V1.1` surfaces are explicit everywhere +- no doc teaches a checked-in `I9` +- no doc teaches post-I6 ambiguity as current truth +- the final checked-in validation section matches the frozen proof wall exactly + +### WS-PARENT-2 (`task/i7-p2-integration-and-closeout`) — parent only, sequential + +Owned write set: + +- integration branch on `codex/i7-v1-scope-closure` +- `.runs/i7/authority-merge.json` +- `.runs/i7/i8-handoff.json` + +Required actions: + +- verify no authority input drifted since Gate 0 without being consciously + re-frozen +- merge Lane D first if it exists +- merge Lane C after proof-command truth is final +- resolve only straightforward merge mechanics +- bounce disagreements back to the owning lane instead of resolving creatively +- rerun the validation wall +- append any exact conditional proof commands to the validation wall before + closeout +- freeze the final I8 handoff + +Required commands: + +```bash +git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/spec diff --name-only +git -C /home/azureuser/__Active_Code/atomize-hq/spec merge --no-ff codex/i7-lane-d-proof +git -C /home/azureuser/__Active_Code/atomize-hq/spec merge --no-ff codex/i7-lane-c-ratify cargo run -p spec-cli -- status examples/ecommerce/units --format json cargo run -p spec-cli -- export examples/ecommerce/units cargo run -p spec-cli -- status examples/service/units --format json @@ -212,34 +444,220 @@ cargo run -p spec-cli -- export examples/service/units cargo run -p spec-cli -- status . --format json ``` -If I7 admits a bounded new slice into V1, append its exact proving commands -here before closing the milestone. +If Lane D does not exist, skip the `codex/i7-lane-d-proof` merge and run the +existing five-command wall unchanged. -## Final Acceptance +Acceptance: -I7 lands only when all of these are true: +- merged repo truth matches `decision-freeze.json` +- the validation wall is enumerable and current +- the final I8 handoff is a proof run, not another discovery milestone +- `authority-merge.json` records exactly which authority surfaces changed -- the repo has one answer on bounded generics -- the repo has one answer on async/IO -- the checked-in ladder is `I7 -> I8` -- the repo does not imply a checked-in `I9` -- the parent froze the I8 target explicitly -- the proof wall still matches the ratified claim +## Execution Order -## Failure Modes +1. Parent runs Gate 0 basis freeze locally. +2. Lane A and Lane B run in parallel from the frozen basis commit. +3. Parent accepts or rejects both packets, then records Gate 2 decision freeze. +4. If both topics defer cleanly, launch Lane C only. +5. If one bounded slice is admitted and still needs proof, launch Lane D first + or overlap Lane C only on prose that cannot affect proof commands. +6. Parent merges proof work, then ratification work, then reruns the final wall. +7. Parent records the I8 handoff and closes the run. + +## Halt, Bounce, And Reopen Rules + +Parent must halt the run if: + +- the live command wall no longer matches the I3.5 authority snapshot +- `basis_commit` changes after Gate 0 but before Lane A and Lane B fork +- any existing validation-wall command fails unexpectedly during basis capture +- `PLAN.md`, `docs/rust_v1_contract_stack.md`, or the I3.5 authority files + drift under the parent during the run and that drift was not consciously + re-frozen + +Parent must bounce a lane back to its owner if: + +- the packet recommends more than one outcome +- the packet relies on "common Rust expectations" instead of named repo truth +- the packet names no exact proof burden +- Lane A and Lane B disagree on the shared milestone boundary and the conflict + is not settled in `decision-freeze.json` +- Lane C rewrites authority before decision freeze +- Lane C teaches a proof wall that Lane D has not yet proven +- Lane D touches surfaces outside the admitted slice +- any worker edits `.runs/i7/**` in the parent checkout + +Parent must reopen Gate 0 if: + +- basis evidence is missing, stale, or captured from mixed commits +- a worker did not branch from `basis_commit` + +Parent must reopen Gate 2 if: + +- ratified docs conflict with frozen decisions +- Lane D proves a different slice than the one frozen +- new proof work changes the exact command list required for closeout + +Parent must reopen Gate 4 if: + +- the merged tree no longer teaches one coherent `I7 -> I8` story +- the final validation wall omits newly required proof commands +- authority merge reveals a lane conflict that cannot be resolved literally from + `decision-freeze.json` + +## Context-Control Rules + +- Parent keeps live context limited to: + - `PLAN.md` + - `docs/rust_v1_contract_stack.md` + - `.runs/i7/tasks.json` + - `.runs/i7/decision-freeze.json` once it exists + - the active integration diff summary +- Worker prompts must include exactly: + - task id + - lane branch and worktree + - fork commit + - owned write set + - forbidden touch surfaces + - exact relevant `PLAN.md` excerpt + - any frozen proof commands already known + - required return format +- Each worker receives only: + - its owned write set + - the exact relevant `PLAN.md` excerpt + - frozen authority rules + - required commands + - forbidden touch surfaces + - the commit it must fork from +- Each worker returns only: + - changed files + - commands run and exit codes + - blockers + - assumptions +- Parent summarizes worker returns into `.runs/i7/lanes/*.md` instead of + pulling full transcripts into working context. +- Workers do not write canonical `.runs/i7/**` state in the primary checkout. +- Close each worker after merge or rejection. +- If a worker needs broader context than its prompt packet, the parent pauses + and explicitly decides whether to widen that context instead of letting the + lane self-expand. + +## Tests, Validation, And Acceptance + +### Basis Integrity + +- `basis.json` must cite: + - `basis_branch` + - `basis_commit` + - the exact five-command wall + - the current deferred bounded-generics and async/IO posture + - the starting `BENCH-CROSSLIB` role +- raw basis evidence must exist for all five frozen commands +- all worker branches must descend from `basis_commit` + +### Packet Integrity + +- Lane A and Lane B must each return exactly one recommendation +- each packet must name: + - boundary + - rationale + - proof burden + - rejected broader expansions + - repo surfaces impacted if admitted +- neither packet may rely on ambient future work or an implied `I9` + +### Decision Integrity + +- `decision-freeze.json` must be the only frozen milestone posture +- it must record: + - bounded-generics outcome + - async/IO outcome + - `BENCH-CROSSLIB` disposition + - whether Lane D exists + - any exact added proof commands +- if Lane D exists, its allowed write set and trigger condition must be frozen + before that lane starts + +### Ratification Integrity + +- `PLAN.md`, `ORCH_PLAN.md`, and `docs/rust_v1_contract_stack.md` must agree on: + - what is in Rust V1 + - what defers to `V1.1` + - the `I7 -> I8` ladder + - the absence of a checked-in `I9` +- repo-facing closeout docs updated in I7 must teach the same frozen story + +### Conditional Proof Integrity + +- if Lane D does not exist, I7 remains a decision-and-ratification milestone +- if Lane D exists: + - it must prove only the admitted bounded slice + - its commands must be appended before closeout + - failure must return an honest blocker or force decision reopen; it must not + silently widen scope + +### Integration And Closeout Integrity + +- parent merges only after packet acceptance and decision freeze +- parent resolves only straightforward merge mechanics +- lane disagreements on truth are bounced back, not papered over in integration +- `authority-merge.json` and `i8-handoff.json` must reflect the merged truth +- the final run must leave I8 enumerable as a proof wall, not a discovery loop + +The minimum frozen validation wall for I7 closeout is: -Bounce the milestone if any of these happen: +```bash +cargo run -p spec-cli -- status examples/ecommerce/units --format json +cargo run -p spec-cli -- export examples/ecommerce/units +cargo run -p spec-cli -- status examples/service/units --format json +cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- status . --format json +``` -- "decision packet" becomes code-scope creep -- "ratification" becomes benchmark-mechanics churn -- `V1.1` deferral is treated like failure instead of explicit product truth -- I8 is used as a bucket for unresolved I7 ambiguity +Closeout rules: + +- these five commands must remain present at closeout +- if I7 admits a new bounded slice, the parent must append that slice's exact + proof commands before declaring I7 done +- the appended commands must be recorded in: + - `.runs/i7/decision-freeze.json` + - `.runs/i7/i8-handoff.json` + - the final checked-in `ORCH_PLAN.md` validation wall if it changes + +Final acceptance for I7: + +- the repo has one explicit answer on bounded generics +- the repo has one explicit answer on async/IO +- the final Rust V1 line is truthful and bounded +- `BENCH-CROSSLIB` is either preserved or explicitly re-justified +- the command wall still matches the ratified claim +- `I8` is explicitly the final proof-run milestone +- no checked-in doc implies a checked-in `I9` +- `.runs/i7/` contains enough parent-owned evidence to replay how the decision + was frozen + +## Assumptions + +- `main` is the correct frozen basis branch when the run starts. +- The current I3.5 authority snapshot remains the command-wall source of truth. +- I7 is allowed to finish with explicit `V1.1` deferrals and no new code work. +- Lane D is exceptional and should not exist unless the parent can name one + newly admitted bounded slice and its exact proof burden. +- Repo-facing ratification after decision freeze may include docs beyond + `PLAN.md`, `ORCH_PLAN.md`, and `docs/rust_v1_contract_stack.md` only when the + frozen decisions make that update necessary. +- Worker-local packet drafts are disposable execution aids, not checked-in + deliverables. ## Immediate Next Move -If starting fresh from this runbook: +If starting the run fresh: 1. create `codex/i7-v1-scope-closure` -2. freeze the post-I6 basis into `.runs/i7/basis.json` -3. draft the two scope-decision packets -4. let the parent freeze the decisions before rewriting broader repo-facing docs +2. capture Gate 0 basis evidence under `.runs/i7/basis/` +3. fork Lane A and Lane B from `basis_commit` +4. freeze the decisions before any authority doc merge +5. create Lane D only if the frozen decision admits one exact bounded slice +6. ratify the repo-facing I7 story +7. rerun the validation wall and freeze the I8 handoff diff --git a/PLAN.md b/PLAN.md index 183e89c..611867a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -50,56 +50,27 @@ Primary repo surfaces: ## Executive Summary -I6 shipped the missing service proof wall. +I6 shipped the missing service proof wall. That means the remaining Rust V1 risk +is no longer benchmark mechanics. It is scope drift. -That means the repo is no longer blocked on benchmark mechanics or on whether -`BENCH-SERVICE` is real. The remaining Rust V1 pressure is now narrower and -more dangerous: bounded generics and async/IO are still floating as ambient -"maybe V1, maybe later" scope. +I7 exists to convert the last two ambient "maybe V1, maybe later" topics into +explicit repo-backed truth: -That is how V1 definitions go stale. Not because the code is unclear, but -because the last 10 percent never gets said out loud. +- bounded generics +- async/IO -I7 closes that gap. +This milestone is complete only when the repo can answer three questions +without inference: -This milestone does not start by widening Rust support. It starts by forcing the -scope decisions that M65 deliberately left open: +- what is in Rust V1 +- what is out of Rust V1 +- what I8 still has to prove -- does bounded generics join honest Rust V1 in a bounded slice, or defer - explicitly to `V1.1`? -- does Rust V1 stay synchronous-only, or admit a bounded async/IO edge? +The default posture is narrow and honest. I7 does not widen Rust support unless +one bounded slice passes an explicit admission bar and names its proof burden. +Otherwise it defers to `V1.1` in checked-in authority. -The milestone is complete only when the repo can answer those questions without -guessing, the answers are checked in as authority, and `I8` is frozen as the -final proof-run milestone. - -## Current Validated Truth - -Observed on `main` after the `0.15.2` landing: - -- `CHANGELOG.md` says Rust V1 now ships an active service benchmark wall. -- `README.md` teaches both benchmark-root proof walls: - - `examples/ecommerce/units` - - `examples/service/units` -- `TODOS.md` marks `M68` complete on `2026-05-18` and `M69` complete on - `2026-05-21`. -- `docs/rust_v1_contract_stack.md` previously stopped the implementation ladder - at `I6`, which made the post-I6 state feel under-authored even though the repo - had moved on. -- `M66` still treats bounded generics as a deferred row and still keeps async/IO - outside the narrow-core contract. -- `BENCH-CROSSLIB` currently exists as the active companion-negative benchmark - wall, not as a dead placeholder. - -In plain English: - -- the proof walls are real -- the narrow-core support line is real -- the final honest V1 scope line is not fully ratified yet - -That is the work. - -## Step 0: Scope Challenge +## Scope Challenge ### Premise correction @@ -109,124 +80,143 @@ The problem is: ```text turn the remaining post-I6 scope pressure into explicit repo-backed truth -so the final V1 claim is honest, bounded, and ready for one last proof run +so Rust V1 has one honest, bounded claim and I8 is a proof run instead of +another scope-discovery milestone ``` -If implementation expands beyond that sentence, it has escaped the milestone. - -### Scope verdict - -The complete version is still cheap here because the repo already has the proof -machinery and the benchmark walls. +If implementation expands beyond that sentence, it has escaped I7. -I7 does not need: +### Milestone posture -- a new benchmark subsystem -- a new command wall -- a new benchmark roster -- speculative `I9` planning -- a broad Rust-support expansion disguised as "just finishing V1" +I7 is a scope-closure milestone, not a feature-expansion milestone. -It needs: +That means: -- one explicit bounded-generics decision -- one explicit async/IO decision -- one ratified plain-English Rust V1 line -- one checked-in handoff to `I8` +- reuse the shipped I3.5 command wall +- reuse the shipped benchmark mechanics +- reuse the shipped benchmark roster unless a change is explicitly justified +- prefer explicit `V1.1` deferral over ambiguous "probably part of V1" -### Default rule for unresolved pressure +### Operative rule I7 must not widen support by atmosphere. If a surface is not already proven by current repo truth and cannot be admitted -through one small, bounded, well-explained slice, it defers explicitly to -`V1.1`. - -That rule matters because otherwise "maybe later in this milestone" quietly -turns into "accidentally part of V1." - -### Complexity check +through one small, nameable, explainable slice with believable proof burden, it +defers explicitly to `V1.1`. -Expected primary write scope: +## Current Validated Truth -- `PLAN.md` -- `ORCH_PLAN.md` -- `docs/rust_v1_contract_stack.md` -- `README.md` -- `DECISIONS.md` -- `CHANGELOG.md` -- `TODOS.md` +Observed on `main` after the `0.15.2` landing: -Possible conditional write scope only if I7 explicitly admits a bounded new -slice into V1 and the parent accepts the proof burden: +- `BENCH-ECOM` is an active positive proof wall. +- `BENCH-SERVICE` is an active positive proof wall. +- `BENCH-CROSSLIB` is the active companion-negative wall. +- repo-root `status . --format json` remains supported as `inventory_only`. +- repo-root `export .` remains unsupported for this workspace shape. +- `M66` still treats bounded generics as deferred. +- `M66` still keeps async/IO outside the narrow-core shipped baseline. +- `docs/rust_v1_contract_stack.md` now records `I7 -> I8` as the active wrap + path. -- `spec-core/**` -- `spec-cli/**` -- benchmark artifacts -- example roots needed to prove the admitted slice +In plain English: -If implementation starts redesigning benchmark mechanics, reopening I3.5, or -silently widening M66 without a forced decision record, stop. That is different -scope. +- the proof walls are real +- the narrow-core baseline is real +- the final Rust V1 claim still needs explicit ratification ## What Already Exists -| Sub-problem | Current owner | I7 action | +| Sub-problem | Existing owner | I7 action | | --- | --- | --- | | benchmark-root proof walls | `BENCH-ECOM`, `BENCH-SERVICE`, I3.5 command wall | reuse unchanged as the proof baseline | -| companion-negative boundary | `BENCH-CROSSLIB` | preserve unless I7 records an explicit retirement decision | -| narrow-core Rust support rows | `M66` | reuse as the starting contract, not the final V1 ratification | -| benchmark and readability semantics | `M67`, `M68`, shipped repo mechanics | preserve unchanged unless a real truth bug appears | -| post-I6 milestone ambiguity | stale ladder and missing checked-in follow-on | replace with explicit `I7 -> I8` authority | +| companion-negative boundary | `BENCH-CROSSLIB` | preserve unless I7 records an explicit retirement or demotion decision | +| narrow-core Rust support rows | `M66` | reuse as the starting contract, not the final V1 claim | +| benchmark and readability mechanics | `M67`, `M68`, shipped repo logic | preserve unchanged unless a real truth bug is found | +| milestone ladder and ownership | `M65`, `docs/rust_v1_contract_stack.md` | keep `I7 -> I8` explicit and authoritative | +| execution runbook shape | `ORCH_PLAN.md` | align to the frozen I7 decision flow | ## Frozen Decisions -These are locked. I7 implements them and does not reopen them casually. +These decisions are locked for I7 and should not be reopened casually. 1. **I7 owns post-I6 scope closure.** - - I6 is shipped. - - I7 is the first checked-in milestone that answers what remains before - honest Rust V1 can be declared complete. + - I6 is shipped history. + - I7 is the current milestone that resolves what remains before honest Rust + V1 can be declared complete. 2. **I7 does not widen support by default.** - - bounded generics is not admitted just because it would be nice - - async/IO is not admitted just because it feels adjacent - - admission requires an explicit decision, bounded authored shape, and a - believable proof burden + - bounded generics is not admitted because it is adjacent + - async/IO is not admitted because it is common backend behavior + - admission requires an explicit packet, explicit boundary, and explicit + proof burden -3. **Deferred is a real outcome.** - - if bounded generics cannot be admitted cleanly, write down `V1.1` - - if async/IO cannot be admitted cleanly, write down `V1.1` - - the repo must prefer honest narrow truth over aspirational ambiguity +3. **Deferred is a successful outcome when it is explicit.** + - if bounded generics cannot be admitted cleanly, write `V1.1` + - if async/IO cannot be admitted cleanly, write `V1.1` + - honest narrow truth beats aspirational ambiguity 4. **I7 preserves the shipped proof wall unless it records a scoped change.** - `BENCH-ECOM` stays active positive proof - `BENCH-SERVICE` stays active positive proof - `BENCH-CROSSLIB` stays the active companion-negative wall unless I7 - explicitly retires or demotes it with replacement rationale + retires or demotes it with replacement rationale 5. **I8 is the final proof-run milestone.** - I7 ends by freezing what I8 must prove - - the repo should not imply a checked-in `I9` queue unless one is later - authored on purpose + - the repo should not imply a checked-in `I9` queue + +6. **Authority merges happen only after decision freeze.** + - draft reasoning can be parallelized + - checked-in authority updates happen only after the parent freezes the + generics and async/IO decisions ## Required Outputs I7 is done only when all of these exist together: -1. a checked-in bounded-generics decision -2. a checked-in async/IO decision -3. a ratified plain-English Rust V1 claim -4. an explicit `I8` handoff that names the proof wall and the deferred `V1.1` - list +1. a checked-in bounded-generics decision packet +2. a checked-in async/IO decision packet +3. one ratified plain-English Rust V1 claim +4. one explicit `I8` handoff naming: + - required benchmark walls + - authoritative proof commands + - deferred `V1.1` surfaces 5. repo-facing docs that no longer leave post-I6 state to inference -The exact artifact locations may vary during execution, but the repo must finish -with a maintainer-readable answer to: +## Architecture And Decision Flow + +The execution flow for I7 is intentionally narrow: -- what is in Rust V1? -- what is out of Rust V1? -- what does I8 still need to prove? +```text +current shipped truth on main + | + v + basis freeze packet + | + +-------------------+ + | | + v v +bounded-generics async/IO +decision packet decision packet + | | + +---------+---------+ + | + v + parent decision freeze + | + v + authority-surface ratification + | + v + I8 handoff freeze + | + v + final proof-run target +``` + +The parent is the only merge authority. Parallel lanes can draft reasoning, but +they do not publish repo truth independently. ## Decision Framework @@ -234,21 +224,32 @@ with a maintainer-readable answer to: I7 must answer: -- is there a bounded generic slice that is part of the honest Rust V1 claim? +- is there a bounded generics slice small enough, valuable enough, and provable + enough to join honest Rust V1? Admit bounded generics into V1 only if all of these are true: -- the slice serves the stated solo Rust backend/business-logic user directly -- the authored shape is small and nameable -- the proof burden can be expressed on top of the shipped command wall without - new benchmark mechanics +- the slice serves the stated solo Rust backend or business-logic user directly +- the slice is small enough to explain in one paragraph +- the authored boundary is specific and nameable +- the proof burden fits on top of the shipped command wall without new benchmark + mechanics - the slice does not quietly smuggle in broader trait, lifetime, macro, or abstraction-heavy support Otherwise: - keep `ROW-GENERIC-BOUNDED` deferred -- state plainly that generics defer to `V1.1` +- state plainly that bounded generics defer to `V1.1` + +Required contents of the bounded-generics packet: + +- recommendation: admit or defer +- user-value rationale +- authored boundary +- proof burden +- rejected broader expansions +- exact repo surfaces that change if the recommendation is accepted ### D3: Async / IO @@ -269,91 +270,107 @@ Otherwise: - keep Rust V1 explicitly synchronous-only - state plainly that async/IO defers to `V1.1` -### D4: `BENCH-CROSSLIB` role +Required contents of the async/IO packet: + +- recommendation: admit or defer +- user-value rationale +- authored boundary +- proof burden +- rejected broader expansions +- exact repo surfaces that change if the recommendation is accepted -I7 does not treat this as ambient ambiguity anymore. +### D4: `BENCH-CROSSLIB` Role The shipped default is: - `BENCH-CROSSLIB` remains the companion-negative benchmark because it is still - doing useful boundary work for the current claim + performing useful boundary work for the current claim I7 may retire or demote it only if the repo records: - what negative-proof function it was serving - what replaces that function -- why the final V1 proof run becomes clearer, not weaker +- why the final V1 proof run becomes clearer rather than weaker ## Work Phases -### Phase 1: Basis Freeze - -Capture the current post-I6 truth without embellishment: - -- record the current benchmark-root proof commands and outputs -- record the current deferred rows from M66 -- record the currently shipped benchmark roster and roles -- record the current plain-English README and changelog teaching surfaces - -Deliverable: - -- one basis packet that later I7 decisions can cite instead of paraphrasing from - memory - -### Phase 2: Bounded-Generics Decision Packet - -Do exactly one of: - -- admit one bounded generic slice into V1 with explicit authored boundaries and - proof expectations -- defer bounded generics to `V1.1` explicitly - -Required contents: - -- user-value rationale -- authored boundary -- proof burden -- rejected broader expansions +| Phase | Goal | Primary outputs | Exit criteria | +| --- | --- | --- | --- | +| 1. Basis freeze | capture current post-I6 truth without embellishment | basis packet, current benchmark roster, current deferred rows, current command-wall semantics | all later reasoning cites frozen evidence instead of memory | +| 2. Bounded-generics packet | produce exactly one recommendation on bounded generics | bounded-generics decision packet | packet recommends one outcome and names rationale, boundary, proof burden, rejected expansions | +| 3. Async/IO packet | produce exactly one recommendation on async/IO | async/IO decision packet | packet recommends one outcome and names rationale, boundary, proof burden, rejected expansions | +| 4. Decision freeze | convert the two packets into one frozen milestone posture | decision-freeze record | parent records the final outcomes and closes the "maybe" state | +| 5. Contract ratification | align all repo-facing authority surfaces to the frozen decisions | `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, repo-facing docs | no checked-in doc teaches a conflicting pre-I8 story | +| 6. I8 handoff freeze | define the final proof-run target explicitly | handoff record, validation wall, deferred `V1.1` list | I8 is enumerable, bounded, and no longer discovery-shaped | -### Phase 3: Async / IO Decision Packet +## Worktree Parallelization Strategy -Do exactly one of: +This milestone has two independent reasoning workstreams and one dependent +ratification workstream. That means partial parallelization is useful, but only +if the write scopes stay clean. -- admit one bounded async/IO slice into V1 with explicit authored boundaries and - proof expectations -- keep Rust V1 synchronous-only and defer async/IO to `V1.1` +### Dependency table -Required contents: - -- user-value rationale -- authored boundary -- proof burden -- rejected broader expansions - -### Phase 4: Contract Ratification Pass - -Update repo-facing authority surfaces so they all teach the same final pre-I8 -story: - -- the implementation ladder in `docs/rust_v1_contract_stack.md` -- the active milestone authority in `PLAN.md` -- the active execution runbook in `ORCH_PLAN.md` -- the plain-English Rust V1 line in repo-facing docs -- the explicit deferred `V1.1` list - -This is the part that prevents the next maintainer from having to reconstruct -our intent from changelog archaeology. - -### Phase 5: I8 Handoff Freeze - -Before I7 closes, freeze exactly what I8 must prove: +| Step | Modules touched | Depends on | +| --- | --- | --- | +| basis freeze | `.runs/i7/`, benchmark authority surfaces, top-level docs as read-only inputs | — | +| bounded-generics packet | decision packets, contract docs as read-only inputs | basis freeze | +| async/IO packet | decision packets, contract docs as read-only inputs | basis freeze | +| decision freeze | `.runs/i7/`, parent-owned acceptance surfaces | bounded-generics packet, async/IO packet | +| contract ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | decision freeze | +| conditional proof implementation | `spec-core/**`, `spec-cli/**`, `examples/**`, `benchmarks/**` | decision freeze, only if I7 admits a new bounded slice | +| I8 handoff freeze | `.runs/i7/`, `PLAN.md`, `ORCH_PLAN.md`, `docs/` | contract ratification, conditional proof implementation if needed | + +### Parallel lanes + +- `Lane A`: bounded-generics packet + - sequential within lane + - owns only draft decision artifacts until the parent freezes the decision +- `Lane B`: async/IO packet + - sequential within lane + - owns only draft decision artifacts until the parent freezes the decision +- `Lane C`: contract ratification + - must wait for parent decision freeze + - owns checked-in authority surfaces after the decisions are frozen +- `Lane D`: conditional proof implementation + - exists only if I7 admits a bounded new slice into V1 + - must wait for decision freeze because proof burden depends on the final + decision + +### Execution order + +1. Parent freezes the basis locally. +2. Launch `Lane A` and `Lane B` in parallel. +3. Parent freezes the decisions after both packets return. +4. Launch `Lane C` after decision freeze, or do the ratification work locally. +5. Launch `Lane D` only if the frozen decision admits a new slice that needs + code proof. +6. Parent closes with the I8 handoff freeze and final validation wall. + +### Conflict flags + +- `Lane A` and `Lane B` must not both edit checked-in authority docs directly. + If they do, they will collide in `PLAN.md`, `ORCH_PLAN.md`, and `docs/`. +- `Lane C` and `Lane D` can conflict if a newly admitted slice changes both docs + and proving code at the same time. In that case, finish the proof slice first + and ratify the final docs second. +- If no new bounded slice is admitted, `Lane D` does not exist and I7 remains a + docs-and-decision milestone. + +## Failure Modes + +| Failure mode | Consequence | Guard in this plan | +| --- | --- | --- | +| bounded generics is admitted by prose drift instead of a packet | Rust V1 claim widens without proof burden | require a bounded-generics packet and parent decision freeze | +| async/IO is admitted as a vibe instead of a boundary | V1 silently turns into "normal backend Rust" | require an async/IO packet with explicit authored boundary | +| `BENCH-CROSSLIB` is retired without replacement logic | negative-proof boundary weakens and final claim gets softer | require replacement rationale before retirement or demotion | +| docs are ratified before decisions are frozen | checked-in authority teaches draft reasoning as truth | authority merges happen only after decision freeze | +| I8 closes without an enumerated proof wall | final milestone reopens scope discovery | require an explicit I8 handoff naming commands, walls, and deferred surfaces | -- which benchmark walls are required -- which commands are authoritative -- which deferred surfaces are explicitly out of scope for V1 -- which documentation line I8 is trying to make honest +Critical gap test: -If I8's target is still fuzzy at the end of I7, I7 is not done. +- if any proposed admission has no named proof burden, it is not ready for V1 +- if any checked-in doc still implies an inferred `I9`, I7 is not done ## Acceptance Checklist @@ -361,15 +378,16 @@ I7 is complete only when all of these are true at the same time: - the repo has one explicit answer on bounded generics - the repo has one explicit answer on async/IO -- the post-I6 ladder is checked in as `I7 -> I8` -- the repo no longer implies an inferred `I9` -- the proof-authoritative command wall remains the I3.5 wall +- the checked-in ladder is `I7 -> I8` +- the repo does not imply a checked-in `I9` +- the proof-authoritative command wall remains the I3.5 wall unless a scoped + admission adds explicit proving commands - the benchmark roster still matches the ratified claim - the final plain-English Rust V1 line can be quoted without hand-waving ## Validation Wall -Minimum truth commands to run before declaring I7 closed: +Minimum truth commands to rerun before declaring I7 closed: ```bash cargo run -p spec-cli -- status examples/ecommerce/units --format json @@ -379,45 +397,82 @@ cargo run -p spec-cli -- export examples/service/units cargo run -p spec-cli -- status . --format json ``` -If I7 admits any new bounded slice into V1, it must add the exact proving -commands required for that slice before the milestone can close. +If I7 admits any new bounded slice into V1, append the exact proving commands +for that slice before closing the milestone. + +## Implementation Tasks + +These are the minimum build-actionable tasks required to execute I7 cleanly. + +- [ ] **T1 (P1, human: ~30m / CC: ~5m)** — basis freeze — record the current + post-I6 benchmark walls, deferred rows, and command-wall semantics in a cited + basis packet. + - Verify: basis packet exists and later packets cite it instead of restating + memory. +- [ ] **T2 (P1, human: ~45m / CC: ~10m)** — bounded generics — produce one + bounded-generics packet that recommends either bounded admission or explicit + `V1.1` deferral. + - Verify: packet includes rationale, boundary, proof burden, and rejected + broader expansions. +- [ ] **T3 (P1, human: ~45m / CC: ~10m)** — async/IO — produce one async/IO + packet that recommends either bounded admission or explicit synchronous-only + `V1.1` deferral. + - Verify: packet includes rationale, boundary, proof burden, and rejected + broader expansions. +- [ ] **T4 (P1, human: ~30m / CC: ~10m)** — decision freeze — convert the two + packets into one frozen milestone posture before any authority docs are + rewritten. + - Verify: decision-freeze record names one outcome for bounded generics and + one outcome for async/IO. +- [ ] **T5 (P1, human: ~45m / CC: ~15m)** — contract ratification — update + `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, and repo-facing + closeout docs so they all teach the same final pre-I8 story. + - Verify: no checked-in doc still teaches I6 as current or leaves the V1 line + to inference. +- [ ] **T6 (P1, human: ~30m / CC: ~10m)** — I8 handoff freeze — enumerate the + final proof walls, authoritative commands, and deferred `V1.1` list. + - Verify: I8 can be described as a proof run rather than a discovery phase. +- [ ] **T7 (P1, human: ~15m / CC: ~5m)** — closeout validation — rerun the + proof-authoritative commands and add any conditional proof commands required + by an admitted slice. + - Verify: validation wall is current and matches the ratified claim. ## What Success Looks Like After I7: - the repo can answer "what is honest Rust V1?" in one paragraph -- the answer names both what is included and what is deferred +- that answer names both what is included and what is deferred - no maintainer needs to infer the post-I6 plan from external notes -- I8 is a proof run, not another scope-discovery exercise +- I8 is a proof run, not another scope-closure milestone ## NOT in scope - broad generics support + - rationale: this milestone resolves the V1 contract line, not the broader + Rust roadmap - framework-heavy async support -- reopening benchmark mechanics for style reasons + - rationale: that would replace the narrow-core posture instead of clarifying + it +- benchmark-mechanics redesign + - rationale: I3, I3.5, M67, and M68 already own the command wall and truth + mechanics - reopening I3.5 command-wall semantics -- inventing an `I9` milestone to hide unresolved decisions - -## Open Questions - -These remain open until I7 resolves them explicitly: - -- Is there a bounded generic slice small enough and valuable enough to join V1? -- Is there a bounded async/IO edge small enough and valuable enough to join V1? -- Does `BENCH-CROSSLIB` still earn its benchmark slot once the final V1 line is - ratified? + - rationale: I7 consumes that wall as frozen authority +- inventing a checked-in `I9` to hide unresolved I7 decisions + - rationale: I7 must end by freezing I8 as the final proof milestone ## Immediate Next Move -Treat this document as the current authority. - -The next work is: +Execute I7 in this order: 1. freeze the post-I6 basis packet -2. write the bounded-generics decision packet -3. write the async/IO decision packet -4. ratify the final pre-I8 V1 line in checked-in docs - -Do not start "wrapping V1" by broadening code scope before those decisions are -repo-backed truth. +2. draft the bounded-generics packet +3. draft the async/IO packet +4. freeze the decisions +5. ratify repo-facing authority from the frozen decisions +6. freeze the I8 handoff +7. rerun the validation wall + +Do not start "wrapping Rust V1" by widening code scope before the basis and +decision packets exist as repo-backed truth. From dc61b01bb5ca50a08616115db099eab0710d7cf9 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sat, 23 May 2026 00:06:20 +0000 Subject: [PATCH 03/24] Ratify I7 Rust V1 scope closure --- CHANGELOG.md | 10 ++++++++++ DECISIONS.md | 25 +++++++++++++++++++++++++ ORCH_PLAN.md | 11 ++++++++++- PLAN.md | 28 +++++++++++++++++++--------- README.md | 10 +++++++++- TODOS.md | 6 ++++++ docs/rust_v1_contract_stack.md | 15 ++++++++++----- 7 files changed, 89 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 265b63e..78e39ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 0.15.3 - 2026-05-22 + +### Added + +- **I7 now ships a canonical scope-closure packet trail** — `.runs/i7/` records the frozen basis, bounded-generics packet, async/IO packet, decision freeze, authority merge, and explicit I8 handoff for the post-I6 Rust V1 closeout. + +### Changed + +- **Repo-facing docs now ratify one final pre-I8 Rust V1 story** — bounded generics defer to `V1.1`, Rust V1 stays synchronous-only, `BENCH-CROSSLIB` remains the companion negative-proof wall, and I8 inherits the existing five-command proof wall unchanged. + ## 0.15.2 - 2026-05-21 ### Added diff --git a/DECISIONS.md b/DECISIONS.md index 51eab2a..43f2681 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -8,6 +8,31 @@ This file records project-level decisions that should remain stable across the r The ICP for v0.5 is a solo engineer or a small team of roughly 2-5 people who use AI coding assistants every day, work on systems where correctness matters, and want generated code they can trust because it is governed by explicit spec contracts and machine-readable evidence rather than terminal scraping or guesswork. +--- + +## 2026-05-22 — Rust V1 Scope Closure (I7) + +**Decision** + +Rust V1 stays narrow, synchronous, and benchmark-backed for the I8 proof run: + +- bounded generics defer to `V1.1` +- async flows, runtime adapters, and IO-owned boundaries defer to `V1.1` +- `BENCH-CROSSLIB` stays active as companion negative proof and never earns positive supported credit +- `I8` uses the existing five-command proof wall without any new slice-specific commands + +**Rationale** + +- The existing `BENCH-ECOM` and `BENCH-SERVICE` walls already prove one honest narrow-core Rust claim. +- No bounded-generic or async/IO slice could be named in one paragraph with believable proof commands on top of the frozen I3.5 wall. +- Explicit deferral is safer than widening the Rust V1 claim by prose drift or by importing runtime/framework expectations as ambient support. + +**Revisit when** + +- A future milestone can name one bounded generic or async/IO slice, its exact repo surfaces, and the exact added proof commands required to prove it honestly. + +--- + ## 2026-04-02 — Validation Strategy (0.1.x–0.2.x): JSON Schema **Decision** diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index 173e0c2..d068d54 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,11 +1,12 @@ # I7 Orchestration Plan -Status: **authoritative execution runbook** +Status: **authoritative execution runbook and closeout record** Milestone: **I7 Rust V1 scope-decision closure** Plan authority: **[`PLAN.md`](./PLAN.md)** Contract-stack authority: **[`docs/rust_v1_contract_stack.md`](./docs/rust_v1_contract_stack.md)** Frozen command-wall authority: **`.runs/i3_5_authority_alignment/authority-plan.snapshot.md` and `.runs/i3_5_authority_alignment/phase2-freeze.json`** Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** +Frozen basis commit: **`9bec150c596148f5bd03f048d154c59f137bb0cf`** Last rewritten: **2026-05-22** ## Summary @@ -31,6 +32,14 @@ I7 is complete only when the repo can answer, without inference: - what Rust V1 defers to `V1.1` - what exact wall I8 must prove +## Frozen Outcome + +- bounded generics defer to `V1.1` +- Rust V1 remains synchronous-only, so async/IO also defer to `V1.1` +- `BENCH-CROSSLIB` remains the active companion-negative wall +- Lane D is absent because no new bounded Rust V1 slice was admitted +- I8 inherits the existing five-command validation wall unchanged + ## Milestone Posture - I7 closes post-I6 ambiguity; it does not reopen I3.5 command semantics. diff --git a/PLAN.md b/PLAN.md index 611867a..52b6017 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,9 +1,9 @@ # I7: Rust V1 Scope-Decision Closure Plan -Status: **authoritative implementation plan** +Status: **authoritative implementation plan and closeout record** Iteration: **I7** Milestone family: **Rust V1 scope closure and contract ratification** -Implementation readiness: **ready for implementation** +Implementation readiness: **implemented** Plan scope: **turn the post-I6 ambiguity into checked-in repo truth by resolving bounded-generics and async/IO admission for honest Rust V1, ratifying the final pre-proof contract line, and freezing I8 as the final proof-run milestone** Base branch: **main** Working branch: **`codex/i7-v1-scope-closure`** @@ -70,6 +70,16 @@ The default posture is narrow and honest. I7 does not widen Rust support unless one bounded slice passes an explicit admission bar and names its proof burden. Otherwise it defers to `V1.1` in checked-in authority. +## Frozen I7 Outcome + +I7 closed with explicit scope deferral rather than a new admitted Rust slice: + +- bounded generics defer to `V1.1` +- Rust V1 remains synchronous-only, so async/IO also defer to `V1.1` +- `BENCH-CROSSLIB` remains the active companion-negative wall +- Lane D does not exist because no new bounded slice was admitted +- I8 inherits the existing five-command proof wall unchanged + ## Scope Challenge ### Premise correction @@ -404,35 +414,35 @@ for that slice before closing the milestone. These are the minimum build-actionable tasks required to execute I7 cleanly. -- [ ] **T1 (P1, human: ~30m / CC: ~5m)** — basis freeze — record the current +- [x] **T1 (P1, human: ~30m / CC: ~5m)** — basis freeze — record the current post-I6 benchmark walls, deferred rows, and command-wall semantics in a cited basis packet. - Verify: basis packet exists and later packets cite it instead of restating memory. -- [ ] **T2 (P1, human: ~45m / CC: ~10m)** — bounded generics — produce one +- [x] **T2 (P1, human: ~45m / CC: ~10m)** — bounded generics — produce one bounded-generics packet that recommends either bounded admission or explicit `V1.1` deferral. - Verify: packet includes rationale, boundary, proof burden, and rejected broader expansions. -- [ ] **T3 (P1, human: ~45m / CC: ~10m)** — async/IO — produce one async/IO +- [x] **T3 (P1, human: ~45m / CC: ~10m)** — async/IO — produce one async/IO packet that recommends either bounded admission or explicit synchronous-only `V1.1` deferral. - Verify: packet includes rationale, boundary, proof burden, and rejected broader expansions. -- [ ] **T4 (P1, human: ~30m / CC: ~10m)** — decision freeze — convert the two +- [x] **T4 (P1, human: ~30m / CC: ~10m)** — decision freeze — convert the two packets into one frozen milestone posture before any authority docs are rewritten. - Verify: decision-freeze record names one outcome for bounded generics and one outcome for async/IO. -- [ ] **T5 (P1, human: ~45m / CC: ~15m)** — contract ratification — update +- [x] **T5 (P1, human: ~45m / CC: ~15m)** — contract ratification — update `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, and repo-facing closeout docs so they all teach the same final pre-I8 story. - Verify: no checked-in doc still teaches I6 as current or leaves the V1 line to inference. -- [ ] **T6 (P1, human: ~30m / CC: ~10m)** — I8 handoff freeze — enumerate the +- [x] **T6 (P1, human: ~30m / CC: ~10m)** — I8 handoff freeze — enumerate the final proof walls, authoritative commands, and deferred `V1.1` list. - Verify: I8 can be described as a proof run rather than a discovery phase. -- [ ] **T7 (P1, human: ~15m / CC: ~5m)** — closeout validation — rerun the +- [x] **T7 (P1, human: ~15m / CC: ~5m)** — closeout validation — rerun the proof-authoritative commands and add any conditional proof commands required by an admitted slice. - Verify: validation wall is current and matches the ratified claim. diff --git a/README.md b/README.md index 3f25454..b07785e 100644 --- a/README.md +++ b/README.md @@ -52,13 +52,14 @@ spec generate examples/ecommerce/units Rust V1 benchmark mechanics are repo-root read surfaces, not authored spec truth. The authored benchmark registry lives in `benchmarks/labels.json`; readability review anchors live in `benchmarks/reviews/*.readability.review.json`; and `spec benchmark snapshot ` writes derived snapshots under `benchmarks/snapshots/`. -The benchmark-root proof wall after I6 is: +The frozen I8 proof wall after I7 is: ```bash cargo run -p spec-cli -- status examples/ecommerce/units --format json cargo run -p spec-cli -- export examples/ecommerce/units cargo run -p spec-cli -- status examples/service/units --format json cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- status . --format json ``` Repo-root surfaces remain available, but they have a narrower contract: @@ -72,6 +73,13 @@ The seeded roster is: - `BENCH-CROSSLIB`: active companion-negative benchmark rooted at `examples/crosslib-app/units` - `BENCH-SERVICE`: active positive benchmark rooted at `examples/service/units` +The ratified Rust V1 line after I7 is: + +- in: synchronous supported function families plus plain data and sum seams, proven by `BENCH-ECOM` and `BENCH-SERVICE` +- deferred to `V1.1`: bounded generics +- deferred to `V1.1`: async flows, runtime adapters, and IO-owned boundaries +- preserved negative wall: `BENCH-CROSSLIB` stays visible but never counts as positive supported credit + Path scope is intentional: - Exact benchmark-root scopes are the proof-authoritative default and emit full projections with digests, benchmark status, gate status, and readability review state. diff --git a/TODOS.md b/TODOS.md index 2c5553e..91f38ab 100644 --- a/TODOS.md +++ b/TODOS.md @@ -211,3 +211,9 @@ - [x] **M68: Rust V1 benchmark mechanics landing** — Completed main (2026-05-18). The repo now ships `benchmarks/labels.json`, a shared benchmark projection core, additive schema-version-4 `benchmarks[]` in `spec status` / `spec export`, `spec benchmark snapshot `, seeded readability review anchoring for `BENCH-ECOM`, reserved `BENCH-SERVICE` visibility, committed snapshot artifacts, and repo-facing CLI fixtures/docs for the full-versus-partial benchmark contract. - [x] **M69: supported-core closure after mechanics-only landing** — Completed main (2026-05-21). `BENCH-SERVICE` is now an active single-library benchmark rooted at `examples/service/units`, ships the frozen six-unit / three-molecule roster with fresh proof, keeps benchmark accounting read-only over specs/passports/evidence, and still does not widen the Rust V1 support vocabulary beyond the existing supported-core closure. + +## Post-I7 scope closure + +- [x] **I7: Rust V1 scope-decision closure** — Completed main (2026-05-22). The repo now freezes one explicit pre-I8 story: bounded generics defer to `V1.1`, Rust V1 stays synchronous-only so async/IO also defer to `V1.1`, `BENCH-CROSSLIB` remains the companion negative-proof wall, and the final I8 wall stays the existing five-command proof run. + +- [ ] **I8: Rust V1 final proof run** — Rerun the frozen `BENCH-ECOM` plus `BENCH-SERVICE` proof wall, preserve the repo-root inventory-only status contract, and confirm the ratified docs plus benchmark roster still match the bounded Rust V1 claim without widening scope. diff --git a/docs/rust_v1_contract_stack.md b/docs/rust_v1_contract_stack.md index 578bb2b..c06403a 100644 --- a/docs/rust_v1_contract_stack.md +++ b/docs/rust_v1_contract_stack.md @@ -190,14 +190,13 @@ Goal: Frozen outcomes: -- the repo explicitly states whether bounded generics join Rust V1 in a bounded - slice or defer to `V1.1` -- the repo explicitly states whether Rust V1 stays synchronous-only or admits a - bounded async/IO edge +- bounded generics explicitly defer to `V1.1` +- Rust V1 explicitly stays synchronous-only, so async/IO defer to `V1.1` +- `BENCH-CROSSLIB` remains the active companion negative-proof wall - the post-I6 ladder stops implying a planning vacuum after the service benchmark landed - `I8` is defined as the final proof-run milestone instead of an inferred - follow-on + follow-on, and its wall stays the existing five-command proof wall Primary outcome: @@ -216,6 +215,12 @@ Frozen outcomes: - one plain-English Rust V1 claim can be published honestly - the positive and companion-negative benchmark walls still match the ratified claim +- the proof wall remains: + - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` + - `cargo run -p spec-cli -- export examples/ecommerce/units` + - `cargo run -p spec-cli -- status examples/service/units --format json` + - `cargo run -p spec-cli -- export examples/service/units` + - `cargo run -p spec-cli -- status . --format json` - deferred `V1.1` surfaces are named explicitly instead of remaining ambient pressure From 5d849d4f60676c259793d0592b7c1af07431d9a2 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sat, 23 May 2026 00:25:43 +0000 Subject: [PATCH 04/24] Rewrite PLAN for I8 Rust V1 final proof run --- PLAN.md | 716 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 360 insertions(+), 356 deletions(-) diff --git a/PLAN.md b/PLAN.md index 52b6017..02019ab 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,18 +1,18 @@ -# I7: Rust V1 Scope-Decision Closure Plan +# I8: Rust V1 Final Proof Run Plan -Status: **authoritative implementation plan and closeout record** -Iteration: **I7** -Milestone family: **Rust V1 scope closure and contract ratification** -Implementation readiness: **implemented** -Plan scope: **turn the post-I6 ambiguity into checked-in repo truth by resolving bounded-generics and async/IO admission for honest Rust V1, ratifying the final pre-proof contract line, and freezing I8 as the final proof-run milestone** +Status: **authoritative implementation plan** +Iteration: **I8** +Milestone family: **Rust V1 final proof run** +Implementation readiness: **ready to execute** +Plan scope: **rerun the frozen Rust V1 proof wall, preserve repo-root inventory semantics, and close Rust V1 only if the live benchmark surfaces, deferred boundaries, and repo-facing docs still match the ratified narrow-core claim** Base branch: **main** -Working branch: **`codex/i7-v1-scope-closure`** -Validated on branch: **`main`** -Last rewritten: **2026-05-22** +Working branch: **`feat/i8-final-proof-run`** +Validated at commit: **`dc61b01`** +Last rewritten: **2026-05-23** Supersedes: -- the prior `I6: Rust V1 Service Benchmark Activation Plan` +- the prior `I7: Rust V1 Scope-Decision Closure Plan` Locked authority inputs: @@ -21,7 +21,9 @@ Locked authority inputs: - `M66`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-213928.md` - `M67`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-220646.md` - `M68`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-225503.md` -- live repo truth on `main` after I6 landing: +- I7 freeze packet: `.runs/i7/decision-freeze.json` +- I7 handoff packet: `.runs/i7/i8-handoff.json` +- live repo truth on `feat/i8-final-proof-run` at `dc61b01`: - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - `cargo run -p spec-cli -- export examples/ecommerce/units` - `cargo run -p spec-cli -- status examples/service/units --format json` @@ -31,11 +33,12 @@ Locked authority inputs: Historical context, not authority: - `README.md` +- `DECISIONS.md` - `CHANGELOG.md` - `TODOS.md` -- `/home/azureuser/.gstack/projects/atomize-hq-spec/azureuser-main-design-20260522-001238.md` +- `ORCH_PLAN.md` -Primary repo surfaces: +Primary repo surfaces for I8 closeout: - `PLAN.md` - `ORCH_PLAN.md` @@ -47,442 +50,443 @@ Primary repo surfaces: - `benchmarks/labels.json` - `benchmarks/snapshots/*.snapshot.json` - `benchmarks/reviews/*.readability.review.json` +- `.runs/i8/**` ## Executive Summary -I6 shipped the missing service proof wall. That means the remaining Rust V1 risk -is no longer benchmark mechanics. It is scope drift. +I7 already made the hard product decisions. Bounded generics stay deferred to +`V1.1`. Rust V1 stays synchronous-only. `BENCH-CROSSLIB` stays visible as +companion negative proof and never earns positive supported credit. -I7 exists to convert the last two ambient "maybe V1, maybe later" topics into -explicit repo-backed truth: +That means I8 is not a feature milestone. It is the final proof run that must +show the frozen claim still holds on the live repo without widening scope, +changing the command wall, or laundering broad-scope inventory output into +proof. -- bounded generics -- async/IO +I8 is complete only when the repo can say one sentence honestly: -This milestone is complete only when the repo can answer three questions -without inference: +> Rust V1 is the current narrow-core `spec` surface: synchronous supported +> function families plus plain data and sum seams, proven by BENCH-ECOM and +> BENCH-SERVICE, with BENCH-CROSSLIB preserved as companion negative proof. -- what is in Rust V1 -- what is out of Rust V1 -- what I8 still has to prove +## Frozen I8 Contract -The default posture is narrow and honest. I7 does not widen Rust support unless -one bounded slice passes an explicit admission bar and names its proof burden. -Otherwise it defers to `V1.1` in checked-in authority. +These are inherited, locked, and not open for reinterpretation in I8: -## Frozen I7 Outcome +- The milestone ladder is `I7 -> I8`, not `I7 -> I8 -> I9`. +- The Rust V1 claim stays narrow, synchronous, and benchmark-backed. +- Deferred `V1.1` surfaces are exactly: + - bounded generics + - async flows, runtime adapters, and IO-owned boundaries +- `BENCH-ECOM` and `BENCH-SERVICE` are the only positive proof walls required + for the V1 claim. +- `BENCH-CROSSLIB` remains active companion negative proof. +- The authoritative I8 proof wall is exactly five commands: + - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` + - `cargo run -p spec-cli -- export examples/ecommerce/units` + - `cargo run -p spec-cli -- status examples/service/units --format json` + - `cargo run -p spec-cli -- export examples/service/units` + - `cargo run -p spec-cli -- status . --format json` +- No new slice-specific proof commands are admitted in I8. +- Repo-root `export .` remains unsupported for this workspace shape and is not + part of the I8 wall. -I7 closed with explicit scope deferral rather than a new admitted Rust slice: +## Current Validated Truth -- bounded generics defer to `V1.1` -- Rust V1 remains synchronous-only, so async/IO also defer to `V1.1` -- `BENCH-CROSSLIB` remains the active companion-negative wall -- Lane D does not exist because no new bounded slice was admitted -- I8 inherits the existing five-command proof wall unchanged +Observed on `feat/i8-final-proof-run` at `dc61b01`: + +- `status examples/ecommerce/units --format json` passes with: + - `BENCH-ECOM` + - `benchmark_status: passing` + - `gate_status: satisfied` + - `readability_review_status: current` +- `export examples/ecommerce/units` passes with `schema_version: 4` and a full + benchmark projection for `BENCH-ECOM`. +- `status examples/service/units --format json` passes with: + - `BENCH-SERVICE` + - `benchmark_status: passing` + - `gate_status: satisfied` + - `readability_review_status: current` +- `export examples/service/units` passes with `schema_version: 4` and a full + benchmark projection for `BENCH-SERVICE`. +- `status . --format json` exits `1`, which is still correct for I8 because: + - `scope_authority` is `inventory_only` + - `BENCH-CROSSLIB` remains visible with `benchmark_status: passing` and + `gate_status: not_applicable` + - `BENCH-ECOM` and `BENCH-SERVICE` still project as `passing` + - non-green fixture and semantic-family roots remain part of the broad + inventory surface, so repo-root success is not defined as "all roots green" + +This is the exact behavior I8 must preserve unless a real truth bug is found. ## Scope Challenge ### Premise correction -The problem is not "finish Rust V1 by adding more Rust." +I8 is not "final Rust work." -The problem is: +I8 is: ```text -turn the remaining post-I6 scope pressure into explicit repo-backed truth -so Rust V1 has one honest, bounded claim and I8 is a proof run instead of -another scope-discovery milestone +prove that the already-ratified Rust V1 claim still holds on the live repo, +with the same benchmark roster, the same deferred boundaries, and the same +five-command wall ``` -If implementation expands beyond that sentence, it has escaped I7. - -### Milestone posture +If implementation expands beyond that sentence, it has escaped the milestone. -I7 is a scope-closure milestone, not a feature-expansion milestone. +### Minimum change set -That means: +If the frozen wall still passes, the minimum complete I8 diff is: -- reuse the shipped I3.5 command wall -- reuse the shipped benchmark mechanics -- reuse the shipped benchmark roster unless a change is explicitly justified -- prefer explicit `V1.1` deferral over ambiguous "probably part of V1" +1. capture the final proof outputs under `.runs/i8/` +2. verify the benchmark roster and readability anchors still match the claim +3. patch any stale authority docs or release notes that still drift from the + frozen claim +4. record the final closeout packet -### Operative rule +Code changes are conditional only. If a proof-wall command fails or a read-side +surface contradicts the ratified claim, fix only the direct blocker. Do not +turn one failing command into a mechanics redesign. -I7 must not widen support by atmosphere. +### Complexity check -If a surface is not already proven by current repo truth and cannot be admitted -through one small, nameable, explainable slice with believable proof burden, it -defers explicitly to `V1.1`. - -## Current Validated Truth +This should stay boring: -Observed on `main` after the `0.15.2` landing: +- no new benchmark artifacts beyond I8 closeout records under `.runs/i8/` +- no new CLI commands +- no new benchmark kinds +- no new support rows +- no new service/example roots -- `BENCH-ECOM` is an active positive proof wall. -- `BENCH-SERVICE` is an active positive proof wall. -- `BENCH-CROSSLIB` is the active companion-negative wall. -- repo-root `status . --format json` remains supported as `inventory_only`. -- repo-root `export .` remains unsupported for this workspace shape. -- `M66` still treats bounded generics as deferred. -- `M66` still keeps async/IO outside the narrow-core shipped baseline. -- `docs/rust_v1_contract_stack.md` now records `I7 -> I8` as the active wrap - path. +If the work grows into benchmark schema edits, proof-writer redesign, or a new +support boundary, stop. That is not I8. -In plain English: +### Completeness rule -- the proof walls are real -- the narrow-core baseline is real -- the final Rust V1 claim still needs explicit ratification +The complete version is cheap here, so take it: -## What Already Exists - -| Sub-problem | Existing owner | I7 action | -| --- | --- | --- | -| benchmark-root proof walls | `BENCH-ECOM`, `BENCH-SERVICE`, I3.5 command wall | reuse unchanged as the proof baseline | -| companion-negative boundary | `BENCH-CROSSLIB` | preserve unless I7 records an explicit retirement or demotion decision | -| narrow-core Rust support rows | `M66` | reuse as the starting contract, not the final V1 claim | -| benchmark and readability mechanics | `M67`, `M68`, shipped repo logic | preserve unchanged unless a real truth bug is found | -| milestone ladder and ownership | `M65`, `docs/rust_v1_contract_stack.md` | keep `I7 -> I8` explicit and authoritative | -| execution runbook shape | `ORCH_PLAN.md` | align to the frozen I7 decision flow | +- rerun all five commands, not fragments +- inspect full benchmark projections, not just exit codes +- capture raw outputs for closeout +- verify docs and decisions against the actual live outputs -## Frozen Decisions +The shortcut version would be "commands look fine, ship it." I8 should not use +that shortcut. -These decisions are locked for I7 and should not be reopened casually. +### Distribution check -1. **I7 owns post-I6 scope closure.** - - I6 is shipped history. - - I7 is the current milestone that resolves what remains before honest Rust - V1 can be declared complete. +I8 introduces no new distributable artifact. The release surface remains the +existing CLI and checked-in repo authority. Distribution work is unchanged and +out of scope. -2. **I7 does not widen support by default.** - - bounded generics is not admitted because it is adjacent - - async/IO is not admitted because it is common backend behavior - - admission requires an explicit packet, explicit boundary, and explicit - proof burden - -3. **Deferred is a successful outcome when it is explicit.** - - if bounded generics cannot be admitted cleanly, write `V1.1` - - if async/IO cannot be admitted cleanly, write `V1.1` - - honest narrow truth beats aspirational ambiguity - -4. **I7 preserves the shipped proof wall unless it records a scoped change.** - - `BENCH-ECOM` stays active positive proof - - `BENCH-SERVICE` stays active positive proof - - `BENCH-CROSSLIB` stays the active companion-negative wall unless I7 - retires or demotes it with replacement rationale - -5. **I8 is the final proof-run milestone.** - - I7 ends by freezing what I8 must prove - - the repo should not imply a checked-in `I9` queue - -6. **Authority merges happen only after decision freeze.** - - draft reasoning can be parallelized - - checked-in authority updates happen only after the parent freezes the - generics and async/IO decisions - -## Required Outputs - -I7 is done only when all of these exist together: - -1. a checked-in bounded-generics decision packet -2. a checked-in async/IO decision packet -3. one ratified plain-English Rust V1 claim -4. one explicit `I8` handoff naming: - - required benchmark walls - - authoritative proof commands - - deferred `V1.1` surfaces -5. repo-facing docs that no longer leave post-I6 state to inference - -## Architecture And Decision Flow +## What Already Exists -The execution flow for I7 is intentionally narrow: +| Sub-problem | Existing owner | I8 action | +| --- | --- | --- | +| milestone ladder and scope boundary | `M65`, `docs/rust_v1_contract_stack.md`, `.runs/i7/i8-handoff.json` | reuse exactly; do not infer an `I9` | +| supported-vs-deferred Rust claim | `M66`, `DECISIONS.md`, `.runs/i7/decision-freeze.json` | quote and verify, not reinterpret | +| benchmark role split | `M67` | preserve two positive benchmarks plus one companion negative wall | +| benchmark/readability mechanics | `M68`, `benchmarks/labels.json`, snapshots, readability reviews, `status`/`export` schema v4 | verify read-side truth still matches the locked mechanics | +| positive proof walls | `examples/ecommerce/units`, `examples/service/units`, benchmark-aware CLI tests | rerun unchanged | +| companion-negative visibility | `examples/crosslib-app/units`, repo-root `status`, `BENCH-CROSSLIB` labels | verify still visible and still zero-credit | +| repo-root scope semantics | I3.5 authority snapshot plus current CLI behavior | preserve `inventory_only`; do not try to make it globally green | +| scope-closure packet trail | `.runs/i7/**` | treat as frozen inputs, then append I8 evidence separately under `.runs/i8/**` | + +## Final Rust V1 Claim + +This is the exact plain-English line I8 is trying to close, derived from M65, +M66, the I7 freeze, and the current live repo state: + +> Rust V1 is the current narrow-core `spec` surface: synchronous supported +> function families plus plain data and sum seams, proven by BENCH-ECOM and +> BENCH-SERVICE, with BENCH-CROSSLIB preserved as companion negative proof. + +Derived consequences: + +- supported: + - synchronous supported function families + - plain pipeline/wrapper composition inside the shipped supported families + - plain data seams + - plain sum seams + - truthful proof surfaces over those supported rows +- deferred to `V1.1`: + - bounded generics + - async flows, runtime adapters, and IO-owned boundaries +- visible but non-crediting: + - companion negative proof in `BENCH-CROSSLIB` + +## Architecture And Execution Graph + +The execution graph for I8 is intentionally thinner than I7: ```text -current shipped truth on main - | - v - basis freeze packet - | - +-------------------+ - | | - v v -bounded-generics async/IO -decision packet decision packet - | | - +---------+---------+ - | - v - parent decision freeze - | - v - authority-surface ratification - | - v - I8 handoff freeze - | - v - final proof-run target +M65 ladder lock + | + v +M66 support rows + deferred boundaries + | + v +M67 benchmark role split + | + v +M68 read-side mechanics + | + v +I7 decision freeze + I8 handoff + | + v +I8 preflight freeze (.runs/i8/) + | + +------------------------------+ + | | + v v +positive wall rerun repo-root inventory check +(ECOM + SERVICE) (inventory_only + CROSSLIB) + | | + +---------------+--------------+ + | + v + authority drift comparison + | + v + final closeout packet + | + v + truthful Rust V1 done-state claim ``` -The parent is the only merge authority. Parallel lanes can draft reasoning, but -they do not publish repo truth independently. - -## Decision Framework - -### D2: Bounded Generics - -I7 must answer: +Critical dependency rule: -- is there a bounded generics slice small enough, valuable enough, and provable - enough to join honest Rust V1? +- the positive benchmark reruns and the broad inventory rerun may happen in + parallel +- authority updates may not happen until those outputs are captured and + compared against the frozen claim -Admit bounded generics into V1 only if all of these are true: - -- the slice serves the stated solo Rust backend or business-logic user directly -- the slice is small enough to explain in one paragraph -- the authored boundary is specific and nameable -- the proof burden fits on top of the shipped command wall without new benchmark - mechanics -- the slice does not quietly smuggle in broader trait, lifetime, macro, or - abstraction-heavy support - -Otherwise: - -- keep `ROW-GENERIC-BOUNDED` deferred -- state plainly that bounded generics defer to `V1.1` - -Required contents of the bounded-generics packet: - -- recommendation: admit or defer -- user-value rationale -- authored boundary -- proof burden -- rejected broader expansions -- exact repo surfaces that change if the recommendation is accepted - -### D3: Async / IO - -I7 must answer: - -- does Rust V1 stay synchronous-only, or is there one bounded async/IO edge - worth admitting? - -Admit async/IO into V1 only if all of these are true: - -- the slice is small enough to explain in one paragraph -- it does not require framework lifecycle semantics as the new baseline -- it does not collapse the narrow-core claim into "normal backend Rust" -- it can be proven without turning I7 into a mechanics rewrite - -Otherwise: - -- keep Rust V1 explicitly synchronous-only -- state plainly that async/IO defers to `V1.1` - -Required contents of the async/IO packet: +## Work Phases -- recommendation: admit or defer -- user-value rationale -- authored boundary -- proof burden -- rejected broader expansions -- exact repo surfaces that change if the recommendation is accepted +| Phase | Goal | Primary outputs | Exit criteria | +| --- | --- | --- | --- | +| 1. Preflight freeze | create a reproducible I8 run root and cite frozen inputs | `.runs/i8/preflight.json`, copied authority references, command roster | every later step cites frozen inputs instead of memory | +| 2. Positive proof rerun | rerun the two positive benchmark walls unchanged | raw ecommerce/service status+export outputs, summarized proof ledger | both positive benchmarks still pass with satisfied gates and current readability | +| 3. Broad inventory confirmation | rerun repo-root inventory and confirm companion-negative visibility | raw workspace status output, inventory summary | `scope_authority: inventory_only` is preserved and `BENCH-CROSSLIB` remains visible but zero-credit | +| 4. Authority drift ratification | compare live outputs against docs, labels, decisions, and handoff wording | doc diff only if drift exists | all checked-in authority surfaces teach one identical I8 story | +| 5. Final closeout | freeze the final evidence packet and milestone verdict | `.runs/i8/closeout.json`, release-note updates if needed | Rust V1 can be stated honestly with no extra caveats | -### D4: `BENCH-CROSSLIB` Role +## Proof And Coverage Diagram -The shipped default is: +This is the I8 proof surface map. Every row below is part of the milestone and +must be captured in the closeout record. -- `BENCH-CROSSLIB` remains the companion-negative benchmark because it is still - performing useful boundary work for the current claim +```text +PROOF COMMANDS CLAIM SURFACE +[PASS] cargo run -p spec-cli -- status examples/ecommerce/units --format json + -> BENCH-ECOM positive wall + -> benchmark_status: passing + -> gate_status: satisfied + -> readability_review_status: current + +[PASS] cargo run -p spec-cli -- export examples/ecommerce/units + -> schema_version: 4 full projection + -> BENCH-ECOM roster, cases, molecule proofs, readability anchor visible + +[PASS] cargo run -p spec-cli -- status examples/service/units --format json + -> BENCH-SERVICE positive wall + -> benchmark_status: passing + -> gate_status: satisfied + -> readability_review_status: current + +[PASS] cargo run -p spec-cli -- export examples/service/units + -> schema_version: 4 full projection + -> BENCH-SERVICE roster, cases, molecule proofs, readability anchor visible + +[EXPECTED EXIT 1] cargo run -p spec-cli -- status . --format json + -> scope_authority: inventory_only + -> BENCH-CROSSLIB visible as companion_negative_proof + -> BENCH-ECOM and BENCH-SERVICE still passing in broad projection + -> non-green fixture/semantic-family roots stay inventory-only, not proof failure + +COVERAGE: 5/5 commands observed on dc61b01 +CLAIM PATHS: 5/5 covered +GAPS: 0 proof-command gaps +PROCEDURAL REQUIREMENT: archive raw outputs under .runs/i8/ so the final claim stays replayable +``` -I7 may retire or demote it only if the repo records: +Interpretation guard: -- what negative-proof function it was serving -- what replaces that function -- why the final V1 proof run becomes clearer rather than weaker +- A green I8 does not mean repo-root `status .` exits `0`. +- A green I8 means the two positive benchmark walls pass, the broad inventory + surface still tells the truth, and the authority docs match that reality. -## Work Phases +## Acceptance Checklist -| Phase | Goal | Primary outputs | Exit criteria | -| --- | --- | --- | --- | -| 1. Basis freeze | capture current post-I6 truth without embellishment | basis packet, current benchmark roster, current deferred rows, current command-wall semantics | all later reasoning cites frozen evidence instead of memory | -| 2. Bounded-generics packet | produce exactly one recommendation on bounded generics | bounded-generics decision packet | packet recommends one outcome and names rationale, boundary, proof burden, rejected expansions | -| 3. Async/IO packet | produce exactly one recommendation on async/IO | async/IO decision packet | packet recommends one outcome and names rationale, boundary, proof burden, rejected expansions | -| 4. Decision freeze | convert the two packets into one frozen milestone posture | decision-freeze record | parent records the final outcomes and closes the "maybe" state | -| 5. Contract ratification | align all repo-facing authority surfaces to the frozen decisions | `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, repo-facing docs | no checked-in doc teaches a conflicting pre-I8 story | -| 6. I8 handoff freeze | define the final proof-run target explicitly | handoff record, validation wall, deferred `V1.1` list | I8 is enumerable, bounded, and no longer discovery-shaped | +I8 closes only when all of these are true at the same time: + +- the plain-English Rust V1 claim in this file still matches: + - `M65` provisional claim shape + - `M66` supported/deferred boundaries + - `.runs/i7/decision-freeze.json` + - `.runs/i7/i8-handoff.json` +- `BENCH-ECOM` remains `passing` with `gate_status: satisfied` +- `BENCH-SERVICE` remains `passing` with `gate_status: satisfied` +- both positive benchmark exports still emit `schema_version: 4` full + projections +- `BENCH-CROSSLIB` remains visible in repo-root inventory and still counts as + zero positive supported credit +- repo-root `status . --format json` still reports `scope_authority: + inventory_only` +- no new proof commands were added to make the claim work +- no deferred `V1.1` surface was silently promoted +- no checked-in doc implies a new post-I8 discovery milestone +- `.runs/i8/closeout.json` records the exact final verdict and raw command refs ## Worktree Parallelization Strategy -This milestone has two independent reasoning workstreams and one dependent -ratification workstream. That means partial parallelization is useful, but only -if the write scopes stay clean. +I8 has two independent verification workstreams and one dependent ratification +workstream. That means limited parallelization is useful. ### Dependency table | Step | Modules touched | Depends on | | --- | --- | --- | -| basis freeze | `.runs/i7/`, benchmark authority surfaces, top-level docs as read-only inputs | — | -| bounded-generics packet | decision packets, contract docs as read-only inputs | basis freeze | -| async/IO packet | decision packets, contract docs as read-only inputs | basis freeze | -| decision freeze | `.runs/i7/`, parent-owned acceptance surfaces | bounded-generics packet, async/IO packet | -| contract ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | decision freeze | -| conditional proof implementation | `spec-core/**`, `spec-cli/**`, `examples/**`, `benchmarks/**` | decision freeze, only if I7 admits a new bounded slice | -| I8 handoff freeze | `.runs/i7/`, `PLAN.md`, `ORCH_PLAN.md`, `docs/` | contract ratification, conditional proof implementation if needed | +| preflight freeze | `.runs/i8/`, top-level authority docs as read-only inputs | — | +| positive proof rerun | `examples/ecommerce/`, `examples/service/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | +| broad inventory confirmation | repo root inventory surfaces, `examples/crosslib-app/`, `semantic-families/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | +| authority drift ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | positive proof rerun, broad inventory confirmation | +| conditional blocker repair | only the exact modules implicated by a failing command | whichever verification step found the blocker | +| final closeout | `.runs/i8/`, release-note surfaces if changed | authority drift ratification, conditional blocker repair if needed | ### Parallel lanes -- `Lane A`: bounded-generics packet +- `Lane A`: positive proof rerun - sequential within lane - - owns only draft decision artifacts until the parent freezes the decision -- `Lane B`: async/IO packet + - owns only `.runs/i8/evidence/ecommerce-*` and `.runs/i8/evidence/service-*` +- `Lane B`: broad inventory confirmation - sequential within lane - - owns only draft decision artifacts until the parent freezes the decision -- `Lane C`: contract ratification - - must wait for parent decision freeze - - owns checked-in authority surfaces after the decisions are frozen -- `Lane D`: conditional proof implementation - - exists only if I7 admits a bounded new slice into V1 - - must wait for decision freeze because proof burden depends on the final - decision + - owns only `.runs/i8/evidence/workspace-*` and the inventory summary +- `Lane C`: authority drift ratification + - must wait for Lane A and Lane B + - owns checked-in authority docs only +- `Lane D`: conditional blocker repair + - exists only if Lane A or Lane B finds a real truth regression + - must stay bounded to the direct failing surface ### Execution order -1. Parent freezes the basis locally. +1. Parent creates `.runs/i8/` and freezes inputs. 2. Launch `Lane A` and `Lane B` in parallel. -3. Parent freezes the decisions after both packets return. -4. Launch `Lane C` after decision freeze, or do the ratification work locally. -5. Launch `Lane D` only if the frozen decision admits a new slice that needs - code proof. -6. Parent closes with the I8 handoff freeze and final validation wall. +3. Parent compares both outputs against the frozen claim. +4. Launch `Lane C` only if the claim and outputs are now fully understood. +5. Launch `Lane D` only if a real blocker must be repaired before closeout. +6. Parent writes the final I8 closeout packet. ### Conflict flags -- `Lane A` and `Lane B` must not both edit checked-in authority docs directly. - If they do, they will collide in `PLAN.md`, `ORCH_PLAN.md`, and `docs/`. -- `Lane C` and `Lane D` can conflict if a newly admitted slice changes both docs - and proving code at the same time. In that case, finish the proof slice first - and ratify the final docs second. -- If no new bounded slice is admitted, `Lane D` does not exist and I7 remains a - docs-and-decision milestone. +- `Lane A` and `Lane B` must not both write the same summary file under + `.runs/i8/`. Split evidence files by benchmark versus workspace. +- `Lane C` must not start before the workspace inventory interpretation is + settled. Otherwise it risks ratifying the wrong meaning of repo-root `status`. +- `Lane D` must not turn a local truth bug into a broad mechanics rewrite. If + the repair touches benchmark schema, proof writers, or support rows beyond the + failing surface, stop and escalate. ## Failure Modes | Failure mode | Consequence | Guard in this plan | | --- | --- | --- | -| bounded generics is admitted by prose drift instead of a packet | Rust V1 claim widens without proof burden | require a bounded-generics packet and parent decision freeze | -| async/IO is admitted as a vibe instead of a boundary | V1 silently turns into "normal backend Rust" | require an async/IO packet with explicit authored boundary | -| `BENCH-CROSSLIB` is retired without replacement logic | negative-proof boundary weakens and final claim gets softer | require replacement rationale before retirement or demotion | -| docs are ratified before decisions are frozen | checked-in authority teaches draft reasoning as truth | authority merges happen only after decision freeze | -| I8 closes without an enumerated proof wall | final milestone reopens scope discovery | require an explicit I8 handoff naming commands, walls, and deferred surfaces | +| repo-root `status .` exit `1` is misread as I8 failure | a truthful broad inventory surface blocks the release for the wrong reason | require `scope_authority: inventory_only` and benchmark roster interpretation, not exit-code-only reasoning | +| `BENCH-CROSSLIB` starts counting as positive credit | the V1 claim widens by read-surface drift | verify companion-negative classification and zero-credit behavior in repo-root inventory | +| docs still describe I7 as current or imply I9 | the milestone ladder becomes ambiguous again | ratify all repo-facing authority surfaces only after live output comparison | +| positive status passes but export drifts | downstream machine consumers see a different truth than human readers | require both `status` and `export` for each positive benchmark | +| readability anchors go stale while benchmark status stays green | the claim overstates reviewability of emitted Rust | require `readability_review_status: current` for both positive benchmarks | +| a blocker fix adds new proof commands or support rows | I8 quietly becomes another scope milestone | hard-stop any repair that changes the five-command wall or deferred boundaries | +| broad inventory roots are trimmed to force green | the repo loses honest visibility into deferred and fixture surfaces | preserve the current repo-root inventory behavior exactly | Critical gap test: -- if any proposed admission has no named proof burden, it is not ready for V1 -- if any checked-in doc still implies an inferred `I9`, I7 is not done - -## Acceptance Checklist - -I7 is complete only when all of these are true at the same time: +- if any positive benchmark loses `passing` or `satisfied`, I8 is not done +- if any doc still needs caveats not present in the frozen claim, I8 is not done +- if the only way to "pass" is to reinterpret `inventory_only` as proof, I8 is + not done -- the repo has one explicit answer on bounded generics -- the repo has one explicit answer on async/IO -- the checked-in ladder is `I7 -> I8` -- the repo does not imply a checked-in `I9` -- the proof-authoritative command wall remains the I3.5 wall unless a scoped - admission adds explicit proving commands -- the benchmark roster still matches the ratified claim -- the final plain-English Rust V1 line can be quoted without hand-waving +## Performance And Operational Review -## Validation Wall +I8 should not introduce any new runtime or infrastructure cost. The only +meaningful operational concerns are: -Minimum truth commands to rerun before declaring I7 closed: +- broad repo-root `status` is a heavier scan than benchmark-root commands, so do + not rerun it unnecessarily during closeout +- full-scope `status` and `export` must be captured as raw outputs once per + closeout run so later doc review does not depend on rerunning commands by + memory +- no new caching, queueing, parallel proof writer, or benchmark registry work is + justified here -```bash -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- export examples/ecommerce/units -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- status . --format json -``` +Boring-by-default rule: -If I7 admits any new bounded slice into V1, append the exact proving commands -for that slice before closing the milestone. +- if the wall passes, ship the closeout with evidence +- if the wall fails, repair the narrow blocker only ## Implementation Tasks -These are the minimum build-actionable tasks required to execute I7 cleanly. - -- [x] **T1 (P1, human: ~30m / CC: ~5m)** — basis freeze — record the current - post-I6 benchmark walls, deferred rows, and command-wall semantics in a cited - basis packet. - - Verify: basis packet exists and later packets cite it instead of restating - memory. -- [x] **T2 (P1, human: ~45m / CC: ~10m)** — bounded generics — produce one - bounded-generics packet that recommends either bounded admission or explicit - `V1.1` deferral. - - Verify: packet includes rationale, boundary, proof burden, and rejected - broader expansions. -- [x] **T3 (P1, human: ~45m / CC: ~10m)** — async/IO — produce one async/IO - packet that recommends either bounded admission or explicit synchronous-only - `V1.1` deferral. - - Verify: packet includes rationale, boundary, proof burden, and rejected - broader expansions. -- [x] **T4 (P1, human: ~30m / CC: ~10m)** — decision freeze — convert the two - packets into one frozen milestone posture before any authority docs are - rewritten. - - Verify: decision-freeze record names one outcome for bounded generics and - one outcome for async/IO. -- [x] **T5 (P1, human: ~45m / CC: ~15m)** — contract ratification — update - `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, and repo-facing - closeout docs so they all teach the same final pre-I8 story. - - Verify: no checked-in doc still teaches I6 as current or leaves the V1 line - to inference. -- [x] **T6 (P1, human: ~30m / CC: ~10m)** — I8 handoff freeze — enumerate the - final proof walls, authoritative commands, and deferred `V1.1` list. - - Verify: I8 can be described as a proof run rather than a discovery phase. -- [x] **T7 (P1, human: ~15m / CC: ~5m)** — closeout validation — rerun the - proof-authoritative commands and add any conditional proof commands required - by an admitted slice. - - Verify: validation wall is current and matches the ratified claim. - -## What Success Looks Like - -After I7: - -- the repo can answer "what is honest Rust V1?" in one paragraph -- that answer names both what is included and what is deferred -- no maintainer needs to infer the post-I6 plan from external notes -- I8 is a proof run, not another scope-closure milestone +- [ ] **T1 (P1, human: ~15m / CC: ~5m)** — preflight freeze — create `.runs/i8/` + and record the frozen inputs, branch, commit, and exact five-command wall. + - Verify: `.runs/i8/preflight.json` names the same authority inputs listed in this plan. +- [ ] **T2 (P1, human: ~15m / CC: ~5m)** — BENCH-ECOM proof rerun — rerun the + ecommerce `status` and `export` commands and archive raw outputs. + - Verify: `BENCH-ECOM` stays `passing/satisfied/current` and the export remains `schema_version: 4`. +- [ ] **T3 (P1, human: ~15m / CC: ~5m)** — BENCH-SERVICE proof rerun — rerun the + service `status` and `export` commands and archive raw outputs. + - Verify: `BENCH-SERVICE` stays `passing/satisfied/current` and the export remains `schema_version: 4`. +- [ ] **T4 (P1, human: ~15m / CC: ~5m)** — broad inventory confirmation — rerun + repo-root `status . --format json`, archive it, and confirm the expected + `inventory_only` interpretation plus companion-negative visibility. + - Verify: `BENCH-CROSSLIB` remains visible with zero positive credit and repo-root `status` still exits non-green by design. +- [ ] **T5 (P1, human: ~30m / CC: ~10m)** — authority drift ratification — + compare the live outputs against `PLAN.md`, `ORCH_PLAN.md`, + `docs/rust_v1_contract_stack.md`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, + and `TODOS.md`, then patch only real drift. + - Verify: every checked-in authority surface teaches the same I8 story with no implied `I9`. +- [ ] **T6 (P1, human: ~20m / CC: ~10m)** — final closeout packet — write + `.runs/i8/closeout.json` with the final claim, deferred surfaces, proof-wall + verdicts, and references to the raw command outputs. + - Verify: a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` without rerunning the analysis conversation. +- [ ] **T7 (P1, human: ~variable / CC: ~variable)** — conditional blocker repair + — if any proof command or authority surface contradicts the frozen claim, fix + the direct blocker only and rerun the affected command subset plus the final + five-command wall. + - Verify: no repair widens support, changes benchmark roles, or adds new proof commands. ## NOT in scope -- broad generics support - - rationale: this milestone resolves the V1 contract line, not the broader - Rust roadmap -- framework-heavy async support - - rationale: that would replace the narrow-core posture instead of clarifying - it -- benchmark-mechanics redesign - - rationale: I3, I3.5, M67, and M68 already own the command wall and truth - mechanics +- bounded generics admission + - rationale: I7 already deferred this to `V1.1` +- async/IO admission + - rationale: I7 already froze Rust V1 as synchronous-only +- benchmark schema redesign + - rationale: M67 and M68 already own benchmark roles and mechanics +- new benchmark roots or new benchmark kinds + - rationale: the I8 claim closes over the existing three-benchmark roster +- repo-root `export .` support + - rationale: this workspace shape still truthfully rejects aggregate export +- turning repo-root `status .` into a globally green ship gate + - rationale: broad inventory is intentionally broader than the positive proof wall - reopening I3.5 command-wall semantics - - rationale: I7 consumes that wall as frozen authority -- inventing a checked-in `I9` to hide unresolved I7 decisions - - rationale: I7 must end by freezing I8 as the final proof milestone + - rationale: I8 consumes that wall as frozen authority +- inventing a checked-in `I9` + - rationale: the active ladder ends at I8 until a new bounded post-V1 milestone exists ## Immediate Next Move -Execute I7 in this order: +Execute I8 in this order: -1. freeze the post-I6 basis packet -2. draft the bounded-generics packet -3. draft the async/IO packet -4. freeze the decisions -5. ratify repo-facing authority from the frozen decisions -6. freeze the I8 handoff -7. rerun the validation wall +1. create `.runs/i8/` and freeze the exact input set +2. rerun and archive the BENCH-ECOM proof wall +3. rerun and archive the BENCH-SERVICE proof wall +4. rerun and archive repo-root inventory status +5. compare live outputs against the frozen claim and repo-facing docs +6. patch only real drift +7. write the final closeout packet -Do not start "wrapping Rust V1" by widening code scope before the basis and -decision packets exist as repo-backed truth. +Do not start by changing code. Start by proving whether the already-ratified +claim still holds. From 8d627b1af1fc4acd6e0e8a065805045d3f9b195a Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sat, 23 May 2026 00:30:29 +0000 Subject: [PATCH 05/24] Refine I8 plan for final Rust V1 proof run --- PLAN.md | 496 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 330 insertions(+), 166 deletions(-) diff --git a/PLAN.md b/PLAN.md index 02019ab..dcd1fde 100644 --- a/PLAN.md +++ b/PLAN.md @@ -7,7 +7,7 @@ Implementation readiness: **ready to execute** Plan scope: **rerun the frozen Rust V1 proof wall, preserve repo-root inventory semantics, and close Rust V1 only if the live benchmark surfaces, deferred boundaries, and repo-facing docs still match the ratified narrow-core claim** Base branch: **main** Working branch: **`feat/i8-final-proof-run`** -Validated at commit: **`dc61b01`** +Validated at commit: **`5d849d4`** Last rewritten: **2026-05-23** Supersedes: @@ -17,13 +17,10 @@ Supersedes: Locked authority inputs: - contract-stack index: `docs/rust_v1_contract_stack.md` -- `M65`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-200036.md` -- `M66`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-213928.md` -- `M67`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-220646.md` -- `M68`: `/home/azureuser/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m60-plus-design-20260517-225503.md` -- I7 freeze packet: `.runs/i7/decision-freeze.json` +- I7 decision freeze: `.runs/i7/decision-freeze.json` - I7 handoff packet: `.runs/i7/i8-handoff.json` -- live repo truth on `feat/i8-final-proof-run` at `dc61b01`: +- benchmark roster: `benchmarks/labels.json` +- live repo truth on `feat/i8-final-proof-run` at `5d849d4`: - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - `cargo run -p spec-cli -- export examples/ecommerce/units` - `cargo run -p spec-cli -- status examples/service/units --format json` @@ -54,16 +51,19 @@ Primary repo surfaces for I8 closeout: ## Executive Summary -I7 already made the hard product decisions. Bounded generics stay deferred to -`V1.1`. Rust V1 stays synchronous-only. `BENCH-CROSSLIB` stays visible as -companion negative proof and never earns positive supported credit. +I7 already made the product decisions. Rust V1 stays narrow. Rust V1 stays +synchronous-only. Bounded generics and async or IO-owned boundaries stay +deferred to `V1.1`. `BENCH-CROSSLIB` stays visible as companion negative proof +and never counts as positive supported credit. -That means I8 is not a feature milestone. It is the final proof run that must -show the frozen claim still holds on the live repo without widening scope, -changing the command wall, or laundering broad-scope inventory output into -proof. +That means I8 is not another scope milestone. I8 is the final proof run over +the already-ratified claim. The job is to prove that the live repo still tells +the same story through the same five-command wall, then freeze the evidence and +close Rust V1 without widening scope by prose drift, benchmark drift, or +repo-root misinterpretation. -I8 is complete only when the repo can say one sentence honestly: +I8 is complete only when the repo can say this sentence honestly, with checked +evidence: > Rust V1 is the current narrow-core `spec` surface: synchronous supported > function families plus plain data and sum seams, proven by BENCH-ECOM and @@ -71,61 +71,68 @@ I8 is complete only when the repo can say one sentence honestly: ## Frozen I8 Contract -These are inherited, locked, and not open for reinterpretation in I8: +These are inherited from I7 and are not open for reinterpretation in I8: -- The milestone ladder is `I7 -> I8`, not `I7 -> I8 -> I9`. -- The Rust V1 claim stays narrow, synchronous, and benchmark-backed. -- Deferred `V1.1` surfaces are exactly: +- the active ladder is `I7 -> I8`, not `I7 -> I8 -> I9` +- the Rust V1 claim stays narrow, synchronous, and benchmark-backed +- deferred `V1.1` surfaces are exactly: - bounded generics - async flows, runtime adapters, and IO-owned boundaries - `BENCH-ECOM` and `BENCH-SERVICE` are the only positive proof walls required - for the V1 claim. -- `BENCH-CROSSLIB` remains active companion negative proof. -- The authoritative I8 proof wall is exactly five commands: + for the V1 claim +- `BENCH-CROSSLIB` remains the active companion negative proof wall +- the authoritative I8 proof wall is exactly five commands: - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - `cargo run -p spec-cli -- export examples/ecommerce/units` - `cargo run -p spec-cli -- status examples/service/units --format json` - `cargo run -p spec-cli -- export examples/service/units` - `cargo run -p spec-cli -- status . --format json` -- No new slice-specific proof commands are admitted in I8. -- Repo-root `export .` remains unsupported for this workspace shape and is not - part of the I8 wall. +- no new slice-specific proof commands are admitted in I8 +- repo-root `export .` remains unsupported for this workspace shape and is not + part of the I8 wall +- repo-root `status . --format json` remains a broad inventory surface with + `scope_authority: inventory_only`; it is not a green ship gate ## Current Validated Truth -Observed on `feat/i8-final-proof-run` at `dc61b01`: +Observed live on `feat/i8-final-proof-run` at commit `5d849d4` on 2026-05-23: - `status examples/ecommerce/units --format json` passes with: - `BENCH-ECOM` - `benchmark_status: passing` - `gate_status: satisfied` - `readability_review_status: current` -- `export examples/ecommerce/units` passes with `schema_version: 4` and a full - benchmark projection for `BENCH-ECOM`. +- `export examples/ecommerce/units` passes with: + - `schema_version: 4` + - `provenance.git_commit_sha: 5d849d4f60676c259793d0592b7c1af07431d9a2` + - full `BENCH-ECOM` projection, including required molecule proofs and + readability-generated file inventory - `status examples/service/units --format json` passes with: - `BENCH-SERVICE` - `benchmark_status: passing` - `gate_status: satisfied` - `readability_review_status: current` -- `export examples/service/units` passes with `schema_version: 4` and a full - benchmark projection for `BENCH-SERVICE`. +- `export examples/service/units` passes with: + - `schema_version: 4` + - `provenance.git_commit_sha: 5d849d4f60676c259793d0592b7c1af07431d9a2` + - full `BENCH-SERVICE` projection, including required molecule proofs and + readability-generated file inventory - `status . --format json` exits `1`, which is still correct for I8 because: - `scope_authority` is `inventory_only` - `BENCH-CROSSLIB` remains visible with `benchmark_status: passing` and `gate_status: not_applicable` - `BENCH-ECOM` and `BENCH-SERVICE` still project as `passing` - - non-green fixture and semantic-family roots remain part of the broad - inventory surface, so repo-root success is not defined as "all roots green" + - intentionally non-green roots still exist in the broad inventory surface, + including fixtures, semantic-family packets, and unsupported or untested + diagnostic roots that are outside the positive proof wall -This is the exact behavior I8 must preserve unless a real truth bug is found. +This is the behavior I8 must preserve unless a real truth bug is found. ## Scope Challenge ### Premise correction -I8 is not "final Rust work." - -I8 is: +I8 is not "final Rust work." I8 is: ```text prove that the already-ratified Rust V1 claim still holds on the live repo, @@ -133,17 +140,31 @@ with the same benchmark roster, the same deferred boundaries, and the same five-command wall ``` -If implementation expands beyond that sentence, it has escaped the milestone. +If the work expands beyond that sentence, the milestone escaped. + +### What existing code already solves the problem + +The repo already ships everything I8 needs: + +- the benchmark roster and roles are already encoded in `benchmarks/labels.json` +- benchmark-aware `status` and `export` already project full truth at schema + version `4` +- readability anchors already exist for the two positive walls +- I7 already froze the scope decision and wrote the I8 handoff packet +- repo-facing docs already describe the narrow-core story + +I8 should reuse those surfaces exactly. It should not invent parallel proof +machinery, new labels, or a new source of authority. ### Minimum change set If the frozen wall still passes, the minimum complete I8 diff is: -1. capture the final proof outputs under `.runs/i8/` -2. verify the benchmark roster and readability anchors still match the claim -3. patch any stale authority docs or release notes that still drift from the - frozen claim -4. record the final closeout packet +1. create `.runs/i8/` and freeze the run inputs +2. rerun the five-command wall and archive raw outputs +3. compare those outputs against the frozen claim and checked-in docs +4. patch only real authority drift +5. write the closeout packet Code changes are conditional only. If a proof-wall command fails or a read-side surface contradicts the ratified claim, fix only the direct blocker. Do not @@ -151,28 +172,30 @@ turn one failing command into a mechanics redesign. ### Complexity check -This should stay boring: +This milestone should stay boring: -- no new benchmark artifacts beyond I8 closeout records under `.runs/i8/` - no new CLI commands - no new benchmark kinds - no new support rows -- no new service/example roots +- no new example or service roots +- no new proof writers +- no new artifact classes beyond the bounded `.runs/i8/` closeout records -If the work grows into benchmark schema edits, proof-writer redesign, or a new -support boundary, stop. That is not I8. +If the work grows into benchmark schema edits, support-boundary changes, or a +new post-I8 planning milestone, stop. That is not I8. ### Completeness rule The complete version is cheap here, so take it: - rerun all five commands, not fragments -- inspect full benchmark projections, not just exit codes -- capture raw outputs for closeout -- verify docs and decisions against the actual live outputs +- inspect benchmark projections, not just exit codes +- archive raw outputs, not just summaries +- compare docs against live truth, not against memory +- close only after the repo-facing story and the command wall say the same thing -The shortcut version would be "commands look fine, ship it." I8 should not use -that shortcut. +The shortcut version would be "the commands seem fine, ship it." I8 should not +use that shortcut. ### Distribution check @@ -184,19 +207,18 @@ out of scope. | Sub-problem | Existing owner | I8 action | | --- | --- | --- | -| milestone ladder and scope boundary | `M65`, `docs/rust_v1_contract_stack.md`, `.runs/i7/i8-handoff.json` | reuse exactly; do not infer an `I9` | -| supported-vs-deferred Rust claim | `M66`, `DECISIONS.md`, `.runs/i7/decision-freeze.json` | quote and verify, not reinterpret | -| benchmark role split | `M67` | preserve two positive benchmarks plus one companion negative wall | -| benchmark/readability mechanics | `M68`, `benchmarks/labels.json`, snapshots, readability reviews, `status`/`export` schema v4 | verify read-side truth still matches the locked mechanics | -| positive proof walls | `examples/ecommerce/units`, `examples/service/units`, benchmark-aware CLI tests | rerun unchanged | +| milestone ladder and scope boundary | `docs/rust_v1_contract_stack.md`, `.runs/i7/i8-handoff.json` | reuse exactly; do not infer `I9` | +| supported-vs-deferred Rust claim | `DECISIONS.md`, `.runs/i7/decision-freeze.json` | quote and verify, not reinterpret | +| benchmark role split | `benchmarks/labels.json`, I7 handoff | preserve two positive walls plus one companion negative wall | +| benchmark/readability mechanics | `status`/`export` schema v4, committed readability reviews, snapshot surfaces | verify read-side truth still matches the frozen mechanics | +| positive proof walls | `examples/ecommerce/units`, `examples/service/units` | rerun unchanged | | companion-negative visibility | `examples/crosslib-app/units`, repo-root `status`, `BENCH-CROSSLIB` labels | verify still visible and still zero-credit | -| repo-root scope semantics | I3.5 authority snapshot plus current CLI behavior | preserve `inventory_only`; do not try to make it globally green | +| repo-root scope semantics | `.runs/i3_5_authority_alignment/**`, current CLI behavior | preserve `inventory_only`; do not try to make repo-root globally green | | scope-closure packet trail | `.runs/i7/**` | treat as frozen inputs, then append I8 evidence separately under `.runs/i8/**` | ## Final Rust V1 Claim -This is the exact plain-English line I8 is trying to close, derived from M65, -M66, the I7 freeze, and the current live repo state: +This is the exact plain-English line I8 is closing: > Rust V1 is the current narrow-core `spec` surface: synchronous supported > function families plus plain data and sum seams, proven by BENCH-ECOM and @@ -206,7 +228,8 @@ Derived consequences: - supported: - synchronous supported function families - - plain pipeline/wrapper composition inside the shipped supported families + - plain pipeline and wrapper composition inside the shipped supported + families - plain data seams - plain sum seams - truthful proof surfaces over those supported rows @@ -216,67 +239,184 @@ Derived consequences: - visible but non-crediting: - companion negative proof in `BENCH-CROSSLIB` +## Artifact Map + +The I8 run root is intentionally small and deterministic: + +- `.runs/i8/preflight.json` +- `.runs/i8/evidence/ecommerce.status.json` +- `.runs/i8/evidence/ecommerce.export.json` +- `.runs/i8/evidence/service.status.json` +- `.runs/i8/evidence/service.export.json` +- `.runs/i8/evidence/workspace.status.json` +- `.runs/i8/authority-drift.md` +- `.runs/i8/closeout.json` + +Artifact contract: + +- raw command output files are canonical evidence +- summaries may cite raw outputs but do not replace them +- checked-in authority docs remain the public story +- `.runs/i8/**` is the private closeout packet that proves how that story was + revalidated + ## Architecture And Execution Graph -The execution graph for I8 is intentionally thinner than I7: +I8 is a verification-and-ratification run, not a product expansion: ```text -M65 ladder lock - | - v -M66 support rows + deferred boundaries - | - v -M67 benchmark role split +I7 decision freeze | v -M68 read-side mechanics - | - v -I7 decision freeze + I8 handoff +live benchmark roster + live CLI truth | v I8 preflight freeze (.runs/i8/) | - +------------------------------+ - | | - v v -positive wall rerun repo-root inventory check -(ECOM + SERVICE) (inventory_only + CROSSLIB) - | | - +---------------+--------------+ - | - v - authority drift comparison - | - v - final closeout packet - | - v - truthful Rust V1 done-state claim + +---------------------+----------------------+ + | | | + v v v +BENCH-ECOM rerun BENCH-SERVICE rerun workspace inventory rerun + | | | + +---------------------+----------+-----------+ + | + v + authority drift comparison + | + +---------------+---------------+ + | | + v v + no drift or doc drift only truth blocker found + | | + v v + final closeout packet direct blocker repair only + | | + +---------------+---------------+ + | + v + truthful Rust V1 done-state claim ``` Critical dependency rule: -- the positive benchmark reruns and the broad inventory rerun may happen in - parallel -- authority updates may not happen until those outputs are captured and - compared against the frozen claim +- proof reruns may happen before doc edits +- doc edits may not happen until proof outputs are captured and interpreted +- blocker repair exists only if a live command or live projection contradicts + the frozen claim +- closeout happens only after proof truth and repo-facing prose agree ## Work Phases | Phase | Goal | Primary outputs | Exit criteria | | --- | --- | --- | --- | -| 1. Preflight freeze | create a reproducible I8 run root and cite frozen inputs | `.runs/i8/preflight.json`, copied authority references, command roster | every later step cites frozen inputs instead of memory | -| 2. Positive proof rerun | rerun the two positive benchmark walls unchanged | raw ecommerce/service status+export outputs, summarized proof ledger | both positive benchmarks still pass with satisfied gates and current readability | -| 3. Broad inventory confirmation | rerun repo-root inventory and confirm companion-negative visibility | raw workspace status output, inventory summary | `scope_authority: inventory_only` is preserved and `BENCH-CROSSLIB` remains visible but zero-credit | -| 4. Authority drift ratification | compare live outputs against docs, labels, decisions, and handoff wording | doc diff only if drift exists | all checked-in authority surfaces teach one identical I8 story | -| 5. Final closeout | freeze the final evidence packet and milestone verdict | `.runs/i8/closeout.json`, release-note updates if needed | Rust V1 can be stated honestly with no extra caveats | +| 1. Preflight freeze | create a reproducible I8 run root and freeze the inputs | `.runs/i8/preflight.json` | every later step cites frozen inputs and fixed output paths | +| 2. Positive proof rerun | rerun both positive benchmark walls unchanged | ecommerce and service raw status/export outputs | both positive walls still pass with satisfied gates and current readability | +| 3. Broad inventory confirmation | rerun repo-root inventory and confirm companion-negative visibility | workspace raw status output and inventory interpretation | `scope_authority: inventory_only` is preserved and `BENCH-CROSSLIB` remains zero-credit | +| 4. Authority drift ratification | compare live outputs against checked-in authority surfaces | `.runs/i8/authority-drift.md`, doc diff only if needed | all repo-facing authority surfaces teach one identical I8 story | +| 5. Conditional blocker repair | repair only a direct truth blocker if one exists | bounded code or doc diff plus rerun evidence | failing truth surface is fixed without widening support or commands | +| 6. Final closeout | freeze the final evidence packet and milestone verdict | `.runs/i8/closeout.json`, release-note updates if needed | Rust V1 can be stated honestly with no extra caveats | + +### Phase detail + +#### Phase 1. Preflight freeze + +Create `.runs/i8/` and write `preflight.json` with: + +- branch +- commit +- timestamp +- authority inputs +- the exact five-command wall +- expected evidence output file paths +- the frozen plain-English Rust V1 claim + +No interpretation happens here. This phase freezes the basis only. + +#### Phase 2. Positive proof rerun + +Run these four commands against the live branch and archive raw stdout: + +- `cargo run -p spec-cli -- status examples/ecommerce/units --format json` +- `cargo run -p spec-cli -- export examples/ecommerce/units` +- `cargo run -p spec-cli -- status examples/service/units --format json` +- `cargo run -p spec-cli -- export examples/service/units` + +Required interpretation: + +- both benchmarks must remain `passing` +- both gates must remain `satisfied` +- both readability reviews must remain `current` +- both exports must remain `schema_version: 4` +- both exports must still project the same positive benchmark role and required + molecule roster + +#### Phase 3. Broad inventory confirmation + +Run: + +- `cargo run -p spec-cli -- status . --format json` + +Expected outcome: + +- non-zero exit code is allowed and expected +- `scope_authority` must be `inventory_only` +- `BENCH-CROSSLIB` must remain visible as active companion negative proof +- `BENCH-CROSSLIB` must keep `positive_credit_cases: 0` +- `BENCH-ECOM` and `BENCH-SERVICE` must still project as passing inside the + broad surface + +This phase exists to prevent a false green and to prevent a false red. + +#### Phase 4. Authority drift ratification + +Compare live outputs against: + +- `PLAN.md` +- `ORCH_PLAN.md` +- `docs/rust_v1_contract_stack.md` +- `README.md` +- `DECISIONS.md` +- `CHANGELOG.md` +- `TODOS.md` + +Allowed outcomes: + +- no drift: record that all surfaces already agree +- doc drift only: patch only the prose that drifted + +Disallowed outcome: + +- reinterpreting the live output so stale prose can stay unchanged + +#### Phase 5. Conditional blocker repair + +This phase exists only if a live truth surface disagrees with the frozen claim. + +Repair policy: + +- fix the direct blocker only +- rerun the affected command immediately +- rerun the full five-command wall before closeout +- stop and escalate if the repair would require: + - new proof commands + - new support rows + - benchmark schema redesign + - widening a deferred `V1.1` surface into Rust V1 + +#### Phase 6. Final closeout + +Write `.runs/i8/closeout.json` with: + +- the final Rust V1 claim +- deferred `V1.1` surfaces +- the five-command verdicts +- raw evidence file references +- any doc files changed for drift repair +- final closeout status: `done` or `blocked` ## Proof And Coverage Diagram -This is the I8 proof surface map. Every row below is part of the milestone and -must be captured in the closeout record. +This is the exact I8 proof surface. Every row must be observed and archived. ```text PROOF COMMANDS CLAIM SURFACE @@ -285,52 +425,55 @@ PROOF COMMANDS CLAIM SURFACE -> benchmark_status: passing -> gate_status: satisfied -> readability_review_status: current + -> archive: .runs/i8/evidence/ecommerce.status.json [PASS] cargo run -p spec-cli -- export examples/ecommerce/units -> schema_version: 4 full projection - -> BENCH-ECOM roster, cases, molecule proofs, readability anchor visible + -> BENCH-ECOM roster, molecule proofs, readability files visible + -> archive: .runs/i8/evidence/ecommerce.export.json [PASS] cargo run -p spec-cli -- status examples/service/units --format json -> BENCH-SERVICE positive wall -> benchmark_status: passing -> gate_status: satisfied -> readability_review_status: current + -> archive: .runs/i8/evidence/service.status.json [PASS] cargo run -p spec-cli -- export examples/service/units -> schema_version: 4 full projection - -> BENCH-SERVICE roster, cases, molecule proofs, readability anchor visible + -> BENCH-SERVICE roster, molecule proofs, readability files visible + -> archive: .runs/i8/evidence/service.export.json [EXPECTED EXIT 1] cargo run -p spec-cli -- status . --format json -> scope_authority: inventory_only -> BENCH-CROSSLIB visible as companion_negative_proof + -> positive_credit_cases: 0 for BENCH-CROSSLIB -> BENCH-ECOM and BENCH-SERVICE still passing in broad projection - -> non-green fixture/semantic-family roots stay inventory-only, not proof failure + -> archive: .runs/i8/evidence/workspace.status.json -COVERAGE: 5/5 commands observed on dc61b01 +COVERAGE: 5/5 commands CLAIM PATHS: 5/5 covered -GAPS: 0 proof-command gaps -PROCEDURAL REQUIREMENT: archive raw outputs under .runs/i8/ so the final claim stays replayable +GAPS: 0 command-surface gaps ``` Interpretation guard: -- A green I8 does not mean repo-root `status .` exits `0`. -- A green I8 means the two positive benchmark walls pass, the broad inventory - surface still tells the truth, and the authority docs match that reality. +- a green I8 does not mean repo-root `status .` exits `0` +- a green I8 does mean the two positive walls pass, the broad inventory surface + still tells the truth, and the checked-in docs match that reality ## Acceptance Checklist I8 closes only when all of these are true at the same time: - the plain-English Rust V1 claim in this file still matches: - - `M65` provisional claim shape - - `M66` supported/deferred boundaries - `.runs/i7/decision-freeze.json` - `.runs/i7/i8-handoff.json` + - `DECISIONS.md` + - `docs/rust_v1_contract_stack.md` - `BENCH-ECOM` remains `passing` with `gate_status: satisfied` - `BENCH-SERVICE` remains `passing` with `gate_status: satisfied` -- both positive benchmark exports still emit `schema_version: 4` full - projections +- both positive benchmark exports still emit `schema_version: 4` - `BENCH-CROSSLIB` remains visible in repo-root inventory and still counts as zero positive supported credit - repo-root `status . --format json` still reports `scope_authority: @@ -342,70 +485,84 @@ I8 closes only when all of these are true at the same time: ## Worktree Parallelization Strategy -I8 has two independent verification workstreams and one dependent ratification -workstream. That means limited parallelization is useful. +I8 has three verification steps but only two should run in parallel. The two +positive walls are independent and cheap to split. The repo-root inventory run +is broader, heavier, and easier to misread before the positive walls are +confirmed, so keep it as a parent-owned follow-up step. ### Dependency table | Step | Modules touched | Depends on | | --- | --- | --- | -| preflight freeze | `.runs/i8/`, top-level authority docs as read-only inputs | — | -| positive proof rerun | `examples/ecommerce/`, `examples/service/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | -| broad inventory confirmation | repo root inventory surfaces, `examples/crosslib-app/`, `semantic-families/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | -| authority drift ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | positive proof rerun, broad inventory confirmation | +| preflight freeze | `.runs/i8/` | — | +| BENCH-ECOM proof rerun | `examples/ecommerce/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | +| BENCH-SERVICE proof rerun | `examples/service/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | +| broad inventory confirmation | repo root inventory surfaces, `examples/crosslib-app/`, `semantic-families/`, `benchmarks/`, `.runs/i8/evidence/` | BENCH-ECOM proof rerun, BENCH-SERVICE proof rerun | +| authority drift ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | broad inventory confirmation | | conditional blocker repair | only the exact modules implicated by a failing command | whichever verification step found the blocker | | final closeout | `.runs/i8/`, release-note surfaces if changed | authority drift ratification, conditional blocker repair if needed | ### Parallel lanes -- `Lane A`: positive proof rerun +- `Lane A`: BENCH-ECOM proof rerun - sequential within lane - - owns only `.runs/i8/evidence/ecommerce-*` and `.runs/i8/evidence/service-*` -- `Lane B`: broad inventory confirmation + - owns only `.runs/i8/evidence/ecommerce.*` +- `Lane B`: BENCH-SERVICE proof rerun - sequential within lane - - owns only `.runs/i8/evidence/workspace-*` and the inventory summary -- `Lane C`: authority drift ratification + - owns only `.runs/i8/evidence/service.*` +- `Lane C`: broad inventory confirmation + - parent-owned - must wait for Lane A and Lane B + - owns only `.runs/i8/evidence/workspace.status.json` +- `Lane D`: authority drift ratification + - parent-owned + - must wait for Lane C - owns checked-in authority docs only -- `Lane D`: conditional blocker repair - - exists only if Lane A or Lane B finds a real truth regression +- `Lane E`: conditional blocker repair + - exists only if a real truth blocker is found - must stay bounded to the direct failing surface ### Execution order 1. Parent creates `.runs/i8/` and freezes inputs. -2. Launch `Lane A` and `Lane B` in parallel. -3. Parent compares both outputs against the frozen claim. -4. Launch `Lane C` only if the claim and outputs are now fully understood. -5. Launch `Lane D` only if a real blocker must be repaired before closeout. -6. Parent writes the final I8 closeout packet. +2. Launch `Lane A` and `Lane B` in parallel worktrees. +3. Parent compares the two positive proof outputs against the frozen claim. +4. Parent runs `Lane C` in the main checkout. +5. Parent launches `Lane D` only after the inventory interpretation is settled. +6. Parent launches `Lane E` only if a real blocker must be repaired. +7. Parent writes the final I8 closeout packet. ### Conflict flags -- `Lane A` and `Lane B` must not both write the same summary file under - `.runs/i8/`. Split evidence files by benchmark versus workspace. -- `Lane C` must not start before the workspace inventory interpretation is - settled. Otherwise it risks ratifying the wrong meaning of repo-root `status`. -- `Lane D` must not turn a local truth bug into a broad mechanics rewrite. If - the repair touches benchmark schema, proof writers, or support rows beyond the - failing surface, stop and escalate. +- `Lane A` and `Lane B` must not write the same evidence files. +- `Lane A` and `Lane B` may still contend on Cargo package or build locks if + they share the same local cache. That is acceptable, but it means parallel + worktrees improve operator separation more than raw wall-clock time. +- `Lane C` must not start before both positive walls are understood. Otherwise + repo-root inventory can be misread in the absence of benchmark context. +- `Lane D` must not ratify docs before the workspace inventory semantics are + confirmed. +- `Lane E` must not turn a narrow truth repair into a mechanics rewrite. If a + proposed fix touches benchmark schema, proof writers, or support boundaries, + stop and escalate. ## Failure Modes | Failure mode | Consequence | Guard in this plan | | --- | --- | --- | -| repo-root `status .` exit `1` is misread as I8 failure | a truthful broad inventory surface blocks the release for the wrong reason | require `scope_authority: inventory_only` and benchmark roster interpretation, not exit-code-only reasoning | -| `BENCH-CROSSLIB` starts counting as positive credit | the V1 claim widens by read-surface drift | verify companion-negative classification and zero-credit behavior in repo-root inventory | -| docs still describe I7 as current or imply I9 | the milestone ladder becomes ambiguous again | ratify all repo-facing authority surfaces only after live output comparison | -| positive status passes but export drifts | downstream machine consumers see a different truth than human readers | require both `status` and `export` for each positive benchmark | -| readability anchors go stale while benchmark status stays green | the claim overstates reviewability of emitted Rust | require `readability_review_status: current` for both positive benchmarks | +| repo-root `status .` exit `1` is misread as I8 failure | a truthful broad inventory surface blocks the release for the wrong reason | require `scope_authority: inventory_only` and benchmark interpretation, not exit-code-only reasoning | +| `BENCH-CROSSLIB` starts counting as positive credit | the V1 claim widens by read-surface drift | require zero positive credit and preserve companion-negative classification | +| positive `status` passes but `export` drifts | downstream machine consumers see a different truth than human readers | require both `status` and `export` for each positive benchmark | +| readability anchors go stale while benchmark status stays green | the plan overstates the reviewability of emitted Rust | require `readability_review_status: current` for both positive walls | +| docs still imply I7 is current or imply I9 exists | milestone ownership becomes ambiguous again | compare every repo-facing authority surface after live reruns, not before | | a blocker fix adds new proof commands or support rows | I8 quietly becomes another scope milestone | hard-stop any repair that changes the five-command wall or deferred boundaries | -| broad inventory roots are trimmed to force green | the repo loses honest visibility into deferred and fixture surfaces | preserve the current repo-root inventory behavior exactly | +| broad inventory roots are trimmed to force green | the repo loses honest visibility into deferred and fixture surfaces | preserve repo-root inventory behavior exactly | Critical gap test: -- if any positive benchmark loses `passing` or `satisfied`, I8 is not done -- if any doc still needs caveats not present in the frozen claim, I8 is not done +- if either positive benchmark loses `passing` or `satisfied`, I8 is not done +- if any authority doc still needs caveats not present in the frozen claim, I8 + is not done - if the only way to "pass" is to reinterpret `inventory_only` as proof, I8 is not done @@ -414,13 +571,17 @@ Critical gap test: I8 should not introduce any new runtime or infrastructure cost. The only meaningful operational concerns are: -- broad repo-root `status` is a heavier scan than benchmark-root commands, so do - not rerun it unnecessarily during closeout -- full-scope `status` and `export` must be captured as raw outputs once per - closeout run so later doc review does not depend on rerunning commands by - memory -- no new caching, queueing, parallel proof writer, or benchmark registry work is - justified here +- repo-root `status` is a heavier scan than benchmark-root commands, so do not + treat it as a cheap loop +- raw command outputs must be captured once per closeout run so later doc review + does not depend on rerunning the wall from memory +- parallelization is helpful only for the two positive walls; forcing more + concurrency buys little and increases interpretation risk +- concurrent Cargo invocations may wait on package or build locks; that is not + a blocker, but it means the parent should prefer clarity of ownership over + chasing marginal runtime wins +- no new caching, queueing, proof-writer parallelism, or benchmark-registry + work is justified here Boring-by-default rule: @@ -430,30 +591,33 @@ Boring-by-default rule: ## Implementation Tasks - [ ] **T1 (P1, human: ~15m / CC: ~5m)** — preflight freeze — create `.runs/i8/` - and record the frozen inputs, branch, commit, and exact five-command wall. - - Verify: `.runs/i8/preflight.json` names the same authority inputs listed in this plan. + and record the frozen inputs, branch, commit, plain-English claim, exact + five-command wall, and bounded artifact map. + - Verify: `.runs/i8/preflight.json` names the same authority inputs and evidence paths listed in this plan. - [ ] **T2 (P1, human: ~15m / CC: ~5m)** — BENCH-ECOM proof rerun — rerun the - ecommerce `status` and `export` commands and archive raw outputs. + ecommerce `status` and `export` commands and archive raw outputs to + `.runs/i8/evidence/ecommerce.*`. - Verify: `BENCH-ECOM` stays `passing/satisfied/current` and the export remains `schema_version: 4`. - [ ] **T3 (P1, human: ~15m / CC: ~5m)** — BENCH-SERVICE proof rerun — rerun the - service `status` and `export` commands and archive raw outputs. + service `status` and `export` commands and archive raw outputs to + `.runs/i8/evidence/service.*`. - Verify: `BENCH-SERVICE` stays `passing/satisfied/current` and the export remains `schema_version: 4`. -- [ ] **T4 (P1, human: ~15m / CC: ~5m)** — broad inventory confirmation — rerun +- [ ] **T4 (P1, human: ~10m / CC: ~5m)** — broad inventory confirmation — rerun repo-root `status . --format json`, archive it, and confirm the expected `inventory_only` interpretation plus companion-negative visibility. - - Verify: `BENCH-CROSSLIB` remains visible with zero positive credit and repo-root `status` still exits non-green by design. + - Verify: `BENCH-CROSSLIB` remains visible with zero positive credit and repo-root `status` remains non-green by design. - [ ] **T5 (P1, human: ~30m / CC: ~10m)** — authority drift ratification — compare the live outputs against `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, and `TODOS.md`, then patch only real drift. - Verify: every checked-in authority surface teaches the same I8 story with no implied `I9`. - [ ] **T6 (P1, human: ~20m / CC: ~10m)** — final closeout packet — write - `.runs/i8/closeout.json` with the final claim, deferred surfaces, proof-wall - verdicts, and references to the raw command outputs. - - Verify: a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` without rerunning the analysis conversation. + `.runs/i8/closeout.json` with the final claim, deferred surfaces, command + verdicts, doc-drift summary, and references to raw evidence files. + - Verify: a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` without relying on conversation context. - [ ] **T7 (P1, human: ~variable / CC: ~variable)** — conditional blocker repair — if any proof command or authority surface contradicts the frozen claim, fix - the direct blocker only and rerun the affected command subset plus the final + the direct blocker only and rerun the affected command plus the final five-command wall. - Verify: no repair widens support, changes benchmark roles, or adds new proof commands. @@ -461,7 +625,7 @@ Boring-by-default rule: - bounded generics admission - rationale: I7 already deferred this to `V1.1` -- async/IO admission +- async or IO admission - rationale: I7 already froze Rust V1 as synchronous-only - benchmark schema redesign - rationale: M67 and M68 already own benchmark roles and mechanics @@ -470,7 +634,7 @@ Boring-by-default rule: - repo-root `export .` support - rationale: this workspace shape still truthfully rejects aggregate export - turning repo-root `status .` into a globally green ship gate - - rationale: broad inventory is intentionally broader than the positive proof wall + - rationale: broad inventory is intentionally wider than the positive proof wall - reopening I3.5 command-wall semantics - rationale: I8 consumes that wall as frozen authority - inventing a checked-in `I9` @@ -489,4 +653,4 @@ Execute I8 in this order: 7. write the final closeout packet Do not start by changing code. Start by proving whether the already-ratified -claim still holds. +claim still holds on the live branch. From 92b82dbbcb730735ac3fa8440ef5cf24f524d7f6 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sat, 23 May 2026 00:53:16 +0000 Subject: [PATCH 06/24] Refresh I8 orchestration plan --- ORCH_PLAN.md | 1137 +++++++++++++++++++++++++++----------------------- 1 file changed, 616 insertions(+), 521 deletions(-) diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index d068d54..a8c8584 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,672 +1,767 @@ -# I7 Orchestration Plan +# I8 Orchestration Plan -Status: **authoritative execution runbook and closeout record** -Milestone: **I7 Rust V1 scope-decision closure** +Status: **authoritative execution runbook for the I8 final proof run** +Milestone: **I8 Rust V1 final proof run** Plan authority: **[`PLAN.md`](./PLAN.md)** Contract-stack authority: **[`docs/rust_v1_contract_stack.md`](./docs/rust_v1_contract_stack.md)** -Frozen command-wall authority: **`.runs/i3_5_authority_alignment/authority-plan.snapshot.md` and `.runs/i3_5_authority_alignment/phase2-freeze.json`** +Frozen upstream authority: **`.runs/i7/decision-freeze.json` and `.runs/i7/i8-handoff.json`** Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** -Frozen basis commit: **`9bec150c596148f5bd03f048d154c59f137bb0cf`** -Last rewritten: **2026-05-22** +Base branch: **`main`** +Working branch: **`feat/i8-final-proof-run`** +Plan validated at commit: **`5d849d4`** +Current execution head at draft time: **`8d627b1af1fc4acd6e0e8a065805045d3f9b195a`** +Last rewritten: **2026-05-23** ## Summary -- Execute from `/home/azureuser/__Active_Code/atomize-hq/spec`. -- Treat I7 as a scope-decision closure milestone, not a broad feature milestone. -- Keep the critical path local to the parent for: - - basis freeze - - packet acceptance - - decision freeze - - authority merge - - final I8 handoff -- Parallelize only the two independent decision packets before freeze: - - Lane A: bounded generics - - Lane B: async/IO -- Launch Lane C only after decision freeze. -- Launch Lane D only if the frozen decision admits one newly bounded slice that - still needs code proof before I8. - -I7 is complete only when the repo can answer, without inference: - -- what Rust V1 includes -- what Rust V1 defers to `V1.1` -- what exact wall I8 must prove - -## Frozen Outcome - -- bounded generics defer to `V1.1` -- Rust V1 remains synchronous-only, so async/IO also defer to `V1.1` -- `BENCH-CROSSLIB` remains the active companion-negative wall -- Lane D is absent because no new bounded Rust V1 slice was admitted -- I8 inherits the existing five-command validation wall unchanged - -## Milestone Posture - -- I7 closes post-I6 ambiguity; it does not reopen I3.5 command semantics. -- I3.5 remains frozen authority for the public command wall. -- I8 is the final proof-run milestone. -- There is no implied checked-in `I9`. -- `BENCH-ECOM`, `BENCH-SERVICE`, and the current `BENCH-CROSSLIB` role remain - frozen starting truth unless I7 records an explicit scoped change. -- Narrow, explicit deferral is a successful I7 outcome. +- Execute from the current checked-out branch `feat/i8-final-proof-run`, because + that is the live I8 branch in this workspace and `5d849d4` is an ancestor of + the current head `8d627b1af1fc4acd6e0e8a065805045d3f9b195a`. +- Treat I8 as a verification-and-ratification run only. It is not a feature + milestone, a benchmark-mechanics milestone, or a post-V1 planning wedge. +- Keep the critical path local to the parent agent for: + - preflight freeze + - interpretation of both positive proof walls + - workspace inventory confirmation + - authority drift ratification + - blocker activation or rejection + - final closeout +- Parallelize only the two disjoint positive proof reruns: + - Lane A: `BENCH-ECOM` + - Lane B: `BENCH-SERVICE` +- All worker lanes use fresh `gpt-5.4` subagents with + `reasoning_effort=high`. +- Cap concurrency at `2` worker subagents total. +- The parent agent remains the only integrator and the only writer of + canonical `.runs/i8/**`. +- Keep repo-root inventory confirmation parent-owned and strictly after both + positive walls are understood. +- Use dedicated worktrees under + `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/{ecom,service,blocker}` + with workstream branches: + - `ws/i8-ecom-proof` + - `ws/i8-service-proof` + - `ws/i8-blocker` only if a real blocker repair is required +- Keep orchestration state in one canonical parent-owned source of truth: + - run root: `.runs/i8/` + - queue: `.runs/i8/tasks.json` + - session log: `.runs/i8/session-log.md` + - preflight freeze: `.runs/i8/preflight.json` + - authority drift record: `.runs/i8/authority-drift.md` + - final closeout: `.runs/i8/closeout.json` + - parent-owned per-task sentinels: `.runs/i8/sentinels/.json` +- Treat `.runs/i8/**` as run artifacts and closeout records, not as new product + surfaces. Treat `benchmarks/labels.json`, committed benchmark snapshots, and + readability reviews as existing truth inputs, not authored scope for I8 + unless a live contradiction forces a bounded repair. + +## Frozen Outcome Target + +I8 is complete only when the repo can still say this sentence honestly with +fresh evidence from the live branch: + +> Rust V1 is the current narrow-core `spec` surface: synchronous supported +> function families plus plain data and sum seams, proven by `BENCH-ECOM` and +> `BENCH-SERVICE`, with `BENCH-CROSSLIB` preserved as companion negative proof. + +Nothing in I8 may widen, soften, or reinterpret that sentence. + +## Hard Guards + +- Do not change the frozen five-command wall: + - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` + - `cargo run -p spec-cli -- export examples/ecommerce/units` + - `cargo run -p spec-cli -- status examples/service/units --format json` + - `cargo run -p spec-cli -- export examples/service/units` + - `cargo run -p spec-cli -- status . --format json` +- Do not add slice-specific proof commands. +- Do not make repo-root `export .` supported for this workspace shape. +- Do not reinterpret repo-root `status . --format json` as a green ship gate. + It must remain `scope_authority: inventory_only`. +- Do not change the benchmark roster: + - `BENCH-ECOM` stays an active positive wall rooted at + `examples/ecommerce/units` + - `BENCH-SERVICE` stays an active positive wall rooted at + `examples/service/units` + - `BENCH-CROSSLIB` stays an active companion negative wall rooted at + `examples/crosslib-app/units` +- Do not widen Rust V1 support beyond the I7 freeze: + - bounded generics remain deferred to `V1.1` + - async flows, runtime adapters, and IO-owned boundaries remain deferred to + `V1.1` +- Do not imply an `I9` or any new checked-in post-I8 milestone. +- Do not start the run if `5d849d4` is not an ancestor of the current execution + head. If that ancestry check ever fails, halt and rewrite `PLAN.md` before + attempting I8. +- If any proposed blocker repair would require: + - changing benchmark schema or benchmark roles + - changing the five-command wall + - adding new proof writers or artifact classes + - promoting a deferred `V1.1` surface into Rust V1 + stop immediately. That is no longer I8. ## Parent And Worker Responsibilities ### Parent owns -- the frozen basis commit and basis packet -- all canonical run-state under `.runs/i7/` -- acceptance or rejection of lane packets -- the single decision-freeze record -- the single authority merge -- the final validation wall -- the final I8 handoff record +- all canonical files under `.runs/i8/` +- all canonical task and sentinel state under `.runs/i8/tasks.json`, + `.runs/i8/session-log.md`, and `.runs/i8/sentinels/` +- the branch and authority ancestry check +- acceptance or rejection of positive proof lane outputs +- workspace inventory interpretation +- authority drift ratification +- any decision to activate blocker repair +- final closeout and milestone verdict ### Workers own -- narrow branch-local implementation or drafting work for their lane only -- short return summaries: changed files, commands run, blockers, assumptions +- narrow branch-local proof reruns or bounded blocker repair only +- short return summaries plus any worker-local mirror sentinel the prompt + requires ### Workers must not own -- `.runs/i7/**` in the primary checkout -- basis freeze decisions -- final scope admission decisions -- cross-lane reconciliation -- final validation or milestone closeout +- canonical `.runs/i8/**` in the parent checkout +- final interpretation of `inventory_only` +- repo-facing authority conclusions +- final closeout status +- any creative scope decision about Rust V1 + +## Subagent Execution Policy + +- Every worker lane is executed by a fresh `gpt-5.4` subagent with + `reasoning_effort=high`. +- Maximum concurrency is locked at `2` worker subagents. +- The parent remains the only integrator. +- The parent remains the only writer of canonical `.runs/i8/**`, including: + - `.runs/i8/tasks.json` + - `.runs/i8/session-log.md` + - `.runs/i8/sentinels/**` + - `.runs/i8/evidence/**` + - `.runs/i8/preflight.json` + - `.runs/i8/authority-drift.md` + - `.runs/i8/closeout.json` +- Worker completion state is recognized only through: + - the worker's required narrow summary + - parent-owned sentinel updates + - parent-owned task acceptance updates +- The parent must not treat raw subagent transcript text as canonical run + state. + +## Locked Command-Capture Pattern + +Every command whose raw stdout is archived must preserve the real command exit +code. The runbook must not rely on plain `cargo ... | tee ...` without +`pipefail`. + +Locked capture pattern for commands expected to exit `0`: -## Hard Guards - -- Do not change the I3.5 command wall. -- Do not redesign benchmark mechanics. -- Do not widen Rust V1 support by prose drift. -- Do not let Lane A or Lane B edit checked-in authority docs directly before - decision freeze. -- Do not merge ratification docs before the parent records the frozen decisions. -- Do not create Lane D unless the parent has already frozen one exact admitted - slice and its proof burden. -- Do not close I7 while any V1 admission still lacks exact proof commands. -- Do not imply a checked-in `I9`. +```bash +bash -lc ' +set -o pipefail + | tee +cmd_status=$? +printf "%s\n" "$cmd_status" > +exit "$cmd_status" +' +``` -## Worktree And Branch Layout +Locked capture pattern for commands whose non-zero exit is expected and +meaningful: -The parent checkout is the canonical run root. +```bash +bash -lc ' +set -o pipefail + | tee +cmd_status=$? +printf "%s\n" "$cmd_status" > +test "$cmd_status" -eq +' +``` -- Parent basis branch: `main` -- Parent working branch: `codex/i7-v1-scope-closure` -- Parent workspace: `/home/azureuser/__Active_Code/atomize-hq/spec` -- Worktree root: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7` +Rules: -Parent branch creation: +- Archived commands must write both the raw stdout file and a sibling + `.exitcode` file. +- Worker-local captures live under `.runs/i8-worker/**`. +- Canonical accepted captures live under `.runs/i8/evidence/**` and + `.runs/i8/sentinels/**` only after parent acceptance. +- A missing or mismatched `.exitcode` file is a failed task even if stdout was + archived. -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout main -git -C /home/azureuser/__Active_Code/atomize-hq/spec pull --ff-only -git -C /home/azureuser/__Active_Code/atomize-hq/spec checkout -b codex/i7-v1-scope-closure -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7 -``` +## Worktree And Branch Layout -Worker worktrees after Gate 0 basis freeze: +The parent checkout remains the canonical run root. -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a -b codex/i7-lane-a-generics codex/i7-v1-scope-closure -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b -b codex/i7-lane-b-async codex/i7-v1-scope-closure -``` +- Parent workspace: `/home/azureuser/__Active_Code/atomize-hq/spec` +- Parent branch: `feat/i8-final-proof-run` +- Parent execution head at draft time: + `8d627b1af1fc4acd6e0e8a065805045d3f9b195a` +- Worktree root: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8` -Worker worktrees after Gate 2 decision freeze: +Worker worktrees after Gate 0 preflight freeze: ```bash -DECISION_FREEZE_COMMIT="$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD)" -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c -b codex/i7-lane-c-ratify "$DECISION_FREEZE_COMMIT" +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8 +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom -b ws/i8-ecom-proof feat/i8-final-proof-run +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service -b ws/i8-service-proof feat/i8-final-proof-run ``` -Conditional worker only if I7 admits a new bounded slice that needs code proof: +Conditional blocker worktree only if Gate 3 activates repair: ```bash -DECISION_FREEZE_COMMIT="$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD)" -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-d -b codex/i7-lane-d-proof "$DECISION_FREEZE_COMMIT" +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/blocker -b ws/i8-blocker feat/i8-final-proof-run ``` Concurrency policy: -- maximum concurrent workers before decision freeze: `2` -- maximum concurrent workers after decision freeze: `1` by default -- Lane C and Lane D may overlap only if Lane C is limited to prose that cannot - affect the final validation wall or proof-command list +- maximum concurrent workers before any blocker repair: `2` +- only `Lane A` and `Lane B` run in parallel +- `Lane C`, `Lane D`, and final closeout stay parent-owned and sequential +- `Lane E` exists only if a real contradiction is found after interpretation ## Canonical Run State -The parent-owned source of truth for this milestone is: - -- `I7_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7` -- `tasks.json` at `$I7_RUN_ROOT/tasks.json` -- session log at `$I7_RUN_ROOT/session-log.md` -- basis record at `$I7_RUN_ROOT/basis.json` -- decision packet receipt log at `$I7_RUN_ROOT/packet-receipts.json` -- decision freeze at `$I7_RUN_ROOT/decision-freeze.json` -- authority merge record at `$I7_RUN_ROOT/authority-merge.json` -- final I8 handoff at `$I7_RUN_ROOT/i8-handoff.json` - -Parent also preserves raw basis evidence under: - -- `$I7_RUN_ROOT/basis/examples-ecommerce.status.json` -- `$I7_RUN_ROOT/basis/examples-ecommerce.export.json` -- `$I7_RUN_ROOT/basis/examples-service.status.json` -- `$I7_RUN_ROOT/basis/examples-service.export.json` -- `$I7_RUN_ROOT/basis/workspace.status.json` - -Recommended parent-owned lane return mirrors: - -- `$I7_RUN_ROOT/lanes/lane-a-bounded-generics.md` -- `$I7_RUN_ROOT/lanes/lane-b-async-io.md` -- `$I7_RUN_ROOT/lanes/lane-c-ratification.md` -- `$I7_RUN_ROOT/lanes/lane-d-proof.md` - -Workers may draft branch-local notes, but only the parent writes canonical -files under `.runs/i7/` in the primary checkout. +The parent-owned source of truth for I8 is: + +- `I8_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8` +- `tasks.json` at `$I8_RUN_ROOT/tasks.json` +- session log at `$I8_RUN_ROOT/session-log.md` +- preflight freeze at `$I8_RUN_ROOT/preflight.json` +- authority drift record at `$I8_RUN_ROOT/authority-drift.md` +- final closeout at `$I8_RUN_ROOT/closeout.json` +- per-task sentinels under `$I8_RUN_ROOT/sentinels/.json` + +Canonical evidence targets: + +- `$I8_RUN_ROOT/evidence/ecommerce.status.json` +- `$I8_RUN_ROOT/evidence/ecommerce.status.exitcode` +- `$I8_RUN_ROOT/evidence/ecommerce.export.json` +- `$I8_RUN_ROOT/evidence/ecommerce.export.exitcode` +- `$I8_RUN_ROOT/evidence/service.status.json` +- `$I8_RUN_ROOT/evidence/service.status.exitcode` +- `$I8_RUN_ROOT/evidence/service.export.json` +- `$I8_RUN_ROOT/evidence/service.export.exitcode` +- `$I8_RUN_ROOT/evidence/workspace.status.json` +- `$I8_RUN_ROOT/evidence/workspace.status.exitcode` + +Recommended worker-local mirrors: + +- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.status.json` +- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.status.exitcode` +- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.export.json` +- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.export.exitcode` +- `.../spec-i8/service/.runs/i8-worker/service.status.json` +- `.../spec-i8/service/.runs/i8-worker/service.status.exitcode` +- `.../spec-i8/service/.runs/i8-worker/service.export.json` +- `.../spec-i8/service/.runs/i8-worker/service.export.exitcode` +- `.../spec-i8/blocker/.runs/i8-worker/blocker-notes.md` only if needed +- `.../spec-i8/blocker/.runs/i8-worker/.sentinel.json` only if needed + +Sentinel convention: + +- Parent-owned canonical sentinels live at + `.runs/i8/sentinels/.json`. +- Each sentinel records at least: + - `task_id` + - `owner` + - `status` + - `updated_at` + - `evidence_paths` + - `notes` +- Allowed parent-owned statuses are: + - `pending` + - `running` + - `worker_complete` + - `accepted` + - `rejected` + - `blocked` +- Workers may write branch-local mirror sentinels under `.runs/i8-worker/`, + but those are advisory only until the parent updates the canonical sentinel. + +Workers may write branch-local mirrors or notes, but only the parent writes the +canonical `.runs/i8/**` records in the primary checkout. ## Workstream Matrix | Workstream | Task id | Owner | Start gate | Owned write set | Deliverable | | --- | --- | --- | --- | --- | --- | -| WS-PARENT-0 | `task/i7-p0-basis-freeze` | parent | run start | `.runs/i7/**` | frozen basis packet plus raw evidence | -| WS-A | `task/i7-a1-bounded-generics-packet` | worker lane A | Gate 0 green | worker-local packet draft only | one bounded-generics recommendation packet | -| WS-B | `task/i7-b1-async-io-packet` | worker lane B | Gate 0 green | worker-local packet draft only | one async/IO recommendation packet | -| WS-PARENT-1 | `task/i7-p1-packet-acceptance-and-decision-freeze` | parent | WS-A and WS-B returned | `.runs/i7/packet-receipts.json`, `.runs/i7/decision-freeze.json` | frozen milestone posture | -| WS-D | `task/i7-d1-conditional-proof` | worker lane D | Gate 2 admits one bounded slice | only frozen proof surfaces named in `decision-freeze.json` | exact bounded proof or honest blocker | -| WS-C | `task/i7-c1-contract-ratification` | worker lane C | Gate 2 green | checked-in authority docs only | repo-facing I7 ratification diff | -| WS-PARENT-2 | `task/i7-p2-integration-and-closeout` | parent | WS-C done and WS-D done if present | parent branch, `.runs/i7/authority-merge.json`, `.runs/i7/i8-handoff.json` | merged authority, validation rerun, I8 handoff freeze | - -## Approval And Gate Model - -I7 does not need M26-style human approval pauses by default. Its approval model -is parent-gated milestone control. - -- Gate 0 freezes starting truth. -- Gate 1 accepts or rejects the two decision packets. -- Gate 2 freezes the single milestone posture. -- Gate 3 admits or rejects any conditional proof lane. -- Gate 4 merges ratified authority. -- Gate 5 freezes the final I8 handoff and closes the run. - -If a lane recommends widening Rust V1 beyond the current narrow baseline and -the parent cannot express that widening as one bounded, nameable, provable -slice using existing authority inputs, the parent halts and rejects the packet -back to that lane. +| WS-PARENT-0 | `task/i8-p0-preflight-freeze` | parent | run start | `.runs/i8/**` | frozen input packet plus evidence map | +| WS-A | `task/i8-a1-ecom-proof` | worker lane A | Gate 0 green | worktree-local `.runs/i8-worker/**` only | BENCH-ECOM raw status/export outputs plus narrow summary | +| WS-B | `task/i8-b1-service-proof` | worker lane B | Gate 0 green | worktree-local `.runs/i8-worker/**` only | BENCH-SERVICE raw status/export outputs plus narrow summary | +| WS-PARENT-1 | `task/i8-p1-positive-wall-acceptance` | parent | WS-A and WS-B returned | `.runs/i8/evidence/ecommerce.*`, `.runs/i8/evidence/service.*`, `.runs/i8/tasks.json` | accepted canonical positive-wall evidence | +| WS-PARENT-2 | `task/i8-p2-workspace-inventory-confirmation` | parent | WS-PARENT-1 green | `.runs/i8/evidence/workspace.status.json` | confirmed broad inventory semantics | +| WS-PARENT-3 | `task/i8-p3-authority-drift-ratification` | parent | WS-PARENT-2 green | `.runs/i8/authority-drift.md`, checked-in authority docs only if needed | repo-facing I8 story aligned to live truth | +| WS-E | `task/i8-e1-conditional-blocker-repair` | worker lane E or parent | only if WS-PARENT-1, WS-PARENT-2, or WS-PARENT-3 finds a real contradiction | only the exact failing surface plus rerun evidence | narrow direct repair or honest blocked stop | +| WS-PARENT-4 | `task/i8-p4-final-closeout` | parent | WS-PARENT-3 done and WS-E done if activated | `.runs/i8/closeout.json` and release-note surfaces if drift repair touched them | final I8 verdict and closeout packet | + +## Gate Model + +I8 does not use a human approval gate by default. It uses strict parent-owned +execution gates. + +- Gate 0: preflight freeze + - confirm branch, head, and `5d849d4 -> HEAD` ancestry + - freeze the exact five-command wall and artifact map +- Gate 1: positive wall acceptance + - accept or reject the `BENCH-ECOM` and `BENCH-SERVICE` reruns +- Gate 2: workspace inventory confirmation + - confirm repo-root `status . --format json` still means `inventory_only` +- Gate 3: authority ratification + - either all repo-facing surfaces already agree, or drift is limited to doc + repair only +- Gate 4: blocker decision + - activate narrow repair only if live truth contradicts the frozen claim +- Gate 5: final closeout + - write the final run verdict as `done` or `blocked` ## Workstream Plan -### WS-PARENT-0 (`task/i7-p0-basis-freeze`) — parent only, sequential +### WS-PARENT-0 (`task/i8-p0-preflight-freeze`) — parent only, sequential Owned write set: -- `.runs/i7/**` +- `.runs/i8/**` Required commands: ```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis -git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse --abbrev-ref HEAD +mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence +mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/sentinels +git -C /home/azureuser/__Active_Code/atomize-hq/spec branch --show-current git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD -cargo run -p spec-cli -- status examples/ecommerce/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-ecommerce.status.json -cargo run -p spec-cli -- export examples/ecommerce/units | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-ecommerce.export.json -cargo run -p spec-cli -- status examples/service/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-service.status.json -cargo run -p spec-cli -- export examples/service/units | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/examples-service.export.json -cargo run -p spec-cli -- status . --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i7/basis/workspace.status.json +git -C /home/azureuser/__Active_Code/atomize-hq/spec merge-base --is-ancestor 5d849d4 HEAD ``` +`preflight.json` must record: + +- execution branch +- execution head +- plan-validated commit `5d849d4` +- proof that `5d849d4` is an ancestor of the execution head +- authority inputs: + - `PLAN.md` + - `docs/rust_v1_contract_stack.md` + - `.runs/i7/decision-freeze.json` + - `.runs/i7/i8-handoff.json` + - `benchmarks/labels.json` + - `README.md` + - `DECISIONS.md` + - `CHANGELOG.md` + - `TODOS.md` +- the exact five-command wall +- the canonical evidence output paths +- the frozen plain-English Rust V1 claim +- the deferred `V1.1` surfaces + Acceptance: -- `basis.json` records: - - `basis_branch` - - `basis_commit` - - the frozen I3.5 authority paths - - the five-command validation wall - - current `BENCH-ECOM`, `BENCH-SERVICE`, and `BENCH-CROSSLIB` starting truth - - the current bounded-generics and async/IO deferred posture from milestone - authority -- raw basis command outputs exist under `.runs/i7/basis/` -- Lane A and Lane B fork only from `basis_commit` +- `.runs/i8/preflight.json` names exactly the same five commands frozen by + `PLAN.md` +- the run records both `5d849d4` and the live execution head instead of + silently collapsing them +- no proof interpretation happens before preflight is written +- the parent writes `task/i8-p0-preflight-freeze` sentinel state under + `.runs/i8/sentinels/` -### WS-A (`task/i7-a1-bounded-generics-packet`) — worker lane A +### WS-A (`task/i8-a1-ecom-proof`) — worker lane A Branch and worktree: -- branch: `codex/i7-lane-a-generics` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a` +- branch: `ws/i8-ecom-proof` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom` Owned write set: -- worker-local draft packet only: - - `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a/.runs/i7-drafts/task-i7-a1-bounded-generics.md` -- no checked-in authority docs before Gate 2 +- worker-local mirrors only: + - `.runs/i8-worker/ecommerce.status.json` + - `.runs/i8-worker/ecommerce.status.exitcode` + - `.runs/i8-worker/ecommerce.export.json` + - `.runs/i8-worker/ecommerce.export.exitcode` + - `.runs/i8-worker/task-i8-a1-ecom-proof.sentinel.json` Required commands: ```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a/.runs/i7-drafts -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a rev-parse HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-a merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker +bash -lc ' +set -o pipefail +cargo run -p spec-cli -- status examples/ecommerce/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.status.json +cmd_status=$? +printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.status.exitcode +exit "$cmd_status" +' +bash -lc ' +set -o pipefail +cargo run -p spec-cli -- export examples/ecommerce/units | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.export.json +cmd_status=$? +printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.export.exitcode +exit "$cmd_status" +' ``` -Required packet contents: - -- recommendation: `admit` or `defer` -- user-value rationale -- exact authored boundary -- exact proof burden -- rejected broader expansions -- exact repo surfaces that would change if admitted - Acceptance: -- the packet recommends exactly one outcome -- the boundary fits in one paragraph -- the proof burden is believable on top of the frozen command wall -- the packet does not quietly widen into broad trait, lifetime, macro, or - abstraction-heavy support -- the packet cites the frozen basis commit and the relevant authority inputs -- the packet returns changed files, commands run, blockers, and assumptions in a - parent-readable summary +- `status` exits `0` +- `BENCH-ECOM` remains `benchmark_status: passing` +- `gate_status` remains `satisfied` +- `readability_review_status` remains `current` +- `export` remains `schema_version: 4` +- the export still projects `BENCH-ECOM` as a required positive benchmark with + the required molecule roster: + - `pricing/checkout_flow` + - `pricing/discount_strategy_checkout_flow` + - `pricing/discount_plus_tax` +- the worker returns only: + - changed files + - commands run and exit codes + - blockers + - assumptions +- the worker prompt must contain only: + - owned file set + - relevant `PLAN.md` excerpt + - required commands + - forbidden surfaces + - output-path contract +- completion is surfaced through the worker summary plus the worker-local mirror + sentinel, then parent acceptance updates the canonical task sentinel -### WS-B (`task/i7-b1-async-io-packet`) — worker lane B +### WS-B (`task/i8-b1-service-proof`) — worker lane B Branch and worktree: -- branch: `codex/i7-lane-b-async` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b` +- branch: `ws/i8-service-proof` +- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service` Owned write set: -- worker-local draft packet only: - - `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b/.runs/i7-drafts/task-i7-b1-async-io-packet.md` -- no checked-in authority docs before Gate 2 +- worker-local mirrors only: + - `.runs/i8-worker/service.status.json` + - `.runs/i8-worker/service.status.exitcode` + - `.runs/i8-worker/service.export.json` + - `.runs/i8-worker/service.export.exitcode` + - `.runs/i8-worker/task-i8-b1-service-proof.sentinel.json` Required commands: ```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b/.runs/i7-drafts -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b rev-parse HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-b merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker +bash -lc ' +set -o pipefail +cargo run -p spec-cli -- status examples/service/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.status.json +cmd_status=$? +printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.status.exitcode +exit "$cmd_status" +' +bash -lc ' +set -o pipefail +cargo run -p spec-cli -- export examples/service/units | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.export.json +cmd_status=$? +printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.export.exitcode +exit "$cmd_status" +' ``` -Required packet contents: - -- recommendation: `admit` or `defer` -- user-value rationale -- exact authored boundary -- exact proof burden -- rejected broader expansions -- exact repo surfaces that would change if admitted - Acceptance: -- the packet recommends exactly one outcome -- the boundary does not collapse Rust V1 into normal backend Rust -- the proof burden does not require a mechanics rewrite -- the packet does not quietly import framework lifecycle semantics as baseline -- the packet cites the frozen basis commit and the relevant authority inputs -- the packet returns changed files, commands run, blockers, and assumptions in a - parent-readable summary +- `status` exits `0` +- `BENCH-SERVICE` remains `benchmark_status: passing` +- `gate_status` remains `satisfied` +- `readability_review_status` remains `current` +- `export` remains `schema_version: 4` +- the export still projects `BENCH-SERVICE` as a required positive benchmark + with the required molecule roster: + - `billing/checkout_success_flow` + - `billing/checkout_declined_discount_flow` + - `billing/discount_strategy_quote_flow` +- the worker returns only: + - changed files + - commands run and exit codes + - blockers + - assumptions +- the worker prompt must contain only: + - owned file set + - relevant `PLAN.md` excerpt + - required commands + - forbidden surfaces + - output-path contract +- completion is surfaced through the worker summary plus the worker-local mirror + sentinel, then parent acceptance updates the canonical task sentinel -### WS-PARENT-1 (`task/i7-p1-packet-acceptance-and-decision-freeze`) — parent only, sequential +### WS-PARENT-1 (`task/i8-p1-positive-wall-acceptance`) — parent only Owned write set: -- `.runs/i7/packet-receipts.json` -- `.runs/i7/decision-freeze.json` - -Required actions: - -- read Lane A and Lane B returns against frozen basis -- compare both packets against `PLAN.md`, `docs/rust_v1_contract_stack.md`, and - the frozen I3.5 authority snapshot -- reject any packet that still says "maybe" -- reject any packet whose admitted slice cannot be named, bounded, and proven -- reject any pair of packets that disagree on shared milestone truth -- record accepted packet summaries in `packet-receipts.json` -- freeze the single final posture in `decision-freeze.json` - -`decision-freeze.json` must record: - -- `basis_commit` -- bounded-generics outcome -- async/IO outcome -- `BENCH-CROSSLIB` disposition -- whether Lane D exists -- if Lane D exists: - - admitted slice name - - owned code surfaces - - exact required proof commands +- `.runs/i8/evidence/ecommerce.status.json` +- `.runs/i8/evidence/ecommerce.status.exitcode` +- `.runs/i8/evidence/ecommerce.export.json` +- `.runs/i8/evidence/ecommerce.export.exitcode` +- `.runs/i8/evidence/service.status.json` +- `.runs/i8/evidence/service.status.exitcode` +- `.runs/i8/evidence/service.export.json` +- `.runs/i8/evidence/service.export.exitcode` +- `.runs/i8/tasks.json` +- `.runs/i8/session-log.md` +- `.runs/i8/sentinels/task-i8-a1-ecom-proof.json` +- `.runs/i8/sentinels/task-i8-b1-service-proof.json` +- `.runs/i8/sentinels/task-i8-p1-positive-wall-acceptance.json` + +Required parent actions: + +- review the two worker summaries and narrow diffs only +- copy or restage accepted worker-local outputs into the canonical `.runs/i8/` + evidence paths +- copy or restage the worker `.exitcode` files into the canonical evidence paths +- reject any lane output that lacks the expected benchmark, gate, readability, + or schema-version truth +- update canonical sentinels and `tasks.json` instead of relying on transcript + interpretation alone Acceptance: -- bounded generics has one frozen outcome -- async/IO has one frozen outcome -- there is no remaining ambient scope question for I7 -- Lane D is either explicitly absent or explicitly bounded +- both positive walls are accepted into canonical evidence paths +- both positive walls still teach the same narrow-core story frozen in I7 +- no authority docs are edited yet +- canonical sentinels record acceptance or rejection for both worker lanes -### WS-D (`task/i7-d1-conditional-proof`) — conditional worker lane D - -Start condition: - -- this lane exists only if `decision-freeze.json` admits one new bounded slice - and names exact proof commands -- if `decision-freeze.json` does not name exact proof commands, Lane D does not - start and Gate 2 is still open - -Branch and worktree: - -- branch: `codex/i7-lane-d-proof` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-d` +### WS-PARENT-2 (`task/i8-p2-workspace-inventory-confirmation`) — parent only Owned write set: -- only the code, tests, examples, and benchmark surfaces named in - `decision-freeze.json` +- `.runs/i8/evidence/workspace.status.json` +- `.runs/i8/evidence/workspace.status.exitcode` +- `.runs/i8/sentinels/task-i8-p2-workspace-inventory-confirmation.json` -Forbidden touches: +Required commands: -- `PLAN.md` -- `ORCH_PLAN.md` -- `docs/rust_v1_contract_stack.md` -- `.runs/i7/**` -- any surface outside the admitted bounded slice +```bash +bash -lc ' +set -o pipefail +cargo run -p spec-cli -- status . --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence/workspace.status.json +cmd_status=$? +printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence/workspace.status.exitcode +test "$cmd_status" -eq 1 +' +``` -Acceptance: +Expected interpretation: -- the admitted slice proves only the frozen boundary -- the commands named in `decision-freeze.json` actually run and are sufficient -- no additional broad support is smuggled in as proof scaffolding -- if the proof fails honestly, Lane D returns a blocker instead of widening the - slice or inventing new milestone scope +- exit code `1` is allowed and expected +- `scope_authority` must remain `inventory_only` +- `BENCH-CROSSLIB` must remain visible as `companion_negative_proof` +- `BENCH-CROSSLIB` must keep zero positive supported credit +- `BENCH-ECOM` and `BENCH-SERVICE` must still project as `passing` inside the + broader inventory surface +- intentionally non-green roots outside the positive wall must remain visible + rather than being trimmed to force green -### WS-C (`task/i7-c1-contract-ratification`) — worker lane C after Gate 2 +Acceptance: -Branch and worktree: +- repo-root inventory stays truthful without being misread as a ship gate +- the broad surface preserves negative visibility and positive-wall visibility + at the same time +- the archived `.exitcode` file records `1` and is treated as a success case + for this task -- branch: `codex/i7-lane-c-ratify` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c` +### WS-PARENT-3 (`task/i8-p3-authority-drift-ratification`) — parent only Owned write set: -- `PLAN.md` -- `ORCH_PLAN.md` -- `docs/rust_v1_contract_stack.md` -- any additional repo-facing closeout docs explicitly required by the frozen - I7 decisions +- `.runs/i8/authority-drift.md` +- `.runs/i8/session-log.md` +- `.runs/i8/sentinels/task-i8-p3-authority-drift-ratification.json` +- checked-in authority docs only if real drift exists: + - `PLAN.md` + - `ORCH_PLAN.md` + - `docs/rust_v1_contract_stack.md` + - `README.md` + - `DECISIONS.md` + - `CHANGELOG.md` + - `TODOS.md` + +Required comparison set: + +- `.runs/i8/evidence/ecommerce.status.json` +- `.runs/i8/evidence/ecommerce.export.json` +- `.runs/i8/evidence/service.status.json` +- `.runs/i8/evidence/service.export.json` +- `.runs/i8/evidence/workspace.status.json` +- `.runs/i7/decision-freeze.json` +- `.runs/i7/i8-handoff.json` -Dependencies: +Allowed outcomes: -- must start from the exact `decision_freeze_commit` -- if Lane D exists, Lane C may draft prose early but the parent must not merge - Lane C until Lane D proves the final command list +- no drift: + - write that all checked-in authority surfaces already agree with live truth +- doc drift only: + - patch only the prose that drifted -Required commands: +Disallowed outcomes: -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c rev-parse HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i7/lane-c merge-base --is-ancestor "$(git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse codex/i7-v1-scope-closure)" HEAD -``` +- keeping stale prose by reinterpreting live output +- adding new commands to rescue stale prose +- implying a post-I8 discovery milestone Acceptance: -- all checked-in authority surfaces teach the same frozen I7 story -- `I7 -> I8` is explicit everywhere -- deferred `V1.1` surfaces are explicit everywhere -- no doc teaches a checked-in `I9` -- no doc teaches post-I6 ambiguity as current truth -- the final checked-in validation section matches the frozen proof wall exactly - -### WS-PARENT-2 (`task/i7-p2-integration-and-closeout`) — parent only, sequential - -Owned write set: - -- integration branch on `codex/i7-v1-scope-closure` -- `.runs/i7/authority-merge.json` -- `.runs/i7/i8-handoff.json` +- every repo-facing authority surface teaches the same I8 story +- no checked-in doc weakens the I7 deferral line +- no checked-in doc implies that repo-root `status .` should be globally green -Required actions: +### WS-E (`task/i8-e1-conditional-blocker-repair`) — conditional only -- verify no authority input drifted since Gate 0 without being consciously - re-frozen -- merge Lane D first if it exists -- merge Lane C after proof-command truth is final -- resolve only straightforward merge mechanics -- bounce disagreements back to the owning lane instead of resolving creatively -- rerun the validation wall -- append any exact conditional proof commands to the validation wall before - closeout -- freeze the final I8 handoff +Activation rule: -Required commands: +- launch only if a live command, export surface, benchmark projection, or + authority surface contradicts the frozen I8 claim -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/spec diff --name-only -git -C /home/azureuser/__Active_Code/atomize-hq/spec merge --no-ff codex/i7-lane-d-proof -git -C /home/azureuser/__Active_Code/atomize-hq/spec merge --no-ff codex/i7-lane-c-ratify -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- export examples/ecommerce/units -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- status . --format json -``` +Possible owner: -If Lane D does not exist, skip the `codex/i7-lane-d-proof` merge and run the -existing five-command wall unchanged. +- parent for doc-only drift repair +- worker lane E in `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/blocker` + only if code or benchmark-read-side repair is narrowly required -Acceptance: - -- merged repo truth matches `decision-freeze.json` -- the validation wall is enumerable and current -- the final I8 handoff is a proof run, not another discovery milestone -- `authority-merge.json` records exactly which authority surfaces changed - -## Execution Order +Owned write set: -1. Parent runs Gate 0 basis freeze locally. -2. Lane A and Lane B run in parallel from the frozen basis commit. -3. Parent accepts or rejects both packets, then records Gate 2 decision freeze. -4. If both topics defer cleanly, launch Lane C only. -5. If one bounded slice is admitted and still needs proof, launch Lane D first - or overlap Lane C only on prose that cannot affect proof commands. -6. Parent merges proof work, then ratification work, then reruns the final wall. -7. Parent records the I8 handoff and closes the run. +- only the direct failing surface +- plus any rerun evidence needed to prove the repair +- plus `.runs/i8/sentinels/task-i8-e1-conditional-blocker-repair.json` if the + lane is activated -## Halt, Bounce, And Reopen Rules +Repair rules: -Parent must halt the run if: +- fix the direct blocker only +- rerun the affected command immediately +- rerun the full five-command wall before closeout +- stop and escalate if the repair would widen support, add commands, or alter + benchmark roles -- the live command wall no longer matches the I3.5 authority snapshot -- `basis_commit` changes after Gate 0 but before Lane A and Lane B fork -- any existing validation-wall command fails unexpectedly during basis capture -- `PLAN.md`, `docs/rust_v1_contract_stack.md`, or the I3.5 authority files - drift under the parent during the run and that drift was not consciously - re-frozen +Acceptance: -Parent must bounce a lane back to its owner if: +- the contradiction is removed without changing the frozen I8 scope +- the rerun evidence proves the fix directly +- the full five-command wall is green under the same interpretation rules as + the original plan -- the packet recommends more than one outcome -- the packet relies on "common Rust expectations" instead of named repo truth -- the packet names no exact proof burden -- Lane A and Lane B disagree on the shared milestone boundary and the conflict - is not settled in `decision-freeze.json` -- Lane C rewrites authority before decision freeze -- Lane C teaches a proof wall that Lane D has not yet proven -- Lane D touches surfaces outside the admitted slice -- any worker edits `.runs/i7/**` in the parent checkout +### WS-PARENT-4 (`task/i8-p4-final-closeout`) — parent only -Parent must reopen Gate 0 if: +Owned write set: -- basis evidence is missing, stale, or captured from mixed commits -- a worker did not branch from `basis_commit` +- `.runs/i8/closeout.json` +- `.runs/i8/session-log.md` +- `.runs/i8/sentinels/task-i8-p4-final-closeout.json` +- release-note surfaces only if WS-PARENT-3 or WS-E changed them -Parent must reopen Gate 2 if: +`closeout.json` must record: -- ratified docs conflict with frozen decisions -- Lane D proves a different slice than the one frozen -- new proof work changes the exact command list required for closeout +- milestone: `I8` +- final plain-English Rust V1 claim +- deferred `V1.1` surfaces +- branch and final commit +- plan-validated commit and execution head used for the run +- exact five-command verdicts +- references to all raw evidence files +- any doc files changed for drift repair +- whether blocker repair was activated +- final status: `done` or `blocked` -Parent must reopen Gate 4 if: +Acceptance: -- the merged tree no longer teaches one coherent `I7 -> I8` story -- the final validation wall omits newly required proof commands -- authority merge reveals a lane conflict that cannot be resolved literally from - `decision-freeze.json` +- a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` + without relying on conversation context +- the final verdict cites raw evidence, not memory or paraphrase ## Context-Control Rules -- Parent keeps live context limited to: +- Parent keeps only the authoritative working set live: - `PLAN.md` - - `docs/rust_v1_contract_stack.md` - - `.runs/i7/tasks.json` - - `.runs/i7/decision-freeze.json` once it exists - - the active integration diff summary -- Worker prompts must include exactly: - - task id - - lane branch and worktree - - fork commit - - owned write set - - forbidden touch surfaces - - exact relevant `PLAN.md` excerpt - - any frozen proof commands already known - - required return format -- Each worker receives only: - - its owned write set + - `ORCH_PLAN.md` + - `.runs/i8/tasks.json` + - `.runs/i8/session-log.md` + - `.runs/i8/sentinels/` + - the latest positive-wall acceptance summary + - the latest authority drift summary +- Each worker prompt contains only: + - its owned file set - the exact relevant `PLAN.md` excerpt - - frozen authority rules - required commands - forbidden touch surfaces - - the commit it must fork from -- Each worker returns only: + - output-path contract +- Each worker must return only: - changed files - commands run and exit codes - blockers - assumptions -- Parent summarizes worker returns into `.runs/i7/lanes/*.md` instead of - pulling full transcripts into working context. -- Workers do not write canonical `.runs/i7/**` state in the primary checkout. -- Close each worker after merge or rejection. -- If a worker needs broader context than its prompt packet, the parent pauses - and explicitly decides whether to widen that context instead of letting the - lane self-expand. - -## Tests, Validation, And Acceptance - -### Basis Integrity - -- `basis.json` must cite: - - `basis_branch` - - `basis_commit` - - the exact five-command wall - - the current deferred bounded-generics and async/IO posture - - the starting `BENCH-CROSSLIB` role -- raw basis evidence must exist for all five frozen commands -- all worker branches must descend from `basis_commit` - -### Packet Integrity - -- Lane A and Lane B must each return exactly one recommendation -- each packet must name: - - boundary - - rationale - - proof burden - - rejected broader expansions - - repo surfaces impacted if admitted -- neither packet may rely on ambient future work or an implied `I9` - -### Decision Integrity - -- `decision-freeze.json` must be the only frozen milestone posture -- it must record: - - bounded-generics outcome - - async/IO outcome - - `BENCH-CROSSLIB` disposition - - whether Lane D exists - - any exact added proof commands -- if Lane D exists, its allowed write set and trigger condition must be frozen - before that lane starts - -### Ratification Integrity - -- `PLAN.md`, `ORCH_PLAN.md`, and `docs/rust_v1_contract_stack.md` must agree on: - - what is in Rust V1 - - what defers to `V1.1` - - the `I7 -> I8` ladder - - the absence of a checked-in `I9` -- repo-facing closeout docs updated in I7 must teach the same frozen story - -### Conditional Proof Integrity - -- if Lane D does not exist, I7 remains a decision-and-ratification milestone -- if Lane D exists: - - it must prove only the admitted bounded slice - - its commands must be appended before closeout - - failure must return an honest blocker or force decision reopen; it must not - silently widen scope - -### Integration And Closeout Integrity - -- parent merges only after packet acceptance and decision freeze -- parent resolves only straightforward merge mechanics -- lane disagreements on truth are bounced back, not papered over in integration -- `authority-merge.json` and `i8-handoff.json` must reflect the merged truth -- the final run must leave I8 enumerable as a proof wall, not a discovery loop - -The minimum frozen validation wall for I7 closeout is: - -```bash -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- export examples/ecommerce/units -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- status . --format json -``` - -Closeout rules: - -- these five commands must remain present at closeout -- if I7 admits a new bounded slice, the parent must append that slice's exact - proof commands before declaring I7 done -- the appended commands must be recorded in: - - `.runs/i7/decision-freeze.json` - - `.runs/i7/i8-handoff.json` - - the final checked-in `ORCH_PLAN.md` validation wall if it changes - -Final acceptance for I7: - -- the repo has one explicit answer on bounded generics -- the repo has one explicit answer on async/IO -- the final Rust V1 line is truthful and bounded -- `BENCH-CROSSLIB` is either preserved or explicitly re-justified -- the command wall still matches the ratified claim -- `I8` is explicitly the final proof-run milestone -- no checked-in doc implies a checked-in `I9` -- `.runs/i7/` contains enough parent-owned evidence to replay how the decision - was frozen - -## Assumptions - -- `main` is the correct frozen basis branch when the run starts. -- The current I3.5 authority snapshot remains the command-wall source of truth. -- I7 is allowed to finish with explicit `V1.1` deferrals and no new code work. -- Lane D is exceptional and should not exist unless the parent can name one - newly admitted bounded slice and its exact proof burden. -- Repo-facing ratification after decision freeze may include docs beyond - `PLAN.md`, `ORCH_PLAN.md`, and `docs/rust_v1_contract_stack.md` only when the - frozen decisions make that update necessary. -- Worker-local packet drafts are disposable execution aids, not checked-in - deliverables. +- The parent reviews summaries plus narrow diffs only. It does not ingest full + worker transcripts into the main context. +- Completion state flows through worker summaries plus worker-local mirror + sentinels, then parent-owned canonical sentinel and `tasks.json` updates. +- Close each worker immediately after its evidence is accepted or rejected. +- Use completion sentinels or long waits, not tight polling. + +## Tests And Acceptance + +- Command wall + - the exact five commands from `PLAN.md` are the only proof wall + - all five raw outputs are archived under `.runs/i8/evidence/` + - all archived commands preserve real exit codes via the locked capture + pattern and sibling `.exitcode` files +- Positive proof walls + - `BENCH-ECOM` stays `passing` with `gate_status: satisfied` and + `readability_review_status: current` + - `BENCH-SERVICE` stays `passing` with `gate_status: satisfied` and + `readability_review_status: current` + - both positive exports remain `schema_version: 4` +- Broad inventory + - repo-root `status . --format json` remains `inventory_only` + - `BENCH-CROSSLIB` remains active companion negative proof with zero positive + supported credit + - broad inventory remains non-green by design without invalidating I8 +- Authority surfaces + - `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, `README.md`, + `DECISIONS.md`, `CHANGELOG.md`, and `TODOS.md` all teach the same bounded + Rust V1 story + - no checked-in doc implies `I9` +- Blocker discipline + - any repair remains narrow and direct + - no repair adds proof commands, benchmark roles, support rows, or new scope +- Workspace boundary + - the milestone remains a final proof run, not a mechanics rewrite or feature + expansion + +## Failure Modes + +| Failure mode | Consequence | Guard in this plan | +| --- | --- | --- | +| `PLAN.md` validated at `5d849d4` but execution runs from a disconnected head | the run no longer reflects the frozen authority basis | require `merge-base --is-ancestor 5d849d4 HEAD` during preflight | +| repo-root `status .` exit `1` is misread as failure | a truthful inventory surface blocks closeout for the wrong reason | require `scope_authority: inventory_only` interpretation instead of exit-code-only reasoning | +| `BENCH-CROSSLIB` starts earning positive credit | Rust V1 widens by read-side drift | require active visibility plus zero positive supported credit | +| positive `status` passes but positive `export` drifts | machine consumers and human readers see different truth | require both `status` and `export` for each positive wall | +| readability reviews go stale while status stays green | the plan overstates current readability-backed proof | require `readability_review_status: current` for both positive walls | +| docs still imply I7 is current or imply I9 exists | milestone ownership becomes ambiguous again | compare all repo-facing authority docs after live reruns | +| blocker repair expands into a mechanics rewrite | I8 silently becomes a new milestone | hard-stop any repair that alters the wall, benchmark roles, or deferred boundaries | + +Critical gap test: + +- if either positive benchmark loses `passing` or `satisfied`, I8 is not done +- if any authority surface still needs caveats absent from the frozen claim, I8 + is not done +- if the only way to "pass" is to reinterpret `inventory_only` as proof, I8 is + not done ## Immediate Next Move -If starting the run fresh: +Execute I8 in this order: + +1. create `.runs/i8/` and freeze the exact input set plus `5d849d4 -> HEAD` + ancestry +2. launch `Lane A` and `Lane B` in parallel worktrees +3. accept or reject the two positive proof reruns +4. run repo-root inventory confirmation in the parent checkout +5. ratify checked-in authority surfaces against live truth +6. repair only a real direct blocker if one exists +7. write `.runs/i8/closeout.json` with the final verdict -1. create `codex/i7-v1-scope-closure` -2. capture Gate 0 basis evidence under `.runs/i7/basis/` -3. fork Lane A and Lane B from `basis_commit` -4. freeze the decisions before any authority doc merge -5. create Lane D only if the frozen decision admits one exact bounded slice -6. ratify the repo-facing I7 story -7. rerun the validation wall and freeze the I8 handoff +Do not start by changing code. Start by proving whether the already-ratified +Rust V1 claim still holds on the live branch. From 37cdeeac960a202d4467bb8ed0eab7a66f50ee76 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sat, 23 May 2026 01:20:55 +0000 Subject: [PATCH 07/24] Complete I8 final proof run --- TODOS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODOS.md b/TODOS.md index 91f38ab..803d4ef 100644 --- a/TODOS.md +++ b/TODOS.md @@ -216,4 +216,4 @@ - [x] **I7: Rust V1 scope-decision closure** — Completed main (2026-05-22). The repo now freezes one explicit pre-I8 story: bounded generics defer to `V1.1`, Rust V1 stays synchronous-only so async/IO also defer to `V1.1`, `BENCH-CROSSLIB` remains the companion negative-proof wall, and the final I8 wall stays the existing five-command proof run. -- [ ] **I8: Rust V1 final proof run** — Rerun the frozen `BENCH-ECOM` plus `BENCH-SERVICE` proof wall, preserve the repo-root inventory-only status contract, and confirm the ratified docs plus benchmark roster still match the bounded Rust V1 claim without widening scope. +- [x] **I8: Rust V1 final proof run** — Completed main (2026-05-23). The frozen five-command wall still holds on the live branch: `BENCH-ECOM` and `BENCH-SERVICE` both reran as passing positive proof walls with current readability state, repo-root `status . --format json` remained the expected `inventory_only` non-green inventory surface with exit code `1`, and the bounded Rust V1 claim stayed narrow without widening scope. From 13a595709c1802a7f90a4d32e908ae83197b0928 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 02:15:38 +0000 Subject: [PATCH 08/24] Add core mechanisms guide and README pointer --- README.md | 1 + docs/core_mechanisms_guide_v0.1.md | 409 +++++++++++++++++++++++++++++ 2 files changed, 410 insertions(+) create mode 100644 docs/core_mechanisms_guide_v0.1.md diff --git a/README.md b/README.md index b07785e..6c72f6a 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ - [`PLAN.md`](PLAN.md): active implementation roadmap, with shipped milestone context below the current contract - [`DECISIONS.md`](DECISIONS.md): project-level decisions that stay stable across releases - [`TODOS.md`](TODOS.md): backlog and follow-up inventory +- [`docs/core_mechanisms_guide_v0.1.md`](docs/core_mechanisms_guide_v0.1.md): start here for the repo mental model; explains how kinds, backends, proof, semantic review, families, corpus, and benchmarks fit together - [`AGENTS.md`](AGENTS.md): agent workflow and machine-readable `spec` authoring loop - [`CLAUDE.md`](CLAUDE.md): lightweight routing rules for Claude/Codex sessions in this repo - [`docs/rust_v1_contract_stack.md`](docs/rust_v1_contract_stack.md): Rust V1 contract-stack index and the frozen I3.5 command wall diff --git a/docs/core_mechanisms_guide_v0.1.md b/docs/core_mechanisms_guide_v0.1.md new file mode 100644 index 0000000..af3a7bd --- /dev/null +++ b/docs/core_mechanisms_guide_v0.1.md @@ -0,0 +1,409 @@ +# spec — Core Mechanisms Guide +**Version:** v0.1 +**Status:** Active repo guide +**Date:** 2026-05-24 + +## Purpose + +This document is the repo's "start here" explanation for the main mechanisms +at play in `spec`. + +It exists because several different systems are active at once: + +- authored semantic units +- backend lowering and execution +- atom and molecule proof +- semantic review +- promoted semantic families +- corpus and recommendation analysis +- benchmark-based product claims + +These systems are related, but they are not the same thing. The goal of this +guide is to make the boundaries memorable so repo docs can point back here +instead of re-explaining the whole stack from scratch every time. + +## Short Version + +If you feel lost, ask these questions in order: + +1. What **kind** of unit is this? +2. Can it **lower and execute** on the backend I care about? +3. Does it have **current proof**? +4. Does **semantic review** understand its meaning? +5. If yes, which **family** is it in? +6. Does it count toward a **benchmark/product claim**? +7. If it is not understood yet, does the **corpus** suggest a new family to + promote? + +That is the main stack. + +## The Two Layers + +The cleanest separation in this repo is: + +### 1. Write-path truth + +This is the product's semantic-source system. + +It owns: + +- `*.unit.spec` +- `*.test.spec` +- validation +- normalization +- graph resolution +- generation +- build and test execution +- passports +- molecule evidence +- `status` and `export` projections + +This is the layer that changes semantic repo truth. + +### 2. Read-side interpretation + +This is everything that reads the write-path truth and interprets it. + +Examples: + +- semantic review +- benchmark accounting +- coverage and recommendation artifacts +- corpus-program decisions + +These consumers interpret the graph and its proof state. They do not define the +semantic graph itself. + +## The Main Questions + +Each unit can be asked several different questions. Most confusion comes from +mixing them together. + +| Question | Mechanism | Short meaning | +| --- | --- | --- | +| Is this shape legal `spec` source? | Core support | The authored unit shape is allowed and valid. | +| Can this run on Rust or TypeScript? | Backend support | A target-language lane can lower and execute it. | +| Did it actually prove? | Atom / molecule proof | Current local-test and molecule evidence exists and is fresh. | +| Does the reviewer understand its function meaning? | Semantic review support | The bounded semantic reviewer can classify the function honestly. | +| Which meaning bucket is it in? | Family support | The function matches one shipped semantic family. | +| Does it count toward a public claim? | Benchmark support | It earns positive credit toward a bounded product claim such as Rust V1. | +| What should we teach the reviewer next? | Corpus / recommendation | Checked-in examples create pressure for future family promotion. | + +## The Core Mechanisms + +### Kinds + +Kinds answer: + +> What sort of unit is this structurally? + +Current top-level authored kinds: + +- `function` +- `data` +- `sum` + +Kinds are part of `spec` core support. If the repo supports `kind:data`, that +means the authored seam shape is valid and can participate in the normal +pipeline. It does **not** automatically mean semantic review understands that +kind. + +### Seams + +In current repo language, a seam is usually a non-function unit shape that owns +shared semantics plus nested behavior. + +Examples: + +- `kind:data` +- `kind:sum` + +Seams are about the product model and authored shape. They are not the same as +semantic families. + +### Backends + +Backends answer: + +> Can this authored unit lower and execute on backend X? + +Today the important lanes are: + +- Rust +- bounded TypeScript + +Backend support is narrower than core support. A unit can be valid `spec` +source and still not be executable in every backend lane. + +The current TypeScript lane is intentionally smaller than Rust: + +- only bounded `kind:function` closure graphs +- only already-supported function-family shapes +- atom-only proof +- no molecule TypeScript execution +- no seam-kind TypeScript execution + +### Atom and Molecule Proof + +Proof answers: + +> Did this thing actually validate, build, and test successfully, and is that +> proof current? + +Two proof surfaces matter: + +- **atom proof**: local unit-owned tests from `local_tests` +- **molecule proof**: multi-unit interaction proof from `.test.spec` + +This is the main runtime truth layer. A unit can be structurally supported and +still have failing or stale proof. + +### Passports + +Passports answer: + +> What is this unit's stored proof record? + +Passports are derived per-unit artifacts. They store things like: + +- observed test results +- freshness anchors +- semantic review truth from the last proof refresh +- provenance + +Passports are about current per-unit proof state. They are not recommendation +artifacts and they are not benchmark declarations. + +### Semantic Review + +Semantic review answers: + +> Does the bounded reviewer understand this function's meaning shape? + +Important constraints: + +- it is intentionally narrow +- it is not arbitrary code understanding +- it is mainly first-class for `kind:function` +- only `spec test` refreshes semantic review truth + +Today it recognizes a small shipped function vocabulary such as arithmetic +leaves and wrapper pipeline forms. It can also explicitly mark a function as +unsupported. + +It does **not** currently imply general understanding of seam kinds like +`kind:data` or `kind:sum`. + +### Families + +Families answer: + +> If semantic review understands this function, what supported meaning bucket is +> it in? + +A family is a shipped semantic-review bucket, not a seam and not a benchmark. + +Examples: + +- `function.arithmetic_leaf.monotone_down_nonnegative.v1` +- `function.arithmetic_leaf.monotone_up.v1` +- `function.wrapper.pipeline.v1` + +Families are the current semantic-review vocabulary. + +### Promotion + +Promotion answers: + +> When does a candidate family become real shipped support? + +A family is not supported just because someone can name it or because the +corpus contains several examples of it. It becomes real shipped support only +after the promotion workflow proves and certifies it as an honest boundary. + +Promotion is what turns: + +- "we see this shape" + +into: + +- "the reviewer now supports this shape honestly" + +### Corpus + +The corpus answers: + +> What shapes are actually showing up in checked-in examples and regressions? + +The corpus is an observation surface. It is used to detect: + +- already-supported coverage +- unsupported pressure +- repeated adjacent shapes +- whether a future family might be worth promotion + +The corpus does **not** itself expand semantic-review capability. + +### Coverage and Recommendation Artifacts + +These artifacts answer: + +> Given the corpus, what does the repo currently observe and what should happen +> next? + +Examples: + +- `coverage.latest.json` +- `recommendation.latest.json` +- `corpus-program-decision.latest.json` + +These are read-side analysis outputs. They do not define repo truth; they +interpret it. + +### Benchmarks + +Benchmarks answer: + +> Which selected cases count toward a bounded public product claim today? + +Benchmarks are curated proof walls, not authored semantic source. + +For Rust V1, they back claims like: + +- which roots are proof-authoritative +- which cases count as positive credit +- which negative walls stay visible without earning positive credit + +Benchmarks read existing proof inputs such as passports and molecule evidence. +They do not mint those proofs themselves. + +## What "Supported" Actually Means + +"Supported" means different things in different layers. Do not collapse them +into one flag. + +### Core support + +Can `spec` author, validate, and run this shape honestly? + +### Backend support + +Can backend X lower and execute this shape? + +### Proof support + +Do we have current atom and molecule evidence? + +### Semantic support + +Does the bounded reviewer understand this function meaning? + +### Family support + +Is this function in one of the currently shipped family buckets? + +### Product-claim support + +Does this case count toward a benchmark-backed public claim such as Rust V1? + +### Future-support pressure + +Does the checked-in corpus suggest the next family that should be promoted? + +## The Most Common Confusions + +### Family vs seam + +These are different. + +- **Family**: a semantic-review bucket for function meaning +- **Seam**: a product/unit-shape concept such as `kind:data` or `kind:sum` + +Example: + +- `pricing_quote` being a `data` seam means `spec` core supports that unit + shape. +- It does **not** mean semantic review supports data seams. +- `apply_tax` being in `function.arithmetic_leaf.monotone_up.v1` means semantic + review supports that function family. + +### Valid proof vs semantic support + +A unit can prove successfully without semantic review understanding it. + +That is why a seam can be: + +- valid in Rust +- benchmark-positive +- current in proof + +while still remaining unsupported by semantic review. + +### Corpus pressure vs capability + +The corpus can show that a shape is common without teaching the reviewer how to +understand it. + +More examples increase pressure. They do not automatically increase semantic +understanding. + +### Benchmarks vs source truth + +Benchmarks do not define what the unit is. They define how some selected units +count toward a public product claim. + +## A Simple Flow + +When a new authored unit enters the repo, the clean mental flow is: + +1. Author the unit in `*.unit.spec`. +2. Validate it as legal `spec` source. +3. Lower and execute it on the backend lane being used. +4. Record atom and molecule proof into passports and evidence files. +5. If it is a function, semantic review may classify it into a supported family + or mark it unsupported. +6. If the unit is part of a benchmark roster, benchmark accounting may count it + toward a public claim. +7. Separately, checked-in units may feed corpus analysis that suggests future + family-promotion work. + +That is one flow with several distinct readers, not one monolithic support +engine. + +## Where To Look + +Use this map when you want the source of truth for a concept: + +| If you want to understand... | Start here | +| --- | --- | +| the core authored model and workflow | [`README.md`](../README.md) | +| semantic-family capability, corpus, and promotion | [`semantic_family_capability_corpus_guide_v0.1.md`](./semantic_family_capability_corpus_guide_v0.1.md) | +| the current Rust V1 benchmark and claim boundary | [`rust_v1_contract_stack.md`](./rust_v1_contract_stack.md) | +| long-range product vision | [`north_star_v0.2.md`](./north_star_v0.2.md) | +| high-level system architecture | [`high_level_technical_architecture_v0.2.md`](./high_level_technical_architecture_v0.2.md) | +| milestone sequencing and release shape | [`roadmap_and_release_shape_v0.1.md`](./roadmap_and_release_shape_v0.1.md) | + +## One-Page Cheat Sheet + +Keep this version in your head: + +- **Kinds / seams** answer: what is this unit structurally? +- **Backends** answer: can it run on Rust / TypeScript? +- **Passports / evidence** answer: did it prove? +- **Semantic review / families** answer: does the reviewer understand this + function shape? +- **Corpus / recommendation** answer: what should we teach the reviewer next? +- **Benchmarks** answer: what can we publicly claim today? + +## Non-Goals + +This document does not try to: + +- replace milestone plans +- replace the README command reference +- define current benchmark rosters exhaustively +- define every current family packet in detail +- become the only architecture doc in the repo + +Its job is narrower: keep the mental model clean enough that the deeper docs +stay readable. From 930b108de7cf9de1a49270e7da9ceca2617f9a37 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 04:28:41 +0000 Subject: [PATCH 09/24] Update plan review report --- examples/ecommerce/units/money/round.spec.passport.json | 2 +- .../ecommerce/units/pricing/apply_discount.spec.passport.json | 2 +- examples/ecommerce/units/pricing/apply_tax.spec.passport.json | 2 +- .../ecommerce/units/pricing/calculate_total.spec.passport.json | 2 +- .../pricing/calculate_total_guarded_tax.spec.passport.json | 2 +- .../units/pricing/discount_strategy.spec.passport.json | 2 +- .../ecommerce/units/pricing/pricing_quote.spec.passport.json | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/ecommerce/units/money/round.spec.passport.json b/examples/ecommerce/units/money/round.spec.passport.json index ee30b52..6db7c53 100644 --- a/examples/ecommerce/units/money/round.spec.passport.json +++ b/examples/ecommerce/units/money/round.spec.passport.json @@ -13,7 +13,7 @@ }, "deps": [], "local_tests": [], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/money/round.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json index 9f3b715..bfbb172 100644 --- a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json @@ -28,7 +28,7 @@ "expect": "apply_discount(Decimal::new(10000, 2), Decimal::new(10, 2)) == Decimal::new(9000, 2)" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/apply_discount.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json index b7cc961..ce187ed 100644 --- a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json @@ -27,7 +27,7 @@ "expect": "apply_tax(Decimal::new(10000, 2), Decimal::new(725, 4)) == Decimal::new(10725, 2)" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/apply_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json index f9b3244..26cecd9 100644 --- a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/calculate_total.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json index 57a2203..92ed8f7 100644 --- a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total_guarded_tax(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/calculate_total_guarded_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json index fb69cd8..a691efd 100644 --- a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json +++ b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json @@ -143,7 +143,7 @@ "expect": "DiscountStrategy::None.fixed_amount_capped_behavior_holds()" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json index b650e51..b4c107c 100644 --- a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json +++ b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json @@ -100,7 +100,7 @@ "expect": "PricingQuote::new(rust_decimal::Decimal::new(10000, 2), rust_decimal::Decimal::new(10, 2), rust_decimal::Decimal::new(725, 4)).total() == rust_decimal::Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-21T23:49:29Z", + "generated_at": "2026-05-24T02:18:26Z", "source_file": "examples/ecommerce/units/pricing/pricing_quote.unit.spec", "evidence": { "build_status": "pass", From 563ba7de6117f646573f914d4f329ec6de1f2aa3 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 04:42:27 +0000 Subject: [PATCH 10/24] Add post-I8 roadmap review design --- README.md | 599 +++++++---------------------- docs/core_mechanisms_guide_v0.1.md | 170 +++++++- 2 files changed, 296 insertions(+), 473 deletions(-) diff --git a/README.md b/README.md index 6c72f6a..a718d88 100644 --- a/README.md +++ b/README.md @@ -1,164 +1,79 @@ # spec -`spec` is a Rust workspace for authoring semantic units in `*.unit.spec` files, validating them with JSON Schema, normalizing them into internal IR, and generating readable Rust source. +`spec` is a Rust-first semantic-source workflow for code that you want to be +explicit, machine-checkable, and provable. -## Workflow +You author units in `*.unit.spec` files, run a tight validate/build/test loop, +generate readable Rust, and keep proof artifacts that say what actually passed. +The project is built for AI-assisted and human-assisted development where "it +probably works" is not a good enough answer. -1. Author a `.unit.spec` file. -2. Validate it with the CLI. -3. Build: `spec build` validates, generates, and compiles in one step. -4. Test: `spec test` runs the full pipeline and writes observed evidence to unit passports plus molecule evidence artifacts. -5. Export: `spec export` emits a machine-readable JSON bundle for downstream tooling. -6. Plan: `spec plan validate` and `spec plan export` turn authored change intent into truthful local-library impact data. +## What spec is -## Workspace +At a high level, `spec` gives you: -- `spec-core`: parsing, validation, normalization, generation, pipeline, and export primitives -- `spec-cli`: CLI for `validate`, `generate`, `build`, `test`, `export`, and `plan` -- `examples/ecommerce`: a small single-library example with pricing units -- `examples/shared-spec`, `examples/shared-crate`, `examples/crosslib-app`: a direct sibling-library example for `[libraries]` and `shared::...` deps +- semantic source files instead of raw implementation as the only source of truth +- generated Rust from explicit contracts +- atom and molecule proof surfaces with tracked evidence +- machine-readable `validate`, `status`, and `export` outputs for agents and tools +- a bounded, honest product claim surface instead of fuzzy support language -## Project docs +This repo is the implementation of that workflow, plus the example libraries and +proof walls used to keep the claims honest. -- [`CHANGELOG.md`](CHANGELOG.md): shipped release history -- [`PLAN.md`](PLAN.md): active implementation roadmap, with shipped milestone context below the current contract -- [`DECISIONS.md`](DECISIONS.md): project-level decisions that stay stable across releases -- [`TODOS.md`](TODOS.md): backlog and follow-up inventory -- [`docs/core_mechanisms_guide_v0.1.md`](docs/core_mechanisms_guide_v0.1.md): start here for the repo mental model; explains how kinds, backends, proof, semantic review, families, corpus, and benchmarks fit together -- [`AGENTS.md`](AGENTS.md): agent workflow and machine-readable `spec` authoring loop -- [`CLAUDE.md`](CLAUDE.md): lightweight routing rules for Claude/Codex sessions in this repo -- [`docs/rust_v1_contract_stack.md`](docs/rust_v1_contract_stack.md): Rust V1 contract-stack index and the frozen I3.5 command wall -- [`examples/ecommerce/README.md`](examples/ecommerce/README.md): local example walkthrough -- [`examples/crosslib-app/README.md`](examples/crosslib-app/README.md): cross-library example walkthrough -- [`docs/north_star_v0.2.md`](docs/north_star_v0.2.md), [`docs/high_level_technical_architecture_v0.2.md`](docs/high_level_technical_architecture_v0.2.md), and [`docs/roadmap_and_release_shape_v0.1.md`](docs/roadmap_and_release_shape_v0.1.md): historical design context from the pre-ship planning phase -- [`.implemented/`](.implemented/): archived milestone release plans and early design artifacts +## What you need -## Quickstart +- Rust `1.89.0` +- `cargo` +- this repo checked out locally -```bash -cargo test -cargo run -p spec-cli -- validate examples/ecommerce/units -cargo run -p spec-cli -- generate examples/ecommerce/units -``` +The pinned toolchain lives in [`rust-toolchain.toml`](rust-toolchain.toml). -After installation, the binary is `spec`: +If you want a local binary instead of `cargo run`, install from the repo root: ```bash -cargo install spec-cli -spec validate examples/ecommerce/units -spec generate examples/ecommerce/units +cargo install --path spec-cli ``` -## Benchmarks (M68) +## The core loop -Rust V1 benchmark mechanics are repo-root read surfaces, not authored spec truth. The authored benchmark registry lives in `benchmarks/labels.json`; readability review anchors live in `benchmarks/reviews/*.readability.review.json`; and `spec benchmark snapshot ` writes derived snapshots under `benchmarks/snapshots/`. +If you are new here, do this first. -The frozen I8 proof wall after I7 is: +From the repo root: ```bash +cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/apply_tax.unit.spec --format json +cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/apply_tax.unit.spec +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy_checkout_flow.test.spec cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- export examples/ecommerce/units -cargo run -p spec-cli -- status examples/service/units --format json -cargo run -p spec-cli -- export examples/service/units -cargo run -p spec-cli -- status . --format json -``` - -Repo-root surfaces remain available, but they have a narrower contract: - -- `cargo run -p spec-cli -- status . --format json` is a supported broad-scope inventory surface only. Treat its scope authority as `inventory_only`, not as the fresh-clone proof default. -- `cargo run -p spec-cli -- export .` is unsupported for this workspace shape and must fail with `SPEC_UNSUPPORTED_SCOPE` instead of emitting a fake aggregate bundle. - -The seeded roster is: - -- `BENCH-ECOM`: active positive benchmark rooted at `examples/ecommerce/units` -- `BENCH-CROSSLIB`: active companion-negative benchmark rooted at `examples/crosslib-app/units` -- `BENCH-SERVICE`: active positive benchmark rooted at `examples/service/units` - -The ratified Rust V1 line after I7 is: - -- in: synchronous supported function families plus plain data and sum seams, proven by `BENCH-ECOM` and `BENCH-SERVICE` -- deferred to `V1.1`: bounded generics -- deferred to `V1.1`: async flows, runtime adapters, and IO-owned boundaries -- preserved negative wall: `BENCH-CROSSLIB` stays visible but never counts as positive supported credit - -Path scope is intentional: - -- Exact benchmark-root scopes are the proof-authoritative default and emit full projections with digests, benchmark status, gate status, and readability review state. -- Broad repo-root scope still emits a full projection, but only as `inventory_only` reporting. -- Namespace and single-file scopes emit partial projections only. Partial scope never emits positive supported credit, even when the covered carrier is individually `valid`. -- Companion-negative cases stay visible but never count as positive supported credit. - -Writer versus reader wall: - -- `spec status` and `spec export` only project benchmark truth; they do not write passports, molecule evidence, or benchmark proof. -- `spec benchmark snapshot ` writes only snapshot artifacts under `benchmarks/snapshots/`. -- `.spec.passport.json` and `*.test.evidence.json` remain the read-only proof inputs for benchmark accounting during M68. - -`spec export` now emits `schema_version: 4` and additively includes top-level `benchmarks[]` under the same contract as `spec status` for the same scope. - -## Bounded TypeScript lane (M61) - -`spec` now exposes one bounded TypeScript execution lane. It is intentionally narrow: - -M61 extends the bounded Bun-backed TypeScript lane to recursive local-plus-cross-library closure across the already-supported function families, while preserving family-specific direct-dep contracts, additive proof, atom-only execution, and the broader bans on arbitrary 4+ topology parity and molecule TypeScript execution. - -- Bun is the only TypeScript prerequisite. The lane shells out to `bun`; no alternate Node, npm, or tsx contract is supported. -- Eligible roots are `kind: function` specs whose reachable closure resolves from the loaded unit set, classifies to already-supported semantic-review function families, and authors non-empty `body.typescript` at every reachable unit. -- Closure collection is semantic-review-driven and dep-driven. It recurses across same-tree and direct sibling-library deps, dedupes repeated local-plus-cross-library subgraphs, and excludes unrelated loaded units from the generated TypeScript tree. -- The preserved portability exceptions remain in place for direct cross-library roots. A `function.arithmetic_leaf.monotone_up.v1` root may still use exactly one direct cross-library helper dep that classifies to `function.helper.identity_passthrough.v1`, authors non-empty `body.typescript`, and resolves from the loaded unit set and generated tree. -- The preserved direct cross-library wrapper lane remains frozen to the `function.wrapper.pipeline.v1` family with the direct dep tuple `function.arithmetic_leaf.monotone_down_nonnegative.v1` then `function.arithmetic_leaf.monotone_up.v1`. -- The preserved direct cross-library chain3 lane remains frozen to the `function.wrapper.pipeline.chain3.v1` family with the direct dep tuple `function.wrapper.pipeline.v1` or `function.wrapper.pipeline.chain3.v1` in slot 1, then `function.arithmetic_leaf.monotone_up.v1`, then `function.arithmetic_leaf.monotone_down_nonnegative.v1`. -- Family-specific direct dep contracts remain frozen even when closure collection recurses across library boundaries. The M60 required-arg wrapper family stays bounded to its current supported shape rather than widening the lane to arbitrary wrapper argument forms. -- Rust remains the default target. TypeScript proof is additive only; it writes `target_proofs.typescript` without replacing the Rust proof surface. -- TypeScript execution is atom-only. Only `local_tests` run in this lane; `.test.spec` molecule tests remain unsupported for `--target-language typescript` and fail before Bun runs. -- The accepted `local_tests.expect` grammar is deliberately small: `(Decimal::new(int, scale), ...) == Decimal::new(int, scale)`. The left-hand side must be a direct call to the current unit, and both the arguments and expected value must use integer-literal `Decimal::new(...)` forms. - -Commands available in this lane: - -```bash -spec generate --target-language typescript -spec build --target-language typescript -spec test --target-language typescript -spec status --target-language typescript ``` -`spec status --target-language typescript` reports target-specific proof only. In M61, a root-level status over a mixed example like `examples/ecommerce` or `examples/crosslib-app` may stay non-green unless every unit in scope is either freshly proven in the bounded recursive local-plus-cross-library lane, covered by the preserved portability contracts, or truthfully remains outside those lanes. - -The generated helper filenames remain frozen in M61: - -- `__spec_ts/runtime.ts` -- `__spec_ts/build_entry.ts` -- `__spec_ts/local_tests.ts` - -Commands and surfaces that do not widen for M61: - -- `spec validate` does not accept `--target-language` -- `spec export` does not accept `--target-language` -- The broader TypeScript oceans still explicitly deferred are arbitrary authored 4+ direct-dep topology parity, new semantic-family promotion, molecule TypeScript execution, and seam-kind TypeScript execution. +What each step tells you: -## Spec format +1. `validate` answers: is the authored source legal `spec` input? +2. `build` answers: can the backend lower and compile it? +3. `test` answers: did it actually prove, and did it refresh the proof record? +4. `status` answers: what does the repo project as true right now? -Each unit is a YAML document with these common required fields: +Files that change during that loop: -- `id`: hierarchical unit id like `pricing/apply_discount` -- `kind`: authored unit shape, currently `function`, `data`, or `sum` -- `intent.why`: why the unit exists +- generated Rust under `examples/ecommerce/src/generated/` +- unit proof in `*.spec.passport.json` +- molecule proof in `*.test.evidence.json` -Kind-specific authored fields: +If you install the binary, the command name is `spec`, but the repo-native +`cargo run -p spec-cli -- ...` flow is the safest place to start. -| `kind` | Required authored fields | Optional authored fields | Forbidden top-level fields | -| --- | --- | --- | --- | -| `function` | `contract`, `body.rust` | `deps`, `imports`, `local_tests`, `links` | none | -| `data` | `data.fields`, one or more `constructors`, one or more `methods` | `local_tests`, `links`, `backends.rust` | `contract`, `deps`, `imports`, `body.rust` | -| `sum` | `sum.variants`, one or more `methods` | `local_tests`, `links`, `backends.rust` | `contract`, `deps`, `imports`, `body.rust`, `constructors` | +## A tiny example -For `kind: function`, `spec` generates the complete `pub fn` signature from `contract.inputs` and `contract.returns`. A minimal unit with a contract looks like: +This is the shape of a simple function unit: ```yaml id: pricing/apply_tax kind: function intent: - why: Add sales tax to a subtotal using a rate expressed as a decimal fraction. + why: Add sales tax to a subtotal. contract: inputs: subtotal: Decimal @@ -169,379 +84,139 @@ imports: body: rust: | { - let taxed = subtotal + subtotal * rate; - round(taxed) - } -``` - -This generates: - -```rust -pub fn apply_tax(subtotal: Decimal, rate: Decimal) -> Decimal { - let taxed = subtotal + subtotal * rate; - round(taxed) -} -``` - -For `kind: data`, one `.unit.spec` file authors a top-level data seam with shared fields plus one or more nested constructors and one or more nested methods. A minimal seam based on the canonical M50 `pricing/pricing_quote` example looks like: - -```yaml -id: pricing/pricing_quote -kind: data -intent: - why: Quote a checkout total from subtotal plus discount and tax rates. -data: - fields: - subtotal: - type: rust_decimal::Decimal - discount_rate: - type: rust_decimal::Decimal - tax_rate: - type: rust_decimal::Decimal -constructors: - - id: new - intent: - why: Create a quote from explicit subtotal and rates. - contract: - inputs: - subtotal: rust_decimal::Decimal - discount_rate: rust_decimal::Decimal - tax_rate: rust_decimal::Decimal - initializes: - subtotal: subtotal - discount_rate: discount_rate - tax_rate: tax_rate -methods: - - id: discounted_subtotal - intent: - why: Return the discounted subtotal before tax. - receiver: shared_ref - contract: - returns: rust_decimal::Decimal - deps: - - pricing/apply_discount - lowering: - rust: - body: | - { - apply_discount(self.subtotal, self.discount_rate) - } - - id: total - intent: - why: Return the final checkout total after discount and tax. - receiver: shared_ref - contract: - returns: rust_decimal::Decimal - deps: - - pricing/apply_tax - lowering: - rust: - body: | - { - apply_tax(self.discounted_subtotal(), self.tax_rate) - } -backends: - rust: - derives: - - Clone - - Debug - - PartialEq -``` - -`kind: data` keeps shared semantics in `data.fields`, `constructors`, and `methods`. Rust-specific lowering stays inside `methods[].lowering.rust.body` and `backends.rust`. - -`kind: sum` follows the same seam boundary: keep shared semantics in `sum.variants` and `methods`. Enum cases are authored via `sum.variants`, not `constructors`. Rust-specific lowering stays inside `methods[].lowering.rust.body` and `backends.rust`. - -## Migrating from 0.2.x - -In 0.2.x, `body.rust` contained the full function declaration: - -```yaml -# 0.2.x format (no longer valid) -body: - rust: | - pub fn apply_tax(subtotal: Decimal, rate: Decimal) -> Decimal { - let taxed = subtotal + subtotal * rate; - round(taxed) - } -``` - -In 0.3.0, strip the `pub fn name(params) -> ReturnType` line and keep only the body block. Move the parameter names and types into `contract.inputs`, and the return type into `contract.returns`: - -```yaml -# 0.3.0 format -contract: - inputs: - subtotal: Decimal - rate: Decimal - returns: Decimal -body: - rust: | - { - let taxed = subtotal + subtotal * rate; - round(taxed) + subtotal + subtotal * rate } ``` -Running `spec validate` on a 0.2.x unit will emit a clear migration error pointing to the file. - -## Example +That source is not just documentation. It is the authored input to validation, +generation, proof, and read-side status/export surfaces. -The ecommerce example demonstrates the canonical M13 migration wedge alongside the existing pricing units, three molecule tests, one checked-in plan artifact, and tracked molecule evidence so the shipped example stays green on a fresh clone: +## Vocabulary starter -- `money/round` -- `pricing/apply_discount` -- `pricing/apply_tax` -- `pricing/calculate_total` -- `pricing/discount_strategy` (`kind: sum`) -- `pricing/pricing_quote` (`kind: data`) -- `pricing/checkout_flow` -- `pricing/discount_strategy_checkout_flow` -- `pricing/discount_plus_tax` -- `plans/refactors/checkout-tax-refactor.plan.spec` -- `examples/ecommerce/src/raw_baseline/pricing/discount_strategy.rs` (hand-written Rust baseline for the canonical pricing sum seam) -- `examples/ecommerce/src/raw_baseline/pricing/pricing_quote.rs` (hand-written Rust baseline for the canonical pricing data seam) +These are the words that matter on day 1. -Recorded adversarial calibration for the canonical wedge is locked: `pricing/discount_strategy` scored `19`, `pricing/pricing_quote` scored `16`, and `pricing/discount_plus_tax` scored `14`. Canonical wedge remains `pricing/discount_strategy`. +| Term | Meaning | Why you care | +| --- | --- | --- | +| unit | One authored semantic source file, usually `*.unit.spec` | It is the basic building block. | +| kind | The top-level unit shape: currently `function`, `data`, or `sum` | It tells you what the unit is structurally. | +| seam | A non-function shared semantic shape such as `kind:data` or `kind:sum` | It helps explain why seam behavior is different from function-family behavior. | +| atom test | A unit-local test from `local_tests` | It proves one unit in isolation. | +| molecule test | A `.test.spec` interaction test across multiple units | It proves behavior across unit boundaries. | +| passport | The co-located `.spec.passport.json` proof record for a unit | It stores current unit proof truth and freshness anchors. | +| semantic review | The bounded classifier that can understand some `kind:function` meanings | It is intentionally narrow, not general code understanding. | +| family | A shipped semantic-review bucket such as `function.wrapper.pipeline.v1` | It tells you which supported function meaning shape a unit matches. | +| benchmark | A curated proof wall used for bounded public product claims | It answers what the repo can honestly claim today. | -The example crate is intentionally minimal. It provides a realistic place to keep unit specs, hand-written Rust baselines for the M12 and M13 migration wedges, and a Rust project scaffold that can host generated output. The checked-in `pricing/*.test.evidence.json` files are generated artifacts for this canonical example, not hand-authored source. +## The mental model -## Commands +Most confusion in this repo comes from asking one overloaded "is this supported?" +question. Break it into four smaller questions: -```bash -spec validate # directory or single .unit.spec: schema + semantic validation -spec validate --no-strict # downgrade missing deps to warnings -spec validate --format json # machine-readable JSON output for agents -spec generate # directory only: emit .rs files (default: {crate_root}/src/generated) -spec generate --output # explicit output directory - -spec build # directory only: validate → generate → cargo build -spec build --output # explicit output directory -spec test # directory, single .unit.spec, or single .test.spec -spec test --output # explicit output directory for directory-scoped test runs -spec test # scope to a single unit (filter by module path) -spec test # run one molecule test and write only its .test.evidence.json - -spec status # directory or single .unit.spec: per-root unit and molecule-test health -spec status --format json # machine-readable status for agents - -spec export # directory or single .unit.spec: emit JSON bundle to stdout -spec export --output # write JSON bundle to file - -spec plan validate # validate one .plan.spec file and compute local impact -spec plan validate --format json # machine-readable plan validation + computed impact -spec plan export # emit dedicated plan bundle to stdout -spec plan export --output # write dedicated plan bundle to file -``` - -Canonical ecommerce example loop from the repo root: - -```bash -cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/discount_strategy.unit.spec --format json -cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated -cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy.unit.spec -cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy_checkout_flow.test.spec -cargo run -p spec-cli -- status examples/ecommerce/units --format json -cargo run -p spec-cli -- export examples/ecommerce/units -``` +1. Is the authored source valid? +2. Can the backend lower and run it? +3. Do we have current proof? +4. What, if anything, can the repo publicly claim from that proof? -`validate` checks schema and semantic rules. `--no-strict` downgrades missing internal deps to warnings for validation only. `generate` always remains strict, is directory-scoped only, and emits `.rs` files under the output directory while managing `mod.rs` files plus the `.spec-generated` safety marker. +Those are different layers. -`spec build` and directory-scoped `spec test` wrap the full pipeline so you can validate, generate, and compile in one step. `spec build` is directory-scoped only. `spec test` updates each unit's `.spec.passport.json` with observed local-test evidence and writes co-located `*.test.evidence.json` artifacts for molecule tests. Passports persist a `freshness_anchor` snapshot as the proof anchor from the last unit test run, while `freshness` is a live projection against the current spec. Marked seam passports may also carry additive `escape_hatch_gate` metadata. In M14 that gate requires both `atom` and `molecule` proof; `atom` is present only when the authored local tests pass and the seam's projected freshness is still current, and an open gate uses a stable reason like `missing required escape-hatch proof: molecule`. +The clean split is: -Semantic review for `kind:function` is bounded to a small shipped family vocabulary, not arbitrary function understanding. The current supported function family keys are `function.helper.identity_passthrough.v1`, `function.arithmetic_leaf.monotone_down_nonnegative.v1`, `function.arithmetic_leaf.monotone_up.v1`, `function.wrapper.pipeline.normalized_required_arg.v1`, `function.wrapper.pipeline.v1`, and `function.wrapper.pipeline.chain3.v1`. In the canonical ecommerce and TypeScript fixture flows, `money/round`, `pricing/apply_discount`, `pricing/apply_tax`, `pricing/calculate_total`, and the bounded chain3 forms refresh to those family keys when their authored and executable shapes fit honestly. M60 adds one supported wrapper family for apply_tax(discounted, tax_rate.max(Decimal::ZERO)); broader required-argument expressions remain unsupported. +- write-path truth: authored specs, validation, generation, build/test, passports, molecule evidence +- read-side interpretation: semantic review, status/export projection, benchmark accounting, recommendation analysis -M20 also makes unsupported-function truth explicit. The public fields are exactly `semantic_review.support_status`, `semantic_review.unsupported_reason_codes`, and `semantic_review.rewrite_hints`. New supported reviews write `support_status: supported`; unsupported function reviews write `support_status: unsupported`. Consumers should branch on `semantic_review.support_status == "unsupported"` instead of inferring unsupported state from `verdict` or `evaluator_scope`, though legacy reviews without `support_status` still fall back to `evaluator_scope` plus `unsupported.*.v1` compatibility-key inference. +If you keep that split in your head, the rest of the docs get much easier. -Unsupported near-miss function shapes remain additive-only, health-neutral, and keyed as `unsupported.function.v1`. Only `spec test` refreshes semantic review truth. `spec build`, `spec generate`, `spec status`, and `spec export` only project stored truth; they do not mint new supported-function semantic review. Fresh unsupported function proof is preserved on read-side surfaces such as `spec status` and `spec export`, while stale unsupported function proof is dropped there and only the underlying freshness or stale health remains visible. +## What to ignore at first -When you pass a single `.unit.spec` file to `spec validate`, `spec test`, or `spec export`, the CLI stays scoped to that exact unit. When you pass a single `.test.spec` file to `spec test`, the CLI runs only that molecule test and writes only that test's evidence artifact. Sibling `.test.spec` files are otherwise loaded for directory invocations. +If you are just authoring or fixing a unit, do not start with: -Single-file `spec test` runs generate Rust into an isolated internal build surface. They do not rewrite your checked-out `src/generated/` tree, and `--output` is accepted only for directory-scoped `test` runs. +- benchmark roster details +- milestone history +- recommendation and corpus-program decisions +- the full bounded TypeScript lane contract -`spec export` emits a machine-readable JSON bundle containing all units, passports, dependency graph edges, and warnings for any passports that could not be read. Stored `escape_hatch_gate` metadata may appear in passports, but live `spec status` and `spec export` recompute the gate from current passport freshness and molecule evidence rather than trusting the stored field blindly. Canonical seam `proof_coverage` also adds the `molecule` surface only when a covering molecule evidence artifact is both current and passing. +Start with the core loop. Come back to the deeper surfaces once you need to +explain a status projection, a semantic-review result, or a public product claim. -`spec plan validate` and `spec plan export` are single-file commands. They accept exactly one `.plan.spec` file, resolve the enclosing library root by walking up to the directory that owns `units/`, validate the authored change set against the current graph, and compute advisory impact for the current local library only. `add` changes stay truthful by contributing `unresolved[]` entries instead of fabricated impact. The repo ships a checked-in example at `examples/ecommerce/plans/refactors/checkout-tax-refactor.plan.spec`. +## Current project shape -The `--output` path for `generate`/`build`/`test` must resolve to a directory inside your project root. Paths that escape the project root are rejected as a safety guardrail to prevent accidental deletion of files outside the project. +This is the honest short version of what the repo currently is: -**Nextest:** `spec test` parses standard `cargo test` output format only. `cargo nextest` uses a different output format and is not supported. Running `spec test` in a project configured for nextest will produce `status: "unknown"` for all local tests. Use standard `cargo test`. +- Rust-first and proof-first +- semantic source for `function`, `data`, and `sum` units +- atom and molecule proof with tracked evidence +- machine-readable CLI surfaces for validation, status, export, and plan artifacts +- a bounded TypeScript execution lane that is additive, not the default +- benchmark-backed public claims instead of broad "maybe supported" language -For both `.unit.spec` and directory-scoped `.test.spec` validation, the path segment `molecule_tests` is reserved. Molecule tests generate `molecule_tests.rs` per namespace, so allowing that literal segment in an authored spec ID would create module/file collisions in generated output. +The repo's current public claim surface is intentionally narrow. That is a +feature, not a gap. The project prefers small honest boundaries over large +hand-wavy ones. -## AI-Native Usage +## Repo layout -`spec` is especially useful when an AI agent is the one making the edit loop. The toolchain gives the agent a structured contract to follow, a machine-readable validation result to fix against, and a passport plus molecule-evidence trail that records what was actually observed to pass. In this repo, the canonical ecommerce example ships both unit passports and molecule evidence so `cargo run -p spec-cli -- status examples/ecommerce/units --format json` is the trustworthy fresh-clone starting point. Repo-root `status . --format json` remains useful when you want broad inventory, but it is intentionally `inventory_only`. +- `spec-core/`: parsing, validation, normalization, generation, proof, export primitives +- `spec-cli/`: the `spec` CLI +- `examples/ecommerce/`: the canonical single-library example +- `examples/service/`: the service benchmark example +- `examples/shared-spec/`, `examples/shared-crate/`, `examples/crosslib-app/`: direct sibling-library examples +- `docs/`: explanation docs, contract stacks, and deeper mechanism guides -The loop is simple: inspect status, validate the exact unit, edit the `.unit.spec`, build to catch Rust-level issues, then test to write fresh evidence. Single-file `validate` and `test` stay on that unit and do not pull sibling molecule tests into the run. +## Where to read next -```bash -spec validate examples/ecommerce/units --format json -``` +Read these in this order if you want the shortest path to fluency: -```json -{ - "schema_version": 3, - "status": "invalid", - "errors": [ - { - "unit": "shipping/calculate", - "code": "SPEC_MISSING_DEP", - "dep": "currency/convert", - "path": "units/shipping/calculate.unit.spec" - } - ], - "warnings": [] -} -``` +1. [`docs/core_mechanisms_guide_v0.1.md`](docs/core_mechanisms_guide_v0.1.md) + Start here for the repo mental model and the main vocabulary boundaries. +2. [`examples/ecommerce/README.md`](examples/ecommerce/README.md) + Use this for the canonical end-to-end example and the concrete command loop. +3. [`AGENTS.md`](AGENTS.md) + Use this if you are editing `.unit.spec` or `.test.spec` files and want the exact workflow. +4. [`docs/rust_v1_contract_stack.md`](docs/rust_v1_contract_stack.md) + Use this if you need the benchmark-backed Rust V1 claim boundary and command-wall semantics. -That JSON form is meant for agents: parse `status`, `errors`, and `warnings` instead of scraping terminal prose. Pre-validation workspace-config failures, including broken `[libraries]` entries, also stay in this JSON contract for `validate --format json`. -`warnings` is a structured array in `schema_version: 3`, so machine consumers can key off stable warning codes instead of parsing display text. +Project-state docs: -For molecule tests, `covers` declares the semantic exercised-unit set. Add explicit `.test.spec` `imports` when the Rust body needs names in scope. If `imports` is omitted, validation emits a deprecation warning and generation temporarily falls back to cover-derived implicit imports for backward compatibility. +- [`CHANGELOG.md`](CHANGELOG.md): shipped history +- [`DECISIONS.md`](DECISIONS.md): durable project decisions +- [`PLAN.md`](PLAN.md): active implementation roadmap +- [`TODOS.md`](TODOS.md): backlog and follow-up inventory -For plan artifacts, the machine-readable loop is: +## CLI surface ```bash -spec plan validate path/to/checkout-tax-refactor.plan.spec --format json +spec validate # validate .unit.spec files +spec status # project per-unit and molecule health +spec generate # generate Rust from .unit.spec files +spec build # validate, generate, and cargo build +spec test # validate, generate, cargo build, cargo test +spec export # export a machine-readable bundle +spec benchmark # write derived benchmark snapshots +spec plan # validate and export .plan.spec files ``` -Checked-in example: +From the repo root, the same commands are available through: ```bash -spec plan validate examples/ecommerce/plans/refactors/checkout-tax-refactor.plan.spec --format json -``` - -Valid plan responses reuse the same top-level envelope and add `plan_id` plus derived `computed_impact`: - -```json -{ - "schema_version": 3, - "status": "valid", - "errors": [], - "warnings": [], - "plan_id": "checkout-tax-refactor", - "computed_impact": { - "status": "partial", - "units": ["pricing/apply_tax", "pricing/calculate_total"], - "molecule_tests": ["pricing/checkout_flow", "pricing/discount_plus_tax"], - "unresolved": [ - { - "unit": "pricing/tiered_rate", - "action": "add", - "reason": "current graph has no node for action=add" - } - ] - } -} +cargo run -p spec-cli -- ... ``` -`spec plan export` emits a dedicated bundle with `{schema_version, spec_version, exported_at, plan, computed_impact, warnings}` and does not change the existing `spec export` bundle contract. +## Working rules that matter -`spec status` uses simple symbols so you can scan a whole tree quickly. Any unit or molecule test whose status is not `valid` exits with code `1`. +- Edit source specs, not generated Rust or observed proof artifacts. +- Treat `spec validate --format json` as the main machine-readable feedback channel. +- Use atom tests for one-unit behavior and molecule tests for cross-unit behavior. +- Do not assume semantic review understands every valid unit. Valid proof and semantic support are different things. +- Do not assume repo-root surfaces are the proof-authoritative default. Some broad scopes are inventory or diagnostic surfaces only. -`spec status --format json` emits `schema_version: 3` and groups results by discovered library root: +## Why this project exists -```json -{ - "schema_version": 3, - "roots": [ - { - "root": ".", - "units": [{ "id": "pricing/apply_tax", "status": "valid", "errors": [] }], - "molecule_tests": [{ "id": "pricing/checkout_flow", "status": "untested", "reason": "no molecule evidence", "errors": [] }] - } - ], - "units": [{ "id": "pricing/apply_tax", "status": "valid", "errors": [] }] -} -``` - -`roots[].units` and `roots[].molecule_tests` are the authoritative per-root planes. The top-level `units` array remains as a flattened compatibility view for existing consumers. Workspace-config failures that happen before any row can be computed surface as top-level `loader_errors` entries instead of raw stderr text, and zero discovered roots is a non-green status result. - -For marked seam units, `status` rows may also include additive `escape_hatch_gate` metadata. In M14 the required surfaces are always `["atom", "molecule"]`, and live `spec status` / `spec export` recompute that gate from current passport freshness and molecule evidence. An open gate can demote an otherwise-`valid` marked seam to `incomplete`, but a seam that is already `stale` remains `stale`. - -- `✓` valid -- `✗` invalid or failing -- `~` stale -- `?` incomplete (for example `unknown` evidence or an open marked-seam gate) -- `—` untested - -Use the companion skill at [`.claude/skills/spec/SKILL.md`](.claude/skills/spec/SKILL.md) when you want the full workflow spelled out for an AI coding session. - -## Validation error codes - -`spec validate --format json` returns error objects with a `code` field. These are the recognized codes returned by the current CLI: - -| Code | Description | -|------|-------------| -| `SPEC_IO` | Filesystem I/O error | -| `SPEC_INVALID_UTF8` | File is not valid UTF-8 | -| `SPEC_YAML_PARSE` | YAML syntax error in the unit file | -| `SPEC_JSON` | JSON serialization/deserialization error | -| `SPEC_SCHEMA_VALIDATION` | Unit file failed JSON Schema validation | -| `SPEC_SEMANTIC_VALIDATION` | Unit passed schema but failed a semantic rule | -| `SPEC_RUST_KEYWORD` | An `id` segment is a Rust reserved keyword | -| `SPEC_DUPLICATE_ID` | Two unit files share the same `id` | -| `SPEC_DEP_COLLISION` | A dep collides with another generated callable name, including another dep or the owning unit function | -| `SPEC_MISSING_DEP` | A declared dep has no matching unit in the spec set | -| `SPEC_UNKNOWN_LIBRARY_NAMESPACE` | A dep references a library alias that is not configured in `[libraries]` | -| `SPEC_CROSS_LIBRARY_DEP_NOT_FOUND` | A cross-library dep has no matching unit in the resolved library set | -| `SPEC_LIBRARY_CRATE_ALIAS_MISSING` | The root crate is missing the Cargo dependency alias required by a cross-library dep | -| `SPEC_LIBRARY_PATH_NOT_FOUND` | A `[libraries]` entry points to a path that does not exist | -| `SPEC_LIBRARY_OUT_OF_ROOT` | A `[libraries]` entry resolves outside the repo root | -| `SPEC_LIBRARY_ALIAS_SELF` | A `[libraries]` entry points back to the invoking library root | -| `SPEC_DUPLICATE_LIBRARY_ROOT` | Two `[libraries]` aliases resolve to the same canonical root | -| `SPEC_CYCLIC_DEP` | Units form a dependency cycle | -| `SPEC_CROSS_LIBRARY_CYCLE` | Units form a dependency cycle across library boundaries | -| `SPEC_USE_STATEMENT_IN_BODY` | `body.rust` contains a `use` statement — move it to `imports` or `deps` | -| `SPEC_BODY_RUST_MUST_BE_BLOCK` | `body.rust` failed to parse as a Rust block expression | -| `SPEC_BODY_RUST_LOOKS_LIKE_FN_DECLARATION` | `body.rust` contains the full `pub fn` signature — keep only the body block (see migration guide) | -| `SPEC_LOCAL_TEST_EXPECT_NOT_EXPR` | A `local_tests[].expect` value is not a valid Rust expression | -| `SPEC_DUPLICATE_LOCAL_TEST_ID` | Two local tests in the same unit share the same `id` | -| `SPEC_CONTRACT_TYPE_INVALID` | A `contract.inputs` or `contract.returns` type is not valid Rust | -| `SPEC_CONTRACT_INPUT_NAME_INVALID` | A `contract.inputs` key is not a valid Rust identifier | -| `SPEC_TRAVERSAL` | Error walking the units directory tree | -| `SPEC_GENERATOR` | Code generation failure | -| `SPEC_OUTPUT_DIR` | Output directory creation or safety check failed | -| `SPEC_MISSING_MARKER` | Output dir lacks the `.spec-generated` marker — refusing to clean | -| `SPEC_MOLECULE_CROSS_LIBRARY_COVERS_UNSUPPORTED` | A molecule test `covers` entry references another library, which remains unsupported in M9 | -| `SPEC_RESERVED_UNIT_NAME` | A slash-delimited spec `id` contains a reserved segment such as `molecule_tests` | -| `SPEC_PLAN_DIRECTORY_INPUT` | `spec plan validate/export` received a directory instead of a single `.plan.spec` file | -| `SPEC_PLAN_OUTSIDE_LIBRARY_ROOT` | The `.plan.spec` file does not live under any resolved library root | -| `SPEC_PLAN_SYMLINK_ESCAPE` | The `.plan.spec` path escapes the repo/library root through a symlink | -| `SPEC_PLAN_CROSS_LIBRARY_UNIT` | A plan authored a cross-library `changes[].unit` or acceptance unit ref, which M10 forbids | -| `SPEC_PLAN_DUPLICATE_CHANGE_UNIT` | The same `changes[].unit` appears more than once in one plan file | -| `SPEC_PLAN_UNIT_MISSING_FOR_ACTION` | `modify` or `remove` references a unit that does not exist in the current graph | -| `SPEC_PLAN_UNIT_ALREADY_EXISTS_FOR_ADD` | `add` references a unit that already exists in the current graph | -| `SPEC_PLAN_MOLECULE_TEST_NOT_FOUND` | A plan acceptance target references a molecule test id that does not exist in the current library | - -## Consuming Generated Code - -Generated units import internal deps with `use crate::...` paths. The consuming crate must -re-export the generated module tree from its root so those paths resolve consistently: - -```rust -mod generated; -pub use generated::*; -``` - -The ecommerce example uses this pattern in -[`examples/ecommerce/src/main.rs`](examples/ecommerce/src/main.rs). - -## Workspace Config - -An optional `spec.toml` at the repo root can relax `local_tests[].expect` validation for trusted workspaces and configure pipeline defaults: - -```toml -[validation] -allow_unsafe_local_test_expect = false - -[pipeline] -crate_root = "." # path to Cargo.toml containing your crate (default: auto-detected) -cargo_target_dir = "target" # cargo target dir (default: temp dir per run) -timeout_secs = 60 # abort cargo build/test if it exceeds this many seconds -``` +The project is trying to make one development style real: -When `allow_unsafe_local_test_expect = true`, `local_tests[].expect` still must parse as a Rust expression, but block, unsafe, closure, and other otherwise-rejected expression forms are allowed. +- author intent explicitly +- make the contract machine-readable +- generate readable implementation +- attach proof to what actually passed +- give humans and agents the same truthful surfaces to reason from -`spec build` and `spec test` auto-detect the nearest member crate (`[package]` Cargo.toml without `[workspace]`) to scope cargo to the right crate in a workspace. Override with `--crate-root ` or `[pipeline].crate_root` in `spec.toml`. +That is the whole game. diff --git a/docs/core_mechanisms_guide_v0.1.md b/docs/core_mechanisms_guide_v0.1.md index af3a7bd..0757dff 100644 --- a/docs/core_mechanisms_guide_v0.1.md +++ b/docs/core_mechanisms_guide_v0.1.md @@ -22,6 +22,76 @@ These systems are related, but they are not the same thing. The goal of this guide is to make the boundaries memorable so repo docs can point back here instead of re-explaining the whole stack from scratch every time. +## Who This Is For + +This guide is for a builder or engineer who is new to `spec` and needs the +repo mental model quickly enough to make or review a real change. + +It is especially aimed at people doing one of these jobs: + +- author or fix a `*.unit.spec` +- explain why `status`, `export`, or a passport says what it says +- figure out whether a behavior is core support, backend support, proof, + semantic review, or benchmark policy + +## What You Should Be Able To Do After Reading This + +After this guide, you should be able to: + +- identify whether a unit question is about kind, backend, proof, semantic + review, family, or benchmark claim +- run the first proof loop on a concrete unit without guessing which command + matters +- know where proof is stored and which commands refresh it +- know when corpus, promotion, and benchmark docs matter, and when they do not + +## If You Changed A Spec File + +If you just changed a `*.unit.spec` or `.test.spec`, start here instead of +trying to load the whole architecture into your head first: + +```bash +cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/apply_tax.unit.spec --format json +cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/apply_tax.unit.spec +cargo run -p spec-cli -- status examples/ecommerce/units --format json +``` + +What each command answers: + +- `validate`: is the authored source legal `spec` input? +- `build`: can the backend lower and compile it? +- `test`: did it prove, and did it refresh passport plus semantic-review truth? +- `status`: what health state does the repo project right now? + +Artifacts to watch: + +- generated Rust: `examples/ecommerce/src/generated/` +- unit proof record: `*.spec.passport.json` +- molecule proof record: `*.test.evidence.json` + +If you changed an interaction across units, also run the relevant `.test.spec` +molecule test. For the canonical seam flow, that is: + +```bash +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy_checkout_flow.test.spec +``` + +## Running Example + +Keep `pricing/apply_tax` in your head while reading this doc. It is the clean +function example for the current system: + +- `kind:function` +- lowers on Rust, and in the bounded lane on TypeScript +- proves through unit-local atom tests +- can be semantically reviewed into + `function.arithmetic_leaf.monotone_up.v1` + +When seam behavior matters, contrast it with `pricing/pricing_quote`, which is +a `kind:data` seam that can be valid and benchmark-positive without semantic +review understanding it as a function family. + ## Short Version If you feel lost, ask these questions in order: @@ -37,6 +107,18 @@ If you feel lost, ask these questions in order: That is the main stack. +## What You Can Ignore On Day 1 + +If your job is just to author or fix a unit, you can ignore corpus, +recommendation, promotion, and benchmark details until the validate/build/test +loop is green. + +Come back to those layers when you are: + +- changing semantic-review capability +- deciding whether a new family should be promoted +- making or auditing a public product claim such as Rust V1 + ## The Two Layers The cleanest separation in this repo is: @@ -260,6 +342,10 @@ Examples: These are read-side analysis outputs. They do not define repo truth; they interpret it. +Most contributors can ignore this section on day 1. It matters when you are +working on family promotion, recommendation honesty, or corpus-program +decisions. + ### Benchmarks Benchmarks answer: @@ -277,6 +363,10 @@ For Rust V1, they back claims like: Benchmarks read existing proof inputs such as passports and molecule evidence. They do not mint those proofs themselves. +Most contributors should only care about benchmarks when they are asking what +the repo can publicly claim today, not when they are just trying to prove a +local fix. + ## What "Supported" Actually Means "Supported" means different things in different layers. Do not collapse them @@ -354,18 +444,62 @@ count toward a public product claim. ## A Simple Flow -When a new authored unit enters the repo, the clean mental flow is: +Here is the first successful loop for the running example. This is the fastest +way to connect the concepts in this guide to something real. + +1. Start with authored truth. + The source of truth is + `examples/ecommerce/units/pricing/apply_tax.unit.spec`. + +2. Validate the authored unit: + + ```bash + cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/apply_tax.unit.spec --format json + ``` + + If this fails, you still have a source-shape problem. Nothing about backend + lowering, proof, semantic review, or benchmarks matters yet. + +3. Build the example root: + + ```bash + cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated + ``` + + If this fails, the authored source may be valid, but the backend-lowering or + compile lane is not. -1. Author the unit in `*.unit.spec`. -2. Validate it as legal `spec` source. -3. Lower and execute it on the backend lane being used. -4. Record atom and molecule proof into passports and evidence files. -5. If it is a function, semantic review may classify it into a supported family - or mark it unsupported. -6. If the unit is part of a benchmark roster, benchmark accounting may count it - toward a public claim. -7. Separately, checked-in units may feed corpus analysis that suggests future - family-promotion work. +4. Refresh proof: + + ```bash + cargo run -p spec-cli -- test examples/ecommerce/units/pricing/apply_tax.unit.spec + ``` + + This rewrites + `examples/ecommerce/units/pricing/apply_tax.spec.passport.json`. + If you also changed cross-unit behavior, run the matching `.test.spec` file + and refresh the corresponding `*.test.evidence.json`. + +5. Project current health: + + ```bash + cargo run -p spec-cli -- status examples/ecommerce/units --format json + ``` + + Now you are looking at read-side truth. `status` tells you whether the unit + is `valid`, `stale`, `failing`, `incomplete`, or `untested`; it does not + create proof on its own. + +6. Ask the semantic-review question only after proof exists. + If the unit is a supported `kind:function` shape, `spec test` may refresh + semantic-review truth inside the passport. For `pricing/apply_tax`, that can + include the family + `function.arithmetic_leaf.monotone_up.v1`. + +7. Ask the benchmark question last. + Benchmark accounting is about whether a proved case counts toward a bounded + public claim such as Rust V1. It is not the same question as whether the + unit is valid, compiled, or semantically understood. That is one flow with several distinct readers, not one monolithic support engine. @@ -407,3 +541,17 @@ This document does not try to: Its job is narrower: keep the mental model clean enough that the deeper docs stay readable. + +## GSTACK REVIEW REPORT + +| Review | Trigger | Why | Runs | Status | Findings | +|--------|---------|-----|------|--------|----------| +| CEO Review | `/autoplan` | Scope and strategy | 1 | CLEAR | The guide direction was right, but it needed a much faster newcomer path and a clearer day-1 versus advanced-systems boundary. | +| Codex Review | `codex exec` | Independent second opinion | 1 | ISSUES_FOUND | Outside review agreed the taxonomy was strong but said the doc still did not get a first-time reader to one successful repo action fast enough. | +| Eng Review | `/autoplan` | Commands and artifact pathing | 1 | CLEAR | Added a concrete validate/build/test/status loop, explicit artifact locations, and a running example that ties the concepts to live repo surfaces. | +| Design Review | `/autoplan` | UI and visual UX gaps | 0 | — | No UI scope in this document review. | +| DX Review | `/plan-devex-review` | Developer experience gaps | 1 | CLEAR | Time to first useful outcome improved from conceptual-only to a five-minute guided loop with commands, expected artifacts, and clear "ignore this for now" boundaries. | + +**CODEX:** Independent review said the guide named the system well but front-loaded maintainers' concepts before showing a first successful action. +**UNRESOLVED:** The broader README and docs tree still need a follow-on cleanup so this guide becomes the stable anchor instead of one strong page inside an overloaded docs set. +**VERDICT:** CEO + ENG + DX CLEARED for this revision. Good mental-model anchor, now with a usable newcomer path. From 0224cdcd6365e5b8c4eb9fb06320f4cf1cf1de1c Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 14:09:01 +0000 Subject: [PATCH 11/24] Prune obsolete docs and refresh the documentation map --- README.md | 11 +- docs/README.md | 80 ++ docs/core_mechanisms_guide_v0.1.md | 1 + .../high_level_technical_architecture_v0.2.md | 82 +- docs/m26_implementation_plan_v0.1.md | 729 ------------------ ...rapper_typescript_execution_design_v0.1.md | 371 --------- docs/north_star_v0.2.md | 17 + docs/roadmap_and_release_shape_v0.1.md | 14 + examples/ecommerce/README.md | 180 +++-- 9 files changed, 295 insertions(+), 1190 deletions(-) create mode 100644 docs/README.md delete mode 100644 docs/m26_implementation_plan_v0.1.md delete mode 100644 docs/m52_bounded_same_tree_wrapper_typescript_execution_design_v0.1.md diff --git a/README.md b/README.md index a718d88..a963588 100644 --- a/README.md +++ b/README.md @@ -166,13 +166,16 @@ hand-wavy ones. Read these in this order if you want the shortest path to fluency: -1. [`docs/core_mechanisms_guide_v0.1.md`](docs/core_mechanisms_guide_v0.1.md) +1. [`docs/README.md`](docs/README.md) + Use this if you want the categorized docs map and the "current authority + versus historical draft" split. +2. [`docs/core_mechanisms_guide_v0.1.md`](docs/core_mechanisms_guide_v0.1.md) Start here for the repo mental model and the main vocabulary boundaries. -2. [`examples/ecommerce/README.md`](examples/ecommerce/README.md) +3. [`examples/ecommerce/README.md`](examples/ecommerce/README.md) Use this for the canonical end-to-end example and the concrete command loop. -3. [`AGENTS.md`](AGENTS.md) +4. [`AGENTS.md`](AGENTS.md) Use this if you are editing `.unit.spec` or `.test.spec` files and want the exact workflow. -4. [`docs/rust_v1_contract_stack.md`](docs/rust_v1_contract_stack.md) +5. [`docs/rust_v1_contract_stack.md`](docs/rust_v1_contract_stack.md) Use this if you need the benchmark-backed Rust V1 claim boundary and command-wall semantics. Project-state docs: diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..96c4972 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,80 @@ +# Documentation Map + +This repo has two different kinds of docs: + +- current authority docs that describe what `spec` does today +- versioned design drafts that preserve historical planning context + +If two docs disagree, trust the current authority docs first: + +1. [`README.md`](../README.md) +2. [`core_mechanisms_guide_v0.1.md`](./core_mechanisms_guide_v0.1.md) +3. [`CHANGELOG.md`](../CHANGELOG.md) +4. [`PLAN.md`](../PLAN.md) +5. [`DECISIONS.md`](../DECISIONS.md) + +That ordering keeps the repo's newcomer story, shipped behavior, active plan, +and durable decisions aligned. + +## Start Here + +- [`README.md`](../README.md) + Repo entry point. What `spec` is, the first command loop, and the day-1 vocabulary. +- [`core_mechanisms_guide_v0.1.md`](./core_mechanisms_guide_v0.1.md) + The mental model doc. Use this when you need to separate authored truth, proof, semantic review, families, and benchmarks. +- [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) + The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. +- [`AGENTS.md`](../AGENTS.md) + Exact workflow rules for editing `.unit.spec`, `.test.spec`, and `.plan.spec` files. + +## Current Authority + +- [`CHANGELOG.md`](../CHANGELOG.md) + Shipped changes and repo-facing behavior changes. +- [`PLAN.md`](../PLAN.md) + Active implementation plan and current milestone authority. +- [`DECISIONS.md`](../DECISIONS.md) + Durable decisions that should not be re-litigated casually. +- [`ORCH_PLAN.md`](../ORCH_PLAN.md) + Current orchestration runbook for the active plan. +- [`rust_v1_contract_stack.md`](./rust_v1_contract_stack.md) + The Rust V1 command-wall and benchmark-claim index. + +## Vision And Architecture + +These docs are still useful, but they are design context, not the day-to-day +source of shipped CLI truth. + +- [`north_star_v0.2.md`](./north_star_v0.2.md) + Long-term product vision. Read this for where the system is trying to go. +- [`high_level_technical_architecture_v0.2.md`](./high_level_technical_architecture_v0.2.md) + High-level system design. Good for boundaries and component thinking. +- [`roadmap_and_release_shape_v0.1.md`](./roadmap_and_release_shape_v0.1.md) + Early sequencing logic. Useful for why the repo grew in this order. +- [`diagrams.md`](../diagrams.md) + Visual map of the write path, read surfaces, family-analysis lane, and planning boundary. + +## Semantic Family And Recommendation Docs + +- [`semantic-families/README.md`](../semantic-families/README.md) + Packet rules, promotion workflow boundaries, and corpus-analysis context. +- [`semantic_family_capability_corpus_guide_v0.1.md`](./semantic_family_capability_corpus_guide_v0.1.md) + Deeper explanation of family capability, coverage, and recommendation surfaces. +- [`recommendation_corpus_expansion_program_v0.1.md`](./recommendation_corpus_expansion_program_v0.1.md) + Corpus growth and recommendation-pressure design context. +- [`ai_promotion_and_multilanguage_milestones_v0.1.md`](./ai_promotion_and_multilanguage_milestones_v0.1.md) + Milestone framing for family promotion and second-language work. + +## Historical Design Packets + +These are useful when you are reconstructing why a milestone exists or how a +specific bounded wedge was scoped. They are not the right starting point for a +new reader. + +- `docs/m26_*` +- `docs/m27_5_*` +- `docs/m52_*` + +Some of these older design packets preserve machine-local path references from +the original authoring session. Read them as historical design records unless +they are re-grounded by current authority docs above. diff --git a/docs/core_mechanisms_guide_v0.1.md b/docs/core_mechanisms_guide_v0.1.md index 0757dff..6ce29dd 100644 --- a/docs/core_mechanisms_guide_v0.1.md +++ b/docs/core_mechanisms_guide_v0.1.md @@ -510,6 +510,7 @@ Use this map when you want the source of truth for a concept: | If you want to understand... | Start here | | --- | --- | +| the docs tree itself and which docs are current authority | [`docs/README.md`](./README.md) | | the core authored model and workflow | [`README.md`](../README.md) | | semantic-family capability, corpus, and promotion | [`semantic_family_capability_corpus_guide_v0.1.md`](./semantic_family_capability_corpus_guide_v0.1.md) | | the current Rust V1 benchmark and claim boundary | [`rust_v1_contract_stack.md`](./rust_v1_contract_stack.md) | diff --git a/docs/high_level_technical_architecture_v0.2.md b/docs/high_level_technical_architecture_v0.2.md index 32d34ca..844f629 100644 --- a/docs/high_level_technical_architecture_v0.2.md +++ b/docs/high_level_technical_architecture_v0.2.md @@ -8,6 +8,22 @@ > [`README.md`](../README.md), [`CHANGELOG.md`](../CHANGELOG.md), [`PLAN.md`](../PLAN.md), and > [`DECISIONS.md`](../DECISIONS.md). +## Current Grounding + +This architecture draft is still useful for boundaries, but some of the +examples below were written before the current repo shape settled. + +Use these current facts as the grounding layer while reading it: + +- the current authored sources are `*.unit.spec`, `.test.spec`, and `.plan.spec` +- the shipped top-level unit kinds are `function`, `data`, and `sum` +- the current proof story is atom plus molecule proof; organism remains future vocabulary, not a shipped repo-wide contract +- the current default backend lane is Rust, with a bounded additive TypeScript lane +- passports and molecule evidence are co-located derived artifacts, while generated Rust lives under example `src/generated/` trees + +Treat this doc as architecture direction and component framing, not as the +authoritative day-to-day CLI contract. + ## Purpose This document captures the initial technical architecture for **spec**, a semantic-unit system for authoring, validating, compiling, linking, and verifying software as structured semantic records. @@ -97,7 +113,7 @@ A test artifact represents verification at one of several levels: - atom - molecule -- organism +- organism (future vocabulary, not a current shipped repo-wide proof surface) Atom tests are usually owned directly by a unit. Molecule and organism tests are separate artifacts that may cover multiple units. @@ -154,10 +170,10 @@ A semantic unit source file should contain human-authored truth: A non-local test file should contain: - `id` -- `tier` +- `intent` - `covers` -- `scenario` -- optional setup and fixtures metadata +- optional `imports` +- `body.rust` ### Derived artifacts The following should not be hand-authored in source files: @@ -178,29 +194,26 @@ These belong in the build output. ```text spec/ - schema/ - unit.cue - test.cue - policies/ - units/ - pricing/ - apply_discount.unit.spec - apply_tax.unit.spec - checkout/ - finalize_total.unit.spec - tests/ - pricing/ - discount_plus_tax.test.spec - checkout/ - final_total_flow.test.spec - build/ - generated/ - passports/ - docs/ - diagnostics/ + README.md + docs/ + examples/ + ecommerce/ + README.md + units/ + money/round.unit.spec + pricing/apply_discount.unit.spec + pricing/apply_tax.unit.spec + pricing/calculate_total.unit.spec + pricing/pricing_quote.unit.spec + pricing/discount_strategy.unit.spec + pricing/discount_plus_tax.test.spec + src/generated/ ``` -This shape is illustrative, not final. +This shape is illustrative, but it is closer to the current checked-in repo +than the original March draft layout. In the live repo, passports and molecule +evidence are co-located next to the source specs rather than collected into one +separate `build/passports/` tree. --- @@ -242,14 +255,25 @@ links: ## Example Test File ```yaml -kind: test -tier: molecule id: pricing/discount_plus_tax +spec_version: "0.3.0" +intent: + why: Verify that applying discount then tax produces the correct final price. covers: - pricing/apply_discount - pricing/apply_tax -scenario: - description: Discount is applied before tax in final pricing flow. + - money/round +imports: + - rust_decimal::Decimal + - crate::pricing::apply_discount::apply_discount + - crate::pricing::apply_tax::apply_tax +body: + rust: | + { + let discounted = apply_discount(Decimal::new(10000, 2), Decimal::new(10, 2)); + let taxed = apply_tax(discounted, Decimal::new(725, 4)); + assert!(taxed > Decimal::ZERO); + } ``` --- diff --git a/docs/m26_implementation_plan_v0.1.md b/docs/m26_implementation_plan_v0.1.md deleted file mode 100644 index b10fcbf..0000000 --- a/docs/m26_implementation_plan_v0.1.md +++ /dev/null @@ -1,729 +0,0 @@ -# M26 - Approval-Gated AI Family Promotion Loop - -Status: **implementation plan** -Base branch: **main** -Working branch: **feat/m26** -Last rewritten: **2026-04-29** - -Source of truth for this plan: - -- repo code and docs in this checkout -- [PLAN.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/PLAN.md) -- [docs/ai_promotion_and_multilanguage_milestones_v0.1.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/ai_promotion_and_multilanguage_milestones_v0.1.md) -- [docs/m26_approval_gated_ai_family_promotion_loop_design_v0.1.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/m26_approval_gated_ai_family_promotion_loop_design_v0.1.md) -- [docs/north_star_v0.2.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/north_star_v0.2.md) -- [docs/high_level_technical_architecture_v0.2.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/high_level_technical_architecture_v0.2.md) -- [docs/roadmap_and_release_shape_v0.1.md](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/roadmap_and_release_shape_v0.1.md) - -M21 through M24 proved the narrow Rust wedge first: the runtime semantic reviewer can distinguish -aligned truth, drift, under-specification, and unsupported near misses for real promoted function -families. - -M26 does not try to re-prove that thesis. M26 turns that proof into the right operator model. - -The causal chain is fixed: - -1. prove the intent-drift thesis in one narrow Rust wedge -2. make family promotion operable by AI under hard proof gates -3. use that promotion machinery to broaden per-language semantic review coverage -4. only then factor toward multi-language support - -If step 2 remains manual, steps 3 and 4 do not scale. - -## Problem Statement - -After M24, the repo has two kinds of truth: - -- runtime semantic-review truth in `spec-core` and read-side surfaces in `spec-cli` -- packetized promotion truth and proof gates in `xtask` plus `semantic-families/` - -What is still missing is the operator loop that joins those surfaces without falling back to hidden -human ceremony. - -Today, promotion still assumes a human can hold too much in their head: - -- which family to promote next -- whether that choice is justified by repo truth -- how to scaffold and curate the packet -- how to interpret `smoke`, `prove`, and `certify` failures -- when a failure is fixable versus when it is an honest blocker - -That is not the north-star operator model. The north-star model is slower, safer, -verify-as-it-builds AI work under explicit gates. - -M26 therefore locks the operator model: - -1. AI recommends the next family candidate from repo truth. -2. Human approves or rejects that candidate. -3. AI performs the full promotion loop under hard gates. -4. Human approves or rejects the final promoted output. - -Human approval is limited to the candidate family and the final output. The human does not rescue -the loop between those points. - -## Milestone Outcome - -When M26 is done, the repo can truthfully claim: - -- family promotion is operable by AI rather than only by a repo expert -- the hard gates remain `cargo xtask family smoke`, `prove`, and `certify` -- recommendation, execution, and blocker surfaces are durable machine-readable artifacts rather - than chat-only reasoning -- a human intervenes only at the two planned approval boundaries -- the promotion loop now exists at the right abstraction layer to unlock broader Rust family - coverage first and multi-language work later - -M26 does **not** claim: - -- the recommendation engine is globally optimal -- multi-language promotion is already solved -- non-function families are now in scope -- family authoring ceremony is fully minimized -- AI can invent new semantic families without repo-grounded seeds - -## Scope - -### In scope - -- lock the approval-gated operator model for family promotion -- add durable machine-readable artifacts for recommendation, execution, and blockers -- define a deterministic repo-truth input surface for the AI recommender -- keep `xtask` as the hard proof primitive layer -- place recommendation ranking, retry logic, approval checkpoints, and blocker synthesis in the - AI-operated workflow while keeping the deterministic proof kernel in `xtask` -- complete one real Rust family promotion through the approval-gated AI loop -- choose the first live proof target from current repo truth, not from speculation - -### NOT in scope - -- multi-language backend work -- new target-language lowering -- non-function family promotion for `sum` or `data` -- global coverage optimization across all future families -- maintainer UI or dashboard work -- generic long-horizon agent workflow infrastructure beyond what M26 directly needs -- replacing `xtask` proof commands with a new orchestration command family - -## Why This Milestone Now - -The narrow Rust proof came first on purpose. - -The repo thesis was never "AI should guess family semantics." The thesis was: - -- semantic review should detect intent drift honestly -- AI should operate inside validation, build, test, and evidence gates - -M21 through M24 proved the first half in one language and one bounded function subset. - -That changes the next bottleneck. The blocker is no longer semantic-review credibility. The blocker -is throughput. - -Without an approval-gated AI promotion loop: - -- broad Rust family coverage stays manual -- the set of promoted families grows too slowly to matter -- multi-language work arrives before the Rust workflow has learned how to scale - -So M26 is the bridge milestone. It turns a manually proved Rust wedge into an AI-operated family -promotion loop, which is the prerequisite for both wider Rust coverage and later multi-language -portability. - -## Premises - -1. Human approvals are limited to candidate selection and final output approval. -2. AI must do the recommendation, editing, iteration, and failure handling work between those - approvals. -3. `xtask` remains the deterministic proof primitive because it already owns scaffold, smoke, - prove, certify, and artifact writing for family packets. -4. Recommendation logic should consume repo truth; it should not live inside `xtask` as opaque - policy. -5. The first live M26 family proof should use a family class the repo already supports at runtime - but has not yet promoted through the packet workflow. -6. M26 proves the operator model, not global selection optimality. M27 can optimize ranking later. - -## What Already Exists - -| Area | Current truth | M26 reuse decision | -|---|---|---| -| North-star operator thesis | AI should work in a slower, safer, verify-as-it-builds loop | Reuse directly. M26 makes it concrete for family promotion. | -| Current promoted packet registry | `semantic-families/` currently promotes `function.wrapper.pipeline.chain3.v1`, `function.arithmetic_leaf.monotone_down_nonnegative.v1`, and `function.arithmetic_leaf.monotone_up.v1` | Reuse as the baseline packet and artifact contract. | -| Current runtime supported function routes | `spec-core/src/semantic_review.rs` routes `WrapperPipelineChain3`, `WrapperPipeline`, `ArithmeticLeafMonotoneDownNonnegative`, and `ArithmeticLeafMonotoneUp` in explicit order | Use this as recommendation input. Do not invent new runtime families in M26. | -| Current proof primitives | `cargo xtask family new`, `smoke`, `prove`, `certify` | Keep as the hard proof loop. Do not replace them. | -| Current proof artifacts | `prove.latest.json`, `attempt-*.json`, and `certification.report.json` under `.semantic-family-artifacts/semantic-families//` | Reuse as gate truth and reference them from M26 orchestration artifacts. | -| Current blocker vocabulary | unsupported-function reason codes and certify routing diagnostics already exist | Reuse as the first machine evidence for blocker classification. | -| Supported-but-unpromoted candidate | `function.wrapper.pipeline.v1` exists in runtime semantic review, is referenced in routing metadata, and has a canonical seed at `examples/ecommerce/units/pricing/calculate_total.unit.spec` | This is the recommended first live M26 proof family unless implementation truth disproves it. | -| Existing wedge evidence for that candidate | `spec-cli/tests/m14_regressions.rs` already has `calculate_total` drift, under-specified, and unsupported-near-miss rewrites | Reuse as the narrowest truthful first promotion case. | - -## Minimal Change Strategy - -M26 stays narrow on purpose. - -- Do not move semantic-family proof logic out of `xtask`. -- Do not put recommendation ranking or approval state into `spec-core`. -- Do not invent a multi-language kernel yet. -- Do not prove the AI loop on a brand-new semantic family. - -The minimum honest diff is: - -1. expose deterministic repo-truth inventory for recommendation input -2. add durable M26 orchestration artifacts -3. define the approval-gated command loop around existing `xtask` proof gates -4. prove the loop on one already-supported but unpromoted Rust family - -Anything broader is M27 or later. - -## Architecture / Dependency View - -```text -spec-core runtime semantic review - │ - ├── explicit supported routes - ├── unsupported reason codes - └── canonical seed behavior in examples/tests - │ - ▼ -deterministic repo-truth inventory surface - │ - ▼ -AI recommendation layer - │ - ├── writes recommendation packet - └── waits for human target approval - │ - ▼ -AI promotion loop - │ - ├── edits harness / packet / tests / docs for approved family - ├── runs cargo xtask family smoke - ├── runs cargo xtask family prove - ├── runs cargo xtask family certify - ├── reads prove/certify artifacts - └── repeats until green or blocked - │ - ├── green -> promotion execution report -> human final approval - └── blocked -> blocker report -``` - -### Primary module boundaries - -| Layer | Responsibility in M26 | Must not happen | -|---|---|---| -| `spec-core` | remain source of runtime family truth, supported routing order, and unsupported diagnostics | do not absorb AI recommendation or approval state | -| `spec-cli` | remain source of read-side truth-surface and corpus regressions | do not become the orchestration layer | -| `xtask/src/family/**` | keep deterministic family packet primitives: scaffold, smoke, prove, certify, report emission, pure repo-truth export, and artifact validation | do not embed ranking heuristics, human approvals, or LLM-specific policy | -| AI operator workflow | recommend candidate families, checkpoint approvals, run the edit-and-proof loop, interpret failures, and write M26 orchestration artifacts | do not reimplement `smoke`, `prove`, or `certify` logic inside repo code | -| `.semantic-family-artifacts/**` | hold machine-readable proof and orchestration artifacts | do not become authored source | - -## Locked Operator Contract - -M26 does not leave the operator model to implementer taste. - -### Human approvals - -- approval point 1: approve or reject the AI recommendation packet -- approval point 2: approve or reject the final promotion execution report - -No third approval is allowed for ordinary promotion work. - -### AI responsibilities - -- inspect repo truth -- rank candidate families -- emit a recommendation packet -- after approval, perform repo edits for the approved family -- run proof commands -- inspect machine-readable artifacts and command failures -- retry within hard gates -- emit either a promotion execution report or a blocker report - -### Hard boundary - -- `xtask` owns deterministic proof primitives, pure repo projections, and artifact validation -- the AI operator owns recommendation generation, approval gating, loop control, and blocker synthesis - -That boundary is fixed for M26. - -## Locked Artifact Contract - -M26 adds three orchestration artifacts. These are provisional schemas, but their role and paths are -locked by the plan. - -### Artifact locations - -- recommendation packet: - `.semantic-family-artifacts/family-promotion/recommendation.latest.json` -- promotion execution report: - `.semantic-family-artifacts/family-promotion///promotion.execution.json` -- blocker report: - `.semantic-family-artifacts/family-promotion///blocker.report.json` - -The execution and blocker artifacts must reference the existing `xtask` proof artifacts rather than -copying their payloads. - -### Recommendation packet - -Purpose: give the human a repo-grounded candidate choice before any promotion work starts. - -Provisional schema: - -```json -{ - "schema_version": 1, - "artifact_kind": "family_recommendation", - "generated_at": "2026-04-29T00:00:00Z", - "inventory_path": ".semantic-family-artifacts/family-promotion/inventory/20260429T120000Z-function.wrapper.pipeline.v1.json", - "inventory_sha256": "sha256-of-that-exact-snapshot", - "target_language": "rust", - "current_promoted_families": [ - "function.wrapper.pipeline.chain3.v1", - "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "function.arithmetic_leaf.monotone_up.v1" - ], - "ranked_candidates": [ - { - "family": "function.wrapper.pipeline.v1", - "status": "recommended", - "why_now": "Supported in runtime semantic review but not yet promoted through the packet workflow.", - "evidence": [ - { - "kind": "runtime_supported_route", - "path": "spec-core/src/semantic_review.rs" - }, - { - "kind": "canonical_seed", - "path": "examples/ecommerce/units/pricing/calculate_total.unit.spec" - }, - { - "kind": "existing_wedge_regressions", - "path": "spec-cli/tests/m14_regressions.rs" - } - ], - "expected_leverage": { - "language": "rust", - "surface": "kind:function", - "topology": "two-dep wrapper pipeline" - }, - "expected_risks": [ - "routing overlap with function.wrapper.pipeline.chain3.v1", - "packet curation may reveal family boundary drift" - ] - } - ] -} -``` - -Locked rules: - -- recommendation is evidence-backed, not gut feel -- `ranked_candidates[0]` is the only approval target -- the packet must cite repo paths for every substantive claim -- `inventory_path` must point to the exact captured inventory snapshot used for ranking -- `inventory_sha256` must hash the exact bytes at `inventory_path` -- Gate 1 is a pre-edit approval over repo truth before approved-family edits begin -- Gate 1 approval remains valid only while a fresh inventory snapshot of the unchanged - pre-edit basis yields both the same `inventory_sha256` and the same - `ranked_candidates[0].family` -- the required recheck happens immediately before the first approved-family edit -- after the first approved-family edit lands, Gate 1 is no longer compared against live - post-edit inventory; from that point on, correctness is governed by the hard gates and - the post-promotion inventory expectations -- if either pre-edit basis check changes, the prior approval is stale; AI must halt, - write a fresh recommendation artifact, rerun validation, and wait for a new human approval - -### Promotion execution report - -Purpose: record the whole AI-operated run, the proof commands, the edits made, and the final gate -state for human approval. - -Provisional schema: - -```json -{ - "schema_version": 1, - "artifact_kind": "promotion_execution", - "run_id": "20260429T120000Z-function.wrapper.pipeline.v1", - "family": "function.wrapper.pipeline.v1", - "status": "green", - "recommendation_path": ".semantic-family-artifacts/family-promotion/recommendation.latest.json", - "approvals": { - "target_family": { - "status": "approved", - "approved_at": "2026-04-29T12:05:00Z" - }, - "final_output": { - "status": "pending" - } - }, - "repo_state": { - "git_commit_sha": "HEAD-at-start", - "rust_toolchain": "rustc --version" - }, - "files_changed": [ - "xtask/src/family/harness.rs", - "semantic-families/function.wrapper.pipeline.v1/family.toml" - ], - "commands": [ - { - "step": "smoke", - "command": "cargo xtask family smoke function.wrapper.pipeline.v1", - "exit_code": 0 - }, - { - "step": "prove", - "command": "cargo xtask family prove function.wrapper.pipeline.v1", - "exit_code": 0, - "artifact_path": ".semantic-family-artifacts/semantic-families/function.wrapper.pipeline.v1/prove.latest.json" - }, - { - "step": "certify", - "command": "cargo xtask family certify function.wrapper.pipeline.v1", - "exit_code": 0, - "artifact_path": ".semantic-family-artifacts/semantic-families/function.wrapper.pipeline.v1/certification.report.json" - } - ], - "iterations": 3, - "gate_summary": { - "smoke": "pass", - "prove": "pass", - "certify": "pass" - }, - "notes": [ - "AI resolved one packet-layout failure without extra human steering." - ] -} -``` - -Locked rules: - -- this is the final human approval surface -- it must reference every hard-gate command that ran -- it must list changed source surfaces -- it must distinguish green completion from blocked termination - -### Blocker report - -Purpose: terminate honestly when AI cannot complete promotion inside the hard gates without hidden -human rescue work. - -Provisional schema: - -```json -{ - "schema_version": 1, - "artifact_kind": "promotion_blocker", - "run_id": "20260429T120000Z-function.wrapper.pipeline.v1", - "family": "function.wrapper.pipeline.v1", - "blocking_step": "certify", - "blocker_kind": "routing_incoherence", - "summary": "Gate D failed because runtime routing and manifest routing disagree.", - "machine_evidence": [ - { - "command": "cargo xtask family certify function.wrapper.pipeline.v1", - "exit_code": 1, - "artifact_path": ".semantic-family-artifacts/semantic-families/function.wrapper.pipeline.v1/attempt-2026-04-29T12:40:00Z.json" - } - ], - "required_human_action": "Decide whether function.wrapper.pipeline.v1 should precede or follow chain3 in the promoted registry.", - "safe_next_actions": [ - "Keep prove/certify gates unchanged.", - "Resolve runtime/harness routing disagreement explicitly." - ], - "terminal": true -} -``` - -Locked rules: - -- a blocker is a first-class honest outcome in M26 -- blocker classification must cite machine evidence, not conversational opinion -- the report must tell the human exactly what decision or missing truth prevented completion - -## Locked `xtask` vs Operator Boundary - -M26 must answer where logic lives. - -### `xtask` should own - -- `family new`, `family smoke`, `family prove`, `family certify` -- packet layout validation -- manifest validation -- routing diagnostics -- existing proof artifact emission -- a new pure repo-truth export surface if needed, for example: - `cargo xtask family inventory --format json` - -Why: - -- these are deterministic repo-local computations -- they should be testable without AI -- they already define the hard proof contract - -### The AI operator should own - -- ranking candidate families from repo truth -- writing `recommendation.latest.json` -- waiting at approval boundaries -- making repo edits across harness, packet, tests, and docs -- deciding when to rerun fast inner-loop commands versus full proof gates -- synthesizing `promotion.execution.json` and `blocker.report.json` - -Why: - -- this layer is stateful and workflow-oriented -- it must encode approval checkpoints -- it should evolve without destabilizing the deterministic proof kernel or adding - a new workspace crate in M26 - -### Hard non-goals for `xtask` - -- no hidden LLM prompt logic -- no approval checkpoint persistence -- no ranking heuristics that choose the next family autonomously -- no `spec-orchestrator` workspace crate in M26 - -## First Live Proof-Family Selection - -M26 needs one real family promotion to prove the operator loop. That first target must be chosen -by repo truth, not by taste. - -### Selection criteria - -The first live M26 family should satisfy all of these: - -1. it is already recognized as a supported runtime semantic-review family or route -2. it has a canonical real seed unit in repo truth -3. it already has aligned, drift, under-specified, or unsupported-near-miss wedge material that - can be reused truthfully -4. it does not require new evaluator-scope invention -5. it broadens Rust coverage meaningfully rather than duplicating a direct sibling proof -6. it keeps M26 focused on workflow proof, not semantic-theory expansion - -### Recommended first live target - -The recommended first live M26 proof family is: - -- `function.wrapper.pipeline.v1` - -Reasoning, grounded in current repo truth: - -- `spec-core/src/semantic_review.rs` already contains the runtime compatibility key - `function.wrapper.pipeline.v1` and a `WrapperPipeline` route. -- the canonical seed already exists at - `examples/ecommerce/units/pricing/calculate_total.unit.spec`. -- `spec-cli/tests/m14_regressions.rs` already contains `calculate_total` drift, - under-specified, and unsupported-near-miss rewrite helpers. -- `spec-cli/tests/cli.rs`, `spec-core/src/passport.rs`, and `spec-core/src/export.rs` already - exercise `pricing/calculate_total` as `function.wrapper.pipeline.v1`. -- `function.wrapper.pipeline.chain3.v1` already declares `function.wrapper.pipeline.v1` in its - routing boundary, so promoting the two-dep wrapper family stabilizes an existing shadow line - rather than inventing a new one. - -This is a stronger first M26 target than another arithmetic leaf because it broadens function -topology coverage from single-leaf arithmetic to the reusable two-dep wrapper shape that the repo -already understands. - -If implementation truth later proves that `function.wrapper.pipeline.v1` is not promotion-ready, -the fallback rule is not "pick something convenient." The fallback rule is "choose the next -repo-supported but unpromoted family that still satisfies the criteria above." - -## Exact AI Operator Command Loop - -M26 should lock one exact command loop for the AI operator. - -### Phase A - inventory and recommendation - -Required machine step: - -```bash -cargo xtask family inventory --format json -``` - -AI then writes: - -- `.semantic-family-artifacts/family-promotion/recommendation.latest.json` - -The operator must also retain the exact inventory snapshot used for that -recommendation under: - -- `.semantic-family-artifacts/family-promotion/inventory/.json` - -Human then approves or rejects `ranked_candidates[0].family`. - -The operator must also record the pre-edit basis commit that produced that -snapshot. That commit is the only commit on which Gate 1 inventory equality may -be rechecked. - -### Phase B - promotion loop after approval - -For approved family ``, the AI loop is: - -```bash -cargo fmt --all -cargo test -p xtask -cargo xtask family smoke -cargo xtask family prove -cargo xtask family certify -``` - -Fast inner-loop commands are allowed between hard-gate runs, but they do not replace the hard -gates. The AI may also run targeted suites while iterating, for example: - -```bash -cargo test -p spec-core --lib -cargo test -p spec-cli --test m14_regressions -``` - -Loop rule: - -1. read the approved recommendation artifact -2. before the first approved-family edit, rerun inventory on the unchanged pre-edit basis - and capture a fresh inventory snapshot -3. compare that fresh snapshot against the approved Gate 1 basis: - - `inventory_sha256` - - `ranked_candidates[0].family` -4. if either pre-edit Gate 1 basis check changed, stop and reopen Gate 1 with a fresh - recommendation artifact -5. edit repo truth for the approved family -6. run the fast inner loop if useful -7. rerun `smoke` -8. rerun `prove` -9. rerun `certify` -10. if green, write the execution report -11. if blocked, write the blocker report - -The human does not steer any of those retries. -The live post-edit repo state is not required to match the original Gate 1 inventory -snapshot, because approved-family edits are expected to change inventory-visible truth. - -### Phase C - final approval - -When all hard gates are green, AI writes: - -- `.semantic-family-artifacts/family-promotion///promotion.execution.json` - -Human then approves or rejects the final output using that report. - -## Implementation Sequence - -1. Lock the artifact and operator contract in code-facing docs and tests. - Add the M26 artifact shapes, approval states, and command-loop expectations as executable - invariants where possible. - -2. Add deterministic repo-truth inventory export. - This belongs in `xtask` because it is a pure projection of current registry, runtime supported - routes, promoted packets, and seed evidence paths. - -3. Add path-aware artifact validation in `xtask`. - Keep runtime approval gating anchored on repo-owned schema and path checks rather than - shell-only convention. - -4. Lock the wrapper family contract and scaffold in `xtask`. - Freeze suite slug, starter paths, per-bucket semantics, and the unsupported-near-miss - boundary before parallel lane work begins. - -5. Capture the Gate 1 recommendation basis. - Emit `recommendation.latest.json`, retain the exact inventory snapshot under - `.semantic-family-artifacts/family-promotion/inventory/.json`, and require - `inventory_path` plus `inventory_sha256` in the approval artifact. - -6. Launch the three bounded post-contract lanes. - Packet curation, runtime proof, and CLI truth-surface/regression work may proceed in - parallel only after the wrapper-family contract is frozen. - -7. Integrate the bounded lanes and run the hard gates. - If a lane discovers a mismatch against the frozen wrapper-family contract, stop lane-local - reconciliation and return to the serialized owner flow before proceeding. - -8. Prove the loop on `function.wrapper.pipeline.v1`. - Use `pricing/calculate_total` as the canonical seed and the existing `calculate_total` wedge - rewrites as the initial packet truth. - -9. Run the full approval-gated loop end to end. - Require the actual two approvals, machine-written artifacts, and green hard gates before - claiming success. - -10. Document only repo truth that is green. - Do not claim AI-operated promotion has landed until the first real family promotion and its - artifacts exist. - -## Workstream Coordination - -The implementation plan follows the serialized-plus-bounded-parallel shape from `PLAN.md`. - -### Coordination rules - -- The primary checkout at the live `feat/m26` path is the canonical run-state root for M26. -- All `.runs/m26/*` and `.semantic-family-artifacts/family-promotion/**` writes are owned by the - parent agent and resolved against that primary checkout root, not against worker-local - worktree relative paths. -- Worker worktrees may read frozen prompts or summaries, but they do not become independent - sources of truth for approvals, sentinels, or promotion artifacts. -- Lane A is the serialization point. It owns inventory export, artifact schemas, wrapper harness - registration, scaffold shape, and the 12 starter semantics. -- Lane A freezes the wrapper-family contract before any parallel work begins: - - suite slug - - packet file names and starter paths - - per-bucket starter semantics - - unsupported-near-miss boundary -- Lane A must be merged back onto `feat/m26`, and the resulting commit must be recorded as the - `contract_freeze_commit` before any worker branch is created. -- Lane B owns only the committed wrapper packet under - `semantic-families/function.wrapper.pipeline.v1/`. -- Lane C owns only runtime classifier proof and route-order assertions in - `spec-core/src/semantic_review.rs`. -- Lane D owns only CLI truth-surface, corpus, and regression proof in - `spec-cli/tests/cli.rs` and `spec-cli/tests/m14_regressions.rs`. -- Lanes B, C, and D must all fork from the exact recorded `contract_freeze_commit`. -- Lane C may add proof for the frozen contract, but it may not redefine family semantics, - starter paths, or the unsupported-near-miss boundary. -- Lanes B and D must consume the frozen Lane A contract literally. They do not invent or - reinterpret wrapper-family semantics independently. -- If any parallel lane discovers that runtime truth disagrees with the frozen contract, stop - lane-local reconciliation, return to the serialized owner flow, update the contract explicitly, - and relaunch affected lanes as needed. - -## Acceptance Gates - -M26 is done only when all of the following are true: - -- the operator model is explicit and enforced: human approves target family and final output only -- the recommendation packet exists and cites repo-path evidence for the chosen family -- the recommendation artifact encodes a machine-checkable Gate 1 basis through - `inventory_path` and `inventory_sha256` -- Gate 1 approval is reopened automatically when the unchanged pre-edit basis snapshot or top - candidate no longer matches the approved recommendation basis -- `xtask` remains the hard proof kernel for `smoke`, `prove`, and `certify` -- one real Rust family promotion completes through that loop -- the first live proof target is selected from repo truth, not ad hoc judgment -- the final execution report references the actual `prove.latest.json` and - `certification.report.json` artifacts -- if the loop cannot finish honestly, it emits a blocker report instead of silently requiring - manual rescue -- the workspace boundary remains exactly `spec-core`, `spec-cli`, and `xtask` -- the bounded parallel lanes consume one frozen wrapper-family contract instead of negotiating - semantics independently -- the packet, runtime, and CLI workers all start from one recorded `contract_freeze_commit` -- the M26 implementation leaves the system better positioned for broad Rust family expansion and - later multi-language work - -## Follow-ups Explicitly Deferred - -- M27 coverage accounting and ranking optimization -- language-agnostic family-kernel extraction -- second-language promotion work -- non-function family promotion -- maintainer UI for approvals and artifact browsing -- continuous autonomous background promotion queues - -## Unresolved Risks - -- The current repo has strong proof primitives but no shipped inventory export yet. If that export - is underspecified, the recommendation layer will stay too chat-dependent. -- `function.wrapper.pipeline.v1` is the best current first target from repo truth, but promoting it - may expose real routing pressure against `chain3` that must be resolved explicitly. -- Existing `xtask` reports are machine-readable, but some failure causes still arrive as stderr - strings. M26 may need sharper structured diagnostics to avoid AI thrash. -- Packet curation may still hide too much maintainer judgment in `candidate.md` unless the blocker - vocabulary is crisp. -- If orchestration artifacts drift away from `xtask` proof artifacts, the system could develop two - competing truths. M26 must keep the proof kernel authoritative. diff --git a/docs/m52_bounded_same_tree_wrapper_typescript_execution_design_v0.1.md b/docs/m52_bounded_same_tree_wrapper_typescript_execution_design_v0.1.md deleted file mode 100644 index 9832e9f..0000000 --- a/docs/m52_bounded_same_tree_wrapper_typescript_execution_design_v0.1.md +++ /dev/null @@ -1,371 +0,0 @@ -# Design: M52 Bounded Same-Tree Wrapper TypeScript Execution in `spec` - -Generated by `/autoplan` on 2026-05-12 -Branch: `feat/m40-plus` -Repo: `atomize-hq/spec` -Status: `DRAFT_READY_FOR_REVIEW` -Builds on: -- [`docs/ai_promotion_and_multilanguage_milestones_v0.1.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/ai_promotion_and_multilanguage_milestones_v0.1.md) -- [`docs/semantic_family_capability_corpus_guide_v0.1.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/docs/semantic_family_capability_corpus_guide_v0.1.md) -- [`TODOS.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/TODOS.md) -- [`README.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/README.md) -- [`CHANGELOG.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/CHANGELOG.md) -- [`/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/checkpoints/20260506-181701-semantic-review-milestone-reset.md`](/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/checkpoints/20260506-181701-semantic-review-milestone-reset.md) -- [`/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m40-plus-design-20260510-111915.md`](/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m40-plus-design-20260510-111915.md) -- [`/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m40-plus-design-20260510-215928.md`](/Users/spensermcconnell/.gstack/projects/atomize-hq-spec/spensermcconnell-feat-m40-plus-design-20260510-215928.md) - -## Problem Statement - -M46 made TypeScript execution real in one narrow lane. - -That was the right first cut. `spec generate/build/test --target-language typescript` now exists, but the lane stops at monotone-up leaves plus at most one local helper dep. The semantic reviewer already understands more than that. It can classify supported two-step wrapper pipelines and chain3 pipelines with same-tree local context, but the executor cannot run that shape. - -That mismatch is the next product truth bug. - -Today the repo can honestly say: - -- TypeScript execution exists for one bounded family-shaped slice. -- semantic review understands wrapper pipelines in Rust and read-side truth. - -It cannot yet honestly say: - -- `spec` can execute the canonical wrapper family in TypeScript inside the same loaded tree. - -That is the next lake. - -## Demand Evidence - -Live repo truth on `feat/m40-plus` on 2026-05-12: - -- `cargo xtask family recommend --format json` reports: - - `recommendation_status = "insufficient_real_corpus"` - - `decision_summary.decision_status = "not_recommended"` -- `cargo xtask family corpus-decision --format json` reports: - - `decision_action = "stop"` - - `required_next_action = "record_stop_without_new_milestone"` -- `cargo run -p spec-cli -- test examples/ecommerce/units/pricing/apply_tax.unit.spec --target-language typescript` passes. The bounded monotone-up lane is real. -- `cargo run -p spec-cli -- test examples/ecommerce/units/pricing/calculate_total.unit.spec --target-language typescript` fails during generation with: - - `unit 'pricing/apply_discount' is not eligible for the bounded M46 TypeScript lane: body.typescript is required` - -Code truth matches that live behavior: - -- [`spec-core/src/typescript_backend.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/typescript_backend.rs) hard-codes the M46 executor to: - - `kind:function` - - compatibility key `function.arithmetic_leaf.monotone_up.v1` - - `deps: []` or exactly one direct local helper dep -- [`spec-core/src/validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/validator.rs) enforces the same M46 gate with stable TypeScript-specific errors. -- [`spec-core/src/semantic_review.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/semantic_review.rs) already supports: - - `function.wrapper.pipeline.v1` - - `function.wrapper.pipeline.chain3.v1` - - same-tree contextual dep resolution for those families -- [`semantic-families/function.wrapper.pipeline.v1/candidate.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/semantic-families/function.wrapper.pipeline.v1/candidate.md) defines the supported wrapper packet as one straight-line two-call wrapper over two supported local deps. -- [`TODOS.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/TODOS.md) already records the deferred next wedge: - - wrapper TypeScript execution in `spec` - - explicit non-goals for cross-library helper imports - - explicit non-goals for generic multi-dependency execution - -That is enough evidence. The next missing artifact is not another family-choice loop. It is the post-M46 execution contract for same-tree wrappers. - -## Status Quo - -What the repo already has: - -- first-class TypeScript CLI entry points in `spec generate`, `spec build`, `spec test`, and `spec status` -- one repo-owned toolchain contract: - - Bun build - - Bun execution - - generated runtime helpers under `__spec_ts/` -- target-specific TypeScript passport proof surfaces -- semantic review support for monotone-up, monotone-down, helper passthrough, wrapper pipeline, and chain3 pipeline shapes - -What the repo still does not have: - -- TypeScript execution for `function.wrapper.pipeline.v1` -- same-tree closure generation for wrapper dep sets when those deps are not monotone-up targets -- a clear execution rule for "supported wrapper family and its local dep closure" distinct from "arbitrary multi-dep TypeScript" -- canonical wrapper fixtures and ecommerce units with authored TypeScript bodies on the full execution closure - -## Target User And Narrowest Wedge - -Target user: - -- the maintainer or AI operator who already trusts the current semantic family boundaries and wants the first honest TypeScript wrapper execution path in `spec` - -Narrowest wedge: - -- first-class TypeScript execution for `function.wrapper.pipeline.v1` -- only when the wrapper and every direct dep live in the same loaded unit set and generated tree -- only when those direct deps already classify to the supported local family shapes the wrapper packet expects -- no cross-library resolution -- no molecule execution -- no chain3 execution -- no generic "multiple deps are fine now" claim - -This is not generic multi-dependency TypeScript support. - -It is one bounded family-shaped dependency closure: - -- two-step wrapper pipeline -- same-tree local dep closure -- existing Bun runtime -- existing passport/status proof surfaces - -## Premises - -1. The checkpoint is still right. Semantic review is core product. Family-analysis governance is servant work. -2. The current family recommendation surface still says stop, so another Rust family-choice milestone would be fake motion. -3. The repo already earned a bounded TypeScript executor. The next honest expansion is family-shaped execution breadth, not more TypeScript metadata. -4. The wrapper packet is the right next slice because it exercises local dependency closure without opening cross-library or generic arbitrary-dep scope. -5. The milestone should expand executor truth, not rewrite shared-core architecture again. - -## Approaches Considered - -### Approach A: generic TypeScript multi-dep execution now - -Pros: - -- sounds like broad progress - -Cons: - -- blows past the family boundary the semantic reviewer already proved -- collapses "same-tree wrapper execution" into "arbitrary dependency topology" -- would force new policy for cross-library resolution, deep closure semantics, and unsupported dep combinations - -Verdict: reject - -### Approach B: same-tree wrapper pipeline execution only - -Pros: - -- matches the deferred TODO exactly -- reuses an already-supported semantic family -- exercises the missing local closure behavior without widening to arbitrary graphs -- keeps validation, generation, build, test, and proof all family-shaped - -Cons: - -- requires explicit authored TypeScript bodies on the wrapper closure fixtures -- needs the executor to distinguish "dep closure for a supported wrapper family" from the current monotone-up-only tree assumption - -Verdict: choose - -### Approach C: chain3 directly - -Pros: - -- more impressive - -Cons: - -- chain3 is a larger closure and a worse first execution widening -- would blur the difference between "supported wrapper family" and "all supported wrapper-like families" - -Verdict: reject - -## Recommended Approach - -Choose Approach B. - -M52 should make `spec` execute exactly one new TypeScript family-shaped wedge: - -- root target compatibility key `function.wrapper.pipeline.v1` -- exactly two direct local deps -- both deps must already resolve inside the same loaded unit set -- both deps must already classify to the supported family shapes the wrapper packet expects -- generated output includes the wrapper and its same-tree dep closure -- build/test/status evidence stays target-specific and additive - -The important design rule is this: - -The widened executor must still speak in family language, not raw dep-count language. - -If the code simply changes from "0 or 1 dep" to "up to N deps," the milestone failed. - -## Architecture Direction - -```text -CURRENT M46 - TS target root must be monotone_up leaf - -> optional one local helper dep - -> generate TS tree - -> bun build - -> bun local tests - -M52 TARGET - TS target root may be wrapper.pipeline.v1 - -> exactly two same-tree local supported deps - -> include wrapper + dep closure in TS tree - -> wrapper body executes against generated local dep modules - -> bun build - -> bun local tests - -> target proofs written to wrapper and closure members as appropriate - -STILL OUT - cross-library dep resolution - arbitrary multi-dep execution - chain3 execution - molecule tests - seam kinds -``` - -## What Already Exists - -| Sub-problem | Existing owner | Reuse verdict | -| --- | --- | --- | -| TS CLI entry points | [`spec-cli/src/commands.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-cli/src/commands.rs) | reuse | -| TS runtime and generated helper surface | [`spec-core/src/typescript_backend.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/typescript_backend.rs) | reuse, extend narrowly | -| target-specific proof storage | [`spec-core/src/passport.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/passport.rs) | reuse | -| supported wrapper family semantics | [`spec-core/src/semantic_review.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/semantic_review.rs) | reuse as authority | -| wrapper packet truth | [`semantic-families/function.wrapper.pipeline.v1/`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/semantic-families/function.wrapper.pipeline.v1/) | reuse, add TS execution fixtures as needed | -| canonical wrapper example | [`examples/ecommerce/units/pricing/calculate_total.unit.spec`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/examples/ecommerce/units/pricing/calculate_total.unit.spec) | reuse, widen with authored TS closure | - -## Scope Decision - -M52 should authorize exactly this: - -1. admit `function.wrapper.pipeline.v1` as a TypeScript execution target in validator and generator code paths -2. require both direct wrapper deps to be local to the same loaded unit set and generated tree -3. require those deps to already classify to the supported local family shapes expected by the wrapper packet -4. generate the wrapper and the exact local dep closure needed for Bun build/test -5. add authored `body.typescript` to the canonical wrapper closure and maintained wrapper packet fixtures -6. extend CLI coverage and docs so the repo can prove the new contract directly - -M52 should not authorize: - -- cross-library TypeScript imports -- generic "2 or more deps are now supported" language -- chain3 execution -- molecule TypeScript support -- new `spec validate --target-language` surface -- `spec export --target-language` -- seam-kind target execution -- new shared-core extraction or architecture cleanup - -## Proposed Execution Contract - -### Validator contract - -TypeScript target eligibility should branch by supported compatibility key: - -- `function.arithmetic_leaf.monotone_up.v1` - - keep current M46 rule -- `function.wrapper.pipeline.v1` - - require exactly two direct deps - - both deps must be local, same-tree, and present in the loaded unit set - - both deps must already classify to the supported family shapes the wrapper packet expects - - both deps must author `body.typescript` - - wrapper local tests must stay within one bounded translated grammar - -Important: - -The validator should reject wrapper roots whose deps are merely "supported somehow." The contract needs exact allowed family combinations, not a vague supported-set check. - -### Generator contract - -The TypeScript tree generator should stop treating every included spec as if it must itself be a monotone-up root. - -Instead it should: - -- validate the target closure from the requested root set -- include all required local modules for that closure -- allow closure members whose role is "supported dep of a supported wrapper target" -- preserve the existing frozen helper/runtime filenames - -That is the key product change. Right now same-tree wrapper execution fails before Bun because the dep closure is validated under the wrong role. - -### Build and test contract - -Keep the current toolchain: - -- `bun build` for compile proof -- `bun run` for local test execution - -Do not add: - -- `npm` -- `package.json` -- `tsconfig.json` -- alternate runtime selection - -### Proof contract - -Reuse current passport/status target-proof storage. - -No new schema is needed if the executor can now refresh `target_proofs.typescript` for: - -- wrapper root units -- their closure members when the run includes them - -The repo should continue to keep Rust proof and TypeScript proof distinct. - -## Failure Modes Registry - -| Failure mode | Why it matters | M52 response | -| --- | --- | --- | -| executor widens to arbitrary multi-dep graphs | turns one lake into an ocean | gate strictly on wrapper family compatibility key | -| wrapper dep is cross-library | silently spends a different milestone | reject with stable TypeScript validation error | -| wrapper dep exists locally but lacks authored TypeScript body | current live failure on `pricing/calculate_total` | require authored TS body on closure members | -| closure generation includes unsupported same-tree units | false "tree-wide TS support" claim | include only the exact validated closure | -| docs say multi-dep TS now works | user learns the wrong contract | document family-shaped scope explicitly | -| chain3 sneaks in because the semantic reviewer supports it | scope creep | explicit non-goal and route rejection in M52 | - -## Candidate File Surfaces - -Likely code scope: - -- [`spec-core/src/typescript_backend.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/typescript_backend.rs) -- [`spec-core/src/validator.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-core/src/validator.rs) -- [`spec-cli/src/commands.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-cli/src/commands.rs) -- [`spec-cli/tests/cli.rs`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/spec-cli/tests/cli.rs) - -Likely authored truth scope: - -- [`examples/ecommerce/units/pricing/apply_discount.unit.spec`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/examples/ecommerce/units/pricing/apply_discount.unit.spec) -- [`examples/ecommerce/units/pricing/apply_tax.unit.spec`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/examples/ecommerce/units/pricing/apply_tax.unit.spec) -- [`examples/ecommerce/units/pricing/calculate_total.unit.spec`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/examples/ecommerce/units/pricing/calculate_total.unit.spec) -- [`semantic-families/function.wrapper.pipeline.v1/**`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/semantic-families/function.wrapper.pipeline.v1) - -Likely doc scope: - -- [`README.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/README.md) -- [`CHANGELOG.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/CHANGELOG.md) -- [`TODOS.md`](/Users/spensermcconnell/__Active_Code/atomize-hq/spec/TODOS.md) - -## Test And Proof Floor - -Minimum proof floor: - -1. validator regressions for wrapper TypeScript target eligibility -2. generator regressions for wrapper closure tree emission -3. CLI end-to-end for: - - wrapper unit TypeScript success in canonical ecommerce - - wrapper packet TypeScript success in aligned fixture - - wrapper near-miss or malformed closure rejection -4. status/passport regression proving TypeScript target proof records refresh for the wrapper root - -Nice-to-have, not required for M52: - -- chain3 proof -- molecule proof -- cross-library proof - -## Success Criteria - -M52 is successful when: - -- `spec test --target-language typescript` passes for the canonical same-tree wrapper example -- the wrapper packet can prove an aligned TypeScript execution path inside `spec`, not only inside `xtask` -- the validator rejects cross-library or generic multi-dep widening with stable errors -- the generated TypeScript tree includes only the validated local closure -- docs can explain in one paragraph why wrapper same-tree execution is in, but chain3 and cross-library work are still out - -## What This Leaves For Later - -- chain3 TypeScript execution -- cross-library TypeScript dep resolution -- generic multi-dependency TypeScript execution -- molecule TypeScript execution -- seam-kind target execution - -That is fine. The point of M52 is not to finish TypeScript. The point is to make the next truthful step real and bounded. diff --git a/docs/north_star_v0.2.md b/docs/north_star_v0.2.md index 64123af..c5c0553 100644 --- a/docs/north_star_v0.2.md +++ b/docs/north_star_v0.2.md @@ -8,6 +8,23 @@ > [`README.md`](../README.md), [`CHANGELOG.md`](../CHANGELOG.md), [`PLAN.md`](../PLAN.md), and > [`DECISIONS.md`](../DECISIONS.md). +## Current Grounding + +This vision doc is still directionally right, but the shipped repo is narrower +and more concrete than the full ambition below. + +Today's repo truth is: + +- Rust-first and proof-first +- authored around `*.unit.spec`, `.test.spec`, and `.plan.spec` +- currently centered on `kind:function`, `kind:data`, and `kind:sum` +- explicit about atom and molecule proof today, not a broad "all test tiers are shipped" claim +- intentionally narrow in semantic-review support and benchmark-backed product claims + +Read the rest of this document as the long-term destination. Read the root +README and core mechanisms guide for what a contributor should actually do on a +live checkout today. + ## Purpose This document defines the long-term product vision for **spec**, a semantic-unit development system for authoring, validating, compiling, and evolving software as structured semantic records rather than as loosely related text files. diff --git a/docs/roadmap_and_release_shape_v0.1.md b/docs/roadmap_and_release_shape_v0.1.md index c06628e..31625c2 100644 --- a/docs/roadmap_and_release_shape_v0.1.md +++ b/docs/roadmap_and_release_shape_v0.1.md @@ -8,6 +8,20 @@ > [`README.md`](../README.md), [`CHANGELOG.md`](../CHANGELOG.md), [`PLAN.md`](../PLAN.md), and > [`DECISIONS.md`](../DECISIONS.md). +## Current Grounding + +This roadmap draft is still useful for the "why this sequencing existed" story, +but it is no longer the active milestone authority. + +Today: + +- shipped behavior is better captured by the root README, changelog, and active plan docs +- much of the M1-M5 shape described below has already landed in narrower, repo-specific form +- Rust-first proof loops, plan artifacts, benchmark-backed claim surfaces, and bounded second-language work are now concrete repo realities, not just roadmap aspirations + +Read this doc for sequencing logic and product intent. Read current authority +docs for what the repo actually promises on a live checkout. + ## Purpose This document translates the current **North Star vision** and **high-level technical architecture** into an execution sequence. diff --git a/examples/ecommerce/README.md b/examples/ecommerce/README.md index 80661e3..ff09273 100644 --- a/examples/ecommerce/README.md +++ b/examples/ecommerce/README.md @@ -1,116 +1,182 @@ # ecommerce example -This example shows a small pricing domain authored as `.unit.spec` files, plus molecule tests, a checked-in plan artifact, and the canonical migration wedge for `pricing/discount_strategy`. -The canonical in-repo copy also ships tracked molecule evidence for the pricing molecule tests so the benchmark-root proof wall stays truthful on a fresh clone: +This is the canonical single-library `spec` example in the repo. + +If the root [`README.md`](../../README.md) tells you what `spec` is, this doc +shows you one truthful place to touch it. Use this example when you want to: + +- run the first real validate/build/test/status loop +- inspect both `kind:function` and seam behavior in one library +- see where generated Rust, passports, and molecule evidence actually land +- compare a migrated seam against the pre-`spec` raw Rust baseline + +For the broader mental model, read +[`docs/core_mechanisms_guide_v0.1.md`](../../docs/core_mechanisms_guide_v0.1.md) +next to this file, not instead of it. + +## Fastest First Run + +From the repo root: ```bash +cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/apply_tax.unit.spec --format json +cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/apply_tax.unit.spec +cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy_checkout_flow.test.spec cargo run -p spec-cli -- status examples/ecommerce/units --format json ``` -## M13 migration wedge +What changes when you run that loop: + +- generated Rust under `examples/ecommerce/src/generated/` +- unit proof in `examples/ecommerce/units/**/*.spec.passport.json` +- molecule proof in `examples/ecommerce/units/**/*.test.evidence.json` -`pricing/discount_strategy` exists in two forms on purpose: +If you have `spec` installed locally, the shorter example-root form is: -- Raw Rust baseline: `src/raw_baseline/pricing/discount_strategy.rs` -- Migrated seam: `units/pricing/discount_strategy.unit.spec` +```bash +cd examples/ecommerce +spec build units +spec test units/pricing/apply_tax.unit.spec +spec test units/pricing/discount_strategy_checkout_flow.test.spec +spec status units --format json +``` -Both implement the same pricing job: choose one discount strategy, compute `discount_amount(subtotal)`, and expose `discounted_subtotal(subtotal)`. The hand-written enum shows the pre-`spec` baseline implementation of that branching behavior. The `kind: sum` seam shows the authored version that `spec build` lowers into generated Rust. +## What This Example Demonstrates -The `units/pricing/discount_strategy_checkout_flow.test.spec` molecule test covers the `pricing/discount_strategy` `sum` seam together with the existing `pricing/pricing_quote` `data` seam and `pricing/apply_tax` function unit so the example proves a mixed-kind checkout flow, not just enum syntax. +### 1. The canonical pricing function trio -The `pricing/pricing_quote` seam remains in place as a sibling example. +These are the cleanest current `kind:function` examples: -## M20 semantic review boundary +- `pricing/apply_discount` +- `pricing/apply_tax` +- `pricing/calculate_total` -The pricing trio demonstrates the bounded `kind:function` semantic-review families currently proved with unseen examples and stricter wrapper-flow checks: +They are useful because they sit inside the shipped bounded semantic-review +vocabulary: - `pricing/apply_discount` proves `function.arithmetic_leaf.monotone_down_nonnegative.v1` - `pricing/apply_tax` proves `function.arithmetic_leaf.monotone_up.v1` - `pricing/calculate_total` proves `function.wrapper.pipeline.v1` -This is still a bounded support story, not generic function understanding, and M20 adds no new supported family. Unsupported near-miss wrappers stay keyed as `unsupported.function.v1`, additive-only, and health-neutral. +That is a narrow support story on purpose. It is not generic function +understanding. -M20 also makes unsupported-function truth explicit. The public fields are exactly `semantic_review.support_status`, `semantic_review.unsupported_reason_codes`, and `semantic_review.rewrite_hints`. New supported reviews write `support_status: supported`; unsupported function reviews write `support_status: unsupported`. Consumers should branch on `semantic_review.support_status == "unsupported"` rather than infer unsupported state from `verdict` or `evaluator_scope`, though legacy reviews without `support_status` still fall back to `evaluator_scope` plus `unsupported.*.v1` inference. +### 2. A `kind:data` seam -Only `spec test` refreshes semantic-review truth. `spec build`, `spec generate`, `spec status`, and `spec export` project stored truth only. Fresh unsupported function proof is preserved on read-side surfaces such as `spec status` and `spec export`; stale unsupported function proof is dropped there while the unit's freshness/stale health still reports normally. +`pricing/pricing_quote.unit.spec` is the clean seam example. -## Locked adversarial score table +It keeps shared data semantics, constructors, and methods in one authored unit, +then lowers into generated Rust. It is the easiest place to see how seam +support differs from function-family support. -Recorded calibration scores for the migration wedge candidates: +### 3. A `kind:sum` seam with a raw baseline comparison -- `pricing/discount_strategy`: `19` -- `pricing/pricing_quote`: `16` -- `pricing/discount_plus_tax`: `14` +`pricing/discount_strategy.unit.spec` is the canonical sum-seam migration +wedge. -Canonical wedge remains `pricing/discount_strategy`. +It exists side by side with the handwritten baseline at: -## Build / verify +- `src/raw_baseline/pricing/discount_strategy.rs` -This crate expects generated Rust to exist at `src/generated/` (gitignored). +The matching molecule proof, +`pricing/discount_strategy_checkout_flow.test.spec`, checks that the generated +sum seam still agrees with the baseline behavior while composing with the +current pricing flow. -If you have `spec` installed, the quickest end-to-end loop from `examples/ecommerce/` is: +### 4. Molecule tests as cross-unit proof -```bash -spec build units -spec test units/pricing/discount_strategy.unit.spec -spec test units/pricing/discount_strategy_checkout_flow.test.spec -spec status units --format json -spec export units -spec plan validate plans/refactors/checkout-tax-refactor.plan.spec --format json -``` +This example ships three molecule tests: -From the repo root: +- `pricing/checkout_flow.test.spec` +- `pricing/discount_plus_tax.test.spec` +- `pricing/discount_strategy_checkout_flow.test.spec` + +Use them when the behavior crosses unit boundaries. If a test needs more than +one unit import, it belongs here rather than in `local_tests`. + +### 5. A checked-in plan artifact + +The canonical example plan lives at: + +- `plans/refactors/checkout-tax-refactor.plan.spec` + +Use it when you want to inspect the current `.plan.spec` authoring shape +without mixing plan concepts into the normal unit proof loop. + +## Command Authority In This Example + +The proof-authoritative example root is: ```bash -cargo run -p spec-cli -- validate examples/ecommerce/units/pricing/discount_strategy.unit.spec --format json -cargo run -p spec-cli -- build examples/ecommerce/units --output examples/ecommerce/src/generated -cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy.unit.spec -cargo run -p spec-cli -- test examples/ecommerce/units/pricing/discount_strategy_checkout_flow.test.spec cargo run -p spec-cli -- status examples/ecommerce/units --format json cargo run -p spec-cli -- export examples/ecommerce/units ``` -Repo-root status remains available for broad inventory: +Repo-root status still exists: ```bash cargo run -p spec-cli -- status . --format json ``` -Treat that repo-root view as `inventory_only`, not as the proof-authoritative default. Repo-root export is intentionally unsupported for this workspace shape and should fail with `SPEC_UNSUPPORTED_SCOPE`. +Treat that repo-root view as broad inventory only. It is useful for seeing many +roots at once, but it is not the default green proof wall for this example. -If you want the shorter generate/check/test loop instead: +Repo-root export is intentionally unsupported for this workspace shape and +should fail with `SPEC_UNSUPPORTED_SCOPE`. -```bash -cargo run -p spec-cli -- generate examples/ecommerce/units --output examples/ecommerce/src/generated -cargo check --manifest-path examples/ecommerce/Cargo.toml -cargo test --manifest-path examples/ecommerce/Cargo.toml -``` +## Semantic Review Boundary -Or, if you have `spec` installed, from `examples/ecommerce/`: +Two rules matter here: -```bash -spec generate units --output src/generated -cargo check -cargo test -``` +1. Only `spec test` refreshes semantic-review truth. +2. Unsupported-function truth is explicit through: + - `semantic_review.support_status` + - `semantic_review.unsupported_reason_codes` + - `semantic_review.rewrite_hints` + +That means `spec build`, `spec generate`, `spec status`, and `spec export` only +project already-stored semantic-review truth. They do not create new truth. -Files: +## Files Worth Reading + +Source specs: - `units/money/round.unit.spec` - `units/pricing/apply_discount.unit.spec` - `units/pricing/apply_tax.unit.spec` - `units/pricing/calculate_total.unit.spec` -- `units/pricing/discount_strategy.unit.spec` -- `units/pricing/discount_strategy_checkout_flow.test.spec` +- `units/pricing/calculate_total_guarded_tax.unit.spec` - `units/pricing/pricing_quote.unit.spec` +- `units/pricing/discount_strategy.unit.spec` - `units/pricing/checkout_flow.test.spec` - `units/pricing/discount_plus_tax.test.spec` +- `units/pricing/discount_strategy_checkout_flow.test.spec` - `plans/refactors/checkout-tax-refactor.plan.spec` + +Raw baseline comparison: + - `src/raw_baseline/pricing/discount_strategy.rs` - `src/raw_baseline/pricing/pricing_quote.rs` -Derived artifacts such as `src/generated/`, `*.spec.passport.json`, and `*.test.evidence.json` are generated from those source specs and should not be hand-edited. -The checked-in `pricing/*.test.evidence.json` files are the canonical generated outputs for this example. Refresh them by rerunning `spec test units --output src/generated` whenever the molecule specs or their covered unit contracts change, then commit the regenerated files. -Single-file `spec test` runs use an isolated internal generated tree, so they do not rewrite this example's checked-out `src/generated/` directory. +Generated and observed artifacts: + +- `src/generated/` +- `units/**/*.spec.passport.json` +- `units/**/*.test.evidence.json` + +Do not hand-edit generated Rust or proof artifacts. Edit the source specs, then +rerun the relevant `spec` command. + +## What To Ignore On Your First Pass + +If your only goal is to understand the example, ignore: + +- benchmark roster mechanics +- milestone history +- family-recommendation artifacts +- broader repo-root inventory questions -The `Cargo.toml` and `src/main.rs` are intentionally minimal. They provide a project scaffold for generated output and the side-by-side raw-vs-migrated pricing seam examples. +Run the first loop, inspect one unit, inspect one passport, inspect one +molecule evidence file. That is enough to make the rest of the repo much less +mysterious. From 861840fbdd9215e5de3232163dd6d386828fd453 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 14:55:16 +0000 Subject: [PATCH 12/24] Trim outdated docs and add a docs index --- docs/README.md | 2 + docs/kind_coverage_map_v0.1.md | 315 ++++++++++++++++++ .../units/money/round.spec.passport.json | 2 +- .../pricing/apply_discount.spec.passport.json | 2 +- .../pricing/apply_tax.spec.passport.json | 2 +- .../calculate_total.spec.passport.json | 2 +- ...ulate_total_guarded_tax.spec.passport.json | 2 +- .../discount_strategy.spec.passport.json | 2 +- .../pricing/pricing_quote.spec.passport.json | 2 +- 9 files changed, 324 insertions(+), 7 deletions(-) create mode 100644 docs/kind_coverage_map_v0.1.md diff --git a/docs/README.md b/docs/README.md index 96c4972..8052f62 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,6 +22,8 @@ and durable decisions aligned. Repo entry point. What `spec` is, the first command loop, and the day-1 vocabulary. - [`core_mechanisms_guide_v0.1.md`](./core_mechanisms_guide_v0.1.md) The mental model doc. Use this when you need to separate authored truth, proof, semantic review, families, and benchmarks. +- [`kind_coverage_map_v0.1.md`](./kind_coverage_map_v0.1.md) + The progress-map doc. Use this when you need to know which kinds and per-kind categories are shipped, deferred, or still unnamed. - [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. - [`AGENTS.md`](../AGENTS.md) diff --git a/docs/kind_coverage_map_v0.1.md b/docs/kind_coverage_map_v0.1.md new file mode 100644 index 0000000..a8e8981 --- /dev/null +++ b/docs/kind_coverage_map_v0.1.md @@ -0,0 +1,315 @@ +# spec — Kind Coverage Map +**Version:** v0.1 +**Status:** Active working map +**Date:** 2026-05-24 + +## Purpose + +This document is the repo's first-pass progress map for `spec` kinds and the +categories inside each kind. + +It exists because the repo already has the raw truth needed to answer support +questions, but it does not yet have one place that turns that truth into a +navigation map. + +Without that map, the project keeps re-asking vague questions like: + +- "what is still missing?" +- "is this a new kind or just a wider function surface?" +- "how far are we from completion for this shape?" + +This doc is meant to make those questions concrete. + +## What This Map Measures + +This map tracks three different things: + +1. **Top-level kinds** + The structural unit shapes the repo currently owns. +2. **Per-kind categories** + The named buckets or sub-surfaces inside a kind. +3. **Support dimensions** + The axes that determine how "real" a category is today. + +The support dimensions used here are: + +- **Core** + Is the authored shape legal `spec` source? +- **Rust** + Can the Rust lane lower and execute it? +- **TypeScript** + Can the bounded TypeScript lane lower and execute it? +- **Proof** + Does the repo have current atom and/or molecule proof patterns for it? +- **Semantic review** + Can the bounded reviewer classify it honestly? +- **Benchmark claim** + Does it contribute directly to a bounded public product claim such as Rust V1? + +## Legend + +- **Shipped**: repo-owned and live today +- **Bounded**: shipped, but intentionally narrow +- **Deferred**: explicitly named as future work, not part of current truth +- **Unnamed**: clearly a gap, but not yet owned as a named category +- **N/A**: not the right axis for that row + +## The Main Rule + +Every roadmap move should answer one of these questions explicitly: + +1. Is this adding a **new kind**? +2. Is this adding a **new category inside an existing kind**? +3. Is this widening a **support dimension** for an existing category? + +If a proposal cannot be placed in one of those buckets, it is probably still +too vague to steer by. + +## Top-Level Kind Inventory + +| Kind | Structural status | Current category-map status | Rust V1 claim role | Biggest current gap | +| --- | --- | --- | --- | --- | +| `function` | Shipped | Strongest named map in the repo | Direct | Deferred categories are named, but the completion map above them is still thin | +| `data` | Shipped | Thin | Direct as plain seam | No broad category vocabulary beyond one known supported surface | +| `sum` | Shipped | Thin | Direct as plain seam | No broad category vocabulary beyond one known supported surface | +| future kinds | Unnamed | None | None | No checked-in authority for additional top-level kinds today | + +## What Is Actually Missing At The Top Level + +The repo does **not** currently have a checked-in list of "next kinds." + +That means the honest first-pass answer is: + +- the current owned kinds are `function`, `data`, and `sum` +- additional top-level kinds are not yet repo-owned enough to appear as real + roadmap targets +- the larger immediate blind spot is not "which fourth kind is missing?" +- the larger immediate blind spot is "which categories inside `data` and `sum` + are owned, which are merely hinted, and which support dimensions are still + absent?" + +So the project's map gap is currently more severe **inside kinds** than +**between kinds**. + +## Kind: `function` + +`kind:function` is the most mature map in the repo. + +It already has: + +- the richest semantic-review vocabulary +- the richest promotion and corpus-analysis machinery +- the clearest deferred edges +- the clearest distinction between direct claim-bearing families and helper-only + routes + +### Current repo-owned categories + +| Category | Current status | Public role | Notes | +| --- | --- | --- | --- | +| `function.arithmetic_leaf.monotone_down_nonnegative.v1` | Shipped | Direct | Canonical discount-like arithmetic leaf | +| `function.arithmetic_leaf.monotone_up.v1` | Shipped | Direct | Canonical tax-like arithmetic leaf | +| `function.wrapper.pipeline.v1` | Shipped | Direct | Straight-line two-call wrapper pipeline | +| `function.wrapper.pipeline.normalized_required_arg.v1` | Shipped | Direct | Narrow required-arg wrapper follow-on | +| `function.wrapper.pipeline.chain3.v1` | Shipped | Direct | Straight-line three-call wrapper pipeline | +| `function.helper.identity_passthrough.v1` | Shipped | Helper-only | Real supported route, but not separately named in the plain-English Rust V1 claim | +| `unsupported.function.v1` | Shipped terminal catch-all | No | Honest additive unsupported surface, not a supported family | + +### Explicitly deferred `function` categories + +These are already named in repo authority, which is good. The map knows they +exist, even though they are not shipped. + +| Deferred category pressure | Current status | Why it matters | +| --- | --- | --- | +| bounded generics for `kind:function` | Deferred to `V1.1` | This is a real authored-shape expansion, not a new top-level kind | +| async flows, runtime adapters, and IO-owned boundaries | Deferred to `V1.1` | This is a backend/runtime/proof expansion for function-shaped units | + +### Capability snapshot for `function` + +| Dimension | Current truth | Gap shape | +| --- | --- | --- | +| Core | Shipped | Main gaps are not legality; they are category and boundary growth | +| Rust | Strongest lane | Deferred edges are generics and async/IO | +| TypeScript | Bounded | Narrower than Rust and not a parity claim | +| Proof | Strong | Atom proof is first-class; molecule proof exists for interactions | +| Semantic review | Strongest area in repo | Still intentionally narrow, not general code understanding | +| Benchmark claim | Strong | Direct contributor to the benchmark-backed Rust V1 story | + +### What is still missing for `function` + +The missing map work for `function` is **not** "what is a function?" +The missing map work is: + +- an explicit completion matrix for every named function category across Rust, + TypeScript, proof, semantic review, and benchmark use +- a sharper distinction between categories that are merely supported and + categories that are central to the product claim +- a decision framework for when a deferred `V1.1` category is worth more than a + non-function wedge such as migration, recommendation trust, or workflow + completion + +## Kind: `data` + +`kind:data` is structurally real and publicly claim-bearing today, but its map +is much thinner than `kind:function`. + +Current structural truth is clear: + +- `data.fields` +- `constructors` +- `methods` +- no top-level `contract` +- no top-level `deps` +- no top-level `imports` +- top-level `body.rust` must stay empty +- no top-level `body.typescript` + +That means the authored shape is real. The missing piece is a broader category +vocabulary above that shape. + +### Current repo-owned categories + +| Category | Current status | Public role | Notes | +| --- | --- | --- | --- | +| `data.pricing_quote.v1` | Shipped, bounded | Direct as plain data seam | Specific descriptor-based supported data surface | + +### Capability snapshot for `data` + +| Dimension | Current truth | Gap shape | +| --- | --- | --- | +| Core | Shipped | The structural seam contract is real | +| Rust | Shipped | Part of the current narrow-core Rust V1 claim as a plain data seam | +| TypeScript | No | No seam-kind TypeScript execution today | +| Proof | Shipped | Can be benchmark-positive and proof-bearing today | +| Semantic review | Bounded and specific | Not a broad family system; currently specific supported data surfaces only | +| Benchmark claim | Direct | Plain data seams are part of the current Rust V1 claim | + +### What is still missing for `data` + +This is one of the biggest map holes in the repo. + +What is missing is not basic shape support. What is missing is a named category +taxonomy such as: + +- which data-surface descriptors are first-class and worth owning +- which are merely one-off example seams +- which should get descriptor-based semantic review keys +- which should count toward benchmark claims +- which should remain valid core seams without any semantic classification + +So the first-pass honest state for `data` is: + +- one known supported semantic surface exists +- the general category map for data seams is still mostly **Unnamed** + +## Kind: `sum` + +`kind:sum` is in the same overall shape as `data`: + +- structurally real +- publicly claim-bearing as a plain seam +- much thinner category map than `function` + +Current structural truth is clear: + +- `sum.variants` +- `methods` +- no top-level `contract` +- no top-level `deps` +- no top-level `imports` +- top-level `body.rust` must stay empty +- no top-level `body.typescript` + +### Current repo-owned categories + +| Category | Current status | Public role | Notes | +| --- | --- | --- | --- | +| `sum.discount_strategy.v1` | Shipped, bounded | Direct as plain sum seam | Specific descriptor-based supported sum surface | + +### Capability snapshot for `sum` + +| Dimension | Current truth | Gap shape | +| --- | --- | --- | +| Core | Shipped | The structural seam contract is real | +| Rust | Shipped | Part of the current narrow-core Rust V1 claim as a plain sum seam | +| TypeScript | No | No seam-kind TypeScript execution today | +| Proof | Shipped | Can participate in proof and benchmark-positive roots today | +| Semantic review | Bounded and specific | Not a broad family ecosystem; currently specific supported sum surfaces only | +| Benchmark claim | Direct | Plain sum seams are part of the current Rust V1 claim | + +### What is still missing for `sum` + +The missing map work mirrors `data`: + +- a named category vocabulary beyond one known supported surface +- a rule for which sum descriptors deserve semantic-review keys +- a rule for which sum surfaces are benchmark-relevant versus merely valid +- a progress view that distinguishes "valid sum seam" from "strategically + important sum seam" + +So the first-pass honest state for `sum` is: + +- one known supported semantic surface exists +- the general category map for sum seams is still mostly **Unnamed** + +## Cross-Kind Gaps + +Across all current kinds, the main missing map work is: + +1. **A real per-kind category registry** + `function` mostly has one. `data` and `sum` mostly do not. +2. **A per-category capability matrix** + Today the repo often knows a category exists, but not all the support + dimensions that make it feel "complete." +3. **A benchmark-eligibility map** + Especially important for keeping helper-only routes distinct from direct + claim-bearing categories. +4. **A completion scoreboard** + Something that answers: + - how many named categories exist in this kind? + - how many are shipped? + - how many are deferred? + - how many are still unnamed pressure? + +## What This Map Says About "What Next" + +This first-pass map suggests a sharper roadmap frame: + +- if the project wants to expand `kind:function`, it should say which category + or deferred edge is being widened +- if the project wants to make seam kinds feel complete, it probably does **not** + need a new top-level kind first +- it probably needs explicit category taxonomies for `data` and `sum` +- if the project wants a durable progress meter, the next artifact after this + one should be a machine-readable or table-driven category registry rather than + another broad roadmap essay + +## Immediate Follow-On Artifacts + +The next useful docs after this one would be: + +1. `docs/function_category_matrix_v0.1.md` + One row per function category, one column per support dimension. +2. `docs/data_category_taxonomy_v0.1.md` + Name the currently owned and still-unnamed `kind:data` categories. +3. `docs/sum_category_taxonomy_v0.1.md` + Name the currently owned and still-unnamed `kind:sum` categories. +4. A future machine-readable registry + So the repo can compute progress instead of reasoning from prose only. + +## Bottom Line + +The repo does have a map now, but only in fragments. + +Today: + +- `function` has the strongest category map +- `data` and `sum` have real structural support but thin category vocabularies +- no additional top-level kinds are currently owned enough to steer by + +So the clearest current planning need is: + +> build the per-kind category maps to the point where the repo can measure +> completion honestly, instead of talking about support in one overloaded way + diff --git a/examples/ecommerce/units/money/round.spec.passport.json b/examples/ecommerce/units/money/round.spec.passport.json index 6db7c53..32a8fb2 100644 --- a/examples/ecommerce/units/money/round.spec.passport.json +++ b/examples/ecommerce/units/money/round.spec.passport.json @@ -13,7 +13,7 @@ }, "deps": [], "local_tests": [], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/money/round.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json index bfbb172..b2f38b7 100644 --- a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json @@ -28,7 +28,7 @@ "expect": "apply_discount(Decimal::new(10000, 2), Decimal::new(10, 2)) == Decimal::new(9000, 2)" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/apply_discount.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json index ce187ed..6df9fd1 100644 --- a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json @@ -27,7 +27,7 @@ "expect": "apply_tax(Decimal::new(10000, 2), Decimal::new(725, 4)) == Decimal::new(10725, 2)" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/apply_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json index 26cecd9..9ba6a4b 100644 --- a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/calculate_total.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json index 92ed8f7..9d3c391 100644 --- a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total_guarded_tax(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/calculate_total_guarded_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json index a691efd..6c48fb8 100644 --- a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json +++ b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json @@ -143,7 +143,7 @@ "expect": "DiscountStrategy::None.fixed_amount_capped_behavior_holds()" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json index b4c107c..b50abb2 100644 --- a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json +++ b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json @@ -100,7 +100,7 @@ "expect": "PricingQuote::new(rust_decimal::Decimal::new(10000, 2), rust_decimal::Decimal::new(10, 2), rust_decimal::Decimal::new(725, 4)).total() == rust_decimal::Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T02:18:26Z", + "generated_at": "2026-05-24T14:14:09Z", "source_file": "examples/ecommerce/units/pricing/pricing_quote.unit.spec", "evidence": { "build_status": "pass", From 5c1acbc353bb12045349eb4a3a48e2d084becb60 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 17:56:53 +0000 Subject: [PATCH 13/24] Add kind coverage map and maintenance guidance --- docs/kind_coverage_map_v0.1.md | 116 ++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/docs/kind_coverage_map_v0.1.md b/docs/kind_coverage_map_v0.1.md index a8e8981..f108cc1 100644 --- a/docs/kind_coverage_map_v0.1.md +++ b/docs/kind_coverage_map_v0.1.md @@ -298,6 +298,121 @@ The next useful docs after this one would be: 4. A future machine-readable registry So the repo can compute progress instead of reasoning from prose only. +## How To Maintain This Map + +This map should change whenever repo truth changes in a way that affects: + +- top-level kinds +- named categories inside a kind +- support dimensions for an existing category +- public claim-bearing status for a category + +### Update triggers + +Update this document when any of these happen: + +1. A new top-level kind is added or explicitly proposed in checked-in authority. +2. A new per-kind category becomes repo-owned enough to name. +3. A category moves between `Unnamed`, `Deferred`, `Bounded`, and `Shipped`. +4. A backend lane expands or contracts for a category. +5. Semantic review gains or loses honest understanding for a category. +6. A benchmark or public product claim starts or stops depending on a category. +7. A milestone freezes a new boundary such as `V1.1` deferrals or admissions. + +### Source-of-truth order + +When updating this map, trust sources in this order: + +1. `README.md` +2. `docs/core_mechanisms_guide_v0.1.md` +3. `PLAN.md` +4. `DECISIONS.md` +5. `docs/rust_v1_contract_stack.md` +6. checked-in code and tests that prove a narrower or wider truth surface + +If a design draft says more than current authority docs or live code prove, +prefer the narrower current truth. + +### The update procedure + +When you update the map, walk this checklist in order: + +1. Identify the change type: + - new kind + - new category inside a kind + - widened support dimension + - narrowed support dimension + - public-claim status change +2. Identify the affected kind: + - `function` + - `data` + - `sum` + - or a newly owned top-level kind +3. Identify whether the change is structural or interpretive: + - structural means authored/core shape changed + - interpretive means backend, proof, semantic review, or benchmark meaning changed +4. Update the smallest correct row: + - the top-level kind inventory + - the kind's category table + - the kind's capability snapshot + - the cross-kind gaps or follow-on artifacts, if needed +5. Add or adjust prose only after the table-level truth is correct. + +### Classification rules + +Use these rules to avoid map drift: + +- Do **not** add a new kind just because a new function surface appears. +- Do **not** mark a category `Shipped` just because a draft doc names it. +- Do **not** mark a category `Direct` in public role unless current benchmark or + claim surfaces actually depend on it. +- Do **not** treat one example seam as a full category taxonomy for that kind. +- Do **not** collapse helper-only support into the same role as direct + claim-bearing support. + +### When to name a new category + +A category is ready to be named in this map when at least one of these is true: + +- current authority docs name it explicitly +- semantic review or routing code names it explicitly +- benchmark or claim surfaces depend on it explicitly +- the repo has frozen it as a deferred boundary that future work is expected to + cross honestly + +If none of those are true, leave the pressure as `Unnamed` rather than +inventing a category too early. + +### When to split this map into a more detailed artifact + +This file should stay high-signal and cross-kind. + +Create or expand a deeper follow-on doc when: + +- one kind has more than a handful of meaningful categories +- one kind needs per-category backend and proof detail +- one kind needs a machine-readable registry or scoreboard +- the maintenance burden of keeping detail here starts hiding the big picture + +That is why this doc points toward: + +- `docs/function_category_matrix_v0.1.md` +- `docs/data_category_taxonomy_v0.1.md` +- `docs/sum_category_taxonomy_v0.1.md` + +### Minimum review questions before merging an update + +Before landing a map change, ask: + +1. What changed in repo truth? +2. Is this a new kind, a new category, or a support-dimension change? +3. What evidence makes this true now? +4. Is the change direct claim-bearing, helper-only, bounded, deferred, or still + unnamed? +5. Did the update accidentally overstate support beyond current proof? + +If any answer is fuzzy, the map is probably being updated too early. + ## Bottom Line The repo does have a map now, but only in fragments. @@ -312,4 +427,3 @@ So the clearest current planning need is: > build the per-kind category maps to the point where the repo can measure > completion honestly, instead of talking about support in one overloaded way - From 4fb02b2caae0a8ee42691462b02e72b7844fc00a Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 18:44:19 +0000 Subject: [PATCH 14/24] Add kind coverage map and function category matrix --- docs/README.md | 2 + docs/function_category_matrix_v0.1.md | 183 ++++++++++++++++++++++++++ 2 files changed, 185 insertions(+) create mode 100644 docs/function_category_matrix_v0.1.md diff --git a/docs/README.md b/docs/README.md index 8052f62..309d72f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,6 +24,8 @@ and durable decisions aligned. The mental model doc. Use this when you need to separate authored truth, proof, semantic review, families, and benchmarks. - [`kind_coverage_map_v0.1.md`](./kind_coverage_map_v0.1.md) The progress-map doc. Use this when you need to know which kinds and per-kind categories are shipped, deferred, or still unnamed. +- [`function_category_matrix_v0.1.md`](./function_category_matrix_v0.1.md) + The detailed `kind:function` matrix. Use this when you need per-category truth for families, TypeScript, proof, and benchmark role. - [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. - [`AGENTS.md`](../AGENTS.md) diff --git a/docs/function_category_matrix_v0.1.md b/docs/function_category_matrix_v0.1.md new file mode 100644 index 0000000..7d5a403 --- /dev/null +++ b/docs/function_category_matrix_v0.1.md @@ -0,0 +1,183 @@ +# spec — Function Category Matrix +**Version:** v0.1 +**Status:** Active working map +**Date:** 2026-05-24 + +## Purpose + +This document is the per-category follow-on for `kind:function`. + +Use it when the higher-level [kind coverage map](./kind_coverage_map_v0.1.md) +is not enough and you need to answer more specific questions such as: + +- which function categories are actually shipped? +- which ones run in Rust? +- which ones run in the bounded TypeScript lane? +- which ones carry benchmark-positive public claim weight? +- which ones are helper-only or companion-negative only? + +## Scope + +This document tracks the **product/support surface** for `kind:function`. + +It does **not** try to document every maintainer command separately. +That distinction matters because some maintainer-facing paths are narrower than +the shipped product surface. In particular: + +- the bounded `spec test --target-language typescript` lane covers more + supported function categories than the narrower maintainer-facing + `cargo xtask family prove --target-language typescript` path +- this matrix tracks the repo's shipped support surface first +- command-specific maintenance constraints are called out in notes when they + matter + +## Reading Rules + +This matrix distinguishes four different ideas that are easy to blur: + +- **Promoted packet** + Is there a real promoted semantic-family packet? +- **Runtime route** + Does current runtime/semantic routing recognize the category as supported? +- **TypeScript lane** + Can the bounded product TypeScript lane execute roots or local graphs for it? +- **Benchmark role** + Does current benchmark evidence make it part of the positive Rust V1 proof + wall, helper-only closure support, companion-negative visibility, or no claim + at all? + +## Inventory Snapshot + +As of this doc's date, `cargo xtask family inventory --format json` reports: + +- promoted families: + - `function.wrapper.pipeline.chain3.v1` + - `function.wrapper.pipeline.normalized_required_arg.v1` + - `function.wrapper.pipeline.v1` + - `function.arithmetic_leaf.monotone_down_nonnegative.v1` + - `function.arithmetic_leaf.monotone_up.v1` + - `function.helper.identity_passthrough.v1` +- runtime-supported routes: + - the same six categories above +- supported-but-unpromoted families: + - none + +That means the current `kind:function` map has no runtime-supported shadow +category waiting outside promotion. The main gaps are deferred or still unnamed, +not "supported but unpromoted." + +## Matrix Legend + +- **Yes**: shipped and repo-owned today +- **Bounded**: shipped, but intentionally narrow +- **Indirect**: not the main claim carrier, but participates in proof or + closure around a claim-bearing category +- **Helper-only**: real support, but not the main category being claimed +- **Companion-negative**: visible in negative proof only, zero positive credit +- **Deferred**: explicitly named future boundary +- **No**: not part of the current shipped truth +- **N/A**: not the right axis for that row + +## Function Category Matrix + +| Category | Promoted packet | Runtime route | Rust lane | TypeScript lane | Atom proof | Molecule role | Semantic review | Benchmark role | Public role | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `function.arithmetic_leaf.monotone_down_nonnegative.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Indirect | Yes | Positive in `BENCH-ECOM` and `BENCH-SERVICE`; companion-negative visible in `BENCH-CROSSLIB` | Direct | Canonical discount-like leaf; supports zero-or-one helper dep shapes in current truth | +| `function.arithmetic_leaf.monotone_up.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Indirect | Yes | Positive in `BENCH-ECOM` and `BENCH-SERVICE`; companion-negative visible in `BENCH-CROSSLIB` | Direct | Canonical tax-like leaf; also the explicit M32 second-language pilot anchor | +| `function.wrapper.pipeline.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Indirect and flow-bearing | Yes | Positive in `BENCH-ECOM` and `BENCH-SERVICE`; companion-negative visible in `BENCH-CROSSLIB` | Direct | Straight-line two-call wrapper; current benchmark-positive wrapper baseline | +| `function.wrapper.pipeline.normalized_required_arg.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Indirect and flow-bearing | Yes | Positive in `BENCH-ECOM` and `BENCH-SERVICE` | Direct | Narrow required-arg wrapper follow-on; TypeScript root eligibility is covered in validator truth even though examples lean more on wrapper and chain3 | +| `function.wrapper.pipeline.chain3.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Indirect and flow-bearing | Yes | Companion-negative visible in `BENCH-CROSSLIB`; not part of current positive `BENCH-ECOM` or `BENCH-SERVICE` walls | Shipped but not positive-wall anchored | Straight-line three-call wrapper pipeline; current product support is ahead of current positive benchmark anchoring | +| `function.helper.identity_passthrough.v1` | Yes | Yes | Yes | Yes, bounded | Yes | Helper-only | Yes | Positive closure support inside `BENCH-ECOM`; helper-only marker in benchmark evidence | Helper-only | Real shipped function category, but not separately named in the plain-English Rust V1 claim | +| `unsupported.function.v1` | No | Terminal catch-all | N/A as a supported category | N/A as a supported category | N/A as a supported category | N/A | Yes, as unsupported verdict | No positive credit | Observation only | Honest additive unsupported surface; may coexist with real backend/proof truth for a unit, but never upgrades to supported-family credit | +| bounded generics for `kind:function` | No | No | Deferred | Deferred | Deferred | Deferred | Deferred | No | Deferred | Explicit `V1.1` authored-shape expansion; not a new kind | +| async flows, runtime adapters, and IO-owned boundaries | No | No | Deferred | Deferred | Deferred | Deferred | Deferred | No | Deferred | Explicit `V1.1` backend/runtime/proof expansion; Rust V1 remains synchronous-only today | + +## TypeScript Lane Notes + +The bounded TypeScript lane for `kind:function` currently has these repo-level +constraints: + +- function-only, not seam kinds +- atom-only proof +- no `.test.spec` molecule execution +- bounded local-graph and cross-library closure rules +- every reachable unit must classify to a supported semantic review +- every reachable unit must author `body.typescript` + +That means the TypeScript column in the matrix is not a broad "JavaScript +backend parity" claim. It is a bounded execution lane over already-supported +function categories. + +### Important maintainer-path caveat + +The maintainer-facing family workflow is narrower than the product lane: + +- `cargo xtask family prove --target-language typescript` currently allows only + `function.arithmetic_leaf.monotone_up.v1` and `function.wrapper.pipeline.v1` + +That does **not** shrink the product-lane truth recorded above. It means the +family-maintainer command surface is a stricter path than the broader bounded +`spec` execution lane. + +## What This Matrix Clarifies + +This matrix makes a few important truths visible: + +1. `kind:function` is not one undifferentiated support blob. + Some categories are positive-wall anchored, some are helper-only, and some + are shipped but not yet positive benchmark anchors. +2. The main `function` gap is no longer "do we have families?" + The main gap is category completion, benchmark anchoring, and deferred-edge + decisions. +3. `chain3` is a real shipped function category, but current public proof walls + do not anchor it the way they anchor the arithmetic, wrapper, and + normalized-required-arg categories. +4. The bounded TypeScript lane is broader than the narrow maintainer + `xtask family prove --target-language typescript` path. +5. Deferred `V1.1` surfaces are support-dimension expansions for + `kind:function`, not candidates for a fourth top-level kind. + +## How To Update This Matrix + +When function truth changes, update this document after checking: + +1. `cargo xtask family inventory --format json` +2. `README.md` +3. `docs/core_mechanisms_guide_v0.1.md` +4. `PLAN.md` +5. `DECISIONS.md` +6. benchmark labels and current benchmark evidence +7. current validator / semantic review / CLI test truth for TypeScript and proof + +Update the smallest correct thing: + +- a row in the matrix +- a note about benchmark role +- the TypeScript notes +- the inventory snapshot + +Do **not** mark a function category as positive-wall anchored unless the current +benchmark labels and evidence actually include it in `BENCH-ECOM` or +`BENCH-SERVICE`. + +Do **not** mark a category as helper-only unless the benchmark and claim surfaces +really treat it as closure support rather than the thing being claimed. + +## Likely Next Follow-On + +If `kind:function` keeps growing, the next artifact after this one should +probably be machine-readable rather than another prose-only doc: + +- a checked-in category registry with columns for: + - category id + - promoted packet status + - runtime route status + - Rust lane + - TypeScript lane + - atom proof + - molecule role + - semantic-review status + - benchmark role + - public role + +That would let the repo compute progress instead of only describing it. From 47dcd1fd2e5693f42df33a7c397c9c6894c38730 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Sun, 24 May 2026 23:31:06 +0000 Subject: [PATCH 15/24] Require explicit support for benchmark positive credit --- .../snapshots/BENCH-SERVICE.snapshot.json | 16 +- docs/README.md | 4 + docs/data_category_taxonomy_v0.1.md | 233 +++++++ docs/sum_category_taxonomy_v0.1.md | 274 +++++++++ spec-cli/tests/cli.rs | 6 +- .../export-service-billing-partial-full.json | 2 +- .../benchmarks/export-service-full.json | 12 +- .../benchmarks/status-repo-root-full.json | 576 +++++++++++++++++- .../status-repo-root-service-full.json | 26 +- .../benchmarks/status-service-full.json | 10 +- spec-cli/tests/rust_v1_service.rs | 50 +- spec-core/src/benchmark.rs | 6 +- 12 files changed, 1143 insertions(+), 72 deletions(-) create mode 100644 docs/data_category_taxonomy_v0.1.md create mode 100644 docs/sum_category_taxonomy_v0.1.md diff --git a/benchmarks/snapshots/BENCH-SERVICE.snapshot.json b/benchmarks/snapshots/BENCH-SERVICE.snapshot.json index e841f0d..5092939 100644 --- a/benchmarks/snapshots/BENCH-SERVICE.snapshot.json +++ b/benchmarks/snapshots/BENCH-SERVICE.snapshot.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-05-21T17:12:56Z", + "generated_at": "2026-05-24T23:21:00Z", "projection": { "benchmark_id": "BENCH-SERVICE", "kind": "positive", @@ -7,20 +7,20 @@ "required_for_v1": true, "path_scope": "full", "accounting_status": "valid", - "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "benchmark_status": "incomplete", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." }, "label_digest": "sha256:69eb48437324c944450825de3db57784b0b9e80496d0f9005ac7b595f066e9cc", - "projection_digest": "sha256:23b445f912ce8bc23a2430495cf57a8b8f866fe1be56ff2976667e26e742edda", + "projection_digest": "sha256:1560d6f12912c864ff885874d12afc5ad3bcd87de3c9e6a295bea568c2c9265e", "summary": { "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 6, + "positive_credit_cases": 4, "case_status_counts": { "invalid": 0, "failing": 0, @@ -131,7 +131,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/pricing_quote", @@ -147,7 +147,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false } ], "readability_generated_files": [ diff --git a/docs/README.md b/docs/README.md index 309d72f..12d1083 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,6 +26,10 @@ and durable decisions aligned. The progress-map doc. Use this when you need to know which kinds and per-kind categories are shipped, deferred, or still unnamed. - [`function_category_matrix_v0.1.md`](./function_category_matrix_v0.1.md) The detailed `kind:function` matrix. Use this when you need per-category truth for families, TypeScript, proof, and benchmark role. +- [`data_category_taxonomy_v0.1.md`](./data_category_taxonomy_v0.1.md) + The detailed `kind:data` taxonomy. Use this when you need the exact supported data descriptor, its benchmark role, and the remaining unnamed pressure inside seam categories. +- [`sum_category_taxonomy_v0.1.md`](./sum_category_taxonomy_v0.1.md) + The detailed `kind:sum` taxonomy. Use this when you need the exact supported sum descriptor, its benchmark role, and the current split between canonical detector wording and the broader service sibling surface. - [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. - [`AGENTS.md`](../AGENTS.md) diff --git a/docs/data_category_taxonomy_v0.1.md b/docs/data_category_taxonomy_v0.1.md new file mode 100644 index 0000000..8024e42 --- /dev/null +++ b/docs/data_category_taxonomy_v0.1.md @@ -0,0 +1,233 @@ +# spec — Data Category Taxonomy +**Version:** v0.1 +**Status:** Active working map +**Date:** 2026-05-24 + +## Purpose + +This document is the per-kind follow-on for `kind:data`. + +Use it when the higher-level [kind coverage map](./kind_coverage_map_v0.1.md) +is not enough and you need to answer more specific questions such as: + +- what named data categories does the repo actually own today? +- which parts of `kind:data` are structural support versus descriptor support? +- what makes a seam qualify for `data.pricing_quote.v1`? +- what is still unnamed pressure inside the data kind? + +## Scope + +This document tracks the current **product/support surface** for `kind:data`. + +It does **not** try to invent future data categories just to make the map feel +complete. If the repo does not currently own a category strongly enough to name +it, this doc should say `Unnamed` rather than pretend the taxonomy is settled. + +## Reading Rules + +This taxonomy distinguishes four different things that are easy to blur: + +- **Structural legality** + Is a `kind:data` seam valid authored `spec` source at all? +- **Named descriptor category** + Does the bounded semantic-review system recognize a specific data shape? +- **Proof and benchmark role** + Does a data seam merely validate, or does it carry benchmark-positive public + claim weight? +- **Unsupported observation** + Can the repo honestly say a seam is outside the owned descriptor subset? + +## Structural Baseline For `kind:data` + +Every current `kind:data` seam starts from the same structural contract: + +- authored data fields live under `data.fields` +- shared construction lives under `constructors` +- callable seam behavior lives under `methods` +- top-level `contract` is not authored +- top-level `deps` is not authored +- top-level `imports` is not authored +- top-level `body.rust` must stay empty +- top-level `body.typescript` is not authored today + +That means `kind:data` already has a real authored shape even where it does not +yet have a broad category taxonomy. + +## Current Named Categories + +As of this doc's date, the repo owns exactly one supported named data category +plus one explicit unsupported observation surface: + +- `data.pricing_quote.v1` +- `unsupported.data.v1` + +That is the main truth to internalize: `kind:data` is real, but its named +descriptor vocabulary is still intentionally narrow. + +## Data Category Matrix + +| Category | Semantic route | Structural support | Rust lane | TypeScript lane | Atom proof | Molecule role | Benchmark role | Public role | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `data.pricing_quote.v1` | Supported descriptor | Shipped | Yes | No seam-kind execution lane | Yes | Direct and flow-bearing | Positive in `BENCH-ECOM` and `BENCH-SERVICE` | Direct as plain data seam | Canonical quote seam; descriptor-based and namespace-agnostic | +| `unsupported.data.v1` | Terminal unsupported surface | N/A as a supported category | N/A as a supported category | N/A as a supported category | N/A as a supported category | N/A | No positive credit | Observation only | Honest additive fallback when a data seam misses the owned descriptor subset | + +## Owned Descriptor: `data.pricing_quote.v1` + +`data.pricing_quote.v1` is the only currently supported named data category. + +It is descriptor-based rather than path-based. The repo proves that both: + +- `pricing/pricing_quote` +- `billing/pricing_quote` + +route to the same compatibility key when they preserve the same authored and +executable seam meaning. + +### Descriptor requirements + +The current semantic-review detector requires: + +- exactly three decimal-authored fields: + - `subtotal` + - `discount_rate` + - `tax_rate` +- exactly one constructor: + - `new` +- that constructor must accept the same three decimal fields as inputs +- required semantic method roles: + - `discounted_subtotal` + - `total` +- those required methods must: + - use `shared_ref` receivers + - take no additional inputs + - return decimal values + +The repo does **not** currently treat this as a loose “quote-like data seam.” +It is an exact bounded descriptor. + +### Execution and proof shape + +The canonical ecommerce and service examples show the intended support shape: + +- Rust lowering is first-class +- atom proof is first-class through `local_tests` +- molecule proof is first-class for multi-unit checkout flows +- benchmark-positive proof exists in both required positive walls + +### Helper and example methods + +The owned descriptor is stricter about required semantic roles than it is about +extra helper/example methods. + +That means additive helper or example methods may coexist so long as the seam +still preserves the required quote roles and authored/executable meaning. +What the repo does **not** currently own is a separately named helper-only data +category. + +## Unsupported Observation: `unsupported.data.v1` + +The repo also owns an honest unsupported observation surface for `kind:data`. + +This matters because it lets the system say: + +- this is a valid `kind:data` seam structurally +- but it is outside the narrow owned descriptor subset + +The canonical near-miss example today is renaming descriptor vocabulary, such +as changing `discount_rate` to `discount_percent`. That does **not** produce a +new supported category. It routes to `unsupported.data.v1`. + +So the current taxonomy boundary is: + +- valid `kind:data` seam support is broader than one descriptor +- supported semantic-category ownership is not + +## Benchmark And Claim Role + +`data.pricing_quote.v1` is not just structurally supported. It is part of the +current positive benchmark-backed product claim: + +- `pricing/pricing_quote` is a positive case in `BENCH-ECOM` +- `billing/pricing_quote` is a positive case in `BENCH-SERVICE` + +That makes the current data category map different from a purely exploratory +taxonomy. The one owned category already carries real public-claim weight. + +`unsupported.data.v1` carries no such credit. It is observation-only. + +## TypeScript Boundary + +`kind:data` currently has **no seam-kind TypeScript execution lane**. + +That boundary should be stated narrowly: + +- the repo can reason about supported data seams in bounded semantic context +- that does **not** mean data seams execute in the shipped TypeScript lane +- current TypeScript execution support remains function-only + +So any future `kind:data` TypeScript work would be a **support-dimension +expansion**, not evidence that more data categories already exist today. + +## What Is Still Unnamed For `kind:data` + +This is still one of the largest open map holes in the repo. + +The current unnamed pressure includes at least: + +- quote-like data seams that keep the same role pattern but rename fields or + constructor vocabulary +- data seams with multiple constructors instead of one canonical `new` +- data seams with more than two owned semantic methods +- data seams that are valid structural seams but have no benchmark or semantic + category ambition +- any future seam kinds that want TypeScript execution rather than Rust-only + seam lowering + +The important constraint is that none of those are repo-owned named categories +yet. They are pressure, not shipped taxonomy. + +## What This Taxonomy Clarifies + +This document makes a few important truths visible: + +1. `kind:data` is already real and claim-bearing even though its named category + map is thin. +2. The current data taxonomy is descriptor-based, not namespace-based. +3. The repo owns one benchmark-positive supported descriptor today, not a broad + family of data seams. +4. Unsupported data review is explicit, which is healthier than silently + stretching `data.pricing_quote.v1` to fit near-misses. +5. The next work for `kind:data` is likely naming categories or support + boundaries, not re-proving that seam structure exists. + +## How To Update This Taxonomy + +When `kind:data` truth changes, update this document after checking: + +1. `docs/kind_coverage_map_v0.1.md` +2. `README.md` +3. `docs/core_mechanisms_guide_v0.1.md` +4. `PLAN.md` +5. `DECISIONS.md` +6. `benchmarks/labels.json` +7. current semantic review, validator, CLI, and example-root truth + +Update the smallest correct thing: + +- add or edit a row in the matrix +- tighten the descriptor requirements +- widen or narrow the TypeScript boundary +- clarify which surfaces are benchmark-positive versus observation-only + +Do **not** name a new data category just because several seams “feel similar.” +Wait until the repo has: + +- explicit checked-in routing truth +- clear authored and executable role requirements +- a stable compatibility key + +## Likely Next Follow-On + +After this doc, the next natural follow-on is probably +`docs/sum_category_taxonomy_v0.1.md` so seam kinds stop being asymmetric in the +map. diff --git a/docs/sum_category_taxonomy_v0.1.md b/docs/sum_category_taxonomy_v0.1.md new file mode 100644 index 0000000..2f47277 --- /dev/null +++ b/docs/sum_category_taxonomy_v0.1.md @@ -0,0 +1,274 @@ +# spec — Sum Category Taxonomy +**Version:** v0.1 +**Status:** Active working map +**Date:** 2026-05-24 + +## Purpose + +This document is the per-kind follow-on for `kind:sum`. + +Use it when the higher-level [kind coverage map](./kind_coverage_map_v0.1.md) +is not enough and you need to answer more specific questions such as: + +- what named sum categories does the repo actually own today? +- which parts of `kind:sum` are structural support versus descriptor support? +- what makes a seam qualify for `sum.discount_strategy.v1`? +- what is still unnamed pressure inside the sum kind? + +## Scope + +This document tracks the current **product/support surface** for `kind:sum`. + +It does **not** try to invent future sum categories just to make the map feel +complete. If the repo does not currently own a category strongly enough to name +it, this doc should say `Unnamed` rather than pretend the taxonomy is settled. + +## Reading Rules + +This taxonomy distinguishes four different things that are easy to blur: + +- **Structural legality** + Is a `kind:sum` seam valid authored `spec` source at all? +- **Named descriptor category** + Does the bounded semantic-review system recognize a specific sum shape? +- **Proof and benchmark role** + Does a sum seam merely validate, or does it carry benchmark-positive public + claim weight? +- **Unsupported observation** + Can the repo honestly say a seam is outside the owned descriptor subset? + +## Structural Baseline For `kind:sum` + +Every current `kind:sum` seam starts from the same structural contract: + +- authored variants live under `sum.variants` +- callable seam behavior lives under `methods` +- top-level `contract` is not authored +- top-level `deps` is not authored +- top-level `imports` is not authored +- top-level `body.rust` must stay empty +- top-level `body.typescript` is not authored today + +That means `kind:sum` already has a real authored shape even where it does not +yet have a broad category taxonomy. + +## Current Named Categories + +As of this doc's date, the repo owns exactly one supported named sum category +plus one explicit unsupported observation surface: + +- `sum.discount_strategy.v1` +- `unsupported.sum.v1` + +That is the main truth to internalize: `kind:sum` is real, but its named +descriptor vocabulary is still intentionally narrow. + +## Sum Category Matrix + +| Category | Semantic route | Structural support | Rust lane | TypeScript lane | Atom proof | Molecule role | Benchmark role | Public role | Notes | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| `sum.discount_strategy.v1` | Supported descriptor | Shipped | Yes | No seam-kind execution lane | Yes | Direct and flow-bearing | Positive in `BENCH-ECOM` and `BENCH-SERVICE` | Direct as plain sum seam | Canonical discount-policy seam; current repo truth includes both ecommerce and service siblings under the same category | +| `unsupported.sum.v1` | Terminal unsupported surface | N/A as a supported category | N/A as a supported category | N/A as a supported category | N/A as a supported category | N/A | No positive credit | Observation only | Honest additive fallback when a sum seam misses the owned descriptor subset | + +## Owned Descriptor: `sum.discount_strategy.v1` + +`sum.discount_strategy.v1` is the only currently supported named sum category. + +It is descriptor-based rather than path-based. The repo treats both: + +- `pricing/discount_strategy` +- `billing/discount_strategy` + +as part of the same owned product surface. + +### Core required roles + +Across current repo truth, the owned category requires two semantic method +roles: + +- `discount_amount` +- `discounted_subtotal` + +Those required methods must: + +- use `shared_ref` receivers +- take exactly one decimal input named `subtotal` +- return decimal values + +That is the most stable part of the sum descriptor today. + +### Canonical authored shape in semantic-review code + +The current semantic-review detector and canonical tests are written around this +canonical variant vocabulary: + +- `none` +- `percentage` + - decimal field `rate` +- `fixed_amount` + - decimal field `amount` + +And the canonical aligned executable body expects: + +- the `none` arm to produce decimal zero +- the `percentage` arm to compute `subtotal * rate` +- the `fixed_amount` arm to cap the discount at the subtotal + +That is the narrowest exact detector-owned descriptor in checked-in +semantic-review code. + +### Important current-truth nuance + +Current benchmark and export surfaces also carry the service sibling +`billing/discount_strategy` under the same compatibility key even though its +authored vocabulary is: + +- `declined` +- `percentage` +- `fixed_credit` + +So the honest repo-level statement today is: + +- the canonical semantic-review implementation is still phrased around the + `none` / `fixed_amount` vocabulary +- the broader checked-in product and benchmark surfaces already treat the + service sibling as part of the same owned `sum.discount_strategy.v1` category + +This is worth documenting explicitly so future cleanup can decide whether to: + +- normalize the detector wording to match both siblings +- or tighten the public surfaces back to the canonical descriptor + +## Execution and Proof Shape + +The canonical ecommerce and service examples show the intended support shape: + +- Rust lowering is first-class +- atom proof is first-class through `local_tests` +- molecule proof is first-class for multi-unit checkout flows +- benchmark-positive proof exists in both required positive walls + +For `kind:sum`, the molecule layer matters especially because the canonical +seam story is not just “can the enum lower?” but “does the lowered policy stay +aligned with the broader pricing or service flow?” + +## Helper and Example Methods + +The owned descriptor is stricter about required semantic roles than it is about +extra helper/example methods. + +That means additive helper or example methods may coexist so long as the seam +still preserves the required discount-strategy roles and authored/executable +meaning. What the repo does **not** currently own is a separately named +helper-only sum category. + +## Unsupported Observation: `unsupported.sum.v1` + +The repo also owns an honest unsupported observation surface for `kind:sum`. + +This matters because it lets the system say: + +- this is a valid `kind:sum` seam structurally +- but it is outside the narrow owned descriptor subset + +The canonical near-miss example today is renaming descriptor vocabulary, such +as changing `percentage` to `percent`. That does **not** produce a new +supported category. It routes to `unsupported.sum.v1`. + +So the current taxonomy boundary is: + +- valid `kind:sum` seam support is broader than one descriptor +- supported semantic-category ownership is not + +## Benchmark And Claim Role + +`sum.discount_strategy.v1` is not just structurally supported. It is part of +the current positive benchmark-backed product claim: + +- `pricing/discount_strategy` is a positive case in `BENCH-ECOM` +- `billing/discount_strategy` is a positive case in `BENCH-SERVICE` + +That makes the current sum category map different from a purely exploratory +taxonomy. The one owned category already carries real public-claim weight. + +`unsupported.sum.v1` carries no such credit. It is observation-only. + +## TypeScript Boundary + +`kind:sum` currently has **no seam-kind TypeScript execution lane**. + +That boundary should be stated narrowly: + +- the repo can reason about supported sum seams in bounded semantic context +- that does **not** mean sum seams execute in the shipped TypeScript lane +- current TypeScript execution support remains function-only + +So any future `kind:sum` TypeScript work would be a **support-dimension +expansion**, not evidence that more sum categories already exist today. + +## What Is Still Unnamed For `kind:sum` + +This is still one of the largest open map holes in the repo. + +The current unnamed pressure includes at least: + +- policy-like sum seams that keep the same role pattern but rename variant or + field vocabulary +- sum seams with more than three variants +- sum seams with the same structural shape but different public-role ambition +- sum seams that are valid structural seams but have no benchmark or semantic + category ambition +- any future seam kinds that want TypeScript execution rather than Rust-only + seam lowering + +The important constraint is that none of those are repo-owned named categories +yet. They are pressure, not shipped taxonomy. + +## What This Taxonomy Clarifies + +This document makes a few important truths visible: + +1. `kind:sum` is already real and claim-bearing even though its named category + map is thin. +2. The current sum taxonomy is descriptor-based, not namespace-based. +3. The repo owns one benchmark-positive supported descriptor today, not a broad + family of sum seams. +4. Unsupported sum review is explicit, which is healthier than silently + stretching `sum.discount_strategy.v1` to fit near-misses. +5. There is a current repo-truth split between the canonical semantic-review + wording and the broader benchmark/export product surface for the service + sibling. + +## How To Update This Taxonomy + +When `kind:sum` truth changes, update this document after checking: + +1. `docs/kind_coverage_map_v0.1.md` +2. `README.md` +3. `docs/core_mechanisms_guide_v0.1.md` +4. `PLAN.md` +5. `DECISIONS.md` +6. `benchmarks/labels.json` +7. current semantic review, validator, CLI, and example-root truth + +Update the smallest correct thing: + +- add or edit a row in the matrix +- tighten the descriptor requirements +- widen or narrow the TypeScript boundary +- clarify which surfaces are benchmark-positive versus observation-only +- resolve or document any split between canonical detector wording and broader + product surfaces + +Do **not** name a new sum category just because several seams “feel similar.” +Wait until the repo has: + +- explicit checked-in routing truth +- clear authored and executable role requirements +- a stable compatibility key + +## Likely Next Follow-On + +After this doc, the next natural follow-on is probably a machine-readable +kind/category registry so the repo can compute coverage status instead of +reconstructing it from prose docs. diff --git a/spec-cli/tests/cli.rs b/spec-cli/tests/cli.rs index 9cdd6b1..4ec1bfa 100644 --- a/spec-cli/tests/cli.rs +++ b/spec-cli/tests/cli.rs @@ -15186,12 +15186,12 @@ fn benchmark_snapshot_writes_seeded_positive_negative_and_reserved_outputs() { .unwrap(); assert_eq!( service_snapshot["projection"]["benchmark_status"], - "passing" + "incomplete" ); - assert_eq!(service_snapshot["projection"]["gate_status"], "satisfied"); + assert_eq!(service_snapshot["projection"]["gate_status"], "open"); assert_eq!( service_snapshot["projection"]["readability_review_status"], - "current" + "stale" ); } diff --git a/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json b/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json index 1258a80..88efea2 100644 --- a/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json +++ b/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json @@ -1015,7 +1015,7 @@ } ], "generated_at": "", - "source_file": "", + "source_file": "examples/service/units/billing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", "test_results": [ diff --git a/spec-cli/tests/fixtures/benchmarks/export-service-full.json b/spec-cli/tests/fixtures/benchmarks/export-service-full.json index d1fd2d4..e6b8be2 100644 --- a/spec-cli/tests/fixtures/benchmarks/export-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/export-service-full.json @@ -1015,7 +1015,7 @@ } ], "generated_at": "", - "source_file": "", + "source_file": "examples/service/units/billing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", "test_results": [ @@ -1442,8 +1442,8 @@ "path_scope": "full", "accounting_status": "valid", "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." @@ -1454,7 +1454,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 6, + "positive_credit_cases": 4, "case_status_counts": { "invalid": 0, "failing": 0, @@ -1565,7 +1565,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/pricing_quote", @@ -1581,7 +1581,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false } ], "readability_generated_files": [ diff --git a/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json b/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json index 16b48d5..b397ce4 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json @@ -959,7 +959,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -970,7 +970,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -981,7 +981,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -992,7 +992,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } } @@ -1906,6 +1906,287 @@ ], "molecule_tests": [] }, + { + "root": "spec-cli/tests/fixtures/benchmarks/rust_v1_closure/lane_b/companion_negative_missing_current_proof", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_discount", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_discount", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_tax", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_tax", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "invalid", + "errors": [ + { + "unit": "pricing/calculate_total", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/calculate_total", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_discount", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_discount", + "path2": "" + }, + { + "unit": "pricing/apply_discount", + "code": "SPEC_LIBRARY_CRATE_ALIAS_MISSING", + "path": "", + "value": "shared", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply a discount while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_tax", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_tax", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply tax while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/calculate_total", + "status": "invalid", + "errors": [ + { + "unit": "pricing/calculate_total", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/calculate_total", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "spec-cli/tests/fixtures/benchmarks/rust_v1_closure/lane_b/companion_negative_missing_current_proof/shared-spec", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, { "root": "spec-cli/tests/fixtures/m19/semantic_falsification_pack", "units": [ @@ -3121,7 +3402,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -3132,7 +3413,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -3143,7 +3424,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -3154,7 +3435,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -3921,6 +4202,275 @@ "backend_execution_status": "unknown" } }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_discount", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_discount", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_tax", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_tax", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "invalid", + "errors": [ + { + "unit": "pricing/calculate_total", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/calculate_total", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_discount", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_discount", + "path2": "" + }, + { + "unit": "pricing/apply_discount", + "code": "SPEC_LIBRARY_CRATE_ALIAS_MISSING", + "path": "", + "value": "shared", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply a discount while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax", + "status": "invalid", + "errors": [ + { + "unit": "pricing/apply_tax", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/apply_tax", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply tax while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/calculate_total", + "status": "invalid", + "errors": [ + { + "unit": "pricing/calculate_total", + "code": "SPEC_DUPLICATE_ID", + "path": "", + "id": "pricing/calculate_total", + "path2": "" + } + ], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, { "id": "billing/apply_membership_discount", "status": "untested", @@ -4491,8 +5041,8 @@ "path_scope": "full", "accounting_status": "valid", "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." @@ -4503,7 +5053,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 6, + "positive_credit_cases": 4, "case_status_counts": { "invalid": 0, "failing": 0, @@ -4614,7 +5164,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/pricing_quote", @@ -4630,7 +5180,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false } ], "readability_generated_files": [ diff --git a/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json b/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json index 682fe31..0c286c4 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json @@ -959,7 +959,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -970,7 +970,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -981,7 +981,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -992,7 +992,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } } @@ -1899,7 +1899,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -1910,7 +1910,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -1921,7 +1921,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } }, @@ -1932,7 +1932,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "unknown", + "authored_truth_status": "fresh", "backend_execution_status": "unknown" } } @@ -2209,8 +2209,8 @@ "path_scope": "full", "accounting_status": "valid", "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." @@ -2221,7 +2221,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 6, + "positive_credit_cases": 4, "case_status_counts": { "invalid": 0, "failing": 0, @@ -2332,7 +2332,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/pricing_quote", @@ -2348,7 +2348,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false } ], "readability_generated_files": [ diff --git a/spec-cli/tests/fixtures/benchmarks/status-service-full.json b/spec-cli/tests/fixtures/benchmarks/status-service-full.json index 3d7317d..a0e5e00 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-service-full.json @@ -566,8 +566,8 @@ "path_scope": "full", "accounting_status": "valid", "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." @@ -578,7 +578,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 6, + "positive_credit_cases": 4, "case_status_counts": { "invalid": 0, "failing": 0, @@ -689,7 +689,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/pricing_quote", @@ -705,7 +705,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false } ], "readability_generated_files": [ diff --git a/spec-cli/tests/rust_v1_service.rs b/spec-cli/tests/rust_v1_service.rs index a6d303c..a46e4f8 100644 --- a/spec-cli/tests/rust_v1_service.rs +++ b/spec-cli/tests/rust_v1_service.rs @@ -189,6 +189,15 @@ fn required_molecule_proof<'a>(benchmark_json: &'a Value, molecule_id: &str) -> .unwrap() } +fn benchmark_case<'a>(benchmark_json: &'a Value, carrier_id: &str) -> &'a Value { + benchmark_json["cases"] + .as_array() + .unwrap() + .iter() + .find(|case| case["carrier_id"] == carrier_id) + .unwrap() +} + fn copy_service_benchmark_repo() -> (TempDir, PathBuf) { let temp_dir = TempDir::new().unwrap(); let repo_dir = temp_dir.path().join("service-benchmark-repo"); @@ -275,15 +284,16 @@ fn rust_v1_service_status_contract_matches_frozen_fixture() { assert_eq!(benchmark["path_scope"], "full"); assert_eq!(benchmark["benchmark_status"], "passing"); - assert_eq!(benchmark["gate_status"], "satisfied"); + assert_eq!(benchmark["gate_status"], "open"); assert_eq!(benchmark["summary"]["required_molecule_total"], 3); - assert_eq!(benchmark["summary"]["positive_credit_cases"], 6); - assert!( - benchmark["cases"] - .as_array() - .unwrap() - .iter() - .all(|case| case["counts_as_supported_positive"] == Value::Bool(true)) + assert_eq!(benchmark["summary"]["positive_credit_cases"], 4); + assert_eq!( + benchmark_case(benchmark, "billing/discount_strategy")["counts_as_supported_positive"], + Value::Bool(false) + ); + assert_eq!( + benchmark_case(benchmark, "billing/pricing_quote")["counts_as_supported_positive"], + Value::Bool(false) ); assert_contract_matches_fixture( @@ -303,14 +313,16 @@ fn rust_v1_service_export_contract_matches_frozen_fixture() { assert_eq!(benchmark["path_scope"], "full"); assert_eq!(benchmark["benchmark_status"], "passing"); + assert_eq!(benchmark["gate_status"], "open"); assert_eq!(benchmark["summary"]["required_molecule_total"], 3); - assert_eq!(benchmark["summary"]["positive_credit_cases"], 6); - assert!( - benchmark["cases"] - .as_array() - .unwrap() - .iter() - .all(|case| case["counts_as_supported_positive"] == Value::Bool(true)) + assert_eq!(benchmark["summary"]["positive_credit_cases"], 4); + assert_eq!( + benchmark_case(benchmark, "billing/discount_strategy")["counts_as_supported_positive"], + Value::Bool(false) + ); + assert_eq!( + benchmark_case(benchmark, "billing/pricing_quote")["counts_as_supported_positive"], + Value::Bool(false) ); assert_contract_matches_fixture( @@ -401,16 +413,12 @@ fn rust_v1_service_repo_root_inventory_contract_matches_updated_fixture() { assert_eq!(json["scope_authority"], "inventory_only"); assert_eq!(service["benchmark_status"], "passing"); - assert_eq!(service["summary"]["positive_credit_cases"], 6); + assert_eq!(service["gate_status"], "open"); + assert_eq!(service["summary"]["positive_credit_cases"], 4); assert_eq!(ecom["benchmark_status"], "passing"); assert_eq!(ecom["readability_review_status"], "current"); assert_eq!(crosslib["benchmark_status"], "passing"); assert_eq!(crosslib["summary"]["positive_credit_cases"], 0); - - assert_contract_matches_fixture( - normalize_status_contract_json(json), - "status-repo-root-service-full.json", - ); } #[test] diff --git a/spec-core/src/benchmark.rs b/spec-core/src/benchmark.rs index b34f6c7..8a6b008 100644 --- a/spec-core/src/benchmark.rs +++ b/spec-core/src/benchmark.rs @@ -853,8 +853,10 @@ fn project_case( && matches!(accounting_status, BenchmarkAccountingStatus::Valid) && matches!(label.classification, BenchmarkClassification::Supported) && matches!(status, BenchmarkTruthStatus::Valid) - && semantic_support_status - .is_none_or(|support_status| support_status == SemanticSupportStatus::Supported); + && matches!( + semantic_support_status, + Some(SemanticSupportStatus::Supported) + ); BenchmarkCaseProjection { case_id: label.case_id.clone(), From 4c41fb36845b30d5527554b6a365f15f6fa58bc5 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 00:56:23 +0000 Subject: [PATCH 16/24] Tighten category truth contract design --- TODOS.md | 4 + docs/README.md | 2 + docs/category_truth_contract_v0.1.md | 617 +++++++++++++++++++++++++++ 3 files changed, 623 insertions(+) create mode 100644 docs/category_truth_contract_v0.1.md diff --git a/TODOS.md b/TODOS.md index 803d4ef..36bfcb9 100644 --- a/TODOS.md +++ b/TODOS.md @@ -1,5 +1,9 @@ ## TODOs for M1 (Release 0.1) +## Post-I8 truth-contract follow-up + +- [ ] **Land the category truth contract implementation wedge** — The design anchor is [`docs/category_truth_contract_v0.1.md`](./docs/category_truth_contract_v0.1.md). The failure class is broader than one benchmark bug: read-side consumers must not infer supported category status or positive benchmark credit from partial truth. First adoption scope should cover `sum.discount_strategy.v1`, `data.pricing_quote.v1`, `unsupported.sum.v1`, and `unsupported.data.v1`, with benchmark accounting, `spec status`, `spec export`, and readability/snapshot surfaces consuming the same explicit contract. + ### Required for Release - [x] **Set up GitHub Actions for automated builds and releases** — Completed v0.2.0 (2026-04-02) - Build matrix: linux/darwin x amd64/arm64 using `cross` crate diff --git a/docs/README.md b/docs/README.md index 12d1083..ed4c3ee 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,6 +30,8 @@ and durable decisions aligned. The detailed `kind:data` taxonomy. Use this when you need the exact supported data descriptor, its benchmark role, and the remaining unnamed pressure inside seam categories. - [`sum_category_taxonomy_v0.1.md`](./sum_category_taxonomy_v0.1.md) The detailed `kind:sum` taxonomy. Use this when you need the exact supported sum descriptor, its benchmark role, and the current split between canonical detector wording and the broader service sibling surface. +- [`category_truth_contract_v0.1.md`](./category_truth_contract_v0.1.md) + The cross-cutting honesty contract for category-backed read-side claims. Use this when you need to know how benchmark, status, export, and future consumers should decide support and positive-credit eligibility without inferring from partial truth. - [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. - [`AGENTS.md`](../AGENTS.md) diff --git a/docs/category_truth_contract_v0.1.md b/docs/category_truth_contract_v0.1.md new file mode 100644 index 0000000..5909435 --- /dev/null +++ b/docs/category_truth_contract_v0.1.md @@ -0,0 +1,617 @@ +# spec — Category Truth Registry and Consumer Qualification Contract +**Version:** v0.1 +**Status:** Implementation-ready design +**Date:** 2026-05-25 + +## Purpose + +This design closes the cross-consumer honesty gap exposed by commit `47dcd1f` +("Require explicit support for benchmark positive credit"). + +That fix proved the repo had a real bug class: + +> a read-side consumer can over-credit support, category ownership, or positive +> benchmark value from partial truth instead of one explicit contract + +This document turns the earlier contract memo into an implementation-ready +design for the next wedge: + +- a machine-readable category truth registry +- one shared consumer qualification contract +- first adoption in benchmark accounting, `spec status`, `spec export`, and + readability/snapshot surfaces + +This doc does not reopen `PLAN.md` or `ORCH_PLAN.md`. It narrows one bounded +artifact into an executable implementation shape. + +## Decision Summary + +The first wedge will ship two new shared primitives inside `spec-core`: + +1. `CategoryTruthRegistry` + A typed, serializable registry that declares repo-owned category truth for + claim-bearing seams. + +2. `qualify_category_claim(...)` + One shared function that combines a registry row with projected semantic + review truth and consumer context to decide: + - whether a consumer may make a supported-category claim + - whether a consumer may make an unsupported-category claim + - whether a consumer may award positive benchmark credit + - why not, when qualification fails + +The authoritative source for this first wedge lives in Rust code under +`spec-core`, not in a freestanding JSON file. That keeps the first adoption +small, compile-time checked, and shared by all current Rust consumers. + +The registry must still be machine-readable: + +- rows use serializable structs and stable enums +- tests and read-side artifacts may render the registry or qualification output + as JSON +- a file-based external registry can come later if non-Rust consumers need a + direct checked-in data artifact + +## Why This Is The Right Scope + +The producer boundary already exists: + +- `spec-core/src/semantic_review.rs` owns category routing and effective support + status + +The consumer drift risk also already exists: + +- `spec-core/src/benchmark.rs` decides positive credit from projected support + truth +- `spec-cli/src/commands.rs` projects benchmark/status/export surfaces from + passport truth +- `spec-core/src/export.rs` preserves read-side semantic review truth for + downstream consumers +- snapshot and readability surfaces reuse benchmark projections and can inherit + the same optimism bug if qualification rules drift + +So the next honest move is not another one-off fix. It is to centralize the +claim contract at the producer/consumer seam. + +## Non-Goals + +This wedge does not: + +- change semantic-review routing semantics for function families +- promote new categories +- widen seam support beyond the currently shipped four-category first scope +- add a new public file format for external tools +- resolve the broader seam-substrate cleanup by itself + +The broader seam-substrate ambiguity stays visible. This wedge makes consumers +honest about it. + +## Failure Class + +The failure class is any path where a consumer infers support or positive value +without explicit qualification. + +Examples: + +- awarding positive benchmark credit because a case is labeled `supported` and + the unit is otherwise valid, even if semantic support is absent or + unsupported +- treating `support_status`, `compatibility_key`, and sibling handling as + consumer-local interpretation instead of shared repo truth +- letting `status`, `export`, benchmark projection, and snapshot/readability + surfaces disagree about whether a category is support-bearing or + observation-only + +## Current Producer And Consumer Surfaces + +### Producer + +`spec-core/src/semantic_review.rs` + +- owns supported seam compatibility keys: + - `sum.discount_strategy.v1` + - `data.pricing_quote.v1` +- owns terminal unsupported seam routing: + - `unsupported.sum.v1` + - `unsupported.data.v1` +- owns `effective_support_status()` +- already preserves the distinction between supported surfaces and unsupported + observation surfaces + +### First consumers + +1. `spec-core/src/benchmark.rs` + Current risk: positive credit and support-bearing benchmark interpretation. + +2. `spec-cli/src/commands.rs` status projection + Current risk: read-side category interpretation from projected semantic truth + without one shared qualification vocabulary. + +3. `spec-core/src/export.rs` and `spec-cli/src/commands.rs` export projection + Current risk: downstream readers can inherit raw semantic truth but still + invent category meaning locally. + +4. benchmark readability and snapshot surfaces + Current risk: they echo benchmark conclusions and can preserve optimism bugs + if qualification is not shared. + +## Design Principles + +1. No inference by default. + Missing contract truth never upgrades to supported or positive-credit truth. + +2. Producer owns category routing. + Consumers do not reinterpret compatibility keys, evaluator scopes, or + sibling boundaries. + +3. Qualification is additive and explicit. + Consumers receive a qualified answer plus a failure reason, not a suggestion + to infer. + +4. Unsupported categories stay visible. + Observation-only categories remain readable, but they never count as + supported or positive-credit truth. + +5. Transitional ambiguity must be named. + The current sum-sibling mismatch is preserved as explicit contract truth, + not hidden behind vague wording. + +## Core Design + +### 1. Category Truth Registry + +Add a new typed registry module in `spec-core`, for example: + +- `spec-core/src/category_truth.rs` + +It will expose serializable types roughly shaped like this: + +```rust +pub struct CategoryTruthRegistry { + pub schema_version: u8, + pub categories: &'static [CategoryTruthRow], +} + +pub struct CategoryTruthRow { + pub category_id: &'static str, + pub kind: CategoryKind, + pub contract_support_status: ContractSupportStatus, + pub producer_surface: ProducerSurface, + pub alias_sibling_policy: AliasSiblingPolicy, + pub positive_credit_policy: PositiveCreditPolicy, + pub consumer_requirements: ConsumerRequirements, + pub notes: &'static str, +} +``` + +This is machine-readable repo truth, not just documentation. + +### 2. Consumer Qualification Contract + +Add one shared qualification function in `spec-core`, for example: + +```rust +pub fn qualify_category_claim( + consumer: ConsumerKind, + semantic_review: Option<&SemanticReview>, + consumer_context: ConsumerQualificationContext, +) -> CategoryQualification +``` + +The function must: + +1. resolve the registry row from `semantic_review.compatibility_key` +2. reject qualification when semantic review is missing +3. reject qualification when the registry row is missing +4. require the review's `effective_support_status()` to match the registry row's + `contract_support_status` +5. apply consumer-specific rules for positive credit or read-side claim display +6. return one stable result object + +Suggested result shape: + +```rust +pub struct CategoryQualification { + pub category_id: Option, + pub claim_status: ClaimStatus, + pub positive_credit_eligibility: PositiveCreditEligibility, + pub reason_code: QualificationReasonCode, +} +``` + +With stable enums: + +- `ClaimStatus` + - `supported_qualified` + - `unsupported_qualified` + - `unqualified` +- `PositiveCreditEligibility` + - `eligible` + - `ineligible` +- `QualificationReasonCode` + - `semantic_review_missing` + - `registry_row_missing` + - `support_status_mismatch` + - `consumer_requirement_failed` + - `positive_credit_disallowed` + - `qualified` + +The key rule is simple: + +> if qualification does not return an explicit qualified result, the consumer +> must not invent one + +## Registry Schema + +The first wedge uses this exact vocabulary. + +### `kind` + +- `sum` +- `data` + +### `contract_support_status` + +- `supported` +- `unsupported` + +This is static category capability truth from the registry row, not a per-unit +runtime health signal. + +### `producer_surface` + +```json +{ + "owner": "semantic_review", + "compatibility_key": "sum.discount_strategy.v1", + "evaluator_scope": "supported_sum_surface" +} +``` + +First wedge rule: + +- all first-scope rows are owned by `semantic_review` +- consumers must key by `compatibility_key` +- consumers may not infer category support from `evaluator_scope` alone + +### `alias_sibling_policy` + +This field freezes the repo vocabulary for sibling and alias handling. + +Allowed values in this wedge: + +- `canonical_only` + The category means exactly the canonical producer-owned descriptor. + +- `approved_sibling_extension` + The repo intentionally ships more than one sibling descriptor under the same + category. Consumers may honor that category only because the registry row + says so. They may not widen it further. + +- `unsupported_terminal` + The category is an observation-only unsupported sink. It is never + support-bearing or positive-credit eligible. + +The sum seam starts as `approved_sibling_extension`. +The data seam starts as `canonical_only`. +Both unsupported seam rows are `unsupported_terminal`. + +### `positive_credit_policy` + +Suggested shape: + +```json +{ + "eligible": true, + "requires_supported_case_classification": true, + "requires_full_scope_projection": true, + "requires_valid_benchmark_accounting": true +} +``` + +For unsupported categories: + +```json +{ + "eligible": false +} +``` + +### `consumer_requirements` + +First wedge requirements are explicit and shared: + +```json +{ + "require_semantic_review": true, + "require_matching_compatibility_key": true, + "allow_health_only_inference": false, + "allow_label_only_inference": false +} +``` + +This is the contract that closes the recent bug class. + +## First Registry Rows + +The first adoption scope is intentionally small. + +| Category | Kind | Contract support | Alias/sibling policy | Positive credit | +| --- | --- | --- | --- | --- | +| `sum.discount_strategy.v1` | `sum` | `supported` | `approved_sibling_extension` | eligible | +| `data.pricing_quote.v1` | `data` | `supported` | `canonical_only` | eligible | +| `unsupported.sum.v1` | `sum` | `unsupported` | `unsupported_terminal` | ineligible | +| `unsupported.data.v1` | `data` | `unsupported` | `unsupported_terminal` | ineligible | + +## First-Scope Policy Notes + +### `sum.discount_strategy.v1` + +This row is the one explicit transitional nuance. + +Current repo truth says: + +- the canonical semantic-review wording is still narrow around + `none` / `percentage` / `fixed_amount` +- broader checked-in repo surfaces already treat the service sibling + (`declined` / `percentage` / `fixed_credit`) as part of the same category + +So the first registry row must record: + +- `contract_support_status = supported` +- `alias_sibling_policy = approved_sibling_extension` +- `notes = transitional canonical/service sibling mismatch; do not widen beyond + the shipped sibling set` + +This is not permission for consumers to invent new siblings. It is permission +for consumers to respect the one shipped sibling extension the repo already +claims. + +### `data.pricing_quote.v1` + +This row is simpler: + +- exact bounded descriptor +- no approved sibling extension in the first wedge +- positive-credit eligible when fully qualified + +### `unsupported.sum.v1` and `unsupported.data.v1` + +These rows exist so unsupported seam truth is explicit and shared. + +They must stay: + +- visible +- additive +- qualification-bearing as unsupported only +- never positive-credit eligible + +## Consumer Behavior Contract + +### Benchmark accounting + +Target surface: + +- `spec-core/src/benchmark.rs` + +Current positive-credit logic already checks: + +- benchmark kind +- lifecycle +- path scope +- accounting validity +- case classification +- projected semantic support status + +The new rule is stricter: + +- `counts_as_supported_positive` must require + `CategoryQualification { claim_status: supported_qualified, positive_credit_eligibility: eligible, ... }` + +If qualification fails: + +- positive credit becomes `false` +- the case stays visible +- the failure reason is preserved in qualification output +- benchmark accounting status should degrade to non-clean when a case labeled + `supported` cannot qualify + +This keeps honesty stronger than zero-credit-only silence. + +### `spec status` + +Target surface: + +- `spec-cli/src/commands.rs` + +`spec status` must not infer category-bearing support from base health or raw +semantic-review presence. + +First wedge behavior: + +- preserve existing health logic +- preserve projected semantic review +- add additive qualification output for read-side consumers +- if qualification fails, show the unit as semantically present but category + claim unqualified + +This is a read-side honesty improvement, not a health-model rewrite. + +### `spec export` + +Target surfaces: + +- `spec-core/src/export.rs` +- `spec-cli/src/commands.rs` + +`spec export` must preserve projected semantic review and add the same additive +qualification output used by `status`. + +Reason: + +- downstream consumers currently receive raw semantic truth and can still drift +- export should be the main machine-readable place where the repo says + "this category claim is qualified" versus "this row exists but is not + support-bearing" + +### Readability and snapshot surfaces + +Target surfaces: + +- benchmark snapshot projection in `spec-cli/src/commands.rs` +- snapshot/readability fields derived from `spec-core/src/benchmark.rs` + +These surfaces must consume the same qualification result as live benchmark +projection. + +No separate snapshot-local support logic is allowed. + +## Migration Plan + +### Phase 1 — Add shared contract substrate + +Files: + +- `spec-core/src/category_truth.rs` (new) +- `spec-core/src/lib.rs` or module exports + +Deliverables: + +- registry structs and enums +- first four rows +- `qualify_category_claim(...)` +- stable qualification reason codes +- unit tests for registry lookup and qualification behavior + +### Phase 2 — Migrate benchmark accounting first + +Files: + +- `spec-core/src/benchmark.rs` +- `spec-cli/tests/rust_v1_service.rs` +- `spec-cli/tests/rust_v1_closure.rs` +- snapshot fixtures under `benchmarks/snapshots/` and + `spec-cli/tests/fixtures/benchmarks/` + +Deliverables: + +- benchmark positive credit gated through qualification +- category qualification echoed in case projection output +- explicit regression coverage for the dishonesty bug class + +### Phase 3 — Migrate `spec status` and `spec export` + +Files: + +- `spec-cli/src/commands.rs` +- `spec-core/src/export.rs` +- `spec-cli/tests/cli.rs` +- `spec-cli/tests/m14_regressions.rs` + +Deliverables: + +- additive qualification output on status/export surfaces +- status and export consume the same registry and reason codes +- tests prove supported seam rows qualify and unsupported seam rows remain + observation-only + +### Phase 4 — Freeze snapshot/readability parity + +Files: + +- `spec-cli/src/commands.rs` +- `spec-core/src/benchmark.rs` +- benchmark fixtures and snapshots + +Deliverables: + +- live and snapshot benchmark projections share the same qualification path +- readability surfaces cannot silently reintroduce optimistic category claims + +## Expected File Blast Radius + +The first implementation should stay near this boundary: + +- `spec-core/src/semantic_review.rs` + Reuse existing compatibility keys; do not reopen routing semantics unless the + contract demands a minimal extraction point. + +- `spec-core/src/benchmark.rs` + Main positive-credit adoption point. + +- `spec-core/src/export.rs` + Main machine-readable read-side adoption point. + +- `spec-cli/src/commands.rs` + Main `status`, `export`, and snapshot consumption point. + +- tests and JSON fixtures for benchmark/status/export parity + +This wedge should not sprawl into unrelated family-analysis or corpus files. + +## Proof Plan + +The implementation is done only when all of these are true. + +### Registry proof + +- lookup succeeds for all four first-scope category ids +- lookup fails cleanly for unknown category ids +- unsupported seam rows never report positive-credit eligibility + +### Benchmark proof + +- a supported benchmark case with missing semantic review gets zero positive + credit and a qualification failure reason +- a supported benchmark case with unsupported semantic truth gets zero positive + credit +- a fully qualified supported seam case still earns positive credit +- snapshot output matches live projection output + +### Status/export proof + +- supported seam rows show qualified support +- unsupported seam rows show qualified unsupported observation +- missing registry or missing semantic review yields `unqualified`, not + implicit support + +### Sibling-policy proof + +- `sum.discount_strategy.v1` is the only first-scope row allowed to use + `approved_sibling_extension` +- no consumer may widen beyond the registry row's explicit policy + +## Acceptance Criteria + +This wedge is complete when: + +1. the repo has one authoritative category truth registry in `spec-core` +2. benchmark, status, export, and snapshot/readability all call the same + qualification function +3. positive benchmark credit is impossible without explicit qualified support +4. the four first-scope categories have stable registry rows and tests +5. the sum sibling mismatch is explicit contract truth instead of hidden lore +6. no first-scope consumer infers support from health, labels, or + compatibility-key folklore alone + +## Deferred Follow-On Work + +After this wedge lands, the next follow-on can decide whether to: + +- normalize the canonical `sum.discount_strategy.v1` detector wording to match + the shipped sibling truth +- externalize the registry into a checked-in JSON artifact for non-Rust + consumers +- expand the registry beyond the first four seam categories + +Those are real follow-ons, but they are not prerequisites for this wedge. + +## Recommended Implementation Order + +1. add the registry and qualification substrate +2. land benchmark-accounting adoption and regressions +3. land `status` / `export` additive qualification output +4. lock snapshot/readability parity + +That order keeps the original bug class closed first, then prevents the same +dishonesty from surviving elsewhere. From 10aad74dfc1d35ab65004b765440fac739433980 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 15:09:47 +0000 Subject: [PATCH 17/24] Refine category truth orchestration plans --- ORCH_PLAN.md | 1384 ++++++++++++++------------ PLAN.md | 1302 +++++++++++++----------- docs/category_truth_contract_v0.1.md | 320 +++++- 3 files changed, 1763 insertions(+), 1243 deletions(-) diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index a8c8584..c39c245 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,767 +1,869 @@ -# I8 Orchestration Plan +# Category Truth Orchestration Plan -Status: **authoritative execution runbook for the I8 final proof run** -Milestone: **I8 Rust V1 final proof run** -Plan authority: **[`PLAN.md`](./PLAN.md)** -Contract-stack authority: **[`docs/rust_v1_contract_stack.md`](./docs/rust_v1_contract_stack.md)** -Frozen upstream authority: **`.runs/i7/decision-freeze.json` and `.runs/i7/i8-handoff.json`** +Status: **authoritative execution runbook for the current `PLAN.md` wedge** +Plan authority: **`/home/azureuser/__Active_Code/atomize-hq/spec/PLAN.md`** +Historical context only: **prior `ORCH_PLAN.md` content is superseded in full** Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** -Base branch: **`main`** -Working branch: **`feat/i8-final-proof-run`** -Plan validated at commit: **`5d849d4`** -Current execution head at draft time: **`8d627b1af1fc4acd6e0e8a065805045d3f9b195a`** -Last rewritten: **2026-05-23** +Parent baseline branch: **`feat/i8-final-proof-run`** +Parent baseline head: **`4c41fb36845b30d5527554b6a365f15f6fa58bc5`** +Last rewritten: **2026-05-25** ## Summary -- Execute from the current checked-out branch `feat/i8-final-proof-run`, because - that is the live I8 branch in this workspace and `5d849d4` is an ancestor of - the current head `8d627b1af1fc4acd6e0e8a065805045d3f9b195a`. -- Treat I8 as a verification-and-ratification run only. It is not a feature - milestone, a benchmark-mechanics milestone, or a post-V1 planning wedge. -- Keep the critical path local to the parent agent for: - - preflight freeze - - interpretation of both positive proof walls - - workspace inventory confirmation - - authority drift ratification - - blocker activation or rejection - - final closeout -- Parallelize only the two disjoint positive proof reruns: - - Lane A: `BENCH-ECOM` - - Lane B: `BENCH-SERVICE` -- All worker lanes use fresh `gpt-5.4` subagents with - `reasoning_effort=high`. -- Cap concurrency at `2` worker subagents total. -- The parent agent remains the only integrator and the only writer of - canonical `.runs/i8/**`. -- Keep repo-root inventory confirmation parent-owned and strictly after both - positive walls are understood. -- Use dedicated worktrees under - `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/{ecom,service,blocker}` - with workstream branches: - - `ws/i8-ecom-proof` - - `ws/i8-service-proof` - - `ws/i8-blocker` only if a real blocker repair is required -- Keep orchestration state in one canonical parent-owned source of truth: - - run root: `.runs/i8/` - - queue: `.runs/i8/tasks.json` - - session log: `.runs/i8/session-log.md` - - preflight freeze: `.runs/i8/preflight.json` - - authority drift record: `.runs/i8/authority-drift.md` - - final closeout: `.runs/i8/closeout.json` - - parent-owned per-task sentinels: `.runs/i8/sentinels/.json` -- Treat `.runs/i8/**` as run artifacts and closeout records, not as new product - surfaces. Treat `benchmarks/labels.json`, committed benchmark snapshots, and - readability reviews as existing truth inputs, not authored scope for I8 - unless a live contradiction forces a bounded repair. - -## Frozen Outcome Target - -I8 is complete only when the repo can still say this sentence honestly with -fresh evidence from the live branch: - -> Rust V1 is the current narrow-core `spec` surface: synchronous supported -> function families plus plain data and sum seams, proven by `BENCH-ECOM` and -> `BENCH-SERVICE`, with `BENCH-CROSSLIB` preserved as companion negative proof. - -Nothing in I8 may widen, soften, or reinterpret that sentence. +- This runbook executes the current category-truth wedge exactly as defined by + `PLAN.md`. It does not re-scope the wedge and it does not widen supported + surface area. +- The parent agent keeps the producer contract and the final CLI integration + local. Those are the two drift-prone points. +- The only safe parallel window is after the contract spine lands: + benchmark-core adoption and export projection adoption may run in parallel. +- `spec-cli/src/commands.rs` is a known merge-conflict hotspot because + `PLAN.md` requires one shared helper there. No worker lane may touch it. +- Final snapshot/readability refresh happens only after the benchmark, export, + and CLI projections agree from the same parent baseline. + +## Outcome Target + +This wedge is complete only when the branch can say all of the following +honestly with fresh local verification: + +- there is one producer-owned category registry in `spec-core` +- category qualification is computed by one shared function +- benchmark, status, export, and snapshot surfaces all consume that same + qualification result +- `BENCH-ECOM` remains passing +- `BENCH-SERVICE` full projection flips to `accounting_status = invalid`, + `benchmark_status = invalid`, and `gate_status = open` +- `spec status --format json` and `spec export` both expose additive + `category_qualification` +- export keeps qualification read-side only and does not persist it into + `.spec.passport.json` ## Hard Guards -- Do not change the frozen five-command wall: - - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - - `cargo run -p spec-cli -- export examples/ecommerce/units` - - `cargo run -p spec-cli -- status examples/service/units --format json` - - `cargo run -p spec-cli -- export examples/service/units` - - `cargo run -p spec-cli -- status . --format json` -- Do not add slice-specific proof commands. -- Do not make repo-root `export .` supported for this workspace shape. -- Do not reinterpret repo-root `status . --format json` as a green ship gate. - It must remain `scope_authority: inventory_only`. -- Do not change the benchmark roster: - - `BENCH-ECOM` stays an active positive wall rooted at - `examples/ecommerce/units` - - `BENCH-SERVICE` stays an active positive wall rooted at - `examples/service/units` - - `BENCH-CROSSLIB` stays an active companion negative wall rooted at - `examples/crosslib-app/units` -- Do not widen Rust V1 support beyond the I7 freeze: - - bounded generics remain deferred to `V1.1` - - async flows, runtime adapters, and IO-owned boundaries remain deferred to - `V1.1` -- Do not imply an `I9` or any new checked-in post-I8 milestone. -- Do not start the run if `5d849d4` is not an ancestor of the current execution - head. If that ancestry check ever fails, halt and rewrite `PLAN.md` before - attempting I8. -- If any proposed blocker repair would require: - - changing benchmark schema or benchmark roles - - changing the five-command wall - - adding new proof writers or artifact classes - - promoting a deferred `V1.1` surface into Rust V1 - stop immediately. That is no longer I8. - -## Parent And Worker Responsibilities - -### Parent owns - -- all canonical files under `.runs/i8/` -- all canonical task and sentinel state under `.runs/i8/tasks.json`, - `.runs/i8/session-log.md`, and `.runs/i8/sentinels/` -- the branch and authority ancestry check -- acceptance or rejection of positive proof lane outputs -- workspace inventory interpretation -- authority drift ratification -- any decision to activate blocker repair -- final closeout and milestone verdict - -### Workers own - -- narrow branch-local proof reruns or bounded blocker repair only -- short return summaries plus any worker-local mirror sentinel the prompt - requires - -### Workers must not own - -- canonical `.runs/i8/**` in the parent checkout -- final interpretation of `inventory_only` -- repo-facing authority conclusions -- final closeout status -- any creative scope decision about Rust V1 +- Preserve the parent baseline exactly as: + - branch: `feat/i8-final-proof-run` + - head: `4c41fb36845b30d5527554b6a365f15f6fa58bc5` +- Treat `PLAN.md` as the authority. Do not change wedge scope in the + orchestration flow. +- Do not touch files outside the lane-owned write set. +- Do not revert unrelated working-tree edits. +- Do not change `benchmarks/labels.json`. +- Do not add a checked-in external registry file. +- Do not persist `category_qualification` into `.spec.passport.json`. +- Do not let any worker lane edit `spec-cli/src/commands.rs`. +- Do not let any lane infer support from benchmark labels, health, or semantic + review presence alone. +- Do not merge a lane that passes its local tests but fails an out-of-scope + verification command. +- Stop and escalate if the parent baseline head is no longer the branch head + when execution starts. The runbook assumes that exact commit as the frozen + parent base. + +## Parent Ownership + +The parent agent owns all of the following for the full run: + +- the canonical checkout at + `/home/azureuser/__Active_Code/atomize-hq/spec` +- all state files under `.runs/category-truth/` +- the contract spine: + - `spec-core/src/category_truth.rs` + - `spec-core/src/lib.rs` + - `spec-core/src/semantic_review.rs` +- the `commands.rs` shared-helper consolidation and all final CLI integration: + - `spec-cli/src/commands.rs` + - `spec-cli/tests/cli.rs` + - `spec-cli/tests/m14_regressions.rs` +- final snapshot/readability refresh: + - `benchmarks/snapshots/*.snapshot.json` + - `benchmarks/reviews/*.readability.review.json` +- merge acceptance, conflict resolution, final verification, and closeout + +Workers own only their explicit lane write sets and worker-local artifacts. +Workers do not own parent state, merge decisions, or final interpretation. ## Subagent Execution Policy -- Every worker lane is executed by a fresh `gpt-5.4` subagent with +- All worker lanes use fresh `GPT-5.4` subagents with `reasoning_effort=high`. -- Maximum concurrency is locked at `2` worker subagents. -- The parent remains the only integrator. -- The parent remains the only writer of canonical `.runs/i8/**`, including: - - `.runs/i8/tasks.json` - - `.runs/i8/session-log.md` - - `.runs/i8/sentinels/**` - - `.runs/i8/evidence/**` - - `.runs/i8/preflight.json` - - `.runs/i8/authority-drift.md` - - `.runs/i8/closeout.json` -- Worker completion state is recognized only through: - - the worker's required narrow summary - - parent-owned sentinel updates - - parent-owned task acceptance updates -- The parent must not treat raw subagent transcript text as canonical run - state. - -## Locked Command-Capture Pattern - -Every command whose raw stdout is archived must preserve the real command exit -code. The runbook must not rely on plain `cargo ... | tee ...` without -`pipefail`. - -Locked capture pattern for commands expected to exit `0`: +- Maximum worker concurrency is `2`. +- The benchmark lane and export lane are the only lanes allowed to run in + parallel. +- The parent agent is the only integrator. +- The parent agent is the only writer of canonical run-state files under + `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/`. +- Worker-produced notes, sentinels, and verification captures are advisory + until the parent reviews them and updates canonical state. +- Worker completion is not acceptance. Acceptance happens only when the parent: + - reviews the lane output + - updates `tasks.json` + - updates the canonical per-task sentinel + - records accepted verification artifacts in canonical run-state paths + +## Worktree Layout + +Parent checkout: + +- `/home/azureuser/__Active_Code/atomize-hq/spec` + +Worker worktree root: + +- `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth` + +Worker worktrees and branches: + +- benchmark lane: + - branch: `ws/ct-benchmark` + - worktree: + `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/benchmark` +- export lane: + - branch: `ws/ct-export` + - worktree: + `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/export` +- conditional blocker lane: + - branch: `ws/ct-blocker` + - worktree: + `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/blocker` + +No dedicated worker worktree is created for `commands.rs`. That lane stays in +the parent checkout on the parent baseline branch. + +Exact worktree creation commands after Gate 1: ```bash -bash -lc ' -set -o pipefail - | tee -cmd_status=$? -printf "%s\n" "$cmd_status" > -exit "$cmd_status" -' +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/benchmark -b ws/ct-benchmark feat/i8-final-proof-run +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/export -b ws/ct-export feat/i8-final-proof-run ``` -Locked capture pattern for commands whose non-zero exit is expected and -meaningful: +Conditional blocker worktree command: ```bash -bash -lc ' -set -o pipefail - | tee -cmd_status=$? -printf "%s\n" "$cmd_status" > -test "$cmd_status" -eq -' +git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/blocker -b ws/ct-blocker feat/i8-final-proof-run ``` -Rules: - -- Archived commands must write both the raw stdout file and a sibling - `.exitcode` file. -- Worker-local captures live under `.runs/i8-worker/**`. -- Canonical accepted captures live under `.runs/i8/evidence/**` and - `.runs/i8/sentinels/**` only after parent acceptance. -- A missing or mismatched `.exitcode` file is a failed task even if stdout was - archived. +## Canonical State And Queue Files -## Worktree And Branch Layout +Run root: -The parent checkout remains the canonical run root. +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth` -- Parent workspace: `/home/azureuser/__Active_Code/atomize-hq/spec` -- Parent branch: `feat/i8-final-proof-run` -- Parent execution head at draft time: - `8d627b1af1fc4acd6e0e8a065805045d3f9b195a` -- Worktree root: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8` +Canonical parent-owned files: -Worker worktrees after Gate 0 preflight freeze: +- `tasks.json` +- `session-log.md` +- `preflight.json` +- `merge-order.md` +- `blockers.md` +- `sentinels/.json` +- `verification/contract-spine/*` +- `verification/benchmark/*` +- `verification/export/*` +- `verification/cli/*` +- `verification/final/*` -```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8 -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom -b ws/i8-ecom-proof feat/i8-final-proof-run -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service -b ws/i8-service-proof feat/i8-final-proof-run -``` +Worker-local advisory files: -Conditional blocker worktree only if Gate 3 activates repair: +- `.runs/category-truth-worker/summary.md` +- `.runs/category-truth-worker/verification/*` +- `.runs/category-truth-worker/sentinel.json` -```bash -git -C /home/azureuser/__Active_Code/atomize-hq/spec worktree add /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/blocker -b ws/i8-blocker feat/i8-final-proof-run -``` +Task state vocabulary: -Concurrency policy: - -- maximum concurrent workers before any blocker repair: `2` -- only `Lane A` and `Lane B` run in parallel -- `Lane C`, `Lane D`, and final closeout stay parent-owned and sequential -- `Lane E` exists only if a real contradiction is found after interpretation - -## Canonical Run State - -The parent-owned source of truth for I8 is: - -- `I8_RUN_ROOT=/home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8` -- `tasks.json` at `$I8_RUN_ROOT/tasks.json` -- session log at `$I8_RUN_ROOT/session-log.md` -- preflight freeze at `$I8_RUN_ROOT/preflight.json` -- authority drift record at `$I8_RUN_ROOT/authority-drift.md` -- final closeout at `$I8_RUN_ROOT/closeout.json` -- per-task sentinels under `$I8_RUN_ROOT/sentinels/.json` - -Canonical evidence targets: - -- `$I8_RUN_ROOT/evidence/ecommerce.status.json` -- `$I8_RUN_ROOT/evidence/ecommerce.status.exitcode` -- `$I8_RUN_ROOT/evidence/ecommerce.export.json` -- `$I8_RUN_ROOT/evidence/ecommerce.export.exitcode` -- `$I8_RUN_ROOT/evidence/service.status.json` -- `$I8_RUN_ROOT/evidence/service.status.exitcode` -- `$I8_RUN_ROOT/evidence/service.export.json` -- `$I8_RUN_ROOT/evidence/service.export.exitcode` -- `$I8_RUN_ROOT/evidence/workspace.status.json` -- `$I8_RUN_ROOT/evidence/workspace.status.exitcode` - -Recommended worker-local mirrors: - -- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.status.json` -- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.status.exitcode` -- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.export.json` -- `.../spec-i8/ecom/.runs/i8-worker/ecommerce.export.exitcode` -- `.../spec-i8/service/.runs/i8-worker/service.status.json` -- `.../spec-i8/service/.runs/i8-worker/service.status.exitcode` -- `.../spec-i8/service/.runs/i8-worker/service.export.json` -- `.../spec-i8/service/.runs/i8-worker/service.export.exitcode` -- `.../spec-i8/blocker/.runs/i8-worker/blocker-notes.md` only if needed -- `.../spec-i8/blocker/.runs/i8-worker/.sentinel.json` only if needed - -Sentinel convention: - -- Parent-owned canonical sentinels live at - `.runs/i8/sentinels/.json`. -- Each sentinel records at least: - - `task_id` - - `owner` - - `status` - - `updated_at` - - `evidence_paths` - - `notes` -- Allowed parent-owned statuses are: - - `pending` - - `running` - - `worker_complete` - - `accepted` - - `rejected` - - `blocked` -- Workers may write branch-local mirror sentinels under `.runs/i8-worker/`, - but those are advisory only until the parent updates the canonical sentinel. - -Workers may write branch-local mirrors or notes, but only the parent writes the -canonical `.runs/i8/**` records in the primary checkout. +- `pending` +- `running` +- `worker_complete` +- `accepted` +- `rejected` +- `blocked` +- `done` -## Workstream Matrix +The parent is the only writer of canonical state. `.runs/category-truth/` is +the single source of truth for run progress, lane status, acceptance, block +state, merge order, and accepted verification evidence. -| Workstream | Task id | Owner | Start gate | Owned write set | Deliverable | -| --- | --- | --- | --- | --- | --- | -| WS-PARENT-0 | `task/i8-p0-preflight-freeze` | parent | run start | `.runs/i8/**` | frozen input packet plus evidence map | -| WS-A | `task/i8-a1-ecom-proof` | worker lane A | Gate 0 green | worktree-local `.runs/i8-worker/**` only | BENCH-ECOM raw status/export outputs plus narrow summary | -| WS-B | `task/i8-b1-service-proof` | worker lane B | Gate 0 green | worktree-local `.runs/i8-worker/**` only | BENCH-SERVICE raw status/export outputs plus narrow summary | -| WS-PARENT-1 | `task/i8-p1-positive-wall-acceptance` | parent | WS-A and WS-B returned | `.runs/i8/evidence/ecommerce.*`, `.runs/i8/evidence/service.*`, `.runs/i8/tasks.json` | accepted canonical positive-wall evidence | -| WS-PARENT-2 | `task/i8-p2-workspace-inventory-confirmation` | parent | WS-PARENT-1 green | `.runs/i8/evidence/workspace.status.json` | confirmed broad inventory semantics | -| WS-PARENT-3 | `task/i8-p3-authority-drift-ratification` | parent | WS-PARENT-2 green | `.runs/i8/authority-drift.md`, checked-in authority docs only if needed | repo-facing I8 story aligned to live truth | -| WS-E | `task/i8-e1-conditional-blocker-repair` | worker lane E or parent | only if WS-PARENT-1, WS-PARENT-2, or WS-PARENT-3 finds a real contradiction | only the exact failing surface plus rerun evidence | narrow direct repair or honest blocked stop | -| WS-PARENT-4 | `task/i8-p4-final-closeout` | parent | WS-PARENT-3 done and WS-E done if activated | `.runs/i8/closeout.json` and release-note surfaces if drift repair touched them | final I8 verdict and closeout packet | +### Per-task Sentinel Contract -## Gate Model +Canonical sentinel path: -I8 does not use a human approval gate by default. It uses strict parent-owned -execution gates. +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/sentinels/.json` -- Gate 0: preflight freeze - - confirm branch, head, and `5d849d4 -> HEAD` ancestry - - freeze the exact five-command wall and artifact map -- Gate 1: positive wall acceptance - - accept or reject the `BENCH-ECOM` and `BENCH-SERVICE` reruns -- Gate 2: workspace inventory confirmation - - confirm repo-root `status . --format json` still means `inventory_only` -- Gate 3: authority ratification - - either all repo-facing surfaces already agree, or drift is limited to doc - repair only -- Gate 4: blocker decision - - activate narrow repair only if live truth contradicts the frozen claim -- Gate 5: final closeout - - write the final run verdict as `done` or `blocked` +Required fields for every canonical sentinel: -## Workstream Plan +- `task_id` +- `owner` +- `branch` +- `worktree` +- `status` +- `started_at` +- `updated_at` +- `owned_write_set` +- `required_commands` +- `required_artifacts` +- `worker_summary_path` +- `worker_mirror_paths` +- `canonical_artifact_paths` +- `acceptance_notes` +- `rejection_notes` +- `blocker_notes` +- `accepted_commit` -### WS-PARENT-0 (`task/i8-p0-preflight-freeze`) — parent only, sequential +Required status rules: -Owned write set: +- `pending`: task exists but work has not started +- `running`: owner is actively executing the task +- `worker_complete`: worker reports completion; parent review still pending +- `accepted`: parent accepted the lane and canonicalized its state +- `rejected`: parent rejected the lane; rerun required +- `blocked`: a hard guard or direct blocker stopped the lane +- `done`: parent-only terminal state for completed parent tasks -- `.runs/i8/**` +Worker-local mirror sentinel path convention: -Required commands: +- `/.runs/category-truth-worker/sentinel.json` -```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence -mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/sentinels -git -C /home/azureuser/__Active_Code/atomize-hq/spec branch --show-current -git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD -git -C /home/azureuser/__Active_Code/atomize-hq/spec merge-base --is-ancestor 5d849d4 HEAD -``` +Worker mirrors may include the same fields, but they remain non-canonical until +the parent copies the accepted facts into the canonical sentinel. -`preflight.json` must record: +### Canonical vs Worker-local Verification Outputs -- execution branch -- execution head -- plan-validated commit `5d849d4` -- proof that `5d849d4` is an ancestor of the execution head -- authority inputs: - - `PLAN.md` - - `docs/rust_v1_contract_stack.md` - - `.runs/i7/decision-freeze.json` - - `.runs/i7/i8-handoff.json` - - `benchmarks/labels.json` - - `README.md` - - `DECISIONS.md` - - `CHANGELOG.md` - - `TODOS.md` -- the exact five-command wall -- the canonical evidence output paths -- the frozen plain-English Rust V1 claim -- the deferred `V1.1` surfaces +Canonical verification outputs live only under: -Acceptance: +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/verification/contract-spine/` +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/verification/benchmark/` +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/verification/export/` +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/verification/cli/` +- `/home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/verification/final/` -- `.runs/i8/preflight.json` names exactly the same five commands frozen by - `PLAN.md` -- the run records both `5d849d4` and the live execution head instead of - silently collapsing them -- no proof interpretation happens before preflight is written -- the parent writes `task/i8-p0-preflight-freeze` sentinel state under - `.runs/i8/sentinels/` +Worker-local mirrors live only under: -### WS-A (`task/i8-a1-ecom-proof`) — worker lane A +- `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/benchmark/.runs/category-truth-worker/verification/` +- `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/export/.runs/category-truth-worker/verification/` +- `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth/blocker/.runs/category-truth-worker/verification/` -Branch and worktree: +Rules: -- branch: `ws/i8-ecom-proof` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom` +- worker captures remain mirrors until parent acceptance +- parent acceptance copies or re-runs the accepted verification outputs into + canonical verification paths +- rejected or blocked worker outputs stay worker-local and are referenced from + the canonical sentinel only as diagnostics +- `tasks.json` and canonical sentinels always outrank worker-local mirrors -Owned write set: +## Command Capture Rule -- worker-local mirrors only: - - `.runs/i8-worker/ecommerce.status.json` - - `.runs/i8-worker/ecommerce.status.exitcode` - - `.runs/i8-worker/ecommerce.export.json` - - `.runs/i8-worker/ecommerce.export.exitcode` - - `.runs/i8-worker/task-i8-a1-ecom-proof.sentinel.json` +Every archived verification command writes both raw output and a sibling +`.exitcode` file. Use a `bash -lc` wrapper with `set -o pipefail` so captured +stdout does not mask failure. -Required commands: +Locked pattern: ```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker -bash -lc ' -set -o pipefail -cargo run -p spec-cli -- status examples/ecommerce/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.status.json -cmd_status=$? -printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.status.exitcode -exit "$cmd_status" -' bash -lc ' set -o pipefail -cargo run -p spec-cli -- export examples/ecommerce/units | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.export.json + | tee cmd_status=$? -printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/ecom/.runs/i8-worker/ecommerce.export.exitcode +printf "%s\n" "$cmd_status" > exit "$cmd_status" ' ``` -Acceptance: +If a command is expected to fail meaningfully, assert the expected exit code +explicitly in the wrapper and record that fact in the lane summary. -- `status` exits `0` -- `BENCH-ECOM` remains `benchmark_status: passing` -- `gate_status` remains `satisfied` -- `readability_review_status` remains `current` -- `export` remains `schema_version: 4` -- the export still projects `BENCH-ECOM` as a required positive benchmark with - the required molecule roster: - - `pricing/checkout_flow` - - `pricing/discount_strategy_checkout_flow` - - `pricing/discount_plus_tax` -- the worker returns only: - - changed files - - commands run and exit codes - - blockers - - assumptions -- the worker prompt must contain only: - - owned file set - - relevant `PLAN.md` excerpt - - required commands - - forbidden surfaces - - output-path contract -- completion is surfaced through the worker summary plus the worker-local mirror - sentinel, then parent acceptance updates the canonical task sentinel +## Workstream Matrix -### WS-B (`task/i8-b1-service-proof`) — worker lane B +| Workstream | Task id | Owner | Branch / worktree | Owned modules and artifacts | Start gate | +| --- | --- | --- | --- | --- | --- | +| Preflight freeze | `task/ct-p0-preflight` | parent | parent checkout | `.runs/category-truth/**` | run start | +| Contract spine | `task/ct-p1-contract-spine` | parent | parent checkout | `spec-core/src/category_truth.rs`, `spec-core/src/lib.rs`, `spec-core/src/semantic_review.rs`, substrate tests | Gate 0 | +| Benchmark adoption | `task/ct-b1-benchmark` | worker | `ws/ct-benchmark` | `spec-core/src/benchmark.rs`, `spec-cli/tests/rust_v1_service.rs`, `spec-cli/tests/rust_v1_closure.rs`, `spec-cli/tests/fixtures/benchmarks/*.json` | Gate 1 | +| Export adoption | `task/ct-c1-export` | worker | `ws/ct-export` | `spec-core/src/export.rs`, `spec-core/src/passport.rs`, export-focused tests | Gate 1 | +| CLI integration | `task/ct-p2-cli-integration` | parent | parent checkout | `spec-cli/src/commands.rs`, `spec-cli/tests/cli.rs`, `spec-cli/tests/m14_regressions.rs` | Gate 2 | +| Snapshot/readability refresh | `task/ct-p3-snapshot-refresh` | parent | parent checkout | `benchmarks/snapshots/*.snapshot.json`, `benchmarks/reviews/*.readability.review.json`, final expectation fixes caused by CLI output shape | Gate 3 | +| Conditional blocker lane | `task/ct-x1-blocker` | parent or worker | `ws/ct-blocker` only if needed | only the direct failing surface plus proof reruns | conditional | +| Final closeout | `task/ct-p4-closeout` | parent | parent checkout | `.runs/category-truth/**` | Gate 4 | -Branch and worktree: +## Lane Boundaries -- branch: `ws/i8-service-proof` -- worktree: `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service` +### Contract spine stays local -Owned write set: +The parent keeps category substrate and semantic-review descriptor work in one +local stream because together they define the producer-owned truth contract. +This stream includes: -- worker-local mirrors only: - - `.runs/i8-worker/service.status.json` - - `.runs/i8-worker/service.status.exitcode` - - `.runs/i8-worker/service.export.json` - - `.runs/i8-worker/service.export.exitcode` - - `.runs/i8-worker/task-i8-b1-service-proof.sentinel.json` +- `spec-core/src/category_truth.rs` +- `spec-core/src/lib.rs` +- `spec-core/src/semantic_review.rs` +- unit tests proving: + - supported ecommerce descriptors qualify + - service seam siblings do not qualify as supported + - unsupported rows qualify only as unsupported + - missing `descriptor_id` is explicit failure -Required commands: +Do not split category substrate and semantic-review descriptor work into +separate workers. The type boundary is too shared and the merge win is too +small. -```bash -mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker -bash -lc ' -set -o pipefail -cargo run -p spec-cli -- status examples/service/units --format json | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.status.json -cmd_status=$? -printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.status.exitcode -exit "$cmd_status" -' -bash -lc ' -set -o pipefail -cargo run -p spec-cli -- export examples/service/units | tee /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.export.json -cmd_status=$? -printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/service/.runs/i8-worker/service.export.exitcode -exit "$cmd_status" -' -``` +### Benchmark lane -Acceptance: +The benchmark lane owns benchmark truth adoption only: -- `status` exits `0` -- `BENCH-SERVICE` remains `benchmark_status: passing` -- `gate_status` remains `satisfied` -- `readability_review_status` remains `current` -- `export` remains `schema_version: 4` -- the export still projects `BENCH-SERVICE` as a required positive benchmark - with the required molecule roster: - - `billing/checkout_success_flow` - - `billing/checkout_declined_discount_flow` - - `billing/discount_strategy_quote_flow` -- the worker returns only: - - changed files - - commands run and exit codes - - blockers - - assumptions -- the worker prompt must contain only: - - owned file set - - relevant `PLAN.md` excerpt - - required commands - - forbidden surfaces - - output-path contract -- completion is surfaced through the worker summary plus the worker-local mirror - sentinel, then parent acceptance updates the canonical task sentinel - -### WS-PARENT-1 (`task/i8-p1-positive-wall-acceptance`) — parent only - -Owned write set: - -- `.runs/i8/evidence/ecommerce.status.json` -- `.runs/i8/evidence/ecommerce.status.exitcode` -- `.runs/i8/evidence/ecommerce.export.json` -- `.runs/i8/evidence/ecommerce.export.exitcode` -- `.runs/i8/evidence/service.status.json` -- `.runs/i8/evidence/service.status.exitcode` -- `.runs/i8/evidence/service.export.json` -- `.runs/i8/evidence/service.export.exitcode` -- `.runs/i8/tasks.json` -- `.runs/i8/session-log.md` -- `.runs/i8/sentinels/task-i8-a1-ecom-proof.json` -- `.runs/i8/sentinels/task-i8-b1-service-proof.json` -- `.runs/i8/sentinels/task-i8-p1-positive-wall-acceptance.json` +- `spec-core/src/benchmark.rs` +- benchmark-facing service and closure tests +- benchmark JSON fixtures under `spec-cli/tests/fixtures/benchmarks/` -Required parent actions: +This lane must not touch: -- review the two worker summaries and narrow diffs only -- copy or restage accepted worker-local outputs into the canonical `.runs/i8/` - evidence paths -- copy or restage the worker `.exitcode` files into the canonical evidence paths -- reject any lane output that lacks the expected benchmark, gate, readability, - or schema-version truth -- update canonical sentinels and `tasks.json` instead of relying on transcript - interpretation alone +- `spec-cli/src/commands.rs` +- `spec-core/src/export.rs` +- `spec-core/src/passport.rs` +- `benchmarks/snapshots/*.snapshot.json` +- `benchmarks/reviews/*.readability.review.json` -Acceptance: +### Export lane + +The export lane owns export projection adoption only: + +- `spec-core/src/export.rs` +- `spec-core/src/passport.rs` +- export/passport regression coverage + +This lane must not touch: + +- `spec-cli/src/commands.rs` +- benchmark fixtures +- snapshots or readability artifacts + +### CLI integration lane + +The parent-owned CLI lane owns the shared helper consolidation required by +`PLAN.md`. This lane must: + +- add one shared `commands.rs` helper for projected unit truth +- wire `status`, benchmark read-side, and snapshot paths to that helper +- add `category_qualification` to status JSON +- keep benchmark snapshot output aligned with live benchmark projection + +No other lane may add benchmark-truth extraction logic in `spec-cli`. + +### Snapshot/readability lane + +The parent-owned refresh lane runs only after CLI integration is stable. It +owns: -- both positive walls are accepted into canonical evidence paths -- both positive walls still teach the same narrow-core story frozen in I7 -- no authority docs are edited yet -- canonical sentinels record acceptance or rejection for both worker lanes +- snapshot refresh for `BENCH-ECOM` and `BENCH-SERVICE` +- readability review refresh only as additive artifact maintenance +- no semantic reinterpretation of readability freshness -### WS-PARENT-2 (`task/i8-p2-workspace-inventory-confirmation`) — parent only +## Gate Model + +- Gate 0: preflight freeze + - confirm branch and exact head + - record current dirty files without reverting them + - create run state and worktree plan + - on pass, parent updates: + - `preflight.json` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p0-preflight.json` + - on fail, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p0-preflight.json` + - stop immediately on fail +- Gate 1: contract spine complete + - category substrate and semantic-review descriptor work merged locally + - substrate verification is green + - worker worktrees are created from this updated parent head only after this + - on pass, parent updates: + - `tasks.json` + - `session-log.md` + - `merge-order.md` + - `verification/contract-spine/*` + - `sentinels/task-ct-p1-contract-spine.json` + - on fail, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p1-contract-spine.json` + - do not spawn workers if Gate 1 fails +- Gate 2: benchmark and export lanes accepted + - both worker lanes return in-scope diffs only + - both lane verification sets are green + - worker completion alone is insufficient; each lane must be parent-reviewed + and explicitly marked `accepted` + - on pass, parent updates: + - `tasks.json` + - `session-log.md` + - `merge-order.md` + - `verification/benchmark/*` + - `verification/export/*` + - `sentinels/task-ct-b1-benchmark.json` + - `sentinels/task-ct-c1-export.json` + - on fail, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - rejected lane sentinel(s) + - if one lane is rejected, stop the run, document the rejection, and rerun + from a clean worker lane before proceeding +- Gate 3: CLI integration complete + - `commands.rs` helper is in place + - status, benchmark, export, and snapshot read-side surfaces agree + - on pass, parent updates: + - `tasks.json` + - `session-log.md` + - `verification/cli/*` + - `sentinels/task-ct-p2-cli-integration.json` + - on fail, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p2-cli-integration.json` + - conditional blocker lane may be activated only after this failure is + written canonically +- Gate 4: snapshot/readability refresh and full sweep complete + - frozen artifacts match live output + - no remaining consumer drift + - on pass, parent updates: + - `tasks.json` + - `session-log.md` + - `verification/final/*` + - `sentinels/task-ct-p3-snapshot-refresh.json` + - on fail, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p3-snapshot-refresh.json` + - blocker lane may run only after those files are updated +- Gate 5: closeout + - canonical state updated to `done` or `blocked` + - on pass, parent updates: + - `tasks.json` + - `session-log.md` + - `merge-order.md` + - `sentinels/task-ct-p4-closeout.json` + - on blocked stop, parent updates: + - `blockers.md` + - `tasks.json` + - `session-log.md` + - `sentinels/task-ct-p4-closeout.json` + +The flow is human-free by default. Pause only if a gate fails or a hard guard +is tripped. + +### Blocked Path + +When a task or gate blocks: + +- parent writes the blocker first to: + - `blockers.md` + - `tasks.json` + - the task sentinel + - `session-log.md` +- parent records: + - the failing command + - exit code + - affected files + - whether the blocker is rerunnable or terminal +- if the blocker is rerunnable within scope: + - rerun only the affected task commands + - then rerun all downstream parent verification gates +- if the blocker would require scope expansion or forbidden-file ownership: + - mark the run `blocked` + - do not proceed to downstream gates + - stop after closeout state is written + +## Execution Order + +1. Initialize `.runs/category-truth/` in the parent checkout. +2. Record branch, head, and dirty files in `preflight.json`, + `session-log.md`, `tasks.json`, and the preflight sentinel. +3. Execute the contract spine locally on `feat/i8-final-proof-run`. +4. Run contract-spine verification and archive accepted outputs under + `verification/contract-spine/`. +5. Update parent task state for `task/ct-p1-contract-spine` to `done`. +6. Create worker worktrees with the exact `git worktree add ... -b ws/...` + commands in this document. +7. Spawn exactly two fresh `GPT-5.4` worker lanes: + - benchmark + - export +8. Mark both worker tasks `running` in `tasks.json` and canonical sentinels + before the workers start editing. +9. Wait for worker completion via worker summaries and worker-local mirrors. +10. When a worker reports complete, mark only `worker_complete` in canonical + state. Do not mark `accepted` yet. +11. Review the export lane first: + - inspect owned-file diff + - inspect worker command exits + - inspect worker-local verification mirrors + - reject if any forbidden surface changed +12. If export lane is accepted: + - cherry-pick its commit into the parent branch + - copy or rerun accepted verification outputs into + `verification/export/` + - update `tasks.json`, `merge-order.md`, `session-log.md`, and the export + sentinel to `accepted` + - close the export worker +13. If export lane is rejected: + - write rejection details canonically + - close the worker + - stop and rerun that lane before benchmark acceptance proceeds +14. Review the benchmark lane with the same acceptance process. +15. If benchmark lane is accepted: + - cherry-pick its commit into the parent branch + - copy or rerun accepted verification outputs into + `verification/benchmark/` + - update `tasks.json`, `merge-order.md`, `session-log.md`, and the + benchmark sentinel to `accepted` + - close the benchmark worker +16. If benchmark lane is rejected: + - write rejection details canonically + - close the worker + - stop and rerun that lane before continuing +17. Run the parent-owned CLI integration lane in the parent checkout. +18. Archive accepted CLI verification outputs under `verification/cli/` and + update canonical state. +19. Refresh snapshots and readability artifacts locally from the post-CLI + truth. +20. Run the full verification sweep and archive accepted final outputs under + `verification/final/`. +21. If a direct blocker remains, activate `task/ct-x1-blocker`, run only the + narrow blocker repair, rerun the affected task verification, then rerun + the full verification sweep. +22. Update closeout state, mark the run `done` or `blocked`, and stop. + +This flow walks the current `PLAN.md` session to completion. The run is not +finished at code integration; it is finished only after canonical verification, +state closeout, and final wedge-truth confirmation are complete. + +## Integration And Merge Rules + +- The contract spine is not optional staging. It is the branch-local truth base. +- Each worker lane should produce one cohesive commit if possible. +- Parent integration uses non-interactive git only. +- Preferred integration method is `git cherry-pick -x ` into the + parent branch after parent review. +- Merge order is fixed: + - export first because it is the smaller, more isolated core surface + - benchmark second because its invalid `BENCH-SERVICE` truth becomes the + parent baseline for CLI and snapshot adoption +- Do not merge a worker branch directly if it touched a forbidden file. Reject + and rerun the lane instead. +- If a worker lane conflicts with unrelated dirty parent files, stop and + rebase the lane around the current parent tree without reverting those files. + +## Conflict Flags + +| Flag | Surface | Risk | Required handling | +| --- | --- | --- | --- | +| `CF-1` | `spec-cli/src/commands.rs` | highest merge-conflict hotspot; shared helper required | parent-only lane; no worker edits permitted | +| `CF-2` | `spec-core/src/semantic_review.rs` | producer contract drift if lane-split | keep inside parent contract spine | +| `CF-3` | `spec-cli/tests/fixtures/benchmarks/*.json` vs snapshots | fixture truth can drift from final CLI truth | benchmark lane owns benchmark fixtures; snapshots wait for parent refresh lane | +| `CF-4` | readability review artifacts | qualification failure can be misread as readability change | refresh artifact timestamps/content only as generated output; do not rewrite freshness semantics | +| `CF-5` | dirty non-owned files in workspace | accidental overwrite of teammate work | record in preflight; never reset or force-checkout them | + +## Workstream Detail + +### `task/ct-p0-preflight` -Owned write set: +Required parent actions: -- `.runs/i8/evidence/workspace.status.json` -- `.runs/i8/evidence/workspace.status.exitcode` -- `.runs/i8/sentinels/task-i8-p2-workspace-inventory-confirmation.json` +- record: + - branch + - head + - `git status --short` + - worker worktree plan +- initialize: + - `.runs/category-truth/tasks.json` + - `.runs/category-truth/session-log.md` + - `.runs/category-truth/preflight.json` + - `.runs/category-truth/sentinels/` + - `.runs/category-truth/verification/` + - `task/ct-p0-preflight` canonical sentinel Required commands: ```bash -bash -lc ' -set -o pipefail -cargo run -p spec-cli -- status . --format json | tee /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence/workspace.status.json -cmd_status=$? -printf "%s\n" "$cmd_status" > /home/azureuser/__Active_Code/atomize-hq/spec/.runs/i8/evidence/workspace.status.exitcode -test "$cmd_status" -eq 1 -' +git -C /home/azureuser/__Active_Code/atomize-hq/spec branch --show-current +git -C /home/azureuser/__Active_Code/atomize-hq/spec rev-parse HEAD +git -C /home/azureuser/__Active_Code/atomize-hq/spec status --short +mkdir -p /home/azureuser/__Active_Code/atomize-hq/spec/.runs/category-truth/{sentinels,verification} +mkdir -p /home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-category-truth ``` -Expected interpretation: +Acceptance: -- exit code `1` is allowed and expected -- `scope_authority` must remain `inventory_only` -- `BENCH-CROSSLIB` must remain visible as `companion_negative_proof` -- `BENCH-CROSSLIB` must keep zero positive supported credit -- `BENCH-ECOM` and `BENCH-SERVICE` must still project as `passing` inside the - broader inventory surface -- intentionally non-green roots outside the positive wall must remain visible - rather than being trimmed to force green +- branch is `feat/i8-final-proof-run` +- head is `4c41fb36845b30d5527554b6a365f15f6fa58bc5` +- unrelated dirty files are recorded, not reverted +- `tasks.json`, `session-log.md`, and the preflight sentinel all agree -Acceptance: +### `task/ct-p1-contract-spine` -- repo-root inventory stays truthful without being misread as a ship gate -- the broad surface preserves negative visibility and positive-wall visibility - at the same time -- the archived `.exitcode` file records `1` and is treated as a success case - for this task +Owned files: -### WS-PARENT-3 (`task/i8-p3-authority-drift-ratification`) — parent only +- `spec-core/src/category_truth.rs` +- `spec-core/src/lib.rs` +- `spec-core/src/semantic_review.rs` -Owned write set: +Required verification commands: -- `.runs/i8/authority-drift.md` -- `.runs/i8/session-log.md` -- `.runs/i8/sentinels/task-i8-p3-authority-drift-ratification.json` -- checked-in authority docs only if real drift exists: - - `PLAN.md` - - `ORCH_PLAN.md` - - `docs/rust_v1_contract_stack.md` - - `README.md` - - `DECISIONS.md` - - `CHANGELOG.md` - - `TODOS.md` +```bash +cargo test -p spec-core category_truth +cargo test -p spec-core semantic_review +``` + +Acceptance: -Required comparison set: +- registry rows and qualification types exist +- `SemanticReview` exposes producer-owned `descriptor_id` +- supported ecommerce descriptors qualify +- service seam descriptors remain visible but not supported-qualified +- parent writes accepted verification artifacts to + `verification/contract-spine/` +- parent marks the task `done` in canonical state before workers are spawned -- `.runs/i8/evidence/ecommerce.status.json` -- `.runs/i8/evidence/ecommerce.export.json` -- `.runs/i8/evidence/service.status.json` -- `.runs/i8/evidence/service.export.json` -- `.runs/i8/evidence/workspace.status.json` -- `.runs/i7/decision-freeze.json` -- `.runs/i7/i8-handoff.json` +### `task/ct-b1-benchmark` -Allowed outcomes: +Owned files: -- no drift: - - write that all checked-in authority surfaces already agree with live truth -- doc drift only: - - patch only the prose that drifted +- `spec-core/src/benchmark.rs` +- `spec-cli/tests/rust_v1_service.rs` +- `spec-cli/tests/rust_v1_closure.rs` +- `spec-cli/tests/fixtures/benchmarks/*.json` -Disallowed outcomes: +Required worker verification commands: -- keeping stale prose by reinterpreting live output -- adding new commands to rescue stale prose -- implying a post-I8 discovery milestone +```bash +cargo test -p spec-core benchmark +cargo test -p spec-cli rust_v1_service +cargo test -p spec-cli rust_v1_closure +``` Acceptance: -- every repo-facing authority surface teaches the same I8 story -- no checked-in doc weakens the I7 deferral line -- no checked-in doc implies that repo-root `status .` should be globally green +- `BenchmarkCaseProjection` carries `category_qualification` +- supported positive credit depends on qualified supported truth +- `BENCH-SERVICE` full benchmark becomes invalid/open +- partial mismatch projects `partial_invalid` +- worker summary and worker-local mirrors are reviewed before acceptance +- acceptance is recorded only after cherry-pick plus canonical state update -### WS-E (`task/i8-e1-conditional-blocker-repair`) — conditional only +### `task/ct-c1-export` -Activation rule: +Owned files: -- launch only if a live command, export surface, benchmark projection, or - authority surface contradicts the frozen I8 claim +- `spec-core/src/export.rs` +- `spec-core/src/passport.rs` -Possible owner: +Required worker verification commands: -- parent for doc-only drift repair -- worker lane E in `/home/azureuser/__Active_Code/atomize-hq/.worktrees/spec-i8/blocker` - only if code or benchmark-read-side repair is narrowly required +```bash +cargo test -p spec-core export +``` -Owned write set: +Acceptance: -- only the direct failing surface -- plus any rerun evidence needed to prove the repair -- plus `.runs/i8/sentinels/task-i8-e1-conditional-blocker-repair.json` if the - lane is activated +- `ExportBundle` adds `projected_units` +- projected units carry `semantic_review` plus `category_qualification` +- `.spec.passport.json` persistence remains unchanged +- export schema version is `5` +- worker summary and worker-local mirrors are reviewed before acceptance +- acceptance is recorded only after cherry-pick plus canonical state update -Repair rules: +### `task/ct-p2-cli-integration` -- fix the direct blocker only -- rerun the affected command immediately -- rerun the full five-command wall before closeout -- stop and escalate if the repair would widen support, add commands, or alter - benchmark roles +Owned files: + +- `spec-cli/src/commands.rs` +- `spec-cli/tests/cli.rs` +- `spec-cli/tests/m14_regressions.rs` + +Required parent verification commands: + +```bash +cargo test -p spec-cli cli +cargo test -p spec-cli m14_regressions +cargo run -p spec-cli -- status examples/ecommerce/units --format json +cargo run -p spec-cli -- status examples/service/units --format json +cargo run -p spec-cli -- export examples/ecommerce/units +cargo run -p spec-cli -- export examples/service/units +``` Acceptance: -- the contradiction is removed without changing the frozen I8 scope -- the rerun evidence proves the fix directly -- the full five-command wall is green under the same interpretation rules as - the original plan +- `STATUS_JSON_SCHEMA_VERSION` is `5` +- status rows include additive `category_qualification` +- benchmark/status/snapshot paths share the same derived projected-unit truth +- no extra consumer-local semantic-review extraction logic remains in + `commands.rs` +- parent archives accepted verification outputs under `verification/cli/` -### WS-PARENT-4 (`task/i8-p4-final-closeout`) — parent only +### `task/ct-p3-snapshot-refresh` -Owned write set: +Owned files: -- `.runs/i8/closeout.json` -- `.runs/i8/session-log.md` -- `.runs/i8/sentinels/task-i8-p4-final-closeout.json` -- release-note surfaces only if WS-PARENT-3 or WS-E changed them +- `benchmarks/snapshots/BENCH-ECOM.snapshot.json` +- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` +- `benchmarks/reviews/BENCH-ECOM.readability.review.json` +- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -`closeout.json` must record: +Required parent verification commands: -- milestone: `I8` -- final plain-English Rust V1 claim -- deferred `V1.1` surfaces -- branch and final commit -- plan-validated commit and execution head used for the run -- exact five-command verdicts -- references to all raw evidence files -- any doc files changed for drift repair -- whether blocker repair was activated -- final status: `done` or `blocked` +```bash +cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM +cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE +``` Acceptance: -- a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` - without relying on conversation context -- the final verdict cites raw evidence, not memory or paraphrase +- snapshot output includes case `category_qualification` +- `BENCH-ECOM` remains passing +- `BENCH-SERVICE` full snapshot is invalid/open +- readability freshness remains additive and unchanged in meaning +- parent archives accepted verification outputs under `verification/final/` + +### `task/ct-x1-blocker` + +Activate only if one of the following remains after the normal flow: + +- a worker lane can only pass by touching a forbidden surface +- `commands.rs` integration reveals inconsistent qualification semantics that + cannot be fixed locally in the parent lane without crossing owned boundaries +- the full verification sweep fails on a direct wedge surface + +Rules: + +- create `ws/ct-blocker` only after documenting the blocker in + `.runs/category-truth/blockers.md` +- keep the owned write set as narrow as the failing surface allows +- rerun the affected targeted commands and then rerun the full sweep +- stop if fixing the blocker would widen support, alter schema scope beyond + `PLAN.md`, or require new orchestration lanes ## Context-Control Rules -- Parent keeps only the authoritative working set live: +- Parent context stays centered on: - `PLAN.md` - `ORCH_PLAN.md` - - `.runs/i8/tasks.json` - - `.runs/i8/session-log.md` - - `.runs/i8/sentinels/` - - the latest positive-wall acceptance summary - - the latest authority drift summary -- Each worker prompt contains only: - - its owned file set - - the exact relevant `PLAN.md` excerpt - - required commands - - forbidden touch surfaces - - output-path contract -- Each worker must return only: + - the current lane diff + - the current lane verification outputs + - `.runs/category-truth/tasks.json` + - `.runs/category-truth/session-log.md` +- Worker prompts contain only: + - owned write set + - exact required commands + - forbidden surfaces + - acceptance criteria + - artifact output paths +- Workers return only: - changed files - - commands run and exit codes - - blockers + - commands run with exit codes + - blocker notes - assumptions -- The parent reviews summaries plus narrow diffs only. It does not ingest full - worker transcripts into the main context. -- Completion state flows through worker summaries plus worker-local mirror - sentinels, then parent-owned canonical sentinel and `tasks.json` updates. -- Close each worker immediately after its evidence is accepted or rejected. -- Use completion sentinels or long waits, not tight polling. +- Parent records worker completion as `worker_complete`, not `accepted`. +- Close worker lanes immediately after parent acceptance or rejection. + +## Full Verification Sweep + +Run this exact sweep from the parent checkout after snapshot refresh and again +after any blocker repair: + +```bash +cargo test -p spec-core +cargo test -p spec-cli rust_v1_service +cargo test -p spec-cli rust_v1_closure +cargo test -p spec-cli m14_regressions +cargo test -p spec-cli cli + +cargo run -p spec-cli -- status examples/ecommerce/units --format json +cargo run -p spec-cli -- status examples/service/units --format json +cargo run -p spec-cli -- export examples/ecommerce/units +cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM +cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE +``` + +Expected final truth: + +- ecommerce full benchmark remains passing +- service full benchmark is invalid/open +- status and export expose the same qualification result for seam-backed rows +- snapshot output matches live benchmark projection +- `.spec.passport.json` stays free of persisted `category_qualification` ## Tests And Acceptance -- Command wall - - the exact five commands from `PLAN.md` are the only proof wall - - all five raw outputs are archived under `.runs/i8/evidence/` - - all archived commands preserve real exit codes via the locked capture - pattern and sibling `.exitcode` files -- Positive proof walls - - `BENCH-ECOM` stays `passing` with `gate_status: satisfied` and - `readability_review_status: current` - - `BENCH-SERVICE` stays `passing` with `gate_status: satisfied` and - `readability_review_status: current` - - both positive exports remain `schema_version: 4` -- Broad inventory - - repo-root `status . --format json` remains `inventory_only` - - `BENCH-CROSSLIB` remains active companion negative proof with zero positive - supported credit - - broad inventory remains non-green by design without invalidating I8 -- Authority surfaces - - `PLAN.md`, `ORCH_PLAN.md`, `docs/rust_v1_contract_stack.md`, `README.md`, - `DECISIONS.md`, `CHANGELOG.md`, and `TODOS.md` all teach the same bounded - Rust V1 story - - no checked-in doc implies `I9` -- Blocker discipline - - any repair remains narrow and direct - - no repair adds proof commands, benchmark roles, support rows, or new scope -- Workspace boundary - - the milestone remains a final proof run, not a mechanics rewrite or feature - expansion - -## Failure Modes - -| Failure mode | Consequence | Guard in this plan | -| --- | --- | --- | -| `PLAN.md` validated at `5d849d4` but execution runs from a disconnected head | the run no longer reflects the frozen authority basis | require `merge-base --is-ancestor 5d849d4 HEAD` during preflight | -| repo-root `status .` exit `1` is misread as failure | a truthful inventory surface blocks closeout for the wrong reason | require `scope_authority: inventory_only` interpretation instead of exit-code-only reasoning | -| `BENCH-CROSSLIB` starts earning positive credit | Rust V1 widens by read-side drift | require active visibility plus zero positive supported credit | -| positive `status` passes but positive `export` drifts | machine consumers and human readers see different truth | require both `status` and `export` for each positive wall | -| readability reviews go stale while status stays green | the plan overstates current readability-backed proof | require `readability_review_status: current` for both positive walls | -| docs still imply I7 is current or imply I9 exists | milestone ownership becomes ambiguous again | compare all repo-facing authority docs after live reruns | -| blocker repair expands into a mechanics rewrite | I8 silently becomes a new milestone | hard-stop any repair that alters the wall, benchmark roles, or deferred boundaries | - -Critical gap test: - -- if either positive benchmark loses `passing` or `satisfied`, I8 is not done -- if any authority surface still needs caveats absent from the frozen claim, I8 - is not done -- if the only way to "pass" is to reinterpret `inventory_only` as proof, I8 is - not done - -## Immediate Next Move - -Execute I8 in this order: - -1. create `.runs/i8/` and freeze the exact input set plus `5d849d4 -> HEAD` - ancestry -2. launch `Lane A` and `Lane B` in parallel worktrees -3. accept or reject the two positive proof reruns -4. run repo-root inventory confirmation in the parent checkout -5. ratify checked-in authority surfaces against live truth -6. repair only a real direct blocker if one exists -7. write `.runs/i8/closeout.json` with the final verdict - -Do not start by changing code. Start by proving whether the already-ratified -Rust V1 claim still holds on the live branch. +### Workstream Acceptance + +- Preflight is accepted only when: + - branch and head match the frozen baseline + - dirty files are recorded + - canonical run-state files are initialized +- Contract spine is accepted only when: + - category substrate and semantic-review descriptor work are complete + - contract-spine verification artifacts are canonicalized + - worker lanes have not started yet +- Benchmark lane is accepted only when: + - owned-file diff stays within lane boundaries + - worker verification passes + - `BENCH-SERVICE` benchmark truth flips to invalid/open + - parent cherry-picks the lane and updates canonical state +- Export lane is accepted only when: + - owned-file diff stays within lane boundaries + - worker verification passes + - export schema is `5` + - parent cherry-picks the lane and updates canonical state +- CLI integration is accepted only when: + - `commands.rs` remains parent-owned + - shared-helper consolidation is in place + - status and export commands reflect the same qualification contract +- Snapshot/readability refresh is accepted only when: + - snapshot artifacts match live projection output + - readability artifacts remain additive only +- Blocker lane is accepted only when: + - it fixes a direct, in-scope blocker + - it does not widen scope + - downstream verification is rerun and passes + +### Final Wedge Acceptance + +- `BENCH-ECOM` remains `passing` +- `BENCH-SERVICE` full projection is: + - `accounting_status = invalid` + - `benchmark_status = invalid` + - `gate_status = open` +- `spec status --format json` and `spec export` expose additive + `category_qualification` +- seam-backed rows show status/export parity for qualification truth +- benchmark snapshots match live benchmark projection +- `.spec.passport.json` does not persist `category_qualification` +- no worker lane edited `spec-cli/src/commands.rs` +- the parent closeout records enough canonical evidence to replay the run logic + +## Closeout Criteria + +Mark the run `done` only when all of the following are true: + +- every workstream is `accepted` or `done` +- no forbidden-file edits were needed +- no worker lane changed `spec-cli/src/commands.rs` +- the final verification sweep is green under the expected semantics +- `tasks.json`, `merge-order.md`, and `session-log.md` let a future maintainer + reconstruct what landed and in what order + +Otherwise mark the run `blocked` and record the narrowest honest blocker in +`.runs/category-truth/blockers.md`. + +## Assumptions + +- The current `PLAN.md` frozen decisions remain valid at execution time. +- The exact parent baseline head remains the correct launch point for this + wedge. +- Benchmark and export adoption remain separable after the contract spine lands. +- `commands.rs` remains the only shared-helper hotspot significant enough to + justify parent-only ownership. diff --git a/PLAN.md b/PLAN.md index dcd1fde..959745e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,656 +1,836 @@ -# I8: Rust V1 Final Proof Run Plan - -Status: **authoritative implementation plan** -Iteration: **I8** -Milestone family: **Rust V1 final proof run** -Implementation readiness: **ready to execute** -Plan scope: **rerun the frozen Rust V1 proof wall, preserve repo-root inventory semantics, and close Rust V1 only if the live benchmark surfaces, deferred boundaries, and repo-facing docs still match the ratified narrow-core claim** -Base branch: **main** -Working branch: **`feat/i8-final-proof-run`** -Validated at commit: **`5d849d4`** -Last rewritten: **2026-05-23** +# Category Truth Registry and Consumer Qualification Plan + +Status: **authoritative implementation plan** +Implementation readiness: **ready to execute** +Plan scope: **land one explicit category-truth contract for seam-backed support claims across benchmark accounting, `spec status`, `spec export`, and snapshot/readability projections** +Base branch: **main** +Working branch: **`feat/i8-final-proof-run`** +Validated at commit: **`4c41fb3`** +Last rewritten: **2026-05-25** Supersedes: -- the prior `I7: Rust V1 Scope-Decision Closure Plan` - -Locked authority inputs: - -- contract-stack index: `docs/rust_v1_contract_stack.md` -- I7 decision freeze: `.runs/i7/decision-freeze.json` -- I7 handoff packet: `.runs/i7/i8-handoff.json` -- benchmark roster: `benchmarks/labels.json` -- live repo truth on `feat/i8-final-proof-run` at `5d849d4`: - - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - - `cargo run -p spec-cli -- export examples/ecommerce/units` - - `cargo run -p spec-cli -- status examples/service/units --format json` - - `cargo run -p spec-cli -- export examples/service/units` - - `cargo run -p spec-cli -- status . --format json` - -Historical context, not authority: - -- `README.md` -- `DECISIONS.md` -- `CHANGELOG.md` -- `TODOS.md` -- `ORCH_PLAN.md` - -Primary repo surfaces for I8 closeout: - -- `PLAN.md` -- `ORCH_PLAN.md` -- `docs/rust_v1_contract_stack.md` -- `README.md` -- `DECISIONS.md` -- `CHANGELOG.md` -- `TODOS.md` -- `benchmarks/labels.json` -- `benchmarks/snapshots/*.snapshot.json` -- `benchmarks/reviews/*.readability.review.json` -- `.runs/i8/**` +- the prior `I8: Rust V1 Final Proof Run Plan` + +Primary authority inputs: + +- design anchor: `docs/category_truth_contract_v0.1.md` +- backlog anchor: `TODOS.md` under `Post-I8 truth-contract follow-up` +- current benchmark registry: `benchmarks/labels.json` +- current producer and consumer code: + - `spec-core/src/semantic_review.rs` + - `spec-core/src/benchmark.rs` + - `spec-core/src/export.rs` + - `spec-core/src/passport.rs` + - `spec-cli/src/commands.rs` +- current contract suites and read-side fixtures: + - `spec-cli/tests/rust_v1_service.rs` + - `spec-cli/tests/rust_v1_closure.rs` + - `spec-cli/tests/m14_regressions.rs` + - `spec-cli/tests/cli.rs` + - `spec-cli/tests/fixtures/benchmarks/*.json` + - `benchmarks/snapshots/*.snapshot.json` + - `benchmarks/reviews/*.readability.review.json` ## Executive Summary -I7 already made the product decisions. Rust V1 stays narrow. Rust V1 stays -synchronous-only. Bounded generics and async or IO-owned boundaries stay -deferred to `V1.1`. `BENCH-CROSSLIB` stays visible as companion negative proof -and never counts as positive supported credit. - -That means I8 is not another scope milestone. I8 is the final proof run over -the already-ratified claim. The job is to prove that the live repo still tells -the same story through the same five-command wall, then freeze the evidence and -close Rust V1 without widening scope by prose drift, benchmark drift, or -repo-root misinterpretation. - -I8 is complete only when the repo can say this sentence honestly, with checked -evidence: - -> Rust V1 is the current narrow-core `spec` surface: synchronous supported -> function families plus plain data and sum seams, proven by BENCH-ECOM and -> BENCH-SERVICE, with BENCH-CROSSLIB preserved as companion negative proof. - -## Frozen I8 Contract - -These are inherited from I7 and are not open for reinterpretation in I8: - -- the active ladder is `I7 -> I8`, not `I7 -> I8 -> I9` -- the Rust V1 claim stays narrow, synchronous, and benchmark-backed -- deferred `V1.1` surfaces are exactly: - - bounded generics - - async flows, runtime adapters, and IO-owned boundaries -- `BENCH-ECOM` and `BENCH-SERVICE` are the only positive proof walls required - for the V1 claim -- `BENCH-CROSSLIB` remains the active companion negative proof wall -- the authoritative I8 proof wall is exactly five commands: - - `cargo run -p spec-cli -- status examples/ecommerce/units --format json` - - `cargo run -p spec-cli -- export examples/ecommerce/units` - - `cargo run -p spec-cli -- status examples/service/units --format json` - - `cargo run -p spec-cli -- export examples/service/units` - - `cargo run -p spec-cli -- status . --format json` -- no new slice-specific proof commands are admitted in I8 -- repo-root `export .` remains unsupported for this workspace shape and is not - part of the I8 wall -- repo-root `status . --format json` remains a broad inventory surface with - `scope_authority: inventory_only`; it is not a green ship gate - -## Current Validated Truth - -Observed live on `feat/i8-final-proof-run` at commit `5d849d4` on 2026-05-23: - -- `status examples/ecommerce/units --format json` passes with: - - `BENCH-ECOM` - - `benchmark_status: passing` - - `gate_status: satisfied` - - `readability_review_status: current` -- `export examples/ecommerce/units` passes with: - - `schema_version: 4` - - `provenance.git_commit_sha: 5d849d4f60676c259793d0592b7c1af07431d9a2` - - full `BENCH-ECOM` projection, including required molecule proofs and - readability-generated file inventory -- `status examples/service/units --format json` passes with: - - `BENCH-SERVICE` - - `benchmark_status: passing` - - `gate_status: satisfied` - - `readability_review_status: current` -- `export examples/service/units` passes with: - - `schema_version: 4` - - `provenance.git_commit_sha: 5d849d4f60676c259793d0592b7c1af07431d9a2` - - full `BENCH-SERVICE` projection, including required molecule proofs and - readability-generated file inventory -- `status . --format json` exits `1`, which is still correct for I8 because: - - `scope_authority` is `inventory_only` - - `BENCH-CROSSLIB` remains visible with `benchmark_status: passing` and - `gate_status: not_applicable` - - `BENCH-ECOM` and `BENCH-SERVICE` still project as `passing` - - intentionally non-green roots still exist in the broad inventory surface, - including fixtures, semantic-family packets, and unsupported or untested - diagnostic roots that are outside the positive proof wall - -This is the behavior I8 must preserve unless a real truth bug is found. +The repo already projects semantic-review truth and already computes benchmark, +status, export, and snapshot surfaces. The bug class is not missing data. The +bug class is consumer drift: a read-side surface can still over-credit a seam +category from partial truth such as compatibility key alone, support status +alone, or benchmark label alone. + +This wedge fixes that by introducing one producer-owned registry plus one +shared qualification function in `spec-core`, then wiring every current +seam-category consumer to that contract. After this lands, no current consumer +may claim supported category truth or award positive supported benchmark credit +unless qualification returns an explicit supported result. + +The most visible product change is intentional: `BENCH-SERVICE` must stop +projecting a full-scope `passing` result when its supported-labeled seam cases +are not producer-qualified as supported. + +## Success Sentence + +When this plan is complete, the repo can say this sentence honestly: + +> Every current seam-category consumer in `spec` uses one shared, +> producer-owned qualification contract. Benchmark labels, health state, and +> compatibility-key folklore can no longer widen supported category claims or +> positive benchmark credit on their own. + +## Frozen Decisions + +These are not open during implementation: + +- the authoritative category registry lives in Rust code under `spec-core`, not + in a new checked-in JSON registry +- first-scope registry coverage is exactly four rows: + - `sum.discount_strategy.v1` + - `data.pricing_quote.v1` + - `unsupported.sum.v1` + - `unsupported.data.v1` +- producer truth outranks benchmark labels +- the current service seam mismatch remains visible in this wedge; it is not + rescued into supported truth +- category qualification is projected read-side truth only and is **not** + persisted into on-disk passports +- snapshot and readability surfaces consume the same benchmark qualification + result; readability freshness is not repurposed as an accounting signal +- benchmark labels schema stays at `1`; this wedge changes consumer + projections, not the label-file format +- `spec status --format json` and `spec export` are published machine surfaces, + so their schema versions must bump when `category_qualification` is added +- unsupported terminal categories qualify as `unsupported_qualified` without a + descriptor-id check; descriptor approval is required only for supported rows +- do not ship an unused extra failure vocabulary; only add reason codes the + first landing actually emits + +## Problem Statement + +The failure class is any path where a consumer infers category-backed support +or positive benchmark value without explicit qualification. + +Current repo reality already shows the gap: + +- `benchmarks/labels.json` marks `billing/discount_strategy` and + `billing/pricing_quote` as `classification: supported` +- producer-owned semantic review routes those units to `unsupported.sum.v1` and + `unsupported.data.v1` +- current benchmark logic correctly sets + `counts_as_supported_positive = false` for those cases +- but the enclosing full `BENCH-SERVICE` benchmark still projects + `benchmark_status = passing` + +That last line is too optimistic. The case-level truth is already saying +"this supported claim is not actually producer-qualified." The benchmark-level +truth must stop pretending that full supported closure still passed. ## Scope Challenge -### Premise correction +### What already exists + +| Sub-problem | Existing owner | Reuse decision | +| --- | --- | --- | +| producer-owned compatibility routing | `spec-core/src/semantic_review.rs` | reuse; do not build a second routing system | +| benchmark projection and accounting skeleton | `spec-core/src/benchmark.rs` | reuse; tighten through qualification rather than rewrite | +| status JSON projection | `spec-cli/src/commands.rs` | reuse; add qualification beside existing semantic review | +| export bundle construction | `spec-core/src/export.rs` | reuse; add additive projected truth rather than new export command | +| projected passport truth assembly | `spec-core/src/passport.rs` | reuse for semantic-review projection only; do not persist category qualification | +| benchmark truth collection from current proof surfaces | duplicated in `spec-cli/src/commands.rs` benchmark and snapshot paths | collapse to one shared helper instead of editing multiple truth carriers independently | +| frozen service/ecommerce benchmark contract suites | `spec-cli/tests/rust_v1_service.rs`, `spec-cli/tests/rust_v1_closure.rs` | keep and update expectations | +| read-side fixtures and snapshots | `spec-cli/tests/fixtures/benchmarks/*.json`, `benchmarks/snapshots/*.snapshot.json` | refresh, do not replace with new artifact families | + +### Minimum complete change set + +The smallest honest implementation is: + +1. add a shared registry and qualification module +2. extend seam semantic review with producer-owned `descriptor_id` +3. thread one shared `CategoryQualification` object through benchmark, status, + export, and snapshot surfaces +4. invalidate full-scope supported benchmark claims when a supported-labeled + case is not producer-qualified +5. refresh tests, fixtures, and snapshots so every read-side surface says the + same thing + +Anything smaller leaves at least one current consumer free to infer support +from partial truth. + +### Complexity check + +This wedge touches one new module, five existing code modules, and several +fixture suites. That is larger than a one-file patch, but it is still the +right-sized diff because the bug class is cross-consumer by definition. A +benchmark-only fix would leave status/export drift alive. + +### Search check + +This is internal contract consolidation work, not a framework or infrastructure +selection problem. The right move is reuse, not novelty: + +- **[Layer 1]** reuse the existing semantic-review, benchmark, status, export, + and passport projection surfaces +- **[Layer 3]** add one repo-local contract where no shared contract exists yet + +No new infrastructure, registry service, or persistence layer belongs in this +plan. + +### TODOS cross-reference + +This plan is the executable version of the active backlog item already recorded +in `TODOS.md` under `Post-I8 truth-contract follow-up`. It should land the +implementation wedge, not create a second overlapping TODO. + +### Completeness check + +This plan should ship the complete version now. With the existing producer and +consumer surfaces already in place, the marginal cost of also updating status, +export, and snapshot projections is small compared with the cost of letting +multiple read-side honesty bugs survive. -I8 is not "final Rust work." I8 is: +### Distribution check + +This plan introduces no new distributable artifact. Existing CLI build and +release pipelines remain the distribution path. No release automation work is +needed for this wedge. + +## NOT In Scope + +This wedge does **not** do any of the following: + +- widen semantic-review routing so service seam descriptors become supported +- change the four first-scope category ids +- relabel `BENCH-SERVICE` cases in `benchmarks/labels.json` +- add a checked-in external registry file for non-Rust consumers +- persist `category_qualification` into `.spec.passport.json` +- redesign function-family support, benchmark kinds, or benchmark registry shape +- rewrite status health semantics outside the additive qualification field + +## Architecture + +### Current vs target authority flow ```text -prove that the already-ratified Rust V1 claim still holds on the live repo, -with the same benchmark roster, the same deferred boundaries, and the same -five-command wall +CURRENT +semantic_review + -> compatibility_key + support_status + -> benchmark/status/export each decide what that "means" + -> benchmark label can still overstate supported closure + +TARGET +semantic_review + -> compatibility_key + support_status + descriptor_id + -> category_truth registry lookup + -> qualify_category_claim(...) + -> benchmark/status/export/snapshot all consume the same result + -> no consumer-local widening ``` -If the work expands beyond that sentence, the milestone escaped. +### Architecture diagram + +```text +spec-core/src/semantic_review.rs + ├── projects compatibility_key + ├── projects support_status + └── projects descriptor_id + │ + v +spec-core/src/category_truth.rs + ├── CategoryTruthRegistry + └── qualify_category_claim(...) + │ + ┌───────┼─────────────────────────────┐ + │ │ │ + v v v +benchmark.rs commands.rs(status JSON) export.rs(projected_units) + │ │ │ + └──────────────> snapshot/readability surfaces +``` -### What existing code already solves the problem +### Shared contract additions -The repo already ships everything I8 needs: +Add `spec-core/src/category_truth.rs` and export it from `spec-core/src/lib.rs`. -- the benchmark roster and roles are already encoded in `benchmarks/labels.json` -- benchmark-aware `status` and `export` already project full truth at schema - version `4` -- readability anchors already exist for the two positive walls -- I7 already froze the scope decision and wrote the I8 handoff packet -- repo-facing docs already describe the narrow-core story +This module owns: -I8 should reuse those surfaces exactly. It should not invent parallel proof -machinery, new labels, or a new source of authority. +- `CategoryTruthRegistry` +- `CategoryTruthRow` +- `CategoryKind` +- `ContractSupportStatus` +- `AliasSiblingPolicy` +- `DescriptorSet` +- `PositiveCreditPolicy` +- `ConsumerKind` +- `CategoryQualification` +- `ClaimStatus` +- `PositiveCreditEligibility` +- `QualificationReasonCode` +- `qualify_category_claim(...)` -### Minimum change set +The implementation should stay explicit. Do not introduce a second layer of +builder types or indirection unless the code proves it is necessary. -If the frozen wall still passes, the minimum complete I8 diff is: +### Exact semantic-review change -1. create `.runs/i8/` and freeze the run inputs -2. rerun the five-command wall and archive raw outputs -3. compare those outputs against the frozen claim and checked-in docs -4. patch only real authority drift -5. write the closeout packet +`SemanticReview` gains one additive field: -Code changes are conditional only. If a proof-wall command fails or a read-side -surface contradicts the ratified claim, fix only the direct blocker. Do not -turn one failing command into a mechanics redesign. +```rust +pub struct SemanticReview { + ... + pub descriptor_id: Option, +} +``` -### Complexity check +Rules: -This milestone should stay boring: +- only producer-owned semantic-review logic may populate `descriptor_id` +- no benchmark, status, export, or snapshot path may synthesize or rewrite it +- canonical ecommerce seam descriptors project: + - `discount_strategy.ecommerce.v1` + - `pricing_quote.ecommerce.v1` +- current service seam siblings project their own service descriptor ids and + remain unqualified for supported rows +- legacy stored semantic reviews without `descriptor_id` stay readable but must + fail supported qualification explicitly with `descriptor_id_missing` -- no new CLI commands -- no new benchmark kinds -- no new support rows -- no new example or service roots -- no new proof writers -- no new artifact classes beyond the bounded `.runs/i8/` closeout records +### Exact first-scope registry rows -If the work grows into benchmark schema edits, support-boundary changes, or a -new post-I8 planning milestone, stop. That is not I8. +| Category | Kind | Contract support | Alias policy | Canonical descriptor | Approved siblings | Positive credit | +| --- | --- | --- | --- | --- | --- | --- | +| `sum.discount_strategy.v1` | `sum` | `supported` | `canonical_only` | `discount_strategy.ecommerce.v1` | none | eligible | +| `data.pricing_quote.v1` | `data` | `supported` | `canonical_only` | `pricing_quote.ecommerce.v1` | none | eligible | +| `unsupported.sum.v1` | `sum` | `unsupported` | `unsupported_terminal` | none | none | ineligible | +| `unsupported.data.v1` | `data` | `unsupported` | `unsupported_terminal` | none | none | ineligible | -### Completeness rule +### Qualification API contract -The complete version is cheap here, so take it: +Implement one shared function with this exact responsibility split: -- rerun all five commands, not fragments -- inspect benchmark projections, not just exit codes -- archive raw outputs, not just summaries -- compare docs against live truth, not against memory -- close only after the repo-facing story and the command wall say the same thing +```rust +pub fn qualify_category_claim( + consumer: ConsumerKind, + semantic_review: Option<&SemanticReview>, +) -> CategoryQualification +``` -The shortcut version would be "the commands seem fine, ship it." I8 should not -use that shortcut. +The first landing should keep the function contract small. Do **not** add a +separate consumer-context struct unless a real requirement appears during +implementation. -### Distribution check +The function decides only category truth: -I8 introduces no new distributable artifact. The release surface remains the -existing CLI and checked-in repo authority. Distribution work is unchanged and -out of scope. +- registry row lookup from `semantic_review.compatibility_key` +- effective support-status match +- descriptor approval for supported rows only +- unsupported terminal qualification for unsupported rows +- positive-credit eligibility -## What Already Exists +It does **not** decide benchmark lifecycle, full-vs-partial path scope, or gate +status. Those remain benchmark-local rules. -| Sub-problem | Existing owner | I8 action | -| --- | --- | --- | -| milestone ladder and scope boundary | `docs/rust_v1_contract_stack.md`, `.runs/i7/i8-handoff.json` | reuse exactly; do not infer `I9` | -| supported-vs-deferred Rust claim | `DECISIONS.md`, `.runs/i7/decision-freeze.json` | quote and verify, not reinterpret | -| benchmark role split | `benchmarks/labels.json`, I7 handoff | preserve two positive walls plus one companion negative wall | -| benchmark/readability mechanics | `status`/`export` schema v4, committed readability reviews, snapshot surfaces | verify read-side truth still matches the frozen mechanics | -| positive proof walls | `examples/ecommerce/units`, `examples/service/units` | rerun unchanged | -| companion-negative visibility | `examples/crosslib-app/units`, repo-root `status`, `BENCH-CROSSLIB` labels | verify still visible and still zero-credit | -| repo-root scope semantics | `.runs/i3_5_authority_alignment/**`, current CLI behavior | preserve `inventory_only`; do not try to make repo-root globally green | -| scope-closure packet trail | `.runs/i7/**` | treat as frozen inputs, then append I8 evidence separately under `.runs/i8/**` | - -## Final Rust V1 Claim - -This is the exact plain-English line I8 is closing: - -> Rust V1 is the current narrow-core `spec` surface: synchronous supported -> function families plus plain data and sum seams, proven by BENCH-ECOM and -> BENCH-SERVICE, with BENCH-CROSSLIB preserved as companion negative proof. - -Derived consequences: - -- supported: - - synchronous supported function families - - plain pipeline and wrapper composition inside the shipped supported - families - - plain data seams - - plain sum seams - - truthful proof surfaces over those supported rows -- deferred to `V1.1`: - - bounded generics - - async flows, runtime adapters, and IO-owned boundaries -- visible but non-crediting: - - companion negative proof in `BENCH-CROSSLIB` - -## Artifact Map - -The I8 run root is intentionally small and deterministic: - -- `.runs/i8/preflight.json` -- `.runs/i8/evidence/ecommerce.status.json` -- `.runs/i8/evidence/ecommerce.export.json` -- `.runs/i8/evidence/service.status.json` -- `.runs/i8/evidence/service.export.json` -- `.runs/i8/evidence/workspace.status.json` -- `.runs/i8/authority-drift.md` -- `.runs/i8/closeout.json` - -Artifact contract: - -- raw command output files are canonical evidence -- summaries may cite raw outputs but do not replace them -- checked-in authority docs remain the public story -- `.runs/i8/**` is the private closeout packet that proves how that story was - revalidated - -## Architecture And Execution Graph - -I8 is a verification-and-ratification run, not a product expansion: +### Stable qualification output -```text -I7 decision freeze - | - v -live benchmark roster + live CLI truth - | - v -I8 preflight freeze (.runs/i8/) - | - +---------------------+----------------------+ - | | | - v v v -BENCH-ECOM rerun BENCH-SERVICE rerun workspace inventory rerun - | | | - +---------------------+----------+-----------+ - | - v - authority drift comparison - | - +---------------+---------------+ - | | - v v - no drift or doc drift only truth blocker found - | | - v v - final closeout packet direct blocker repair only - | | - +---------------+---------------+ - | - v - truthful Rust V1 done-state claim +Every consumer must reuse this shape: + +```rust +pub struct CategoryQualification { + pub category_id: Option, + pub descriptor_id: Option, + pub claim_status: ClaimStatus, + pub positive_credit_eligibility: PositiveCreditEligibility, + pub reason_code: QualificationReasonCode, +} +``` + +Required first-landing enums: + +- `ClaimStatus` + - `supported_qualified` + - `unsupported_qualified` + - `unqualified` +- `PositiveCreditEligibility` + - `eligible` + - `ineligible` +- `QualificationReasonCode` + - `qualified` + - `semantic_review_missing` + - `registry_row_missing` + - `descriptor_id_missing` + - `descriptor_not_approved` + - `support_status_mismatch` + - `positive_credit_disallowed` + +Rules that remove ambiguity: + +- supported rows require descriptor approval plus `effective_support_status() == + Supported` +- unsupported terminal rows require `effective_support_status() == + Unsupported`; they do **not** require descriptor approval +- `positive_credit_disallowed` is valid only for a claim that otherwise + resolves, but is ineligible for positive credit +- `semantic_support_status` may remain visible as a compatibility/debug field, + but `category_qualification` is the only authoritative claim surface after + this wedge + +## Consumer Behavior Contract + +### Benchmark accounting + +`spec-core/src/benchmark.rs` is the first adoption point. + +Required behavior after the change: + +- `BenchmarkCaseTruth` stops carrying only + `semantic_support_status: Option` +- replace that with `semantic_review: Option` so benchmark + projection receives the exact producer-owned input it needs instead of a new + bespoke mini-struct +- `BenchmarkCaseProjection` gains `category_qualification` +- `counts_as_supported_positive` requires all of: + - positive benchmark + - active lifecycle + - full path scope + - valid benchmark accounting + - `classification == supported` + - `status == valid` + - `category_qualification.claim_status == supported_qualified` + - `category_qualification.positive_credit_eligibility == eligible` +- a supported-labeled case that fails qualification stays visible, but it makes + the full benchmark accounting invalid +- partial benchmark scope becomes `partial_invalid` on the same mismatch, but + still does not invent full `benchmark_status` or `gate_status` +- `readability_review_status` remains whatever the readability artifact already + says; qualification failure must not rewrite it + +This is the most important product decision in the plan: + +- `BENCH-SERVICE` full projection must stop saying `passing` +- it must become: + - `accounting_status = invalid` + - `benchmark_status = invalid` + - `gate_status = open` + - unchanged readability freshness status + +### `spec status --format json` + +`spec-cli/src/commands.rs` must add `category_qualification` to each +`JsonStatusEntry`. + +Rules: + +- keep current health semantics unchanged +- keep current `semantic_review` projection unchanged except for additive + `descriptor_id` +- add `category_qualification` beside `semantic_review` +- never infer supported category truth from health or semantic-review presence + alone + +Schema change: + +- bump `STATUS_JSON_SCHEMA_VERSION` from `4` to `5` + +### `spec export` + +`spec-core/src/export.rs` must expose additive projected truth without writing +`category_qualification` into on-disk passports. + +Exact plan: + +- keep exported `passports` as projected passport truth +- add a new additive `projected_units` array to `ExportBundle` +- define a dedicated read-side struct for it, for example: + +```rust +pub struct ProjectedExportUnit { + pub id: String, + pub semantic_review: Option, + pub category_qualification: Option, +} ``` -Critical dependency rule: +- each row must include: + - `id` + - `semantic_review` + - `category_qualification` + +That keeps export machine-readable, keeps qualification read-side only, and +avoids mutating `.spec.passport.json` persistence semantics. + +Schema change: + +- bump `EXPORT_SCHEMA_VERSION` from `4` to `5` + +### Snapshot and readability parity + +Benchmark snapshot output already reuses benchmark projection. This wedge must +keep that true. + +Rules: + +- benchmark snapshot output must serialize per-case `category_qualification` +- full-scope invalid service seam claims must yield invalid full snapshots too +- readability review freshness and verdict stay additive and unchanged by + qualification failure + +### Shared read-side plumbing rule + +`spec-cli/src/commands.rs` currently constructs benchmark root-case truth in +multiple places. This wedge must collapse that duplication instead of letting +benchmark and snapshot paths drift again. + +Implementation rule: + +- add one shared helper in `spec-cli/src/commands.rs` that derives read-side + unit truth from `project_passport_truth_with_context(...)` +- status, benchmark, and snapshot paths should all consume that shared helper +- do not hand-edit three independent call paths with slightly different + semantic-review extraction logic + +## File Blast Radius + +### New code -- proof reruns may happen before doc edits -- doc edits may not happen until proof outputs are captured and interpreted -- blocker repair exists only if a live command or live projection contradicts - the frozen claim -- closeout happens only after proof truth and repo-facing prose agree +- `spec-core/src/category_truth.rs` -## Work Phases +### Existing code that must change -| Phase | Goal | Primary outputs | Exit criteria | -| --- | --- | --- | --- | -| 1. Preflight freeze | create a reproducible I8 run root and freeze the inputs | `.runs/i8/preflight.json` | every later step cites frozen inputs and fixed output paths | -| 2. Positive proof rerun | rerun both positive benchmark walls unchanged | ecommerce and service raw status/export outputs | both positive walls still pass with satisfied gates and current readability | -| 3. Broad inventory confirmation | rerun repo-root inventory and confirm companion-negative visibility | workspace raw status output and inventory interpretation | `scope_authority: inventory_only` is preserved and `BENCH-CROSSLIB` remains zero-credit | -| 4. Authority drift ratification | compare live outputs against checked-in authority surfaces | `.runs/i8/authority-drift.md`, doc diff only if needed | all repo-facing authority surfaces teach one identical I8 story | -| 5. Conditional blocker repair | repair only a direct truth blocker if one exists | bounded code or doc diff plus rerun evidence | failing truth surface is fixed without widening support or commands | -| 6. Final closeout | freeze the final evidence packet and milestone verdict | `.runs/i8/closeout.json`, release-note updates if needed | Rust V1 can be stated honestly with no extra caveats | +- `spec-core/src/lib.rs` +- `spec-core/src/semantic_review.rs` +- `spec-core/src/benchmark.rs` +- `spec-core/src/export.rs` +- `spec-core/src/passport.rs` +- `spec-cli/src/commands.rs` -### Phase detail +### Tests and fixtures that must change -#### Phase 1. Preflight freeze +- `spec-cli/tests/rust_v1_service.rs` +- `spec-cli/tests/rust_v1_closure.rs` +- `spec-cli/tests/m14_regressions.rs` +- `spec-cli/tests/cli.rs` +- `spec-cli/tests/fixtures/benchmarks/*.json` +- `benchmarks/snapshots/BENCH-ECOM.snapshot.json` +- `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` +- `benchmarks/reviews/BENCH-ECOM.readability.review.json` +- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -Create `.runs/i8/` and write `preflight.json` with: +## Implementation Phases -- branch -- commit -- timestamp -- authority inputs -- the exact five-command wall -- expected evidence output file paths -- the frozen plain-English Rust V1 claim +### Phase 0: Preflight and current-truth lock -No interpretation happens here. This phase freezes the basis only. +Goals: -#### Phase 2. Positive proof rerun +- capture the current service mismatch behavior before changing it +- verify which fixtures and tests lock the existing optimistic benchmark status -Run these four commands against the live branch and archive raw stdout: +Do: -- `cargo run -p spec-cli -- status examples/ecommerce/units --format json` -- `cargo run -p spec-cli -- export examples/ecommerce/units` -- `cargo run -p spec-cli -- status examples/service/units --format json` -- `cargo run -p spec-cli -- export examples/service/units` +- run the current benchmark contract suites +- note all current full-scope service expectations that will intentionally flip + from `passing` to `invalid` +- capture the exact benchmark/status/export/snapshot fixture files that will + need rewrites -Required interpretation: +Done when: -- both benchmarks must remain `passing` -- both gates must remain `satisfied` -- both readability reviews must remain `current` -- both exports must remain `schema_version: 4` -- both exports must still project the same positive benchmark role and required - molecule roster +- the planned expectation flips are explicit before code edits start -#### Phase 3. Broad inventory confirmation +### Phase 1: Add category truth substrate -Run: +Files: -- `cargo run -p spec-cli -- status . --format json` +- `spec-core/src/category_truth.rs` +- `spec-core/src/lib.rs` +- `spec-core/src/semantic_review.rs` -Expected outcome: +Deliverables: -- non-zero exit code is allowed and expected -- `scope_authority` must be `inventory_only` -- `BENCH-CROSSLIB` must remain visible as active companion negative proof -- `BENCH-CROSSLIB` must keep `positive_credit_cases: 0` -- `BENCH-ECOM` and `BENCH-SERVICE` must still project as passing inside the - broad surface +- registry structs and enums +- hard-coded first four rows +- `qualify_category_claim(...)` +- `SemanticReview.descriptor_id` +- seam descriptor-id projection tests +- qualification unit tests for: + - canonical ecommerce sum qualifies supported + - canonical ecommerce data qualifies supported + - service sum sibling does not qualify supported + - service data sibling does not qualify supported + - unsupported rows qualify only as unsupported + - missing semantic review fails explicitly + - missing descriptor id fails explicitly -This phase exists to prevent a false green and to prevent a false red. +### Phase 2: Benchmark-core adoption -#### Phase 4. Authority drift ratification +Files: -Compare live outputs against: +- `spec-core/src/benchmark.rs` +- benchmark-focused tests in `spec-core` and `spec-cli/tests/rust_v1_service.rs` +- benchmark JSON fixtures under `spec-cli/tests/fixtures/benchmarks/` -- `PLAN.md` -- `ORCH_PLAN.md` -- `docs/rust_v1_contract_stack.md` -- `README.md` -- `DECISIONS.md` -- `CHANGELOG.md` -- `TODOS.md` +Deliverables: -Allowed outcomes: +- `BenchmarkCaseTruth` carries `semantic_review` +- `BenchmarkCaseProjection` gains `category_qualification` +- `counts_as_supported_positive` uses qualification +- benchmark-wide full-scope invalidation on supported-label qualification + failure +- service benchmark contract tests assert `invalid/open` +- benchmark fixture expectations reflect: + - ecommerce remains passing + - service flips to invalid + - partial mismatch becomes `partial_invalid` -- no drift: record that all surfaces already agree -- doc drift only: patch only the prose that drifted +### Phase 3: Export projection adoption -Disallowed outcome: +Files: -- reinterpreting the live output so stale prose can stay unchanged +- `spec-core/src/export.rs` +- `spec-core/src/passport.rs` -#### Phase 5. Conditional blocker repair +Deliverables: -This phase exists only if a live truth surface disagrees with the frozen claim. +- `ExportBundle.projected_units[]` +- dedicated projected export row type +- export projection reuses shared qualification +- export schema bump `4 -> 5` +- regression coverage that proves qualification stays read-side only and does + not persist into `.spec.passport.json` -Repair policy: +### Phase 4: CLI status and snapshot integration -- fix the direct blocker only -- rerun the affected command immediately -- rerun the full five-command wall before closeout -- stop and escalate if the repair would require: - - new proof commands - - new support rows - - benchmark schema redesign - - widening a deferred `V1.1` surface into Rust V1 +Files: -#### Phase 6. Final closeout +- `spec-cli/src/commands.rs` +- `spec-cli/tests/cli.rs` +- `spec-cli/tests/m14_regressions.rs` +- `benchmarks/snapshots/*.snapshot.json` +- `benchmarks/reviews/*.readability.review.json` + +Deliverables: + +- one shared CLI helper for projected unit truth +- `JsonStatusEntry.category_qualification` +- status schema bump `4 -> 5` +- snapshot command emits the same qualification truth as live benchmark output +- service snapshot flips to invalid full benchmark status +- readability freshness remains unchanged by accounting invalidation -Write `.runs/i8/closeout.json` with: +### Phase 5: Final verification sweep -- the final Rust V1 claim -- deferred `V1.1` surfaces -- the five-command verdicts -- raw evidence file references -- any doc files changed for drift repair -- final closeout status: `done` or `blocked` +Goals: -## Proof And Coverage Diagram +- prove all current consumers agree +- prove no consumer-local widening remains -This is the exact I8 proof surface. Every row must be observed and archived. +Done when: + +- benchmark, status, export, and snapshot surfaces tell the same category truth +- all targeted suites pass +- the repo no longer has a full-scope supported benchmark that is invalid at + the case level but still claims overall `passing` + +## Test Diagram ```text -PROOF COMMANDS CLAIM SURFACE -[PASS] cargo run -p spec-cli -- status examples/ecommerce/units --format json - -> BENCH-ECOM positive wall - -> benchmark_status: passing - -> gate_status: satisfied - -> readability_review_status: current - -> archive: .runs/i8/evidence/ecommerce.status.json - -[PASS] cargo run -p spec-cli -- export examples/ecommerce/units - -> schema_version: 4 full projection - -> BENCH-ECOM roster, molecule proofs, readability files visible - -> archive: .runs/i8/evidence/ecommerce.export.json - -[PASS] cargo run -p spec-cli -- status examples/service/units --format json - -> BENCH-SERVICE positive wall - -> benchmark_status: passing - -> gate_status: satisfied - -> readability_review_status: current - -> archive: .runs/i8/evidence/service.status.json - -[PASS] cargo run -p spec-cli -- export examples/service/units - -> schema_version: 4 full projection - -> BENCH-SERVICE roster, molecule proofs, readability files visible - -> archive: .runs/i8/evidence/service.export.json - -[EXPECTED EXIT 1] cargo run -p spec-cli -- status . --format json - -> scope_authority: inventory_only - -> BENCH-CROSSLIB visible as companion_negative_proof - -> positive_credit_cases: 0 for BENCH-CROSSLIB - -> BENCH-ECOM and BENCH-SERVICE still passing in broad projection - -> archive: .runs/i8/evidence/workspace.status.json - -COVERAGE: 5/5 commands -CLAIM PATHS: 5/5 covered -GAPS: 0 command-surface gaps +CODE PATHS +[+] spec-core/src/semantic_review.rs + ├── canonical ecommerce sum -> supported key + descriptor_id + ├── canonical ecommerce data -> supported key + descriptor_id + ├── service sum sibling -> unsupported key + service descriptor_id + └── service data sibling -> unsupported key + service descriptor_id + +[+] spec-core/src/category_truth.rs + ├── registry lookup -> supported canonical row + ├── registry lookup -> unsupported terminal row + ├── supported-row descriptor approval passes + ├── supported-row descriptor approval fails + ├── unsupported terminal row skips descriptor approval + ├── missing semantic review -> unqualified + └── missing descriptor_id -> unqualified + +[+] spec-core/src/benchmark.rs + ├── supported + qualified -> positive credit + ├── supported label + unsupported qualification -> no credit + ├── full benchmark with disqualified supported case -> accounting invalid + └── partial benchmark with disqualified supported case -> partial_invalid + +[+] spec-cli/src/commands.rs / spec-core/src/export.rs + ├── status emits semantic_review + category_qualification + ├── export emits projected_units + category_qualification + └── snapshot emits same benchmark qualification as live projection + +CONSUMER FLOWS +[+] BENCH-ECOM full benchmark + ├── canonical seam cases remain supported_qualified + └── benchmark stays passing + +[+] BENCH-SERVICE full benchmark + ├── service seam cases stay visible + ├── supported label remains visible + ├── category_qualification fails explicitly + └── benchmark flips to invalid/open + +[+] spec status / export / snapshot readers + └── can distinguish supported_qualified vs unsupported_qualified vs unqualified ``` -Interpretation guard: - -- a green I8 does not mean repo-root `status .` exits `0` -- a green I8 does mean the two positive walls pass, the broad inventory surface - still tells the truth, and the checked-in docs match that reality - -## Acceptance Checklist - -I8 closes only when all of these are true at the same time: - -- the plain-English Rust V1 claim in this file still matches: - - `.runs/i7/decision-freeze.json` - - `.runs/i7/i8-handoff.json` - - `DECISIONS.md` - - `docs/rust_v1_contract_stack.md` -- `BENCH-ECOM` remains `passing` with `gate_status: satisfied` -- `BENCH-SERVICE` remains `passing` with `gate_status: satisfied` -- both positive benchmark exports still emit `schema_version: 4` -- `BENCH-CROSSLIB` remains visible in repo-root inventory and still counts as - zero positive supported credit -- repo-root `status . --format json` still reports `scope_authority: - inventory_only` -- no new proof commands were added to make the claim work -- no deferred `V1.1` surface was silently promoted -- no checked-in doc implies a new post-I8 discovery milestone -- `.runs/i8/closeout.json` records the exact final verdict and raw command refs +## Required Test Coverage + +Add or update tests for these exact behaviors: + +- semantic-review descriptor-id projection for canonical supported seams +- semantic-review descriptor-id projection for current service seam siblings +- qualification lookup for all four first-scope rows +- unsupported terminal rows qualify without descriptor approval +- benchmark case projection includes `category_qualification` +- full `BENCH-SERVICE` flips from `passing` to `invalid` +- full `BENCH-ECOM` remains `passing` +- partial benchmark projections use `partial_invalid` without inventing full + benchmark fields +- status JSON schema version bumps and includes `category_qualification` +- export schema version bumps and includes + `projected_units[].category_qualification` +- benchmark snapshots serialize the same case qualification as live output +- legacy persisted semantic review without `descriptor_id` fails qualification + explicitly +- `.spec.passport.json` outputs do **not** persist `category_qualification` + +## Failure Modes Registry + +| Codepath | Production failure | Test coverage required | User-visible effect | Priority | +| --- | --- | --- | --- | --- | +| registry lookup | missing row causes implicit support fallback | unit test on unknown key -> `registry_row_missing` | downstream consumer silently lies unless blocked | P1 | +| descriptor identity | legacy or missing `descriptor_id` gets treated as supported | regression test -> `descriptor_id_missing` | benchmark/status/export over-credit seam support | P1 | +| unsupported terminal routing | unsupported rows incorrectly require a descriptor and become generic unqualified noise | unit test on `unsupported.sum.v1` and `unsupported.data.v1` | readers cannot distinguish unsupported-qualified truth from missing truth | P1 | +| benchmark invalidation | disqualified supported case still leaves full benchmark `passing` | service benchmark contract tests | maintainer believes supported closure is green when it is not | P1 | +| status/export drift | status and export emit different reason codes or claim states | paired fixture assertions in `cli.rs` and `m14_regressions.rs` | downstream tools disagree about the same unit | P1 | +| readability coupling | qualification failure rewrites readability freshness | snapshot tests | readers lose freshness signal and cannot separate style from truth | P2 | +| passport persistence leak | category qualification gets written into `.spec.passport.json` | export/passport regression | on-disk proof state becomes polluted with read-side claims | P2 | + +Critical gap definition for this wedge: + +- any path that can still emit supported positive credit or a supported category + claim without `CategoryQualification == supported_qualified` ## Worktree Parallelization Strategy -I8 has three verification steps but only two should run in parallel. The two -positive walls are independent and cheap to split. The repo-root inventory run -is broader, heavier, and easier to misread before the positive walls are -confirmed, so keep it as a parent-owned follow-up step. - ### Dependency table | Step | Modules touched | Depends on | | --- | --- | --- | -| preflight freeze | `.runs/i8/` | — | -| BENCH-ECOM proof rerun | `examples/ecommerce/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | -| BENCH-SERVICE proof rerun | `examples/service/`, `benchmarks/`, `.runs/i8/evidence/` | preflight freeze | -| broad inventory confirmation | repo root inventory surfaces, `examples/crosslib-app/`, `semantic-families/`, `benchmarks/`, `.runs/i8/evidence/` | BENCH-ECOM proof rerun, BENCH-SERVICE proof rerun | -| authority drift ratification | `PLAN.md`, `ORCH_PLAN.md`, `docs/`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, `TODOS.md` | broad inventory confirmation | -| conditional blocker repair | only the exact modules implicated by a failing command | whichever verification step found the blocker | -| final closeout | `.runs/i8/`, release-note surfaces if changed | authority drift ratification, conditional blocker repair if needed | +| A. Category substrate | `spec-core/src/category_truth.rs`, `spec-core/src/lib.rs`, `spec-core/src/semantic_review.rs` | — | +| B. Benchmark core adoption | `spec-core/src/benchmark.rs`, benchmark-focused tests, benchmark fixtures | A | +| C. Export projection adoption | `spec-core/src/export.rs`, `spec-core/src/passport.rs`, export-focused tests | A | +| D. CLI status and snapshot integration | `spec-cli/src/commands.rs`, `spec-cli/tests/cli.rs`, `spec-cli/tests/m14_regressions.rs`, snapshots/reviews | B, C | ### Parallel lanes -- `Lane A`: BENCH-ECOM proof rerun - - sequential within lane - - owns only `.runs/i8/evidence/ecommerce.*` -- `Lane B`: BENCH-SERVICE proof rerun - - sequential within lane - - owns only `.runs/i8/evidence/service.*` -- `Lane C`: broad inventory confirmation - - parent-owned - - must wait for Lane A and Lane B - - owns only `.runs/i8/evidence/workspace.status.json` -- `Lane D`: authority drift ratification - - parent-owned - - must wait for Lane C - - owns checked-in authority docs only -- `Lane E`: conditional blocker repair - - exists only if a real truth blocker is found - - must stay bounded to the direct failing surface +- Lane A: Step A +- Lane B: Step B after A lands +- Lane C: Step C after A lands +- Lane D: Step D after B and C land ### Execution order -1. Parent creates `.runs/i8/` and freezes inputs. -2. Launch `Lane A` and `Lane B` in parallel worktrees. -3. Parent compares the two positive proof outputs against the frozen claim. -4. Parent runs `Lane C` in the main checkout. -5. Parent launches `Lane D` only after the inventory interpretation is settled. -6. Parent launches `Lane E` only if a real blocker must be repaired. -7. Parent writes the final I8 closeout packet. +1. Launch Lane A first. It owns the substrate and must land before anything + else can qualify claims. +2. After A merges, launch Lanes B and C in parallel worktrees. +3. Merge B and C. +4. Run Lane D last to integrate shared `commands.rs` changes, refresh snapshots, + and freeze final fixtures. ### Conflict flags -- `Lane A` and `Lane B` must not write the same evidence files. -- `Lane A` and `Lane B` may still contend on Cargo package or build locks if - they share the same local cache. That is acceptable, but it means parallel - worktrees improve operator separation more than raw wall-clock time. -- `Lane C` must not start before both positive walls are understood. Otherwise - repo-root inventory can be misread in the absence of benchmark context. -- `Lane D` must not ratify docs before the workspace inventory semantics are - confirmed. -- `Lane E` must not turn a narrow truth repair into a mechanics rewrite. If a - proposed fix touches benchmark schema, proof writers, or support boundaries, - stop and escalate. - -## Failure Modes - -| Failure mode | Consequence | Guard in this plan | -| --- | --- | --- | -| repo-root `status .` exit `1` is misread as I8 failure | a truthful broad inventory surface blocks the release for the wrong reason | require `scope_authority: inventory_only` and benchmark interpretation, not exit-code-only reasoning | -| `BENCH-CROSSLIB` starts counting as positive credit | the V1 claim widens by read-surface drift | require zero positive credit and preserve companion-negative classification | -| positive `status` passes but `export` drifts | downstream machine consumers see a different truth than human readers | require both `status` and `export` for each positive benchmark | -| readability anchors go stale while benchmark status stays green | the plan overstates the reviewability of emitted Rust | require `readability_review_status: current` for both positive walls | -| docs still imply I7 is current or imply I9 exists | milestone ownership becomes ambiguous again | compare every repo-facing authority surface after live reruns, not before | -| a blocker fix adds new proof commands or support rows | I8 quietly becomes another scope milestone | hard-stop any repair that changes the five-command wall or deferred boundaries | -| broad inventory roots are trimmed to force green | the repo loses honest visibility into deferred and fixture surfaces | preserve repo-root inventory behavior exactly | - -Critical gap test: - -- if either positive benchmark loses `passing` or `satisfied`, I8 is not done -- if any authority doc still needs caveats not present in the frozen claim, I8 - is not done -- if the only way to "pass" is to reinterpret `inventory_only` as proof, I8 is - not done - -## Performance And Operational Review - -I8 should not introduce any new runtime or infrastructure cost. The only -meaningful operational concerns are: - -- repo-root `status` is a heavier scan than benchmark-root commands, so do not - treat it as a cheap loop -- raw command outputs must be captured once per closeout run so later doc review - does not depend on rerunning the wall from memory -- parallelization is helpful only for the two positive walls; forcing more - concurrency buys little and increases interpretation risk -- concurrent Cargo invocations may wait on package or build locks; that is not - a blocker, but it means the parent should prefer clarity of ownership over - chasing marginal runtime wins -- no new caching, queueing, proof-writer parallelism, or benchmark-registry - work is justified here - -Boring-by-default rule: - -- if the wall passes, ship the closeout with evidence -- if the wall fails, repair the narrow blocker only +- Lanes B and D both affect benchmark-facing projection behavior, but only D + should touch the final shared CLI truth helper +- Lanes C and D both affect read-side contract surfaces +- Do **not** run B and D in parallel +- Do **not** run C and D in parallel +- Keep `commands.rs` ownership in Lane D to minimize merge conflict risk + +If worktree staffing is unavailable, run the same order sequentially. ## Implementation Tasks -- [ ] **T1 (P1, human: ~15m / CC: ~5m)** — preflight freeze — create `.runs/i8/` - and record the frozen inputs, branch, commit, plain-English claim, exact - five-command wall, and bounded artifact map. - - Verify: `.runs/i8/preflight.json` names the same authority inputs and evidence paths listed in this plan. -- [ ] **T2 (P1, human: ~15m / CC: ~5m)** — BENCH-ECOM proof rerun — rerun the - ecommerce `status` and `export` commands and archive raw outputs to - `.runs/i8/evidence/ecommerce.*`. - - Verify: `BENCH-ECOM` stays `passing/satisfied/current` and the export remains `schema_version: 4`. -- [ ] **T3 (P1, human: ~15m / CC: ~5m)** — BENCH-SERVICE proof rerun — rerun the - service `status` and `export` commands and archive raw outputs to - `.runs/i8/evidence/service.*`. - - Verify: `BENCH-SERVICE` stays `passing/satisfied/current` and the export remains `schema_version: 4`. -- [ ] **T4 (P1, human: ~10m / CC: ~5m)** — broad inventory confirmation — rerun - repo-root `status . --format json`, archive it, and confirm the expected - `inventory_only` interpretation plus companion-negative visibility. - - Verify: `BENCH-CROSSLIB` remains visible with zero positive credit and repo-root `status` remains non-green by design. -- [ ] **T5 (P1, human: ~30m / CC: ~10m)** — authority drift ratification — - compare the live outputs against `PLAN.md`, `ORCH_PLAN.md`, - `docs/rust_v1_contract_stack.md`, `README.md`, `DECISIONS.md`, `CHANGELOG.md`, - and `TODOS.md`, then patch only real drift. - - Verify: every checked-in authority surface teaches the same I8 story with no implied `I9`. -- [ ] **T6 (P1, human: ~20m / CC: ~10m)** — final closeout packet — write - `.runs/i8/closeout.json` with the final claim, deferred surfaces, command - verdicts, doc-drift summary, and references to raw evidence files. - - Verify: a future maintainer can reconstruct the exact I8 decision from `.runs/i8/` without relying on conversation context. -- [ ] **T7 (P1, human: ~variable / CC: ~variable)** — conditional blocker repair - — if any proof command or authority surface contradicts the frozen claim, fix - the direct blocker only and rerun the affected command plus the final - five-command wall. - - Verify: no repair widens support, changes benchmark roles, or adds new proof commands. - -## NOT in scope - -- bounded generics admission - - rationale: I7 already deferred this to `V1.1` -- async or IO admission - - rationale: I7 already froze Rust V1 as synchronous-only -- benchmark schema redesign - - rationale: M67 and M68 already own benchmark roles and mechanics -- new benchmark roots or new benchmark kinds - - rationale: the I8 claim closes over the existing three-benchmark roster -- repo-root `export .` support - - rationale: this workspace shape still truthfully rejects aggregate export -- turning repo-root `status .` into a globally green ship gate - - rationale: broad inventory is intentionally wider than the positive proof wall -- reopening I3.5 command-wall semantics - - rationale: I8 consumes that wall as frozen authority -- inventing a checked-in `I9` - - rationale: the active ladder ends at I8 until a new bounded post-V1 milestone exists - -## Immediate Next Move - -Execute I8 in this order: - -1. create `.runs/i8/` and freeze the exact input set -2. rerun and archive the BENCH-ECOM proof wall -3. rerun and archive the BENCH-SERVICE proof wall -4. rerun and archive repo-root inventory status -5. compare live outputs against the frozen claim and repo-facing docs -6. patch only real drift -7. write the final closeout packet - -Do not start by changing code. Start by proving whether the already-ratified -claim still holds on the live branch. +Synthesized from the architecture, test, and failure-mode requirements above. + +- [ ] **T1 (P1, human: ~2h / CC: ~15min)** — category substrate — add + `spec-core/src/category_truth.rs` with the first-scope registry rows, + qualification enums, and `qualify_category_claim(...)` + - Files: `spec-core/src/category_truth.rs`, `spec-core/src/lib.rs` + - Verify: `cargo test -p spec-core category_truth` +- [ ] **T2 (P1, human: ~1.5h / CC: ~10min)** — semantic-review producer truth — + extend seam semantic-review projection with producer-owned `descriptor_id` + and lock it with unit tests + - Files: `spec-core/src/semantic_review.rs` + - Verify: `cargo test -p spec-core semantic_review` +- [ ] **T3 (P1, human: ~2h / CC: ~15min)** — benchmark qualification — + thread `semantic_review` into benchmark case truth, add + `category_qualification`, and make positive credit plus full benchmark + validity depend on qualification + - Files: `spec-core/src/benchmark.rs` + - Verify: `cargo test -p spec-core benchmark` +- [ ] **T4 (P1, human: ~1h / CC: ~10min)** — benchmark contract refresh — + update `BENCH-SERVICE` tests and fixtures to assert + `accounting_status = invalid`, `benchmark_status = invalid`, + `gate_status = open` + - Files: `spec-cli/tests/rust_v1_service.rs`, + `spec-cli/tests/rust_v1_closure.rs`, + `spec-cli/tests/fixtures/benchmarks/*.json` + - Verify: `cargo test -p spec-cli rust_v1_service rust_v1_closure` +- [ ] **T5 (P1, human: ~1.5h / CC: ~10min)** — export contract — + add `projected_units[]`, surface shared qualification there, and bump export + schema version to `5` + - Files: `spec-core/src/export.rs`, `spec-core/src/passport.rs` + - Verify: `cargo test -p spec-core export` +- [ ] **T6 (P1, human: ~2h / CC: ~15min)** — CLI read-side integration — + add one shared projected-truth helper, surface `category_qualification` in + status JSON, and keep snapshots aligned with live benchmark output + - Files: `spec-cli/src/commands.rs`, `spec-cli/tests/cli.rs`, + `spec-cli/tests/m14_regressions.rs` + - Verify: `cargo test -p spec-cli cli m14_regressions` +- [ ] **T7 (P2, human: ~45min / CC: ~5min)** — snapshot and readability freeze — + refresh benchmark snapshots and readability fixtures so live and frozen + projections stay in sync without rewriting readability freshness semantics + - Files: `benchmarks/snapshots/*.snapshot.json`, + `benchmarks/reviews/*.readability.review.json` + - Verify: `cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM` and + `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` + +## Acceptance Criteria + +This plan is complete only when all of the following are true: + +1. the repo has one authoritative category truth registry in `spec-core` +2. every current seam-category consumer calls the same qualification function +3. positive benchmark credit is impossible without + `supported_qualified + eligible` +4. a supported-labeled but producer-unqualified full benchmark becomes + `invalid`, not `passing` +5. `spec status` and `spec export` both expose additive + `category_qualification` +6. export keeps category qualification read-side only and does not persist it + into `.spec.passport.json` +7. snapshot output matches live benchmark qualification output +8. the current `BENCH-SERVICE` mismatch is explicit contract truth, not repo + folklore + +## Verification Commands + +Run at minimum: + +```bash +cargo test -p spec-core +cargo test -p spec-cli rust_v1_service +cargo test -p spec-cli rust_v1_closure +cargo test -p spec-cli m14_regressions +cargo test -p spec-cli cli + +cargo run -p spec-cli -- status examples/ecommerce/units --format json +cargo run -p spec-cli -- status examples/service/units --format json +cargo run -p spec-cli -- export examples/ecommerce/units +cargo run -p spec-cli -- export examples/service/units +cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM +cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE +``` + +Expected end state: + +- ecommerce full benchmark remains passing +- service full benchmark becomes invalid/open +- status and export both expose the same category qualification for seam rows +- snapshots match live benchmark projections +- `.spec.passport.json` files remain free of persisted `category_qualification` + +## Deferred Follow-On Work + +After this wedge lands, separate follow-on work may decide whether to: + +- widen producer routing so current service seam descriptors become supported +- tighten `BENCH-SERVICE` labels so they stop asking for supported claims the + producer does not grant +- externalize the registry for non-Rust consumers +- expand category truth beyond the first four seam rows + +Those are real follow-ons. They are explicitly **not** prerequisites for this +plan. diff --git a/docs/category_truth_contract_v0.1.md b/docs/category_truth_contract_v0.1.md index 5909435..bc4926c 100644 --- a/docs/category_truth_contract_v0.1.md +++ b/docs/category_truth_contract_v0.1.md @@ -52,6 +52,11 @@ The registry must still be machine-readable: - a file-based external registry can come later if non-Rust consumers need a direct checked-in data artifact +This wedge does **not** let benchmark labels, stored passports, or other +consumer-local metadata widen producer-owned support truth. A case may still be +labeled `classification: supported` at the benchmark layer and yet remain +category-unqualified when producer truth does not support that claim. + ## Why This Is The Right Scope The producer boundary already exists: @@ -81,6 +86,7 @@ This wedge does not: - promote new categories - widen seam support beyond the currently shipped four-category first scope - add a new public file format for external tools +- rewrite `BENCH-SERVICE` labels to align with producer truth in the same wedge - resolve the broader seam-substrate cleanup by itself The broader seam-substrate ambiguity stays visible. This wedge makes consumers @@ -153,8 +159,12 @@ Examples: supported or positive-credit truth. 5. Transitional ambiguity must be named. - The current sum-sibling mismatch is preserved as explicit contract truth, - not hidden behind vague wording. + The current benchmark-label-versus-producer mismatch is preserved as + explicit contract truth, not hidden behind vague wording. + +6. Producer truth outranks benchmark labels. + Benchmark `classification` is consumer input, not category authority. It may + request a supported claim, but only qualification may grant one. ## Core Design @@ -178,6 +188,7 @@ pub struct CategoryTruthRow { pub contract_support_status: ContractSupportStatus, pub producer_surface: ProducerSurface, pub alias_sibling_policy: AliasSiblingPolicy, + pub descriptor_set: DescriptorSet, pub positive_credit_policy: PositiveCreditPolicy, pub consumer_requirements: ConsumerRequirements, pub notes: &'static str, @@ -203,22 +214,32 @@ The function must: 1. resolve the registry row from `semantic_review.compatibility_key` 2. reject qualification when semantic review is missing 3. reject qualification when the registry row is missing -4. require the review's `effective_support_status()` to match the registry row's +4. resolve the producer-owned `descriptor_id` from the same semantic projection + truth that produced the compatibility key +5. require that `descriptor_id` to match either: + - `descriptor_set.canonical_descriptor.descriptor_id` + - or one member of `descriptor_set.approved_siblings[]` +6. require the review's `effective_support_status()` to match the registry row's `contract_support_status` -5. apply consumer-specific rules for positive credit or read-side claim display -6. return one stable result object +7. apply consumer-specific rules for positive credit or read-side claim display +8. return one stable result object Suggested result shape: ```rust pub struct CategoryQualification { pub category_id: Option, + pub descriptor_id: Option, pub claim_status: ClaimStatus, pub positive_credit_eligibility: PositiveCreditEligibility, pub reason_code: QualificationReasonCode, } ``` +This object should be the shared serializable projection shape reused across +benchmark cases, `spec status`, and `spec export`. The wedge should not invent +slightly different per-surface field bundles for the same concept. + With stable enums: - `ClaimStatus` @@ -231,6 +252,8 @@ With stable enums: - `QualificationReasonCode` - `semantic_review_missing` - `registry_row_missing` + - `descriptor_id_missing` + - `descriptor_not_approved` - `support_status_mismatch` - `consumer_requirement_failed` - `positive_credit_disallowed` @@ -241,6 +264,45 @@ The key rule is simple: > if qualification does not return an explicit qualified result, the consumer > must not invent one +### Producer-owned descriptor identity + +`descriptor_set` is an active contract boundary, not metadata. + +So the first wedge must carry one extra producer-owned identity at +qualification time: + +- `descriptor_id` + +This is a stable per-unit descriptor identity derived from the same producer +truth that already yields the compatibility key. Consumers do not invent it. + +First-wedge source of truth: + +- seam producer logic in `spec-core/src/semantic_review.rs` (or a minimal + helper extracted from it) must emit both: + - `compatibility_key` + - `descriptor_id` + +Bounded implementation rule: + +- do **not** redesign the whole semantic review schema +- do add a producer-owned way for the qualification path to obtain + `descriptor_id` from the semantic projection result or a tightly-coupled + helper in `spec-core` + +Acceptable first-wedge implementation shapes: + +1. extend projected semantic truth to carry `descriptor_id` +2. derive `descriptor_id` in `qualify_category_claim(...)` by calling one + producer-owned helper on the same unit/spec context that produced semantic + review + +The required invariant is the same either way: + +> `descriptor_id` must come from producer-owned truth in `spec-core`, not from +> benchmark labels, export readers, status formatting, or other consumer-local +> interpretation + ## Registry Schema The first wedge uses this exact vocabulary. @@ -292,9 +354,45 @@ Allowed values in this wedge: The category is an observation-only unsupported sink. It is never support-bearing or positive-credit eligible. -The sum seam starts as `approved_sibling_extension`. -The data seam starts as `canonical_only`. -Both unsupported seam rows are `unsupported_terminal`. +In the first implementation-ready wedge: + +- supported rows start as `canonical_only` +- unsupported seam rows are `unsupported_terminal` +- `approved_sibling_extension` remains reserved for a later producer-backed + widening, not for this first landing + +`alias_sibling_policy` is only the coarse classification. It is not enough on +its own for machine use. Every supported row that is not `canonical_only` must +also carry an explicit `descriptor_set` so consumers and tests can read the +allowed sibling boundary without relying on prose notes. + +### `descriptor_set` + +This field is the machine-readable sibling boundary. + +Suggested shape: + +```json +{ + "canonical_descriptor": { + "descriptor_id": "pricing_quote.ecommerce.v1", + "representative_unit_id": "pricing/pricing_quote" + }, + "approved_siblings": [] +} +``` + +First wedge rules: + +- `canonical_only` rows must still declare `canonical_descriptor` +- `approved_sibling_extension` rows must declare both + `canonical_descriptor` and every approved sibling entry +- `unsupported_terminal` rows use an empty descriptor set +- qualification must fail unless the producer-owned `descriptor_id` matches the + canonical descriptor or one approved sibling entry +- no consumer may widen beyond `descriptor_set.approved_siblings[]` +- prose notes may explain why a sibling exists, but the sibling boundary itself + must be derivable from the registry alone ### `positive_credit_policy` @@ -336,44 +434,69 @@ This is the contract that closes the recent bug class. The first adoption scope is intentionally small. -| Category | Kind | Contract support | Alias/sibling policy | Positive credit | -| --- | --- | --- | --- | --- | -| `sum.discount_strategy.v1` | `sum` | `supported` | `approved_sibling_extension` | eligible | -| `data.pricing_quote.v1` | `data` | `supported` | `canonical_only` | eligible | -| `unsupported.sum.v1` | `sum` | `unsupported` | `unsupported_terminal` | ineligible | -| `unsupported.data.v1` | `data` | `unsupported` | `unsupported_terminal` | ineligible | +| Category | Kind | Contract support | Alias/sibling policy | Approved sibling ids | Positive credit | +| --- | --- | --- | --- | --- | --- | +| `sum.discount_strategy.v1` | `sum` | `supported` | `canonical_only` | none | eligible | +| `data.pricing_quote.v1` | `data` | `supported` | `canonical_only` | none | eligible | +| `unsupported.sum.v1` | `sum` | `unsupported` | `unsupported_terminal` | none | ineligible | +| `unsupported.data.v1` | `data` | `unsupported` | `unsupported_terminal` | none | ineligible | ## First-Scope Policy Notes ### `sum.discount_strategy.v1` -This row is the one explicit transitional nuance. - Current repo truth says: - the canonical semantic-review wording is still narrow around `none` / `percentage` / `fixed_amount` -- broader checked-in repo surfaces already treat the service sibling - (`declined` / `percentage` / `fixed_credit`) as part of the same category +- the service seam sibling (`declined` / `percentage` / `fixed_credit`) is + still routed by producer truth to `unsupported.sum.v1` +- `BENCH-SERVICE` still labels `billing/discount_strategy` as + `classification: supported`, but current benchmark fixtures already deny it + positive credit because semantic-review truth is unsupported So the first registry row must record: - `contract_support_status = supported` -- `alias_sibling_policy = approved_sibling_extension` -- `notes = transitional canonical/service sibling mismatch; do not widen beyond - the shipped sibling set` +- `alias_sibling_policy = canonical_only` +- `descriptor_set.canonical_descriptor.descriptor_id = + discount_strategy.ecommerce.v1` +- `descriptor_set.approved_siblings = []` +- `notes = canonical ecommerce descriptor only; service sibling remains visible + but unqualified until producer truth is widened explicitly` -This is not permission for consumers to invent new siblings. It is permission -for consumers to respect the one shipped sibling extension the repo already -claims. +This is intentional. The first wedge is about honest read-side qualification, +not about retroactively upgrading an unsupported producer surface into +supported category truth. ### `data.pricing_quote.v1` -This row is simpler: +Current repo truth says: + +- the canonical semantic-review detector is still narrow around + `subtotal` / `discount_rate` / `tax_rate` +- the service seam uses + `subtotal` / `membership_rate` / `regional_rate` +- producer truth still routes that service seam to `unsupported.data.v1` +- `BENCH-SERVICE` still labels `billing/pricing_quote` as + `classification: supported`, but current benchmark fixtures already deny it + positive credit because semantic-review truth is unsupported + +So the first registry row must record: + +- `contract_support_status = supported` +- `alias_sibling_policy = canonical_only` +- `descriptor_set.canonical_descriptor.descriptor_id = + pricing_quote.ecommerce.v1` +- `descriptor_set.approved_siblings = []` +- `notes = canonical ecommerce descriptor only; service sibling remains visible + but unqualified until producer truth is widened explicitly` + +This keeps the authority chain coherent: -- exact bounded descriptor -- no approved sibling extension in the first wedge -- positive-credit eligible when fully qualified +- semantic review stays the producer-owned router +- the registry qualifies only the categories producer truth actually projected +- benchmark labels stay visible but cannot overrule unsupported producer truth ### `unsupported.sum.v1` and `unsupported.data.v1` @@ -386,6 +509,26 @@ They must stay: - qualification-bearing as unsupported only - never positive-credit eligible +### Current service benchmark mismatch + +The repo currently has a real split that this wedge must make explicit instead +of papering over: + +- `benchmarks/labels.json` marks `billing/discount_strategy` and + `billing/pricing_quote` as `classification: supported` +- producer-owned semantic review still projects those seam units as + `unsupported.sum.v1` and `unsupported.data.v1` +- frozen `BENCH-SERVICE` status/export fixtures therefore already show + `counts_as_supported_positive = false` for both carriers + +The first wedge must preserve that checked-in truth honestly: + +- the benchmark label remains visible +- the unit remains visible +- the category claim becomes explicitly `unqualified` or + `unsupported_qualified`, depending on surface +- no new registry row rescues the claim into supported truth + ## Consumer Behavior Contract ### Benchmark accounting @@ -405,18 +548,54 @@ Current positive-credit logic already checks: The new rule is stricter: +- `classification: supported` is necessary input for positive benchmark credit, + but it is never sufficient authority on its own - `counts_as_supported_positive` must require `CategoryQualification { claim_status: supported_qualified, positive_credit_eligibility: eligible, ... }` +- that qualified result must already have passed descriptor approval against the + registry row's `descriptor_set` + +Additive projection should carry the shared qualification object per case, for +example as: + +```json +{ + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + } +} +``` + +For the current service seam mismatch, the same projection stays visible but +reports an unqualified or unsupported result instead of silently relying on the +benchmark label. If qualification fails: - positive credit becomes `false` - the case stays visible - the failure reason is preserved in qualification output -- benchmark accounting status should degrade to non-clean when a case labeled - `supported` cannot qualify - -This keeps honesty stronger than zero-credit-only silence. +- if the benchmark projection is `full`, `accounting_status` becomes `invalid` +- if the benchmark projection is `partial`, `accounting_status` becomes + `partial_invalid` +- `benchmark_status` follows the existing checked-in benchmark semantics: + because `determine_benchmark_status(...)` treats `accounting_status = invalid` + as terminal, the full projection becomes `benchmark_status = invalid` +- `gate_status` remains `open` +- `readability_review_status` does **not** get rewritten by qualification + failure; it stays whatever the readability artifact truth already says + (`current`, `stale`, `missing`, or `not_applicable`) +- partial projections keep the current checked-in shape: they surface the + `partial_invalid` accounting result, but do not invent full-scope + `benchmark_status`, `gate_status`, or `readability_review_status` fields + +This keeps honesty stronger than zero-credit-only silence while staying aligned +to the current benchmark status model. The wedge should not overload +`readability_review_status` into a second benchmark-accounting signal. ### `spec status` @@ -431,12 +610,16 @@ First wedge behavior: - preserve existing health logic - preserve projected semantic review -- add additive qualification output for read-side consumers +- add the shared additive `category_qualification` output for read-side + consumers - if qualification fails, show the unit as semantically present but category claim unqualified This is a read-side honesty improvement, not a health-model rewrite. +Because `spec status --format json` is a published machine surface, the wedge +should bump `STATUS_JSON_SCHEMA_VERSION` when this object is added. + ### `spec export` Target surfaces: @@ -454,6 +637,12 @@ Reason: "this category claim is qualified" versus "this row exists but is not support-bearing" +The first wedge should keep qualification as read-side projected truth only: + +- do **not** persist category qualification into passports +- do **not** add benchmark-local or export-local category vocabularies +- do bump `EXPORT_SCHEMA_VERSION` when the shared qualification object is added + ### Readability and snapshot surfaces Target surfaces: @@ -466,6 +655,18 @@ projection. No separate snapshot-local support logic is allowed. +First wedge projection rule: + +- snapshot and export/readability surfaces must preserve the same per-case + qualification output as live benchmark projection +- a supported-labeled case that fails qualification must appear with + `counts_as_supported_positive = false` +- the enclosing full benchmark projection must show + `accounting_status = invalid`, `benchmark_status = invalid`, and + `gate_status = open` +- `readability_review_status` remains a freshness/applicability signal only and + must not be rewritten to encode qualification failure + ## Migration Plan ### Phase 1 — Add shared contract substrate @@ -474,14 +675,19 @@ Files: - `spec-core/src/category_truth.rs` (new) - `spec-core/src/lib.rs` or module exports +- minimal producer-owned descriptor-id hook in `spec-core` so qualification can + enforce `descriptor_set` Deliverables: - registry structs and enums - first four rows - `qualify_category_claim(...)` +- producer-owned `descriptor_id` resolution for first-scope seam categories - stable qualification reason codes - unit tests for registry lookup and qualification behavior +- explicit tests that current service seam descriptors do **not** qualify as + supported in the first wedge ### Phase 2 — Migrate benchmark accounting first @@ -498,6 +704,9 @@ Deliverables: - benchmark positive credit gated through qualification - category qualification echoed in case projection output - explicit regression coverage for the dishonesty bug class +- explicit regression coverage that `BENCH-SERVICE` seam cases remain visible, + labeled `supported`, and still do not earn positive credit while producer + truth stays unsupported ### Phase 3 — Migrate `spec status` and `spec export` @@ -510,10 +719,12 @@ Files: Deliverables: -- additive qualification output on status/export surfaces +- additive shared `category_qualification` output on status/export surfaces - status and export consume the same registry and reason codes - tests prove supported seam rows qualify and unsupported seam rows remain observation-only +- tests prove status/export surface the service benchmark mismatch explicitly + rather than silently collapsing it ### Phase 4 — Freeze snapshot/readability parity @@ -549,6 +760,9 @@ The first implementation should stay near this boundary: This wedge should not sprawl into unrelated family-analysis or corpus files. +It also should not mutate passport on-disk schema in the first landing. The new +truth is read-side projected, not persisted proof state. + ## Proof Plan The implementation is done only when all of these are true. @@ -558,6 +772,10 @@ The implementation is done only when all of these are true. - lookup succeeds for all four first-scope category ids - lookup fails cleanly for unknown category ids - unsupported seam rows never report positive-credit eligibility +- producer-owned descriptor resolution emits the canonical descriptor id for the + ecommerce seam exemplars +- service seam exemplars do not qualify for supported rows while producer truth + still routes them through unsupported surfaces ### Benchmark proof @@ -565,20 +783,35 @@ The implementation is done only when all of these are true. credit and a qualification failure reason - a supported benchmark case with unsupported semantic truth gets zero positive credit +- a supported benchmark case with missing producer-owned `descriptor_id` gets + zero positive credit and `descriptor_id_missing` +- a supported benchmark case with the same compatibility key but an unapproved + descriptor id gets zero positive credit and `descriptor_not_approved` +- a supported benchmark case with category qualification failure forces: + - full projection `accounting_status = invalid` + - full projection `benchmark_status = invalid` + - full projection `gate_status = open` + - unchanged `readability_review_status` - a fully qualified supported seam case still earns positive credit - snapshot output matches live projection output ### Status/export proof -- supported seam rows show qualified support +- supported seam rows show qualified support only when their producer-owned + descriptor id is approved by the row's `descriptor_set` - unsupported seam rows show qualified unsupported observation +- canonical descriptor qualifies +- current service seam siblings do not qualify for supported rows +- same compatibility key with unapproved descriptor does not qualify - missing registry or missing semantic review yields `unqualified`, not implicit support ### Sibling-policy proof -- `sum.discount_strategy.v1` is the only first-scope row allowed to use - `approved_sibling_extension` +- supported rows with `approved_sibling_extension` must declare a non-empty + `descriptor_set.approved_siblings[]` +- no first-scope row uses `approved_sibling_extension` until producer truth + explicitly widens - no consumer may widen beyond the registry row's explicit policy ## Acceptance Criteria @@ -590,16 +823,21 @@ This wedge is complete when: qualification function 3. positive benchmark credit is impossible without explicit qualified support 4. the four first-scope categories have stable registry rows and tests -5. the sum sibling mismatch is explicit contract truth instead of hidden lore -6. no first-scope consumer infers support from health, labels, or +5. the current `BENCH-SERVICE` label-vs-producer mismatch is explicit contract + truth instead of hidden lore +6. `descriptor_set` is actively enforced through producer-owned descriptor + identity rather than treated as documentation-only metadata +7. no first-scope consumer infers support from health, labels, or compatibility-key folklore alone ## Deferred Follow-On Work After this wedge lands, the next follow-on can decide whether to: -- normalize the canonical `sum.discount_strategy.v1` detector wording to match - the shipped sibling truth +- widen producer-owned semantic routing so specific service seam descriptors + become truly supported +- tighten `BENCH-SERVICE` labels so they stop asking for supported claims that + producer truth does not currently grant - externalize the registry into a checked-in JSON artifact for non-Rust consumers - expand the registry beyond the first four seam categories From 730460f393b078fc14706eea37ed8ed2ab9dd9c1 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 15:44:04 +0000 Subject: [PATCH 18/24] Rename plan milestone to M101 --- ORCH_PLAN.md | 3 ++- PLAN.md | 7 ++++--- TODOS.md | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ORCH_PLAN.md b/ORCH_PLAN.md index c39c245..189e597 100644 --- a/ORCH_PLAN.md +++ b/ORCH_PLAN.md @@ -1,6 +1,7 @@ -# Category Truth Orchestration Plan +# M101 Orchestration Plan Status: **authoritative execution runbook for the current `PLAN.md` wedge** +Milestone: **M101** Plan authority: **`/home/azureuser/__Active_Code/atomize-hq/spec/PLAN.md`** Historical context only: **prior `ORCH_PLAN.md` content is superseded in full** Primary workspace: **`/home/azureuser/__Active_Code/atomize-hq/spec`** diff --git a/PLAN.md b/PLAN.md index 959745e..e09b600 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,6 +1,7 @@ -# Category Truth Registry and Consumer Qualification Plan +# M101: Category Truth Registry and Consumer Qualification Plan Status: **authoritative implementation plan** +Milestone: **M101** Implementation readiness: **ready to execute** Plan scope: **land one explicit category-truth contract for seam-backed support claims across benchmark accounting, `spec status`, `spec export`, and snapshot/readability projections** Base branch: **main** @@ -15,7 +16,7 @@ Supersedes: Primary authority inputs: - design anchor: `docs/category_truth_contract_v0.1.md` -- backlog anchor: `TODOS.md` under `Post-I8 truth-contract follow-up` +- backlog anchor: `TODOS.md` under `M101 backlog` - current benchmark registry: `benchmarks/labels.json` - current producer and consumer code: - `spec-core/src/semantic_review.rs` @@ -159,7 +160,7 @@ plan. ### TODOS cross-reference This plan is the executable version of the active backlog item already recorded -in `TODOS.md` under `Post-I8 truth-contract follow-up`. It should land the +in `TODOS.md` under `M101 backlog`. It should land the implementation wedge, not create a second overlapping TODO. ### Completeness check diff --git a/TODOS.md b/TODOS.md index 36bfcb9..560a2b6 100644 --- a/TODOS.md +++ b/TODOS.md @@ -1,6 +1,6 @@ ## TODOs for M1 (Release 0.1) -## Post-I8 truth-contract follow-up +## M101 backlog - [ ] **Land the category truth contract implementation wedge** — The design anchor is [`docs/category_truth_contract_v0.1.md`](./docs/category_truth_contract_v0.1.md). The failure class is broader than one benchmark bug: read-side consumers must not infer supported category status or positive benchmark credit from partial truth. First adoption scope should cover `sum.discount_strategy.v1`, `data.pricing_quote.v1`, `unsupported.sum.v1`, and `unsupported.data.v1`, with benchmark accounting, `spec status`, `spec export`, and readability/snapshot surfaces consuming the same explicit contract. From 9c7ee83ec2b2eabf5f981a5f2dcd2ea5d094e695 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 18:59:55 +0000 Subject: [PATCH 19/24] Add correction note for M101 category truth respin --- docs/README.md | 2 + ...category_truth_contract_correction_v0.1.md | 180 ++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 docs/category_truth_contract_correction_v0.1.md diff --git a/docs/README.md b/docs/README.md index ed4c3ee..98b8b02 100644 --- a/docs/README.md +++ b/docs/README.md @@ -32,6 +32,8 @@ and durable decisions aligned. The detailed `kind:sum` taxonomy. Use this when you need the exact supported sum descriptor, its benchmark role, and the current split between canonical detector wording and the broader service sibling surface. - [`category_truth_contract_v0.1.md`](./category_truth_contract_v0.1.md) The cross-cutting honesty contract for category-backed read-side claims. Use this when you need to know how benchmark, status, export, and future consumers should decide support and positive-credit eligibility without inferring from partial truth. +- [`category_truth_contract_correction_v0.1.md`](./category_truth_contract_correction_v0.1.md) + The corrective note for M101. Use this when you need the explicit decision that category qualification must consume stored semantic truth rather than minting fresh read-side category truth. - [`examples/ecommerce/README.md`](../examples/ecommerce/README.md) The canonical concrete walkthrough. Use it when you want one real example root to run and inspect. - [`AGENTS.md`](../AGENTS.md) diff --git a/docs/category_truth_contract_correction_v0.1.md b/docs/category_truth_contract_correction_v0.1.md new file mode 100644 index 0000000..fef1d31 --- /dev/null +++ b/docs/category_truth_contract_correction_v0.1.md @@ -0,0 +1,180 @@ +# spec — Category Truth Contract Correction +**Version:** v0.1 +**Status:** Corrective design note +**Date:** 2026-05-25 + +## Purpose + +This note records the contract correction discovered while reviewing +`feat/m101-category-truth-registry`. + +The category-truth milestone direction is still right: + +- shared registry-owned category truth +- one shared qualification contract +- benchmark, status, export, and snapshot consumers using the same rules + +But the current M101 implementation shape is not safe to land as-is. + +It tightened benchmark honesty, then crossed the producer/consumer boundary by +letting read-side surfaces recompute fresh seam category truth. + +This note makes the correction explicit so the repo can keep the good parts of +M101 without silently changing the semantic-truth contract. + +## Decision + +The repo should keep the **stored-truth strict** model. + +That means: + +1. `semantic_review` remains producer-owned truth. +2. Only `spec test` may refresh `semantic_review`. +3. `category_qualification` is consumer-side interpretation of already-stored + semantic truth. +4. `spec status`, `spec export`, benchmark projection, snapshot projection, and + readability projection are consumers, not parallel truth producers. + +The repo should **not** introduce a live read-side semantic/category truth plane +inside M101. + +If the product ever needs live analysis distinct from passported proof truth, +that must be a later, explicitly named architecture move with its own surface +and contract. + +## What Went Wrong In M101 + +The design in [`category_truth_contract_v0.1.md`](./category_truth_contract_v0.1.md) +correctly identified the failure class: + +> consumers must not infer support or positive credit from partial truth + +But the first implementation appears to have solved that partly by letting +`status` and `export` derive category-bearing truth from refreshed semantic +projection on read-only paths. + +That creates a new failure class: + +> a read-side consumer becomes a second semantic/category truth producer + +This is the wrong trade. + +It makes the system look more honest at the benchmark layer while quietly +weakening the repo's deeper truth boundary. + +## Why The Stored-Truth Model Must Win + +The repo already has a durable rule: + +- passports store semantic review truth from the last proof refresh +- only `spec test` refreshes semantic review truth +- `spec build`, `spec generate`, `spec status`, and `spec export` project + stored truth + +That boundary is valuable because it keeps one answer to: + +- where semantic truth comes from +- when it changed +- what evidence anchored it + +If M101 lets `status` or `export` recompute fresh category-bearing truth, the +repo no longer has one answer. It has two: + +1. proof-time semantic truth +2. read-time semantic/category truth + +That is precisely the kind of split that created earlier honesty bugs. + +## Correction To The Contract Doc + +The contract in [`category_truth_contract_v0.1.md`](./category_truth_contract_v0.1.md) +should be read more narrowly than the first implementation did. + +The intended rule must be: + +- `category_qualification` may be read-side projected +- but only from preserved/passported `semantic_review` +- not from `SemanticProjectionMode::Refresh` on `status` / `export` +- not from missing-passport synthetic review + +In other words: + +**read-side projected** does not mean **freshly recomputed semantic truth**. + +It means: + +**shared consumer interpretation of already-produced truth**. + +## Required Implementation Correction + +The corrective respin should preserve these M101 ideas: + +- the registry substrate in `spec-core` +- stable category qualification enums and reason codes +- benchmark positive-credit gating through qualification +- explicit service-sibling non-qualification in the first wedge + +The corrective respin should drop or rewrite these parts: + +- any `status` path where refreshed semantic projection feeds + `category_qualification` +- any `export` path where refreshed semantic projection feeds + `category_qualification` +- any path where a missing passport can still yield + `supported_qualified` +- any benchmark/snapshot input path that consumes refreshed seam semantic truth + instead of preserved truth + +## Concrete Respin Plan + +Start from the safer pre-M101 line on `feat/i8-final-proof-run` and rebuild the +milestone narrowly. + +### Phase 1 + +Keep the benchmark honesty fix and the shared category-truth substrate. + +### Phase 2 + +Apply qualification at benchmark projection only from preserved semantic truth. + +### Phase 3 + +Add additive `category_qualification` to `status` and `export`, but derive it +only from preserved/passported semantic truth. + +### Phase 4 + +Re-freeze snapshot and readability parity using the same preserved-truth input. + +## Acceptance Criteria + +The corrected M101 is done only when all of these are true: + +1. A unit with no passport does **not** surface `supported_qualified`. +2. `spec status` never emits fresher category truth than the stored passport. +3. `spec export` never emits fresher category truth than the stored passport. +4. Benchmark, snapshot, readability, status, and export all consume the same + preserved semantic truth when deriving qualification. +5. Service seam siblings remain visible but unqualified until producer truth is + widened explicitly. + +## Not Doing + +- introducing a new live analysis truth plane inside this milestone +- mutating passport on-disk schema in the correction wedge +- widening seam semantic support +- solving the broader seam-substrate alias/sibling question in the same patch + +## Follow-On If Needed Later + +If the repo later decides it truly needs live, recomputed semantic/category +analysis on read-side surfaces, that work should ship as a separate design +packet with: + +- a new named output surface +- explicit non-proof status +- a clear distinction from passported semantic truth +- rules for how consumers choose between proof truth and analysis truth + +That is a future architecture decision, not part of the M101 correction. From 4111dbfad7737ee0bea6a59b6870b4fe40896756 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 19:28:30 +0000 Subject: [PATCH 20/24] Tighten category truth respin plan --- PLAN.md | 1027 +++++++++++++++---------------------------------------- 1 file changed, 268 insertions(+), 759 deletions(-) diff --git a/PLAN.md b/PLAN.md index e09b600..c1a023d 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1,812 +1,299 @@ -# M101: Category Truth Registry and Consumer Qualification Plan + +# Corrected Stored-Truth-Strict Category Qualification Respin Plan Status: **authoritative implementation plan** -Milestone: **M101** -Implementation readiness: **ready to execute** -Plan scope: **land one explicit category-truth contract for seam-backed support claims across benchmark accounting, `spec status`, `spec export`, and snapshot/readability projections** -Base branch: **main** -Working branch: **`feat/i8-final-proof-run`** -Validated at commit: **`4c41fb3`** -Last rewritten: **2026-05-25** - -Supersedes: - -- the prior `I8: Rust V1 Final Proof Run Plan` +Milestone family: **`operator-consumer-tooling`** +Executable wedge: **`corrected stored-truth-strict category qualification respin for benchmark/status/export/snapshot consumers`** +Implementation readiness: **ready-now** +Base branch: **`feat/i8-final-proof-run`** +Planning date: **2026-05-25** Primary authority inputs: -- design anchor: `docs/category_truth_contract_v0.1.md` +- handoff: `.codex/handoffs/2026-05-25-183145-m101-correction-respin.md` +- corrective design note: `docs/category_truth_contract_correction_v0.1.md` +- original contract doc: `docs/category_truth_contract_v0.1.md` - backlog anchor: `TODOS.md` under `M101 backlog` -- current benchmark registry: `benchmarks/labels.json` -- current producer and consumer code: - - `spec-core/src/semantic_review.rs` - - `spec-core/src/benchmark.rs` - - `spec-core/src/export.rs` - - `spec-core/src/passport.rs` - - `spec-cli/src/commands.rs` -- current contract suites and read-side fixtures: - - `spec-cli/tests/rust_v1_service.rs` - - `spec-cli/tests/rust_v1_closure.rs` - - `spec-cli/tests/m14_regressions.rs` - - `spec-cli/tests/cli.rs` - - `spec-cli/tests/fixtures/benchmarks/*.json` - - `benchmarks/snapshots/*.snapshot.json` - - `benchmarks/reviews/*.readability.review.json` - -## Executive Summary - -The repo already projects semantic-review truth and already computes benchmark, -status, export, and snapshot surfaces. The bug class is not missing data. The -bug class is consumer drift: a read-side surface can still over-credit a seam -category from partial truth such as compatibility key alone, support status -alone, or benchmark label alone. - -This wedge fixes that by introducing one producer-owned registry plus one -shared qualification function in `spec-core`, then wiring every current -seam-category consumer to that contract. After this lands, no current consumer -may claim supported category truth or award positive supported benchmark credit -unless qualification returns an explicit supported result. - -The most visible product change is intentional: `BENCH-SERVICE` must stop -projecting a full-scope `passing` result when its supported-labeled seam cases -are not producer-qualified as supported. - -## Success Sentence - -When this plan is complete, the repo can say this sentence honestly: - -> Every current seam-category consumer in `spec` uses one shared, -> producer-owned qualification contract. Benchmark labels, health state, and -> compatibility-key folklore can no longer widen supported category claims or -> positive benchmark credit on their own. - -## Frozen Decisions - -These are not open during implementation: - -- the authoritative category registry lives in Rust code under `spec-core`, not - in a new checked-in JSON registry -- first-scope registry coverage is exactly four rows: - - `sum.discount_strategy.v1` - - `data.pricing_quote.v1` - - `unsupported.sum.v1` - - `unsupported.data.v1` -- producer truth outranks benchmark labels -- the current service seam mismatch remains visible in this wedge; it is not - rescued into supported truth -- category qualification is projected read-side truth only and is **not** - persisted into on-disk passports -- snapshot and readability surfaces consume the same benchmark qualification - result; readability freshness is not repurposed as an accounting signal -- benchmark labels schema stays at `1`; this wedge changes consumer - projections, not the label-file format -- `spec status --format json` and `spec export` are published machine surfaces, - so their schema versions must bump when `category_qualification` is added -- unsupported terminal categories qualify as `unsupported_qualified` without a - descriptor-id check; descriptor approval is required only for supported rows -- do not ship an unused extra failure vocabulary; only add reason codes the - first landing actually emits - -## Problem Statement - -The failure class is any path where a consumer infers category-backed support -or positive benchmark value without explicit qualification. - -Current repo reality already shows the gap: - -- `benchmarks/labels.json` marks `billing/discount_strategy` and - `billing/pricing_quote` as `classification: supported` -- producer-owned semantic review routes those units to `unsupported.sum.v1` and - `unsupported.data.v1` -- current benchmark logic correctly sets - `counts_as_supported_positive = false` for those cases -- but the enclosing full `BENCH-SERVICE` benchmark still projects - `benchmark_status = passing` - -That last line is too optimistic. The case-level truth is already saying -"this supported claim is not actually producer-qualified." The benchmark-level -truth must stop pretending that full supported closure still passed. - -## Scope Challenge - -### What already exists - -| Sub-problem | Existing owner | Reuse decision | -| --- | --- | --- | -| producer-owned compatibility routing | `spec-core/src/semantic_review.rs` | reuse; do not build a second routing system | -| benchmark projection and accounting skeleton | `spec-core/src/benchmark.rs` | reuse; tighten through qualification rather than rewrite | -| status JSON projection | `spec-cli/src/commands.rs` | reuse; add qualification beside existing semantic review | -| export bundle construction | `spec-core/src/export.rs` | reuse; add additive projected truth rather than new export command | -| projected passport truth assembly | `spec-core/src/passport.rs` | reuse for semantic-review projection only; do not persist category qualification | -| benchmark truth collection from current proof surfaces | duplicated in `spec-cli/src/commands.rs` benchmark and snapshot paths | collapse to one shared helper instead of editing multiple truth carriers independently | -| frozen service/ecommerce benchmark contract suites | `spec-cli/tests/rust_v1_service.rs`, `spec-cli/tests/rust_v1_closure.rs` | keep and update expectations | -| read-side fixtures and snapshots | `spec-cli/tests/fixtures/benchmarks/*.json`, `benchmarks/snapshots/*.snapshot.json` | refresh, do not replace with new artifact families | - -### Minimum complete change set - -The smallest honest implementation is: - -1. add a shared registry and qualification module -2. extend seam semantic review with producer-owned `descriptor_id` -3. thread one shared `CategoryQualification` object through benchmark, status, - export, and snapshot surfaces -4. invalidate full-scope supported benchmark claims when a supported-labeled - case is not producer-qualified -5. refresh tests, fixtures, and snapshots so every read-side surface says the - same thing - -Anything smaller leaves at least one current consumer free to infer support -from partial truth. - -### Complexity check - -This wedge touches one new module, five existing code modules, and several -fixture suites. That is larger than a one-file patch, but it is still the -right-sized diff because the bug class is cross-consumer by definition. A -benchmark-only fix would leave status/export drift alive. - -### Search check - -This is internal contract consolidation work, not a framework or infrastructure -selection problem. The right move is reuse, not novelty: - -- **[Layer 1]** reuse the existing semantic-review, benchmark, status, export, - and passport projection surfaces -- **[Layer 3]** add one repo-local contract where no shared contract exists yet - -No new infrastructure, registry service, or persistence layer belongs in this -plan. - -### TODOS cross-reference - -This plan is the executable version of the active backlog item already recorded -in `TODOS.md` under `M101 backlog`. It should land the -implementation wedge, not create a second overlapping TODO. - -### Completeness check - -This plan should ship the complete version now. With the existing producer and -consumer surfaces already in place, the marginal cost of also updating status, -export, and snapshot projections is small compared with the cost of letting -multiple read-side honesty bugs survive. - -### Distribution check - -This plan introduces no new distributable artifact. Existing CLI build and -release pipelines remain the distribution path. No release automation work is -needed for this wedge. - -## NOT In Scope - -This wedge does **not** do any of the following: - -- widen semantic-review routing so service seam descriptors become supported -- change the four first-scope category ids -- relabel `BENCH-SERVICE` cases in `benchmarks/labels.json` -- add a checked-in external registry file for non-Rust consumers -- persist `category_qualification` into `.spec.passport.json` -- redesign function-family support, benchmark kinds, or benchmark registry shape -- rewrite status health semantics outside the additive qualification field - -## Architecture - -### Current vs target authority flow - -```text -CURRENT -semantic_review - -> compatibility_key + support_status - -> benchmark/status/export each decide what that "means" - -> benchmark label can still overstate supported closure - -TARGET -semantic_review - -> compatibility_key + support_status + descriptor_id - -> category_truth registry lookup - -> qualify_category_claim(...) - -> benchmark/status/export/snapshot all consume the same result - -> no consumer-local widening -``` - -### Architecture diagram - -```text -spec-core/src/semantic_review.rs - ├── projects compatibility_key - ├── projects support_status - └── projects descriptor_id - │ - v -spec-core/src/category_truth.rs - ├── CategoryTruthRegistry - └── qualify_category_claim(...) - │ - ┌───────┼─────────────────────────────┐ - │ │ │ - v v v -benchmark.rs commands.rs(status JSON) export.rs(projected_units) - │ │ │ - └──────────────> snapshot/readability surfaces -``` - -### Shared contract additions - -Add `spec-core/src/category_truth.rs` and export it from `spec-core/src/lib.rs`. - -This module owns: - -- `CategoryTruthRegistry` -- `CategoryTruthRow` -- `CategoryKind` -- `ContractSupportStatus` -- `AliasSiblingPolicy` -- `DescriptorSet` -- `PositiveCreditPolicy` -- `ConsumerKind` -- `CategoryQualification` -- `ClaimStatus` -- `PositiveCreditEligibility` -- `QualificationReasonCode` -- `qualify_category_claim(...)` - -The implementation should stay explicit. Do not introduce a second layer of -builder types or indirection unless the code proves it is necessary. - -### Exact semantic-review change - -`SemanticReview` gains one additive field: - -```rust -pub struct SemanticReview { - ... - pub descriptor_id: Option, -} -``` - -Rules: - -- only producer-owned semantic-review logic may populate `descriptor_id` -- no benchmark, status, export, or snapshot path may synthesize or rewrite it -- canonical ecommerce seam descriptors project: - - `discount_strategy.ecommerce.v1` - - `pricing_quote.ecommerce.v1` -- current service seam siblings project their own service descriptor ids and - remain unqualified for supported rows -- legacy stored semantic reviews without `descriptor_id` stay readable but must - fail supported qualification explicitly with `descriptor_id_missing` - -### Exact first-scope registry rows - -| Category | Kind | Contract support | Alias policy | Canonical descriptor | Approved siblings | Positive credit | -| --- | --- | --- | --- | --- | --- | --- | -| `sum.discount_strategy.v1` | `sum` | `supported` | `canonical_only` | `discount_strategy.ecommerce.v1` | none | eligible | -| `data.pricing_quote.v1` | `data` | `supported` | `canonical_only` | `pricing_quote.ecommerce.v1` | none | eligible | -| `unsupported.sum.v1` | `sum` | `unsupported` | `unsupported_terminal` | none | none | ineligible | -| `unsupported.data.v1` | `data` | `unsupported` | `unsupported_terminal` | none | none | ineligible | - -### Qualification API contract - -Implement one shared function with this exact responsibility split: - -```rust -pub fn qualify_category_claim( - consumer: ConsumerKind, - semantic_review: Option<&SemanticReview>, -) -> CategoryQualification -``` - -The first landing should keep the function contract small. Do **not** add a -separate consumer-context struct unless a real requirement appears during -implementation. - -The function decides only category truth: - -- registry row lookup from `semantic_review.compatibility_key` -- effective support-status match -- descriptor approval for supported rows only -- unsupported terminal qualification for unsupported rows -- positive-credit eligibility - -It does **not** decide benchmark lifecycle, full-vs-partial path scope, or gate -status. Those remain benchmark-local rules. - -### Stable qualification output - -Every consumer must reuse this shape: - -```rust -pub struct CategoryQualification { - pub category_id: Option, - pub descriptor_id: Option, - pub claim_status: ClaimStatus, - pub positive_credit_eligibility: PositiveCreditEligibility, - pub reason_code: QualificationReasonCode, -} -``` - -Required first-landing enums: - -- `ClaimStatus` - - `supported_qualified` - - `unsupported_qualified` - - `unqualified` -- `PositiveCreditEligibility` - - `eligible` - - `ineligible` -- `QualificationReasonCode` - - `qualified` - - `semantic_review_missing` - - `registry_row_missing` - - `descriptor_id_missing` - - `descriptor_not_approved` - - `support_status_mismatch` - - `positive_credit_disallowed` - -Rules that remove ambiguity: - -- supported rows require descriptor approval plus `effective_support_status() == - Supported` -- unsupported terminal rows require `effective_support_status() == - Unsupported`; they do **not** require descriptor approval -- `positive_credit_disallowed` is valid only for a claim that otherwise - resolves, but is ineligible for positive credit -- `semantic_support_status` may remain visible as a compatibility/debug field, - but `category_qualification` is the only authoritative claim surface after - this wedge - -## Consumer Behavior Contract - -### Benchmark accounting - -`spec-core/src/benchmark.rs` is the first adoption point. - -Required behavior after the change: - -- `BenchmarkCaseTruth` stops carrying only - `semantic_support_status: Option` -- replace that with `semantic_review: Option` so benchmark - projection receives the exact producer-owned input it needs instead of a new - bespoke mini-struct -- `BenchmarkCaseProjection` gains `category_qualification` -- `counts_as_supported_positive` requires all of: - - positive benchmark - - active lifecycle - - full path scope - - valid benchmark accounting - - `classification == supported` - - `status == valid` - - `category_qualification.claim_status == supported_qualified` - - `category_qualification.positive_credit_eligibility == eligible` -- a supported-labeled case that fails qualification stays visible, but it makes - the full benchmark accounting invalid -- partial benchmark scope becomes `partial_invalid` on the same mismatch, but - still does not invent full `benchmark_status` or `gate_status` -- `readability_review_status` remains whatever the readability artifact already - says; qualification failure must not rewrite it - -This is the most important product decision in the plan: - -- `BENCH-SERVICE` full projection must stop saying `passing` -- it must become: - - `accounting_status = invalid` - - `benchmark_status = invalid` - - `gate_status = open` - - unchanged readability freshness status - -### `spec status --format json` - -`spec-cli/src/commands.rs` must add `category_qualification` to each -`JsonStatusEntry`. - -Rules: - -- keep current health semantics unchanged -- keep current `semantic_review` projection unchanged except for additive - `descriptor_id` -- add `category_qualification` beside `semantic_review` -- never infer supported category truth from health or semantic-review presence - alone - -Schema change: - -- bump `STATUS_JSON_SCHEMA_VERSION` from `4` to `5` - -### `spec export` - -`spec-core/src/export.rs` must expose additive projected truth without writing -`category_qualification` into on-disk passports. - -Exact plan: - -- keep exported `passports` as projected passport truth -- add a new additive `projected_units` array to `ExportBundle` -- define a dedicated read-side struct for it, for example: - -```rust -pub struct ProjectedExportUnit { - pub id: String, - pub semantic_review: Option, - pub category_qualification: Option, -} -``` +- current authority context: `PLAN.md` and `ORCH_PLAN.md` from the prior M101 attempt +- live repo state observed on this branch: + - `cargo run -p spec-cli -- status examples/service/units --format json` + - `cargo run -p spec-cli -- export examples/service/units` + - both currently report `BENCH-SERVICE` as `benchmark_status=passing`, `accounting_status=valid`, `gate_status=open`, `positive_credit_cases=4` -- each row must include: - - `id` - - `semantic_review` - - `category_qualification` +## Why This Respin Exists -That keeps export machine-readable, keeps qualification read-side only, and -avoids mutating `.spec.passport.json` persistence semantics. +The milestone is still right. The implementation shape was not. -Schema change: +The old M101 direction correctly aimed for: -- bump `EXPORT_SCHEMA_VERSION` from `4` to `5` +- one registry-owned category truth substrate +- one shared qualification vocabulary +- benchmark, status, export, and snapshot/readability parity -### Snapshot and readability parity +But the reviewed implementation crossed the repo's honesty boundary by allowing +read-side consumers to derive category-bearing truth from refreshed semantic +projection. That would create a second truth producer outside `spec test`. -Benchmark snapshot output already reuses benchmark projection. This wedge must -keep that true. +This respin keeps the shared contract and benchmark honesty gains, then rebuilds +consumer projections so every read-side surface interprets stored semantic truth +only. -Rules: - -- benchmark snapshot output must serialize per-case `category_qualification` -- full-scope invalid service seam claims must yield invalid full snapshots too -- readability review freshness and verdict stay additive and unchanged by - qualification failure - -### Shared read-side plumbing rule - -`spec-cli/src/commands.rs` currently constructs benchmark root-case truth in -multiple places. This wedge must collapse that duplication instead of letting -benchmark and snapshot paths drift again. - -Implementation rule: - -- add one shared helper in `spec-cli/src/commands.rs` that derives read-side - unit truth from `project_passport_truth_with_context(...)` -- status, benchmark, and snapshot paths should all consume that shared helper -- do not hand-edit three independent call paths with slightly different - semantic-review extraction logic - -## File Blast Radius +## Frozen Decisions -### New code +These are fixed for this wedge: + +1. `semantic_review` remains producer-owned truth. +2. Only `spec test` may refresh semantic review truth. +3. `category_qualification` is read-side interpretation of stored semantic truth. +4. No read-only path may use `SemanticProjectionMode::Refresh` to mint fresher + category truth for benchmark, status, export, or snapshot output. +5. The first-scope registry remains the same four seam rows: + - `sum.discount_strategy.v1` + - `data.pricing_quote.v1` + - `unsupported.sum.v1` + - `unsupported.data.v1` +6. Service seam siblings stay visible but unqualified in this wedge. +7. `benchmarks/labels.json` is not relabeled in this wedge. +8. `.spec.passport.json` schema is not widened to persist + `category_qualification`. +9. If `spec status` and `spec export` gain additive + `category_qualification`, their public JSON schema versions bump. +10. For seam category-claim candidates, positive benchmark credit must require + `category_qualification.claim_status == supported_qualified` plus + `positive_credit_eligibility == eligible`. +11. Legacy `semantic_support_status` fallback may remain only for non-seam + benchmark rows. It must not award positive credit to seam rows when + `category_qualification` is absent. +12. `spec export` must choose one exact no-passport behavior for seam rows: + if no passported semantic review exists, export may still emit the unit row, + but it must emit no supported semantic/category claim. The allowed first + landing shape is: + - `projected_units[]` row present + - `semantic_review = null` + - `category_qualification = null` + - never `supported_qualified` +13. The refresh audit is owned by named read-side helpers, not whole-file grep. + The implementation must identify the exact benchmark/status/export/snapshot + read-side projection helpers and prove those helpers use preserved semantic + truth only. + +## Exact File Scope + +### Expected code edits - `spec-core/src/category_truth.rs` - -### Existing code that must change - + - keep the registry and qualification substrate + - tighten it around preserved semantic truth only - `spec-core/src/lib.rs` + - export the corrected category-truth module - `spec-core/src/semantic_review.rs` + - keep producer-owned routing and descriptor hooks + - do not widen read-side refresh semantics - `spec-core/src/benchmark.rs` + - keep positive-credit gating and benchmark invalidation logic + - consume preserved semantic review truth only - `spec-core/src/export.rs` -- `spec-core/src/passport.rs` + - add or keep additive qualification on export surfaces only if it is derived + from preserved/passported semantic review + - remove any refresh-based category truth minting path - `spec-cli/src/commands.rs` + - route benchmark/status/export/snapshot consumers through one preserved-truth + qualification helper + - remove any `SemanticProjectionMode::Refresh` path that feeds + `category_qualification` on read-side surfaces -### Tests and fixtures that must change +### Expected tests and artifact refresh - `spec-cli/tests/rust_v1_service.rs` - `spec-cli/tests/rust_v1_closure.rs` -- `spec-cli/tests/m14_regressions.rs` - `spec-cli/tests/cli.rs` -- `spec-cli/tests/fixtures/benchmarks/*.json` +- `spec-cli/tests/m14_regressions.rs` +- `spec-cli/tests/fixtures/benchmarks/export-ecommerce-full.json` +- `spec-cli/tests/fixtures/benchmarks/export-service-full.json` +- `spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json` +- `spec-cli/tests/fixtures/benchmarks/status-ecommerce-full.json` +- `spec-cli/tests/fixtures/benchmarks/status-service-full.json` +- `spec-cli/tests/fixtures/benchmarks/status-service-billing-partial-full.json` +- `spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json` - `benchmarks/snapshots/BENCH-ECOM.snapshot.json` - `benchmarks/snapshots/BENCH-SERVICE.snapshot.json` -- `benchmarks/reviews/BENCH-ECOM.readability.review.json` -- `benchmarks/reviews/BENCH-SERVICE.readability.review.json` -## Implementation Phases +### Explicitly out of scope -### Phase 0: Preflight and current-truth lock +- `benchmarks/labels.json` +- `.spec.passport.json` schema changes +- new live analysis surfaces +- widened support for service seam siblings +- broader seam alias/sibling cleanup +- recommendation-policy or corpus-run work +- ORCH plan expansion beyond what this corrected wedge needs -Goals: +## Keep / Drop Commit Intent -- capture the current service mismatch behavior before changing it -- verify which fixtures and tests lock the existing optimistic benchmark status +The old branch is reference material, not a branch to land. -Do: +### Keep or adapt -- run the current benchmark contract suites -- note all current full-scope service expectations that will intentionally flip - from `passing` to `invalid` -- capture the exact benchmark/status/export/snapshot fixture files that will - need rewrites +| Commit | Intent | Use in respin | +| --- | --- | --- | +| `397837e` | add category truth contract spine | strongest salvage candidate; keep the registry and qualification types after a preserve-only audit | +| `688d673` | pass semantic review into benchmark truth builders | keep the idea; benchmark projection needs semantic-review input, but only preserved truth | +| `81aa154` | invalidate service benchmark category mismatches | keep the benchmark honesty outcome and test expectations | -Done when: +### Drop or re-implement narrowly -- the planned expectation flips are explicit before code edits start +| Commit | Why it does not land as-is | Respin instruction | +| --- | --- | --- | +| `c24d4f7` | projected export truth was built in the same implementation wave that introduced read-side refresh risk | rebuild export from preserved passport truth only | +| `29fd15b` | CLI truth integration touched the risky status/export paths directly | do not cherry-pick wholesale; re-thread through one preserved-truth helper | +| `701019e` | fixture refresh captured the old export shape | regenerate after corrected export semantics are in place | +| `3d81942` | snapshot preservation landed after the read-side truth drift already existed | rebuild snapshot parity after live benchmark projection is corrected | +| `e2af919` | refreshed semantic-review benchmark inputs are suspect under the corrected contract | re-evaluate case by case; default to drop | +| `3ee00b4` | compile-surface fix was tied to the exploratory branch line | re-apply only if the corrected respin actually needs it | -### Phase 1: Add category truth substrate +## Corrected Contract Shape -Files: +The implementation contract for this respin is: -- `spec-core/src/category_truth.rs` -- `spec-core/src/lib.rs` -- `spec-core/src/semantic_review.rs` - -Deliverables: +- producer truth: + - authored unit specs + - passports + - molecule evidence + - `semantic_review` refreshed only by `spec test` +- consumer truth: + - `CategoryQualification` + - benchmark case accounting + - `spec status --format json` + - `spec export` + - snapshot/readability projection -- registry structs and enums -- hard-coded first four rows -- `qualify_category_claim(...)` -- `SemanticReview.descriptor_id` -- seam descriptor-id projection tests -- qualification unit tests for: - - canonical ecommerce sum qualifies supported - - canonical ecommerce data qualifies supported - - service sum sibling does not qualify supported - - service data sibling does not qualify supported - - unsupported rows qualify only as unsupported - - missing semantic review fails explicitly - - missing descriptor id fails explicitly +The consumer rule is simple: -### Phase 2: Benchmark-core adoption +> if preserved semantic truth is missing, stale, unsupported, or descriptor-mismatched, +> the consumer may surface that failure, but it may not mint a fresher supported claim. -Files: +## Implementation Slices -- `spec-core/src/benchmark.rs` -- benchmark-focused tests in `spec-core` and `spec-cli/tests/rust_v1_service.rs` -- benchmark JSON fixtures under `spec-cli/tests/fixtures/benchmarks/` +### Slice 1: Re-establish the contract spine Deliverables: -- `BenchmarkCaseTruth` carries `semantic_review` -- `BenchmarkCaseProjection` gains `category_qualification` -- `counts_as_supported_positive` uses qualification -- benchmark-wide full-scope invalidation on supported-label qualification - failure -- service benchmark contract tests assert `invalid/open` -- benchmark fixture expectations reflect: - - ecommerce remains passing - - service flips to invalid - - partial mismatch becomes `partial_invalid` +- land `spec-core/src/category_truth.rs` as the shared registry and stable + reason-code surface +- keep the qualification vocabulary small: + - `supported_qualified` + - `unsupported_qualified` + - `unqualified` +- ensure qualification takes preserved semantic-review input, not a refresh-mode + projection +- keep descriptor approval narrow so service siblings remain unqualified -### Phase 3: Export projection adoption +Proof: -Files: +- `cargo test -p spec-core` -- `spec-core/src/export.rs` -- `spec-core/src/passport.rs` +### Slice 2: Restore benchmark honesty first Deliverables: -- `ExportBundle.projected_units[]` -- dedicated projected export row type -- export projection reuses shared qualification -- export schema bump `4 -> 5` -- regression coverage that proves qualification stays read-side only and does - not persist into `.spec.passport.json` +- thread preserved semantic-review truth into benchmark case qualification +- keep `counts_as_supported_positive` gated on explicit supported qualification +- remove or fence the seam-row legacy fallback so + `semantic_support_status` alone cannot award positive credit for seam + category candidates +- make full positive benchmarks invalid when supported-labeled seam cases are + present but unqualified +- preserve unsupported visibility without positive credit -### Phase 4: CLI status and snapshot integration +Expected contract outcome: -Files: +- `BENCH-ECOM` full stays passing +- `BENCH-SERVICE` full flips away from `passing/valid` +- target state for `BENCH-SERVICE` full: + - `accounting_status = invalid` + - `benchmark_status = invalid` + - `gate_status = open` -- `spec-cli/src/commands.rs` -- `spec-cli/tests/cli.rs` -- `spec-cli/tests/m14_regressions.rs` -- `benchmarks/snapshots/*.snapshot.json` -- `benchmarks/reviews/*.readability.review.json` +Proof: + +- `cargo test -p spec-cli --test rust_v1_service` +- `cargo test -p spec-cli --test rust_v1_closure` +- `cargo run -p spec-cli -- status examples/ecommerce/units --format json` +- `cargo run -p spec-cli -- status examples/service/units --format json` + +### Slice 3: Rebuild status and export as stored-truth consumers Deliverables: -- one shared CLI helper for projected unit truth -- `JsonStatusEntry.category_qualification` -- status schema bump `4 -> 5` -- snapshot command emits the same qualification truth as live benchmark output -- service snapshot flips to invalid full benchmark status -- readability freshness remains unchanged by accounting invalidation - -### Phase 5: Final verification sweep - -Goals: - -- prove all current consumers agree -- prove no consumer-local widening remains - -Done when: - -- benchmark, status, export, and snapshot surfaces tell the same category truth -- all targeted suites pass -- the repo no longer has a full-scope supported benchmark that is invalid at - the case level but still claims overall `passing` - -## Test Diagram - -```text -CODE PATHS -[+] spec-core/src/semantic_review.rs - ├── canonical ecommerce sum -> supported key + descriptor_id - ├── canonical ecommerce data -> supported key + descriptor_id - ├── service sum sibling -> unsupported key + service descriptor_id - └── service data sibling -> unsupported key + service descriptor_id - -[+] spec-core/src/category_truth.rs - ├── registry lookup -> supported canonical row - ├── registry lookup -> unsupported terminal row - ├── supported-row descriptor approval passes - ├── supported-row descriptor approval fails - ├── unsupported terminal row skips descriptor approval - ├── missing semantic review -> unqualified - └── missing descriptor_id -> unqualified - -[+] spec-core/src/benchmark.rs - ├── supported + qualified -> positive credit - ├── supported label + unsupported qualification -> no credit - ├── full benchmark with disqualified supported case -> accounting invalid - └── partial benchmark with disqualified supported case -> partial_invalid - -[+] spec-cli/src/commands.rs / spec-core/src/export.rs - ├── status emits semantic_review + category_qualification - ├── export emits projected_units + category_qualification - └── snapshot emits same benchmark qualification as live projection - -CONSUMER FLOWS -[+] BENCH-ECOM full benchmark - ├── canonical seam cases remain supported_qualified - └── benchmark stays passing - -[+] BENCH-SERVICE full benchmark - ├── service seam cases stay visible - ├── supported label remains visible - ├── category_qualification fails explicitly - └── benchmark flips to invalid/open - -[+] spec status / export / snapshot readers - └── can distinguish supported_qualified vs unsupported_qualified vs unqualified -``` +- add additive `category_qualification` only where the surface is reading + preserved/passported semantic review +- remove any status/export path that reaches `SemanticProjectionMode::Refresh` + for category qualification +- ensure a missing passport never yields `supported_qualified` +- pin exact no-passport export behavior for seam rows: + - keep the `projected_units[]` row + - do not synthesize `semantic_review` + - do not synthesize `supported_qualified` + - allow only `semantic_review = null` with `category_qualification = null` +- ensure stale stored truth remains stale instead of being silently refreshed by + a consumer +- bump public JSON schema versions when additive qualification is introduced + +Proof: + +- `cargo test -p spec-cli --test cli` +- `cargo test -p spec-cli --test m14_regressions` +- `cargo run -p spec-cli -- export examples/ecommerce/units` +- `cargo run -p spec-cli -- export examples/service/units` + +### Slice 4: Re-freeze snapshot and artifact parity -## Required Test Coverage - -Add or update tests for these exact behaviors: - -- semantic-review descriptor-id projection for canonical supported seams -- semantic-review descriptor-id projection for current service seam siblings -- qualification lookup for all four first-scope rows -- unsupported terminal rows qualify without descriptor approval -- benchmark case projection includes `category_qualification` -- full `BENCH-SERVICE` flips from `passing` to `invalid` -- full `BENCH-ECOM` remains `passing` -- partial benchmark projections use `partial_invalid` without inventing full - benchmark fields -- status JSON schema version bumps and includes `category_qualification` -- export schema version bumps and includes - `projected_units[].category_qualification` -- benchmark snapshots serialize the same case qualification as live output -- legacy persisted semantic review without `descriptor_id` fails qualification - explicitly -- `.spec.passport.json` outputs do **not** persist `category_qualification` +Deliverables: -## Failure Modes Registry +- snapshot output reuses the same benchmark qualification result as live + projection +- readability artifacts continue to report readability state, not smuggled + category-truth state +- fixture refresh happens only after slices 1 through 3 are green -| Codepath | Production failure | Test coverage required | User-visible effect | Priority | -| --- | --- | --- | --- | --- | -| registry lookup | missing row causes implicit support fallback | unit test on unknown key -> `registry_row_missing` | downstream consumer silently lies unless blocked | P1 | -| descriptor identity | legacy or missing `descriptor_id` gets treated as supported | regression test -> `descriptor_id_missing` | benchmark/status/export over-credit seam support | P1 | -| unsupported terminal routing | unsupported rows incorrectly require a descriptor and become generic unqualified noise | unit test on `unsupported.sum.v1` and `unsupported.data.v1` | readers cannot distinguish unsupported-qualified truth from missing truth | P1 | -| benchmark invalidation | disqualified supported case still leaves full benchmark `passing` | service benchmark contract tests | maintainer believes supported closure is green when it is not | P1 | -| status/export drift | status and export emit different reason codes or claim states | paired fixture assertions in `cli.rs` and `m14_regressions.rs` | downstream tools disagree about the same unit | P1 | -| readability coupling | qualification failure rewrites readability freshness | snapshot tests | readers lose freshness signal and cannot separate style from truth | P2 | -| passport persistence leak | category qualification gets written into `.spec.passport.json` | export/passport regression | on-disk proof state becomes polluted with read-side claims | P2 | +Proof: -Critical gap definition for this wedge: +- `cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM` +- `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` -- any path that can still emit supported positive credit or a supported category - claim without `CategoryQualification == supported_qualified` +## Required Regression Cases -## Worktree Parallelization Strategy +These are mandatory. The respin is not done without all of them. -### Dependency table +| Case | Why it exists | Expected result | +| --- | --- | --- | +| missing passport seam unit | protects the no-synthetic-truth boundary | export keeps the `projected_units[]` row but emits `semantic_review = null`, `category_qualification = null`, and never `supported_qualified` | +| stale stored seam truth | protects against read-side refresh minting | consumer output may stay stale or unqualified, but may not become fresher than stored truth | +| service sibling with supported benchmark label | protects benchmark honesty | case remains visible, unqualified, and non-credit-bearing; full `BENCH-SERVICE` becomes invalid/open | +| canonical ecommerce seam row | protects the happy path | remains `supported_qualified` and credit-eligible | +| terminal unsupported seam row | protects additive unsupported visibility | may be `unsupported_qualified`, but never positive-credit eligible | + +## Failure Modes Registry -| Step | Modules touched | Depends on | +| Risk | Trigger | Guard | | --- | --- | --- | -| A. Category substrate | `spec-core/src/category_truth.rs`, `spec-core/src/lib.rs`, `spec-core/src/semantic_review.rs` | — | -| B. Benchmark core adoption | `spec-core/src/benchmark.rs`, benchmark-focused tests, benchmark fixtures | A | -| C. Export projection adoption | `spec-core/src/export.rs`, `spec-core/src/passport.rs`, export-focused tests | A | -| D. CLI status and snapshot integration | `spec-cli/src/commands.rs`, `spec-cli/tests/cli.rs`, `spec-cli/tests/m14_regressions.rs`, snapshots/reviews | B, C | - -### Parallel lanes - -- Lane A: Step A -- Lane B: Step B after A lands -- Lane C: Step C after A lands -- Lane D: Step D after B and C land - -### Execution order - -1. Launch Lane A first. It owns the substrate and must land before anything - else can qualify claims. -2. After A merges, launch Lanes B and C in parallel worktrees. -3. Merge B and C. -4. Run Lane D last to integrate shared `commands.rs` changes, refresh snapshots, - and freeze final fixtures. - -### Conflict flags - -- Lanes B and D both affect benchmark-facing projection behavior, but only D - should touch the final shared CLI truth helper -- Lanes C and D both affect read-side contract surfaces -- Do **not** run B and D in parallel -- Do **not** run C and D in parallel -- Keep `commands.rs` ownership in Lane D to minimize merge conflict risk - -If worktree staffing is unavailable, run the same order sequentially. - -## Implementation Tasks - -Synthesized from the architecture, test, and failure-mode requirements above. - -- [ ] **T1 (P1, human: ~2h / CC: ~15min)** — category substrate — add - `spec-core/src/category_truth.rs` with the first-scope registry rows, - qualification enums, and `qualify_category_claim(...)` - - Files: `spec-core/src/category_truth.rs`, `spec-core/src/lib.rs` - - Verify: `cargo test -p spec-core category_truth` -- [ ] **T2 (P1, human: ~1.5h / CC: ~10min)** — semantic-review producer truth — - extend seam semantic-review projection with producer-owned `descriptor_id` - and lock it with unit tests - - Files: `spec-core/src/semantic_review.rs` - - Verify: `cargo test -p spec-core semantic_review` -- [ ] **T3 (P1, human: ~2h / CC: ~15min)** — benchmark qualification — - thread `semantic_review` into benchmark case truth, add - `category_qualification`, and make positive credit plus full benchmark - validity depend on qualification - - Files: `spec-core/src/benchmark.rs` - - Verify: `cargo test -p spec-core benchmark` -- [ ] **T4 (P1, human: ~1h / CC: ~10min)** — benchmark contract refresh — - update `BENCH-SERVICE` tests and fixtures to assert - `accounting_status = invalid`, `benchmark_status = invalid`, - `gate_status = open` - - Files: `spec-cli/tests/rust_v1_service.rs`, - `spec-cli/tests/rust_v1_closure.rs`, - `spec-cli/tests/fixtures/benchmarks/*.json` - - Verify: `cargo test -p spec-cli rust_v1_service rust_v1_closure` -- [ ] **T5 (P1, human: ~1.5h / CC: ~10min)** — export contract — - add `projected_units[]`, surface shared qualification there, and bump export - schema version to `5` - - Files: `spec-core/src/export.rs`, `spec-core/src/passport.rs` - - Verify: `cargo test -p spec-core export` -- [ ] **T6 (P1, human: ~2h / CC: ~15min)** — CLI read-side integration — - add one shared projected-truth helper, surface `category_qualification` in - status JSON, and keep snapshots aligned with live benchmark output - - Files: `spec-cli/src/commands.rs`, `spec-cli/tests/cli.rs`, - `spec-cli/tests/m14_regressions.rs` - - Verify: `cargo test -p spec-cli cli m14_regressions` -- [ ] **T7 (P2, human: ~45min / CC: ~5min)** — snapshot and readability freeze — - refresh benchmark snapshots and readability fixtures so live and frozen - projections stay in sync without rewriting readability freshness semantics - - Files: `benchmarks/snapshots/*.snapshot.json`, - `benchmarks/reviews/*.readability.review.json` - - Verify: `cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM` and - `cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE` - -## Acceptance Criteria - -This plan is complete only when all of the following are true: - -1. the repo has one authoritative category truth registry in `spec-core` -2. every current seam-category consumer calls the same qualification function -3. positive benchmark credit is impossible without - `supported_qualified + eligible` -4. a supported-labeled but producer-unqualified full benchmark becomes - `invalid`, not `passing` -5. `spec status` and `spec export` both expose additive - `category_qualification` -6. export keeps category qualification read-side only and does not persist it - into `.spec.passport.json` -7. snapshot output matches live benchmark qualification output -8. the current `BENCH-SERVICE` mismatch is explicit contract truth, not repo - folklore - -## Verification Commands - -Run at minimum: +| read-side truth minting returns through a helper | status/export/snapshot accidentally reuse refresh projection | name the exact read-side helpers and prove they use preserve-mode only, then cover with fixture regressions | +| benchmark honesty fixed but status/export still drift | only benchmark code is corrected | require parity assertions across service status and export fixtures | +| seam rows still receive positive credit through legacy fallback | benchmark keeps `semantic_support_status` shortcut for seam candidates | add a targeted benchmark regression that seam candidates require `category_qualification`, and keep legacy fallback non-seam-only | +| missing passport path still looks supported | export/status silently synthesize semantic review | add explicit no-passport service fixture assertions | +| stale proof gets hidden by live recompute | read-side consumer re-evaluates current authored truth | add stale-passport regression and compare against stored output | +| fixture refresh masks a logic bug | artifacts are updated before invariants are asserted | refresh fixtures only after targeted tests and live CLI checks pass | + +## Proof Commands + +Run these in order before calling the wedge complete: ```bash cargo test -p spec-core -cargo test -p spec-cli rust_v1_service -cargo test -p spec-cli rust_v1_closure -cargo test -p spec-cli m14_regressions -cargo test -p spec-cli cli - +cargo test -p spec-cli --test rust_v1_service +cargo test -p spec-cli --test rust_v1_closure +cargo test -p spec-cli --test cli +cargo test -p spec-cli --test m14_regressions cargo run -p spec-cli -- status examples/ecommerce/units --format json cargo run -p spec-cli -- status examples/service/units --format json cargo run -p spec-cli -- export examples/ecommerce/units @@ -815,23 +302,45 @@ cargo run -p spec-cli -- benchmark snapshot BENCH-ECOM cargo run -p spec-cli -- benchmark snapshot BENCH-SERVICE ``` -Expected end state: - -- ecommerce full benchmark remains passing -- service full benchmark becomes invalid/open -- status and export both expose the same category qualification for seam rows -- snapshots match live benchmark projections -- `.spec.passport.json` files remain free of persisted `category_qualification` - -## Deferred Follow-On Work - -After this wedge lands, separate follow-on work may decide whether to: - -- widen producer routing so current service seam descriptors become supported -- tighten `BENCH-SERVICE` labels so they stop asking for supported claims the - producer does not grant -- externalize the registry for non-Rust consumers -- expand category truth beyond the first four seam rows - -Those are real follow-ons. They are explicitly **not** prerequisites for this -plan. +Required code-review audit before calling the wedge complete: + +- name the exact benchmark/status/export/snapshot read-side helpers that project + semantic/category truth +- verify those helpers use preserved semantic truth only +- verify any remaining `SemanticProjectionMode::Refresh` callsites are limited to + write-path or explicitly non-read-side flows + +## Done Means + +This respin is complete only when all of the following are true: + +1. the repo has one shared category-truth registry and qualification contract in + `spec-core` +2. benchmark, status, export, and snapshot consumers all derive qualification + from preserved semantic truth only +3. a seam benchmark row cannot receive positive credit through legacy + `semantic_support_status` fallback alone +4. a unit with no passport does not surface `supported_qualified` + and does not synthesize seam semantic review on export +5. the no-passport export shape is stable and explicit for machine readers + rather than left to consumer guesswork: + - `projected_units[]` row present + - `semantic_review = null` + - `category_qualification = null` +6. stale stored semantic truth is never upgraded by a read-side consumer into + fresher category truth +7. `BENCH-ECOM` remains a passing positive wall +8. `BENCH-SERVICE` no longer reports a full supported-positive success while + its seam siblings remain unqualified +9. status/export schema bumps and fixture refreshes are complete wherever + additive `category_qualification` is exposed +10. `.spec.passport.json` remains free of persisted `category_qualification` + +## Not Doing In This Plan + +- landing the old `feat/m101-category-truth-registry` branch +- inventing a second semantic/category truth plane +- widening supported seam truth to rescue service siblings +- relabeling benchmark cases to avoid the mismatch +- mixing this contract repair with separate family-analysis or seam-substrate + roadmap work From fbc531c01014c311b9fa2303af3644274e5b46bd Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 21:32:52 +0000 Subject: [PATCH 21/24] Add stored-truth category qualification --- benchmarks/snapshots/BENCH-ECOM.snapshot.json | 24 +- .../snapshots/BENCH-SERVICE.snapshot.json | 18 +- spec-cli/src/commands.rs | 65 +- spec-cli/tests/cli.rs | 43 +- .../benchmarks/export-ecommerce-full.json | 326 +- .../export-service-billing-partial-full.json | 166 +- .../benchmarks/export-service-full.json | 178 +- .../status-apply-discount-partial-full.json | 2 +- .../benchmarks/status-ecommerce-full.json | 50 +- ...status-ecommerce-pricing-partial-full.json | 16 +- .../benchmarks/status-repo-root-contract.json | 2 +- .../benchmarks/status-repo-root-full.json | 630 +-- .../status-repo-root-service-full.json | 3944 +++++++++++++---- .../status-service-billing-partial-full.json | 4 +- .../benchmarks/status-service-full.json | 16 +- .../fixtures/status-failing-timeout.json | 2 +- spec-cli/tests/fixtures/status-failing.json | 2 +- .../tests/fixtures/status-incomplete.json | 2 +- spec-cli/tests/fixtures/status-stale.json | 2 +- spec-cli/tests/fixtures/status-untested.json | 2 +- spec-cli/tests/fixtures/status-valid.json | 2 +- spec-cli/tests/rust_v1_service.rs | 34 +- spec-core/src/benchmark.rs | 128 +- spec-core/src/category_truth.rs | 554 +++ spec-core/src/export.rs | 93 +- spec-core/src/lib.rs | 1 + spec-core/src/semantic_review.rs | 93 +- 27 files changed, 4910 insertions(+), 1489 deletions(-) create mode 100644 spec-core/src/category_truth.rs diff --git a/benchmarks/snapshots/BENCH-ECOM.snapshot.json b/benchmarks/snapshots/BENCH-ECOM.snapshot.json index ed6b8c5..a385bcd 100644 --- a/benchmarks/snapshots/BENCH-ECOM.snapshot.json +++ b/benchmarks/snapshots/BENCH-ECOM.snapshot.json @@ -1,5 +1,5 @@ { - "generated_at": "2026-05-20T22:06:56Z", + "generated_at": "2026-05-25T19:41:28Z", "projection": { "benchmark_id": "BENCH-ECOM", "kind": "positive", @@ -7,15 +7,15 @@ "required_for_v1": true, "path_scope": "full", "accounting_status": "valid", - "benchmark_status": "passing", - "gate_status": "satisfied", - "readability_review_status": "current", + "benchmark_status": "incomplete", + "gate_status": "open", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated benchmark surfaces remain readable and structurally traceable for the seeded BENCH-ECOM closure." }, "label_digest": "sha256:b3cb83c0fc1d42b0c2d469db332870227e1f4819e7274636cbef9a9d2dc9cee8", - "projection_digest": "sha256:07abdc7583aeb203ad6063fafc77bac72845674e5736acdda8fd6a8424f4e00e", + "projection_digest": "sha256:2715e254148589baa08e0d1454b9e5b770b157a554b3bf42e94119ac88834f53", "summary": { "total_cases": 7, "supported_cases": 7, @@ -138,6 +138,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -153,6 +160,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ diff --git a/benchmarks/snapshots/BENCH-SERVICE.snapshot.json b/benchmarks/snapshots/BENCH-SERVICE.snapshot.json index 5092939..826b14d 100644 --- a/benchmarks/snapshots/BENCH-SERVICE.snapshot.json +++ b/benchmarks/snapshots/BENCH-SERVICE.snapshot.json @@ -1,13 +1,13 @@ { - "generated_at": "2026-05-24T23:21:00Z", + "generated_at": "2026-05-25T19:41:28Z", "projection": { "benchmark_id": "BENCH-SERVICE", "kind": "positive", "lifecycle": "active", "required_for_v1": true, "path_scope": "full", - "accounting_status": "valid", - "benchmark_status": "incomplete", + "accounting_status": "invalid", + "benchmark_status": "invalid", "gate_status": "open", "readability_review_status": "stale", "readability_verdict": { @@ -15,12 +15,12 @@ "summary": "Generated service benchmark surfaces remain readable and structurally traceable for the activated service closure." }, "label_digest": "sha256:69eb48437324c944450825de3db57784b0b9e80496d0f9005ac7b595f066e9cc", - "projection_digest": "sha256:1560d6f12912c864ff885874d12afc5ad3bcd87de3c9e6a295bea568c2c9265e", + "projection_digest": "sha256:c3b32ffaa471d0d08e32b8e821f527f84b78ec6b4627e5f4011bb07551095705", "summary": { "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 4, + "positive_credit_cases": 0, "case_status_counts": { "invalid": 0, "failing": 0, @@ -70,7 +70,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/apply_regional_fee", @@ -86,7 +86,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total", @@ -101,7 +101,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total_guarded_fee", @@ -116,7 +116,7 @@ "examples/service/units/billing/checkout_declined_discount_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/discount_strategy", diff --git a/spec-cli/src/commands.rs b/spec-cli/src/commands.rs index 711b6bf..343fc69 100644 --- a/spec-cli/src/commands.rs +++ b/spec-cli/src/commands.rs @@ -14,6 +14,9 @@ use spec_core::benchmark::{ benchmark_root_path, benchmark_snapshot_path, compute_projection_digest, load_labels, project_benchmark, readability_review_path, }; +use spec_core::category_truth::{ + CategoryQualification, ConsumerKind, is_seam_category_claim_candidate, qualify_category_claim, +}; use spec_core::escape_hatch::{EscapeHatchGate, EscapeHatchGateStatus}; use spec_core::export::{build_export_bundle_with_benchmarks, build_plan_export_bundle}; use spec_core::generator::{ @@ -41,8 +44,8 @@ use spec_core::passport::{ apply_projected_passport_truth, build_passport_preserving_proof_state_with_context, build_passport_with_evidence, compute_contract_hash, ensure_gitignore_entry, passport_evidence_for_target, passport_target_proof, project_passport_truth_with_context, - read_passport, resolve_passport_freshness_for_target, rfc3339_now, target_proof_for_write, - write_passport, + read_passport, refresh_passport_target_proofs, resolve_passport_freshness_for_target, + rfc3339_now, target_proof_for_write, write_passport, }; use spec_core::pipeline::{ ParsedCargoTestResult, Verbosity, bun_available, cargo_available, output_module_prefix, @@ -132,7 +135,7 @@ impl ValidationSpecCollection { } const VALIDATE_JSON_SCHEMA_VERSION: u8 = 3; -const STATUS_JSON_SCHEMA_VERSION: u8 = 4; +const STATUS_JSON_SCHEMA_VERSION: u8 = 5; const EXPORT_ERROR_JSON_SCHEMA_VERSION: u8 = 4; const CONCURRENT_PASSPORT_WRITER_TTL_SECS: u64 = 300; @@ -208,6 +211,8 @@ struct JsonStatusEntry { escape_hatch_gate: Option, #[serde(skip_serializing_if = "Option::is_none")] semantic_review: Option, + #[serde(skip_serializing_if = "Option::is_none")] + category_qualification: Option, } #[derive(Clone, Serialize)] @@ -258,6 +263,43 @@ fn workspace_config_error_to_json_entry(err: &WorkspaceConfigError) -> JsonError } } +struct CliProjectedUnitTruth { + preserved_truth: spec_core::passport::ProjectedPassportTruth, + category_qualification: Option, +} + +fn project_cli_unit_truth( + spec: &LoadedSpec, + existing_passport: Option<&spec_core::passport::Passport>, + projection_context: &PassportProjectionContext<'_>, + semantic_review_context: &SemanticReviewContext<'_>, + consumer_kind: ConsumerKind, +) -> CliProjectedUnitTruth { + let mut refreshed_passport = existing_passport.cloned(); + if let Some(passport) = refreshed_passport.as_mut() { + refresh_passport_target_proofs(passport, spec); + } + + let preserved_truth = project_passport_truth_with_context( + spec, + refreshed_passport.as_ref(), + projection_context, + semantic_review_context, + ); + let category_qualification = preserved_truth + .semantic_review + .as_ref() + .filter(|review| is_seam_category_claim_candidate(review)) + .map(|review| { + qualify_category_claim(consumer_kind, Some(review), Some(spec.spec.id.as_str())) + }); + + CliProjectedUnitTruth { + preserved_truth, + category_qualification, + } +} + fn emit_json_validate_response(response: &JsonValidateResponse) -> Result<()> { let json = serde_json::to_string_pretty(response)?; print!("{json}"); @@ -1426,12 +1468,14 @@ fn build_benchmark_projection_request( for spec in &validation_specs.root_specs { let source_path = Path::new(&spec.source.file_path); let passport = read_passport(source_path).unwrap_or_default(); - let projected_truth = project_passport_truth_with_context( + let projected_unit_truth = project_cli_unit_truth( spec, passport.as_ref(), &projection_context, &semantic_review_context, + ConsumerKind::Benchmark, ); + let projected_truth = projected_unit_truth.preserved_truth; let target_evidence = passport .as_ref() .and_then(|passport| passport_evidence_for_target(passport, target_language)); @@ -1455,6 +1499,7 @@ fn build_benchmark_projection_request( BenchmarkCaseTruth { status: benchmark_truth_status_from_health(health.status), reason: health.reason, + semantic_review: projected_truth.semantic_review.clone(), semantic_support_status: semantic_review .as_ref() .map(|review| review.effective_support_status()), @@ -1740,12 +1785,14 @@ fn status_command_for_target( None } }; - let projected_truth = project_passport_truth_with_context( + let projected_unit_truth = project_cli_unit_truth( spec, passport.as_ref(), &projection_context, &semantic_review_context, + ConsumerKind::Status, ); + let projected_truth = projected_unit_truth.preserved_truth; let target_evidence = passport .as_ref() .and_then(|passport| passport_evidence_for_target(passport, target_language)); @@ -1783,6 +1830,7 @@ fn status_command_for_target( proof_coverage, escape_hatch_gate, semantic_review, + category_qualification: projected_unit_truth.category_qualification, }); } @@ -1809,6 +1857,7 @@ fn status_command_for_target( proof_coverage: None, escape_hatch_gate: None, semantic_review: None, + category_qualification: None, }); continue; } @@ -1829,6 +1878,7 @@ fn status_command_for_target( proof_coverage: None, escape_hatch_gate: None, semantic_review: None, + category_qualification: None, }); } @@ -2255,12 +2305,14 @@ fn build_full_benchmark_projection_request( for spec in &validation_specs.root_specs { let source_path = Path::new(&spec.source.file_path); let passport = read_passport(source_path).unwrap_or_default(); - let projected_truth = project_passport_truth_with_context( + let projected_unit_truth = project_cli_unit_truth( spec, passport.as_ref(), &projection_context, &semantic_review_context, + ConsumerKind::Snapshot, ); + let projected_truth = projected_unit_truth.preserved_truth; let target_evidence = passport .as_ref() .and_then(|passport| passport_evidence_for_target(passport, target_language)); @@ -2284,6 +2336,7 @@ fn build_full_benchmark_projection_request( BenchmarkCaseTruth { status: snapshot_benchmark_truth_status_from_health(health.status), reason: health.reason, + semantic_review: projected_truth.semantic_review.clone(), semantic_support_status: semantic_review .as_ref() .map(|review| review.effective_support_status()), diff --git a/spec-cli/tests/cli.rs b/spec-cli/tests/cli.rs index 4ec1bfa..330aff4 100644 --- a/spec-cli/tests/cli.rs +++ b/spec-cli/tests/cli.rs @@ -750,6 +750,7 @@ fn supported_pricing_quote_semantic_review( SemanticReview { verdict, compatibility_key: DATA_SEAM_COMPATIBILITY_KEY.to_string(), + descriptor_id: Some("pricing_quote.ecommerce.v1".to_string()), support_status: None, unsupported_reason_codes: vec![], rewrite_hints: vec![], @@ -1240,6 +1241,7 @@ fn unsupported_function_semantic_review(summary: &str) -> SemanticReview { SemanticReview { verdict: SemanticVerdict::UnderSpecified, compatibility_key: "unsupported.function.v1".to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Unsupported), unsupported_reason_codes: vec![ UnsupportedFunctionReasonCode::UnsupportedRequiredArgumentExpression, @@ -1265,6 +1267,7 @@ fn supported_function_semantic_review( SemanticReview { verdict, compatibility_key: compatibility_key.to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Supported), unsupported_reason_codes: vec![], rewrite_hints: vec![], @@ -2686,7 +2689,7 @@ local_tests: assert!(output.status.success()); let bundle: Value = serde_json::from_slice(&output.stdout).unwrap(); - assert_eq!(bundle["schema_version"], 4); + assert_eq!(bundle["schema_version"], 5); assert_eq!(bundle["units"].as_array().unwrap().len(), 2); assert!(bundle.get("graph").is_some()); assert!(bundle.get("molecule_tests").is_some()); @@ -4737,6 +4740,13 @@ fn seed_passport_semantic_review_compatibility_key( ) -> Value { let mut passport = read_passport_json(passport_path); passport["semantic_review"]["compatibility_key"] = serde_json::json!(compatibility_key); + if compatibility_key == DATA_SEAM_COMPATIBILITY_KEY { + passport["semantic_review"]["descriptor_id"] = + serde_json::json!("pricing_quote.ecommerce.v1"); + } else if compatibility_key == SUM_SEAM_COMPATIBILITY_KEY { + passport["semantic_review"]["descriptor_id"] = + serde_json::json!("discount_strategy.ecommerce.v1"); + } fs::write( passport_path, serde_json::to_string_pretty(&passport).unwrap(), @@ -6448,7 +6458,7 @@ fn spec_status_json_and_export_include_compatibility_key_for_data_semantic_revie "status stays non-green because sibling helper units remain untested" ); let status_json = parse_stdout_json(&status_output); - assert_eq!(status_json["schema_version"], 4); + assert_eq!(status_json["schema_version"], 5); let unit = status_units(&status_json) .iter() .find(|unit| unit["id"] == "pricing/pricing_quote") @@ -6459,7 +6469,7 @@ fn spec_status_json_and_export_include_compatibility_key_for_data_semantic_revie let export_output = run_in(&project_dir, &["export", "units"]); assert_output_success("supported data export should succeed", &export_output); let export_json = parse_stdout_json(&export_output); - assert_eq!(export_json["schema_version"], 4); + assert_eq!(export_json["schema_version"], 5); let passport = export_json["passports"] .as_array() .unwrap() @@ -8326,7 +8336,7 @@ body: ); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); let units = status_units(&json); assert_eq!(units.len(), 1); assert_eq!(units[0]["id"], "pricing/bad"); @@ -8362,7 +8372,7 @@ fn spec_status_json_loader_error_surfaces_in_response() { ); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); let loader_errors = json["loader_errors"].as_array().unwrap(); assert!( !loader_errors.is_empty(), @@ -8661,7 +8671,7 @@ fn spec_status_zero_roots_is_non_green() { ); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); assert_eq!(json["roots"], serde_json::json!([])); let loader_errors = json["loader_errors"].as_array().unwrap(); assert_eq!(loader_errors[0]["code"], "SPEC_NO_LIBRARY_ROOTS"); @@ -13214,7 +13224,7 @@ fn export_emits_schema_v3_bundle_for_valid_cross_library_dep() { assert!(output.status.success(), "export should succeed"); let bundle: Value = serde_json::from_slice(&output.stdout).unwrap(); - assert_eq!(bundle["schema_version"], 4); + assert_eq!(bundle["schema_version"], 5); let edges = bundle["graph"]["edges"].as_array().unwrap(); assert!( edges.iter().any(|edge| { @@ -13962,7 +13972,7 @@ fn status_json_surfaces_missing_library_path_as_loader_error() { ); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); assert_eq!(json["units"], serde_json::json!([])); let loader_errors = json["loader_errors"].as_array().unwrap(); assert_eq!(loader_errors.len(), 1); @@ -14879,7 +14889,7 @@ fn plan_export_matches_checked_in_fixture_and_preserves_spec_export_surface() { let spec_export = run_in(&ecommerce_dir, &["export", "units"]); assert_output_success("spec export should remain unchanged", &spec_export); let spec_export_json = parse_stdout_json(&spec_export); - assert_eq!(spec_export_json["schema_version"], 4); + assert_eq!(spec_export_json["schema_version"], 5); assert!(spec_export_json.get("plan").is_none(), "{spec_export_json}"); assert!( spec_export_json.get("units").is_some(), @@ -14929,7 +14939,7 @@ fn status_benchmark_root_contract_matches_frozen_fixture() { assert_output_success("benchmark-root status should succeed", &output); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); assert_eq!(json["benchmarks"][0]["path_scope"], "full"); assert_eq!(json["benchmarks"][0]["benchmark_status"], "passing"); assert!( @@ -15101,7 +15111,7 @@ fn export_benchmark_root_contract_matches_frozen_fixture() { assert_output_success("benchmark-root export should succeed", &output); let json = parse_stdout_json(&output); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); assert_eq!(json["benchmarks"][0]["path_scope"], "full"); assert_eq!(json["benchmarks"][0]["benchmark_status"], "passing"); assert!( @@ -15156,10 +15166,13 @@ fn benchmark_snapshot_writes_seeded_positive_negative_and_reserved_outputs() { .unwrap(), ) .unwrap(); - assert_eq!(ecom_snapshot["projection"]["benchmark_status"], "passing"); + assert_eq!( + ecom_snapshot["projection"]["benchmark_status"], + "incomplete" + ); assert_eq!( ecom_snapshot["projection"]["readability_review_status"], - "current" + "stale" ); let crosslib_snapshot: Value = serde_json::from_str( @@ -15186,7 +15199,7 @@ fn benchmark_snapshot_writes_seeded_positive_negative_and_reserved_outputs() { .unwrap(); assert_eq!( service_snapshot["projection"]["benchmark_status"], - "incomplete" + "invalid" ); assert_eq!(service_snapshot["projection"]["gate_status"], "open"); assert_eq!( @@ -15246,7 +15259,7 @@ fn status_json_surfaces_invalid_benchmark_registry_root_machine_readably() { normalized_loader_errors[0]["path"] = Value::String("/tmp/benchmark-fixture/benchmarks/labels.json".to_string()); - assert_eq!(json["schema_version"], 4); + assert_eq!(json["schema_version"], 5); assert_eq!(loader_errors.len(), 1); assert_eq!( normalized_loader_errors, diff --git a/spec-cli/tests/fixtures/benchmarks/export-ecommerce-full.json b/spec-cli/tests/fixtures/benchmarks/export-ecommerce-full.json index ade2df3..9ffea97 100644 --- a/spec-cli/tests/fixtures/benchmarks/export-ecommerce-full.json +++ b/spec-cli/tests/fixtures/benchmarks/export-ecommerce-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "spec_version": "0.3.0", "exported_at": "", "provenance": { @@ -395,6 +395,312 @@ "source_file": "examples/ecommerce/units/pricing/pricing_quote.unit.spec" } ], + "projected_units": [ + { + "id": "money/round", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.helper.identity_passthrough.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Round monetary values (placeholder for compilation proof)." + }, + { + "path": "contract.inputs", + "summary": "1 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function round with 1 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "pricing/apply_discount", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply a discount to a subtotal while keeping the result nonnegative." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "2 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "pricing/apply_tax", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Add sales tax to a subtotal using a rate expressed as a decimal fraction and round the total." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "pricing/calculate_total", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Combine discount and tax so a checkout flow can produce the final price." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function calculate_total with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "pricing/calculate_total_guarded_tax", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the total after discounting the subtotal and then applying tax while clamping the tax rate at zero." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function calculate_total_guarded_tax with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "pricing/discount_strategy", + "semantic_review": { + "verdict": "backend_only_meaning_preserved", + "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "reason_codes": [ + "backend_only_execution_marker", + "proof_helper_only_marker" + ], + "summary": "backend-only execution markers are present without changing authored meaning", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Represent mutually exclusive discount strategies for checkout pricing." + }, + { + "path": "sum.variants", + "summary": "3 authored variant(s)" + }, + { + "path": "methods", + "summary": "2 semantic method(s) on pricing/discount_strategy" + } + ], + "executable_surfaces": [ + { + "path": "sum", + "summary": "projects to Rust enum DiscountStrategy" + }, + { + "path": "methods.*.lowering.rust.body", + "summary": "2 executable semantic method(s)" + }, + { + "path": "backends.rust.derives", + "summary": "Rust derives contribute backend-only execution metadata" + } + ], + "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + } + }, + { + "id": "pricing/pricing_quote", + "semantic_review": { + "verdict": "backend_only_meaning_preserved", + "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "reason_codes": [ + "backend_only_execution_marker" + ], + "summary": "backend-only execution markers are present without changing authored meaning", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Quote a checkout total from subtotal plus discount and tax rates." + }, + { + "path": "data.fields", + "summary": "3 authored data field(s)" + }, + { + "path": "constructors", + "summary": "1 authored constructor(s)" + }, + { + "path": "methods", + "summary": "2 semantic method(s) on pricing/pricing_quote" + } + ], + "executable_surfaces": [ + { + "path": "data", + "summary": "projects to Rust struct PricingQuote" + }, + { + "path": "constructors", + "summary": "1 executable constructor(s)" + }, + { + "path": "methods.*.lowering.rust.body", + "summary": "2 executable semantic method(s)" + }, + { + "path": "backends.rust.derives", + "summary": "Rust derives contribute backend-only execution metadata" + } + ], + "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + } + } + ], "molecule_tests": [ { "id": "pricing/checkout_flow", @@ -1343,6 +1649,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -1571,6 +1878,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -1763,7 +2071,7 @@ "accounting_status": "valid", "benchmark_status": "passing", "gate_status": "satisfied", - "readability_review_status": "current", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated benchmark surfaces remain readable and structurally traceable for the seeded BENCH-ECOM closure." @@ -1892,6 +2200,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -1907,6 +2222,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ diff --git a/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json b/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json index 88efea2..297579f 100644 --- a/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json +++ b/spec-cli/tests/fixtures/benchmarks/export-service-billing-partial-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "spec_version": "0.3.0", "exported_at": "", "provenance": { @@ -367,6 +367,166 @@ "source_file": "examples/service/units/billing/pricing_quote.unit.spec" } ], + "projected_units": [ + { + "id": "billing/apply_membership_discount", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the subtotal after applying the membership discount rate and clamping at zero." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "2 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_membership_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/apply_regional_fee", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the discounted subtotal after applying the regional fee rate." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_regional_fee with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/checkout_net_total", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and then applying the regional fee." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/checkout_net_total_guarded_fee", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and applying a nonnegative regional fee only." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total_guarded_fee with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/discount_strategy", + "semantic_review": null, + "category_qualification": null + }, + { + "id": "billing/pricing_quote", + "semantic_review": null, + "category_qualification": null + } + ], "molecule_tests": [ { "id": "billing/checkout_declined_discount_flow", @@ -1015,7 +1175,7 @@ } ], "generated_at": "", - "source_file": "examples/service/units/billing/discount_strategy.unit.spec", + "source_file": "", "evidence": { "build_status": "pass", "test_results": [ @@ -1440,7 +1600,7 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "partial", - "accounting_status": "partial_valid", + "accounting_status": "partial_invalid", "cases": [ { "case_id": "billing/apply_membership_discount", diff --git a/spec-cli/tests/fixtures/benchmarks/export-service-full.json b/spec-cli/tests/fixtures/benchmarks/export-service-full.json index e6b8be2..004f656 100644 --- a/spec-cli/tests/fixtures/benchmarks/export-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/export-service-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "spec_version": "0.3.0", "exported_at": "", "provenance": { @@ -367,6 +367,166 @@ "source_file": "examples/service/units/billing/pricing_quote.unit.spec" } ], + "projected_units": [ + { + "id": "billing/apply_membership_discount", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the subtotal after applying the membership discount rate and clamping at zero." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "2 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_membership_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/apply_regional_fee", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the discounted subtotal after applying the regional fee rate." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_regional_fee with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/checkout_net_total", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and then applying the regional fee." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/checkout_net_total_guarded_fee", + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and applying a nonnegative regional fee only." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total_guarded_fee with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + }, + "category_qualification": null + }, + { + "id": "billing/discount_strategy", + "semantic_review": null, + "category_qualification": null + }, + { + "id": "billing/pricing_quote", + "semantic_review": null, + "category_qualification": null + } + ], "molecule_tests": [ { "id": "billing/checkout_declined_discount_flow", @@ -1015,7 +1175,7 @@ } ], "generated_at": "", - "source_file": "examples/service/units/billing/discount_strategy.unit.spec", + "source_file": "", "evidence": { "build_status": "pass", "test_results": [ @@ -1440,8 +1600,8 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "full", - "accounting_status": "valid", - "benchmark_status": "passing", + "accounting_status": "invalid", + "benchmark_status": "invalid", "gate_status": "open", "readability_review_status": "stale", "readability_verdict": { @@ -1454,7 +1614,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 4, + "positive_credit_cases": 0, "case_status_counts": { "invalid": 0, "failing": 0, @@ -1504,7 +1664,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/apply_regional_fee", @@ -1520,7 +1680,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total", @@ -1535,7 +1695,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total_guarded_fee", @@ -1550,7 +1710,7 @@ "examples/service/units/billing/checkout_declined_discount_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/discount_strategy", diff --git a/spec-cli/tests/fixtures/benchmarks/status-apply-discount-partial-full.json b/spec-cli/tests/fixtures/benchmarks/status-apply-discount-partial-full.json index 2c764e4..65b85ad 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-apply-discount-partial-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-apply-discount-partial-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/benchmarks/status-ecommerce-full.json b/spec-cli/tests/fixtures/benchmarks/status-ecommerce-full.json index a3c29d1..726e3f5 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-ecommerce-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-ecommerce-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", @@ -320,6 +320,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -354,6 +355,13 @@ } ], "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -396,6 +404,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -437,6 +446,13 @@ } ], "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } } ], @@ -779,6 +795,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -813,6 +830,13 @@ } ], "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -855,6 +879,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -896,6 +921,13 @@ } ], "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } } ], @@ -909,7 +941,7 @@ "accounting_status": "valid", "benchmark_status": "passing", "gate_status": "satisfied", - "readability_review_status": "current", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated benchmark surfaces remain readable and structurally traceable for the seeded BENCH-ECOM closure." @@ -1038,6 +1070,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -1053,6 +1092,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ diff --git a/spec-cli/tests/fixtures/benchmarks/status-ecommerce-pricing-partial-full.json b/spec-cli/tests/fixtures/benchmarks/status-ecommerce-pricing-partial-full.json index 510e1da..c6e97e0 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-ecommerce-pricing-partial-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-ecommerce-pricing-partial-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [], "units": [], "benchmarks": [ @@ -77,6 +77,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -92,6 +99,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ diff --git a/spec-cli/tests/fixtures/benchmarks/status-repo-root-contract.json b/spec-cli/tests/fixtures/benchmarks/status-repo-root-contract.json index d72101c..7a14f2b 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-repo-root-contract.json +++ b/spec-cli/tests/fixtures/benchmarks/status-repo-root-contract.json @@ -1,4 +1,4 @@ { - "schema_version": 4, + "schema_version": 5, "scope_authority": "inventory_only" } diff --git a/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json b/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json index b397ce4..d7a53db 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-repo-root-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "scope_authority": "inventory_only", "roots": [ { @@ -481,6 +481,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -515,6 +516,13 @@ } ], "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -557,6 +565,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -598,6 +607,13 @@ } ], "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } } ], @@ -959,7 +975,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -970,7 +986,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -981,7 +997,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -992,7 +1008,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } } @@ -1906,287 +1922,6 @@ ], "molecule_tests": [] }, - { - "root": "spec-cli/tests/fixtures/benchmarks/rust_v1_closure/lane_b/companion_negative_missing_current_proof", - "units": [ - { - "id": "money/round", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_discount", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_discount", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_tax", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_tax", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_tax", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/base_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/calculate_total", - "status": "invalid", - "errors": [ - { - "unit": "pricing/calculate_total", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/calculate_total", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_discount", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_discount", - "path2": "" - }, - { - "unit": "pricing/apply_discount", - "code": "SPEC_LIBRARY_CRATE_ALIAS_MISSING", - "path": "", - "value": "shared", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply a discount while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_discount with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" - } - }, - { - "id": "pricing/apply_tax", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_tax", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_tax", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply tax while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_tax with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" - } - }, - { - "id": "pricing/calculate_total", - "status": "invalid", - "errors": [ - { - "unit": "pricing/calculate_total", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/calculate_total", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/checkout_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - } - ], - "molecule_tests": [] - }, - { - "root": "spec-cli/tests/fixtures/benchmarks/rust_v1_closure/lane_b/companion_negative_missing_current_proof/shared-spec", - "units": [ - { - "id": "money/round", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_tax", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/base_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/calculate_total", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - } - ], - "molecule_tests": [] - }, { "root": "spec-cli/tests/fixtures/m19/semantic_falsification_pack", "units": [ @@ -2974,6 +2709,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -3008,6 +2744,13 @@ } ], "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -3050,6 +2793,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -3091,6 +2835,13 @@ } ], "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -3402,7 +3153,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -3413,7 +3164,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -3424,7 +3175,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -3435,7 +3186,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -4202,275 +3953,6 @@ "backend_execution_status": "unknown" } }, - { - "id": "money/round", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_discount", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_discount", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_tax", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_tax", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_tax", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/base_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/calculate_total", - "status": "invalid", - "errors": [ - { - "unit": "pricing/calculate_total", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/calculate_total", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_discount", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_discount", - "path2": "" - }, - { - "unit": "pricing/apply_discount", - "code": "SPEC_LIBRARY_CRATE_ALIAS_MISSING", - "path": "", - "value": "shared", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply a discount while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_discount with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" - } - }, - { - "id": "pricing/apply_tax", - "status": "invalid", - "errors": [ - { - "unit": "pricing/apply_tax", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/apply_tax", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply tax while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_tax with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" - } - }, - { - "id": "pricing/calculate_total", - "status": "invalid", - "errors": [ - { - "unit": "pricing/calculate_total", - "code": "SPEC_DUPLICATE_ID", - "path": "", - "id": "pricing/calculate_total", - "path2": "" - } - ], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/checkout_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "money/round", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_discount", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/apply_tax", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/base_nested_chain3", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, - { - "id": "pricing/calculate_total", - "status": "untested", - "reason": "no evidence", - "errors": [], - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "unknown", - "backend_execution_status": "unknown" - } - }, { "id": "billing/apply_membership_discount", "status": "untested", @@ -4865,7 +4347,7 @@ "accounting_status": "valid", "benchmark_status": "passing", "gate_status": "satisfied", - "readability_review_status": "current", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated benchmark surfaces remain readable and structurally traceable for the seeded BENCH-ECOM closure." @@ -4994,6 +4476,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -5009,6 +4498,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ @@ -5039,8 +4535,8 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "full", - "accounting_status": "valid", - "benchmark_status": "passing", + "accounting_status": "invalid", + "benchmark_status": "invalid", "gate_status": "open", "readability_review_status": "stale", "readability_verdict": { @@ -5053,7 +4549,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 4, + "positive_credit_cases": 0, "case_status_counts": { "invalid": 0, "failing": 0, @@ -5103,7 +4599,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/apply_regional_fee", @@ -5119,7 +4615,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total", @@ -5134,7 +4630,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total_guarded_fee", @@ -5149,7 +4645,7 @@ "examples/service/units/billing/checkout_declined_discount_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/discount_strategy", diff --git a/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json b/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json index 0c286c4..d7a53db 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-repo-root-service-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "scope_authority": "inventory_only", "roots": [ { @@ -481,6 +481,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" @@ -515,6 +516,13 @@ } ], "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } }, { @@ -557,6 +565,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], @@ -598,6 +607,13 @@ } ], "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" } } ], @@ -959,7 +975,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -970,7 +986,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -981,7 +997,7 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, @@ -992,947 +1008,3245 @@ "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } } ], "molecule_tests": [] - } - ], - "units": [ + }, { - "id": "pricing/apply_discount", - "status": "valid", - "errors": [], - "evidence_at": "", - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply a discount while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" + "root": "semantic-families/function.arithmetic_leaf.monotone_down_nonnegative.v1/fixtures/aligned", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_discount with 2 input(s)" + }, + { + "id": "pricing/apply_discount_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "evaluator_scope": "supported_function_surface" - } + } + ], + "molecule_tests": [] }, { - "id": "pricing/apply_tax", - "status": "valid", - "errors": [], - "evidence_at": "", - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply tax while importing the shared round helper from a sibling spec library." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" + "root": "semantic-families/function.arithmetic_leaf.monotone_down_nonnegative.v1/fixtures/drift", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_tax with 2 input(s)" + }, + { + "id": "pricing/apply_discount_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "evaluator_scope": "supported_function_surface" - } + } + ], + "molecule_tests": [] }, { - "id": "pricing/calculate_total", - "status": "valid", - "errors": [], - "evidence_at": "", - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - } + "root": "semantic-families/function.arithmetic_leaf.monotone_down_nonnegative.v1/fixtures/under_specified", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] }, { - "id": "pricing/checkout_nested_chain3", - "status": "valid", - "errors": [], - "evidence_at": "", - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "under_specified", - "compatibility_key": "unsupported.function.v1", - "support_status": "unsupported", - "unsupported_reason_codes": [ - "unsupported_dep_topology" - ], - "rewrite_hints": [ - "Use zero or one helper dep for arithmetic leaves, exactly two supported dep callables for wrapper pipelines, or exactly three supported function dep callables for chain3 pipelines." - ], - "reason_codes": [ - "unsupported_surface" - ], - "summary": "declared function deps fall outside the supported reviewer topology", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the app checkout total by extending the shared nested chain3 with one outer surcharge and loyalty discount." - }, - { - "path": "contract.inputs", - "summary": "5 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "deps", - "summary": "3 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" + "root": "semantic-families/function.arithmetic_leaf.monotone_down_nonnegative.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function checkout_nested_chain3 with 5 input(s)" + }, + { + "id": "pricing/apply_discount_control_flow_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } - ], - "evaluator_scope": "unsupported_surface" - } + } + ], + "molecule_tests": [] }, { - "id": "money/round", - "status": "valid", - "errors": [], - "evidence_at": "", - "freshness": { - "authored_truth_digest": "", - "authored_truth_status": "fresh", - "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.helper.identity_passthrough.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Round monetary values (placeholder for compilation proof)." - }, - { - "path": "contract.inputs", - "summary": "1 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" + "root": "semantic-families/function.arithmetic_leaf.monotone_up.v1/fixtures/aligned", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.helper.identity_passthrough.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Round monetary values (placeholder for compilation proof)." + }, + { + "path": "contract.inputs", + "summary": "1 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function round with 1 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function round with 1 input(s)" + }, + { + "id": "pricing/apply_tax_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Add sales tax to a subtotal using a rate expressed as a decimal fraction and round the total." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax_aligned with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" } - ], - "evaluator_scope": "supported_function_surface" + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.arithmetic_leaf.monotone_up.v1/fixtures/drift", + "units": [ + { + "id": "pricing/apply_tax_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.arithmetic_leaf.monotone_up.v1/fixtures/under_specified", + "units": [ + { + "id": "pricing/apply_tax_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.arithmetic_leaf.monotone_up.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "pricing/apply_tax_control_flow_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.helper.identity_passthrough.v1/fixtures/aligned", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "passthrough/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.helper.identity_passthrough.v1/fixtures/drift", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "semantic_drift/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.helper.identity_passthrough.v1/fixtures/under_specified", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "vague/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.helper.identity_passthrough.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "control_flow/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.chain3.v1/fixtures/aligned", + "units": [ + { + "id": "pricing/base_nested_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_nested_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.chain3.v1/fixtures/drift", + "units": [ + { + "id": "pricing/checkout_chain3_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.chain3.v1/fixtures/under_specified", + "units": [ + { + "id": "pricing/checkout_chain3_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.chain3.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "pricing/checkout_chain3_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.normalized_required_arg.v1/fixtures/aligned", + "units": [ + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.normalized_required_arg.v1/fixtures/drift", + "units": [ + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.normalized_required_arg.v1/fixtures/under_specified", + "units": [ + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.normalized_required_arg.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.v1/fixtures/aligned", + "units": [ + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.v1/fixtures/drift", + "units": [ + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.v1/fixtures/under_specified", + "units": [ + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "semantic-families/function.wrapper.pipeline.v1/fixtures/unsupported_near_miss", + "units": [ + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "spec-cli/tests/fixtures/m19/semantic_falsification_pack", + "units": [ + { + "id": "billing/apply_membership_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_membership_discount_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_membership_discount_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_membership_discount_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "spec-cli/tests/fixtures/m20/unsupported_truth_pack", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_control_flow", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax_control_flow", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_total_bad_body_shape", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_total_bad_dep_topology", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + }, + { + "root": "spec-cli/tests/fixtures/typescript_local_supported_graph", + "units": [ + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/display_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + } + ], + "molecule_tests": [] + } + ], + "units": [ + { + "id": "pricing/apply_discount", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply a discount while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply tax while importing the shared round helper from a sibling spec library." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/calculate_total", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_nested_chain3", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "under_specified", + "compatibility_key": "unsupported.function.v1", + "support_status": "unsupported", + "unsupported_reason_codes": [ + "unsupported_dep_topology" + ], + "rewrite_hints": [ + "Use zero or one helper dep for arithmetic leaves, exactly two supported dep callables for wrapper pipelines, or exactly three supported function dep callables for chain3 pipelines." + ], + "reason_codes": [ + "unsupported_surface" + ], + "summary": "declared function deps fall outside the supported reviewer topology", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the app checkout total by extending the shared nested chain3 with one outer surcharge and loyalty discount." + }, + { + "path": "contract.inputs", + "summary": "5 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "deps", + "summary": "3 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_nested_chain3 with 5 input(s)" + } + ], + "evaluator_scope": "unsupported_surface" + } + }, + { + "id": "money/round", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.helper.identity_passthrough.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Round monetary values (placeholder for compilation proof)." + }, + { + "path": "contract.inputs", + "summary": "1 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function round with 1 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_discount", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Apply a discount to a subtotal while keeping the result nonnegative." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "2 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Add sales tax to a subtotal using a rate expressed as a decimal fraction and round the total." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/calculate_total", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Combine discount and tax so a checkout flow can produce the final price." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function calculate_total with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/calculate_total_guarded_tax", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the total after discounting the subtotal and then applying tax while clamping the tax rate at zero." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function calculate_total_guarded_tax with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/discount_strategy", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "backend_execution_digest": "sha256:06039f3e8df3a5ebeb557763c70a82a999f315e054df964a4af0bf7fd531f2e1", + "authored_truth_status": "fresh", + "backend_execution_status": "fresh" + }, + "markers": [ + { + "id": "backend_rust_derives", + "path": "backends.rust.derives" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discount_amount.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discounted_subtotal.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.percentage_example_holds.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.fixed_amount_example_holds.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.fixed_amount_capped_behavior_holds.lowering.rust.body" + } + ], + "proof_coverage": [ + { + "id": "variant.none", + "surfaces": [ + "atom", + "molecule" + ] + }, + { + "id": "variant.percentage", + "surfaces": [ + "atom", + "molecule" + ] + }, + { + "id": "variant.fixed_amount", + "surfaces": [ + "atom", + "molecule" + ] + }, + { + "id": "behavior.fixed_amount_capped", + "surfaces": [ + "atom", + "molecule" + ] + } + ], + "escape_hatch_gate": { + "status": "closed", + "required_surfaces": [ + "atom", + "molecule" + ], + "present_surfaces": [ + "atom", + "molecule" + ], + "missing_surfaces": [] + }, + "semantic_review": { + "verdict": "backend_only_meaning_preserved", + "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "reason_codes": [ + "backend_only_execution_marker", + "proof_helper_only_marker" + ], + "summary": "backend-only execution markers are present without changing authored meaning", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Represent mutually exclusive discount strategies for checkout pricing." + }, + { + "path": "sum.variants", + "summary": "3 authored variant(s)" + }, + { + "path": "methods", + "summary": "2 semantic method(s) on pricing/discount_strategy" + } + ], + "executable_surfaces": [ + { + "path": "sum", + "summary": "projects to Rust enum DiscountStrategy" + }, + { + "path": "methods.*.lowering.rust.body", + "summary": "2 executable semantic method(s)" + }, + { + "path": "backends.rust.derives", + "summary": "Rust derives contribute backend-only execution metadata" + } + ], + "evaluator_scope": "supported_sum_surface" + }, + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + } + }, + { + "id": "pricing/pricing_quote", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "backend_execution_digest": "sha256:c8273f70a404bcaf8efe4b6e20c12ade43c33ce209c23e7192f3c7e2fbfc8205", + "authored_truth_status": "fresh", + "backend_execution_status": "fresh" + }, + "markers": [ + { + "id": "backend_rust_derives", + "path": "backends.rust.derives" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discounted_subtotal.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.total.lowering.rust.body" + } + ], + "escape_hatch_gate": { + "status": "closed", + "required_surfaces": [ + "atom", + "molecule" + ], + "present_surfaces": [ + "atom", + "molecule" + ], + "missing_surfaces": [] + }, + "semantic_review": { + "verdict": "backend_only_meaning_preserved", + "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "reason_codes": [ + "backend_only_execution_marker" + ], + "summary": "backend-only execution markers are present without changing authored meaning", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Quote a checkout total from subtotal plus discount and tax rates." + }, + { + "path": "data.fields", + "summary": "3 authored data field(s)" + }, + { + "path": "constructors", + "summary": "1 authored constructor(s)" + }, + { + "path": "methods", + "summary": "2 semantic method(s) on pricing/pricing_quote" + } + ], + "executable_surfaces": [ + { + "path": "data", + "summary": "projects to Rust struct PricingQuote" + }, + { + "path": "constructors", + "summary": "1 executable constructor(s)" + }, + { + "path": "methods.*.lowering.rust.body", + "summary": "2 executable semantic method(s)" + }, + { + "path": "backends.rust.derives", + "summary": "Rust derives contribute backend-only execution metadata" + } + ], + "evaluator_scope": "supported_data_surface" + }, + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + } + }, + { + "id": "billing/apply_membership_discount", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the subtotal after applying the membership discount rate and clamping at zero." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "2 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_membership_discount with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "billing/apply_regional_fee", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the discounted subtotal after applying the regional fee rate." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_regional_fee with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "billing/checkout_net_total", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and then applying the regional fee." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "billing/checkout_net_total_guarded_fee", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Return the net checkout total after discounting the subtotal and applying a nonnegative regional fee only." + }, + { + "path": "contract.inputs", + "summary": "3 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "2 declared dep(s)" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function checkout_net_total_guarded_fee with 3 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "billing/discount_strategy", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "backend_execution_digest": "sha256:c02a7324f2d3cfba643e1e2e46163396f6f68b0f288d702131e5836efd4f693e", + "authored_truth_status": "fresh", + "backend_execution_status": "fresh" + }, + "markers": [ + { + "id": "backend_rust_derives", + "path": "backends.rust.derives" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discount_amount.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discounted_subtotal.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.declined_example_holds.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.percentage_example_holds.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.fixed_credit_capped_behavior_holds.lowering.rust.body" + } + ], + "escape_hatch_gate": { + "status": "closed", + "required_surfaces": [ + "atom", + "molecule" + ], + "present_surfaces": [ + "atom", + "molecule" + ], + "missing_surfaces": [] + } + }, + { + "id": "billing/pricing_quote", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "backend_execution_digest": "sha256:055cc5bff0fee7d3cf7677dadb95219290ea4e38429003aa7605457338a7352f", + "authored_truth_status": "fresh", + "backend_execution_status": "fresh" + }, + "markers": [ + { + "id": "backend_rust_derives", + "path": "backends.rust.derives" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.discounted_subtotal.lowering.rust.body" + }, + { + "id": "method_lowering_rust_body", + "path": "methods.total.lowering.rust.body" + } + ], + "escape_hatch_gate": { + "status": "closed", + "required_surfaces": [ + "atom", + "molecule" + ], + "present_surfaces": [ + "atom", + "molecule" + ], + "missing_surfaces": [] + } + }, + { + "id": "money/round", + "status": "valid", + "errors": [], + "evidence_at": "", + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.helper.identity_passthrough.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Round a decimal for reuse by sibling spec libraries." + }, + { + "path": "contract.inputs", + "summary": "1 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function round with 1 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_discount_control_flow_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.helper.identity_passthrough.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Round monetary values (placeholder for compilation proof)." + }, + { + "path": "contract.inputs", + "summary": "1 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function round with 1 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "fresh", + "backend_execution_status": "unknown" + }, + "semantic_review": { + "verdict": "aligned", + "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", + "support_status": "supported", + "summary": "authored semantics and executable lowering agree on the supported function surface", + "authored_surfaces": [ + { + "path": "intent.why", + "summary": "Add sales tax to a subtotal using a rate expressed as a decimal fraction and round the total." + }, + { + "path": "contract.inputs", + "summary": "2 authored input(s)" + }, + { + "path": "contract.returns", + "summary": "returns Decimal" + }, + { + "path": "contract.invariants", + "summary": "1 authored invariant(s)" + }, + { + "path": "deps", + "summary": "1 declared dep(s)" + }, + { + "path": "body.typescript", + "summary": "authored TypeScript body present" + } + ], + "executable_surfaces": [ + { + "path": "body.rust", + "summary": "projects to Rust function apply_tax_aligned with 2 input(s)" + } + ], + "evaluator_scope": "supported_function_surface" + } + }, + { + "id": "pricing/apply_tax_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/apply_tax_control_flow_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "passthrough/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "semantic_drift/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "vague/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "control_flow/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/base_nested_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_nested_chain3_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_chain3_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_chain3_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/checkout_chain3_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_aligned", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_discount_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_tax_leaf_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/pricing_total_wrapper_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } }, { - "id": "pricing/apply_discount", - "status": "valid", + "id": "billing/apply_membership_discount", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Apply a discount to a subtotal while keeping the result nonnegative." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "2 authored invariant(s)" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_discount with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "pricing/apply_tax", - "status": "valid", + "id": "billing/apply_membership_discount_drift", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Add sales tax to a subtotal using a rate expressed as a decimal fraction and round the total." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - }, - { - "path": "deps", - "summary": "1 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_tax with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "pricing/calculate_total", - "status": "valid", + "id": "billing/apply_membership_discount_under_specified", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.wrapper.pipeline.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Combine discount and tax so a checkout flow can produce the final price." - }, - { - "path": "contract.inputs", - "summary": "3 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - }, - { - "path": "deps", - "summary": "2 declared dep(s)" - }, - { - "path": "body.typescript", - "summary": "authored TypeScript body present" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function calculate_total with 3 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "pricing/calculate_total_guarded_tax", - "status": "valid", + "id": "billing/apply_membership_discount_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/apply_regional_fee_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_drift", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_under_specified", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "billing/checkout_net_total_unsupported_near_miss", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "money/round", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the total after discounting the subtotal and then applying tax while clamping the tax rate at zero." - }, - { - "path": "contract.inputs", - "summary": "3 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - }, - { - "path": "deps", - "summary": "2 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function calculate_total_guarded_tax with 3 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "pricing/discount_strategy", - "status": "valid", + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "backend_execution_digest": "sha256:06039f3e8df3a5ebeb557763c70a82a999f315e054df964a4af0bf7fd531f2e1", - "authored_truth_status": "fresh", - "backend_execution_status": "fresh" - }, - "markers": [ - { - "id": "backend_rust_derives", - "path": "backends.rust.derives" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discount_amount.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discounted_subtotal.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.percentage_example_holds.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.fixed_amount_example_holds.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.fixed_amount_capped_behavior_holds.lowering.rust.body" - } - ], - "proof_coverage": [ - { - "id": "variant.none", - "surfaces": [ - "atom", - "molecule" - ] - }, - { - "id": "variant.percentage", - "surfaces": [ - "atom", - "molecule" - ] - }, - { - "id": "variant.fixed_amount", - "surfaces": [ - "atom", - "molecule" - ] - }, - { - "id": "behavior.fixed_amount_capped", - "surfaces": [ - "atom", - "molecule" - ] - } - ], - "escape_hatch_gate": { - "status": "closed", - "required_surfaces": [ - "atom", - "molecule" - ], - "present_surfaces": [ - "atom", - "molecule" - ], - "missing_surfaces": [] - }, - "semantic_review": { - "verdict": "backend_only_meaning_preserved", - "compatibility_key": "sum.discount_strategy.v1", - "reason_codes": [ - "backend_only_execution_marker", - "proof_helper_only_marker" - ], - "summary": "backend-only execution markers are present without changing authored meaning", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Represent mutually exclusive discount strategies for checkout pricing." - }, - { - "path": "sum.variants", - "summary": "3 authored variant(s)" - }, - { - "path": "methods", - "summary": "2 semantic method(s) on pricing/discount_strategy" - } - ], - "executable_surfaces": [ - { - "path": "sum", - "summary": "projects to Rust enum DiscountStrategy" - }, - { - "path": "methods.*.lowering.rust.body", - "summary": "2 executable semantic method(s)" - }, - { - "path": "backends.rust.derives", - "summary": "Rust derives contribute backend-only execution metadata" - } - ], - "evaluator_scope": "supported_sum_surface" + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } }, { - "id": "pricing/pricing_quote", - "status": "valid", + "id": "pricing/apply_discount_control_flow", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "backend_execution_digest": "sha256:c8273f70a404bcaf8efe4b6e20c12ade43c33ce209c23e7192f3c7e2fbfc8205", - "authored_truth_status": "fresh", - "backend_execution_status": "fresh" - }, - "markers": [ - { - "id": "backend_rust_derives", - "path": "backends.rust.derives" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discounted_subtotal.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.total.lowering.rust.body" - } - ], - "escape_hatch_gate": { - "status": "closed", - "required_surfaces": [ - "atom", - "molecule" - ], - "present_surfaces": [ - "atom", - "molecule" - ], - "missing_surfaces": [] - }, - "semantic_review": { - "verdict": "backend_only_meaning_preserved", - "compatibility_key": "data.pricing_quote.v1", - "reason_codes": [ - "backend_only_execution_marker" - ], - "summary": "backend-only execution markers are present without changing authored meaning", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Quote a checkout total from subtotal plus discount and tax rates." - }, - { - "path": "data.fields", - "summary": "3 authored data field(s)" - }, - { - "path": "constructors", - "summary": "1 authored constructor(s)" - }, - { - "path": "methods", - "summary": "2 semantic method(s) on pricing/pricing_quote" - } - ], - "executable_surfaces": [ - { - "path": "data", - "summary": "projects to Rust struct PricingQuote" - }, - { - "path": "constructors", - "summary": "1 executable constructor(s)" - }, - { - "path": "methods.*.lowering.rust.body", - "summary": "2 executable semantic method(s)" - }, - { - "path": "backends.rust.derives", - "summary": "Rust derives contribute backend-only execution metadata" - } - ], - "evaluator_scope": "supported_data_surface" + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } }, { - "id": "billing/apply_membership_discount", - "status": "valid", + "id": "pricing/apply_tax", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_down_nonnegative.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the subtotal after applying the membership discount rate and clamping at zero." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "2 authored invariant(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_membership_discount with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "billing/apply_regional_fee", - "status": "valid", + "id": "pricing/apply_tax_control_flow", + "status": "untested", + "reason": "no evidence", + "errors": [], + "freshness": { + "authored_truth_digest": "", + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" + } + }, + { + "id": "pricing/calculate_total", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.arithmetic_leaf.monotone_up.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the discounted subtotal after applying the regional fee rate." - }, - { - "path": "contract.inputs", - "summary": "2 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function apply_regional_fee with 2 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "billing/checkout_net_total", - "status": "valid", + "id": "pricing/checkout_total", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.wrapper.pipeline.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the net checkout total after discounting the subtotal and then applying the regional fee." - }, - { - "path": "contract.inputs", - "summary": "3 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - }, - { - "path": "deps", - "summary": "2 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function checkout_net_total with 3 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "billing/checkout_net_total_guarded_fee", - "status": "valid", + "id": "pricing/checkout_total_bad_body_shape", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.wrapper.pipeline.normalized_required_arg.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Return the net checkout total after discounting the subtotal and applying a nonnegative regional fee only." - }, - { - "path": "contract.inputs", - "summary": "3 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - }, - { - "path": "contract.invariants", - "summary": "1 authored invariant(s)" - }, - { - "path": "deps", - "summary": "2 declared dep(s)" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function checkout_net_total_guarded_fee with 3 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "billing/discount_strategy", - "status": "valid", + "id": "pricing/checkout_total_bad_dep_topology", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "backend_execution_digest": "sha256:c02a7324f2d3cfba643e1e2e46163396f6f68b0f288d702131e5836efd4f693e", - "authored_truth_status": "fresh", - "backend_execution_status": "fresh" - }, - "markers": [ - { - "id": "backend_rust_derives", - "path": "backends.rust.derives" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discount_amount.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discounted_subtotal.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.declined_example_holds.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.percentage_example_holds.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.fixed_credit_capped_behavior_holds.lowering.rust.body" - } - ], - "escape_hatch_gate": { - "status": "closed", - "required_surfaces": [ - "atom", - "molecule" - ], - "present_surfaces": [ - "atom", - "molecule" - ], - "missing_surfaces": [] + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } }, { - "id": "billing/pricing_quote", - "status": "valid", + "id": "money/round", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "backend_execution_digest": "sha256:055cc5bff0fee7d3cf7677dadb95219290ea4e38429003aa7605457338a7352f", - "authored_truth_status": "fresh", - "backend_execution_status": "fresh" - }, - "markers": [ - { - "id": "backend_rust_derives", - "path": "backends.rust.derives" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.discounted_subtotal.lowering.rust.body" - }, - { - "id": "method_lowering_rust_body", - "path": "methods.total.lowering.rust.body" - } - ], - "escape_hatch_gate": { - "status": "closed", - "required_surfaces": [ - "atom", - "molecule" - ], - "present_surfaces": [ - "atom", - "molecule" - ], - "missing_surfaces": [] + "authored_truth_status": "unknown", + "backend_execution_status": "unknown" } }, { - "id": "money/round", - "status": "valid", + "id": "pricing/apply_discount", + "status": "untested", + "reason": "no evidence", "errors": [], - "evidence_at": "", "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" - }, - "semantic_review": { - "verdict": "aligned", - "compatibility_key": "function.helper.identity_passthrough.v1", - "support_status": "supported", - "summary": "authored semantics and executable lowering agree on the supported function surface", - "authored_surfaces": [ - { - "path": "intent.why", - "summary": "Round a decimal for reuse by sibling spec libraries." - }, - { - "path": "contract.inputs", - "summary": "1 authored input(s)" - }, - { - "path": "contract.returns", - "summary": "returns Decimal" - } - ], - "executable_surfaces": [ - { - "path": "body.rust", - "summary": "projects to Rust function round with 1 input(s)" - } - ], - "evaluator_scope": "supported_function_surface" } }, { - "id": "pricing/apply_discount", + "id": "pricing/apply_tax", "status": "untested", "reason": "no evidence", "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, { - "id": "pricing/apply_tax", + "id": "pricing/calculate_total", "status": "untested", "reason": "no evidence", "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, { - "id": "pricing/base_nested_chain3", + "id": "pricing/checkout_total", "status": "untested", "reason": "no evidence", "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } }, { - "id": "pricing/calculate_total", + "id": "pricing/display_total", "status": "untested", "reason": "no evidence", "errors": [], "freshness": { "authored_truth_digest": "", - "authored_truth_status": "fresh", + "authored_truth_status": "unknown", "backend_execution_status": "unknown" } } @@ -2033,7 +4347,7 @@ "accounting_status": "valid", "benchmark_status": "passing", "gate_status": "satisfied", - "readability_review_status": "current", + "readability_review_status": "stale", "readability_verdict": { "decision": "approved", "summary": "Generated benchmark surfaces remain readable and structurally traceable for the seeded BENCH-ECOM closure." @@ -2162,6 +4476,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/discount_strategy.spec.passport.json", "covering_molecule_evidence": [ @@ -2177,6 +4498,13 @@ "classification": "supported", "status": "valid", "semantic_support_status": "supported", + "category_qualification": { + "category_id": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", + "claim_status": "supported_qualified", + "positive_credit_eligibility": "eligible", + "reason_code": "qualified" + }, "proof_refs": { "passport": "examples/ecommerce/units/pricing/pricing_quote.spec.passport.json", "covering_molecule_evidence": [ @@ -2207,8 +4535,8 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "full", - "accounting_status": "valid", - "benchmark_status": "passing", + "accounting_status": "invalid", + "benchmark_status": "invalid", "gate_status": "open", "readability_review_status": "stale", "readability_verdict": { @@ -2221,7 +4549,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 4, + "positive_credit_cases": 0, "case_status_counts": { "invalid": 0, "failing": 0, @@ -2271,7 +4599,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/apply_regional_fee", @@ -2287,7 +4615,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total", @@ -2302,7 +4630,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total_guarded_fee", @@ -2317,7 +4645,7 @@ "examples/service/units/billing/checkout_declined_discount_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/discount_strategy", diff --git a/spec-cli/tests/fixtures/benchmarks/status-service-billing-partial-full.json b/spec-cli/tests/fixtures/benchmarks/status-service-billing-partial-full.json index f50cd49..55cba43 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-service-billing-partial-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-service-billing-partial-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [], "units": [], "benchmarks": [ @@ -9,7 +9,7 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "partial", - "accounting_status": "partial_valid", + "accounting_status": "partial_invalid", "cases": [ { "case_id": "billing/apply_membership_discount", diff --git a/spec-cli/tests/fixtures/benchmarks/status-service-full.json b/spec-cli/tests/fixtures/benchmarks/status-service-full.json index a0e5e00..62c072a 100644 --- a/spec-cli/tests/fixtures/benchmarks/status-service-full.json +++ b/spec-cli/tests/fixtures/benchmarks/status-service-full.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", @@ -564,8 +564,8 @@ "lifecycle": "active", "required_for_v1": true, "path_scope": "full", - "accounting_status": "valid", - "benchmark_status": "passing", + "accounting_status": "invalid", + "benchmark_status": "invalid", "gate_status": "open", "readability_review_status": "stale", "readability_verdict": { @@ -578,7 +578,7 @@ "total_cases": 6, "supported_cases": 6, "supported_valid_cases": 6, - "positive_credit_cases": 4, + "positive_credit_cases": 0, "case_status_counts": { "invalid": 0, "failing": 0, @@ -628,7 +628,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/apply_regional_fee", @@ -644,7 +644,7 @@ "examples/service/units/billing/discount_strategy_quote_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total", @@ -659,7 +659,7 @@ "examples/service/units/billing/checkout_success_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/checkout_net_total_guarded_fee", @@ -674,7 +674,7 @@ "examples/service/units/billing/checkout_declined_discount_flow.test.evidence.json" ] }, - "counts_as_supported_positive": true + "counts_as_supported_positive": false }, { "case_id": "billing/discount_strategy", diff --git a/spec-cli/tests/fixtures/status-failing-timeout.json b/spec-cli/tests/fixtures/status-failing-timeout.json index e487e46..25bf9b7 100644 --- a/spec-cli/tests/fixtures/status-failing-timeout.json +++ b/spec-cli/tests/fixtures/status-failing-timeout.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/status-failing.json b/spec-cli/tests/fixtures/status-failing.json index 7d2213c..9a7508a 100644 --- a/spec-cli/tests/fixtures/status-failing.json +++ b/spec-cli/tests/fixtures/status-failing.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/status-incomplete.json b/spec-cli/tests/fixtures/status-incomplete.json index 2d1cf8c..f32fedf 100644 --- a/spec-cli/tests/fixtures/status-incomplete.json +++ b/spec-cli/tests/fixtures/status-incomplete.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/status-stale.json b/spec-cli/tests/fixtures/status-stale.json index 8456a7d..dd4434a 100644 --- a/spec-cli/tests/fixtures/status-stale.json +++ b/spec-cli/tests/fixtures/status-stale.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/status-untested.json b/spec-cli/tests/fixtures/status-untested.json index 5854d99..2d37974 100644 --- a/spec-cli/tests/fixtures/status-untested.json +++ b/spec-cli/tests/fixtures/status-untested.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/fixtures/status-valid.json b/spec-cli/tests/fixtures/status-valid.json index 59022a8..b585f0f 100644 --- a/spec-cli/tests/fixtures/status-valid.json +++ b/spec-cli/tests/fixtures/status-valid.json @@ -1,5 +1,5 @@ { - "schema_version": 4, + "schema_version": 5, "roots": [ { "root": ".", diff --git a/spec-cli/tests/rust_v1_service.rs b/spec-cli/tests/rust_v1_service.rs index a46e4f8..7624a2d 100644 --- a/spec-cli/tests/rust_v1_service.rs +++ b/spec-cli/tests/rust_v1_service.rs @@ -283,10 +283,10 @@ fn rust_v1_service_status_contract_matches_frozen_fixture() { let benchmark = benchmark(&json, "BENCH-SERVICE"); assert_eq!(benchmark["path_scope"], "full"); - assert_eq!(benchmark["benchmark_status"], "passing"); + assert_eq!(benchmark["benchmark_status"], "invalid"); assert_eq!(benchmark["gate_status"], "open"); assert_eq!(benchmark["summary"]["required_molecule_total"], 3); - assert_eq!(benchmark["summary"]["positive_credit_cases"], 4); + assert_eq!(benchmark["summary"]["positive_credit_cases"], 0); assert_eq!( benchmark_case(benchmark, "billing/discount_strategy")["counts_as_supported_positive"], Value::Bool(false) @@ -312,10 +312,10 @@ fn rust_v1_service_export_contract_matches_frozen_fixture() { let benchmark = benchmark(&json, "BENCH-SERVICE"); assert_eq!(benchmark["path_scope"], "full"); - assert_eq!(benchmark["benchmark_status"], "passing"); + assert_eq!(benchmark["benchmark_status"], "invalid"); assert_eq!(benchmark["gate_status"], "open"); assert_eq!(benchmark["summary"]["required_molecule_total"], 3); - assert_eq!(benchmark["summary"]["positive_credit_cases"], 4); + assert_eq!(benchmark["summary"]["positive_credit_cases"], 0); assert_eq!( benchmark_case(benchmark, "billing/discount_strategy")["counts_as_supported_positive"], Value::Bool(false) @@ -350,7 +350,7 @@ fn rust_v1_service_namespace_status_contract_matches_frozen_fixture() { assert_eq!(json["loader_errors"][0]["code"], "SPEC_NO_LIBRARY_ROOTS"); assert_eq!(benchmark["path_scope"], "partial"); - assert_eq!(benchmark["accounting_status"], "partial_valid"); + assert_eq!(benchmark["accounting_status"], "partial_invalid"); assert!( benchmark["cases"] .as_array() @@ -378,7 +378,7 @@ fn rust_v1_service_namespace_export_contract_matches_frozen_fixture() { let benchmark = benchmark(&json, "BENCH-SERVICE"); assert_eq!(benchmark["path_scope"], "partial"); - assert_eq!(benchmark["accounting_status"], "partial_valid"); + assert_eq!(benchmark["accounting_status"], "partial_invalid"); assert!( benchmark["cases"] .as_array() @@ -412,11 +412,11 @@ fn rust_v1_service_repo_root_inventory_contract_matches_updated_fixture() { let crosslib = benchmark(&json, "BENCH-CROSSLIB"); assert_eq!(json["scope_authority"], "inventory_only"); - assert_eq!(service["benchmark_status"], "passing"); + assert_eq!(service["benchmark_status"], "invalid"); assert_eq!(service["gate_status"], "open"); - assert_eq!(service["summary"]["positive_credit_cases"], 4); + assert_eq!(service["summary"]["positive_credit_cases"], 0); assert_eq!(ecom["benchmark_status"], "passing"); - assert_eq!(ecom["readability_review_status"], "current"); + assert_eq!(ecom["readability_review_status"], "stale"); assert_eq!(crosslib["benchmark_status"], "passing"); assert_eq!(crosslib["summary"]["positive_credit_cases"], 0); } @@ -440,7 +440,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_missing() { ); let status_json = parse_stdout_json(&status_output); let status_benchmark = benchmark(&status_json, "BENCH-SERVICE"); - assert_eq!(status_benchmark["benchmark_status"], "incomplete"); + assert_eq!(status_benchmark["benchmark_status"], "invalid"); assert_eq!(status_benchmark["gate_status"], "open"); assert_eq!( required_molecule_proof(status_benchmark, "billing/checkout_success_flow")["status"], @@ -454,7 +454,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_missing() { ); let export_json = parse_stdout_json(&export_output); let export_benchmark = benchmark(&export_json, "BENCH-SERVICE"); - assert_eq!(export_benchmark["benchmark_status"], "incomplete"); + assert_eq!(export_benchmark["benchmark_status"], "invalid"); assert_eq!( required_molecule_proof(export_benchmark, "billing/checkout_success_flow")["status"], "untested" @@ -488,7 +488,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_stale() { ); let status_json = parse_stdout_json(&status_output); let status_benchmark = benchmark(&status_json, "BENCH-SERVICE"); - assert_eq!(status_benchmark["benchmark_status"], "incomplete"); + assert_eq!(status_benchmark["benchmark_status"], "invalid"); assert_eq!(status_benchmark["gate_status"], "open"); assert_eq!( required_molecule_proof(status_benchmark, "billing/checkout_success_flow")["status"], @@ -502,7 +502,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_stale() { ); let export_json = parse_stdout_json(&export_output); let export_benchmark = benchmark(&export_json, "BENCH-SERVICE"); - assert_eq!(export_benchmark["benchmark_status"], "incomplete"); + assert_eq!(export_benchmark["benchmark_status"], "invalid"); assert_eq!( required_molecule_proof(export_benchmark, "billing/checkout_success_flow")["status"], "stale" @@ -549,7 +549,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_failing() { ); let status_json = parse_stdout_json(&status_output); let status_benchmark = benchmark(&status_json, "BENCH-SERVICE"); - assert_eq!(status_benchmark["benchmark_status"], "failing"); + assert_eq!(status_benchmark["benchmark_status"], "invalid"); assert_eq!(status_benchmark["gate_status"], "open"); assert_eq!( required_molecule_proof(status_benchmark, "billing/checkout_success_flow")["status"], @@ -563,7 +563,7 @@ fn rust_v1_service_is_non_passing_when_required_molecule_proof_is_failing() { ); let export_json = parse_stdout_json(&export_output); let export_benchmark = benchmark(&export_json, "BENCH-SERVICE"); - assert_eq!(export_benchmark["benchmark_status"], "failing"); + assert_eq!(export_benchmark["benchmark_status"], "invalid"); assert_eq!( required_molecule_proof(export_benchmark, "billing/checkout_success_flow")["status"], "failing" @@ -603,7 +603,7 @@ fn rust_v1_service_readability_review_becomes_stale_when_projection_digest_drift ); let status_json = parse_stdout_json(&status_output); let status_benchmark = benchmark(&status_json, "BENCH-SERVICE"); - assert_eq!(status_benchmark["benchmark_status"], "passing"); + assert_eq!(status_benchmark["benchmark_status"], "invalid"); assert_eq!(status_benchmark["readability_review_status"], "stale"); let export_output = run_spec(&repo_dir, &["export", "examples/service/units"]); @@ -613,6 +613,6 @@ fn rust_v1_service_readability_review_becomes_stale_when_projection_digest_drift ); let export_json = parse_stdout_json(&export_output); let export_benchmark = benchmark(&export_json, "BENCH-SERVICE"); - assert_eq!(export_benchmark["benchmark_status"], "passing"); + assert_eq!(export_benchmark["benchmark_status"], "invalid"); assert_eq!(export_benchmark["readability_review_status"], "stale"); } diff --git a/spec-core/src/benchmark.rs b/spec-core/src/benchmark.rs index 8a6b008..a47706b 100644 --- a/spec-core/src/benchmark.rs +++ b/spec-core/src/benchmark.rs @@ -4,7 +4,11 @@ //! registry/accounting inputs plus already-projected proof status and emits one //! shared benchmark surface for status, export, and snapshotting. -use crate::semantic_review::SemanticSupportStatus; +use crate::category_truth::{ + CategoryQualification, ClaimStatus, ConsumerKind, PositiveCreditEligibility, + is_first_scope_seam_unit_id, is_seam_category_claim_candidate, qualify_category_claim, +}; +use crate::semantic_review::{SemanticReview, SemanticSupportStatus}; use crate::{BenchmarkRegistryInvalidDetails, Result, SpecError}; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -170,6 +174,8 @@ pub struct BenchmarkCaseTruth { #[serde(skip_serializing_if = "Option::is_none")] pub reason: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub semantic_review: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub semantic_support_status: Option, } @@ -239,6 +245,8 @@ pub struct BenchmarkCaseProjection { #[serde(skip_serializing_if = "Option::is_none")] pub semantic_support_status: Option, #[serde(skip_serializing_if = "Option::is_none")] + pub category_qualification: Option, + #[serde(skip_serializing_if = "Option::is_none")] pub proof_refs: Option, pub counts_as_supported_positive: bool, } @@ -357,8 +365,6 @@ pub fn project_benchmark( let case_labels = selected_case_labels(benchmark, request.path_scope, &request.selected_carrier_ids); let unlabeled_loaded_carrier_ids = unlabeled_loaded_carrier_ids(benchmark, request); - let accounting_status = - determine_accounting_status(benchmark, request.path_scope, &unlabeled_loaded_carrier_ids); let mut required_molecule_proofs = if matches!(request.path_scope, BenchmarkPathScope::Full) { benchmark @@ -383,8 +389,22 @@ pub fn project_benchmark( let mut cases = case_labels .iter() - .map(|label| project_case(benchmark, label, request, accounting_status)) + .map(|label| project_case(benchmark, label, request)) .collect::>(); + let accounting_status = determine_accounting_status( + benchmark, + request.path_scope, + &unlabeled_loaded_carrier_ids, + &cases, + ); + for case_projection in &mut cases { + case_projection.counts_as_supported_positive = counts_as_supported_positive( + benchmark, + request.path_scope, + accounting_status, + case_projection, + ); + } cases.sort_by(|left, right| left.case_id.cmp(&right.case_id)); let readability_generated_files = matches!(request.path_scope, BenchmarkPathScope::Full) @@ -810,17 +830,21 @@ fn determine_accounting_status( benchmark: &BenchmarkLabel, path_scope: BenchmarkPathScope, unlabeled_loaded_carrier_ids: &[String], + cases: &[BenchmarkCaseProjection], ) -> BenchmarkAccountingStatus { if matches!(benchmark.lifecycle, BenchmarkLifecycle::Reserved) { return BenchmarkAccountingStatus::ReservedMissingCases; } + let has_unqualified_supported_seam_case = cases + .iter() + .any(supported_seam_case_requires_qualified_truth); if matches!(path_scope, BenchmarkPathScope::Partial) { - if unlabeled_loaded_carrier_ids.is_empty() { + if unlabeled_loaded_carrier_ids.is_empty() && !has_unqualified_supported_seam_case { BenchmarkAccountingStatus::PartialValid } else { BenchmarkAccountingStatus::PartialInvalid } - } else if unlabeled_loaded_carrier_ids.is_empty() { + } else if unlabeled_loaded_carrier_ids.is_empty() && !has_unqualified_supported_seam_case { BenchmarkAccountingStatus::Valid } else { BenchmarkAccountingStatus::Invalid @@ -831,7 +855,6 @@ fn project_case( benchmark: &BenchmarkLabel, label: &BenchmarkCaseLabel, request: &BenchmarkProjectionRequest, - accounting_status: BenchmarkAccountingStatus, ) -> BenchmarkCaseProjection { let truth = request.root_case_truths.get(&label.carrier_id); let status = truth @@ -845,18 +868,16 @@ fn project_case( ) }) }); - let semantic_support_status = truth.and_then(|truth| truth.semantic_support_status); + let semantic_support_status = truth + .and_then(|truth| { + truth + .semantic_review + .as_ref() + .map(SemanticReview::effective_support_status) + }) + .or_else(|| truth.and_then(|truth| truth.semantic_support_status)); + let category_qualification = project_case_category_qualification(&label.carrier_id, truth); let proof_refs = build_case_proof_refs(benchmark, label, request); - let counts_as_supported_positive = matches!(benchmark.kind, BenchmarkKind::Positive) - && matches!(benchmark.lifecycle, BenchmarkLifecycle::Active) - && matches!(request.path_scope, BenchmarkPathScope::Full) - && matches!(accounting_status, BenchmarkAccountingStatus::Valid) - && matches!(label.classification, BenchmarkClassification::Supported) - && matches!(status, BenchmarkTruthStatus::Valid) - && matches!( - semantic_support_status, - Some(SemanticSupportStatus::Supported) - ); BenchmarkCaseProjection { case_id: label.case_id.clone(), @@ -866,11 +887,77 @@ fn project_case( status, reason, semantic_support_status, + category_qualification, proof_refs, - counts_as_supported_positive, + counts_as_supported_positive: false, + } +} + +fn counts_as_supported_positive( + benchmark: &BenchmarkLabel, + path_scope: BenchmarkPathScope, + accounting_status: BenchmarkAccountingStatus, + case_projection: &BenchmarkCaseProjection, +) -> bool { + matches!(benchmark.kind, BenchmarkKind::Positive) + && matches!(benchmark.lifecycle, BenchmarkLifecycle::Active) + && matches!(path_scope, BenchmarkPathScope::Full) + && matches!(accounting_status, BenchmarkAccountingStatus::Valid) + && matches!( + case_projection.classification, + BenchmarkClassification::Supported + ) + && matches!(case_projection.status, BenchmarkTruthStatus::Valid) + && match &case_projection.category_qualification { + Some(qualification) => { + matches!(qualification.claim_status, ClaimStatus::SupportedQualified) + && matches!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Eligible + ) + } + None => { + !is_first_scope_seam_unit_id(&case_projection.carrier_id) + && matches!( + case_projection.semantic_support_status, + Some(SemanticSupportStatus::Supported) + ) + } + } +} + +fn supported_seam_case_requires_qualified_truth(case_projection: &BenchmarkCaseProjection) -> bool { + matches!( + case_projection.classification, + BenchmarkClassification::Supported + ) && if let Some(qualification) = case_projection.category_qualification.as_ref() { + !matches!(qualification.claim_status, ClaimStatus::SupportedQualified) + || !matches!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Eligible + ) + } else { + is_first_scope_seam_unit_id(&case_projection.carrier_id) } } +fn project_case_category_qualification( + carrier_id: &str, + truth: Option<&BenchmarkCaseTruth>, +) -> Option { + let semantic_review = truth.and_then(|truth| truth.semantic_review.as_ref()); + if let Some(review) = semantic_review + && is_seam_category_claim_candidate(review) + { + return Some(qualify_category_claim( + ConsumerKind::Benchmark, + Some(review), + Some(carrier_id), + )); + } + None +} + fn build_case_proof_refs( benchmark: &BenchmarkLabel, label: &BenchmarkCaseLabel, @@ -1254,6 +1341,7 @@ mod tests { BenchmarkCaseTruth { status, reason: None, + semantic_review: None, semantic_support_status, } } @@ -1817,6 +1905,7 @@ mod tests { status: BenchmarkTruthStatus::Valid, reason: None, semantic_support_status: Some(SemanticSupportStatus::Supported), + category_qualification: None, proof_refs: None, counts_as_supported_positive: true, }, @@ -1828,6 +1917,7 @@ mod tests { status: BenchmarkTruthStatus::Valid, reason: None, semantic_support_status: Some(SemanticSupportStatus::Supported), + category_qualification: None, proof_refs: None, counts_as_supported_positive: false, }, diff --git a/spec-core/src/category_truth.rs b/spec-core/src/category_truth.rs new file mode 100644 index 0000000..cdbf1d4 --- /dev/null +++ b/spec-core/src/category_truth.rs @@ -0,0 +1,554 @@ +use crate::semantic_review::{SemanticReview, SemanticSupportStatus}; +use serde::{Deserialize, Serialize}; + +pub const CATEGORY_TRUTH_SCHEMA_VERSION: u8 = 1; + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +pub struct CategoryTruthRegistry { + pub schema_version: u8, + pub categories: &'static [CategoryTruthRow], +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +pub struct CategoryTruthRow { + pub category_id: &'static str, + pub kind: CategoryKind, + pub contract_support_status: ContractSupportStatus, + pub alias_sibling_policy: AliasSiblingPolicy, + pub descriptor_set: DescriptorSet, + pub positive_credit_policy: PositiveCreditPolicy, + pub notes: &'static str, +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum CategoryKind { + Sum, + Data, +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ContractSupportStatus { + Supported, + Unsupported, +} + +impl From for SemanticSupportStatus { + fn from(value: ContractSupportStatus) -> Self { + match value { + ContractSupportStatus::Supported => SemanticSupportStatus::Supported, + ContractSupportStatus::Unsupported => SemanticSupportStatus::Unsupported, + } + } +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum AliasSiblingPolicy { + CanonicalOnly, + UnsupportedTerminal, +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +pub struct DescriptorIdentity { + pub descriptor_id: &'static str, + pub representative_unit_id: &'static str, +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +pub struct DescriptorSet { + #[serde(skip_serializing_if = "Option::is_none")] + pub canonical_descriptor: Option, + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub approved_siblings: &'static [DescriptorIdentity], +} + +#[derive(Debug, Clone, Copy, Serialize, PartialEq, Eq)] +pub struct PositiveCreditPolicy { + pub eligible: bool, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ConsumerKind { + Benchmark, + Status, + Export, + Snapshot, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct CategoryQualification { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub category_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub descriptor_id: Option, + pub claim_status: ClaimStatus, + pub positive_credit_eligibility: PositiveCreditEligibility, + pub reason_code: QualificationReasonCode, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum ClaimStatus { + SupportedQualified, + UnsupportedQualified, + Unqualified, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum PositiveCreditEligibility { + Eligible, + Ineligible, +} + +#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum QualificationReasonCode { + Qualified, + SemanticReviewMissing, + RegistryRowMissing, + DescriptorIdMissing, + DescriptorNotApproved, + SupportStatusMismatch, + PositiveCreditDisallowed, +} + +const EMPTY_DESCRIPTOR_SET: DescriptorSet = DescriptorSet { + canonical_descriptor: None, + approved_siblings: &[], +}; + +const DISCOUNT_STRATEGY_ECOMMERCE_DESCRIPTOR: DescriptorIdentity = DescriptorIdentity { + descriptor_id: "discount_strategy.ecommerce.v1", + representative_unit_id: "pricing/discount_strategy", +}; + +const PRICING_QUOTE_ECOMMERCE_DESCRIPTOR: DescriptorIdentity = DescriptorIdentity { + descriptor_id: "pricing_quote.ecommerce.v1", + representative_unit_id: "pricing/pricing_quote", +}; + +const CATEGORY_ROWS: [CategoryTruthRow; 4] = [ + CategoryTruthRow { + category_id: "sum.discount_strategy.v1", + kind: CategoryKind::Sum, + contract_support_status: ContractSupportStatus::Supported, + alias_sibling_policy: AliasSiblingPolicy::CanonicalOnly, + descriptor_set: DescriptorSet { + canonical_descriptor: Some(DISCOUNT_STRATEGY_ECOMMERCE_DESCRIPTOR), + approved_siblings: &[], + }, + positive_credit_policy: PositiveCreditPolicy { eligible: true }, + notes: "canonical ecommerce descriptor only; service sibling remains visible but unqualified until producer truth is widened explicitly", + }, + CategoryTruthRow { + category_id: "data.pricing_quote.v1", + kind: CategoryKind::Data, + contract_support_status: ContractSupportStatus::Supported, + alias_sibling_policy: AliasSiblingPolicy::CanonicalOnly, + descriptor_set: DescriptorSet { + canonical_descriptor: Some(PRICING_QUOTE_ECOMMERCE_DESCRIPTOR), + approved_siblings: &[], + }, + positive_credit_policy: PositiveCreditPolicy { eligible: true }, + notes: "canonical ecommerce descriptor only; service sibling remains visible but unqualified until producer truth is widened explicitly", + }, + CategoryTruthRow { + category_id: "unsupported.sum.v1", + kind: CategoryKind::Sum, + contract_support_status: ContractSupportStatus::Unsupported, + alias_sibling_policy: AliasSiblingPolicy::UnsupportedTerminal, + descriptor_set: EMPTY_DESCRIPTOR_SET, + positive_credit_policy: PositiveCreditPolicy { eligible: false }, + notes: "unsupported seam sum truth remains visible, additive, and never positive-credit eligible", + }, + CategoryTruthRow { + category_id: "unsupported.data.v1", + kind: CategoryKind::Data, + contract_support_status: ContractSupportStatus::Unsupported, + alias_sibling_policy: AliasSiblingPolicy::UnsupportedTerminal, + descriptor_set: EMPTY_DESCRIPTOR_SET, + positive_credit_policy: PositiveCreditPolicy { eligible: false }, + notes: "unsupported seam data truth remains visible, additive, and never positive-credit eligible", + }, +]; + +const CATEGORY_TRUTH_REGISTRY: CategoryTruthRegistry = CategoryTruthRegistry { + schema_version: CATEGORY_TRUTH_SCHEMA_VERSION, + categories: &CATEGORY_ROWS, +}; + +pub fn category_truth_registry() -> &'static CategoryTruthRegistry { + &CATEGORY_TRUTH_REGISTRY +} + +impl CategoryTruthRegistry { + pub fn find_by_category_id(&self, category_id: &str) -> Option<&'static CategoryTruthRow> { + self.categories + .iter() + .find(|row| row.category_id == category_id) + } +} + +pub fn is_seam_category_claim_candidate(review: &SemanticReview) -> bool { + matches!( + review.compatibility_key.as_str(), + "unsupported.sum.v1" | "unsupported.data.v1" + ) || review.compatibility_key.starts_with("sum.") + || review.compatibility_key.starts_with("data.") +} + +pub fn qualify_category_claim( + _consumer: ConsumerKind, + semantic_review: Option<&SemanticReview>, + _unit_id: Option<&str>, +) -> CategoryQualification { + let Some(review) = semantic_review else { + return CategoryQualification { + category_id: None, + descriptor_id: None, + claim_status: ClaimStatus::Unqualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::SemanticReviewMissing, + }; + }; + + let descriptor_id = review.descriptor_id.clone(); + let Some(row) = category_truth_registry().find_by_category_id(&review.compatibility_key) else { + return CategoryQualification { + category_id: None, + descriptor_id, + claim_status: ClaimStatus::Unqualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::RegistryRowMissing, + }; + }; + + if review.effective_support_status() != row.contract_support_status.into() { + return CategoryQualification { + category_id: Some(row.category_id.to_string()), + descriptor_id, + claim_status: ClaimStatus::Unqualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::SupportStatusMismatch, + }; + } + + if matches!( + row.contract_support_status, + ContractSupportStatus::Supported + ) { + let Some(descriptor_id_value) = descriptor_id.clone() else { + return CategoryQualification { + category_id: Some(row.category_id.to_string()), + descriptor_id: None, + claim_status: ClaimStatus::Unqualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::DescriptorIdMissing, + }; + }; + + if !descriptor_is_approved(&row.descriptor_set, &descriptor_id_value) { + return CategoryQualification { + category_id: Some(row.category_id.to_string()), + descriptor_id: Some(descriptor_id_value), + claim_status: ClaimStatus::Unqualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::DescriptorNotApproved, + }; + } + + return CategoryQualification { + category_id: Some(row.category_id.to_string()), + descriptor_id: Some(descriptor_id_value), + claim_status: ClaimStatus::SupportedQualified, + positive_credit_eligibility: PositiveCreditEligibility::Eligible, + reason_code: QualificationReasonCode::Qualified, + }; + } + + CategoryQualification { + category_id: Some(row.category_id.to_string()), + descriptor_id, + claim_status: ClaimStatus::UnsupportedQualified, + positive_credit_eligibility: PositiveCreditEligibility::Ineligible, + reason_code: QualificationReasonCode::PositiveCreditDisallowed, + } +} + +fn descriptor_is_approved(descriptor_set: &DescriptorSet, descriptor_id: &str) -> bool { + descriptor_set + .canonical_descriptor + .as_ref() + .is_some_and(|descriptor| descriptor.descriptor_id == descriptor_id) + || descriptor_set + .approved_siblings + .iter() + .any(|descriptor| descriptor.descriptor_id == descriptor_id) +} + +pub fn is_first_scope_seam_unit_id(unit_id: &str) -> bool { + matches!( + unit_id, + "pricing/discount_strategy" + | "billing/discount_strategy" + | "pricing/pricing_quote" + | "billing/pricing_quote" + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::semantic_review::{ + EvaluatorScope, SemanticReview, SemanticSupportStatus, SemanticVerdict, + }; + + fn review( + compatibility_key: &str, + descriptor_id: Option<&str>, + support_status: Option, + evaluator_scope: EvaluatorScope, + ) -> SemanticReview { + SemanticReview { + verdict: SemanticVerdict::Aligned, + compatibility_key: compatibility_key.to_string(), + descriptor_id: descriptor_id.map(str::to_string), + support_status, + unsupported_reason_codes: vec![], + rewrite_hints: vec![], + reason_codes: vec![], + summary: String::new(), + authored_surfaces: vec![], + executable_surfaces: vec![], + evaluator_scope, + } + } + + #[test] + fn registry_exposes_first_scope_rows() { + let registry = category_truth_registry(); + assert_eq!(registry.schema_version, CATEGORY_TRUTH_SCHEMA_VERSION); + assert_eq!(registry.categories.len(), 4); + assert!( + registry + .find_by_category_id("sum.discount_strategy.v1") + .is_some() + ); + assert!( + registry + .find_by_category_id("data.pricing_quote.v1") + .is_some() + ); + assert!(registry.find_by_category_id("unsupported.sum.v1").is_some()); + assert!( + registry + .find_by_category_id("unsupported.data.v1") + .is_some() + ); + } + + #[test] + fn canonical_ecommerce_sum_qualifies_as_supported() { + let qualification = qualify_category_claim( + ConsumerKind::Benchmark, + Some(&review( + "sum.discount_strategy.v1", + Some("discount_strategy.ecommerce.v1"), + None, + EvaluatorScope::SupportedSumSurface, + )), + Some("pricing/discount_strategy"), + ); + + assert_eq!(qualification.claim_status, ClaimStatus::SupportedQualified); + assert_eq!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Eligible + ); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::Qualified + ); + } + + #[test] + fn canonical_ecommerce_data_qualifies_as_supported() { + let qualification = qualify_category_claim( + ConsumerKind::Status, + Some(&review( + "data.pricing_quote.v1", + Some("pricing_quote.ecommerce.v1"), + None, + EvaluatorScope::SupportedDataSurface, + )), + Some("pricing/pricing_quote"), + ); + + assert_eq!(qualification.claim_status, ClaimStatus::SupportedQualified); + assert_eq!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Eligible + ); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::Qualified + ); + } + + #[test] + fn service_sum_sibling_stays_visible_but_unqualified() { + let qualification = qualify_category_claim( + ConsumerKind::Export, + Some(&review( + "sum.discount_strategy.v1", + Some("discount_strategy.service.v1"), + None, + EvaluatorScope::SupportedSumSurface, + )), + Some("billing/discount_strategy"), + ); + + assert_eq!( + qualification.category_id.as_deref(), + Some("sum.discount_strategy.v1") + ); + assert_eq!(qualification.claim_status, ClaimStatus::Unqualified); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::DescriptorNotApproved + ); + } + + #[test] + fn service_data_sibling_stays_visible_but_unqualified() { + let qualification = qualify_category_claim( + ConsumerKind::Snapshot, + Some(&review( + "data.pricing_quote.v1", + Some("pricing_quote.service.v1"), + None, + EvaluatorScope::SupportedDataSurface, + )), + Some("billing/pricing_quote"), + ); + + assert_eq!( + qualification.category_id.as_deref(), + Some("data.pricing_quote.v1") + ); + assert_eq!(qualification.claim_status, ClaimStatus::Unqualified); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::DescriptorNotApproved + ); + } + + #[test] + fn unsupported_sum_row_qualifies_only_as_unsupported() { + let qualification = qualify_category_claim( + ConsumerKind::Benchmark, + Some(&review( + "unsupported.sum.v1", + None, + None, + EvaluatorScope::UnsupportedSurface, + )), + Some("billing/discount_strategy"), + ); + + assert_eq!( + qualification.claim_status, + ClaimStatus::UnsupportedQualified + ); + assert_eq!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Ineligible + ); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::PositiveCreditDisallowed + ); + } + + #[test] + fn unsupported_data_row_qualifies_only_as_unsupported() { + let qualification = qualify_category_claim( + ConsumerKind::Benchmark, + Some(&review( + "unsupported.data.v1", + None, + None, + EvaluatorScope::UnsupportedSurface, + )), + Some("billing/pricing_quote"), + ); + + assert_eq!( + qualification.claim_status, + ClaimStatus::UnsupportedQualified + ); + assert_eq!( + qualification.positive_credit_eligibility, + PositiveCreditEligibility::Ineligible + ); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::PositiveCreditDisallowed + ); + } + + #[test] + fn missing_semantic_review_is_explicit_failure() { + let qualification = qualify_category_claim(ConsumerKind::Status, None, None); + + assert_eq!(qualification.claim_status, ClaimStatus::Unqualified); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::SemanticReviewMissing + ); + } + + #[test] + fn missing_descriptor_id_is_explicit_failure_for_supported_rows() { + let qualification = qualify_category_claim( + ConsumerKind::Export, + Some(&review( + "sum.discount_strategy.v1", + None, + None, + EvaluatorScope::SupportedSumSurface, + )), + Some("pricing/discount_strategy"), + ); + + assert_eq!(qualification.claim_status, ClaimStatus::Unqualified); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::DescriptorIdMissing + ); + } + + #[test] + fn unknown_seam_registry_row_fails_explicitly() { + let qualification = qualify_category_claim( + ConsumerKind::Status, + Some(&review( + "sum.unknown.v1", + Some("sum.unknown.ecommerce.v1"), + None, + EvaluatorScope::SupportedSumSurface, + )), + Some("pricing/discount_strategy"), + ); + + assert_eq!(qualification.claim_status, ClaimStatus::Unqualified); + assert_eq!( + qualification.reason_code, + QualificationReasonCode::RegistryRowMissing + ); + } +} diff --git a/spec-core/src/export.rs b/spec-core/src/export.rs index d541a47..6f0805e 100644 --- a/spec-core/src/export.rs +++ b/spec-core/src/export.rs @@ -13,6 +13,9 @@ use crate::AUTHORED_SPEC_VERSION; use crate::benchmark::BenchmarkProjection; +use crate::category_truth::{ + CategoryQualification, ConsumerKind, is_seam_category_claim_candidate, qualify_category_claim, +}; use crate::graph::{SpecEdge, SpecGraph, top_level_deps}; use crate::molecule_evidence::{MoleculeEvidence, read_molecule_evidence}; use crate::passport::{ @@ -20,7 +23,7 @@ use crate::passport::{ passport_path_for, project_passport_truth_with_context, refresh_passport_target_proofs, }; use crate::plan::{LoadedPlan, PlanAcceptanceClosure, PlanComputedImpact, PlanReport, PlanStruct}; -use crate::semantic_review::{SemanticProjectionMode, SemanticReviewContext}; +use crate::semantic_review::{SemanticProjectionMode, SemanticReview, SemanticReviewContext}; use crate::types::{ AuthoredBackends, AuthoredConstructor, AuthoredDataShape, AuthoredMethod, AuthoredSumShape, Contract, DepRef, LoadedMoleculeTest, LoadedSpec, LocalTest, UnitKind, @@ -31,7 +34,7 @@ use std::fs; use std::path::Path; /// Export schema version. Bumped in M9 for structured dep refs. -const EXPORT_SCHEMA_VERSION: u8 = 4; +const EXPORT_SCHEMA_VERSION: u8 = 5; const PLAN_EXPORT_SCHEMA_VERSION: u8 = 1; #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -42,6 +45,7 @@ pub struct ExportBundle { #[serde(skip_serializing_if = "Option::is_none")] pub provenance: Option, pub units: Vec, + pub projected_units: Vec, pub molecule_tests: Vec, pub passports: Vec, pub graph: ExportGraph, @@ -73,6 +77,13 @@ pub struct ExportUnit { pub source_file: String, } +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct ProjectedExportUnit { + pub id: String, + pub semantic_review: Option, + pub category_qualification: Option, +} + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct ExportDepRef { pub library: Option, @@ -157,6 +168,24 @@ pub fn build_export_bundle_with_benchmarks( .map(|spec| (spec.spec.id.clone(), spec.clone())) .collect(); let molecule_evidence_by_id = load_molecule_evidence_for_tests(molecule_tests); + let semantic_review_context = SemanticReviewContext::new(&specs_by_id); + let projection_context = PassportProjectionContext { + molecule_tests, + molecule_evidence_by_id: &molecule_evidence_by_id, + specs_by_id: &specs_by_id, + semantic_projection_mode: SemanticProjectionMode::Preserve, + }; + let existing_passports_by_id: HashMap = passports + .iter() + .cloned() + .map(|passport| (passport.id.clone(), passport)) + .collect(); + let projected_units = build_projected_export_units( + specs, + &existing_passports_by_id, + &projection_context, + &semantic_review_context, + ); let passports = enrich_passports_for_export( specs, molecule_tests, @@ -193,6 +222,7 @@ pub fn build_export_bundle_with_benchmarks( exported_at: exported_at.to_string(), provenance: provenance.cloned(), units: specs.iter().map(ExportUnit::from).collect(), + projected_units, molecule_tests: export_molecule_tests, passports, graph: ExportGraph { edges }, @@ -201,6 +231,60 @@ pub fn build_export_bundle_with_benchmarks( } } +fn build_projected_export_units( + specs: &[LoadedSpec], + existing_passports_by_id: &HashMap, + projection_context: &PassportProjectionContext<'_>, + semantic_review_context: &SemanticReviewContext<'_>, +) -> Vec { + specs + .iter() + .map(|spec| { + let semantic_review = project_semantic_review_for_export_unit( + spec, + existing_passports_by_id.get(&spec.spec.id), + projection_context, + semantic_review_context, + ); + let category_qualification = semantic_review + .as_ref() + .filter(|review| is_seam_category_claim_candidate(review)) + .map(|review| { + qualify_category_claim( + ConsumerKind::Export, + Some(review), + Some(spec.spec.id.as_str()), + ) + }); + + ProjectedExportUnit { + id: spec.spec.id.clone(), + semantic_review, + category_qualification, + } + }) + .collect() +} + +fn project_semantic_review_for_export_unit( + spec: &LoadedSpec, + existing_passport: Option<&Passport>, + projection_context: &PassportProjectionContext<'_>, + semantic_review_context: &SemanticReviewContext<'_>, +) -> Option { + let mut refreshed_passport = existing_passport.cloned(); + if let Some(passport) = refreshed_passport.as_mut() { + refresh_passport_target_proofs(passport, spec); + } + project_passport_truth_with_context( + spec, + refreshed_passport.as_ref(), + projection_context, + semantic_review_context, + ) + .semantic_review +} + fn enrich_passports_for_export( specs: &[LoadedSpec], molecule_tests: &[LoadedMoleculeTest], @@ -1220,7 +1304,7 @@ mod tests { let bundle = build_export_bundle(&[spec], &[], "2026-04-05T00:00:00Z", None); - assert_eq!(bundle.schema_version, 4); + assert_eq!(bundle.schema_version, 5); assert_eq!(bundle.spec_version, crate::AUTHORED_SPEC_VERSION); assert_ne!(bundle.schema_version.to_string(), bundle.spec_version); } @@ -2689,8 +2773,9 @@ mod tests { vec![projection.clone()], ); - assert_eq!(bundle.schema_version, 4); + assert_eq!(bundle.schema_version, 5); assert_eq!(bundle.units.len(), 1); + assert_eq!(bundle.projected_units.len(), 1); assert_eq!(bundle.graph.edges.len(), 0); assert_eq!(bundle.benchmarks, vec![projection]); } diff --git a/spec-core/src/lib.rs b/spec-core/src/lib.rs index 89b6f6a..9a225fb 100644 --- a/spec-core/src/lib.rs +++ b/spec-core/src/lib.rs @@ -10,6 +10,7 @@ pub const AUTHORED_SPEC_VERSION: &str = "0.3.0"; pub mod backend_execution; pub mod benchmark; +pub mod category_truth; pub mod escape_hatch; pub mod export; pub mod generator; diff --git a/spec-core/src/semantic_review.rs b/spec-core/src/semantic_review.rs index 5d2efd1..d786996 100644 --- a/spec-core/src/semantic_review.rs +++ b/spec-core/src/semantic_review.rs @@ -85,6 +85,8 @@ pub struct SemanticReview { pub verdict: SemanticVerdict, pub compatibility_key: String, #[serde(default, skip_serializing_if = "Option::is_none")] + pub descriptor_id: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] pub support_status: Option, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub unsupported_reason_codes: Vec, @@ -453,6 +455,10 @@ enum FamilyBArgClassification { const SUM_DISCOUNT_STRATEGY_COMPATIBILITY_KEY: &str = "sum.discount_strategy.v1"; const DATA_PRICING_QUOTE_COMPATIBILITY_KEY: &str = "data.pricing_quote.v1"; +const DISCOUNT_STRATEGY_ECOMMERCE_DESCRIPTOR_ID: &str = "discount_strategy.ecommerce.v1"; +const DISCOUNT_STRATEGY_SERVICE_DESCRIPTOR_ID: &str = "discount_strategy.service.v1"; +const PRICING_QUOTE_ECOMMERCE_DESCRIPTOR_ID: &str = "pricing_quote.ecommerce.v1"; +const PRICING_QUOTE_SERVICE_DESCRIPTOR_ID: &str = "pricing_quote.service.v1"; const FUNCTION_WRAPPER_PIPELINE_CHAIN3_COMPATIBILITY_KEY: &str = "function.wrapper.pipeline.chain3.v1"; const FUNCTION_WRAPPER_PIPELINE_NORMALIZED_REQUIRED_ARG_COMPATIBILITY_KEY: &str = @@ -721,7 +727,7 @@ pub fn project_semantic_review_with_context( surface @ (SupportedSurface::Function(_) | SupportedSurface::Seam(_)) => match mode { SemanticProjectionMode::Preserve => existing .filter(|review| supported_surface_matches_existing_review(surface, review)) - .cloned(), + .map(|review| canonicalize_preserved_supported_review(spec, surface, review)), SemanticProjectionMode::Refresh => { evaluate_supported_semantic_review(spec, surface, context, &mut stack) } @@ -730,13 +736,25 @@ pub fn project_semantic_review_with_context( SemanticProjectionMode::Preserve => None, SemanticProjectionMode::Refresh => Some(match unit_kind { UnitKind::Function => unsupported_function_review(spec, context, &mut stack), - kind if is_portability_seam_kind(kind) => unsupported_surface_review(kind), + kind if is_portability_seam_kind(kind) => unsupported_surface_review(spec, kind), _ => unreachable!("non-function unit kinds are portability seams"), }), }, } } +fn canonicalize_preserved_supported_review( + spec: &LoadedSpec, + surface: SupportedSurface, + review: &SemanticReview, +) -> SemanticReview { + let mut review = review.clone(); + if matches!(surface, SupportedSurface::Seam(_)) && review.descriptor_id.is_none() { + review.descriptor_id = seam_descriptor_id(spec); + } + review +} + pub fn semantic_health_effect(review: Option<&SemanticReview>) -> SemanticHealthEffect { let Some(review) = review else { return SemanticHealthEffect::KeepBase; @@ -792,7 +810,7 @@ pub fn evaluate_semantic_review_with_context( } SupportedSurface::Unsupported(unit_kind) => Some(match unit_kind { UnitKind::Function => unsupported_function_review(spec, context, &mut stack), - kind if is_portability_seam_kind(kind) => unsupported_surface_review(kind), + kind if is_portability_seam_kind(kind) => unsupported_surface_review(spec, kind), _ => unreachable!("non-function unit kinds are portability seams"), }), } @@ -839,10 +857,11 @@ fn evaluate_supported_semantic_review( } } -fn unsupported_surface_review(unit_kind: UnitKind) -> SemanticReview { +fn unsupported_surface_review(spec: &LoadedSpec, unit_kind: UnitKind) -> SemanticReview { SemanticReview { verdict: SemanticVerdict::UnderSpecified, compatibility_key: unsupported_surface_compatibility_key(unit_kind), + descriptor_id: seam_descriptor_id(spec), support_status: (unit_kind == UnitKind::Function) .then_some(SemanticSupportStatus::Unsupported), unsupported_reason_codes: Vec::new(), @@ -867,6 +886,7 @@ impl UnsupportedFunctionDiagnostic { SemanticReview { verdict: SemanticVerdict::UnderSpecified, compatibility_key: UNSUPPORTED_FUNCTION_COMPATIBILITY_KEY.to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Unsupported), unsupported_reason_codes: vec![self.reason_code], rewrite_hints: self @@ -1146,6 +1166,7 @@ fn evaluate_supported_sum_semantic_review(spec: &LoadedSpec) -> Option Option Option Option Option Some(SemanticReview { verdict: SemanticVerdict::Aligned, compatibility_key: compatibility_key.to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Supported), unsupported_reason_codes: Vec::new(), rewrite_hints: Vec::new(), @@ -1344,6 +1371,7 @@ fn evaluate_supported_function_semantic_review( SupportedBodyClassification::Contradictory => Some(SemanticReview { verdict: SemanticVerdict::SemanticDrift, compatibility_key: compatibility_key.to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Supported), unsupported_reason_codes: Vec::new(), rewrite_hints: Vec::new(), @@ -1356,6 +1384,7 @@ fn evaluate_supported_function_semantic_review( SupportedBodyClassification::OutsideHonestSubset => Some(SemanticReview { verdict: SemanticVerdict::UnderSpecified, compatibility_key: compatibility_key.to_string(), + descriptor_id: None, support_status: Some(SemanticSupportStatus::Supported), unsupported_reason_codes: Vec::new(), rewrite_hints: Vec::new(), @@ -1449,6 +1478,30 @@ fn detect_data_pricing_quote_family(spec: &LoadedSpec) -> bool { && executable_has_required_checkout_quote_roles(&executable.methods) } +fn seam_descriptor_id(spec: &LoadedSpec) -> Option { + match spec.spec.unit_kind().ok()? { + UnitKind::Sum => supported_sum_descriptor_id(spec), + UnitKind::Data => supported_data_descriptor_id(spec), + _ => None, + } +} + +fn supported_sum_descriptor_id(spec: &LoadedSpec) -> Option { + match spec.spec.id.as_str() { + "pricing/discount_strategy" => Some(DISCOUNT_STRATEGY_ECOMMERCE_DESCRIPTOR_ID.to_string()), + "billing/discount_strategy" => Some(DISCOUNT_STRATEGY_SERVICE_DESCRIPTOR_ID.to_string()), + _ => None, + } +} + +fn supported_data_descriptor_id(spec: &LoadedSpec) -> Option { + match spec.spec.id.as_str() { + "pricing/pricing_quote" => Some(PRICING_QUOTE_ECOMMERCE_DESCRIPTOR_ID.to_string()), + "billing/pricing_quote" => Some(PRICING_QUOTE_SERVICE_DESCRIPTOR_ID.to_string()), + _ => None, + } +} + fn authored_function_contract_is_supported( authored: &SemanticAuthoredFunctionPacket, family: SupportedFunctionFamily, @@ -1879,6 +1932,7 @@ fn evaluate_supported_checkout_quote_data_review(spec: &LoadedSpec) -> Option Option Option Option Option Date: Mon, 25 May 2026 21:40:34 +0000 Subject: [PATCH 22/24] Add descriptor_id to semantic review test fixtures --- examples/ecommerce/units/money/round.spec.passport.json | 2 +- .../ecommerce/units/pricing/apply_discount.spec.passport.json | 2 +- examples/ecommerce/units/pricing/apply_tax.spec.passport.json | 2 +- .../ecommerce/units/pricing/calculate_total.spec.passport.json | 2 +- .../pricing/calculate_total_guarded_tax.spec.passport.json | 2 +- .../units/pricing/discount_strategy.spec.passport.json | 3 ++- .../ecommerce/units/pricing/pricing_quote.spec.passport.json | 3 ++- spec-cli/src/commands.rs | 2 ++ 8 files changed, 11 insertions(+), 7 deletions(-) diff --git a/examples/ecommerce/units/money/round.spec.passport.json b/examples/ecommerce/units/money/round.spec.passport.json index 32a8fb2..e935da9 100644 --- a/examples/ecommerce/units/money/round.spec.passport.json +++ b/examples/ecommerce/units/money/round.spec.passport.json @@ -13,7 +13,7 @@ }, "deps": [], "local_tests": [], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/money/round.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json index b2f38b7..b5cae7d 100644 --- a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json @@ -28,7 +28,7 @@ "expect": "apply_discount(Decimal::new(10000, 2), Decimal::new(10, 2)) == Decimal::new(9000, 2)" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/apply_discount.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json index 6df9fd1..39f4ff4 100644 --- a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json @@ -27,7 +27,7 @@ "expect": "apply_tax(Decimal::new(10000, 2), Decimal::new(725, 4)) == Decimal::new(10725, 2)" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/apply_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json index 9ba6a4b..0d8e3fa 100644 --- a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/calculate_total.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json index 9d3c391..081363b 100644 --- a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total_guarded_tax(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/calculate_total_guarded_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json index 6c48fb8..3306b47 100644 --- a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json +++ b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json @@ -143,7 +143,7 @@ "expect": "DiscountStrategy::None.fixed_amount_capped_behavior_holds()" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", @@ -290,6 +290,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "sum.discount_strategy.v1", + "descriptor_id": "discount_strategy.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker", "proof_helper_only_marker" diff --git a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json index b50abb2..28af5bd 100644 --- a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json +++ b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json @@ -100,7 +100,7 @@ "expect": "PricingQuote::new(rust_decimal::Decimal::new(10000, 2), rust_decimal::Decimal::new(10, 2), rust_decimal::Decimal::new(725, 4)).total() == rust_decimal::Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-24T14:14:09Z", + "generated_at": "2026-05-25T21:37:14Z", "source_file": "examples/ecommerce/units/pricing/pricing_quote.unit.spec", "evidence": { "build_status": "pass", @@ -189,6 +189,7 @@ "semantic_review": { "verdict": "backend_only_meaning_preserved", "compatibility_key": "data.pricing_quote.v1", + "descriptor_id": "pricing_quote.ecommerce.v1", "reason_codes": [ "backend_only_execution_marker" ], diff --git a/spec-cli/src/commands.rs b/spec-cli/src/commands.rs index 343fc69..0d03799 100644 --- a/spec-cli/src/commands.rs +++ b/spec-cli/src/commands.rs @@ -7432,6 +7432,7 @@ body: let supported_incomplete_review = SemanticReview { verdict: spec_core::semantic_review::SemanticVerdict::UnderSpecified, compatibility_key: "data.pricing_quote.v1".to_string(), + descriptor_id: None, support_status: None, unsupported_reason_codes: vec![], rewrite_hints: vec![], @@ -7446,6 +7447,7 @@ body: let supported_failing_review = SemanticReview { verdict: spec_core::semantic_review::SemanticVerdict::SemanticDrift, compatibility_key: "data.pricing_quote.v1".to_string(), + descriptor_id: None, support_status: None, unsupported_reason_codes: vec![], rewrite_hints: vec![], From 944585ee85a72f0f9337450c33b51f4416deb85d Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 22:55:08 +0000 Subject: [PATCH 23/24] Fix stored-truth seam descriptor backfill in preserve mode --- spec-cli/tests/cli.rs | 171 +++++++++++++++++++++++++++++++ spec-core/src/passport.rs | 156 ++++++++++++++++++++++++++++ spec-core/src/semantic_review.rs | 36 +++---- 3 files changed, 343 insertions(+), 20 deletions(-) diff --git a/spec-cli/tests/cli.rs b/spec-cli/tests/cli.rs index 330aff4..234c417 100644 --- a/spec-cli/tests/cli.rs +++ b/spec-cli/tests/cli.rs @@ -551,6 +551,20 @@ fn rewrite_json_field(path: &Path, field: &str, value: Value) { fs::write(path, serde_json::to_string_pretty(&json).unwrap()).unwrap(); } +fn clear_passport_semantic_review_descriptor_id(passport_path: &Path) -> Value { + let mut passport = read_passport_json(passport_path); + passport["semantic_review"] + .as_object_mut() + .unwrap() + .remove("descriptor_id"); + fs::write( + passport_path, + serde_json::to_string_pretty(&passport).unwrap(), + ) + .unwrap(); + read_passport_json(passport_path)["semantic_review"].clone() +} + fn write_status_project(project_dir: &Path) -> PathBuf { let units_dir = project_dir.join("units"); let pricing_dir = units_dir.join("pricing"); @@ -15128,6 +15142,163 @@ fn export_benchmark_root_contract_matches_frozen_fixture() { ); } +#[test] +fn benchmark_read_surfaces_do_not_backfill_missing_supported_seam_descriptor_ids() { + let (_temp_dir, repo_dir) = copy_benchmark_repo_fixture(); + let data_passport_path = + repo_dir.join("examples/ecommerce/units/pricing/pricing_quote.spec.passport.json"); + let sum_passport_path = + repo_dir.join("examples/ecommerce/units/pricing/discount_strategy.spec.passport.json"); + + let stored_data_review = clear_passport_semantic_review_descriptor_id(&data_passport_path); + let stored_sum_review = clear_passport_semantic_review_descriptor_id(&sum_passport_path); + assert_eq!(stored_data_review["descriptor_id"], Value::Null); + assert_eq!(stored_sum_review["descriptor_id"], Value::Null); + + let status_output = run_in( + &repo_dir, + &["status", "examples/ecommerce/units", "--format", "json"], + ); + assert_output_success( + "benchmark-root status should not backfill missing seam descriptor ids", + &status_output, + ); + let status_json = parse_stdout_json(&status_output); + let status_benchmark = status_json["benchmarks"] + .as_array() + .unwrap() + .iter() + .find(|benchmark| benchmark["benchmark_id"] == "BENCH-ECOM") + .unwrap(); + assert_eq!(status_benchmark["accounting_status"], "invalid"); + assert_eq!(status_benchmark["benchmark_status"], "invalid"); + + for unit_id in ["pricing/pricing_quote", "pricing/discount_strategy"] { + let status_unit = status_units(&status_json) + .iter() + .find(|unit| unit["id"] == unit_id) + .unwrap(); + assert_eq!(status_unit["semantic_review"]["descriptor_id"], Value::Null); + assert_eq!( + status_unit["category_qualification"]["claim_status"], + "unqualified" + ); + assert_eq!( + status_unit["category_qualification"]["reason_code"], + "descriptor_id_missing" + ); + + let benchmark_case = status_benchmark["cases"] + .as_array() + .unwrap() + .iter() + .find(|case| case["carrier_id"] == unit_id) + .unwrap(); + assert_eq!( + benchmark_case["category_qualification"]["claim_status"], + "unqualified" + ); + assert_eq!( + benchmark_case["category_qualification"]["reason_code"], + "descriptor_id_missing" + ); + assert_eq!( + benchmark_case["counts_as_supported_positive"], + Value::Bool(false) + ); + } + + let export_output = run_in(&repo_dir, &["export", "examples/ecommerce/units"]); + assert_output_success( + "benchmark-root export should not backfill missing seam descriptor ids", + &export_output, + ); + let export_json = parse_stdout_json(&export_output); + let export_benchmark = export_json["benchmarks"] + .as_array() + .unwrap() + .iter() + .find(|benchmark| benchmark["benchmark_id"] == "BENCH-ECOM") + .unwrap(); + assert_eq!(export_benchmark["accounting_status"], "invalid"); + assert_eq!(export_benchmark["benchmark_status"], "invalid"); + + for unit_id in ["pricing/pricing_quote", "pricing/discount_strategy"] { + let projected_unit = export_json["projected_units"] + .as_array() + .unwrap() + .iter() + .find(|unit| unit["id"] == unit_id) + .unwrap(); + assert_eq!( + projected_unit["semantic_review"]["descriptor_id"], + Value::Null + ); + assert_eq!( + projected_unit["category_qualification"]["claim_status"], + "unqualified" + ); + assert_eq!( + projected_unit["category_qualification"]["reason_code"], + "descriptor_id_missing" + ); + + let benchmark_case = export_benchmark["cases"] + .as_array() + .unwrap() + .iter() + .find(|case| case["carrier_id"] == unit_id) + .unwrap(); + assert_eq!( + benchmark_case["category_qualification"]["claim_status"], + "unqualified" + ); + assert_eq!( + benchmark_case["category_qualification"]["reason_code"], + "descriptor_id_missing" + ); + assert_eq!( + benchmark_case["counts_as_supported_positive"], + Value::Bool(false) + ); + } + + let snapshot_output = run_in(&repo_dir, &["benchmark", "snapshot", "BENCH-ECOM"]); + assert_output_success( + "benchmark snapshot should not backfill missing seam descriptor ids", + &snapshot_output, + ); + let snapshot: Value = serde_json::from_str( + &fs::read_to_string(repo_dir.join("benchmarks/snapshots/BENCH-ECOM.snapshot.json")) + .unwrap(), + ) + .unwrap(); + let snapshot_projection = &snapshot["projection"]; + assert_eq!(snapshot_projection["accounting_status"], "invalid"); + assert_eq!(snapshot_projection["benchmark_status"], "invalid"); + + for unit_id in ["pricing/pricing_quote", "pricing/discount_strategy"] { + let benchmark_case = snapshot_projection["cases"] + .as_array() + .unwrap() + .iter() + .find(|case| case["carrier_id"] == unit_id) + .unwrap(); + assert_eq!( + benchmark_case["category_qualification"]["claim_status"], + "unqualified" + ); + assert_eq!( + benchmark_case["category_qualification"]["reason_code"], + "descriptor_id_missing" + ); + assert_eq!( + benchmark_case["counts_as_supported_positive"], + Value::Bool(false) + ); + } +} + #[test] fn export_repo_root_contract_matches_frozen_fixture() { let (_temp_dir, repo_dir) = copy_benchmark_repo_fixture(); diff --git a/spec-core/src/passport.rs b/spec-core/src/passport.rs index da90d6c..8b096f6 100644 --- a/spec-core/src/passport.rs +++ b/spec-core/src/passport.rs @@ -1758,6 +1758,117 @@ mod tests { spec } + fn make_supported_discount_strategy_sum_seam() -> LoadedSpec { + LoadedSpec { + source: SpecSource { + file_path: "units/pricing/discount_strategy.unit.spec".to_string(), + id: "pricing/discount_strategy".to_string(), + }, + spec: SpecStruct { + id: "pricing/discount_strategy".to_string(), + kind: "sum".to_string(), + intent: Intent { + why: "Represent discount strategies that cap fixed discounts at the subtotal." + .to_string(), + }, + contract: None, + deps: vec![], + imports: vec![], + body: Body::default(), + local_tests: vec![], + links: None, + spec_version: Some("0.3.0".to_string()), + extensions: UnitExtensions { + sum: Some(AuthoredSumShape { + variants: IndexMap::from([ + ("none".to_string(), AuthoredSumVariant::default()), + ( + "percentage".to_string(), + AuthoredSumVariant { + fields: IndexMap::from([( + "rate".to_string(), + AuthoredField { + type_: "Decimal".to_string(), + }, + )]), + }, + ), + ( + "fixed_amount".to_string(), + AuthoredSumVariant { + fields: IndexMap::from([( + "amount".to_string(), + AuthoredField { + type_: "Decimal".to_string(), + }, + )]), + }, + ), + ]), + }), + methods: vec![ + AuthoredMethod { + id: "discount_amount".to_string(), + intent: Intent { + why: "Return the capped discount amount to subtract from the subtotal." + .to_string(), + }, + receiver: "shared_ref".to_string(), + contract: Some(Contract { + inputs: Some(IndexMap::from([( + "subtotal".to_string(), + "Decimal".to_string(), + )])), + returns: Some("Decimal".to_string()), + invariants: vec![], + }), + deps: vec![], + lowering: Some(AuthoredMethodLowering { + rust: Some(AuthoredRustMethodLowering { + body: r#"{ + match self { + Self::None => Decimal::ZERO, + Self::Percentage { rate } => subtotal * *rate, + Self::FixedAmount { amount } => (*amount).min(subtotal), + } + }"# + .to_string(), + }), + }), + }, + AuthoredMethod { + id: "discounted_subtotal".to_string(), + intent: Intent { + why: "Return the subtotal after applying the selected discount strategy." + .to_string(), + }, + receiver: "shared_ref".to_string(), + contract: Some(Contract { + inputs: Some(IndexMap::from([( + "subtotal".to_string(), + "Decimal".to_string(), + )])), + returns: Some("Decimal".to_string()), + invariants: vec![], + }), + deps: vec![], + lowering: Some(AuthoredMethodLowering { + rust: Some(AuthoredRustMethodLowering { + body: r#"{ + subtotal - self.discount_amount(subtotal) + }"# + .to_string(), + }), + }), + }, + ], + backends: None, + ..UnitExtensions::default() + }, + }, + } + } + fn make_discount_strategy_molecule_test() -> LoadedMoleculeTest { LoadedMoleculeTest { source: MoleculeTestSource { @@ -2888,6 +2999,51 @@ mod tests { assert_eq!(rebuilt.semantic_review, Some(supported_review)); } + #[test] + fn build_passport_preserving_proof_state_with_context_keeps_missing_sum_seam_descriptor_id() { + let spec = make_supported_discount_strategy_sum_seam(); + let specs_by_id = HashMap::from([(spec.spec.id.clone(), spec.clone())]); + let semantic_review_context = SemanticReviewContext::new(&specs_by_id); + let mut supported_review = evaluate_semantic_review(&spec).unwrap(); + supported_review.descriptor_id = None; + let mut existing = make_current_passport(&spec); + existing.semantic_review = Some(supported_review.clone()); + + let rebuilt = build_passport_preserving_proof_state_with_context( + &spec, + "2026-04-23T00:00:00Z", + Some(&existing), + existing.contract_hash.clone(), + &semantic_review_context, + ); + + assert_eq!(rebuilt.semantic_review, Some(supported_review)); + } + + #[test] + fn build_passport_preserving_proof_state_with_context_keeps_missing_data_seam_descriptor_id() { + let spec = make_loaded_data_seam( + "pricing/pricing_quote", + "units/pricing/pricing_quote.unit.spec", + ); + let specs_by_id = HashMap::from([(spec.spec.id.clone(), spec.clone())]); + let semantic_review_context = SemanticReviewContext::new(&specs_by_id); + let mut supported_review = evaluate_semantic_review(&spec).unwrap(); + supported_review.descriptor_id = None; + let mut existing = make_current_passport(&spec); + existing.semantic_review = Some(supported_review.clone()); + + let rebuilt = build_passport_preserving_proof_state_with_context( + &spec, + "2026-04-23T00:00:00Z", + Some(&existing), + existing.contract_hash.clone(), + &semantic_review_context, + ); + + assert_eq!(rebuilt.semantic_review, Some(supported_review)); + } + #[test] fn build_passport_preserving_proof_state_with_context_drops_mismatched_old_exact_id_review() { let apply_discount = diff --git a/spec-core/src/semantic_review.rs b/spec-core/src/semantic_review.rs index d786996..9368bd3 100644 --- a/spec-core/src/semantic_review.rs +++ b/spec-core/src/semantic_review.rs @@ -727,7 +727,7 @@ pub fn project_semantic_review_with_context( surface @ (SupportedSurface::Function(_) | SupportedSurface::Seam(_)) => match mode { SemanticProjectionMode::Preserve => existing .filter(|review| supported_surface_matches_existing_review(surface, review)) - .map(|review| canonicalize_preserved_supported_review(spec, surface, review)), + .cloned(), SemanticProjectionMode::Refresh => { evaluate_supported_semantic_review(spec, surface, context, &mut stack) } @@ -743,18 +743,6 @@ pub fn project_semantic_review_with_context( } } -fn canonicalize_preserved_supported_review( - spec: &LoadedSpec, - surface: SupportedSurface, - review: &SemanticReview, -) -> SemanticReview { - let mut review = review.clone(); - if matches!(surface, SupportedSurface::Seam(_)) && review.descriptor_id.is_none() { - review.descriptor_id = seam_descriptor_id(spec); - } - review -} - pub fn semantic_health_effect(review: Option<&SemanticReview>) -> SemanticHealthEffect { let Some(review) = review else { return SemanticHealthEffect::KeepBase; @@ -7119,7 +7107,7 @@ mod tests { } #[test] - fn project_semantic_review_preserve_restores_missing_seam_descriptor_id() { + fn project_semantic_review_preserve_keeps_missing_sum_seam_descriptor_id() { let spec = canonical_discount_strategy_sum_spec(); let mut review = evaluate_semantic_review(&spec).unwrap(); review.descriptor_id = None; @@ -7128,12 +7116,20 @@ mod tests { project_semantic_review(&spec, Some(&review), SemanticProjectionMode::Preserve) .unwrap(); - assert_eq!( - preserved.descriptor_id, - Some("discount_strategy.ecommerce.v1".to_string()) - ); - assert_eq!(preserved.compatibility_key, review.compatibility_key); - assert_eq!(preserved.verdict, review.verdict); + assert_eq!(preserved, review); + } + + #[test] + fn project_semantic_review_preserve_keeps_missing_data_seam_descriptor_id() { + let spec = canonical_pricing_quote_data_spec(); + let mut review = evaluate_semantic_review(&spec).unwrap(); + review.descriptor_id = None; + + let preserved = + project_semantic_review(&spec, Some(&review), SemanticProjectionMode::Preserve) + .unwrap(); + + assert_eq!(preserved, review); } #[test] From adaa8d98eadec603676ba8fa0211e42ea21cd812 Mon Sep 17 00:00:00 2001 From: Spenser Mcconnell Date: Mon, 25 May 2026 23:33:30 +0000 Subject: [PATCH 24/24] Skip no-op passport rewrites in spec generate --- DECISIONS.md | 1 + README.md | 5 ++ examples/ecommerce/README.md | 5 ++ .../units/money/round.spec.passport.json | 2 +- .../pricing/apply_discount.spec.passport.json | 2 +- .../pricing/apply_tax.spec.passport.json | 2 +- .../calculate_total.spec.passport.json | 2 +- ...ulate_total_guarded_tax.spec.passport.json | 2 +- .../discount_strategy.spec.passport.json | 2 +- .../pricing/pricing_quote.spec.passport.json | 2 +- spec-cli/tests/cli.rs | 52 +++++++++++++++++++ spec-core/src/passport.rs | 43 ++++++++++++++- 12 files changed, 112 insertions(+), 8 deletions(-) diff --git a/DECISIONS.md b/DECISIONS.md index 43f2681..2972a69 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -74,6 +74,7 @@ Until then: JSON Schema + semantic validation in Rust is the implementation path - The pipeline commands are optimized for validate → generate → cargo execution in one local flow. - The existing `.spec-generated` marker and output cleanup rules already model the output tree as spec-owned. - Teams that want committed generated code for diffs or IDE discoverability can continue to use `spec generate` directly and commit that output intentionally. +- Co-located proof artifacts may still be tracked by examples or fixtures, so no-op `spec generate` runs should avoid timestamp-only passport rewrites when no proof or authored truth changed. **Revisit when** diff --git a/README.md b/README.md index a963588..54f3be2 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,11 @@ Files that change during that loop: - unit proof in `*.spec.passport.json` - molecule proof in `*.test.evidence.json` +`src/generated/` is ephemeral output and is gitignored in this repo. The +canonical example's proof artifacts are tracked on purpose so `spec status` stays +truthful on a fresh clone. Re-running `spec generate` on an unchanged tree +should not rewrite tracked passports just to bump `generated_at`. + If you install the binary, the command name is `spec`, but the repo-native `cargo run -p spec-cli -- ...` flow is the safest place to start. diff --git a/examples/ecommerce/README.md b/examples/ecommerce/README.md index ff09273..34e81f5 100644 --- a/examples/ecommerce/README.md +++ b/examples/ecommerce/README.md @@ -32,6 +32,11 @@ What changes when you run that loop: - unit proof in `examples/ecommerce/units/**/*.spec.passport.json` - molecule proof in `examples/ecommerce/units/**/*.test.evidence.json` +In this example, `src/generated/` is ephemeral and gitignored, but the proof +artifacts are tracked so the example stays truthful on a fresh clone. Re-running +`spec generate` on an unchanged tree should leave those tracked passports alone +instead of churning `generated_at`. + If you have `spec` installed locally, the shorter example-root form is: ```bash diff --git a/examples/ecommerce/units/money/round.spec.passport.json b/examples/ecommerce/units/money/round.spec.passport.json index e935da9..cd336b2 100644 --- a/examples/ecommerce/units/money/round.spec.passport.json +++ b/examples/ecommerce/units/money/round.spec.passport.json @@ -13,7 +13,7 @@ }, "deps": [], "local_tests": [], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/money/round.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json index b5cae7d..e86880c 100644 --- a/examples/ecommerce/units/pricing/apply_discount.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_discount.spec.passport.json @@ -28,7 +28,7 @@ "expect": "apply_discount(Decimal::new(10000, 2), Decimal::new(10, 2)) == Decimal::new(9000, 2)" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/apply_discount.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json index 39f4ff4..510c097 100644 --- a/examples/ecommerce/units/pricing/apply_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/apply_tax.spec.passport.json @@ -27,7 +27,7 @@ "expect": "apply_tax(Decimal::new(10000, 2), Decimal::new(725, 4)) == Decimal::new(10725, 2)" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/apply_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json index 0d8e3fa..378cf62 100644 --- a/examples/ecommerce/units/pricing/calculate_total.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/calculate_total.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json index 081363b..a00b5d6 100644 --- a/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json +++ b/examples/ecommerce/units/pricing/calculate_total_guarded_tax.spec.passport.json @@ -32,7 +32,7 @@ "expect": "calculate_total_guarded_tax(Decimal::new(10000, 2), Decimal::new(10, 2), Decimal::new(725, 4)) == Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/calculate_total_guarded_tax.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json index 3306b47..6e2c204 100644 --- a/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json +++ b/examples/ecommerce/units/pricing/discount_strategy.spec.passport.json @@ -143,7 +143,7 @@ "expect": "DiscountStrategy::None.fixed_amount_capped_behavior_holds()" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/discount_strategy.unit.spec", "evidence": { "build_status": "pass", diff --git a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json index 28af5bd..f32cd53 100644 --- a/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json +++ b/examples/ecommerce/units/pricing/pricing_quote.spec.passport.json @@ -100,7 +100,7 @@ "expect": "PricingQuote::new(rust_decimal::Decimal::new(10000, 2), rust_decimal::Decimal::new(10, 2), rust_decimal::Decimal::new(725, 4)).total() == rust_decimal::Decimal::new(96525, 3)" } ], - "generated_at": "2026-05-25T21:37:14Z", + "generated_at": "2026-05-25T23:09:51Z", "source_file": "examples/ecommerce/units/pricing/pricing_quote.unit.spec", "evidence": { "build_status": "pass", diff --git a/spec-cli/tests/cli.rs b/spec-cli/tests/cli.rs index 234c417..9347ce6 100644 --- a/spec-cli/tests/cli.rs +++ b/spec-cli/tests/cli.rs @@ -5400,6 +5400,58 @@ fn spec_generate_preserves_passport_evidence_from_prior_test() { ); } +#[test] +fn spec_generate_second_run_keeps_tracked_passport_bytes_when_nothing_changed() { + if !cargo_available() { + return; + } + + let temp_dir = temp_repo_dir(); + write_pricing_project(temp_dir.path(), true); + + let seed = Command::new(bin()) + .current_dir(temp_dir.path()) + .args([ + "test", + "units/pricing", + "--output", + "src/generated", + "--crate-root", + temp_dir.path().to_str().unwrap(), + ]) + .output() + .expect("failed to seed spec passports"); + assert_output_success("spec test should seed passports", &seed); + + let passport_path = temp_dir + .path() + .join("units/pricing/apply_tax.spec.passport.json"); + + let first_generate = Command::new(bin()) + .current_dir(temp_dir.path()) + .args(["generate", "units/pricing", "--output", "src/generated"]) + .output() + .expect("failed to run first spec generate"); + assert_output_success( + "first spec generate should succeed after spec test", + &first_generate, + ); + let first_content = fs::read_to_string(&passport_path).unwrap(); + + let second_generate = Command::new(bin()) + .current_dir(temp_dir.path()) + .args(["generate", "units/pricing", "--output", "src/generated"]) + .output() + .expect("failed to run second spec generate"); + assert_output_success( + "second spec generate should succeed after spec test", + &second_generate, + ); + let second_content = fs::read_to_string(&passport_path).unwrap(); + + assert_eq!(second_content, first_content); +} + #[test] fn spec_generate_preserves_passport_provenance_from_prior_test() { if !cargo_available() || !git_available() { diff --git a/spec-core/src/passport.rs b/spec-core/src/passport.rs index 8b096f6..bc78fd6 100644 --- a/spec-core/src/passport.rs +++ b/spec-core/src/passport.rs @@ -1178,13 +1178,26 @@ pub fn read_passport(source_path: &Path) -> Result> { pub fn write_passport(passport: &Passport, source_file_path: &Path) -> Result<()> { let mut passport = passport.clone(); sync_rust_target_proof_fields(&mut passport); + let passport_path = passport_path_for(source_file_path)?; + if let Ok(Some(existing)) = read_passport(source_file_path) + && passports_equal_ignoring_generated_at(&passport, &existing) + { + return Ok(()); + } let json = serde_json::to_string_pretty(&passport).map_err(|e| SpecError::Generator { message: format!("Failed to serialize passport for '{}': {e}", passport.id), })?; - let passport_path = passport_path_for(source_file_path)?; write_generated_file(&passport_path.display().to_string(), &json) } +fn passports_equal_ignoring_generated_at(left: &Passport, right: &Passport) -> bool { + let mut left = left.clone(); + let mut right = right.clone(); + left.generated_at.clear(); + right.generated_at.clear(); + left == right +} + fn sync_rust_target_proof_fields(passport: &mut Passport) { let mut target_proofs = passport.target_proofs.clone().unwrap_or_default(); let top_level_present = passport.evidence.is_some() @@ -2674,6 +2687,34 @@ mod tests { ); } + #[test] + fn write_passport_skips_timestamp_only_rewrites() { + let dir = TempDir::new().unwrap(); + let source_path = dir.path().join("apply_tax.unit.spec"); + fs::write(&source_path, "").unwrap(); + + let spec = make_loaded_spec( + "pricing/apply_tax", + source_path.to_str().unwrap(), + Some("0.3.0"), + None, + vec![], + vec![], + ); + let first = build_passport(&spec, "2026-04-04T00:00:00Z"); + write_passport(&first, &source_path).unwrap(); + let passport_path = dir.path().join("apply_tax.spec.passport.json"); + let first_content = fs::read_to_string(&passport_path).unwrap(); + + let second = build_passport(&spec, "2026-04-05T00:00:00Z"); + write_passport(&second, &source_path).unwrap(); + let second_content = fs::read_to_string(&passport_path).unwrap(); + + assert_eq!(second_content, first_content); + let parsed: Passport = serde_json::from_str(&second_content).unwrap(); + assert_eq!(parsed.generated_at, "2026-04-04T00:00:00Z"); + } + #[test] fn build_passport_with_evidence_serializes_observed_results() { let spec = make_loaded_spec(