@gobing-ai/spur 0.3.80 → 0.3.82

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 (177) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/config.example.yaml +31 -18
  3. package/config/config.global.yaml +13 -11
  4. package/config/pipeline-budgets.json +34 -2
  5. package/config/plugin-scripts.json +25 -0
  6. package/config/rules/boundary/config-loading-ownership.yaml +0 -3
  7. package/config/rules/boundary/dao-boundary.yaml +4 -17
  8. package/config/rules/boundary/planning-folder-hardcode.yaml +0 -1
  9. package/config/rules/boundary/sp-no-vendor-refs.yaml +3 -2
  10. package/config/rules/boundary/sp-runtime-path.yaml +3 -14
  11. package/config/rules/quality/coverage-gate.yaml +3 -14
  12. package/config/rules/quality/tsdoc-exports.yaml +4 -7
  13. package/config/rules/strict/http-boundaries.yaml +5 -8
  14. package/config/rules/strict/runtime-boundaries.yaml +1 -5
  15. package/config/rules/structure/protected-files.yaml +9 -3
  16. package/config/rules/structure/test-focus-skip.yaml +0 -2
  17. package/config/rules/structure/test-location.yaml +0 -5
  18. package/config/rules/surface/check-cli-surface.yaml +3 -2
  19. package/config/rules/typescript/bun-tooling.yaml +5 -7
  20. package/config/rules/typescript/guarded-happy-dom-register.yaml +0 -2
  21. package/config/rules/typescript/happy-dom-teardown.yaml +0 -2
  22. package/config/rules/typescript/no-biome-suppressions.yaml +0 -2
  23. package/config/rules/typescript/no-debugger.yaml +0 -2
  24. package/config/rules/typescript/no-eslint-suppressions.yaml +0 -4
  25. package/config/rules/typescript/no-leaky-module-mocks.yaml +6 -13
  26. package/config/rules/typescript/no-module-scope-import-calls.yaml +0 -2
  27. package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +0 -3
  28. package/config/rules/typescript/no-unmocked-module-eval-side-effects.yaml +0 -3
  29. package/config/rules/typescript/output-boundaries.yaml +0 -3
  30. package/config/rules/typescript/prefer-accessible-role-for-button-queries.yaml +0 -3
  31. package/config/rules/ui/ui-import-boundary.yaml +1 -5
  32. package/config/templates/docs/99_PROJECT_CONSTITUTION.md +75 -13
  33. package/config/transition-shims.json +0 -7
  34. package/config/workflows/basic.yaml +4 -0
  35. package/config/workflows/docs-pipeline.yaml +13 -14
  36. package/config/workflows/feature-dev.yaml +20 -65
  37. package/config/workflows/history-anatomy.yaml +22 -1
  38. package/config/workflows/idea-pipeline.yaml +53 -97
  39. package/config/workflows/pr-review.yaml +21 -33
  40. package/config/workflows/task-pipeline.yaml +87 -330
  41. package/config/workflows/wayfinder-resolution.yaml +12 -26
  42. package/config/workflows/wrapup-pipeline.yaml +48 -189
  43. package/package.json +9 -9
  44. package/plugins/sp/README.md +12 -3
  45. package/plugins/sp/agents/expert-spur.md +41 -19
  46. package/plugins/sp/lib/idea-handoff.generated.d.mts +17 -0
  47. package/plugins/sp/lib/idea-handoff.generated.mjs +1301 -0
  48. package/plugins/sp/plugin.json +1 -1
  49. package/plugins/sp/scripts/feature-dev-precheck.mjs +146 -0
  50. package/plugins/sp/scripts/feature-dev-precheck.ts +238 -0
  51. package/plugins/sp/scripts/idea-handoff.mjs +27 -0
  52. package/plugins/sp/scripts/idea-handoff.ts +44 -0
  53. package/plugins/sp/scripts/inline-run-setup.ts +69 -1
  54. package/plugins/sp/scripts/quality-gate.mjs +216 -0
  55. package/plugins/sp/scripts/quality-gate.ts +287 -0
  56. package/plugins/sp/scripts/surface-drift-inventory.ts +0 -2
  57. package/plugins/sp/scripts/task-size-precheck.ts +44 -16
  58. package/plugins/sp/scripts/verify-answer-lint.ts +17 -1
  59. package/plugins/sp/scripts/workflow-step-profile.mjs +319 -0
  60. package/plugins/sp/scripts/workflow-step-profile.ts +456 -0
  61. package/plugins/sp/scripts/wrapup-steps.mjs +350 -0
  62. package/plugins/sp/scripts/wrapup-steps.ts +466 -0
  63. package/plugins/sp/skills/code-review/references/review-lenses.md +3 -0
  64. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
  65. package/plugins/sp/skills/spec-decomposition/references/decomposition.md +29 -0
  66. package/plugins/sp/skills/spur-cli/SKILL.md +4 -8
  67. package/plugins/sp/skills/spur-cli/references/agent.md +44 -69
  68. package/plugins/sp/skills/spur-cli/references/message.md +30 -3
  69. package/plugins/sp/skills/spur-cli/references/projects.md +25 -1
  70. package/plugins/sp/skills/spur-cli/references/self.md +6 -5
  71. package/plugins/sp/skills/spur-cli/references/serve.md +8 -7
  72. package/plugins/sp/skills/spur-cli/references/tasks.md +1 -1
  73. package/plugins/sp/skills/spur-cli/references/workflows/operations.md +6 -3
  74. package/plugins/sp/skills/spur-cli/references/workflows/workflow-fit-and-tuning.md +57 -18
  75. package/plugins/sp/skills/spur-composer/SKILL.md +145 -0
  76. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +10 -0
  77. package/plugins/sp/skills/spur-dev/references/execution-batch.md +1 -1
  78. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +4 -6
  79. package/plugins/sp/skills/spur-dev/references/glossary.md +1 -1
  80. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +11 -1
  81. package/plugins/sp/skills/spur-dev/references/planning-workflow.md +24 -0
  82. package/plugins/sp/skills/spur-doctor/SKILL.md +138 -0
  83. package/plugins/sp/skills/taste-refactoring-api/README.md +43 -0
  84. package/plugins/sp/skills/taste-refactoring-api/SKILL.md +334 -0
  85. package/plugins/sp/skills/taste-refactoring-api/checklists/daily-api-review.md +71 -0
  86. package/plugins/sp/skills/taste-refactoring-api/examples/refactor-example.md +72 -0
  87. package/plugins/sp/skills/taste-refactoring-api/examples/review-template.md +93 -0
  88. package/plugins/sp/skills/taste-refactoring-api/references/api-refactoring-playbook.md +253 -0
  89. package/plugins/sp/skills/taste-refactoring-api/references/protocol-modes.md +79 -0
  90. package/plugins/sp/skills/taste-refactoring-api/references/research-basis.md +58 -0
  91. package/plugins/sp/skills/taste-refactoring-architect/README.md +26 -0
  92. package/plugins/sp/skills/taste-refactoring-architect/SKILL.md +471 -0
  93. package/plugins/sp/skills/taste-refactoring-architect/checklists/daily-architecture-review.md +48 -0
  94. package/plugins/sp/skills/taste-refactoring-architect/examples/refactor-example.md +55 -0
  95. package/plugins/sp/skills/taste-refactoring-architect/examples/review-template.md +51 -0
  96. package/plugins/sp/skills/taste-refactoring-architect/references/architecture-refactoring-playbook.md +173 -0
  97. package/plugins/sp/skills/taste-refactoring-architect/references/research-basis.md +28 -0
  98. package/plugins/sp/skills/taste-refactoring-tests/README.md +28 -0
  99. package/plugins/sp/skills/taste-refactoring-tests/SKILL.md +482 -0
  100. package/plugins/sp/skills/taste-refactoring-tests/checklists/daily-test-review.md +39 -0
  101. package/plugins/sp/skills/taste-refactoring-tests/examples/refactor-example.md +85 -0
  102. package/plugins/sp/skills/taste-refactoring-tests/examples/review-template.md +59 -0
  103. package/plugins/sp/skills/taste-refactoring-tests/references/research-basis.md +47 -0
  104. package/plugins/sp/skills/taste-refactoring-tests/references/test-refactoring-playbook.md +222 -0
  105. package/plugins/sp/skills/taste-refactoring-ui/README.md +12 -0
  106. package/plugins/sp/skills/taste-refactoring-ui/SKILL.md +290 -0
  107. package/plugins/sp/skills/taste-refactoring-ui/checklists/daily-ui-review.md +72 -0
  108. package/plugins/sp/skills/taste-refactoring-ui/examples/review-template.md +51 -0
  109. package/plugins/sp/skills/taste-refactoring-ui/references/refactoring-ui-playbook.md +170 -0
  110. package/plugins/sp/skills/wayfinder/SKILL.md +2 -2
  111. package/plugins/sp/skills/wayfinder/references/pipeline-resolution.md +30 -0
  112. package/schemas/spur-config.schema.json +105 -85
  113. package/spur.js +44616 -43320
  114. package/web/_astro/BoardApp.D-WlxiN2.js +1 -0
  115. package/web/_astro/{BoardApp.CHQ1lycZ.js → BoardApp.D8bM9pKL.js} +97 -95
  116. package/web/_astro/{TaskDetail.GKfQJ60c.js → TaskDetail.BPRqgVUE.js} +1 -1
  117. package/web/_astro/{arc.DWEtA3Tx.js → arc.BPrPES3z.js} +1 -1
  118. package/web/_astro/{architectureDiagram-3BPJPVTR.DB42oWmP.js → architectureDiagram-3BPJPVTR.qX_7q02P.js} +1 -1
  119. package/web/_astro/{blockDiagram-GPEHLZMM.rhv-zNQV.js → blockDiagram-GPEHLZMM.CUZfj5V7.js} +1 -1
  120. package/web/_astro/{c4Diagram-AAUBKEIU.Ci4-4VvY.js → c4Diagram-AAUBKEIU.CTaOr8hH.js} +1 -1
  121. package/web/_astro/channel.DGZaFHZx.js +1 -0
  122. package/web/_astro/{chunk-2J33WTMH.Cc9veUgf.js → chunk-2J33WTMH.Dt-9wf3h.js} +1 -1
  123. package/web/_astro/{chunk-4BX2VUAB.Bec9c4eI.js → chunk-4BX2VUAB.CTC2sdoN.js} +1 -1
  124. package/web/_astro/{chunk-55IACEB6.DoV8S1iB.js → chunk-55IACEB6.DQcxt2_g.js} +1 -1
  125. package/web/_astro/{chunk-727SXJPM.DwR-Qlyj.js → chunk-727SXJPM.DXFPSn-a.js} +1 -1
  126. package/web/_astro/{chunk-AQP2D5EJ.ND_a81WY.js → chunk-AQP2D5EJ.BCx3U4bT.js} +1 -1
  127. package/web/_astro/{chunk-FMBD7UC4.Wv_jwG48.js → chunk-FMBD7UC4.DL2tJkdO.js} +1 -1
  128. package/web/_astro/{chunk-ND2GUHAM.CXKXCMmp.js → chunk-ND2GUHAM.DZyflMro.js} +1 -1
  129. package/web/_astro/{chunk-QZHKN3VN.nkaoNYQq.js → chunk-QZHKN3VN.CUI2mT09.js} +1 -1
  130. package/web/_astro/{classDiagram-4FO5ZUOK.cMQcVlQu.js → classDiagram-4FO5ZUOK.g4rX4Fr1.js} +1 -1
  131. package/web/_astro/{classDiagram-v2-Q7XG4LA2.cMQcVlQu.js → classDiagram-v2-Q7XG4LA2.g4rX4Fr1.js} +1 -1
  132. package/web/_astro/{cose-bilkent-S5V4N54A.OaDJ7Mr2.js → cose-bilkent-S5V4N54A.CzWJLqp0.js} +1 -1
  133. package/web/_astro/{cynefin-OW5HDTMX.Chi8IphF.js → cynefin-OW5HDTMX.WgsvQeCR.js} +1 -1
  134. package/web/_astro/{cytoscape.esm.DzSz-X2X.js → cytoscape.esm.BB4DxJjf.js} +1 -1
  135. package/web/_astro/{dagre-BM42HDAG.CzK2t_Fp.js → dagre-BM42HDAG.Dzv6ngql.js} +1 -1
  136. package/web/_astro/{diagram-2AECGRRQ.DRvxlVS7.js → diagram-2AECGRRQ.CpJ4a9rU.js} +1 -1
  137. package/web/_astro/{diagram-5GNKFQAL.CnYvNdwA.js → diagram-5GNKFQAL.CzPlF_dq.js} +1 -1
  138. package/web/_astro/{diagram-KO2AKTUF.CpLpMw5R.js → diagram-KO2AKTUF.TAkZNTcQ.js} +1 -1
  139. package/web/_astro/{diagram-LMA3HP47.JTb78qUA.js → diagram-LMA3HP47.uCjoKSag.js} +1 -1
  140. package/web/_astro/{diagram-OG6HWLK6.Bk-1jDIb.js → diagram-OG6HWLK6.eMplIjoK.js} +1 -1
  141. package/web/_astro/{erDiagram-TEJ5UH35.D8hN9GZq.js → erDiagram-TEJ5UH35.Bf7zoXGz.js} +1 -1
  142. package/web/_astro/{flowDiagram-I6XJVG4X.-6zQr6m5.js → flowDiagram-I6XJVG4X.B_bHj3gN.js} +1 -1
  143. package/web/_astro/{ganttDiagram-6RSMTGT7.DboLQ9ca.js → ganttDiagram-6RSMTGT7.BasrHRMj.js} +1 -1
  144. package/web/_astro/{gitGraphDiagram-PVQCEYII.4tYvJKGR.js → gitGraphDiagram-PVQCEYII.C6iphq1x.js} +1 -1
  145. package/web/_astro/index.DayyIngm.css +1 -0
  146. package/web/_astro/{infoDiagram-5YYISTIA.Bd9rXpsB.js → infoDiagram-5YYISTIA.HXmDMhW4.js} +1 -1
  147. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CvMoaf67.js → ishikawaDiagram-YF4QCWOH.BSmW8NiU.js} +1 -1
  148. package/web/_astro/{journeyDiagram-JHISSGLW.Ccy1CA7y.js → journeyDiagram-JHISSGLW.DEQow5fo.js} +1 -1
  149. package/web/_astro/{kanban-definition-UN3LZRKU.0MaMqHNS.js → kanban-definition-UN3LZRKU.IVm9cTdc.js} +1 -1
  150. package/web/_astro/{linear.CHXgcIbN.js → linear.CrsM73_9.js} +1 -1
  151. package/web/_astro/{mermaid.core.Ca-kcelG.js → mermaid.core.CfBeDJls.js} +6 -6
  152. package/web/_astro/{mindmap-definition-RKZ34NQL.BUIDlHa0.js → mindmap-definition-RKZ34NQL.C3j60Y-0.js} +1 -1
  153. package/web/_astro/ordinal.BYWQX77i.js +1 -0
  154. package/web/_astro/{pieDiagram-4H26LBE5.2dX3CU1s.js → pieDiagram-4H26LBE5.B-aCMeEA.js} +1 -1
  155. package/web/_astro/{quadrantDiagram-W4KKPZXB.B3LBlRiv.js → quadrantDiagram-W4KKPZXB.Cib965yq.js} +1 -1
  156. package/web/_astro/{requirementDiagram-4Y6WPE33.X12I2uNx.js → requirementDiagram-4Y6WPE33.D61cS4O-.js} +1 -1
  157. package/web/_astro/{sankeyDiagram-5OEKKPKP.BXohIHqx.js → sankeyDiagram-5OEKKPKP.GKF2qVPy.js} +1 -1
  158. package/web/_astro/{sequenceDiagram-3UESZ5HK.C37ZIUzg.js → sequenceDiagram-3UESZ5HK.DZnq8F2h.js} +1 -1
  159. package/web/_astro/{stateDiagram-AJRCARHV.BRgz317z.js → stateDiagram-AJRCARHV.DXUFmdgM.js} +1 -1
  160. package/web/_astro/{stateDiagram-v2-BHNVJYJU.7VYSXN9-.js → stateDiagram-v2-BHNVJYJU.BtHmhLEz.js} +1 -1
  161. package/web/_astro/{timeline-definition-PNZ67QCA.BVNz_HiN.js → timeline-definition-PNZ67QCA.Cy-WW2ln.js} +1 -1
  162. package/web/_astro/{vennDiagram-CIIHVFJN.CHVDkPX4.js → vennDiagram-CIIHVFJN.SLp5b9KI.js} +1 -1
  163. package/web/_astro/{wardleyDiagram-YWT4CUSO.EQQ_qT9v.js → wardleyDiagram-YWT4CUSO.Bww45mWV.js} +1 -1
  164. package/web/_astro/{xychartDiagram-2RQKCTM6.DrAT9WoP.js → xychartDiagram-2RQKCTM6.DR4swI6a.js} +1 -1
  165. package/web/apple-touch-icon.png +0 -0
  166. package/web/favicon.ico +0 -0
  167. package/web/favicon.svg +17 -4
  168. package/web/icon-192.png +0 -0
  169. package/web/icon-512.png +0 -0
  170. package/web/index.html +2 -2
  171. package/web/site.webmanifest +31 -0
  172. package/web/spur_logo.svg +1 -0
  173. package/plugins/sp/skills/spur-cli/references/team.md +0 -145
  174. package/web/_astro/BoardApp.DV9kx0wo.js +0 -1
  175. package/web/_astro/channel.BAI6xLeV.js +0 -1
  176. package/web/_astro/index.Dcr_8fiK.css +0 -1
  177. package/web/_astro/ordinal.DBvzRdQf.js +0 -1
