@gobing-ai/spur 0.3.73 → 0.3.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +15 -4
  3. package/config/pipeline-budgets.json +4 -4
  4. package/config/rules/strict/runtime-boundaries.yaml +1 -1
  5. package/config/templates/AGENTS.md +5 -0
  6. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +2 -0
  7. package/config/workflows/basic.yaml +5 -2
  8. package/config/workflows/docs-pipeline.yaml +117 -55
  9. package/config/workflows/feature-dev.yaml +185 -121
  10. package/config/workflows/feature-lifecycle.yaml +1 -0
  11. package/config/workflows/history-anatomy.yaml +8 -4
  12. package/config/workflows/idea-pipeline.yaml +123 -43
  13. package/config/workflows/pr-review.yaml +31 -8
  14. package/config/workflows/task-lifecycle.yaml +1 -0
  15. package/config/workflows/task-pipeline.yaml +965 -905
  16. package/config/workflows/wayfinder-resolution.yaml +124 -90
  17. package/config/workflows/wrapup-pipeline.yaml +304 -107
  18. package/package.json +1 -1
  19. package/plugins/sp/agents/expert-spur.md +7 -4
  20. package/plugins/sp/commands/dev-idea.md +1 -1
  21. package/plugins/sp/plugin.json +1 -1
  22. package/plugins/sp/scripts/history-anatomy-cache.mjs +7 -1
  23. package/plugins/sp/scripts/history-anatomy-cache.ts +11 -2
  24. package/plugins/sp/scripts/pr-reviewing.mjs +5 -1
  25. package/plugins/sp/scripts/pr-reviewing.ts +7 -1
  26. package/plugins/sp/scripts/transition-shim-check.ts +6 -6
  27. package/plugins/sp/skills/pr-reviewing/SKILL.md +4 -1
  28. package/plugins/sp/skills/spur-cli/references/self.md +17 -3
  29. package/plugins/sp/skills/spur-cli/references/tasks.md +4 -2
  30. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +5 -5
  31. package/plugins/sp/skills/spur-cli/references/workflows.md +4 -4
  32. package/plugins/sp/skills/spur-dev/SKILL.md +17 -0
  33. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +23 -8
  34. package/plugins/sp/skills/spur-dev/references/dev-operations.md +1 -1
  35. package/plugins/sp/skills/spur-dev/references/execution-batch.md +3 -3
  36. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +5 -3
  37. package/plugins/sp/skills/wayfinder/SKILL.md +1 -1
  38. package/spur.js +2999 -1010
  39. package/web/_astro/{BoardApp.BYCNkMOn.js → BoardApp.BOW1815F.js} +101 -100
  40. package/web/_astro/BoardApp.C4zimv1U.js +1 -0
  41. package/web/_astro/{TaskDetail.CgUreSP2.js → TaskDetail.CX6C7dSJ.js} +1 -1
  42. package/web/_astro/{arc.BySSh34M.js → arc.CwSvH1ji.js} +1 -1
  43. package/web/_astro/{architectureDiagram-3BPJPVTR.DM46TS_h.js → architectureDiagram-3BPJPVTR.Dxne1RvP.js} +1 -1
  44. package/web/_astro/{blockDiagram-GPEHLZMM.tZhvNUHA.js → blockDiagram-GPEHLZMM.D9Fb14rC.js} +1 -1
  45. package/web/_astro/{c4Diagram-AAUBKEIU.PT4Or4Nf.js → c4Diagram-AAUBKEIU.DmvWSFvv.js} +1 -1
  46. package/web/_astro/channel.CQHdDVp9.js +1 -0
  47. package/web/_astro/{chunk-2J33WTMH.J9r0_Bbe.js → chunk-2J33WTMH.BCCktIrE.js} +1 -1
  48. package/web/_astro/{chunk-4BX2VUAB.hzyeIvhR.js → chunk-4BX2VUAB.BzCk9q27.js} +1 -1
  49. package/web/_astro/{chunk-55IACEB6.B0rO7qVh.js → chunk-55IACEB6.CZXVgTk4.js} +1 -1
  50. package/web/_astro/{chunk-727SXJPM.wE_Uk5D4.js → chunk-727SXJPM.CJL8UIpX.js} +1 -1
  51. package/web/_astro/{chunk-AQP2D5EJ.DqEEjQw7.js → chunk-AQP2D5EJ.Dz8MDEdw.js} +1 -1
  52. package/web/_astro/{chunk-FMBD7UC4.CDoD9sBX.js → chunk-FMBD7UC4.CkQeYYUw.js} +1 -1
  53. package/web/_astro/{chunk-ND2GUHAM.CtX5nF9P.js → chunk-ND2GUHAM.KiC1QgzH.js} +1 -1
  54. package/web/_astro/{chunk-QZHKN3VN.CK_EwfaT.js → chunk-QZHKN3VN.AJ08mw2e.js} +1 -1
  55. package/web/_astro/{classDiagram-4FO5ZUOK.DLt5a8Lh.js → classDiagram-4FO5ZUOK.DRWRDKzK.js} +1 -1
  56. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DLt5a8Lh.js → classDiagram-v2-Q7XG4LA2.DRWRDKzK.js} +1 -1
  57. package/web/_astro/{cose-bilkent-S5V4N54A.CMCWP49h.js → cose-bilkent-S5V4N54A.CavydfLP.js} +1 -1
  58. package/web/_astro/{cynefin-OW5HDTMX.HyXw_vdS.js → cynefin-OW5HDTMX.D_7o_a0B.js} +1 -1
  59. package/web/_astro/{dagre-BM42HDAG.BTuAzh01.js → dagre-BM42HDAG.LWk2dKgg.js} +1 -1
  60. package/web/_astro/{diagram-2AECGRRQ.D9dr9wfT.js → diagram-2AECGRRQ.82lbq6aB.js} +1 -1
  61. package/web/_astro/{diagram-5GNKFQAL.C4Rot0hj.js → diagram-5GNKFQAL.hyNL1pwY.js} +1 -1
  62. package/web/_astro/{diagram-KO2AKTUF.B_TK5uWC.js → diagram-KO2AKTUF.BaLqVf-b.js} +1 -1
  63. package/web/_astro/{diagram-LMA3HP47.JkXKK7CO.js → diagram-LMA3HP47.DQJxj0oz.js} +1 -1
  64. package/web/_astro/{diagram-OG6HWLK6.BzMN8Bd6.js → diagram-OG6HWLK6.DhlL8cG2.js} +1 -1
  65. package/web/_astro/{erDiagram-TEJ5UH35.DVZaWGUd.js → erDiagram-TEJ5UH35.HoTnXwkF.js} +1 -1
  66. package/web/_astro/{flowDiagram-I6XJVG4X.rjEiWUfR.js → flowDiagram-I6XJVG4X.DkFKlZIZ.js} +1 -1
  67. package/web/_astro/{ganttDiagram-6RSMTGT7.C_EgAarK.js → ganttDiagram-6RSMTGT7.9qPfOTDb.js} +1 -1
  68. package/web/_astro/{gitGraphDiagram-PVQCEYII.B-QQSDsK.js → gitGraphDiagram-PVQCEYII.B4G18Dwc.js} +1 -1
  69. package/web/_astro/index.Dcr_8fiK.css +1 -0
  70. package/web/_astro/{infoDiagram-5YYISTIA.DlWesz7T.js → infoDiagram-5YYISTIA.e3KJkXAM.js} +1 -1
  71. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BUMZOawi.js → ishikawaDiagram-YF4QCWOH.CY3yddhD.js} +1 -1
  72. package/web/_astro/{journeyDiagram-JHISSGLW.CWfkxfjY.js → journeyDiagram-JHISSGLW.DaAKIO1t.js} +1 -1
  73. package/web/_astro/{kanban-definition-UN3LZRKU.B-YpMwXf.js → kanban-definition-UN3LZRKU.DGkDddtc.js} +1 -1
  74. package/web/_astro/{linear.D7uqzENp.js → linear.DnDPcpd1.js} +1 -1
  75. package/web/_astro/{mermaid.core.CxrNppBD.js → mermaid.core.1uBmxa9t.js} +4 -4
  76. package/web/_astro/{mindmap-definition-RKZ34NQL.B4Qe7cM2.js → mindmap-definition-RKZ34NQL.XFJVayxw.js} +1 -1
  77. package/web/_astro/{pieDiagram-4H26LBE5.Ds-5j2ro.js → pieDiagram-4H26LBE5.BXm2OgHh.js} +1 -1
  78. package/web/_astro/{quadrantDiagram-W4KKPZXB.tBd38uNC.js → quadrantDiagram-W4KKPZXB.DgB2p5fc.js} +1 -1
  79. package/web/_astro/{requirementDiagram-4Y6WPE33.sFENkWl3.js → requirementDiagram-4Y6WPE33.jBR39bnS.js} +1 -1
  80. package/web/_astro/{sankeyDiagram-5OEKKPKP.BeB-Hk7C.js → sankeyDiagram-5OEKKPKP.N3u9OhE3.js} +1 -1
  81. package/web/_astro/{sequenceDiagram-3UESZ5HK.DnTeaSpx.js → sequenceDiagram-3UESZ5HK.CZhqZXNL.js} +1 -1
  82. package/web/_astro/{stateDiagram-AJRCARHV.B-8Jt5EJ.js → stateDiagram-AJRCARHV.DlNu1VEa.js} +1 -1
  83. package/web/_astro/{stateDiagram-v2-BHNVJYJU.Br7xoqMW.js → stateDiagram-v2-BHNVJYJU.BEqi8NQV.js} +1 -1
  84. package/web/_astro/{timeline-definition-PNZ67QCA.C-3WdOyi.js → timeline-definition-PNZ67QCA.BPcexclc.js} +1 -1
  85. package/web/_astro/{vennDiagram-CIIHVFJN.DCIs7Lc6.js → vennDiagram-CIIHVFJN.BZalxKGQ.js} +1 -1
  86. package/web/_astro/{wardleyDiagram-YWT4CUSO.rGAL-bbz.js → wardleyDiagram-YWT4CUSO.BqOljod9.js} +1 -1
  87. package/web/_astro/{xychartDiagram-2RQKCTM6.hkfQKiRl.js → xychartDiagram-2RQKCTM6.tkO4ppN5.js} +1 -1
  88. package/web/index.html +2 -2
  89. package/config/corpus-baseline.json +0 -1805
  90. package/config/workflow-composition-baseline.json +0 -615
  91. package/web/_astro/BoardApp.E12MFjOS.js +0 -1
  92. package/web/_astro/channel.5cYKr5cs.js +0 -1
  93. package/web/_astro/index.B5MTfe7k.css +0 -1
