@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.
- package/config/rules/strict/runtime-boundaries.yaml +2 -1
- package/config/templates/AGENTS.md +3 -8
- package/config/templates/docs/04_DESIGN.md +8 -5
- package/config/templates/docs/99_PROJECT_CONSTITUTION.md +4 -2
- package/config/workflows/basic.yaml +92 -14
- package/config/workflows/docs-pipeline.yaml +71 -25
- package/config/workflows/feature-dev.yaml +104 -35
- package/config/workflows/feature-lifecycle.yaml +10 -2
- package/config/workflows/idea-pipeline.yaml +118 -54
- package/config/workflows/planning-pipeline.yaml +78 -30
- package/config/workflows/task-lifecycle.yaml +10 -2
- package/config/workflows/task-pipeline.yaml +214 -56
- package/config/workflows/wayfinder-resolution.yaml +64 -27
- package/config/workflows/wrapup-pipeline.yaml +79 -39
- package/package.json +9 -9
- package/schemas/state-machine-workflow.schema.json +5 -0
- package/spur.js +1306 -764
- package/web/_astro/BoardApp.CBVEjvSW.js +1 -0
- package/web/_astro/{BoardApp.BwAdAgK9.js → BoardApp.CyyIiK76.js} +74 -74
- package/web/_astro/TaskDetail.BfUuRsfM.js +1 -0
- package/web/_astro/{arc.CO-I_PZe.js → arc.C7fql4LL.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.Si-A1Ncl.js → architectureDiagram-3BPJPVTR.IJPsRX-V.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CeNy_ta6.js → blockDiagram-GPEHLZMM.B1AUwBxq.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.BgN_scEU.js → c4Diagram-AAUBKEIU.BuSJPR3z.js} +1 -1
- package/web/_astro/channel.D3uZSASb.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.C3oDtmdi.js → chunk-2J33WTMH.CaOPTr9l.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.Bv8Fxh4H.js → chunk-4BX2VUAB.DQzxExn8.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.BT-TqtV6.js → chunk-55IACEB6.IIb103c7.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.DkGMsI0B.js → chunk-727SXJPM.DOYz1K1b.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.B_-SqGnF.js → chunk-AQP2D5EJ.CFcfW-wt.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.DwR3skMv.js → chunk-FMBD7UC4.C7Hn0jo3.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BaTQutFl.js → chunk-ND2GUHAM.Ba83VQ1m.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BJMSqqJp.js → chunk-QZHKN3VN.CizNbIIP.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.SaqCNUPF.js → classDiagram-4FO5ZUOK.2NYYPXHq.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.SaqCNUPF.js → classDiagram-v2-Q7XG4LA2.2NYYPXHq.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.BkmjA_E2.js → cose-bilkent-S5V4N54A.DwBqVzml.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.CNUvWhrX.js → dagre-BM42HDAG.DqnYsHWG.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.C68Cg9uE.js → diagram-2AECGRRQ.D8TKhgy-.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.DbHkPLEn.js → diagram-5GNKFQAL.BUD6Prj5.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.Dt0EdErs.js → diagram-KO2AKTUF.Cvg7hmND.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.Bixs6mnm.js → diagram-LMA3HP47.FUG_B9eS.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.B4KBI6KT.js → diagram-OG6HWLK6.XsBZQ0hQ.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.C_4i9Xvx.js → erDiagram-TEJ5UH35.M_LSGgLQ.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.CfGFczAY.js → flowDiagram-I6XJVG4X.DZikZBRa.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.C2AIvtmg.js → ganttDiagram-6RSMTGT7.DTKuEmXY.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.HlXuGqQg.js → gitGraphDiagram-PVQCEYII.Dt-E9XmY.js} +1 -1
- package/web/_astro/index.yAse9IaO.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.BWQJsJ4Z.js → infoDiagram-5YYISTIA.D5snevU1.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.CCHgIHik.js → ishikawaDiagram-YF4QCWOH.DmXdBf9W.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.D9M6YwOJ.js → journeyDiagram-JHISSGLW.HPJSRkkU.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.K20D-a1_.js → kanban-definition-UN3LZRKU.BxCqCnL1.js} +1 -1
- package/web/_astro/{linear.DeP5Qsol.js → linear.BnXQUpS-.js} +1 -1
- package/web/_astro/{mermaid.core.ClzxNMPU.js → mermaid.core.D-ZeOmpg.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.CGn9RUNj.js → mindmap-definition-RKZ34NQL.DxrftiFR.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.CoA92Csb.js → pieDiagram-4H26LBE5.D-ZcWxwg.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.CJ_UPjM1.js → quadrantDiagram-W4KKPZXB.C-dVJ7GW.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.KkEPNNUU.js → requirementDiagram-4Y6WPE33.lalE_R8X.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.CoCWbQgh.js → sankeyDiagram-5OEKKPKP.C4uPynAF.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.DUFpV3EP.js → sequenceDiagram-3UESZ5HK.uBOenkqh.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CgWX0_9m.js → stateDiagram-AJRCARHV.CQOusSP2.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BKUXeGSf.js → stateDiagram-v2-BHNVJYJU.Bw0ZdjYb.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.CXayAguY.js → timeline-definition-PNZ67QCA.DbkPA5ce.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.DPak_Elh.js → vennDiagram-CIIHVFJN.CIKhzwoA.js} +1 -1
- package/web/_astro/{wardley-L42UT6IY.t3ZlDKd0.js → wardley-L42UT6IY.0P-tqaFs.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CwPSVASQ.js → wardleyDiagram-YWT4CUSO.DrwTvM_M.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.CJfrQgpF.js → xychartDiagram-2RQKCTM6.BgJsQVms.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.Bk3T0o6s.js +0 -1
- package/web/_astro/TaskDetail.L36slEqE.js +0 -1
- package/web/_astro/channel.CYdGnFHM.js +0 -1
- 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-
|
|
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,
|
|
108
|
-
|
|
109
|
-
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
-
|
|
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:
|
|
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: ${
|
|
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:
|
|
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:
|
|
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:
|
|
69
|
+
description: >
|
|
70
|
+
Bounded auto-fix via pure slash /sp:dev-fixall (ADR-043) against qualityGateCmd.
|
|
31
71
|
onEnter:
|
|
32
|
-
- kind:
|
|
72
|
+
- kind: shell
|
|
33
73
|
options:
|
|
34
|
-
|
|
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 —
|
|
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
|
-
#
|
|
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:
|
|
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
|
|
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
|
-
#
|
|
4
|
-
# code
|
|
5
|
-
#
|
|
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:
|
|
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: "
|
|
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}.
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
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: "$
|
|
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: "$
|
|
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\":\"$
|
|
76
|
-
> ".spur/run/$
|
|
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: "$
|
|
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 "$
|
|
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 "$
|
|
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 "$
|
|
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 "$
|
|
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: "$
|
|
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: '! $
|
|
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) →
|
|
6
|
-
feature-verify → done. Drives a whole feature (one description → a
|
|
7
|
-
many tasks → all tasks done → feature verified), NOT a single
|
|
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
|
|
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`
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
41
|
-
|
|
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
|
|
53
|
-
The batch-create gate
|
|
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
|
-
|
|
64
|
-
|
|
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
|
-
|
|
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=$
|
|
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;
|
|
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:
|
|
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: "$
|
|
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: '! $
|
|
202
|
+
command: '! $spurBin feature check $featureId --strict'
|