@@ -60,18 +60,10 @@ states:
60
60
  onEnter:
61
61
  - kind: shell
62
62
  options:
63
+ # (e) 0825: 8 lines — task check + show capture the investigate input bundle in the
64
+ # same soft probe; show must stay verbatim (it IS the bundle downstream reads).
63
65
  command: >-
64
- mkdir -p .spur/run &&
65
- set +e &&
66
- $spurBin task check $wbs; check_rc=$?;
67
- $spurBin task show $wbs --json > .spur/run/$__runId-wayfinder-input.json; show_rc=$?;
68
- set -e &&
69
- if [ "$check_rc" -eq 0 ] && [ "$show_rc" -eq 0 ]; then
70
- printf 'PASS\n' > .spur/run/$__runId-wayfinder-precheck.status;
71
- else
72
- printf 'FAIL\n' > .spur/run/$__runId-wayfinder-precheck.status;
73
- fi &&
74
- exit 0
66
+ mkdir -p .spur/run; $spurBin task check $wbs; c=$?; $spurBin task show $wbs --json > .spur/run/$__runId-wayfinder-input.json && [ "$c" -eq 0 ] && printf 'PASS\n' > .spur/run/$__runId-wayfinder-precheck.status || printf 'FAIL\n' > .spur/run/$__runId-wayfinder-precheck.status; exit 0
75
67
  - kind: note
