@gobing-ai/spur 0.3.29 → 0.3.31

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 (71) hide show
  1. package/config/rules/strict/runtime-boundaries.yaml +2 -1
  2. package/config/templates/AGENTS.md +3 -8
  3. package/config/templates/docs/04_DESIGN.md +8 -5
  4. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +4 -2
  5. package/config/workflows/basic.yaml +92 -14
  6. package/config/workflows/docs-pipeline.yaml +71 -25
  7. package/config/workflows/feature-dev.yaml +104 -35
  8. package/config/workflows/feature-lifecycle.yaml +10 -2
  9. package/config/workflows/idea-pipeline.yaml +118 -54
  10. package/config/workflows/planning-pipeline.yaml +78 -30
  11. package/config/workflows/task-lifecycle.yaml +10 -2
  12. package/config/workflows/task-pipeline.yaml +214 -56
  13. package/config/workflows/wayfinder-resolution.yaml +64 -27
  14. package/config/workflows/wrapup-pipeline.yaml +79 -39
  15. package/package.json +9 -9
  16. package/schemas/state-machine-workflow.schema.json +5 -0
  17. package/spur.js +1306 -764
  18. package/web/_astro/BoardApp.CBVEjvSW.js +1 -0
  19. package/web/_astro/{BoardApp.BwAdAgK9.js → BoardApp.CyyIiK76.js} +74 -74
  20. package/web/_astro/TaskDetail.BfUuRsfM.js +1 -0
  21. package/web/_astro/{arc.CO-I_PZe.js → arc.C7fql4LL.js} +1 -1
  22. package/web/_astro/{architectureDiagram-3BPJPVTR.Si-A1Ncl.js → architectureDiagram-3BPJPVTR.IJPsRX-V.js} +1 -1
  23. package/web/_astro/{blockDiagram-GPEHLZMM.CeNy_ta6.js → blockDiagram-GPEHLZMM.B1AUwBxq.js} +1 -1
  24. package/web/_astro/{c4Diagram-AAUBKEIU.BgN_scEU.js → c4Diagram-AAUBKEIU.BuSJPR3z.js} +1 -1
  25. package/web/_astro/channel.D3uZSASb.js +1 -0
  26. package/web/_astro/{chunk-2J33WTMH.C3oDtmdi.js → chunk-2J33WTMH.CaOPTr9l.js} +1 -1
  27. package/web/_astro/{chunk-4BX2VUAB.Bv8Fxh4H.js → chunk-4BX2VUAB.DQzxExn8.js} +1 -1
  28. package/web/_astro/{chunk-55IACEB6.BT-TqtV6.js → chunk-55IACEB6.IIb103c7.js} +1 -1
  29. package/web/_astro/{chunk-727SXJPM.DkGMsI0B.js → chunk-727SXJPM.DOYz1K1b.js} +1 -1
  30. package/web/_astro/{chunk-AQP2D5EJ.B_-SqGnF.js → chunk-AQP2D5EJ.CFcfW-wt.js} +1 -1
  31. package/web/_astro/{chunk-FMBD7UC4.DwR3skMv.js → chunk-FMBD7UC4.C7Hn0jo3.js} +1 -1
  32. package/web/_astro/{chunk-ND2GUHAM.BaTQutFl.js → chunk-ND2GUHAM.Ba83VQ1m.js} +1 -1
  33. package/web/_astro/{chunk-QZHKN3VN.BJMSqqJp.js → chunk-QZHKN3VN.CizNbIIP.js} +1 -1
  34. package/web/_astro/{classDiagram-4FO5ZUOK.SaqCNUPF.js → classDiagram-4FO5ZUOK.2NYYPXHq.js} +1 -1
  35. package/web/_astro/{classDiagram-v2-Q7XG4LA2.SaqCNUPF.js → classDiagram-v2-Q7XG4LA2.2NYYPXHq.js} +1 -1
  36. package/web/_astro/{cose-bilkent-S5V4N54A.BkmjA_E2.js → cose-bilkent-S5V4N54A.DwBqVzml.js} +1 -1
  37. package/web/_astro/{dagre-BM42HDAG.CNUvWhrX.js → dagre-BM42HDAG.DqnYsHWG.js} +1 -1
  38. package/web/_astro/{diagram-2AECGRRQ.C68Cg9uE.js → diagram-2AECGRRQ.D8TKhgy-.js} +1 -1
  39. package/web/_astro/{diagram-5GNKFQAL.DbHkPLEn.js → diagram-5GNKFQAL.BUD6Prj5.js} +1 -1
  40. package/web/_astro/{diagram-KO2AKTUF.Dt0EdErs.js → diagram-KO2AKTUF.Cvg7hmND.js} +1 -1
  41. package/web/_astro/{diagram-LMA3HP47.Bixs6mnm.js → diagram-LMA3HP47.FUG_B9eS.js} +1 -1
  42. package/web/_astro/{diagram-OG6HWLK6.B4KBI6KT.js → diagram-OG6HWLK6.XsBZQ0hQ.js} +1 -1
  43. package/web/_astro/{erDiagram-TEJ5UH35.C_4i9Xvx.js → erDiagram-TEJ5UH35.M_LSGgLQ.js} +1 -1
  44. package/web/_astro/{flowDiagram-I6XJVG4X.CfGFczAY.js → flowDiagram-I6XJVG4X.DZikZBRa.js} +1 -1
  45. package/web/_astro/{ganttDiagram-6RSMTGT7.C2AIvtmg.js → ganttDiagram-6RSMTGT7.DTKuEmXY.js} +1 -1
  46. package/web/_astro/{gitGraphDiagram-PVQCEYII.HlXuGqQg.js → gitGraphDiagram-PVQCEYII.Dt-E9XmY.js} +1 -1
  47. package/web/_astro/index.yAse9IaO.css +1 -0
  48. package/web/_astro/{infoDiagram-5YYISTIA.BWQJsJ4Z.js → infoDiagram-5YYISTIA.D5snevU1.js} +1 -1
  49. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CCHgIHik.js → ishikawaDiagram-YF4QCWOH.DmXdBf9W.js} +1 -1
  50. package/web/_astro/{journeyDiagram-JHISSGLW.D9M6YwOJ.js → journeyDiagram-JHISSGLW.HPJSRkkU.js} +1 -1
  51. package/web/_astro/{kanban-definition-UN3LZRKU.K20D-a1_.js → kanban-definition-UN3LZRKU.BxCqCnL1.js} +1 -1
  52. package/web/_astro/{linear.DeP5Qsol.js → linear.BnXQUpS-.js} +1 -1
  53. package/web/_astro/{mermaid.core.ClzxNMPU.js → mermaid.core.D-ZeOmpg.js} +4 -4
  54. package/web/_astro/{mindmap-definition-RKZ34NQL.CGn9RUNj.js → mindmap-definition-RKZ34NQL.DxrftiFR.js} +1 -1
  55. package/web/_astro/{pieDiagram-4H26LBE5.CoA92Csb.js → pieDiagram-4H26LBE5.D-ZcWxwg.js} +1 -1
  56. package/web/_astro/{quadrantDiagram-W4KKPZXB.CJ_UPjM1.js → quadrantDiagram-W4KKPZXB.C-dVJ7GW.js} +1 -1
  57. package/web/_astro/{requirementDiagram-4Y6WPE33.KkEPNNUU.js → requirementDiagram-4Y6WPE33.lalE_R8X.js} +1 -1
  58. package/web/_astro/{sankeyDiagram-5OEKKPKP.CoCWbQgh.js → sankeyDiagram-5OEKKPKP.C4uPynAF.js} +1 -1
  59. package/web/_astro/{sequenceDiagram-3UESZ5HK.DUFpV3EP.js → sequenceDiagram-3UESZ5HK.uBOenkqh.js} +1 -1
  60. package/web/_astro/{stateDiagram-AJRCARHV.CgWX0_9m.js → stateDiagram-AJRCARHV.CQOusSP2.js} +1 -1
  61. package/web/_astro/{stateDiagram-v2-BHNVJYJU.BKUXeGSf.js → stateDiagram-v2-BHNVJYJU.Bw0ZdjYb.js} +1 -1
  62. package/web/_astro/{timeline-definition-PNZ67QCA.CXayAguY.js → timeline-definition-PNZ67QCA.DbkPA5ce.js} +1 -1
  63. package/web/_astro/{vennDiagram-CIIHVFJN.DPak_Elh.js → vennDiagram-CIIHVFJN.CIKhzwoA.js} +1 -1
  64. package/web/_astro/{wardley-L42UT6IY.t3ZlDKd0.js → wardley-L42UT6IY.0P-tqaFs.js} +1 -1
  65. package/web/_astro/{wardleyDiagram-YWT4CUSO.CwPSVASQ.js → wardleyDiagram-YWT4CUSO.DrwTvM_M.js} +1 -1
  66. package/web/_astro/{xychartDiagram-2RQKCTM6.CJfrQgpF.js → xychartDiagram-2RQKCTM6.BgJsQVms.js} +1 -1
  67. package/web/index.html +2 -2
  68. package/web/_astro/BoardApp.Bk3T0o6s.js +0 -1
  69. package/web/_astro/TaskDetail.L36slEqE.js +0 -1
  70. package/web/_astro/channel.CYdGnFHM.js +0 -1
  71. package/web/_astro/index.D5Wqk2Tw.css +0 -1
