@gobing-ai/spur 0.3.73 → 0.3.75

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 (93) 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 +117 -55
  9. package/config/workflows/feature-dev.yaml +185 -121
  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 +123 -43
  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 +965 -905
  16. package/config/workflows/wayfinder-resolution.yaml +124 -90
  17. package/config/workflows/wrapup-pipeline.yaml +304 -107
  18. package/package.json +1 -1
  19. package/plugins/sp/agents/expert-spur.md +7 -4
  20. package/plugins/sp/commands/dev-idea.md +1 -1
  21. package/plugins/sp/plugin.json +1 -1
  22. package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
  23. package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
  24. package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
  25. package/plugins/sp/scripts/pr-reviewing.ts +7 -1
  26. package/plugins/sp/scripts/transition-shim-check.ts +6 -6
  27. package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
  28. package/plugins/sp/skills/spur-cli/references/self.md +17 -3
  29. package/plugins/sp/skills/spur-cli/references/tasks.md +4 -2
  30. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
  31. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  32. package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
  33. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +23 -8
  34. package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
  35. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  36. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +5 -3
  37. package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
  38. package/spur.js +2999 -1010
  39. package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.BOW1815F.js} +101 -100
  40. package/web/_astro/BoardApp.C4zimv1U.js +1 -0
  41. package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.CX6C7dSJ.js} +1 -1
  42. package/web/_astro/{arc.BySSh34M.js → arc.CwSvH1ji.js} +1 -1
  43. package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.Dxne1RvP.js} +1 -1
  44. package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.D9Fb14rC.js} +1 -1
  45. package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.DmvWSFvv.js} +1 -1
  46. package/web/_astro/channel.CQHdDVp9.js +1 -0
  47. package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.BCCktIrE.js} +1 -1
  48. package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.BzCk9q27.js} +1 -1
  49. package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.CZXVgTk4.js} +1 -1
  50. package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.CJL8UIpX.js} +1 -1
  51. package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.Dz8MDEdw.js} +1 -1
  52. package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.CkQeYYUw.js} +1 -1
  53. package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.KiC1QgzH.js} +1 -1
  54. package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.AJ08mw2e.js} +1 -1
  55. package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.DRWRDKzK.js} +1 -1
  56. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.DRWRDKzK.js} +1 -1
  57. package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.CavydfLP.js} +1 -1
  58. package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.D_7o_a0B.js} +1 -1
  59. package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.LWk2dKgg.js} +1 -1
  60. package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.82lbq6aB.js} +1 -1
  61. package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.hyNL1pwY.js} +1 -1
  62. package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.BaLqVf-b.js} +1 -1
  63. package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.DQJxj0oz.js} +1 -1
  64. package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DhlL8cG2.js} +1 -1
  65. package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.HoTnXwkF.js} +1 -1
  66. package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.DkFKlZIZ.js} +1 -1
  67. package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.9qPfOTDb.js} +1 -1
  68. package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.B4G18Dwc.js} +1 -1
  69. package/web/_astro/index.Dcr_8fiK.css +1 -0
  70. package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.e3KJkXAM.js} +1 -1
  71. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.CY3yddhD.js} +1 -1
  72. package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.DaAKIO1t.js} +1 -1
  73. package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.DGkDddtc.js} +1 -1
  74. package/web/_astro/{linear.D7uqzENp.js → linear.DnDPcpd1.js} +1 -1
  75. package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.1uBmxa9t.js} +4 -4
  76. package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.XFJVayxw.js} +1 -1
  77. package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.BXm2OgHh.js} +1 -1
  78. package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DgB2p5fc.js} +1 -1
  79. package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.jBR39bnS.js} +1 -1
  80. package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.N3u9OhE3.js} +1 -1
  81. package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.CZhqZXNL.js} +1 -1
  82. package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.DlNu1VEa.js} +1 -1
  83. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.BEqi8NQV.js} +1 -1
  84. package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BPcexclc.js} +1 -1
  85. package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.BZalxKGQ.js} +1 -1
  86. package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.BqOljod9.js} +1 -1
  87. package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.tkO4ppN5.js} +1 -1
  88. package/web/index.html +2 -2
  89. package/config/corpus-baseline.json +0 -1805
  90. package/config/workflow-composition-baseline.json +0 -615
  91. package/web/_astro/BoardApp.E12MFjOS.js +0 -1
  92. package/web/_astro/channel.5cYKr5cs.js +0 -1
  93. package/web/_astro/index.B5MTfe7k.css +0 -1