76
68
  options:
77
69
  message: "Wayfinder resolution started for task ${vars.wbs} (mode=${vars.resolutionMode}, evidence=${vars.evidencePolicy}); no code implementation pipeline is permitted."
@@ -86,22 +78,10 @@ states:
86
78
  # Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
87
79
  role: planner
88
80
  mode: text
81
+ # (e) 0825: bounded reference-reading prompt (260 chars) — the authoring contract
82
+ # lives in the skill reference sp:wayfinder references/pipeline-resolution.md.
89
83
  input: >-
90
- Resolve task ${vars.wbs} in resolution mode ${vars.resolutionMode} using the prepared
91
- local bundle `.spur/run/${vars.__runId}-wayfinder-input.json` and only local shell/file
92
- reads plus the `spur` CLI; do not use network search or MCP tools. Do not
93
- invoke task-pipeline.yaml, /sp:dev-run, /sp:dev-runall, or code implementation.
94
- Read the task file, map its existing Requirements and Acceptance Criteria to the
95
- existing Design/Plan/References, then author concise Solution, Testing, and Review
96
- sections through `spur task update --section --from-file`. Preserve the research
97
- boundary. Finish with a short summary and stop. Evidence policy: ${vars.evidencePolicy}.
98
-
99
- Evidence-anchor contract (mandatory — 0299 R1 line-anchor rule): when the task's
100
- evidence lives in a shared evidence file, anchor every Solution/Testing `file:line`
101
- citation truthfully — resolve the section's real start line with
102
- `grep -n "^## <wbs> " <evidence-file>` and cite that exact line. NEVER copy a line
103
- number from another ticket's example; each WBS anchors to its own section. Before
104
- finishing, re-read each cited line and confirm it names the requirement's subject.
84
+ Resolve wayfinder task ${vars.wbs} per the sp:wayfinder skill (references/pipeline-resolution.md). Mode ${vars.resolutionMode}; evidence policy ${vars.evidencePolicy}; bundle .spur/run/${vars.__runId}-wayfinder-input.json. Finish with a short summary and stop.
105
85
  answerFile: .spur/run/${vars.__runId}-wayfinder-answer.md
106
86
  expectFile: .spur/run/${vars.__runId}-wayfinder-answer.md
107
87
  timeoutMs: ${vars.stepTimeoutMs}
@@ -121,6 +101,8 @@ states:
121
101
  # proof digest via taskFile; a silent miss would degrade the proof to tree-only.
122
102
  - kind: shell
123
103
  options:
104
+ # (e) 0825: 6 lines — fail-closed lookup feeds every downstream artifact name;
105
+ # folding a silent miss into the digest would fake tree coverage.
124
106
  command: >-
125
107
  set -e;
126
108
  task_path="$($spurBin task path $wbs --json | jq -r '.path // .filePath // empty')";
@@ -155,6 +137,8 @@ states:
155
137
  command: "$spurBin task verdict $wbs --from-answer .spur/run/$__runId-wayfinder-verify-answer.txt"
156
138
  - kind: shell
157
139
  options:
140
+ # (e) 0825: 6 lines — stamp shell freezes the proof digest + capture point into the
141
+ # verdict before it is registered; the closing guard compares against this stamp.
158
142
  command: >-
159
143
  V=".spur/run/$wbs-verdict.json";
160
144
  if [ -f "$V" ] && [ -n "$proofDigest" ]; then
@@ -246,6 +230,8 @@ transitions:
246
230
  guard:
247
231
  kind: shell
248
232
  options:
233
+ # (e) 0825: 5-line guard — approval + digest-bracket + verdict PASS must all hold
234
+ # before record; dropping any operand would let a stale proof auto-record.
249
235
  command: >-
250
236
  test "$approval" = auto &&
251
237
  test -n "$proofDigest" &&
@@ -130,91 +130,36 @@ states:
130
130
  - kind: note
131
131
  options:
132
132
  message: "Resolving task list: ${vars.tasks} (proportional mode: ${vars.mode})"
133
- # 0770/0783 truthful input: parse and validate vars.tasks exactly once,
134
- # here and only here. Malformed JSON, a non-array, a non-string entry,
135
- # whitespace or a non-canonical WBS (anything but four digits), a missing
136
- # __runId, an unresolved task, a failed or malformed lookup, or a task
137
- # not at a completed status records FAIL (routed to `failed` by the first
138
- # declared edge) and is never re-interpreted as an empty list by a later
139
- # sibling guard. The normalized, deduplicated list is persisted
140
- # run-scoped; the route writer, metrics, prompts, notes and guards only
141
- # ever read that capture.
133
+ # (d) wrapup-steps.ts resolve owns task capture and status resolution (0824);
134
+ # the wrapper only locates it (monorepo first, registered twin under node) and
135
+ # fails closed, writing FAIL for the failed defense edge — never PASS.
142
136
  - kind: shell
143
137
  options:
144
138
  command: >-
145
139
  mkdir -p .spur/run &&
146
- RUN_ID="$__runId" &&
147
- if [ -z "$RUN_ID" ]; then
148
- echo "task-resolve: __runId is empty — refusing the legacy fixed-path fallback" >&2;
149
- exit 1;
150
- fi &&
151
- REASON_FILE=".spur/run/$RUN_ID-route-reason.txt" &&
152
- STATUS_FILE=".spur/run/$RUN_ID-wrapup-resolve.status" &&
153
- TASKS_FILE=".spur/run/$RUN_ID-wrapup-tasks.json" &&
154
- if ! printf '%s' "$tasks" | jq -e 'type == "array" and all(.[]; type == "string" and test("^[0-9]{4}$"))' > /dev/null 2>&1; then
155
- echo "task-resolve: tasks must be a JSON array of canonical four-digit WBS strings (whitespace is rejected, not trimmed)" >&2;
156
- echo "failed:tasks is not a JSON array of canonical four-digit WBS strings" > "$REASON_FILE";
157
- printf 'FAIL\n' > "$STATUS_FILE";
140
+ if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
141
+ bun plugins/sp/scripts/wrapup-steps.ts resolve;
142
+ elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
143
+ node "$W" resolve;
158
144
  else
159
- printf '%s' "$tasks" | jq -c 'reduce .[] as $w ([]; if any(.[]; . == $w) then . else . + [$w] end)' > "$TASKS_FILE";
160
- RESOLVE_RC=0;
161
- for wbs in $(jq -r '.[]' "$TASKS_FILE"); do
162
- TASK_JSON=$($spurBin task show "$wbs" --json 2>/dev/null || true);
163
- TASK_STATUS=$(printf '%s' "$TASK_JSON" | jq -r '.frontmatter.status // .status // empty' 2>/dev/null || true);
164
- if [ "$TASK_STATUS" != "done" ] && [ "$TASK_STATUS" != "cancelled" ]; then
165
- STATUS_DISPLAY="$TASK_STATUS";
166
- if [ -z "$STATUS_DISPLAY" ]; then STATUS_DISPLAY=unresolved; fi;
167
- echo "task-resolve: task $wbs did not resolve to a completed status (status=$STATUS_DISPLAY)" >&2;
168
- RESOLVE_RC=1;
169
- fi;
170
- done;
171
- if [ "$RESOLVE_RC" -eq 0 ]; then
172
- printf 'PASS\n' > "$STATUS_FILE";
173
- else
174
- echo "failed:unresolved or non-completed task (see $TASKS_FILE)" > "$REASON_FILE";
175
- printf 'FAIL\n' > "$STATUS_FILE";
176
- fi;
177
- fi;
178
- exit 0
179
- # Route reason writer (0758 R3/R4 run-attribution pins intact). Runs AFTER
180
- # validation and reads ONLY the validated run-scoped capture — never raw
181
- # vars.tasks (0783 R2/R5); the fixed-path fallback is gone because
182
- # validation refuses an empty __runId above. A FAIL resolve exits without
183
- # writing: the failed reason written by validation stands, and the skip
184
- # reason can never mask it. A missing/corrupted capture yields N=-1 and
185
- # no skipped claim; the failed defense edge owns the run.
145
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
146
+ printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
147
+ fi
148
+ # (e) route-reason writer: one jq table lookup over the mode var over the
149
+ # validated capture; workflow-local routing glue (0824). Runs AFTER validation
150
+ # and reads ONLY .spur/run/<runId>-wrapup-tasks.json — never raw vars.tasks
151
+ # (0783 R2/R5). A FAIL resolve exits without writing: the failed reason written
152
+ # by validation stands, and the skip reason can never mask it. A missing or
153
+ # corrupted capture fails the jq lookup (no skipped claim can be invented);
154
+ # the failed defense edge owns the run.
186
155
  - kind: shell
187
156
  options:
188
157
  command: >-
189
- mkdir -p .spur/run .spur/memory &&
190
- RUN_ID="$__runId" &&
191
- if [ -z "$RUN_ID" ]; then
192
- echo "task-resolve: __runId is empty — refusing to write a route reason" >&2;
193
- exit 1;
194
- fi &&
195
- REASON_FILE=".spur/run/$RUN_ID-route-reason.txt" &&
196
- STATUS_FILE=".spur/run/$RUN_ID-wrapup-resolve.status" &&
197
- TASKS_FILE=".spur/run/$RUN_ID-wrapup-tasks.json" &&
198
- if [ "$(cat "$STATUS_FILE" 2>/dev/null)" = FAIL ]; then
199
- exit 0;
200
- fi &&
201
- N=$(jq length "$TASKS_FILE" 2>/dev/null || echo -1) &&
202
- if [ "$N" -eq 0 ]; then
203
- echo "skipped:empty task list" > "$REASON_FILE";
204
- elif [ "$mode" = "fast" ]; then
205
- echo "fast:evidence complete+consistent" > "$REASON_FILE";
206
- elif [ -z "$mode" ]; then
207
- echo "safety:missing evidence (mode empty)" > "$REASON_FILE";
208
- elif [ "$mode" = "unknown" ]; then
209
- echo "safety:unknown evidence quality" > "$REASON_FILE";
210
- elif [ "$mode" = "conflict" ]; then
211
- echo "safety:conflicting evidence" > "$REASON_FILE";
212
- else
213
- echo "safety:unrecognized evidence (mode=$mode)" > "$REASON_FILE";
214
- fi &&
215
- printf '%s %s\n' "$RUN_ID" "$(cat "$REASON_FILE")"
216
- >> .spur/memory/wrapup-routes.log &&
217
- exit 0
158
+ mkdir -p .spur/run .spur/memory; RUN_ID="$__runId"; case "$RUN_ID" in '') echo "task-resolve: __runId is empty — refusing to write a route reason" >&2; exit 1 ;; esac;
159
+ REASON_FILE=".spur/run/$RUN_ID-route-reason.txt"; case "$(cat ".spur/run/$RUN_ID-wrapup-resolve.status" 2>/dev/null)" in FAIL) exit 0 ;; esac;
160
+ N=$(jq length ".spur/run/$RUN_ID-wrapup-tasks.json" 2>/dev/null);
161
+ jq -rn --arg m "$mode" --arg n "$N" 'if $n == "0" then "skipped:empty task list" else {"fast":"fast:evidence complete+consistent","":"safety:missing evidence (mode empty)","unknown":"safety:unknown evidence quality","conflict":"safety:conflicting evidence"}[$m] // "safety:unrecognized evidence (mode=\($m))" end' > "$REASON_FILE";
162
+ printf '%s %s\n' "$RUN_ID" "$(cat "$REASON_FILE")" >> .spur/memory/wrapup-routes.log
218
163
 