@@ -60,7 +60,8 @@ rules:
60
60
  - "packages/app/src/services/token-ledger-service.ts" # FD byte-window log tailing
61
61
  - "packages/app/src/services/token-ledger-watcher.ts" # node:fs watch() live watcher
62
62
  - "packages/app/src/services/project-registry.ts" # atomic projects.json persistence
63
- - "packages/app/src/observability/run-output-sink.ts" # sync FD append for mid-run tail-able artifact (task 0414 R2); observe() is sync from the lifecycle relay
63
+ - "packages/app/src/observability/workflow-run-log-sink.ts" # sync FD append for mid-run tail-able all-in-one run log (task 0426 / feature D2); append() is sync from the observability bus
64
+ - "apps/cli/src/commands/workflow.ts" # FD byte-window tail of the mid-run run log for `workflow trace --follow` streaming (task 0428 / feature D2); readSync at offset over the observability sink's FDs
64
65
  - "apps/web/src/modules/discover.ts" # Vite/Astro module scanner fallback under bun test
65
66
 
66
67
  # Synchronous bootstrap & path resolution:
@@ -104,14 +104,9 @@ feature status → `05`. Working-layer, audit, and satellite rules live in the p
104
104
 
105
105
  ## Design system
106
106
 
107
- **Conditional contract:** If repository-root `DESIGN.md` exists, read it before planning or
108
- implementing any change to UI, styling, interaction, accessibility, or responsive behavior. Treat
109
- it as the project source of truth for visual and interaction design tokens, components, patterns,
110
- and UX constraints — and keep affected work consistent with it. If it is absent, continue with the
111
- project's established UI conventions.
112
-
113
- Root `DESIGN.md` owns UI/UX guidance; `docs/04_DESIGN.md` still owns command, config, schema, and DTO
114
- surface shapes under the doc map above.
107
+ **Conditional contract:** If repository-root `DESIGN.md` exists, leverage it dynamically as the industry-standard SSOT for UI design documentation — visual language, color tokens, typography, component specs, layout, micro-animations, accessibility, and responsive patterns. Read it before planning or implementing any UI changes, and keep affected work consistent with it. If `DESIGN.md` is absent, ignore it and continue with the project's established UI conventions.
108
+
109
+ **Boundary distinction:** Root `DESIGN.md` owns UI/UX design guidance; `docs/04_DESIGN.md` owns non-UI surface design by default (command signatures, flags, config schemas, DTOs, and system boundaries). When working with design teams, choose `DESIGN.md` for UI/UX visual design and `docs/04_DESIGN.md` for non-UI API/schema surfaces.
115
110
 
