@gobing-ai/spur 0.3.94 → 0.3.96

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 (198) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +42 -0
  3. package/board/index.d.ts +38 -0
  4. package/config/plugin-scripts.json +3 -52
  5. package/config/rules/boundary/sp-script-placement.yaml +19 -0
  6. package/config/rules/strict/runtime-boundaries.yaml +5 -0
  7. package/config/rules/structure/test-location.yaml +2 -0
  8. package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
  9. package/config/rules/typescript/output-boundaries.yaml +15 -2
  10. package/config/script-placement-baseline.json +51 -0
  11. package/config/workflows/feature-verification.yaml +7 -4
  12. package/config/workflows/history-anatomy.yaml +23 -11
  13. package/config/workflows/idea-pipeline.yaml +87 -86
  14. package/config/workflows/pr-review.yaml +18 -9
  15. package/config/workflows/task-pipeline.yaml +84 -109
  16. package/config/workflows/wrapup-pipeline.yaml +173 -46
  17. package/package.json +15 -11
  18. package/plugins/sp/README.md +8 -10
  19. package/plugins/sp/agents/super-reviewer.md +43 -18
  20. package/plugins/sp/commands/dev-fixgha.md +83 -0
  21. package/plugins/sp/commands/dev-gitmsg.md +8 -6
  22. package/plugins/sp/commands/dev-gtd.md +2 -2
  23. package/plugins/sp/commands/dev-idea.md +22 -12
  24. package/plugins/sp/commands/dev-plan.md +8 -9
  25. package/plugins/sp/commands/dev-review.md +22 -13
  26. package/plugins/sp/commands/dev-verify.md +7 -4
  27. package/plugins/sp/commands/dev-verifyall.md +5 -4
  28. package/plugins/sp/commands/dev-wrap.md +1 -1
  29. package/plugins/sp/commands/spur-init.md +2 -2
  30. package/plugins/sp/hooks/context-post-tool.ts +2 -2
  31. package/plugins/sp/hooks/context-session-start.ts +2 -1
  32. package/plugins/sp/hooks/context-session-stop.ts +22 -14
  33. package/plugins/sp/hooks/pi/guard-extension.ts +109 -150
  34. package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
  35. package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
  36. package/plugins/sp/lib/idea-handoff.generated.mjs +6 -5
  37. package/plugins/sp/lib/inline-run.generated.d.mts +13 -2
  38. package/plugins/sp/lib/inline-run.generated.mjs +33 -16
  39. package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
  40. package/plugins/sp/lib/quality-gate.generated.mjs +438 -0
  41. package/plugins/sp/lib/residual-scan.generated.d.mts +62 -0
  42. package/plugins/sp/lib/residual-scan.generated.mjs +210 -0
  43. package/plugins/sp/lib/spur-bin.ts +36 -0
  44. package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
  45. package/plugins/sp/lib/step-profile.generated.mjs +174 -0
  46. package/plugins/sp/plugin.json +1 -1
  47. package/plugins/sp/references/roles.md +1 -1
  48. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
  49. package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
  50. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
  51. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
  52. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
  53. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
  54. package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
  55. package/plugins/sp/scripts/history-anatomy-cache.mjs +23 -22
  56. package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
  57. package/plugins/sp/scripts/inline-run-setup.mjs +90 -315
  58. package/plugins/sp/scripts/inline-run-setup.ts +109 -639
  59. package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
  60. package/plugins/sp/scripts/pr-reviewing.ts +1 -0
  61. package/plugins/sp/scripts/quality-gate.mjs +38 -21
  62. package/plugins/sp/scripts/quality-gate.ts +20 -660
  63. package/plugins/sp/scripts/residual-scan.mjs +138 -159
  64. package/plugins/sp/scripts/residual-scan.ts +102 -499
  65. package/plugins/sp/scripts/script-root.mjs +129 -0
  66. package/plugins/sp/scripts/script-root.ts +199 -0
  67. package/plugins/sp/scripts/workflow-step-profile.mjs +27 -16
  68. package/plugins/sp/scripts/workflow-step-profile.ts +22 -314
  69. package/plugins/sp/scripts/wrapup-drift-probe.mjs +12 -3
  70. package/plugins/sp/scripts/wrapup-drift-probe.ts +6 -3
  71. package/plugins/sp/scripts/wrapup-steps.mjs +42 -40
  72. package/plugins/sp/scripts/wrapup-steps.ts +64 -46
  73. package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
  74. package/plugins/sp/skills/code-verification/SKILL.md +46 -16
  75. package/plugins/sp/skills/code-verification/references/verdict-schema.md +13 -2
  76. package/plugins/sp/skills/functional-review/SKILL.md +8 -5
  77. package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
  78. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
  79. package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
  80. package/plugins/sp/skills/next-router/references/routing-table.md +2 -2
  81. package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
  82. package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
  83. package/plugins/sp/skills/spur-cli/references/features.md +17 -6
  84. package/plugins/sp/skills/spur-cli/references/init.md +17 -16
  85. package/plugins/sp/skills/spur-cli/references/self.md +3 -2
  86. package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
  87. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +28 -5
  88. package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
  89. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
  90. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +11 -9
  91. package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
  92. package/plugins/sp/skills/spur-dev/references/dev-operations.md +86 -70
  93. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +3 -3
  94. package/plugins/sp/skills/spur-dev/references/execution-batch.md +107 -44
  95. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -9
  96. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
  97. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +31 -21
  98. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
  99. package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
  100. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +35 -6
  101. package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
  102. package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
  103. package/plugins/sp/tsconfig.json +8 -0
  104. package/spur.js +6638 -4775
  105. package/web/_astro/BoardApp.BIjMatT1.js +1 -0
  106. package/web/_astro/{BoardApp.FTEs3-N8.js → BoardApp.GvjIe9Z6.js} +150 -153
  107. package/web/_astro/TaskDetail.BZM3EAFx.js +1 -0
  108. package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
  109. package/web/_astro/{arc.uuAf51IT.js → arc.HkRiZnoI.js} +1 -1
  110. package/web/_astro/architectureDiagram-3BPJPVTR.BDdK-tgZ.js +36 -0
  111. package/web/_astro/{blockDiagram-GPEHLZMM.D1mGCq3p.js → blockDiagram-GPEHLZMM.CZ9kOBxx.js} +1 -1
  112. package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
  113. package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
  114. package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
  115. package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
  116. package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
  117. package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
  118. package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
  119. package/web/_astro/{c4Diagram-AAUBKEIU.CMsolcde.js → c4Diagram-AAUBKEIU.DXK9qeRD.js} +1 -1
  120. package/web/_astro/channel.DlhX2MEt.js +1 -0
  121. package/web/_astro/{chunk-2J33WTMH.CrGA3fik.js → chunk-2J33WTMH.BTD2WeX5.js} +1 -1
  122. package/web/_astro/{chunk-4BX2VUAB.DsLVVla0.js → chunk-4BX2VUAB.Ci6Qbcvk.js} +1 -1
  123. package/web/_astro/{chunk-55IACEB6.Dxc59Tfi.js → chunk-55IACEB6.yl3zsj7p.js} +1 -1
  124. package/web/_astro/{chunk-727SXJPM.CaEVE2Wy.js → chunk-727SXJPM.ClXZsfyR.js} +1 -1
  125. package/web/_astro/{chunk-AQP2D5EJ.BiJ4HXeI.js → chunk-AQP2D5EJ.BmKWZBcP.js} +1 -1
  126. package/web/_astro/{chunk-FMBD7UC4.Mxf1fru5.js → chunk-FMBD7UC4.By30fcb7.js} +1 -1
  127. package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
  128. package/web/_astro/{chunk-ND2GUHAM.pyOWQixH.js → chunk-ND2GUHAM.24NmrD-k.js} +1 -1
  129. package/web/_astro/{chunk-QZHKN3VN.BmEsg4vr.js → chunk-QZHKN3VN.BN4sKdcS.js} +1 -1
  130. package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
  131. package/web/_astro/{classDiagram-4FO5ZUOK.BHhhMFTO.js → classDiagram-4FO5ZUOK.CJpoMPb5.js} +1 -1
  132. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BHhhMFTO.js → classDiagram-v2-Q7XG4LA2.CJpoMPb5.js} +1 -1
  133. package/web/_astro/client.BhluLQqe.js +1 -0
  134. package/web/_astro/client.DBpSRLnx.js +9 -0
  135. package/web/_astro/cose-bilkent-S5V4N54A.BraxQ2Nt.js +1 -0
  136. package/web/_astro/{cynefin-OW5HDTMX.C2j1_lKL.js → cynefin-OW5HDTMX.gzoU73oL.js} +1 -1
  137. package/web/_astro/{dagre-BM42HDAG.hZ2NCTdT.js → dagre-BM42HDAG.wyDfWySU.js} +1 -1
  138. package/web/_astro/{diagram-2AECGRRQ.Bfw5_EzK.js → diagram-2AECGRRQ.h2_EbMxV.js} +1 -1
  139. package/web/_astro/{diagram-5GNKFQAL.Bt1V_Tmk.js → diagram-5GNKFQAL.Db6dcs89.js} +1 -1
  140. package/web/_astro/{diagram-KO2AKTUF.DEk-YFwp.js → diagram-KO2AKTUF.NVwzxu_U.js} +1 -1
  141. package/web/_astro/{diagram-LMA3HP47.CDjndtQm.js → diagram-LMA3HP47.B-eNgDQk.js} +1 -1
  142. package/web/_astro/{diagram-OG6HWLK6.cFnUHScG.js → diagram-OG6HWLK6.Bsa8ZRbJ.js} +1 -1
  143. package/web/_astro/{erDiagram-TEJ5UH35.DlhYp7NV.js → erDiagram-TEJ5UH35.BospRE5Q.js} +1 -1
  144. package/web/_astro/{flowDiagram-I6XJVG4X.DNTpsxfx.js → flowDiagram-I6XJVG4X.DMzvHwAA.js} +1 -1
  145. package/web/_astro/ganttDiagram-6RSMTGT7.Dmdxgd-M.js +292 -0
  146. package/web/_astro/{gitGraphDiagram-PVQCEYII.DkEqNI0P.js → gitGraphDiagram-PVQCEYII.CaqdO0rL.js} +1 -1
  147. package/web/_astro/index.5dn-Zm-2.js +1 -0
  148. package/web/_astro/index.C-IqPpW2.js +1 -0
  149. package/web/_astro/index.Cyc_GA4f.js +1 -0
  150. package/web/_astro/index.EoZlzLK-.css +1 -0
  151. package/web/_astro/index.tZfqwSut.js +1 -0
  152. package/web/_astro/{infoDiagram-5YYISTIA.CrjioCTG.js → infoDiagram-5YYISTIA.DDVD9Wn7.js} +1 -1
  153. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BcK0CN8n.js → ishikawaDiagram-YF4QCWOH.EKyCUFsL.js} +1 -1
  154. package/web/_astro/{journeyDiagram-JHISSGLW.p0CbBDX1.js → journeyDiagram-JHISSGLW.By2-RkYB.js} +1 -1
  155. package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
  156. package/web/_astro/{kanban-definition-UN3LZRKU.puHIFt6J.js → kanban-definition-UN3LZRKU.ar-WmlyR.js} +1 -1
  157. package/web/_astro/{linear.Bb-3a1d7.js → linear.BnzMBgo_.js} +1 -1
  158. package/web/_astro/{mermaid.core.CJDgXOJs.js → mermaid.core.DSqeFu2Y.js} +4 -4
  159. package/web/_astro/{mindmap-definition-RKZ34NQL.BoAZEI9t.js → mindmap-definition-RKZ34NQL.1XcF8Wh2.js} +1 -1
  160. package/web/_astro/{pieDiagram-4H26LBE5.CJNSdqY7.js → pieDiagram-4H26LBE5.DnItHOdK.js} +1 -1
  161. package/web/_astro/{quadrantDiagram-W4KKPZXB.DtTy7_0Z.js → quadrantDiagram-W4KKPZXB.BQQyrnBt.js} +1 -1
  162. package/web/_astro/{requirementDiagram-4Y6WPE33.BNYV98Xg.js → requirementDiagram-4Y6WPE33.Dmzxun82.js} +1 -1
  163. package/web/_astro/{sankeyDiagram-5OEKKPKP.DRq9DGHp.js → sankeyDiagram-5OEKKPKP.BBIeRKYB.js} +1 -1
  164. package/web/_astro/{sequenceDiagram-3UESZ5HK.B7KdBFCm.js → sequenceDiagram-3UESZ5HK.CUNVdBBf.js} +1 -1
  165. package/web/_astro/{stateDiagram-AJRCARHV.CD62ZJ2H.js → stateDiagram-AJRCARHV.DlFf2sJm.js} +1 -1
  166. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CeVk1TAj.js → stateDiagram-v2-BHNVJYJU.DXR7I1H5.js} +1 -1
  167. package/web/_astro/{timeline-definition-PNZ67QCA.C_ZPA5tT.js → timeline-definition-PNZ67QCA.BcO-GyX2.js} +1 -1
  168. package/web/_astro/{vennDiagram-CIIHVFJN.CrThO_FQ.js → vennDiagram-CIIHVFJN.ChCECmmW.js} +1 -1
  169. package/web/_astro/{wardleyDiagram-YWT4CUSO.DSZCA5nl.js → wardleyDiagram-YWT4CUSO.CRaT12mD.js} +1 -1
  170. package/web/_astro/{xychartDiagram-2RQKCTM6.KI7baTuk.js → xychartDiagram-2RQKCTM6.B3lsHVpj.js} +1 -1
  171. package/web/board-runtime.json +69 -0
  172. package/web/index.html +2 -2
  173. package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
  174. package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
  175. package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -298
  176. package/plugins/sp/scripts/feature-sync-bounded.ts +0 -479
  177. package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
  178. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
  179. package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
  180. package/plugins/sp/scripts/record-feature-sync.ts +0 -84
  181. package/plugins/sp/scripts/script-contract-check.ts +0 -377
  182. package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
  183. package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
  184. package/plugins/sp/scripts/task-evidence-precheck.ts +0 -187
  185. package/plugins/sp/scripts/task-size-precheck.ts +0 -210
  186. package/plugins/sp/scripts/transition-shim-check.ts +0 -238
  187. package/plugins/sp/scripts/validate-commands.ts +0 -689
  188. package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
  189. package/plugins/sp/scripts/verify-answer-lint.ts +0 -547
  190. package/web/_astro/BoardApp.C02hAHPO.js +0 -1
  191. package/web/_astro/TaskDetail.C-GdsS-t.js +0 -1
  192. package/web/_astro/architectureDiagram-3BPJPVTR.CGe629A1.js +0 -36
  193. package/web/_astro/channel.fsgl7o5j.js +0 -1
  194. package/web/_astro/client.yhYJvxCU.js +0 -9
  195. package/web/_astro/cose-bilkent-S5V4N54A.2fH4YOlp.js +0 -1
  196. package/web/_astro/ganttDiagram-6RSMTGT7.DC_p36PI.js +0 -292
  197. package/web/_astro/index.De90oHcH.js +0 -1
  198. package/web/_astro/index.Hjbr15fG.css +0 -1
