@gobing-ai/spur 0.4.33 → 0.4.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.global.yaml +1 -4
  3. package/config/decisions/task-pipeline.yaml +1 -29
  4. package/config/rules/boundary/sp-runtime-path.yaml +0 -2
  5. package/config/rules/strict/runtime-boundaries.yaml +2 -12
  6. package/config/rules/structure/protected-files.yaml +0 -3
  7. package/config/rules/surface/check-cli-surface.yaml +1 -2
  8. package/config/workflow-candidates.json +1 -1
  9. package/config/workflows/idea-pipeline.yaml +1 -1
  10. package/config/workflows/task-pipeline.yaml +53 -185
  11. package/package.json +1 -1
  12. package/plugins/sp/agents/super-reviewer.md +18 -60
  13. package/plugins/sp/commands/dev-fixall.md +0 -7
  14. package/plugins/sp/commands/dev-review.md +1 -1
  15. package/plugins/sp/commands/dev-run.md +8 -3
  16. package/plugins/sp/commands/dev-runall.md +7 -4
  17. package/plugins/sp/commands/dev-verify.md +0 -10
  18. package/plugins/sp/commands/dev-verifyall.md +0 -5
  19. package/plugins/sp/plugin.json +1 -1
  20. package/plugins/sp/skills/code-implementation/SKILL.md +1 -3
  21. package/plugins/sp/skills/code-verification/SKILL.md +11 -41
  22. package/plugins/sp/skills/code-verification/references/verdict-schema.md +18 -119
  23. package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
  24. package/plugins/sp/skills/next-router/references/routing-table.md +0 -7
  25. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +1 -6
  26. package/plugins/sp/skills/spur-cli/references/decision.md +3 -6
  27. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +0 -28
  28. package/plugins/sp/skills/spur-cli/references/tasks.md +3 -6
  29. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +5 -12
  30. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +2 -3
  31. package/plugins/sp/skills/spur-dev/references/dev-operations.md +4 -4
  32. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +4 -5
  33. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +9 -5
  34. package/plugins/sp/skills/spur-dev/references/execution-batch.md +4 -5
  35. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +1 -1
  36. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +34 -0
  37. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +14 -1
  38. package/spur.js +1408 -4406
  39. package/web/_astro/{BoardApp.CuCuw0lw.js → BoardApp.BzY_krKQ.js} +1 -1
  40. package/web/_astro/{BoardApp.BGGDrfHl.js → BoardApp.CIImQFjG.js} +3 -3
  41. package/web/_astro/{TaskDetail.DWXct9_X.js → TaskDetail.c48Pnba6.js} +1 -1
  42. package/web/_astro/{arc.BxG3NS1o.js → arc.CpU6Cddr.js} +1 -1
  43. package/web/_astro/{architectureDiagram-3BPJPVTR.BxaiM2Hv.js → architectureDiagram-3BPJPVTR.CaXT9ZEz.js} +1 -1
  44. package/web/_astro/{blockDiagram-GPEHLZMM.DJ3r6SdD.js → blockDiagram-GPEHLZMM.Cnv3IsW9.js} +1 -1
  45. package/web/_astro/{c4Diagram-AAUBKEIU.BkzpXBQs.js → c4Diagram-AAUBKEIU.VO_hjv9n.js} +1 -1
  46. package/web/_astro/channel.DrtdR7d_.js +1 -0
  47. package/web/_astro/{chunk-2J33WTMH.DI7UanTc.js → chunk-2J33WTMH.BOunhbrU.js} +1 -1
  48. package/web/_astro/{chunk-4BX2VUAB.C2uLhAlQ.js → chunk-4BX2VUAB.Brg-Gmb2.js} +1 -1
  49. package/web/_astro/{chunk-55IACEB6.KwNI2kxL.js → chunk-55IACEB6.BqF9dxiC.js} +1 -1
  50. package/web/_astro/{chunk-727SXJPM.CHZUthkg.js → chunk-727SXJPM.CEIGJZdc.js} +1 -1
  51. package/web/_astro/{chunk-AQP2D5EJ.DLr4QNJo.js → chunk-AQP2D5EJ.BxXI0bx9.js} +1 -1
  52. package/web/_astro/{chunk-FMBD7UC4.B1G8WrnE.js → chunk-FMBD7UC4.CveXCF83.js} +1 -1
  53. package/web/_astro/{chunk-ND2GUHAM.CzsA-SkD.js → chunk-ND2GUHAM.DwKjp403.js} +1 -1
  54. package/web/_astro/{chunk-QZHKN3VN.DAvBQYli.js → chunk-QZHKN3VN.DgwjHrXx.js} +1 -1
  55. package/web/_astro/{classDiagram-4FO5ZUOK.Cddmz8Ze.js → classDiagram-4FO5ZUOK.BcVv8Qmo.js} +1 -1
  56. package/web/_astro/{classDiagram-v2-Q7XG4LA2.Cddmz8Ze.js → classDiagram-v2-Q7XG4LA2.BcVv8Qmo.js} +1 -1
  57. package/web/_astro/{cose-bilkent-S5V4N54A.CKEu7KRX.js → cose-bilkent-S5V4N54A.28xfMBER.js} +1 -1
  58. package/web/_astro/{cynefin-OW5HDTMX.-B4Cy_Vl.js → cynefin-OW5HDTMX.BAFHBcJj.js} +1 -1
  59. package/web/_astro/{dagre-BM42HDAG.bLxFvI-t.js → dagre-BM42HDAG.DzR6wa9Z.js} +1 -1
  60. package/web/_astro/{diagram-2AECGRRQ.DAHU0t5h.js → diagram-2AECGRRQ.IpFlr4yd.js} +1 -1
  61. package/web/_astro/{diagram-5GNKFQAL.DFciwCNk.js → diagram-5GNKFQAL.DGNU2pOL.js} +1 -1
  62. package/web/_astro/{diagram-KO2AKTUF.BvH0dGId.js → diagram-KO2AKTUF.D_wP6t4w.js} +1 -1
  63. package/web/_astro/{diagram-LMA3HP47.BgVlskRO.js → diagram-LMA3HP47.B0DMC97s.js} +1 -1
  64. package/web/_astro/{diagram-OG6HWLK6.BpurMISo.js → diagram-OG6HWLK6.DQXmNEBs.js} +1 -1
  65. package/web/_astro/{erDiagram-TEJ5UH35.SDj5bUMU.js → erDiagram-TEJ5UH35.BZ0VPjjp.js} +1 -1
  66. package/web/_astro/{flowDiagram-I6XJVG4X.Bm5U58FE.js → flowDiagram-I6XJVG4X.zu5gJs9Z.js} +1 -1
  67. package/web/_astro/{ganttDiagram-6RSMTGT7.CnMnds77.js → ganttDiagram-6RSMTGT7.DYy6LDqD.js} +1 -1
  68. package/web/_astro/{gitGraphDiagram-PVQCEYII.BQUwwyWo.js → gitGraphDiagram-PVQCEYII.C6c_wPdO.js} +1 -1
  69. package/web/_astro/index.C4tUn4AU.css +1 -0
  70. package/web/_astro/{infoDiagram-5YYISTIA.BiSKdORH.js → infoDiagram-5YYISTIA.C3bQPQl8.js} +1 -1
  71. package/web/_astro/{ishikawaDiagram-YF4QCWOH.Br69sBTI.js → ishikawaDiagram-YF4QCWOH.Cc1D58oG.js} +1 -1
  72. package/web/_astro/{journeyDiagram-JHISSGLW.C39WX1W8.js → journeyDiagram-JHISSGLW.CY51-lNM.js} +1 -1
  73. package/web/_astro/{kanban-definition-UN3LZRKU.DppWcbSX.js → kanban-definition-UN3LZRKU.D_3v92y6.js} +1 -1
  74. package/web/_astro/{linear.DeivTDuc.js → linear.C6BrJ38s.js} +1 -1
  75. package/web/_astro/{mermaid.core.Dixrjrnj.js → mermaid.core.DO9lF20B.js} +4 -4
  76. package/web/_astro/{mindmap-definition-RKZ34NQL.BWIQ2MWG.js → mindmap-definition-RKZ34NQL.BMiM91P6.js} +1 -1
  77. package/web/_astro/{pieDiagram-4H26LBE5.vvtHpfT7.js → pieDiagram-4H26LBE5.CcgIgRlR.js} +1 -1
  78. package/web/_astro/{quadrantDiagram-W4KKPZXB.9c-T6qCy.js → quadrantDiagram-W4KKPZXB.CM1CzrHl.js} +1 -1
  79. package/web/_astro/{requirementDiagram-4Y6WPE33.9ZuRw-kn.js → requirementDiagram-4Y6WPE33.CQvQ1E8r.js} +1 -1
  80. package/web/_astro/{sankeyDiagram-5OEKKPKP.CTIWteEl.js → sankeyDiagram-5OEKKPKP.D-b1ONCV.js} +1 -1
  81. package/web/_astro/{sequenceDiagram-3UESZ5HK.DEvu4vLL.js → sequenceDiagram-3UESZ5HK.BQzFGYtH.js} +1 -1
  82. package/web/_astro/{stateDiagram-AJRCARHV.Bxvsgk-_.js → stateDiagram-AJRCARHV.DiCukZ7I.js} +1 -1
  83. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CVuog5yY.js → stateDiagram-v2-BHNVJYJU.4Iut-DMk.js} +1 -1
  84. package/web/_astro/{timeline-definition-PNZ67QCA.CpPItB3E.js → timeline-definition-PNZ67QCA.BKQiLF24.js} +1 -1
  85. package/web/_astro/{vennDiagram-CIIHVFJN.C7eKaLRK.js → vennDiagram-CIIHVFJN.CyeSCofx.js} +1 -1
  86. package/web/_astro/{wardleyDiagram-YWT4CUSO.DWXHT_CW.js → wardleyDiagram-YWT4CUSO.B140ei48.js} +1 -1
  87. package/web/_astro/{xychartDiagram-2RQKCTM6.DOsrNQOF.js → xychartDiagram-2RQKCTM6.DjPyVoO1.js} +1 -1
  88. package/web/index.html +2 -2
  89. package/config/rules/structure/scratch-evidence-pointer.yaml +0 -35
  90. package/web/_astro/channel.CYWRkoDI.js +0 -1
  91. package/web/_astro/index.CKtaGArK.css +0 -1
