@gobing-ai/spur 0.3.73 → 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 (88) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +15 -4
  3. package/config/pipeline-budgets.json +4 -4
  4. package/config/rules/strict/runtime-boundaries.yaml +1 -1
  5. package/config/templates/AGENTS.md +5 -0
  6. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
  7. package/config/workflows/basic.yaml +5 -2
  8. package/config/workflows/docs-pipeline.yaml +92 -55
  9. package/config/workflows/feature-dev.yaml +43 -20
  10. package/config/workflows/feature-lifecycle.yaml +1 -0
  11. package/config/workflows/history-anatomy.yaml +8 -4
  12. package/config/workflows/idea-pipeline.yaml +66 -33
  13. package/config/workflows/pr-review.yaml +31 -8
  14. package/config/workflows/task-lifecycle.yaml +1 -0
  15. package/config/workflows/task-pipeline.yaml +929 -905
  16. package/config/workflows/wayfinder-resolution.yaml +124 -90
  17. package/config/workflows/wrapup-pipeline.yaml +208 -83
  18. package/package.json +1 -1
  19. package/plugins/sp/plugin.json +1 -1
  20. package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
  21. package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
  22. package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
  23. package/plugins/sp/scripts/pr-reviewing.ts +7 -1
  24. package/plugins/sp/scripts/transition-shim-check.ts +6 -6
  25. package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
  26. package/plugins/sp/skills/spur-cli/references/self.md +17 -3
  27. package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
  28. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
  29. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  30. package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
  31. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  32. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -2
  33. package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
  34. package/spur.js +1125 -784
  35. package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.C60RJZRj.js} +78 -77
  36. package/web/_astro/BoardApp.ymfj1EHA.js +1 -0
  37. package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.Guk8VuNu.js} +1 -1
  38. package/web/_astro/{arc.BySSh34M.js → arc.CAZtlWJM.js} +1 -1
  39. package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.9-XbF_Tz.js} +1 -1
  40. package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.DC4WLx3L.js} +1 -1
  41. package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.JgjUQMgG.js} +1 -1
  42. package/web/_astro/channel.Bxxe2Byc.js +1 -0
  43. package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.D_JFiXa-.js} +1 -1
  44. package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.Bk_RxoeT.js} +1 -1
  45. package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.a_Rj2VxL.js} +1 -1
  46. package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.C2NoS1U6.js} +1 -1
  47. package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.D1Xn6CNa.js} +1 -1
  48. package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.BhJrSBsX.js} +1 -1
  49. package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.ByVGduYQ.js} +1 -1
  50. package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.B47Paunq.js} +1 -1
  51. package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.BERMSD5C.js} +1 -1
  52. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.BERMSD5C.js} +1 -1
  53. package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.O_rHGlhU.js} +1 -1
  54. package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.qCW2GdNp.js} +1 -1
  55. package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.C5Y4lH_u.js} +1 -1
  56. package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.DxZmRXxF.js} +1 -1
  57. package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.qSxZeYSK.js} +1 -1
  58. package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.-7vl3bXs.js} +1 -1
  59. package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.BHVV-UH3.js} +1 -1
  60. package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DHAuW9sK.js} +1 -1
  61. package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.DMRLQwPN.js} +1 -1
  62. package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.B3EpPp_8.js} +1 -1
  63. package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.BgUrExXM.js} +1 -1
  64. package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.CU-9yVN4.js} +1 -1
  65. package/web/_astro/{index.B5MTfe7k.css → index.BhvM3djt.css} +1 -1
  66. package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.CvVTCRLe.js} +1 -1
  67. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.BUzOfKPf.js} +1 -1
  68. package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.BBTb7ziz.js} +1 -1
  69. package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.CTQQr70i.js} +1 -1
  70. package/web/_astro/{linear.D7uqzENp.js → linear.CLKlJPiS.js} +1 -1
  71. package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.BA4wFhLP.js} +4 -4
  72. package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.t1vG2l5e.js} +1 -1
  73. package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.C8zJhyTu.js} +1 -1
  74. package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DqYYvb7T.js} +1 -1
  75. package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.lzfDZ9nI.js} +1 -1
  76. package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.lm6N5ORM.js} +1 -1
  77. package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.E6_IV4Dj.js} +1 -1
  78. package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.Bnv6Ok2p.js} +1 -1
  79. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js} +1 -1
  80. package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BhRgqSLa.js} +1 -1
  81. package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.pjb5DMC9.js} +1 -1
  82. package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.CW20KUng.js} +1 -1
  83. package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.vX9_yuMl.js} +1 -1
  84. package/web/index.html +2 -2
  85. package/config/corpus-baseline.json +0 -1805
  86. package/config/workflow-composition-baseline.json +0 -615
  87. package/web/_astro/BoardApp.E12MFjOS.js +0 -1
  88. package/web/_astro/channel.5cYKr5cs.js +0 -1
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.3.73",
10
+ "version": "0.3.74",
11
11
  "source": "./plugins/sp"
