@gobing-ai/spur 0.3.72 → 0.3.74

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 (101) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +48 -27
  3. package/config/pipeline-budgets.json +9 -5
  4. package/config/plugin-scripts.json +4 -0
  5. package/config/proportional-route-table.ts +155 -0
  6. package/config/rules/strict/runtime-boundaries.yaml +1 -1
  7. package/config/rules/structure/protected-files.yaml +3 -0
  8. package/config/task-pipeline-proportional-migration-plan.md +79 -0
  9. package/config/templates/AGENTS.md +5 -0
  10. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
  11. package/config/workflows/basic.yaml +5 -2
  12. package/config/workflows/docs-pipeline.yaml +101 -54
  13. package/config/workflows/feature-dev.yaml +57 -27
  14. package/config/workflows/feature-lifecycle.yaml +1 -0
  15. package/config/workflows/history-anatomy.yaml +8 -4
  16. package/config/workflows/idea-pipeline.yaml +66 -33
  17. package/config/workflows/pr-review.yaml +31 -8
  18. package/config/workflows/task-lifecycle.yaml +28 -15
  19. package/config/workflows/task-pipeline.yaml +929 -844
  20. package/config/workflows/wayfinder-resolution.yaml +124 -90
  21. package/config/workflows/wrapup-pipeline.yaml +290 -107
  22. package/package.json +9 -9
  23. package/plugins/sp/plugin.json +1 -1
  24. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +2 -4
  25. package/plugins/sp/scripts/daily-summary/daily-summary.ts +2 -4
  26. package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
  27. package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
  28. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +185 -0
  29. package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
  30. package/plugins/sp/scripts/pr-reviewing.ts +7 -1
  31. package/plugins/sp/scripts/task-evidence-precheck.ts +1 -1
  32. package/plugins/sp/scripts/transition-shim-check.ts +6 -6
  33. package/plugins/sp/scripts/verify-answer-lint.ts +4 -0
  34. package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
  35. package/plugins/sp/skills/spur-cli/references/self.md +17 -3
  36. package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
  37. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +23 -0
  38. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
  39. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  40. package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
  41. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  42. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -2
  43. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +20 -0
  44. package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
  45. package/schemas/state-machine-workflow.schema.json +3 -1
  46. package/schemas/transition-flow-workflow.schema.json +3 -1
  47. package/spur.js +12639 -10106
  48. package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.C60RJZRj.js} +78 -77
  49. package/web/_astro/BoardApp.ymfj1EHA.js +1 -0
  50. package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.Guk8VuNu.js} +1 -1
  51. package/web/_astro/{arc.BySSh34M.js → arc.CAZtlWJM.js} +1 -1
  52. package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.9-XbF_Tz.js} +1 -1
  53. package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.DC4WLx3L.js} +1 -1
  54. package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.JgjUQMgG.js} +1 -1
  55. package/web/_astro/channel.Bxxe2Byc.js +1 -0
  56. package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.D_JFiXa-.js} +1 -1
  57. package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.Bk_RxoeT.js} +1 -1
  58. package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.a_Rj2VxL.js} +1 -1
  59. package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.C2NoS1U6.js} +1 -1
  60. package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.D1Xn6CNa.js} +1 -1
  61. package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.BhJrSBsX.js} +1 -1
  62. package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.ByVGduYQ.js} +1 -1
  63. package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.B47Paunq.js} +1 -1
  64. package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.BERMSD5C.js} +1 -1
  65. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.BERMSD5C.js} +1 -1
  66. package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.O_rHGlhU.js} +1 -1
  67. package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.qCW2GdNp.js} +1 -1
  68. package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.C5Y4lH_u.js} +1 -1
  69. package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.DxZmRXxF.js} +1 -1
  70. package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.qSxZeYSK.js} +1 -1
  71. package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.-7vl3bXs.js} +1 -1
  72. package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.BHVV-UH3.js} +1 -1
  73. package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DHAuW9sK.js} +1 -1
  74. package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.DMRLQwPN.js} +1 -1
  75. package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.B3EpPp_8.js} +1 -1
  76. package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.BgUrExXM.js} +1 -1
  77. package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.CU-9yVN4.js} +1 -1
  78. package/web/_astro/{index.B5MTfe7k.css → index.BhvM3djt.css} +1 -1
  79. package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.CvVTCRLe.js} +1 -1
  80. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.BUzOfKPf.js} +1 -1
  81. package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.BBTb7ziz.js} +1 -1
  82. package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.CTQQr70i.js} +1 -1
  83. package/web/_astro/{linear.D7uqzENp.js → linear.CLKlJPiS.js} +1 -1
  84. package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.BA4wFhLP.js} +4 -4
  85. package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.t1vG2l5e.js} +1 -1
  86. package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.C8zJhyTu.js} +1 -1
  87. package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DqYYvb7T.js} +1 -1
  88. package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.lzfDZ9nI.js} +1 -1
  89. package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.lm6N5ORM.js} +1 -1
  90. package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.E6_IV4Dj.js} +1 -1
  91. package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.Bnv6Ok2p.js} +1 -1
  92. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js} +1 -1
  93. package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BhRgqSLa.js} +1 -1
  94. package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.pjb5DMC9.js} +1 -1
  95. package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.CW20KUng.js} +1 -1
  96. package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.vX9_yuMl.js} +1 -1
  97. package/web/index.html +2 -2
  98. package/config/corpus-baseline.json +0 -1637
  99. package/config/workflow-composition-baseline.json +0 -576
  100. package/web/_astro/BoardApp.E12MFjOS.js +0 -1
  101. package/web/_astro/channel.5cYKr5cs.js +0 -1