@@ -25,7 +25,7 @@
25
25
  # captures and compares, digest-bound receipt preconditions, `run.artifact` verdict
26
26
  # registration and the `verifiedOutcome` it fed — is gone; a PASS receipt is reused while
27
27
  # the tree hash holds and invalidated when the tree moves. `done` closes on the verb's own
28
- # target-aware `task check` plus the residual sweep, not on a re-asserted digest.
28
+ # target-aware `task check`, not on a re-asserted digest.
29
29
  #
30
30
  # No operator pause lives in this file (D65 R10): the only pause is the host executor's
31
31
  # `agent.run` transform (1232). `escalate` asks its question through the responder and the
@@ -131,9 +131,9 @@ vars:
131
131
  # Project quality gate for the `gate` hop, overridable per project:
132
132
  # `--vars '{"qualityGateCmd":"bun run lint && bun run test"}'`.
133
133
  # INVARIANT (ADR-124, clarified 2026-10-07 by task 1111 R5): only the FULL tier writes PASS.
134
- # Under the opt-in `deferQualityGate` policy the per-task token is DEFERRED (light tier) and
135
- # the "review runs only after a green full gate" invariant moves to BATCH scope — nothing is
136
- # reported PASS without the integrated full gate on the base ref.
134
+ # Under the default incremental policy (`deferQualityGate`) the per-task token is DEFERRED and
135
+ # the "green full gate" invariant lives at FEATURE/BATCH scope (`spur feature verify`, the
136
+ # batch integration gate) — nothing is reported PASS without the full gate.
137
137
  # TRUSTED CONFIG ONLY — `spur task gate` passes this to the project's gate command. Never
138
138
  # interpolate untrusted operator/LLM input into qualityGateCmd (task 0436 SECUA residual).
139
139
  # The gate observes, it does not mutate: `implement` already ran `$formatCmd` on its way out,
@@ -172,19 +172,15 @@ vars:
172
172
  # anchors directly in the /sp:dev-fixall input, so the fix agent starts AT the failing
173
173
  # file:line instead of re-deriving it from a fresh gate run (0482 R3).
174
174
  gateFindings: ""
175
- # 1319 R5: lint findings from the previous review visit, echoed into the review prompt on the
176
- # inline retry. Empty on the happy path, so the prompt pays nothing when nothing is wrong.
177
- reviewLint: ""
178
175
  # The quality-gate mode this visit runs — `run` on the first gate visit of the run,
179
176
  # `recheck` after a remediation hop, `deferred` under the opt-in batch policy. Written by
180
177
  # the gate's own shell and projected into this var (args cannot branch) — D65 1239 R2.
181
178
  gateMode: ""
182
- # Review depth resolved from the lane decision (0943 R1/R2): `functional` for the fast
179
+ # Review depth resolved from the diffstat lane (0943 R1/R2): `functional` for the fast
183
180
  # lane (requirement/AC traceability only — the reviewer's R → file:line table is what
184
181
  # certifies, so the fast lane never drops the evidence), `all` for every other lane.
185
182
  reviewFocus: ""
186
- # Task spec path resolved at `gate` entry (also the decide evidence for the review-lane
187
- # routing). `docs/tasks*` is a versioned input, not a run artifact.
183
+ # Task spec path resolved at `gate` entry. `docs/tasks*` is a versioned input, not a run artifact.
188
184
  taskSpecPath: ""
189
185
  # 0710 R4: task priority tier (P0..P4) extracted from the task frontmatter at `gate`
190
186
  # entry. P0/P1 make the review distinct-executor policy apply; unknown/empty priority
@@ -196,13 +192,14 @@ vars:
196
192
  # (default) = on; set to "off" to bypass:
197
193
  # `--vars '{"implementScopeGuard":"off"}'`.
198
194
  implementScopeGuard: ""
199
- # 1111 R1: opt-in parallel-batch gate policy. When "true", `gate` runs the LIGHT tier for
200
- # per-task early feedback and writes `DEFERRED` (light PASS) or `FAIL` to
201
- # `.spur/run/<wbs>-test-gate.status` — never `PASS`, because that token means the full
202
- # project gate ran green on this tree. The batch orchestrator owes ONE integrated full gate
203
- # on the base ref over the merged slices (1111 R3). Default "false" = unchanged behavior,
204
- # and a stray `DEFERRED` file fails the guards.
205
- deferQualityGate: "false"
195
+ # Gate mode. "true" (default) = INCREMENTAL: `gate` runs lint, rules, typecheck of the touched
196
+ # workspaces and their dependents, the related tests, and the per-file coverage floor on the
197
+ # changed files, then writes `DEFERRED` or `FAIL` to `.spur/run/<wbs>-test-gate.status` —
198
+ # never `PASS`, because that token means the full project gate ran green on this tree. The
199
+ # full gate runs once per feature (`spur feature verify`) or batch (integration gate).
200
+ # "false" = FULL: every task pays the whole project gate; a stray `DEFERRED` fails the guards.
201
+ # `--vars '{"deferQualityGate":"false"}'`.
202
+ deferQualityGate: "true"
206
203
  # 0931 R5: parallel batches defer the per-task feature sync so a task branch never