116
111
  ---
117
112
 
@@ -18,12 +18,15 @@ sync: [T3, T9]
18
18
  > `docs/design/<slug>.md` satellite; this index carries the surface map + pointers.
19
19
  > Edit order: satellite first, then index row — same change (T9).
20
20
 
21
- ## UI/UX boundary
21
+ ## UI/UX boundary & DESIGN.md
22
22
 
23
- Repository-root `DESIGN.md` owns all UI/UX design, including visual language, design tokens,
24
- components, layout, interaction, accessibility, and responsive behavior. Read and update it for UI
25
- work; keep this document focused on non-UI surface design. If `DESIGN.md` is absent, follow the
26
- project's established UI conventions rather than adding UI guidance here.
23
+ Repository-root `DESIGN.md` owns all UI/UX design documentation (industry standard visual language, color tokens, typography, component specs, accessibility, and responsive patterns). Read and update it for UI work; keep `docs/04_DESIGN.md` focused on non-UI surface design by default. If `DESIGN.md` is absent, ignore it and follow the project's established UI conventions.
24
+
25
+ By contrast, `docs/04_DESIGN.md` is our SSOT of non-UI surface design by default covering CLI command signatures, flags, config schemas, DTOs, tables, and system boundaries.
26
+
27
+ When collaborating with the design team:
28
+ - **UI/UX & Visual Design:** Refer to and update repository-root `DESIGN.md`.
29
+ - **Non-UI Surface & API/Schema DTOs:** Refer to and update `docs/04_DESIGN.md` (and `docs/design/<slug>.md` satellites).
27
30
 
28
31
  ## 1. CLI commands
29
32
 
@@ -80,7 +80,8 @@ Each project's `AGENTS.md` embeds an instantiated copy of this table (§4.4). A
80
80
  | `docs/01_PRD.md` | **WHAT** — product vision, users, scope (in / out / deferred) | **Authoritative on scope** | Read before adding a command/feature; edit when scope changes |
81
81
  | `docs/02_ROADMAP.md` | **WHEN** — phases, current vs deferred, sequencing | Derived | Read to place work in a phase; edit when phase status changes |
82
82
  | `docs/03_ARCHITECTURE.md` | **HOW** — module boundaries, data flow, runtime model, invariants, rationale-in-depth | Derived (ADR wins) | Read before cross-module/seam/schema work; edit when boundaries or mechanisms change |
83
- | `docs/04_DESIGN.md` | **SURFACE** — concrete shapes: every CLI command, flag, config key, env var, table, DTO; **index over `docs/design/<slug>.md`** (§4.5) | Derived | Read/edit when changing a command, flag, env var, or schema — same commit |
83
+ | `docs/04_DESIGN.md` | **SURFACE** — concrete shapes: every CLI command, flag, config key, env var, table, DTO; **index over `docs/design/<slug>.md`** (§4.5) | Derived | Read/edit when changing a non-UI command, flag, env var, or schema — same commit |
84
+ | `DESIGN.md` (repo root) | **UI/UX SURFACE** — visual design, color tokens, typography, component specs, layout, micro-animations, accessibility | **Authoritative for UI/UX when present** | Read/edit when planning or implementing UI/UX visual changes (dynamically supported; ignored when absent) |
84
85
  | `docs/05_FEATURES.md` | **STATUS** — feature decomposition + state (✅ done / 🔶 partial / ⏳ planned / 💤 deferred); **index over `docs/features/<id>_<slug>.md`** (§4.5) | Derived | Read to find a feature's state; edit when a feature's status changes |
85
86
  | `docs/99_PROJECT_CONSTITUTION.md` | **PROCESS** — how the files above are maintained | **Authoritative on process** | Read before editing any doc above; edit per §6.8 |
86
87
  | `AGENTS.md` (repo root) | **ENTRY** — how agents work in this repo: stack, commands, gates, conventions + the instantiated doc map | Derived (from 99 + 00/01/04) | Read first every session; regenerate factual blocks from code (§6.7) |
@@ -89,7 +90,8 @@ Each project's `AGENTS.md` embeds an instantiated copy of this table (§4.4). A
89
90
 
90
91
  - Decision + one-line reason → `00`. Rationale/mechanism in depth → `03`.
91
92
  - Scope (in/out/deferred) → `01`. Mechanism / data flow / invariants → `03`.
92
- - Command/flag/config/schema/DTO shapes `04`. Phase timing → `02`. Feature status `05`.
93
+ - UI/UX visual design, design tokens, component specs & accessibility → `DESIGN.md` (when present; otherwise follow established project UI conventions).
94
+ - Non-UI command/flag/config/schema/DTO shapes → `04`. Phase timing → `02`. Feature status → `05`.
93
95
  - If you are writing *how it's built* or *why* inside `00`/`01`/`02`, it belongs in `03`/`04`.
