@gobing-ai/spur 0.3.95 → 0.3.97

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 (165) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/plugin-scripts.json +0 -54
  3. package/config/rules/boundary/sp-script-placement.yaml +19 -0
  4. package/config/rules/strict/runtime-boundaries.yaml +6 -0
  5. package/config/rules/structure/test-location.yaml +2 -0
  6. package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
  7. package/config/rules/typescript/output-boundaries.yaml +15 -2
  8. package/config/script-placement-baseline.json +51 -0
  9. package/config/templates/AGENTS.md +3 -3
  10. package/config/workflows/feature-verification.yaml +1 -1
  11. package/config/workflows/history-anatomy.yaml +23 -11
  12. package/config/workflows/idea-pipeline.yaml +80 -85
  13. package/config/workflows/pr-review.yaml +18 -9
  14. package/config/workflows/task-pipeline.yaml +42 -95
  15. package/config/workflows/wrapup-pipeline.yaml +91 -38
  16. package/package.json +2 -2
  17. package/plugins/sp/README.md +14 -10
  18. package/plugins/sp/agents/super-reviewer.md +43 -18
  19. package/plugins/sp/commands/dev-fixgha.md +83 -0
  20. package/plugins/sp/commands/dev-gitmsg.md +8 -6
  21. package/plugins/sp/commands/dev-gtd.md +2 -2
  22. package/plugins/sp/commands/dev-idea.md +22 -12
  23. package/plugins/sp/commands/dev-job-dump.md +29 -0
  24. package/plugins/sp/commands/dev-job-resume.md +29 -0
  25. package/plugins/sp/commands/dev-plan.md +8 -9
  26. package/plugins/sp/commands/dev-review.md +22 -13
  27. package/plugins/sp/commands/dev-verifyall.md +1 -1
  28. package/plugins/sp/commands/spur-init.md +2 -2
  29. package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
  30. package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
  31. package/plugins/sp/lib/idea-handoff.generated.mjs +5 -4
  32. package/plugins/sp/lib/inline-run.generated.d.mts +11 -0
  33. package/plugins/sp/lib/inline-run.generated.mjs +39 -13
  34. package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
  35. package/plugins/sp/lib/quality-gate.generated.mjs +445 -0
  36. package/plugins/sp/lib/residual-scan.generated.d.mts +63 -0
  37. package/plugins/sp/lib/residual-scan.generated.mjs +226 -0
  38. package/plugins/sp/lib/spur-bin.ts +36 -0
  39. package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
  40. package/plugins/sp/lib/step-profile.generated.mjs +174 -0
  41. package/plugins/sp/plugin.json +1 -1
  42. package/plugins/sp/references/roles.md +3 -3
  43. package/plugins/sp/scripts/feature-verification-steps.mjs +14 -1
  44. package/plugins/sp/scripts/feature-verification-steps.ts +14 -1
  45. package/plugins/sp/scripts/history-anatomy-cache.mjs +20 -19
  46. package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
  47. package/plugins/sp/scripts/inline-run-setup.mjs +85 -320
  48. package/plugins/sp/scripts/inline-run-setup.ts +104 -667
  49. package/plugins/sp/scripts/quality-gate.mjs +46 -24
  50. package/plugins/sp/scripts/quality-gate.ts +13 -659
  51. package/plugins/sp/scripts/residual-scan.mjs +179 -174
  52. package/plugins/sp/scripts/residual-scan.ts +125 -517
  53. package/plugins/sp/scripts/script-root.mjs +5 -1
  54. package/plugins/sp/scripts/script-root.ts +5 -1
  55. package/plugins/sp/scripts/workflow-step-profile.mjs +25 -17
  56. package/plugins/sp/scripts/workflow-step-profile.ts +21 -315
  57. package/plugins/sp/scripts/wrapup-drift-probe.mjs +8 -2
  58. package/plugins/sp/scripts/wrapup-drift-probe.ts +3 -2
  59. package/plugins/sp/scripts/wrapup-steps.mjs +72 -33
  60. package/plugins/sp/scripts/wrapup-steps.ts +128 -37
  61. package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
  62. package/plugins/sp/skills/code-verification/SKILL.md +34 -9
  63. package/plugins/sp/skills/code-verification/references/verdict-schema.md +3 -3
  64. package/plugins/sp/skills/functional-review/SKILL.md +7 -4
  65. package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
  66. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
  67. package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
  68. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  69. package/plugins/sp/skills/source-driven-development/SKILL.md +11 -0
  70. package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
  71. package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
  72. package/plugins/sp/skills/spur-cli/references/features.md +17 -6
  73. package/plugins/sp/skills/spur-cli/references/init.md +17 -16
  74. package/plugins/sp/skills/spur-cli/references/self.md +3 -2
  75. package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
  76. package/plugins/sp/skills/spur-cli/references/tasks/section-editing.md +9 -5
  77. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +21 -6
  78. package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
  79. package/plugins/sp/skills/spur-cli/references/workflows.md +17 -14
  80. package/plugins/sp/skills/spur-dev/SKILL.md +2 -0
  81. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
  82. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +14 -10
  83. package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
  84. package/plugins/sp/skills/spur-dev/references/dev-operations.md +165 -75
  85. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +7 -5
  86. package/plugins/sp/skills/spur-dev/references/execution-batch.md +84 -39
  87. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +8 -12
  88. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
  89. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +40 -21
  90. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
  91. package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
  92. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +72 -15
  93. package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
  94. package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
  95. package/spur.js +4461 -2085
  96. package/web/_astro/{BoardApp.Cpxntzad.js → BoardApp.Bmv5WkJ9.js} +1 -1
  97. package/web/_astro/BoardApp.vveLOdDq.js +322 -0
  98. package/web/_astro/{TaskDetail.C5bW4WGV.js → TaskDetail.IKc3uDYV.js} +1 -1
  99. package/web/_astro/{arc.CyjRvNMY.js → arc.C63Ufg35.js} +1 -1
  100. package/web/_astro/{architectureDiagram-3BPJPVTR.B4lWRzJA.js → architectureDiagram-3BPJPVTR.CP8Hyldk.js} +1 -1
  101. package/web/_astro/{blockDiagram-GPEHLZMM.Be38USQ2.js → blockDiagram-GPEHLZMM.Bo_48MRA.js} +1 -1
  102. package/web/_astro/{c4Diagram-AAUBKEIU.DT9Fj5Qx.js → c4Diagram-AAUBKEIU.Ch61sw-O.js} +1 -1
  103. package/web/_astro/channel.BKCmJpWM.js +1 -0
  104. package/web/_astro/{chunk-2J33WTMH.SeSBWLg5.js → chunk-2J33WTMH.CHpzS7xf.js} +1 -1
  105. package/web/_astro/{chunk-4BX2VUAB.DoO4VE14.js → chunk-4BX2VUAB.DzMNNWIw.js} +1 -1
  106. package/web/_astro/{chunk-55IACEB6.DrmTFA53.js → chunk-55IACEB6.BqPrDw_Q.js} +1 -1
  107. package/web/_astro/{chunk-727SXJPM.Bi-TUb_V.js → chunk-727SXJPM.ZzBHhNw-.js} +1 -1
  108. package/web/_astro/{chunk-AQP2D5EJ.B-xp8Uhi.js → chunk-AQP2D5EJ.1_O_LLkx.js} +1 -1
  109. package/web/_astro/{chunk-FMBD7UC4.DGWrDnUU.js → chunk-FMBD7UC4.GS4UHg7_.js} +1 -1
  110. package/web/_astro/{chunk-ND2GUHAM._BagvPDy.js → chunk-ND2GUHAM.BnqotN4g.js} +1 -1
  111. package/web/_astro/{chunk-QZHKN3VN.VOboYWQ0.js → chunk-QZHKN3VN.Ghuh8zoJ.js} +1 -1
  112. package/web/_astro/{classDiagram-4FO5ZUOK.D_apfHS7.js → classDiagram-4FO5ZUOK.BW9vC3zg.js} +1 -1
  113. package/web/_astro/{classDiagram-v2-Q7XG4LA2.D_apfHS7.js → classDiagram-v2-Q7XG4LA2.BW9vC3zg.js} +1 -1
  114. package/web/_astro/{cose-bilkent-S5V4N54A.DNLU_L-x.js → cose-bilkent-S5V4N54A.D6jNC0ti.js} +1 -1
  115. package/web/_astro/{cynefin-OW5HDTMX.D8borhV-.js → cynefin-OW5HDTMX.BOGoCs4L.js} +1 -1
  116. package/web/_astro/{dagre-BM42HDAG.Uc0lvjcV.js → dagre-BM42HDAG.8iU5VcWi.js} +1 -1
  117. package/web/_astro/{diagram-2AECGRRQ.DDIxtDBo.js → diagram-2AECGRRQ.CVt88HwH.js} +1 -1
  118. package/web/_astro/{diagram-5GNKFQAL.DVoDnD3H.js → diagram-5GNKFQAL.C9HxUUMl.js} +1 -1
  119. package/web/_astro/{diagram-KO2AKTUF.CRubTJ-0.js → diagram-KO2AKTUF.4ywkQN3t.js} +1 -1
  120. package/web/_astro/{diagram-LMA3HP47.m9SeYcXQ.js → diagram-LMA3HP47.C48A6gpz.js} +1 -1
  121. package/web/_astro/{diagram-OG6HWLK6.C6s2ZjV5.js → diagram-OG6HWLK6.DxFieQW4.js} +1 -1
  122. package/web/_astro/{erDiagram-TEJ5UH35.nSzr5KTj.js → erDiagram-TEJ5UH35.CtjtQqYs.js} +1 -1
  123. package/web/_astro/{flowDiagram-I6XJVG4X.DrZcys16.js → flowDiagram-I6XJVG4X.C5zAiahU.js} +1 -1
  124. package/web/_astro/{ganttDiagram-6RSMTGT7.Do4M1b-K.js → ganttDiagram-6RSMTGT7.D_L6zsny.js} +1 -1
  125. package/web/_astro/{gitGraphDiagram-PVQCEYII.BWc9oJ4l.js → gitGraphDiagram-PVQCEYII.Ignnrk3V.js} +1 -1
  126. package/web/_astro/index.CbNz17Sx.css +1 -0
  127. package/web/_astro/{infoDiagram-5YYISTIA.BIdOctFk.js → infoDiagram-5YYISTIA.DLv8-P5u.js} +1 -1
  128. package/web/_astro/{ishikawaDiagram-YF4QCWOH.CV5SuG2t.js → ishikawaDiagram-YF4QCWOH.p0OTpwFO.js} +1 -1
  129. package/web/_astro/{journeyDiagram-JHISSGLW.CxE-rEJ-.js → journeyDiagram-JHISSGLW.CIaBt-DD.js} +1 -1
  130. package/web/_astro/{kanban-definition-UN3LZRKU.BPRdKWs_.js → kanban-definition-UN3LZRKU.DGySIntq.js} +1 -1
  131. package/web/_astro/{linear.zRsuuDTE.js → linear.BATV4RXu.js} +1 -1
  132. package/web/_astro/{mermaid.core.jAJTcMKc.js → mermaid.core.DzkwM3VX.js} +4 -4
  133. package/web/_astro/{mindmap-definition-RKZ34NQL.BffJxfCr.js → mindmap-definition-RKZ34NQL.3piBRRzA.js} +1 -1
  134. package/web/_astro/{pieDiagram-4H26LBE5.CacwWaE3.js → pieDiagram-4H26LBE5.DUNueLub.js} +1 -1
  135. package/web/_astro/{quadrantDiagram-W4KKPZXB.hXELD06-.js → quadrantDiagram-W4KKPZXB.9bihVbrt.js} +1 -1
  136. package/web/_astro/{requirementDiagram-4Y6WPE33.DdEbPcqT.js → requirementDiagram-4Y6WPE33.CkjsC-YT.js} +1 -1
  137. package/web/_astro/{sankeyDiagram-5OEKKPKP.DgaDocKv.js → sankeyDiagram-5OEKKPKP.Dbq4vaoQ.js} +1 -1
  138. package/web/_astro/{sequenceDiagram-3UESZ5HK.B0KScnAu.js → sequenceDiagram-3UESZ5HK.BivrH99P.js} +1 -1
  139. package/web/_astro/{stateDiagram-AJRCARHV.CV9M_WNc.js → stateDiagram-AJRCARHV.CqLQ1Vzz.js} +1 -1
  140. package/web/_astro/{stateDiagram-v2-BHNVJYJU.BOAo74Et.js → stateDiagram-v2-BHNVJYJU.DvvoLD3b.js} +1 -1
  141. package/web/_astro/{timeline-definition-PNZ67QCA.CqqepUMe.js → timeline-definition-PNZ67QCA.B-KR78gv.js} +1 -1
  142. package/web/_astro/{vennDiagram-CIIHVFJN.DDqe4dOG.js → vennDiagram-CIIHVFJN.Ck0Eieb5.js} +1 -1
  143. package/web/_astro/{wardleyDiagram-YWT4CUSO.B3R9OPdO.js → wardleyDiagram-YWT4CUSO.CRfdd_do.js} +1 -1
  144. package/web/_astro/{xychartDiagram-2RQKCTM6.B3SvgXpQ.js → xychartDiagram-2RQKCTM6.Ck_Jxk9C.js} +1 -1
  145. package/web/index.html +2 -2
  146. package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
  147. package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
  148. package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -301
  149. package/plugins/sp/scripts/feature-sync-bounded.ts +0 -481
  150. package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
  151. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
  152. package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
  153. package/plugins/sp/scripts/record-feature-sync.ts +0 -84
  154. package/plugins/sp/scripts/script-contract-check.ts +0 -506
  155. package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
  156. package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
  157. package/plugins/sp/scripts/task-evidence-precheck.ts +0 -189
  158. package/plugins/sp/scripts/task-size-precheck.ts +0 -212
  159. package/plugins/sp/scripts/transition-shim-check.ts +0 -238
  160. package/plugins/sp/scripts/validate-commands.ts +0 -689
  161. package/plugins/sp/scripts/validate-flag-contracts.ts +0 -890
  162. package/plugins/sp/scripts/verify-answer-lint.ts +0 -549
  163. package/web/_astro/BoardApp.BxJuwD7I.js +0 -191
  164. package/web/_astro/channel.CI6N_tCg.js +0 -1
  165. package/web/_astro/index.CENnIEqT.css +0 -1