207
204
  # touches feature files or docs/features/INDEX.md. When "true", the close's post-record
208
205
  # shell appends a deferral note and skips the sync; the parallel batch orchestrator runs
@@ -222,8 +219,7 @@ states:
222
219
  onEnter:
223
220
  # (e) git-status hygiene advisory; never fails the run (0487 R6 / 0511 R1). 1007 R3:
224
221
  # the F96 base-sha capture (0950) merged into this shell — original commands in order,
225
- # then the one-time base commit capture; a resumed run keeps its original base so
226
- # residual scans stay anchored to the same diff.
222
+ # then the one-time base commit capture; a resumed run keeps its original base.
227
223
  - kind: shell
228
224
  options:
229
225
  command: >-
@@ -357,15 +353,6 @@ states:
357
353
  resultFile: .spur/run/${vars.__runId}-implement-format.status
358
354
  softFail: true
359
355
  timeoutMs: "${vars.stepTimeoutMs}"
360
- # 1109 R3: qualify this task's unique basename anchors to repo-relative paths.
361
- # Placement is load-bearing: it runs AFTER implement's mutations (and the format
362
- # above) and BEFORE the gate reads the tree, so the receipt is keyed to the final
363
- # tree. `--wbs` scopes the pass to this task's resolved file.
364
- # Non-fatal (`; exit 0`): the done gate still reports anything left. Scope
365
- # contract: task 1109 R5.
366
- - kind: shell
367
- options:
368
- command: "$spurBin task migrate-anchors --wbs $wbs --json ; exit 0"
369
356
 
370
357
  # ── escalate hop (operator-question pause, 0933) ──────────────────────────
371
358
  # The implement agent paused on an operator question: it wrote
@@ -417,11 +404,10 @@ states:
417
404
  # attempt counter and consults the tree-hash receipt), `recheck` after a remediation hop
418
405
  # (probe-then-full), `deferred` under the opt-in batch policy.
419
406
  #
420
- # The three former routing states are decision CALLS here and in `review` (D65 1239 R1):
421
- # the lane decision (0943 R1/R2, catalog `task-semantic-risk`, 1262 R5) picks the review depth, and the
422
- # gate's own failure class (catalog `failure-class`) drives the red-gate transitions.
423
- # Both run unconditionally — the engine has no onEnter conditionals — and both degrade to
424
- # their catalog fallback with no model call while `workflows.decideDecisionMaker` is off.
407
+ # The review lane is resolved deterministically from the diffstat (no model call); the
408
+ # gate's own failure class (catalog `failure-class`) drives the red-gate transitions and
409
+ # degrades to its catalog fallback with no model call while
410
+ # `workflows.decideDecisionMaker` is off.
425
411
  - id: gate
426
412
  display:
427
413
  phase: test
@@ -429,15 +415,14 @@ states:
429
415
  title: Run quality gate
430
416
  description: >
431
417
  Typed quality gate: resolve the task path/priority, produce the diffstat lane
432
- evidence, consult `task-semantic-risk` for the review depth (deterministic safety floor
433
- first), run `${vars.qualityGateCmd}` through `spur task gate <wbs> <mode>` under the
418
+ evidence, resolve the review depth from it deterministically, run `${vars.qualityGateCmd}` through `spur task gate <wbs> <mode>` under the
434
419
  host-wide lock (tree-hash receipt reuse on the same tree), then classify a red gate
435
420
  through `failure-class`. Writes the typed outcome to
436
421
  `.spur/run/<runId>-gate.status` and the gate's own token to
437
422
  `.spur/run/<wbs>-test-gate.status`; always a recorded outcome, never a state failure.
438
423
  onEnter:
439
424
  # (e) task path + priority resolution; fail-closed on an unresolvable spec (0751 R2,
440
- # 0710 R4). The path is the decide evidence for the review lane and the priority is
425
+ # 0710 R4). The path is the review's task spec and the priority is
441
426
  # the review/executor distinctness input (0710 R4) — both are consumed downstream, so
442
427
  # an unresolved path fails here instead of degrading the run silently.
443
428
  - kind: shell
@@ -459,39 +444,19 @@ states:
459
444
  options:
460
445
  command: >-
461
446
  $spurBin task diffstat "$wbs" --no-logo || { echo "task-diffstat failed closed — spur task diffstat exited $?" >&2; printf '{"files":0,"insertions":0,"deletions":0,"paths":[],"sensitive":true}\n' > ".spur/run/$wbs-diffstat.json"; }; exit 0
462
- # (b)+(c) lane pre-guard BEFORE the decide: a caller-set mode is projected verbatim
463
- # (R2b); a sensitive path or >400 changed lines pins safety (R2c) and the decide
464
- # result is never consulted. A missing/malformed diffstat row pins safety HERE: an
465
- # empty mode file would let the low decide row project the fast lane downstream, so
466
- # the "never to fast" fail-safe is enforced at the guard, not by the projection.
447
+ # (b)+(c) deterministic lane: a caller-set mode is projected verbatim (R2b); a
448
+ # sensitive path or >400 changed lines pins safety (R2c); a change set touching only
449
+ # docs and tests takes the fast lane; everything else stays empty → standard review.
450
+ # A missing/malformed diffstat row pins safety — never fast. No model call.
467
451
  - kind: shell
468
452
  options:
469
453
  command: >-
470
- mkdir -p .spur/run; if [ -n "$mode" ]; then printf '%s\n' "$mode" > ".spur/run/$wbs-mode.txt"; exit 0; fi; DF=".spur/run/$wbs-diffstat.json"; if ! jq -e 'type == "object"' "$DF" >/dev/null 2>&1; then printf 'safety\n' > ".spur/run/$wbs-mode.txt"; exit 0; fi; jq -r 'if .sensitive == true or ((.insertions // 0) + (.deletions // 0)) > 400 then "safety" else empty end' "$DF" 2>/dev/null > ".spur/run/$wbs-mode.txt"; exit 0
471
- # (d) lane decision (1262 R5) — one narrow question: does this change set alter
472
- # behaviour beyond its diffstat? `false` projects the fast lane; `true` or the catalog
473
- # fallback projects standard. Degrades to that fallback with no model call whenever
474
- # workflows.decideDecisionMaker is off (the bundled catalog owns question, criteria and
475
- # fallback), and `skipWhenEvidenceEmpty` keeps a missing diffstat from costing a call.
476
- - kind: decide
477
- options:
478
- id: task-semantic-risk
479
- decision: task-semantic-risk
480
- params:
481
- wbs: ${vars.wbs}
482
- evidence:
483
- - .spur/run/${vars.wbs}-diffstat.json
484
- - ${vars.taskSpecPath}
485
- skipWhenEvidenceEmpty: true
486
- resultFile: .spur/run/${vars.wbs}-semantic-risk.decision
487
- # (d) project the decision and append the route reason (1007 R3: the non-empty mode
488
- # file is never overridden; the append keeps the failure signal of the original pair).
489
- # `no` (a noul row stores the label, not a boolean) → fast lane; anything else (`yes`,
490
- # degraded default, missing row) stays empty → the standard review path.
454
+ mkdir -p .spur/run; if [ -n "$mode" ]; then printf '%s\n' "$mode" > ".spur/run/$wbs-mode.txt"; exit 0; fi; DF=".spur/run/$wbs-diffstat.json"; if ! jq -e 'type == "object"' "$DF" >/dev/null 2>&1; then printf 'safety\n' > ".spur/run/$wbs-mode.txt"; exit 0; fi; jq -r 'if .sensitive == true or ((.insertions // 0) + (.deletions // 0)) > 400 then "safety" elif ((.paths // []) | length) > 0 and ((.paths // []) | all(test("[.](md|mdx|txt)$|(^|/)tests?/|[.](test|spec)[.][cm]?[jt]sx?$"))) then "fast" else empty end' "$DF" 2>/dev/null > ".spur/run/$wbs-mode.txt"; exit 0
455
+ # (d) append the route reason for the resolved lane.
491
456
  - kind: shell