94
96
 
95
97
  ### 4.2 Working layers (outside the authority chain)
@@ -2,58 +2,136 @@
2
2
  # iteration bound is exhausted. Authored for the @gobing-ai/ts-dual-workflow-engine
3
3
  # state-machine schema (initialState / states[].id / onEnter / top-level transitions).
4
4
  # See Architecture Section 5 and Design Section 3.3.
5
+ #
6
+ # Reliability (aligned with task-pipeline / ADR-043):
7
+ # - check is a soft probe (status file + always exit 0) so red gates route to fix
8
+ # via transitions, not a raw lifecycle abort that skips the failed terminal
9
+ # - fix is pure slash /sp:dev-fixall against the same qualityGateCmd (single-sourced)
10
+ # - qualityGateMaxFixAttempts bounds the fix loop; exhausted → failed (fail-closed)
11
+ # - Bun package scripts only for gate surface (no alternate package-manager CLIs)
12
+
5
13
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
6
14
  name: basic
7
15
  kind: state-machine
8
16
  description: The canonical implement-check-fix-until-pass loop
9
- iterationBound: 2
17
+ iterationBound: 8
10
18
  initialState: implement
11
19
  terminalStates:
12
20
  - done
13
21
  - failed
22
+ failureStates:
23
+ - failed
24
+ vars:
25
+ # `task` is reserved by the engine runtime — use taskLabel for the free-form label.
26
+ taskLabel: "task"
27
+ agent: "omp"
28
+ stepTimeoutMs: "1800000"
29
+ # Override per project: `--vars '{"qualityGateCmd":"bun run lint && bun run test"}'`
30
+ qualityGateCmd: "bun run check"
31
+ # Max /sp:dev-fixall hops after a red gate (default 2). Counter: .spur/run/${vars.__runId}-basic-fix-attempt.
32
+ qualityGateMaxFixAttempts: "2"
33
+ # Injected by WorkflowAppService.run(); scopes .spur/run artifacts per run (0425 R4).
34
+ __runId: ""
35
+
14
36
  states:
15
37
  - id: implement
16
- description: Implement the requested task
38
+ description: Implement the requested task (note-only stub; real work uses task-pipeline)
17
39
  onEnter:
18
40
  - kind: note
19
41
  options:
20
- message: 'Implementing task: ${task}'
42
+ message: 'Implementing task: ${vars.taskLabel}'
43
+ - kind: shell
44
+ options:
45
+ command: >-
46
+ mkdir -p .spur/run &&
47
+ printf '0\n' > .spur/run/$__runId-basic-fix-attempt
21
48
 
22
49
  - id: check
23
- description: Verify the implementation passes all checks
50
+ description: >
51
+ Soft quality-gate probe. Runs qualityGateCmd, records PASS|FAIL under
52
+ .spur/run/${vars.__runId}-basic-gate.status, always exit 0 so transitions branch cleanly.
24
53
  onEnter:
25
54
  - kind: shell
26
55
  options:
27
- command: bun run check
56
+ command: >-
57
+ mkdir -p .spur/run &&
58
+ STATUS_FILE=".spur/run/$__runId-basic-gate.status" &&
59
+ set +e &&
60
+ ( $qualityGateCmd ); gate_rc=$?; set -e &&
61
+ if [ "$gate_rc" -eq 0 ]; then
62
+ printf 'PASS\n' > "$STATUS_FILE";
63
+ else
64
+ printf 'FAIL\n' > "$STATUS_FILE";
65
+ fi &&
66
+ exit 0
28
67
 
29
68
  - id: fix
30
- description: Address failures from the check pass
69
+ description: >
70
+ Bounded auto-fix via pure slash /sp:dev-fixall (ADR-043) against qualityGateCmd.
31
71
  onEnter:
32
- - kind: note
72
+ - kind: shell
33
73
  options:
34
- message: 'Please fix the issues found'
74
+ command: >-
75
+ mkdir -p .spur/run &&
76
+ ATTEMPT_FILE=".spur/run/$__runId-basic-fix-attempt" &&
77
+ n=$(cat "$ATTEMPT_FILE" 2>/dev/null || echo 0) &&
78
+ printf '%s\n' "$((n + 1))" > "$ATTEMPT_FILE"
79
+ - kind: agent.run
80
+ options:
81
+ agent: ${vars.agent}
82
+ input: /sp:dev-fixall "${vars.qualityGateCmd}"
83
+ timeoutMs: ${vars.stepTimeoutMs}
35
84
 
36
85
  - id: done
37
86
  description: Terminal — workflow completed successfully
38
87
  - id: failed
39
- description: Terminal — workflow failed
88
+ description: Terminal — quality gate still red after max fix attempts, or unrecoverable
40
89
 
41
90
  transitions:
42
91
  - from: implement
43
92
  to: check
44
93
  description: Hand the implemented task to the check pass
94
+ guard:
95
+ kind: always
45
96
 
46
- # Check passed -> done; otherwise loop back to fix. Declaration order matters:
47
- # the action-ok guard is tried first, so a passing check short-circuits to done.
97
+ # Soft probe branching: PASS first, FAIL fix under cap, exhausted → failed.
48
98
  - from: check
49
99
  to: done
50
100
  description: Check passed — finish successfully
51
101
  guard:
52
- kind: action-ok
102
+ kind: shell
103
+ options:
104
+ command: 'test "$(cat .spur/run/$__runId-basic-gate.status 2>/dev/null)" = PASS'
53
105
  - from: check
54
106
  to: fix
55
- description: Check failed — route to a fix attempt
107
+ description: Check failed and under max fix attempts — route to fixall
108
+ guard:
109
+ kind: shell
110
+ options:
111
+ command: 'test "$(cat .spur/run/$__runId-basic-gate.status 2>/dev/null)" = FAIL && test "$(cat .spur/run/$__runId-basic-fix-attempt 2>/dev/null || echo 0)" -lt "$qualityGateMaxFixAttempts"'
112
+ - from: check
113
+ to: failed
114
+ description: Check still red after max fix attempts — stop at failed
115
+ guard:
116
+ kind: shell
117
+ options:
118
+ command: 'test "$(cat .spur/run/$__runId-basic-gate.status 2>/dev/null)" = FAIL && test "$(cat .spur/run/$__runId-basic-fix-attempt 2>/dev/null || echo 0)" -ge "$qualityGateMaxFixAttempts"'
119
+ # Defense: missing/corrupt status — attempt fix if under cap, else failed.
120
+ - from: check
121
+ to: fix
122
+ description: Probe status missing/corrupt and under max attempts — attempt fixall
123
+ guard:
124
+ kind: shell
125
+ options:
126
+ command: 'test "$(cat .spur/run/$__runId-basic-fix-attempt 2>/dev/null || echo 0)" -lt "$qualityGateMaxFixAttempts"'
127
+ - from: check
128
+ to: failed
129
+ description: Probe status missing/corrupt and attempts exhausted — failed
130
+ guard:
131
+ kind: always
56
132
 
57
133
  - from: fix
58
134
  to: check
59
- description: Re-run the check after fixing
135
+ description: Re-run the soft check after fixing
136
+ guard:
137
+ kind: always
@@ -1,46 +1,79 @@
1
1
  # Docs-only task pipeline — one run per documentation/design-doc deliverable.
2
2
  #
3
- # This sibling of task-pipeline.yaml handles tasks whose output is a document, not a
4
- # code change. It keeps docs deliverables out of the implement/test/verify code gate
5
- # and routes them through an explicit docs-review HITL gate.
3
+ # Sibling of task-pipeline.yaml for tasks whose output is a document, not product
4
+ # code. Keeps docs deliverables out of the full quality gate and routes them
5
+ # through an explicit docs-review HITL gate.
6
+ #
7
+ # Reliability (aligned with task-pipeline / ADR-043):
8
+ # - Soft precheck (task check + status file) so red precheck → failed via transitions
9
+ # - draft agent.run is a pure slash command (docs discipline lives in
10
+ # /sp:dev-run --mode implement + sp:code-implementation, not YAML prose)
11
+ # - requireDiff fails silent no-op drafts
12
+ # - HITL answer routing is exhaustive (yes/no/cancel)
13
+ # - record → done is fail-closed on `spur task check`
6
14
 
7
15
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
8
16
  kind: state-machine
9
17
  name: docs-pipeline
10
- description: "Docs-only execution pipeline (draft -> docs-review -> record -> done)"
18
+ description: "Docs-only execution pipeline (precheck -> draft -> docs-review -> record -> done)"
11
19
  iterationBound: 12
12
- initialState: draft
20
+ initialState: precheck
13
21
  terminalStates:
14
22
  - done
15
23
  - failed
16
24
  - cancelled
25
+ failureStates:
26
+ - failed
27
+ - cancelled
17
28
  vars:
18
29
  wbs: "0000"
19
30
  profile: "standard"
20
31
  spurBin: "spur"
21
32
  agent: "omp"
22
- stepTimeoutMs: "600000"
33
+ stepTimeoutMs: "1800000"
23
34
  __hitlAnswer: ""
24
35
 
25
36
  states:
37
+ - id: precheck
38
+ description: >
39
+ Soft precheck: task check + agent doctor. Status files always exit 0 so
40
+ failures route to `failed` via transitions (not a raw lifecycle abort).
41
+ onEnter:
42
+ - kind: shell
43
+ options:
44
+ command: >-
45
+ mkdir -p .spur/run &&
46
+ set +e &&
47
+ $spurBin task check $wbs; check_rc=$?;
48
+ $spurBin agent doctor $agent; doctor_rc=$?;
49
+ set -e &&
50
+ if [ "$check_rc" -eq 0 ] && [ "$doctor_rc" -eq 0 ]; then
51
+ printf 'PASS\n' > ".spur/run/$wbs-docs-precheck.status";
52
+ else
53
+ printf 'FAIL\n' > ".spur/run/$wbs-docs-precheck.status";
54
+ fi &&
55
+ exit 0
56
+ - kind: note
57
+ options:
58
+ message: "Docs pipeline start for task ${vars.wbs}."
59
+
26
60
  - id: draft
27
61
  description: >
28
- Author or update the documentation deliverable for task ${vars.wbs}. This is a
29
- docs-only path: do not run implementation, unit-test generation, or code review
30
- steps from task-pipeline.yaml.
62
+ Author or update the documentation deliverable for task ${vars.wbs}. Pure slash
63
+ implement hop (ADR-043). Docs-only scope is owned by the implement competency when
64
+ the task template/background is documentation — not by YAML essays.
31
65
  onEnter:
32
66
  - kind: agent.run
33
67
  options:
34
68
  agent: ${vars.agent}