@@ -11,12 +11,15 @@
11
11
  # - requireDiff fails silent no-op drafts
12
12
  # - HITL answer routing is exhaustive (yes/no/cancel)
13
13
  # - verify measures the deliverable read-only (answer file + `spur task verdict` + proof
14
- # digest bracket) record done is fail-closed on measured PASS, never on a stub
14
+ # digest bracket); recording happens only AFTER measured PASS (0769 ordering:
15
+ # precheck → draft → docs-review → verify → record → done) and `record → done` is
16
+ # fail-closed on the captured record result + re-asserted verdict — never on a stub
15
17
 
16
18
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
19
+ version: "1"
17
20
  kind: state-machine
18
21
  name: docs-pipeline
19
- description: "Docs-only execution pipeline (precheck -> draft -> docs-review -> record -> verify -> done)"
22
+ description: "Docs-only execution pipeline (precheck -> draft -> docs-review -> verify -> record -> done)"
20
23
  iterationBound: 12
21
24
  initialState: precheck
22
25
  terminalStates:
@@ -34,13 +37,15 @@ vars:
34
37
  stepTimeoutMs: "1800000"
35
38
  # Proof-state bracket (task 0704, mirroring task-pipeline 0612/0703). `proofDigest` is the
36
39
  # canonical capture at verify entry; `proofDigestNow` is the live re-capture compared against
37
- # it by the `verify → done` guard so verifier-time mutation fails closed (R4/R5).
40
+ # it by the `verify → record` guard so verifier-time mutation fails closed (R4/R5).
38
41
  proofDigest: ""
39
42
  proofDigestNow: ""
40
43
  # `taskSpecPath` carries the task file path because `docs/tasks*` is excluded from the digest's
41
44
  # git-tree half (task 0704 R4); resolved per-run by the verify state's taskpath shell step.
42
45
  taskSpecPath: ""
43
46
  __hitlAnswer: ""
47
+ # R8 (0366): injected by WorkflowAppService.run(); run-scopes every temporary capture.
48
+ __runId: ""
44
49
 
45
50
  states:
46
51
  - id: precheck
@@ -49,14 +54,16 @@ states:
49
54
  (D5-J, ADR-069). Both gates run `softFail: true`, so a red probe records FAIL and lets