492
457
  options:
493
458
  command: >-
494
- MF=".spur/run/$wbs-mode.txt"; [ -s "$MF" ] || jq -r 'if (.value == false or .value == "no") then "fast" else empty end' ".spur/run/$wbs-semantic-risk.decision" 2>/dev/null > "$MF"; m="$(cat "$MF")"; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; mkdir -p .spur/memory; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(jq -rn --arg m "$m" --arg c "$mode" 'if $c != "" then ($c + ":caller-set") else {"fast":"fast:semantic-risk mechanical change","safety":"safety:deterministic-high","":"safety:standard lane"}[$m] // "safety:unrecognized mode" end')" >> .spur/memory/task-pipeline-routes.log
459
+ m="$(cat ".spur/run/$wbs-mode.txt" 2>/dev/null)"; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; mkdir -p .spur/memory; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(jq -rn --arg m "$m" --arg c "$mode" 'if $c != "" then ($c + ":caller-set") else {"fast":"fast:docs/tests-only change","safety":"safety:deterministic-high","":"safety:standard lane"}[$m] // "safety:unrecognized mode" end')" >> .spur/memory/task-pipeline-routes.log
495
460
  # (d) review depth from the resolved lane (D65 1239 R2): the fast lane reviews the
496
461
  # requirement/AC traceability only — that table is the certification input, so the
497
462
  # cheap lane never drops the evidence, it drops the SECUA/architecture passes.
@@ -593,14 +558,11 @@ states:
593
558
  options:
594
559
  command: >-
595
560
  TASK_POLICY=$($spurBin task show "$wbs" --json | jq -er '.frontmatter.mutationPolicy // ([.content | scan("(?m)^mutationPolicy:[ \t]*([^ \r\n]+)[ \t]*$") | .[0]] | if length == 0 then "code" elif length == 1 then .[0] else "ambiguous" end)') || exit 1; [ "$mutationPolicy:$TASK_POLICY" = "code:code" ] || { echo "remediate: mutation policy forbids automatic code repair (run=$mutationPolicy, task=$TASK_POLICY) — gate failure preserved, no agent dispatched" >&2; exit 1; }
596
- # (e) attempt counter + residual artifact projection in one log append
597
- # (1007 R3: the two appends merged — original commands in order, exit 0).
598
- # (d) F96 R3 (0950): dev-fixall treats residual items as fix targets and may defer
599
- # P3/markers to residual-deferrals.json; this carries the structured artifact too.
561
+ # (e) attempt counter.
600
562
  - kind: shell
601
563
  options:
602
564
  command: >-
603
- printf '%s\n' "$(( $(cat ".spur/run/$wbs-test-fix-attempt" 2>/dev/null || echo 0) + 1 ))" > ".spur/run/$wbs-test-fix-attempt"; [ -f ".spur/run/$wbs-residuals.json" ] && echo '--- residual artifact (F96 fix targets; deferrals go to residual-deferrals.json, P3/markers only) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$wbs-residuals.json" >> ".spur/run/$wbs-test-gate.log"; exit 0
565
+ printf '%s\n' "$(( $(cat ".spur/run/$wbs-test-fix-attempt" 2>/dev/null || echo 0) + 1 ))" > ".spur/run/$wbs-test-fix-attempt"; exit 0
604
566
  # Review-lane input: a non-PASS review status routes into this same bounded repair hop
605
567
  # (1262 R6), but it stages no gate evidence — the fixer would receive a GREEN gate log
606
568
  # and an empty findings list. Project the reviewer's answer into the same log the other
@@ -645,11 +607,10 @@ states:
645
607
  # D65 1239 R2: `verify` merged into `review`. The reviewer's answer must carry the
646
608
  # requirement/AC traceability table (`| Req | Status | Evidence |`, evidence = `file:line`
647
609
  # or a named test/command) — sp:functional-review produces it and the answer contract owns
648
- # the shape. `spur task verdict --from-answer` lints that answer, derives PASS/PARTIAL/FAIL
610
+ # the shape. `spur task verdict --from-answer` derives PASS/PARTIAL/FAIL
649
611
  # from the rows and writes the canonical `.spur/run/<wbs>-verdict.json` that `spur task
650
612
  # record` consumes for box flips and `## Testing`; a review whose rows do not resolve (an
651
- # UNMET requirement, a missing row, a bad AC identity) cannot derive PASS. The review-gate
652
- # residual check (1122) still blocks an open P1-P3 finding. Everything is folded into one
613
+ # UNMET requirement, a missing row, a bad AC identity) cannot derive PASS. Everything is folded into one
653
614
  # status token the transitions read, so a non-PASS review routes into the bounded repair
654
615
  # hop BEFORE `done` spends the close.
655
616
  - id: review
@@ -661,22 +622,8 @@ states:
661
622
  Three-dimensional code review via /sp:dev-review (functional requirements traceability
662
623
  + SECUA framework + architecture depth), findings written to `## Review`, then
663
624
  deterministic certification: `spur task verdict --from-answer` over the reviewer's
664
- own traceability table, the review-gate residual check, and the operator/maker
665
- approval decision point. Cost at this decision point (1122): open P1-P3 review
666
- findings block `done`; only P4 rows and DEFER-ed P3 rows are wrap residuals.
625
+ own traceability table, and the operator/maker approval decision point.
667
626
  onEnter:
668
- # 1319 R5(b)/Q9: surface the previous visit's verdict-lint findings for THIS visit's prompt.
669
- # Empty on the first visit (and after a clean lint), capped so the prompt stays inside its
670
- # composition budget, and it names the declared R/AC ids because the lint already does.
671
- - kind: shell
672
- options:
673
- command: >-
674
- mkdir -p .spur/run; L=".spur/run/$wbs-verdict-lint.txt"; O=".spur/run/$__runId-review-lint.txt";
675
- if [ -s "$L" ]; then head -c 600 "$L" > "$O"; else : > "$O"; fi; exit 0
676
- - kind: file.read.into-var
677
- options:
678
- path: .spur/run/${vars.__runId}-review-lint.txt
679
- var: reviewLint
680
627
  - kind: agent.run
681
628
  options:
682
629
  # 0710 R2: review always runs on a fresh session — no implementation-session
@@ -693,19 +640,12 @@ states:
693
640
  # of the prompt. A report body ending with a `| WBS | Verdict | P1 | … |` summary and
694
641
  # no per-requirement rows derives UNKNOWN and can never certify, which looped every
695
642
  # run through review -> remediate.
696
- # 1254: the review emits TWO artifacts with different consumers — the `## Review`
697
- # section (finding dispositions, deriveReviewConfidence) and this captured answer
698
- # (requirement/AC evidence, deriveVerifyConfidence). The prompt has to teach both
699
- # lanes and the fact that a single `Confidence:` line may only carry the second one.
700
- # 1254: the prompt stays inside the 1000-char composition cap — the full
701
- # contract (tables, vocabularies, the two confidence lanes, AC identity vs
702
- # feature credit) is owned by `plugins/sp/agents/super-reviewer.md` Output Format,
703
- # which this pointer names.
643
+ # The prompt stays inside the 1000-char composition cap — the full contract
644
+ # (tables, vocabularies) is owned by `plugins/sp/agents/super-reviewer.md`
645
+ # Output Format, which this pointer names.
704
646
  input: >-
705
647
  /sp:dev-review --tasks ${vars.wbs} --auto --focus ${vars.reviewFocus}
