@gobing-ai/spur 0.3.30 → 0.3.32

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 (68) hide show
  1. package/config/rules/strict/runtime-boundaries.yaml +2 -1
  2. package/config/workflows/basic.yaml +92 -14
  3. package/config/workflows/docs-pipeline.yaml +71 -25
  4. package/config/workflows/feature-dev.yaml +104 -35
  5. package/config/workflows/feature-lifecycle.yaml +10 -2
  6. package/config/workflows/idea-pipeline.yaml +118 -54
  7. package/config/workflows/planning-pipeline.yaml +78 -30
  8. package/config/workflows/task-lifecycle.yaml +10 -2
  9. package/config/workflows/task-pipeline.yaml +219 -56
  10. package/config/workflows/wayfinder-resolution.yaml +64 -27
  11. package/config/workflows/wrapup-pipeline.yaml +79 -39
  12. package/package.json +9 -9
  13. package/schemas/state-machine-workflow.schema.json +5 -0
  14. package/spur.js +1302 -707
  15. package/web/_astro/BoardApp.CBVEjvSW.js +1 -0
  16. package/web/_astro/{BoardApp.BwAdAgK9.js → BoardApp.CyyIiK76.js} +74 -74
  17. package/web/_astro/TaskDetail.BfUuRsfM.js +1 -0
  18. package/web/_astro/{arc.CO-I_PZe.js → arc.C7fql4LL.js} +1 -1
  19. package/web/_astro/{architectureDiagram-3BPJPVTR.Si-A1Ncl.js → architectureDiagram-3BPJPVTR.IJPsRX-V.js} +1 -1
  20. package/web/_astro/{blockDiagram-GPEHLZMM.CeNy_ta6.js → blockDiagram-GPEHLZMM.B1AUwBxq.js} +1 -1
  21. package/web/_astro/{c4Diagram-AAUBKEIU.BgN_scEU.js → c4Diagram-AAUBKEIU.BuSJPR3z.js} +1 -1
  22. package/web/_astro/channel.D3uZSASb.js +1 -0
  23. package/web/_astro/{chunk-2J33WTMH.C3oDtmdi.js → chunk-2J33WTMH.CaOPTr9l.js} +1 -1
  24. package/web/_astro/{chunk-4BX2VUAB.Bv8Fxh4H.js → chunk-4BX2VUAB.DQzxExn8.js} +1 -1
  25. package/web/_astro/{chunk-55IACEB6.BT-TqtV6.js → chunk-55IACEB6.IIb103c7.js} +1 -1
  26. package/web/_astro/{chunk-727SXJPM.DkGMsI0B.js → chunk-727SXJPM.DOYz1K1b.js} +1 -1
  27. package/web/_astro/{chunk-AQP2D5EJ.B_-SqGnF.js → chunk-AQP2D5EJ.CFcfW-wt.js} +1 -1
  28. package/web/_astro/{chunk-FMBD7UC4.DwR3skMv.js → chunk-FMBD7UC4.C7Hn0jo3.js} +1 -1
  29. package/web/_astro/{chunk-ND2GUHAM.BaTQutFl.js → chunk-ND2GUHAM.Ba83VQ1m.js} +1 -1
  30. package/web/_astro/{chunk-QZHKN3VN.BJMSqqJp.js → chunk-QZHKN3VN.CizNbIIP.js} +1 -1
  31. package/web/_astro/{classDiagram-4FO5ZUOK.SaqCNUPF.js → classDiagram-4FO5ZUOK.2NYYPXHq.js} +1 -1
  32. package/web/_astro/{classDiagram-v2-Q7XG4LA2.SaqCNUPF.js → classDiagram-v2-Q7XG4LA2.2NYYPXHq.js} +1 -1
  33. package/web/_astro/{cose-bilkent-S5V4N54A.BkmjA_E2.js → cose-bilkent-S5V4N54A.DwBqVzml.js} +1 -1
  34. package/web/_astro/{dagre-BM42HDAG.CNUvWhrX.js → dagre-BM42HDAG.DqnYsHWG.js} +1 -1
  35. package/web/_astro/{diagram-2AECGRRQ.C68Cg9uE.js → diagram-2AECGRRQ.D8TKhgy-.js} +1 -1
  36. package/web/_astro/{diagram-5GNKFQAL.DbHkPLEn.js → diagram-5GNKFQAL.BUD6Prj5.js} +1 -1
  37. package/web/_astro/{diagram-KO2AKTUF.Dt0EdErs.js → diagram-KO2AKTUF.Cvg7hmND.js} +1 -1
  38. package/web/_astro/{diagram-LMA3HP47.Bixs6mnm.js → diagram-LMA3HP47.FUG_B9eS.js} +1 -1
  39. package/web/_astro/{diagram-OG6HWLK6.B4KBI6KT.js → diagram-OG6HWLK6.XsBZQ0hQ.js} +1 -1
  40. package/web/_astro/{erDiagram-TEJ5UH35.C_4i9Xvx.js → erDiagram-TEJ5UH35.M_LSGgLQ.js} +1 -1
  41. package/web/_astro/{flowDiagram-I6XJVG4X.CfGFczAY.js → flowDiagram-I6XJVG4X.DZikZBRa.js} +1 -1
  42. package/web/_astro/{ganttDiagram-6RSMTGT7.C2AIvtmg.js → ganttDiagram-6RSMTGT7.DTKuEmXY.js} +1 -1
  43. package/web/_astro/{gitGraphDiagram-PVQCEYII.HlXuGqQg.js → gitGraphDiagram-PVQCEYII.Dt-E9XmY.js} +1 -1
  44. package/web/_astro/index.yAse9IaO.css +1 -0
  45. package/web/_astro/{infoDiagram-5YYISTIA.BWQJsJ4Z.js → infoDiagram-5YYISTIA.D5snevU1.js} +1 -1
  46. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CCHgIHik.js → ishikawaDiagram-YF4QCWOH.DmXdBf9W.js} +1 -1
  47. package/web/_astro/{journeyDiagram-JHISSGLW.D9M6YwOJ.js → journeyDiagram-JHISSGLW.HPJSRkkU.js} +1 -1
  48. package/web/_astro/{kanban-definition-UN3LZRKU.K20D-a1_.js → kanban-definition-UN3LZRKU.BxCqCnL1.js} +1 -1
  49. package/web/_astro/{linear.DeP5Qsol.js → linear.BnXQUpS-.js} +1 -1
  50. package/web/_astro/{mermaid.core.ClzxNMPU.js → mermaid.core.D-ZeOmpg.js} +4 -4
  51. package/web/_astro/{mindmap-definition-RKZ34NQL.CGn9RUNj.js → mindmap-definition-RKZ34NQL.DxrftiFR.js} +1 -1
  52. package/web/_astro/{pieDiagram-4H26LBE5.CoA92Csb.js → pieDiagram-4H26LBE5.D-ZcWxwg.js} +1 -1
  53. package/web/_astro/{quadrantDiagram-W4KKPZXB.CJ_UPjM1.js → quadrantDiagram-W4KKPZXB.C-dVJ7GW.js} +1 -1
  54. package/web/_astro/{requirementDiagram-4Y6WPE33.KkEPNNUU.js → requirementDiagram-4Y6WPE33.lalE_R8X.js} +1 -1
  55. package/web/_astro/{sankeyDiagram-5OEKKPKP.CoCWbQgh.js → sankeyDiagram-5OEKKPKP.C4uPynAF.js} +1 -1
  56. package/web/_astro/{sequenceDiagram-3UESZ5HK.DUFpV3EP.js → sequenceDiagram-3UESZ5HK.uBOenkqh.js} +1 -1
  57. package/web/_astro/{stateDiagram-AJRCARHV.CgWX0_9m.js → stateDiagram-AJRCARHV.CQOusSP2.js} +1 -1
  58. package/web/_astro/{stateDiagram-v2-BHNVJYJU.BKUXeGSf.js → stateDiagram-v2-BHNVJYJU.Bw0ZdjYb.js} +1 -1
  59. package/web/_astro/{timeline-definition-PNZ67QCA.CXayAguY.js → timeline-definition-PNZ67QCA.DbkPA5ce.js} +1 -1
  60. package/web/_astro/{vennDiagram-CIIHVFJN.DPak_Elh.js → vennDiagram-CIIHVFJN.CIKhzwoA.js} +1 -1
  61. package/web/_astro/{wardley-L42UT6IY.t3ZlDKd0.js → wardley-L42UT6IY.0P-tqaFs.js} +1 -1
  62. package/web/_astro/{wardleyDiagram-YWT4CUSO.CwPSVASQ.js → wardleyDiagram-YWT4CUSO.DrwTvM_M.js} +1 -1
  63. package/web/_astro/{xychartDiagram-2RQKCTM6.CJfrQgpF.js → xychartDiagram-2RQKCTM6.BgJsQVms.js} +1 -1
  64. package/web/index.html +2 -2
  65. package/web/_astro/BoardApp.Bk3T0o6s.js +0 -1
  66. package/web/_astro/TaskDetail.L36slEqE.js +0 -1
  67. package/web/_astro/channel.CYdGnFHM.js +0 -1
  68. package/web/_astro/index.D5Wqk2Tw.css +0 -1
