@gobing-ai/spur 0.3.55 → 0.3.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -3
- package/config/corpus-baseline.json +2521 -49
- package/config/rules/strict/runtime-boundaries.yaml +3 -0
- package/config/rules/surface/check-cli-surface.yaml +1 -0
- package/config/workflow-composition-baseline.json +241 -22
- package/package.json +1 -1
- package/plugins/sp/agents/expert-spur.md +3 -0
- package/plugins/sp/commands/dev-idea.md +7 -19
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/task-size-precheck.ts +11 -6
- package/plugins/sp/skills/dogfood-testing/references/monitor-ledger.md +4 -4
- package/plugins/sp/skills/dogfood-testing/references/report-template.md +4 -2
- package/plugins/sp/skills/issue-finding/SKILL.md +23 -11
- package/plugins/sp/skills/spur-cli/SKILL.md +23 -15
- package/plugins/sp/skills/spur-cli/references/agent.md +5 -0
- package/plugins/sp/skills/spur-cli/references/builder.md +49 -0
- package/plugins/sp/skills/spur-cli/references/features.md +6 -1
- package/plugins/sp/skills/spur-cli/references/message.md +5 -0
- package/plugins/sp/skills/spur-cli/references/rules.md +5 -0
- package/plugins/sp/skills/spur-cli/references/self.md +101 -0
- package/plugins/sp/skills/spur-cli/references/tasks.md +6 -1
- package/plugins/sp/skills/spur-cli/references/team.md +5 -0
- package/plugins/sp/skills/spur-cli/references/workflows.md +40 -0
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +9 -8
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +19 -0
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +27 -5
- package/spur.js +1546 -314
|
@@ -57,6 +57,7 @@ rules:
|
|
|
57
57
|
|
|
58
58
|
# Low-level system primitives (O_EXCL locks, FD byte-tailing, FS watcher, atomic registry, Vite glob fallback):
|
|
59
59
|
- "packages/domain/src/planning/locks.ts" # POSIX atomic O_EXCL file locks
|
|
60
|
+
- "packages/domain/src/retention.ts" # recursive backup-dir reclamation + mtime stat (task 0622 R8); ts-runtime FileSystem seam has no recursive dir remove
|
|
60
61
|
- "packages/app/src/services/token-ledger-service.ts" # FD byte-window log tailing
|
|
61
62
|
- "packages/app/src/services/token-ledger-watcher.ts" # node:fs watch() live watcher
|
|
62
63
|
- "packages/app/src/services/project-registry.ts" # atomic projects.json persistence
|
|
@@ -73,3 +74,5 @@ rules:
|
|
|
73
74
|
# Synchronous template & workflow resolvers:
|
|
74
75
|
- "apps/cli/src/commands/task.ts" # sync loadTemplateContent/Bodies callbacks
|
|
75
76
|
- "apps/cli/src/workflow/make-lifecycle-adapter.ts" # sync resolveWorkflowPath lookup
|
|
77
|
+
- "packages/app/src/services/workflow-service.ts" # sync workflow-composition-baseline.json lookup (walk-up) for the validate composition advisory (task 0614); sync read inside the synchronous validate path
|
|
78
|
+
- "apps/cli/src/release-ops.ts" # sync manifest/workspace reads + Bun.write rewrites inside the git release flow (task 0617); mirrors task.ts sync-template exemption
|
|
@@ -114,7 +114,8 @@
|
|
|
114
114
|
"kind": "shell",
|
|
115
115
|
"invocation": "mkdir -p .spur/run && DOCTOR_FILE=\".spur/run/$__runId-idea-precheck-doctor.status\" && set +e && $spurBin agent doctor $agent; doctor_rc=$?; set -e && if [ \"$doctor_rc\" -eq 0 ] && test -n \"$idea\"; then\n printf 'PASS\\n' > \"$DOCTOR_FILE\";\nelse\n printf 'FAIL\\n' > \"$DOCTOR_FILE\";\nfi && exit 0",
|
|
116
116
|
"stateEffect": "read",
|
|
117
|
-
"evidenceEffect": "write"
|
|
117
|
+
"evidenceEffect": "write",
|
|
118
|
+
"disposition": "GLUE"
|
|
118
119
|
},
|
|
119
120
|
"discovery:onEnter:0": {
|
|
120
121
|
"kind": "agent.run",
|
|
@@ -166,7 +167,8 @@
|
|
|
166
167
|
"kind": "shell",
|
|
167
168
|
"invocation": "if test -s .spur/run/$__runId-idea-ac-content.md; then\n $spurBin feature update \"$featureId\" --section \"Acceptance Criteria\" --from-file .spur/run/$__runId-idea-ac-content.md && date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-ac-done.txt;\nelse\n exit 0;\nfi",
|
|
168
169
|
"stateEffect": "write",
|
|
169
|
-
"evidenceEffect": "none"
|
|
170
|
+
"evidenceEffect": "none",
|
|
171
|
+
"disposition": "GLUE"
|
|
170
172
|
},
|
|
171
173
|
"feature-check:onEnter:0": {
|
|
172
174
|
"kind": "hitl.confirm",
|
|
@@ -218,7 +220,8 @@
|
|
|
218
220
|
"kind": "shell",
|
|
219
221
|
"invocation": "BATCH=\".spur/run/$__runId-idea-task-batch.json\" && ORDER=\".spur/run/$__runId-idea-task-order.json\" && jq -e --slurpfile b \"$BATCH\" '\n (type == \"array\") and\n (($b[0] | map(.name) | length) == ($b[0] | map(.name) | unique | length)) and\n ((map(.name) | length) == (map(.name) | unique | length)) and\n ((map(.name) - ($b[0] | map(.name))) | length == 0) and\n ((($b[0] | map(.name)) - map(.name)) | length == 0) and\n (((map(.depends_on_names // []) | flatten | unique) - ($b[0] | map(.name))) | length == 0)\n' \"$ORDER\" >/dev/null",
|
|
220
222
|
"stateEffect": "read",
|
|
221
|
-
"evidenceEffect": "none"
|
|
223
|
+
"evidenceEffect": "none",
|
|
224
|
+
"disposition": "GLUE"
|
|
222
225
|
},
|
|
223
226
|
"batch-create:onEnter:0": {
|
|
224
227
|
"kind": "hitl.confirm",
|
|
@@ -229,13 +232,15 @@
|
|
|
229
232
|
"kind": "shell",
|
|
230
233
|
"invocation": "if test -f .spur/run/$__runId-idea-batch-create.done; then exit 0; fi; rm -f .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-batch-create-result.json.tmp; if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --json > .spur/run/$__runId-idea-batch-create-result.json.tmp && jq -e \".created == (.wbs | length)\" .spur/run/$__runId-idea-batch-create-result.json.tmp >/dev/null 2>&1; then\n mv .spur/run/$__runId-idea-batch-create-result.json.tmp .spur/run/$__runId-idea-batch-create-result.json &&\n date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.done;\nelse\n rm -f .spur/run/$__runId-idea-batch-create-result.json.tmp;\n date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.failed;\nfi",
|
|
231
234
|
"stateEffect": "write",
|
|
232
|
-
"evidenceEffect": "write"
|
|
235
|
+
"evidenceEffect": "write",
|
|
236
|
+
"disposition": "GLUE"
|
|
233
237
|
},
|
|
234
238
|
"handoff-finalize:onEnter:0": {
|
|
235
239
|
"kind": "shell",
|
|
236
240
|
"invocation": "if [ -f packages/app/src/workflow/idea-handoff-cli.ts ]; then\n bun packages/app/src/workflow/idea-handoff-cli.ts;\n exit $?;\nfi; BATCH=\".spur/run/$__runId-idea-task-batch.json\" && RESULT=\".spur/run/$__runId-idea-batch-create-result.json\" && ORDER=\".spur/run/$__runId-idea-task-order.json\" && REPORT=\".spur/run/$__runId-idea-handoff.md\" && DEPMAP=\".spur/run/$__runId-idea-dep-map.tsv\" && CHECKS=\".spur/run/$__runId-idea-check-results.jsonl\" && rm -f \"$DEPMAP\" \"$CHECKS\" && jq -e --slurpfile b \"$BATCH\" '(.wbs | length) == ($b[0] | length) and (($b[0] | map(.name) | unique | length) == ($b[0] | length))' \"$RESULT\" >/dev/null && jq -r --slurpfile b \"$BATCH\" --slurpfile r \"$RESULT\" --slurpfile o \"$ORDER\" '\n ($b[0] | map(.name)) as $names | ($r[0].wbs) as $wbs |\n $o[0] | .[] |\n (.name as $n | $names | index($n)) as $i |\n (if $i == null or $wbs[$i] == null then \"MISSING\" else $wbs[$i] end) as $own |\n ((.depends_on_names // []) | map(. as $dep | $names | index($dep) as $d | if $d == null then \"MISSING\" else $wbs[$d] end) | join(\" \")) as $deps |\n [.name, $own, $deps] | @tsv\n' \"$ORDER\" > \"$DEPMAP\" && while IFS=\"$(printf '\\t')\" read -r name own deps; do\n test \"$own\" != MISSING || exit 1;\n if test -n \"$deps\"; then\n for d in $deps; do test \"$d\" != MISSING || exit 1; done;\n $spurBin task deps \"$own\" set $deps --json >/dev/null || exit 1;\n fi;\ndone < \"$DEPMAP\" && $spurBin feature refresh --feature \"$featureId\" --json >/dev/null && WBS_LIST=$(jq -r '.wbs | join(\" \")' \"$RESULT\") && for wbs in $WBS_LIST; do\n TMP=\".spur/run/$__runId-idea-check-$wbs.tmp\";\n if $spurBin task check \"$wbs\" --json > \"$TMP\" 2>&1; then P=true; else P=false; fi;\n jq -c --arg w \"$wbs\" --argjson p \"$P\" 'if (type == \"array\" and length > 0) then {wbs: $w, pass: $p, status: (.[0].status // \"unknown\")} else {wbs: $w, pass: $p, status: \"missing\"} end' \"$TMP\" >> \"$CHECKS\" || exit 1;\n rm -f \"$TMP\";\ndone && CHECK_ROWS=$(wc -l < \"$CHECKS\" | tr -d ' ') && WBS_COUNT=$(printf '%s\\n' $WBS_LIST | wc -l | tr -d ' ') && test \"$CHECK_ROWS\" = \"$WBS_COUNT\" && NEXT=$(jq -r --arg feature \"$featureId\" --slurpfile c \"$CHECKS\" 'if ($c | any(.[]; .pass == false)) then \"/sp:dev-refineall --feature \\($feature) --auto --depth ready\" else \"/sp:dev-runall --feature \\($feature) --auto\" end' \"$RESULT\") && {\n echo \"# Idea pipeline handoff report\";\n echo;\n echo \"Feature: $featureId\";\n echo \"Run ID: $__runId\";\n echo;\n echo \"## Created tasks\";\n printf '%s\\n' $WBS_LIST | sed 's/^/ - /';\n echo;\n echo \"## Per-task readiness (spur task check)\";\n echo;\n echo \"| WBS | Outcome |\";\n echo \"|-----|---------|\";\n while IFS= read -r line; do\n w=$(printf '%s' \"$line\" | jq -r '.wbs');\n o=$(printf '%s' \"$line\" | jq -r 'if .pass then \"PASS\" else \"FAIL\" end');\n printf '| %s | %s |\\n' \"$w\" \"$o\";\n done < \"$CHECKS\";\n echo;\n echo \"## Next command\";\n echo;\n echo \"$NEXT\";\n} > \"$REPORT\"",
|
|
237
241
|
"stateEffect": "write",
|
|
238
|
-
"evidenceEffect": "write"
|
|
242
|
+
"evidenceEffect": "write",
|
|
243
|
+
"disposition": "DUAL"
|
|
239
244
|
},
|
|
240
245
|
"handoff:onEnter:0": {
|
|
241
246
|
"kind": "note",
|
|
@@ -274,7 +279,8 @@
|
|
|
274
279
|
"kind": "shell",
|
|
275
280
|
"invocation": "DIRTY=$(git status --porcelain -- . ':(exclude)docs/tasks*' ':(exclude)docs/features' 2>/dev/null); if [ -n \"$DIRTY\" ]; then\n echo \"precheck: WARNING - working tree has uncommitted non-corpus changes; commit or stash before starting a new task:\";\n echo \"$DIRTY\";\nfi; CORPUS_DIRTY=$(git status --porcelain -- ':(glob)docs/tasks*/**' 2>/dev/null); if [ -n \"$CORPUS_DIRTY\" ]; then\n echo \"precheck: NOTE - task corpus has uncommitted changes; review before staging with this task:\";\n echo \"$CORPUS_DIRTY\";\nfi; exit 0",
|
|
276
281
|
"stateEffect": "read",
|
|
277
|
-
"evidenceEffect": "none"
|
|
282
|
+
"evidenceEffect": "none",
|
|
283
|
+
"disposition": "GLUE"
|
|
278
284
|
},
|
|
279
285
|
"precheck:onEnter:2": {
|
|
280
286
|
"kind": "note",
|
|
@@ -285,13 +291,15 @@
|
|
|
285
291
|
"kind": "shell",
|
|
286
292
|
"invocation": "if [ \"$profile\" = auto ]; then\n FID=$($spurBin task show $wbs --json 2>/dev/null |\n jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null);\n if [ -n \"$FID\" ]; then\n $spurBin feature sync \"$FID\" --force --json 2>/dev/null ||\n $spurBin feature update \"$FID\" active 2>/dev/null || true;\n fi;\nfi; exit 0",
|
|
287
293
|
"stateEffect": "write",
|
|
288
|
-
"evidenceEffect": "none"
|
|
294
|
+
"evidenceEffect": "none",
|
|
295
|
+
"disposition": "GLUE"
|
|
289
296
|
},
|
|
290
297
|
"precheck:onEnter:4": {
|
|
291
298
|
"kind": "shell",
|
|
292
299
|
"invocation": "SIZE_FILE=\".spur/run/$wbs-precheck-size.status\" && mkdir -p .spur/run && if [ -f plugins/sp/scripts/task-size-precheck.ts ]; then bun plugins/sp/scripts/task-size-precheck.ts \"$wbs\" --spur-bin \"$spurBin\" --max-reqs \"$maxImplementReqs\" --max-plan-items \"$maxImplementPlanItems\" --executor \"$implementAgent\"; else\n echo \"task-size-precheck skipped — plugins/sp/scripts/task-size-precheck.ts not present in project.\" >&2 &&\n echo \"PASS\" > \"$SIZE_FILE\";\nfi && exit 0",
|
|
293
300
|
"stateEffect": "read",
|
|
294
|
-
"evidenceEffect": "write"
|
|
301
|
+
"evidenceEffect": "write",
|
|
302
|
+
"disposition": "EXT"
|
|
295
303
|
},
|
|
296
304
|
"implement:onEnter:0": {
|
|
297
305
|
"kind": "agent.run",
|
|
@@ -303,7 +311,8 @@
|
|
|
303
311
|
"kind": "shell",
|
|
304
312
|
"invocation": "retry_transient() {\n RETRY_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_OUTPUT\";\n if [ \"$RETRY_RC\" -eq 0 ]; then return 0; fi;\n if ! printf '%s' \"$RETRY_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY|database is locked'; then return \"$RETRY_RC\"; fi;\n sleep 2;\n RETRY_SECOND_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_SECOND_OUTPUT\";\n if [ \"$RETRY_RC\" -ne 0 ] && printf '%s\\n%s' \"$RETRY_OUTPUT\" \"$RETRY_SECOND_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY'; then\n echo \"node_modules link/dependency broken — run bun install and retry\";\n fi;\n return \"$RETRY_RC\";\n}; retry_transient $spurBin task update \"$wbs\" wip --no-lifecycle",
|
|
305
313
|
"stateEffect": "write",
|
|
306
|
-
"evidenceEffect": "none"
|
|
314
|
+
"evidenceEffect": "none",
|
|
315
|
+
"disposition": "GLUE"
|
|
307
316
|
},
|
|
308
317
|
"implement:onEnter:2": {
|
|
309
318
|
"kind": "shell",
|
|
@@ -315,7 +324,8 @@
|
|
|
315
324
|
"kind": "shell",
|
|
316
325
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$wbs-test-gate.status\" && LOG_FILE=\".spur/run/$wbs-test-gate.log\" && ATTEMPT_FILE=\".spur/run/$wbs-test-fix-attempt\" && echo 0 > \"$ATTEMPT_FILE\" && : > \"$LOG_FILE\" && gate_attempt=1; while [ \"$gate_attempt\" -le 5 ]; do\n ATTEMPT_LOG=\"$LOG_FILE.attempt-$gate_attempt\";\n sh -c \"$qualityGateCmd\" > \"$ATTEMPT_LOG\" 2>&1; gate_rc=$?;\n gate_locked=0;\n grep -q 'SQLiteError: database is locked' \"$ATTEMPT_LOG\" && gate_locked=1;\n cat \"$ATTEMPT_LOG\" >> \"$LOG_FILE\";\n rm -f \"$ATTEMPT_LOG\";\n if [ \"$gate_rc\" -eq 0 ] || [ \"$gate_locked\" -ne 1 ] || [ \"$gate_attempt\" -ge 5 ]; then break; fi;\n printf 'quality gate: database is locked; retrying (%s/5) in 10s\\n' \"$gate_attempt\" | tee -a \"$LOG_FILE\";\n sleep 10;\n gate_attempt=$((gate_attempt + 1));\ndone && cat \"$LOG_FILE\" && FINDINGS_FILE=\".spur/run/$wbs-test-gate.findings\" && set +e; grep -oE '[A-Za-z0-9_./-]+\\.[A-Za-z]+:[0-9]+' \"$LOG_FILE\" | sort -u | head -20 | tr '\\n' ' ' > \"$FINDINGS_FILE\"; set -e && if [ \"$gate_rc\" -eq 0 ]; then\n printf 'PASS\\n' > \"$STATUS_FILE\";\nelse\n printf 'FAIL\\n' > \"$STATUS_FILE\";\nfi && exit 0",
|
|
317
326
|
"stateEffect": "write",
|
|
318
|
-
"evidenceEffect": "write"
|
|
327
|
+
"evidenceEffect": "write",
|
|
328
|
+
"disposition": "POLICY"
|
|
319
329
|
},
|
|
320
330
|
"test-fix:onEnter:0": {
|
|
321
331
|
"kind": "shell",
|
|
@@ -338,7 +348,8 @@
|
|
|
338
348
|
"kind": "shell",
|
|
339
349
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$wbs-test-gate.status\" && LOG_FILE=\".spur/run/$wbs-test-gate.log\" && FINDINGS_FILE=\".spur/run/$wbs-test-gate.findings\" && : > \"$LOG_FILE\" && probe_rc=0; if [ -n \"$gateProbeCmd\" ]; then\n sh -c \"$gateProbeCmd\" > \"$LOG_FILE.probe\" 2>&1; probe_rc=$?;\n cat \"$LOG_FILE.probe\" >> \"$LOG_FILE\";\n rm -f \"$LOG_FILE.probe\";\nfi; if [ \"$probe_rc\" -ne 0 ]; then\n gate_rc=$probe_rc;\nelse\n gate_attempt=1;\n while [ \"$gate_attempt\" -le 5 ]; do\n ATTEMPT_LOG=\"$LOG_FILE.attempt-$gate_attempt\";\n sh -c \"$qualityGateCmd\" > \"$ATTEMPT_LOG\" 2>&1; gate_rc=$?;\n gate_locked=0;\n grep -q 'SQLiteError: database is locked' \"$ATTEMPT_LOG\" && gate_locked=1;\n cat \"$ATTEMPT_LOG\" >> \"$LOG_FILE\";\n rm -f \"$ATTEMPT_LOG\";\n if [ \"$gate_rc\" -eq 0 ] || [ \"$gate_locked\" -ne 1 ] || [ \"$gate_attempt\" -ge 5 ]; then break; fi;\n printf 'quality gate: database is locked; retrying (%s/5) in 10s\\n' \"$gate_attempt\" | tee -a \"$LOG_FILE\";\n sleep 10;\n gate_attempt=$((gate_attempt + 1));\n done;\nfi && cat \"$LOG_FILE\" && set +e; grep -oE '[A-Za-z0-9_./-]+\\.[A-Za-z]+:[0-9]+' \"$LOG_FILE\" | sort -u | head -20 | tr '\\n' ' ' > \"$FINDINGS_FILE\"; set -e && if [ \"$gate_rc\" -eq 0 ]; then\n printf 'PASS\\n' > \"$STATUS_FILE\";\nelse\n printf 'FAIL\\n' > \"$STATUS_FILE\";\nfi && exit 0",
|
|
340
350
|
"stateEffect": "write",
|
|
341
|
-
"evidenceEffect": "write"
|
|
351
|
+
"evidenceEffect": "write",
|
|
352
|
+
"disposition": "POLICY"
|
|
342
353
|
},
|
|
343
354
|
"review:onEnter:0": {
|
|
344
355
|
"kind": "agent.run",
|
|
@@ -394,19 +405,22 @@
|
|
|
394
405
|
"kind": "shell",
|
|
395
406
|
"invocation": "retry_transient() {\n RETRY_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_OUTPUT\";\n if [ \"$RETRY_RC\" -eq 0 ]; then return 0; fi;\n if ! printf '%s' \"$RETRY_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY|database is locked'; then return \"$RETRY_RC\"; fi;\n sleep 2;\n RETRY_SECOND_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_SECOND_OUTPUT\";\n if [ \"$RETRY_RC\" -ne 0 ] && printf '%s\\n%s' \"$RETRY_OUTPUT\" \"$RETRY_SECOND_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY'; then\n echo \"node_modules link/dependency broken — run bun install and retry\";\n fi;\n return \"$RETRY_RC\";\n}; retry_transient $spurBin task record \"$wbs\" --solution-from-diff --transition testing",
|
|
396
407
|
"stateEffect": "write",
|
|
397
|
-
"evidenceEffect": "write"
|
|
408
|
+
"evidenceEffect": "write",
|
|
409
|
+
"disposition": "GLUE"
|
|
398
410
|
},
|
|
399
411
|
"record:onEnter:2": {
|
|
400
412
|
"kind": "shell",
|
|
401
413
|
"invocation": "FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r \".feature_id // .frontmatter.feature_id // empty\" 2>/dev/null); if [ -n \"$FID\" ]; then\n if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then\n bun plugins/sp/scripts/feature-sync-bounded.ts \"$FID\" --spur-bin \"$spurBin\" --json;\n else\n $spurBin feature sync \"$FID\" --json;\n fi;\nelse\n echo \"Orphan task $wbs — no feature_id linked; proposal: consider linking to a parent feature.\" >> \".spur/run/$wbs-report.txt\";\nfi; exit 0",
|
|
402
414
|
"stateEffect": "write",
|
|
403
|
-
"evidenceEffect": "none"
|
|
415
|
+
"evidenceEffect": "none",
|
|
416
|
+
"disposition": "EXT"
|
|
404
417
|
},
|
|
405
418
|
"done:onEnter:0": {
|
|
406
419
|
"kind": "shell",
|
|
407
420
|
"invocation": "retry_transient() {\n RETRY_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_OUTPUT\";\n if [ \"$RETRY_RC\" -eq 0 ]; then return 0; fi;\n if ! printf '%s' \"$RETRY_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY|database is locked'; then return \"$RETRY_RC\"; fi;\n sleep 2;\n RETRY_SECOND_OUTPUT=$(\"$@\" 2>&1); RETRY_RC=$?;\n printf '%s\\n' \"$RETRY_SECOND_OUTPUT\";\n if [ \"$RETRY_RC\" -ne 0 ] && printf '%s\\n%s' \"$RETRY_OUTPUT\" \"$RETRY_SECOND_OUTPUT\" | grep -Eq 'ENOENT|EBUSY|ENOTEMPTY'; then\n echo \"node_modules link/dependency broken — run bun install and retry\";\n fi;\n return \"$RETRY_RC\";\n}; retry_transient $spurBin task update \"$wbs\" done --no-lifecycle",
|
|
408
421
|
"stateEffect": "write",
|
|
409
|
-
"evidenceEffect": "none"
|
|
422
|
+
"evidenceEffect": "none",
|
|
423
|
+
"disposition": "GLUE"
|
|
410
424
|
},
|
|
411
425
|
"done:onEnter:1": {
|
|
412
426
|
"kind": "run.artifact",
|
|
@@ -456,19 +470,22 @@
|
|
|
456
470
|
"kind": "shell",
|
|
457
471
|
"invocation": "mkdir -p .spur/memory && if test -s .spur/run/wrapup-learnings.md; then\n cat .spur/run/wrapup-learnings.md >> .spur/memory/learnings.md &&\n printf '\\n' >> .spur/memory/learnings.md;\nelse\n echo \"wrapup: learning capture missing or empty at $PWD/.spur/run/wrapup-learnings.md - append skipped\" >&2;\nfi && exit 0",
|
|
458
472
|
"stateEffect": "write",
|
|
459
|
-
"evidenceEffect": "write"
|
|
473
|
+
"evidenceEffect": "write",
|
|
474
|
+
"disposition": "GLUE"
|
|
460
475
|
},
|
|
461
476
|
"metrics-record:onEnter:0": {
|
|
462
477
|
"kind": "shell",
|
|
463
478
|
"invocation": "mkdir -p .spur/memory && printf '%s' \"$tasks\" | jq -r '(if type == \"string\" then (try fromjson catch .) else . end) | if type == \"array\" then .[] else empty end' | while IFS= read -r wbs; do\n if [ -n \"$wbs\" ]; then\n task_json=$($spurBin task show \"$wbs\" --json 2>/dev/null || true);\n if [ -n \"$task_json\" ]; then\n feat=$(printf '%s' \"$task_json\" | jq -r '.frontmatter.feature_id // .feature_id // empty' 2>/dev/null || true);\n stat=$(printf '%s' \"$task_json\" | jq -r '.frontmatter.status // .status // \"unknown\"' 2>/dev/null || true);\n verdict=\"UNKNOWN\";\n if [ -f \".spur/run/$wbs-verdict.json\" ]; then\n v=$(jq -r '.verdict // \"UNKNOWN\"' \".spur/run/$wbs-verdict.json\" 2>/dev/null || true);\n if [ -n \"$v\" ]; then verdict=\"$v\"; fi;\n fi;\n ts=$(date -u +\"%Y-%m-%dT%H:%M:%SZ\");\n printf '{\"wbs\":\"%s\",\"feature_id\":\"%s\",\"status\":\"%s\",\"verdict\":\"%s\",\"timestamp\":\"%s\"}\\n' \"$wbs\" \"$feat\" \"$stat\" \"$verdict\" \"$ts\" >> .spur/memory/wrapup-metrics.jsonl;\n fi;\n fi;\ndone && exit 0",
|
|
464
479
|
"stateEffect": "write",
|
|
465
|
-
"evidenceEffect": "write"
|
|
480
|
+
"evidenceEffect": "write",
|
|
481
|
+
"disposition": "GLUE"
|
|
466
482
|
},
|
|
467
483
|
"feature-transition:onEnter:0": {
|
|
468
484
|
"kind": "shell",
|
|
469
485
|
"invocation": "if [ -z \"$feature\" ]; then\n echo \"feature-transition: vars.feature is empty — refusing no-op feature sync (mis-invocation, not a blocked sync)\" >&2;\n exit 1;\nfi; if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then\n bun plugins/sp/scripts/feature-sync-bounded.ts \"$feature\" --spur-bin \"$spurBin\" --json;\nelse\n $spurBin feature sync \"$feature\" --json;\nfi; exit 0",
|
|
470
486
|
"stateEffect": "write",
|
|
471
|
-
"evidenceEffect": "none"
|
|
487
|
+
"evidenceEffect": "none",
|
|
488
|
+
"disposition": "EXT"
|
|
472
489
|
},
|
|
473
490
|
"branch-cleanup:onEnter:0": {
|
|
474
491
|
"kind": "hitl.confirm",
|
|
@@ -511,7 +528,8 @@
|
|
|
511
528
|
"kind": "shell",
|
|
512
529
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-preflight.status\" && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" preflight --base \"$baseBranch\" --json > \".spur/run/$__runId-pr-context.json\"; rc=$?; set -e && if [ \"$rc\" -eq 0 ]; then printf 'PASS\\n' > \"$STATUS_FILE\"; else printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0",
|
|
513
530
|
"stateEffect": "read",
|
|
514
|
-
"evidenceEffect": "write"
|
|
531
|
+
"evidenceEffect": "write",
|
|
532
|
+
"disposition": "EXT"
|
|
515
533
|
},
|
|
516
534
|
"hygiene:onEnter:0": {
|
|
517
535
|
"kind": "shell",
|
|
@@ -523,7 +541,8 @@
|
|
|
523
541
|
"kind": "shell",
|
|
524
542
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-precheck.status\" && if [ -z \"$preReviewCmd\" ]; then\n printf 'SKIP\\n' > \"$STATUS_FILE\";\nelse\n set +e; sh -c \"$preReviewCmd\"; rc=$?; set -e;\n if [ \"$rc\" -eq 0 ]; then printf 'PASS\\n' > \"$STATUS_FILE\"; else printf 'FAIL\\n' > \"$STATUS_FILE\"; fi;\nfi && exit 0",
|
|
525
543
|
"stateEffect": "read",
|
|
526
|
-
"evidenceEffect": "write"
|
|
544
|
+
"evidenceEffect": "write",
|
|
545
|
+
"disposition": "POLICY"
|
|
527
546
|
},
|
|
528
547
|
"push:onEnter:0": {
|
|
529
548
|
"kind": "shell",
|
|
@@ -541,19 +560,219 @@
|
|
|
541
560
|
"kind": "shell",
|
|
542
561
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-request.status\" && FORCE_FLAG=\"\" && if [ \"$mode\" = \"rerun\" ]; then FORCE_FLAG=\"--force\"; fi && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" request --focus \"$focus\" $FORCE_FLAG --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr-request.json\"; rc=$?; set -e && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0",
|
|
543
562
|
"stateEffect": "write",
|
|
544
|
-
"evidenceEffect": "write"
|
|
563
|
+
"evidenceEffect": "write",
|
|
564
|
+
"disposition": "EXT"
|
|
545
565
|
},
|
|
546
566
|
"wait:onEnter:0": {
|
|
547
567
|
"kind": "shell",
|
|
548
568
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-wait.status\" && set +e && SINCE=\"$(bun -e 'const fs=require(\"node:fs\");const value=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\")).requestedAt;process.stdout.write(value ?? \"\")' \".spur/run/$__runId-pr-request.json\")\" && REQUEST_HEAD=\"$(bun -e 'const fs=require(\"node:fs\");const value=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\")).head;process.stdout.write(value ?? \"\")' \".spur/run/$__runId-pr-request.json\")\" && bun \"$(superskill script path sp pr-reviewing.ts)\" wait --since \"$SINCE\" --head \"$REQUEST_HEAD\" --timeout \"$waitTimeoutSec\" --interval \"$waitIntervalSec\" --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr-wait.json\"; rc=$?; set -e && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0",
|
|
549
569
|
"stateEffect": "read",
|
|
550
|
-
"evidenceEffect": "write"
|
|
570
|
+
"evidenceEffect": "write",
|
|
571
|
+
"disposition": "EXT"
|
|
551
572
|
},
|
|
552
573
|
"collect:onEnter:0": {
|
|
553
574
|
"kind": "shell",
|
|
554
575
|
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-collect.status\" && set +e && SINCE=\"$(bun -e 'const fs=require(\"node:fs\");const value=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\")).requestedAt;process.stdout.write(value ?? \"\")' \".spur/run/$__runId-pr-request.json\")\" && REQUEST_HEAD=\"$(bun -e 'const fs=require(\"node:fs\");const value=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\")).head;process.stdout.write(value ?? \"\")' \".spur/run/$__runId-pr-request.json\")\" && bun \"$(superskill script path sp pr-reviewing.ts)\" collect --since \"$SINCE\" --head \"$REQUEST_HEAD\" --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr-findings.json\"; collect_rc=$?; bun \"$(superskill script path sp pr-reviewing.ts)\" status --since \"$SINCE\" --head \"$REQUEST_HEAD\" --json > \".spur/run/$__runId-pr-status.json\"; status_rc=$?; set -e && if [ \"$collect_rc\" -ne 0 ] || [ \"$status_rc\" -ne 0 ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && { cat \".spur/run/$__runId-pr-findings.json\" 2>/dev/null || true; } && exit 0",
|
|
555
576
|
"stateEffect": "read",
|
|
556
|
-
"evidenceEffect": "write"
|
|
577
|
+
"evidenceEffect": "write",
|
|
578
|
+
"disposition": "EXT"
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
},
|
|
582
|
+
"basic": {
|
|
583
|
+
"definition": "config/workflows/basic.yaml",
|
|
584
|
+
"boundary": "generic-execution",
|
|
585
|
+
"disposition": "keep",
|
|
586
|
+
"artifacts": ["fix-attempt-counter", "gate-status"],
|
|
587
|
+
"callers": ["spur workflow run"],
|
|
588
|
+
"terminalStates": ["done", "failed"],
|
|
589
|
+
"failurePolicy": "fail-closed",
|
|
590
|
+
"modelQueries": ["fix"],
|
|
591
|
+
"actions": {
|
|
592
|
+
"implement:onEnter:0": {
|
|
593
|
+
"kind": "note",
|
|
594
|
+
"stateEffect": "read",
|
|
595
|
+
"evidenceEffect": "none"
|
|
596
|
+
},
|
|
597
|
+
"implement:onEnter:1": {
|
|
598
|
+
"kind": "shell",
|
|
599
|
+
"invocation": "mkdir -p .spur/run && printf '0\\n' > .spur/run/$__runId-basic-fix-attempt",
|
|
600
|
+
"stateEffect": "write",
|
|
601
|
+
"evidenceEffect": "write",
|
|
602
|
+
"disposition": "GLUE"
|
|
603
|
+
},
|
|
604
|
+
"check:onEnter:0": {
|
|
605
|
+
"kind": "shell",
|
|
606
|
+
"invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-basic-gate.status\" && set +e && ( $qualityGateCmd ); gate_rc=$?; set -e && if [ \"$gate_rc\" -eq 0 ]; then\n printf 'PASS\\n' > \"$STATUS_FILE\";\nelse\n printf 'FAIL\\n' > \"$STATUS_FILE\";\nfi && exit 0",
|
|
607
|
+
"stateEffect": "write",
|
|
608
|
+
"evidenceEffect": "write",
|
|
609
|
+
"disposition": "POLICY"
|
|
610
|
+
},
|
|
611
|
+
"fix:onEnter:0": {
|
|
612
|
+
"kind": "shell",
|
|
613
|
+
"invocation": "mkdir -p .spur/run && ATTEMPT_FILE=\".spur/run/$__runId-basic-fix-attempt\" && n=$(cat \"$ATTEMPT_FILE\" 2>/dev/null || echo 0) && printf '%s\\n' \"$((n + 1))\" > \"$ATTEMPT_FILE\"",
|
|
614
|
+
"stateEffect": "write",
|
|
615
|
+
"evidenceEffect": "write",
|
|
616
|
+
"disposition": "GLUE"
|
|
617
|
+
},
|
|
618
|
+
"fix:onEnter:1": {
|
|
619
|
+
"kind": "agent.run",
|
|
620
|
+
"invocation": "/sp:dev-fixall \"${vars.qualityGateCmd}\"",
|
|
621
|
+
"stateEffect": "read",
|
|
622
|
+
"evidenceEffect": "none"
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
"feature-dev": {
|
|
627
|
+
"definition": "config/workflows/feature-dev.yaml",
|
|
628
|
+
"boundary": "feature-umbrella",
|
|
629
|
+
"disposition": "keep",
|
|
630
|
+
"artifacts": ["feature-checkpoint"],
|
|
631
|
+
"callers": ["/sp:dev-runall --feature"],
|
|
632
|
+
"terminalStates": ["done", "failed"],
|
|
633
|
+
"failurePolicy": "fail-closed",
|
|
634
|
+
"modelQueries": ["brainstorm", "plan", "execute-tasks-auto", "execute-tasks"],
|
|
635
|
+
"actions": {
|
|
636
|
+
"precheck:onEnter:0": {
|
|
637
|
+
"kind": "shell",
|
|
638
|
+
"invocation": "mkdir -p .spur/run && set +e && test -n \"$featureId\"; id_rc=$?; $spurBin agent doctor $agent; doctor_rc=$?; set -e && if [ \"$id_rc\" -eq 0 ] && [ \"$doctor_rc\" -eq 0 ]; then\n printf 'PASS\\n' > \".spur/run/$__runId-feature-dev-precheck.status\";\nelse\n printf 'FAIL\\n' > \".spur/run/$__runId-feature-dev-precheck.status\";\nfi && exit 0",
|
|
639
|
+
"stateEffect": "write",
|
|
640
|
+
"evidenceEffect": "write",
|
|
641
|
+
"disposition": "GLUE"
|
|
642
|
+
},
|
|
643
|
+
"precheck:onEnter:1": {
|
|
644
|
+
"kind": "note",
|
|
645
|
+
"stateEffect": "read",
|
|
646
|
+
"evidenceEffect": "none"
|
|
647
|
+
},
|
|
648
|
+
"brainstorm:onEnter:0": {
|
|
649
|
+
"kind": "agent.run",
|
|
650
|
+
"invocation": "/sp:dev-brainstorm --feature ${vars.featureId}",
|
|
651
|
+
"stateEffect": "read",
|
|
652
|
+
"evidenceEffect": "none"
|
|
653
|
+
},
|
|
654
|
+
"plan:onEnter:0": {
|
|
655
|
+
"kind": "agent.run",
|
|
656
|
+
"invocation": "/sp:dev-plan --feature ${vars.featureId}",
|
|
657
|
+
"stateEffect": "read",
|
|
658
|
+
"evidenceEffect": "none"
|
|
659
|
+
},
|
|
660
|
+
"execute-tasks-auto:onEnter:0": {
|
|
661
|
+
"kind": "agent.run",
|
|
662
|
+
"invocation": "/sp:dev-runall --feature ${vars.featureId} --auto",
|
|
663
|
+
"stateEffect": "read",
|
|
664
|
+
"evidenceEffect": "none"
|
|
665
|
+
},
|
|
666
|
+
"execute-tasks:onEnter:0": {
|
|
667
|
+
"kind": "agent.run",
|
|
668
|
+
"invocation": "/sp:dev-runall --feature ${vars.featureId}",
|
|
669
|
+
"stateEffect": "read",
|
|
670
|
+
"evidenceEffect": "none"
|
|
671
|
+
},
|
|
672
|
+
"integration-review:onEnter:0": {
|
|
673
|
+
"kind": "command.gate",
|
|
674
|
+
"stateEffect": "read",
|
|
675
|
+
"evidenceEffect": "none"
|
|
676
|
+
},
|
|
677
|
+
"integration-review:onEnter:1": {
|
|
678
|
+
"kind": "note",
|
|
679
|
+
"stateEffect": "read",
|
|
680
|
+
"evidenceEffect": "none"
|
|
681
|
+
},
|
|
682
|
+
"done:onEnter:0": {
|
|
683
|
+
"kind": "shell",
|
|
684
|
+
"invocation": "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",
|
|
685
|
+
"stateEffect": "write",
|
|
686
|
+
"evidenceEffect": "write",
|
|
687
|
+
"disposition": "GLUE"
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
"wayfinder-resolution": {
|
|
692
|
+
"definition": "config/workflows/wayfinder-resolution.yaml",
|
|
693
|
+
"boundary": "wayfinder-resolution",
|
|
694
|
+
"disposition": "keep",
|
|
695
|
+
"artifacts": ["wayfinder-report", "resolution-status"],
|
|
696
|
+
"callers": ["spur workflow run"],
|
|
697
|
+
"terminalStates": ["done", "failed", "cancelled"],
|
|
698
|
+
"failurePolicy": "fail-closed",
|
|
699
|
+
"modelQueries": ["investigate", "verify"],
|
|
700
|
+
"actions": {
|
|
701
|
+
"precheck:onEnter:0": {
|
|
702
|
+
"kind": "shell",
|
|
703
|
+
"invocation": "mkdir -p .spur/run/wayfinder && set +e && $spurBin task check $wbs; check_rc=$?; $spurBin task show $wbs --json > .spur/run/wayfinder/$wbs-show.json; show_rc=$?; set -e && if [ \"$check_rc\" -eq 0 ] && [ \"$show_rc\" -eq 0 ]; then\n printf 'PASS\\n' > .spur/run/wayfinder/$wbs-precheck.status;\nelse\n printf 'FAIL\\n' > .spur/run/wayfinder/$wbs-precheck.status;\nfi && exit 0",
|
|
704
|
+
"stateEffect": "write",
|
|
705
|
+
"evidenceEffect": "write",
|
|
706
|
+
"disposition": "GLUE"
|
|
707
|
+
},
|
|
708
|
+
"precheck:onEnter:1": {
|
|
709
|
+
"kind": "note",
|
|
710
|
+
"stateEffect": "read",
|
|
711
|
+
"evidenceEffect": "none"
|
|
712
|
+
},
|
|
713
|
+
"collect:onEnter:0": {
|
|
714
|
+
"kind": "shell",
|
|
715
|
+
"invocation": "mkdir -p .spur/run/wayfinder && $spurBin task show $wbs --json > .spur/run/wayfinder/$wbs-input.json",
|
|
716
|
+
"stateEffect": "read",
|
|
717
|
+
"evidenceEffect": "write",
|
|
718
|
+
"disposition": "GLUE"
|
|
719
|
+
},
|
|
720
|
+
"collect:onEnter:1": {
|
|
721
|
+
"kind": "shell",
|
|
722
|
+
"invocation": "$spurBin workflow validate .spur/workflows/wayfinder-resolution.yaml > .spur/run/wayfinder/$wbs-workflow-check.txt",
|
|
723
|
+
"stateEffect": "read",
|
|
724
|
+
"evidenceEffect": "write",
|
|
725
|
+
"disposition": "SIMPLE"
|
|
726
|
+
},
|
|
727
|
+
"collect:onEnter:2": {
|
|
728
|
+
"kind": "shell",
|
|
729
|
+
"invocation": "$spurBin task check $wbs > .spur/run/wayfinder/$wbs-precheck.txt",
|
|
730
|
+
"stateEffect": "read",
|
|
731
|
+
"evidenceEffect": "write",
|
|
732
|
+
"disposition": "SIMPLE"
|
|
733
|
+
},
|
|
734
|
+
"investigate:onEnter:0": {
|
|
735
|
+
"kind": "agent.run",
|
|
736
|
+
"invocation": "Resolve task ${vars.wbs} in resolution mode ${vars.resolutionMode} using the prepared local bundle `.spur/run/wayfinder/${vars.wbs}-input.json` and only local shell/file reads plus the `spur` CLI; do not use network search or MCP tools. Do not invoke task-pipeline.yaml, /sp:dev-run, /sp:dev-runall, or code implementation. Read the task file, map its existing Requirements and Acceptance Criteria to the existing Design/Plan/References, then author concise Solution, Testing, and Review sections through `spur task update --section --from-file`. Preserve the research boundary. Finish with a short summary and stop. Evidence policy: ${vars.evidencePolicy}.\nEvidence-anchor and content contract (mandatory — 0300 R1/R2): when the task's evidence lives in a shared evidence file (e.g. `.spur/run/wayfinder-O/implementation-evidence.md`), the `## <wbs>` section you author MUST (a) be substantive — more than 5 lines AND more than 60 words, covering the task's own requirements, not a generic 2–3 sentence placeholder; and (b) be anchored truthfully — after writing the section, resolve its real start line with `grep -n \"^## <wbs> \" <evidence-file>` and use that exact line number in every Solution/Testing `file:line` citation. NEVER copy a line number from another ticket's example (e.g. `:5` from an earlier WBS); each WBS anchors to its own section. Before finishing, re-read each cited line and confirm it names the requirement's subject (0299 R1 line-anchor rule).",
|
|
737
|
+
"stateEffect": "read",
|
|
738
|
+
"evidenceEffect": "none"
|
|
739
|
+
},
|
|
740
|
+
"verify:onEnter:0": {
|
|
741
|
+
"kind": "shell",
|
|
742
|
+
"invocation": "rm -f .spur/run/wayfinder/$wbs-resolution-verdict.txt",
|
|
743
|
+
"stateEffect": "read",
|
|
744
|
+
"evidenceEffect": "write",
|
|
745
|
+
"disposition": "GLUE"
|
|
746
|
+
},
|
|
747
|
+
"verify:onEnter:1": {
|
|
748
|
+
"kind": "agent.run",
|
|
749
|
+
"invocation": "Verify resolved task ${vars.wbs} using only local reads and `spur` CLI. Check its requirements, acceptance scenarios, Solution, Testing, and Review. Confirm workflow orchestration uses `spur workflow` and execution uses `spur` CLI by default. Run `spur task check ${vars.wbs}` and update Testing/Review via the CLI only if needed. Do not implement plugin code. Write the final verdict word (exactly PASS, PARTIAL, or FAIL, nothing else) to `.spur/run/wayfinder/${vars.wbs}-resolution-verdict.txt`, then finish with the same verdict and stop.",
|
|
750
|
+
"stateEffect": "read",
|
|
751
|
+
"evidenceEffect": "none"
|
|
752
|
+
},
|
|
753
|
+
"approve:onEnter:0": {
|
|
754
|
+
"kind": "hitl.confirm",
|
|
755
|
+
"stateEffect": "read",
|
|
756
|
+
"evidenceEffect": "none"
|
|
757
|
+
},
|
|
758
|
+
"record:onEnter:0": {
|
|
759
|
+
"kind": "shell",
|
|
760
|
+
"invocation": "$spurBin task check $wbs ; exit 0",
|
|
761
|
+
"stateEffect": "read",
|
|
762
|
+
"evidenceEffect": "none",
|
|
763
|
+
"disposition": "GLUE"
|
|
764
|
+
},
|
|
765
|
+
"record:onEnter:1": {
|
|
766
|
+
"kind": "shell",
|
|
767
|
+
"invocation": "$spurBin task update $wbs done ; exit 0",
|
|
768
|
+
"stateEffect": "write",
|
|
769
|
+
"evidenceEffect": "none",
|
|
770
|
+
"disposition": "GLUE"
|
|
771
|
+
},
|
|
772
|
+
"done:onEnter:0": {
|
|
773
|
+
"kind": "note",
|
|
774
|
+
"stateEffect": "read",
|
|
775
|
+
"evidenceEffect": "none"
|
|
557
776
|
}
|
|
558
777
|
}
|
|
559
778
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.57",
|
|
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",
|
|
@@ -46,6 +46,9 @@ guide and conventions before acting.
|
|
|
46
46
|
- **Section-editing campaigns** — update the same section across multiple tasks.
|
|
47
47
|
- **Rule-catalog work** — author, fine-tune, validate, or harden constraint rules across the catalog.
|
|
48
48
|
- **Workflow work** — author, validate, dry-run, or refactor one or more workflows.
|
|
49
|
+
When authoring or refactoring, read the composition-measures section in
|
|
50
|
+
`sp:spur-cli` `references/workflows.md` and apply the advisory's fix-path map — findings are
|
|
51
|
+
advisory only; never block a run or edit an executing pipeline over them.
|
|
49
52
|
- **Corpus health checks** — run `check`/`validate` across a batch and report findings.
|
|
50
53
|
|
|
51
54
|
For a single operation, use the `spur` CLI directly. For the planning/execution lifecycle, use
|
|
@@ -2,12 +2,13 @@
|
|
|
2
2
|
description: Turn a vague idea into a feature with AC and a decomposed task batch — discovery, idea-eval, feature-create, AC, feature-check, system-design, decompose, batch-create (Design by default), handoff
|
|
3
3
|
role: planner
|
|
4
4
|
argument-hint: "\"<idea>\" [--auto] [--skip-design] [--approve-taste] [--agent <inline|auto|name>]"
|
|
5
|
-
allowed-tools: ["Bash", "Read", "AskUserQuestion"]
|
|
5
|
+
allowed-tools: ["Bash", "Read", "Skill", "AskUserQuestion"]
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Dev Idea
|
|
9
9
|
|
|
10
|
-
Wraps the **idea-pipeline.yaml**
|
|
10
|
+
Wraps the **sp:spur-dev** skill; the machine is **idea-pipeline.yaml** — the stage
|
|
11
|
+
contract below maps to that workflow's transitions.
|
|
11
12
|
|
|
12
13
|
## Argument Flags
|
|
13
14
|
|
|
@@ -40,20 +41,7 @@ vars as subsets of `--approve-taste` (`idea_approved` / `design_approved`). Pref
|
|
|
40
41
|
|
|
41
42
|
## Implementation
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
"idea":"<text>",
|
|
48
|
-
"profile":"interactive|auto",
|
|
49
|
-
"design":"auto|skip",
|
|
50
|
-
"design_approved":"false|true",
|
|
51
|
-
"idea_approved":"false|true",
|
|
52
|
-
"agent":"<executor-or-role>"
|
|
53
|
-
}'
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
Omit `agent` from `--vars` unless the operator passed `--agent`: an absent var lets `agent.default`
|
|
57
|
-
(a Layer-1 role, resolved to its tier's cheapest usable executor) govern, which is the intended
|
|
58
|
-
routing. Passing `--agent <name>` pins that executor for every `agent.run` stage — the escape hatch
|
|
59
|
-
when the resolved executor is unusable (quota exhaustion, auth failure).
|
|
44
|
+
- Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
|
|
45
|
+
- `Skill(skill="sp:spur-dev", args="idea $ARGUMENTS")`
|
|
46
|
+
- Stage contract (discovery → idea-eval → feature-create → AC → feature-check → system-design →
|
|
47
|
+
decompose → batch-create → handoff): `plugins/sp/skills/spur-dev/references/dev-operations.md` § idea.
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.57",
|
|
4
4
|
"description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
|
|
5
5
|
"extensions": {
|
|
6
6
|
"pi": ["./hooks/pi/guard-extension.ts"]
|
|
@@ -141,13 +141,18 @@ function runSpur(spurBin: string, args: string[]): string {
|
|
|
141
141
|
* tier all read as `standard` — conservative: a false block is one flag away,
|
|
142
142
|
* a false pass costs a 30-minute timed-out implement.
|
|
143
143
|
*/
|
|
144
|
-
function resolveCapabilityTier(spurBin: string, executor: string): string {
|
|
144
|
+
function resolveCapabilityTier(spurBin: string, executor: string): { tier: string; resolvedName: string } {
|
|
145
145
|
try {
|
|
146
146
|
const out = runSpur(spurBin, ['agent', 'doctor', executor, '--json']);
|
|
147
|
-
const
|
|
148
|
-
|
|
147
|
+
const row = JSON.parse(out)?.agents?.[0];
|
|
148
|
+
const tier = row?.capabilityTier;
|
|
149
|
+
// R1 (0622 F2/F4 residue): `doctor <role>` resolves the role to its cheapest
|
|
150
|
+
// eligible executor (`coder` → `omp`); surface the resolved executor name in
|
|
151
|
+
// the block message, not the role the caller passed in.
|
|
152
|
+
const resolvedName = typeof row?.agent === 'string' && row.agent.length > 0 ? row.agent : executor;
|
|
153
|
+
return { tier: typeof tier === 'string' && tier ? tier : 'standard', resolvedName };
|
|
149
154
|
} catch {
|
|
150
|
-
return 'standard';
|
|
155
|
+
return { tier: 'standard', resolvedName: executor };
|
|
151
156
|
}
|
|
152
157
|
}
|
|
153
158
|
|
|
@@ -185,11 +190,11 @@ function main(): void {
|
|
|
185
190
|
// R3 (0487): a large task on a sub-capable executor blocks even when the caller
|
|
186
191
|
// raised the caps — the caps are an acceptance of size, not a capability grant.
|
|
187
192
|
if (executor && (reqCount > LARGE_TASK_REQS || planItemCount > LARGE_TASK_PLAN_ITEMS)) {
|
|
188
|
-
const tier = resolveCapabilityTier(spurBin, executor);
|
|
193
|
+
const { tier, resolvedName } = resolveCapabilityTier(spurBin, executor);
|
|
189
194
|
if (!CAPABLE_TIERS.has(tier)) {
|
|
190
195
|
reasons.push(
|
|
191
196
|
`Task size (${reqCount} R-items / ${planItemCount} Plan items) requires a capable executor, ` +
|
|
192
|
-
`but ${
|
|
197
|
+
`but ${resolvedName} is tier ${tier}. ` +
|
|
193
198
|
`Pass \`--agent <capable>\` or \`--vars '{"implementAgent":"<capable>"}'\`, or split the task.`,
|
|
194
199
|
);
|
|
195
200
|
}
|
|
@@ -19,7 +19,7 @@ lives in the dual artifacts (see [report-template.md](report-template.md) → Al
|
|
|
19
19
|
artifacts):
|
|
20
20
|
|
|
21
21
|
| File | Path |
|
|
22
|
-
|
|
22
|
+
| ------ | ------ |
|
|
23
23
|
| Live | `.spur/run/dogfood/<run_id>.md` |
|
|
24
24
|
| Report | `docs/dogfood/YYYY-MM-DD-<testee-slug>-dogfood.md` |
|
|
25
25
|
|
|
@@ -73,7 +73,7 @@ in the report's §6 Findings (no exemption applies).
|
|
|
73
73
|
```
|
|
74
74
|
|
|
75
75
|
| Column | Meaning |
|
|
76
|
-
|
|
76
|
+
| -------- | --------- |
|
|
77
77
|
| `Step` | The derived step label (Phase 1) or `N` for a single-step testee. |
|
|
78
78
|
| `Attempts` | How many times the step was run (1 = first-try; >1 = retried under the fix budget). |
|
|
79
79
|
| `Outcome` | `PASS` / `FIXED` / `UNRESOLVED` / `N/A`. (`FIXED` = failed then passed within budget.) |
|
|
@@ -114,7 +114,7 @@ Ledger estimates alone are **confidence: LOW**. When assembling the report Cost
|
|
|
114
114
|
([report-template.md](report-template.md) §2):
|
|
115
115
|
|
|
116
116
|
| Source | When to use | Confidence | Scope label |
|
|
117
|
-
|
|
117
|
+
| -------- | ------------- | ------------ | ------------- |
|
|
118
118
|
| Ledger `chars/4` heuristic | Always | LOW | per-step trend |
|
|
119
119
|
| `ccusage` daily/session | If CLI available and returns data | MEDIUM | day or session — **not** per-step |
|
|
120
120
|
| Agent usage fields in tool results | If present (never invent) | MEDIUM | as reported by the tool |
|
|
@@ -168,7 +168,7 @@ the driver re-fetching data it already holds. Apply these while monitoring each
|
|
|
168
168
|
When aggregate cache% risks falling under 50%, apply this checklist **before** re-reading:
|
|
169
169
|
|
|
170
170
|
| # | Action | Why |
|
|
171
|
-
|
|
171
|
+
| --- | -------- | ----- |
|
|
172
172
|
| 1 | Reuse the Step-1 `spur task show --json` capture for the rest of the run | Avoids re-tokenizing the full task body |
|
|
173
173
|
| 2 | Do not re-Read SKILL.md / report-template after Phase 1 loaded them | Skill body is large; keep one copy in context |
|
|
174
174
|
| 3 | Prefer `--json` CLI over re-parsing freeform prose | Smaller, stable payloads |
|