@@ -44,7 +44,7 @@ description: "Standard task pipeline: precheck → implement → quality-gate (t
44
44
  # any non-terminal state may transition to cancelled.
45
45
  # Guards:
46
46
  # wip → testing: spur task check <wbs>
47
- # testing → done: spur task check <wbs> --strict-core, then PASS-verdict provenance
47
+ # testing → done: spur task check <wbs> --as done, then PASS-verdict provenance
48
48
  # Invalid: todo → testing, todo → done, and wip → done.
49
49
  # Pipeline states below are orchestration states, not task statuses.
50
50
  iterationBound: 20
@@ -163,12 +163,6 @@ vars:
163
163
  # anchors directly in the /sp:dev-fixall input, so the fix agent starts AT the failing
164
164
  # file:line instead of re-deriving it from a fresh gate run (0482 R3).
165
165
  gateFindings: ""
166
- # Max R-items in ## Requirements before size precheck fails (R2, task 0454).
167
- # Override with `--vars '{"maxImplementReqs":"20"}'`.
168
- maxImplementReqs: "10"
169
- # Max checklist items under ## Plan before size precheck fails (R2, task 0454).
170
- # Override with `--vars '{"maxImplementPlanItems":"32"}'`.
171
- maxImplementPlanItems: "16"
172
166
  # Diff-scope guard on the implement hop (R1, task 0487). When the target task
