@gobing-ai/spur 0.3.30 → 0.3.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/rules/strict/runtime-boundaries.yaml +2 -1
- 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 +219 -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 +1302 -707
- 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
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
# Wayfinder resolution pipeline — research/specification tasks without the code
|
|
2
|
+
# implementation pipeline (no task-pipeline / /sp:dev-run recursion).
|
|
3
|
+
#
|
|
4
|
+
# Reliability (aligned with task-pipeline / ADR-043):
|
|
5
|
+
# - Soft precheck (task check + show) → failed via transitions (status file, always exit 0)
|
|
6
|
+
# - collect fail-closed on non-empty input bundle
|
|
7
|
+
# - investigate→verify enforces Testing content floor (0300 R2) before independent verify
|
|
8
|
+
# - verify writes machine-readable PASS|PARTIAL|FAIL with expectFile
|
|
9
|
+
# - HITL approve routing is exhaustive (yes/no/cancel)
|
|
10
|
+
# - record → done is fail-closed on check + actual status=done
|
|
11
|
+
# - Free-form agent inputs are intentional: no pure-slash research surface yet
|
|
12
|
+
|
|
1
13
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
2
14
|
kind: state-machine
|
|
3
15
|
name: wayfinder-resolution
|
|
@@ -8,12 +20,15 @@ terminalStates:
|
|
|
8
20
|
- done
|
|
9
21
|
- failed
|
|
10
22
|
- cancelled
|
|
23
|
+
failureStates:
|
|
24
|
+
- failed
|
|
25
|
+
- cancelled
|
|
11
26
|
vars:
|
|
12
27
|
wbs: "0000"
|
|
13
28
|
profile: "standard"
|
|
14
29
|
spurBin: "spur"
|
|
15
30
|
agent: "omp"
|
|
16
|
-
stepTimeoutMs: "
|
|
31
|
+
stepTimeoutMs: "1800000"
|
|
17
32
|
approval: "required"
|
|
18
33
|
resolutionMode: "research"
|
|
19
34
|
evidencePolicy: "spur-cli-first"
|
|
@@ -21,14 +36,24 @@ vars:
|
|
|
21
36
|
|
|
22
37
|
states:
|
|
23
38
|
- id: precheck
|
|
24
|
-
description:
|
|
39
|
+
description: >
|
|
40
|
+
Soft precheck: task check + show, status files, always exit 0 so failures
|
|
41
|
+
route to `failed` via transitions (not a raw lifecycle abort).
|
|
25
42
|
onEnter:
|
|
26
43
|
- kind: shell
|
|
27
44
|
options:
|
|
28
|
-
command:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
45
|
+
command: >-
|
|
46
|
+
mkdir -p .spur/run/wayfinder &&
|
|
47
|
+
set +e &&
|
|
48
|
+
$spurBin task check $wbs; check_rc=$?;
|
|
49
|
+
$spurBin task show $wbs --json > .spur/run/wayfinder/$wbs-show.json; show_rc=$?;
|
|
50
|
+
set -e &&
|
|
51
|
+
if [ "$check_rc" -eq 0 ] && [ "$show_rc" -eq 0 ]; then
|
|
52
|
+
printf 'PASS\n' > .spur/run/wayfinder/$wbs-precheck.status;
|
|
53
|
+
else
|
|
54
|
+
printf 'FAIL\n' > .spur/run/wayfinder/$wbs-precheck.status;
|
|
55
|
+
fi &&
|
|
56
|
+
exit 0
|
|
32
57
|
- kind: note
|
|
33
58
|
options:
|
|
34
59
|
message: "Wayfinder resolution started for task ${vars.wbs} (mode=${vars.resolutionMode}, evidence=${vars.evidencePolicy}); no code implementation pipeline is permitted."
|
|
@@ -38,13 +63,17 @@ states:
|
|
|
38
63
|
onEnter:
|
|
39
64
|
- kind: shell
|
|
40
65
|
options:
|
|
41
|
-
command: "mkdir -p .spur/run/wayfinder && $
|
|
66
|
+
command: "mkdir -p .spur/run/wayfinder && $spurBin task show $wbs --json > .spur/run/wayfinder/$wbs-input.json"
|
|
67
|
+
# `.spur/workflows/` is the runtime read path in every layout — a symlink to
|
|
68
|
+
# config/workflows/ inside the Spur monorepo, a real directory in a seeded
|
|
69
|
+
# project. The previous `config/workflows/…` literal only resolved in the
|
|
70
|
+
# monorepo and hard-failed `collect` anywhere else.
|
|
42
71
|
- kind: shell
|
|
43
72
|
options:
|
|
44
|
-
command: "$
|
|
73
|
+
command: "$spurBin workflow validate .spur/workflows/wayfinder-resolution.yaml > .spur/run/wayfinder/$wbs-workflow-check.txt"
|
|
45
74
|
- kind: shell
|
|
46
75
|
options:
|
|
47
|
-
command: "$
|
|
76
|
+
command: "$spurBin task check $wbs > .spur/run/wayfinder/$wbs-precheck.txt"
|
|
48
77
|
|
|
49
78
|
- id: investigate
|
|
50
79
|
description: "Resolve the task's research/specification requirements and author evidence-backed sections through Spur CLI."
|
|
@@ -79,6 +108,9 @@ states:
|
|
|
79
108
|
- id: verify
|
|
80
109
|
description: "Perform an independent evidence and traceability check for the resolved research task."
|
|
81
110
|
onEnter:
|
|
111
|
+
- kind: shell
|
|
112
|
+
options:
|
|
113
|
+
command: 'rm -f .spur/run/wayfinder/$wbs-resolution-verdict.txt'
|
|
82
114
|
- kind: agent.run
|
|
83
115
|
options:
|
|
84
116
|
agent: ${vars.agent}
|
|
@@ -92,6 +124,7 @@ states:
|
|
|
92
124
|
Do not implement plugin code. Write the final verdict word (exactly PASS, PARTIAL,
|
|
93
125
|
or FAIL, nothing else) to `.spur/run/wayfinder/${vars.wbs}-resolution-verdict.txt`,
|
|
94
126
|
then finish with the same verdict and stop.
|
|
127
|
+
expectFile: .spur/run/wayfinder/${vars.wbs}-resolution-verdict.txt
|
|
95
128
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
96
129
|
|
|
97
130
|
- id: approve
|
|
@@ -105,14 +138,20 @@ states:
|
|
|
105
138
|
- id: record
|
|
106
139
|
description: "Record the resolution through the canonical task CLI and transition only after checks pass."
|
|
107
140
|
onEnter:
|
|
141
|
+
# Both steps are soft (`; exit 0`) BY DESIGN, not laxity: the real gate is the
|
|
142
|
+
# `record → done` guard below, which re-runs `spur task check` AND asserts the
|
|
143
|
+
# status actually reached done. Hard-failing here aborted the run inside the
|
|
144
|
+
# state, so the declared `record → failed` edge was unreachable and a blocked
|
|
145
|
+
# lifecycle transition surfaced as a bare "Command exited with 1" instead of
|
|
146
|
+
# the terminal `failed` this workflow documents.
|
|
108
147
|
- kind: shell
|
|
109
148
|
options:
|
|
110
|
-
command: "$
|
|
149
|
+
command: "$spurBin task check $wbs ; exit 0"
|
|
111
150
|
- kind: shell
|
|
112
151
|
options:
|
|
113
152
|
# PASS-only completion (preserve-list P4): lifecycle guards stay on; a forced
|
|
114
153
|
# done without verdict evidence is the 0280 regression this replaces (see 0292).
|
|
115
|
-
command: "$
|
|
154
|
+
command: "$spurBin task update $wbs done ; exit 0"
|
|
116
155
|
|
|
117
156
|
- id: done
|
|
118
157
|
description: "Research task resolved and ready to feed the implementation backlog."
|
|
@@ -130,53 +169,51 @@ states:
|
|
|
130
169
|
transitions:
|
|
131
170
|
- from: precheck
|
|
132
171
|
to: collect
|
|
133
|
-
description: "Precheck
|
|
172
|
+
description: "Precheck PASS — collect deterministic local inputs."
|
|
134
173
|
guard:
|
|
135
174
|
kind: shell
|
|
136
175
|
options:
|
|
137
|
-
command: "$
|
|
176
|
+
command: 'test "$(cat .spur/run/wayfinder/$wbs-precheck.status 2>/dev/null)" = PASS'
|
|
138
177
|
- from: precheck
|
|
139
178
|
to: failed
|
|
140
|
-
description: "Precheck
|
|
179
|
+
description: "Precheck FAIL — stop before delegation."
|
|
141
180
|
guard:
|
|
142
|
-
kind:
|
|
143
|
-
options:
|
|
144
|
-
command: "! ${vars.spurBin} task check ${vars.wbs}"
|
|
181
|
+
kind: always
|
|
145
182
|
- from: collect
|
|
146
183
|
to: investigate
|
|
147
184
|
description: "Input bundle collected — resolve the research task."
|
|
148
185
|
guard:
|
|
149
186
|
kind: shell
|
|
150
187
|
options:
|
|
151
|
-
command: "test -s .spur/run/wayfinder/$
|
|
188
|
+
command: "test -s .spur/run/wayfinder/$wbs-input.json && test -s .spur/run/wayfinder/$wbs-precheck.txt"
|
|
152
189
|
- from: collect
|
|
153
190
|
to: failed
|
|
154
191
|
description: "Input collection failed — stop before delegation."
|
|
155
192
|
guard:
|
|
156
193
|
kind: shell
|
|
157
194
|
options:
|
|
158
|
-
command: "! test -s .spur/run/wayfinder/$
|
|
195
|
+
command: "! test -s .spur/run/wayfinder/$wbs-input.json || ! test -s .spur/run/wayfinder/$wbs-precheck.txt"
|
|
159
196
|
- from: investigate
|
|
160
197
|
to: verify
|
|
161
198
|
description: "Research resolution authored — verify evidence and traceability. Guard enforces the evidence content floor (0300 R2): the task's Testing section must be non-placeholder (>5 lines, >60 words) before independent verification may begin."
|
|
162
199
|
guard:
|
|
163
200
|
kind: shell
|
|
164
201
|
options:
|
|
165
|
-
command: 'test "$($
|
|
202
|
+
command: 'test "$($spurBin task show $wbs --json | jq -r ".content" | awk "/^### Testing/{f=1;next} /^### /{f=0} f" | wc -l)" -gt 5 && test "$($spurBin task show $wbs --json | jq -r ".content" | awk "/^### Testing/{f=1;next} /^### /{f=0} f" | wc -w)" -gt 60'
|
|
166
203
|
- from: investigate
|
|
167
204
|
to: failed
|
|
168
205
|
description: "Evidence content floor not met (0300 R2) — investigate authored a placeholder Testing section; stop before verification rather than certify hollow evidence."
|
|
169
206
|
guard:
|
|
170
207
|
kind: shell
|
|
171
208
|
options:
|
|
172
|
-
command: 'test "$($
|
|
209
|
+
command: 'test "$($spurBin task show $wbs --json | jq -r ".content" | awk "/^### Testing/{f=1;next} /^### /{f=0} f" | wc -l)" -le 5 || test "$($spurBin task show $wbs --json | jq -r ".content" | awk "/^### Testing/{f=1;next} /^### /{f=0} f" | wc -w)" -le 60'
|
|
173
210
|
- from: verify
|
|
174
211
|
to: record
|
|
175
212
|
description: "Auto profile — record only on a machine-readable PASS verdict (fail-closed to the approve gate otherwise)."
|
|
176
213
|
guard:
|
|
177
214
|
kind: shell
|
|
178
215
|
options:
|
|
179
|
-
command: 'test "$
|
|
216
|
+
command: 'test "$approval" = auto && grep -qx PASS .spur/run/wayfinder/$wbs-resolution-verdict.txt'
|
|
180
217
|
- from: verify
|
|
181
218
|
to: approve
|
|
182
219
|
description: "Verification completed — require explicit review approval."
|
|
@@ -188,32 +225,32 @@ transitions:
|
|
|
188
225
|
guard:
|
|
189
226
|
kind: shell
|
|
190
227
|
options:
|
|
191
|
-
command: 'test "$
|
|
228
|
+
command: 'test "$__hitlAnswer" = yes'
|
|
192
229
|
- from: approve
|
|
193
230
|
to: cancelled
|
|
194
231
|
description: "Operator cancelled the research resolution."
|
|
195
232
|
guard:
|
|
196
233
|
kind: shell
|
|
197
234
|
options:
|
|
198
|
-
command: 'test "$
|
|
235
|
+
command: 'test "$__hitlAnswer" = cancel'
|
|
199
236
|
- from: approve
|
|
200
237
|
to: failed
|
|
201
238
|
description: "Operator rejected the research resolution."
|
|
202
239
|
guard:
|
|
203
240
|
kind: shell
|
|
204
241
|
options:
|
|
205
|
-
command: 'test "$
|
|
242
|
+
command: 'test "$__hitlAnswer" = no'
|
|
206
243
|
- from: record
|
|
207
244
|
to: done
|
|
208
245
|
description: "Task check passed and the task status is actually done (the lifecycle transition succeeded)."
|
|
209
246
|
guard:
|
|
210
247
|
kind: shell
|
|
211
248
|
options:
|
|
212
|
-
command: '$
|
|
249
|
+
command: '$spurBin task check $wbs && test "$($spurBin task show $wbs --json | jq -r .status)" = done'
|
|
213
250
|
- from: record
|
|
214
251
|
to: failed
|
|
215
252
|
description: "Recording/check failed or the task never reached done — stop without claiming completion."
|
|
216
253
|
guard:
|
|
217
254
|
kind: shell
|
|
218
255
|
options:
|
|
219
|
-
command: '! $
|
|
256
|
+
command: '! $spurBin task check $wbs || test "$($spurBin task show $wbs --json | jq -r .status)" != done'
|
|
@@ -22,7 +22,15 @@
|
|
|
22
22
|
# spurBin — PATH-independent spur invocation (overridden by CLI at run start)
|
|
23
23
|
# agent — agent for agent.run steps (default: omp)
|
|
24
24
|
#
|
|
25
|
-
#
|
|
25
|
+
# Reliability (aligned with task-pipeline / ADR-043):
|
|
26
|
+
# - Prefer pure slash commands when a command exists; free-form inputs remain only
|
|
27
|
+
# where capture artifacts (answerFile) have no dedicated slash surface yet.
|
|
28
|
+
# - learning-capture / metrics-record use expectFile + soft append (empty capture
|
|
29
|
+
# does not abort wrap-up after prior steps already landed).
|
|
30
|
+
# - feature-transition is a soft shell (always exit 0) so a blocked feature sync
|
|
31
|
+
# does not abort wrap-up after learnings/metrics already landed.
|
|
32
|
+
# - branch-cleanup HITL is exhaustive (yes/no/cancel → done; missing answer defense).
|
|
33
|
+
# - Wrap-up never mutates task status (consumes completed work only).
|
|
26
34
|
|
|
27
35
|
"$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
|
|
28
36
|
kind: state-machine
|
|
@@ -40,7 +48,8 @@ vars:
|
|
|
40
48
|
merge: "false"
|
|
41
49
|
spurBin: "spur"
|
|
42
50
|
agent: "omp"
|
|
43
|
-
stepTimeoutMs: "
|
|
51
|
+
stepTimeoutMs: "1800000"
|
|
52
|
+
__hitlAnswer: ""
|
|
44
53
|
|
|
45
54
|
states:
|
|
46
55
|
- id: start
|
|
@@ -64,67 +73,81 @@ states:
|
|
|
64
73
|
|
|
65
74
|
- id: doc-sync
|
|
66
75
|
description: >
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
following the constitution's edit rules. Does NOT write task or feature corpus.
|
|
76
|
+
Project-level doc drift repair for the batch (04_DESIGN, 03_ARCHITECTURE,
|
|
77
|
+
00_ADR, docs/design/*). No dedicated slash command yet — free-form skill
|
|
78
|
+
pointer until a /sp:doc-evolve command exists (ADR-043 residual).
|
|
71
79
|
onEnter:
|
|
72
80
|
- kind: agent.run
|
|
73
81
|
options:
|
|
74
82
|
agent: ${vars.agent}
|
|
75
|
-
input: "
|
|
83
|
+
input: "Skill(skill=\"sp:doc-evolve\", args=\"wrapup tasks=${vars.tasks}\"). Repair drift in docs/00_ADR.md, docs/03_ARCHITECTURE.md, docs/04_DESIGN.md, docs/design/* per constitution edit rules. Do not write task/feature corpus."
|
|
76
84
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
77
85
|
|
|
78
86
|
- id: learning-capture
|
|
79
87
|
description: >
|
|
80
|
-
Append working learnings to .spur/memory/learnings.md.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
generation from file persistence: the shell step's exit code is the real
|
|
84
|
-
success signal, and the capture file is verified before the append.
|
|
88
|
+
Append working learnings to .spur/memory/learnings.md. Capture+shell decoupling:
|
|
89
|
+
expectFile gates non-empty capture before append; append is best-effort soft
|
|
90
|
+
(empty capture skips without aborting wrap-up).
|
|
85
91
|
onEnter:
|
|
86
92
|
- kind: agent.run
|
|
87
93
|
options:
|
|
88
94
|
agent: ${vars.agent}
|
|
89
|
-
input: "Extract working learnings from tasks ${vars.tasks}. Output
|
|
95
|
+
input: "Extract working learnings from tasks ${vars.tasks}. Output raw markdown (no fences) grouped by date and task WBS: conventions, errors fixed, patterns, gotchas. Captured to .spur/run/wrapup-learnings.md."
|
|
90
96
|
answerFile: .spur/run/wrapup-learnings.md
|
|
91
97
|
expectFile: .spur/run/wrapup-learnings.md
|
|
92
98
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
93
99
|
- kind: shell
|
|
94
100
|
options:
|
|
95
|
-
command:
|
|
101
|
+
command: >-
|
|
102
|
+
mkdir -p .spur/memory &&
|
|
103
|
+
if test -s .spur/run/wrapup-learnings.md; then
|
|
104
|
+
cat .spur/run/wrapup-learnings.md >> .spur/memory/learnings.md &&
|
|
105
|
+
printf '\n' >> .spur/memory/learnings.md;
|
|
106
|
+
fi &&
|
|
107
|
+
exit 0
|
|
96
108
|
|
|
97
109
|
- id: metrics-record
|
|
98
110
|
description: >
|
|
99
|
-
Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl.
|
|
100
|
-
|
|
101
|
-
it. Each row records: task WBS, feature id, status, verdict, timestamp.
|
|
102
|
-
Append-only and machine-readable. The capture+shell decoupling means a
|
|
103
|
-
non-empty capture file is verified before the append (the agent.run exit-0
|
|
104
|
-
is no longer the sole success signal — the shell step's exit code gates the
|
|
105
|
-
write).
|
|
111
|
+
Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl. Soft append
|
|
112
|
+
(empty capture does not abort wrap-up after learnings already landed).
|
|
106
113
|
onEnter:
|
|
107
114
|
- kind: agent.run
|
|
108
115
|
options:
|
|
109
116
|
agent: ${vars.agent}
|
|
110
|
-
input: "For each task in ${vars.tasks},
|
|
117
|
+
input: "For each task in ${vars.tasks}, output one JSONL line (no fences) with fields: wbs, feature_id, status, verdict, timestamp. Captured to .spur/run/wrapup-metrics.jsonl."
|
|
111
118
|
answerFile: .spur/run/wrapup-metrics.jsonl
|
|
112
119
|
expectFile: .spur/run/wrapup-metrics.jsonl
|
|
113
120
|
timeoutMs: ${vars.stepTimeoutMs}
|
|
114
121
|
- kind: shell
|
|
115
122
|
options:
|
|
116
|
-
command:
|
|
123
|
+
command: >-
|
|
124
|
+
mkdir -p .spur/memory &&
|
|
125
|
+
if test -s .spur/run/wrapup-metrics.jsonl; then
|
|
126
|
+
cat .spur/run/wrapup-metrics.jsonl >> .spur/memory/wrapup-metrics.jsonl;
|
|
127
|
+
fi &&
|
|
128
|
+
exit 0
|
|
117
129
|
|
|
118
130
|
- id: feature-transition
|
|
119
131
|
description: >
|
|
120
|
-
If vars.feature is set,
|
|
121
|
-
|
|
122
|
-
Replaces prior unconditional `spur feature advance` with conservative status sync that
|
|
123
|
-
suppresses identical blocked retries until relevant inputs change.
|
|
132
|
+
If vars.feature is set, sync feature status via bounded feature-sync-bounded
|
|
133
|
+
(0411). Soft shell — blocked/failed sync must not abort wrap-up after metrics.
|
|
124
134
|
onEnter:
|
|
135
|
+
# Genuinely soft: the terminating `exit 0` must be reached on BOTH branches, so
|
|
136
|
+
# the sync is chained with `;` — with `&&` a non-zero sync skipped `exit 0` and
|
|
137
|
+
# aborted the run at this state, discarding the learnings/metrics that already
|
|
138
|
+
# landed and never reaching branch-cleanup/done (the exact failure the state
|
|
139
|
+
# description promises against). `feature-sync-bounded.ts` is a Spur-monorepo
|
|
140
|
+
# path (`spur init` does not scaffold `plugins/sp/`), so seeded projects fall
|
|
141
|
+
# back to the plain `spur feature sync` verb.
|
|
125
142
|
- kind: shell
|
|
126
143
|
options:
|
|
127
|
-
command:
|
|
144
|
+
command: >-
|
|
145
|
+
if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then
|
|
146
|
+
bun plugins/sp/scripts/feature-sync-bounded.ts "$feature" --spur-bin "$spurBin" --json;
|
|
147
|
+
else
|
|
148
|
+
$spurBin feature sync "$feature" --json;
|
|
149
|
+
fi;
|
|
150
|
+
exit 0
|
|
128
151
|
|
|
129
152
|
- id: branch-cleanup
|
|
130
153
|
description: >
|
|
@@ -148,7 +171,7 @@ states:
|
|
|
148
171
|
# Checkpoint write: record session state for resume (Phase 4, task 0171 R3)
|
|
149
172
|
- kind: shell
|
|
150
173
|
options:
|
|
151
|
-
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: wrapup-pipeline done tasks=$
|
|
174
|
+
command: 'mkdir -p .spur/memory/sessions && echo "checkpoint: wrapup-pipeline done tasks=$tasks ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)" > .spur/memory/sessions/wrapup-checkpoint.md'
|
|
152
175
|
|
|
153
176
|
- id: skipped
|
|
154
177
|
description: Terminal — wrap-up skipped (empty task list or operator abort).
|
|
@@ -161,21 +184,30 @@ transitions:
|
|
|
161
184
|
guard:
|
|
162
185
|
kind: always
|
|
163
186
|
|
|
164
|
-
# ── task-resolve: non-empty -> doc-sync, empty -> skipped ──
|
|
187
|
+
# ── task-resolve: non-empty -> doc-sync, empty/unparseable -> skipped ──
|
|
188
|
+
# `|| echo 0` is load-bearing: a malformed vars.tasks (or a box without `jq`) makes
|
|
189
|
+
# `jq length` emit nothing, and bare `test "" -gt 0` exits 2 — so BOTH edges failed
|
|
190
|
+
# and the run died with an opaque `no-passing-transition` instead of skipping. The
|
|
191
|
+
# trailing `always` edge is the belt-and-braces defense for any other guard fault.
|
|
165
192
|
- from: task-resolve
|
|
166
193
|
to: doc-sync
|
|
167
194
|
description: Task list resolved and non-empty — begin doc-sync.
|
|
168
195
|
guard:
|
|
169
196
|
kind: shell
|
|
170
197
|
options:
|
|
171
|
-
command: 'test "$(echo "$
|
|
198
|
+
command: 'test "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -gt 0'
|
|
172
199
|
- from: task-resolve
|
|
173
200
|
to: skipped
|
|
174
201
|
description: Task list is empty — skip wrap-up.
|
|
175
202
|
guard:
|
|
176
203
|
kind: shell
|
|
177
204
|
options:
|
|
178
|
-
command: 'test "$(echo "$
|
|
205
|
+
command: 'test "$(echo "$tasks" | jq length 2>/dev/null || echo 0)" -eq 0'
|
|
206
|
+
- from: task-resolve
|
|
207
|
+
to: skipped
|
|
208
|
+
description: Defense — task list unparseable; skip rather than fail with no-passing-transition.
|
|
209
|
+
guard:
|
|
210
|
+
kind: always
|
|
179
211
|
|
|
180
212
|
# ── linear body: doc-sync -> learning-capture -> metrics-record ──
|
|
181
213
|
- from: doc-sync
|
|
@@ -198,21 +230,21 @@ transitions:
|
|
|
198
230
|
guard:
|
|
199
231
|
kind: shell
|
|
200
232
|
options:
|
|
201
|
-
command: 'test -n "$
|
|
233
|
+
command: 'test -n "$feature"'
|
|
202
234
|
- from: metrics-record
|
|
203
235
|
to: branch-cleanup
|
|
204
236
|
description: No feature id, but merge is requested — go to branch cleanup.
|
|
205
237
|
guard:
|
|
206
238
|
kind: shell
|
|
207
239
|
options:
|
|
208
|
-
command: 'test -z "$
|
|
240
|
+
command: 'test -z "$feature" && test "$merge" = true'
|
|
209
241
|
- from: metrics-record
|
|
210
242
|
to: done
|
|
211
243
|
description: No feature id and no merge — wrap-up is done.
|
|
212
244
|
guard:
|
|
213
245
|
kind: shell
|
|
214
246
|
options:
|
|
215
|
-
command: 'test -z "$
|
|
247
|
+
command: 'test -z "$feature" && test "$merge" != true'
|
|
216
248
|
|
|
217
249
|
# ── feature-transition: -> branch-cleanup (if merge) or done ──
|
|
218
250
|
- from: feature-transition
|
|
@@ -221,18 +253,26 @@ transitions:
|
|
|
221
253
|
guard:
|
|
222
254
|
kind: shell
|
|
223
255
|
options:
|
|
224
|
-
command: 'test "$
|
|
256
|
+
command: 'test "$merge" = true'
|
|
225
257
|
- from: feature-transition
|
|
226
258
|
to: done
|
|
227
259
|
description: Feature advanced — no merge requested.
|
|
228
260
|
guard:
|
|
229
261
|
kind: shell
|
|
230
262
|
options:
|
|
231
|
-
command: 'test "$
|
|
263
|
+
command: 'test "$merge" != true'
|
|
232
264
|
|
|
233
|
-
# ── branch-cleanup: HITL
|
|
265
|
+
# ── branch-cleanup: HITL exhaustive routing (yes / no / cancel → done) ──
|
|
266
|
+
# No irreversible git op is wired yet; confirmation is recorded, then wrap completes.
|
|
267
|
+
- from: branch-cleanup
|
|
268
|
+
to: done
|
|
269
|
+
description: Branch cleanup HITL answered — wrap-up complete.
|
|
270
|
+
guard:
|
|
271
|
+
kind: shell
|
|
272
|
+
options:
|
|
273
|
+
command: 'test "$__hitlAnswer" = yes || test "$__hitlAnswer" = no || test "$__hitlAnswer" = cancel'
|
|
234
274
|
- from: branch-cleanup
|
|
235
275
|
to: done
|
|
236
|
-
description:
|
|
276
|
+
description: Defense — missing HITL answer still completes wrap (cleanup is optional).
|
|
237
277
|
guard:
|
|
238
278
|
kind: always
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.32",
|
|
4
4
|
"description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"spur",
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@commander-js/extra-typings": "^14.0.0",
|
|
53
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
54
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
55
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
56
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
57
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
53
|
+
"@gobing-ai/ts-db": "^0.4.18",
|
|
54
|
+
"@gobing-ai/ts-ai-runner": "^0.4.18",
|
|
55
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.18",
|
|
56
|
+
"@gobing-ai/ts-infra": "^0.4.18",
|
|
57
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.18",
|
|
58
|
+
"@gobing-ai/ts-rule-engine": "^0.4.18",
|
|
59
|
+
"@gobing-ai/ts-runtime": "^0.4.18",
|
|
60
|
+
"@gobing-ai/ts-utils": "^0.4.18",
|
|
61
61
|
"@types/bun": "1.3.14",
|
|
62
62
|
"@types/figlet": "^1.7.0",
|
|
63
63
|
"@types/node-notifier": "8.0.5",
|
|
@@ -13,6 +13,11 @@
|
|
|
13
13
|
"description": { "type": "string" },
|
|
14
14
|
"initialState": { "type": "string", "minLength": 1 },
|
|
15
15
|
"terminalStates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
16
|
+
"failureStates": {
|
|
17
|
+
"type": "array",
|
|
18
|
+
"items": { "type": "string", "minLength": 1 },
|
|
19
|
+
"description": "Subset of terminalStates whose reach finalizes the run as failed. Absent ⇒ every terminal is a success."
|
|
20
|
+
},
|
|
16
21
|
"iterationBound": { "type": "integer", "exclusiveMinimum": 0 },
|
|
17
22
|
"vars": {
|
|
18
23
|
"type": "object",
|