50
55
  the transitions route to `failed` instead of aborting the run — the shipped action
51
56
  schema exposes no `onError`, so soft mode is what preserves that routing.
52
- The canonical `.spur/run/$wbs-docs-precheck.status` is the AND of the two gate files.
57
+ The canonical `.spur/run/$__runId-docs-precheck.status` is the AND of the two gate files.
58
+ (0769: temporary captures are run-scoped — `.spur/run/<runId>-docs-*` — so concurrent
59
+ runs on one task never share or clobber a capture.)
53
60
  onEnter:
54
61
  - kind: command.gate
55
62
  options:
56
63
  id: docs-precheck-task
57
64
  executable: "${vars.spurBin}"
58
65
  args: ["task", "check", "${vars.wbs}"]
59
- resultFile: .spur/run/${vars.wbs}-docs-precheck-task.status
66
+ resultFile: .spur/run/${vars.__runId}-docs-precheck-task.status
60
67
  softFail: true
61
68
  timeoutMs: 120000
62
69
  - kind: command.gate
@@ -64,16 +71,16 @@ states:
64
71
  id: docs-precheck-agent
65
72
  executable: "${vars.spurBin}"
66
73
  args: ["agent", "doctor", "${vars.agent}"]
67
- resultFile: .spur/run/${vars.wbs}-docs-precheck-agent.status
74
+ resultFile: .spur/run/${vars.__runId}-docs-precheck-agent.status
68
75
  softFail: true
69
76
  timeoutMs: 120000
70
77
  - kind: shell
71
78
  options:
72
79
  command: >-
73
- if test "$(cat .spur/run/$wbs-docs-precheck-task.status 2>/dev/null)" = PASS &&
74
- test "$(cat .spur/run/$wbs-docs-precheck-agent.status 2>/dev/null)" = PASS;
75
- then printf 'PASS\n' > ".spur/run/$wbs-docs-precheck.status";
76
- else printf 'FAIL\n' > ".spur/run/$wbs-docs-precheck.status"; fi
80
+ if test "$(cat .spur/run/$__runId-docs-precheck-task.status 2>/dev/null)" = PASS &&
81
+ test "$(cat .spur/run/$__runId-docs-precheck-agent.status 2>/dev/null)" = PASS;
82
+ then printf 'PASS\n' > ".spur/run/$__runId-docs-precheck.status";
83
+ else printf 'FAIL\n' > ".spur/run/$__runId-docs-precheck.status"; fi
77
84
  - kind: note
78
85
  options:
79
86
  message: "Docs pipeline start for task ${vars.wbs}."
@@ -108,44 +115,39 @@ states:
108
115
  options:
109
116
  prompt: "Approve docs-only deliverable for task ${vars.wbs}?"
110
117
 
111
- - id: record
112
- description: >
113
- Record the docs-only outcome into the task file. The task record verb backfills
114
- Solution from the diff and moves the task to testing; the done gate still runs
115
- the normal task matrix check before completion.
116
- onEnter:
117
- - kind: shell
118
- options:
119
- command: "$spurBin task record $wbs --solution-from-diff --transition testing"
120
-
121
- - id: done
122
- description: Terminal — docs deliverable measured, reviewed, and recorded.
123
- onEnter:
124
- # R1 (0704): no synthetic verdict writer. The verdict artifact is produced by measured
125
- # verification in `verify` and registered there; done only performs the guarded
126
- # transition. Override path remains: --force-done --reason.
127
- - kind: shell
128
- options:
129
- command: "$spurBin task update $wbs done --no-lifecycle"
130
- - kind: note
131
- options:
132
- message: "Docs pipeline complete for task ${vars.wbs}."
133
118
  - id: verify
134
119
  description: >
135
120
  Read-only measured verification (task 0704, R2/R4/R7): dispatch /sp:dev-verify --fix none
136
121
  with an answer file, derive the ONE standard verdict via `spur task verdict`, register the