173
167
  # body backticks at least one path, non-corpus changes outside those paths
174
168
  # fail the step by name. New files beside a declared file are allowed. Empty
@@ -194,38 +188,19 @@ states:
194
188
  options:
195
189
  command: >-
196
190
  S=plugins/sp/scripts/script-root.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp script-root.mjs 2>/dev/null)"; RUNNER=bun; case "$S" in *.mjs) RUNNER=node ;; esac; [ -f "$S" ] && "$RUNNER" "$S" --run-id "$__runId" --spur-bin "$spurBin" || echo "script-root failed closed — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; exit 0
197
- # (e) git-status hygiene advisory; never fails the run (0487 R6 / 0511 R1).
191
+ # (e) git-status hygiene advisory; never fails the run (0487 R6 / 0511 R1). 1007 R3:
192
+ # the F96 base-sha capture (0950) merged into this shell — original commands in order,
193
+ # then the one-time base commit capture; a resumed run keeps its original base so
194
+ # residual scans stay anchored to the same diff.
198
195
  - kind: shell
199
196
  options:
200
197
  command: >-
201
- D=$(git status --porcelain -- . ':(exclude)docs/tasks*' ':(exclude)docs/features' 2>/dev/null); [ -z "$D" ] || printf 'precheck: WARNING - working tree has uncommitted non-corpus changes — commit or stash before starting a new task:\n%s\n' "$D"; C=$(git status --porcelain -- ':(glob)docs/tasks*/**' 2>/dev/null); [ -z "$C" ] || printf 'precheck: NOTE - task corpus has uncommitted changes — review before staging with this task:\n%s\n' "$C"; exit 0
202
- - kind: note
203
- options:
204
- # R4 task_run_links linkage (kind=pipeline) is written by a WorkflowService
205
- # hook at run start — see the 0062 follow-up note; there is no link-writing
206
- # CLI verb to call from a shell step, so it cannot live in pure YAML.
207
- message: "Pipeline start for task ${vars.wbs}."
198
+ D=$(git status --porcelain -- . ':(exclude)docs/tasks*' ':(exclude)docs/features' 2>/dev/null); [ -z "$D" ] || printf 'precheck: WARNING - working tree has uncommitted non-corpus changes — commit or stash before starting a new task:\n%s\n' "$D"; C=$(git status --porcelain -- ':(glob)docs/tasks*/**' 2>/dev/null); [ -z "$C" ] || printf 'precheck: NOTE - task corpus has uncommitted changes — review before staging with this task:\n%s\n' "$C"; mkdir -p .spur/run; [ -f ".spur/run/$wbs-base.sha" ] || git rev-parse HEAD > ".spur/run/$wbs-base.sha"; exit 0
208
199
  # (e) auto-profile feature reopen; exit 1 when reactivation fails (0723 R3).
209
200
  - kind: shell
210
201
  options:
211
202
  command: >-
212
203
  [ "$profile" = auto ] || exit 0; FID=$($spurBin task show $wbs --json 2>/dev/null | jq -r '.feature_id // .frontmatter.feature_id // empty' 2>/dev/null); [ -z "$FID" ] || $spurBin feature sync "$FID" --force 2>/dev/null || $spurBin feature update "$FID" active 2>/dev/null || { echo "precheck: FAIL - feature reactivation $FID failed — feature sync + feature update both errored" >&2; exit 1; }; exit 0
213
- # (d) task-size-precheck.ts owns the check; shell only resolves it and fails closed (0454 R2 / 0723 R2).
214
- - kind: shell
215
- options:
216
- command: >-
217
- mkdir -p .spur/run; S=plugins/sp/scripts/task-size-precheck.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp task-size-precheck.ts 2>/dev/null)"; if [ -f "$S" ]; then bun "$S" "$wbs" --spur-bin "$spurBin" --max-reqs "$maxImplementReqs" --max-plan-items "$maxImplementPlanItems"; else echo "task-size-precheck failed closed — checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp --marketplace gobing-ai/spur'." >&2; echo "FAIL" > ".spur/run/$wbs-precheck-size.status"; fi; exit 0
218
- # (d) task-evidence-precheck.ts owns the check; shell only resolves it and fails closed (0726 R2).
219
- - kind: shell
220
- options:
221
- command: >-
222
- mkdir -p .spur/run; S=plugins/sp/scripts/task-evidence-precheck.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp task-evidence-precheck.ts 2>/dev/null)"; if [ -f "$S" ]; then bun "$S" "$wbs" --spur-bin "$spurBin"; else echo "task-evidence-precheck failed closed — checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp --marketplace gobing-ai/spur'." >&2; echo "FAIL" > ".spur/run/$wbs-precheck-evidence.status"; fi; exit 0
223
- # (e) F96 R1 (0950): capture the run's base commit once — a resumed or re-entered run
224
- # keeps its original base so residual scans stay anchored to the same diff.
225
- - kind: shell
226
- options:
227
- command: >-
228
- mkdir -p .spur/run; [ -f ".spur/run/$wbs-base.sha" ] || git rev-parse HEAD > ".spur/run/$wbs-base.sha"; exit 0
229
204
  # (e) route-reason lookup and routes log; proportional-routing tests locate it