219
164
  - id: doc-sync
220
165
  description: >
@@ -227,6 +172,8 @@ states:
227
172
  a non-empty capture before the append; append is best-effort soft (empty capture
228
173
  skips without aborting wrap-up).
229
174
  onEnter:
175
+ # (warn) free-form prompt: no /sp:doc-evolve command surface yet (ADR-043
176
+ # residual, 0607 R2 one-query merge); capture is bounded by expectFile.
230
177
  - kind: agent.run
231
178
  options:
232
179
  agent: ${vars.agent}
@@ -245,6 +192,8 @@ states:
245
192
  answerFile: .spur/run/${vars.__runId}-wrapup-learnings.md
246
193
  expectFile: .spur/run/${vars.__runId}-wrapup-learnings.md
247
194
  timeoutMs: ${vars.stepTimeoutMs}
195
+ # (warn) learnings append is intentionally soft (empty capture skips
196
+ # without aborting wrap-up, 0607 R2).
248
197
  - kind: shell
249
198
  options:
250
199
  command: >-
@@ -270,40 +219,20 @@ states:
270
219
  is UNKNOWN telemetry, never proof of completion; previously written valid rows
271
220
  survive a failure.
272
221
  onEnter:
222
+ # (d) wrapup-steps.ts metrics owns the JSONL row append (0824); the wrapper
223
+ # only locates it and fails closed — FAIL, never a silent skip.
273
224
  - kind: shell
274
225
  options:
275
226
  command: >-
276
- mkdir -p .spur/run .spur/memory &&
277
- STATUS_FILE=".spur/run/$__runId-wrapup-metrics.status" &&
278
- TASKS_FILE=".spur/run/$__runId-wrapup-tasks.json" &&
279
- METRICS_RC=0 &&
280
- if ! jq -e 'type == "array" and all(.[]; type == "string" and test("^[0-9]{4}$"))' "$TASKS_FILE" > /dev/null 2>&1; then
281
- echo "metrics-record: run-scoped task capture missing, corrupted or non-canonical — refusing to record metrics" >&2;
282
- printf 'FAIL\n' > "$STATUS_FILE";
227
+ mkdir -p .spur/run &&
228
+ if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
229
+ bun plugins/sp/scripts/wrapup-steps.ts metrics;
230
+ elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
231
+ node "$W" metrics;
283
232
  else
284
- for wbs in $(jq -r '.[]' "$TASKS_FILE"); do
285
- task_json=$($spurBin task show "$wbs" --json 2>/dev/null || true);
286
- if printf '%s' "$task_json" | jq -e '(.frontmatter.status // .status) != null' > /dev/null 2>&1; then
287
- feat=$(printf '%s' "$task_json" | jq -r '.frontmatter.feature_id // .feature_id // ""');
288
- stat=$(printf '%s' "$task_json" | jq -r '.frontmatter.status // .status // "unknown"');
289
- verdict="UNKNOWN";
290
- if [ -f ".spur/run/$wbs-verdict.json" ]; then
291
- v=$(jq -r '.verdict // "UNKNOWN"' ".spur/run/$wbs-verdict.json" 2>/dev/null || true);
292
- if [ -n "$v" ]; then verdict="$v"; fi;
293
- fi;
294
- ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
295
- if ! jq -cn --arg wbs "$wbs" --arg feature_id "$feat" --arg status "$stat" --arg verdict "$verdict" --arg timestamp "$ts" '{wbs:$wbs, feature_id:$feature_id, status:$status, verdict:$verdict, timestamp:$timestamp}' >> .spur/memory/wrapup-metrics.jsonl; then
296
- echo "metrics-record: metrics append failed for task $wbs — recording FAIL instead of claiming the row landed" >&2;
297
- METRICS_RC=1;
298
- fi;
299
- else
300
- echo "metrics-record: task $wbs lookup failed or was malformed — recording FAIL instead of silently omitting its metrics row" >&2;
301
- METRICS_RC=1;
302
- fi;
303
- done;
304
- if [ "$METRICS_RC" -eq 0 ]; then printf 'PASS\n' > "$STATUS_FILE"; else printf 'FAIL\n' > "$STATUS_FILE"; fi;
305
- fi;
306
- exit 0
233
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
234
+ printf 'FAIL\n' > ".spur/run/$__runId-wrapup-metrics.status";
235
+ fi
307
236
 
308
237
  - id: feature-transition
309
238
  description: >
@@ -320,94 +249,24 @@ states:
320
249
  (the engine only enters this state when vars.feature is set) and fails
321
250
  loud (dogfood 2026-08-15, feature I3).
322
251
  onEnter:
323
- # The terminating `exit 0` must be reached on every branch — the sync is
324
- # chained with `;`, because `&&` would abort the state, discarding the
325
- # learnings/metrics that already landed. Soft TERMINATION is not soft
326
- # TRUTH: the result is classified per 0783 R4 and only a verified sync
327
- # writes PASS. `feature-sync-bounded.ts` is a Spur-monorepo path (`spur
328
- # init` does not scaffold `plugins/sp/`), so seeded projects fall back to
329
- # the plain `spur feature sync` verb.
330
- # R1 (0625): a feature transition that changed state may have left
331
- # cross-task fallout the per-task fast gates never see. The
332
- # affected-feature gate (featureGateCmd) runs after an applied sync OR a
333
- # non-zero exit that may follow a partial multi-hop transition. It is a
334
- # diagnostic only: a gate PASS can never convert a failed sync into
335
- # success, and a gate FAIL fails the step (0770 failure routing).
336
- # NOTE: do not add
337
- # `#` comments INSIDE the folded shell string — YAML `>-` folds them onto
338
- # the following statement line and comments it out.
252
+ # (d) wrapup-steps.ts feature-transition owns the bounded sync, observation
253
+ # and the gate (0824); the wrapper only locates it and fails closed. The
254
+ # script terminates exit 0 on every path so artifacts that already landed
255
+ # are never discarded; soft TERMINATION is not soft TRUTH — only a verified
256
+ # sync writes PASS (0783 R4). A gate PASS can never convert a failed sync
257
+ # into success, and a gate FAIL fails the step (0770 failure routing).
339
258
  - kind: shell
340
259
  options:
341
260
  command: >-
342
261
  mkdir -p .spur/run &&
