@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
@@ -331,7 +331,7 @@ states:
331
331
  - kind: agent.run
332
332
  options:
333
333
  agent: ${vars.planningAgent}
334
- input: "Run sp:spec-decomposition for feature ${vars.featureId}. Read the brainstorm artifact, feature AC, and design doc. SIZING FIRST, before any JSON: apply the skill's `Default to NOT decomposing` rubric to the whole unit of work — if it scores 0-2 the correct output is a ONE-entry batch, not many. Scenario count is not task count: merge scenarios that one task delivers (same file surface, same subsystem, or unreadable apart in review), and list every scenario a task covers in its background. Merging never costs AC coverage — one task may carry several scenarios. Do not emit one entry per scenario or per requirement by reflex. Then produce a task-batch JSON array at .spur/run/${vars.__runId}-idea-task-batch.json, validated against task-batch.schema.json. Schema-permitted fields per entry: `name`, `background`, `requirements`, `design`, `plan`, `acceptance_criteria`, `feature_id`, `parent_wbs`, `priority`, `tags`, `template` — schema validation rejects anything else. `design`, `plan`, and `acceptance_criteria` are supported batch fields and normal default planning fills them from your analysis; the per-task refine step after batch-create still deepens them when a task needs more detail. 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."
334
+ input: "Run sp:spec-decomposition for feature ${vars.featureId} per skill references/decomposition.md § Idea-pipeline emission: sizing first, then the batch JSON at .spur/run/${vars.__runId}-idea-task-batch.json and the private task-order sidecar at .spur/run/${vars.__runId}-idea-task-order.json."
335
335
  # Declared Layer-1 role (0538 R2): routing reason beside the agent: pin.
336
336
  role: planner
337
337
  expectFile: .spur/run/${vars.__runId}-idea-task-batch.json
@@ -341,6 +341,8 @@ states:
341
341
  # unique, every sidecar name/dependency must refer to exactly one batch name, and — the
342
342
  # converse (F2, 0518 verify) — every batch name must appear in the sidecar, so a partial
343
343
  # sidecar can never silently skip `task deps` for an unlisted item (`[]` is valid).
344
+ # (warn) stays inline by design: one jq predicate over two run-scoped files; splitting
345
+ # it into a script would hide the exact fail-closed contract this guard pins (0824).
344
346
  - kind: shell
345
347
  options:
346
348
  command: >-
@@ -380,17 +382,22 @@ states:
380
382
  (CLI error or malformed result) writes the failed sentinel, preserving the
381
383
  existing retry behavior.
382
384
  onEnter:
385
+ # (e) idempotent batch creation: sentinel + temp/mv atomicity stay inline (0824);
386
+ # the jq verdict check keeps the done sentinel truthful so handoff-finalize can
387
+ # zip batch names to the returned WBS list.
383
388
  - kind: shell
384
389
  options:
385
390
  command: >-
386
- if test -f .spur/run/$__runId-idea-batch-create.done; then exit 0; fi;
387
- 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;
388
- if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --skip-ready --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
389
- mv .spur/run/$__runId-idea-batch-create-result.json.tmp .spur/run/$__runId-idea-batch-create-result.json &&
390
- date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.done;
391
+ P=".spur/run/$__runId-idea-batch-create" &&
392
+ test ! -f "$P.done" || exit 0 &&
393
+ rm -f "$P.failed" "$P-result.json" "$P-result.json.tmp" &&
394
+ if $spurBin task batch-create --file .spur/run/$__runId-idea-task-batch.json --skip-ready --json > "$P-result.json.tmp" &&
395
+ jq -e ".created == (.wbs | length)" "$P-result.json.tmp" >/dev/null 2>&1; then
396
+ mv "$P-result.json.tmp" "$P-result.json" &&
397
+ date -u +%Y-%m-%dT%H:%M:%SZ > "$P.done";
391
398
  else
392
- rm -f .spur/run/$__runId-idea-batch-create-result.json.tmp;
393
- date -u +%Y-%m-%dT%H:%M:%SZ > .spur/run/$__runId-idea-batch-create.failed;
399
+ rm -f "$P-result.json.tmp" &&
400
+ date -u +%Y-%m-%dT%H:%M:%SZ > "$P.failed";
394
401
  fi
395
402
 
396
403
  - id: ready-prepare
