@gobing-ai/spur 0.3.70 → 0.3.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/config/workflow-composition-baseline.json +7 -7
- package/config/workflows/idea-pipeline.yaml +4 -3
- package/config/workflows/task-pipeline.yaml +31 -11
- package/config/workflows/wrapup-pipeline.yaml +10 -4
- package/package.json +9 -9
- package/plugins/sp/commands/dev-fixall.md +4 -3
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/scripts/verify-answer-lint.ts +12 -3
- package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
- package/plugins/sp/skills/spec-decomposition/SKILL.md +1 -1
- package/plugins/sp/skills/spur-cli/references/history.md +7 -6
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +2 -2
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +8 -1
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +25 -0
- package/spur.js +313 -221
- package/web/_astro/BoardApp.BnjsI80-.js +178 -0
- package/web/_astro/BoardApp.SJcrHBZp.js +1 -0
- package/web/_astro/{TaskDetail.DKzpkDj5.js → TaskDetail.BvkKvo57.js} +1 -1
- package/web/_astro/{arc.Bsa0gprH.js → arc.DuEIzPMi.js} +1 -1
- package/web/_astro/{architectureDiagram-3BPJPVTR.LOUZCDBC.js → architectureDiagram-3BPJPVTR.D0dUxCjA.js} +1 -1
- package/web/_astro/{blockDiagram-GPEHLZMM.CGee4isG.js → blockDiagram-GPEHLZMM.Cv60iOpM.js} +1 -1
- package/web/_astro/{c4Diagram-AAUBKEIU.B2CrQJ_O.js → c4Diagram-AAUBKEIU.Ddx7WGhb.js} +1 -1
- package/web/_astro/channel.tWfETQvX.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.DJvf8RiP.js → chunk-2J33WTMH.BGKzU_1t.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.B_l35O-Y.js → chunk-4BX2VUAB.FlApjIIH.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.DqbxMswU.js → chunk-55IACEB6.CDfmvDeW.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.D_7ZGNjE.js → chunk-727SXJPM.T9bc-xir.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.Cq6yZ0s8.js → chunk-AQP2D5EJ.BiTc-MQo.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.BWrlUPYi.js → chunk-FMBD7UC4.he4KrHni.js} +1 -1
- package/web/_astro/{chunk-ND2GUHAM.BPLaXFZH.js → chunk-ND2GUHAM.D05XuUuJ.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BtmAEAwZ.js → chunk-QZHKN3VN.CA2NThIE.js} +1 -1
- package/web/_astro/{classDiagram-4FO5ZUOK.CWycT7Ia.js → classDiagram-4FO5ZUOK.Couj-zYZ.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.CWycT7Ia.js → classDiagram-v2-Q7XG4LA2.Couj-zYZ.js} +1 -1
- package/web/_astro/{cose-bilkent-S5V4N54A.DyxjSWon.js → cose-bilkent-S5V4N54A.B8YYW7NG.js} +1 -1
- package/web/_astro/{cynefin-OW5HDTMX.DnNfLBpX.js → cynefin-OW5HDTMX.BExFdiin.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.BFAvnjKD.js → dagre-BM42HDAG.BybKbz3q.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.BAoNCFeB.js → diagram-2AECGRRQ.UyRTSl9n.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.DiiKe7BP.js → diagram-5GNKFQAL.BrxCucBf.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.t_uOQWVP.js → diagram-KO2AKTUF.CdE5oy5J.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.SB9997cP.js → diagram-LMA3HP47.BJLgdosK.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.Bgsxgf5G.js → diagram-OG6HWLK6.CUynieTU.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DO9OAOAc.js → erDiagram-TEJ5UH35.C0vS6DJv.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.CIb41ZyL.js → flowDiagram-I6XJVG4X.T3QLi_en.js} +1 -1
- package/web/_astro/{ganttDiagram-6RSMTGT7.Dc8Lk0fV.js → ganttDiagram-6RSMTGT7.BNsk3w9Z.js} +1 -1
- package/web/_astro/{gitGraphDiagram-PVQCEYII.CpPmMWrl.js → gitGraphDiagram-PVQCEYII.Mwe2I4V6.js} +1 -1
- package/web/_astro/index.9npdrEIr.css +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.DxBXfQSf.js → infoDiagram-5YYISTIA.BlcjLmtc.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.gA6OKtXq.js → ishikawaDiagram-YF4QCWOH.js8qeS0h.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.CMEyLMmm.js → journeyDiagram-JHISSGLW.CM6UK0a4.js} +1 -1
- package/web/_astro/{kanban-definition-UN3LZRKU.CKXlUNCr.js → kanban-definition-UN3LZRKU.a6ihOzMd.js} +1 -1
- package/web/_astro/{linear.CpTGdVx3.js → linear.CsIB2jFu.js} +1 -1
- package/web/_astro/{mermaid.core.c8SSCsE5.js → mermaid.core.Br2Fo22q.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BH59HDw6.js → mindmap-definition-RKZ34NQL.29inC1Mk.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.DmgFXkZR.js → pieDiagram-4H26LBE5.C9CxG_Kf.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.DyfB7N4J.js → quadrantDiagram-W4KKPZXB.6qo9MOJM.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.DosszoFo.js → requirementDiagram-4Y6WPE33.DN07zrP5.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.pBkHhvVN.js → sankeyDiagram-5OEKKPKP.BSw5o173.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.BE8tmkNR.js → sequenceDiagram-3UESZ5HK.LJPzySKw.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.C4n7vDiG.js → stateDiagram-AJRCARHV.ClNjEiKV.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.BpgwYlRy.js → stateDiagram-v2-BHNVJYJU.c6Z-_WfX.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.DvcVKc-W.js → timeline-definition-PNZ67QCA.CIMR-87j.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.BLOk-UOl.js → vennDiagram-CIIHVFJN.BnRSRI9I.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.CIzB7M2o.js → wardleyDiagram-YWT4CUSO.uN08C3gv.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.CGC_lZ19.js → xychartDiagram-2RQKCTM6.BjbEvfuq.js} +1 -1
- package/web/index.html +2 -2
- package/web/_astro/BoardApp.DHj-03Dp.js +0 -1
- package/web/_astro/BoardApp.DOadeEJV.js +0 -178
- package/web/_astro/channel.BGL7KSHC.js +0 -1
- package/web/_astro/index.C-t8kB0T.css +0 -1
|
@@ -117,7 +117,7 @@
|
|
|
117
117
|
},
|
|
118
118
|
"discovery:onEnter:0": {
|
|
119
119
|
"kind": "agent.run",
|
|
120
|
-
"invocation": "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per
|
|
120
|
+
"invocation": "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per the `sp:spur-dev` skill's `idea-evaluation` reference (urgency/necessity 0–5, premises, pros/cons, alternatives, enhanced idea, recommendation). At the END of the report, append a provenance footer block of the exact form: `---\\nrun_id: ${vars.__runId}\\ngenerated_at: <RFC3339 timestamp>\\n---` (omit the footer only if run_id is empty)."
|
|
121
121
|
},
|
|
122
122
|
"idea-eval:onEnter:0": {
|
|
123
123
|
"kind": "hitl.confirm"
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
},
|
|
178
178
|
"decompose:onEnter:1": {
|
|
179
179
|
"kind": "agent.run",
|
|
180
|
-
"invocation": "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against
|
|
180
|
+
"invocation": "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting. Also emit the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json: a JSON array (one entry per batch item) of `{ name: <exact batch item name>, depends_on_names: [<batch item names>] }` declaring ordering/dependencies between the batch items; use `[]` when no ordering exists. Every `name` and every dependency must match exactly one batch item `name` — it is private workflow data, not part of task-batch.schema.json."
|
|
181
181
|
},
|
|
182
182
|
"decompose:onEnter:2": {
|
|
183
183
|
"kind": "shell",
|
|
@@ -230,11 +230,11 @@
|
|
|
230
230
|
},
|
|
231
231
|
"precheck:onEnter:3": {
|
|
232
232
|
"kind": "shell",
|
|
233
|
-
"invocation": "SIZE_FILE=\".spur/run/$wbs-precheck-size.status\" && mkdir -p .spur/run &&
|
|
233
|
+
"invocation": "SIZE_FILE=\".spur/run/$wbs-precheck-size.status\" && mkdir -p .spur/run && SIZE_SCRIPT=\"plugins/sp/scripts/task-size-precheck.ts\"; [ -f \"$SIZE_SCRIPT\" ] || SIZE_SCRIPT=\"$(superskill script path sp task-size-precheck.ts 2>/dev/null)\"; if [ -n \"$SIZE_SCRIPT\" ] && [ -f \"$SIZE_SCRIPT\" ]; then bun \"$SIZE_SCRIPT\" \"$wbs\" --spur-bin \"$spurBin\" --max-reqs \"$maxImplementReqs\" --max-plan-items \"$maxImplementPlanItems\"; else\n echo \"task-size-precheck failed closed — checker not found in\" >&2 &&\n echo \"plugins/sp/scripts/ nor staged; run 'superskill install sp'.\" >&2 &&\n echo \"FAIL\" > \"$SIZE_FILE\";\nfi && exit 0"
|
|
234
234
|
},
|
|
235
235
|
"precheck:onEnter:4": {
|
|
236
236
|
"kind": "shell",
|
|
237
|
-
"invocation": "EVID_FILE=\".spur/run/$wbs-precheck-evidence.status\" && mkdir -p .spur/run &&
|
|
237
|
+
"invocation": "EVID_FILE=\".spur/run/$wbs-precheck-evidence.status\" && mkdir -p .spur/run && EVID_SCRIPT=\"plugins/sp/scripts/task-evidence-precheck.ts\"; [ -f \"$EVID_SCRIPT\" ] || EVID_SCRIPT=\"$(superskill script path sp task-evidence-precheck.ts 2>/dev/null)\"; if [ -n \"$EVID_SCRIPT\" ] && [ -f \"$EVID_SCRIPT\" ]; then bun \"$EVID_SCRIPT\" \"$wbs\" --spur-bin \"$spurBin\"; else\n echo \"task-evidence-precheck failed closed — checker not found\" >&2 &&\n echo \"in plugins/sp/scripts/ nor staged; run 'superskill install sp'.\" >&2 &&\n echo \"FAIL\" > \"$EVID_FILE\";\nfi && exit 0"
|
|
238
238
|
},
|
|
239
239
|
"implement:onEnter:0": {
|
|
240
240
|
"kind": "agent.run",
|
|
@@ -299,7 +299,7 @@
|
|
|
299
299
|
},
|
|
300
300
|
"verify:onEnter:2": {
|
|
301
301
|
"kind": "shell",
|
|
302
|
-
"invocation": "
|
|
302
|
+
"invocation": "LINT_SCRIPT=\"plugins/sp/scripts/verify-answer-lint.ts\"; [ -f \"$LINT_SCRIPT\" ] || LINT_SCRIPT=\"$(superskill script path sp verify-answer-lint.ts 2>/dev/null)\"; if [ -z \"$LINT_SCRIPT\" ] || [ ! -f \"$LINT_SCRIPT\" ]; then\n echo \"verify-answer-lint: checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp'\" >&2;\n exit 1;\nfi; bun \"$LINT_SCRIPT\" \"$wbs\" --answer \".spur/run/$wbs-verify-answer.txt\" --spur-bin \"$spurBin\""
|
|
303
303
|
},
|
|
304
304
|
"verify:onEnter:3": {
|
|
305
305
|
"kind": "shell",
|
|
@@ -318,7 +318,7 @@
|
|
|
318
318
|
},
|
|
319
319
|
"record:onEnter:2": {
|
|
320
320
|
"kind": "shell",
|
|
321
|
-
"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"
|
|
321
|
+
"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 elif SYNC_MJS=\"$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)\" && [ -f \"$SYNC_MJS\" ]; then\n node \"$SYNC_MJS\" \"$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"
|
|
322
322
|
},
|
|
323
323
|
"done:onEnter:0": {
|
|
324
324
|
"kind": "shell",
|
|
@@ -367,7 +367,7 @@
|
|
|
367
367
|
},
|
|
368
368
|
"feature-transition:onEnter:0": {
|
|
369
369
|
"kind": "shell",
|
|
370
|
-
"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; SYNC_OUTPUT=$(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); SYNC_RC=$?; printf '%s\\n' \"$SYNC_OUTPUT\"; APPLIED=$(printf '%s' \"$SYNC_OUTPUT\" | jq -r '.applied // false' 2>/dev/null || echo false); if [ \"$APPLIED\" = \"true\" ] || [ \"$SYNC_RC\" -ne 0 ]; then\n echo \"feature-transition: sync applied or failed after a possible partial transition for $feature — running corpus-aware gate: $featureGateCmd\";\n if sh -c \"$featureGateCmd\"; then\n echo \"feature-transition: corpus-aware gate PASS for feature $feature\";\n else\n echo \"feature-transition: corpus-aware gate FAIL for feature $feature — inspect findings before reporting the transition complete\" >&2;\n fi;\nelse\n echo \"feature-transition: sync did not apply a transition (rc=$SYNC_RC, applied=$APPLIED) — corpus-aware gate skipped\";\nfi; exit 0"
|
|
370
|
+
"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; SYNC_OUTPUT=$(if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then\n bun plugins/sp/scripts/feature-sync-bounded.ts \"$feature\" --spur-bin \"$spurBin\" --json;\nelif SYNC_MJS=\"$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)\" && [ -f \"$SYNC_MJS\" ]; then\n node \"$SYNC_MJS\" \"$feature\" --spur-bin \"$spurBin\" --json;\nelse\n $spurBin feature sync \"$feature\" --json;\nfi); SYNC_RC=$?; printf '%s\\n' \"$SYNC_OUTPUT\"; APPLIED=$(printf '%s' \"$SYNC_OUTPUT\" | jq -r '.applied // false' 2>/dev/null || echo false); if [ \"$APPLIED\" = \"true\" ] || [ \"$SYNC_RC\" -ne 0 ]; then\n echo \"feature-transition: sync applied or failed after a possible partial transition for $feature — running corpus-aware gate: $featureGateCmd\";\n if sh -c \"$featureGateCmd\"; then\n echo \"feature-transition: corpus-aware gate PASS for feature $feature\";\n else\n echo \"feature-transition: corpus-aware gate FAIL for feature $feature — inspect findings before reporting the transition complete\" >&2;\n fi;\nelse\n echo \"feature-transition: sync did not apply a transition (rc=$SYNC_RC, applied=$APPLIED) — corpus-aware gate skipped\";\nfi; exit 0"
|
|
371
371
|
},
|
|
372
372
|
"branch-cleanup:onEnter:0": {
|
|
373
373
|
"kind": "hitl.confirm"
|
|
@@ -99,13 +99,14 @@ states:
|
|
|
99
99
|
boolean signal written to .spur/run/${vars.__runId}-idea-needs-design.json — this determines
|
|
100
100
|
whether the system-design state runs. As its terminal artifact for the idea path,
|
|
101
101
|
brainstorm also emits the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md
|
|
102
|
-
(template:
|
|
102
|
+
(template: the `sp:spur-dev` skill's `idea-evaluation` reference — named by skill, not by
|
|
103
|
+
repo path, because `spur init` never scaffolds `plugins/sp/` into a seeded project).
|
|
103
104
|
expectFile fails a silent no-op discovery (no eval report).
|
|
104
105
|
onEnter:
|
|
105
106
|
- kind: agent.run
|
|
106
107
|
options:
|
|
107
108
|
agent: ${vars.planningAgent}
|
|
108
|
-
input: "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per
|
|
109
|
+
input: "Run sp:brainstorm for the idea: ${vars.idea}. The skill owns the approach-generation, design summary, and `needs_design` signal criteria; emit .spur/run/${vars.__runId}-idea-needs-design.json ({\"needs_design\": true|false}) and the design summary per the skill's `Design Approval Gate` and `The needs_design signal` sections. Also emit the idea-evaluation report to .spur/run/${vars.__runId}-idea-eval-report.md per the `sp:spur-dev` skill's `idea-evaluation` reference (urgency/necessity 0–5, premises, pros/cons, alternatives, enhanced idea, recommendation). At the END of the report, append a provenance footer block of the exact form: `---\\nrun_id: ${vars.__runId}\\ngenerated_at: <RFC3339 timestamp>\\n---` (omit the footer only if run_id is empty)."
|
|
109
110
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
110
111
|
role: planner
|
|
111
112
|
expectFile: .spur/run/${vars.__runId}-idea-eval-report.md
|
|
@@ -298,7 +299,7 @@ states:
|
|
|
298
299
|
- kind: agent.run
|
|
299
300
|
options:
|
|
300
301
|
agent: ${vars.planningAgent}
|
|
301
|
-
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against
|
|
302
|
+
input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. Acceptance Criteria, Design, and Plan sections are filled in by the per-task refine step after batch-create, NOT at decompose time. Validate locally against the schema before emitting. Also emit the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json: a JSON array (one entry per batch item) of `{ name: <exact batch item name>, depends_on_names: [<batch item names>] }` declaring ordering/dependencies between the batch items; use `[]` when no ordering exists. Every `name` and every dependency must match exactly one batch item `name` — it is private workflow data, not part of task-batch.schema.json."
|
|
302
303
|
# Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
|
|
303
304
|
role: planner
|
|
304
305
|
expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# agent / spurBin — executor + spur binary (CLI overrides spurBin)
|
|
20
20
|
# stepTimeoutMs — agent.run budget for review/verify/test-fix (ms)
|
|
21
21
|
# implementTimeoutMs — implement agent.run budget (ms)
|
|
22
|
-
# qualityGateCmd — project gate (default: bun run
|
|
22
|
+
# qualityGateCmd — project gate (default: bun run spur-check)
|
|
23
23
|
# qualityGateMaxFixAttempts — max /sp:dev-fixall hops after a red gate (default: 2)
|
|
24
24
|
#
|
|
25
25
|
# Seeded by `spur init`. agent.run inputs are pure slash commands (ADR-043).
|
|
@@ -104,7 +104,12 @@ vars:
|
|
|
104
104
|
# and the fixall slash input all use this same var so the command stays single-sourced.
|
|
105
105
|
# TRUSTED CONFIG ONLY — this string is executed via `sh -c` (see test/test-recheck). Never
|
|
106
106
|
# interpolate untrusted operator/LLM input into qualityGateCmd (task 0436 SECUA residual).
|
|
107
|
-
|
|
107
|
+
# NO `format` PREFIX: `implement` already ran `$formatCmd` on its way out, and `test`
|
|
108
|
+
# captures `proofDigest` (onEnter[3]) BEFORE this command runs (onEnter[4]) — a formatter
|
|
109
|
+
# inside the gate rewrites the very tree the digest just fingerprinted (ADR-071 proof
|
|
110
|
+
# window). It was a no-op only because the implement-stage format got there first; that is
|
|
111
|
+
# an accident, not an invariant. The gate observes, it does not mutate.
|
|
112
|
+
qualityGateCmd: "bun run spur-check"
|
|
108
113
|
# Cheap red-detector run before the full gate on **recheck only**; empty ⇒ no probe
|
|
109
114
|
# (full gate every recheck — the pre-0587 behavior). A project overriding qualityGateCmd
|
|
110
115
|
# should override this too. TRUSTED CONFIG ONLY — executed via `sh -c` (same surface as
|
|
@@ -208,13 +213,16 @@ states:
|
|
|
208
213
|
command: >-
|
|
209
214
|
SIZE_FILE=".spur/run/$wbs-precheck-size.status" &&
|
|
210
215
|
mkdir -p .spur/run &&
|
|
211
|
-
|
|
212
|
-
|
|
216
|
+
SIZE_SCRIPT="plugins/sp/scripts/task-size-precheck.ts";
|
|
217
|
+
[ -f "$SIZE_SCRIPT" ] ||
|
|
218
|
+
SIZE_SCRIPT="$(superskill script path sp task-size-precheck.ts 2>/dev/null)";
|
|
219
|
+
if [ -n "$SIZE_SCRIPT" ] && [ -f "$SIZE_SCRIPT" ]; then
|
|
220
|
+
bun "$SIZE_SCRIPT" "$wbs"
|
|
213
221
|
--spur-bin "$spurBin" --max-reqs "$maxImplementReqs"
|
|
214
222
|
--max-plan-items "$maxImplementPlanItems";
|
|
215
223
|
else
|
|
216
|
-
echo "task-size-precheck failed closed — checker
|
|
217
|
-
echo "plugins/sp/scripts/
|
|
224
|
+
echo "task-size-precheck failed closed — checker not found in" >&2 &&
|
|
225
|
+
echo "plugins/sp/scripts/ nor staged; run 'superskill install sp'." >&2 &&
|
|
218
226
|
echo "FAIL" > "$SIZE_FILE";
|
|
219
227
|
fi &&
|
|
220
228
|
exit 0
|
|
@@ -228,12 +236,15 @@ states:
|
|
|
228
236
|
command: >-
|
|
229
237
|
EVID_FILE=".spur/run/$wbs-precheck-evidence.status" &&
|
|
230
238
|
mkdir -p .spur/run &&
|
|
231
|
-
|
|
232
|
-
|
|
239
|
+
EVID_SCRIPT="plugins/sp/scripts/task-evidence-precheck.ts";
|
|
240
|
+
[ -f "$EVID_SCRIPT" ] ||
|
|
241
|
+
EVID_SCRIPT="$(superskill script path sp task-evidence-precheck.ts 2>/dev/null)";
|
|
242
|
+
if [ -n "$EVID_SCRIPT" ] && [ -f "$EVID_SCRIPT" ]; then
|
|
243
|
+
bun "$EVID_SCRIPT" "$wbs"
|
|
233
244
|
--spur-bin "$spurBin";
|
|
234
245
|
else
|
|
235
|
-
echo "task-evidence-precheck failed closed —" >&2 &&
|
|
236
|
-
echo "plugins/sp/scripts/
|
|
246
|
+
echo "task-evidence-precheck failed closed — checker not found" >&2 &&
|
|
247
|
+
echo "in plugins/sp/scripts/ nor staged; run 'superskill install sp'." >&2 &&
|
|
237
248
|
echo "FAIL" > "$EVID_FILE";
|
|
238
249
|
fi &&
|
|
239
250
|
exit 0
|
|
@@ -563,7 +574,14 @@ states:
|
|
|
563
574
|
- kind: shell
|
|
564
575
|
options:
|
|
565
576
|
command: >-
|
|
566
|
-
|
|
577
|
+
LINT_SCRIPT="plugins/sp/scripts/verify-answer-lint.ts";
|
|
578
|
+
[ -f "$LINT_SCRIPT" ] ||
|
|
579
|
+
LINT_SCRIPT="$(superskill script path sp verify-answer-lint.ts 2>/dev/null)";
|
|
580
|
+
if [ -z "$LINT_SCRIPT" ] || [ ! -f "$LINT_SCRIPT" ]; then
|
|
581
|
+
echo "verify-answer-lint: checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp'" >&2;
|
|
582
|
+
exit 1;
|
|
583
|
+
fi;
|
|
584
|
+
bun "$LINT_SCRIPT" "$wbs"
|
|
567
585
|
--answer ".spur/run/$wbs-verify-answer.txt"
|
|
568
586
|
--spur-bin "$spurBin"
|
|
569
587
|
- kind: shell
|
|
@@ -643,6 +661,8 @@ states:
|
|
|
643
661
|
if [ -n "$FID" ]; then
|
|
644
662
|
if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then
|
|
645
663
|
bun plugins/sp/scripts/feature-sync-bounded.ts "$FID" --spur-bin "$spurBin" --json;
|
|
664
|
+
elif SYNC_MJS="$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)" && [ -f "$SYNC_MJS" ]; then
|
|
665
|
+
node "$SYNC_MJS" "$FID" --spur-bin "$spurBin" --json;
|
|
646
666
|
else
|
|
647
667
|
$spurBin feature sync "$FID" --json;
|
|
648
668
|
fi;
|
|
@@ -54,12 +54,16 @@ vars:
|
|
|
54
54
|
# Corpus-aware quality gate for the feature-transition hop (R1, task 0625).
|
|
55
55
|
# The per-task gate (`spur-check`) deliberately excludes corpus-check — the only
|
|
56
56
|
# sweep that observes feature-level findings — so a transition that arms one
|
|
57
|
-
# would go green.
|
|
58
|
-
#
|
|
59
|
-
# and
|
|
57
|
+
# would go green. This gate is therefore the CORPUS SWEEP ALONE (~29 s), not
|
|
58
|
+
# `spur-check-new`: every task in the feature already paid a full `spur-check`
|
|
59
|
+
# in its own pipeline, and the states that run before this hop (`doc-sync`,
|
|
60
|
+
# `metrics-record`) write only markdown and task sections, which Biome skips
|
|
61
|
+
# (`ignoreUnknown: true`). Re-running the ~105 s per-task gate here re-verifies
|
|
62
|
+
# unchanged code and measures nothing new. Soft by design: the gate reports
|
|
63
|
+
# PASS/FAIL and lets the operator decide; it never hard-fails the wrap-up shell.
|
|
60
64
|
# TRUSTED CONFIG ONLY — this string is executed via `sh -c` (same surface as
|
|
61
65
|
# task-pipeline's qualityGateCmd). Never interpolate untrusted input into it.
|
|
62
|
-
featureGateCmd: "bun run
|
|
66
|
+
featureGateCmd: "bun run corpus-check"
|
|
63
67
|
__hitlAnswer: ""
|
|
64
68
|
|
|
65
69
|
states:
|
|
@@ -178,6 +182,8 @@ states:
|
|
|
178
182
|
fi;
|
|
179
183
|
SYNC_OUTPUT=$(if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then
|
|
180
184
|
bun plugins/sp/scripts/feature-sync-bounded.ts "$feature" --spur-bin "$spurBin" --json;
|
|
185
|
+
elif SYNC_MJS="$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)" && [ -f "$SYNC_MJS" ]; then
|
|
186
|
+
node "$SYNC_MJS" "$feature" --spur-bin "$spurBin" --json;
|
|
181
187
|
else
|
|
182
188
|
$spurBin feature sync "$feature" --json;
|
|
183
189
|
fi);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/spur",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.71",
|
|
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",
|
|
@@ -53,14 +53,14 @@
|
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@commander-js/extra-typings": "^14.0.0",
|
|
56
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
57
|
-
"@gobing-ai/ts-ai-runner": "^0.4.
|
|
58
|
-
"@gobing-ai/ts-dual-workflow-engine": "^0.4.
|
|
59
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
60
|
-
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.
|
|
61
|
-
"@gobing-ai/ts-rule-engine": "^0.4.
|
|
62
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
63
|
-
"@gobing-ai/ts-utils": "^0.4.
|
|
56
|
+
"@gobing-ai/ts-db": "^0.4.50",
|
|
57
|
+
"@gobing-ai/ts-ai-runner": "^0.4.50",
|
|
58
|
+
"@gobing-ai/ts-dual-workflow-engine": "^0.4.50",
|
|
59
|
+
"@gobing-ai/ts-infra": "^0.4.50",
|
|
60
|
+
"@gobing-ai/ts-llm-jsonl-importer": "^0.4.50",
|
|
61
|
+
"@gobing-ai/ts-rule-engine": "^0.4.50",
|
|
62
|
+
"@gobing-ai/ts-runtime": "^0.4.50",
|
|
63
|
+
"@gobing-ai/ts-utils": "^0.4.50",
|
|
64
64
|
"@types/bun": "1.3.14",
|
|
65
65
|
"@types/figlet": "^1.7.0",
|
|
66
66
|
"@types/node-notifier": "8.0.5",
|
|
@@ -28,7 +28,8 @@ For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag
|
|
|
28
28
|
## Implementation
|
|
29
29
|
|
|
30
30
|
Under the pipeline, the `test-recheck` state runs the full gate immediately after this hop — that is
|
|
31
|
-
the deciding run.
|
|
32
|
-
probes (`bun test <file> --test-name-pattern <test>`) during fix loops,
|
|
33
|
-
|
|
31
|
+
the deciding run. When `--gate-log` is set (the pipeline signal), fixall runs **no full gate at
|
|
32
|
+
all**: targeted probes (`bun test <file> --test-name-pattern <test>`) during fix loops, then one
|
|
33
|
+
`bun run lint` before returning. Invoked standalone, it keeps the single confirming run (R4, task
|
|
34
|
+
0483). `qualityGateCmd` itself is unchanged so `test-recheck` still runs the full gate.
|
|
34
35
|
|
package/plugins/sp/plugin.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.71",
|
|
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"]
|
|
@@ -245,15 +245,24 @@ function sectionBetween(text: string, heading: string): string {
|
|
|
245
245
|
function extractRequirementIds(taskContent: string): string[] {
|
|
246
246
|
const section = sectionBetween(taskContent, 'Requirements');
|
|
247
247
|
const ids = new Set<string>();
|
|
248
|
-
|
|
249
|
-
|
|
248
|
+
// Corpus forms: bold-wrapped (`**R1. Title.**`, bare `**R1**`); right after a list
|
|
249
|
+
// marker with optional checkbox (`- [ ] R1.` — the dominant corpus form, `- R1. Title.`,
|
|
250
|
+
// `- R1:`) or a bare checkbox with the marker omitted (`[x] R1.`); line-start (`R1:`).
|
|
251
|
+
// The marker and the checkbox are never both optional — that would match bare prose
|
|
252
|
+
// (`R1 is …`) and fabricate declarations. Sub-IDs (`R1.1`) match in every form.
|
|
253
|
+
for (const m of section.matchAll(/\*\*(R\d+(?:\.\d+)*)\b/g)) ids.add(m[1] ?? '');
|
|
254
|
+
for (const m of section.matchAll(/^(?:[-*]\s+(?:\[[ xX]\]\s+)?|\[[ xX]\]\s+)(R\d+(?:\.\d+)*)/gm))
|
|
255
|
+
ids.add(m[1] ?? '');
|
|
256
|
+
for (const m of section.matchAll(/^(R\d+(?:\.\d+)*)\s*[.:]/gm)) ids.add(m[1] ?? '');
|
|
250
257
|
return [...ids];
|
|
251
258
|
}
|
|
252
259
|
|
|
253
260
|
function extractAcIdentities(taskContent: string, featureContent: string | null): string[] {
|
|
254
261
|
const identities = new Set<string>();
|
|
255
262
|
const section = sectionBetween(taskContent, 'Acceptance Criteria');
|
|
256
|
-
|
|
263
|
+
// Checkbox labels (`- [x] AC1 (R1): …`, 0726) and plain bullets (`- AC1: Given …`,
|
|
264
|
+
// 0713/0727) both yield the label text up to `:` plus its leading token.
|
|
265
|
+
for (const m of section.matchAll(/^[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*(?::|$)/gm)) {
|
|
257
266
|
const label = (m[1] ?? '').trim();
|
|
258
267
|
if (!label) continue;
|
|
259
268
|
identities.add(label);
|
|
@@ -81,7 +81,7 @@ surfaces is what keeps that gate quiet.
|
|
|
81
81
|
## Implement scope: do not run the project quality gate
|
|
82
82
|
|
|
83
83
|
During implement, the pipeline's `test` hop runs `${vars.qualityGateCmd}` (the full project gate:
|
|
84
|
-
`bun run
|
|
84
|
+
`bun run spur-check`) immediately after this step and is the gate that actually
|
|
85
85
|
decides pass/fail. Running it inside implement is pure redundancy — it cannot change the outcome and
|
|
86
86
|
only burns wall clock and context budget.
|
|
87
87
|
|
|
@@ -57,7 +57,7 @@ granularity knobs (min/target/force-split hours), and parent/umbrella-task conve
|
|
|
57
57
|
spur task batch-create --file decomposition.json # bare JSON array; atomic, all-or-nothing
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
Validate locally against `
|
|
60
|
+
Validate locally against `task-batch.schema.json` (runtime SSOT: the Zod
|
|
61
61
|
`taskBatchSchema`) before invoking the CLI — a single violation rejects the entire batch. The gate is
|
|
62
62
|
the only proof the decomposition is well-formed; never hand-write task files to bypass it.
|
|
63
63
|
|
|
@@ -29,9 +29,9 @@ Every JSON-capable verb also advertises `--json-envelope`; use the facade's mach
|
|
|
29
29
|
## `import` - isolated fan-out
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
spur history import --source all --dry-run --json
|
|
33
|
+
spur history import --source codex --mode incremental --json
|
|
34
|
+
spur history import --source codex --file session.jsonl --mode force-file --json
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
- `--source all` and a single source use the same per-source fan-out path. A failed/timed-out source
|
|
@@ -39,9 +39,10 @@ bun run apps/cli/src/index.ts history import --source codex --file session.jsonl
|
|
|
39
39
|
- Modes are `incremental`, `full`, and `force-file`. `--file` with the default `all` source is a
|
|
40
40
|
usage error. `--file --mode full` requires `--dry-run`; use `force-file` for a real single-file
|
|
41
41
|
write.
|
|
42
|
-
- JSON contains `entries`, `warnings`, `exitCode`, and CLI/importer `provenance`.
|
|
43
|
-
|
|
44
|
-
`spur` that may be
|
|
42
|
+
- JSON contains `entries`, `warnings`, `exitCode`, and CLI/importer `provenance`. Record that
|
|
43
|
+
provenance for any real-data validation. **When developing Spur itself**, invoke the source-local
|
|
44
|
+
CLI (`bun run apps/cli/src/index.ts history import …`) instead of a global `spur` that may be a
|
|
45
|
+
stale published bundle; in every other project the installed `spur` is the CLI.
|
|
45
46
|
- Exit `0` when every source is clean/empty, `2` for a mixed failure or any degraded source, and `1`
|
|
46
47
|
when all sources fail. CLI usage guards also exit `1` on this noun.
|
|
47
48
|
|
|
@@ -463,8 +463,8 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
|
|
|
463
463
|
6. Run `bun run test`. Collect all failures.
|
|
464
464
|
7. If tests are green, done.
|
|
465
465
|
8. **Test fix loop:** for each failure, diagnose (test bug vs implementation bug), apply the fix, re-run the **failing test only** (`bun test <file> --test-name-pattern "<test>"`). Do NOT re-run the full suite per fix — it is the dominant loop cost (task 0436 R2).
|
|
466
|
-
9. **Confirming run (at most once).** After all fixes, run `bun run format && bun run lint && bun run test` **at most once** to confirm. If it passes, the hop is done.
|
|
467
|
-
10. **Pipeline-awareness (R4, task 0483).**
|
|
466
|
+
9. **Confirming run — standalone invocation only (at most once).** After all fixes, run `bun run format && bun run lint && bun run test` **at most once** to confirm. If it passes, the hop is done. **Skip this step entirely when `--gate-log` is set** (see step 10) — under the pipeline nothing consumes its verdict.
|
|
467
|
+
10. **Pipeline-awareness (R4, task 0483; confirming run dropped 2026-08-31).** `--gate-log` is set only by the pipeline's `test-fix` hop, so treat it as the pipeline signal. There, `test-recheck` runs the full `${vars.qualityGateCmd}` gate immediately after this hop returns — that is the **deciding** run, the only one that writes PASS to `.spur/run/<wbs>-test-gate.status`. A confirming run inside this hop decides nothing and costs a second full gate (~105 s here) to answer a question `test-recheck` is about to answer anyway. So under `--gate-log`: **run no full gate at all.** Your self-check is what step 8 already gives you — the failing tests you re-ran individually — plus **one `bun run lint`** (~7 s: Biome + typecheck) to catch type or lint breakage your fixes introduced. Then return and let `test-recheck` judge. `test-recheck` opens with the same cheap `${vars.gateProbeCmd}` probe, so a still-red tree is caught in seconds rather than by a full suite. Historical anti-pattern this replaces: 0482 ran the gate 3× plus a standalone `bun run test`, and `test-recheck` then ran it a 5th time.
|
|
468
468
|
11. Report: list what was fixed (file + one-line summary per fix). If any error could not be resolved, report it explicitly — do not suppress.
|
|
469
469
|
- **Invariants:** Never bypass with `--no-verify`, `--force`, or new `biome-ignore`/`eslint-disable` suppressions. Never skip or `.skip` a test to make the suite green. Fix the root cause, not the symptom. Never claim green on `bun run lint` alone — a formatter-only diff passes `lint` but fails the formatter; run `bun run format` (or assert it produces no diff) before declaring the gate clean. **Never re-run the full gate more than once per confirming pass** (R4) — use targeted probes during the fix loops and let the pipeline's `test-recheck` state be the deciding run.
|
|
470
470
|
- **MANDATORY Exit Condition.** The ONLY way to complete successfully:
|
|
@@ -46,7 +46,7 @@ one thing and yields, so the **pipeline (not the agent) owns the loop**.
|
|
|
46
46
|
| Stage | Operation | Defined in |
|
|
47
47
|
| ------- | ----------- | ------------ |
|
|
48
48
|
| `implement` | `/sp:dev-run --mode implement <wbs>` — write the code that satisfies the task; author `## Solution`. | [dev-operations.md §4 run](dev-operations.md) → `sp:code-implementation` |
|
|
49
|
-
| `test` → (`test-fix` ↔ `test-recheck`) → `review` \| `failed` | **Project quality gate** (not `/sp:dev-unit`). Soft shell probe of `${vars.qualityGateCmd}` (default `bun run
|
|
49
|
+
| `test` → (`test-fix` ↔ `test-recheck`) → `review` \| `failed` | **Project quality gate** (not `/sp:dev-unit`). Soft shell probe of `${vars.qualityGateCmd}` (default `bun run spur-check`) — green path pays **one** full gate run. On FAIL: bounded `/sp:dev-fixall` loop (`qualityGateMaxFixAttempts`, default 2) with soft recheck; exhausted attempts route to pipeline `failed`. `/sp:dev-unit` remains **coverage gap-fill** (router C3/C5 / standalone). | [dev-operations.md §10 fixall](dev-operations.md); unit op still §1 |
|
|
50
50
|
| `review` | `/sp:dev-review <wbs>` — SECUA-framework review of the diff. | [dev-operations.md §2 review](dev-operations.md) |
|
|
51
51
|
| `verify` | `sp:code-verification` — requirements traceability + verdict. | [dev-operations.md §3 verify](dev-operations.md) |
|
|
52
52
|
|
|
@@ -97,6 +97,7 @@ cache-conservation discipline (`plugins/sp/skills/dogfood-testing/references/mon
|
|
|
97
97
|
## Step 2: Pipeline run
|
|
98
98
|
|
|
99
99
|
> **Pre-launch size-gate pre-check (R1 / 0478).** Before launching `spur workflow run task-pipeline.yaml`, probe the task's `## Plan` checklist item count (`spur task show <wbs> --json`). The default cap is 8 items (`maxImplementPlanItems: 8`). If the plan item count exceeds 8:
|
|
100
|
+
>
|
|
100
101
|
> - Without `--auto`: warn the operator before calling `spur workflow run` and prompt for confirmation or a plan-item override via `--vars '{"maxImplementPlanItems":"<count>"}'`.
|
|
101
102
|
> - With `--auto`: automatically append `"maxImplementPlanItems": "<count>"` to `--vars` and log a single-line notice (e.g. `Notice: task <wbs> has N plan items (>8 default cap); injecting maxImplementPlanItems override`).
|
|
102
103
|
|
|
@@ -313,6 +314,12 @@ the partial work still in the working tree. The failure output names the partial
|
|
|
313
314
|
[`done-housekeeping.md`](done-housekeeping.md) F6 — it carries the provenance obligations
|
|
314
315
|
(honest `done_reason`, verdict regeneration).
|
|
315
316
|
|
|
317
|
+
**Inline path (task 0727).** There is no `<runId>-implement-partial.md` artifact on the inline
|
|
318
|
+
driver path — it is written only by the subprocess `agent.run` action. The inline equivalent is
|
|
319
|
+
the dispatch-timeout contract in [`inline-pipeline-driver.md`](inline-pipeline-driver.md):
|
|
320
|
+
resume from the partial tree, never restart the stage inline; the partial working tree is the
|
|
321
|
+
recovery input.
|
|
322
|
+
|
|
316
323
|
**3. Match the executor to the size (task 0487 R3).** Size and executor capability are one
|
|
317
324
|
decision, not two. **≥ 6 requirements or ≥ 9 Plan items → a `reviewer`-role executor (the
|
|
318
325
|
`reviewer` row of [`roles.md`](../../../references/roles.md) declares the floor), or split the
|
|
@@ -52,7 +52,7 @@ Entered before `spur task batch-create --file <json>` (idea-pipeline `batch-crea
|
|
|
52
52
|
decomposition gate `/sp:dev-plan` also routes through since D5-K).
|
|
53
53
|
|
|
54
54
|
- [ ] The batch JSON is a bare array (not an object with a `tasks` key).
|
|
55
|
-
- [ ] Each entry validates locally against `
|
|
55
|
+
- [ ] Each entry validates locally against `task-batch.schema.json` (`additionalProperties: false` — no unknown keys).
|
|
56
56
|
- [ ] Each entry has a non-empty `name` (required).
|
|
57
57
|
- [ ] `feature_id` matches an existing feature (or is intentionally deferred with operator awareness).
|
|
58
58
|
- [ ] `parent_wbs` is set when the task is a child of a decomposition parent.
|
|
@@ -41,6 +41,12 @@ command, skill, script, or second workflow.
|
|
|
41
41
|
the YAML parsed in step 1, shown only for the active state.
|
|
42
42
|
- **Refresh cadence** = stage boundaries only (when the current state changes after a transition),
|
|
43
43
|
never per action.
|
|
44
|
+
- **Transition reconciliation (task 0727)** = at every stage boundary the host must
|
|
45
|
+
**mark the finished stage completed and the next stage in_progress** in the host todo list.
|
|
46
|
+
This reconciliation is **host-owned and execution-surface-independent**: it fires identically
|
|
47
|
+
whether the stage ran via native subagent, host-inline execution, or the post-dispatch host
|
|
48
|
+
fallback, so a run can never terminate with earlier stages stuck `in_progress` (task 0726
|
|
49
|
+
ended 0/11 with precheck and implement still open).
|
|
44
50
|
- **Source of truth** = the CLI projection for layer 1; the YAML parsed in step 1 for layer 2.
|
|
45
51
|
Never hand-copy or hand-derive the state list into the driver, a command, a skill, or a script.
|
|
46
52
|
5. For task execution only, record lifecycle provenance before entering the FSM:
|
|
@@ -119,6 +125,19 @@ before the subagent starts, log the reason and use host fallback. If a started s
|
|
|
119
125
|
leaves invalid artifacts, do **not** replay the stage in the host — follow the YAML error policy so
|
|
120
126
|
partial mutations are not duplicated.
|
|
121
127
|
|
|
128
|
+
**Timeout boundary (task 0727):** a dispatched subagent is governed by
|
|
129
|
+
**the host platform's subagent limit, not the YAML timeoutMs** — `timeoutMs` stays not-applicable
|
|
130
|
+
for host execution only — and before dispatch the driver must
|
|
131
|
+
**record the governing timeout boundary and its source before dispatch** in the run log
|
|
132
|
+
(e.g. `host timeout <ms> (<platform subagent limit|yaml timeoutMs>)`). If the dispatch reaches that
|
|
133
|
+
boundary, **a dispatch timeout is a started-subagent failure**: the no-replay rule above and the
|
|
134
|
+
stage's declared YAML error policy govern (implement's default `fail` policy routes the run to
|
|
135
|
+
`failed`); it is never a host re-execution. Recovery follows the
|
|
136
|
+
[timed-out implement runbook](execution-workflow.md)'s inline-path equivalent:
|
|
137
|
+
**resume from the partial tree, never restart the stage inline** — no
|
|
138
|
+
`<runId>-implement-partial.md` artifact is written on this path, so the partial working tree
|
|
139
|
+
itself is the recovery input.
|
|
140
|
+
|
|
122
141
|
**Host-owned interaction:** the host alone executes operator-confirmation actions, owns
|
|
123
142
|
`pause: true`, and surfaces approve/taste/ask decisions. A subagent that discovers missing authority
|
|
124
143
|
or an operator decision returns a blocker; the host pauses at the current state and presents it. The
|
|
@@ -129,6 +148,12 @@ subagent form above) to `.spur/run/<run-id>.log`, where `<id>` is the current YA
|
|
|
129
148
|
log start/failure and the ignored timeout value so an inline run remains auditable without
|
|
130
149
|
fabricating an `AgentRunTracedResult`.
|
|
131
150
|
|
|
151
|
+
Run-log stamps (task 0727): every appended line is prefixed with an **ISO-8601 UTC** timestamp
|
|
152
|
+
(`YYYY-MM-DDTHH:MM:SSZ`, e.g. `2026-08-31T17:51:11Z`); the exact-template provenance lines above
|
|
153
|
+
keep their exact content after the stamp prefix. This normalization is contractual:
|
|
154
|
+
**bare local-clock stamps are prohibited** — a hand-appended `[stage 12:31]` form mixes timezones
|
|
155
|
+
in one file and makes the run unauditable (task 0726 mixed both forms).
|
|
156
|
+
|
|
132
157
|
Transition guards are not advisory. Execute the declared guard exactly, in order, with the same
|
|
133
158
|
resolved variables and artifacts. `--no-lifecycle` remains bookkeeping only; the YAML's task checks,
|
|
134
159
|
verdict gate, record step, and done guard all remain authoritative.
|