137
122
  standard verify-verdict artifact, and bracket the verifier with proof-input digest capture/compare so any
138
123
  task-spec or document mutation during certification fails closed (R4). No docs assurance
139
- subclass — PASS semantics are identical to task-pipeline (R7).
124
+ subclass — PASS semantics are identical to task-pipeline (R7). 0769: verification now
125
+ PRECEDES recording; the `verify → record` guard compares the proof bracket, and the
126
+ verdict artifact keeps its `.spur/run/<wbs>-verdict.json` compatibility path with
127
+ runId + definitionDigest stamped through the existing proof/run-artifact contract.
140
128
  onEnter:
129
+ # 0769: clear any stale current-run answer before the fresh-session verifier writes it.
130
+ - kind: shell
131
+ options:
132
+ command: "mkdir -p .spur/run && rm -f .spur/run/$__runId-docs-verify-answer.txt"
141
133
  # R4 (0703 R6 precedent): resolve the task-spec path BEFORE capture. `docs/tasks*` is
142
134
  # excluded from the digest's git-tree half, so the spec is folded in via `taskFile`.
143
135
  - kind: shell
144
136
  options:
145
- command: '$spurBin task path $wbs --json 2>/dev/null | jq -r ".path // .filePath // empty" > ".spur/run/$wbs-docs-taskpath.txt" || true; exit 0'
137
+ # 0760 R1 (sibling of 0751 R2): task-path lookup fails closed. Drop
138
+ # `2>/dev/null`, `|| true`, and the forced `exit 0` so an unresolved
139
+ # wbs exits non-zero with a named message. The resolved path folds
140
+ # into the proof digest via `taskFile:` below; a silent miss would
141
+ # degrade the docs-pipeline proof to tree-only (same hazard 0751 R2
142
+ # removed from task-pipeline). (0769: run-scoped capture path.)
143
+ command: >-
144
+ set -e;
145
+ task_path="$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')";
146
+ if [ -z "$task_path" ]; then echo "docs-pipeline: task path did not resolve for wbs $wbs" >&2; exit 1; fi;
147
+ printf '%s' "$task_path" > ".spur/run/$__runId-docs-taskpath.txt"
146
148
  - kind: file.read.into-var
147
149
  options:
148
- path: .spur/run/${vars.wbs}-docs-taskpath.txt
150
+ path: .spur/run/${vars.__runId}-docs-taskpath.txt
149
151
  var: taskSpecPath
150
152
  # R4: canonical capture BEFORE the verifier runs. Same fingerprint input as the
151
153
  # re-capture below so the bracket compares like for like.
@@ -164,19 +166,19 @@ states:
164
166
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
165
167
  role: reviewer
166
168
  timeoutMs: ${vars.stepTimeoutMs}
167
- answerFile: .spur/run/${vars.wbs}-verify-answer.txt
169
+ answerFile: .spur/run/${vars.__runId}-docs-verify-answer.txt
168
170
  - kind: shell
169
171
  options:
170
- command: "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
171
- # R7: stamp the proof block into the standard verdict artifact, then register it.
172
- # Soft action + hard guard: a missing/malformed stamp fails the `verify -> done`
172
+ command: "$spurBin task verdict $wbs --from-answer .spur/run/$__runId-docs-verify-answer.txt"
173
+ # R7: stamp the proof block + run identity into the standard verdict artifact, then register
174
+ # it. Soft action + hard guard: a missing/malformed stamp fails the `verify -> record`
173
175
  # guard below, not this step.
174
176
  - kind: shell
175
177
  options:
176
178
  command: >-
177
179
  V=".spur/run/$wbs-verdict.json";
178
180
  if [ -f "$V" ] && [ -n "$proofDigest" ]; then
179
- jq --arg d "$proofDigest" '. + {proof: {digest: $d, capturePoint: "docs-verify-entry"}}' "$V" > "$V.tmp" && mv "$V.tmp" "$V";
181
+ jq --arg d "$proofDigest" --arg r "$__runId" '. + {proof: {digest: $d, capturePoint: "docs-verify-entry", runId: $r}}' "$V" > "$V.tmp" && mv "$V.tmp" "$V";
180
182
  fi;