@@ -411,24 +418,22 @@ states:
411
418
  # Declared Layer-1 role (0538 R2), same planner executor as decompose.
412
419
  role: planner
413
420
  timeoutMs: ${vars.stepTimeoutMs}
421
+ # (warn) non-slash pointer: the 0788 checklist is per-checkout (digest via the
422
+ # project's own computePlanningDigest), so the bounded prompt pins the skill
423
+ # reference instead of a command; artifacts are gated by answerFile/expectFile.
414
424
  input: >-
415
- Ready-by-default preparation for feature ${vars.featureId} (0788). The created WBS
416
- values are in .spur/run/${vars.__runId}-idea-batch-create-result.json under .wbs.
417
- For EACH wbs: resolve the task file with `spur task path <wbs> --json`; open the
418
- task doc and apply the ready-refinement checklist — make requirements, design,
419
- plan, acceptance criteria, decisions, dependencies and premises present and
420
- non-placeholder so `spur task check <wbs> --json` exits 0; edit only planning
421
- sections, never Solution/Testing/Review/History. Record one checklist row per id
422
- with concrete evidence of how you verified it. Compute the planning digest with
423
- the project's own implementation when this is a monorepo checkout:
424
- `bun -e 'const m = await import("./packages/app/src/services/task-readiness"); console.log(m.computePlanningDigest(await Bun.file(process.argv[1]).text()))' <task-file>`
425
- — when that is impossible in this checkout, set status "skipped" instead of
426
- guessing a digest. Finally write .spur/run/${vars.__runId}-idea-ready.json with
427
- exactly this shape: {"runId":"${vars.__runId}","depth":"ready","tasks":[{"wbs":"<wbs>","status":"ready" or "failed" or "skipped","planningDigest":"<sha256 hex>","checks":[{"id":"requirements" or "design" or "plan" or "ac" or "decisions" or "dependencies" or "premises","pass":true or false,"evidence":"<how verified>"}]}]}. A task you cannot fully prepare gets status "failed" or "skipped" — never fabricate evidence; the handoff degrades to refineall.
425
+ Run the ready-prepare stage for feature ${vars.featureId} per sp:spur-dev
426
+ references/planning-workflow.md § Step 5.6 (Ready preparation): read
427
+ .spur/run/${vars.__runId}-idea-batch-create-result.json and write
428
+ .spur/run/${vars.__runId}-idea-ready.json.
429
+ answerFile: .spur/run/${vars.__runId}-ready-prepare-answer.txt
430
+ expectFile: .spur/run/${vars.__runId}-ready-prepare-answer.txt
428
431
  # Fail-closed shape validation (mirrors the order-sidecar guard). Absence is
429
432
  # normalized to an empty sidecar: finalize and the seeded fallback then degrade
430
433
  # the recommendation to refineall instead of failing the run. A PRESENT but
431
434
  # malformed sidecar fails the run here — it must not masquerade as evidence.
435
+ # (warn) stays inline by design: single jq shape predicate over the run-scoped
436
+ # sidecar; the failed/skipped rows are what degrade the recommendation (0824).
432
437
  - kind: shell
433
438
  options:
434
439
  command: >-
@@ -447,87 +452,23 @@ states:
447
452
  A mapping or CLI error fails the run before terminal handoff; an unready
448
453
  task is a successful planning outcome recorded as a refineall recommendation.
449
454
  onEnter:
450
- # F1 (0518 verify): the per-task check loop fails closed. A stderr-only (non-JSON)
451
- # `task check --json` exception would otherwise drop that task from the JSONL results
452
- # without failing the run (the loop's exit status is its last body command), silently
453
- # flipping the recommendation to runall — violating R3's any-fail=>refineall invariant.
454
- # Each `jq -c … >> "$CHECKS"` appends under `|| exit 1`, and a row-count assertion
455
- # (CHECKS lines == WBS count) gates the recommendation computation.
456
- # D5-O (R4): the deterministic writer owns finalization wherever it exists.
457
- # `idea-handoff-cli.ts` calls the tested `finalizeIdeaHandoff` capability and
458
- # implements this exact contract (equal-length/unique-name zip, `spur task deps`,
459
- # `spur feature refresh`, per-task check with a row-count assertion, and a single
460
- # recommended next command). `spur init` never scaffolds `packages/`, so seeded
461
- # projects fall through to the portable shell program below — the same
462
- # monorepo-writer/shell-fallback split the wrap-up metrics hop uses (0604 Q&A).
455
+ # (d) idea-handoff owns finalization (0824): the bundled finalizeIdeaHandoff
456
+ # capability runs from the monorepo checkout first; seeded projects (no
457
+ # packages/, no plugins/) resolve the registered idea-handoff.mjs twin under
458
+ # bare node; neither present fails closed (exit 1) instead of silently
459
+ # skipping finalization. The zip/deps/refresh/check/report contract itself is
460
+ # pinned by finalizeIdeaHandoff unit tests, not by this file.
463
461
  - kind: shell