12
12
  }
13
13
  ]
@@ -130,10 +130,21 @@ agent:
130
130
  # agent: omp
131
131
  # model: minimax/MiniMax-M3
132
132
  # tier: cheap
133
- - name: omp
134
- agent: omp
135
- model: opencode/deepseek-v4-flash
136
- tier: standard
133
+ - name: pi-dsv4-flash-volc
134
+ agent: pi
135
+ # tier: standard
136
+ executionCapabilities:
137
+ version: 1
138
+ axes:
139
+ fsRead:
140
+ state: available
141
+ provenance: native-known
142
+ fsWrite:
143
+ state: available
144
+ provenance: native-known
145
+ processSpawn:
146
+ state: available
147
+ provenance: native-known
137
148
  # - name: omp-zai
138
149
  # agent: omp
139
150
  # model: zai/glm-5.2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "generatedAt": "2026-08-20T00:00:00.000Z",
4
- "note": "Per-pipeline model-query + wall-clock cost budgets (task 0607 R3). modelQueries anchors the frozen config/workflow-composition-baseline.json list (the SSOT, two-sided checked). wallClockMs is a gross-regression ceiling anchored to measured real-run data (R5): the gate compares the median-of-sane terminal-run wall-clock against it. A null budget is unenforced until measured — never treated as 0. Raising a numeric budget requires a fresh recorded decision in the same commit (config/pipeline-budgets.json vs git HEAD); the check-pipeline-budgets gate fails a silent raise by name.",
4
+ "note": "Per-pipeline model-query + wall-clock cost budgets (task 0607 R3). modelQueries anchors the live workflow definitions' query lists — the SSOT, extracted by extractResolvedWorkflowFacts and guarded by composition-baseline.test.ts (0775 retired the snapshot this file previously anchored to). wallClockMs is a gross-regression ceiling anchored to measured real-run data (R5): the gate compares the median-of-sane terminal-run wall-clock against it. A null budget is unenforced until measured — never treated as 0. Raising a numeric budget requires a fresh recorded decision in the same commit (config/pipeline-budgets.json vs git HEAD); the check-pipeline-budgets gate fails a silent raise by name.",
5
5
  "budgets": {
6
6
  "task-pipeline": {
7
7
  "modelQueries": 4,
@@ -28,18 +28,18 @@
28
28
  "modelQueries": 2,
29
29
  "wallClockMs": null,
30
30
  "tokenCostUsd": null,
31
- "source": "No fixture (0607 option a) and only dry/short runs recorded in history (max 1s, n=4) — wall-clock budget unenforced until a real docs-pipeline run exists. Query budget 2 matches the live SSOT list ['draft','verify'] in config/workflow-composition-baseline.json (added by task 0704; 0607 R3 recorded 1, which is stale against the SSOT). 0754 R6: FIX, not raise — the value was wrong against the SSOT, the workflow's declared query count did not change.",
31
+ "source": "No fixture (0607 option a) and only dry/short runs recorded in history (max 1s, n=4) — wall-clock budget unenforced until a real docs-pipeline run exists. Query budget 2 matches the live workflow definition's query list ['draft','verify'] (added by task 0704; 0607 R3 recorded 1, which is stale against the definition). 0754 R6: FIX, not raise — the value was wrong against the definition, the workflow's declared query count did not change.",
32
32
  "decision": {
33
33
  "date": "2026-09-03",
34
34
  "wbs": "0754",
35
- "note": "align docs-pipeline.modelQueries to the live baseline SSOT ['draft','verify']; not a measured-cost raise — the workflow's declared query count was already 2 (added by 0704); the budget was the stale side, not the workflow. Per D8 Decision 11."
35
+ "note": "align docs-pipeline.modelQueries to the live workflow definition's query list ['draft','verify']; not a measured-cost raise — the workflow's declared query count was already 2 (added by 0704); the budget was the stale side, not the workflow. Per D8 Decision 11."
36
36
  }
37
37
  },
38
38
  "pr-review": {
39
39
  "modelQueries": 0,
40
40
  "wallClockMs": null,
41
41
  "tokenCostUsd": null,
42
- "source": "Zero-query pipeline (baseline modelQueries []); query budget trivially 0 — the gate handles an empty list (0607 R3). No real full run recorded; wall-clock unenforced.",
42
+ "source": "Zero-query pipeline (the live definition declares no model queries); query budget trivially 0 — the gate handles an empty list (0607 R3). No real full run recorded; wall-clock unenforced.",
43
43
  "decision": null
44
44
  }
45
45
  }