181
183
  exit 0
182
184
  - kind: run.artifact
@@ -192,6 +194,37 @@ states:
192
194
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
193
195
  timeoutMs: ${vars.stepTimeoutMs}
194
196
 
197
+ - id: record
198
+ description: >
199
+ Record the docs-only outcome AFTER measured verification (task 0769 ordering:
200
+ draft/approval → verify → record → done — the pre-verification record could write
201
+ UNKNOWN/old Testing evidence). The verdict is already derived and proof-bracketed in
202
+ `verify`; `verify → record` only opens on measured PASS with an intact bracket. The
203
+ task record verb backfills Solution from the diff and moves the task to testing; its
204
+ result is captured to a run-scoped status file so a record denial reaches `failed`
205
+ (record → failed) and is never converted to success by an exit 0.
206
+ onEnter:
207
+ - kind: shell
208
+ options:
209
+ command: >-
210
+ if $spurBin task record $wbs --solution-from-diff --transition testing; then
211
+ printf 'PASS\n' > ".spur/run/$__runId-docs-record.status";
212
+ else
213
+ printf 'FAIL\n' > ".spur/run/$__runId-docs-record.status";
214
+ fi
215
+
216
+ - id: done
217
+ description: Terminal — docs deliverable measured, reviewed, and recorded.
218
+ onEnter:
219
+ # R1 (0704): no synthetic verdict writer. The verdict artifact is produced by measured
220
+ # verification in `verify` and registered there; done only performs the guarded
221
+ # transition. Override path remains: --force-done --reason.
222
+ - kind: shell
223
+ options:
224
+ command: "$spurBin task update $wbs done --no-lifecycle"
225
+ - kind: note
226
+ options:
227
+ message: "Docs pipeline complete for task ${vars.wbs}."
195
228
  - id: failed
196
229
  description: Terminal — docs pipeline failed or was rejected.
197
230
 
@@ -213,8 +246,8 @@ transitions:
213
246
  kind: always
214
247
 
215
248
  - from: draft
216
- to: record
217
- description: profile=auto — skip the docs-review HITL gate.
249
+ to: verify
250
+ description: "profile=auto — skip the docs-review HITL gate; verify measures BEFORE record (0769: a pre-verification record could write UNKNOWN/old Testing evidence)."
218
251
  guard:
219
252
  kind: shell
220
253
  options:
@@ -226,8 +259,8 @@ transitions:
226
259
  kind: always
227
260
 
228
261
  - from: docs-review
229
- to: record
230
- description: Docs approved — record results.
262
+ to: verify
263
+ description: "Docs approved — measure the deliverable (0769: verification precedes recording)."
231
264
  guard:
232
265
  kind: shell
233
266
  options:
@@ -247,16 +280,11 @@ transitions:
247
280
  options:
248
281
  command: 'test "$__hitlAnswer" = cancel'
249
282
 
250
- - from: record
251
- to: verify
252
- description: Recorded — measure the deliverable (R2). Structural task-check gates remain
253
- at precheck, record (wip→testing), and done (verdict-artifact guard, R3).
254
- guard:
255
- kind: always
256
-
257
283
  - from: verify
258
- to: done
259
- description: Measured PASS with the proof bracket intact (R4/R5).
284
+ to: record
285
+ description: Measured PASS with the proof bracket intact (R4/R5) — record only verified
286
+ outcomes (0769). Structural task-check gates remain at precheck, record (wip→testing),
287
+ and done (verdict-artifact guard, R3).
260
288
  guard:
261
289
  kind: shell
262
290
  options:
@@ -274,6 +302,25 @@ transitions:
274
302
  - from: verify
275
303
  to: failed
276
304
  description: Non-PASS verdict, missing/malformed answer, or digest mismatch (R5) — the run