@@ -68,13 +68,13 @@ each would be scope creep for one-liner procedures.
68
68
  | # | Operation | Command | Backing | Skill / Verb | Arg-hint |
69
69
  | --- | ---------- | ------------------- | ----------------- | ---------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
70
70
  | 1 | unit | `dev-unit` | `Skill()` | `sp:code-testing` | `<target> [--coverage <pct>] [--agent <inline\|auto\|name>] [--auto]` |
71
- | 2 | review | `dev-review` | `Skill()` | `sp:code-verification` (`review`) + `sp:functional-review` + `sp:code-improvement` | `[<wbs\|path>] [--agent <inline\|auto\|name>] [--focus <dims>] [--triage] [--worktree [<name>]]` |
71
+ | 2 | review | `dev-review` | `Skill()` | `sp:code-verification` (`review`) + `sp:functional-review` + `sp:code-improvement` | `[--tasks <selector> \| --feature <id>[,<id>] \| --scope <path>[,<path>]] [--agent <inline\|auto\|name>] [--focus <dims>] [--triage] [--worktree [<name>]] [--auto]` |
72
72
  | 3 | verify | `dev-verify` | `Skill()` | `sp:code-verification` (`verify`) | `<wbs> [--agent <inline\|auto\|name>] [--fix <none\|blockers-first\|all>] [--focus <lens>] [--bdd] [--auto] [--force] [--next] [--skip-shippable]` |
73
73
  | 3a | verifyall | `dev-verifyall` | `Skill()` → agent | `sp:spur-dev` (`verifyall`) | `--tasks <selector> [--feature <id>] [--agent <inline\|auto\|name>] [--fix <none\|blockers-first\|all>] [--focus <lens>] [--bdd] [--auto] [--force] [--next] [--json] [--skip-shippable] [--worktree [<name>]]` |
74
74
  | 4 | run | `dev-run` | `Skill()` | `sp:spur-dev` (`run` / `implement`) | `<wbs> [--mode <full\|implement>] [--agent <inline\|auto\|name>] [--auto] [--next] [--wrap] [--continue] [--worktree [<name>]]` |
75
75
  | 5 | refine | `dev-refine` | `Skill()` | `sp:spur-dev` (`refine`) | `<wbs> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--next]` |
76
76
  | 5a | refineall | `dev-refineall` | `Skill()` | `sp:spur-dev` (`refineall`) | `--feature <id> \| --tasks <selector> [--focus <mode>] [--description <text>] [--depth <standard\|ready>] [--agent <inline\|auto\|name>] [--auto] [--keep-going] [--status <s>] [--json] [--worktree [<name>]]` |
77
- | 6 | plan | `dev-plan` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<description>" [--feature <id>] [--parent <feature-id>] [--agent <inline\|auto\|name>] [--skip-design] [--auto] [--approve-taste]` |
77
+ | 6 | plan | `dev-plan` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<description>" [--feature <id>] [--parent <feature-id>] [--skip-design] [--agent <inline\|auto\|name>] [--auto]` |
78
78
  | 7 | docs | _(no thin wrapper)_ | `Skill()` | `sp:doc-evolve` | `"<change description>"` |
79
79
  | 8 | changelog | `dev-changelog` | `inline` | git log + conventional-commit grouping | `[--since <ref>] [--until <ref>] [--version <ver>]` |
80
80
  | 9 | gitmsg | `dev-gitmsg` | `inline` | bounded diff capture → concern grouping → conventional commit | `[--commit] [--squash] [--all] [--scope <path>]` |
@@ -85,7 +85,7 @@ each would be scope creep for one-liner procedures.
85
85
  | 13a | parallel | `dev-parallel` | `Skill()` | `sp:parallel-execution` | `--tasks <selector> [--feature <id>] [--mode <fan-out\|review-panel\|investigation>] [--agent <inline\|auto\|name>] [--json]` |
