@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/config.global.yaml +4 -2
- package/config/decisions/maker-liveness.yaml +39 -0
- package/config/plugin-scripts.json +0 -4
- package/config/rules/README.md +1 -0
- package/config/rules/docs/sourced-api-claims.yaml +56 -12
- package/config/rules/strict/runtime-boundaries.yaml +7 -0
- package/config/script-placement-baseline.json +1 -19
- package/config/templates/AGENTS.md +1 -1
- package/config/workflows/feature-lifecycle.yaml +46 -1
- package/config/workflows/feature-verification.yaml +11 -0
- package/config/workflows/wrapup-pipeline.yaml +1 -1
- package/package.json +10 -10
- package/plugins/sp/README.md +23 -18
- package/plugins/sp/agents/super-planner.md +5 -6
- package/plugins/sp/agents/super-reviewer.md +43 -0
- package/plugins/sp/bunfig.toml +8 -5
- package/plugins/sp/commands/dev-review-session.md +2 -1
- package/plugins/sp/commands/dev-runall.md +18 -9
- package/plugins/sp/hooks/careful-guard.ts +1 -1
- package/plugins/sp/hooks/context-post-tool.ts +7 -404
- package/plugins/sp/hooks/context-session-start.ts +6 -192
- package/plugins/sp/hooks/context-session-stop.ts +6 -91
- package/plugins/sp/hooks/pi/guard-extension.ts +26 -12
- package/plugins/sp/hooks/task-write-guard.ts +1 -1
- package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
- package/plugins/sp/lib/context-owner.ts +56 -0
- package/plugins/sp/lib/context-post-tool.ts +432 -0
- package/plugins/sp/lib/context-session-start.ts +214 -0
- package/plugins/sp/lib/context-session-stop.ts +196 -0
- package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
- package/plugins/sp/lib/dogfood-report.ts +278 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +6 -4
- package/plugins/sp/lib/inline-run-app.ts +40 -0
- package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
- package/plugins/sp/lib/inline-run.generated.mjs +1361 -87
- package/plugins/sp/lib/plan-projection.generated.d.mts +169 -0
- package/plugins/sp/lib/plan-projection.generated.mjs +704 -1
- package/plugins/sp/lib/quality-gate.generated.d.mts +0 -12
- package/plugins/sp/lib/quality-gate.generated.mjs +62 -231
- package/plugins/sp/lib/residual-scan.generated.d.mts +7 -1
- package/plugins/sp/lib/residual-scan.generated.mjs +71 -6
- package/plugins/sp/lib/run-summary-core.ts +34 -370
- package/plugins/sp/lib/transcript.ts +37 -200
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/batch-plan.mjs +740 -1
- package/plugins/sp/scripts/batch-plan.ts +48 -3
- package/plugins/sp/scripts/batch-preflight.mjs +23 -1
- package/plugins/sp/scripts/batch-preflight.ts +35 -295
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +108 -87
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -1
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +3 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
- package/plugins/sp/scripts/feature-verification-steps.mjs +7 -123
- package/plugins/sp/scripts/feature-verification-steps.ts +29 -181
- package/plugins/sp/scripts/inline-run-dispatch.mjs +27 -26
- package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
- package/plugins/sp/scripts/inline-run-setup.mjs +158 -120
- package/plugins/sp/scripts/inline-run-setup.ts +1 -21
- package/plugins/sp/scripts/pr-reviewing.mjs +19 -5
- package/plugins/sp/scripts/pr-reviewing.ts +34 -9
- package/plugins/sp/scripts/quality-gate.mjs +38 -208
- package/plugins/sp/scripts/residual-scan.mjs +73 -6
- package/plugins/sp/scripts/residual-scan.ts +8 -5
- package/plugins/sp/scripts/run-summary.mjs +160 -95
- package/plugins/sp/scripts/run-summary.ts +7 -5
- package/plugins/sp/scripts/session-timeline.mjs +57 -29
- package/plugins/sp/scripts/session-timeline.ts +4 -18
- package/plugins/sp/scripts/task-diffstat.mjs +43 -30
- package/plugins/sp/scripts/task-diffstat.ts +40 -71
- package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
- package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
- package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
- package/plugins/sp/skills/indexed-context/SKILL.md +14 -7
- package/plugins/sp/skills/next-feature/SKILL.md +1 -1
- package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
- package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +2 -2
- package/plugins/sp/skills/parallel-execution/references/fan-out-patterns.md +2 -2
- package/plugins/sp/skills/pr-reviewing/SKILL.md +7 -3
- package/plugins/sp/skills/session-review/SKILL.md +5 -3
- package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
- package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
- package/plugins/sp/skills/spur-cli/references/decision.md +40 -8
- package/plugins/sp/skills/spur-cli/references/features.md +46 -5
- package/plugins/sp/skills/spur-cli/references/projects.md +7 -5
- package/plugins/sp/skills/spur-dev/SKILL.md +2 -0
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +27 -0
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -3
- package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +6 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +56 -7
- package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +4 -1
- package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
- package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +13 -11
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +37 -25
- package/spur.js +89792 -76992
- package/web/_astro/{BoardApp.D66k_s_T.js → BoardApp.5IIl2O-z.js} +1 -1
- package/web/_astro/BoardApp.Drnx-KeM.js +324 -0
- package/web/_astro/TaskDetail.Ci4LNn9S.js +1 -0
- package/web/_astro/{arc._BUadrdH.js → arc.j2EVkYVd.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.Dh7xS1Pp.js → architectureDiagram-3BPJPVTR.DZWSFqEG.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.DFDVpfXm.js → blockDiagram-GPEHLZMM.DE6GpXCN.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.V7_3_glh.js → c4Diagram-AAUBKEIU.DoVR1G6B.js} +1 -1
- package/web/_astro/channel.DmdDxGsq.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.De1yx7Jz.js → chunk-2J33WTMH.Do6-Gbyf.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.BDhHFuxx.js → chunk-4BX2VUAB.BYV1HGHx.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DDLkWnmC.js → chunk-55IACEB6.CvI1zkrc.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.CwfiLnA7.js → chunk-727SXJPM.DNGSCRlX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.CtGDmUnM.js → chunk-AQP2D5EJ.DXhhgT5z.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BOLjpw73.js → chunk-FMBD7UC4.dzkRKP2u.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BDhQls6O.js → chunk-ND2GUHAM.DujGLeEf.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.76qBynDF.js → chunk-QZHKN3VN.BWtIT3ZU.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.N0mpPHT0.js → classDiagram-4FO5ZUOK.DyApg0Nn.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.N0mpPHT0.js → classDiagram-v2-Q7XG4LA2.DyApg0Nn.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CKknFfjB.js → cose-bilkent-S5V4N54A.DJQB-u_l.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.Cw3ZWBAh.js → cynefin-OW5HDTMX.bi3D_siV.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.D3yIKCRD.js → dagre-BM42HDAG.C-f7TrRg.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D51dO83Z.js → diagram-2AECGRRQ.DrPfjHQB.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.Bb-FViDj.js → diagram-5GNKFQAL.C9S4WUHO.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.D3mN8S6x.js → diagram-KO2AKTUF.Dzzye8QH.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.dMMyPcbJ.js → diagram-LMA3HP47.DX-LRiof.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.AzbvzY4C.js → diagram-OG6HWLK6.Z-kAKwIz.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BR5I4OZb.js → erDiagram-TEJ5UH35.D6dyClSv.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.DFqvhGkn.js → flowDiagram-I6XJVG4X.Cx3q2cxD.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.ByJbhH6J.js → ganttDiagram-6RSMTGT7.DU3gH6CT.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CInHjQR3.js → gitGraphDiagram-PVQCEYII.ISmCnJ87.js} +1 -1
- package/web/_astro/index.Do2lLdgo.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.Cm0dGVjJ.js → infoDiagram-5YYISTIA.DR6_iNOK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.C3nOP6Sj.js → ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.4IvLa2MW.js → journeyDiagram-JHISSGLW.DkP9-WK8.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.DS87Cl-k.js → kanban-definition-UN3LZRKU.wtgY98M2.js} +1 -1
- package/web/_astro/{linear.MzmRrNER.js → linear.CI6djNi7.js} +1 -1
- package/web/_astro/{mermaid.core.fK-WNswS.js → mermaid.core.CuQPxS8b.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BqipOHhY.js → mindmap-definition-RKZ34NQL.5HNIEG7R.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.0nBiPqFG.js → pieDiagram-4H26LBE5.CchhDFjE.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.Szj7Zpx5.js → quadrantDiagram-W4KKPZXB.PYXXJPSJ.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.D4UR6TWo.js → requirementDiagram-4Y6WPE33.cUQuNAKU.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.DAE7BdOs.js → sankeyDiagram-5OEKKPKP.BO7MnRIA.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BP9ZVq25.js → sequenceDiagram-3UESZ5HK.D9bElyD6.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.BQW7mIxk.js → stateDiagram-AJRCARHV.DhGdRz7I.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.PbK4nDih.js → stateDiagram-v2-BHNVJYJU.5GvLeAoL.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.Ci_GvUpo.js → timeline-definition-PNZ67QCA.LtAT9haM.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DNw1Y5qx.js → vennDiagram-CIIHVFJN.DRth1yLA.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CwlkaaNe.js → wardleyDiagram-YWT4CUSO.h9MlZfcr.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.MI6ZuiH_.js → xychartDiagram-2RQKCTM6.DHqy9DYA.js} +1 -1
- package/web/index.html +2 -2
- package/config/proportional-route-table.ts +0 -155
- package/plugins/sp/commands/dev-parallel.md +0 -37
- package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
- package/web/_astro/BoardApp.D6sjAJr_.js +0 -324
- package/web/_astro/TaskDetail.BynLnMNc.js +0 -1
- package/web/_astro/channel.DZVfto-r.js +0 -1
- package/web/_astro/index.COh_kBxN.css +0 -1
- /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
- /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
|
|
85
|
-
enforces legal transitions; `verifying` is the gate where AC
|
|
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
|
|
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>` |
|
|
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
|
|
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`
|
|
80
|
-
|
|
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` (
|
|
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
|
-
|
|
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):
|
|
325
|
-
non-overlapping
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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`,
|
|
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`
|
|
171
|
-
worktrees to the one place the integrated tree exists.
|
|
172
|
-
|
|
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-
|
|
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-
|
|
279
|
-
|
|
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
|
|
288
|
-
|
|
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/`
|
|
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
|
|
443
|
-
(25.6 min host-wide lock wait + 7.4 min runtime) was SIGTERM-killed at the
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
775
|
-
|
|
776
|
-
stage
|
|
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
|
|
918
|
-
|
|
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
|
|
923
|
-
- **Run it detached and poll the log**, so losing the foreground call cannot lose the
|
|
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
|
-
- **
|
|
930
|
-
|
|
931
|
-
|
|
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
|
|