@gobing-ai/spur 0.3.99 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/config.global.yaml +17 -0
- package/config/decisions/task-pipeline.yaml +55 -0
- package/config/plugin-scripts.json +10 -0
- package/config/rules/boundary/sp-runtime-path.yaml +2 -0
- package/config/rules/quality/coverage-gate.yaml +5 -3
- package/config/rules/strict/runtime-boundaries.yaml +11 -0
- package/config/rules/structure/protected-files.yaml +3 -0
- package/config/workflows/task-pipeline.yaml +178 -18
- package/package.json +9 -9
- package/plugins/sp/README.md +14 -1
- package/plugins/sp/agents/super-reviewer.md +25 -11
- package/plugins/sp/commands/dev-changelog.md +5 -4
- package/plugins/sp/commands/dev-review-session.md +12 -7
- package/plugins/sp/commands/dev-run.md +5 -2
- package/plugins/sp/commands/dev-runall.md +5 -2
- package/plugins/sp/hooks/agent-lifecycle.ts +155 -0
- package/plugins/sp/hooks/fleet-guest-stop.ts +180 -0
- package/plugins/sp/hooks/hooks.json +45 -1
- package/plugins/sp/hooks/pi/guard-extension.ts +21 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +5 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +34 -9
- package/plugins/sp/lib/inline-run.generated.d.mts +1 -0
- package/plugins/sp/lib/inline-run.generated.mjs +49 -19
- package/plugins/sp/lib/transcript.ts +160 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +5 -0
- package/plugins/sp/scripts/inline-run-setup.mjs +21 -20
- package/plugins/sp/scripts/inline-run-setup.ts +25 -31
- package/plugins/sp/scripts/residual-scan.mjs +2 -7
- package/plugins/sp/scripts/residual-scan.ts +7 -8
- package/plugins/sp/scripts/run-summary.mjs +286 -0
- package/plugins/sp/scripts/run-summary.ts +241 -0
- package/plugins/sp/scripts/session-timeline.mjs +234 -0
- package/plugins/sp/scripts/session-timeline.ts +152 -0
- package/plugins/sp/skills/code-verification/SKILL.md +5 -0
- package/plugins/sp/skills/fleet-join/SKILL.md +63 -0
- package/plugins/sp/skills/session-review/SKILL.md +46 -24
- package/plugins/sp/skills/spur-cli/SKILL.md +1 -0
- package/plugins/sp/skills/spur-cli/references/agent.md +108 -6
- package/plugins/sp/skills/spur-cli/references/decision.md +97 -0
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +22 -0
- package/plugins/sp/skills/spur-composer/SKILL.md +27 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +28 -16
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +64 -7
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +14 -0
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -4
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +32 -7
- package/plugins/sp/skills/spur-dev/references/planning-workflow.md +6 -0
- package/schemas/decision-catalog.schema.json +226 -0
- package/schemas/spur-config.schema.json +25 -0
- package/spur.js +7440 -2424
- package/web/_astro/BoardApp.Bzr4OXrW.js +324 -0
- package/web/_astro/{BoardApp.i4eh_fHA.js → BoardApp.VeoxxLBy.js} +1 -1
- package/web/_astro/{TaskDetail.C5Cu8LdB.js → TaskDetail.LAyR_Pvn.js} +1 -1
- package/web/_astro/{arc.Bu5XpBEz.js → arc.f4e0OYLg.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.9vHQTvhF.js → architectureDiagram-3BPJPVTR.SSIvxi-Z.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.Cn2Xl7D6.js → blockDiagram-GPEHLZMM.DSXIHOFf.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.7o0P_QDo.js → c4Diagram-AAUBKEIU.DG9Ubu85.js} +1 -1
- package/web/_astro/channel.CUFdtWhm.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.C3zZFsNt.js → chunk-2J33WTMH.C7QU88Qh.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.CNy_M7fO.js → chunk-4BX2VUAB.26Z94xBj.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.2TiPnLBL.js → chunk-55IACEB6.DYRiBkqw.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.5CGSyKAp.js → chunk-727SXJPM.D5Vfv5pe.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.w-ZIgfsK.js → chunk-AQP2D5EJ.DXHV47bN.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BkcEyhKa.js → chunk-FMBD7UC4.D1suAfOs.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.mxKKbBG4.js → chunk-ND2GUHAM.D6BmdHEY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.cyA0z9aT.js → chunk-QZHKN3VN.rnPU-Ak-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CqtUzwhX.js → classDiagram-4FO5ZUOK.CvEfXIo1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CqtUzwhX.js → classDiagram-v2-Q7XG4LA2.CvEfXIo1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.BExPTopI.js → cose-bilkent-S5V4N54A.DkMisLSm.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.Bq2i10Cn.js → cynefin-OW5HDTMX.C77Q-pF6.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.By_HsOuv.js → dagre-BM42HDAG.YaV8ce27.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.BtU0b7hz.js → diagram-2AECGRRQ.CUyGEKge.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.D00ztMdT.js → diagram-5GNKFQAL.CRgd8dgj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DMVJWaUN.js → diagram-KO2AKTUF.Bmzys9hq.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.Cxqrjg5-.js → diagram-LMA3HP47.BvmJY4Lw.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.2lz_1B5h.js → diagram-OG6HWLK6.0hEhD30d.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.BVQEuJ3T.js → erDiagram-TEJ5UH35.Bjt8lrnm.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.Qlyo90ky.js → flowDiagram-I6XJVG4X.CJZTjRDV.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.DRwb8HHZ.js → ganttDiagram-6RSMTGT7._VqoVafc.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.YfoPthfS.js → gitGraphDiagram-PVQCEYII.CEDb7tVo.js} +1 -1
- package/web/_astro/index.Dg_3bOaJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DvFREck1.js → infoDiagram-5YYISTIA.CgwifPpK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BzCj9Q0-.js → ishikawaDiagram-YF4QCWOH.B99wx2J1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.BDMT_AFb.js → journeyDiagram-JHISSGLW.2hRwJbEo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.D6Fh3gj9.js → kanban-definition-UN3LZRKU.BahJkKNC.js} +1 -1
- package/web/_astro/{linear.Dxw00fGQ.js → linear.DguUUe6V.js} +1 -1
- package/web/_astro/{mermaid.core.CsoxUWa0.js → mermaid.core.CvgfmqVr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.CN8oeHLu.js → mindmap-definition-RKZ34NQL.B3qEA3x9.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.C7ADqQ_7.js → pieDiagram-4H26LBE5.FlWA-_ru.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.Cw9lKyHP.js → quadrantDiagram-W4KKPZXB.9bVn35X7.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.B1ni-z4q.js → requirementDiagram-4Y6WPE33.ChykIhct.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BhexdjVr.js → sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.C3Rm7dpQ.js → sequenceDiagram-3UESZ5HK.J7JsHWDt.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CkuMt27u.js → stateDiagram-AJRCARHV.DAPbiCSS.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Dc_aazUA.js → stateDiagram-v2-BHNVJYJU.D2jvSi0q.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.ja7sUFhH.js → timeline-definition-PNZ67QCA.psW2ci29.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BxIshX_K.js → vennDiagram-CIIHVFJN.DlILYVmg.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DfHlC0sh.js → wardleyDiagram-YWT4CUSO.xNWwLJnn.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.DSnhHwCh.js → xychartDiagram-2RQKCTM6.EU_BuH7V.js} +1 -1
- package/web/board-runtime.json +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.BVp-LYi6.js +0 -322
- package/web/_astro/channel.6IFIKBhu.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -23,14 +23,18 @@ that before using `run` for fan-out dispatch.
|
|
|
23
23
|
| ---- | ------- | --------- |
|
|
24
24
|
| `run <prompt>` | Execute a prompt or slash command via a coding agent | `--agent <name>` `--spec <id>` `--model <name>` `--mode <mode>` `--continue` `--cwd <path>` `--drain` `--json` |
|
|
25
25
|
| `wait [<specId>]` | Identity-pinned wait for an occupant run to reach a lifecycle state (G4 wave 2; `--role` selector per 0685) | `--role <name>` `--run <runId>` `--until <state>...` `--timeout <ms>` `--json` |
|
|
26
|
+
| `trace <runId>` | Print one execution record's lineage — the run, its dispatch parents, agent session ids and streams (1076 R4, ADR-132) | `--follow` `--timeout <ms>` `--json` |
|
|
26
27
|
| `list` | List detected coding agents, or agent specs with `--specs` (live run status + member session merged from `spur self serve`) | `--specs` `--server <url>` `--json` |
|
|
28
|
+
| `report` | Report a fleet member lifecycle state (`working`/`idle`/`blocked`) from a host hook (G73 R1/R2, task 1080) | `--state <state>` `--seq <ns>` `--spec <id>` `--json` |
|
|
29
|
+
| `join` | Join the fleet as a guest occupant — a live session that pulls its own work (G73 R2, task 1081) | `--role <name>` `--id <id>` `--session-id <sid>` `--pid <n>` `--executor <name>` `--json` |
|
|
30
|
+
| `leave` | Leave the fleet: release the guest lease, retire the occupant, return claimed messages to pending | `[id]` `--session-id <sid>` `--json` |
|
|
27
31
|
| `status` | Agent specs with live process status and member session (requires `spur self serve`) | `--server <url>` `--json` |
|
|
28
32
|
| `doctor [agent]` | Check agent readiness | `--json` `--probe-health` `--force-refresh` |
|
|
29
33
|
| `usage` | Run-once provider usage capture (codexbar) → quota-owned availability refresh; scheduled externally | `--dry-run` `--source <name>` `--json` |
|
|
30
34
|
| `start <spec-id>` | Start a supervised agent process (requires `spur self serve`) | `--server <url>` `--json` |
|
|
31
35
|
| `stop <spec-id>` | Stop a supervised agent process (requires `spur self serve`) | `--server <url>` `--json` |
|
|
32
36
|
|
|
33
|
-
`list`, `status`, `doctor`, `run`, `wait`, `start`, and `stop` accept `--json` plus `--json-envelope`. The hidden
|
|
37
|
+
`list`, `status`, `doctor`, `run`, `wait`, `trace`, `start`, and `stop` accept `--json` plus `--json-envelope`. The hidden
|
|
34
38
|
`loop` is a supervisor-internal process surface. **Exit codes:** `0` success, `1` failure, and `2`
|
|
35
39
|
invalid usage; `run` can also propagate the invoked agent's non-zero result.
|
|
36
40
|
|
|
@@ -91,7 +95,9 @@ It waits for a wake on the `system_events` ledger — a human request (`message.
|
|
|
91
95
|
change (`strategy.changed`), a capacity change (`fleet.capacity.changed`), or a completion receipt
|
|
92
96
|
(`agent.invoke.exit`) — then drains the inbox into an `agent run` invocation. An idle wake records
|
|
93
97
|
the hold reason instead of dispatching; with no wake event it still drains every `--poll` ms
|
|
94
|
-
(default `2000`). It runs until `SIGINT` / `SIGTERM
|
|
98
|
+
(default `2000`). It runs until `SIGINT` / `SIGTERM`, or until its parent `spur serve` disappears —
|
|
99
|
+
the parent is re-read each poll and a change aborts the loop, so a `SIGKILL`ed serve leaves no
|
|
100
|
+
orphan loop (1088 R2).
|
|
95
101
|
|
|
96
102
|
## `wait` - identity-pinned occupant wait (G4 wave 2)
|
|
97
103
|
|
|
@@ -146,8 +152,9 @@ supervisor**: each row carries a trailing status column
|
|
|
146
152
|
(`running` / `stopped` / `errored` / `unknown`), `pid=<n>` where a process exists, and the member
|
|
147
153
|
session (0897): the session mode plus a shortened resume id (`resume id=3f9c2a1d`), or `-` when the
|
|
148
154
|
member has no recorded session. When `spur self serve` is unreachable, the listing falls back to all
|
|
149
|
-
`stopped` with a stderr warning. `--server <url>`
|
|
150
|
-
(
|
|
155
|
+
`stopped` with a stderr warning. `--server <url>` targets the supervisor API; without it the URL is
|
|
156
|
+
this project's serve — the port in the cwd's project-registry entry (`http://localhost:<port>/api`),
|
|
157
|
+
falling back to `http://localhost:3000/api` only when the project has no live entry (1088 R1).
|
|
151
158
|
|
|
152
159
|
```bash
|
|
153
160
|
spur agent list --specs
|
|
@@ -164,9 +171,77 @@ spur agent status --json # full objects, session carried whole ({ mode, id }
|
|
|
164
171
|
|
|
165
172
|
Reads the same supervisor feed as `list --specs` (liveness **and** session come from
|
|
166
173
|
`GET /api/processes`; the served project's ledger is the source of the session state). An
|
|
167
|
-
unreachable server reports every spec `stopped` with no session.
|
|
174
|
+
unreachable server reports every spec `stopped` with no session. `--server <url>`
|
|
175
|
+
(default: this project's registry serve port, `http://localhost:<port>/api`; else
|
|
176
|
+
`http://localhost:3000/api` — 1088 R1) targets it. See
|
|
168
177
|
[Member sessions](#member-sessions-g66) for what the modes mean.
|
|
169
178
|
|
|
179
|
+
## `report` - fleet lifecycle state from a host hook (G73, task 1080)
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
spur agent report --state working --seq 1759665600000000000
|
|
183
|
+
spur agent report --state blocked --seq 1759665600000000000 --spec proj-worker-1 --json
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
| Flag | Description |
|
|
187
|
+
| --- | --- |
|
|
188
|
+
| `--state <state>` | Lifecycle state: `working` \| `idle` \| `blocked` (required) |
|
|
189
|
+
| `--seq <ns>` | Monotonic report sequence in nanoseconds (required); not greater than the last accepted one → ignored |
|
|
190
|
+
| `--spec <id>` | Member spec id; defaults to `SPUR_SPEC_ID` |
|
|
191
|
+
| `--json` | Machine-readable result (`accepted`, `observation`) |
|
|
192
|
+
| `--json-envelope` | Wrap the JSON in the standard output envelope |
|
|
193
|
+
|
|
194
|
+
An accepted report writes the cataloged `agent.lifecycle.changed` ledger row — the newest
|
|
195
|
+
accepted row for the member is its current state, so there is no separate state store. The
|
|
196
|
+
dispatch strategy treats `blocked` as unavailable (hold reason `member-blocked`), and the Board
|
|
197
|
+
shows `needs human` on that member.
|
|
198
|
+
|
|
199
|
+
Exit `2` when `--state`/`--seq` is missing or malformed, or when no member id resolves
|
|
200
|
+
(`SPUR_SPEC_ID` unset and no `--spec`). The `sp` plugin's `agent-lifecycle` host hook calls this
|
|
201
|
+
in the background on `SessionStart` (idle), `UserPromptSubmit` (working), `Notification`
|
|
202
|
+
(`permission_prompt` → blocked) and `Stop` (idle); outside a fleet it makes no call at all.
|
|
203
|
+
|
|
204
|
+
## `join` / `leave` - guest occupancy (G73 R2/R5, task 1081)
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
spur agent join --role reviewer --json # id defaults to <role>-g<n>
|
|
208
|
+
spur agent join --role coder --id my-session --json
|
|
209
|
+
spur agent leave --json # the guest joined by this session
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
A guest is a live session that **pulls** work: never supervised, never restarted, addressable by
|
|
213
|
+
concrete id only (role/executor selectors count declared members, so a guest is invisible to them).
|
|
214
|
+
`join` writes an occupant row in `coordination_runs`, claims a `guest:<id>` lease on the shared
|
|
215
|
+
claim table (same TTL/heartbeat semantics as the write slot — R5) and writes
|
|
216
|
+
`.spur/run/guests/<id>.json` so a host Stop hook can recognize its own session without a CLI call.
|
|
217
|
+
`leave`, or lease expiry in the reconciler pass, releases the lease, marks the occupant exited,
|
|
218
|
+
returns its claimed messages to `pending` and removes the record.
|
|
219
|
+
|
|
220
|
+
| Flag | Description |
|
|
221
|
+
| --- | --- |
|
|
222
|
+
| `--role <name>` | Layer-1 role (`scribe` \| `coder` \| `reviewer` \| `planner`); required for `join` |
|
|
223
|
+
| `--id <id>` | Guest id (defaults to `<role>-g<n>`); colliding with a declared member or a joined guest exits 2 |
|
|
224
|
+
| `--session-id <sid>` | Host session id (defaults to `CLAUDE_CODE_SESSION_ID`) |
|
|
225
|
+
| `--pid <n>` | Process id to record |
|
|
226
|
+
| `--executor <name>` | Executor name to record (informational — a guest is never dispatched to) |
|
|
227
|
+
| `--json` | Machine-readable result |
|
|
228
|
+
|
|
229
|
+
A guest carrying no executor attestation is never a stage target: `spur agent run --spec <guest-id>`
|
|
230
|
+
is refused (R3), and `requiresCapabilities` stages therefore can never reach one.
|
|
231
|
+
|
|
232
|
+
## `wait --inbox <id>` - the guest pull primitive (G73 R4)
|
|
233
|
+
|
|
234
|
+
```bash
|
|
235
|
+
spur agent wait --inbox reviewer-g1 --timeout 540000 --json
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Returns `0` as soon as queued inbox work exists for the guest and `1` on timeout or when the id is
|
|
239
|
+
not a joined guest — never a silent hang. Every call heartbeats the guest's lease, so a session that
|
|
240
|
+
keeps looping stays joined; the timeout stays under the Bash tool's ~10-minute limit. The
|
|
241
|
+
`fleet-join` skill loops it (wait → `spur message inbox --agent <id>` → work → `spur message reply`),
|
|
242
|
+
and the `fleet-guest-stop` plugin hook delivers the same messages at a turn boundary on hosts that
|
|
243
|
+
support a blocking Stop hook.
|
|
244
|
+
|
|
170
245
|
## `doctor` - readiness check
|
|
171
246
|
|
|
172
247
|
```bash
|
|
@@ -193,6 +268,31 @@ stderr-clean and carries `capabilities`, `capabilityStale: {verifiedAgainst, det
|
|
|
193
268
|
normalized `availability` object, and a top-level `usage` per agent row instead. Exit `1` if any
|
|
194
269
|
checked agent is not ready.
|
|
195
270
|
|
|
271
|
+
## `trace` - one execution record's lineage (1076 R4, ADR-132)
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
spur agent trace <runId> # the run, its parents, its children
|
|
275
|
+
spur agent trace <rootId> --follow # poll until every run in the lineage is terminal
|
|
276
|
+
spur agent trace <runId> --json
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
`trace` reports ADR-132's execution record as one lineage. A node is a run: a workflow run comes from
|
|
280
|
+
the `runs` table, an agent run from `coordination_runs`, and the dispatch edge is
|
|
281
|
+
`coordination_runs.parent_run_id` — set from a workflow dispatch key (`<runId>/<state>`) or the
|
|
282
|
+
inherited `SPUR_RUN_ID`. `trace <runId>` first walks that edge UP to the root and then DOWN to the
|
|
283
|
+
leaves, so any id in the chain yields the whole picture. Each node carries its status, the agent
|
|
284
|
+
session ids recorded for it (`history_run_session`, exact rows only) and its durable stream under
|
|
285
|
+
`.spur/memory/runs/<runId>.md` when one was written. The stream is the record; the supervisor's ring
|
|
286
|
+
buffer is only a live view.
|
|
287
|
+
|
|
288
|
+
### Flags
|
|
289
|
+
|
|
290
|
+
| Flag | Purpose |
|
|
291
|
+
| ------ | --------- |
|
|
292
|
+
| `--follow` | Poll until every run in the lineage reaches a terminal status. |
|
|
293
|
+
| `--timeout <ms>` | `--follow` budget (default 600000). On expiry one checkpoint line is printed and the command exits 1; the runs continue. |
|
|
294
|
+
| `--json` | `{ rootRunId, nodes: [{ runId, kind, status, parentRunId, sessionIds, logPath }] }` — root first, depth-first. |
|
|
295
|
+
|
|
196
296
|
## `start` - start a supervised process
|
|
197
297
|
|
|
198
298
|
```bash
|
|
@@ -202,7 +302,9 @@ spur agent start worker-1 --json
|
|
|
202
302
|
|
|
203
303
|
Posts to the `spur self serve` supervisor API
|
|
204
304
|
(`POST /api/agents/:id/start`) and prints `started <id> (pid=<n>, status=<s>)`. Requires a
|
|
205
|
-
reachable `spur self serve`; `--server <url>`
|
|
305
|
+
reachable `spur self serve`; `--server <url>` targets it, defaulting to this project's registry
|
|
306
|
+
serve port (`http://localhost:<port>/api`, else `http://localhost:3000/api` when the project has no
|
|
307
|
+
live entry — 1088 R1). Exit `1`
|
|
206
308
|
when the server is unreachable or the start fails.
|
|
207
309
|
|
|
208
310
|
## `stop` - stop a supervised process
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: spur-cli-decision
|
|
3
|
+
description: "spur-cli noun reference: operate `spur decision` as the read/serve surface over the decision catalog - list registered decisions, show one served contract (parameters, criteria vocabulary, effective maker), run a decision to a served outcome (never writes a workflow resultFile), and status the catalog layers. Backed by @gobing-ai/ts-ai-decision 0.5.16 hub; offline by default via the built-in `typesafe` maker. Introduced by task 1093."
|
|
4
|
+
see_also:
|
|
5
|
+
- spur-cli
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# spur decision - decision catalog surface
|
|
9
|
+
|
|
10
|
+
`spur decision` exposes the **decision catalog** (ADR-117 trace discipline, task 1092) to agents
|
|
11
|
+
and scripts: discover which decisions exist, inspect one's contract, serve one outcome, and audit
|
|
12
|
+
catalog health. It is a thin transport over `DecisionService` (`packages/app`); the decision hub
|
|
13
|
+
lives in `@gobing-ai/ts-ai-decision`.
|
|
14
|
+
|
|
15
|
+
**Frozen at exactly four verbs** (`list`, `show`, `run`, `status`) by task 1093's design
|
|
16
|
+
(`docs/design/decision-catalog.md` §3.4) — do not invent additional subcommands.
|
|
17
|
+
|
|
18
|
+
## Catalog layers
|
|
19
|
+
|
|
20
|
+
Decisions resolve first-wins across three layers:
|
|
21
|
+
|
|
22
|
+
| Layer | Root | Notes |
|
|
23
|
+
| ----- | ---- | ----- |
|
|
24
|
+
| `project` | `<cwd>/.spur/decisions/*.yaml` | Highest precedence |
|
|
25
|
+
| `registered` | programmatic catalog registrations | Agents register in-process |
|
|
26
|
+
| `shared` | `decisions/` under the bundled config root | Shipped catalogs (`config/decisions/` in-repo) |
|
|
27
|
+
|
|
28
|
+
The bundled shared catalog ships `task-triage`, `failure-class`, and `review-failure-class` (all
|
|
29
|
+
`choice` type, param `wbs`). Duplicate ids across layers keep the higher layer; duplicates inside
|
|
30
|
+
one file are a load error.
|
|
31
|
+
|
|
32
|
+
**Config keys** (under `decisions:` in `config.global.yaml` or project config):
|
|
33
|
+
`paths` (extra catalog folders joined to the registered layer), `maker` (default DecisionMaker
|
|
34
|
+
for every decision point), `makers` (per-decision-id overrides). An unregistered maker name is a
|
|
35
|
+
config error, never a silent fallback (design §3.6).
|
|
36
|
+
|
|
37
|
+
## Verb map
|
|
38
|
+
|
|
39
|
+
| Verb | Purpose | Key flags |
|
|
40
|
+
| ---- | ------- | --------- |
|
|
41
|
+
| `list` | Every visible decision: id, type, layer, catalog file, description | `--layer <layer>`, `--json` |
|
|
42
|
+
| `show <id>` | One served contract: type, parameters, criteria, fallback, minConfidence, effective maker + source | `--json` |
|
|
43
|
+
| `run <id>` | Serve one outcome (closed vocabulary; **never writes a resultFile**) | `--param <pairs...>`, `--evidence <files...>`, `--maker <name>`, `--json` |
|
|
44
|
+
| `status` | Layer counts, registered makers, per-decision maker resolution, load errors | `--json` |
|
|
45
|
+
|
|
46
|
+
All verbs support `--json` (bare payload) and `--json-envelope` (`{ok, data|error}` envelope,
|
|
47
|
+
ADR-091). Failure output for `--json` commands uses the canonical error envelope only under
|
|
48
|
+
`--json-envelope`; plain `--json` keeps the human message on stderr.
|
|
49
|
+
|
|
50
|
+
**Exit codes:** `status` exits 1 on catalog load errors; `show` exits 1 on unknown id; `list` always
|
|
51
|
+
exits 0 (design §3.4 — load errors surface via `status`).
|
|
52
|
+
|
|
53
|
+
**Exit contract for `run` (design §3.4):** any backend-observed outcome — low confidence, model
|
|
54
|
+
error surfaced as fallback, declined — is still a **successful serve** (exit 0, envelope carries
|
|
55
|
+
`source`/`confidence` so callers can see why). Exit 1 is reserved for caller mistakes caught
|
|
56
|
+
**before** the backend: unknown id, unknown parameter, missing required parameter, type-invalid
|
|
57
|
+
parameter, unregistered `--maker`, unreadable evidence file.
|
|
58
|
+
|
|
59
|
+
## Parameters and evidence
|
|
60
|
+
|
|
61
|
+
`--param k=v` is repeatable and validated against the decision's declared parameters: values for
|
|
62
|
+
`number`/`boolean`/`json` params are parsed accordingly (`json` accepts inline JSON objects);
|
|
63
|
+
string/enum params pass through verbatim. A parameter with no `default` is **required** (hub
|
|
64
|
+
loader derives `required: !hasDefault`). Undeclared keys exit 1.
|
|
65
|
+
|
|
66
|
+
`--evidence <file>` is repeatable; each file is read, **redacted and bounded to
|
|
67
|
+
`DECIDE_EVIDENCE_MAX_CHARS` (2000 chars)** via the same `redactAndBound` used by workflow
|
|
68
|
+
agent execution. Evidence rides the hub's implicit `instructions` input parameter — it reaches the
|
|
69
|
+
maker through the rendered question, never as a raw input key (the hub rejects undeclared input
|
|
70
|
+
keys). An unreadable evidence file exits 1 before any backend call.
|
|
71
|
+
|
|
72
|
+
## Effective maker resolution
|
|
73
|
+
|
|
74
|
+
Makers resolve `flag → config-decision → config-default → catalog-decision → catalog-default`;
|
|
75
|
+
`decision show` reports the effective maker as `{name, source}` with that source precedence
|
|
76
|
+
(operator config outranks the shipped catalog because only the machine owner knows which backends
|
|
77
|
+
exist there). The final `catalog-default` maker is the hub built-in `typesafe` — decisions served
|
|
78
|
+
without a configured backend still produce deterministic closed-vocabulary outcomes, which is why
|
|
79
|
+
`run` is usable in tests and scripts with no model access.
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
spur decision list --json | jq -r '.[].id'
|
|
85
|
+
spur decision show task-triage # inspect contract before serving
|
|
86
|
+
spur decision run task-triage --param wbs=1093 --json
|
|
87
|
+
spur decision run failure-class --param wbs=1093 \
|
|
88
|
+
--evidence .spur/run/<runId>-test-output.txt # redacted evidence -> instructions
|
|
89
|
+
spur decision status --json | jq '.data.errors' # audit catalog health
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## References
|
|
93
|
+
|
|
94
|
+
- Design: `docs/design/decision-catalog.md` (§2 catalog authoring, §3.4 CLI surface)
|
|
95
|
+
- Hub: `@gobing-ai/ts-ai-decision` 0.5.16 (`DecisionService`, catalog schema, maker registry)
|
|
96
|
+
- App service: `packages/app/src/decision/decision-service.ts`
|
|
97
|
+
- Shipped catalogs: `config/decisions/*.yaml`
|
|
@@ -448,3 +448,25 @@ spur task path <wbs> [--folder] [--json]
|
|
|
448
448
|
spur task run-link <wbs> [--source <src>] [--run-id <id>] [--json]
|
|
449
449
|
spur task scaffold-tests <wbs> [--file <path>] [--folder] [--json]
|
|
450
450
|
```
|
|
451
|
+
|
|
452
|
+
**No `delete` verb — and that is the surface's shape, not an oversight.** The corpus is
|
|
453
|
+
append-only history: a task that is no longer wanted is a *decision*, and the lifecycle already owns
|
|
454
|
+
it. Use
|
|
455
|
+
|
|
456
|
+
```
|
|
457
|
+
spur task update <wbs> cancelled [--json] # the in-band terminal; the file stays as the record
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
Removing the file entirely is reserved for a task that should never have existed (a duplicate WBS, a
|
|
461
|
+
mis-scaffolded capture) — a **corpus repair**, not a lifecycle move. Do it as a tracked deletion so
|
|
462
|
+
the removal itself is auditable:
|
|
463
|
+
|
|
464
|
+
```
|
|
465
|
+
git rm docs/tasks<N>/<wbs>_<slug>.md
|
|
466
|
+
git commit -m "chore(corpus): drop <wbs> — <why it never existed>"
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Check for inbound references before deleting (`grep -rn "<wbs>" docs/ config/ plugins/`, plus any
|
|
470
|
+
`dependencies[]` edge via `spur task list --json`). The `deps remove` verb clears an edge. A deleted
|
|
471
|
+
WBS must not stay referenced: a dangling `dependencies[]` entry is a broken traceability edge, not a
|
|
472
|
+
harmless leftover.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spur-composer
|
|
3
|
-
description: "Select, compose and tune spur artifacts — tasks, features, rules, workflows and
|
|
3
|
+
description: "Select, compose and tune spur artifacts — tasks, features, rules, workflows, agent specs and project Board modules. Owns workflow catalog selection, the ephemeral→project→shared ladder, ADR-115 budgets, trace-driven rule tuning and applying accepted sp:spur-doctor proposals. Triggers: compose a workflow, tune a rule, add a Board module."
|
|
4
4
|
license: Apache-2.0
|
|
5
5
|
version: 1.0.0
|
|
6
6
|
metadata:
|
|
@@ -50,6 +50,7 @@ its own output and never runs a recurring loop — evaluation is the doctor's jo
|
|
|
50
50
|
| rule | The trace-driven tuning loop (§ Rule tuning loop) | [../spur-cli/references/rules.md](../spur-cli/references/rules.md) · [fine-tuning](../spur-cli/references/rules/fine-tuning.md) |
|
|
51
51
|
| workflow | Catalog selection, the composition ladder, and the ADR-115 budgets (§ below) | [../spur-cli/references/workflows.md](../spur-cli/references/workflows.md) · [operations](../spur-cli/references/workflows/operations.md) |
|
|
52
52
|
| agent spec | Read through `spur agent list --specs`; specs are materialized from the fleet declaration at serve start | [../spur-cli/references/agent.md](../spur-cli/references/agent.md) |
|
|
53
|
+
| board module | Declare under `bootstrap.modules`, gated by the serve preflight (§ Board module composition) | [../spur-cli/references/serve.md](../spur-cli/references/serve.md) |
|
|
53
54
|
|
|
54
55
|
Do not drive the planning→execution lifecycle from here — that is `sp:spur-dev`.
|
|
55
56
|
|
|
@@ -106,6 +107,31 @@ budgets:
|
|
|
106
107
|
A new model step in a shared workflow raises its `pipeline-budgets` `modelQueries`; that needs a
|
|
107
108
|
recorded decision before the shared step.
|
|
108
109
|
|
|
110
|
+
## Board module composition
|
|
111
|
+
|
|
112
|
+
A project adds its own Spur Board modules (ADR-128) by declaration, never by editing the installed
|
|
113
|
+
package. The contract — the `react`/`iframe` union, the `@gobing-ai/spur/board` type, the Vite
|
|
114
|
+
externals and the frame/trust limits — is owned by
|
|
115
|
+
[downstream Board modules §6](../../../../docs/design/downstream-board-modules.md#6-authoring-guide);
|
|
116
|
+
the user walkthrough is
|
|
117
|
+
[docs/help/how_to_customize_project_modules.md](../../../../docs/help/how_to_customize_project_modules.md).
|
|
118
|
+
Link them, never restate them.
|
|
119
|
+
|
|
120
|
+
| Step | Action |
|
|
121
|
+
| --- | --- |
|
|
122
|
+
| select | A built-in module or an existing declaration already covers the need → no new module. An app that already runs and permits framing → `type: iframe`. Otherwise → `type: react`. |
|
|
123
|
+
| compose | Add one declaration to the project `.spur/config.yaml` `bootstrap.modules` list. Stage it with `enabled: false` first: it is fully validated (id shape, duplicates, host/retired collisions) but loads no assets. |
|
|
124
|
+
| build | `react` only: the downstream project builds its own ESM entry plus CSS into the declared `directory`, externalizing exactly the `imports` keys of the installed `web/board-runtime.json`. Building the module is the project's code work, not composer's. |
|
|
125
|
+
| gate | Restart `spur self serve`; the preflight fails startup on any declaration, asset or runtime-version error. Confirm the module in `GET /api/board/modules` and open `/board/modules/<id>`. `spur self serve --json` is a dry port probe and does **not** run this preflight. |
|
|
126
|
+
| enable | Flip to `enabled: true`, restart and reload. There is no hot reload. |
|
|
127
|
+
|
|
128
|
+
- The module is project-owned: it never climbs a shared layer, and it needs no `build:bundle`.
|
|
129
|
+
- Declare only trusted code — importing a `react` entry evaluates it. Spur never starts or proxies an
|
|
130
|
+
`iframe` app, and a frame the child refuses stays blank by design.
|
|
131
|
+
- Changing a built-in module is internal Board work in the spur repository
|
|
132
|
+
([docs/help/how_to_add_a_new_ui_module.md](../../../../docs/help/how_to_add_a_new_ui_module.md)),
|
|
133
|
+
not a downstream declaration.
|
|
134
|
+
|
|
109
135
|
## Rule tuning loop
|
|
110
136
|
|
|
111
137
|
Start from trace evidence, never from a guess:
|
|
@@ -40,8 +40,9 @@ each would be scope creep for one-liner procedures.
|
|
|
40
40
|
|
|
41
41
|
> **`dev-review-session`** is not in this table. It is a thin `Skill()` wrapper over
|
|
42
42
|
> **`sp:session-review`** for an immediate, inline review of the active conversation. Report-only
|
|
43
|
-
> is the default; `--triage` applies pure-doc / one-to-two-line fixes and files
|
|
44
|
-
> findings as implement-ready tasks through the task CLI
|
|
43
|
+
> is the default; `--fix auto` (alias `--triage`) applies pure-doc / one-to-two-line fixes and files
|
|
44
|
+
> remaining actionable findings as implement-ready tasks through the task CLI; `--fix all` fixes
|
|
45
|
+
> every actionable finding inline and files none. It launches no workflow or agent and
|
|
45
46
|
> imports no history. Use it before the active session ends; use history-anatomy for ended sessions,
|
|
46
47
|
> cross-agent windows, trends, or quantitative forensics.
|
|
47
48
|
|
|
@@ -72,19 +73,19 @@ each would be scope creep for one-liner procedures.
|
|
|
72
73
|
| 2 | review | `dev-review` | `Skill()` | `sp:code-verification` (`review`) + `sp:functional-review` + `sp:code-improvement` | `[--tasks <selector> \| --feature <id>[,<id>] \| --scope <path>[,<path>]] [--agent <inline\|auto\|name>] [--focus <dims>] [--triage] [--worktree [<name>]] [--auto]` |
|
|
73
74
|
| 3 | verify | `dev-verify` | `Skill()` | `sp:code-verification` (`verify`) | `<wbs> [--agent <inline\|auto\|name>] [--fix <none\|blockers-first\|all>] [--focus <lens>] [--bdd] [--auto] [--force] [--next] [--skip-shippable]` |
|
|
74
75
|
| 3a | verifyall | `dev-verifyall` | `Skill()` → agent | `sp:spur-dev` (`verifyall`) | `--tasks <selector> [--feature <id>] [--agent <inline\|auto\|name>] [--fix <none\|blockers-first\|all>] [--focus <lens>] [--bdd] [--auto] [--force] [--next] [--json] [--skip-shippable] [--worktree [<name>]]` |
|
|
75
|
-
| 4 | run | `dev-run` | `Skill()` | `sp:spur-dev` (`run` / `implement`) | `<wbs> [--mode <full\|implement>] [--agent <inline\|auto\|name>] [--auto] [--next] [--wrap] [--continue] [--worktree [<name>]]` |
|
|
76
|
+
| 4 | run | `dev-run` | `Skill()` | `sp:spur-dev` (`run` / `implement`) | `<wbs> [--mode <full\|implement>] [--agent <inline\|auto\|name>] [--auto] [--next] [--wrap] [--continue] [--worktree [<name>]] [--no-summary]` |
|
|
76
77
|
| 5 | refine | `dev-refine` | `Skill()` | `sp:spur-dev` (`refine`) | `<wbs> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--next]` |
|
|
77
78
|
| 5a | refineall | `dev-refineall` | `Skill()` | `sp:spur-dev` (`refineall`) | `--feature <id> \| --tasks <selector> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--keep-going] [--status <s>] [--json] [--worktree [<name>]]` |
|
|
78
79
|
| 6 | plan | `dev-plan` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<description>" [--feature <id>] [--parent <feature-id>] [--skip-design] [--agent <inline\|auto\|name>] [--auto]` |
|
|
79
80
|
| 7 | docs | _(no thin wrapper)_ | `Skill()` | `sp:doc-evolve` | `"<change description>"` |
|
|
80
|
-
| 8 | changelog | `dev-changelog` | `inline` | git log + conventional-commit grouping | `[--since <ref>] [--until <ref>] [--version <ver>]`
|
|
81
|
+
| 8 | changelog | `dev-changelog` | `inline` | git log + conventional-commit grouping | `[<file>] [--since <ref>] [--until <ref>] [--version <ver>]` |
|
|
81
82
|
| 9 | gitmsg | `dev-gitmsg` | `inline` | bounded diff capture → concern grouping → conventional commit | `[--commit] [--squash] [--all] [--scope <path>]` |
|
|
82
83
|
| 10 | fixall | `dev-fixall` | `inline` | lint + test fix loop | `[<validation-command>] [--max-retry <n>] [--scope <path>] [--gate-log <path>] [--findings <anchors>]` |
|
|
83
84
|
| 11 | handover | `dev-handover` | `inline` | structured doc generation | `"<blocker description>"` |
|
|
84
85
|
| 11a | job-dump | `dev-job-dump` | `Skill()` | `sp:spur-dev` (`job-dump`) | `--file <path>` |
|
|
85
86
|
| 11b | job-resume | `dev-job-resume` | `Skill()` | `sp:spur-dev` (`job-resume`) | `--file <path>` |
|
|
86
87
|
| 12 | brainstorm | `dev-brainstorm` | `Skill()` | `sp:brainstorm` (`dev-brainstorm`) | `<topic> [--depth <basic\|detailed\|comprehensive>] [--options <n>] [--agent <inline\|auto\|name>] [--skip-discovery] [--wayfind] [--task [<feature-id>]] [--feature [<parent-id>]] [--next]` |
|
|
87
|
-
| 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>]]` |
|
|
88
|
+
| 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]` |
|
|
88
89
|
| 13a | parallel | `dev-parallel` | `Skill()` | `sp:parallel-execution` | `--tasks <selector> [--feature <id>] [--mode <fan-out\|review-panel\|investigation>] [--agent <inline\|auto\|name>] [--json]` |
|
|
89
90
|
| 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
|
|
90
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]` |
|
|
@@ -155,13 +156,28 @@ must not be changed without updating the backing skill.
|
|
|
155
156
|
### 4. run
|
|
156
157
|
|
|
157
158
|
- **Purpose:** Run a task through the execution pipeline (full) or execute a single pipeline step (implement).
|
|
158
|
-
- **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` resolves the selector uniformly (task 0687): omit ≡ explicit `--agent inline`; full mode reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess, with 0508 eligibility applying to the resolved-inline selector (eligible `agent.run` stages dispatch once to a native subagent, host fallback)). `--agent auto`, a name, or headless invocation launches the workflow subprocess. `--agent <inline|auto|name>` selects the execution surface (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips the HITL approve gate / confirmations and propagates down the `--next` chain. `--next` controls chaining only and never changes the mode; a pipeline implement stage must invoke `/sp:dev-run <wbs> --mode implement`. On implement success with `--next`, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`. On a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` section must state that explicitly and the verify verdict will record the scope. `--worktree [<name>]` runs the full pipeline inside an isolated git worktree (create or reuse; FF-merge on success, retain on failure) — the batch lifecycle in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) applied to a batch of one; rejected with `--mode implement`. `--wrap` hands off to `/sp:dev-wrap <wbs>` after the main step; the `--agent` selector is preserved into that handoff when supplied (omission remains omission), and the wrap hop reports its own trigger-3 subprocess override per the wrap contract.
|
|
159
|
+
- **Inputs:** `<wbs>` (required). `--mode <full|implement>` selects the execution mode. `implement` invokes `sp:code-implementation` inline by default. Interactive `full` resolves the selector uniformly (task 0687): omit ≡ explicit `--agent inline`; full mode reads `task-pipeline.yaml` and drives its actions/guards in the host session — host-controlled and non-subprocess, with 0508 eligibility applying to the resolved-inline selector (eligible `agent.run` stages dispatch once to a native subagent, host fallback)). `--agent auto`, a name, or headless invocation launches the workflow subprocess. `--agent <inline|auto|name>` selects the execution surface (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips the HITL approve gate / confirmations and propagates down the `--next` chain. `--next` controls chaining only and never changes the mode; a pipeline implement stage must invoke `/sp:dev-run <wbs> --mode implement`. On implement success with `--next`, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`. On a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` section must state that explicitly and the verify verdict will record the scope. `--worktree [<name>]` runs the full pipeline inside an isolated git worktree (create or reuse; FF-merge on success, retain on failure) — the batch lifecycle in [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) applied to a batch of one; rejected with `--mode implement`. `--no-summary` skips the [execution summary](#execution-summary) printed after a full-mode run. `--wrap` hands off to `/sp:dev-wrap <wbs>` after the main step; the `--agent` selector is preserved into that handoff when supplied (omission remains omission), and the wrap hop reports its own trigger-3 subprocess override per the wrap contract.
|
|
159
160
|
- **Backing:** `sp:spur-dev` skill — `run` operation for the full pipeline (the spine drives it); `sp:code-implementation` competency skill for the implement step (the spine dispatches to it).
|
|
160
161
|
- **Modes:**
|
|
161
162
|
- **`full`** (default): Drive the full pipeline — precheck → implement → test → review → approve(HITL) → verify → record → done. Interactive omit/inline uses [inline-pipeline-driver.md](inline-pipeline-driver.md) (host-controlled; eligible stages may use a native subagent); explicit/headless executor selection invokes `spur workflow run task-pipeline.yaml --vars '{"wbs":"<wbs>"}'` (with `profile: auto` when `--auto`). Both monitor/surface HITL and preserve the YAML gates. `--next` never changes this mode.
|
|
162
163
|
- **`implement`** (explicit `--mode implement` only): Execute only the implement step. Read the task's `## Requirements` / `## Design` / `## Plan`, write the code that satisfies them, author the `## Solution` change-map section (file:line + what/why per changed file) via `spur task update <wbs> --section Solution --from-file`. This is the implement step the pipeline calls — it is NOT the pipeline driver. With `--next`: on success, transition `todo → wip → testing` through the FSM (guards honored — no `--no-lifecycle`) + chain to `/sp:dev-verify <wbs> --auto --next`; on a guard failure, stop as review-pending. **Partial-deliverable rule:** if the task ships only part of its requirements (e.g. an R1/R2 split with the rest in a follow-up task), the `## Solution` and `## Review` sections MUST carry a `⚠️ PARTIAL` marker naming the deferred part and the follow-up WBS — see `plugins/sp/commands/dev-run.md` → "Section ownership".
|
|
163
164
|
- **Delegation:** `Skill(skill="sp:spur-dev", args="run-inline $ARGUMENTS")` for interactive full omit/inline (resolved-inline semantics, task 0687); `Skill(skill="sp:spur-dev", args="run $ARGUMENTS")` for explicit/headless full mode; `Skill(skill="sp:code-implementation", args="$ARGUMENTS")` for implement mode.
|
|
164
165
|
|
|
166
|
+
#### Execution summary
|
|
167
|
+
|
|
168
|
+
Default on for full-mode `dev-run` and `dev-runall`; `--no-summary` skips it. At command start record
|
|
169
|
+
`SINCE=$(date -u +%Y-%m-%dT%H:%M:%SZ)`. When the run settles (done, failed or paused), write each run's
|
|
170
|
+
`spur workflow progress <run-id> --json` to `$TMPDIR/<run-id>-progress.json` and print, verbatim under an
|
|
171
|
+
`### Execution summary` heading:
|
|
172
|
+
`node "$(superskill script path sp run-summary.mjs)" --since "$SINCE" --progress <file> --markdown`
|
|
173
|
+
(`dev-runall`: one `--progress <wbs>=<file>` per attempted run). One file renders one row per state visit;
|
|
174
|
+
several render one row per run; a `driver overhead` row carries host work outside every stage window so rows
|
|
175
|
+
sum to `Total` (omitted when parallel runs overlap). Columns: Stage | Status | Time | Wait | Tool calls |
|
|
176
|
+
Token (total / non-cached) — total counts every input class plus output; non-cached drops cache reads.
|
|
177
|
+
Stage windows with no host transcript record (subprocess executors) render `n/a`, never 0. The summary is
|
|
178
|
+
reporting only: a failure prints `Execution summary: n/a (<reason>)` and never changes the run's outcome.
|
|
179
|
+
Not printed for `--mode implement` (no workflow run).
|
|
180
|
+
|
|
165
181
|
### 5. refine
|
|
166
182
|
|
|
167
183
|
- **Purpose:** Refine a task's requirements via structured Q&A — clarify scope, elicit missing details, tighten acceptance criteria before execution. Optional **implement-ready** depth freezes Design/Requirements/Plan so another agent can implement without inventing design, and is also the path for **evaluating and correcting an existing task** — a review-triage filing, a stale backlog capture, or any task whose claims and proposed fixes may no longer hold.
|
|
@@ -429,7 +445,7 @@ gate commands, pending questions and approval requirements.>
|
|
|
429
445
|
### 13. runall
|
|
430
446
|
|
|
431
447
|
- **Purpose:** Run a batch of tasks through their pipelines in dependency-correct order — resolve a set, topo-sort, run each via `task-pipeline.yaml`, inspect verdicts, apply the failure policy, emit a batch report.
|
|
432
|
-
- **Inputs:** `--tasks <selector>` (required — explicit WBS list, status pseudo-list, `feature:<id>`, or `ready`). `--mode <sequential|parallel>` (default `sequential`; `parallel` fans out a proven-independent subset per `execution-batch.md` § Parallel Execution). `--keep-going` skips a failed task's in-batch dependents and continues independents (default halts on first failure). `--auto` sets `profile=auto` on each per-task run (skips the HITL approve gate). `--feature <id>` is sugar for `--tasks feature:<id>`; when the effective selector is feature-derived, the batch runs `spur feature check <id> --strict --json` **once** before task resolution — a non-zero strict check aborts with verdict `aborted`, zero attempted tasks, and the structured findings (task 0510 R2); scoped: `L4.scenario-unverified` (expected pre-run state of any not-yet-run feature) is reported verbatim but does not abort — any other strict error aborts. Explicit WBS/status/`ready` selectors add no feature check. The orchestrator loop itself continues in this session; interactive sequential omit/`inline` uses the host driver — host-controlled with eligible `agent.run` stages dispatching once to a native subagent and host fallback (task 0508) — while `--agent auto`/a name, parallel mode, and headless invocation keep the isolated per-task workflow subprocess boundary. `--agent <inline|auto|name>` pins the executor for the per-task stages (see [SSOT](cross-cutting.md#inline-default-execution-surface)); the value crosses into per-task `vars.agent`, not the orchestrator. `--json` emits the report as JSON. `--wrap` triggers `wrapup-pipeline.yaml` **once for the batch** over the `done` subset only (execution-batch.md Step 6: `vars.feature` only when every frozen task is `done`/`cancelled`; empty done subset skips with a reason), `--next` chains each task to terminal status then runs the same batch-once wrap, `--continue` resumes from checkpoint.
|
|
448
|
+
- **Inputs:** `--tasks <selector>` (required — explicit WBS list, status pseudo-list, `feature:<id>`, or `ready`). `--mode <sequential|parallel>` (default `sequential`; `parallel` fans out a proven-independent subset per `execution-batch.md` § Parallel Execution). `--keep-going` skips a failed task's in-batch dependents and continues independents (default halts on first failure). `--auto` sets `profile=auto` on each per-task run (skips the HITL approve gate). `--feature <id>` is sugar for `--tasks feature:<id>`; when the effective selector is feature-derived, the batch runs `spur feature check <id> --strict --json` **once** before task resolution — a non-zero strict check aborts with verdict `aborted`, zero attempted tasks, and the structured findings (task 0510 R2); scoped: `L4.scenario-unverified` (expected pre-run state of any not-yet-run feature) is reported verbatim but does not abort — any other strict error aborts. Explicit WBS/status/`ready` selectors add no feature check. The orchestrator loop itself continues in this session; interactive sequential omit/`inline` uses the host driver — host-controlled with eligible `agent.run` stages dispatching once to a native subagent and host fallback (task 0508) — while `--agent auto`/a name, parallel mode, and headless invocation keep the isolated per-task workflow subprocess boundary. `--agent <inline|auto|name>` pins the executor for the per-task stages (see [SSOT](cross-cutting.md#inline-default-execution-surface)); the value crosses into per-task `vars.agent`, not the orchestrator. `--json` emits the report as JSON. `--wrap` triggers `wrapup-pipeline.yaml` **once for the batch** over the `done` subset only (execution-batch.md Step 6: `vars.feature` only when every frozen task is `done`/`cancelled`; empty done subset skips with a reason), `--next` chains each task to terminal status then runs the same batch-once wrap, `--continue` resumes from checkpoint. `--no-summary` skips the [execution summary](#execution-summary) printed after the batch report (one row per attempted run).
|
|
433
449
|
- **Three orthogonal axes (do not confuse):** `--keep-going` = batch failure policy (halt vs skip dependents); `--continue` = resume from checkpoint against the batch's original frozen identity (execution-batch.md § Batch continuation, task 0919); `--next` = per-task lifecycle chaining (advance status on a verdict — `dev-verify`/`dev-verifyall` only). `routing-table.md` offers `--continue` and `--next` as competing options for the same situation only when the batch was interrupted mid-run; otherwise they address different problems.
|
|
434
450
|
- **Backing:** `sp:spur-dev` skill, `runall` operation → delegates the driver loop to the **`sp:super-planner`** agent (the batch orchestrator).
|
|
435
451
|
- **Behavior:** The orchestrator reads [execution-batch.md](execution-batch.md) and drives: resolve selector → freeze set → topo-sort by `dependencies[]` (Kahn, WBS-ascending tie-break; cycle aborts) → resolve out-of-set deps by status (done → allow, else → block subtree) → run each task via `spur workflow run task-pipeline.yaml --async` + `spur workflow trace` polling → inspect terminal state + `.spur/run/<wbs>-verdict.json` → stop-the-batch default or `--keep-going` subtree skip → emit batch report. Per-task pipeline is invoked **verbatim** — no new FSM, no step edits. `--auto`/`--agent` are the only flags that cross the orchestrator→pipeline boundary (both into per-task `--vars`).
|
|
@@ -519,7 +535,7 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
519
535
|
### 8. changelog
|
|
520
536
|
|
|
521
537
|
- **Purpose:** Generate a structured changelog from git commits between two refs.
|
|
522
|
-
- **Inputs:** `--since <ref>` (default: last tag), `--until <ref>` (default: `HEAD`), `--version <ver>` (default: auto-detect from latest tag).
|
|
538
|
+
- **Inputs:** `<file>` (optional positional, e.g. `CHANGELOG.md`; default: stdout), `--since <ref>` (default: last tag), `--until <ref>` (default: `HEAD`), `--version <ver>` (default: auto-detect from latest tag).
|
|
523
539
|
- **Backing:** `inline` — git log + conventional-commit grouping.
|
|
524
540
|
- **Behavior:**
|
|
525
541
|
1. Resolve `--since`: if not given, use the most recent tag (`git describe --tags --abbrev=0`). If no tags exist, use the repo root commit.
|
|
@@ -529,14 +545,10 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
529
545
|
5. Format as markdown:
|
|
530
546
|
- `keepachangelog` (default): `## [<version>] - <date>` header, then category headings per the keepachangelog convention — `### Added` / `### Fixed` / `### Changed` / `### Removed` / `### Other` — mapped from conventional-commit types.
|
|
531
547
|
- `simple`: flat bulleted list grouped by type heading (`### feat`, `### fix`, …).
|
|
532
|
-
6.
|
|
533
|
-
7.
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
- **Header** — `[<version>] - <date>` matches the resolved `--version` (or detected tag) and the header date.
|
|
537
|
-
- **Citations** — a bullet that asserts API/library behavior or a version/dependency claim beyond its commit summary carries a source citation: an official docs URL, or a local `path:line` verified against the installed manifest/lockfile (`package.json`, lockfile, or the cited file). A commit hash is provenance for what shipped, not a citation for what the API/library does; an uncited claim is flagged inline on the bullet (`(uncited: <claim>)`) rather than silently dropped.
|
|
538
|
-
- **HIGH** — all checks pass with every API/library claim cited. **MEDIUM** — coverage intact but mapping or header deviates, or at least one API/library claim lacks a source citation. **LOW** — coverage broken (missing/duplicate hashes), the range cannot be reconciled against git, or a citation fails verification (dead URL, or a `path:line` that does not support the claim).
|
|
539
|
-
- **Invariants:** Never mutates `CHANGELOG.md` directly — the command surface is stdout-only; writing it to a file (e.g. appending to `CHANGELOG.md`) is the operator's redirect choice, never the command's.
|
|
548
|
+
6. Emit: with `<file>`, insert the section into that file above its newest `## [` release heading (or after its title when none exists) and touch nothing else in the file; without `<file>`, print it to stdout.
|
|
549
|
+
7. Self-check before emitting: every short hash in `<since>..<until>` appears exactly once. Fix any miss or duplicate; do not report a verdict.
|
|
550
|
+
8. Reply in at most three lines: the range, bullet counts per category, and the output target.
|
|
551
|
+
- **Invariants:** Output is derived from `git log` only. No quality gates — no lint/test/`spur-check`/rule runs, citation or manifest lookups, confidence ratings, or edits beyond the one inserted section. The file is left uncommitted.
|
|
540
552
|
|
|
541
553
|
### 9. gitmsg
|
|
542
554
|
|
|
@@ -424,7 +424,8 @@ one or more tasks sit on an operator question (escalate, 0933) or an approval ga
|
|
|
424
424
|
(cycle / unknown selector) — emit a structured report. The report is the orchestrator's sole
|
|
425
425
|
output; it does not mutate the corpus (the pipeline's `record` step already wrote per-task
|
|
426
426
|
results). A `paused` task is non-terminal — the report marks it `paused` (never `done`) and names
|
|
427
|
-
the resume command.
|
|
427
|
+
the resume command. Unless `--no-summary`, follow it with the measured
|
|
428
|
+
[execution summary](dev-operations.md#execution-summary), one `--progress <wbs>=<file>` per attempted run.
|
|
428
429
|
|
|
429
430
|
```
|
|
430
431
|
## Batch Report — <selector>
|
|
@@ -744,6 +745,34 @@ existing batch loop (Steps 1–5) with the worktree as process cwd. `spur workfl
|
|
|
744
745
|
from the process (`apps/cli/src/commands/workflow.ts:124`), so no CLI change is needed — `cd` into
|
|
745
746
|
the worktree directory before launching the loop.
|
|
746
747
|
|
|
748
|
+
#### Evidence staging before the first task (create and reuse mode)
|
|
749
|
+
|
|
750
|
+
`.spur/memory/evidence/` and `.spur/run/` are **untracked per-tree** planes, so a fresh worktree
|
|
751
|
+
starts with neither while the invoking tree holds every earlier task's verdict. Both
|
|
752
|
+
`preflightFeature` (`plugins/sp/scripts/wrapup-steps.ts` → `spur feature check --strict --as done`
|
|
753
|
+
with cwd = the worktree) and `runMetrics` resolve verdict artifacts relative to the tree they run
|
|
754
|
+
in, so without this step an already-`done` linked task reads as missing evidence
|
|
755
|
+
(`L4.evidence-not-recoverable`, `L4.scenario-unverified`) and its metrics row degrades to
|
|
756
|
+
`UNKNOWN`.
|
|
757
|
+
|
|
758
|
+
Stage the invoking tree's verdict artifacts into the worktree **before the first task runs** —
|
|
759
|
+
from the invoking tree (cwd), with `$WT` the resolved absolute worktree path. `cp -n` is
|
|
760
|
+
load-bearing: reuse mode may be re-entering a tree that already owns divergence-checked evidence,
|
|
761
|
+
and this step must never clobber it. Only `*-verdict.json` travels in each direction; receipts and
|
|
762
|
+
other scratch artifacts are not staged, so WT-4a's identity classification is unaffected.
|
|
763
|
+
|
|
764
|
+
```bash
|
|
765
|
+
# Run from the INVOKING tree; $WT is the worktree (create- or reuse-mode) absolute path.
|
|
766
|
+
for d in .spur/memory/evidence .spur/run; do
|
|
767
|
+
mkdir -p "$WT/$d"
|
|
768
|
+
for f in "$d"/*-verdict.json; do [ -f "$f" ] && cp -n "$f" "$WT/$d/"; done
|
|
769
|
+
done
|
|
770
|
+
```
|
|
771
|
+
|
|
772
|
+
The position relative to the dependency install is immaterial (this is a plain copy into an
|
|
773
|
+
untracked plane); the position relative to the **first task** is not — a preflight or metrics read
|
|
774
|
+
that runs before this step sees an empty evidence plane.
|
|
775
|
+
|
|
747
776
|
#### Name resolution (`--worktree <name>`)
|
|
748
777
|
|
|
749
778
|
<a id="name-resolution---worktree-name"></a>
|
|
@@ -988,8 +1017,10 @@ if [ -n "$FINAL" ]; then
|
|
|
988
1017
|
fi # NO prune/remove/branch delete
|
|
989
1018
|
# Pre-removal evidence assertion (task 1067 R1, E71 enforcement): refuse to remove the
|
|
990
1019
|
# worktree while it still owns evidence absent from (or divergent from) the invoking tree —
|
|
991
|
-
# the D63 landing skipped persist-out and silently lost verdicts + receipts.
|
|
992
|
-
#
|
|
1020
|
+
# the D63 landing skipped persist-out and silently lost verdicts + receipts. WBSes ride the
|
|
1021
|
+
# same TASK_FILE_ARGS resolved for WT-4a; run ids are a separate `--run-id <id>` per run and
|
|
1022
|
+
# belong to THIS check only — `inline-run-setup.ts --persist-out` accepts `--from` and
|
|
1023
|
+
# `--task-file` alone and exits 2 on `--run-id` (1089 driver note).
|
|
993
1024
|
CHECK_SCRIPT=plugins/sp/scripts/persist-out-check.ts; [ -f config/plugin-scripts.json -a -f "$CHECK_SCRIPT" ] || CHECK_SCRIPT="$(superskill script path sp persist-out-check.mjs 2>/dev/null)"
|
|
994
1025
|
bun "$CHECK_SCRIPT" --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
|
|
995
1026
|
|| { echo "WT-4 halt: worktree evidence would be abandoned - persist-out missing or stale (WT-5)" >&2; write_marker retained; exit 1; }
|
|
@@ -1179,16 +1210,42 @@ ordered divergence recipe, run by the operator — the driver never merges, reba
|
|
|
1179
1210
|
conflicts itself. Other halt causes (task failure, HITL pause) keep the hint as printed:
|
|
1180
1211
|
|
|
1181
1212
|
```
|
|
1182
|
-
# 1.
|
|
1183
|
-
git
|
|
1213
|
+
# 1. decide whether conflicts need resolving FIRST, because that decides the merge form:
|
|
1214
|
+
# git merge-tree $(git merge-base <base-ref> <branch>) <base-ref> <branch> | grep -q '^<<<<<<<' \
|
|
1215
|
+
# && echo "conflicts — use the two-phase form below" || echo "clean — use the atomic form"
|
|
1216
|
+
#
|
|
1217
|
+
# 1a. CLEAN (the common case): integrate as ONE atomic merge commit. Never a rebase — task
|
|
1218
|
+
# evidence cites the branch's commit SHAs. This is the file's sanctioned merge commit.
|
|
1219
|
+
# Do NOT split this into a two-phase merge + commit when nothing needs resolving: that spans
|
|
1220
|
+
# the whole gate run, and anything in that window that touches the repository's git state
|
|
1221
|
+
# (a test, a hook, a tool invocation) discards MERGE_HEAD silently. The failure is invisible
|
|
1222
|
+
# until the history is read — it produced a single-parent commit in run ada5a36c (task 1090),
|
|
1223
|
+
# which then needed an extra empty ancestry merge to repair. A cheap probe could not
|
|
1224
|
+
# reproduce it (MERGE_HEAD survived `bun run lint`, the rule preset and a test-file subset),
|
|
1225
|
+
# so the cross-command dependency is removed rather than diagnosed.
|
|
1226
|
+
git checkout <base-ref> && git merge --no-ff -m "<prepared message>" <branch>
|
|
1227
|
+
|
|
1228
|
+
# 1b. CONFLICTING: the two-phase form is correct here, because the resolution work must land
|
|
1229
|
+
# between the merge and the commit. Everything in steps 2–4 belongs to this branch only.
|
|
1230
|
+
# A divergent branch cannot fast-forward, so the merge flag from 1a is not needed here.
|
|
1231
|
+
# git checkout <base-ref> && git merge --no-commit <branch>
|
|
1184
1232
|
# 2. resolve source conflicts by hand; generated files are then regenerated with the project's
|
|
1185
1233
|
# generator, never hand-merged
|
|
1186
1234
|
# (this repo: bun run build:plugin-lib && bun run --filter @gobing-ai/spur build:bundle)
|
|
1187
1235
|
# 3. stage every resolved path and regenerated bundle (git add …) — an unmerged or
|
|
1188
1236
|
# unstaged path makes step 5 abort
|
|
1189
1237
|
# 4. run qualityGateCmd once, after ALL conflicts are resolved
|
|
1190
|
-
# 5. commit the merge with the prepared message file
|
|
1191
|
-
git commit -F <message-file>
|
|
1238
|
+
# 5. commit the merge with the prepared message file — CHECK THE PARENTS FIRST (step 5a)
|
|
1239
|
+
# git commit -F <message-file>
|
|
1240
|
+
#
|
|
1241
|
+
# 5a. ALWAYS verify the merge really is a merge before treating it as landed:
|
|
1242
|
+
# git log -1 --format='%h parents=%p' # must print TWO parents
|
|
1243
|
+
# One parent means MERGE_HEAD was lost somewhere between steps 1 and 5 — the content is on
|
|
1244
|
+
# the base ref but the branch is not an ancestor, so `git branch -d` would refuse and the
|
|
1245
|
+
# retained worktree's provenance would not be readable from the base ref. Recover by
|
|
1246
|
+
# re-running step 1a against the already-landed tree (a content-free merge commit restores
|
|
1247
|
+
# ancestry).
|
|
1248
|
+
|
|
1192
1249
|
# 6. persist evidence out (WT-4a), then WT-4b/4c cleanup, and set the marker to merged
|
|
1193
1250
|
inline-run-setup --persist-out --from <worktree> --task-file …
|
|
1194
1251
|
```
|
|
@@ -163,6 +163,20 @@ The pipeline (`kind: state-machine`) runs the work loop:
|
|
|
163
163
|
precheck → implement → test [→ test-fix → test-recheck] → review → approve(HITL) → verify → record → done
|
|
164
164
|
```
|
|
165
165
|
|
|
166
|
+
Two branches reach the bounded repair hop, not one (session finding after 1088):
|
|
167
|
+
|
|
168
|
+
- `test`/`test-recheck` FAIL → `test-fail-triage` (0943's failure-class router) → `test-fix`;
|
|
169
|
+
- `review` without a `Verdict: PASS` in its answer → `review-fail-triage` (the review-side twin) →
|
|
170
|
+
`test-fix`, bounded by the SAME `qualityGateMaxFixAttempts` counter. A FAIL review therefore has
|
|
171
|
+
an on-graph, bounded path to remediation instead of dead-ending the run, and the fix re-enters
|
|
172
|
+
quality → review → verify on a freshly captured proof digest.
|
|
173
|
+
|
|
174
|
+
The completion gate also reads the verifier's confidence (session finding after 1088): the verify
|
|
175
|
+
answer's `Confidence: HIGH|MEDIUM|LOW` line reaches the verdict artifact, and both `verify → record`
|
|
176
|
+
and `record → done` refuse a `LOW` (or absent) level unless the run was launched with
|
|
177
|
+
`--vars '{"ackLowConfidence":"true"}'`. A PASS the verifier would not stand behind is an operator
|
|
178
|
+
risk decision, so it takes an operator acknowledgement — not a silent certification.
|
|
179
|
+
|
|
166
180
|
Agentic steps use the pure slash inputs declared by each YAML `agent.run` action (ADR-043). The
|
|
167
181
|
workflow engine dispatches them; the interactive driver invokes their backing skills in-session.
|
|
168
182
|
The `test` hop is primarily
|