@@ -74,5 +74,5 @@ rules:
74
74
  # Synchronous template & workflow resolvers:
75
75
  - "apps/cli/src/commands/task.ts" # sync loadTemplateContent/Bodies callbacks
76
76
  - "apps/cli/src/workflow/make-lifecycle-adapter.ts" # sync resolveWorkflowPath lookup
77
- - "packages/app/src/services/workflow-service.ts" # sync workflow-composition-baseline.json lookup (walk-up) for the validate composition advisory (task 0614); sync read inside the synchronous validate path
77
+ - "packages/app/src/services/workflow-service.ts" # composition advisory source (0775: facts extracted from the live definition inside the synchronous validate path; the baseline snapshot sync read was retired)
78
78
  - "apps/cli/src/release-ops.ts" # sync manifest/workspace reads + Bun.write rewrites inside the git release flow (task 0617); mirrors task.ts sync-template exemption
@@ -74,6 +74,11 @@ All product development work goes through the harness by default.
74
74
  Use `spur task path <wbs> --json` only when a filesystem consumer needs the absolute path. Both
75
75
  commands resolve across configured task folders. Reuse the first `show` response within the run.
76
76
 
77
+ **Corpus checks (constitution T10/T11):** checker-policy changes require one explicit unsuppressed
78
+ audit, recording/reconciling essential failures without waivers. Ordinary commit prep checks changed
79
+ task/feature documents and their required linked evidence, not the whole corpus. Do not regenerate
80
+ an acceptance baseline or insert audits into routine batch/wrapup loops.
81
+
77
82
  **Platform fallback:** Platforms without slash commands and/or subagents still use the harness.
78
83
  Install the plugin through Superskill for the target platform, then use skills `sp:spur-dev`,
79
84
  `sp:spur-cli`, `sp:code-verification` (and related) plus the `spur` CLI. Do not invent a parallel
@@ -200,6 +200,8 @@ names the docs that must be touched **in the same commit / same change**:
200
200
  | T7 | The doc map or process changes | this file → re-sync `AGENTS.md` (§4.4) → propagate to sibling projects |
201
201
  | T8 | A multi-wave batch is planned | schedule "doc sync" as an **explicit work item** — same-commit discipline does not survive on memory alone |
202
202
  | T9 | A design or feature item is added/changed | the satellite **first** (`docs/design/<slug>.md` or `docs/features/<id>_<slug>.md`), **then** its index row in `04`/`05` — same change (§4.5 rule 5) |
203
+ | T10 | Corpus checker policy changes | Run one explicit unsuppressed audit plus focused rule tests; record/reconcile exposed essential failures without waivers. |
204
+ | T11 | Ordinary task/feature commit prep | Check changed documents and their required linked evidence, not the whole corpus. Never regenerate an acceptance baseline or add routine audit callers. |
203
205
 
204
206
  ## 6. Edit principles per file
205
207
 
@@ -6,6 +6,8 @@
6
6
  # Reliability (aligned with task-pipeline / ADR-043):