86
86
  | 14 | wrap | `dev-wrap` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `<wbs> [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
87
87
  | 15 | wrapall | `dev-wrapall` | `Skill()` | `spur workflow run` (wrapup-pipeline) | `[--since <iso>] [--feature <id>] [--status <s>] [--agent <inline\|auto\|name>] [--auto] [--merge] [--dry-run]` |
88
- | 16 | idea | `dev-idea` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<idea>" [--from-file <path>] [--auto] [--skip-design] [--approve-taste] [--agent <inline\|auto\|name>]` |
88
+ | 16 | idea | `dev-idea` | `Skill()` | inline driver (`idea-pipeline`) or async workflow | `"<idea>" \| --from-file <path> [--skip-design] [--agent <inline\|auto\|name>] [--auto]` |
89
89
  | 17 | refactor | `dev-refactor` | `Skill()` | `sp:code-refactoring` skill (thin wrapper, ADR-032) | `[<description>] [--scope <path>] [--focus <api\|architect\|tests\|ui\|auto>] [--fix <none\|blockers-first\|all>] [--check <cmd>] [--agent <inline\|auto\|name>] [--auto]` |
90
90
 
91
91
  ## Skill-backed operations
@@ -112,17 +112,19 @@ must not be changed without updating the backing skill.
112
112
 
113
113
  ### 2. review
114
114
 
115
- - **Purpose:** Multi-dimensional code review of a task or path — (1) functional requirements traceability (WBS mode only), (2) SECUA framework (Security, Efficiency, Correctness, Usability, Architecture), (3) architecture depth.
115
+ - **Purpose:** Multi-dimensional code review of a task, a task set, or paths — (1) functional requirements traceability (per task target), (2) SECUA framework (Security, Efficiency, Correctness, Usability, Architecture), (3) architecture depth.
116
116
  - **Modes:**