343
- if [ -z "$feature" ]; then
344
- echo "feature-transition: vars.feature is empty — refusing no-op feature sync (mis-invocation, not a blocked sync)" >&2;
345
- exit 1;
346
- fi;
347
- SYNC_OUTPUT=$(if [ -f plugins/sp/scripts/feature-sync-bounded.ts ]; then
348
- bun plugins/sp/scripts/feature-sync-bounded.ts "$feature" --spur-bin "$spurBin" --json;
349
- elif SYNC_MJS="$(superskill script path sp feature-sync-bounded.mjs 2>/dev/null)" && [ -f "$SYNC_MJS" ]; then
350
- node "$SYNC_MJS" "$feature" --spur-bin "$spurBin" --json;
351
- else
352
- $spurBin feature sync "$feature" --json;
353
- fi);
354
- SYNC_RC=$?;
355
- printf '%s\n' "$SYNC_OUTPUT";
356
- OBSERVED=$($spurBin feature show "$feature" --json 2>/dev/null | jq -r '.status // .frontmatter.status // empty' 2>/dev/null || true);
357
- if [ -z "$OBSERVED" ]; then OBSERVED=unreadable; fi;
358
- APPLIED=unreadable;
359
- SYNC_OK=0;
360
- REASON="";
361
- if [ "$SYNC_RC" -ne 0 ]; then
362
- REASON="sync exited nonzero (rc=$SYNC_RC)";
363
- elif ! printf '%s' "$SYNC_OUTPUT" | jq -e 'type == "object" and (.proposal | type == "object") and (.proposal.featureId | type == "string") and (.proposal.from | type == "string") and (.proposal.to | type == "string") and (.applied | type == "boolean")' > /dev/null 2>&1; then
364
- REASON="malformed or unreadable sync result";
365
- else
366
- P_FROM=$(printf '%s' "$SYNC_OUTPUT" | jq -r '.proposal.from // ""');
367
- P_TO=$(printf '%s' "$SYNC_OUTPUT" | jq -r '.proposal.to // ""');
368
- APPLIED=$(printf '%s' "$SYNC_OUTPUT" | jq -r '.applied');
369
- if [ "$(printf '%s' "$SYNC_OUTPUT" | jq -r '.proposal.featureId // ""')" != "$feature" ]; then
370
- REASON="sync proposal does not match feature $feature";
371
- elif [ "$(printf '%s' "$SYNC_OUTPUT" | jq -r '.proposal.gateBlocked // false')" = "true" ]; then
372
- REASON="sync proposal is gate-blocked — a blocked sync is not a no-change success";
373
- elif [ "$(printf '%s' "$SYNC_OUTPUT" | jq -r '.proposal.requiresConfirm // false')" = "true" ]; then
374
- REASON="sync proposal requires operator confirmation";
375
- elif [ "$APPLIED" = "true" ] && [ "$OBSERVED" != "$P_TO" ]; then
376
- REASON="applied sync did not land on the proposal target (observed=$OBSERVED, to=$P_TO)";
377
- elif [ "$APPLIED" = "false" ] && { [ "$P_FROM" != "$P_TO" ] || [ "$OBSERVED" != "$P_TO" ]; }; then
378
- REASON="sync applied nothing without a from==to observed no-op (from=$P_FROM, to=$P_TO, observed=$OBSERVED)";
379
- else
380
- SYNC_OK=1;
381
- fi;
382
- fi;
383
- GATE="skipped";
384
- if [ "$APPLIED" = "true" ] || [ "$SYNC_RC" -ne 0 ]; then
385
- echo "feature-transition: sync applied or failed after a possible partial transition for $feature — running feature gate: $featureGateCmd";
386
- if sh -c "$featureGateCmd"; then
387
- GATE="PASS";
388
- echo "feature-transition: feature gate PASS for feature $feature";
389
- else
390
- GATE="FAIL";
391
- echo "feature-transition: feature gate FAIL for feature $feature — inspect findings before reporting the transition complete" >&2;
392
- fi;
393
- else
394
- echo "feature-transition: sync did not apply a transition (rc=$SYNC_RC, applied=$APPLIED) — feature gate skipped";
395
- fi;
396
- SYNC_STATUS="PASS";
397
- if [ "$SYNC_OK" -ne 1 ] || [ "$GATE" = "FAIL" ]; then
398
- SYNC_STATUS="FAIL";
399
- echo "feature-transition: required synchronization failed for $feature — $REASON; gate=$GATE" >&2;
400
- elif [ "$APPLIED" = "false" ]; then
401
- echo "feature-transition: feature sync verified for $feature (from==to observed at $OBSERVED, gate=$GATE) — explicit no-change";
402
- else
403
- echo "feature-transition: feature sync verified for $feature (applied, observed=$OBSERVED, gate=$GATE)";
404
- fi;
405
- if [ "$SYNC_STATUS" = "PASS" ]; then
406
- printf 'PASS\n' > ".spur/run/$__runId-wrapup-sync.status";
262
+ if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
263
+ bun plugins/sp/scripts/wrapup-steps.ts feature-transition;
264
+ elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
265
+ node "$W" feature-transition;
407
266
  else
267
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
408
268
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-sync.status";
409
- fi;
410
- exit 0
269
+ fi
411
270
 
412
271
  - id: branch-cleanup
413
272
  description: >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.80",
3
+ "version": "0.3.82",
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.62",
57
- "@gobing-ai/ts-ai-runner": "^0.4.62",
58
- "@gobing-ai/ts-dual-workflow-engine": "^0.4.62",
59
- "@gobing-ai/ts-infra": "^0.4.62",
60
- "@gobing-ai/ts-llm-jsonl-importer": "^0.4.62",
61
- "@gobing-ai/ts-rule-engine": "^0.4.62",
62
- "@gobing-ai/ts-runtime": "^0.4.62",
63
- "@gobing-ai/ts-utils": "^0.4.62",
56
+ "@gobing-ai/ts-db": "^0.4.66",
57
+ "@gobing-ai/ts-ai-runner": "^0.4.66",
58
+ "@gobing-ai/ts-dual-workflow-engine": "^0.4.66",
59
+ "@gobing-ai/ts-infra": "^0.4.66",
60
+ "@gobing-ai/ts-llm-jsonl-importer": "^0.4.66",
61
+ "@gobing-ai/ts-rule-engine": "^0.4.66",
62
+ "@gobing-ai/ts-runtime": "^0.4.66",
63
+ "@gobing-ai/ts-utils": "^0.4.66",
64
64
  "@types/bun": "1.3.14",
65
65
  "@types/figlet": "^1.7.0",
66
66
  "@types/node-notifier": "8.0.5",
@@ -185,7 +185,7 @@ pipeline step.
185
185
 