7
7
  # - check is a soft probe (status file + always exit 0) so red gates route to fix
8
8
  # via transitions, not a raw lifecycle abort that skips the failed terminal
9
+ # - qualityGateCmd is TRUSTED CONFIG ONLY — executed via `sh -c` so compound
10
+ # commands (`&&`, `||`) preserve their exit status; never interpolated task data
9
11
  # - fix is pure slash /sp:dev-fixall against the same qualityGateCmd (single-sourced)
10
12
  # - qualityGateMaxFixAttempts bounds the fix loop; exhausted → failed (fail-closed)
11
13
  # - Bun package scripts only for gate surface (no alternate package-manager CLIs)
@@ -13,7 +15,8 @@
13
15
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
14
16
  name: basic
15
17
  kind: state-machine
16
- description: The canonical implement-check-fix-until-pass loop
18
+ description: The canonical implement-check-fix-until-pass loop (example definition; override qualityGateCmd per project)
19
+ version: "1"
17
20
  iterationBound: 8
18
21
  initialState: implement
19
22
  terminalStates:
@@ -57,7 +60,7 @@ states:
57
60
  mkdir -p .spur/run &&
58
61
  STATUS_FILE=".spur/run/$__runId-basic-gate.status" &&
59
62
  set +e &&
60
- ( $qualityGateCmd ); gate_rc=$?; set -e &&
63
+ sh -c "$qualityGateCmd"; gate_rc=$?; set -e &&
61
64
  if [ "$gate_rc" -eq 0 ]; then
62
65
  printf 'PASS\n' > "$STATUS_FILE";
63
66
  else
@@ -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,36 +115,21 @@ 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
@@ -147,15 +139,15 @@ states:
147
139
  # wbs exits non-zero with a named message. The resolved path folds
148
140
  # into the proof digest via `taskFile:` below; a silent miss would
149
141
  # degrade the docs-pipeline proof to tree-only (same hazard 0751 R2
150
- # removed from task-pipeline).
142
+ # removed from task-pipeline). (0769: run-scoped capture path.)
151
143
  command: >-
152
144
  set -e;
153
145
  task_path="$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')";
154
146
  if [ -z "$task_path" ]; then echo "docs-pipeline: task path did not resolve for wbs $wbs" >&2; exit 1; fi;
155
- printf '%s' "$task_path" > ".spur/run/$wbs-docs-taskpath.txt"
147
+ printf '%s' "$task_path" > ".spur/run/$__runId-docs-taskpath.txt"
156
148
  - kind: file.read.into-var
157
149
  options:
158
- path: .spur/run/${vars.wbs}-docs-taskpath.txt
150
+ path: .spur/run/${vars.__runId}-docs-taskpath.txt
159
151
  var: taskSpecPath
160
152
  # R4: canonical capture BEFORE the verifier runs. Same fingerprint input as the
161
153
  # re-capture below so the bracket compares like for like.
@@ -174,19 +166,19 @@ states:
174
166
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
175
167
  role: reviewer
176
168
  timeoutMs: ${vars.stepTimeoutMs}
177
- answerFile: .spur/run/${vars.wbs}-verify-answer.txt
169
+ answerFile: .spur/run/${vars.__runId}-docs-verify-answer.txt
178
170
  - kind: shell
179
171
  options:
180
- command: "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
181
- # R7: stamp the proof block into the standard verdict artifact, then register it.
182
- # 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`
183
175
  # guard below, not this step.
184
176
  - kind: shell
185
177
  options:
186
178
  command: >-
187
179
  V=".spur/run/$wbs-verdict.json";
188
180
  if [ -f "$V" ] && [ -n "$proofDigest" ]; then
189
- 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";
190
182
  fi;
191
183
  exit 0
192
184
  - kind: run.artifact
@@ -202,6 +194,37 @@ states:
202
194
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
203
195
  timeoutMs: ${vars.stepTimeoutMs}
204
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}."
205
228
  - id: failed
206
229
  description: Terminal — docs pipeline failed or was rejected.
207
230
 
@@ -223,8 +246,8 @@ transitions:
223
246
  kind: always
224
247
 
225
248
  - from: draft