706
- Answer contract: super-reviewer Output Format. Emit the machine-linted artifact, not a prose summary.
707
- `## Review`: deriveReviewConfidence. Captured answer (`spur task verdict`) has one `Confidence: HIGH|MEDIUM|LOW`
708
- line (deriveVerifyConfidence), never the section's disposition level.
648
+ Answer contract: super-reviewer Output Format. Emit the verdict tables, not a prose summary.
709
649
  End with one `Verdict: PASS|PARTIAL|FAIL` line after
710
650
  `| Req | Status | Evidence |` (one row per requirement; MET | PARTIAL | UNMET) and
711
651
  `| AC | Status | Evidence Type | Evidence |` (one row per AC; MET | PARTIAL | UNMET | N/A).
@@ -714,12 +654,11 @@ states:
714
654
  a declared `AC-N` alias; a linked scenario covers its task; a task-local criterion needs no feature credit.
715
655
  Open P1-P3 blocks completion.
716
656
  Also write it to `.spur/run/${vars.__runId}-review-cert.md`.
717
- ${vars.reviewLint}
718
657
  # The captured review answer is the run's certification input (and the
719
658
  # `task verdict` input below): expectFile fails the step when no answer landed.
720
659
  answerFile: .spur/run/${vars.__runId}-review-answer.txt
721
660
  expectFile: .spur/run/${vars.__runId}-review-answer.txt
722
- # The reviewer WRITES this one itself (see (c2)); declared so the composition lint can
661
+ # The reviewer WRITES this one itself (see (c2)); declared so the composition check can
723
662
  # see the run path the prompt names (1233 R2).
724
663
  inputs:
725
664
  - path: .spur/run/${vars.__runId}-review-cert.md
@@ -737,51 +676,38 @@ states:
737
676
  # (c2) The reviewer is asked to WRITE its canonical artifact to `-review-cert.md`, because
738
677
  # the captured answer above is the agent's stdout: a reviewer that writes the artifact and
739
678
  # then narrates its summary has the artifact overwritten by the narration, and the verdict
740
- # lint fails work that is actually complete (four such runs in the G74 batch, 2026-10-24).
679
+ # derivation fails work that is actually complete (four such runs in the G74 batch, 2026-10-24).
741
680
  # The agent-written cert wins; when it is absent or empty the captured answer is used, so
742
681
  # this is a strict widening of accepted inputs, never a new failure mode.
743
682
  - kind: shell
744
683
  options:
745
684
  command: >-
746
685
  A=".spur/run/$__runId-review-answer.txt"; C=".spur/run/$__runId-review-cert.md"; [ -s "$C" ] || cp "$A" "$C" 2>/dev/null; exit 0
747
- # (d) `spur task verdict` owns the answer lint and the verdict derivation (task 1003
748
- # R2): one complete requirement row per declared requirement, AC identities resolved,
749
- # status/evidence vocabularies accepted, hedged or uncited evidence rejected. It
750
- # writes `.spur/run/<wbs>-verdict.json` — the review verdict artifact `spur task
751
- # record` consumes — and exits 1 on a non-PASS verdict BY DESIGN, which `softFail`
752
- # records as the typed outcome the guards route. A lint failure (exit 1) is a failed
753
- # certification, never a warning, and there is no grep fallback.
686
+ # (d) `spur task verdict` owns the verdict derivation (task 1003 R2). It writes
687
+ # `.spur/run/<wbs>-verdict.json` — the review verdict artifact `spur task record`
688
+ # consumes — and exits 1 on a non-PASS verdict BY DESIGN, which `softFail` records as
689
+ # the typed outcome the guards route.
754
690
  - kind: command.gate
755
691
  options:
756
692
  id: review-verdict
757
693
  executable: "${vars.spurBin}"
758
- args: ["task", "verdict", "${vars.wbs}", "--from-answer", ".spur/run/${vars.__runId}-review-cert.md", "--replay"]
694
+ args: ["task", "verdict", "${vars.wbs}", "--from-answer", ".spur/run/${vars.__runId}-review-cert.md"]
759
695
  resultFile: .spur/run/${vars.__runId}-review-verdict.status
760
696
  softFail: true
761
- # 1319 R4: the replay runs up to 5 cited suites at 120 s each; the action bound is
762
- # the same headroom the review hop gets, so a slow cited suite cannot pin the visit.
763
- timeoutMs: "900000"
764
- # (e) one fold, one token: PASS requires the typed verdict PASS, the artifact's own
765
- # PASS, and a clean review gate (open P1-P3 finding or an uncited claim blocks it).
697
+ # Derivation only (no `--replay`: the gate already ran the tests for this tree).
698
+ timeoutMs: "120000"
699
+ # (e) one fold, one token: PASS requires the typed verdict PASS and the artifact's own PASS.
766
700
  # Fail-closed: any missing/corrupt input leaves FAIL, so the transitions can route a
767
701
  # non-PASS review into remediation instead of dead-ending the run.
768
702
  # Idempotent re-entry: the answer file is consumed as a stage input, so a second visit to
769
703
  # this state has no answer to re-judge. An ABSENT answer is not new evidence against a
770
- # PASS — the durable `<wbs>-verdict.json` and the residual gate still have to pass — so the
704
+ # PASS — the durable `<wbs>-verdict.json` still has to pass — so the
771
705
  # run-scoped verdict status is consulted only when an answer is actually present (without
772
706
  # this, re-entry overwrote a PASS with FAIL and closed the task's run as `failed`).
773
707
  - kind: shell
774
708
  options:
775
709
  command: >-
776
- mkdir -p .spur/run; S=FAIL; if [ ! -f ".spur/run/$__runId-review-answer.txt" -o "$(cat ".spur/run/$__runId-review-verdict.status" 2>/dev/null)" = PASS ]; then [ "$(jq -r '.verdict // empty' ".spur/run/$wbs-verdict.json" 2>/dev/null)" = PASS ] && $spurBin task residual "$wbs" review-gate --no-logo >&2 && S=PASS; fi; printf '%s\n' "$S" > ".spur/run/$wbs-review.status"; exit 0
777
- # (d) record the review severity as the lane decision's outcome (1263 R3b): the
778
- # question `task-semantic-risk` answered is "is this risky?", and the Review table's
779
- # worst finding is the answer as observed. Resolved rows count (a fixed P2 was still
780
- # risky). First write wins, so a later review visit changes nothing.
781
- - kind: shell
782
- options:
783
- command: >-
784
- [ -f ".spur/run/$wbs-semantic-risk.decision" ] || exit 0; ID="$(jq -r '.invocationId // empty' ".spur/run/$wbs-semantic-risk.decision" 2>/dev/null)"; [ -n "$ID" ] || exit 0; $spurBin decision outcome "$ID" --review-severity "$wbs" --source "task-pipeline review" --no-logo >/dev/null 2>&1; exit 0
710
+ mkdir -p .spur/run; S=FAIL; if [ ! -f ".spur/run/$__runId-review-answer.txt" -o "$(cat ".spur/run/$__runId-review-verdict.status" 2>/dev/null)" = PASS ]; then [ "$(jq -r '.verdict // empty' ".spur/run/$wbs-verdict.json" 2>/dev/null)" = PASS ] && S=PASS; fi; printf '%s\n' "$S" > ".spur/run/$wbs-review.status"; exit 0
785
711
  # (d) Operator approval as a DECISION POINT, not a state (D65 1239 R1/R5, ADR-140
786
712
  # item 5): interactively the operator responder answers; under `profile=auto` the
787
713
  # declared binding answers (evidence status var, catalog fallback when the maker
@@ -805,21 +731,12 @@ states:
805
731
  options:
806
732
  command: >-