35
- input: >-
36
- /sp:dev-run --mode implement ${vars.wbs} --auto.
37
- Treat this as a docs-only/design-doc deliverable: update the requested
38
- documentation and do not add product code or tests unless the task
39
- explicitly requires them.
69
+ # Pure slash only (ADR-043). Do not bolt docs-only prose onto the input line.
70
+ input: /sp:dev-run --mode implement ${vars.wbs} --auto
40
71
  timeoutMs: ${vars.stepTimeoutMs}
72
+ # Fail a silent no-op draft (same empty-implement class as task-pipeline R3).
73
+ requireDiff: true
41
74
  - kind: shell
42
75
  options:
43
- command: "${vars.spurBin} task update ${vars.wbs} wip --no-lifecycle"
76
+ command: "$spurBin task update $wbs wip --no-lifecycle"
44
77
 
45
78
  - id: docs-review
46
79
  description: >
@@ -60,7 +93,7 @@ states:
60
93
  onEnter:
61
94
  - kind: shell
62
95
  options:
63
- command: "${vars.spurBin} task record ${vars.wbs} --solution-from-diff --transition testing"
96
+ command: "$spurBin task record $wbs --solution-from-diff --transition testing"
64
97
 
65
98
  - id: done
66
99
  description: Terminal — docs deliverable reviewed and recorded.
@@ -72,11 +105,11 @@ states:
72
105
  options:
73
106
  command: >
74
107
  mkdir -p .spur/run &&
75
- printf '%s\n' "{\"wbs\":\"${vars.wbs}\",\"verdict\":\"PASS\",\"source\":\"docs-pipeline\",\"requirements\":[{\"id\":\"docs-deliverable\",\"status\":\"MET\",\"evidence\":\"docs-pipeline record step\"}],\"acceptanceCriteria\":[],\"checks\":[{\"name\":\"docs-only\",\"status\":\"pass\",\"evidence\":\"docs-pipeline\"}]}"
76
- > ".spur/run/${vars.wbs}-verdict.json"
108
+ printf '%s\n' "{\"wbs\":\"$wbs\",\"verdict\":\"PASS\",\"source\":\"docs-pipeline\",\"requirements\":[{\"id\":\"docs-deliverable\",\"status\":\"MET\",\"evidence\":\"docs-pipeline record step\"}],\"acceptanceCriteria\":[],\"checks\":[{\"name\":\"docs-only\",\"status\":\"pass\",\"evidence\":\"docs-pipeline\"}]}"
109
+ > ".spur/run/$wbs-verdict.json"
77
110
  - kind: shell
78
111
  options:
79
- command: "${vars.spurBin} task update ${vars.wbs} done --no-lifecycle"
112
+ command: "$spurBin task update $wbs done --no-lifecycle"
80
113
  - kind: note
81
114
  options:
82
115
  message: "Docs pipeline complete for task ${vars.wbs}."
@@ -88,13 +121,26 @@ states:
88
121
  description: Terminal — docs pipeline cancelled by operator.
89
122
 
90
123
  transitions:
124
+ - from: precheck
125
+ to: draft
126
+ description: Precheck PASS — begin docs draft.
127
+ guard:
128
+ kind: shell
129
+ options:
130
+ command: 'test "$(cat .spur/run/$wbs-docs-precheck.status 2>/dev/null)" = PASS'
131
+ - from: precheck
132
+ to: failed
133
+ description: Precheck FAIL — stop before draft.
134
+ guard:
135
+ kind: always
136
+
91
137
  - from: draft
92
138
  to: record
93
139
  description: profile=auto — skip the docs-review HITL gate.
94
140
  guard:
95
141
  kind: shell
96
142
  options:
97
- command: 'test "${vars.profile}" = auto'
143
+ command: 'test "$profile" = auto'
98
144
  - from: draft
99
145
  to: docs-review
100
146
  description: Interactive — enter docs-review.
@@ -107,21 +153,21 @@ transitions:
107
153
  guard:
108
154
  kind: shell
109
155
  options:
110
- command: 'test "${vars.__hitlAnswer}" = yes'
156
+ command: 'test "$__hitlAnswer" = yes'
111
157
  - from: docs-review
112
158
  to: failed
113
159
  description: Docs rejected — stop for revision.
114
160
  guard:
115
161
  kind: shell
116
162
  options:
117
- command: 'test "${vars.__hitlAnswer}" = no'
163
+ command: 'test "$__hitlAnswer" = no'
118
164
  - from: docs-review
119
165
  to: cancelled
120
166
  description: Operator cancelled docs review.
121
167
  guard:
122
168
  kind: shell
123
169
  options:
124
- command: 'test "${vars.__hitlAnswer}" = cancel'
170
+ command: 'test "$__hitlAnswer" = cancel'
125
171
 
126
172
  - from: record
127
173
  to: done
@@ -129,11 +175,11 @@ transitions:
129
175
  guard:
130
176
  kind: shell
131
177
  options:
132
- command: "${vars.spurBin} task check ${vars.wbs}"
178
+ command: "$spurBin task check $wbs"
133
179
  - from: record
134
180
  to: failed
135
181
  description: Task check failed — block before done.
136
182
  guard:
137
183
  kind: shell
138
184
  options:
139
- command: '! ${vars.spurBin} task check ${vars.wbs}'
185
+ command: '! $spurBin task check $wbs'
@@ -1,11 +1,19 @@
1
+ # Feature development loop — brainstorm → plan → execute-tasks → feature-verify → done.
2
+ #
3
+ # Reliability (aligned with task-pipeline / ADR-043):
4
+ # - Soft precheck (featureId non-empty + agent doctor) → failed via transitions
5
+ # - Every agent.run input is a pure slash command (no free-form pipeline essays)
6
+ # - featureId must be supplied by the caller (workflow vars are not agent-mutated)
7
+ # - feature-verify is fail-closed on `spur feature check --strict`
8
+
1
9
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
2
10
  kind: state-machine