277
- reaches failed and no PASS artifact is certified.
305
+ reaches failed and nothing is recorded.
306
+ guard:
307
+ kind: always
308
+
309
+ - from: record
310
+ to: done
311
+ description: Recorded from a verified outcome; the persisted verdict/bracket is re-asserted
312
+ so a failed `task record` (captured FAIL) never converts to done (0769).
313
+ guard:
314
+ kind: shell
315
+ options:
316
+ command: >-
317
+ test "$(cat .spur/run/$__runId-docs-record.status 2>/dev/null)" = PASS &&
318
+ test -f ".spur/run/$wbs-verdict.json" &&
319
+ test "$(jq -r '.verdict // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = PASS &&
320
+ test -n "$proofDigest" &&
321
+ test "$(jq -r '.proof.digest // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = "$proofDigest"
322
+ - from: record
323
+ to: failed
324
+ description: "Task record denied or errored (captured status not PASS) — the record denial reaches failed; the done guard above never converts it to success."
278
325
  guard:
279
326
  kind: always
@@ -9,6 +9,7 @@
9
9
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
10
10
  kind: state-machine
11
11
  name: feature-dev
12
+ version: "1"
12
13
  description: >
13
14
  End-to-end FEATURE development loop: precheck → brainstorm → plan (decompose) →