807
733
  mkdir -p .spur/run; A=APPROVED; if [ "$profile" = auto ]; then [ "$__hitlAnswer" = no ] && A=DENIED; else [ "$__hitlAnswer" = yes ] || A=DENIED; fi; printf '%s\n' "$A" > ".spur/run/$wbs-approval.status"; exit 0
808
- # 1319 R5(b): one inline re-review token, decided AFTER every fold above so deleting the
809
- # stale cert/answer cannot make a fold read the previous visit's verdict. Run-scoped, so a
810
- # stale file from an earlier run can never trigger a retry; `-retried` makes it once only.
811
- # A second lint failure leaves the token NONE and routes to remediate exactly as today.
812
- - kind: shell
813
- options:
814
- command: >-
815
- if [ -s ".spur/run/$wbs-verdict-lint.txt" ] && [ ! -f ".spur/run/$__runId-review-lint-retried" ]; then touch ".spur/run/$__runId-review-lint-retried"; printf 'RETRY\n' > ".spur/run/$__runId-review-lint-retry"; rm -f ".spur/run/$__runId-review-cert.md" ".spur/run/$__runId-review-answer.txt"; else printf 'NONE\n' > ".spur/run/$__runId-review-lint-retry"; fi; exit 0
816
734
 
817
735
  - id: done
818
736
  description: >
819
737
  Terminal — the task is closed deterministically. onEnter performs the close the
820
738
  former `record` state owned: `spur task record` writes Testing/Review from the review
821
- verdict and flips the requirement/AC boxes it proves, the residual sweep folds any
822
- leftover, and `spur task update <wbs> done --no-lifecycle` runs the verb's own
739
+ verdict and flips the requirement/AC boxes it proves, and `spur task update <wbs> done --no-lifecycle` runs the verb's own
823
740
  target-aware `task check` as the completion gate — a task whose boxes or evidence do
824
741
  not certify cannot reach the stored `done` status.
825
742
  onEnter:
@@ -847,38 +764,6 @@ states:
847
764
  options:
848
765
  command: >-
849
766
  if [ "$deferFeatureSync" = "true" ]; then echo "feature sync deferred to batch integration" >> ".spur/run/$wbs-report.txt"; else FID="$($spurBin task show $wbs --json 2>/dev/null | jq -r '(.feature_id // .frontmatter.feature_id) // empty' 2>/dev/null)"; if [ -n "$FID" ]; then $spurBin feature sync "$FID" --json || echo "feature sync failed — re-run: $spurBin feature sync $FID" >> ".spur/run/$wbs-report.txt"; else echo "Orphan task $wbs — no feature_id linked — proposal: consider linking to a parent feature." >> ".spur/run/$wbs-report.txt"; fi; fi; exit 0
850
- # (d) F96 (0983): `spur task residual` owns the sweep; shell only sequences it. The
851
- # scan runs AFTER `task record` flipped the verdict-proven boxes, or a proven-but-
852
- # unticked box folds every good run to PARTIAL and the done gate is unreachable
853
- # (0967). Any box still unchecked post-record stays blocking.
854
- # ONE hard action (scan+fold): a scanner crash fails the close closed.
855
- - kind: shell
856
- options:
857
- command: >-
858
- $spurBin task residual "$wbs" scan --no-logo && $spurBin task residual "$wbs" fold --no-logo
859
- # (e) F96 R5 (0983): a sweep downgrade must reach the task record — re-record from
860
- # the FINAL (post-fold) artifact so Testing carries the downgraded verdict; a failed
861
- # task must not retain a stale PASS Testing narrative. Soft (exit 0): the downgrade
862
- # itself fails the re-assert below.
863
- # 0980: --no-lifecycle for the same orphan reason as the main record command.
864
- - kind: shell
865
- options:
866
- command: >-
867
- verdict="$(jq -r .verdict .spur/run/$wbs-verdict.json 2>/dev/null)"; if [ -n "$verdict" ] && [ "$verdict" != PASS ]; then $spurBin task record $wbs --solution-from-diff --transition testing --no-lifecycle || echo "post-downgrade re-record failed — re-run: $spurBin task record $wbs" >&2; fi; exit 0
868
- # (e) The completion contract, re-asserted after the sweep: a post-record residual
869
- # downgrade must block the close (hard action, no `; exit 0`). The verdict artifact is
870
- # the review's own evidence, not a re-captured digest — the old proof re-assertion is
871
- # gone, the fail-closed check on the verdict it certifies is not.
872
- - kind: shell
873
- options:
874
- command: 'jq -e ".verdict == \"PASS\"" .spur/run/$wbs-verdict.json >/dev/null'
875
- # (d) F96 R4 (0950): `spur task residual` owns the settle.
876
- # Soft best-effort (exit 0) — runs after certification and must never change
877
- # the outcome; failure prints the re-run command.
878
- - kind: shell
879
- options:
880
- command: >-
881
- $spurBin task residual "$wbs" settle --no-logo || echo "residual-settle failed — re-run: spur task residual $wbs settle" >&2; exit 0
882
767
  # (c) command.gate owns the done transition with classified transient retry. The verb
883
768
  # runs its own target-aware `task check --as done` (the completion gate) before the
884
769
  # status write, so a task that does not certify a `done` target never stores `done`.
@@ -921,13 +806,6 @@ states:
921
806
  printf '\n## Escalation bound reached\n\nThe implement step asked an operator question %s times (bound %s) and ended without a resumable answer. Unanswered question:\n\n' "$n" "$maxEscalations" >> .spur/run/$wbs-report.txt;
922
807
  cat .spur/run/$wbs-question.md >> .spur/run/$wbs-report.txt;
923
808
  exit 0
924
- # (d) F96 R5 (0950): `spur task residual` owns the report.
925
- # Soft (exit 0): a run that exhausts its fix budget on residuals leaves the
926
- # report + recovery line while the task stays `wip`.
927
- - kind: shell
928
- options:
929
- command: >-
930
- $spurBin task residual "$wbs" report --no-logo || echo "residual-report failed — re-run: spur task residual $wbs report" >&2; exit 0
931
809
 
932
810
  transitions:
933
811
  # ── precheck: task check --precheck (size + evidence + four-layer, 1002 R4) → implement; ──
@@ -1022,12 +900,12 @@ transitions:
1022
900
  test "$(cat ".spur/run/$wbs-test-gate.status" 2>/dev/null)" = PASS
1023
901
  - from: gate
1024
902
  to: review
1025
- description: Opt-in batch policy — the typed outcome is PASS and the gate wrote the DEFERRED (light-tier) token.
903
+ description: Incremental mode — the typed outcome is PASS and the gate wrote the DEFERRED (incremental) token.
1026
904
  guard:
1027
905
  kind: shell
1028
906
  options:
1029
- # 1111 R2: `DEFERRED` is accepted ONLY when the opt-in var is "true" — with the
1030
- # default "false" a stray DEFERRED file fails the guard exactly like a corrupt one.
907
+ # `DEFERRED` is accepted ONLY in incremental mode — in full mode ("false") a stray
908
+ # DEFERRED file fails the guard exactly like a corrupt one.
1031
909
  command: >-
1032
910
  test "$deferQualityGate" = "true" &&
1033
911
  test "$(cat ".spur/run/$__runId-gate.status" 2>/dev/null)" = PASS &&
@@ -1104,16 +982,6 @@ transitions:
1104
982
  kind: shell
1105
983
  options:
1106
984
  command: 'test "$(cat ".spur/run/$wbs-approval.status" 2>/dev/null)" = DENIED'