230
205
  # (0759 R1/R5 run-scoped artifact; 0804 R8 run-id safety — one line, no in-scalar `#`).
231
206
  - kind: shell
@@ -439,7 +414,7 @@ states:
439
414
  - kind: shell
440
415
  options:
441
416
  command: >-
442
- mkdir -p .spur/run; if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/quality-gate.ts ]; then bun plugins/sp/scripts/quality-gate.ts run; elif Q="$(superskill script path sp quality-gate.mjs 2>/dev/null)" && [ -f "$Q" ]; then node "$Q" run; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
417
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/quality-gate.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/quality-gate.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then "$RUNNER" "$S" run; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
443
418
 
444
419
  - id: test-fix
445
420
  description: >
@@ -454,19 +429,15 @@ states:
454
429
  options:
455
430
  command: >-
456
431
  TASK_POLICY=$($spurBin task show "$wbs" --json | jq -er '.frontmatter.mutationPolicy // ([.content | scan("(?m)^mutationPolicy:[ \t]*([^ \r\n]+)[ \t]*$") | .[0]] | if length == 0 then "code" elif length == 1 then .[0] else "ambiguous" end)') || exit 1; [ "$mutationPolicy:$TASK_POLICY" = "code:code" ] || { echo "test-fix: mutation policy forbids automatic code repair (run=$mutationPolicy, task=$TASK_POLICY) — gate failure preserved, no agent dispatched" >&2; exit 1; }
457
- # (e) fixall attempt counter and verdict hand-off to the remediation log.
432
+ # (e) fixall attempt counter, verdict hand-off and residual artifact in one log
433
+ # append shell (1007 R3: the two appends merged — original commands in order, exit 0).
434
+ # (d) F96 R3 (0950): dev-fixall treats residual items as fix targets and may defer
435
+ # P3/markers to residual-deferrals.json (see dev-fixall.md); fold already merged the
436
+ # anchors into <wbs>-test-gate.findings, this carries the structured artifact too.
458
437
  - kind: shell
459
438
  options:
460
439
  command: >-
461
- mkdir -p .spur/run; A=".spur/run/$wbs-test-fix-attempt"; n=$(cat "$A" 2>/dev/null || echo 0); printf '%s\n' "$((n + 1))" > "$A"; [ ! -f ".spur/run/$wbs-verdict.json" ] || { echo '--- verify verdict (remediation input, task 0703 R4) ---'; cat ".spur/run/$wbs-verdict.json"; } >> ".spur/run/$wbs-test-gate.log"; exit 0
462
- # (d) F96 R3 (0950): hand the residual list to the remediation loop — dev-fixall
463
- # treats residual items as fix targets and may defer P3/markers to
464
- # residual-deferrals.json (see dev-fixall.md). Fold already merged the anchors
465
- # into <wbs>-test-gate.findings; this carries the structured artifact too.
466
- - kind: shell
467
- options:
468
- command: >-
469
- [ ! -f ".spur/run/$wbs-residuals.json" ] || { echo '--- residual artifact (F96 fix targets; deferrals go to residual-deferrals.json, P3/markers only) ---'; cat ".spur/run/$wbs-residuals.json"; } >> ".spur/run/$wbs-test-gate.log"; exit 0
440
+ printf '%s\n' "$(( $(cat ".spur/run/$wbs-test-fix-attempt" 2>/dev/null || echo 0) + 1 ))" > ".spur/run/$wbs-test-fix-attempt"; [ -f ".spur/run/$wbs-verdict.json" ] && echo '--- verify verdict (remediation input, task 0703 R4) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$wbs-verdict.json" >> ".spur/run/$wbs-test-gate.log"; [ -f ".spur/run/$wbs-residuals.json" ] && echo '--- residual artifact (F96 fix targets; deferrals go to residual-deferrals.json, P3/markers only) ---' >> ".spur/run/$wbs-test-gate.log" && cat ".spur/run/$wbs-residuals.json" >> ".spur/run/$wbs-test-gate.log"; exit 0
470
441
  # R3 (0482): project the extracted gate anchors into a var so the dispatch input
471
442
  # can NAME the failing file:line, not merely point at a log. A vars template cannot
472
443
  # run a shell, so the read is an action, not an inline `$(cat ...)` substitution.
@@ -522,7 +493,7 @@ states:
522
493
  - kind: shell
523
494
  options:
524
495
  command: >-
525
- mkdir -p .spur/run; if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/quality-gate.ts ]; then bun plugins/sp/scripts/quality-gate.ts recheck; elif Q="$(superskill script path sp quality-gate.mjs 2>/dev/null)" && [ -f "$Q" ]; then node "$Q" recheck; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
496
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/quality-gate.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/quality-gate.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then "$RUNNER" "$S" recheck; else echo "quality gate failed closed — quality-gate script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf 'FAIL\n' > ".spur/run/$wbs-test-gate.status"; fi; exit 0
526
497
 
527
498
  - id: triage
528
499
  description: >-
@@ -542,7 +513,7 @@ states:
542
513
  - kind: shell
543
514
  options:
544
515
  command: >-
545
- mkdir -p .spur/run; if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/task-diffstat.ts ]; then bun plugins/sp/scripts/task-diffstat.ts --spur-bin "$spurBin"; elif D="$(superskill script path sp task-diffstat.mjs 2>/dev/null)" && [ -f "$D" ]; then node "$D"; else echo "task-diffstat failed closed — task-diffstat script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf '{"files":0,"insertions":0,"deletions":0,"paths":[],"sensitive":true}\n' > ".spur/run/$wbs-diffstat.json"; fi; exit 0
516
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/task-diffstat.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/task-diffstat.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then "$RUNNER" "$S" --spur-bin "$spurBin"; else echo "task-diffstat failed closed — task-diffstat script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; printf '{"files":0,"insertions":0,"deletions":0,"paths":[],"sensitive":true}\n' > ".spur/run/$wbs-diffstat.json"; fi; exit 0
546
517
  # (b)+(c) lane pre-guard BEFORE decide: a caller-set mode is projected verbatim (R2b);
547
518
  # a sensitive path or >400 changed lines pins safety (R2c) and the decide result below
548
519
  # is never consulted. A missing/malformed diffstat row (producer crashed or never ran,
@@ -568,18 +539,15 @@ states:
568
539
  - .spur/run/${vars.wbs}-diffstat.json
569
540
  - ${vars.taskSpecPath}
570
541
  resultFile: .spur/run/${vars.wbs}-triage.decision
571
- # (d) project the decision: low → fast lane; anything else (standard/high, degraded
572
- # default, missing row) leaves the mode empty → standard review path. A non-empty mode
573
- # file (caller-set or deterministic-high) is never overridden.
574
- - kind: shell
575
- options:
576
- command: >-
577
- MF=".spur/run/$wbs-mode.txt"; [ -s "$MF" ] && exit 0; jq -r 'if .value == "low" then "fast" else empty end' ".spur/run/$wbs-triage.decision" 2>/dev/null > "$MF"; exit 0
578
- # R5: append the triage route reason in the shared routes-log format.
542
+ # (d) project the decision, then append the triage route reason (1007 R3: the
543
+ # mode-fallback shell merged into the memory-append shell — original commands in
544
+ # order, the non-empty mode file is still never overridden; the append keeps the
545
+ # failure signal of the original pair). low → fast lane; anything else (standard/high,
546
+ # degraded default, missing row) leaves the mode empty → standard review path.
579
547
  - kind: shell