14
15
  execute-tasks → feature-verify → done. Drives a whole feature (one description → a
@@ -143,32 +144,56 @@ states:
143
144
 
144
145
  - id: integration-review
145
146
  description: >
146
- Advisory integration review (D5-P, feature R11). Runs `config/workflows/pr-review.yaml`
147
- ONCE for this feature/branch HEAD, after the local gates and before wrap merge/cleanup.
148
- Current-HEAD dedup stays inside pr-review (`mode: full`), so a re-entry on the same HEAD
149
- is a no-op there rather than a second quota spend here.
147
+ Advisory integration review (D5-P, feature R11), upgraded by 0770 to a
148
+ truthful collect: ONE Codex review request per HEAD, then exactly ONE
149
+ `pr-reviewing.ts collect --head <captured-head>` against that same HEAD
150
+ no wait loop, no polling. The request verdict (`REQUESTED` /
151
+ `ALREADY_REQUESTED` / `ALREADY_REVIEWED`) describes request state only;
152
+ it is NEVER clean evidence. The ONLY clean evidence is the collected
153
+ status (CLEAN / FINDINGS / PENDING) persisted run-scoped at
154
+ .spur/run/<runId>-integration-review-collect.status. A missing head SHA,
155
+ an invalid collect result, or head drift records collect FAIL.
150
156
 
151
- The gate is `softFail: true`, so `pending`, a wait timeout, a missing pr-review
152
- definition, or an unavailable review quota records FAIL and still reaches `done`.
153
- Only `requireCleanReview=true` turns that FAIL into a blocking edge. Findings are
154
- collected by pr-review itself and re-enter the affected local gates on the next run.
157
+ D8 decision D1 (0753 R3): invoking `pr-reviewing.ts request` directly
158
+ the same per-HEAD dedupe-or-request logic `config/workflows/pr-review.yaml`
159
+ uses internally. Request FAIL records FAIL in
160
+ .spur/run/<runId>-integration-review.status, loudly (no softFail).
161
+ `requireCleanReview=true` turns a non-clean COLLECTED verdict (including
162
+ collect FAIL) into the blocking edge declared first below.
155
163
  onEnter:
156
- - kind: command.gate
164
+ - kind: shell
157
165
  options:
158
- id: feature-integration-review
159
- executable: "${vars.spurBin}"
160
- args:
161
- - workflow
162
- - run
163
- - .spur/workflows/pr-review.yaml
164
- - --vars
165
- - '{"mode":"full","baseBranch":"${vars.baseBranch}"}'
166
- resultFile: .spur/run/${vars.__runId}-integration-review.status
167
- softFail: true
168
- timeoutMs: 1800000
166
+ command: >-
167
+ mkdir -p .spur/run &&
168
+ STATUS_FILE=".spur/run/$__runId-integration-review.status" &&
169
+ REQUEST_JSON=".spur/run/$__runId-integration-review.json" &&
170
+ COLLECT_JSON=".spur/run/$__runId-integration-review-collect.json" &&
171
+ COLLECT_STATUS=".spur/run/$__runId-integration-review-collect.status" &&
172
+ PR_REQUEST_STATUS=".spur/run/$__runId-integration-review-pr-request.status" &&
173
+ set +e &&
174
+ bun "$(superskill script path sp pr-reviewing.ts)" request --base "$baseBranch" --json
175
+ --status-file "$PR_REQUEST_STATUS" > "$REQUEST_JSON" 2>&1;
176
+ request_rc=$?; set -e &&
177
+ if [ "$request_rc" -eq 0 ]; then
178
+ printf 'PASS\n' > "$STATUS_FILE";
179
+ else
180
+ printf 'FAIL\n' > "$STATUS_FILE";
181
+ fi &&
182
+ HEAD_SHA=$(jq -r '.head // empty' "$REQUEST_JSON" 2>/dev/null || true) &&
183
+ if [ -z "$HEAD_SHA" ]; then
184
+ echo "integration-review: request result carried no head SHA — collect not run, recorded FAIL" >&2;
185
+ printf 'FAIL\n' > "$COLLECT_STATUS";
186
+ else
187
+ set +e &&
188
+ bun "$(superskill script path sp pr-reviewing.ts)" collect --head "$HEAD_SHA" --json
189
+ --status-file "$COLLECT_STATUS" > "$COLLECT_JSON" 2>&1;
190
+ collect_rc=$?; set -e &&
191
+ echo "integration-review: collect rc=$collect_rc status=$(cat "$COLLECT_STATUS" 2>/dev/null || echo missing) — verdict captured to $COLLECT_JSON" >&2;
192
+ fi;
193
+ exit 0
169
194
  - kind: note
170
195
  options:
171
- message: "Integration review for feature ${vars.featureId} recorded at .spur/run/${vars.__runId}-integration-review.status (advisory unless requireCleanReview=true)."
196
+ message: "Integration review for feature ${vars.featureId}: request verdict at .spur/run/${vars.__runId}-integration-review.status (request state only — REQUESTED/ALREADY_* are NEVER clean evidence); collected verdict at .spur/run/${vars.__runId}-integration-review-collect.status (CLEAN/FINDINGS/PENDING — the only clean evidence). Advisory unless requireCleanReview=true."
172
197
 
173
198
  - id: done
174
199
  description: Terminal — feature planned, all tasks executed, and the feature verified.
@@ -239,18 +264,23 @@ transitions:
239
264
  - from: integration-review
240
265
  to: failed
241
266
  description: >
242
- Review came back FAIL and this branch opted into a hard gate
243
- (requireCleanReview=true). Ordered before the advisory edge so the strict
244
- policy wins when both would match.
267
+ Collected verdict is not CLEAN (FINDINGS / PENDING / collect FAIL or
268
+ missing) and this branch opted into a hard gate (requireCleanReview=true).
269
+ Exactly one collect ran against the captured request HEAD — no wait loop —
270
+ so pending or findings stop the feature here with the actual reason.
271
+ Ordered before the advisory edge so the strict policy wins when both
272
+ would match.
245
273
  guard:
246
274
  kind: shell
247
275
  options:
248
- command: 'test "$requireCleanReview" = true && test "$(cat .spur/run/$__runId-integration-review.status 2>/dev/null)" = FAIL'
276
+ command: 'test "$requireCleanReview" = true && test "$(cat .spur/run/$__runId-integration-review-collect.status 2>/dev/null)" != CLEAN'
249
277
  - from: integration-review
250
278
  to: done
251
279
  description: >
252
- Review clean, pending, unavailable, or advisory the feature is complete. A
253
- non-clean advisory review never blocks a locally-verified feature (feature R11).
280
+ Collected CLEAN, or advisory mode (requireCleanReview != true) the
281
+ feature is complete. A non-clean advisory collect never blocks a
282
+ locally-verified feature (feature R11); the note above preserves the
283
+ collected non-clean state in the run record instead of hiding it.
254
284
  guard:
255
285
  kind: always
256
286
  - from: feature-verify
@@ -9,6 +9,7 @@
9
9
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
10
10
  kind: state-machine
11
11
  name: feature-lifecycle
12
+ version: "1"
12
13
  description: >
13
14
  Feature lifecycle FSM (design §2.3, §5.1, DD-13). States are the canonical
14
15
  FeatureStatus vocabulary; transitions encode the §2.3 graph including the
@@ -48,6 +48,7 @@
48
48
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
49
49
  kind: state-machine
50
50
  name: history-anatomy
51
+ version: "1"
51
52
  description: "Daily/ad-hoc history-anatomy report: cache branch, deterministic analyze/render, skill enrichment, deterministic structure gate, independent evidence validation, bounded correction, atomic publication"
52
53
  iterationBound: 20
53
54
  initialState: start
@@ -153,7 +154,7 @@ states:
153
154
  --baseline .spur/run/$__runId-history-anatomy-baseline.json --target "$HA_TARGET"
154
155
  --mode "$mode" --date "$HA_DATE" --recompute "$recompute" --executor "$agent"
155
156
  --skill-dir "$HA_SKILL" --contract "$HA_SKILL/references/report-contract.md"
156
- --workflow "$workflowFile" --contract-version "$contractVersion" --run-id "$__runId"
157
+ --workflow "$workflowFile" --helper "$HA_HELPER" --contract-version "$contractVersion" --run-id "$__runId"
157
158
  --out .spur/run/$__runId-provenance.json
158
159
  > .spur/run/$__runId-cache-disposition.txt
159
160
 
@@ -208,6 +209,9 @@ states:
208
209
  description: >
209
210
  Independent evidence validation via the skill operation sp:history-anatomy validate.
210
211
  Publication is reachable only from a PASS here. Same undeclared-write assertion as enrich (0676 R3).
212
+ The publish guard reads the ANCHORED FINAL LINE (0771): only a validation artifact whose last
213
+ line is exactly `Verdict: PASS` publishes — a leading PASS under a later FAIL or a
214
+ `not Verdict: PASS` line can never satisfy it.
211
215
  onEnter:
212
216
  - kind: shell
213
217
  options:
@@ -390,18 +394,18 @@ transitions:
390
394
 
391
395
  - from: validate
392
396
  to: stamp
393
- description: Independent validation PASS — stamp provenance, then publish atomically.
397
+ description: Independent validation PASS (anchored final line, 0771) — stamp provenance, then publish atomically.
394
398
  guard:
395
399
  kind: shell
396
400
  options:
397
- command: 'grep -q "Verdict: PASS" .spur/run/$__runId-validation.txt 2>/dev/null'
401
+ command: 'tail -n 1 .spur/run/$__runId-validation.txt 2>/dev/null | grep -qx "Verdict: PASS"'
398
402
  - from: validate
399
403
  to: correct
400
404
  description: Validation FAIL under the shared two-pass correction cap.
401
405
  guard:
402
406
  kind: shell
403
407
  options:
404
- command: '! grep -q "Verdict: PASS" .spur/run/$__runId-validation.txt 2>/dev/null && test "$(cat .spur/run/$__runId-correction-count 2>/dev/null || echo 0)" -lt 2'
408
+ command: '! tail -n 1 .spur/run/$__runId-validation.txt 2>/dev/null | grep -qx "Verdict: PASS" && test "$(cat .spur/run/$__runId-correction-count 2>/dev/null || echo 0)" -lt 2'
405
409
  - from: validate
406
410
  to: failed
407
411
  description: Validation FAIL with the correction cap exhausted — terminate without publishing.