464
462
  options:
465
463
  command: >-
466
464
  if [ -f packages/app/src/workflow/idea-handoff-cli.ts ]; then
467
465
  bun packages/app/src/workflow/idea-handoff-cli.ts;
468
- exit $?;
469
- fi;
470
- BATCH=".spur/run/$__runId-idea-task-batch.json" &&
471
- RESULT=".spur/run/$__runId-idea-batch-create-result.json" &&
472
- ORDER=".spur/run/$__runId-idea-task-order.json" &&
473
- REPORT=".spur/run/$__runId-idea-handoff.md" &&
474
- DEPMAP=".spur/run/$__runId-idea-dep-map.tsv" &&
475
- CHECKS=".spur/run/$__runId-idea-check-results.jsonl" &&
476
- rm -f "$DEPMAP" "$CHECKS" &&
477
- jq -e --slurpfile b "$BATCH" '(.wbs | length) == ($b[0] | length) and (($b[0] | map(.name) | unique | length) == ($b[0] | length))' "$RESULT" >/dev/null &&
478
- jq -r --slurpfile b "$BATCH" --slurpfile r "$RESULT" --slurpfile o "$ORDER" '
479
- ($b[0] | map(.name)) as $names | ($r[0].wbs) as $wbs |
480
- $o[0] | .[] |
481
- (.name as $n | $names | index($n)) as $i |
482
- (if $i == null or $wbs[$i] == null then "MISSING" else $wbs[$i] end) as $own |
483
- ((.depends_on_names // []) | map(. as $dep | $names | index($dep) as $d | if $d == null then "MISSING" else $wbs[$d] end) | join(" ")) as $deps |
484
- [.name, $own, $deps] | @tsv
485
- ' "$ORDER" > "$DEPMAP" &&
486
- while IFS="$(printf '\t')" read -r name own deps; do
487
- test "$own" != MISSING || exit 1;
488
- if test -n "$deps"; then
489
- for d in $deps; do test "$d" != MISSING || exit 1; done;
490
- $spurBin task deps "$own" set $deps --json >/dev/null || exit 1;
491
- fi;
492
- done < "$DEPMAP" &&
493
- $spurBin feature refresh --feature "$featureId" --json >/dev/null &&
494
- WBS_LIST=$(jq -r '.wbs | join(" ")' "$RESULT") &&
495
- for wbs in $WBS_LIST; do
496
- TMP=".spur/run/$__runId-idea-check-$wbs.tmp";
497
- if $spurBin task check "$wbs" --json > "$TMP" 2>&1; then P=true; else P=false; fi;
498
- 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;
499
- rm -f "$TMP";
500
- done &&
501
- CHECK_ROWS=$(wc -l < "$CHECKS" | tr -d ' ') &&
502
- WBS_COUNT=$(printf '%s\n' $WBS_LIST | wc -l | tr -d ' ') &&
503
- test "$CHECK_ROWS" = "$WBS_COUNT" &&
504
- READY=".spur/run/$__runId-idea-ready.json" &&
505
- (test -f "$READY" || printf '{"tasks":[]}\n' > "$READY") &&
506
- NEXT=$(jq -r --arg feature "$featureId" --slurpfile c "$CHECKS" --slurpfile k "$READY" 'if (($c | any(.[]; .pass == false)) or (($k[0].tasks // []) | length == 0) or (($k[0].tasks // []) | any(.[]; .status != "ready"))) then "/sp:dev-refineall --feature \($feature) --auto --depth ready" else "/sp:dev-runall --feature \($feature) --auto" end' "$RESULT") &&
507
- {
508
- echo "# Idea pipeline handoff report";
509
- echo;
510
- echo "Feature: $featureId";
511
- echo "Run ID: $__runId";
512
- echo;
513
- echo "## Created tasks";
514
- printf '%s\n' $WBS_LIST | sed 's/^/ - /';
515
- echo;
516
- echo "## Per-task readiness (spur task check)";
517
- echo;
518
- echo "| WBS | Outcome |";
519
- echo "|-----|---------|";
520
- while IFS= read -r line; do
521
- w=$(printf '%s' "$line" | jq -r '.wbs');
522
- o=$(printf '%s' "$line" | jq -r 'if .pass then "PASS" else "FAIL" end');
523
- printf '| %s | %s |\n' "$w" "$o";
524
- done < "$CHECKS";
525
- echo;
526
- echo "## Next command";
527
- echo;
528
- echo "$NEXT";
529
- } > "$REPORT"
530
-
466
+ elif H="$(superskill script path sp idea-handoff.mjs 2>/dev/null)" && [ -f "$H" ]; then
467
+ node "$H";
468
+ else
469
+ echo "idea handoff failed closed — idea-handoff script not found — run 'superskill install sp'" >&2;
470
+ exit 1;
471
+ fi
531
472
  - id: handoff
532
473
  description: >
533
474
  Terminal — idea pipeline complete. Ordering applied, roster refreshed, and
@@ -627,6 +568,8 @@ transitions:
627
568
  - from: ac-generate
628
569
  to: system-design
629
570
  description: "profile=auto, check passed, design route — run system design."
571
+ # (warn) 4 test segments: profile gate + captured status + route signal jointly own the
572
+ # route; guards never re-run the CLI (0769), so there is nothing smaller to extract.
630
573
  guard:
631
574
  kind: shell
632
575
  options:
@@ -635,6 +578,8 @@ transitions:
635
578
  - from: ac-generate
636
579
  to: decompose
637
580
  description: "profile=auto, check passed, skip-design route — go directly to decompose."
581
+ # (warn) 5 test segments: same as auto-skip 1 plus the OR'd design=auto/needs_design=false
582
+ # pair that keeps one captured signal file authoritative for both routes (0769).
638
583
  guard:
639
584
  kind: shell
640
585
  options:
@@ -643,6 +588,8 @@ transitions:
643
588
  - from: ac-generate
644
589
  to: ac-generate
645
590
  description: "profile=auto, check failed, retry cap not reached — re-run ac-generate."
591
+ # (warn) 4 test segments: profile gate + captured status + captured retry count; the
592
+ # retry loop is the smallest honest formulation of cap<3 routing (0769).
646
593
  guard:
647
594
  kind: shell
648
595
  options:
@@ -651,6 +598,8 @@ transitions:
651
598
  - from: ac-generate
652
599
  to: failed
653
600
  description: "profile=auto, check failed after 3 retries — escalate to failed."
601
+ # (warn) 4 test segments: mirror of the retry guard with cap>=3; keeping escalation and
602
+ # retry as one test-chain pair makes the cap boundary auditable in the diff (0769).
654
603
  guard:
655
604
  kind: shell
656
605
  options:
@@ -667,6 +616,8 @@ transitions:
667
616
  - from: feature-check
668
617
  to: system-design
669
618
  description: Feature check passed, design route — run system design.
619
+ # (warn) 4 test segments: HITL answer + captured status + design route; same shape as the
620
+ # ac-generate auto-skips, reading the same captured signal files (0769).
670
621
  guard:
671
622
  kind: shell
672
623
  options:
@@ -674,6 +625,8 @@ transitions:
674
625
  - from: feature-check
675
626
  to: decompose
676
627
  description: Feature check passed, skip-design route — go directly to decompose.
628
+ # (warn) 5 test segments: same as the feature-check design route plus the OR'd
629
+ # design=auto/needs_design=false pair (0769).
677
630
  guard:
678
631
  kind: shell
679
632
  options:
@@ -681,6 +634,8 @@ transitions:
681
634
  - from: feature-check
682
635
  to: ac-generate
683
636
  description: "Feature check failed — revise AC (retry cap: 3)."
637
+ # (warn) 4 test segments: HITL reject/failed-status OR-pair + captured retry count; the
638
+ # interactive retry mirror of the ac-generate cap<3 guard (0769).
684
639
  guard:
685
640
  kind: shell
686
641
  options:
@@ -688,6 +643,7 @@ transitions:
688
643
  - from: feature-check
689
644
  to: failed
690
645
  description: Feature check failed after 3 retries — escalate to failed.
646
+ # (warn) 4 test segments: mirror of the revise guard with cap>=3 (0769).
691
647
  guard:
692
648
  kind: shell
693
649
  options:
@@ -67,6 +67,8 @@ states:
67
67
  onEnter:
68
68
  - kind: shell
69
69
  options:
70
+ # (e) 0825: 10 lines — detached-HEAD/dirty/gh-auth/base-refusal probes must record
71
+ # before any push can publish; split would let a half-probed run continue.
70
72
  command: >-
71
73
  mkdir -p .spur/run &&
72
74
  STATUS_FILE=".spur/run/$__runId-pr-preflight.status" &&
@@ -83,6 +85,8 @@ states:
83
85
  onEnter:
84
86
  - kind: shell
85
87
  options:
88
+ # (e) 0825: 9 lines — hygiene verdict + json capture land together; the status-file
89
+ # fallback keeps a silent driver from passing the gate.
86
90
  command: >-
87
91
  mkdir -p .spur/run &&
88
92
  STATUS_FILE=".spur/run/$__runId-pr-hygiene.status" &&
@@ -100,16 +104,10 @@ states:
100
104
  onEnter:
101
105
  - kind: shell
102
106
  options:
107
+ # (e) 0825: 7 lines — unchanged shape (optional preReviewCmd probe, SKIP/PASS/FAIL)
108
+ # condensed onto folded one-liners to hold the §1.2 budget.
103
109
  command: >-
104
- mkdir -p .spur/run &&
105
- STATUS_FILE=".spur/run/$__runId-pr-precheck.status" &&
106
- if [ -z "$preReviewCmd" ]; then
107
- printf 'SKIP\n' > "$STATUS_FILE";
108
- else
109
- set +e; sh -c "$preReviewCmd"; rc=$?; set -e;
110
- if [ "$rc" -eq 0 ]; then printf 'PASS\n' > "$STATUS_FILE"; else printf 'FAIL\n' > "$STATUS_FILE"; fi;
111
- fi &&
112
- exit 0
110
+ mkdir -p .spur/run; if [ -z "$preReviewCmd" ]; then R=SKIP; elif sh -c "$preReviewCmd"; then R=PASS; else R=FAIL; fi; printf '%s\n' "$R" > ".spur/run/$__runId-pr-precheck.status"
113
111
 
114
112
  - id: push
115
113
  description: >
@@ -118,6 +116,8 @@ states:
118
116
  onEnter:
119
117
  - kind: shell
120
118
  options:
119
+ # (e) 0825: 9 lines — push result and status fallback are atomic; a split could
120
+ # publish commits without recording the outcome.
121
121
  command: >-
122
122
  mkdir -p .spur/run &&
123
123
  STATUS_FILE=".spur/run/$__runId-pr-push.status" &&
@@ -133,6 +133,8 @@ states:
133
133
  onEnter:
134
134
  - kind: shell
135
135
  options:
136
+ # (e) 0825: 9 lines — PR dedupe lookup captures its record for the downstream
137
+ # review link; ensure + capture must not race.
136
138
  command: >-
137
139
  mkdir -p .spur/run &&
138
140
  STATUS_FILE=".spur/run/$__runId-pr-ensure.status" &&
@@ -150,19 +152,10 @@ states:
150
152
  onEnter:
151
153
  - kind: shell
152
154
  options:
155
+ # (e) 0825: 10 lines — the write-then-read split is the file-var transport feeding
156
+ # wait/collect and must stay one action (same record, same run scope).
153
157
  command: >-
154
- mkdir -p .spur/run &&
155
- STATUS_FILE=".spur/run/$__runId-pr-request.status" &&
156
- FORCE_FLAG="" &&
157
- if [ "$mode" = "rerun" ]; then FORCE_FLAG="--force"; fi &&
158
- set +e &&
159
- bun "$(superskill script path sp pr-reviewing.ts)" request --focus "$focus" $FORCE_FLAG --json
160
- --status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-request.json";
161
- rc=$?; set -e &&
162
- if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
163
- [ -f ".spur/run/$__runId-pr-request.json" ] &&
164
- bun -e 'const fs=require("node:fs");const r=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));fs.writeFileSync(process.argv[2],(r.requestedAt ?? "")+"\n");fs.writeFileSync(process.argv[3],(r.head ?? "")+"\n")' ".spur/run/$__runId-pr-request.json" ".spur/run/$__runId-pr-since.txt" ".spur/run/$__runId-pr-head.txt";
165
- exit 0
158
+ mkdir -p .spur/run; case "$mode" in rerun) F=--force ;; *) F= ;; esac; bun "$(superskill script path sp pr-reviewing.ts)" request --focus "$focus" $F --json --status-file ".spur/run/$__runId-pr-request.status" > ".spur/run/$__runId-pr-request.json"; [ -f ".spur/run/$__runId-pr-request.status" ] || printf 'FAIL\n' > ".spur/run/$__runId-pr-request.status"; jq -e . ".spur/run/$__runId-pr-request.json" > /dev/null 2>&1 && jq -r '.requestedAt // ""' ".spur/run/$__runId-pr-request.json" > ".spur/run/$__runId-pr-since.txt" && jq -r '.head // ""' ".spur/run/$__runId-pr-request.json" > ".spur/run/$__runId-pr-head.txt"; exit 0
166
159
 