580
548
  options:
581
549
  command: >-
582
- m="$(cat ".spur/run/$wbs-mode.txt")"; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; mkdir -p .spur/memory; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(jq -rn --arg m "$m" --arg c "$mode" 'if $c != "" then ($c + ":caller-set") else {"fast":"fast:triage low lane","safety":"safety:triage deterministic-high","":"safety:triage standard lane"}[$m] // "safety:triage unrecognized mode" end')" >> .spur/memory/task-pipeline-routes.log
550
+ MF=".spur/run/$wbs-mode.txt"; [ -s "$MF" ] || jq -r 'if .value == "low" then "fast" else empty end' ".spur/run/$wbs-triage.decision" 2>/dev/null > "$MF"; m="$(cat "$MF")"; RUN_ID="$__runId"; [ -n "$RUN_ID" ] || RUN_ID="pipeline-$wbs"; mkdir -p .spur/memory; printf '%s %s %s\n' "$RUN_ID" "$wbs" "$(jq -rn --arg m "$m" --arg c "$mode" 'if $c != "" then ($c + ":caller-set") else {"fast":"fast:triage low lane","safety":"safety:triage deterministic-high","":"safety:triage standard lane"}[$m] // "safety:triage unrecognized mode" end')" >> .spur/memory/task-pipeline-routes.log
583
551
  # (d) the resolved lane becomes the `mode` var for the triage → verify/review guards.
584
552
  - kind: file.read.into-var
585
553
  options:
@@ -622,7 +590,7 @@ states:
622
590
  # through the executor registry, and the runner enforces (pre-dispatch,
623
591
  # fail-closed) that a P0/P1 task's review resolves a DIFFERENT executor
624
592
  # spec than the implement stage recorded in __agentRouting_implement.
625
- input: /sp:dev-review ${vars.wbs} --auto
593
+ input: /sp:dev-review --tasks ${vars.wbs} --auto
626
594
  # The captured review answer is the run's dispatch evidence (0823 R1):
627
595
  # expectFile fails the step when no answer was captured. Quality stays with
628
596
  # the review proof digest and the verify verdict.
@@ -704,24 +672,8 @@ states:
704
672
  compareExecutorWith: implement
705
673
  timeoutMs: ${vars.stepTimeoutMs}
706
674
  expectFile: .spur/run/${vars.wbs}-verify-answer.txt
707
- # 0726 R3: hard lint gate over the verifier-owned answer — shape and
708
- # evidence-row identity, before the verdict derivation reads it.
709
- # Hard action: a malformed answer halts the sequence here instead of
710
- # poisoning the verdict parse downstream.
711
- # (d) verify-answer-lint.ts owns the answer lint; shell only resolves it.
712
- - kind: shell
713
- options:
714
- command: >-
715
- LINT_SCRIPT="plugins/sp/scripts/verify-answer-lint.ts";
716
- [ -f config/plugin-scripts.json -a -f "$LINT_SCRIPT" ] ||
717
- LINT_SCRIPT="$(superskill script path sp verify-answer-lint.ts 2>/dev/null)";
718
- if [ -z "$LINT_SCRIPT" ] || [ ! -f "$LINT_SCRIPT" ]; then
719
- echo "verify-answer-lint: checker not found in plugins/sp/scripts/ nor staged — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
720
- exit 1;
721
- fi;
722
- bun "$LINT_SCRIPT" "$wbs"
723
- --answer ".spur/run/$wbs-verify-answer.txt"
724
- --spur-bin "$spurBin"
675
+ # (d) `spur task verdict` owns the answer lint (task 1003 R2) — the shell below
676
+ # fails closed when the verifier's answer is malformed, before any verdict artifact.
725
677
  - kind: shell
726
678
  options:
727
679
  command: "$spurBin task verdict $wbs --from-answer .spur/run/$wbs-verify-answer.txt"
@@ -740,7 +692,7 @@ states:
740
692
  `run.artifact` registration (0785 R3): the verify verdict is ledger-registered
741
693
  against a freshly captured proof digest — including review-completion marker
742
694
  evidence (0785 R4) — before any task record or status mutation. Post-record step
743
- conditionally syncs feature status via the bounded `feature-sync-bounded` wrapper
695
+ conditionally syncs feature status via `spur feature sync`
744
696
  (task 0411 retry-suppression) if `feature_id` is present, or appends an orphan
745
697
  link proposal to the run report if absent (task 0328 / ADR-0322). The post-record
746
698
  residual sweep (0983) re-scans the task file after the verdict-proven box flips and
@@ -784,19 +736,17 @@ states:
784
736
  resultFile: .spur/run/${vars.__runId}-record-transition.status
785
737
  softFail: false
786
738
  timeoutMs: 120000
787
- # (d) feature-sync-bounded.ts owns the sync; shell adds the orphan note and fallbacks
788
- # (0411 retry-suppression; 0328 / ADR-0322). Best-effort `exit 0` — feature status sync is a
789
- # follow-up, not a completion gate; `record → done` runs `spur task check`.
790
- # 0931 R5: with deferFeatureSync "true" (parallel mode) the deferral note lands in the
791
- # task report and the sync is skipped entirely, so task branches never touch feature
792
- # corpus files; the batch orchestrator performs the deferred sync on the base ref.
793
- # The sync itself is owned by record-feature-sync.ts (ADR-115: the guard plus the old
794
- # inline chain cannot share one shell); resolution follows the standard in-repo-first,
795
- # superskill-staged fallback used by every pipeline checker.
739
+ # (d) Best-effort `exit 0` — feature status sync is a follow-up, not a completion gate;
740
+ # `record → done` runs `spur task check`. 0931 R5: with deferFeatureSync "true" (parallel
741
+ # mode) the deferral note lands in the task report and the sync is skipped entirely, so
742
+ # task branches never touch feature corpus files; the batch orchestrator performs the
743
+ # deferred sync on the base ref. 1004 R4: the sync is a bare `feature sync` — repeated-
744
+ # BLOCKED suppression (0411) now lives in the feature sync service itself, so the shell
745
+ # resolves feature_id via jq and delegates without a helper script.
796
746
  - kind: shell
797
747
  options:
798
748
  command: >-