@@ -28,6 +28,14 @@
28
28
  # spurBin — PATH-independent spur invocation (overridden by CLI at run start)
29
29
  # agent — agent for agent.run steps (default: omp)
30
30
  #
31
+ # Reliability (aligned with task-pipeline / ADR-043):
32
+ # - Soft agent doctor at start → failed via transitions (not raw lifecycle abort)
33
+ # - expectFile on feature-create / ac-generate / decompose artifacts (no silent no-ops)
34
+ # - Caped AC and batch-create retry loops with fail-closed escalation
35
+ # - HITL taste gates (idea-eval, design-approval) exhaustive yes/no/cancel
36
+ # - Free-form skill pointers remain only where no pure slash surface exists yet
37
+ # (discovery/sys-architecture/decompose internals — residual until dedicated commands)
38
+ #
31
39
  # Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
32
40
 
33
41
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
@@ -43,6 +51,9 @@ terminalStates:
43
51
  - handoff
44
52
  - cancelled
45
53
  - failed
54
+ failureStates:
55
+ - failed
56
+ - cancelled
46
57
  vars:
47
58
  idea: ""
48
59
  profile: "standard"
@@ -55,44 +66,55 @@ vars:
55
66
  # R8 (0366): injected by WorkflowAppService.run(); stamps discovery artifact provenance.