167
160
  - id: wait
168
161
  description: >
@@ -181,6 +174,8 @@ states:
181
174
  var: prHead
182
175
  - kind: shell
183
176
  options:
177
+ # (e) 0825: 9 lines — bounded wait loop keeps rc bookkeeping so a crashed driver
178
+ # fails closed instead of polling forever.
184
179
  command: >-
185
180
  mkdir -p .spur/run &&
186
181
  STATUS_FILE=".spur/run/$__runId-pr-wait.status" &&
@@ -206,19 +201,10 @@ states:
206
201
  var: prHead
207
202
  - kind: shell
208
203
  options:
204
+ # (e) 0825: 9 lines — collect + status must run against the same since/head pair
205
+ # before the composite status is decided (same 0771 record contract).
209
206
  command: >-
210
- mkdir -p .spur/run &&
211
- STATUS_FILE=".spur/run/$__runId-pr-collect.status" &&
212
- set +e &&
213
- bun "$(superskill script path sp pr-reviewing.ts)" collect --since "$prSince" --head "$prHead" --json
214
- --status-file "$STATUS_FILE" > ".spur/run/$__runId-pr-findings.json";
215
- collect_rc=$?;
216
- bun "$(superskill script path sp pr-reviewing.ts)" status --since "$prSince" --head "$prHead" --json > ".spur/run/$__runId-pr-status.json";
217
- status_rc=$?; set -e &&
218
- if [ "$collect_rc" -ne 0 ] || [ "$status_rc" -ne 0 ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
219
- if [ ! -f "$STATUS_FILE" ]; then printf 'FAIL\n' > "$STATUS_FILE"; fi &&
220
- { cat ".spur/run/$__runId-pr-findings.json" 2>/dev/null || true; } &&
221
- exit 0
207
+ mkdir -p .spur/run; bun "$(superskill script path sp pr-reviewing.ts)" collect --since "$prSince" --head "$prHead" --json --status-file ".spur/run/$__runId-pr-collect.status" > ".spur/run/$__runId-pr-findings.json"; c=$?; bun "$(superskill script path sp pr-reviewing.ts)" status --since "$prSince" --head "$prHead" --json > ".spur/run/$__runId-pr-status.json" && [ "$c" -eq 0 ] && [ -f ".spur/run/$__runId-pr-collect.status" ] || printf 'FAIL\n' > ".spur/run/$__runId-pr-collect.status"; cat ".spur/run/$__runId-pr-findings.json" 2>/dev/null; exit 0
222
208
 
223
209
  - id: done
224
210
  description: Terminal — review collected; findings artifact at .spur/run/${vars.__runId}-pr-findings.json
@@ -308,6 +294,8 @@ transitions:
308
294
  guard:
309
295
  kind: shell
310
296
  options:
297
+ # (e) 0825: 4-predicate guard — ALREADY_REQUESTED/REQUESTED pair state is the
298
+ # fallback route when the review driver stalls (mode/noWait honored).
311
299
  command: 'test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = ALREADY_REQUESTED || { test "$(cat .spur/run/$__runId-pr-request.status 2>/dev/null)" = REQUESTED && { test "$mode" = "submit" || test "$noWait" = "true"; }; }'
312
300
  - from: request
313
301
  to: wait