799
- S=plugins/sp/scripts/record-feature-sync.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp record-feature-sync.mjs 2>/dev/null)"; [ "$deferFeatureSync" = "true" ] && echo "feature sync deferred to batch integration" >> ".spur/run/$wbs-report.txt" || if [ -f "$S" ]; then bun "$S" --spur-bin "$spurBin"; else echo "feature sync skipped — record-feature-sync not found in plugins/sp/scripts/ nor staged" >> ".spur/run/$wbs-report.txt"; fi; exit 0
749
+ if [ "$deferFeatureSync" = "true" ]; then echo "feature sync deferred to batch integration" >> ".spur/run/$wbs-report.txt"; else FID="$($spurBin task show $wbs --json 2>/dev/null | jq -r '(.feature_id // .frontmatter.feature_id) // empty' 2>/dev/null)"; if [ -n "$FID" ]; then $spurBin feature sync "$FID" --json || echo "feature sync failed — re-run: $spurBin feature sync $FID" >> ".spur/run/$wbs-report.txt"; else echo "Orphan task $wbs — no feature_id linked — proposal: consider linking to a parent feature." >> ".spur/run/$wbs-report.txt"; fi; fi; exit 0
800
750
  # (d) F96 (0983): residual-scan.ts owns the sweep; shell only resolves it. The
801
751
  # scan moved here from verify: it must read the task file AFTER `task record`
802
752
  # flipped the verdict-proven Requirement/AC boxes (0692), or a proven-but-unticked
@@ -807,7 +757,7 @@ states:
807
757
  - kind: shell
808
758
  options:
809
759
  command: >-
810
- S=plugins/sp/scripts/residual-scan.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp residual-scan.mjs 2>/dev/null)"; if [ ! -f "$S" ]; then echo "residual-scan failed closed — scanner not found in plugins/sp/scripts/ nor staged — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; exit 1; fi; case "$S" in *.mjs) RUNNER=node ;; *) RUNNER=bun ;; esac; "$RUNNER" "$S" scan "$wbs" --spur-bin "$spurBin" && "$RUNNER" "$S" fold "$wbs" --spur-bin "$spurBin"
760
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/residual-scan.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/residual-scan.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ ! -f "$S" ]; then echo "residual-scan failed closed — scanner not found in plugins/sp/scripts/ nor staged — run 'superskill install sp --marketplace gobing-ai/spur'" >&2; exit 1; fi; "$RUNNER" "$S" scan "$wbs" --spur-bin "$spurBin" && "$RUNNER" "$S" fold "$wbs" --spur-bin "$spurBin"
811
761
  # (e) F96 R5 (0983): a sweep downgrade must reach the task record — re-record from
812
762
  # the FINAL (post-fold) artifact so Testing carries the downgraded verdict; a failed
813
763
  # task must not retain a stale PASS Testing narrative. Soft (exit 0): the downgrade
@@ -833,7 +783,7 @@ states:
833
783
  - kind: shell
834
784
  options:
835
785
  command: >-
836
- S=plugins/sp/scripts/residual-scan.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp residual-scan.mjs 2>/dev/null)"; if [ -f "$S" ]; then case "$S" in *.mjs) RUNNER=node ;; *) RUNNER=bun ;; esac; "$RUNNER" "$S" settle "$wbs" --spur-bin "$spurBin" || echo "residual-settle failed — re-run: residual-scan settle $wbs" >&2; else echo "residual-scan not staged — settle skipped — re-run: residual-scan settle $wbs" >&2; fi; exit 0
786
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/residual-scan.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/residual-scan.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then "$RUNNER" "$S" settle "$wbs" --spur-bin "$spurBin" || echo "residual-settle failed — re-run: residual-scan settle $wbs" >&2; else echo "residual-scan not staged — settle skipped — re-run: residual-scan settle $wbs" >&2; fi; exit 0
837
787
  # (c) command.gate owns the done transition with classified transient retry.
838
788
  - kind: command.gate
839
789
  options:
@@ -847,10 +797,9 @@ states:
847
797
  resultFile: .spur/run/${vars.__runId}-done-transition.status
848
798
  softFail: false
849
799
  timeoutMs: 120000
850
- - kind: note
851
- options:
852
- message: "Pipeline complete for task ${vars.wbs} (done gate cleared)."
853
800
  # (e) advisory terminal checkpoint, one printf (0711 R1–R3; binding corrected 0784 R4).
801
+ # 1007 R3: the former done note ("Pipeline complete ...") is dropped — the checkpoint
802
+ # below is the terminal record.
854
803
  - kind: shell
855
804
  options:
856
805
  command: >-
@@ -880,24 +829,22 @@ states:
880
829
  - kind: shell
881
830
  options:
882
831
  command: >-
883
- S=plugins/sp/scripts/residual-scan.ts; [ -f config/plugin-scripts.json -a -f "$S" ] || S="$(superskill script path sp residual-scan.mjs 2>/dev/null)"; if [ -f "$S" ]; then case "$S" in *.mjs) RUNNER=node ;; *) RUNNER=bun ;; esac; "$RUNNER" "$S" report "$wbs" --spur-bin "$spurBin" || echo "residual-report failed — re-run: residual-scan report $wbs" >&2; else echo "residual-scan not staged — report skipped — re-run: residual-scan report $wbs" >&2; fi; exit 0
832
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/residual-scan.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/residual-scan.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then "$RUNNER" "$S" report "$wbs" --spur-bin "$spurBin" || echo "residual-report failed — re-run: residual-scan report $wbs" >&2; else echo "residual-scan not staged — report skipped — re-run: residual-scan report $wbs" >&2; fi; exit 0
884
833
 
885
834
  - id: cancelled
886
835
  description: Terminal — pipeline cancelled by operator at the approval gate (R1).
887
836
 
888
837
  transitions:
889
- # ── precheck: size PASS + task check → implement; else → failed ──
838
+ # ── precheck: task check --precheck (size + evidence + four-layer, 1002 R4) → implement; ──
839
+ # else → failed. The flag folds the two deleted precheck scripts into one fail-closed
840
+ # guard: any size/evidence/structural error exits 1 and the fallback edge terminates.
890
841
  - from: precheck
891
842
  to: implement
892
843
  description: Deterministic size, evidence, and task checks are green — begin implementation.
893
- # (warn) 5 commands: named size/evidence status reads + task check (legibility, 0874).
894
844
  guard:
895
845
  kind: shell
896
846
  options:
897
- command: >-
898
- size_status="$(cat .spur/run/$wbs-precheck-size.status 2>/dev/null)";
899
- evidence_status="$(cat .spur/run/$wbs-precheck-evidence.status 2>/dev/null)";
900
- test "$size_status" = PASS && test "$evidence_status" = PASS && $spurBin task check $wbs
847
+ command: '$spurBin task check $wbs --precheck'
901
848
  - from: precheck
902
849
  to: failed
903
850
  terminalReason: failed-check
@@ -14,6 +14,8 @@
14
14
  # even under --auto (Iron Law #6: irreversible action -> surface to human).
15
15
  #
16
16
  # Shape: start -> task-resolve -> doc-sync -> learnings-validate -> learnings-append
17
+ # -> doc-tripwire (1037: repo-wide tripwire over the still-uncommitted wrap
18
+ # diff; repair also enters it; the task-resolve fast path does not)
17
19
  # -> metrics-record
18
20
  # -> feature-transition (conditional: if vars.feature set)
19
21
  # -> feature-verify (post-transition feature check gate, 0915 F1)
@@ -25,10 +27,14 @@
25
27
  # the same `repair` edge with a distinct invalid-learnings-shape status —