56
67
  __runId: ""
57
68
  agent: "omp"
58
- stepTimeoutMs: "600000"
69
+ stepTimeoutMs: "1800000"
59
70
 
60
71
  states:
61
72
  - id: start
62
73
  description: >
63
74
  Pipeline start. The idea text arrives as vars.idea. The pipeline stops at
64
- handoff — no task execution.
75
+ handoff — no task execution. Soft agent doctor (status file) so doctor red
76
+ routes to `failed` via transitions instead of a raw lifecycle abort.
65
77
  onEnter:
66
78
  - kind: note
67
79
  options:
68
80
  message: "Idea pipeline start for idea: ${vars.idea}"
69
81
  - kind: shell
70
82
  options:
71
- command: "${vars.spurBin} agent doctor ${vars.agent}"
72
- - kind: shell
73
- options:
74
- command: 'mkdir -p .spur/run && { [ -f .spur/run/idea-eval-report.md ] || [ -f .spur/run/idea-needs-design.json ]; } && { a=".spur/run/idea-archive/$(date +%Y%m%d-%H%M%S)"; mkdir -p "$a"; mv -f .spur/run/idea-eval-report.md "$a/" 2>/dev/null; mv -f .spur/run/idea-needs-design.json "$a/" 2>/dev/null; } ; rm -f .spur/run/idea-ac-retry-count .spur/run/idea-decompose-retry-count .spur/run/idea-feature-id.txt .spur/run/idea-ac-content.md .spur/run/idea-ac-done.txt .spur/run/idea-task-batch.json .spur/run/idea-batch-create.done .spur/run/idea-batch-create.failed'
83
+ command: >-
84
+ mkdir -p .spur/run &&
85
+ DOCTOR_FILE=".spur/run/$__runId-idea-precheck-doctor.status" &&
86
+ set +e &&
87
+ $spurBin agent doctor $agent; doctor_rc=$?;
88
+ set -e &&
89
+ if [ "$doctor_rc" -eq 0 ] && test -n "$idea"; then
90
+ printf 'PASS\n' > "$DOCTOR_FILE";
91
+ else
92
+ printf 'FAIL\n' > "$DOCTOR_FILE";
93
+ fi &&
94
+ exit 0
75
95
 
76
96
  - id: discovery
77
97
  description: >
78
98
  Dispatch sp:brainstorm to explore the idea, generate approaches with trade-offs,
79
99
  and record a design summary. The brainstorm ALWAYS records a design summary
80
100
  ("nothing is too simple" pattern). The brainstorm also emits a needs_design
81
- boolean signal written to .spur/run/idea-needs-design.json — this determines
101
+ boolean signal written to .spur/run/${vars.__runId}-idea-needs-design.json — this determines
82
102
  whether the system-design state runs. As its terminal artifact for the idea path,
83
- brainstorm also emits the idea-evaluation report to .spur/run/idea-eval-report.md
103
+ brainstorm also emits the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md
84
104
  (template: plugins/sp/skills/spur-dev/references/idea-evaluation.md).
105
+ expectFile fails a silent no-op discovery (no eval report).
85
106
  onEnter:
86
107
  - kind: agent.run
87
108
  options:
88
109
  agent: ${vars.agent}
