@gobing-ai/spur 0.4.1 → 0.4.3

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.
Files changed (159) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +4 -2
  3. package/config/decisions/maker-liveness.yaml +39 -0
  4. package/config/plugin-scripts.json +0 -4
  5. package/config/rules/README.md +1 -0
  6. package/config/rules/docs/sourced-api-claims.yaml +56 -12
  7. package/config/rules/strict/runtime-boundaries.yaml +7 -0
  8. package/config/script-placement-baseline.json +1 -19
  9. package/config/templates/AGENTS.md +1 -1
  10. package/config/workflows/feature-lifecycle.yaml +46 -1
  11. package/config/workflows/feature-verification.yaml +11 -0
  12. package/config/workflows/wrapup-pipeline.yaml +1 -1
  13. package/package.json +10 -10
  14. package/plugins/sp/README.md +23 -18
  15. package/plugins/sp/agents/super-planner.md +5 -6
  16. package/plugins/sp/agents/super-reviewer.md +43 -0
  17. package/plugins/sp/bunfig.toml +8 -5
  18. package/plugins/sp/commands/dev-review-session.md +2 -1
  19. package/plugins/sp/commands/dev-runall.md +18 -9
  20. package/plugins/sp/hooks/careful-guard.ts +1 -1
  21. package/plugins/sp/hooks/context-post-tool.ts +7 -404
  22. package/plugins/sp/hooks/context-session-start.ts +6 -192
  23. package/plugins/sp/hooks/context-session-stop.ts +6 -91
  24. package/plugins/sp/hooks/pi/guard-extension.ts +26 -12
  25. package/plugins/sp/hooks/task-write-guard.ts +1 -1
  26. package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
  27. package/plugins/sp/lib/context-owner.ts +56 -0
  28. package/plugins/sp/lib/context-post-tool.ts +432 -0
  29. package/plugins/sp/lib/context-session-start.ts +214 -0
  30. package/plugins/sp/lib/context-session-stop.ts +196 -0
  31. package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
  32. package/plugins/sp/lib/dogfood-report.ts +278 -0
  33. package/plugins/sp/lib/idea-handoff.generated.mjs +6 -4
  34. package/plugins/sp/lib/inline-run-app.ts +40 -0
  35. package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
  36. package/plugins/sp/lib/inline-run.generated.mjs +1361 -87
  37. package/plugins/sp/lib/plan-projection.generated.d.mts +169 -0
  38. package/plugins/sp/lib/plan-projection.generated.mjs +704 -1
  39. package/plugins/sp/lib/quality-gate.generated.d.mts +0 -12
  40. package/plugins/sp/lib/quality-gate.generated.mjs +62 -231
  41. package/plugins/sp/lib/residual-scan.generated.d.mts +7 -1
  42. package/plugins/sp/lib/residual-scan.generated.mjs +71 -6
  43. package/plugins/sp/lib/run-summary-core.ts +34 -370
  44. package/plugins/sp/lib/transcript.ts +37 -200
  45. package/plugins/sp/plugin.json +1 -1
  46. package/plugins/sp/references/roles.md +1 -1
  47. package/plugins/sp/scripts/batch-plan.mjs +740 -1
  48. package/plugins/sp/scripts/batch-plan.ts +48 -3
  49. package/plugins/sp/scripts/batch-preflight.mjs +23 -1
  50. package/plugins/sp/scripts/batch-preflight.ts +35 -295
  51. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +108 -87
  52. package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
  53. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -1
  54. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
  55. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +3 -0
  56. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
  57. package/plugins/sp/scripts/feature-verification-steps.mjs +7 -123
  58. package/plugins/sp/scripts/feature-verification-steps.ts +29 -181
  59. package/plugins/sp/scripts/inline-run-dispatch.mjs +27 -26
  60. package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
  61. package/plugins/sp/scripts/inline-run-setup.mjs +158 -120
  62. package/plugins/sp/scripts/inline-run-setup.ts +1 -21
  63. package/plugins/sp/scripts/pr-reviewing.mjs +19 -5
  64. package/plugins/sp/scripts/pr-reviewing.ts +34 -9
  65. package/plugins/sp/scripts/quality-gate.mjs +38 -208
  66. package/plugins/sp/scripts/residual-scan.mjs +73 -6
  67. package/plugins/sp/scripts/residual-scan.ts +8 -5
  68. package/plugins/sp/scripts/run-summary.mjs +160 -95
  69. package/plugins/sp/scripts/run-summary.ts +7 -5
  70. package/plugins/sp/scripts/session-timeline.mjs +57 -29
  71. package/plugins/sp/scripts/session-timeline.ts +4 -18
  72. package/plugins/sp/scripts/task-diffstat.mjs +43 -30
  73. package/plugins/sp/scripts/task-diffstat.ts +40 -71
  74. package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
  75. package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
  76. package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
  77. package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
  78. package/plugins/sp/skills/indexed-context/SKILL.md +14 -7
  79. package/plugins/sp/skills/next-feature/SKILL.md +1 -1
  80. package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
  81. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +2 -2
  82. package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +2 -2
  83. package/plugins/sp/skills/pr-reviewing/SKILL.md +7 -3
  84. package/plugins/sp/skills/session-review/SKILL.md +5 -3
  85. package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
  86. package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
  87. package/plugins/sp/skills/spur-cli/references/decision.md +40 -8
  88. package/plugins/sp/skills/spur-cli/references/features.md +46 -5
  89. package/plugins/sp/skills/spur-cli/references/projects.md +7 -5
  90. package/plugins/sp/skills/spur-dev/SKILL.md +2 -0
  91. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +27 -0
  92. package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -3
  93. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +6 -1
  94. package/plugins/sp/skills/spur-dev/references/execution-batch.md +56 -7
  95. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +4 -1
  96. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
  97. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
  98. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +13 -11
  99. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +37 -25
  100. package/spur.js +89792 -76992
  101. package/web/_astro/{BoardApp.D66k_s_T.js → BoardApp.5IIl2O-z.js} +1 -1
  102. package/web/_astro/BoardApp.Drnx-KeM.js +324 -0
  103. package/web/_astro/TaskDetail.Ci4LNn9S.js +1 -0
  104. package/web/_astro/{arc._BUadrdH.js → arc.j2EVkYVd.js} +1 -1
  105. package/web/_astro/{architectureDiagram-3BPJPVTR.Dh7xS1Pp.js → architectureDiagram-3BPJPVTR.DZWSFqEG.js} +1 -1
  106. package/web/_astro/{blockDiagram-GPEHLZMM.DFDVpfXm.js → blockDiagram-GPEHLZMM.DE6GpXCN.js} +1 -1
  107. package/web/_astro/{c4Diagram-AAUBKEIU.V7_3_glh.js → c4Diagram-AAUBKEIU.DoVR1G6B.js} +1 -1
  108. package/web/_astro/channel.DmdDxGsq.js +1 -0
  109. package/web/_astro/{chunk-2J33WTMH.De1yx7Jz.js → chunk-2J33WTMH.Do6-Gbyf.js} +1 -1
  110. package/web/_astro/{chunk-4BX2VUAB.BDhHFuxx.js → chunk-4BX2VUAB.BYV1HGHx.js} +1 -1
  111. package/web/_astro/{chunk-55IACEB6.DDLkWnmC.js → chunk-55IACEB6.CvI1zkrc.js} +1 -1
  112. package/web/_astro/{chunk-727SXJPM.CwfiLnA7.js → chunk-727SXJPM.DNGSCRlX.js} +1 -1
  113. package/web/_astro/{chunk-AQP2D5EJ.CtGDmUnM.js → chunk-AQP2D5EJ.DXhhgT5z.js} +1 -1
  114. package/web/_astro/{chunk-FMBD7UC4.BOLjpw73.js → chunk-FMBD7UC4.dzkRKP2u.js} +1 -1
  115. package/web/_astro/{chunk-ND2GUHAM.BDhQls6O.js → chunk-ND2GUHAM.DujGLeEf.js} +1 -1
  116. package/web/_astro/{chunk-QZHKN3VN.76qBynDF.js → chunk-QZHKN3VN.BWtIT3ZU.js} +1 -1
  117. package/web/_astro/{classDiagram-4FO5ZUOK.N0mpPHT0.js → classDiagram-4FO5ZUOK.DyApg0Nn.js} +1 -1
  118. package/web/_astro/{classDiagram-v2-Q7XG4LA2.N0mpPHT0.js → classDiagram-v2-Q7XG4LA2.DyApg0Nn.js} +1 -1
  119. package/web/_astro/{cose-bilkent-S5V4N54A.CKknFfjB.js → cose-bilkent-S5V4N54A.DJQB-u_l.js} +1 -1
  120. package/web/_astro/{cynefin-OW5HDTMX.Cw3ZWBAh.js → cynefin-OW5HDTMX.bi3D_siV.js} +1 -1
  121. package/web/_astro/{dagre-BM42HDAG.D3yIKCRD.js → dagre-BM42HDAG.C-f7TrRg.js} +1 -1
  122. package/web/_astro/{diagram-2AECGRRQ.D51dO83Z.js → diagram-2AECGRRQ.DrPfjHQB.js} +1 -1
  123. package/web/_astro/{diagram-5GNKFQAL.Bb-FViDj.js → diagram-5GNKFQAL.C9S4WUHO.js} +1 -1
  124. package/web/_astro/{diagram-KO2AKTUF.D3mN8S6x.js → diagram-KO2AKTUF.Dzzye8QH.js} +1 -1
  125. package/web/_astro/{diagram-LMA3HP47.dMMyPcbJ.js → diagram-LMA3HP47.DX-LRiof.js} +1 -1
  126. package/web/_astro/{diagram-OG6HWLK6.AzbvzY4C.js → diagram-OG6HWLK6.Z-kAKwIz.js} +1 -1
  127. package/web/_astro/{erDiagram-TEJ5UH35.BR5I4OZb.js → erDiagram-TEJ5UH35.D6dyClSv.js} +1 -1
  128. package/web/_astro/{flowDiagram-I6XJVG4X.DFqvhGkn.js → flowDiagram-I6XJVG4X.Cx3q2cxD.js} +1 -1
  129. package/web/_astro/{ganttDiagram-6RSMTGT7.ByJbhH6J.js → ganttDiagram-6RSMTGT7.DU3gH6CT.js} +1 -1
  130. package/web/_astro/{gitGraphDiagram-PVQCEYII.CInHjQR3.js → gitGraphDiagram-PVQCEYII.ISmCnJ87.js} +1 -1
  131. package/web/_astro/index.Do2lLdgo.css +1 -0
  132. package/web/_astro/{infoDiagram-5YYISTIA.Cm0dGVjJ.js → infoDiagram-5YYISTIA.DR6_iNOK.js} +1 -1
  133. package/web/_astro/{ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js → ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js} +1 -1
  134. package/web/_astro/{journeyDiagram-JHISSGLW.4IvLa2MW.js → journeyDiagram-JHISSGLW.DkP9-WK8.js} +1 -1
  135. package/web/_astro/{kanban-definition-UN3LZRKU.DS87Cl-k.js → kanban-definition-UN3LZRKU.wtgY98M2.js} +1 -1
  136. package/web/_astro/{linear.MzmRrNER.js → linear.CI6djNi7.js} +1 -1
  137. package/web/_astro/{mermaid.core.fK-WNswS.js → mermaid.core.CuQPxS8b.js} +4 -4
  138. package/web/_astro/{mindmap-definition-RKZ34NQL.BqipOHhY.js → mindmap-definition-RKZ34NQL.5HNIEG7R.js} +1 -1
  139. package/web/_astro/{pieDiagram-4H26LBE5.0nBiPqFG.js → pieDiagram-4H26LBE5.CchhDFjE.js} +1 -1
  140. package/web/_astro/{quadrantDiagram-W4KKPZXB.Szj7Zpx5.js → quadrantDiagram-W4KKPZXB.PYXXJPSJ.js} +1 -1
  141. package/web/_astro/{requirementDiagram-4Y6WPE33.D4UR6TWo.js → requirementDiagram-4Y6WPE33.cUQuNAKU.js} +1 -1
  142. package/web/_astro/{sankeyDiagram-5OEKKPKP.DAE7BdOs.js → sankeyDiagram-5OEKKPKP.BO7MnRIA.js} +1 -1
  143. package/web/_astro/{sequenceDiagram-3UESZ5HK.BP9ZVq25.js → sequenceDiagram-3UESZ5HK.D9bElyD6.js} +1 -1
  144. package/web/_astro/{stateDiagram-AJRCARHV.BQW7mIxk.js → stateDiagram-AJRCARHV.DhGdRz7I.js} +1 -1
  145. package/web/_astro/{stateDiagram-v2-BHNVJYJU.PbK4nDih.js → stateDiagram-v2-BHNVJYJU.5GvLeAoL.js} +1 -1
  146. package/web/_astro/{timeline-definition-PNZ67QCA.Ci_GvUpo.js → timeline-definition-PNZ67QCA.LtAT9haM.js} +1 -1
  147. package/web/_astro/{vennDiagram-CIIHVFJN.DNw1Y5qx.js → vennDiagram-CIIHVFJN.DRth1yLA.js} +1 -1
  148. package/web/_astro/{wardleyDiagram-YWT4CUSO.CwlkaaNe.js → wardleyDiagram-YWT4CUSO.h9MlZfcr.js} +1 -1
  149. package/web/_astro/{xychartDiagram-2RQKCTM6.MI6ZuiH_.js → xychartDiagram-2RQKCTM6.DHqy9DYA.js} +1 -1
  150. package/web/index.html +2 -2
  151. package/config/proportional-route-table.ts +0 -155
  152. package/plugins/sp/commands/dev-parallel.md +0 -37
  153. package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
  154. package/web/_astro/BoardApp.D6sjAJr_.js +0 -324
  155. package/web/_astro/TaskDetail.BynLnMNc.js +0 -1
  156. package/web/_astro/channel.DZVfto-r.js +0 -1
  157. package/web/_astro/index.COh_kBxN.css +0 -1
  158. /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
  159. /package/plugins/sp/{hooks → lib}/task-file-policy.ts +0 -0