@@ -7,7 +7,7 @@
7
7
  "plugins": [
8
8
  {
9
9
  "name": "sp",
10
- "version": "0.3.73",
10
+ "version": "0.3.75",
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: "2"
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,19 @@ 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: ""
46
+ # 0785 R2: linked feature spec path, resolved at verify entry. Empty (orphan task) means the
47
+ # feature spec is legitimately omitted from the digest inputs (empty-string compatibility);
48
+ # a declared feature whose path fails to resolve fails closed.
49
+ featureSpecPath: ""
43
50
  __hitlAnswer: ""
51
+ # R8 (0366): injected by WorkflowAppService.run(); run-scopes every temporary capture.
52
+ __runId: ""
44
53
 
45
54
  states:
46
55
  - id: precheck
@@ -49,14 +58,16 @@ states:
49
58
  (D5-J, ADR-069). Both gates run `softFail: true`, so a red probe records FAIL and lets
50
59
  the transitions route to `failed` instead of aborting the run — the shipped action
51
60
  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.
61
+ The canonical `.spur/run/$__runId-docs-precheck.status` is the AND of the two gate files.
62
+ (0769: temporary captures are run-scoped — `.spur/run/<runId>-docs-*` — so concurrent
63
+ runs on one task never share or clobber a capture.)
53
64
  onEnter:
54
65
  - kind: command.gate
55
66
  options:
56
67
  id: docs-precheck-task
57
68
  executable: "${vars.spurBin}"
58
69
  args: ["task", "check", "${vars.wbs}"]
59
- resultFile: .spur/run/${vars.wbs}-docs-precheck-task.status
70
+ resultFile: .spur/run/${vars.__runId}-docs-precheck-task.status
60
71
  softFail: true
61
72
  timeoutMs: 120000
62
73
  - kind: command.gate
@@ -64,16 +75,16 @@ states:
64
75
  id: docs-precheck-agent
65
76
  executable: "${vars.spurBin}"
66
77
  args: ["agent", "doctor", "${vars.agent}"]
67
- resultFile: .spur/run/${vars.wbs}-docs-precheck-agent.status
78
+ resultFile: .spur/run/${vars.__runId}-docs-precheck-agent.status
68
79
  softFail: true
69
80
  timeoutMs: 120000
70
81
  - kind: shell
71
82
  options:
72
83
  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
84
+ if test "$(cat .spur/run/$__runId-docs-precheck-task.status 2>/dev/null)" = PASS &&
85
+ test "$(cat .spur/run/$__runId-docs-precheck-agent.status 2>/dev/null)" = PASS;
86
+ then printf 'PASS\n' > ".spur/run/$__runId-docs-precheck.status";
87
+ else printf 'FAIL\n' > ".spur/run/$__runId-docs-precheck.status"; fi
77
88
  - kind: note
78
89
  options:
79
90
  message: "Docs pipeline start for task ${vars.wbs}."
@@ -108,36 +119,21 @@ states:
108
119
  options:
109
120
  prompt: "Approve docs-only deliverable for task ${vars.wbs}?"
110
121
 
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
122
  - id: verify
134
123
  description: >
135
124
  Read-only measured verification (task 0704, R2/R4/R7): dispatch /sp:dev-verify --fix none
136
125
  with an answer file, derive the ONE standard verdict via `spur task verdict`, register the
137
126
  standard verify-verdict artifact, and bracket the verifier with proof-input digest capture/compare so any
138
127
  task-spec or document mutation during certification fails closed (R4). No docs assurance
139
- subclass — PASS semantics are identical to task-pipeline (R7).
128
+ subclass — PASS semantics are identical to task-pipeline (R7). 0769: verification now
129
+ PRECEDES recording; the `verify → record` guard compares the proof bracket, and the
130
+ verdict artifact keeps its `.spur/run/<wbs>-verdict.json` compatibility path with
131
+ runId + definitionDigest stamped through the existing proof/run-artifact contract.
140
132
  onEnter:
133
+ # 0769: clear any stale current-run answer before the fresh-session verifier writes it.
134
+ - kind: shell
135
+ options:
136
+ command: "mkdir -p .spur/run && rm -f .spur/run/$__runId-docs-verify-answer.txt"
141
137
  # R4 (0703 R6 precedent): resolve the task-spec path BEFORE capture. `docs/tasks*` is
142
138
  # excluded from the digest's git-tree half, so the spec is folded in via `taskFile`.
143
139
  - kind: shell
@@ -147,22 +143,42 @@ states:
147
143
  # wbs exits non-zero with a named message. The resolved path folds
148
144
  # into the proof digest via `taskFile:` below; a silent miss would
149
145
  # degrade the docs-pipeline proof to tree-only (same hazard 0751 R2
150
- # removed from task-pipeline).
146
+ # removed from task-pipeline). (0769: run-scoped capture path.)
151
147
  command: >-
152
148
  set -e;
153
149
  task_path="$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')";
154
150
  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"
151
+ printf '%s' "$task_path" > ".spur/run/$__runId-docs-taskpath.txt"
156
152
  - kind: file.read.into-var
157
153
  options:
158
- path: .spur/run/${vars.wbs}-docs-taskpath.txt
154
+ path: .spur/run/${vars.__runId}-docs-taskpath.txt
159
155
  var: taskSpecPath
156
+ # 0785 R2: resolve the linked feature spec path so the digest covers it too. An orphan task
157
+ # (no feature_id) resolves to empty — legitimately omitted via empty-string compatibility;
158
+ # a declared feature whose spec path does not resolve fails closed instead of silently
159
+ # folding a stale spec into the proof.
160
+ - kind: shell
161
+ options:
162
+ command: >-
163
+ set -e;
164
+ FID="$($spurBin task show $wbs --json 2>/dev/null | jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null)";
165
+ FPATH_OUT="";
166
+ if [ -n "$FID" ]; then
167
+ FPATH_OUT="$($spurBin feature show "$FID" --json | jq -r '.filePath // .path // empty')";
168
+ if [ -z "$FPATH_OUT" ]; then echo "docs-pipeline: feature $FID linked to task $wbs but its spec path did not resolve" >&2; exit 1; fi;
169
+ fi;
170
+ printf '%s' "$FPATH_OUT" > ".spur/run/$__runId-docs-featurepath.txt"
171
+ - kind: file.read.into-var
172
+ options:
173
+ path: .spur/run/${vars.__runId}-docs-featurepath.txt
174
+ var: featureSpecPath
160
175
  # R4: canonical capture BEFORE the verifier runs. Same fingerprint input as the
161
176
  # re-capture below so the bracket compares like for like.
162
177
  - kind: proof.fingerprint
163
178
  options:
164
179
  var: proofDigest
165
180
  taskFile: ${vars.taskSpecPath}
181
+ featureFile: ${vars.featureSpecPath}
166
182
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
167
183
  timeoutMs: ${vars.stepTimeoutMs}
168
184
  - kind: agent.run
@@ -174,19 +190,19 @@ states:
174
190
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
175
191
  role: reviewer
176
192
  timeoutMs: ${vars.stepTimeoutMs}
177
- answerFile: .spur/run/${vars.wbs}-verify-answer.txt
193
+ answerFile: .spur/run/${vars.__runId}-docs-verify-answer.txt
178
194
  - kind: shell
179
195
  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`
196
+ command: "$spurBin task verdict $wbs --from-answer .spur/run/$__runId-docs-verify-answer.txt"
197
+ # R7: stamp the proof block + run identity into the standard verdict artifact, then register
198
+ # it. Soft action + hard guard: a missing/malformed stamp fails the `verify -> record`
183
199
  # guard below, not this step.
184
200
  - kind: shell
185
201
  options:
186
202
  command: >-
187
203
  V=".spur/run/$wbs-verdict.json";
188
204
  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";
205
+ jq --arg d "$proofDigest" --arg r "$__runId" '. + {proof: {digest: $d, capturePoint: "docs-verify-entry", runId: $r}}' "$V" > "$V.tmp" && mv "$V.tmp" "$V";
190
206
  fi;
191
207
  exit 0
192
208
  - kind: run.artifact
@@ -199,9 +215,41 @@ states:
199
215
  options:
200
216
  var: proofDigestNow
201
217
  taskFile: ${vars.taskSpecPath}
218
+ featureFile: ${vars.featureSpecPath}
202
219
  input: /sp:dev-verify ${vars.wbs} --auto --fix none --focus all
203
220
  timeoutMs: ${vars.stepTimeoutMs}
204
221
 
222
+ - id: record
223
+ description: >
224
+ Record the docs-only outcome AFTER measured verification (task 0769 ordering:
225
+ draft/approval → verify → record → done — the pre-verification record could write
226
+ UNKNOWN/old Testing evidence). The verdict is already derived and proof-bracketed in
227
+ `verify`; `verify → record` only opens on measured PASS with an intact bracket. The
228
+ task record verb backfills Solution from the diff and moves the task to testing; its
229
+ result is captured to a run-scoped status file so a record denial reaches `failed`
230
+ (record → failed) and is never converted to success by an exit 0.
231
+ onEnter:
232
+ - kind: shell
233
+ options:
234
+ command: >-
235
+ if $spurBin task record $wbs --solution-from-diff --transition testing; then
236
+ printf 'PASS\n' > ".spur/run/$__runId-docs-record.status";
237
+ else
238
+ printf 'FAIL\n' > ".spur/run/$__runId-docs-record.status";
239
+ fi
240
+
241
+ - id: done
242
+ description: Terminal — docs deliverable measured, reviewed, and recorded.
243
+ onEnter:
244
+ # R1 (0704): no synthetic verdict writer. The verdict artifact is produced by measured
245
+ # verification in `verify` and registered there; done only performs the guarded
246
+ # transition. Override path remains: --force-done --reason.
247
+ - kind: shell
248
+ options:
249
+ command: "$spurBin task update $wbs done --no-lifecycle"
250
+ - kind: note
251
+ options:
252
+ message: "Docs pipeline complete for task ${vars.wbs}."
205
253
  - id: failed
206
254
  description: Terminal — docs pipeline failed or was rejected.
207
255
 
@@ -223,8 +271,8 @@ transitions:
223
271
  kind: always
224
272
 
225
273
  - from: draft
226
- to: record
227
- description: profile=auto — skip the docs-review HITL gate.
274
+ to: verify
275
+ 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
276
  guard:
229
277
  kind: shell
230
278
  options:
@@ -236,8 +284,8 @@ transitions:
236
284
  kind: always
237
285
 
238
286
  - from: docs-review
239
- to: record
240
- description: Docs approved — record results.
287
+ to: verify
288
+ description: "Docs approved — measure the deliverable (0769: verification precedes recording)."
241
289
  guard:
242
290
  kind: shell
243
291
  options:
@@ -257,16 +305,11 @@ transitions:
257
305
  options:
258
306
  command: 'test "$__hitlAnswer" = cancel'
259
307
 
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
308
  - from: verify
268
- to: done
269
- description: Measured PASS with the proof bracket intact (R4/R5).
309
+ to: record
310
+ description: Measured PASS with the proof bracket intact (R4/R5) — record only verified
311
+ outcomes (0769). Structural task-check gates remain at precheck, record (wip→testing),
312
+ and done (verdict-artifact guard, R3).
270
313
  guard:
271
314
  kind: shell
272
315
  options:
@@ -284,6 +327,25 @@ transitions:
284
327
  - from: verify
285
328
  to: failed
286
329
  description: Non-PASS verdict, missing/malformed answer, or digest mismatch (R5) — the run
287
- reaches failed and no PASS artifact is certified.
330
+ reaches failed and nothing is recorded.
331
+ guard:
332
+ kind: always
333
+
334
+ - from: record
335
+ to: done
336
+ description: Recorded from a verified outcome; the persisted verdict/bracket is re-asserted
337
+ so a failed `task record` (captured FAIL) never converts to done (0769).
338
+ guard:
339
+ kind: shell
340
+ options:
341
+ command: >-
342
+ test "$(cat .spur/run/$__runId-docs-record.status 2>/dev/null)" = PASS &&
343
+ test -f ".spur/run/$wbs-verdict.json" &&
344
+ test "$(jq -r '.verdict // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = PASS &&
345
+ test -n "$proofDigest" &&
346
+ test "$(jq -r '.proof.digest // empty' .spur/run/$wbs-verdict.json 2>/dev/null)" = "$proofDigest"
347
+ - from: record
348
+ to: failed
349
+ 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
350
  guard:
289
351
  kind: always