89
- input: "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/idea-eval-report.md per plugins/sp/skills/spur-dev/references/idea-evaluation.md (urgency/necessity 0–5, premises, pros/cons, alternatives, enhanced idea, recommendation). At the END of the report, append a provenance footer block of the exact form: `---\\nrun_id: ${vars.__runId}\\ngenerated_at: <RFC3339 timestamp>\\n---` (omit the footer only if run_id is empty)."
110
+ input: "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per plugins/sp/skills/spur-dev/references/idea-evaluation.md (urgency/necessity 0–5, premises, pros/cons, alternatives, enhanced idea, recommendation). At the END of the report, append a provenance footer block of the exact form: `---\\nrun_id: ${vars.__runId}\\ngenerated_at: <RFC3339 timestamp>\\n---` (omit the footer only if run_id is empty)."
111
+ expectFile: .spur/run/${vars.__runId}-idea-eval-report.md
90
112
  timeoutMs: ${vars.stepTimeoutMs}
91
113
 
92
114
  - id: idea-eval
93
115
  description: >
94
116
  HITL taste gate. Discovery has produced the idea-evaluation report
95
- (.spur/run/idea-eval-report.md) with urgency/necessity scores, premises,
117
+ (.spur/run/${vars.__runId}-idea-eval-report.md) with urgency/necessity scores, premises,
96
118
  pros/cons, and a recommendation. The operator reviews and approves or
97
119
  rejects. This gate is NOT auto-clicked by --auto (taste gate, like
98
120
  design-approval). Only routes around when vars.idea_approved=true
@@ -101,24 +123,24 @@ states:
101
123
  onEnter:
102
124
  - kind: hitl.confirm
103
125
  options:
104
- prompt: "Review the idea evaluation report (.spur/run/idea-eval-report.md). Approve to create a feature, or reject to cancel? (Reject creates no feature.)"
126
+ prompt: "Review the idea evaluation report (.spur/run/${vars.__runId}-idea-eval-report.md). Approve to create a feature, or reject to cancel? (Reject creates no feature.)"
105
127
 
106
128
  - id: feature-create
107
129
  description: >
108
130
  Create a feature via spur feature create, or select an existing feature id.
109
- The agent writes the feature id to .spur/run/idea-feature-id.txt for subsequent
110
- states to read. Prefer the enhanced idea from .spur/run/idea-eval-report.md as
131
+ The agent writes the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt for subsequent
132
+ states to read. Prefer the enhanced idea from .spur/run/${vars.__runId}-idea-eval-report.md as
111
133
  context; do not overwrite vars.idea.
112
134
  onEnter:
113
135
  - kind: agent.run
114
136
  options:
115
137
  agent: ${vars.agent}
116
- input: "Create a feature for the idea: ${vars.idea}. Read .spur/run/idea-eval-report.md if present for the enhanced idea and scores. Use 'spur feature create \"<name>\" --json' to create it. Write the feature id to .spur/run/idea-feature-id.txt. If an existing feature is appropriate, use its id instead."
117
- expectFile: .spur/run/idea-feature-id.txt
138
+ input: "Create a feature for the idea: ${vars.idea}. Read .spur/run/${vars.__runId}-idea-eval-report.md if present for the enhanced idea and scores. Use 'spur feature create \"<name>\" --json' to create it. Write the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt. If an existing feature is appropriate, use its id instead."
139
+ expectFile: .spur/run/${vars.__runId}-idea-feature-id.txt
118
140
  timeoutMs: ${vars.stepTimeoutMs}
119
141
  - kind: file.read.into-var
120
142
  options:
121
- path: .spur/run/idea-feature-id.txt
143
+ path: .spur/run/${vars.__runId}-idea-feature-id.txt
122
144
  var: featureId
123
145
 
124
146
  - id: ac-generate