117
- - **WBS mode (`<wbs>`)**: Runs functional requirements traceability (`sp:functional-review`), SECUA framework (`sp:code-verification`), and architectural depth (`sp:code-improvement`). The three skills return review fragments; the coordinator writes the combined `## Review` (F92 0593 R1).
118
- - **Path mode (`<path>`)**: Runs advisory SECUA framework (`sp:code-verification`) and architectural depth (`sp:code-improvement`). Performs no task mutation.
119
- - **Inputs:** `<wbs|path>` (required). Review executes inline (in-session) by default. `--agent <inline|auto|name>` selector accepted (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--focus <lens>` narrows to one SECUA dimension. `--triage` opts into bounded remediation after the report (below). `--worktree [<name>]` isolates the triage writes (below; requires `--triage`). Note: `--fix` and `--next` are **deprecated** (no-op with warning; route remediation to `--triage` or `/sp:dev-verify --fix` and progression to `/sp:dev-next`). The task pipeline's review stage never passes `--triage` or `--worktree`.
117
+ - **Task-set mode (`--tasks <selector>` / `--feature <id>[,<id>]`)**: resolves the set once with the batch selector grammar ([execution-batch.md § Step 1](execution-batch.md#step-1--selector-resolution-r1)) — comma WBS list or `feature:<id>` (the `--feature` comma list is union sugar); status pseudo-lists and `ready` are rejected for review. One WBS-mode review per implemented task, each writing its own merged `## Review` (F92 0593 R1); tasks in `backlog`/`todo`/`blocked` are reported NOT-STARTED and skipped (§ 3a vocabulary); a per-task failure does not stop the remaining tasks; the run ends with a combined summary table (WBS, verdict, P1/P2 counts).
118
+ - **WBS mode (`<wbs>`, the per-task recipe)**: Runs functional requirements traceability (`sp:functional-review`), SECUA framework (`sp:code-verification`), and architectural depth (`sp:code-improvement`). The three skills return review fragments; the coordinator writes the combined `## Review` (F92 0593 R1).
119
+ - **Path mode (`<path>`, the per-path recipe)**: Runs advisory SECUA framework (`sp:code-verification`) and architectural depth (`sp:code-improvement`). Performs no task mutation.
120
+ - **Path-set mode (`--scope <path>[,<path>]`)**: paths must exist (exit 2 otherwise), are normalized, duplicates dropped and nested paths collapse to the ancestor (`--scope apps,apps/cli` → `apps`); one advisory sub-review per surviving path (Step 3p path scope, eligible for native-subagent dispatch per [dispatch-surface.md](../../parallel-execution/references/dispatch-surface.md)); the coordinator merges the sub-reviews, runs one cross-path architecture pass (`sp:code-improvement` over the inter-path imports), and emits one merged advisory report. No task mutation.
121
+ - **Inputs:** exactly one of `--tasks <selector>`, `--feature <id>[,<id>]`, or `--scope <path>[,<path>]` (deprecated positional alias, below) — more than one exits 2 naming the conflict; none exits 2 with usage (no implicit `cwd` target). Review executes inline (in-session) by default. `--agent <inline|auto|name>` selector accepted (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--focus <dims>` narrows the review dimensions — vocabulary SSOT: [code-verification/SKILL.md](../../code-verification/SKILL.md) review mode (`functional` → `sp:functional-review`; `architecture` → `sp:code-improvement` + SECUA-A; S/E/C/U → `sp:code-verification`). `--auto` skips objective HITL confirmations (the pipeline's review stage passes it). `--triage` opts into bounded remediation after the report (below). `--worktree [<name>]` isolates the triage writes (below; requires `--triage`). Note: the positional `<wbs\|path>` is a **deprecated alias** for one release — a `^\d{4}$` token with a resolvable task → `--tasks`; an existing path → `--scope`; otherwise exit 2, with a deprecation warning naming the replacement. `--fix` is **deprecated** (accepted no-op with warning; route remediation to `--triage` or `/sp:dev-verify --fix`); `--next` is **removed** (route progression to `/sp:dev-next`). The task pipeline's review stage never passes `--triage` or `--worktree`.
120
122
  - **Triage (`--triage`):** runs inline in the invoking session after the merged report — never inside `sp:super-reviewer` or the review skills, which stay report-only. With `--agent <name>`, only the review is delegated; triage still runs inline on the returned findings. Never fix straight from the raw findings list:
121
123
  1. **Bucket every finding exactly once.** **Direct fix** — local, low-risk, obvious root cause: no design choice, no public-surface / schema / dependency change, not a shared write path, verifiable by a targeted check (docs, a guard, a missing throw, a wrong path helper, a stale comment). **Task** — real and actionable but needs design, crosses modules, touches a public surface, or is not verifiable locally; skip it when an existing task already owns it (cite that WBS). **Note** — pre-existing, environmental, or ownerless; report only.
122
124
  2. **Apply direct fixes** — smallest surgical diff in project style, then re-verify each with its targeted check (focused test, lint, or the exact command that exhibited the defect). A fix that grows beyond the bar or fails its check moves to the Task bucket with what was learned; do not leave it half-applied.
123
- 3. **File the Task bucket as one or more tasks**, one per cohesive unit of work that one agent can implement and verify in one run — split unrelated fixes, merge findings that share a root cause or file set. File under the existing feature that owns the affected surface; if none fits, create a child feature under the closest existing parent — never a new root feature. Write through the CLI-gated corpus surface only (`spur task create --feature <id> --skip-ready`, then `spur task update <wbs> --section <s> --from-file`). Each task must meet the implement-ready bar ([§ 5. refine](#5-refine), `--depth ready`) so it cannot drift: per finding the evidence (`file:line`), the defect, the chosen fix direction with rejected alternatives, file targets, AC, and out-of-scope; promote `backlog → todo` only when `spur task check <wbs> --json` is clean. Name what the direct fixes already resolved in each task's Background.
125
+ 3. **File the Task bucket as one or more tasks**, one per cohesive unit of work that one agent can implement and verify in one run — split unrelated fixes, merge findings that share a root cause or file set. File under the existing non-terminal feature that owns the affected surface (a `done` owner blocks `todo` promotion with `L4.feature-terminal` — reopen it with `spur feature update <id> active` when its goal still covers the work); if none fits, create a child feature under the closest existing parent — never a new root feature. Write through the CLI-gated corpus surface only (`spur task create --feature <id> --skip-ready`, then `spur task update <wbs> --section <s> --from-file`). Each task must meet the implement-ready bar ([§ 5. refine](#5-refine), `--depth ready`) so it cannot drift: per finding the evidence (`file:line`), the defect (a root cause not reproduced in this session is written as a labeled hypothesis with its confirming repro, never as the fix premise), the chosen fix direction with rejected alternatives, file targets, AC, and out-of-scope; promote `backlog → todo` only when `spur task check <wbs> --json` is clean. Name what the direct fixes already resolved in each task's Background.
124
126
  4. **Gate and report.** Run the project gate once after the direct fixes. Report a Triage section: each applied fix (path + one-line what + verification), each filed task (WBS + feature + scope), and the Notes. Never commit or create a branch outside `--worktree`.
125
- - **Worktree (`--worktree [<name>]`):** omitted → all writes land in the current working tree on the current branch; no branch creation, checkout, or commit (the operator commits). Given → the [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) lifecycle as a run of one: marker `command` = `dev-review`, `selector` = the review target, derived branch/directory slug = the WBS or the path's basename (`sp/review-<slug>-<short-id>`). Admission is "the target resolves" (a known WBS or an existing path) — no `quickReadiness` task-set check. Review and triage both run in the tree; WT-3b commits the fixes and filed tasks. The WT-4 success condition reads as "every direct fix passed its check and the project gate is green"; a failed check or gate takes the WT-5 retention path. Rejected without `--triage` (WT-7) — to review another worktree read-only, pass a path inside it.
127
+ - **Worktree (`--worktree [<name>]`):** omitted → all writes land in the current working tree on the current branch; no branch creation, checkout, or commit (the operator commits). Given → the [execution-batch.md § Worktree isolation](execution-batch.md#worktree-isolation---worktree-name) lifecycle as a run of one: marker `command` = `dev-review`, `selector` = the full normalized target list, derived branch/directory slug = `sp/review-<first>-and-<N>-<short-id>` for a multi-target run (N = target count) or `sp/review-<slug>-<short-id>` for a single target (the WBS or the path's basename). Admission is "every target resolves" (each a known WBS or an existing path) — no `quickReadiness` task-set check. Review and triage both run in the tree; WT-3b commits the fixes and filed tasks, bucketed across all targets once (identical `file:line` findings deduped). The WT-4 success condition reads as "every direct fix passed its check and the project gate is green"; a failed check or gate takes the WT-5 retention path. Rejected without `--triage` (WT-7) — to review another worktree read-only, pass a path inside it.
126
128
  - **Backing:** `sp:functional-review`, `sp:code-verification` (review mode), `sp:code-improvement`.
127
129
  - **Behavior:** WBS mode runs functional traceability + SECUA + architecture depth, ranking findings P1–P4, and hands the merged report to the review coordinator, which writes `## Review`. Component skills never write `## Review` in coordinated mode. Path mode runs advisory SECUA + architecture depth with no task mutation.
128
130
  - **Delegation:** WBS mode: `sp:functional-review` + `sp:code-verification` (review) + `sp:code-improvement`; Path mode: `sp:code-verification` (review) + `sp:code-improvement`.
@@ -322,7 +324,7 @@ must not be changed without updating the backing skill.
322
324
  - **Inputs:** `"<description>"` (required). `--feature <id>` links to an existing feature. `--parent <feature-id>` nests under a parent. Omitted/`inline` drives `idea-pipeline.yaml` in the host session; `auto` or a name launches the async workflow worker. **Design package flags (unified with `/sp:dev-idea`):**
323
325
  - **Default:** author task `design` on every batch item + feature satellite when the seam heuristic fires (**ties lean design**). There is **no** `--design` force flag.
324
326
  - `--skip-design` — skip feature satellite **and** omit task `design` fields (scaffold only; refine fills later). Sole design opt-out.
325
- - `--approve-taste` — with `--auto`, pre-clear design-approval taste pause when that gate is used (`design_approved=true`). Alias: `--design-approved`.
327
+ - `--auto` — accept the recommendation at every operator gate, taste gates included (same contract as § 16 idea).
326
328
  - **Backing:** `sp:spur-dev` skill, `plan` operation. Stage floor: the `planner` role per [`roles.md`](../../../references/roles.md) — this file names roles, never tiers (0538 R4).
327
329
  - **Behavior:** Clarify scope → `spur feature create` → author BDD AC → `spur feature check` gate → decompose into task-batch JSON **including `design` (unless `--skip-design`)** → `spur task batch-create` gate. Design package details: [planning-workflow.md](planning-workflow.md) Step 5.5.
328
330
  - **Delegation:** `Skill(skill="sp:spur-dev", args="plan $ARGUMENTS")`
@@ -369,7 +371,7 @@ must not be changed without updating the backing skill.
369
371
  - **Purpose:** Wrap up a single completed task — capture learnings, record metrics, sync docs, and optionally advance the feature / clean up the branch.
370
372
  - **Inputs:** `<wbs>` (required, positional). `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations (the branch-cleanup HITL gate still pauses — irreversible). `--merge` triggers branch cleanup (irreversible HITL gate).
371
373
  - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation (no backing skill; the pipeline IS the procedure).
372
- - **Behavior:** Resolves the executor (`agent.default` for omit/`inline`, tier-resolved for `auto`, unchanged for a name), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[\"<wbs>\"]","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs: task-resolve → doc-sync → learning-capture → metrics-record → (feature-transition) → (branch-cleanup) → done. Task statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
374
+ - **Behavior:** Resolves the executor (`agent.default` for omit/`inline`, tier-resolved for `auto`, unchanged for a name), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[\"<wbs>\"]","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs: task-resolve → doc-sync → learnings-validate → learnings-append → doc-tripwire → metrics-record → (feature-transition) → (branch-cleanup) → done. `doc-tripwire` (1037) runs the TRUSTED CONFIG ONLY `docTripwireCmd` over the still-uncommitted wrap diff after the doc-sync exits converge — the default runs `bun run test-repo-wide` only when `package.json` declares the script (a no-op in other projects); override it via `--vars`, and an empty string disables the check while still recording PASS; a FAIL fails the wrap with already-written learnings/docs preserved. Task statuses are NOT mutated. Branch cleanup is an irreversible HITL gate that always pauses, even under `--auto`.
373
375
  - **Vars string typing:** `tasks` is a JSON-encoded **string**, not a JSON array — `spur workflow run --vars` accepts only string values (`--vars values must be strings`); the pipeline's guards parse the string with `jq length`. `jq -nc` guarantees the shape:
374
376
 
375
377
  ```bash
@@ -385,7 +387,7 @@ must not be changed without updating the backing skill.
385
387
  - **Purpose:** Wrap up a batch of completed tasks — capture learnings, record metrics, sync docs, advance a feature through legal lifecycle edges, and optionally clean up branches.
386
388
  - **Inputs:** `--since <iso-date>` filters done tasks by frontmatter `updated_at >= date` (v1 approximation). `--feature <id>` selects all tasks under a feature AND advances the feature through legal lifecycle edges (`backlog → active → verifying → done`, guards honored). `--status <s>` (default: `done`) filters by task status. `--agent <inline|auto|name>` names the wrap's model-bearing executor (default: `inline`, resolving identically when omitted; task 0687); wrap is workflow-backed (headless), so omit ≡ explicit `inline` (task 0687): tier substitution under objective trigger 3 (durable auditable run record required) plus one warning naming the substituted executor. `auto` tier-resolves an executor, and a name pins that executor into `vars.agent` (see [SSOT](cross-cutting.md#inline-default-execution-surface)). `--auto` skips objective confirmations. `--merge` triggers branch cleanup (irreversible HITL gate).
387
389
  - **Backing:** `spur workflow run wrapup-pipeline.yaml` — direct workflow invocation.
388
- - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (omission and explicit `--agent inline` tier-substitute identically with a warning per task 0687, `auto` tier-resolves, a name pins), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[...]","feature":"<id>","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` but with the full task list and optional feature transition. Task statuses are NOT mutated. Feature transitions go through `spur feature update` so lifecycle guards apply. Branch cleanup is an irreversible HITL gate.
390
+ - **Behavior:** Resolves the task list via `spur task list --json` (filtered by `--feature`, `--since`, `--status`), resolves the executor (omission and explicit `--agent inline` tier-substitute identically with a warning per task 0687, `auto` tier-resolves, a name pins), emits a pre-dispatch notice naming the subprocess override — `execution surface: subprocess`, `reason: trigger 3 — durable auditable run record required`, `requested agent: <selector>`, `executor: <resolved>` — then builds `--vars '{"tasks":"[...]","feature":"<id>","agent":"<resolved>","profile":"interactive|auto","merge":"true|false"}'` and invokes the wrapup pipeline. The pipeline runs the same states as `wrap` — including the `doc-tripwire` hop (1037), whose TRUSTED CONFIG ONLY `docTripwireCmd` runs the repo-wide tripwires over the still-uncommitted wrap diff and can be overridden or disabled (empty string) via `--vars` — but with the full task list and optional feature transition. Task statuses are NOT mutated. Feature transitions go through `spur feature update` so lifecycle guards apply. Branch cleanup is an irreversible HITL gate.
389
391
  - **Vars string typing:** `tasks` is a JSON-encoded **string**, not a JSON array — `--vars` values must be strings (the CLI rejects raw arrays); `jq -nc` passes the array text through as a string value:
390
392
 
391
393
  ```bash
@@ -400,11 +402,14 @@ must not be changed without updating the backing skill.
400
402
 
401
403
  - **Purpose:** Turn a vague idea into a feature with AC and a decomposed task batch — the unified entry point for the planning half.
402
404
  - **Inputs:** `"<idea>"` (quoted) or `--from-file <path>` — exactly one; the two are mutually
403
- exclusive (0887 R7). Three everyday axes:
404
- - `--auto` — skip **objective** HITL (feature-check, batch-create); taste gates still pause.
405
- - `--skip-design` — design package off (system-design + task Design).
406
- - `--approve-taste` — with `--auto`, skip **all** remaining taste pauses this run (idea-eval + design-approval). Sets `idea_approved=true` and `design_approved=true`.
407
- Aliases (prefer `--approve-taste`): `--idea-approved` → `idea_approved`; `--design-approved` → `design_approved`. There is **no** `--design` force flag.
405
+ exclusive (0887 R7). Two everyday axes:
406
+ - `--auto` — accept the recommendation at **every** operator gate (objective and taste):
407
+ sets `profile=auto`, `idea_approved=true`, `design_approved=true`. idea-eval follows the
408
+ report's `## Recommendation` (`proceed`/`reshape` continue, `drop` cancels, missing still
409
+ asks); design-approval approves only on a PASS design check (FAIL still asks).
410
+ - `--skip-design` — design package off (system-design + task Design). There is **no** `--design` force flag.
411
+ - Removed: `--approve-taste`, `--idea-approved`, `--design-approved` (folded into `--auto`;
412
+ ignored with a one-line notice if passed).
408
413
  - `--from-file <path>` — read the idea text from a file instead of the positional argument
409
414
  (verbatim; long/multiline asks). Mutually exclusive with `"<idea>"`.
410
415
  - **Verbatim idea artifact:** before `start` executes, the driver persists the idea argument (or
@@ -414,7 +419,7 @@ must not be changed without updating the backing skill.
414
419
  - **Backing:** `idea-pipeline.yaml` through the inline driver for omitted/`inline`, or `spur workflow run idea-pipeline.yaml --async` for `auto`/name.
415
420
  - **Behavior:** Builds vars from the table above and drives the idea pipeline. Flow: discovery → **idea-eval** (taste; reject → cancelled) → feature-create → ac-generate → feature-check → system-design (conditional) → design-approval (taste) → decompose → batch-create (`--skip-ready`) → ready-prepare (ready checklist per created task + ready-evidence sidecar, 0788) → handoff. STOPS at handoff — no task execution, no pipeline nesting. Headless runs use one `trace --follow`; cancellation is reported stopped only when `workflow cancel --json` returns `killed: true`.
416
421
  - **Delegation:** Host-session inline driver by default; explicit executor selection uses the async workflow worker.
417
- - **Idea-evaluation gate:** After discovery, operator reviews `.spur/run/idea-eval-report.md` ([idea-evaluation.md](idea-evaluation.md)). Approve continues; reject/cancel → no feature. Under `--auto`, still pauses unless taste pre-cleared (`--approve-taste` / alias). Enhanced idea is a sidecar — `vars.idea` is not overwritten.
422
+ - **Idea-evaluation gate:** After discovery, operator reviews `.spur/run/idea-eval-report.md` ([idea-evaluation.md](idea-evaluation.md)). Approve continues; reject/cancel → no feature. Under `--auto`, follows the report's recommendation (`drop` → cancelled; missing recommendation → pauses). Enhanced idea is a sidecar — `vars.idea` is not overwritten.
418
423
  - **Design package (`--skip-design` only):**
419
424
 
420
425
  | Flags | Feature satellite (`system-design`) | Task `### Design` in batch |
@@ -425,7 +430,7 @@ must not be changed without updating the backing skill.
425
430
  Ties lean design — when the signal is ambiguous, `system-design` runs. Task Design defaults on
426
431
  unless `--skip-design`. Plan path uses the same package contract (no `--design` force flag).
427
432
 
428
- - **Taste pre-clear (`--approve-taste`):** owned with design-approval var semantics in [cross-cutting.md](cross-cutting.md) § "Design Approval Gate"; idea-eval uses the parallel `idea_approved` var. One CLI flag sets both.
433
+ - **Operator gates:** every pause is one `AskUserQuestion` [decision brief](decision-brief.md) — recommended option first; see the gate table in `commands/dev-idea.md` and the `hitl.confirm` rule in [inline-pipeline-driver.md](inline-pipeline-driver.md).
429
434
 
430
435
  ### 17. refactor
431
436
 
@@ -460,73 +465,84 @@ is the procedure. The backing is a combination of git CLI, `spur` CLI, and agent
460
465
 
461
466
  ### 9. gitmsg
462
467
 
463
- - **Purpose:** Generate conventional commit message(s) for the current change set — one bounded diff capture → concern grouping → one message per concern; optionally commit.
468
+ - **Purpose:** Generate conventional commit message(s) for the current change set — one gather call → concern grouping → one message per concern; optionally commit. Git + POSIX shell only; no helper script.
464
469
  - **Inputs:** `--scope <path>` (default: the whole change set) — path filter; an explicit `--scope` always wins over change-set auto-discovery. `--all` (default: off) — widen the change set past the index to every change in the tree: unstaged **and untracked** files. Omitted, the change set is the index only, which keeps pre-commit semantics stable. `--commit` (default: off) — commit the change set, one commit per concern. `--squash` (default: off) — collapse every concern into one message and one commit; implies `--commit`.
465
- - **Backing:** `inline` — bounded diff capture + concern grouping + conventional commit formatting.
470
+ - **Backing:** `inline` — one bounded gather call + concern grouping + conventional commit formatting.
466
471
  - **Behavior:**
467
472
 
468
- 1. **Gather once — a single shell round trip.** No temp file, no second read, no follow-up `git` call for context:
473
+ 1. **Gather — exactly one Bash call.** Set the three variables from the flags and run the block
474
+ verbatim. It resolves the change set, applies the empty-index rule, and prints everything the
475
+ message needs; the guards run in shell, not in the model:
469
476
 
470
477
  ```bash
471
- # default: RANGE=--cached (the index)
472
- # --all: RANGE=HEAD (plus untracked, listed below)
473
- git rev-parse --abbrev-ref HEAD --git-dir --git-common-dir
474
- git diff $RANGE --stat $PATHSPEC
475
- git diff $RANGE --name-status $PATHSPEC
476
- git diff $RANGE -U0 $PATHSPEC ':(exclude)*.lock' ':(exclude)*lock.json' ':(exclude)*.lockb' | head -c 60000
477
- # --all only:
478
- git ls-files --others --exclude-standard $PATHSPEC
478
+ bash <<'EOF'
479
+ ALL=0 COMMIT=0 SCOPE=':/' # --all → ALL=1 · --commit/--squash → COMMIT=1 · --scope <p> → SCOPE=<p>
480
+ EX=(':!*.lock' ':!*lock.json' ':!*.lockb')
481
+ untracked() { git ls-files -o --exclude-standard -- "$SCOPE" "${EX[@]}"; }
482
+ if [ "$ALL" = 0 ] && git diff --cached --quiet -- "$SCOPE"; then
483
+ t=$(git diff --name-only HEAD -- "$SCOPE" | wc -l | tr -d ' '); u=$(untracked | wc -l | tr -d ' ')
484
+ [ "$COMMIT" = 1 ] && { echo "STOP index empty ($t tracked, $u untracked changed) — re-run with --all"; exit; }
485
+ echo "NOTE index empty — reading the whole tree instead"; ALL=1
486
+ fi
487
+ [ "$ALL" = 1 ] && R=HEAD || R=--cached
488
+ stream() {
489
+ git diff $R -U0 --diff-filter=d -- "$SCOPE" "${EX[@]}"
490
+ [ "$ALL" = 1 ] && untracked | while IFS= read -r f; do git diff --no-index -U0 -- /dev/null "$f"; done
491
+ }
492
+ [ -z "$(stream | head -c1)" ] && { echo "STOP no changes in $SCOPE"; exit; }
493
+ echo "## context"; git rev-parse --abbrev-ref HEAD --git-dir --git-common-dir --show-superproject-working-tree
494
+ echo "## recent subjects"; git log -12 --format=%s
495
+ echo "## stat"; git diff $R --stat=100 --compact-summary -- "$SCOPE"
496
+ [ "$ALL" = 1 ] && untracked | sed 's/^/ (untracked) /'
497
+ [ "$ALL" = 0 ] && { echo "## partially staged"; comm -12 <(git diff --cached --name-only | sort) <(git diff --name-only | sort); }
498
+ echo "## secret hits"
499
+ stream | awk '/^\+\+\+ /{f=substr($0,7)} /^\+[^+]/{print f": "$0}' \
500
+ | grep -E '(password|passwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)["'"'"']?[[:space:]]*[:=][[:space:]]*["'"'"'][^"'"'"'[:space:]]{12,}|BEGIN [A-Z ]*PRIVATE KEY|(ghp_[A-Za-z0-9]{36}|github_pat_[A-Za-z0-9_]{40,}|sk-[A-Za-z0-9_-]{20,}|AKIA[0-9A-Z]{16})' | head -5
501
+ echo "## diff (-U0, ≤80 lines/file, ≤40 KB)"
502
+ stream | awk '/^diff --git/{n=0} n++<80' | head -c 40000
503
+ EOF
479
504
  ```
480
505
 
481
- `$PATHSPEC` is `-- <path>` when `--scope` is given, empty otherwise.
506
+ What the block guarantees:
507
+ - **Untracked files are read, not just named** — under `--all` each one is diffed against `/dev/null`, so a new file (often the main change) shapes the message.
508
+ - **Every file gets a share** — the per-file 80-line cap runs before the 40 KB total cap, so one large file cannot starve the rest. Deleted files contribute only their `(gone)` stat line; lockfiles are excluded.
509
+ - **Empty index, no `--all`:** a message-only run widens to the whole tree and says so (`NOTE`); a committing run prints `STOP` with the counts — re-print the exact re-run line (`/sp:dev-gitmsg <same flags> --all`) and stop. Committing untracked files is never inferred.
510
+ - **`STOP no changes`** → report it and stop.
482
511
 
483
- **Empty index, no `--all`** — the dead-end this command used to hand back. Reading is
484
- not a mutation, so the two cases split there:
485
- - **No committing flag** → re-gather once with `--all` semantics and say so
486
- (`index empty — read the whole tree instead`). A message-only run has nothing to lose by
487
- looking wider, and the operator gets an answer instead of an errand.
488
- - **`--commit` / `--squash`** → stop. Print the worktree counts (`N tracked, M untracked`) and
489
- the exact re-run line (`/sp:dev-gitmsg <the same flags> --all`). Committing
490
- untracked files is never inferred from an empty index — that is the operator's call, and it
491
- is now one paste away rather than a guess.
512
+ 2. **Stay inside the budget.** The block's output is the whole input. Never run a second, wider `git diff`, `git show`, or file `Read` to "see more"; never paste hunks into the output. A truncated file is typed from its stat line and path.
492
513
 
493
- Empty under `--all`, or still empty after the widen → report `no changes in <scope>` and stop.
514
+ 3. **Group by concern.** A concern is one coherent change a reviewer would accept or revert as a unit:
515
+ - Tests, docs, and registry/index updates travel **with the code they cover** — not as separate `test`/`docs` groups. A standalone `test`/`docs` group exists only when nothing else in the change set motivates it.
516
+ - Behavior changes and pure formatting/refactor churn are **different** concerns.
517
+ - Prefer fewer groups; a single group is the common case. Order groups by dependency (refactor before the feature that uses it).
494
518
 
495
- 2. **Stay inside the budget.** `-U0` (no context lines) plus the lockfile exclusions plus the 60 KB cap is the token contract — never re-run the diff with context to "see more", never paste diff hunks into the output. If the cap truncated the diff, say so and derive the message from `--stat` + `--name-status` alone; a large mechanical change rarely needs hunk detail to be typed and scoped correctly.
519
+ 4. **Write each message.**
520
+ - **Type** from the dominant change: `feat` · `fix` · `refactor` · `perf` · `docs` · `test` · `style` · `build` (deps, bundling) · `ci` (workflows, pipelines) · `chore` (other tooling/config) · `revert`.
521
+ - **Scope** — reuse a scope that appears in `## recent subjects` for the same area (the repo's own vocabulary beats a guessed one); `--scope <path>` overrides. Omit the scope rather than invent one.
522
+ - **Summary** — imperative, ≤72 chars, lowercase first word, no period; says what changed in user/maintainer terms, not which files.
523
+ - **Body** — only when the *why* is not obvious from the summary: intent and consequence in 1–3 lines, never a restated diff or file list. `--squash` → one combined message (dominant type/scope, one body bullet per concern).
496
524
 
497
- 3. **Summarize only what shapes the message.** One sentence — what changed and _why_, not a line count — for each file whose change is not obvious from its path and status. Skip the obvious ones (generated files, lockfiles, pure renames, `docs/**` under a `docs` group). Past ~12 interesting files, summarize per directory instead of per file.
525
+ 5. **Report.** Print: one context line (branch; `linked worktree` when `--git-dir` ≠ `--git-common-dir` and the superproject line is empty), then each message in a fenced block. No per-file summaries, no restated diff. With neither `--commit` nor `--squash`, add one copy-paste `git commit` line per message and stop.
498
526
 
499
- 4. **Group by concern**, and for each group derive type, scope, message:
500
- - Type from the dominant change — `feat` (new functionality) · `fix` (bug fix) · `refactor` (restructuring, no behavior change) · `docs` (documentation only) · `chore` (build/config/tooling) · `perf` · `test` · `style`.
501
- - Scope from the affected module/package (`cli`, `domain`, `server`, `web`, `app`, …); `--scope` overrides.
502
- - Message:
527
+ 6. **Commit (`--commit` / `--squash`) — one more Bash call.** `--squash` implies `--commit`, so both together is just `--squash`. Refuse before touching the index when:
528
+ - `## secret hits` is non-empty → print each `file: line`, commit nothing;
529
+ - `--commit` would split (more than one group) and `## partially staged` is non-empty → splitting would widen those `git add -p` stagings to whole files; print the paths and commit nothing (`--squash` is allowed — it commits the index as staged).
503
530
 
504
- ```
505
- <type>(<scope>): <summary>
531
+ Otherwise run the commits in one heredoc — `--all` first stages the scope (`git add -A -- "$SCOPE"`); a single group or `--squash` is one `git commit -F -`; multiple groups commit in dependency order:
506
532
 
507
- <body — why, only when the why is not obvious>
508
- ```
509
-
510
- Summary: imperative mood, ≤72 chars, lowercase first word, no period. Body explains intent and consequence; it never restates the diff. Behavior changes and pure formatting/refactor churn are **different concerns** — never one group.
511
-
512
- 5. **Resolve the message shape without a round trip to the operator.** `--squash` → one combined
513
- message (dominant type/scope, one body bullet per group). Otherwise one message per group, in
514
- dependency order (refactor before the feature that uses it); a single group is that same rule
515
- with one group.
516
-
517
- 6. **Report, then commit if asked.** Print the resolved message(s), a copy-paste `git commit` line per message, and a one-line context header: current branch, and `linked worktree` when `--git-dir` differs from `--git-common-dir` and `git rev-parse --show-superproject-working-tree` is empty (a non-empty result means submodule, not worktree). With neither `--commit` nor `--squash`, stop here — the operator commits.
518
-
519
- 7. **Committing (`--commit` / `--squash`).** The two flags name outcomes, not dimensions: `--commit`
520
- commits **by concern**, `--squash` commits **everything as one**. `--squash` implies `--commit`,
521
- so `--commit --squash` is just `--squash`, never an error.
533
+ ```bash
534
+ bash <<'EOF'
535
+ git reset -q -- :/ && git add -A -- <group-1 paths> && git commit -q -F - <<'MSG'
536
+ <group-1 message>
537
+ MSG
538
+ # …repeat per group…
539
+ git log --oneline -<n>; git status --short
540
+ EOF
541
+ ```
522
542
 
523
- First scan the captured diff's added lines for credentials (`password`, `secret`, `api[_-]?key`, `token`, `BEGIN [A-Z ]*PRIVATE KEY`, long base64-looking literals). On a hit: print the offending `file:line`, commit nothing, stop. Otherwise:
524
- - `--all` → `git add -A $PATHSPEC` first (this is what stages the untracked files).
525
- - `--squash`, or `--commit` on a single group → `git commit -m "$MESSAGE"`.
526
- - `--commit` on multiple groups → **commit each group in sequence**: record the full staged file list once, then per group `git reset -q -- <all staged paths>` → `git add -- <that group's paths>` → `git commit -m "<that group's message>"`. This is the split; it needs no re-run and no re-staging by the operator.
527
- - **Partial-staging guard:** if any path appears in _both_ `git diff --cached --name-only` and `git diff --name-only` (a `git add -p` staging), the sequence above would silently widen those commits to the whole file. Do not split — report the affected paths and commit nothing unless `--squash` was given.
543
+ Hooks stay enabled; a failing hook stops the run with its output — never `--no-verify`.
528
544
 
529
- - **Invariants:** With neither `--commit` nor `--squash`, never runs `git commit` — message only. Neither flag commits across a credential hit, and `--commit` never splits across a partial staging. Without `--all` the change set is the index, so plain `/sp:dev-gitmsg` keeps pre-commit semantics; only `--all` reaches unstaged and untracked files, and only a committing run (`--commit` / `--squash`) stages them. The empty-index widen is read-only and always announced — a committing run never widens its own scope. An explicit `--scope <path>` always bounds the change set, with or without `--all`. One diff capture per run, bounded and context-free — never a second, wider read.
545
+ - **Invariants:** Git and POSIX shell only — no helper script, no temp file. At most two Bash calls: gather, then (only when committing) commit. With neither `--commit` nor `--squash`, never runs `git commit` — message only. Neither flag commits across a secret hit, and `--commit` never splits across a partial staging. Without `--all` the change set is the index, so plain `/sp:dev-gitmsg` keeps pre-commit semantics; only `--all` reaches unstaged and untracked files, and only a committing run stages them. The empty-index widen is read-only and always announced — a committing run never widens its own scope. An explicit `--scope <path>` always bounds the change set, with or without `--all`. One bounded, context-free capture per run — never a second, wider read.
530
546
 
531
547
  ### 10. fixall
532
548
 
@@ -34,7 +34,7 @@ If you hand-walk lifecycle statuses (manual `spur task update <wbs> <status>` wi
34
34
  pipeline), you MUST state so explicitly in your final message and name the gate you verified:
35
35
 
36
36
  ```
37
- Transitioned manually. Gate verified: spur task check <wbs> --strict-core -> PASS
37
+ Transitioned manually. Gate verified: spur task check <wbs> --as done -> PASS
38
38
  ```
39
39
 
40
40
  Silent manual transitions are the anti-pattern to forbid. Either the pipeline ran (name the
@@ -128,7 +128,7 @@ silently incomplete (H6 shipped at 23/48 that way, with one verdict carrying an
128
128
  `acceptanceCriteria` array and still reading PASS). See `ac-style-guide.md` §
129
129
  "Verdict AC ↔ feature scenario linkage" for the id forms and evidence vocabulary.
130
130
 
131
- **Parser contract (verify-answer-lint + `task verdict`, 0817 re-verify findings):**
131
+ **Parser contract (`spur task verdict` answer lint, 0817 re-verify findings):**
132
132
 
133
133
  1. The requirement id cell must be the **bare** id — `| R1 | MET | … |`. Suffixes (`R1 (AC1)`) or
134
134
  decoration (`**R1**`) fail the exact-match completeness check (`missing requirement row`).
@@ -160,7 +160,7 @@ command printing `0`.
160
160
  | # | Check | Command to run (literal - paste the output) | Pass condition |
161
161
  |---|-------|----------------------------------------------|----------------|
162
162
  | 1 | F1 - no unchecked boxes anywhere | `grep -c '^\s*- \[ \]' <task-file>` | output is exactly `0` (whole file, not just Plan) |
163
- | 2 | F2 - honest transition | (state it) | named a pipeline run-id, OR "manual + `spur task check <wbs> --strict-core` PASS" |
163
+ | 2 | F2 - honest transition | (state it) | named a pipeline run-id, OR "manual + `spur task check <wbs> --as done` PASS" |
164
164
  | 3 | F4 - gate evidence | (recall change type) | raw gate tails pasted if code/test/infra touched; one-liner only if pure-doc |
165
165
  | 4 | F5 - no `/tmp` residue | `ls /tmp/<wbs>-* 2>/dev/null \| wc -l` | output is `0` |
166
166
  | 5 | Dogfood (only if in dogfood mode) | `rg -c '^### 3\. Monitor Ledger' <report> && rg -c '── Dogfood Summary ──' <report> && rg -c '^status: (complete\|aborted)' <report>` | all three counts are `>= 1` (report exists under `docs/dogfood/` AND carries the mandatory ledger section AND the mandatory summary footer AND terminal frontmatter status - not just any file matching the slug) |
@@ -64,6 +64,10 @@ function normalizeArgs(raw: Args): Args {
64
64
 
65
65
  - If `--feature FOO` is present and `--tasks` is absent, treat the effective selector as `feature:FOO`.
66
66
  - If both are present, `--tasks` wins (with a one-line note in the batch report).
67
+ - **Per-command admission filters (I33 1023).** Step 1 is the shared baseline; commands may layer
68
+ stricter grammar on top — e.g. `/sp:dev-review` rejects `ready`/status pseudo-lists and the mixed
69
+ `--tasks` + `--feature` combination (exit 2), and accepts multi-id `--feature <id>,<id>` as
70
+ caller-level sugar expanded by the command layer before the resolver.
67
71
 
68
72
  **Feature-derived strict preflight (R2, task 0510).** After normalization, if the **effective
69
73
  selector** is `feature:<id>` (whether via `--tasks feature:<id>` or the `--feature <id>` sugar),
@@ -291,8 +295,8 @@ Each pipeline run ends in one of two terminal states:
291
295
  `.spur/run/<wbs>-verify-answer.txt` AC table is exactly four columns:
292
296
  `| AC | Status | Evidence Type | Evidence |`. The evidence-type token
293
297
  (`test`, `command`, `static-ref`, `manual-review`, `llm-judge`, `n/a`, or a `+`
294
- compound) is isolated in cell 3. A token merged into the evidence cell fails
295
- `verify-answer-lint`.
298
+ compound) is isolated in cell 3. A token merged into the evidence cell fails the
299
+ `spur task verdict` answer lint.
296
300
 
297
301
  **Driver acceptance (0930 R3).** The trace row and `.spur/run/<wbs>-verdict.json` are accepted as
298
302
  terminal evidence only if BOTH hold:
@@ -328,40 +332,35 @@ node "$(superskill script path sp batch-preflight.mjs)" --wbs <wbs> --status <st
328
332
  Helper: `recoveryHint(status, wbs)` in `plugins/sp/scripts/batch-preflight.ts`. Tables remain SSOT
329
333
  in next-router; this only maps status → primary TABLE A hop for recovery.
330
334
 
331
- ### 3.3c Bounded feature-sync retry suppression (task 0411)
335
+ ### 3.3c Feature-sync retry suppression (task 0411; 1004 R3 moved it into the sync service)
332
336
 
333
337
  During a batch, the per-task `record` step and the wrap-up `feature-transition` step each invoke
334
338
  feature status sync. When a feature is L4-gate-blocked (e.g. not all linked tasks are `done`), the
335
339
  identical blocked proposal repeats on every call with no intervening input change — in the H9
336
- dogfood, 4 redundant sync calls produced the same blocked result. The orchestration seam fixes
337
- this, not the engine.
340
+ dogfood, 4 redundant sync calls produced the same blocked result. The service fixes this, not the
341
+ engine.
338
342
 
339
343
  Both `task-pipeline.yaml` (`record` step) and `wrapup-pipeline.yaml` (`feature-transition` step)
340
- invoke the bounded wrapper instead of raw `feature sync`:
341
-
342
- ```bash
343
- node "$(superskill script path sp feature-sync-bounded.mjs)" <feature-id> --spur-bin "<spurBin>" --json
344
- ```
345
-
346
- The wrapper:
347
-
348
- 1. Reads an input fingerprint (feature file content hash, linked task statuses, verdict artifact
349
- mtimes) **before** invoking `feature sync`.
350
- 2. Classifies the structured result — `gateBlocked` checked first (a partial hop can have
351
- `applied: true` while still gate-blocked), then `applied`, then `no-op`.
352
- 3. On a **blocked** result, persists `.spur/run/feature-sync-blocked-<id>.json` and, on the next
353
- call with an **identical fingerprint**, suppresses the redundant sync and replays the prior
354
- blocked result.
355
- 4. On **applied** or **no-op** results, passes through unchanged (no suppression).
356
- 5. When the fingerprint **changes** (a task completed, a verdict file updated), suppression is
344
+ invoke `spur feature sync <feature-id> --json` directly — retry suppression lives inside the
345
+ `FeatureService.syncFeature` implementation:
346
+
347
+ 1. On a **blocked** result (`gateBlocked` checked first — a partial hop can have `applied: true`
348
+ while still gate-blocked — then an unapplied from≠to deferral), the service persists
349
+ `.spur/run/feature-sync-blocked-<id>.json` keyed by an input fingerprint (feature file content
350
+ hash, linked task statuses, verdict artifact mtimes).
351
+ 2. On the next call with an **identical fingerprint**, the service suppresses the redundant sync
352
+ and replays the prior blocked result (`suppressed: true`) without re-deriving hops.
353
+ 3. On **applied** or **no-op** results, the state file is cleared (no suppression).
354
+ 4. When the fingerprint **changes** (a task completed, a verdict file updated), suppression is
357
355
  invalidated and a fresh sync runs.
356
+ 5. `--force` (and an explicit confirm re-attempt) bypass the replay and re-derive live; dry-run
357
+ never reads or writes the state.
358
358
 
359
- **Batch driver contract:** the orchestrator does **nothing extra** — the wrapper lives inside the
360
- pipeline's `record` step and the wrap-up's `feature-transition` step. The driver still launches
361
- `task-pipeline.yaml` verbatim (R4.1). Suppression is transparent: the wrapper emits the same
362
- `FeatureSyncResult` JSON shape as `feature sync --json`, so downstream report logic is unchanged.
363
- The only observable difference is fewer redundant `feature sync` invocations and a one-line
364
- `feature-sync-bounded:` annotation on stderr when a duplicate is suppressed.
359
+ **Batch driver contract:** the orchestrator does **nothing extra** — the suppression lives inside
360
+ the pipeline's `record` step and the wrap-up's `feature-transition` step. The driver still
361
+ launches `task-pipeline.yaml` verbatim (R4.1). Suppression is transparent: the sync emits the same
362
+ `FeatureSyncResult` JSON shape (`suppressed: true` added on replay), so downstream report logic is
363
+ unchanged. The only observable difference is fewer redundant `feature sync` derivations.
365
364
 
366
365
  ### 3.4 Metadata-only host controller (R5, task 0510)
367
366
 
@@ -492,7 +491,8 @@ batch can never destroy its own evidence. Reuse mode retains its operator-owned
492
491
  persists the Step 5 report under the invoking tree; the reused tree's `.spur/run/` remains the live
493
492
  copy while that tree lives on. The per-run provenance — the worktree DB's run/action rows and the
494
493
  `.spur/run/<runId>.md` + `.state.json` records — is persisted mechanically by WT-4a's
495
- `inline-run-setup.ts --persist-out --from <worktree>` call, not by hand.
494
+ `inline-run-setup.ts --persist-out --from <worktree> [--task-file <merged-task>]...` call,
495
+ not by hand.
496
496
 
497
497
  **Stage records are worktree-local too (0948 R9, E7 Finding 5; persisted by 0975 R1).** Each task's
498
498
  own per-stage run record (`.spur/run/<runId>.md` + `.state.json`) and the worktree DB's run rows are
@@ -500,10 +500,38 @@ written inside the worktree and **are removed with it** in create mode — the E
500
500
  this evidence. Copying them out is no longer a manual audit-time duty: WT-4a (create-mode block
501
501
  below) runs `inline-run-setup.ts --persist-out --from "$WT_PATH"` **before** WT-4b holder cleanup,
502
502
  which copies the run/action/phase/transition/workflow-state rows and both record files into the
503
- invoking tree. The shapes are pinned (task 0975 R1): idempotent on re-persist; success exits 0
504
- printing `{"ok":true,"persisted":<n>,"skipped":[{"id":<run-id>,"reason":"id-exists"|"external-key-conflict"|"record-conflict:<file>"}]}`
503
+ invoking tree.
504
+
505
+ **Cited evidence rides the same call (0984 R1/R2).** The driver forwards each merged task file with
506
+ repeatable `--task-file <path>` (paths resolved in the invoking tree after the FF merge, e.g.
507
+ `spur task show <wbs> --json` → `.filePath`), and persist-out then copies/verifies every literal
508
+ `.spur/run/<file>` that file cites — WBS verdicts, check receipts, test-gate logs, run-ID records —
509
+ so a merged task file never anchors a path that died with the tree. Abbreviated references
510
+ (`fadca099-…`, `run-*-ac87.log`, `{batch-report.md,…}`) are not literal files and carry no
511
+ obligation; neither do root-qualified paths (`knowledge-kit/.spur/run/…`, `/abs/.spur/run/…`),
512
+ which cite another project's evidence — cite foreign run artifacts that way, never bare. A citation missing in BOTH trees, a divergent cited file (never overwritten — reconcile
513
+ by hand), an unreadable task file, or more than 64 distinct cited files fails the pass → WT-5.
514
+
515
+ **Owned evidence rides it too (1012).** With at least one `--task-file`, persist-out also treats as
516
+ copy obligations the worktree's `.spur/run/` direct children named `<wbs>-…` (the WBS is each
517
+ forwarded task file's leading four digits before `_`) or `<runId>-…` (every run row in the worktree
518
+ DB, whichever task it ran) — `<wbs>-verdict.json`, check receipts, route reasons — whether or not
519
+ the task file cites them. They join the cited set: same copy / byte-identical no-op /
520
+ divergent-refuse handling. The 64-file cap bounds citations alone; owned names are bounded per
521
+ owner (each `<wbs>-` / `<runId>-` prefix gets its own 64-file budget, task 1034), so the bound
522
+ scales with the batch and one runaway owner refuses by name before any write. `<runId>.md` /
523
+ `<runId>.state.json` stay with the record copy (a conflict there is a reported skip). Files
524
+ matching neither a citation nor an ownership prefix are left behind. An absent worktree `.spur/run/`
525
+ means nothing is owned; any other listing failure (not a directory, permission denied) fails the
526
+ pass before the invoking tree is written → WT-5. Without `--task-file` nothing is enumerated.
527
+
528
+ The shapes are pinned (task 0975 R1; `record-missing` and citation behavior per 0984): idempotent on re-persist;
529
+ success exits 0 printing
530
+ `{"ok":true,"persisted":<n>,"skipped":[{"id":<run-id>,"reason":"id-exists"|"external-key-conflict"|"record-conflict:<file>"|"record-missing:<file>"|"cited-directory:<name>"|"cited-symlink:<name>"|"cited-non-file:<name>"}]}`
505
531
  — an `id-exists` / `external-key-conflict` skip never modifies the pre-existing target rows, a
506
- `record-conflict:<file>` skip never overwrites a divergent invoking-tree record — and any failure
532
+ `record-conflict:<file>` skip never overwrites a divergent invoking-tree record, and a
533
+ `record-missing:<file>` skip is a known `task-lifecycle`/`feature-lifecycle` row with no record file
534
+ at all (its inserted DB row still counts in `persisted` — 0984 R5). Any failure
507
535
  exits 1 printing `{"ok":false,"error":<message>}` (a worktree DB run id that is not a single safe
508
536
  filename component is rejected before any target write). Any persist-out failure
509
537
  routes to **WT-5** — worktree and branch retained — the same copy-out-first contract as the
@@ -528,6 +556,15 @@ The wrap receives only what it would accept:
528
556
  3. When the done subset is **empty**, skip the wrap entirely with the reason (e.g. `batch wrap
529
557
  skipped: no done tasks`) instead of invoking wrapup-pipeline on an empty set.
530
558
 
559
+ **Repo-wide tripwire (1037).** After the doc-sync exits converge, the pipeline's `doc-tripwire` hop
560
+ runs the TRUSTED CONFIG ONLY `docTripwireCmd` over the still-uncommitted wrap diff before
561
+ metrics-record. The default probes `package.json` for a `test-repo-wide` script and runs
562
+ `bun run test-repo-wide` only when it is declared (a no-op in other projects), so the batch driver
563
+ passes no extra vars. Batch callers override it like any wrap var (`docTripwireCmd` in `--vars`);
564
+ an empty string disables the check while still recording PASS. A FAIL routes the wrap to `failed`
565
+ with already-written learnings/docs preserved — fix the flagged working-diff violation and re-run
566
+ the wrap.
567
+
531
568
  Filtering lives here, in the batch driver — no change to wrapup-pipeline.yaml or wrapup-steps.ts;
532
569
  the wrap's refusal of non-done tasks remains the hard invariant.
533
570
 
@@ -566,11 +603,14 @@ non-PASS verify verdict, or a HITL pause that ends the run take the WT-5 retenti
566
603
  full pipeline is eligible — `--worktree --mode implement` is rejected (WT-7), because that mode is
567
604
  the pipeline's implement stage and already runs in the driver's tree.
568
605
 
569
- **Review triage `dev-review` (run of one).** `/sp:dev-review <target> --triage --worktree [<name>]`
606
+ **Review triage `dev-review` (run of one).** `/sp:dev-review [--tasks <selector> | --feature <id>[,<id>] | --scope <path>[,<path>]] --triage --worktree [<name>]`
570
607
  runs this lifecycle around one review-plus-triage pass: WT-1…WT-6 apply unchanged, the marker's
571
- `command` is `dev-review` and its `selector` is the review target, and the slug is the WBS or the
572
- path's basename (`sp/review-<slug>-<short-id>`). It skips `quickReadiness` (there is no task set;
573
- admission is "the target resolves"). WT-4 success reads as "every direct fix passed its check and the
608
+ `command` is `dev-review` and its `selector` records the full normalized target list, and the slug
609
+ is `sp/review-<first>-and-<N>-<short-id>` for a multi-target run (N = target count) or
610
+ `sp/review-<slug>-<short-id>` for a single target (the WBS or the path's basename). It skips
611
+ `quickReadiness` (there is no task set; admission is "every target resolves" — each WBS/path must
612
+ resolve before the tree is cut). Under `--triage` the findings are bucketed across all targets once
613
+ (identical `file:line` findings deduped). WT-4 success reads as "every direct fix passed its check and the
574
614
  project gate is green"; anything else takes WT-5. Contract: [dev-operations.md § 2. review](dev-operations.md#2-review).
575
615
 
576
616
  One flag, two modes (see the glossary entry for the ownership rule). Bare `--worktree` is **create
@@ -828,12 +868,16 @@ git merge --ff-only "$BRANCH" # FF-only: never rebase, merge-commit, or
828
868
  WT_PATH="$(cd "../<worktree-dir>" && pwd)" # hoisted: needed by WT-4a AND WT-4b below
829
869
  # WT-4a provenance persist-out (task 0975 R1): copy the worktree DB's run rows plus
830
870
  # the .spur/run/<runId>.md + .state.json records into THIS tree. Run from the main
831
- # tree (cwd = the invoking tree). Idempotent; conflicts are reported, never
832
- # overwritten. A non-zero exit — including a half-readable worktree — must NOT
833
- # proceed to WT-4b removal:
834
- SETUP_SCRIPT="plugins/sp/scripts/inline-run-setup.ts"
835
- [ -f "$SETUP_SCRIPT" ] || SETUP_SCRIPT="$(superskill script path sp inline-run-setup.mjs 2>/dev/null)"
836
- bun "$SETUP_SCRIPT" --persist-out --from "$WT_PATH" \
871
+ # tree (cwd = the invoking tree). --task-file (0984 R2) forwards each merged task
872
+ # file (post-merge path) so the cited .spur/run/<file> evidence is copied/verified
873
+ # too. Resolve the merged path(s) BEFORE this block — an empty value exits 2:
874
+ # TASK_FILE="$(spur task show <wbs> --json | jq -r .filePath)" # per done task;
875
+ # build TASK_FILE_ARGS=(--task-file "$TASK_FILE") # one flag each
876
+ # Idempotent; conflicts are reported, never overwritten. A non-zero exit —
877
+ # including an unresolved or divergent citation, or a half-readable worktree — must
878
+ # NOT proceed to WT-4b removal:
879
+ SETUP_SCRIPT=plugins/sp/scripts/inline-run-setup.ts; [ -f config/plugin-scripts.json -a -f "$SETUP_SCRIPT" ] || SETUP_SCRIPT="$(superskill script path sp inline-run-setup.mjs 2>/dev/null)"
880
+ bun "$SETUP_SCRIPT" --persist-out --from "$WT_PATH" "${TASK_FILE_ARGS[@]}" \
837
881
  || { echo "halt: worktree run-record persist-out failed - worktree retained (WT-5)" >&2; exit 1; }
838
882
  #
839
883
  # WT-4b — bounded CWD-holder cleanup (task 0720 R1). $WT_PATH above is the EXACT
@@ -959,6 +1003,25 @@ Resume, merge, or discard:
959
1003
  discard: git worktree remove <worktree-path> && git branch -D <branch> && spur projects remove <worktree-path>
960
1004
  ```
961
1005
 
1006
+ When the halt cause is `non-FF base ref`, the report replaces the one-line `merge:` hint with this
1007
+ ordered divergence recipe, run by the operator — the driver never merges, rebases, or resolves
1008
+ conflicts itself. Other halt causes (task failure, HITL pause) keep the hint as printed:
1009
+
1010
+ ```
1011
+ # 1. integrate as a merge commit — never a rebase; task evidence cites the branch's commit SHAs
1012
+ git checkout <base-ref> && git merge --no-ff --no-commit <branch>
1013
+ # 2. resolve source conflicts by hand; generated files are then regenerated with the project's
1014
+ # generator, never hand-merged
1015
+ # (this repo: bun run build:plugin-lib && bun run --filter @gobing-ai/spur build:bundle)
1016
+ # 3. stage every resolved path and regenerated bundle (git add …) — an unmerged or
1017
+ # unstaged path makes step 5 abort
1018
+ # 4. run qualityGateCmd once, after ALL conflicts are resolved
1019
+ # 5. commit the merge with the prepared message file
1020
+ git commit -F <message-file>
1021
+ # 6. persist evidence out (WT-4a), then WT-4b/4c cleanup, and set the marker to merged
1022
+ inline-run-setup --persist-out --from <worktree> --task-file …
1023
+ ```
1024
+
962
1025
  The report reuses the [`--next` chain contract](flag-glossary.md#--next-chain-contract) halt-report
963
1026
  shape (halt cause + where + why), not new vocabulary. Retention is the right default: these batches
964
1027
  are long and already resumable via `--continue`; auto-deleting is data loss, auto-merging is a
@@ -1136,11 +1199,11 @@ per-task with `/sp:dev-run <wbs> --worktree <branch>`.
1136
1199
  ### Generated regions — defer the sync, regenerate once (R5)
1137
1200
 
1138
1201
  The only per-task writer of feature files is the `record` step's post-record feature sync
1139
- (`task-pipeline.yaml`, the `feature-sync-bounded` wrapper). Parallel launches set
1202
+ (`task-pipeline.yaml`). Parallel launches set
1140
1203
  the pipeline var `deferFeatureSync: "true"` (default `"false"`): the record step appends
1141
1204
  `feature sync deferred to batch integration` to the task report and skips the sync, so task
1142
1205
  branches never touch feature files or `docs/features/INDEX.md`. After the last integration, on the
1143
- base ref, the orchestrator runs the same bounded wrapper plus `spur feature refresh --feature <f>`
1206
+ base ref, the orchestrator runs `spur feature sync <f> --json` (service-level suppression) plus `spur feature refresh --feature <f>`
1144
1207
  once per touched feature and commits the result as one `chore(corpus)` commit. Sequential and
1145
1208
  inline runs keep the default `"false"` and are unchanged. Any rebase conflict — on a generated
1146
1209
  path or any other — is an R4 `integration-conflict`; there is no path-based exception.