26
28
  # expectFile proves existence, never shape — while empty/missing captures
27
29
  # keep the soft skip by passing through to learnings-append;
30
+ # learnings-append and repair converge on doc-tripwire (1037), which runs
31
+ # docTripwireCmd — the repo-wide tripwires over the still-uncommitted wrap
32
+ # diff — before metrics-record; the task-resolve fast path never enters it;
28
33
  # task-resolve with a VALIDATED empty list short-circuits to `skipped`;
29
34
  # invalid wrap input, an unresolved/non-completed task, a failed metrics
30
- # capture, a failed required sync, or a failed feature gate routes to
31
- # `failed` (0770) instead of being absorbed by an always-defensive skip.
35
+ # capture, a failed required sync, a failed repo-wide tripwire (1037), or
36
+ # a failed feature gate routes to `failed` (0770) instead of being
37
+ # absorbed by an always-defensive skip.
32
38
  #
33
39
  # Vars (passed as a JSON object via `--vars`):
34
40
  # tasks — JSON array of WBS strings, passed as a JSON-encoded STRING value
@@ -80,7 +86,7 @@
80
86
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
81
87
  kind: state-machine
82
88
  name: wrapup-pipeline
83
- version: "6"
89
+ version: "7"
84
90
  description: "Post-execution wrap-up: doc-sync (doc drift + learning capture), metrics, feature-transition, branch-cleanup"
85
91
  iterationBound: 10
86
92
  initialState: start
@@ -105,6 +111,13 @@ vars:
105
111
  # TRUSTED CONFIG ONLY — this string is executed via `sh -c` (same surface as
106
112
  # task-pipeline's qualityGateCmd). Never interpolate untrusted input into it.
107
113
  featureGateCmd: '$spurBin feature check "$feature"'
114
+ # Repo-wide tripwire over the wrap diff (1037): runs `bun run test-repo-wide`
115
+ # only when package.json declares the script, so other projects no-op. Callers
116
+ # can override it with --vars; an empty string disables the check (the hop
117
+ # still writes PASS).
118
+ # TRUSTED CONFIG ONLY — this string is executed via `sh -c` (same surface as
119
+ # featureGateCmd). Never interpolate untrusted input into it.
120
+ docTripwireCmd: 'if jq -e ''.scripts["test-repo-wide"]'' package.json >/dev/null 2>&1; then bun run test-repo-wide; fi'
108
121
  __hitlAnswer: ""
109
122
 
110
123
  states:
@@ -126,7 +139,8 @@ states:
126
139
  - id: task-resolve
127
140
  description: >
128
141
  Validate the task list ONCE and evaluate closed proportional routing (0758
129
- R1-R4). Validation (0770 + 0783): vars.tasks must be a JSON array of
142
+ R1-R4). When vars.feature is set, resolve also runs the feature pre-flight
143
+ (1033 R2). Validation (0770 + 0783): vars.tasks must be a JSON array of
130
144
  canonical four-digit WBS strings — non-arrays, non-strings, whitespace or
131
145
  other shapes are rejected, not trimmed — deduplicated in first-seen