186
186
  ```
187
187
  plugins/sp/
188
- ├── skills/ # Domain knowledge + workflow docs (32 skills)
188
+ ├── skills/ # Domain knowledge + workflow docs (34 skills)
189
189
  │ ├── brainstorm/ # Structured ideation workflow
190
190
  │ │ ├── agents/openai.yaml
191
191
  │ │ ├── examples/ideation-example.md
@@ -227,6 +227,8 @@ plugins/sp/
227
227
  │ │ ├── features.md + features/{verbs, acceptance-criteria, roadmap-priority}.md
228
228
  │ │ ├── rules.md + rules/{operations, authoring-rules, fine-tuning, validation-and-extension}.md
229
229
  │ │ └── workflows.md + workflows/{operations, authoring-workflows, validation-and-extension}.md
230
+ │ ├── spur-composer/ # Cross-noun selection, composition ladder, rule tuning; applies doctor proposals
231
+ │ ├── spur-doctor/ # Read-only artifact evaluation → proposal table (backs spur-composer)
230
232
  │ ├── spur-dev/ # Thin planning→execution orchestration spine
231
233
  │ │ └── references/ # ac-style-guide, cross-cutting, decision-brief, dev-operations,
232
234
  │ │ execution-batch, execution-workflow, feature-link-helper,
@@ -330,7 +332,13 @@ surface or run one workflow. All skills target the same five core platforms: `cl
330
332
  | `indexed-context` | 1.0 | Cross-agent project context — anatomy/learnings/pitfalls/buglog/memory in `.spur/context/`; hook-tracked token-ledger; graceful degradation on agents without hooks |
331
333
  | `history-anatomy` | 1.0 | Diagnostic interpretation owner over already-imported history — daily/ad-hoc mode contract, closed finding taxonomy, twelve-section report contract, `enrich`/`validate` rubrics; no workflow launch, no JSONL fallback, no corpus mutation |
332
334
  | `session-review` | 1.1 | Inline review of the active coding-agent session — compact outcomes, evidence-backed resolved/open issue classification, proposal-only improvements, and next actions; no workflow, import, delegation, or mutation |