@@ -1,615 +0,0 @@
1
- {
2
- "schemaVersion": 1,
3
- "workflows": {
4
- "docs-pipeline": {
5
- "definition": "config/workflows/docs-pipeline.yaml",
6
- "terminalStates": ["done", "failed", "cancelled"],
7
- "modelQueries": ["draft", "verify"],
8
- "actions": {
9
- "precheck:onEnter:0": {
10
- "kind": "command.gate"
11
- },
12
- "precheck:onEnter:1": {
13
- "kind": "command.gate"
14
- },
15
- "precheck:onEnter:2": {
16
- "kind": "shell",
17
- "invocation": "if test \"$(cat .spur/run/$wbs-docs-precheck-task.status 2>/dev/null)\" = PASS &&\n test \"$(cat .spur/run/$wbs-docs-precheck-agent.status 2>/dev/null)\" = PASS;\nthen printf 'PASS\\n' > \".spur/run/$wbs-docs-precheck.status\"; else printf 'FAIL\\n' > \".spur/run/$wbs-docs-precheck.status\"; fi"
18
- },
19
- "precheck:onEnter:3": {
20
- "kind": "note"
21
- },
22
- "draft:onEnter:0": {
23
- "kind": "agent.run",
24
- "invocation": "/sp:dev-run --mode implement ${vars.wbs} --auto"
25
- },
26
- "draft:onEnter:1": {
27
- "kind": "shell",
28
- "invocation": "$spurBin task update $wbs wip --no-lifecycle"
29
- },
30
- "docs-review:onEnter:0": {
31
- "kind": "hitl.confirm"
32
- },
33
- "record:onEnter:0": {
34
- "kind": "shell",
35
- "invocation": "$spurBin task record $wbs --solution-from-diff --transition testing"
36
- },
37
- "done:onEnter:0": {
38
- "kind": "shell",
39
- "invocation": "$spurBin task update $wbs done --no-lifecycle"
40
- },
41
- "done:onEnter:1": {
42
- "kind": "note"
43
- },
44
- "verify:onEnter:0": {
45
- "kind": "shell",
46
- "invocation": "set -e; task_path=\"$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')\"; if [ -z \"$task_path\" ]; then echo \"docs-pipeline: task path did not resolve for wbs $wbs\" >&2; exit 1; fi; printf '%s' \"$task_path\" > \".spur/run/$wbs-docs-taskpath.txt\""
47
- },
48
- "verify:onEnter:1": {
49
- "kind": "file.read.into-var"
50
- },
51
- "verify:onEnter:2": {
52
- "kind": "proof.fingerprint",
53
- "invocation": "/sp:dev-verify ${vars.wbs} --auto --fix none --focus all"
54
- },
55
- "verify:onEnter:3": {
56
- "kind": "agent.run",
57
- "invocation": "/sp:dev-verify ${vars.wbs} --auto --fix none --focus all"
58
- },
59
- "verify:onEnter:4": {
60
- "kind": "shell",
61
- "invocation": "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
62
- },
63
- "verify:onEnter:5": {
64
- "kind": "shell",
65
- "invocation": "V=\".spur/run/$wbs-verdict.json\"; if [ -f \"$V\" ] && [ -n \"$proofDigest\" ]; then\n jq --arg d \"$proofDigest\" '. + {proof: {digest: $d, capturePoint: \"docs-verify-entry\"}}' \"$V\" > \"$V.tmp\" && mv \"$V.tmp\" \"$V\";\nfi; exit 0"
66
- },
67
- "verify:onEnter:6": {
68
- "kind": "run.artifact"
69
- },
70
- "verify:onEnter:7": {
71
- "kind": "proof.fingerprint",
72
- "invocation": "/sp:dev-verify ${vars.wbs} --auto --fix none --focus all"
73
- }
74
- }
75
- },
76
- "idea-pipeline": {
77
- "definition": "config/workflows/idea-pipeline.yaml",
78
- "terminalStates": ["handoff", "cancelled", "failed"],
79
- "modelQueries": ["discovery", "feature-create", "ac-generate", "system-design", "decompose"],
80
- "actions": {
81
- "start:onEnter:0": {
82
- "kind": "note"
83
- },
84
- "start:onEnter:1": {
85
- "kind": "doctor.probe"
86
- },
87
- "start:onEnter:2": {
88
- "kind": "shell",
89
- "invocation": "test -n \"$idea\" || printf 'FAIL\\n' > \".spur/run/$__runId-idea-precheck-doctor.status\""
90
- },
91
- "discovery:onEnter:0": {
92
- "kind": "agent.run",
93
- "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)."
94
- },
95
- "idea-eval:onEnter:0": {
96
- "kind": "hitl.confirm"
97
- },
98
- "feature-create:onEnter:0": {
99
- "kind": "agent.run",
100
- "invocation": "Create a feature for the idea: ${vars.idea}. Read .spur/run/${vars.__runId}-idea-eval-report.md if present for the enhanced idea and scores. Use 'spur feature create \"<name>\" --json' to create it. Write the feature id to .spur/run/${vars.__runId}-idea-feature-id.txt. If an existing feature is appropriate, use its id instead. Also write two body-only intent artifacts: .spur/run/${vars.__runId}-idea-goal.md with concise Goal intent only (a short statement of what the feature achieves; never task breakdowns, checklists, or how-to steps), and .spur/run/${vars.__runId}-idea-scope.md with explicit in-scope and out-of-scope boundary bullets."
101
- },
102
- "feature-create:onEnter:1": {
103
- "kind": "file.read.into-var"
104
- },
105
- "feature-create:onEnter:2": {
106
- "kind": "shell",
107
- "invocation": "mkdir -p .spur/run && test -s .spur/run/$__runId-idea-goal.md && $spurBin feature update \"$featureId\" --section Goal --from-file .spur/run/$__runId-idea-goal.md"
108
- },
109
- "feature-create:onEnter:3": {
110
- "kind": "shell",
111
- "invocation": "mkdir -p .spur/run && test -s .spur/run/$__runId-idea-scope.md && $spurBin feature update \"$featureId\" --section Scope --from-file .spur/run/$__runId-idea-scope.md"
112
- },
113
- "ac-generate:onEnter:0": {
114
- "kind": "shell",
115
- "invocation": "mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-ac-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-ac-retry-count; rm -f .spur/run/$__runId-idea-ac-content.md .spur/run/$__runId-idea-ac-done.txt"
116
- },
117
- "ac-generate:onEnter:1": {
118
- "kind": "agent.run",
119
- "invocation": "Generate acceptance criteria for feature ${vars.featureId}. Read the feature file and author R-numbered BDD Gherkin scenarios per ac-style-guide.md. Output only the complete Acceptance Criteria section body, including the gherkin code fence, with no surrounding commentary and no section heading."
120
- },
121
- "ac-generate:onEnter:2": {
122
- "kind": "shell",
123
- "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"
124
- },
125
- "feature-check:onEnter:0": {
126
- "kind": "hitl.confirm"
127
- },
128
- "system-design:onEnter:0": {
129
- "kind": "shell",
130
- "invocation": "mkdir -p .spur/run && REVIEW=\".spur/run/$__runId-idea-design-review.md\" && test -f \"$REVIEW\" || printf '## Proposed design\\n\\n## Operator feedback\\n\\n## Reconciliation\\n' > \"$REVIEW\""
131
- },
132
- "system-design:onEnter:1": {
133
- "kind": "agent.run",
134
- "invocation": "Run sp:sys-architecture for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and .spur/run/${vars.__runId}-idea-design-review.md. Produce ADR entries, architecture updates, and design satellites (docs/design/<slug>.md) following the constitution edit rules. Do not write task or feature corpus files directly. Design-review contract (.spur/run/${vars.__runId}-idea-design-review.md, fixed headings `## Proposed design`, `## Operator feedback`, `## Reconciliation`): on the first pass write the proposed design summary under `## Proposed design` and leave `## Operator feedback` empty; on retry with operator feedback present, revise the design/ADR artifacts, document the changes under `## Reconciliation`, and when the feedback invalidates an Acceptance Criteria scenario write the revised AC section body to a file and persist it via `$spurBin feature update \"$featureId\" --section \"Acceptance Criteria\" --from-file <file>` — never edit feature corpus files directly."
135
- },
136
- "system-design:onEnter:2": {
137
- "kind": "shell",
138
- "invocation": "REVIEW=\".spur/run/$__runId-idea-design-review.md\" && awk '/^## Proposed design/{f=1; next} /^## Operator feedback/{f=0} f' \"$REVIEW\" | grep -q '[^[:space:]]'"
139
- },
140
- "design-approval:onEnter:0": {
141
- "kind": "shell",
142
- "invocation": "mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-design-reject-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-design-reject-count"
143
- },
144
- "design-approval:onEnter:1": {
145
- "kind": "hitl.confirm"
146
- },
147
- "decompose:onEnter:0": {
148
- "kind": "shell",
149
- "invocation": "mkdir -p .spur/run && count=$(cat .spur/run/$__runId-idea-decompose-retry-count 2>/dev/null || echo 0); echo $((count + 1)) > .spur/run/$__runId-idea-decompose-retry-count; rm -f .spur/run/$__runId-idea-task-batch.json .spur/run/$__runId-idea-task-order.json .spur/run/$__runId-idea-batch-create.done .spur/run/$__runId-idea-batch-create.failed .spur/run/$__runId-idea-batch-create-result.json .spur/run/$__runId-idea-handoff.md"
150
- },
151
- "decompose:onEnter:1": {
152
- "kind": "agent.run",
153
- "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."
154
- },
155
- "decompose:onEnter:2": {
156
- "kind": "shell",
157
- "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"
158
- },
159
- "batch-create:onEnter:0": {
160
- "kind": "hitl.confirm"
161
- },
162
- "batch-create-run:onEnter:0": {
163
- "kind": "shell",
164
- "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"
165
- },
166
- "handoff-finalize:onEnter:0": {
167
- "kind": "shell",
168
- "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\""
169
- },
170
- "handoff:onEnter:0": {
171
- "kind": "note"
172
- },
173
- "handoff:onEnter:1": {
174
- "kind": "run.artifact"
175
- },
176
- "handoff:onEnter:2": {
177
- "kind": "shell",
178
- "invocation": "mkdir -p .spur/memory/sessions && echo \"checkpoint: idea-pipeline handoff feature=$featureId ts=$(date -u +%Y-%m-%dT%H:%M:%SZ)\" > .spur/memory/sessions/idea-checkpoint.md"
179
- }
180
- }
181
- },
182
- "task-pipeline": {
183
- "definition": "config/workflows/task-pipeline.yaml",
184
- "terminalStates": ["done", "failed", "cancelled"],
185
- "modelQueries": ["implement", "test-fix", "review", "verify"],
186
- "actions": {
187
- "precheck:onEnter:0": {
188
- "kind": "shell",
189
- "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"
190
- },
191
- "precheck:onEnter:1": {
192
- "kind": "note"
193
- },
194
- "precheck:onEnter:2": {
195
- "kind": "shell",
196
- "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 if ! $spurBin feature sync \"$FID\" --force 2>/dev/null; then\n if ! $spurBin feature update \"$FID\" active 2>/dev/null; then\n echo \"precheck: FAIL - feature reactivation $FID failed;\" >&2;\n echo \"precheck: feature sync + feature update both errored\" >&2;\n exit 1;\n fi;\n fi;\n fi;\nfi; exit 0"
197
- },
198
- "precheck:onEnter:3": {
199
- "kind": "shell",
200
- "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"
201
- },
202
- "precheck:onEnter:4": {
203
- "kind": "shell",
204
- "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"
205
- },
206
- "precheck:onEnter:5": {
207
- "kind": "shell",
208
- "invocation": "mkdir -p .spur/run .spur/memory && RUN_ID=\"$__runId\" && if [ -z \"$RUN_ID\" ]; then RUN_ID=\"pipeline-$wbs\"; fi && REASON_FILE=\".spur/run/$RUN_ID-route-reason.txt\" && if [ \"$mode\" = \"fast\" ]; then\n echo \"fast:evidence complete+consistent\" > \"$REASON_FILE\";\nelif [ -z \"$mode\" ]; then\n echo \"safety:standard verification\" > \"$REASON_FILE\";\nelif [ \"$mode\" = \"unknown\" ]; then\n echo \"safety:unknown evidence quality\" > \"$REASON_FILE\";\nelif [ \"$mode\" = \"conflict\" ]; then\n echo \"safety:conflicting evidence\" > \"$REASON_FILE\";\nelse\n echo \"safety:unrecognized evidence (mode=$mode)\" > \"$REASON_FILE\";\nfi && printf '%s %s %s\\n' \"$RUN_ID\" \"$wbs\" \"$(cat \"$REASON_FILE\")\" >> .spur/memory/task-pipeline-routes.log && exit 0"
209
- },
210
- "implement:onEnter:0": {
211
- "kind": "agent.run",
212
- "invocation": "/sp:dev-run --mode implement ${vars.wbs} --auto"
213
- },
214
- "implement:onEnter:1": {
215
- "kind": "shell",
216
- "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"
217
- },
218
- "implement:onEnter:2": {
219
- "kind": "shell",
220
- "invocation": "$formatCmd ; exit 0"
221
- },
222
- "test:onEnter:0": {
223
- "kind": "shell",
224
- "invocation": "mkdir -p .spur/run; $spurBin task path $wbs --json | jq -r \".path // .filePath // empty\" > \".spur/run/$wbs-taskpath.txt\"; task_path=\"$(cat \".spur/run/$wbs-taskpath.txt\")\"; if [ -z \"$task_path\" ]; then echo \"fail-closed proof chain (0751 R2): task path for $wbs did not resolve - the task spec cannot be folded into the proof digest\" >&2; exit 1; fi; sed -n \"s/^priority:[[:space:]]*//p\" \"$task_path\" | head -1 | tr -d \"[:space:]\" | tr \"[:lower:]\" \"[:upper:]\" > \".spur/run/$wbs-priority.txt\""
225
- },
226
- "test:onEnter:1": {
227
- "kind": "file.read.into-var"
228
- },
229
- "test:onEnter:2": {
230
- "kind": "file.read.into-var"
231
- },
232
- "test:onEnter:3": {
233
- "kind": "proof.fingerprint"
234
- },
235
- "test:onEnter:4": {
236
- "kind": "shell",
237
- "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 && printf 'proof-digest: %s\\n' \"$proofDigest\" >> \"$LOG_FILE\" && exit 0"
238
- },
239
- "test-fix:onEnter:0": {
240
- "kind": "shell",
241
- "invocation": "mkdir -p .spur/run && ATTEMPT_FILE=\".spur/run/$wbs-test-fix-attempt\" && n=$(cat \"$ATTEMPT_FILE\" 2>/dev/null || echo 0) && printf '%s\\n' \"$((n + 1))\" > \"$ATTEMPT_FILE\" && if [ -f \".spur/run/$wbs-verdict.json\" ]; then\n { echo '--- verify verdict (remediation input, task 0703 R4) ---';\n cat \".spur/run/$wbs-verdict.json\";\n } >> \".spur/run/$wbs-test-gate.log\";\nfi; exit 0"
242
- },
243
- "test-fix:onEnter:1": {
244
- "kind": "file.read.into-var"
245
- },
246
- "test-fix:onEnter:2": {
247
- "kind": "agent.run",
248
- "invocation": "/sp:dev-fixall \"${vars.qualityGateCmd}\" --gate-log .spur/run/${vars.wbs}-test-gate.log --findings \"${vars.gateFindings}\""
249
- },
250
- "test-recheck:onEnter:0": {
251
- "kind": "proof.fingerprint"
252
- },
253
- "test-recheck:onEnter:1": {
254
- "kind": "shell",
255
- "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 && printf 'proof-digest: %s\\n' \"$proofDigest\" >> \"$LOG_FILE\" && exit 0"
256
- },
257
- "review:onEnter:0": {
258
- "kind": "agent.run",
259
- "invocation": "/sp:dev-review ${vars.wbs} --auto"
260
- },
261
- "approve:onEnter:0": {
262
- "kind": "hitl.confirm"
263
- },
264
- "verify:onEnter:0": {
265
- "kind": "proof.fingerprint"
266
- },
267
- "verify:onEnter:1": {
268
- "kind": "agent.run",
269
- "invocation": "/sp:dev-verify ${vars.wbs} --auto --fix none --focus all"
270
- },
271
- "verify:onEnter:2": {
272
- "kind": "shell",
273
- "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\""
274
- },
275
- "verify:onEnter:3": {
276
- "kind": "shell",
277
- "invocation": "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
278
- },
279
- "verify:onEnter:4": {
280
- "kind": "shell",
281
- "invocation": "V=\".spur/run/$wbs-verdict.json\"; if [ -f \"$V\" ] && [ -n \"$proofDigest\" ]; then\n jq --arg d \"$proofDigest\" --arg r \"$__runId\" --arg dd \"$__definitionDigest\" --arg g \"$(cat .spur/run/$wbs-test-gate.status 2>/dev/null || echo UNKNOWN)\"\n '. + {proof: {digest: $d, runId: $r, definitionDigest: $dd, capturePoint: \"quality-gate-entry\", stages: {\n qualityGate: {status: $g, digest: $d},\n review: {status: \"completed\", digest: $d},\n verification: {status: .verdict, digest: $d}}}}\n | .checks += [{name: \"proof-input-digest\", status: \"pass\", evidence: $d}]'\n \"$V\" > \"$V.tmp\" && mv \"$V.tmp\" \"$V\";\nfi; exit 0"
282
- },
283
- "record:onEnter:0": {
284
- "kind": "proof.fingerprint"
285
- },
286
- "record:onEnter:1": {
287
- "kind": "shell",
288
- "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"
289
- },
290
- "record:onEnter:2": {
291
- "kind": "shell",
292
- "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"
293
- },
294
- "done:onEnter:0": {
295
- "kind": "shell",
296
- "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"
297
- },
298
- "done:onEnter:1": {
299
- "kind": "run.artifact"
300
- },
301
- "done:onEnter:2": {
302
- "kind": "note"
303
- },
304
- "done:onEnter:3": {
305
- "kind": "shell",
306
- "invocation": "mkdir -p .spur/memory/sessions; CP_TS=\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"; CP_COMMIT=\"$(git rev-parse HEAD 2>/dev/null || echo unknown)\"; CP_DIGEST=\"$(cut -d= -f2 .spur/run/$wbs-proofdigest.txt 2>/dev/null || echo '')\"; CP_RUN=\"$SPUR_RUN_ID\"; [ -z \"$CP_RUN\" ] && CP_RUN=\"$RUN_ID\"; printf '%s\\n' '---' 'schema_version: 1' \"session_id: $(date -u +%Y-%m-%d)-$wbs\" 'workflow: task-pipeline' \"run_id: $CP_RUN\" \"task_wbs: $wbs\" 'feature_id: \"\"' 'phase: done' 'status: done' 'last_gate: record' \"source_commit: $CP_COMMIT\" \"digest: $CP_DIGEST\" \"generated_at: $CP_TS\" \"updated_at: $CP_TS\" \"next_action: none - task $wbs complete (terminal; advisory only)\" 'artifacts:' ' - .spur/run/$wbs-verdict.json' ' - .spur/run/$wbs-test-gate.log' '---' '' '## Session Notes' '' \"Terminal checkpoint for task $wbs (task-pipeline done).\" 'Advisory only; the task file is authoritative.' > .spur/memory/sessions/$wbs-checkpoint.md; exit 0"
307
- }
308
- }
309
- },
310
- "wrapup-pipeline": {
311
- "definition": "config/workflows/wrapup-pipeline.yaml",
312
- "terminalStates": ["done", "skipped"],
313
- "modelQueries": ["doc-sync"],
314
- "actions": {
315
- "start:onEnter:0": {
316
- "kind": "note"
317
- },
318
- "task-resolve:onEnter:0": {
319
- "kind": "note"
320
- },
321
- "task-resolve:onEnter:1": {
322
- "kind": "shell",
323
- "invocation": "mkdir -p .spur/run .spur/memory && RUN_ID=\"$__runId\" && if [ -z \"$RUN_ID\" ]; then RUN_ID=\"wrapup\"; fi && REASON_FILE=\".spur/run/$RUN_ID-route-reason.txt\" && if [ \"$(echo \"$tasks\" | jq length 2>/dev/null || echo 0)\" -eq 0 ]; then\n echo \"skipped:empty task list\" > \"$REASON_FILE\";\nelif [ \"$mode\" = \"fast\" ]; then\n echo \"fast:evidence complete+consistent\" > \"$REASON_FILE\";\nelif [ -z \"$mode\" ]; then\n echo \"safety:missing evidence (mode empty)\" > \"$REASON_FILE\";\nelif [ \"$mode\" = \"unknown\" ]; then\n echo \"safety:unknown evidence quality\" > \"$REASON_FILE\";\nelif [ \"$mode\" = \"conflict\" ]; then\n echo \"safety:conflicting evidence\" > \"$REASON_FILE\";\nelse\n echo \"safety:unrecognized evidence (mode=$mode)\" > \"$REASON_FILE\";\nfi && printf '%s %s\\n' \"$RUN_ID\" \"$(cat \"$REASON_FILE\")\" >> .spur/memory/wrapup-routes.log && exit 0"
324
- },
325
- "doc-sync:onEnter:0": {
326
- "kind": "agent.run",
327
- "invocation": "First run Skill(skill=\"sp:doc-evolve\", args=\"wrapup tasks=${vars.tasks}\") to 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. THEN extract working learnings from tasks ${vars.tasks} — conventions, errors fixed, patterns, gotchas, grouped by date and task WBS — as raw markdown (no fences) and END your final message with that markdown (it is captured to .spur/run/wrapup-learnings.md)."
328
- },
329
- "doc-sync:onEnter:1": {
330
- "kind": "shell",
331
- "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"
332
- },
333
- "metrics-record:onEnter:0": {
334
- "kind": "shell",
335
- "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"
336
- },
337
- "feature-transition:onEnter:0": {
338
- "kind": "shell",
339
- "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"
340
- },
341
- "branch-cleanup:onEnter:0": {
342
- "kind": "hitl.confirm"
343
- },
344
- "done:onEnter:0": {
345
- "kind": "note"
346
- },
347
- "done:onEnter:1": {
348
- "kind": "shell",
349
- "invocation": "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"
350
- }
351
- }
352
- },
353
- "pr-review": {
354
- "definition": "config/workflows/pr-review.yaml",
355
- "terminalStates": ["done", "pending", "failed"],
356
- "modelQueries": [],
357
- "actions": {
358
- "preflight:onEnter:0": {
359
- "kind": "shell",
360
- "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"
361
- },
362
- "hygiene:onEnter:0": {
363
- "kind": "shell",
364
- "invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-hygiene.status\" && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" hygiene --base \"$baseBranch\" --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr-hygiene.json\"; rc=$?; set -e && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0"
365
- },
366
- "precheck:onEnter:0": {
367
- "kind": "shell",
368
- "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"
369
- },
370
- "push:onEnter:0": {
371
- "kind": "shell",
372
- "invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-push.status\" && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" push --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr-push.json\"; rc=$?; set -e && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0"
373
- },
374
- "ensure-pr:onEnter:0": {
375
- "kind": "shell",
376
- "invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-pr-ensure.status\" && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" ensure-pr --base \"$baseBranch\" --json --status-file \"$STATUS_FILE\" > \".spur/run/$__runId-pr.json\"; rc=$?; set -e && if [ ! -f \"$STATUS_FILE\" ]; then printf 'FAIL\\n' > \"$STATUS_FILE\"; fi && exit 0"
377
- },
378
- "request:onEnter:0": {
379
- "kind": "shell",
380
- "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"
381
- },
382
- "wait:onEnter:0": {
383
- "kind": "shell",
384
- "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"
385
- },
386
- "collect:onEnter:0": {
387
- "kind": "shell",
388
- "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"
389
- }
390
- }
391
- },
392
- "basic": {
393
- "definition": "config/workflows/basic.yaml",
394
- "terminalStates": ["done", "failed"],
395
- "modelQueries": ["fix"],
396
- "actions": {
397
- "implement:onEnter:0": {
398
- "kind": "note"
399
- },
400
- "implement:onEnter:1": {
401
- "kind": "shell",
402
- "invocation": "mkdir -p .spur/run && printf '0\\n' > .spur/run/$__runId-basic-fix-attempt"
403
- },
404
- "check:onEnter:0": {
405
- "kind": "shell",
406
- "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"
407
- },
408
- "fix:onEnter:0": {
409
- "kind": "shell",
410
- "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\""
411
- },
412
- "fix:onEnter:1": {
413
- "kind": "agent.run",
414
- "invocation": "/sp:dev-fixall \"${vars.qualityGateCmd}\""
415
- }
416
- }
417
- },
418
- "feature-dev": {
419
- "definition": "config/workflows/feature-dev.yaml",
420
- "terminalStates": ["done", "failed"],
421
- "modelQueries": ["brainstorm", "plan", "execute-tasks-auto", "execute-tasks"],
422
- "actions": {
423
- "precheck:onEnter:0": {
424
- "kind": "shell",
425
- "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"
426
- },
427
- "precheck:onEnter:1": {
428
- "kind": "note"
429
- },
430
- "brainstorm:onEnter:0": {
431
- "kind": "agent.run",
432
- "invocation": "/sp:dev-brainstorm --feature ${vars.featureId}"
433
- },
434
- "plan:onEnter:0": {
435
- "kind": "agent.run",
436
- "invocation": "/sp:dev-plan --feature ${vars.featureId}"
437
- },
438
- "execute-tasks-auto:onEnter:0": {
439
- "kind": "agent.run",
440
- "invocation": "/sp:dev-runall --feature ${vars.featureId} --auto"
441
- },
442
- "execute-tasks:onEnter:0": {
443
- "kind": "agent.run",
444
- "invocation": "/sp:dev-runall --feature ${vars.featureId}"
445
- },
446
- "integration-review:onEnter:0": {
447
- "kind": "shell",
448
- "invocation": "mkdir -p .spur/run && STATUS_FILE=\".spur/run/$__runId-integration-review.status\" && REQUEST_JSON=\".spur/run/$__runId-integration-review.json\" && PR_REQUEST_STATUS=\".spur/run/$__runId-integration-review-pr-request.status\" && set +e && bun \"$(superskill script path sp pr-reviewing.ts)\" request --base \"$baseBranch\" --json --status-file \"$PR_REQUEST_STATUS\" > \"$REQUEST_JSON\" 2>&1; rc=$?; set -e && if [ \"$rc\" -eq 0 ]; then\n printf 'PASS\\n' > \"$STATUS_FILE\";\nelse\n printf 'FAIL\\n' > \"$STATUS_FILE\";\nfi && exit 0"
449
- },
450
- "integration-review:onEnter:1": {
451
- "kind": "note"
452
- },
453
- "done:onEnter:0": {
454
- "kind": "shell",
455
- "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"
456
- }
457
- }
458
- },
459
- "wayfinder-resolution": {
460
- "definition": "config/workflows/wayfinder-resolution.yaml",
461
- "terminalStates": ["done", "failed", "cancelled"],
462
- "modelQueries": ["investigate", "verify"],
463
- "actions": {
464
- "precheck:onEnter:0": {
465
- "kind": "shell",
466
- "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"
467
- },
468
- "precheck:onEnter:1": {
469
- "kind": "note"
470
- },
471
- "collect:onEnter:0": {
472
- "kind": "shell",
473
- "invocation": "mkdir -p .spur/run/wayfinder && $spurBin task show $wbs --json > .spur/run/wayfinder/$wbs-input.json"
474
- },
475
- "collect:onEnter:1": {
476
- "kind": "shell",
477
- "invocation": "$spurBin workflow validate wayfinder-resolution.yaml > .spur/run/wayfinder/$wbs-workflow-check.txt"
478
- },
479
- "collect:onEnter:2": {
480
- "kind": "shell",
481
- "invocation": "$spurBin task check $wbs > .spur/run/wayfinder/$wbs-precheck.txt"
482
- },
483
- "investigate:onEnter:0": {
484
- "kind": "agent.run",
485
- "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)."
486
- },
487
- "verify:onEnter:0": {
488
- "kind": "shell",
489
- "invocation": "rm -f .spur/run/wayfinder/$wbs-resolution-verdict.txt"
490
- },
491
- "verify:onEnter:1": {
492
- "kind": "agent.run",
493
- "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."
494
- },
495
- "approve:onEnter:0": {
496
- "kind": "hitl.confirm"
497
- },
498
- "record:onEnter:0": {
499
- "kind": "shell",
500
- "invocation": "$spurBin task check $wbs ; exit 0"
501
- },
502
- "record:onEnter:1": {
503
- "kind": "shell",
504
- "invocation": "$spurBin task update $wbs done ; exit 0"
505
- },
506
- "done:onEnter:0": {
507
- "kind": "note"
508
- }
509
- }
510
- },
511
- "feature-lifecycle": {
512
- "definition": "config/workflows/feature-lifecycle.yaml",
513
- "terminalStates": ["cancelled"],
514
- "modelQueries": [],
515
- "actions": {}
516
- },
517
- "history-anatomy": {
518
- "definition": "config/workflows/history-anatomy.yaml",
519
- "terminalStates": ["published", "failed"],
520
- "modelQueries": ["resolve-scope", "enrich", "validate", "correct"],
521
- "actions": {
522
- "start:onEnter:0": {
523
- "kind": "shell",
524
- "invocation": "mkdir -p .spur/run; if [ -z \"$__runId\" ]; then __runId=$(uuidgen | tr 'A-Z' 'a-z' | cut -c1-8); fi; echo \"$__runId\" > .spur/run/history-anatomy-run.id"
525
- },
526
- "resolve-scope:onEnter:0": {
527
- "kind": "agent.run",
528
- "invocation": "Validate the mode arguments per sp:history-anatomy references/modes.md and write the normalized selector to .spur/run/${vars.__runId}-selector.json. mode=${vars.mode} date=${vars.date} since=${vars.since} until=${vars.until} focus=${vars.focus}. Reject conflicting arguments, naming the offending one; a rejection marks the run failed."
529
- },
530
- "resolve-paths:onEnter:0": {
531
- "kind": "shell",
532
- "invocation": "h=$(superskill script path sp history-anatomy-cache.mjs); node \"$h\" paths --helper \"$h\" --report-dir \"$reportDir\" --date \"$date\" --output \"$output\" --mode \"$mode\" --since \"$since\" --until \"$until\" --out .spur/run/$__runId-paths.txt"
533
- },
534
- "analyze:onEnter:0": {
535
- "kind": "shell",
536
- "invocation": "mkdir -p .spur/run; . .spur/run/$__runId-paths.txt; $spurBin history analyze --out .spur/run/$__runId-history-anatomy-current.json --since \"$HA_SINCE\" --until \"$HA_UNTIL\" --json"
537
- },
538
- "analyze:onEnter:1": {
539
- "kind": "shell",
540
- "invocation": ". .spur/run/$__runId-paths.txt; $spurBin history analyze --out .spur/run/$__runId-history-anatomy-baseline.json --since \"$HA_BASELINE_SINCE\" --until \"$HA_BASELINE_UNTIL\" --json 2>/dev/null || true"
541
- },
542
- "cache-probe:onEnter:0": {
543
- "kind": "shell",
544
- "invocation": ". .spur/run/$__runId-paths.txt; node \"$HA_HELPER\" probe --artifact .spur/run/$__runId-history-anatomy-current.json --baseline .spur/run/$__runId-history-anatomy-baseline.json --target \"$HA_TARGET\" --mode \"$mode\" --date \"$HA_DATE\" --recompute \"$recompute\" --executor \"$agent\" --skill-dir \"$HA_SKILL\" --contract \"$HA_SKILL/references/report-contract.md\" --workflow \"$workflowFile\" --contract-version \"$contractVersion\" --run-id \"$__runId\" --out .spur/run/$__runId-provenance.json > .spur/run/$__runId-cache-disposition.txt"
545
- },
546
- "render:onEnter:0": {
547
- "kind": "shell",
548
- "invocation": "mkdir -p .spur/run; $spurBin history report .spur/run/$__runId-history-anatomy-current.json --mode forensics > .spur/run/$__runId-history-anatomy-current.md; $spurBin history report .spur/run/$__runId-history-anatomy-baseline.json --mode forensics > .spur/run/$__runId-history-anatomy-baseline.md || true"
549
- },
550
- "enrich:onEnter:0": {
551
- "kind": "shell",
552
- "invocation": "git status --porcelain > .spur/run/$__runId-baseline-enrich.txt || true"
553
- },
554
- "enrich:onEnter:1": {
555
- "kind": "agent.run",
556
- "invocation": "Run sp:history-anatomy enrich: given .spur/run/${vars.__runId}-history-anatomy-current.json and -baseline.json, author the model half of the report (Baseline comparison, Findings, Recurrence ledger, Remediation options, Performance analysis, Workflow/process improvements, Positive patterns) to .spur/run/${vars.__runId}-candidate.md per references/report-contract.md. This operation never launches a workflow."
557
- },
558
- "enrich:onEnter:2": {
559
- "kind": "shell",
560
- "invocation": "node \"$(superskill script path sp history-anatomy-cache.mjs)\" assert-clean --baseline .spur/run/$__runId-baseline-enrich.txt --expect .spur/run/$__runId-candidate.md"
561
- },
562
- "structure-gate:onEnter:0": {
563
- "kind": "shell",
564
- "invocation": "node \"$(superskill script path sp history-anatomy-cache.mjs)\" check \\\n .spur/run/$__runId-candidate.md > .spur/run/$__runId-structure-gate.txt 2>&1 || true"
565
- },
566
- "validate:onEnter:0": {
567
- "kind": "shell",
568
- "invocation": "git status --porcelain > .spur/run/$__runId-baseline-validate.txt || true"
569
- },
570
- "validate:onEnter:1": {
571
- "kind": "agent.run",
572
- "invocation": "Run sp:history-anatomy validate: independently check .spur/run/${vars.__runId}-candidate.md against the two artifacts per references/operations.md; write Verdict: PASS or FAIL to .spur/run/${vars.__runId}-validation.txt. This operation never launches a workflow."
573
- },
574
- "validate:onEnter:2": {
575
- "kind": "shell",
576
- "invocation": "node \"$(superskill script path sp history-anatomy-cache.mjs)\" assert-clean --baseline .spur/run/$__runId-baseline-validate.txt --expect .spur/run/$__runId-validation.txt"
577
- },
578
- "correct:onEnter:0": {
579
- "kind": "shell",
580
- "invocation": "n=$(cat .spur/run/$__runId-correction-count 2>/dev/null || echo 0) && printf '%s\\n' \"$((n + 1))\" > .spur/run/$__runId-correction-count"
581
- },
582
- "correct:onEnter:1": {
583
- "kind": "shell",
584
- "invocation": "git status --porcelain > .spur/run/$__runId-baseline-correct.txt || true"
585
- },
586
- "correct:onEnter:2": {
587
- "kind": "agent.run",
588
- "invocation": "Repair .spur/run/${vars.__runId}-candidate.md in place. Read its structure-gate findings, optional validation notes, references/report-contract.md, and the references/operations.md validate rubric; fix every violation. Use a backticked `.md`/`.ts`/`.json` path or a `path:line` anchor, never `current #/...`; remove placeholders and empty rows; restore the twelve-section order; ensure every problem and positive finding carries the full field set; verify every quantitative claim (counts, durations, percentages, distributions) and every current/baseline full-digest match against .spur/run/${vars.__runId}-history-anatomy-current.json and -baseline.json. Overwrite only the candidate; never launch a workflow."
589
- },
590
- "correct:onEnter:3": {
591
- "kind": "shell",
592
- "invocation": "node \"$(superskill script path sp history-anatomy-cache.mjs)\" assert-clean --baseline .spur/run/$__runId-baseline-correct.txt --expect .spur/run/$__runId-candidate.md"
593
- },
594
- "refresh-provenance:onEnter:0": {
595
- "kind": "shell",
596
- "invocation": ". .spur/run/$__runId-paths.txt; node \"$HA_HELPER\" refresh --report \"$HA_TARGET\" --out .spur/run/$__runId-publishable.md --disposition hit"
597
- },
598
- "stamp:onEnter:0": {
599
- "kind": "shell",
600
- "invocation": ". .spur/run/$__runId-paths.txt; node \"$HA_HELPER\" stamp --candidate .spur/run/$__runId-candidate.md --provenance .spur/run/$__runId-provenance.json --out .spur/run/$__runId-publishable.md"
601
- },
602
- "publish:onEnter:0": {
603
- "kind": "shell",
604
- "invocation": ". .spur/run/$__runId-paths.txt; mkdir -p \"$(dirname \"$HA_TARGET\")\"; node \"$HA_HELPER\" publish .spur/run/$__runId-publishable.md \"$HA_TARGET\""
605
- }
606
- }
607
- },
608
- "task-lifecycle": {
609
- "definition": "config/workflows/task-lifecycle.yaml",
610
- "terminalStates": ["cancelled"],
611
- "modelQueries": [],
612
- "actions": {}
613
- }
614
- }
615
- }