@sabaiway/agent-workflow-engine 3.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,68 @@ All notable changes to the methodology engine. Versions are this **package's** n
4
4
  they are distinct from the **deployment-lineage** stamp written into a project's `docs/ai/`
5
5
  (which tracks the shared `agent-workflow` lineage, head `3.0.0`).
6
6
 
7
+ ## 4.0.0 — the scenario floor, and five answers the canon now states at its own points of use (AD-117)
8
+
9
+ Slice 4 wrote the layer's first real specs and came back with six questions the canon had left for
10
+ the next reader to rediscover. Five are answered here, IN `references/specs.md`, each at the point
11
+ where it is needed — and four of the five answers are a refusal to add a mechanism.
12
+
13
+ > ### ⚠ BREAKING — a spec written to the 3.3.0 canon can be invalid under this one
14
+ >
15
+ > `## Scenarios` gains a floor: at least one scenario line, with no empty-marker escape. A document
16
+ > the 3.3.0 canon and its reader both accepted now refuses as `scenarios-empty`. Same class as
17
+ > **3.0.0**, where deleting a vocabulary from the canon was already called MAJOR. The remedy is one
18
+ > line per scenario — `- S<N> <name> :: unbound` while no test pins it.
19
+
20
+ - **(1) No empty-marker escape on `## Scenarios`.** `*(empty)*` written there refuses as
21
+ `scenario-line`; `unbound` already covers a scenario no test pins yet. An empty exclusion list is a
22
+ claim, an empty scenario list is an absence. The refusal table gains the matching `scenarios-empty`
23
+ row, in the reader's own order.
24
+ - **(2) A binding marker is an ORDINARY source line.** It counts toward the source-size practice like
25
+ any other line, and a pinning file already at its cap raises its recorded ratchet to host one. No
26
+ carve-out: the size judge counts bytes and must not learn spec vocabulary.
27
+ - **(3) There is NO sidecar binding form.** Retroactive coverage of a PUBLISHED package rides a
28
+ release train, because the marker moves that package's subtree — a cost the family already pays
29
+ deliberately, against a second place a binding could live.
30
+ - **(4) No `root` op verb.** The store root is the navigator, never an op target; it is judged as the
31
+ listing parent of its declared child. The canon says so where containment is defined.
32
+ - **(5) The promoting event of a RETROACTIVE draft is PLAN APPROVAL.** `live` lands in the SAME slice
33
+ that authors the draft, once every scenario is bound — otherwise a slice whose whole deliverable IS
34
+ the specs has no later landing row, and the store's first contracts stay permanently provisional by
35
+ the canon's own wording.
36
+ - `test/specs-canon.test.mjs` pins all five as prose assertions at their point of use, absorbs the
37
+ new rule in the rules-table deep-equal, and gains two refuse fixtures — the empty section and the
38
+ empty marker — each yielding exactly its one rule. The arm was red-proofed against the unchanged
39
+ canon.
40
+
41
+
42
+ ## 3.3.0 — the feature-spec canon: `references/specs.md`, the `Spec-first` lens bullet, specs as plan rows (AD-112)
43
+
44
+ - **`references/specs.md`** (NEW) — the canon of the feature-spec layer in the family's own
45
+ vocabulary: where specs live and how the navigator counts them; the FROZEN schema (kinds,
46
+ statuses and their forward-only transitions, the caps and the numeric fan-out / promotion
47
+ thresholds, the slug pattern, the scenario-binding grammar, the module-root grammar, the
48
+ Out-of-scope rule, the advisory unbound warning); the shape per kind; the 33-row refusal table
49
+ the deployed reader enforces; lifecycle, per-scenario binding and atomic plan approval; the
50
+ complete feature-vs-page precedence table; spec-driven feature-sliced architecture with ONE
51
+ dependency rule and an honestly stated review-level enforcement altitude; spec vs ADR; the
52
+ retroactive onboarding path; the scale budget. `test/specs-canon.test.mjs` pins this file against
53
+ the reader's `SPEC_SCHEMA` — no second source of the numbers — reads the repo-only fixture corpus
54
+ (81 files: every accept case clean, every refuse case exactly its one rule) and reads the two
55
+ rendered templates clean.
56
+ - **`references/agent-rules-lens.md`** gains ONE bullet, `Spec-first`: a plan names its governing
57
+ spec(s) — zero, one or many, one per touched slice — each one's Out of scope bounds that slice
58
+ (no global union; a conflict is a spec revision BEFORE approval); a new feature's draft spec
59
+ exists AT plan review, a contract change rides the plan as a proposed revision, and the revision
60
+ lands with the code; page-only coverage governs as an adoption shim. The outgoing body is appended
61
+ to the append-only prior store, so every unmodified deployment converges on the next kit touch.
62
+ - **`references/planning.md`** — Goal and boundary names the governing spec(s); a contract change
63
+ is a ledger row present at review (`create` = draft spec, `modify` = revision; the landing row
64
+ moves `draft -> live`, a removal row `live -> retired`). **`references/procedures.md`** —
65
+ plan-authoring step 2 names the governing spec(s) and writes the `create` / `modify` spec rows so
66
+ they exist at review; plan-execution step 3 lands the approved draft or revision WITH the code.
67
+ - `provides` stays `["plan"]`; the payload pins (tarball, installer) name the new file.
68
+
7
69
  ## 3.2.0 — the state table comes BEFORE the guard: enumerate by proof, never by exclusion (AD-111)
