@gobing-ai/spur 0.3.73 → 0.3.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +92 -55
- package/config/workflows/feature-dev.yaml +43 -20
- package/config/workflows/feature-lifecycle.yaml +1 -0
- package/config/workflows/history-anatomy.yaml +8 -4
- package/config/workflows/idea-pipeline.yaml +66 -33
- package/config/workflows/pr-review.yaml +31 -8
- package/config/workflows/task-lifecycle.yaml +1 -0
- package/config/workflows/task-pipeline.yaml +929 -905
- package/config/workflows/wayfinder-resolution.yaml +124 -90
- package/config/workflows/wrapup-pipeline.yaml +208 -83
- package/package.json +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 +1 -1
- 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/execution-batch.md +3 -3
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -2
- package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
- package/spur.js +1125 -784
- package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.C60RJZRj.js} +78 -77
- package/web/_astro/BoardApp.ymfj1EHA.js +1 -0
- package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.Guk8VuNu.js} +1 -1
- package/web/_astro/{arc.BySSh34M.js → arc.CAZtlWJM.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.9-XbF_Tz.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.DC4WLx3L.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.JgjUQMgG.js} +1 -1
- package/web/_astro/channel.Bxxe2Byc.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.D_JFiXa-.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.Bk_RxoeT.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.a_Rj2VxL.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.C2NoS1U6.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.D1Xn6CNa.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.BhJrSBsX.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.ByVGduYQ.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.B47Paunq.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.BERMSD5C.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.BERMSD5C.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.O_rHGlhU.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.qCW2GdNp.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.C5Y4lH_u.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.DxZmRXxF.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.qSxZeYSK.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.-7vl3bXs.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.BHVV-UH3.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DHAuW9sK.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.DMRLQwPN.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.B3EpPp_8.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.BgUrExXM.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.CU-9yVN4.js} +1 -1
- package/web/_astro/{index.B5MTfe7k.css → index.BhvM3djt.css} +1 -1
- package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.CvVTCRLe.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.BUzOfKPf.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.BBTb7ziz.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.CTQQr70i.js} +1 -1
- package/web/_astro/{linear.D7uqzENp.js → linear.CLKlJPiS.js} +1 -1
- package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.BA4wFhLP.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.t1vG2l5e.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.C8zJhyTu.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DqYYvb7T.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.lzfDZ9nI.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.lm6N5ORM.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.E6_IV4Dj.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.Bnv6Ok2p.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.mJHH_Ng_.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BhRgqSLa.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.pjb5DMC9.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.CW20KUng.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.vX9_yuMl.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
|
@@ -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: "1"
|
|
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: >
|
|
@@ -299,7 +331,7 @@ states:
|
|
|
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
|
|
@@ -545,9 +577,10 @@ transitions:
|
|
|
545
577
|
|
|
546
578
|
# ── ac-generate: auto-skip (profile=auto) OR enter feature-check HITL gate (interactive) ──
|
|
547
579
|
# 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
|
|
580
|
+
# and design-gen→handoff). Under profile=auto, the recorded `idea-ac-check` result routes
|
|
581
|
+
# directly to the appropriate next state — guards never re-run the CLI (task 0769). Under
|
|
582
|
+
# interactive, the always fallback enters the feature-check state whose onEnter hitl.confirm
|
|
583
|
+
# pauses for operator confirmation.
|
|
551
584
|
#
|
|
552
585
|
# Auto-skip 1: pass + design route → system-design (design=auto + needs_design != false)
|
|
553
586
|
- from: ac-generate
|
|
@@ -556,7 +589,7 @@ transitions:
|
|
|
556
589
|
guard:
|
|
557
590
|
kind: shell
|
|
558
591
|
options:
|
|
559
|
-
command: 'test "$profile" = auto && $
|
|
592
|
+
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
593
|
# Auto-skip 2: pass + skip-design route → decompose
|
|
561
594
|
- from: ac-generate
|
|
562
595
|
to: decompose
|
|
@@ -564,7 +597,7 @@ transitions:
|
|
|
564
597
|
guard:
|
|
565
598
|
kind: shell
|
|
566
599
|
options:
|
|
567
|
-
command: 'test "$profile" = auto && $
|
|
600
|
+
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
601
|
# Auto-skip 3: check failed, retry < 3 → loop back to ac-generate (self-loop)
|
|
569
602
|
- from: ac-generate
|
|
570
603
|
to: ac-generate
|
|
@@ -572,7 +605,7 @@ transitions:
|
|
|
572
605
|
guard:
|
|
573
606
|
kind: shell
|
|
574
607
|
options:
|
|
575
|
-
command: 'test "$profile" = auto &&
|
|
608
|
+
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
609
|
# Auto-skip 4: check failed, retry cap reached → escalate to failed
|
|
577
610
|
- from: ac-generate
|
|
578
611
|
to: failed
|
|
@@ -580,7 +613,7 @@ transitions:
|
|
|
580
613
|
guard:
|
|
581
614
|
kind: shell
|
|
582
615
|
options:
|
|
583
|
-
command: 'test "$profile" = auto &&
|
|
616
|
+
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
617
|
# Interactive fallback: enter feature-check HITL gate
|
|
585
618
|
- from: ac-generate
|
|
586
619
|
to: feature-check
|
|
@@ -596,28 +629,28 @@ transitions:
|
|
|
596
629
|
guard:
|
|
597
630
|
kind: shell
|
|
598
631
|
options:
|
|
599
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
632
|
+
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
633
|
- from: feature-check
|
|
601
634
|
to: decompose
|
|
602
635
|
description: Feature check passed, skip-design route — go directly to decompose.
|
|
603
636
|
guard:
|
|
604
637
|
kind: shell
|
|
605
638
|
options:
|
|
606
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
639
|
+
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
640
|
- from: feature-check
|
|
608
641
|
to: ac-generate
|
|
609
642
|
description: "Feature check failed — revise AC (retry cap: 3)."
|
|
610
643
|
guard:
|
|
611
644
|
kind: shell
|
|
612
645
|
options:
|
|
613
|
-
command: '(test "$__hitlAnswer" = no ||
|
|
646
|
+
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
647
|
- from: feature-check
|
|
615
648
|
to: failed
|
|
616
649
|
description: Feature check failed after 3 retries — escalate to failed.
|
|
617
650
|
guard:
|
|
618
651
|
kind: shell
|
|
619
652
|
options:
|
|
620
|
-
command: '(test "$__hitlAnswer" = no ||
|
|
653
|
+
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
654
|
- from: feature-check
|
|
622
655
|
to: cancelled
|
|
623
656
|
description: Operator cancelled the feature-check gate.
|
|
@@ -630,11 +663,11 @@ transitions:
|
|
|
630
663
|
# Declaration order: auto-skip guard tried FIRST (like task-pipeline's review->verify pattern).
|
|
631
664
|
- from: system-design
|
|
632
665
|
to: decompose
|
|
633
|
-
description: profile=auto AND design_approved=true AND
|
|
666
|
+
description: profile=auto AND design_approved=true AND recorded design check passes — skip design approval gate.
|
|
634
667
|
guard:
|
|
635
668
|
kind: shell
|
|
636
669
|
options:
|
|
637
|
-
command: 'test "$profile" = auto && test "$design_approved" = true && $
|
|
670
|
+
command: 'test "$profile" = auto && test "$design_approved" = true && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" = PASS'
|
|
638
671
|
- from: system-design
|
|
639
672
|
to: design-approval
|
|
640
673
|
description: System design done — gate on design approval.
|
|
@@ -644,18 +677,18 @@ transitions:
|
|
|
644
677
|
# ── design-approval -> decompose (after operator approval), AC-failure -> feature-check ──
|
|
645
678
|
- from: design-approval
|
|
646
679
|
to: decompose
|
|
647
|
-
description: Design approved and
|
|
680
|
+
description: Design approved and recorded design check passes — proceed to decomposition.
|
|
648
681
|
guard:
|
|
649
682
|
kind: shell
|
|
650
683
|
options:
|
|
651
|
-
command: 'test "$__hitlAnswer" = yes && $
|
|
684
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" = PASS'
|
|
652
685
|
- from: design-approval
|
|
653
686
|
to: feature-check
|
|
654
|
-
description: "Design approved but
|
|
687
|
+
description: "Design approved but recorded design check fails — route back through the AC gate (revise AC, retry cap: 3)."
|
|
655
688
|
guard:
|
|
656
689
|
kind: shell
|
|
657
690
|
options:
|
|
658
|
-
command: 'test "$__hitlAnswer" = yes &&
|
|
691
|
+
command: 'test "$__hitlAnswer" = yes && test "$(cat .spur/run/$__runId-idea-design-check.status 2>/dev/null)" != PASS'
|
|
659
692
|
- from: design-approval
|
|
660
693
|
to: system-design
|
|
661
694
|
description: "Design rejected - revise system design (cap: 1 revise)."
|
|
@@ -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
|