1107
- # 1319 R5(c): one inline re-review when the answer LINT failed, before any remediate hop is
1108
- # spent. Declared after the denial route and before `retry-exhausted` because a lint retry is
1109
- # not a repair attempt (it does not consume the shared fix-attempt counter). First match wins.
1110
- - from: review
1111
- to: review
1112
- description: The verdict lint rejected the answer's shape — re-issue the review prompt once with the lint findings instead of buying a remediate hop.
1113
- guard:
1114
- kind: shell
1115
- options:
1116
- command: 'test "$(cat ".spur/run/$__runId-review-lint-retry" 2>/dev/null)" = RETRY'
1117
985
  - from: review
1118
986
  to: failed
1119
987
  terminalReason: retry-exhausted
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.4.33",
3
+ "version": "0.4.34",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -174,23 +174,18 @@ fails it. One explicit mapping, no transcription step anywhere downstream:
174
174
  Severity words stay visible in the same cell — the mapping adds the machine-readable label, it does
175
175
  not replace the semantics.
176
176
 
177
- **Disposition column (1089, E71).** The findings table carries a `Disposition` cell. It is not
178
- cosmetic: the record stage's residual sweep reads these rows (`parseReviewFindings` in
179
- `packages/app/src/services/residual-scan.ts`), and a `P1`–`P3` row with no disposition classifies as
180
- **blocking**, which downgrades a PASS verdict to PARTIAL and closes the `done` gate. A finding you
181
- resolved inside the same task therefore says so in that cell — `RESOLVED`/`FIXED`/`DONE` (optionally
182
- with the commit) drops the row from the sweep; `DEFER(<reason>)` reclassifies a P3 as deferrable
183
- (never P1/P2). Prose such as "Closed." inside the *Finding* cell does not count — the sweep reads
184
- the cell, not the sentence. `OPEN` states "still live", and `ACCEPTED` marks a non-blocking P4
185
- observation you are deliberately not acting on; both behave as an ordinary open row.
186
-
187
- **Pipeline cost of an open finding (1122).** The review certification runs this same classification
188
- (`spur task residual <wbs> review-gate`): a PASS whose table still carries an open P1-P3 row is a
189
- non-PASS review, so the `review-failure-class` decision routes it to `remediate` instead of `done`.
190
- Open P1-P3 findings block `done`; fixing one invalidates the certified digest and re-runs
191
- gate → review on a fresh digest. Only P4 rows and `DEFER`-ed P3 rows are wrap residuals — disposition the finding in
192
- its cell now, while you hold the context, instead of leaving the cost to be rediscovered after a
193
- full cycle.
177
+ **Disposition column (1089, E71).** The findings table carries a `Disposition` cell, and it is
178
+ your statement of each finding's state: `RESOLVED`/`FIXED`/`DONE` (optionally with the commit) for a
179
+ finding closed inside the same task, `DEFER(<reason>)` for a P3 you deliberately carry (never
180
+ P1/P2), `ACCEPTED` for a P4 observation you are not acting on, `OPEN` for one still live. Nothing
181
+ parses these rows — your `Verdict:` line is the only gate — so the two must agree: a table with an
182
+ open P1-P3 row carries a non-PASS verdict.
183
+
184
+ **Pipeline cost of an open finding (1122).** A non-PASS review routes through the
185
+ `review-failure-class` decision to `remediate` instead of `done`. Open P1-P3 findings block `done`;
186
+ fixing one invalidates the certified digest and re-runs gate → review on a fresh digest. Only P4 rows
187
+ and `DEFER`-ed P3 rows are wrap residuals — disposition the finding in its cell now, while you hold
188
+ the context, instead of leaving the cost to be rediscovered after a full cycle.
194
189
 
195
190
  **Section-relative headings.** In WBS mode (standalone or pipeline) the report body is written
196
191
  *into* the task's `### Review` section, so every heading inside it MUST be `####` or deeper. A
@@ -228,32 +223,10 @@ FAIL, PARTIAL, a missing line, a line split across two lines — routes the run
228
223
  `review-failure-class` decision into the bounded `remediate` hop. Keep it on one line, in that shape, in every
229
224
  review answer; never restate it as prose only.
230
225
 
231
- **State `Confidence: HIGH | MEDIUM | LOW` — the gate derives it from your dispositions (ADR-141).**
232
- The review report carries exactly one top-level `Confidence:` line, and it is **cross-checked**: a
233
- stated level that disagrees with the table is a blocking `review-contract` violation, so a PASS
234
- carrying citation-free or untested claims cannot launder itself as `HIGH`.
235
-
236
- | Derived | When the table says so |
237
- | --- | --- |
238
- | `LOW` | the verdict is not PASS, or an open (or undispositioned) P1–P3 row survives |
239
- | `HIGH` | PASS with every finding closed (`RESOLVED` / `FIXED` / `DONE`) and no carried residual |
240
- | `MEDIUM` | PASS with carried residuals only — `DEFER(...)`, `ACCEPTED`, or an open P4 |
241
-
242
- Derivation: `packages/app/src/services/answer-confidence.ts` (shared with the verify answer). This
243
- does not revive the retired self-grade: the level records **evidence composition** — what a reader
244
- would otherwise have to re-derive from the rows — and the gate recomputes it rather than trusting it.
245
- No verdict-artifact field, no receipt level, no per-finding grade, and no operator ack var exists
246
- (ADR-138 stands for every other carrier). Evidence gaps still belong in the report body: name them,
247
- then let the dispositions decide the level.
248
-
249
- **Two artifacts, one `Confidence:` line each (1254).** The body you write into `### Review` states
250
- the disposition-derived level above. When the pipeline also captures your final answer as the
251
- certification input for `spur task verdict`, that text is a *second* artifact with a *different*
252
- consumer, and it carries the **evidence-derived** level (`deriveVerifyConfidence`): PASS with every
253
- AC row MET on executable evidence (`test`/`command`) is HIGH; a citation-only (`static-ref`),
254
- `manual-review`, `llm-judge` or `N/A` AC is MEDIUM; a non-PASS verdict or any PARTIAL/UNMET row is
255
- LOW. It ends with exactly one plain `Verdict: PASS|PARTIAL|FAIL` line preceded by both tables, in
256
- this exact shape:
226
+ **The certification answer.** No `Confidence:` line exists on any carrier — name evidence gaps in
227
+ the report body instead. When the pipeline captures your final answer as the certification input
228
+ for `spur task verdict`, it ends with exactly one plain `Verdict: PASS|PARTIAL|FAIL` line preceded
229
+ by both tables, in this exact shape:
257
230
 
258
231
  | Req | Status | Evidence |
259
232
  | --- | --- | --- |
@@ -267,15 +240,10 @@ One row per declared requirement (`MET | PARTIAL | UNMET`, none omitted or merge
267
240
  acceptance criterion (`MET | PARTIAL | UNMET | N/A`), with the evidence-type token alone in its own
268
241
  cell — `test | command | static-ref | manual-review | llm-judge | n/a`, or a `+` compound when more
269
242
  than one kind applies. Evidence cells cite a repo-root-relative `path:line` or a named test/command.
270
- An absolute path outside the repo (`/tmp/<report>.md:12`) is an unresolved anchor that blocks the
271
- done projection (`L4.anchor-unresolved`); cite the owning source or a corpus artifact instead. AC
243
+ Cite the owning source or a corpus artifact, never a scratch path. AC
272
244
  identities stay the task's own (checklist label, title, bold head, `Scenario:` prefix, bracket tag or
273
245
  declared `AC-N` alias): a linked feature's scenario title covers the task that implements it, and a
274
246
  task-local criterion is legal as-is — it needs no feature credit.
275
- Never repeat the section's disposition-derived `Confidence:` line here: a second `Confidence:` line is
276
- a `confidence-duplicate` rejection of the whole answer, and so is a level the rows do not derive. A
277
- PASS with executable AC and one accepted P4 therefore certifies as `Confidence: HIGH` in the answer
278
- while the section keeps `Confidence: MEDIUM`.
279
247
 