3
11
  name: feature-dev
4
12
  description: >
5
- End-to-end FEATURE development loop: brainstorm → plan (decompose) → execute-tasks →
6
- feature-verify → done. Drives a whole feature (one description → a feature with BDD AC →
7
- many tasks → all tasks done → feature verified), NOT a single task — for one deliverable
8
- use task-pipeline.yaml directly.
13
+ End-to-end FEATURE development loop: precheck → brainstorm → plan (decompose) →
14
+ execute-tasks → feature-verify → done. Drives a whole feature (one description → a
15
+ feature with BDD AC → many tasks → all tasks done → feature verified), NOT a single
16
+ task — for one deliverable use task-pipeline.yaml directly.
9
17
 
10
18
  The engine has no native for-each over a dynamic task list, so the execute-tasks step
11
19
  DELEGATES the fan-out: a single agent.run instructs the agent to run every pending task
@@ -13,70 +21,106 @@ description: >
13
21
  This mirrors how /sp:dev-run --mode full delegates its inner work loop (ADR-022: orchestration
14
22
  is configuration — zero new engine code).
15
23
 
16
- Shape: brainstorm → plan → execute-tasks(delegated loop) → feature-verify → done
17
- (plan failure or a non-PASS task short-circuits to `failed`).
24
+ Shape: precheck → brainstorm → plan → execute-tasks[-auto](delegated loop) → feature-verify → done
25
+ (precheck/plan failure or a non-PASS feature check short-circuits to `failed`).
18
26
 
19
27
  Vars (passed as a JSON object via `--vars`):
20
28
  featureId — the feature ID this loop drives (required), e.g. `--vars '{"featureId":"B3"}'`
21
29
  agent — agent the agent.run steps invoke; override `--vars '{"agent":"claude"}'`
22
- profile — `auto` skips the per-task HITL approval inside task-pipeline
30
+ profile — `auto` selects execute-tasks-auto (`/sp:dev-runall --auto`); otherwise
31
+ interactive execute-tasks (no --auto, per-task HITL remains)
23
32
  spurBin — PATH-independent spur invocation (CLI overrides at run start; literal fallback)
33
+ stepTimeoutMs — agent.run budget (ms) for brainstorm/plan/runall
24
34
 
25
- Seeded by `spur init`; adapt the agent.run inputs to your project's command set.
35
+ Reliability (ADR-043): every agent.run input is a pure slash command. featureId must be
36
+ supplied by the caller (workflow vars cannot be mutated by the agent). profile is wired
37
+ via two execute hops (templates cannot branch a single input string).
26
38
  iterationBound: 20
27
- initialState: brainstorm
39
+ initialState: precheck
28
40
  terminalStates:
29
41
  - done
30
42
  - failed
43
+ failureStates:
44
+ - failed
31
45
  vars:
32
46
  featureId: ""
33
47
  agent: "omp"
34
48
  profile: "standard"
35
49
  spurBin: "spur"
50
+ stepTimeoutMs: "1800000"
51
+ # Injected by WorkflowAppService.run(); scopes .spur/run artifacts per run (0425 R4).
52
+ __runId: ""
36
53
 
37
54
  states:
55
+ - id: precheck
56
+ description: >
57
+ Soft precheck: non-empty featureId + agent doctor. Always exit 0 so failures
58
+ route to `failed` via transitions (not a raw lifecycle abort).
59
+ onEnter:
60
+ - kind: shell
61
+ options:
62
+ command: >-
63
+ mkdir -p .spur/run &&
64
+ set +e &&
65
+ test -n "$featureId"; id_rc=$?;
66
+ $spurBin agent doctor $agent; doctor_rc=$?;
67
+ set -e &&
68
+ if [ "$id_rc" -eq 0 ] && [ "$doctor_rc" -eq 0 ]; then
69
+ printf 'PASS\n' > ".spur/run/$__runId-feature-dev-precheck.status";
70
+ else
71
+ printf 'FAIL\n' > ".spur/run/$__runId-feature-dev-precheck.status";
72
+ fi &&
73
+ exit 0
74
+ - kind: note
75
+ options:
76
+ message: "Feature-dev start for feature ${vars.featureId}."
77
+
38
78
  - id: brainstorm
39
79
  description: >
40
- Turn vague intent into a validated feature with BDD acceptance criteria via
41
- /sp:dev-brainstorm --feature. The grilling interview maps the decision space; the
42
- --feature exit authors AC and loops `spur feature check` until clean. Record the
43
- derived feature ID into ${vars.featureId} for the downstream steps.
80
+ Validate / expand the feature with BDD AC via pure slash /sp:dev-brainstorm.
81
+ Caller must pass vars.featureId (required for plan/runall).
44
82
  onEnter:
45
83
  - kind: agent.run
46
84
  options:
47
85
  agent: ${vars.agent}
48
- input: /sp:dev-brainstorm --feature
86
+ input: /sp:dev-brainstorm --feature ${vars.featureId}
87
+ timeoutMs: ${vars.stepTimeoutMs}
49
88
 
50
89
  - id: plan
51
90
  description: >
52
- Decompose the validated feature into a CLI-validated task batch via /sp:dev-plan.
53
- The batch-create gate (task-batch.schema.json) rejects a malformed decomposition,
54
- so a bad plan never lands tasks.
91
+ Decompose the validated feature into a CLI-validated task batch via pure slash
92
+ /sp:dev-plan. The batch-create gate rejects a malformed decomposition.
55
93
  onEnter:
