@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/config.example.yaml +15 -4
- package/config/pipeline-budgets.json +4 -4
- package/config/rules/strict/runtime-boundaries.yaml +1 -1
- package/config/templates/AGENTS.md +5 -0
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
- package/config/workflows/basic.yaml +5 -2
- package/config/workflows/docs-pipeline.yaml +117 -55
- package/config/workflows/feature-dev.yaml +185 -121
- package/config/workflows/feature-lifecycle.yaml +1 -0
- package/config/workflows/history-anatomy.yaml +8 -4
- package/config/workflows/idea-pipeline.yaml +123 -43
- package/config/workflows/pr-review.yaml +31 -8
- package/config/workflows/task-lifecycle.yaml +1 -0
- package/config/workflows/task-pipeline.yaml +965 -905
- package/config/workflows/wayfinder-resolution.yaml +124 -90
- package/config/workflows/wrapup-pipeline.yaml +304 -107
- package/package.json +1 -1
- package/plugins/sp/agents/expert-spur.md +7 -4
- package/plugins/sp/commands/dev-idea.md +1 -1
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
- package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
- package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
- package/plugins/sp/scripts/pr-reviewing.ts +7 -1
- package/plugins/sp/scripts/transition-shim-check.ts +6 -6
- package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
- package/plugins/sp/skills/spur-cli/references/self.md +17 -3
- package/plugins/sp/skills/spur-cli/references/tasks.md +4 -2
- package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
- package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
- package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +23 -8
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +5 -3
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/spur.js +2999 -1010
- package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.BOW1815F.js} +101 -100
- package/web/_astro/BoardApp.C4zimv1U.js +1 -0
- package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.CX6C7dSJ.js} +1 -1
- package/web/_astro/{arc.BySSh34M.js → arc.CwSvH1ji.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.Dxne1RvP.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.D9Fb14rC.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.DmvWSFvv.js} +1 -1
- package/web/_astro/channel.CQHdDVp9.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.BCCktIrE.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.BzCk9q27.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.CZXVgTk4.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.CJL8UIpX.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.Dz8MDEdw.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.CkQeYYUw.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.KiC1QgzH.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.AJ08mw2e.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.DRWRDKzK.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.DRWRDKzK.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.CavydfLP.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.D_7o_a0B.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.LWk2dKgg.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.82lbq6aB.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.hyNL1pwY.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.BaLqVf-b.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.DQJxj0oz.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DhlL8cG2.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.HoTnXwkF.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.DkFKlZIZ.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.9qPfOTDb.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.B4G18Dwc.js} +1 -1
- package/web/_astro/index.Dcr_8fiK.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.e3KJkXAM.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.CY3yddhD.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.DaAKIO1t.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.DGkDddtc.js} +1 -1
- package/web/_astro/{linear.D7uqzENp.js → linear.DnDPcpd1.js} +1 -1
- package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.1uBmxa9t.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.XFJVayxw.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.BXm2OgHh.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DgB2p5fc.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.jBR39bnS.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.N3u9OhE3.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.CZhqZXNL.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.DlNu1VEa.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.BEqi8NQV.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BPcexclc.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.BZalxKGQ.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.BqOljod9.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.tkO4ppN5.js} +1 -1
- package/web/index.html +2 -2
- package/config/corpus-baseline.json +0 -1805
- package/config/workflow-composition-baseline.json +0 -615
- package/web/_astro/BoardApp.E12MFjOS.js +0 -1
- package/web/_astro/channel.5cYKr5cs.js +0 -1
- package/web/_astro/index.B5MTfe7k.css +0 -1
|
@@ -14,6 +14,13 @@
|
|
|
14
14
|
# (idea-eval rejection -> cancelled)
|
|
15
15
|
# (feature-check failure routes back to ac-generate; batch-create failure to decompose).
|
|
16
16
|
#
|
|
17
|
+
# Evidence instead of ceremony (task 0769): the feature check is MEASURED exactly once at
|
|
18
|
+
# each author/revise boundary — `idea-ac-check` at the end of ac-generate, `idea-design-check`
|
|
19
|
+
# at the end of system-design — by a deterministic `command.gate` writing a run-scoped
|
|
20
|
+
# PASS/FAIL result file. Transition guards only CONSUME the recorded result (run-scoped path,
|
|
21
|
+
# no CLI re-run per guard); a gate re-runs only after a relevant write (revision pass) or on
|
|
22
|
+
# the next boundary.
|
|
23
|
+
#
|
|
17
24
|
# Vars (passed as a JSON object via `--vars`):
|
|
18
25
|
# idea — the idea text (required), e.g. --vars '{"idea":"add a --dry-run flag to dev-wrap"}'
|
|
19
26
|
# profile — set --vars '{"profile":"auto"}' to skip objective HITL gates (feature-check, batch-create)
|
|
@@ -39,6 +46,7 @@
|
|
|
39
46
|
# Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
|
|
40
47
|
|
|
41
48
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
49
|
+
version: "2"
|
|
42
50
|
kind: state-machine
|
|
43
51
|
name: idea-pipeline
|
|
44
52
|
description: "Idea to feature + AC + task batch: discovery, idea-eval, feature-create, ac-generate, feature-check, system-design, decompose, batch-create, handoff"
|
|
@@ -169,11 +177,11 @@ states:
|
|
|
169
177
|
--from-file`. The agent authors R-numbered Gherkin scenarios tied to the
|
|
170
178
|
design summary into a captured file; the shell step verifies the file is
|
|
171
179
|
non-empty, writes it through the CLI, and writes a completion sentinel
|
|
172
|
-
(.spur/run/${vars.__runId}-idea-ac-done.txt). AC quality
|
|
173
|
-
|
|
174
|
-
check
|
|
175
|
-
|
|
176
|
-
run before the retry edges are
|
|
180
|
+
(.spur/run/${vars.__runId}-idea-ac-done.txt). AC quality is MEASURED once at this
|
|
181
|
+
boundary by the deterministic `idea-ac-check` command.gate below (task 0769): a failing
|
|
182
|
+
check records FAIL and routes through the capped retry loop via guards that consume the
|
|
183
|
+
recorded result — it never fails the run (the engine's default onError policy is `fail`,
|
|
184
|
+
so an in-action check failure would kill the run before the retry edges are evaluated).
|
|
177
185
|
onEnter:
|
|
178
186
|
- kind: shell
|
|
179
187
|
options:
|
|
@@ -192,8 +200,8 @@ states:
|
|
|
192
200
|
# old `test -s … && …` chain exited 1 on empty content — killing the run under
|
|
193
201
|
# the default `fail` policy before the capped retry edges were ever evaluated,
|
|
194
202
|
# contradicting this state's own contract. Empty now falls through with exit 0
|
|
195
|
-
# so
|
|
196
|
-
#
|
|
203
|
+
# so the recorded FAIL routes it back here (cap: 3). A failing CLI *write* still
|
|
204
|
+
# exits non-zero and aborts — that one is not retryable.
|
|
197
205
|
- kind: shell
|
|
198
206
|
options:
|
|
199
207
|
command: >-
|
|
@@ -202,20 +210,31 @@ states:
|
|
|
202
210
|
else
|
|
203
211
|
exit 0;
|
|
204
212
|
fi
|
|
213
|
+
# 0769: ONE measured check at the AC author/revise boundary. `softFail: true` keeps a
|
|
214
|
+
# FAIL routable (retry loop / failed) instead of aborting; sibling guards consume the
|
|
215
|
+
# recorded result from the run-scoped status file and never re-run the CLI.
|
|
216
|
+
- kind: command.gate
|
|
217
|
+
options:
|
|
218
|
+
id: idea-ac-check
|
|
219
|
+
executable: "${vars.spurBin}"
|
|
220
|
+
args: ["feature", "check", "${vars.featureId}"]
|
|
221
|
+
resultFile: .spur/run/${vars.__runId}-idea-ac-check.status
|
|
222
|
+
softFail: true
|
|
223
|
+
timeoutMs: 120000
|
|
205
224
|
|
|
206
225
|
- id: feature-check
|
|
207
226
|
description: >
|
|
208
|
-
HITL gate
|
|
227
|
+
HITL gate over the recorded `idea-ac-check` result (measured at the ac-generate
|
|
228
|
+
boundary — this gate never re-runs the CLI). Deliberately NOT --strict: before
|
|
209
229
|
decompose/batch-create a feature has scenarios but zero linked tasks, which
|
|
210
230
|
emits L4.orphan-scenarios at severity `warning`; --strict would elevate that
|
|
211
231
|
to a failure, making the success edges unreachable and looping ac-generate
|
|
212
232
|
into `failed`. Malformed AC still blocks via the error-severity L3 BDD
|
|
213
|
-
checks. Strict coverage belongs at the shippable gate, not here. The
|
|
214
|
-
is objective
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
back to ac-generate (≤3 retries) or escalates to failed.
|
|
233
|
+
checks. Strict coverage belongs at the shippable gate, not here. The recorded
|
|
234
|
+
result is objective (schema/AC validation) and auto-routable — under profile=auto
|
|
235
|
+
the transition guards route directly from ac-generate to the appropriate next
|
|
236
|
+
state, so this state is only entered in interactive mode. On failure, the retry
|
|
237
|
+
cap routes back to ac-generate (≤3 retries) or escalates to failed.
|
|
219
238
|
pause: true
|
|
220
239
|
onEnter:
|
|
221
240
|
- kind: hitl.confirm
|
|
@@ -232,7 +251,9 @@ states:
|
|
|
232
251
|
carries operator rejection feedback back into this state: on the first pass the agent
|
|
233
252
|
fills `## Proposed design`; on retry (operator feedback present) it revises the design,
|
|
234
253
|
records the reconciliation, and updates invalidated Acceptance Criteria through
|
|
235
|
-
`spur feature update` before design can exit.
|
|
254
|
+
`spur feature update` before design can exit. Design quality is then MEASURED once at
|
|
255
|
+
this boundary by the deterministic `idea-design-check` command.gate (task 0769). The
|
|
256
|
+
design-approval state follows (taste gate).
|
|
236
257
|
onEnter:
|
|
237
258
|
- kind: shell
|
|
238
259
|
options:
|
|
@@ -256,6 +277,17 @@ states:
|
|
|
256
277
|
command: >-
|
|
257
278
|
REVIEW=".spur/run/$__runId-idea-design-review.md" &&
|
|
258
279
|
awk '/^## Proposed design/{f=1; next} /^## Operator feedback/{f=0} f' "$REVIEW" | grep -q '[^[:space:]]'
|
|
280
|
+
# 0769: ONE measured check at the design author/revise boundary — after this pass's
|
|
281
|
+
# design writes (including AC reconciliation). softFail keeps FAIL routable; sibling
|
|
282
|
+
# guards consume the recorded result and never re-run the CLI.
|
|
283
|
+
- kind: command.gate
|
|
284
|
+
options:
|
|
285
|
+
id: idea-design-check
|
|
286
|
+
executable: "${vars.spurBin}"
|
|
287
|
+
args: ["feature", "check", "${vars.featureId}"]
|
|
288
|
+
resultFile: .spur/run/${vars.__runId}-idea-design-check.status
|
|
289
|
+
softFail: true
|
|
290
|
+
timeoutMs: 120000
|
|
259
291
|
|
|
260
292
|
- id: design-approval
|
|
261
293
|
description: >
|
|
@@ -295,11 +327,11 @@ states:
|
|
|
295
327
|
onEnter:
|
|
296
328
|
- kind: shell
|
|
297
329
|
options:
|
|
298
|
-
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-task-order.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-handoff.md"
|
|
330
|
+
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-task-order.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-handoff.md .spur/run/$__runId-idea-ready.json"
|
|
299
331
|
- kind: agent.run
|
|
300
332
|
options:
|
|
301
333
|
agent: ${vars.planningAgent}
|
|
302
|
-
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else.
|
|
334
|
+
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `design`, `plan`, `acceptance_criteria`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. `design`, `plan`, and `acceptance_criteria` are supported batch fields and normal default planning fills them from your analysis; the per-task refine step after batch-create still deepens them when a task needs more detail. Validate locally against the schema before emitting. Also emit the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json: a JSON array (one entry per batch item) of `{ name: <exact batch item name>, depends_on_names: [<batch item names>] }` declaring ordering/dependencies between the batch items; use `[]` when no ordering exists. Every `name` and every dependency must match exactly one batch item `name` — it is private workflow data, not part of task-batch.schema.json."
|
|
303
335
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
304
336
|
role: planner
|
|
305
337
|
expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
|
|
@@ -353,7 +385,7 @@ states:
|
|
|
353
385
|
command: >-
|
|
354
386
|
if test -f .spur/run/$__runId-idea-batch-create.done; then exit 0; fi;
|
|
355
387
|
rm -f .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-batch-create-result.json.tmp;
|
|
356
|
-
if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --json > .spur/run/$__runId-idea-batch-create-result.json.tmp && jq -e ".created == (.wbs | length)" .spur/run/$__runId-idea-batch-create-result.json.tmp >/dev/null 2>&1; then
|
|
388
|
+
if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --skip-ready --json > .spur/run/$__runId-idea-batch-create-result.json.tmp && jq -e ".created == (.wbs | length)" .spur/run/$__runId-idea-batch-create-result.json.tmp >/dev/null 2>&1; then
|
|
357
389
|
mv .spur/run/$__runId-idea-batch-create-result.json.tmp .spur/run/$__runId-idea-batch-create-result.json &&
|
|
358
390
|
date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.done;
|
|
359
391
|
else
|
|
@@ -361,6 +393,49 @@ states:
|
|
|
361
393
|
date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.failed;
|
|
362
394
|
fi
|
|
363
395
|
|
|
396
|
+
- id: ready-prepare
|
|
397
|
+
description: >
|
|
398
|
+
Ready-by-default preparation (0788): the planning owner applies the
|
|
399
|
+
ready-refinement checklist to each created task (requirements, design,
|
|
400
|
+
plan, ac, decisions, dependencies, premises) so the deterministic task
|
|
401
|
+
check passes, then writes the run-scoped ready evidence sidecar
|
|
402
|
+
(.spur/run/${vars.__runId}-idea-ready.json) with one row per task: wbs,
|
|
403
|
+
status (ready|failed|skipped), planningDigest, and seven check rows.
|
|
404
|
+
Handoff-finalize verifies presence, digest and checklist evidence before
|
|
405
|
+
recommending auto runall; missing or failing evidence never fails the
|
|
406
|
+
run — it degrades the recommendation to ready-depth refineall.
|
|
407
|
+
onEnter:
|
|
408
|
+
- kind: agent.run
|
|
409
|
+
options:
|
|
410
|
+
agent: ${vars.planningAgent}
|
|
411
|
+
# Declared Layer-1 role (0538 R2), same planner executor as decompose.
|
|
412
|
+
role: planner
|
|
413
|
+
timeoutMs: ${vars.stepTimeoutMs}
|
|
414
|
+
input: >-
|
|
415
|
+
Ready-by-default preparation for feature ${vars.featureId} (0788). The created WBS
|
|
416
|
+
values are in .spur/run/${vars.__runId}-idea-batch-create-result.json under .wbs.
|
|
417
|
+
For EACH wbs: resolve the task file with `spur task path <wbs> --json`; open the
|
|
418
|
+
task doc and apply the ready-refinement checklist — make requirements, design,
|
|
419
|
+
plan, acceptance criteria, decisions, dependencies and premises present and
|
|
420
|
+
non-placeholder so `spur task check <wbs> --json` exits 0; edit only planning
|
|
421
|
+
sections, never Solution/Testing/Review/History. Record one checklist row per id
|
|
422
|
+
with concrete evidence of how you verified it. Compute the planning digest with
|
|
423
|
+
the project's own implementation when this is a monorepo checkout:
|
|
424
|
+
`bun -e 'const m = await import("./packages/app/src/services/task-readiness"); console.log(m.computePlanningDigest(await Bun.file(process.argv[1]).text()))' <task-file>`
|
|
425
|
+
— when that is impossible in this checkout, set status "skipped" instead of
|
|
426
|
+
guessing a digest. Finally write .spur/run/${vars.__runId}-idea-ready.json with
|
|
427
|
+
exactly this shape: {"runId":"${vars.__runId}","depth":"ready","tasks":[{"wbs":"<wbs>","status":"ready" or "failed" or "skipped","planningDigest":"<sha256 hex>","checks":[{"id":"requirements" or "design" or "plan" or "ac" or "decisions" or "dependencies" or "premises","pass":true or false,"evidence":"<how verified>"}]}]}. A task you cannot fully prepare gets status "failed" or "skipped" — never fabricate evidence; the handoff degrades to refineall.
|
|
428
|
+
# Fail-closed shape validation (mirrors the order-sidecar guard). Absence is
|
|
429
|
+
# normalized to an empty sidecar: finalize and the seeded fallback then degrade
|
|
430
|
+
# the recommendation to refineall instead of failing the run. A PRESENT but
|
|
431
|
+
# malformed sidecar fails the run here — it must not masquerade as evidence.
|
|
432
|
+
- kind: shell
|
|
433
|
+
options:
|
|
434
|
+
command: >-
|
|
435
|
+
READY=".spur/run/$__runId-idea-ready.json" &&
|
|
436
|
+
if ! test -f "$READY"; then printf '{"runId":"%s","depth":"ready","tasks":[]}\n' "$__runId" > "$READY"; fi &&
|
|
437
|
+
jq -e 'type == "object" and (.tasks | type == "array") and (all(.tasks[]; (.wbs | type == "string") and (.status == "ready" or .status == "failed" or .status == "skipped") and (.planningDigest | type == "string") and (.checks | type == "array") and (all(.checks[]; (.id | type == "string") and (.pass | type == "boolean") and (.evidence | type == "string")))))' "$READY" >/dev/null
|
|
438
|
+
|
|
364
439
|
- id: handoff-finalize
|
|
365
440
|
description: >
|
|
366
441
|
Post-create finalization (0518): zip batch item names to the created WBS
|
|
@@ -426,7 +501,9 @@ states:
|
|
|
426
501
|
CHECK_ROWS=$(wc -l < "$CHECKS" | tr -d ' ') &&
|
|
427
502
|
WBS_COUNT=$(printf '%s\n' $WBS_LIST | wc -l | tr -d ' ') &&
|
|
428
503
|
test "$CHECK_ROWS" = "$WBS_COUNT" &&
|
|
429
|
-
|
|
504
|
+
READY=".spur/run/$__runId-idea-ready.json" &&
|
|
505
|
+
(test -f "$READY" || printf '{"tasks":[]}\n' > "$READY") &&
|
|
506
|
+
NEXT=$(jq -r --arg feature "$featureId" --slurpfile c "$CHECKS" --slurpfile k "$READY" 'if (($c | any(.[]; .pass == false)) or (($k[0].tasks // []) | length == 0) or (($k[0].tasks // []) | any(.[]; .status != "ready"))) then "/sp:dev-refineall --feature \($feature) --auto --depth ready" else "/sp:dev-runall --feature \($feature) --auto" end' "$RESULT") &&
|
|
430
507
|
{
|
|
431
508
|
echo "# Idea pipeline handoff report";
|
|
432
509
|
echo;
|
|
@@ -466,10 +543,6 @@ states:
|
|
|
466
543
|
options:
|
|
467
544
|
path: .spur/run/${vars.__runId}-idea-handoff.md
|
|
468
545
|
artifactKind: idea-handoff
|
|
469
|
-
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
470
|
-
- kind: shell
|
|
471
|
-
options:
|
|
472
|
-
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'
|
|
473
546
|
|
|
474
547
|
- id: cancelled
|
|
475
548
|
description: Terminal — pipeline cancelled by operator or error.
|
|
@@ -545,9 +618,10 @@ transitions:
|
|
|
545
618
|
|
|
546
619
|
# ── ac-generate: auto-skip (profile=auto) OR enter feature-check HITL gate (interactive) ──
|
|
547
620
|
# Declaration order: auto-skip guards tried FIRST (same pattern as task-pipeline review→verify
|
|
548
|
-
# and design-gen→handoff). Under profile=auto, the
|
|
549
|
-
#
|
|
550
|
-
# the feature-check state whose onEnter hitl.confirm
|
|
621
|
+
# and design-gen→handoff). Under profile=auto, the recorded `idea-ac-check` result routes
|
|
622
|
+
# directly to the appropriate next state — guards never re-run the CLI (task 0769). Under
|
|
623
|
+
# interactive, the always fallback enters the feature-check state whose onEnter hitl.confirm
|
|
624
|
+
# pauses for operator confirmation.
|
|
551
625
|
#
|
|
552
626
|
# Auto-skip 1: pass + design route → system-design (design=auto + needs_design != false)
|
|
553
627
|
- from: ac-generate
|
|
@@ -556,7 +630,7 @@ transitions:
|
|
|
556
630
|
guard:
|
|
557
631
|
kind: shell
|
|
558
632
|
options:
|
|
559
|
-
command: 'test "$profile" = auto && $
|
|
633
|
+
command: 'test "$profile" = auto && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" = PASS && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
560
634
|
# Auto-skip 2: pass + skip-design route → decompose
|
|
561
635
|
- from: ac-generate
|
|
562
636
|
to: decompose
|
|
@@ -564,7 +638,7 @@ transitions:
|
|
|
564
638
|
guard:
|
|
565
639
|
kind: shell
|
|
566
640
|
options:
|
|
567
|
-
command: 'test "$profile" = auto && $
|
|
641
|
+
command: 'test "$profile" = auto && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" = PASS && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
568
642
|
# Auto-skip 3: check failed, retry < 3 → loop back to ac-generate (self-loop)
|
|
569
643
|
- from: ac-generate
|
|
570
644
|
to: ac-generate
|
|
@@ -572,7 +646,7 @@ transitions:
|
|
|
572
646
|
guard:
|
|
573
647
|
kind: shell
|
|
574
648
|
options:
|
|
575
|
-
command: 'test "$profile" = auto &&
|
|
649
|
+
command: 'test "$profile" = auto && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" != PASS && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
576
650
|
# Auto-skip 4: check failed, retry cap reached → escalate to failed
|
|
577
651
|
- from: ac-generate
|
|
578
652
|
to: failed
|
|
@@ -580,7 +654,7 @@ transitions:
|
|
|
580
654
|
guard:
|
|
581
655
|
kind: shell
|
|
582
656
|
options:
|
|
583
|
-
command: 'test "$profile" = auto &&
|
|
657
|
+
command: 'test "$profile" = auto && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" != PASS && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
584
658
|
# Interactive fallback: enter feature-check HITL gate
|
|
585
659
|
- from: ac-generate
|
|
586
660
|
to: feature-check
|
|
@@ -596,28 +670,28 @@ transitions:
|
|
|
596
670
|
guard:
|
|
597
671
|
kind: shell
|
|
598
672
|
options:
|
|
599
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
673
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" = PASS && test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" != false'
|
|
600
674
|
- from: feature-check
|
|
601
675
|
to: decompose
|
|
602
676
|
description: Feature check passed, skip-design route — go directly to decompose.
|
|
603
677
|
guard:
|
|
604
678
|
kind: shell
|
|
605
679
|
options:
|
|
606
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
680
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" = PASS && (test "$design" = skip || (test "$design" = auto && test "$(jq -r .needs_design .spur/run/$__runId-idea-needs-design.json 2>/dev/null)" = false))'
|
|
607
681
|
- from: feature-check
|
|
608
682
|
to: ac-generate
|
|
609
683
|
description: "Feature check failed — revise AC (retry cap: 3)."
|
|
610
684
|
guard:
|
|
611
685
|
kind: shell
|
|
612
686
|
options:
|
|
613
|
-
command: '(test "$__hitlAnswer" = no ||
|
|
687
|
+
command: '(test "$__hitlAnswer" = no || test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" != PASS) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -lt 3'
|
|
614
688
|
- from: feature-check
|
|
615
689
|
to: failed
|
|
616
690
|
description: Feature check failed after 3 retries — escalate to failed.
|
|
617
691
|
guard:
|
|
618
692
|
kind: shell
|
|
619
693
|
options:
|
|
620
|
-
command: '(test "$__hitlAnswer" = no ||
|
|
694
|
+
command: '(test "$__hitlAnswer" = no || test "$(cat .spur/run/$__runId-idea-ac-check.status 2>/dev/null)" != PASS) && test "$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0)" -ge 3'
|
|
621
695
|
- from: feature-check
|
|
622
696
|
to: cancelled
|
|
623
697
|
description: Operator cancelled the feature-check gate.
|
|
@@ -630,11 +704,11 @@ transitions:
|
|
|
630
704
|
# Declaration order: auto-skip guard tried FIRST (like task-pipeline's review->verify pattern).
|
|
631
705
|
- from: system-design
|
|
632
706
|
to: decompose
|
|
633
|
-
description: profile=auto AND design_approved=true AND
|
|
707
|
+
description: profile=auto AND design_approved=true AND recorded design check passes — skip design approval gate.
|
|
634
708
|
guard:
|
|
635
709
|
kind: shell
|
|
636
710
|
options:
|
|
637
|
-
command: 'test "$profile" = auto && test "$design_approved" = true && $
|
|
711
|
+
command: 'test "$profile" = auto && test "$design_approved" = true && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" = PASS'
|
|
638
712
|
- from: system-design
|
|
639
713
|
to: design-approval
|
|
640
714
|
description: System design done — gate on design approval.
|
|
@@ -644,18 +718,18 @@ transitions:
|
|
|
644
718
|
# ── design-approval -> decompose (after operator approval), AC-failure -> feature-check ──
|
|
645
719
|
- from: design-approval
|
|
646
720
|
to: decompose
|
|
647
|
-
description: Design approved and
|
|
721
|
+
description: Design approved and recorded design check passes — proceed to decomposition.
|
|
648
722
|
guard:
|
|
649
723
|
kind: shell
|
|
650
724
|
options:
|
|
651
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
725
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" = PASS'
|
|
652
726
|
- from: design-approval
|
|
653
727
|
to: feature-check
|
|
654
|
-
description: "Design approved but
|
|
728
|
+
description: "Design approved but recorded design check fails — route back through the AC gate (revise AC, retry cap: 3)."
|
|
655
729
|
guard:
|
|
656
730
|
kind: shell
|
|
657
731
|
options:
|
|
658
|
-
command: 'test "$__hitlAnswer" = yes &&
|
|
732
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" != PASS'
|
|
659
733
|
- from: design-approval
|
|
660
734
|
to: system-design
|
|
661
735
|
description: "Design rejected - revise system design (cap: 1 revise)."
|
|
@@ -715,14 +789,20 @@ transitions:
|
|
|
715
789
|
options:
|
|
716
790
|
command: 'test "$__hitlAnswer" = cancel'
|
|
717
791
|
|
|
718
|
-
# ── batch-create-run: success ->
|
|
792
|
+
# ── batch-create-run: success -> ready-prepare, failure -> decompose (retry cap: 3), failure+cap -> failed ──
|
|
719
793
|
- from: batch-create-run
|
|
720
|
-
to:
|
|
721
|
-
description: Batch created and result captured — apply
|
|
794
|
+
to: ready-prepare
|
|
795
|
+
description: Batch created and result captured — apply ready-by-default preparation, then finalize the handoff.
|
|
722
796
|
guard:
|
|
723
797
|
kind: shell
|
|
724
798
|
options:
|
|
725
799
|
command: "test -f .spur/run/$__runId-idea-batch-create.done"
|
|
800
|
+
# ── ready-prepare: -> handoff-finalize (always; evidence absence degrades to refineall, never fails the run) ──
|
|
801
|
+
- from: ready-prepare
|
|
802
|
+
to: handoff-finalize
|
|
803
|
+
description: Ready evidence sidecar written (or normalized empty) — finalize the handoff report.
|
|
804
|
+
guard:
|
|
805
|
+
kind: always
|
|
726
806
|
- from: batch-create-run
|
|
727
807
|
to: decompose
|
|
728
808
|
description: "Batch-create failed — revise decomposition (retry cap: 3)."
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
# __runId — injected by WorkflowAppService.run(); scopes .spur/run artifacts.
|
|
32
32
|
|
|
33
33
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
34
|
+
version: "1"
|
|
34
35
|
name: pr-review
|
|
35
36
|
kind: state-machine
|
|
36
37
|
description: GitHub Codex PR-review spine — preflight → hygiene → precheck → push → ensure-pr → request → wait → collect
|
|
@@ -51,6 +52,10 @@ vars:
|
|
|
51
52
|
waitIntervalSec: "30"
|
|
52
53
|
preReviewCmd: ""
|
|
53
54
|
__runId: ""
|
|
55
|
+
# Runtime-set by file.read.into-var (0771): the request record's requestedAt|head,
|
|
56
|
+
# extracted once by `request` and read per state — no repeated JSON parsing in shell.
|
|
57
|
+
prSince: ""
|
|
58
|
+
prHead: ""
|
|
54
59
|
|
|
55
60
|
states:
|
|
56
61
|
- id: preflight
|
|
@@ -155,42 +160,60 @@ states:
|
|
|
155
160
|
--status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-request.json";
|
|
156
161
|
rc=$?; set -e &&
|
|
157
162
|
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
163
|
+
[ -f ".spur/run/$__runId-pr-request.json" ] &&
|
|
164
|
+
bun -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));fs.writeFileSync(process.argv[2],(r.requestedAt ?? "")+"\n");fs.writeFileSync(process.argv[3],(r.head ?? "")+"\n")' ".spur/run/$__runId-pr-request.json" ".spur/run/$__runId-pr-since.txt" ".spur/run/$__runId-pr-head.txt";
|
|
158
165
|
exit 0
|
|
159
166
|
|
|
160
167
|
- id: wait
|
|
161
168
|
description: >
|
|
162
169
|
Bounded poll for Codex output on the current pushed HEAD. Timeout records TIMEOUT
|
|
163
170
|
and routes to `pending` — never a failure (collect later with /sp:dev-pr-review collect).
|
|
171
|
+
Reads the request record once at `request` (0771): requestedAt/head land in
|
|
172
|
+
run-scoped .txt files, projected into prSince/prHead vars via file.read.into-var.
|
|
164
173
|
onEnter:
|
|
174
|
+
- kind: file.read.into-var
|
|
175
|
+
options:
|
|
176
|
+
path: .spur/run/${vars.__runId}-pr-since.txt
|
|
177
|
+
var: prSince
|
|
178
|
+
- kind: file.read.into-var
|
|
179
|
+
options:
|
|
180
|
+
path: .spur/run/${vars.__runId}-pr-head.txt
|
|
181
|
+
var: prHead
|
|
165
182
|
- kind: shell
|
|
166
183
|
options:
|
|
167
184
|
command: >-
|
|
168
185
|
mkdir -p .spur/run &&
|
|
169
186
|
STATUS_FILE=".spur/run/$__runId-pr-wait.status" &&
|
|
170
187
|
set +e &&
|
|
171
|
-
|
|
172
|
-
REQUEST_HEAD="$(bun -e 'const fs=require("node:fs");const value=JSON.parse(fs.readFileSync(process.argv[1],"utf8")).head;process.stdout.write(value ?? "")' ".spur/run/$__runId-pr-request.json")" &&
|
|
173
|
-
bun "$(superskill script path sp pr-reviewing.ts)" wait --since "$SINCE" --head "$REQUEST_HEAD" --timeout "$waitTimeoutSec" --interval "$waitIntervalSec"
|
|
188
|
+
bun "$(superskill script path sp pr-reviewing.ts)" wait --since "$prSince" --head "$prHead" --timeout "$waitTimeoutSec" --interval "$waitIntervalSec"
|
|
174
189
|
--json --status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-wait.json";
|
|
175
190
|
rc=$?; set -e &&
|
|
176
191
|
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
177
192
|
exit 0
|
|
178
193
|
|
|
179
194
|
- id: collect
|
|
180
|
-
description:
|
|
195
|
+
description: >-
|
|
196
|
+
Normalize the latest current-HEAD Codex result and record composite PR/CI status.
|
|
197
|
+
Request record read via file.read.into-var vars (0771, see wait).
|
|
181
198
|
onEnter:
|
|
199
|
+
- kind: file.read.into-var
|
|
200
|
+
options:
|
|
201
|
+
path: .spur/run/${vars.__runId}-pr-since.txt
|
|
202
|
+
var: prSince
|
|
203
|
+
- kind: file.read.into-var
|
|
204
|
+
options:
|
|
205
|
+
path: .spur/run/${vars.__runId}-pr-head.txt
|
|
206
|
+
var: prHead
|
|
182
207
|
- kind: shell
|
|
183
208
|
options:
|
|
184
209
|
command: >-
|
|
185
210
|
mkdir -p .spur/run &&
|
|
186
211
|
STATUS_FILE=".spur/run/$__runId-pr-collect.status" &&
|
|
187
212
|
set +e &&
|
|
188
|
-
|
|
189
|
-
REQUEST_HEAD="$(bun -e 'const fs=require("node:fs");const value=JSON.parse(fs.readFileSync(process.argv[1],"utf8")).head;process.stdout.write(value ?? "")' ".spur/run/$__runId-pr-request.json")" &&
|
|
190
|
-
bun "$(superskill script path sp pr-reviewing.ts)" collect --since "$SINCE" --head "$REQUEST_HEAD" --json
|
|
213
|
+
bun "$(superskill script path sp pr-reviewing.ts)" collect --since "$prSince" --head "$prHead" --json
|
|
191
214
|
--status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-findings.json";
|
|
192
215
|
collect_rc=$?;
|
|
193
|
-
bun "$(superskill script path sp pr-reviewing.ts)" status --since "$
|
|
216
|
+
bun "$(superskill script path sp pr-reviewing.ts)" status --since "$prSince" --head "$prHead" --json > ".spur/run/$__runId-pr-status.json";
|
|
194
217
|
status_rc=$?; set -e &&
|
|
195
218
|
if [ "$collect_rc" -ne 0 ] || [ "$status_rc" -ne 0 ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
196
219
|
if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
21
21
|
kind: state-machine
|
|
22
22
|
name: task-lifecycle
|
|
23
|
+
version: "1"
|
|
23
24
|
description: >
|
|
24
25
|
Task lifecycle FSM (design §2.3, §5.1). States are the canonical TaskStatus
|
|
25
26
|
vocabulary (DD-01); transitions encode the §2.3 graph; guards invoke
|