@@ -81,9 +81,45 @@ mode needs its own required args).
81
81
  spur feature update H2 active
82
82
  ```
83
83
 
84
- Valid statuses: `backlog → active → verifying → blocked → done → cancelled`. The lifecycle engine
85
- enforces legal transitions; `verifying` is the gate where AC traceability must hold (L4).
86
- **One active goal** at a time is enforced by `check` (a corpus invariant).
84
+ Valid statuses: `backlog → active → verifying → blocked → done → cancelled`, plus the frozen
85
+ `deprecated`. The lifecycle engine enforces legal transitions; `verifying` is the gate where AC
86
+ traceability must hold (L4). **One active goal** at a time is enforced by `check` (a corpus invariant).
87
+
88
+ **`deprecated` — frozen, not terminal (ADR-136).** The state means *retained and frozen*: the record
89
+ stays readable and countable, but nothing may attach new work to it or advance it. It is entered from
90
+ `backlog`, `active`, `verifying`, `blocked` and `done`, and left only to `active` (un-deprecate,
91
+ mandatory History entry) or `cancelled` (removed) — every other edge out of `deprecated` is undeclared
92
+ and refused by the graph. The freeze, as shipped:
93
+
94
+ - **Task links are refused** — `task create`, `update --feature` and `batch-create` exit non-zero and
95
+ write nothing (no sibling hint; `--no-reopen` is irrelevant for a frozen parent):
96
+
97
+ ```
98
+ parent-feature-status: feature <id> is deprecated (frozen); new tasks cannot link to it. Run removal work under its own feature, or un-deprecate: spur feature update <id> active
99
+ ```
100
+
101
+ - **`L4.feature-deprecated`** — `task check` warns (does not error) for a *live* task under a
102
+ deprecated feature; a terminal task is the correct end state and gets no finding; `--strict`
103
+ elevates the warning as usual:
104
+
105
+ ```
106
+ Feature "<id>" is deprecated (frozen) — re-parent this live task (`spur task update <wbs> --feature <otherId>`) or cancel it.
107
+ ```
108
+
109
+ - **`update --section` still lands** (exit `0`), appending a warning to `warnings[]`:
110
+
111
+ ```
112
+ Feature <id> is deprecated (frozen); change it only as part of removal
113
+ ```
114
+
115
+ - **`sync` / `refresh` / `check --fix` never move it** — derivation is sticky and returns the no-op
116
+ proposal `{ from: 'deprecated', to: 'deprecated', reason: 'Feature is deprecated (frozen)' }`.
117
+
118
+ - **`advance` refuses** with exit `1`, naming the un-deprecate command:
119
+
120
+ ```
121
+ <id> is deprecated; un-deprecate first: spur feature update <id> active
122
+ ```
87
123
 
88
124
  **Scalar field set:**
89
125
 
@@ -113,7 +149,8 @@ spur feature advance H2 --to verifying
113
149
  ```