56
94
  - kind: agent.run
57
95
  options:
58
96
  agent: ${vars.agent}
59
97
  input: /sp:dev-plan --feature ${vars.featureId}
98
+ timeoutMs: ${vars.stepTimeoutMs}
99
+
100
+ # Two pure-slash execute hops so vars.profile actually controls --auto (ADR-043).
101
+ # Engine templates cannot branch a single input string on profile; declaration-order
102
+ # transitions pick the correct hop (auto first, then interactive fallback).
103
+ - id: execute-tasks-auto
104
+ description: >
105
+ profile=auto — drive every pending task via pure slash /sp:dev-runall --auto
106
+ (skips per-task HITL inside task-pipeline).
107
+ onEnter:
108
+ - kind: agent.run
109
+ options:
110
+ agent: ${vars.agent}
111
+ input: /sp:dev-runall --feature ${vars.featureId} --auto
112
+ timeoutMs: ${vars.stepTimeoutMs}
60
113
 
61
114
  - id: execute-tasks
62
115
  description: >
63
- Drive EVERY pending task under the feature to done (e.g. `/sp:dev-runall --feature ${featureId} --auto`).
64
- The agent enumerates the work with `spur task list --feature ${featureId} --status todo --json`,
65
- then runs each task through task-pipeline.yaml (precheck → implement → test → review → approve → verify → record → done).
66
- It stops and reports on the first non-PASS verdict — a failing task fails the feature.
116
+ Interactive drive every pending task via pure slash /sp:dev-runall without
117
+ --auto (per-task HITL approve gates remain available).
67
118
  onEnter:
68
119
  - kind: agent.run
69
120
  options:
70
121
  agent: ${vars.agent}
71
- input: >
72
- Run every pending task under feature ${vars.featureId} to done. Enumerate with
73
- `${vars.spurBin} task list --feature ${vars.featureId} --status todo --json`; then
74
- for each WBS run the standard single-task pipeline VERBATIM:
75
- `${vars.spurBin} workflow run .spur/workflows/task-pipeline.yaml --vars
76
- '{"wbs":"<wbs>","profile":"${vars.profile}","agent":"${vars.agent}"}' --async --json`,
77
- polling `${vars.spurBin} workflow trace <run-id>` until terminal and inspecting
78
- `.spur/run/<wbs>-verdict.json`. Stop and report on the first non-PASS verdict
79
- (a failing task fails the feature).
122
+ input: /sp:dev-runall --feature ${vars.featureId}
123
+ timeoutMs: ${vars.stepTimeoutMs}
80
124
 
81
125
  - id: feature-verify
82
126
  description: >
@@ -90,12 +134,25 @@ states:
90
134
  onEnter:
91
135
  - kind: shell
92
136
  options:
93
- command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: feature-dev done featureId=${vars.featureId} ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/${vars.featureId}-checkpoint.md'
137
+ command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: feature-dev done featureId=$featureId ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/$featureId-checkpoint.md'
94
138
 
95
139
  - id: failed
96
140
  description: Terminal — plan rejected, a task failed verification, or the feature check did not pass.
97
141
 
98
142
  transitions:
143
+ - from: precheck
144
+ to: brainstorm
145
+ description: Precheck PASS — begin brainstorm.
146
+ guard:
147
+ kind: shell
148
+ options:
149
+ command: 'test "$(cat .spur/run/$__runId-feature-dev-precheck.status 2>/dev/null)" = PASS'
150
+ - from: precheck
151
+ to: failed
152
+ description: Precheck FAIL (missing featureId and/or doctor red) — stop.
153
+ guard:
154
+ kind: always
155
+
99
156
  # Linear prefix: brainstorm → plan → execute-tasks
100
157
  - from: brainstorm
101
158
  to: plan
@@ -103,15 +160,27 @@ transitions:
103
160
  guard:
104
161
  kind: always
105
162
 
163
+ - from: plan
164
+ to: execute-tasks-auto
165
+ description: Tasks created; profile=auto — batch with --auto (no per-task HITL).
166
+ guard:
167
+ kind: shell
168
+ options:
169
+ command: 'test "$profile" = auto'
106
170
  - from: plan
107
171
  to: execute-tasks
108
- description: Tasks created; execute them
172
+ description: Tasks created; interactive — batch without --auto.
109
173
  guard:
110
174
  kind: always
111
175
 
176
+ - from: execute-tasks-auto
177
+ to: feature-verify
178
+ description: Auto batch finished — verify the feature.
179
+ guard:
180
+ kind: always
112
181
  - from: execute-tasks
113
182
  to: feature-verify
114
- description: All tasks reported done; verify the feature
183
+ description: Interactive batch finished verify the feature.
115
184
  guard:
116
185
  kind: always
117
186
 
@@ -123,11 +192,11 @@ transitions:
123
192
  guard:
124
193
  kind: shell
125
194
  options:
126
- command: "${vars.spurBin} feature check ${vars.featureId} --strict"
195
+ command: "$spurBin feature check $featureId --strict"
127
196
  - from: feature-verify
128
197
  to: failed
129
198
  description: Strict feature check failed — block before done; investigate incomplete tasks or AC.
130
199
  guard:
131
200
  kind: shell
132
201
  options:
133
- command: '! ${vars.spurBin} feature check ${vars.featureId} --strict'
202
+ command: '! $spurBin feature check $featureId --strict'