226
- to: record
227
- 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)."
228
251
  guard:
229
252
  kind: shell
230
253
  options:
@@ -236,8 +259,8 @@ transitions:
236
259
  kind: always
237
260
 
238
261
  - from: docs-review
239
- to: record
240
- description: Docs approved — record results.
262
+ to: verify
263
+ description: "Docs approved — measure the deliverable (0769: verification precedes recording)."
241
264
  guard:
242
265
  kind: shell
243
266
  options:
@@ -257,16 +280,11 @@ transitions:
257
280
  options:
258
281
  command: 'test "$__hitlAnswer" = cancel'
259
282
 
260
- - from: record
261
- to: verify
262
- description: Recorded — measure the deliverable (R2). Structural task-check gates remain
263
- at precheck, record (wip→testing), and done (verdict-artifact guard, R3).
264
- guard:
265
- kind: always
266
-
267
283
  - from: verify
268
- to: done
269
- 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).
270
288
  guard:
271
289
  kind: shell
272
290
  options:
@@ -284,6 +302,25 @@ transitions:
284
302
  - from: verify
285
303
  to: failed
286
304
  description: Non-PASS verdict, missing/malformed answer, or digest mismatch (R5) — the run
287
- 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."
288
325
  guard:
289
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,18 +144,22 @@ states:
143
144
 
144
145
  - id: integration-review
145
146
  description: >
146
- Advisory integration review (D5-P, feature R11). Spends one Codex review
147
- on the current HEAD after the local gates and before wrap merge/cleanup.
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.
148
156
 
149
- D8 decision D1 (0753 R3): the nested `spur workflow run pr-review.yaml`
150
- was refused by the SPUR_WORKFLOW_RUN_ACTIVE child guard and the refusal
151
- was masked by `softFail: true`. The non-spawning replacement invokes
152
- `pr-reviewing.ts request` directly — the same per-HEAD dedupe-or-request
153
- logic `config/workflows/pr-review.yaml` uses internally and writes its
154
- own PASS/FAIL status. `ALREADY_REVIEWED` / `ALREADY_REQUESTED` / `REQUESTED`
155
- all count as PASS (review is initiated for this HEAD); only a request
156
- error counts as FAIL. `softFail` is gone: a real failure now records FAIL
157
- loudly. `requireCleanReview=true` still turns that FAIL into a blocking edge.
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.
158
163
  onEnter:
159
164
  - kind: shell
160
165
  options:
@@ -162,20 +167,33 @@ states:
162
167
  mkdir -p .spur/run &&
163
168
  STATUS_FILE=".spur/run/$__runId-integration-review.status" &&
164
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" &&
165
172
  PR_REQUEST_STATUS=".spur/run/$__runId-integration-review-pr-request.status" &&
166
173
  set +e &&
167
174
  bun "$(superskill script path sp pr-reviewing.ts)" request --base "$baseBranch" --json
168
175
  --status-file "$PR_REQUEST_STATUS" > "$REQUEST_JSON" 2>&1;
169
- rc=$?; set -e &&
170
- if [ "$rc" -eq 0 ]; then
176
+ request_rc=$?; set -e &&
177
+ if [ "$request_rc" -eq 0 ]; then
171
178
  printf 'PASS\n' > "$STATUS_FILE";
172
179
  else
173
180
  printf 'FAIL\n' > "$STATUS_FILE";
174
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;
175
193
  exit 0
176
194
  - kind: note
177
195
  options:
178
- 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."
179
197
 
180
198
  - id: done
181
199
  description: Terminal — feature planned, all tasks executed, and the feature verified.
@@ -246,18 +264,23 @@ transitions:
246
264
  - from: integration-review
247
265
  to: failed
248
266
  description: >
249
- Review came back FAIL and this branch opted into a hard gate
250
- (requireCleanReview=true). Ordered before the advisory edge so the strict
251
- 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.
252
273
  guard:
253
274
  kind: shell
254
275
  options:
255
- 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'
256
277
  - from: integration-review
257
278
  to: done
258
279
  description: >
259
- Review clean, pending, unavailable, or advisory the feature is complete. A
260
- 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.
261
284
  guard:
262
285
  kind: always
263
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.