@@ -128,29 +150,47 @@ states:
128
150
  --from-file`. The agent authors R-numbered Gherkin scenarios tied to the
129
151
  design summary into a captured file; the shell step verifies the file is
130
152
  non-empty, writes it through the CLI, and writes a completion sentinel
131
- (.spur/run/idea-ac-done.txt). AC quality checking lives in the transition
132
- guards (`spur feature check --strict`), NOT in this action chain — a failing
153
+ (.spur/run/${vars.__runId}-idea-ac-done.txt). AC quality checking lives in the transition
154
+ guards (`spur feature check`), NOT in this action chain — a failing
133
155
  check must route through the capped retry loop, not fail the run (the engine's
134
156
  default onError policy is `fail`, so an in-action check failure would kill the
135
157
  run before the retry edges are ever evaluated).
136
158
  onEnter:
137
159
  - kind: shell
138
160
  options:
139
- command: 'mkdir -p .spur/run && count=$(cat .spur/run/idea-ac-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/idea-ac-retry-count; rm -f .spur/run/idea-ac-content.md .spur/run/idea-ac-done.txt'
161
+ command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-ac-retry-count; rm -f .spur/run/$__runId-idea-ac-content.md .spur/run/$__runId-idea-ac-done.txt'
140
162
  - kind: agent.run
141
163
  options:
142
164
  agent: ${vars.agent}
143
165
  input: "Generate acceptance criteria for feature ${vars.featureId}. Read the feature file and author R-numbered BDD Gherkin scenarios per ac-style-guide.md. Output only the complete Acceptance Criteria section body, including the gherkin code fence, with no surrounding commentary and no section heading."
144
- answerFile: .spur/run/idea-ac-content.md
145
- expectFile: .spur/run/idea-ac-content.md
166
+ answerFile: .spur/run/${vars.__runId}-idea-ac-content.md
167
+ expectFile: .spur/run/${vars.__runId}-idea-ac-content.md
146
168
  timeoutMs: ${vars.stepTimeoutMs}
169
+ # Empty capture is a RETRYABLE outcome, not a run abort. `answerFile` always
170
+ # creates the file (so `expectFile` above cannot catch an empty answer), and the
171
+ # old `test -s … && …` chain exited 1 on empty content — killing the run under
172
+ # the default `fail` policy before the capped retry edges were ever evaluated,
173
+ # contradicting this state's own contract. Empty now falls through with exit 0
174
+ # so `spur feature check` in the guards routes it back here (cap: 3). A failing
175
+ # CLI *write* still exits non-zero and aborts — that one is not retryable.
147
176
  - kind: shell
148
177
  options:
149
- command: 'test -s .spur/run/idea-ac-content.md && ${vars.spurBin} feature update ${vars.featureId} --section "Acceptance Criteria" --from-file .spur/run/idea-ac-content.md && date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/idea-ac-done.txt'
178
+ command: >-
179
+ if test -s .spur/run/$__runId-idea-ac-content.md; then
180
+ $spurBin feature update $featureId --section "Acceptance Criteria" --from-file .spur/run/$__runId-idea-ac-content.md && date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-ac-done.txt;
181
+ else
182
+ exit 0;
183
+ fi
150
184
 
151
185
  - id: feature-check
152
186
  description: >
153
- HITL gate: runs spur feature check <id> --strict. The check is objective
187
+ HITL gate: runs spur feature check <id>. Deliberately NOT --strict: before
188
+ decompose/batch-create a feature has scenarios but zero linked tasks, which
189
+ emits L4.orphan-scenarios at severity `warning`; --strict would elevate that
190
+ to a failure, making the success edges unreachable and looping ac-generate
191
+ into `failed`. Malformed AC still blocks via the error-severity L3 BDD
192
+ checks. Strict coverage belongs at the shippable gate, not here. The check
193
+ is objective
154
194
  (schema/AC validation) and auto-routable — under profile=auto the transition
155
195
  guards route directly from ac-generate to the appropriate next state, so this
156
196
  state is only entered in interactive mode. On failure, the retry cap routes
@@ -180,8 +220,17 @@ states:
180
220
  rejects it. This gate is NOT auto-clicked by --auto (Auto-Decision Principle
181
221
  #5: taste-decision -> surface to human). Only routes around when
182
222
  vars.design_approved=true (explicit prior approval).
223
+
224
+ R7 (0433): a headless `no` persisted by DefaultHitlResponder would loop
225
+ design-approval -> system-design -> design-approval, burning unbounded
226
+ agent passes. The onEnter shell increments a reject counter; the `no`
227
+ edge to system-design is capped at 1 revise, after which the run
228
+ terminates as `failed` naming this gate.
183
229
  pause: true
184
230
  onEnter:
231
+ - kind: shell
232
+ options:
233
+ command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-design-reject-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-design-reject-count'
185
234
  - kind: hitl.confirm
186
235
  options:
187
236
  prompt: "Review the system design for feature ${vars.featureId}. Approve to proceed to decomposition?"
@@ -190,15 +239,16 @@ states:
190
239
  description: >
191
240
  Dispatch sp:spec-decomposition with the brainstorm artifact, feature AC, and
192
241
  design doc as input. The agent produces a task-batch JSON file at
193
- .spur/run/idea-task-batch.json, validated against task-batch.schema.json.
242
+ .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json.
194
243
  onEnter:
195
244
  - kind: shell
196
245
  options:
197
- command: 'mkdir -p .spur/run && count=$(cat .spur/run/idea-decompose-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/idea-decompose-retry-count; rm -f .spur/run/idea-task-batch.json .spur/run/idea-batch-create.done .spur/run/idea-batch-create.failed'
246
+ command: 'mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-decompose-retry-count; rm -f .spur/run/$__runId-idea-task-batch.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed'
198
247
  - kind: agent.run
199
248
  options:
200
249
  agent: ${vars.agent}
201
- input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. Produce a task-batch JSON array at .spur/run/idea-task-batch.json, validated against apps/cli/schemas/task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting."
250
+ input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. Produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against apps/cli/schemas/task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting."
251
+ expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
202
252
  timeoutMs: ${vars.stepTimeoutMs}
203
253
 
204
254
  - id: batch-create
@@ -223,7 +273,7 @@ states:
223
273
  onEnter:
224
274
  - kind: shell
225
275
  options:
226
- command: 'if test -f .spur/run/idea-batch-create.done; then exit 0; fi; rm -f .spur/run/idea-batch-create.failed; if ${vars.spurBin} task batch-create --file .spur/run/idea-task-batch.json; then date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/idea-batch-create.done; else date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/idea-batch-create.failed; fi'
276
+ command: 'if test -f .spur/run/$__runId-idea-batch-create.done; then exit 0; fi; rm -f .spur/run/$__runId-idea-batch-create.failed; if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json; then date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.done; else date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.failed; fi'
227
277
 
228
278
  - id: handoff
229
279
  description: >
@@ -237,7 +287,7 @@ states:
237
287
  # Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
238
288
  - kind: shell
239
289
  options:
240
- command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: idea-pipeline handoff feature=${vars.featureId} ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/idea-checkpoint.md'
290
+ command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: idea-pipeline handoff feature=$featureId ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/idea-checkpoint.md'
241
291
 
242
292
  - id: cancelled
243
293
  description: Terminal — pipeline cancelled by operator or error.
@@ -252,7 +302,14 @@ transitions:
252
302
  # ── start -> discovery ──
253
303
  - from: start
254
304
  to: discovery
255
- description: Begin discoverybrainstorm the idea.
305
+ description: Agent doctor PASS begin discovery.
306
+ guard:
307
+ kind: shell
308
+ options:
309
+ command: 'test "$(cat .spur/run/$__runId-idea-precheck-doctor.status 2>/dev/null)" = PASS'
310
+ - from: start
311
+ to: failed
312
+ description: Agent doctor FAIL — stop before discovery.
256
313
  guard:
257
314
  kind: always
258
315
 
@@ -266,7 +323,7 @@ transitions:
266
323
  guard:
267
324
  kind: shell
268
325
  options:
269
- command: 'test "${vars.profile}" = auto && test "${vars.idea_approved}" = true'
326
+ command: 'test "$profile" = auto && test "$idea_approved" = true'
270
327
  - from: discovery
271
328
  to: idea-eval
272
329
  description: Discovery complete — gate on idea evaluation taste decision.
@@ -281,21 +338,21 @@ transitions:
281
338
  guard:
282
339
  kind: shell
283
340
  options:
284
- command: 'test "${vars.profile}" = auto && test "${vars.idea_approved}" = true'
341
+ command: 'test "$profile" = auto && test "$idea_approved" = true'
285
342
  - from: idea-eval
286
343
  to: feature-create
287
344
  description: Idea evaluation approved — create or select a feature.
288
345
  guard:
289
346
  kind: shell
290
347
  options:
291
- command: 'test "${vars.__hitlAnswer}" = yes'
348
+ command: 'test "$__hitlAnswer" = yes'
292
349
  - from: idea-eval
293
350
  to: cancelled
294
351
  description: Idea evaluation rejected or cancelled — no feature created.
295
352
  guard:
296
353
  kind: shell
297
354
  options:
298
- command: 'test "${vars.__hitlAnswer}" = no || test "${vars.__hitlAnswer}" = cancel'
355
+ command: 'test "$__hitlAnswer" = no || test "$__hitlAnswer" = cancel'
299
356
 
300
357
  # ── feature-create -> ac-generate ──
301
358
  - from: feature-create
@@ -317,7 +374,7 @@ transitions:
317
374
  guard:
318
375
  kind: shell
319
376
  options:
320
- command: 'test "${vars.profile}" = auto && ${vars.spurBin} feature check ${vars.featureId} --strict && test "${vars.design}" = auto && test "$(jq -r .needs_design .spur/run/idea-needs-design.json 2>/dev/null)" != false'
377
+ command: 'test "$profile" = auto && $spurBin feature check $featureId && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
321
378
  # Auto-skip 2: pass + skip-design route → decompose
322
379
  - from: ac-generate
323
380
  to: decompose
@@ -325,7 +382,7 @@ transitions:
325
382
  guard:
326
383
  kind: shell
327
384
  options:
328
- command: 'test "${vars.profile}" = auto && ${vars.spurBin} feature check ${vars.featureId} --strict && (test "${vars.design}" = skip || (test "${vars.design}" = auto && test "$(jq -r .needs_design .spur/run/idea-needs-design.json 2>/dev/null)" = false))'
385
+ command: 'test "$profile" = auto && $spurBin feature check $featureId && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
329
386
  # Auto-skip 3: check failed, retry < 3 → loop back to ac-generate (self-loop)
330
387
  - from: ac-generate
331
388
  to: ac-generate
@@ -333,7 +390,7 @@ transitions:
333
390
  guard:
334
391
  kind: shell
335
392
  options:
336
- command: 'test "${vars.profile}" = auto && ! ${vars.spurBin} feature check ${vars.featureId} --strict && test "$(cat .spur/run/idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
393
+ command: 'test "$profile" = auto && ! $spurBin feature check $featureId && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
337
394
  # Auto-skip 4: check failed, retry cap reached → escalate to failed
338
395
  - from: ac-generate
339
396
  to: failed
@@ -341,7 +398,7 @@ transitions:
341
398
  guard:
342
399
  kind: shell
343
400
  options:
344
- command: 'test "${vars.profile}" = auto && ! ${vars.spurBin} feature check ${vars.featureId} --strict && test "$(cat .spur/run/idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
401
+ command: 'test "$profile" = auto && ! $spurBin feature check $featureId && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
345
402
  # Interactive fallback: enter feature-check HITL gate
346
403
  - from: ac-generate
347
404
  to: feature-check
@@ -357,35 +414,35 @@ transitions:
357
414
  guard:
358
415
  kind: shell
359
416
  options:
360
- command: 'test "${vars.__hitlAnswer}" = yes && ${vars.spurBin} feature check ${vars.featureId} --strict && test "${vars.design}" = auto && test "$(jq -r .needs_design .spur/run/idea-needs-design.json 2>/dev/null)" != false'
417
+ command: 'test "$__hitlAnswer" = yes && $spurBin feature check $featureId && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
361
418
  - from: feature-check
362
419
  to: decompose
363
420
  description: Feature check passed, skip-design route — go directly to decompose.
364
421
  guard:
365
422
  kind: shell
366
423
  options:
367
- command: 'test "${vars.__hitlAnswer}" = yes && ${vars.spurBin} feature check ${vars.featureId} --strict && (test "${vars.design}" = skip || (test "${vars.design}" = auto && test "$(jq -r .needs_design .spur/run/idea-needs-design.json 2>/dev/null)" = false))'
424
+ command: 'test "$__hitlAnswer" = yes && $spurBin feature check $featureId && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
368
425
  - from: feature-check
369
426
  to: ac-generate
370
427
  description: "Feature check failed — revise AC (retry cap: 3)."
371
428
  guard:
372
429
  kind: shell
373
430
  options:
374
- command: '(test "${vars.__hitlAnswer}" = no || ! ${vars.spurBin} feature check ${vars.featureId} --strict) && test "$(cat .spur/run/idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
431
+ command: '(test "$__hitlAnswer" = no || ! $spurBin feature check $featureId) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
375
432
  - from: feature-check
376
433
  to: failed
377
434
  description: Feature check failed after 3 retries — escalate to failed.
378
435
  guard:
379
436
  kind: shell
380
437
  options:
381
- command: '(test "${vars.__hitlAnswer}" = no || ! ${vars.spurBin} feature check ${vars.featureId} --strict) && test "$(cat .spur/run/idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
438
+ command: '(test "$__hitlAnswer" = no || ! $spurBin feature check $featureId) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
382
439
  - from: feature-check
383
440
  to: cancelled
384
441
  description: Operator cancelled the feature-check gate.
385
442
  guard:
386
443
  kind: shell
387
444
  options:
388
- command: 'test "${vars.__hitlAnswer}" = cancel'
445
+ command: 'test "$__hitlAnswer" = cancel'
389
446
 
390
447
  # ── system-design: -> design-approval (normal) OR -> decompose (auto + prior approval) ──
391
448
  # Declaration order: auto-skip guard tried FIRST (like task-pipeline's review->verify pattern).
@@ -395,7 +452,7 @@ transitions:
395
452
  guard:
396
453
  kind: shell
397
454
  options:
398
- command: 'test "${vars.profile}" = auto && test "${vars.design_approved}" = true'
455
+ command: 'test "$profile" = auto && test "$design_approved" = true'
399
456
  - from: system-design
400
457
  to: design-approval
401
458
  description: System design done — gate on design approval.
@@ -409,21 +466,28 @@ transitions:
409
466
  guard:
410
467
  kind: shell
411
468
  options:
412
- command: 'test "${vars.__hitlAnswer}" = yes'
469
+ command: 'test "$__hitlAnswer" = yes'
413
470
  - from: design-approval
414
471
  to: system-design
415
- description: Design rejected revise system design.
472
+ description: "Design rejected - revise system design (cap: 1 revise)."
473
+ guard:
474
+ kind: shell
475
+ options:
476
+ command: 'test "$__hitlAnswer" = no && test "$(cat .spur/run/$__runId-idea-design-reject-count 2>/dev/null || echo 0)" -le 1'
477
+ - from: design-approval
478
+ to: failed
479
+ description: "Design rejected after 1 revise - design-approval gate exhausted (R7 0433)."
416
480
  guard:
417
481
  kind: shell
418
482
  options:
419
- command: 'test "${vars.__hitlAnswer}" = no'
483
+ command: 'test "$__hitlAnswer" = no && test "$(cat .spur/run/$__runId-idea-design-reject-count 2>/dev/null || echo 0)" -gt 1'
420
484
  - from: design-approval
421
485
  to: cancelled
422
486
  description: Operator cancelled design approval.
423
487
  guard:
424
488
  kind: shell
425
489
  options:
426
- command: 'test "${vars.__hitlAnswer}" = cancel'
490
+ command: 'test "$__hitlAnswer" = cancel'
427
491
 
428
492
  # ── decompose: auto-skip (profile=auto) OR enter batch-create HITL gate (interactive) ──
429
493
  - from: decompose
@@ -432,7 +496,7 @@ transitions:
432
496
  guard:
433
497
  kind: shell
434
498
  options:
435
- command: 'test "${vars.profile}" = auto'
499
+ command: 'test "$profile" = auto'
436
500
  # Interactive fallback: enter batch-create HITL gate
437
501
  - from: decompose
438
502
  to: batch-create
@@ -446,21 +510,21 @@ transitions:
446
510
  guard:
447
511
  kind: shell
448
512
  options:
449
- command: 'test "${vars.__hitlAnswer}" = yes'
513
+ command: 'test "$__hitlAnswer" = yes'
450
514
  - from: batch-create
451
515
  to: decompose
452
516
  description: Operator requested decomposition revisions.
453
517
  guard:
454
518
  kind: shell
455
519
  options:
456
- command: 'test "${vars.__hitlAnswer}" = no'
520
+ command: 'test "$__hitlAnswer" = no'
457
521
  - from: batch-create
458
522
  to: cancelled
459
523
  description: Operator cancelled batch creation.
460
524
  guard:
461
525
  kind: shell
462
526
  options:
463
- command: 'test "${vars.__hitlAnswer}" = cancel'
527
+ command: 'test "$__hitlAnswer" = cancel'
464
528
 
465
529
  # ── batch-create-run: success -> handoff, failure -> decompose (retry cap: 3), failure+cap -> failed ──
466
530
  - from: batch-create-run
@@ -469,18 +533,18 @@ transitions:
469
533
  guard:
470
534
  kind: shell
471
535
  options:
472
- command: 'test -f .spur/run/idea-batch-create.done'
536
+ command: 'test -f .spur/run/$__runId-idea-batch-create.done'
473
537
  - from: batch-create-run
474
538
  to: decompose
475
539
  description: "Batch-create failed — revise decomposition (retry cap: 3)."
476
540
  guard:
477
541
  kind: shell
478
542
  options:
479
- command: 'test -f .spur/run/idea-batch-create.failed && test "$(cat .spur/run/idea-decompose-retry-count 2>/dev/null || echo 0)" -lt 3'
543
+ command: 'test -f .spur/run/$__runId-idea-batch-create.failed && test "$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0)" -lt 3'
480
544
  - from: batch-create-run
481
545
  to: failed
482
546
  description: Batch-create failed after 3 retries — escalate to failed.
483
547
  guard:
484
548
  kind: shell
485
549
  options:
486
- command: 'test -f .spur/run/idea-batch-create.failed && test "$(cat .spur/run/idea-decompose-retry-count 2>/dev/null || echo 0)" -ge 3'
550
+ command: 'test -f .spur/run/$__runId-idea-batch-create.failed && test "$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0)" -ge 3'