132
146
  order, with __runId present, and every member must resolve via `spur task
@@ -154,10 +168,8 @@ states:
154
168
  options:
155
169
  command: >-
156
170
  mkdir -p .spur/run &&
157
- if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
158
- bun plugins/sp/scripts/wrapup-steps.ts resolve;
159
- elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
160
- node "$W" resolve;
171
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/wrapup-steps.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/wrapup-steps.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then
172
+ "$RUNNER" "$S" resolve;
161
173
  else
162
174
  echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
163
175
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
@@ -174,13 +186,14 @@ states:
174
186
  command: >-
175
187
  if [ -n "$mode" ]; then
176
188
  printf '%s\n' "$mode" > ".spur/run/$__runId-mode.txt";
177
- elif [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-drift-probe.ts ]; then
178
- bun plugins/sp/scripts/wrapup-drift-probe.ts;
179
- elif W="$(superskill script path sp wrapup-drift-probe.mjs 2>/dev/null)" && [ -f "$W" ]; then
180
- node "$W";
181
189
  else
182
- printf '{"clean":false,"reasons":["probe script not found"],"paths":[]}' > ".spur/run/$__runId-drift-probe.json";
183
- printf '\n' > ".spur/run/$__runId-mode.txt";
190
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/wrapup-drift-probe.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/wrapup-drift-probe.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)";
191
+ if [ -f "$S" ]; then
192
+ "$RUNNER" "$S";
193
+ else
194
+ printf '{"clean":false,"reasons":["probe script not found"],"paths":[]}' > ".spur/run/$__runId-drift-probe.json";
195
+ printf '\n' > ".spur/run/$__runId-mode.txt";
196
+ fi;
184
197
  fi
185
198
  - kind: file.read.into-var
186
199
  options:
@@ -201,10 +214,8 @@ states:
201
214
  options:
202
215
  command: >-
203
216
  mkdir -p .spur/run .spur/memory &&
204
- if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
205
- bun plugins/sp/scripts/wrapup-steps.ts route-reason;
206
- elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
207
- node "$W" route-reason;
217
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/wrapup-steps.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/wrapup-steps.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then
218
+ "$RUNNER" "$S" route-reason;
208
219
  else
209
220
  echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
210
221
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
@@ -302,8 +313,8 @@ states:
302
313
  capture that fails learnings-validate's shape gate (0986). The miss is
303
314
  recorded to a run-scoped repair status — text distinguishing a narration-only
304
315
  shape failure from the missing-file contract violation — and wrap-up proceeds
305
- to metrics-record; the run log and action trace already name the violated
306
- contract and the observed value.
316
+ through the doc-tripwire hop (1037) to metrics-record; the run log and
317
+ action trace already name the violated contract and the observed value.
307
318
  onEnter:
308
319
  - kind: shell
309
320
  options:
@@ -316,6 +327,27 @@ states:
316
327
  fi &&
317
328
  exit 0
318
329
 
330
+ - id: doc-tripwire
331
+ description: >
332
+ Repo-wide tripwire over the wrap diff (1037): the doc-sync edits are still
333
+ uncommitted when wrap-up reaches a terminal success, and the repo-wide
334
+ tripwires (repo-wide-tests/) execute only inside spur-check-feature — once
335
+ a wrap edit is committed on a clean tree, the working-diff tests (e.g.
336
+ adr-supersession (e)) can no longer see it. Entered from learnings-append
337
+ and repair, before metrics or feature state mutate, it runs the trusted
338
+ `docTripwireCmd` and records PASS or FAIL to a run-scoped status file; the
339
+ action always exits 0 so status truth lives in the file (0783 R4). Only a
340
+ verified run writes PASS; an empty docTripwireCmd disables the check and
341
+ still writes PASS. The no-doc-sync fast path (task-resolve →
342
+ metrics-record) never enters this state.
343
+ onEnter:
344
+ - kind: shell
345
+ options:
346
+ command: >-
347
+ sh -c "$docTripwireCmd" &&
348
+ printf 'PASS\n' > ".spur/run/$__runId-wrapup-doc-tripwire.status" ||
349
+ printf 'FAIL\n' > ".spur/run/$__runId-wrapup-doc-tripwire.status"
350
+
319
351
  - id: metrics-record
320
352
  description: >
321
353
  Append one JSONL row per task to .spur/memory/wrapup-metrics.jsonl deterministically.
@@ -334,10 +366,8 @@ states:
334
366
  options:
335
367
  command: >-
336
368
  mkdir -p .spur/run &&
337
- if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
338
- bun plugins/sp/scripts/wrapup-steps.ts metrics;
339
- elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
340
- node "$W" metrics;
369
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/wrapup-steps.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/wrapup-steps.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then
370
+ "$RUNNER" "$S" metrics;
341
371
  else
342
372
  echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
343
373
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-metrics.status";
@@ -345,7 +375,8 @@ states:
345
375
 
346
376
  - id: feature-transition
347
377
  description: >
348
- If vars.feature is set, sync feature status via bounded feature-sync-bounded
378
+ If vars.feature is set, sync feature status via `spur feature sync` (repeated-BLOCKED
379
+ suppression is built into the sync service, so no wrapper script is needed)
349
380
  (0411). The shell terminates `exit 0` so artifacts already landed are never
350
381
  discarded, but the outcome is truthful (0770 + 0783 R4): sync succeeds only
351
382
  with a valid proposal matching vars.feature, no gateBlocked/requiresConfirm
@@ -368,10 +399,8 @@ states:
368
399
  options:
369
400
  command: >-
370
401
  mkdir -p .spur/run &&
371
- if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
372
- bun plugins/sp/scripts/wrapup-steps.ts feature-transition;
373
- elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
374
- node "$W" feature-transition;
402
+ eval "$(jq -r 'if .mode=="source-repo" then "S="+@sh"\(.dir)/wrapup-steps.ts"+" RUNNER=bun" elif .mode=="installed" then "S="+@sh"\(.dir)/wrapup-steps.mjs"+" RUNNER=node" else empty end' ".spur/run/$__runId-script-root.json" 2>/dev/null)"; if [ -f "$S" ]; then
403
+ "$RUNNER" "$S" feature-transition;
375
404
  else
376
405
  echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
377
406
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-sync.status";
@@ -433,9 +462,10 @@ states:
433
462
  - id: failed
434
463
  description: >
435
464
  Terminal — wrap-up failed (0770): invalid wrap input, an unresolved or
436
- non-completed task, a failed metrics capture, a failed required sync, or
437
- a failed feature gate. Already-written artifacts (learnings, metrics
438
- rows, route reason) are preserved; nothing is rolled back.
465
+ non-completed task, a failed metrics capture, a failed required sync, a
466
+ failed repo-wide tripwire (1037), or a failed feature gate.
467
+ Already-written artifacts (learnings, metrics rows, route reason) are
468
+ preserved; nothing is rolled back.
439
469
 
440
470
  transitions:
441
471
  # ── start -> task-resolve ──
@@ -550,20 +580,43 @@ transitions:
550
580
  guard:
551
581
  kind: always
552
582
 
553
- # ── linear body: learnings-append -> metrics-record ──
583
+ # ── doc-sync exits converge on the doc-tripwire hop (1037) before metrics ──
554
584
  - from: learnings-append
555
- to: metrics-record
556
- description: Learning capture appended (or soft-skipped) — record metrics.
585
+ to: doc-tripwire
586
+ description: Learning capture appended (or soft-skipped) — run the repo-wide tripwire over the wrap diff.
557
587
  guard:
558
588
  kind: always
559
589
 
560
- # ── repair: cheap path, no re-dispatch -> metrics-record ──
561
590
  - from: repair
562
- to: metrics-record
563
- description: Contract-violation repair recorded — proceed to metrics without re-dispatch.
591
+ to: doc-tripwire
592
+ description: Contract-violation repair recorded — run the tripwire before metrics without re-dispatch.
564
593
  guard:
565
594
  kind: always
566
595
 
596
+ # ── doc-tripwire: repo-wide tripwire gate (1037) ──
597
+ # Declaration order matters: the FAIL edge is tried first so a failed tripwire
598
+ # can never be absorbed by the PASS edge; a missing status matches neither
599
+ # edge and follows the engine's no-match behavior for status-file states.
600
+ - from: doc-tripwire
601
+ to: failed
602
+ terminalReason: failed-check
603
+ description: >
604
+ Repo-wide tripwire FAILED on the still-uncommitted wrap diff (1037): an
605
+ edit the doc-sync stage made (e.g. an in-place historical ADR change)
606
+ violates a working-diff test. Learnings and docs already on disk stay
607
+ put; nothing is rolled back.
608
+ guard:
609
+ kind: shell
610
+ options:
611
+ command: 'test "$(cat .spur/run/$__runId-wrapup-doc-tripwire.status 2>/dev/null)" = FAIL'
612
+ - from: doc-tripwire
613
+ to: metrics-record
614
+ description: Repo-wide tripwire PASS (or the check disabled by an empty docTripwireCmd) — record metrics.
615
+ guard:
616
+ kind: shell
617
+ options:
618
+ command: 'test "$(cat .spur/run/$__runId-wrapup-doc-tripwire.status 2>/dev/null)" = PASS'
619
+
567
620
  # ── metrics-record: fail edge, then conditional routing (feature → merge → done) ──
568
621
  # Declaration order matters: the FAIL edge is tried first (0770), then
569
622
  # feature-transition (if feature is set), then branch-cleanup (if merge=true
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.95",
3
+ "version": "0.3.97",
4
4
  "description": "Spur CLI — local-first harness for mainstream coding agents: constraint checking, workflow orchestration, agent health, and history analytics. Bun-native; exposes the `spur` command.",
5
5
  "keywords": [
6
6
  "spur",
@@ -49,7 +49,7 @@
49
49
  "dev": "bun run src/index.ts",
50
50
  "build": "bun run ../../scripts/spur-dev.ts build-cli",
51
51
  "build:binaries": "bun run ../../scripts/spur-dev.ts build-binaries",
52
- "build:bundle": "bun run ../../scripts/spur-dev.ts bundle-web && bun run ../../scripts/spur-dev.ts emit-board-types && bun build src/index.ts --target=bun --outfile spur.js && bun run ../../scripts/spur-dev.ts bundle-config config && bun run ../../scripts/spur-dev.ts bundle-plugins",
52
+ "build:bundle": "bun run ../../scripts/commands/bundle-plugin-lib.ts && bun run ../../scripts/spur-dev.ts bundle-web && bun run ../../scripts/spur-dev.ts emit-board-types && bun build src/index.ts --target=bun --outfile spur.js && bun run ../../scripts/spur-dev.ts bundle-config config && bun run ../../scripts/spur-dev.ts bundle-plugins",
53
53
  "test": "bun test tests --coverage --coverage-dir=../../.coverage --pass-with-no-tests",
54
54
  "typecheck": "tsc --noEmit",
55
55
  "prepack": "bun run build:bundle",