114
150
 
115
151
  Walks the legal forward path (`backlog → active → verifying → done`) hop-by-hop until `--to`
116
- (default `done`). No-op when already at the target.
152
+ (default `done`). No-op when already at the target; a `deprecated` feature is refused up front (see
153
+ the freeze block above).
117
154
 
118
155
  ## Acceptance criteria conventions
119
156
 
@@ -169,7 +206,8 @@ the AC coverage map. Habits that keep it green:
169
206
  Use feature hierarchy for user-facing capabilities and roadmap themes; keep technical-module mapping
170
207
  as prose or notes unless the product surface is developer infrastructure. Priority is a scalar field
171
208
  (`spur feature update <id> --field priority --value P1`), while status is lifecycle state
172
- (`backlog → active → verifying → blocked → done → cancelled`). Do not conflate the two.
209
+ (`backlog → active → verifying → blocked → done → cancelled`, plus the frozen `deprecated`). Do not
210
+ conflate the two.
173
211
 
174
212
  **Structure first:** [features/hierarchy-mece.md](features/hierarchy-mece.md) (MECE roots, when to
175
213
  extend vs create). **Then priority:** [features/roadmap-priority.md](features/roadmap-priority.md).
@@ -216,6 +254,9 @@ spur feature sync H2 --force # re-derive live: bypass replay +
216
254
  spur feature sync H2 --folder docs/custom-tasks --json # non-default tasks folder