335
+ | `spur-composer` | 1.0 | Cross-noun composition — workflow catalog selection, the ephemeral→project→shared ladder, ADR-115 budgets, trace-driven rule tuning; applies accepted `spur-doctor` proposals through `spur` verbs; never judges its own output and never runs a recurring loop |
336
+ | `spur-doctor` | 1.0 | Cross-noun evaluation — read-only CLI evidence per noun (task/feature/rule/workflow/agent spec), reflection over `history-anatomy` findings through a closed action-class map, and a proposal table; writes nothing; diagnoses artifacts, not runtime environments (`spur agent doctor`'s job) |
333
337
  | `redesign-web-ui` | 1.0 | Existing-UI visual upgrade — audit generic AI fingerprints, apply in-stack polish against `DESIGN.md` / live tokens, verify behavior and viewports; does not migrate frameworks |
338
+ | `taste-refactoring-api` | — | API design and refactoring for REST/HTTP, RPC/gRPC, GraphQL, and event contracts; compatibility, security, and migration review |
339
+ | `taste-refactoring-ui` | — | UI design and refactoring with visual hierarchy, typography, spacing, color, and interaction review |
340
+ | `taste-refactoring-architect` | — | Architecture review, simplification, and refactoring; boundary modeling, dependency reduction, and migration planning |
341
+ | `taste-refactoring-tests` | — | Test suite refactoring for failure sensitivity, regression detection, and assertion strength; eliminates brittle mocks |
334
342
 
335
343
  #### Bounded coexistence and retirement gate — `sp:issue-finding` (HA-S1 0661)
336
344
 
@@ -488,12 +496,13 @@ hold `SKILL.md` and prompt-side companions only.
488
496
  | `hooks/context-*.ts` | Runtime for the three registered context hooks (session-start, post-tool, session-stop) — token-cost estimation + ledger append for `sp:indexed-context` |
489
497
  | `hooks/careful-guard.ts` | Opt-in destructive-command guard (unwired — see Hooks above) |
490
498
  | `scripts/batch-preflight.ts` | Pure TABLE A STOP evaluation for `super-planner` — skip doomed pipeline launches without spawning a Skill subprocess; recovery hints map stuck statuses to a single `/sp:dev-*` hop |
499
+ | `scripts/workflow-step-profile.ts` | Read-only step profile from `spur workflow trace` — per node and action-kind durations, idle gaps, session mode and `cacheHit` coverage, plus the ADR-115 cache-window flags `sp:spur-doctor` turns into workflow-optimization proposals |
491
500
  | `scripts/dogfood-testing/detect-pipeline-driving.ts` | Word-boundary detector for pipeline-driving testees (leading-space invariant) |
492
501
  | `scripts/dogfood-testing/validate-report.ts` | Pure `validateReport(md)` — footer-mandatory + 7-check finalize-or-abort contract with stable error codes |
493
502
  | `scripts/daily-summary/{daily-summary,logger}.ts` | ccusage + git-history orchestration helpers for `sp:daily-summary` |
494
503
  | `scripts/feature-sync-bounded.ts` | Bounded retry-suppression wrapper for `spur feature sync` during batch/wrap-up runs — suppresses identical L4-blocked repeats |
495
504
  | `scripts/stage-registry-adapter.ts` | dev-next golden-path adapter over the canonical stage registry — TABLE A/B/C resolution bridge for the status-aware facade |
496
- | `scripts/task-size-precheck.ts` | Pipeline size precheck guard (R2) + size-vs-executor-capability gate (R3) — evaluates R-item/Plan-count limits to PASS/FAIL |
505
+ | `scripts/task-size-precheck.ts` | Pipeline size precheck guard (R2) — evaluates R-item/Plan-count limits to PASS/FAIL |
497
506
  | `scripts/validate-flag-contracts.ts` | Mechanical consistency gate — compares flag claims across command files, flag-glossary, cross-cutting, dev-operations, and ADR; reports disagreements |
498
507
  | `*.test.ts` | Unit suites — in `hooks/` for guards, in `tests/<skill>/` per ADR-031 pairing |
499
508
 
@@ -527,7 +536,7 @@ graph TB
527
536
  end
528
537
 
529
538
  subgraph "Execution layer"
530
- CLI["spur CLI<br/>task · feature · rule · workflow<br/>· agent · history · team · message"]
539
+ CLI["spur CLI<br/>task · feature · rule · workflow<br/>· agent · history · message"]
531
540
  SCRIPT["hooks/task-write-guard.ts"]
532
541
  end
533
542
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: expert-spur
3
3
  description: |
4
- Use PROACTIVELY for "create tasks for this feature", "update all task statuses", "audit task traceability", "create a feature with acceptance criteria", "harden the rule catalog", "author a batch of workflows", or "expert-spur". Multi-step corpus work across `spur task`, `feature`, `rule`, and `workflow`: batch creation, status sweeps, section campaigns, traceability audits, rule hardening, and workflow authoring/refactoring. For one deterministic operation, run the CLI directly.
4
+ Use PROACTIVELY for "create tasks for this feature", "update all task statuses", "audit task traceability", "create a feature with acceptance criteria", "harden the rule catalog", "author a batch of workflows", "compose a workflow", "tune a rule", "apply accepted doctor proposals", "evaluate spur artifact health", "reflect over history findings", or "expert-spur". Multi-step corpus work across `spur task`, `feature`, `rule`, `workflow`, and agent specs: batch creation, status sweeps, section campaigns, traceability audits, rule hardening, workflow authoring/refactoring, composition and tuning (sp:spur-composer), and evaluation/reflection proposal passes (sp:spur-doctor). For one deterministic operation, run the CLI directly. Recurring evolution loops and multi-agent coordination belong to sp:super-planner — never this agent.
5
5
 
6
6
  <example>
7
7
  Context: Batch task status update across a feature.
@@ -12,19 +12,23 @@ description: |
12
12
  tools: [Read, Grep, Glob, Bash, Skill]
13
13
  model: inherit
14
14
  color: green
15
- skills: [sp:spur-cli]
15
+ skills: [sp:spur-cli, sp:spur-composer, sp:spur-doctor]
16
16
  ---
17
17
 
18
18
  # Expert Spur
19
19
 
20
20
  Thin specialist for multi-step Spur **corpus** work. The backend skill `sp:spur-cli` owns noun,
21
21
  verb, flag, output, and exit semantics; this agent selects its task/feature/rule/workflow reference,
22
- sequences operations, and evaluates each result before continuing.
22
+ sequences operations, and evaluates each result before continuing. Composition, tuning, evaluation
23
+ and reflection campaigns are method-backed by `sp:spur-composer` and `sp:spur-doctor`; their verbs
24
+ and flags still resolve through the `sp:spur-cli` references.
23
25
 
24
26
  ## Role
25
27
 
26
- You are the Spur corpus steward: a specialist sequencer over `skill: sp:spur-cli`, not a second
27
- implementation of the CLI or lifecycle spine.
28
+ You are the Spur corpus steward: a specialist sequencer over `skill: sp:spur-cli`, backed by
29
+ `sp:spur-composer` (select, compose, tune, apply) and `sp:spur-doctor` (evaluate, reflect, propose).
30
+ You are not a second implementation of the CLI or lifecycle spine, not a coordinator, and not a
31
+ recurring loop.
28
32
 
29
33
  ## Scope
30
34
 
@@ -34,19 +38,28 @@ Use for:
34
38
  - Cross-corpus traceability or structural audits.
35
39
  - Rule catalog authoring, validation, execution, and hardening.
36
40
  - Workflow fit decisions, authoring/refactoring, validation, dry-runs, and trace comparison.
37
-
38
- Do not use for one CLI invocation. Do not use for planning→implementation→verification lifecycle
39
- or batch task execution; `sp:spur-dev` owns that orchestration. The backend skill covers the other
41
+ - Composition and tuning campaigns via `sp:spur-composer`: catalog selection, the composition
42
+ ladder, trace-driven rule tuning, and applying accepted doctor proposals.
43
+ - Evaluation and reflection campaigns via `sp:spur-doctor`: read-only evidence, history-finding
44
+ reflection, and a proposal table.
45
+
46
+ One bounded campaign per dispatch. Do not use for one CLI invocation. Do not use for
47
+ planning→implementation→verification lifecycle or batch task execution; `sp:spur-dev` owns that
48
+ orchestration. Recurring evolution loops and multi-agent coordination are not this agent's duty —
49
+ they hand off to `sp:super-planner` or a workflow (§ Hand-offs). The backend skill covers the other
40
50
  CLI nouns for direct use, but they are not this corpus specialist's scope.
41
51
 
42
52
  ## Process
43
53
 
44
54
  1. Load `plugins/sp/skills/spur-cli/SKILL.md` and the exact noun reference before invoking a verb.
45
- 2. Resolve and freeze the target set. Report ambiguity instead of guessing identifiers or flags.
46
- 3. Run the noun's read/check/validate path before mutation where available.
47
- 4. Mutate only through `spur`; parse `--json` output when the verb advertises it.
48
- 5. Inspect each result before the next dependent operation; stop on structural or validation failure.
49
- 6. Run affected-input checks after mutation (constitution T11): after task/feature batch writes,
55
+ 2. For compose/tune/apply work, load `plugins/sp/skills/spur-composer/SKILL.md` first; for
56
+ evaluate/reflect/propose work, load `plugins/sp/skills/spur-doctor/SKILL.md` first. Follow the
57
+ owning skill's method; never restate it here.
58
+ 3. Resolve and freeze the target set. Report ambiguity instead of guessing identifiers or flags.
59
+ 4. Run the noun's read/check/validate path before mutation where available.
60
+ 5. Mutate only through `spur`; parse `--json` output when the verb advertises it.
61
+ 6. Inspect each result before the next dependent operation; stop on structural or validation failure.
62
+ 7. Run affected-input checks after mutation (constitution T11): after task/feature batch writes,
50
63
  run `spur task check <wbs>` / `spur feature check <id>` for each changed document and its
51
64
  required linked evidence — not a corpus sweep. The explicit unsuppressed audit
52
65
  (`spur task check --corpus --json`) is reserved for checker-policy changes (T10).
@@ -71,13 +84,19 @@ their runbook here.
71
84
  - Reimplement CLI validation in prose or shell.
72
85
  - Never drive the planning/execution lifecycle; do not run application implementation or task
73
86
  pipelines through this agent.
87
+ - Drive a task batch through execution, chain campaigns into a recurring loop, or dispatch and
88
+ coordinate multiple agents — no batch driving, no recurring loops, no coordination dispatch here;
89
+ they hand off to `sp:super-planner` or a workflow.
90
+ - Use `spur agent loop` — a supervisor-internal, forbidden surface. Agent specs are read through
91
+ `spur agent list --specs`.
74
92
 
75
93
  ## Output Format
76
94
 
77
95
  ```markdown
78
96
  ## Spur Corpus Operations Report
79
97
 
80
- **Noun(s):** task | feature | rule | workflow
98
+ **Noun(s):** task | feature | rule | workflow | agent spec
99
+ **Method:** corpus campaign | compose/tune (`sp:spur-composer`) | evaluate/reflect (`sp:spur-doctor`)
81
100
  **Scope:** <resolved ids/files>
82
101
  **Confidence:** HIGH | MEDIUM | LOW
83
102
 
@@ -95,11 +114,14 @@ their runbook here.
95
114
 
96
115
  ## Platform Notes
97
116
 
98
- - Claude Code: use `Skill(skill="sp:spur-cli", args="<noun> <query>")`, then Bash for `spur`.
99
- - Other platforms: invoke `sp:spur-cli` directly; the agent wrapper is optional.
117
+ - Claude Code: load the bound skill the campaign needs — `Skill(skill="sp:spur-cli" | "sp:spur-composer" | "sp:spur-doctor", args="<query>")` — then Bash for `spur`.
118
+ - Other platforms: invoke the bound `sp:*` skill directly; the agent wrapper is optional.
100
119
 
101
- ## Dispatch surface
120
+ ## Hand-offs
102
121
 
103
- If corpus work must be dispatched again, follow
122
+ This agent runs one bounded campaign per dispatch and reports. Batch driving of task pipelines,
123
+ recurring evolution loops, and multi-agent coordination are not dispatch duties here — hand them
124
+ to `sp:super-planner` or a workflow definition. If the operator explicitly asks for a follow-up
125
+ corpus dispatch, its surface contract is
104
126
  [dispatch-surface.md](../skills/parallel-execution/references/dispatch-surface.md): native subagent
105
- by default, `spur agent run` only on a named trigger.
127
+ by default, `spur agent run` only on a named trigger — never `spur agent loop`.
@@ -0,0 +1,17 @@
1
+ /**
2
+ * GENERATED by scripts/commands/bundle-plugin-lib.ts — do not edit.
3
+ * Source of truth: packages/app/src/workflow/idea-handoff-cli.ts (task 0824).
4
+ */
5
+ export declare interface IdeaHandoffCliEnv {
6
+ __runId?: string;
7
+ featureId?: string;
8
+ spurBin?: string;
9
+ }
10
+ export declare interface IdeaHandoffCliOutcome {
11
+ exitCode: number;
12
+ result?: unknown;
13
+ }
14
+ export declare function runIdeaHandoffCli(
15
+ env: IdeaHandoffCliEnv,
16
+ run?: (input: { runId: string; featureId: string; spurBin: string }) => Promise<unknown>,
17
+ ): Promise<IdeaHandoffCliOutcome>;