8
70
 
9
71
  The canon already said a repeat finding in one subarea routes to SUBTRACTION rather than a fourth
package/README.md CHANGED
@@ -42,6 +42,12 @@ the canonical methodology reference on disk:
42
42
  - [`references/planning.md`](references/planning.md) — the **plan canon**: the capped plan shape
43
43
  (Goal and boundary · Module ledger · Verification), the subtraction rubric, the plan-file
44
44
  lifecycle, the `queue.md` series index, and the mandatory final **Phase: Cleanup**.
45
+ - [`references/specs.md`](references/specs.md) — the **feature-spec canon**: the durable
46
+ per-feature contract layer under `docs/ai/specs/` (Contract · Scenarios · Out of scope · Module),
47
+ the frozen schema (kinds, statuses, forward-only transitions, the numeric fan-out and promotion
48
+ thresholds, the scenario-binding grammar, the refusal table), governing specs cited per plan, the
49
+ feature-vs-page precedence table, spec-driven feature-sliced architecture, and the retroactive
50
+ onboarding path. The deployed reader `scripts/spec-schema.mjs` (memory) is pinned against it.
45
51
  - [`references/methodology-slot.md`](references/methodology-slot.md) — the **bounded**
46
52
  methodology fragment the composition root injects into a deployed `AGENTS.md` (a short summary +
47
53
  pointer, kept under the entry point's line cap).
package/SKILL.md CHANGED
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  name: agent-workflow-engine
3
- description: Canonical home of the agent-workflow planning methodology — the capped plan shape (goal and boundary, module ledger, verification), plan lifecycle, queue.md series index, mandatory Cleanup phase, the bounded methodology slot fragment, the orchestration-recipe vocabulary (Solo / Reviewed / Council / Delegated), and the activity-procedures canon (plan-authoring / plan-execution, with typed recipe slots). A published, installable npm package (available:true) that *provides* the methodology text; it mutates nothing. The composition root (agent-workflow-kit) reads this canon LIVE from the installed engine and injects the bounded slots from it — one source of truth, no bundled mirror; `npx @sabaiway/agent-workflow-kit@latest init` installs the engine.
3
+ description: Canonical home of the agent-workflow planning methodology — the capped plan shape (goal and boundary, module ledger, verification), plan lifecycle, queue.md series index, mandatory Cleanup phase, the feature-spec canon (the durable per-feature contract layer with its frozen schema and Out-of-scope discipline), the bounded methodology slot fragment, the orchestration-recipe vocabulary (Solo / Reviewed / Council / Delegated), and the activity-procedures canon (plan-authoring / plan-execution, with typed recipe slots). A published, installable npm package (available:true) that *provides* the methodology text; it mutates nothing. The composition root (agent-workflow-kit) reads this canon LIVE from the installed engine and injects the bounded slots from it — one source of truth, no bundled mirror; `npx @sabaiway/agent-workflow-kit@latest init` installs the engine.
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '3.2.0'
6
+ version: '4.0.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-engine
@@ -22,6 +22,13 @@ slot fill is needed but the engine is absent, the kit's reconcile **fails loudly
22
22
  (Goal and boundary · Module ledger · Verification — literal headings), the subtraction rubric for
23
23
  review, the plan-file lifecycle (`docs/plans/*.md`, gitignored, never committed), the `queue.md`
24
24
  series index and the mandatory final **Phase: Cleanup**.
25
+ - [`references/specs.md`](references/specs.md) — the **feature-spec canon**: the durable per-feature
26
+ contract layer under `docs/ai/specs/` (Contract · Scenarios · Out of scope · Module), the frozen
27
+ schema (kinds, statuses, forward-only transitions, the numeric fan-out and promotion thresholds,
28
+ the scenario-binding grammar, the refusal table the deployed reader enforces), governing specs
29
+ cited per plan, the feature-vs-page precedence table, spec-driven feature-sliced architecture,
30
+ and the retroactive onboarding path. The memory substrate's deployed reader
31
+ `scripts/spec-schema.mjs` is pinned against this canon by the engine's own test.
25
32
  - [`references/methodology-slot.md`](references/methodology-slot.md) — the **bounded** fragment the
26
33
  composition root injects into a deployed project's `AGENTS.md`, between the
27
34
  `<!-- workflow:methodology:start -->` / `<!-- workflow:methodology:end -->` markers. A short
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-engine",
5
5
  "kind": "methodology-engine",
6
- "version": "3.2.0",
6
+ "version": "4.0.0",
7
7
  "available": true,
8
8
  "provides": ["plan"],
9
9
  "roles": {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-engine",
3
- "version": "3.2.0",
3
+ "version": "4.0.0",
4
4
  "description": "Canonical home of the agent-workflow planning methodology — the capped plan shape (goal and boundary, module ledger, verification), plan lifecycle, queue.md series index, and mandatory Cleanup phase, consumed by the kit (composition root). The methodology engine of the agent-workflow family.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -176,3 +176,21 @@ Apply these when authoring a plan, reviewing, folding a finding, or editing code
176
176
  - **Recipe fidelity.** Council runs every backend the recipe names, **every round**; silently dropping a ready backend for quota/convenience is a forbidden downgrade — an unavailable backend is a LOUD, stated degrade, never a quiet drop.
177
177
  - **ExitPlanMode ≠ execute.** A harness "approved — start coding" prompt authorizes the PLAN only; this methodology overrides it. Continue into execution only as a DELIBERATE transition after the plan + cold-start prompt exist, never an implicit slide.
178
178
  - **Cost lanes.** Route every step to the **cheapest adequate executor** — L0 deterministic script (the batched gate matrix over `gates.json`, the rotation `--check`s) · L1 cheap subagent (extraction/drafting only; the orchestrator verifies) · L2 subscription bridge · L3 frontier judgment. A step with **no named guardrail does not move down** a lane, and the **red lines never move down** (council review models · real code · ADR/handover/changelog-entry wording · persuasive copy · go/no-go · the approval asks). Own-error repair: salvage recorded state first (L0/L1, batched), never frontier re-derivation. **Prompt economy:** read-only fan-out (research/sweeps/extraction) runs ONLY on restricted-tool vehicles — a full-tool subagent for read-only work is a forbidden lane downgrade (invisible prompt-flood + blast radius), and a subagent is never told to shell out for facts obtainable read-only; the orchestrator's own shell form is ONE plain pipeline per call (a `;`/`&&` chain or env-prefixed invocation never matches a prefix allow rule); a fan-out launcher that gates per call yields to the agent-spawn lane — capability-gated: without restricted-tool vehicles (generic full-tool spawning does not count), read-only research stays in the orchestrator's own context, never a vehicle mandate a host cannot satisfy. Judgment, code, synthesis stay at the frontier lane (a task that genuinely runs/writes keeps a full-tool subagent); honest limit: no deterministic gate classifies a dispatch — canon at the point of use + placed vehicles + the retro loop. **Writer economy:** a stage's repeated WRITER commands batch — evidence declarations ride consecutive plain invocations of ONE allow-listed tool, other stage writers combine via one launcher per stage; never an unbatched writer scatter (each gated write is its own prompt).
179
+
180
+ <!-- prior: 2026-08-23 (AD-112) — before the spec-first bullet (the spec layer) -->
181
+ ### 2.x. Planning, review & process-fidelity invariants
182
+ Apply these when authoring a plan, reviewing, folding a finding, or editing code — the layer read **before any code change**. (Full canon: the project's planning / workflow-methodology + orchestration canon. This section is rendered from that canon and refreshed on upgrade; a custom edit is preserved verbatim, but flagged.)
183
+ - **Fold by code, not prose.** Before folding a code-touching finding into a plan or change, read the cited `file:line` and cite it — a prose fold drifts from the code and seeds the next bug.
184
+ - **Finding scope (plan-execution) — name the invariant BEFORE the edit.** During EXECUTION only — a plan under authoring has no shipped behaviour to call a live defect in, so plan-review carries none of this. Every finding names the invariant its fix would enforce, and where that invariant already lives decides the disposition: already an acceptance criterion of the phase → **fold here**; it would have to be ADDED → ship the **narrow fix** for the found site (red first, then green) and queue ONLY the generalization — a deferral row carries the invariant, the origin `file:line`, the narrow fix, its proof and a residual exposure declared NOT live; no correct narrow fix → **blocking**: the phase does not close, and it is **never queued**. Two bars declared before each round: a finding counts only if it changes a **WRITE/REMOVE decision** or is a false statement in shipped text; a repeat finding in one subarea **routes to SUBTRACTION**, not a fourth patch.
185
+ - **Right altitude.** Pin intent + invariants + acceptance criteria (named tests); leave fine code-mechanics to Execute, where prose cannot diverge from reality.
186
+ - **No code-mechanics in the plan.** A ledger row carries its path and anchor, and Verification carries the exact commands (the plan-shape canon) — checked syntax: the plan's own Verification runs them against an explicit expected outcome or gate; the only other syntax a plan may carry is a literal fixture/schema fragment a named test copies or validates. Un-run, logic-bearing syntax — control-flow, a regex, a glob, a grammar, an algorithm body, a mini-DSL — never lives in plan prose, however plausible or shell-verified it looks: a fold or draft that wants one is the trigger to write the test instead.
187
+ - **Test-as-spec.** Fold a code-touching finding into a red→green TEST, not a prose paragraph — the gate is the only deterministic checker; a paragraph cannot self-check.
188
+ - **Characterize-first.** Before editing UNCOVERED code, pin its current behavior in a green test, then edit — any unintended change goes red. Never edit what has no checker; first give it one. Keep edits atomic/reversible; prefer SUBTRACTIVE folds.
189
+ - **State table BEFORE the guard — enumerate by PROOF, never by exclusion.** The subtraction rule above fires on a repeat finding, which is a LATE signal: by then the review has paid for each miss. The EARLY signal is structural — a decision whose input has **several independent state dimensions** (is it tracked? do the bytes still match the source? does the neighbouring file exist?). Write the table first, admit the write with **ONE conjunction of proven facts**, and funnel every other cell into a single refusal; the table is then the table-driven test. An exclusion list (`if (bad1) return; if (bad2) return;`) fails **OPEN** on the first state nobody enumerated — and "unreadable" is a state, distinct from "absent". A reviewer cannot save you here: it judges the patch in front of it and can only name the NEXT missing state, one round at a time.
190
+ - **Fold minimally — prose has no checker.** An ephemeral, gitignored plan is prose with no executable checker; fold **minimally, in ONE place** and run a **self-consistency** read across the plan before every re-review — a fold that drifts several prose spots is what turns a 2-round review into churn.
191
+ - **Heavy review at the diff.** Plan-review settles architecture only (≤2 rounds, stop at the pre-existing→fold-induced crossover); the exhaustive per-row review runs against real compiling code + the full suite, where a regression fails a gate immediately. **Backend divergence** (one backend grounded-ships while another keeps revising mechanics) IS that crossover — resolve at altitude, don't exhaust the strictest backend; route an all-mechanics/CI or prose-only artifact to a **thin plan + diff-review**.
192
+ - **Convergence bar.** A review loop is CLEAN only when one round returns **0 blockers + 0 majors** from EVERY backend the recipe names (nits + a ship verdict is the stop). Folding ≠ convergence — re-review after folding.
193
+ - **Per-round emission.** Every review round emits **{round N · finding-origin tally · per-backend verdict}** so the crossover is a computed, visible signal, not a remembered rule.
194
+ - **Recipe fidelity.** Council runs every backend the recipe names, **every round**; silently dropping a ready backend for quota/convenience is a forbidden downgrade — an unavailable backend is a LOUD, stated degrade, never a quiet drop.
195
+ - **ExitPlanMode ≠ execute.** A harness "approved — start coding" prompt authorizes the PLAN only; this methodology overrides it. Continue into execution only as a DELIBERATE transition after the plan + cold-start prompt exist, never an implicit slide.
196
+ - **Cost lanes.** Route every step to the **cheapest adequate executor** — L0 deterministic script (the batched gate matrix over `gates.json`, the rotation `--check`s) · L1 cheap subagent (extraction/drafting only; the orchestrator verifies) · L2 subscription bridge · L3 frontier judgment. A step with **no named guardrail does not move down** a lane, and the **red lines never move down** (council review models · real code · ADR/handover/changelog-entry wording · persuasive copy · go/no-go · the approval asks). Own-error repair: salvage recorded state first (L0/L1, batched), never frontier re-derivation. **Prompt economy:** read-only fan-out (research/sweeps/extraction) runs ONLY on restricted-tool vehicles — a full-tool subagent for read-only work is a forbidden lane downgrade (invisible prompt-flood + blast radius), and a subagent is never told to shell out for facts obtainable read-only; the orchestrator's own shell form is ONE plain pipeline per call (a `;`/`&&` chain or env-prefixed invocation never matches a prefix allow rule); a fan-out launcher that gates per call yields to the agent-spawn lane — capability-gated: without restricted-tool vehicles (generic full-tool spawning does not count), read-only research stays in the orchestrator's own context, never a vehicle mandate a host cannot satisfy. Judgment, code, synthesis stay at the frontier lane (a task that genuinely runs/writes keeps a full-tool subagent); honest limit: no deterministic gate classifies a dispatch — canon at the point of use + placed vehicles + the retro loop. **Writer economy:** a stage's repeated WRITER commands batch — evidence declarations ride consecutive plain invocations of ONE allow-listed tool, other stage writers combine via one launcher per stage; never an unbatched writer scatter (each gated write is its own prompt).
@@ -3,6 +3,7 @@ Apply these when authoring a plan, reviewing, folding a finding, or editing code
3
3
  - **Fold by code, not prose.** Before folding a code-touching finding into a plan or change, read the cited `file:line` and cite it — a prose fold drifts from the code and seeds the next bug.
4
4
  - **Finding scope (plan-execution) — name the invariant BEFORE the edit.** During EXECUTION only — a plan under authoring has no shipped behaviour to call a live defect in, so plan-review carries none of this. Every finding names the invariant its fix would enforce, and where that invariant already lives decides the disposition: already an acceptance criterion of the phase → **fold here**; it would have to be ADDED → ship the **narrow fix** for the found site (red first, then green) and queue ONLY the generalization — a deferral row carries the invariant, the origin `file:line`, the narrow fix, its proof and a residual exposure declared NOT live; no correct narrow fix → **blocking**: the phase does not close, and it is **never queued**. Two bars declared before each round: a finding counts only if it changes a **WRITE/REMOVE decision** or is a false statement in shipped text; a repeat finding in one subarea **routes to SUBTRACTION**, not a fourth patch.
5
5
  - **Right altitude.** Pin intent + invariants + acceptance criteria (named tests); leave fine code-mechanics to Execute, where prose cannot diverge from reality.
6
+ - **Spec-first.** A plan names its GOVERNING spec(s) — zero, one or many, one per touched spec-covered slice (the feature spec under `docs/ai/specs/`; page-only coverage governs as an ADOPTION SHIM, with Out of scope + Revision stated inline in the plan). Each cited spec's Out of scope bounds that slice's work and the plan's non-goals restate it per slice — no global union; a cross-spec conflict is resolved by a spec revision BEFORE approval, never by silent precedence. A NEW feature's draft spec exists AT plan review (a `create` row); a change to a governed contract rides the plan as its proposed revision (a `modify` row); approval confirms plan and contract atomically, and the revision lands with the code. Scenario bindings are per scenario: a new scenario is `unbound` until its test lands in the same plan, and a status never regresses for an extension.
6
7
  - **No code-mechanics in the plan.** A ledger row carries its path and anchor, and Verification carries the exact commands (the plan-shape canon) — checked syntax: the plan's own Verification runs them against an explicit expected outcome or gate; the only other syntax a plan may carry is a literal fixture/schema fragment a named test copies or validates. Un-run, logic-bearing syntax — control-flow, a regex, a glob, a grammar, an algorithm body, a mini-DSL — never lives in plan prose, however plausible or shell-verified it looks: a fold or draft that wants one is the trigger to write the test instead.
7
8
  - **Test-as-spec.** Fold a code-touching finding into a red→green TEST, not a prose paragraph — the gate is the only deterministic checker; a paragraph cannot self-check.
8
9
  - **Characterize-first.** Before editing UNCOVERED code, pin its current behavior in a green test, then edit — any unintended change goes red. Never edit what has no checker; first give it one. Keep edits atomic/reversible; prefer SUBTRACTIVE folds.
@@ -24,7 +24,8 @@ A plan that does not fit is not under-described. Either the TASK is too big —
24
24
  independently verifiable boundaries, never by document size — or it is a SWEEP (below).
25
25
 
26
26
  - **Goal and boundary** (10 lines) — the observable outcome, what behaviour is preserved, explicit
27
- non-goals.
27
+ non-goals, and the GOVERNING spec(s) ([`specs.md`](specs.md)): zero, one or many — one per touched
28
+ spec-covered slice — each cited spec's Out of scope restated as a non-goal for that slice.
28
29
  - **Module ledger** (60 lines) — the single list of paths, and the plan's execution order.
29
30
  - **Verification** (20 lines) — the acceptance check, plus one command that validates the whole ledger.
30
31
  - **Phase: Cleanup** and **Next steps** (human-actionable only) share the 10 reserved lines.
@@ -41,6 +42,11 @@ One row per path, ≤200 bytes per row, six fields:
41
42
  only anchor on a path above it or on existing code. There is no separate step/phase numbering — the
42
43
  only phases are session boundaries in a multi-session plan, and Cleanup.
43
44
 
45
+ **A contract change is a row, present at review.** A NEW feature's draft spec is a `create` row
46
+ (`docs/ai/specs/<slug>.md`) and a proposed REVISION of a governed contract is a `modify` row — both
47
+ written WITH the plan, so approval confirms the plan and every cited draft or revision atomically; the
48
+ landing row moves the spec `draft -> live`, a removal row `live -> retired`.
49
+
44
50
  A `create` row's responsibility names the **exported surface** the module must provide — the names
45
51
  other rows import. That surface does not exist in the checkout yet, so it cannot be derived from it;
46
52
  this is the one interface contract a plan owes its executor.
@@ -33,7 +33,10 @@ Slots: review
33
33
 
34
34
  1. **Research** — the exact files, contracts and constraints touched.
35
35
  2. **Draft** — write to the shape [`planning.md`](planning.md) fixes; its *Module ledger* decides
36
- the layout and every budget before any file exists — a size gate is only the backstop.
36
+ the layout and every budget before any file exists — a size gate is only the backstop. Name the
37
+ governing spec(s) in *Goal and boundary* ([`specs.md`](specs.md)); a new feature's draft spec is
38
+ a `create` row and a revision of a governed contract a `modify` row, both written here so they
39
+ exist AT review.
37
40
  3. **Self-review** — apply *What gets cut*; fold by code (read and cite the `file:line`); update
38
41
  `queue.md` for a series.
39
42
  4. **review {recipe}** — Solo (self-review only) / Reviewed (one backend) / Council (both; you
@@ -67,7 +70,8 @@ Each ledger row is one logical commit.
67
70
  readiness (`--override <slot>=<recipe>` per run).
68
71
  2. **If `execute` resolved to Delegated, dispatch execution FIRST** — the backend returns a diff
69
72
  (codex-exec) *before* you integrate; otherwise implement directly.
70
- 3. **Implement / integrate** — your own edits or the reviewed delegated diff.
73
+ 3. **Implement / integrate** — your own edits or the reviewed delegated diff; a spec row lands its
74
+ approved draft or revision WITH the code ([`specs.md`](specs.md)).
71
75
  4. **Self-review** — the change against its [`planning.md`](planning.md) ledger row and the plan's
72
76
  Verification, under the project's reuse and clean-code rules; fold by code (cite the
73
77
  `file:line`); **characterize-first**: pin uncovered code's behaviour in a green test before
@@ -0,0 +1,202 @@
1
+ # Feature Specs
2
+
3
+ The durable per-feature CONTRACT layer of `docs/ai/`: what a feature accepts and returns, which
4
+ scenarios pin it, and — the core value — what is OUT of scope. A plan is the ephemeral delta; a spec
5
+ is the living, name-addressed WHAT; an ADR is the immutable, numbered WHY. Specs are not numbered.
6
+
7
+ The layer is a continuation of the family's own practice, not a methodology import: the existing
8
+ plan-approval checkpoint confirms a contract, the existing `maxLines` caps bound every file, the
9
+ existing `[[AD-NNN]]` convention links the why, and hidden mode covers the store with no new
10
+ machinery. **No new blocking checkpoints**: statuses are lifecycle markers, never approval gates.
11
+
12
+ ## Where specs live
13
+
14
+ - `docs/ai/specs/`, inside the memory substrate, hierarchical by domain and feature slice, built for
15
+ thousands of specs. The store root `docs/ai/specs/index.md` links up to `technical_specification.md`
16
+ (the top spec stays the system root).
17
+ - Every folder's `index.md` lists ONLY its immediate children; there is no global per-spec index.
18
+ The always-loaded navigator `docs/ai/index.md` carries ONE counted `specs/` row (specs / parts /
19
+ indexes) generated from the store, so adding or removing a valid spec changes the navigator and the
20
+ freshness gate sees it. A file the reader refuses keeps its own visible navigator row — never
21
+ swallowed by the aggregate.
22
+ - "Well-formed" has ONE definition: the deployed reader `scripts/spec-schema.mjs`
23
+ (`readSpecDocument(text, rel)`), read through by the navigator collapse and by the structural
24
+ checker alike. It is pure text — it imports nothing, opens no file, and models no markdown code
25
+ (a fence line refuses; a spec carries no code sample, so no line is ever ambiguous).
26
+
27
+ ## The frozen schema
28
+
29
+ These values are frozen; the reader's `SPEC_SCHEMA` carries them and the canon test pins this file
30
+ against it.
31
+
32
+ - **Frontmatter**: the substrate six keys — `type: spec`, `lastUpdated`, `scope`, `staleAfter`,
33
+ `owner`, `maxLines` — plus `kind`; the key set is CLOSED (an unknown or repeated key refuses).
34
+ `status` and an integer `revision >= 1` ride ONLY on
35
+ `kind: spec` (the contract root owns them); a `part` or an `index` carrying either REFUSES.
36
+ - **kind** — `index` | `spec` | `part` (the entity discriminant: an `index.md` with `kind: spec` is a
37
+ promoted contract root, with `kind: index` a domain navigator).
38
+ - **status** — `draft` | `live` | `retired`. Transitions run forward only: draft -> live (the
39
+ landing row of the approved plan) and live -> retired (the removal row), never backwards.
40
+ `revision` increments by one per live contract change.
41
+ - **Caps and thresholds** — `maxLines: 80` on an index, `maxLines: 150` on a spec or a part. Fan-out:
42
+ an index lists at most 30 immediate children; beyond that it subdivides along feature-slice /
43
+ subdomain boundaries, never into arbitrary buckets. Promotion: a spec over its own `maxLines: 150`
44
+ becomes `<slug>/index.md` (`kind: spec`) plus `part` files beside it — recursive, no depth limit.
45
+ - **slug** — the file stem of a flat spec, the folder name of a promoted root or a domain index:
46
+ `^[a-z0-9]+(-[a-z0-9]+)*$`. Every path segment under the store is a slug.
47
+ - **Scenario line** — `- S<N> <name> :: <repo-relative test path> :: spec:<slug>/S<N>` or
48
+ `- S<N> <name> :: unbound`; N runs contiguously from 1; the marker equals the line's own id.
49
+ - **`## Scenarios`** — at least one scenario line; `*(empty)*` is NOT an escape here (it refuses as
50
+ `scenario-line`), because an `unbound` line already covers a scenario no test pins yet. A contract
51
+ that pins nothing is the one shape the layer exists to refuse.
52
+ - **`## Module`** — bullets of repo-relative paths: ONE `dir/` root OR a literal file list. `..`,
53
+ an absolute path, a backslash, a glob, and a dir + file mix each REFUSE. A retired spec may carry
54
+ `*(empty)*`.
55
+ - **`## Out of scope`** — at least one non-blank bullet, or exactly `*(empty)*` when the emptiness is a decision.
56
+ - An `unbound` scenario on a `live` spec is an advisory warning, never a refusal.
57
+
58
+ ## Shape per kind
59
+
60
+ - **`index`** — frontmatter: the six keys + `kind: index` (+ `maxLines: 80`). Headings: `# <title>`,
61
+ `## Children` — each immediate child exactly once as `- [name](./<child>.md)` or
62
+ `- [name](./<child>/index.md)`, at most 30. No `status`, no `revision`, no `## Scenarios`,
63
+ `## Out of scope`, `## Module` or `## Parts`. The store root additionally carries, before its
64
+ first section, exactly the line `> Up: [technical_specification.md](../technical_specification.md)`.
65
+ - **`spec`** — frontmatter: the six keys + `kind: spec` + `status` + `revision` (+ `maxLines: 150`).
66
+ File: `<slug>.md` (flat) or `<slug>/index.md` (promoted). Headings in order: `# Spec: <title>`,
67
+ `## Contract`, `## Scenarios`, `## Out of scope`, `## Module`, optional `## Parts` (promoted roots
68
+ only: each part exactly once as `- [name](./<part>.md)`), optional `## Links` (`[[AD-NNN]]`, page
69
+ citations).
70
+ - **`part`** — frontmatter: the six keys + `kind: part` (+ `maxLines: 150`). File: `<name>.md` beside
71
+ a promoted root. Heading: `# Part: <title>`. No `status`, `revision`, `## Scenarios`,
72
+ `## Out of scope`, `## Module` or `## Parts` — the root owns them.
73
+
74
+ ## Refusals
75
+
76
+ The reader names exactly one rule per defect. The repo-only fixture corpus carries at least one
77
+ refuse case per rule and an accept case per kind; a refuse fixture yields exactly its rule.
78
+
79
+ | Rule | Refuses |
80
+ |------|---------|
81
+ | `frontmatter` | no YAML frontmatter |
82
+ | `frontmatter-key` | an unknown key, a repeated key, or a line that is not `key: value` (the key set is closed; this ends the read) |
83
+ | `substrate-key` | one of the six substrate keys is missing |
84
+ | `type` | `type` is not `spec` |
85
+ | `kind` | `kind` missing or outside `index`, `spec`, `part` |
86
+ | `maxlines` | `maxLines` differs from the kind's frozen cap |
87
+ | `status` | a spec's `status` missing or outside `draft`, `live`, `retired` |
88
+ | `revision` | a spec's `revision` missing or not an integer >= 1 |
89
+ | `root-owns` | a part or an index carrying `status` or `revision` |
90
+ | `slug` | a path segment outside the slug pattern |
91
+ | `kind-path` | an index not named `index.md`, a part named `index.md` or placed at the store root, a contract root at the store root |
92
+ | `root-uplink` | the store root without the exact up-link line before its first section |
93
+ | `title` | the first heading is not the kind's title form, or a section opens before it |
94
+ | `section-missing` | a required section absent |
95
+ | `section-order` | the required and optional sections out of order |
96
+ | `section-forbidden` | a section the kind never carries |
97
+ | `fence` | a code fence line — a spec document carries no code sample; the reader models no markdown code |
98
+ | `children-link` | a `## Children` line that is not a child link to a slug |
99
+ | `children-duplicate` | a child listed twice |
100
+ | `fan-out` | more than 30 children |
101
+ | `scenario-line` | a `## Scenarios` line outside the scenario grammar |
102
+ | `scenario-number` | scenario ids not contiguous from 1 |
103
+ | `scenario-marker` | a marker whose slug or id differs from its own line |
104
+ | `scenario-path` | a binding's test path that is not a repo-relative file |
105
+ | `scenarios-empty` | a `## Scenarios` section carrying no scenario line |
106
+ | `out-of-scope` | no non-blank bullet and not exactly `*(empty)*` |
107
+ | `module-line` | a `## Module` line that is not a `- <path>` bullet |
108
+ | `module-empty` | no module path and not exactly `*(empty)*` (a retired spec may carry the marker instead of a path) |
109
+ | `module-traversal` | a module path with `..` |
110
+ | `module-absolute` | an absolute module path |
111
+ | `module-backslash` | a module path with a backslash |
112
+ | `module-glob` | a module path with a wildcard |
113
+ | `module-mix` | neither ONE `dir/` root nor a literal file list |
114
+ | `parts` | `## Parts` on a flat spec, a malformed part link, or a part listed twice |
115
+
116
+ ## Lifecycle, binding and approval
117
+
118
+ - **One entity.** A spec is the durable contract; the ephemeral plan is the delta vehicle; the spec
119
+ revision lands with the code. There is no change-spec entity.
120
+ - **Governing specs are plural.** A plan cites ZERO (nothing spec-covered touched — legal during
121
+ adoption), ONE or MANY governing specs — one per touched spec-covered slice; a shared-module change
122
+ cites the specs of every slice whose contract it can alter.
123
+ - **Out of scope composes PER GOVERNING SLICE — there is no global union.** Each cited spec's
124
+ exclusions bound only the work inside that slice, and the plan's non-goals restate them per slice.
125
+ A cross-spec conflict is resolved by a spec REVISION BEFORE plan approval — never by silent
126
+ precedence or review-time improvisation.
127
+ - **Every contract change is reviewable at plan review.** A NEW feature's draft spec is authored WITH
128
+ the plan and exists AT review (a `create` ledger row); a plan that ALTERS a governed contract
129
+ carries the proposed revision at review the same way (a `modify` row). Approval confirms the plan
130
+ and every cited draft or revision atomically; the rows then land them. A contract is never
131
+ "confirmed" before it is visible.
132
+ - **Test-as-spec binding is per scenario.** Every scenario names the test that pins it; a NEW
133
+ scenario on a live spec is individually `unbound` until its test lands within the same plan — the
134
+ spec's status never regresses for an extension. The binding is literal and language-agnostic: the
135
+ marker `spec:<slug>/S<N>` must occur exactly once in the named file. The honest claim is an
136
+ advisory structural pin; runnability and greenness stay the test suite's own gate. A marker is an
137
+ ORDINARY source line — it counts toward the source-size practice like any other, and a pinning file
138
+ already at its cap raises the recorded ratchet to host one. There is NO sidecar binding form:
139
+ retroactive coverage of a PUBLISHED package rides a release train, because the marker moves that
140
+ package's subtree.
141
+ - **Containment** is lexical AND realpath on every path-bearing field (bindings, child links, module
142
+ roots). The reader judges the lexical half; realpath and symlink escapes are the structural
143
+ checker's duty (`spec-check`, a later slice), together with marker existence and uniqueness, link
144
+ resolution and the global invariants (slug uniqueness, module-root overlap, tree acyclicity) that
145
+ only a full-tree sweep can prove. The store ROOT is never an op target and needs no `root` verb: it
146
+ is the navigator, judged as the listing parent of its declared child.
147
+
148
+ ## Precedence — feature spec and page spec
149
+
150
+ | Feature spec | Page spec | Governs |
151
+ |--------------|-----------|---------|
152
+ | present | present | the feature spec; `pages/<page>.md` is a subordinate view-layer document cited from it |
153
+ | absent | present | the page spec, as an ADOPTION SHIM — the citing plan states Out of scope + Revision inline |
154
+ | present | absent | the feature spec |
155
+
156
+ `PAGE_TEMPLATE.md` is not upgraded for this; the durable fix is authoring the feature spec through
157
+ the retroactive path below.
158
+
159
+ ## Spec and code
160
+
161
+ - **A discoverable bijection.** The spec path mirrors the feature-slice path; a slug is IDENTICAL to
162
+ the slice or module name for NEW and deliberately refactored slices. A LEGACY module keeps its
163
+ existing name — the spec's declared module root IS the mapping, no rename ride-alongs; folder
164
+ promotion keeps the slug. A module root is a DIRECTORY or a DECLARED FILE SET, so a file-based
165
+ boundary binds without an architectural refactor.
166
+ - **Spec drives modular, feature-sliced architecture.** Code is organized by feature slice, not by
167
+ technical layer; a spec's drill-down mirrors the slice's module boundaries; the pure rule / IO
168
+ shell split is the default module anatomy, composing with the source-size practice (a module you
169
+ can hold whole is the unit of review).
170
+ - **ONE dependency rule: the dependency graph is explicit and acyclic.** A slice depends on `shared`
171
+ and on another slice's PUBLIC contract only when that edge is DECLARED in its spec; an undeclared
172
+ cross-slice reach-in is refused at review.
173
+ - **Enforcement altitude, stated honestly.** This series the dependency and reach-in rules are
174
+ REVIEW-level canon defaults for NEW and deliberately refactored slices — no machine enforcement
175
+ (declared-edge fields, cycle checks, reach-in detection) ships; a legacy layered project is not
176
+ rendered nonconforming and adopts per feature through the retroactive path.
177
+
178
+ ## Spec and ADR
179
+
180
+ Complementary, never competing: an ADR is the immutable, numbered, chronological WHY; a spec is the
181
+ living, name-addressed WHAT. A spec cites its shaping ADRs in `## Links` via `[[AD-NNN]]`; an ADR
182
+ that changes a contract triggers a spec revision.
183
+
184
+ ## Retroactive coverage
185
+
186
+ The onboarding path for an existing feature: read the code, author a `draft` spec from it (the
187
+ contract it actually keeps, the scenarios its tests already pin, the Out of scope its boundaries
188
+ imply), then human review promotes it to `live` through a plan. The promoting event of a retroactive
189
+ draft is PLAN APPROVAL — `live` lands in the SAME slice that authors the draft, once every scenario
190
+ is bound; a slice whose whole deliverable is the specs needs no later plan to leave them provisional.
191
+ Used to dogfood the family's own subsystems; a legacy module binds through a declared file set, never
192
+ a refactor ride-along.
193
+
194
+ ## Scale
195
+
196
+ The navigator walk is O(all docs) per pre-commit; the spec store joins it. The budget is a numeric
197
+ release gate: 1500 ms for both hook runs over a valid 1000-spec, 30-per-folder tree (the median of
198
+ three trials), measured by the repo's scale probe before every release of the checker — exceeding it
199
+ is fixed inside the walk (cheaper per-file work, early exits), never queued. The structural checker
200
+ is O(changed) by construction: it takes explicit spec paths, and `--all` is the explicit full sweep;
201
+ a git-derived "changed" lane is refused, because hidden mode ignores `docs/ai/**` and git would see
202
+ no spec at all.