217
255
  ```
218
256
 
257
+ - **`deprecated` is sticky:** derivation returns no proposal for it
258
+ (`reason: 'Feature is deprecated (frozen)'`), so neither `sync` nor `check --fix` moves a frozen
259
+ feature — see the freeze block in §Editing a feature.
219
260
  - **`[id]`** syncs one feature; **`--all`** syncs every feature with linked tasks. One of the two is
220
261
  required - exit `2` if neither is given.
221
262
  - **`--dry-run`** reports proposed transitions without applying. **`--force`** applies a *reopen*
@@ -22,7 +22,7 @@ shapes live in `apps/cli/src/commands/projects.ts`.
22
22
  | `clean` | Purge missing project folders and terminate lingering processes (alias: `refresh`) | `--no-terminate-processes` `--json` |
23
23
  | `list` | List entries with live running status | `--json` `--fleet` |
24
24
  | `start <target>` | Start or reuse a detached project server | `--port <n>` `--json` |
25
- | `stop <target>` | Best-effort stop the listener and clear its recorded port | `--json` |
25
+ | `stop <target>` | Ownership-verified stop of the listener and clear its recorded port | `--json` |
26
26
 
27
27
  Every verb also advertises `--json-envelope`; use the facade's machine-output contract. Success is
28
28
  exit `0`; validation, registry, spawn, health, or lookup failure is exit `1`.
@@ -36,8 +36,8 @@ exit `0`; validation, registry, spawn, health, or lookup failure is exit `1`.
36
36
  - `add` requires an existing path, resolves a relative path from the current working directory, and
37
37
  defaults the display name to its basename. It upserts; it does not start a server. The current
38
38
  source does not enforce a `.spur/` marker or directory type.
39
- - `list` automatically heals tilde paths, verifies project directories exist on disk (purging missing entries and terminating lingering processes), and heals stale entries to `port: 0` before reporting `running`.
40
- - `clean` (alias `refresh`) explicitly purges non-existent project directories from `projects.json`, probes live ports for removed entries, and cleanly terminates orphaned listening processes (SIGTERM with bounded wait and SIGKILL escalation). Supports `--no-terminate-processes` to skip process kills.
39
+ - `list` automatically heals tilde paths, verifies project directories exist on disk (purging missing entries and terminating only ownership-verified lingering processes), and heals stale entries to `port: 0` before reporting `running`.
40
+ - `clean` (alias `refresh`) explicitly purges non-existent project directories from `projects.json`, probes live ports for removed entries, and terminates orphaned listeners only after ownership verification (SIGTERM with bounded wait and SIGKILL escalation, each signal gated on `service:spur` identity plus exact project path and a stable PID; foreign or unverified listeners are never signaled). Supports `--no-terminate-processes` to skip process kills.
41
41
  - `list --fleet` (0835/0858) additionally resolves each project's `agent.fleet` section from that
42
42
  project's `.spur/config.yaml` under the existing verb (no new noun). Per project it prints one line
43
43
  per member: instance id (the spec id / mailbox identity), `role`, resolved `executor`,
@@ -76,8 +76,10 @@ port is returned idempotently; otherwise the service allocates a port (3000–39
76
76
  set), spawns `spur serve --host 127.0.0.1 --no-open` detached in the project root, waits for the port,
77
77
  then persists it.
78
78
 
79
- `stop` finds processes bound to the recorded port, sends `SIGTERM` best-effort while excluding the
80
- CLI and its parent, and clears the registry port. It is not a persistent supervisor contract.
79
+ `stop` signals only a listener that proves `service:spur` identity plus the exact registered project
80
+ path with a stable rediscovered PID; it refuses foreign or unverified listeners with exit 1 and
81
+ preserves the port, treats an absent listener as idempotent success, and excludes the CLI and its
82
+ parent. It is not a persistent supervisor contract.
81
83
  `remove` only removes the registry entry; stop a running project first when process cleanup matters.
82
84
 
83
85
  Raw JSON success payloads are verb-specific (`project`, `projects`, `removed`, `stopped`, or start
@@ -110,6 +110,8 @@ Read this `SKILL.md` plus your mode's files below before the first dispatch; eve
110
110
  - --worktree: `references/execution-batch.md`, `references/execution-worktree-setup.md`
111
111
  - --mode parallel: `references/execution-batch.md`, `references/execution-parallel-isolation.md`
112
112
 
113
+ `--mode auto` reads as sequential-inline until § 2.4a resolves it.
114
+
113
115
  Byte budgets for these sums live in `plugins/sp/tests/bootstrap-budget.test.ts`
114
116
  (`BOOTSTRAP_BUDGETS_BYTES`, task 1128); the test fails the build when a mode's bootstrap grows.
115
117
 
@@ -88,6 +88,33 @@ DD-06 permissive start: treat only the core tier as must-ship initially; promote
88
88
  prove important in a later iteration. (When tiered gating lands in `spur feature check`, these tags
89
89
  become the gate input — the CLI stays the validator; this guide stays the convention.)
90
90
 
91
+ ### Home-node tier (structural, exempt from the orphan sweep)
92
+
93
+ A **home node** carries one structural or placement scenario that states *where the work lives*
94
+ (e.g. "work on the kk workflow-run ledger has a home node"). No task can cover that statement — it
95
+ describes planning placement, not behaviour — so tag it `@home-node` to remove it from the
96
+ feature-side DD-09 orphan sweep (`L4.uncovered-feature-scenario`):
97
+
98
+ ```gherkin
99
+ @home-node
100
+ Scenario: R1 — Work on the kk workflow run ledger has a home node
101
+ Given a change that targets the kk workflow run ledger workflow
102
+ When the change is planned as a feature
103
+ Then it is feature G7 or a child feature of G7
104
+ ```
105
+
106
+ Rules:
107
+
108
+ - **Scenario-level tag only.** A `Feature:`-level `@home-node` exempts nothing; the tag is read per
109
+ scenario.
110
+ - **Exempts the orphan sweep only.** Scenario satisfaction (0340), verdict-row matching, and the
111
+ known-issues ratchet are unchanged — a tagged scenario a task actually covers is classified
112
+ exactly as before.
113
+ - **Never a hiding place.** Use it only for a scenario no task can own. A deliberately uncoverable
114
+ *capability* scenario belongs to `@core`/`@edge` and must stay in the sweep: tagging it
115
+ `@home-node` hides a real gap. Wording is never the signal.
116
+ - A feature with **no linked task** is still flagged as `L4.orphan-scenarios`, tagged or not.
117
+
91
118
  ## Scenario-title stability
92
119
 
93
120
  The scenario title is the **identity key** for traceability. When a task references a
@@ -23,7 +23,7 @@ The `Skill()` commands back onto six skills: `sp:spur-dev` (planning + execution
23
23
  `sp:code-verification` (SECUA review + traceability), `sp:parallel-execution` (fan-out / parallel batch),
24
24
  and `sp:brainstorm` (structured ideation). The `runall` operation (#13) is `sp:spur-dev`'s batch entry — it delegates the driver loop to the
25
25
  `sp:super-planner` agent (the batch orchestrator) per [execution-batch.md](execution-batch.md).
26
- `dev-parallel` (#13a) is the parallel counterpart. `dev-brainstorm` carries the two artifact exits — `--task` (one task) and `--feature` (validated
26
+ Parallel task batches are `dev-runall --mode parallel|auto` (`dev-parallel` retired 2026-10-10). `dev-brainstorm` carries the two artifact exits — `--task` (one task) and `--feature` (validated
27
27
  feature with BDD AC; the front-half entry that hands off to `dev-plan`). The 4 `inline` commands
28
28
  cover git tooling and operational utilities that have no natural skill home — creating a skill for
29
29
  each would be scope creep for one-liner procedures.
@@ -86,8 +86,7 @@ each would be scope creep for one-liner procedures.
86
86
  | 11b | job-resume | `dev-job-resume` | `Skill()` | `sp:spur-dev` (`job-resume`) | `--file <path>` |
87
87
  | 12 | brainstorm | `dev-brainstorm` | `Skill()` | `sp:brainstorm` (`dev-brainstorm`) | `<topic> [--depth <basic\|detailed\|comprehensive>] [--options <n>] [--agent <inline\|auto\|name>] [--skip-discovery] [--task [<feature-id>]] [--feature [<parent-id>]] [--next]` |
88
88
  | 12a | find-way | `dev-find-way` | `Skill()` | `sp:wayfinder` (worktree-isolated) | `<idea> \| <feature-id> [<wbs>] [--agent <inline\|auto\|name>] [--auto] [--worktree [<name>]] [--wrap]` |
89
- | 13 | runall | `dev-runall` | `Skill()` → agent | `sp:spur-dev` (`runall`) → `sp:super-planner` | `--tasks <selector> [--feature <id>] [--mode <sequential\|parallel>] [--keep-going] [--auto] [--agent <inline\|auto\|name>] [--json] [--wrap] [--next] [--continue] [--worktree [<name>]] [--no-summary] [--defer-gate]` |
90
- | 13a | parallel | `dev-parallel` | `Skill()` | `sp:parallel-execution` | `--tasks <selector> [--feature <id>] [--mode <fan-out\|review-panel\|investigation>] [--agent <inline\|auto\|name>] [--json] [--defer-gate]` |
89
+ | 13 | runall | `dev-runall` | `Skill()` → agent | `sp:spur-dev` (`runall`) → `sp:super-planner` | `--tasks <selector> [--feature <id>] [--mode <sequential\|parallel\|auto>] [--concurrency <n>] [--keep-going] [--auto] [--agent <inline\|auto\|name>] [--json] [--wrap] [--next] [--continue] [--worktree [<name>]] [--no-summary] [--defer-gate]` |
91
90
  | 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
92
91
  | 15 | wrapall | `dev-wrapall` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `[--since <iso>] [--feature <id>] [--status <s>] [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
93
92
  | 16 | idea | `dev-idea` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<idea>" \| --from-file <path> [--skip-design] [--agent <inline\|auto\|name>] [--auto]` |
@@ -126,7 +126,7 @@ success exits 0 printing
126
126
  `{"ok":true,"persisted":<n>,"skipped":[{"id":<run-id>,"reason":"id-exists"|"external-key-conflict"|"record-conflict:<file>"|"record-missing:<file>"|"cited-directory:<name>"|"cited-symlink:<name>"|"cited-non-file:<name>"}],"evidenceSkipped":[{"name":<file>,"reason":"foreign-divergent","newer":"invoking"|"worktree"}]}`
127
127
  (`evidenceSkipped` is omitted when empty; the same object is written to `<invoking-tree>/.spur/run/persist-out.json` for `persist-out-check` to read)
128
128
  — an `external-key-conflict` skip leaves the target run unchanged **and fails the pass (1049)**: the delegate exits 1 printing `{"ok":false,"error":…}` naming the skipped source run ids, because that (workflow, external key) identity already belongs to a different receiving run — the batch was not persisted, so reconcile the source worktree by hand (it stays the provenance owner of record) before any teardown: auditable reconciliation names the original archived DB snapshot and the skipped source run identities, verifies the archive by file hash and by merged-commit ancestry of the source branch, and treats any residual deletion as explicit operator-authorized cleanup under the strict canonical evidence rules above. Task 1149 R1 grades a terminal duplicate lifecycle row (`task-lifecycle`/`feature-lifecycle`) as `external-key-conflict-bookkeeping` (non-fatal), so guarded transitions driven inside the worktree do not block teardown; the receiving lifecycle row is reconciled automatically during persist-out. An `id-exists` replay repairs missing owned artifacts and task links without duplicating them. A
129
- `record-conflict:<file>` never overwrites a divergent invoking-tree record and causes the delegate to exit 1, retaining the worktree. A
129
+ `record-conflict:<file>` never overwrites a divergent invoking-tree record and causes the delegate to exit 1, retaining the worktree. Before reconciling one by hand, check **what** the invoking record is: when the run closed `done` inside a linked worktree the owning tree holds that run's **WT-0 landing obligation** (`landing: required branch=… path=… base=…`, see [execution-worktree-landing.md § WT-0](execution-worktree-landing.md#wt-0--markerless-worktree-runs)) — a 3-line record that is *not* a stub of the worktree's stage narrative, so copying the worktree record over it destroys the very obligation a later merge must rewrite. The reconciliation is therefore: discharge the obligation in place (`landing: merged <sha>` or `landing: retained`), and preserve the worktree narrative beside it (e.g. `<runId>.worktree-provenance.md`) rather than in place of it. A
130
130
  `record-missing:<file>` skip is a known `task-lifecycle`/`feature-lifecycle` row with no record file
131
131
  at all (its inserted DB row still counts in `persisted` — 0984 R5). Any failure
132
132
  exits 1 printing `{"ok":false,"error":<message>}` (a worktree DB run id that is not a single safe
@@ -178,6 +178,11 @@ the wrap's refusal of non-done tasks remains the hard invariant.
178
178
 
179
179
  **Close-out commit (1129 R2/R5).** In sequential mode the wrap's writes (learnings, metrics,
180
180
  doc-sync) land in the invoking tree and are committed as ONE close-out commit after the wrap exits.
181
+ **Under `--worktree` they land in the EXECUTION tree instead** and ride the branch as its own commit
182
+ on `$BRANCH`: Step 6 runs before WT-4, so a close-out commit on the base ref would advance it and
183
+ make the batch's own FF-merge impossible (the fresh base tip would no longer be an ancestor of the
184
+ batch tip — the step that makes the close-out the report's own contradiction). The close-out commit
185
+ is never a point on the base ref.
181
186
  Stage them through the guard — never a free-form `git add` — so a concurrent writer's file cannot
182
187
  ride into the close-out commit (incident `36f274590`). `$RUN_ID` is the id Step 3 fixed before
183
188
  `commit-guard start` (execution-batch.md § Step 3); an unset id makes the guard exit 1 — fail closed:
@@ -204,7 +204,18 @@ For each dependency edge to a task **outside** the frozen set, resolve its curre
204
204
 
205
205
  ### 2.4 Topological sort
206
206
 
207
- Topological-sort the remaining in-set, non-blocked tasks using Kahn's algorithm:
207
+ Do not hand-run the sort — compute it. Write the remaining in-set, non-blocked tasks to a temp file
208
+ as a JSON array of `{wbs, dependencies, files?}` (`dependencies` from `spur task show <wbs> --json`
209
+ frontmatter; `files` = repo paths the task's Design/Plan names, omit when it names none) and run:
210
+
211
+ ```bash
212
+ node "$(superskill script path sp batch-plan.mjs)" order --tasks <tasks.json> [--concurrency <n>]
213
+ # → {"order":[…],"waves":[[…],…],"cycle":null|[…],"overlaps":[…],"mode":"sequential|parallel","reason":"…"}
214
+ ```
215
+
216
+ `order` is the execution plan; a non-null `cycle` is the abort below. `waves`, `overlaps`, `mode`
217
+ and `reason` feed `--mode auto` (§ 2.4a) and the batch report. The script implements Kahn's
218
+ algorithm (ADR-130 glue over `scheduleBatch` in `packages/app/src/workflow/plan-projection.ts`):
208
219
 
209
220
  1. Seed the queue with zero-indegree nodes, **sorted WBS-ascending** (deterministic tie-break).
210
221
  2. Repeatedly dequeue the lowest-WBS zero-indegree node, emit it, decrement its successors'
@@ -214,7 +225,35 @@ Topological-sort the remaining in-set, non-blocked tasks using Kahn's algorithm:
214
225
  **Cycle handling (R2.3):** a cycle aborts the **entire batch** before any task runs. Reconstruct a
215
226
  representative cycle path via DFS over the remaining unsorted nodes and report it (e.g.
216
227
  `0040 → 0042 → 0040`). Do not run any task in a cyclic batch — running a prefix would partially
217
- execute work whose ordering is undefined.
228
+ execute work whose ordering is undefined. The script reports it as `cycle` (e.g.
229
+ `["0040","0042","0040"]`) with an empty `order`.
230
+
231
+ **File overlap.** Two tasks that declare the same file get a serialization edge oriented by their
232
+ dependency order (`overlaps[]` records each), so overlap never creates a cycle and never puts
233
+ overlapping tasks in the same wave (`sp:parallel-execution` row 3). It only matters under parallel
234
+ execution; sequential order stays a valid topological order either way.
235
+
236
+ ### 2.4a `--mode auto` resolution
237
+
238
+ `--mode auto` is decided **once, here, by the active orchestrator** (the host session, or
239
+ `sp:super-planner` when an explicit executor is set), after freeze, Step 2.3 blocking and
240
+ the § 2.4 schedule, and before any task runs:
241
+
242
+ | Condition (first match wins) | Resolved mode |
243
+ | --- | --- |
244
+ | `--worktree` present | `sequential` (parallel rejects `--worktree`) |
245
+ | `--agent inline` explicitly passed | `sequential` (inline is the host-session driver) |
246
+ | script `mode` is `sequential` (fewer than 2 tasks, `--concurrency 1`, or every wave holds one task) | `sequential` |
247
+ | script `mode` is `parallel` | `parallel` |
248
+
249
+ - **Sequential resolution** continues the Step 3 loop with the frozen `order` (host-inline, or
250
+ subprocess per task under an explicit executor).
251
+ - **Parallel resolution** hands off to the explicit/parallel path as
252
+ `runall --tasks <order as comma WBS list> --mode parallel` plus the operator's other flags. The
253
+ explicit list carries the frozen membership, so the feature preflight (Step 1) is not re-run.
254
+ - `--defer-gate` survives only a parallel resolution; a sequential one drops it.
255
+ - The batch report records `mode: auto → <resolved>` with the script's `reason`, the widest wave,
256
+ and any `overlaps`.
218
257
 
219
258
  ### 2.5 Result
220
259
 
@@ -321,8 +360,8 @@ emit batch report (per-task outcome + preflight skips + recovery hints + autoRep
321
360
  ```
322
361
 
323
362
  Parallel mode keeps the same lifecycle but swaps the inner loop for the per-task-worktree fan-out
324
- specified in [§ Parallel isolation](#parallel-isolation---mode-parallel): identify a zero-edge,
325
- non-overlapping subset; **preflight each** selected task; run each ready task's `task-pipeline.yaml`
363
+ specified in [§ Parallel isolation](#parallel-isolation---mode-parallel): take the zero-edge,
364
+ non-overlapping frontier from the § 2.4 schedule's waves; **preflight each** selected task; run each ready task's `task-pipeline.yaml`
326
365
  invocation in its own create-mode worktree; integrate by rebase + `--ff-only` as tasks finish;
327
366
  recovery stays **sequential** (one WBS). If any decision-framework check fails, serialize and
328
367
  record the reason.
@@ -567,15 +606,25 @@ The cheap rule gates fail fast when run first — the full-gate run is dominated
567
606
  suite, so a gate run that dies at `test-post-check` or `corpus-check` wastes most of its wall time.
568
607
  Before launching a full `spur-check-new`:
569
608
 
570
- 1. **Run the two rule gates first** — `bun run test-pre-check` (43 rules: `no-console-output`,
609
+ 1. **Run the static checks first** — `bun run lint` (biome over the whole tree plus every workspace
610
+ typecheck). They fail in ~20 s and are the two defects that most often kill a full launch: a
611
+ formatting miss in a touched test file, or a generated declaration surface that no longer lists a
612
+ new export. Measured 2026-10-12: two consecutive `spur-check` launches died here and each spent
613
+ its whole wall time before reaching the suite.
614
+ 2. **Then the two rule gates** — `bun run test-pre-check` (43 rules: `no-console-output`,
571
615
  `no-direct-process-spawn`, `cli-*`, `require-corresponding-test`) and `bun run test-post-check`
572
616
  (`every-export-has-tsdoc`, `coverage-gate`). They catch boundary/TSDoc violations in seconds.
573
- 2. **Promoted code must satisfy the boundary rules `scripts/` never enforced.** A command module
617
+ Two `coverage-gate` facts change how you call post-check: it **fails closed when
618
+ `.coverage/lcov.info` is absent**, which is the normal state of a fresh worktree, so run it after
619
+ the suite has written coverage or expect that one error instead of reading it as a finding; and a
620
+ targeted `bun test <file>` **overwrites** the tracefile, so post-check judges partial data if it
621
+ runs after one (the `coverage-gate` rule's own header documents both facts).
622
+ 3. **Promoted code must satisfy the boundary rules `scripts/` never enforced.** A command module
574
623
  moving from `scripts/` into `apps/cli/src` must route output through the `CommandOutput` seam (no
575
624
  `console.*`), spawn processes via `NodeProcessExecutor` (no `Bun.spawnSync`), get a
576
625
  `runtime-boundaries` fs-io exemption for sync reads (mirrors `task.ts`), and a non-command helper
577
626
  must not live in `apps/cli/src/commands/` (the noun scan treats every file there as a noun).
578
- 3. **Doc/TSDoc edits shift `file:line` anchors** cited by other tasks — the per-task gate surfaces
627
+ 4. **Doc/TSDoc edits shift `file:line` anchors** cited by other tasks — the per-task gate surfaces
579
628
  them as `L4.anchor-subject-mismatch` (0775: the corpus sweep retired; run `spur task check <wbs>`
580
629
  on touched tasks). Repoint the shifted citations (via `spur task update --section`)
581
630
 
@@ -45,7 +45,10 @@ emit batch report (per-task outcomes + preflight skips + recovery hints + batch
45
45
  - **Eligibility (R2).** A task becomes eligible only when all of its in-set dependencies are
46
46
  **integrated** onto the base ref — pipeline-terminal is not enough, because its branch must
47
47
  rebase over theirs. Omitting `--mode` stays sequential; this loop is entered only via
48
- `--mode parallel` on `/sp:dev-runall`.
48
+ `--mode parallel` on `/sp:dev-runall`, directly or as a `--mode auto` resolution
49
+ ([execution-batch.md § 2.4a](execution-batch.md#24a---mode-auto-resolution)).
50
+ - **Fan-out set.** Run order and eligibility come from the § 2.4 `batch-plan.mjs order` schedule;
51
+ its file-overlap edges are in-set dependencies here, so overlapping tasks never run at once.
49
52
  - **Workdir.** `spur workflow run` records the launch workdir (0784 R1), so launch with `cwd` = the
50
53
  task worktree: resume and `.spur/run/` artifact paths resolve there. Read the verdict from
51
54
  `$WT/.spur/run/<wbs>-verdict.json` before integration; WT-4a persists it into the invoking tree.
@@ -16,6 +16,28 @@ When the batch completes with **no failed task**, fast-forward-merge the worktre
16
16
  base ref. The terminal action after the merge differs by mode (ownership rule: *the flag removes
17
17
  only what it created*):
18
18
 
19
+ **Duplicate-WBS sweep before the merge (observed 2026-10-12).** WBS allocation is per-tree — a
20
+ worktree's allocator reads only its own corpus and DB — so two trees cut from the same base both
21
+ allocate the same next number, and the collision surfaces only here (measured: the F42 batch's
22
+ residual task shipped as `1183` while the base ref had already given `1183` to another task, so the
23
+ file had to be re-filed before landing). Run this against the ref you are about to merge INTO:
24
+
25
+ ```bash
26
+ TARGET=<base-ref>; BRANCH=sp/<branch>
27
+ for f in $(git diff --diff-filter=A --name-only "$(git merge-base "$TARGET" "$BRANCH")..$BRANCH" -- docs \
28
+ | grep -E '^docs/tasks[0-9]*/'); do
29
+ wbs=$(basename "$f" | cut -c1-4)
30
+ git ls-tree -r --name-only "$TARGET" -- docs | grep -qE "/${wbs}_" && echo "COLLISION $wbs $f"
31
+ done
32
+ ```
33
+
34
+ A hit means one file must lose: keep the target's task, re-create the branch's task in the invoking
35
+ tree through `spur task create` (the allocator picks the next free number), and drop the branch's
36
+ file in the merge resolution — never land both, and never renumber by hand. Verified against a
37
+ fixture (flags a duplicate, silent on a free WBS). The pathspec form is load-bearing: `-- docs` plus
38
+ the `^docs/tasks[0-9]*/` filter, because plain `docs/tasks*` matches nothing under `ls-tree` here and
39
+ `:(glob)` magic is rejected by `ls-tree`/`diff` (it works for `git status`).
40
+
19
41
  #### Create mode — merge, remove, delete
20
42
 
21
43
  ```bash
@@ -180,6 +180,13 @@ main-repo hooks from inside the "isolated" tree. Scripts are skipped only at thi
180
180
  a normal clone keeps `prepare`. The worktree still gets a usable dependency tree — the install
181
181
  exists so the first `bun test` resolves workspace imports.
182
182
 
183
+ **Generated trees are still missing after the install.** A gitignored generated tree does not ride the
184
+ worktree, so any suite comparing the canonical source against its generated copy fails until the
185
+ generator runs. In this repo that is `apps/cli/config/` (from `bun run bundle:config`): without it
186
+ `plugins/sp/tests/command-flag-parity.test.ts` fails on the missing generated pipeline YAML under that
187
+ directory (observed 2026-10-12, A91 batch). Run the generator once after a create-mode install, and
188
+ again after a rebase or merge that touches the canonical workflow sources it is built from.
189
+
183
190
  #### Reuse mode (`--worktree <name>`)
184
191
 
185
192
  Creation is **skipped entirely**. `$BRANCH` is the resolved worktree's already-checked-out branch; a
@@ -374,6 +381,13 @@ same call, verifies the WT-3 identity, and never relies on a persistent `cd` or
374
381
  > concurrent writer happened to leave in the tree — the two incidents this guard exists for are
375
382
  > `a94f9f431` and `36f274590` — so a driver stages only through `commit-guard stage`.
376
383
 
384
+ > **`stage` takes files, never directories.** A directory argument is refused as `(unchanged since
385
+ > base)` and stages *nothing*, so a task whose diff lives in a moved tree must expand the changed
386
+ > paths (`git status --porcelain` plus the filter for the task's own files) instead of passing
387
+ > `plugins/sp/hooks` or `packages/app/tests/hooks`. Symptom observed on 2026-10-12 (A91 batch): the
388
+ > first commit captured only the pre-staged `git mv`/`git rm` renames and needed an amend to carry
389
+ > the rest of the task.
390
+
377
391
  The FF-only git merge carries only commits — uncommitted writes in the worktree would be left
378
392
  behind by the merge and then destroyed by create mode's `git worktree remove`. WT-3b exists so
379
393
  that can never happen.
@@ -148,7 +148,7 @@ halt-on-first-failure. Never silently retried; the failure is still reported.
148
148
 
149
149
  ### `--defer-gate` — opt-in parallel-batch gate policy (task 1111 / H15)
150
150
 
151
- **Applies to:** `/sp-dev-runall --mode parallel`, `/sp-dev-parallel --mode fan-out`.
151
+ **Applies to:** `/sp-dev-runall --mode parallel`, and `--mode auto` when it resolves to parallel.
152
152
  **Default:** off — every task pipeline runs its own FULL project gate inside its worktree.
153
153
 
154
154
  With `--defer-gate`, each task's `test` / `test-recheck` hop runs the **light tier** (changed-scope
@@ -167,9 +167,10 @@ Two properties are load-bearing and are asserted structurally:
167
167
  project gate; the per-task token is `DEFERRED`, and the `test → triage` /
168
168
  `test-recheck → triage` guards accept it only while `deferQualityGate = "true"`.
169
169
 
170
- `--defer-gate` without `--mode parallel` is rejected — the flag exists to move one full gate from N
171
- worktrees to the one place the integrated tree exists. Sequential and inline batches never set the
172
- var. ADR-124 carries the dated clarification.
170
+ `--defer-gate` with `--mode sequential` (or omitted `--mode`) is rejected — the flag exists to move
171
+ one full gate from N worktrees to the one place the integrated tree exists. Under `--mode auto` it
172
+ applies only if the batch resolves to parallel; a sequential resolution drops it with a report note.
173
+ Sequential and inline batches never set the var. ADR-124 carries the dated clarification.
173
174
 
174
175
  **Anchor:** `#flag-defer-gate`.
175
176
 
@@ -177,11 +178,11 @@ var. ADR-124 carries the dated clarification.
177
178
 
178
179
  **Anchor:** `#flag-concurrency`.
179
180
 
180
- `dev-runall` under `--mode parallel` (task 0931): at most `<n>` task pipelines run at once
181
+ `dev-runall` under `--mode parallel` (task 0931), or `--mode auto` resolved to parallel: at most `<n>` task pipelines run at once
181
182
  (default 2, `n ≥ 1`). A dependent task starts only after every in-set dependency has been
182
183
  **integrated** onto the base ref — see
183
184
  [execution-batch.md § Parallel isolation](execution-batch.md#parallel-isolation---mode-parallel).
184
- Ignored in sequential mode. Each worker is one more concurrent quality gate; raise it only when
185
+ Ignored in sequential mode; `--concurrency 1` pins `--mode auto` to sequential. Each worker is one more concurrent quality gate; raise it only when
185
186
  the host has the CPU for it.
186
187
 
187
188
  ### `--continue` — resume an interrupted batch from checkpoint
@@ -271,12 +272,12 @@ is the contract; divergence between `--dry-run` and the real run is a bug.
271
272
 
272
273
  **Anchor:** `#flag-tasks`.
273
274
 
274
- Batch operation (`dev-parallel`, `dev-refineall`, `dev-review`, `dev-runall`, `dev-verifyall`). An
275
+ Batch operation (`dev-refineall`, `dev-review`, `dev-runall`, `dev-verifyall`). An
275
276
  explicit selector — WBS list, status pseudo-list (`todo`, `wip`), `feature:<id>`, or `ready` —
276
277
  resolving to the set the batch runs over. On `dev-review` the selector is restricted to the
277
278
  review-safe forms: comma WBS list and `feature:<id>` — status pseudo-lists and `ready` are
278
- rejected (they select work to do, not work to review). Required on `dev-parallel`, `dev-runall`,
279
- and `dev-verifyall`, where `--feature` is an optional restrictor. On `dev-refineall` it is instead
279
+ rejected (they select work to do, not work to review). Required on `dev-runall` and
280
+ `dev-verifyall`, where `--feature` is an optional restrictor. On `dev-refineall` it is instead
280
281
  one of a required pair — supply exactly one of `--feature` or `--tasks`.
281
282
 
282
283
  ### `--mode <kind>` — select an execution mode
@@ -284,8 +285,9 @@ one of a required pair — supply exactly one of `--feature` or `--tasks`.
284
285
  **Anchor:** `#flag-mode`.
285
286
 
286
287
  Select an execution mode: `full|implement` on `dev-run` (full pipeline vs implement-only),
287
- `sequential|parallel` on `dev-runall` (serial vs fanned-out-independent-subset),
288
- `fan-out|review-panel|investigation` on `dev-parallel`, the reconstruction depth
288
+ `sequential|parallel|auto` on `dev-runall` (serial, fanned-out independent subset, or chosen from the
289
+ frozen batch's dependency waves — see [execution-batch.md § 2.4](execution-batch.md#24-topological-sort)),
290
+ the reconstruction depth
289
291
  `briefing|structure|architecture|design|full` on `dev-reverse`, and `daily|ad-hoc` on
290
292
  `dev-find-issue` (the history-anatomy report mode). Mode selection is explicit and orthogonal
291
293
  to `--next`.
@@ -86,7 +86,9 @@ fallback) — labels are display addresses only, never an execution key.
86
86
  1. Resolve the command inputs, `--auto`, and any explicit `--vars` — **without reading the selected
87
87
  YAML yet**. An explicit non-inline executor selection chooses the subprocess workflow path.
88
88
  2. Allocate a collision-resistant inline run id (`uuidgen`, with a timestamp/pid fallback), create
89
- `.spur/run/` for attempt staging and `.spur/memory/runs/` for retained records, and use the two-file run record (task 0927) — append lines to
89
+ `.spur/run/` and the run's scratch subdirectory `.spur/run/<run-id>/` for attempt staging (action
90
+ batches, captured stage output, temporary status/answer files) plus `.spur/memory/runs/` for
91
+ retained records, and use the two-file run record (task 0927) — append lines to
90
92
  `.spur/memory/runs/<run-id>.md` and read machine state from `.spur/memory/runs/<run-id>.state.json`.
91
93
  The run id lives in those two files and the persisted run row — never stage it in an ad-hoc
92
94
  `*.env` file: the `no-env-files` gate rule flags any `.env` name, so run-scoped staging files
@@ -439,9 +441,9 @@ Action semantics come from the YAML and the workflow action contract:
439
441
  recheck's `no-progress` guard honours it, so a cap-kill reads as a red gate the next time. Preserve
440
442
  the signal-induced receipt under a distinct name (`<wbs>-check-receipt.false-fail-<signal>.json`)
441
443
  and say so in the run log before re-running: never delete it silently, and never report a
442
- cap-killed attempt as a genuine gate failure. Measured this session: one foreground gate call
443
- (25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the host cap and produced
444
- exactly that false FAIL receipt.
444
+ cap-killed attempt as a genuine gate failure. Measured 2026-10-10 (before the gate-lock removal):
445
+ one foreground gate call (25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the
446
+ host cap and produced exactly that false FAIL receipt.
445
447
  - `note` — append the expanded message to the inline run log.
446
448
  - `doctor.probe` — run the declared Spur doctor once, persist its status file, and apply any
447
449
  `setVars` result (including a resolved executor) before the next action or state.
@@ -606,7 +608,7 @@ prompt the host cannot see is invisible until join. Record one run-log line per
606
608
  stage <id> permission precheck: ok | <missing capability>
607
609
  ```
608
610
 
609
- Read-only investigation fan-out (`/sp:dev-parallel --mode investigation`) dispatches read-only
611
+ Read-only investigation fan-out (N-way investigation) dispatches read-only
610
612
  worker shapes by construction — see the [pre-dispatch permission
611
613
  rule](../../parallel-execution/references/fan-out-patterns.md).
612
614
 
@@ -761,21 +763,14 @@ grading its own work defeats the stage. A continuation stage also dispatches fre
761
763
  same-task subagent exists (the earlier stage ran host-inline or below the dispatch floor), when a
762
764
  host-owned gate sat between the stages, or when the platform cannot address completed subagents.
763
765
 
764
- **Implement and test-fix workers never run the full gate (task 1127 R8 / task 1142 R3).** Since 1127 the full gate
765
- takes a host-wide lock (`SPUR_GATE_LOCK_DIR`, default `~/.config/spur/run/full-gate.lock`):
766
- concurrent full-gate runs serialize, and a manual `spur-check` (wrapped via
767
- `scripts/commands/gate-lock.ts`) holds the same lock. A dispatched **implement** or `test-fix` child
768
- that re-ran the full gate would queue behind every other gate on the host and burn its dispatch
769
- timeout waiting instead of working. Workers use the light tier as their iteration check (task 1142 R3) in exact order:
766
+ **Implement and test-fix workers never run the full gate (task 1142 R3).** Workers use the light tier as their iteration check (task 1142 R3) in exact order:
770
767
  1. the **changed-path matrix** (`code-implementation/SKILL.md` § Changed-path targeted checks) over the paths they touched;
771
768
  2. the resolved `quality-gate` script in `light` mode (source-repo probe, else the installed twin) (biome, per-workspace typecheck, rule presets, related tests).
772
- Neither step takes the full-gate lock. The pipeline's `test` and `test-recheck` hops remain the only full gate. Carry
769
+ Neither step takes the full gate. The pipeline's `test` and `test-recheck` hops remain the only full gate. Carry
773
770
  `--gate-log <path>` through to the child **verbatim**: never paraphrase it into "re-run the named
774
- gate command". That paraphrase is not cosmetic — it dropped this rule in the H15 run (task
775
- Background, seq 232) and sent a worker into the locked gate. The pipeline's next `test` (recheck)
776
- stage re-acquires the lock and delivers the deciding verdict; that deciding run may wait for a live
777
- holder, so a queued worker's effective budget is dispatch timeout minus observed lock queue wait,
778
- not the timeout alone.
771
+ gate command". That paraphrase is not cosmetic — it sent a worker into a full gate run instead of
772
+ targeted iteration checks. The pipeline's next `test` (recheck)
773
+ stage delivers the deciding verdict.
779
774
 
780
775
  **Null or timed-out dispatch (task 1134 R9).** The dispatch bound stays the existing YAML
781
776
  `timeoutMs` (`implementTimeoutMs` / `stepTimeoutMs`), passed to the host dispatch as today — R9 adds
@@ -840,6 +835,19 @@ The quality gate's `proofDigest` certifies a specific tree. Two rules protect it
840
835
  before a review dispatch is spent. The mismatch routes through the existing tripwire to `failed`;
841
836
  there is no new re-certify path.
842
837
 
838
+ **A host-driven pass that skips the `test` hop has no digest-bound receipt.** `proofDigest` is
839
+ captured by the `test` state's `proof.fingerprint` action, so hops driven out of band — an operator
840
+ implementing/verifying/recording by hand, or an audit of a task whose gate the host ran directly —
841
+ leave no `.spur/run/<wbs>-check-receipt.json`. The deciding artifacts are then
842
+ `.spur/run/<wbs>-test-gate.status` plus its gate log. Two consequences, in order of preference:
843
+
844
+ 1. **Drive the pipeline** for anything that must be certified: only a digest-bound full receipt
845
+ satisfies a review/verify/record boundary (`sp-spur-check`), and the `test` hop owns the capture.
846
+ 2. **When a host-driven pass is unavoidable, say so in the verdict** — name the status file and gate
847
+ log as the evidence and grade confidence `MEDIUM`. A `HIGH` claim needs re-runnable evidence from a
848
+ checked path (the `sp-code-verification` ladder); claiming a receipt that was never written is the
849
+ failure mode this note exists to prevent (observed across four A91 tasks, 2026-10-12).
850
+
843
851
  **A mismatch names the drifted paths (R5).** At gate entry the pipeline snapshots the non-corpus tree
844
852
  state (`git status --porcelain=v1 -uall` plus `git diff --name-only HEAD`, excluding
845
853
  `docs/tasks*` / `docs/features*`) into `.spur/run/<run-id>-gate-paths.txt`. On a mismatch the
@@ -914,21 +922,25 @@ verdict artifact exists (tasks 0617, 0619 — the clobbering spiral).
914
922
 
915
923
  ## Running the full gate (task 0872 / 1127 R7)
916
924
 
917
- The full gate serializes behind a host-wide lock and legitimately runs 10-25 minutes. Two rules keep
918
- it from becoming a hazard (both violated once on 2026-10-10, blocking every other session for ~11
919
- minutes and aborting the run's own verification):
925
+ The full gate legitimately runs 10-25 minutes. Two rules keep it from becoming a hazard (both
926
+ violated once on 2026-10-10, aborting the run's own verification):
920
927
 
921
928
  - **Never wrap it in an external `timeout`, and never treat its runtime as a hang.** An outer bound
922
- that fires mid-suite leaves the holder alive holding the lock while the caller believes it is gone.
923
- - **Run it detached and poll the log**, so losing the foreground call cannot lose the holder:
929
+ that fires mid-suite leaves a half-finished suite behind while the caller believes it is gone.
930
+ - **Run it detached and poll the log**, so losing the foreground call cannot lose the run:
924
931
 
925
932
  ```bash
926
933
  nohup bash -c 'bun run spur-check > /tmp/gate.log 2>&1; echo "EXIT=$?" >> /tmp/gate.log' &
927
934
  ```
928
935
 
929
- - **Never kill a gate holder whose pid is alive.** The waiter line `waiting for holder (… held Nms)`
930
- is printed only after `isProcessAlive(held.claim.pid)` succeeded, so a printed holder is a live one
931
- by construction; only the lock's own dead-pid reclaim is authorised to clear a claim.
936
+ - **Preserve a signal-induced receipt.** A cap-killed attempt leaves a red receipt that the next
937
+ recheck's `no-progress` guard honours, so it reads as a genuine failure unless the receipt is kept
938
+ under a distinct name and the run log says so.
939
+
940
+ > Historical (task 1127, removed in task 1173): these rules were once stated in terms of a host-wide
941
+ > `full-gate.lock`. The lock mechanism was deleted (`refactor(gate): remove gate-lock mechanism and
942
+ > simplify quality-gate`), so nothing serializes gate runs today — the guidance above stands on its
943
+ > own without it.
932
944
 
933
945
  ## Runtime interface contracts (from run evidence)
934
946