280
248
  **Source citations for external claims (one claim contract, 2026-10-12).** A Finding cell or a
281
249
  Functional Traceability `Evidence` cell that asserts an external API/library claim carries a
@@ -289,18 +257,8 @@ finding's subject, not the claim's source. Cell-free receipts (test counts, revi
289
257
  assert no external claim and need no citation. An uncited external claim is an assumption: name it
290
258
  as an explicit gap in the report body (above), so a reader can re-check it.
291
259
 
292
-
293
- **Machine-checked at the gate (findings table).** `spur task residual <wbs> review-gate` applies the same two
294
- predicates to every finding row: a Finding cell carrying one of the verify lint's claim markers with
295
- no citation form becomes a **blocking** `review-contract` item, so `review → done` stops and the
296
- run enters the `review-failure-class` decision. The `Location` column is deliberately not consulted as a citation —
297
- that is the rule stated above, and a gate that accepted it would pass every row that has an anchor.
298
- Functional Traceability `Evidence` cells stay reviewer-enforced (SECUA review): they are free prose
299
- in a table the gate does not parse.
300
-
301
260
  **No findings.** Never invent a defect to populate the table. Emit one substantive `P4 (advisory)`
302
- row that states what was reviewed and what was found — placeholder cells (empty, `—`, `n/a`) are
303
- rejected by the checker, and so they should be:
261
+ row that states what was reviewed and what was found — placeholder cells (empty, `—`, `n/a`) tell a reader nothing:
304
262
 
305
263
  ```markdown
306
264
  | # | Priority | Dimension | Finding | Location | Disposition |
@@ -30,13 +30,6 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
30
30
 
31
31
  Follow the inline procedure in [dev-operations.md](../skills/spur-dev/references/dev-operations.md#10-fixall) (fixall). Pipeline signal: when `--gate-log` is set (the `remediate` hop), run no full gate — the `gate` state's recheck immediately after is the deciding run (R4, task 0483).
32
32
 
33
- When dispatched from the pipeline's `remediate` hop (F96, task 0950), the gate log carries a
34
- `residual artifact` block (`<wbs>-residuals.json`) and the findings file already
35
- contains the merged residual anchors: treat those items as fix targets, in order,
36
- alongside gate anchors. The fixer may write `.spur/run/<wbs>-residual-deferrals.json`
37
- — one `{id, reason}` entry per item — but only for P3 findings or diff markers it
38
- cannot fix inside the task; the done stage settles deferrals into follow-up tasks.
39
-
40
33
  The `remediate` hop is entered only after a `fix` decision (`failure-class` on a red gate,
41
34
  `review-failure-class` on a non-PASS review). A `stop` decision ends the run before this command
42
35
  runs, so do not invoke it by hand to overrule a `stop`
@@ -44,7 +44,7 @@ Put the target first: a bare token after `--worktree` is read as the worktree na
44
44
  - **WBS mode (per task target):** `Skill(skill="sp:functional-review", args="<wbs> $FLAGS")` + `Skill(skill="sp:code-verification", args="review <wbs> $FLAGS")` + `Skill(skill="sp:code-improvement", args="<wbs> $FLAGS")` (functional traceability + SECUA framework + architectural depth — each skill receives its one resolved WBS exactly once plus `$FLAGS` = `$ARGUMENTS` minus the selector (`--tasks`/`--feature`/`--scope` or positional) and its value, never the raw selector, and returns a review fragment only — F92 0593 R1)
45
45
  - **Task fan-out (`--tasks` / `--feature`).** One WBS-mode review per task in the frozen set. Tasks in `backlog`/`todo`/`blocked` are reported NOT-STARTED and skipped (§ 3a outcome vocabulary); a per-task failure does not stop the remaining tasks. Each implemented task gets its own merged `## Review`; the run ends with a combined summary table (WBS, verdict, P1/P2 counts).
46
46
  - Coordinator = this session. No `sp:super-reviewer` subagent is dispatched: act as the review coordinator defined in [agents/super-reviewer.md](../agents/super-reviewer.md) — merge the fragments per its Output Format (native `P1`–`P4` priority cells, section-relative headings) and, for every task target (standalone or pipeline), write that task's combined `## Review` via `spur task update <wbs> --section Review --from-file`. Path mode emits advisory output only.
47
- - **Answer contract (pipeline dispatches).** The captured review answer (`.spur/run/<runId>-review-answer.txt`) must carry exactly one `Verdict: PASS|FAIL` line **and** the requirement traceability table `| Req | Status | Evidence |` (D65 1239: `verify` merged into `review`, so this answer is the pipeline's only verification record). It also carries exactly one `Confidence: HIGH|MEDIUM|LOW` line whose level its own findings derive (ADR-141 — no open P1–P3 and no carried residual is HIGH, a carried residual is MEDIUM, an open P1–P3 or a non-PASS verdict is LOW); the gate recomputes the level and rejects a disagreement, so stating a level the dispositions do not support fails the review. `spur task verdict --from-answer` owns the parse — it lints the table and the level, writes the canonical `.spur/run/<wbs>-verdict.json` that `spur task record` reads for box flips and `## Testing`, and a non-zero exit fails the review stage; never re-implement the parse in shell. On a feature-linked task, key each AC row to its feature scenario (scenario title, `AC-N` alias, `(feature R<n>)` or `[R<n>]`) — a row keyed only by a task requirement id credits no scenario at the feature done gate.
47
+ - **Answer contract (pipeline dispatches).** The captured review answer (`.spur/run/<runId>-review-answer.txt`) must carry exactly one `Verdict: PASS|FAIL` line **and** the requirement traceability table `| Req | Status | Evidence |` (D65 1239: `verify` merged into `review`, so this answer is the pipeline's only verification record). No `Confidence:` line exists; with an open P1–P3 finding the verdict must be non-PASS. `spur task verdict --from-answer` owns the parse — it writes the canonical `.spur/run/<wbs>-verdict.json` that `spur task record` reads for box flips and `## Testing`, and a non-zero exit fails the review stage; never re-implement the parse in shell. On a feature-linked task, key each AC row to its feature scenario (scenario title, `AC-N` alias, `(feature R<n>)` or `[R<n>]`) so `record` can tick the aliased AC line.
48
48
  - **Path mode (per path target):** `Skill(skill="sp:code-verification", args="review <path> $FLAGS")` + `Skill(skill="sp:code-improvement", args="<path> $FLAGS")` (advisory SECUA quality + architectural depth — one normalized path forwarded once plus `$FLAGS`; performs no task mutation). Under `--scope <path>[,<path>]`: paths must exist (exit 2 otherwise), are normalized, duplicates dropped and nested paths collapse to the ancestor (`--scope apps,apps/cli` → `apps`); each surviving path gets a sub-review (Step 3p path scope), eligible for native-subagent dispatch per [dispatch-surface.md](../skills/parallel-execution/references/dispatch-surface.md); the coordinator merges the sub-reviews, runs **one** cross-path architecture pass (`sp:code-improvement` over the inter-path imports), and emits **one** advisory report. No task mutation.
49
49
  - **Positional alias (deprecated).** A positional `<wbs|path>` is accepted for one release: a `^\d{4}$` token with a resolvable task behaves as `--tasks <wbs>`; an existing path as `--scope <path>`; otherwise exit 2. Print a deprecation warning naming the replacement selector; removal is a follow-up.
50
50
  - `--triage`: after the review returns, run the triage protocol in [dev-operations.md § 2. review](../skills/spur-dev/references/dev-operations.md#2-review) **inline in this session** — the review skills and `sp:super-reviewer` stay report-only. With multiple targets, bucket the findings across all targets once (identical `file:line` findings deduped before bucketing). `Edit`/`Write` in `allowed-tools` exist only for these direct fixes.