@gobing-ai/spur 0.3.98 → 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 +15 -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/templates/task/brainstorm.md +1 -1
- package/config/templates/task/feature-impl.md +1 -1
- package/config/templates/task/issue.md +1 -1
- package/config/templates/task/meta.md +1 -1
- package/config/templates/task/review.md +1 -1
- package/config/templates/task/standard.md +1 -1
- 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 +68 -10
- package/plugins/sp/lib/inline-run.generated.d.mts +1 -0
- package/plugins/sp/lib/inline-run.generated.mjs +83 -20
- package/plugins/sp/lib/residual-scan.generated.d.mts +5 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +45 -5
- 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/persist-out-check.mjs +164 -0
- package/plugins/sp/scripts/persist-out-check.ts +185 -0
- package/plugins/sp/scripts/residual-scan.mjs +52 -15
- package/plugins/sp/scripts/residual-scan.ts +11 -11
- 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/scripts/task-diffstat.mjs +17 -5
- package/plugins/sp/scripts/task-diffstat.ts +25 -4
- package/plugins/sp/skills/code-verification/SKILL.md +8 -0
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +17 -1
- 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/ac-style-guide.md +40 -7
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +28 -16
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +278 -42
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +17 -1
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -4
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +139 -10
- 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 +8856 -2564
- package/web/_astro/BoardApp.Bzr4OXrW.js +324 -0
- package/web/_astro/{BoardApp.Dflq5a8x.js → BoardApp.VeoxxLBy.js} +1 -1
- package/web/_astro/{TaskDetail.CK7oXddP.js → TaskDetail.LAyR_Pvn.js} +1 -1
- package/web/_astro/{arc.BIqkDATc.js → arc.f4e0OYLg.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.D0B3SvWv.js → architectureDiagram-3BPJPVTR.SSIvxi-Z.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.C2YyKQmL.js → blockDiagram-GPEHLZMM.DSXIHOFf.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.DmtTYrt_.js → c4Diagram-AAUBKEIU.DG9Ubu85.js} +1 -1
- package/web/_astro/channel.CUFdtWhm.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.HsRXbh7l.js → chunk-2J33WTMH.C7QU88Qh.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.obE_P-R3.js → chunk-4BX2VUAB.26Z94xBj.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.CjWTF5mz.js → chunk-55IACEB6.DYRiBkqw.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DMo799Ax.js → chunk-727SXJPM.D5Vfv5pe.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.-cE6JOKZ.js → chunk-AQP2D5EJ.DXHV47bN.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.D6Rw6HIk.js → chunk-FMBD7UC4.D1suAfOs.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.d5rBD73Z.js → chunk-ND2GUHAM.D6BmdHEY.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.r3ox40Q3.js → chunk-QZHKN3VN.rnPU-Ak-.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CGGbvjjJ.js → classDiagram-4FO5ZUOK.CvEfXIo1.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CGGbvjjJ.js → classDiagram-v2-Q7XG4LA2.CvEfXIo1.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CmKtPd4l.js → cose-bilkent-S5V4N54A.DkMisLSm.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.QNg5GXs8.js → cynefin-OW5HDTMX.C77Q-pF6.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.B1SabsjC.js → dagre-BM42HDAG.YaV8ce27.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.RtDbNj89.js → diagram-2AECGRRQ.CUyGEKge.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.IzZpuM-Y.js → diagram-5GNKFQAL.CRgd8dgj.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DPTF_fgv.js → diagram-KO2AKTUF.Bmzys9hq.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CCyza39P.js → diagram-LMA3HP47.BvmJY4Lw.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BXAi8zcZ.js → diagram-OG6HWLK6.0hEhD30d.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DUDX7_MR.js → erDiagram-TEJ5UH35.Bjt8lrnm.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.C63QTh3N.js → flowDiagram-I6XJVG4X.CJZTjRDV.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.CnlZJSwG.js → ganttDiagram-6RSMTGT7._VqoVafc.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CTqIrOPC.js → gitGraphDiagram-PVQCEYII.CEDb7tVo.js} +1 -1
- package/web/_astro/index.Dg_3bOaJ.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BYMiDFh9.js → infoDiagram-5YYISTIA.CgwifPpK.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CRiQmI-z.js → ishikawaDiagram-YF4QCWOH.B99wx2J1.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.YQgWBsEU.js → journeyDiagram-JHISSGLW.2hRwJbEo.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.1ECnrrK1.js → kanban-definition-UN3LZRKU.BahJkKNC.js} +1 -1
- package/web/_astro/{linear.DzVA71yT.js → linear.DguUUe6V.js} +1 -1
- package/web/_astro/{mermaid.core.BK7P2Sr_.js → mermaid.core.CvgfmqVr.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BvXyWZK7.js → mindmap-definition-RKZ34NQL.B3qEA3x9.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.BiXk1TW3.js → pieDiagram-4H26LBE5.FlWA-_ru.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.krvBH8lh.js → quadrantDiagram-W4KKPZXB.9bVn35X7.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.ih1gL25c.js → requirementDiagram-4Y6WPE33.ChykIhct.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CpUxkv4u.js → sankeyDiagram-5OEKKPKP.Z2Y3hkYb.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.clAfeN7y.js → sequenceDiagram-3UESZ5HK.J7JsHWDt.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.DpXWb8-m.js → stateDiagram-AJRCARHV.DAPbiCSS.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.ClZb9GIH.js → stateDiagram-v2-BHNVJYJU.D2jvSi0q.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.BLcX5dh7.js → timeline-definition-PNZ67QCA.psW2ci29.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.oGgpKMpx.js → vennDiagram-CIIHVFJN.DlILYVmg.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.BPHtMtt-.js → wardleyDiagram-YWT4CUSO.xNWwLJnn.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.BNZOPYSk.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.CYbe-OTb.js +0 -322
- package/web/_astro/channel.JuKVyuhZ.js +0 -1
- package/web/_astro/index.CbNz17Sx.css +0 -1
|
@@ -19,13 +19,17 @@
|
|
|
19
19
|
* other probe problem writes `sensitive: true` (and exits 0) so the lane degrades to
|
|
20
20
|
* safety, never to fast.
|
|
21
21
|
*
|
|
22
|
+
* The artifact is written atomically (tmp + rename), so redirecting stdout onto the artifact path
|
|
23
|
+
* cannot corrupt it — the rename swaps in the real JSON after the shell's redirect truncates the
|
|
24
|
+
* old inode (1067 R3); that redirect is still refused with a nonzero exit. Normal output is silent.
|
|
25
|
+
*
|
|
22
26
|
* Node-builtin imports only; the git lookup is a spawnable `GitRunner` so tests can fake
|
|
23
27
|
* it; `main(argv, env, options)` is the same injection point the workflow wrapper and its
|
|
24
28
|
* node twin exercise. Only an empty `wbs` (mis-invocation) exits nonzero.
|
|
25
29
|
*/
|
|
26
30
|
|
|
27
31
|
import { spawnSync } from 'node:child_process';
|
|
28
|
-
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
32
|
+
import { fstatSync, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
29
33
|
import { join } from 'node:path';
|
|
30
34
|
import { getEnvVars } from '../lib/env';
|
|
31
35
|
|
|
@@ -166,10 +170,18 @@ export function runDiffstat(
|
|
|
166
170
|
const failSafe = (reason: string): DiffstatResult => {
|
|
167
171
|
process.stderr.write(`task-diffstat: ${reason} — failing safe (sensitive)\n`);
|
|
168
172
|
const row: Diffstat = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
|
|
169
|
-
|
|
173
|
+
writeArtifact(abs(relResult), row);
|
|
170
174
|
return { ...row, resultFile: relResult, exitCode: 0 };
|
|
171
175
|
};
|
|
172
176
|
|
|
177
|
+
let stdoutIsArtifact = false; // 1067 R3: stdout redirected onto the artifact itself clobbered it once
|
|
178
|
+
try {
|
|
179
|
+
const [out, art] = [fstatSync(1), statSync(abs(relResult))];
|
|
180
|
+
stdoutIsArtifact = out.dev === art.dev && out.ino === art.ino;
|
|
181
|
+
} catch {
|
|
182
|
+
/* artifact absent or stdout not a regular file — nothing to compare */
|
|
183
|
+
}
|
|
184
|
+
|
|
173
185
|
let base = '';
|
|
174
186
|
try {
|
|
175
187
|
base = readFileSync(abs(join(relRun, `${wbs}-base.sha`)), 'utf8').trim();
|
|
@@ -203,8 +215,17 @@ export function runDiffstat(
|
|
|
203
215
|
const all = [...paths].filter((p) => p.length > 0).sort();
|
|
204
216
|
const sensitive = all.some((p) => sensitiveReasonForPath(p) !== null);
|
|
205
217
|
const row: Diffstat = { files: all.length, insertions, deletions, paths: all, sensitive };
|
|
206
|
-
|
|
207
|
-
|
|
218
|
+
writeArtifact(abs(relResult), row);
|
|
219
|
+
if (stdoutIsArtifact)
|
|
220
|
+
process.stderr.write('task-diffstat: stdout redirected onto the artifact — kept; redirect stdout elsewhere\n');
|
|
221
|
+
return { ...row, resultFile: relResult, exitCode: stdoutIsArtifact ? 1 : 0 };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** Atomic artifact write (tmp + rename): a stdout redirect onto the path cannot interleave (1067 R3). */
|
|
225
|
+
function writeArtifact(absPath: string, row: Diffstat): void {
|
|
226
|
+
const tmp = `${absPath}.tmp`;
|
|
227
|
+
writeFileSync(tmp, `${JSON.stringify(row)}\n`);
|
|
228
|
+
renameSync(tmp, absPath);
|
|
208
229
|
}
|
|
209
230
|
|
|
210
231
|
export const TASK_DIFFSTAT_USAGE = 'usage: task-diffstat (env: wbs)';
|
|
@@ -315,6 +315,11 @@ node "$RESIDUAL" fold <wbs>
|
|
|
315
315
|
> for each dropped MET-matched scenario key and when the new rows match no feature scenario —
|
|
316
316
|
> treat those warnings as a re-key instruction, not noise.
|
|
317
317
|
|
|
318
|
+
> **Keying rule for feature-linked tasks (1086/1089 re-audits).** Key AC rows by the exact feature
|
|
319
|
+
> scenario title (`R1 — …`): feature `Scenario:` titles are declared identities and satisfy L4
|
|
320
|
+
> scenario matching directly. Bare `AC1` fails `ac-identity` on Gherkin AC sections, and checklist
|
|
321
|
+
> labels truncate at the first colon (a `(req: …)` suffix), so the task's own AC title may not resolve.
|
|
322
|
+
|
|
318
323
|
> **Do not write `## Review` directly, ever.** The `## Review` section is owned by the
|
|
319
324
|
> `review` coordinator (`/sp:dev-review` → `sp:super-reviewer`), which merges
|
|
320
325
|
> `functional-review` + `code-verification` review mode + `code-improvement` fragments. The
|
|
@@ -336,6 +341,7 @@ Verdict: PASS (or PARTIAL / FAIL)
|
|
|
336
341
|
|
|
337
342
|
```markdown
|
|
338
343
|
Verdict: PASS
|
|
344
|
+
Confidence: HIGH
|
|
339
345
|
|
|
340
346
|
### Per-Requirement Traceability
|
|
341
347
|
| Req | Status | Evidence |
|
|
@@ -354,6 +360,8 @@ Verdict: PASS
|
|
|
354
360
|
```
|
|
355
361
|
|
|
356
362
|
The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant — `Status` in column 2 — and no extra columns between Req and Status). The Acceptance Criteria table MUST use `| AC | Status | Evidence Type | Evidence |`.
|
|
363
|
+
The answer MUST carry exactly one `Confidence: HIGH|MEDIUM|LOW` line (task 1068): the verifier's stated confidence in the verdict — HIGH = fresh executable evidence + re-read anchors this run; MEDIUM = partial reliance on prior evidence, external/delegated outcomes, or an unstable tree; LOW = key evidence not re-executed. A missing or out-of-vocabulary line fails lint (`confidence-missing` / `confidence-value`) and blocks derivation; the value lands in the verdict artifact's `confidence` field and the recorded `## Testing` section.
|
|
364
|
+
The answer MUST cite a source for external API/library claims in Requirement/AC evidence cells: a cell asserting a scoped-package (`@scope/name`), `node_modules/`, semver-version (`x.y.z`), or backticked function-call (`` `name(args)` ``) claim is rejected with `evidence-citation` unless the SAME cell carries a citation — a repo `path:line(-end)` (backticked or bare), the external named-origin form (@origin `path` line N), or a URL. Cross-cell references ("receipt above") do not count; marker-free receipts ("bun test 398 pass / 0 fail") need no citation.
|
|
357
365
|
**MUST NOT:** place a `Severity` column between `Req` and `Status` in the authoring contract.
|
|
358
366
|
The parser is tolerant of these variants (defense-in-depth), but the authoring contract is
|
|
359
367
|
canonical.
|
|
@@ -115,7 +115,9 @@ AC ids that do not resolve to one accepted identity — a task AC checklist labe
|
|
|
115
115
|
(exact/bare title, `Scenario:` prefix, bracket tags, `AC-N`); paraphrases and ambiguous aliases
|
|
116
116
|
fail — invalid status (`MET | PARTIAL | UNMET` for requirements;
|
|
117
117
|
`N/A` additionally allowed for AC), invalid evidence type (`test | command | static-ref |
|
|
118
|
-
manual-review | llm-judge | n/a`, or a `+` compound), and empty evidence.
|
|
118
|
+
manual-review | llm-judge | n/a`, or a `+` compound), and empty evidence. Evidence cells that assert an
|
|
119
|
+
external API/library claim without a same-cell citation are rejected with `evidence-citation` (see
|
|
120
|
+
"Source citations for external claims" below). Interrupted runs keep the
|
|
119
121
|
rows that pass the lint and complete only the missing IDs on retry.
|
|
120
122
|
|
|
121
123
|
**Feature-credited AC ids are narrower than the lint's accepted set (task 0966 finding).** A row keyed
|
|
@@ -136,6 +138,20 @@ the run actually verified. Since 0804 R9 the checker ignores complete parsed cit
|
|
|
136
138
|
scanning for subjects, so a citation's filename (including snake_case paths) can never become a
|
|
137
139
|
false subject — a real absent symbol, nonexistent file, or invalid range still reports.
|
|
138
140
|
|
|
141
|
+
## Source citations for external claims (task 1070)
|
|
142
|
+
|
|
143
|
+
A Requirement/AC evidence cell that asserts an external API/library claim must carry a verifiable
|
|
144
|
+
citation in the SAME cell, or lint rejects the row with `evidence-citation` (blocking derivation).
|
|
145
|
+
|
|
146
|
+
- Claim markers (closed set): a scoped-package mention (`@scope/name`), a `node_modules/` reference,
|
|
147
|
+
a semver version claim (`x.y.z`), or a backticked function-call-shaped token (`` `name(args)` ``).
|
|
148
|
+
- Accepted citation forms (any one clears the cell): a repo `path:line(-end)` (backticked or bare),
|
|
149
|
+
the external named-origin form (@origin `path` line N — line number outside the backticks), or an
|
|
150
|
+
http(s) URL.
|
|
151
|
+
- The rule is cell-local: a citation in another row or a "receipt above" cross-reference does not
|
|
152
|
+
satisfy it. Marker-free receipts (test counts, reviewed-log notes) need no citation. The check is
|
|
153
|
+
presence-only — which citation proves which claim is not deterministically decidable.
|
|
154
|
+
|
|
139
155
|
## Checks evidence
|
|
140
156
|
|
|
141
157
|
Wave C verification can emit the following additive `checks[]` rows:
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fleet-join
|
|
3
|
+
description: >-
|
|
4
|
+
Join a project fleet as a guest occupant and pull its work. Use when the operator asks to
|
|
5
|
+
"join the fleet", "become a fleet guest", "pull fleet inbox work in this session", or when a
|
|
6
|
+
session should receive fleet messages without being supervised. Runs `spur agent join`, then
|
|
7
|
+
loops `spur agent wait --inbox`, and releases the lease with `spur agent leave`.
|
|
8
|
+
---
|
|
9
|
+
# fleet-join — a live session as a fleet guest (G73, task 1081)
|
|
10
|
+
|
|
11
|
+
A guest is a session that **pulls** work: it is neither supervised nor restarted, it is addressed
|
|
12
|
+
by concrete id only, and it never receives a stage (`requiresCapabilities` stages can never route
|
|
13
|
+
to it — a guest carries no attestation). Joining registers an occupant row plus a heartbeat lease;
|
|
14
|
+
leaving (or an expired lease) releases it and returns any claimed messages to `pending`.
|
|
15
|
+
|
|
16
|
+
## Join
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
spur agent join --role reviewer --json # id defaults to <role>-g<n>
|
|
20
|
+
spur agent join --role coder --id my-session --json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Pick the role whose work this session can actually do. `--session-id` defaults to
|
|
24
|
+
`CLAUDE_CODE_SESSION_ID`, which is what the Stop hook matches against — without it the hook cannot
|
|
25
|
+
find this session, though the `wait --inbox` loop still works.
|
|
26
|
+
|
|
27
|
+
## Pull loop
|
|
28
|
+
|
|
29
|
+
`spur agent wait --inbox <id> --timeout 540000` returns **0** the moment queued work exists and
|
|
30
|
+
**1** on timeout. Each call heartbeats the lease, so a looping session stays joined. The Bash tool's
|
|
31
|
+
own ~10-minute limit is why the timeout stays under it: one bounded call per turn, re-armed after
|
|
32
|
+
the work is done.
|
|
33
|
+
|
|
34
|
+
1. `spur agent wait --inbox <id> --timeout 540000 --json` → exit 0 means work is waiting.
|
|
35
|
+
2. `spur message inbox --agent <id> --json` → read the messages.
|
|
36
|
+
3. Do the work the message asks for.
|
|
37
|
+
4. `spur message reply <msgId> "<result>"` → reply to each one.
|
|
38
|
+
5. Re-arm: go back to step 1 while the operator wants this session in the fleet.
|
|
39
|
+
|
|
40
|
+
## Leave
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
spur agent leave --json # the guest joined by this session
|
|
44
|
+
spur agent leave my-session --json # an explicit id
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Leaving releases the lease immediately. If the session dies without leaving, the lease expires and
|
|
48
|
+
the reconciler pass retires the guest — pending work is never lost, it returns to `queued`.
|
|
49
|
+
|
|
50
|
+
## Stop-hook delivery (opportunistic)
|
|
51
|
+
|
|
52
|
+
When the host fires a `Stop` hook and the host supports the block contract, the
|
|
53
|
+
`fleet-guest-stop` plugin hook delivers pending messages at turn end by replying
|
|
54
|
+
`{"decision":"block","reason":"Pending fleet messages: …"}` — only for a session whose id matches a
|
|
55
|
+
joined guest, never twice in one continued turn, and always fail-open. Claude Code supports this;
|
|
56
|
+
Codex `exec` on 0.160.0 does not deliver `Stop` at all (`docs/plans/2026-10-05-codex-guest-spike.md`),
|
|
57
|
+
so the pull loop above — not the hook — is what makes a guest work anywhere.
|
|
58
|
+
|
|
59
|
+
## Boundaries
|
|
60
|
+
|
|
61
|
+
- Guests are **not** supervised: do not expect restart, pid ownership, or `--drain` injection.
|
|
62
|
+
- Never dispatch a stage to a guest id: `spur agent run --spec <guest-id>` is refused on purpose.
|
|
63
|
+
- Never hold the write slot through a guest: the guest lease is the only heartbeat (R5).
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: session-review
|
|
3
|
-
description: "Review the active coding-agent session: separate resolved from open issues with evidence, propose bounded improvements. With --triage, apply pure-doc / 1–2-line fixes inline and file the rest as
|
|
3
|
+
description: "Review the active coding-agent session: separate resolved from open issues with evidence, propose bounded improvements. With --fix auto (alias --triage), apply pure-doc / 1–2-line fixes inline and file the rest as tasks; with --fix all, fix every actionable finding inline and file none. Triggers: review this session, wrap-up, triage findings."
|
|
4
4
|
license: Apache-2.0
|
|
5
|
-
version: 1.
|
|
5
|
+
version: 1.2.0
|
|
6
6
|
metadata:
|
|
7
7
|
author: spur
|
|
8
8
|
platforms: "claude-code,codex,openclaw,opencode,antigravity,pi"
|
|
@@ -31,7 +31,8 @@ cross-agent windows, recurrence, trends, or quantitative performance forensics.
|
|
|
31
31
|
| Argument | Description | Default |
|
|
32
32
|
| --- | --- | --- |
|
|
33
33
|
| `[focus]` | Question or operation to emphasize. It changes ordering, not evidence collection. | full active session |
|
|
34
|
-
| `--
|
|
34
|
+
| `--fix <none\|auto\|all>` | Remediation after the report. `none`: report-only. `auto`: apply direct fixes (pure docs / one-to-two-line fixes) inline, then file all remaining actionable findings as one or more implement-ready tasks via the CLI-gated corpus surface. `all`: fix every actionable finding inline and file no task. | `none` |
|
|
35
|
+
| `--triage` | `--triage` is an alias of `--fix auto`; an explicit `--fix` wins when both are passed. | off |
|
|
35
36
|
|
|
36
37
|
## Evidence boundary
|
|
37
38
|
|
|
@@ -46,14 +47,16 @@ cross-agent windows, recurrence, trends, or quantitative performance forensics.
|
|
|
46
47
|
name the confirmation needed.
|
|
47
48
|
- State `not available` when compaction or missing output removed evidence. Never reconstruct it from
|
|
48
49
|
memory or claim a verification that did not run.
|
|
49
|
-
- Derive timing only from
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
- Derive timing and tokens only from the host transcript measurement (Protocol step 1): the visible
|
|
51
|
+
conversation carries no timestamps or usage, so never estimate them from it. Use non-overlapping
|
|
52
|
+
stages whose durations sum to elapsed time; render a value as `n/a` only when the measurement
|
|
53
|
+
reports `available: false` or omits it.
|
|
52
54
|
|
|
53
|
-
##
|
|
55
|
+
## Fix modes (`--fix auto|all`)
|
|
54
56
|
|
|
55
|
-
Report-only stays the default. With `--
|
|
56
|
-
three buckets — never skip triage and start fixing from the raw
|
|
57
|
+
Report-only stays the default. With `--fix auto` or `--fix all`, run the same
|
|
58
|
+
evidence pass, then remediate in three buckets — never skip triage and start fixing from the raw
|
|
59
|
+
findings list. Steps 1–4 below are `--fix auto`; `--fix all` differs only as stated after them.
|
|
57
60
|
|
|
58
61
|
1. **Triage every finding into exactly one bucket:**
|
|
59
62
|
- **Direct fix** — pure documentation work, or a one-to-two-line fix with obvious, local,
|
|
@@ -74,10 +77,22 @@ three buckets — never skip triage and start fixing from the raw findings list.
|
|
|
74
77
|
4. **Report** — add a Triage section: applied fixes (path + one-line what + verification) and
|
|
75
78
|
each created task WBS. The Resolved/Open tables keep their evidence rules unchanged.
|
|
76
79
|
|
|
80
|
+
**`--fix all` (defer nothing).** Triage still runs, but the Task bucket is fixed inline instead of
|
|
81
|
+
filed, so the mode creates no task. Fix in dependency order with the same surgical-diff and
|
|
82
|
+
per-fix re-verification rule as direct fixes, then run the project gate once at the end. A finding
|
|
83
|
+
that needs a design or operator decision is asked through the host question tool, never deferred;
|
|
84
|
+
one that still cannot be fixed (failed re-verification, blocked by the environment) stays in Open
|
|
85
|
+
issues with its evidence. Notes stay report-only.
|
|
86
|
+
|
|
77
87
|
## Protocol
|
|
78
88
|
|
|
79
89
|
1. **Resolve scope.** Review the active session from the operator's initiating request through the
|
|
80
|
-
latest result. Use `[focus]` only to rank relevant material.
|
|
90
|
+
latest result. Use `[focus]` only to rank relevant material. Measure it with
|
|
91
|
+
`node "$(superskill script path sp session-timeline.mjs)" --group "<ranges>"`: one segment per
|
|
92
|
+
operator prompt, read-only from the host transcript. Run it once without `--group` to list the
|
|
93
|
+
segments, then map them to stages (`"1-3,4,5-6"`). It splits each segment into `work` and
|
|
94
|
+
operator `wait` (idle before the next prompt plus AskUserQuestion answer time) and counts tokens
|
|
95
|
+
once per message id.
|
|
81
96
|
2. **Inventory outcomes.** List requested outcomes and classify each as completed, partial, blocked,
|
|
82
97
|
or not attempted. Collapse repeated attempts into one outcome.
|
|
83
98
|
3. **Classify issues.** For every material issue, distinguish resolved, open, or attempted. Record
|
|
@@ -108,26 +123,32 @@ State the overall result in one to three sentences, including partial or blocked
|
|
|
108
123
|
Summarize elapsed time and, when supported by evidence, productive work, avoidable setup/recovery,
|
|
109
124
|
and operator wait time. Then render non-overlapping stages:
|
|
110
125
|
|
|
111
|
-
| Stage | Time | Tool calls | Assessment |
|
|
112
|
-
| --- | ---: | ---: | --- |
|
|
126
|
+
| Stage | Time | Wait | Tool calls | Token | Assessment |
|
|
127
|
+
| --- | ---: | ---: | ---: | ---: | --- |
|
|
113
128
|
|
|
114
129
|
Format durations as `M:SS` below one hour and `H:MM:SS` at one hour or above (`1:44`, not `1m44s`;
|
|
115
130
|
`0:33`, not `33s`). Include a `Total` row when elapsed time is available. Keep operator approval
|
|
116
|
-
waits separate from execution bottlenecks.
|
|
117
|
-
|
|
131
|
+
waits separate from execution bottlenecks. Time is the stage `work` and Wait its operator `wait`.
|
|
132
|
+
Token renders `<total> / <non-cached>` in compact units (`28.7M / 717k`) — the measurement's
|
|
133
|
+
`token` field: total = input + cache creation + cache read + output; non-cached = total minus cache
|
|
134
|
+
read (fresh input, cache writes and output). These are measured session counts, not the 0912 baseline token/USD claims that
|
|
135
|
+
step 4 excludes. Use `n/a` for any value not supported by the measurement.
|
|
118
136
|
|
|
119
137
|
### Resolved issues
|
|
120
138
|
|
|
121
|
-
| Issue | Root cause | Resolution | Evidence |
|
|
122
|
-
| --- | --- | --- | --- |
|
|
139
|
+
| Issue | Root cause | Resolution | Evidence | Confidence |
|
|
140
|
+
| --- | --- | --- | --- | --- |
|
|
123
141
|
|
|
124
142
|
Evidence names the tool result, verification command, or concrete repository state visible in the
|
|
125
|
-
session. Do not list ordinary implementation steps as issues.
|
|
143
|
+
session. Do not list ordinary implementation steps as issues. **Confidence** is `HIGH`, `MEDIUM`, or
|
|
144
|
+
`LOW` for that row's claim, with its grounds in the cell (a level without grounds is not
|
|
145
|
+
verification); any API/library statement in a row cites the installed source as `path:line` or a URL,
|
|
146
|
+
and a claim the session could not ground is written `LOW` with the missing evidence named.
|
|
126
147
|
|
|
127
148
|
### Open issues and risks
|
|
128
149
|
|
|
129
|
-
| Issue or risk | State | Evidence or confirmation needed |
|
|
130
|
-
| --- | --- | --- |
|
|
150
|
+
| Issue or risk | State | Evidence or confirmation needed | Confidence |
|
|
151
|
+
| --- | --- | --- | --- |
|
|
131
152
|
|
|
132
153
|
### Process and environment improvements
|
|
133
154
|
|
|
@@ -137,13 +158,13 @@ observation adopted from the 0912 workflow baseline cites its anchor
|
|
|
137
158
|
(`docs/reports/i31/0912-workflow-baseline.md`) and owner handoff, and carries no unsupported
|
|
138
159
|
performance claim.
|
|
139
160
|
|
|
140
|
-
### Triage (only
|
|
161
|
+
### Triage (only with `--fix auto|all`)
|
|
141
162
|
|
|
142
163
|
| Applied fix / created task | Bucket | What + verification |
|
|
143
164
|
| --- | --- | --- |
|
|
144
165
|
|
|
145
|
-
One row per applied fix and one per created task (with its WBS). Omit the
|
|
146
|
-
|
|
166
|
+
One row per applied fix and one per created task (with its WBS; `--fix all` has none). Omit the
|
|
167
|
+
section entirely under `--fix none`.
|
|
147
168
|
|
|
148
169
|
### Next actions
|
|
149
170
|
|
|
@@ -157,8 +178,9 @@ improvement. Use `None` when the session is complete and no follow-up is justifi
|
|
|
157
178
|
comparison or recurrence classification, or emit a twelve-section forensic report; those belong
|
|
158
179
|
to imported-history analysis.
|
|
159
180
|
- Report-only by default: do not create or update corpus items or edit files. The single exception
|
|
160
|
-
is `--
|
|
161
|
-
bucket, and the triage tasks
|
|
181
|
+
is `--fix auto|all`: `auto` permits exactly two mutation classes — direct fixes from the triage
|
|
182
|
+
bucket, and the triage tasks; `all` permits one — inline fixes of actionable findings. Anything
|
|
183
|
+
beyond that stays a proposal.
|
|
162
184
|
- Do not turn a single low-impact observation into a new policy. Report it as a candidate until it
|
|
163
185
|
recurs or demonstrates a high-impact contract violation.
|
|
164
186
|
|
|
@@ -49,6 +49,7 @@ Pick the noun, read its reference. Each Tier A and Tier B reference owns that no
|
|
|
49
49
|
| **Tier B** | **self** | Self-management verbs: scaffold (`init`), database maintenance (`maintain`), schema migrations (`migrate`), local web server (`serve`), status overview (`status`); `self init` runs post-scaffold validation probes & layout classification | [references/self.md](references/self.md) |
|
|
50
50
|
| **Tier B** | **history** | Import agent histories, aggregate forensic artifacts, render reports, and run the checkpoint-resumed daily pipeline | [references/history.md](references/history.md) |
|
|
51
51
|
| **Tier B** | **projects** | Manage the local multi-project registry and start/stop project servers | [references/projects.md](references/projects.md) |
|
|
52
|
+
| **Tier B** | **decision** | Decision-catalog transport: list visible decisions, show a served contract, run a decision to a served outcome, audit catalog health | [references/decision.md](references/decision.md) |
|
|
52
53
|
| **Tier C** | **help** | Commander-generated help command; not a Spur noun | Generated `--help` |
|
|
53
54
|
|
|
54
55
|
**Execute-First Contract:** Load the noun reference first and execute Tier A or Tier B commands
|
|
@@ -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.
|