@gobing-ai/spur 0.3.93 → 0.3.95

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 (153) 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 +5 -0
  5. package/config/rules/boundary/test-subpath-boundary.yaml +48 -0
  6. package/config/rules/strict/runtime-boundaries.yaml +2 -1
  7. package/config/rules/structure/protected-files.yaml +4 -0
  8. package/config/workflows/feature-verification.yaml +7 -4
  9. package/config/workflows/idea-pipeline.yaml +10 -4
  10. package/config/workflows/task-pipeline.yaml +71 -52
  11. package/config/workflows/wrapup-pipeline.yaml +92 -17
  12. package/package.json +15 -11
  13. package/plugins/sp/README.md +1 -1
  14. package/plugins/sp/commands/dev-review-session.md +4 -4
  15. package/plugins/sp/commands/dev-review.md +16 -5
  16. package/plugins/sp/commands/dev-verify.md +7 -4
  17. package/plugins/sp/commands/dev-verifyall.md +4 -3
  18. package/plugins/sp/commands/dev-wrap.md +1 -1
  19. package/plugins/sp/hooks/context-post-tool.ts +2 -2
  20. package/plugins/sp/hooks/context-session-start.ts +2 -1
  21. package/plugins/sp/hooks/context-session-stop.ts +22 -14
  22. package/plugins/sp/hooks/pi/guard-extension.ts +112 -172
  23. package/plugins/sp/lib/idea-handoff.generated.mjs +2 -2
  24. package/plugins/sp/lib/inline-run.generated.d.mts +4 -2
  25. package/plugins/sp/lib/inline-run.generated.mjs +35 -14
  26. package/plugins/sp/plugin.json +1 -1
  27. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
  28. package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
  29. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
  30. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
  31. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
  32. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
  33. package/plugins/sp/scripts/feature-sync-bounded.mjs +5 -2
  34. package/plugins/sp/scripts/feature-sync-bounded.ts +3 -1
  35. package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
  36. package/plugins/sp/scripts/history-anatomy-cache.mjs +3 -3
  37. package/plugins/sp/scripts/history-anatomy-cache.ts +4 -4
  38. package/plugins/sp/scripts/inline-run-setup.mjs +60 -12
  39. package/plugins/sp/scripts/inline-run-setup.ts +122 -15
  40. package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
  41. package/plugins/sp/scripts/pr-reviewing.ts +1 -0
  42. package/plugins/sp/scripts/quality-gate.mjs +15 -6
  43. package/plugins/sp/scripts/quality-gate.ts +30 -9
  44. package/plugins/sp/scripts/residual-scan.mjs +4 -4
  45. package/plugins/sp/scripts/residual-scan.ts +5 -4
  46. package/plugins/sp/scripts/script-contract-check.ts +175 -46
  47. package/plugins/sp/scripts/script-root.mjs +125 -0
  48. package/plugins/sp/scripts/script-root.ts +195 -0
  49. package/plugins/sp/scripts/stage-registry-adapter.ts +1 -1
  50. package/plugins/sp/scripts/task-evidence-precheck.ts +4 -2
  51. package/plugins/sp/scripts/task-size-precheck.ts +3 -1
  52. package/plugins/sp/scripts/validate-commands.ts +12 -12
  53. package/plugins/sp/scripts/validate-flag-contracts.ts +42 -30
  54. package/plugins/sp/scripts/verify-answer-lint.ts +3 -1
  55. package/plugins/sp/scripts/workflow-step-profile.mjs +5 -2
  56. package/plugins/sp/scripts/workflow-step-profile.ts +3 -1
  57. package/plugins/sp/scripts/wrapup-drift-probe.mjs +5 -2
  58. package/plugins/sp/scripts/wrapup-drift-probe.ts +4 -2
  59. package/plugins/sp/scripts/wrapup-steps.mjs +32 -8
  60. package/plugins/sp/scripts/wrapup-steps.ts +56 -10
  61. package/plugins/sp/skills/code-verification/SKILL.md +12 -7
  62. package/plugins/sp/skills/code-verification/references/verdict-schema.md +11 -0
  63. package/plugins/sp/skills/functional-review/SKILL.md +1 -1
  64. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  65. package/plugins/sp/skills/session-review/SKILL.md +11 -9
  66. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +9 -1
  67. package/plugins/sp/skills/spur-dev/references/dev-operations.md +8 -2
  68. package/plugins/sp/skills/spur-dev/references/execution-batch.md +65 -17
  69. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +16 -3
  70. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +22 -5
  71. package/plugins/sp/tsconfig.json +8 -0
  72. package/spur.js +20934 -21087
  73. package/web/_astro/{BoardApp.CerSBgis.js → BoardApp.BxJuwD7I.js} +151 -152
  74. package/web/_astro/BoardApp.Cpxntzad.js +1 -0
  75. package/web/_astro/TaskDetail.C5bW4WGV.js +1 -0
  76. package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
  77. package/web/_astro/arc.CyjRvNMY.js +1 -0
  78. package/web/_astro/architectureDiagram-3BPJPVTR.B4lWRzJA.js +36 -0
  79. package/web/_astro/{blockDiagram-GPEHLZMM.DXVIiv0p.js → blockDiagram-GPEHLZMM.Be38USQ2.js} +1 -1
  80. package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
  81. package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
  82. package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
  83. package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
  84. package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
  85. package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
  86. package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
  87. package/web/_astro/{c4Diagram-AAUBKEIU.BbF_zCxW.js → c4Diagram-AAUBKEIU.DT9Fj5Qx.js} +1 -1
  88. package/web/_astro/channel.CI6N_tCg.js +1 -0
  89. package/web/_astro/{chunk-2J33WTMH.CAgQHpPC.js → chunk-2J33WTMH.SeSBWLg5.js} +1 -1
  90. package/web/_astro/{chunk-4BX2VUAB.BN-5tpw4.js → chunk-4BX2VUAB.DoO4VE14.js} +1 -1
  91. package/web/_astro/{chunk-55IACEB6.CnPkEEr0.js → chunk-55IACEB6.DrmTFA53.js} +1 -1
  92. package/web/_astro/{chunk-727SXJPM.BQzQeMVm.js → chunk-727SXJPM.Bi-TUb_V.js} +4 -4
  93. package/web/_astro/{chunk-AQP2D5EJ.B6xNyDnL.js → chunk-AQP2D5EJ.B-xp8Uhi.js} +1 -1
  94. package/web/_astro/{chunk-FMBD7UC4.C7f9Ih78.js → chunk-FMBD7UC4.DGWrDnUU.js} +1 -1
  95. package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
  96. package/web/_astro/{chunk-ND2GUHAM.CNV1dFXT.js → chunk-ND2GUHAM._BagvPDy.js} +1 -1
  97. package/web/_astro/{chunk-QZHKN3VN.Cudn2TkJ.js → chunk-QZHKN3VN.VOboYWQ0.js} +1 -1
  98. package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
  99. package/web/_astro/{classDiagram-4FO5ZUOK.D1NwP50q.js → classDiagram-4FO5ZUOK.D_apfHS7.js} +1 -1
  100. package/web/_astro/{classDiagram-v2-Q7XG4LA2.D1NwP50q.js → classDiagram-v2-Q7XG4LA2.D_apfHS7.js} +1 -1
  101. package/web/_astro/client.BhluLQqe.js +1 -0
  102. package/web/_astro/client.DBpSRLnx.js +9 -0
  103. package/web/_astro/cose-bilkent-S5V4N54A.DNLU_L-x.js +1 -0
  104. package/web/_astro/{cynefin-OW5HDTMX.BmK52w8G.js → cynefin-OW5HDTMX.D8borhV-.js} +1 -1
  105. package/web/_astro/{dagre-BM42HDAG.Bfy5CTDT.js → dagre-BM42HDAG.Uc0lvjcV.js} +2 -2
  106. package/web/_astro/diagram-2AECGRRQ.DDIxtDBo.js +43 -0
  107. package/web/_astro/diagram-5GNKFQAL.DVoDnD3H.js +10 -0
  108. package/web/_astro/{diagram-KO2AKTUF.CW_vMJ4z.js → diagram-KO2AKTUF.CRubTJ-0.js} +3 -3
  109. package/web/_astro/{diagram-LMA3HP47.B_8ZGF67.js → diagram-LMA3HP47.m9SeYcXQ.js} +1 -1
  110. package/web/_astro/{diagram-OG6HWLK6.BppnHsdS.js → diagram-OG6HWLK6.C6s2ZjV5.js} +1 -1
  111. package/web/_astro/{erDiagram-TEJ5UH35.BEuHXcjJ.js → erDiagram-TEJ5UH35.nSzr5KTj.js} +5 -5
  112. package/web/_astro/{flowDiagram-I6XJVG4X.CH-UlnGr.js → flowDiagram-I6XJVG4X.DrZcys16.js} +4 -4
  113. package/web/_astro/ganttDiagram-6RSMTGT7.Do4M1b-K.js +292 -0
  114. package/web/_astro/{gitGraphDiagram-PVQCEYII.XnPxPPZN.js → gitGraphDiagram-PVQCEYII.BWc9oJ4l.js} +1 -1
  115. package/web/_astro/index.5dn-Zm-2.js +1 -0
  116. package/web/_astro/index.C-IqPpW2.js +1 -0
  117. package/web/_astro/index.CENnIEqT.css +1 -0
  118. package/web/_astro/index.Cyc_GA4f.js +1 -0
  119. package/web/_astro/index.tZfqwSut.js +1 -0
  120. package/web/_astro/{infoDiagram-5YYISTIA.JyjYRu_T.js → infoDiagram-5YYISTIA.BIdOctFk.js} +1 -1
  121. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BBRBF-Fo.js → ishikawaDiagram-YF4QCWOH.CV5SuG2t.js} +5 -5
  122. package/web/_astro/{journeyDiagram-JHISSGLW.C_iymSyp.js → journeyDiagram-JHISSGLW.CxE-rEJ-.js} +1 -1
  123. package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
  124. package/web/_astro/{kanban-definition-UN3LZRKU.DdfW-Oqt.js → kanban-definition-UN3LZRKU.BPRdKWs_.js} +7 -7
  125. package/web/_astro/{linear.C2_IkbZT.js → linear.zRsuuDTE.js} +1 -1
  126. package/web/_astro/mermaid.core.jAJTcMKc.js +301 -0
  127. package/web/_astro/{mindmap-definition-RKZ34NQL.DAZIxQSK.js → mindmap-definition-RKZ34NQL.BffJxfCr.js} +2 -2
  128. package/web/_astro/{pieDiagram-4H26LBE5.CN8sIhKM.js → pieDiagram-4H26LBE5.CacwWaE3.js} +3 -3
  129. package/web/_astro/{quadrantDiagram-W4KKPZXB.3dGcX5GP.js → quadrantDiagram-W4KKPZXB.hXELD06-.js} +1 -1
  130. package/web/_astro/{requirementDiagram-4Y6WPE33.BV2y4dd6.js → requirementDiagram-4Y6WPE33.DdEbPcqT.js} +3 -3
  131. package/web/_astro/{sankeyDiagram-5OEKKPKP.Cqo15Tvo.js → sankeyDiagram-5OEKKPKP.DgaDocKv.js} +4 -4
  132. package/web/_astro/{sequenceDiagram-3UESZ5HK.CROCPMJB.js → sequenceDiagram-3UESZ5HK.B0KScnAu.js} +1 -1
  133. package/web/_astro/{stateDiagram-AJRCARHV.RfXZrkFE.js → stateDiagram-AJRCARHV.CV9M_WNc.js} +1 -1
  134. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CPXmbBs9.js → stateDiagram-v2-BHNVJYJU.BOAo74Et.js} +1 -1
  135. package/web/_astro/{timeline-definition-PNZ67QCA.DdgKTiO8.js → timeline-definition-PNZ67QCA.CqqepUMe.js} +3 -3
  136. package/web/_astro/{vennDiagram-CIIHVFJN.CPNVSHF1.js → vennDiagram-CIIHVFJN.DDqe4dOG.js} +5 -5
  137. package/web/_astro/{wardleyDiagram-YWT4CUSO.CQhA0Jyr.js → wardleyDiagram-YWT4CUSO.B3R9OPdO.js} +3 -3
  138. package/web/_astro/{xychartDiagram-2RQKCTM6.n61BWyy4.js → xychartDiagram-2RQKCTM6.B3SvgXpQ.js} +1 -1
  139. package/web/board-runtime.json +69 -0
  140. package/web/index.html +2 -2
  141. package/web/_astro/BoardApp.eoTz0pZs.js +0 -1
  142. package/web/_astro/TaskDetail.DCqiC-OZ.js +0 -1
  143. package/web/_astro/arc.CPwg6Rw0.js +0 -1
  144. package/web/_astro/architectureDiagram-3BPJPVTR.DM_vp_hO.js +0 -36
  145. package/web/_astro/channel.MYZLKNwy.js +0 -1
  146. package/web/_astro/client.yhYJvxCU.js +0 -9
  147. package/web/_astro/cose-bilkent-S5V4N54A.B1wSL-Xb.js +0 -1
  148. package/web/_astro/diagram-2AECGRRQ.DhNnvUvX.js +0 -43
  149. package/web/_astro/diagram-5GNKFQAL.lTX5KwnS.js +0 -10
  150. package/web/_astro/ganttDiagram-6RSMTGT7.BO81S85v.js +0 -292
  151. package/web/_astro/index.De90oHcH.js +0 -1
  152. package/web/_astro/index.Hjbr15fG.css +0 -1
  153. package/web/_astro/mermaid.core.GAOYeSR0.js +0 -303
@@ -13,13 +13,18 @@
13
13
  # pauses,
14
14
  # even under --auto (Iron Law #6: irreversible action -> surface to human).
15
15
  #
16
- # Shape: start -> task-resolve -> doc-sync -> learnings-append -> metrics-record
16
+ # Shape: start -> task-resolve -> doc-sync -> learnings-validate -> learnings-append
17
+ # -> metrics-record
17
18
  # -> feature-transition (conditional: if vars.feature set)
18
19
  # -> feature-verify (post-transition feature check gate, 0915 F1)
19
20
  # -> branch-cleanup (conditional: if vars.merge=true)
20
21
  # -> done
21
22
  # doc-sync routes a contract violation to `repair` (cheap, no
22
23
  # re-dispatch — ADR-118 pilot) and an executor failure to `failed`;
24
+ # learnings-validate (0986) rejects a nonempty narration-only capture to
25
+ # the same `repair` edge with a distinct invalid-learnings-shape status —
26
+ # expectFile proves existence, never shape — while empty/missing captures
27
+ # keep the soft skip by passing through to learnings-append;
23
28
  # task-resolve with a VALIDATED empty list short-circuits to `skipped`;
24
29
  # invalid wrap input, an unresolved/non-completed task, a failed metrics
25
30
  # capture, a failed required sync, or a failed feature gate routes to
@@ -75,7 +80,7 @@
75
80
  "$schema": "@gobing-ai/spur/schemas/state-machine-workflow.schema.json"
76
81
  kind: state-machine
77
82
  name: wrapup-pipeline
78
- version: "5"
83
+ version: "6"
79
84
  description: "Post-execution wrap-up: doc-sync (doc drift + learning capture), metrics, feature-transition, branch-cleanup"
80
85
  iterationBound: 10
81
86
  initialState: start
@@ -108,6 +113,12 @@ states:
108
113
  Pipeline start. Task statuses are NOT mutated by wrap-up — wrap-up consumes
109
114
  completed tasks and produces learning/metrics/doc artifacts only.
110
115
  onEnter:
116
+ # 0960 R2: run-start script-resolution identity — record which tree this run
117
+ # resolved (source-repo vs installed) before any step executes. Never aborts.
118
+ - kind: shell
119
+ options:
120
+ command: >-
121
+ 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
111
122
  - kind: note
112
123
  options:
113
124
  message: "Wrap-up pipeline start for tasks: ${vars.tasks}. Task statuses are NOT mutated."
@@ -143,12 +154,12 @@ states:
143
154
  options:
144
155
  command: >-
145
156
  mkdir -p .spur/run &&
146
- if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
157
+ if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
147
158
  bun plugins/sp/scripts/wrapup-steps.ts resolve;
148
159
  elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
149
160
  node "$W" resolve;
150
161
  else
151
- echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
162
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
152
163
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
153
164
  fi
154
165
  # (0944 R1-R3) deterministic drift probe — runs ONLY when the caller left mode
@@ -163,7 +174,7 @@ states:
163
174
  command: >-
164
175
  if [ -n "$mode" ]; then
165
176
  printf '%s\n' "$mode" > ".spur/run/$__runId-mode.txt";
166
- elif [ -f plugins/sp/scripts/wrapup-drift-probe.ts ]; then
177
+ elif [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-drift-probe.ts ]; then
167
178
  bun plugins/sp/scripts/wrapup-drift-probe.ts;
168
179
  elif W="$(superskill script path sp wrapup-drift-probe.mjs 2>/dev/null)" && [ -f "$W" ]; then
169
180
  node "$W";
@@ -190,12 +201,12 @@ states:
190
201
  options:
191
202
  command: >-
192
203
  mkdir -p .spur/run .spur/memory &&
193
- if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
204
+ if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
194
205
  bun plugins/sp/scripts/wrapup-steps.ts route-reason;
195
206
  elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
196
207
  node "$W" route-reason;
197
208
  else
198
- echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
209
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
199
210
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-resolve.status";
200
211
  fi
201
212
 
@@ -234,6 +245,34 @@ states:
234
245
  expectFile: .spur/run/${vars.__runId}-wrapup-learnings.md
235
246
  timeoutMs: ${vars.stepTimeoutMs}
236
247
 
248
+ - id: learnings-validate
249
+ description: >
250
+ Structural shape gate between doc-sync and the append (0986): expectFile
251
+ asserts the capture EXISTS and nothing about its shape, so a
252
+ stream-of-thought capture could be appended verbatim to the tracked
253
+ memory file. This state classifies a NONEMPTY capture — a date, a
254
+ four-digit task WBS, and at least one markdown bullet — and writes PASS
255
+ or invalid-learnings-shape to a run-scoped status. An empty or missing
256
+ capture keeps the existing soft skip (PASS) so learnings-append still
257
+ performs its no-op; an unclassifiable status fails loud instead of
258
+ appending it. Structural check only — the doc-sync prompt owns prose
259
+ quality; no new engine-level content contract.
260
+ onEnter:
261
+ # (warn) structural check only; the doc-sync prompt owns prose quality (0986 Design).
262
+ # The WBS predicate excludes digit/dash neighbours so a date's year cannot satisfy it.
263
+ - kind: shell
264
+ options:
265
+ command: >-
266
+ mkdir -p .spur/run &&
267
+ S=".spur/run/$__runId-wrapup-learnings.status" &&
268
+ F=".spur/run/$__runId-wrapup-learnings.md" &&
269
+ if [ ! -s "$F" ] || awk '/[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/{d=1} /(^|[^0-9-])[0-9][0-9][0-9][0-9]([^0-9-]|$)/{w=1} /^[[:space:]]*[-*] /{b=1} END{exit !(d+w+b==3)}' "$F"; then
270
+ printf 'PASS\n' > "$S";
271
+ else
272
+ printf 'invalid-learnings-shape\n' > "$S";
273
+ fi &&
274
+ exit 0
275
+
237
276
  - id: learnings-append
238
277
  description: >
239
278
  Best-effort soft append of the captured learnings to .spur/memory/learnings.md
@@ -259,15 +298,22 @@ states:
259
298
  description: >
260
299
  Contract-violation repair (ADR-118 pilot, 0871): doc-sync's agent.run missed
261
300
  its declared post-condition after a clean exit, so wrap-up does NOT re-dispatch
262
- the expensive agent on this attempt. The miss is recorded to a run-scoped
263
- repair status and wrap-up proceeds to metrics-record — the run log and action
264
- trace already name the violated contract and the observed value.
301
+ the expensive agent on this attempt. Also the destination for a nonempty
302
+ capture that fails learnings-validate's shape gate (0986). The miss is
303
+ recorded to a run-scoped repair status — text distinguishing a narration-only
304
+ 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.
265
307
  onEnter:
266
308
  - kind: shell
267
309
  options:
268
310
  command: >-
269
311
  mkdir -p .spur/run &&
270
- printf 'contract-violation: doc-sync learnings capture missed its declared contract — repair skipped re-dispatch (ADR-118 pilot)\n' > ".spur/run/$__runId-wrapup-repair.status" &&
312
+ R=".spur/run/$__runId-wrapup-repair.status" &&
313
+ V="$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" &&
314
+ if [ "$V" = invalid-learnings-shape ]; then printf 'invalid-learnings-shape: narration-only learnings capture — append skipped, repair recorded (0986)\n' > "$R";
315
+ else printf 'contract-violation: doc-sync learnings capture missed its declared contract — repair skipped re-dispatch (ADR-118 pilot)\n' > "$R";
316
+ fi &&
271
317
  exit 0
272
318
 
273
319
  - id: metrics-record
@@ -288,12 +334,12 @@ states:
288
334
  options:
289
335
  command: >-
290
336
  mkdir -p .spur/run &&
291
- if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
337
+ if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
292
338
  bun plugins/sp/scripts/wrapup-steps.ts metrics;
293
339
  elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
294
340
  node "$W" metrics;
295
341
  else
296
- echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
342
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
297
343
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-metrics.status";
298
344
  fi
299
345
 
@@ -322,12 +368,12 @@ states:
322
368
  options:
323
369
  command: >-
324
370
  mkdir -p .spur/run &&
325
- if [ -f plugins/sp/scripts/wrapup-steps.ts ]; then
371
+ if [ -f config/plugin-scripts.json -a -f plugins/sp/scripts/wrapup-steps.ts ]; then
326
372
  bun plugins/sp/scripts/wrapup-steps.ts feature-transition;
327
373
  elif W="$(superskill script path sp wrapup-steps.mjs 2>/dev/null)" && [ -f "$W" ]; then
328
374
  node "$W" feature-transition;
329
375
  else
330
- echo "wrapup-steps failed closed — script not found — run 'superskill install sp'" >&2;
376
+ echo "wrapup-steps failed closed — script not found — run 'superskill install sp --marketplace gobing-ai/spur'" >&2;
331
377
  printf 'FAIL\n' > ".spur/run/$__runId-wrapup-sync.status";
332
378
  fi
333
379
 
@@ -460,8 +506,8 @@ transitions:
460
506
  guard:
461
507
  kind: contract-violation
462
508
  - from: doc-sync
463
- to: learnings-append
464
- description: Doc drift repair + learning capture succeeded — append learnings.
509
+ to: learnings-validate
510
+ description: Doc drift repair + learning capture succeeded — validate the capture's shape before appending.
465
511
  guard:
466
512
  kind: action-ok
467
513
  - from: doc-sync
@@ -475,6 +521,35 @@ transitions:
475
521
  guard:
476
522
  kind: always
477
523
 
524
+ # ── learnings-validate: shape gate (0986) — invalid-shape to repair, PASS to append ──
525
+ # Declaration order matters: the discriminating invalid-shape edge is tried
526
+ # first, then PASS; an unreadable status fails loud rather than appending an
527
+ # unclassified capture.
528
+ - from: learnings-validate
529
+ to: repair
530
+ description: >
531
+ Nonempty capture failed the structural shape gate (no date/WBS/bullet) —
532
+ route to the cheap repair edge and never append narration (0986 R2).
533
+ guard:
534
+ kind: shell
535
+ options:
536
+ command: 'test "$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" = invalid-learnings-shape'
537
+ - from: learnings-validate
538
+ to: learnings-append
539
+ description: Capture is shape-valid (or empty/missing, keeping the soft skip) — append.
540
+ guard:
541
+ kind: shell
542
+ options:
543
+ command: 'test "$(cat .spur/run/$__runId-wrapup-learnings.status 2>/dev/null)" = PASS'
544
+ - from: learnings-validate
545
+ to: failed
546
+ terminalReason: failed-check
547
+ description: >
548
+ Validation status missing or unreadable — fail loud instead of appending
549
+ a capture whose shape was never classified.
550
+ guard:
551
+ kind: always
552
+
478
553
  # ── linear body: learnings-append -> metrics-record ──
479
554
  - from: learnings-append
480
555
  to: metrics-record
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/spur",
3
- "version": "0.3.93",
3
+ "version": "0.3.95",
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",
@@ -24,12 +24,16 @@
24
24
  "spur": "spur.js"
25
25
  },
26
26
  "exports": {
27
- "./schemas/*": "./schemas/*"
27
+ "./schemas/*": "./schemas/*",
28
+ "./board": {
29
+ "types": "./board/index.d.ts"
30
+ }
28
31
  },
29
32
  "files": [
30
33
  "spur.js",
31
34
  "config",
32
35
  "schemas",
36
+ "board",
33
37
  "web",
34
38
  "README.md",
35
39
  "plugins/",
@@ -45,7 +49,7 @@
45
49
  "dev": "bun run src/index.ts",
46
50
  "build": "bun run ../../scripts/spur-dev.ts build-cli",
47
51
  "build:binaries": "bun run ../../scripts/spur-dev.ts build-binaries",
48
- "build:bundle": "bun run ../../scripts/spur-dev.ts bundle-web && 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/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",
49
53
  "test": "bun test tests --coverage --coverage-dir=../../.coverage --pass-with-no-tests",
50
54
  "typecheck": "tsc --noEmit",
51
55
  "prepack": "bun run build:bundle",
@@ -53,14 +57,14 @@
53
57
  },
54
58
  "devDependencies": {
55
59
  "@commander-js/extra-typings": "^14.0.0",
56
- "@gobing-ai/ts-db": "^0.5.7",
57
- "@gobing-ai/ts-ai-runner": "^0.5.7",
58
- "@gobing-ai/ts-dual-workflow-engine": "^0.5.7",
59
- "@gobing-ai/ts-infra": "^0.5.7",
60
- "@gobing-ai/ts-llm-jsonl-importer": "^0.5.7",
61
- "@gobing-ai/ts-rule-engine": "^0.5.7",
62
- "@gobing-ai/ts-runtime": "^0.5.7",
63
- "@gobing-ai/ts-utils": "^0.5.7",
60
+ "@gobing-ai/ts-db": "^0.5.11",
61
+ "@gobing-ai/ts-ai-runner": "^0.5.11",
62
+ "@gobing-ai/ts-dual-workflow-engine": "^0.5.11",
63
+ "@gobing-ai/ts-infra": "^0.5.11",
64
+ "@gobing-ai/ts-llm-jsonl-importer": "^0.5.11",
65
+ "@gobing-ai/ts-rule-engine": "^0.5.11",
66
+ "@gobing-ai/ts-runtime": "^0.5.11",
67
+ "@gobing-ai/ts-utils": "^0.5.11",
64
68
  "@types/bun": "1.3.14",
65
69
  "@types/figlet": "^1.7.0",
66
70
  "@types/node-notifier": "8.0.5",
@@ -102,7 +102,7 @@ list this README is checked against.
102
102
  | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
103
103
  | `dev-next` | Status-aware router — inspect a task (or next frontier under a feature), dispatch the best existing `/sp:dev-*` command, chain on clean success |
104
104
  | `dev-run` | Run a task — full pipeline (precheck→implement→test→review→approve→verify→record→done) or single-step (implement) |
105
- | `dev-review` | Multi-dimensional review for a task or path — functional requirements traceability, SECUA framework, and architectural depth |
105
+ | `dev-review` | Multi-dimensional review for a task or path (traceability, SECUA, architecture); `--triage` fixes small findings, files the rest as tasks |
106
106
  | `dev-pr-review` | GitHub Codex PR review — prepare/reuse a PR, request `@codex review`, collect findings, validate/fix/re-review (fix mode); workflow-backed spine |
107
107
  | `dev-verify` | Verify a task against its requirements and Acceptance Criteria — traceability check producing a PASS/PARTIAL/FAIL verdict with evidence |
108
108
  | `dev-unit` | Generate or extend tests until the unit target is met |
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "Review the active coding-agent session immediately: summarize outcomes, distinguish resolved and open issues with evidence, and propose bounded improvements. With --triage: apply pure-doc / one-to-two-line fixes inline, then file remaining findings as one new task. Triggers: review this session, session wrap-up, immediate retrospective, what happened, what was resolved, triage findings"
2
+ description: "Review the active coding-agent session immediately: summarize outcomes, distinguish resolved and open issues with evidence, and propose bounded improvements. With --triage: apply pure-doc / one-to-two-line fixes inline, then file remaining findings as one or more implement-ready tasks. Triggers: review this session, session wrap-up, immediate retrospective, what happened, what was resolved, triage findings"
3
3
  role: reviewer
4
4
  argument-hint: "[<focus>] [--triage]"
5
5
  allowed-tools: ["Bash", "Read", "Grep", "Glob", "Skill"]
@@ -11,8 +11,8 @@ Wraps the **sp:session-review** skill for a lightweight review of the active hos
11
11
  default it is report-only: current conversation plus read-only repository evidence, run inline so
12
12
  the session context is preserved — no workflow launch, history import, task creation, or
13
13
  remediation. With `--triage`, it first triages the findings, then applies direct fixes (pure
14
- documentation work and one-to-two-line fixes) inline and files everything remaining as exactly one
15
- new task for further fixing. The result includes a non-overlapping time breakdown with durations in
14
+ documentation work and one-to-two-line fixes) inline and files everything remaining as one or more
15
+ implement-ready tasks for further fixing. The result includes a non-overlapping time breakdown with durations in
16
16
  `M:SS` or `H:MM:SS` form and `n/a` for unavailable measurements.
17
17
 
18
18
  ## Argument Flags
@@ -20,7 +20,7 @@ new task for further fixing. The result includes a non-overlapping time breakdow
20
20
  | Flag | Description | Default |
21
21
  | --- | --- | --- |
22
22
  | `[<focus>]` | Optional question or operation to emphasize without excluding material session outcomes. | full active session |
23
- | `--triage` | After the report: bucket findings → apply pure-doc / 1–2-line fixes inline → create one task for the remainder. | off (report-only) |
23
+ | `--triage` | After the report: bucket findings → apply pure-doc / 1–2-line fixes inline → file the remainder as one or more implement-ready tasks. | off (report-only) |
24
24
 
25
25
  For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
26
26
 
@@ -1,7 +1,7 @@
1
1
  ---
2
- description: "Review code for a task or path — multi-dimensional review across functional traceability, SECUA quality, and architectural depth. Triggers: \"review this\", \"check the code\", \"SECUA review\", \"dev review\", \"audit this\"."
2
+ description: "Review code for a task or path — multi-dimensional review across functional traceability, SECUA quality, and architectural depth. With --triage: fix small findings directly, then file the rest as implement-ready tasks. Triggers: \"review this\", \"check the code\", \"SECUA review\", \"dev review\", \"audit this\", \"triage review findings\"."
3
3
  role: reviewer
4
- argument-hint: "[<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--fix (deprecated)]"
4
+ argument-hint: "[<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--triage] [--worktree [<name>]]"
5
5
  allowed-tools: ["Bash", "Read", "Skill"]
6
6
  ---
7
7
 
@@ -16,17 +16,28 @@ Wraps the **sp:functional-review**, **sp:code-verification**, and **sp:code-impr
16
16
  | `[<wbs\|path>]` | Task WBS or source path to review. | cwd |
17
17
  | `--agent` `<inline\|auto\|name>` | Who runs the model-bearing review. | omit |
18
18
  | `--focus` `<dims>` | Review dimensions (functional / SECUA / architecture). | all |
19
- | `--fix` | Deprecated no-op + warning; route remediation to /sp:dev-verify --fix. | off |
19
+ | `--triage` | After the review: fix small findings directly, file the rest as one or more implement-ready tasks. | off (report-only) |
20
+ | `--worktree` `[<name>]` | Run the triage in an isolated git worktree; FF-merge on success, retain on failure. Bare `--worktree` creates a fresh tree; `--worktree <name>` adopts an existing worktree by name/path/branch. Requires `--triage`. | off |
20
21
 
21
22
  For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
22
23
 
23
24
  ## Usage
24
25
 
25
- /sp:dev-review [<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--fix (deprecated)]
26
+ ```
27
+ /sp:dev-review [<wbs|path>] [--agent <inline|auto|name>] [--focus <dims>] [--triage] [--worktree [<name>]]
28
+ /sp:dev-review scripts --triage
29
+ /sp:dev-review 0962 --triage --worktree
30
+ ```
31
+
32
+ Put the target first: a bare token after `--worktree` is read as the worktree name.
26
33
 
27
34
  ## Implementation
28
35
 
29
36
  - Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
30
37
  - WBS mode (`<wbs>`): `Skill(skill="sp:functional-review", args="<wbs> $ARGUMENTS")` + `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<wbs> $ARGUMENTS")` (functional traceability + SECUA framework + architectural depth; the three skills return review fragments and the coordinator (`sp:super-reviewer`) writes the combined `## Review` — F92 0593 R1)
31
38
  - Path mode (`<path>`): `Skill(skill="sp:code-verification", args="review $ARGUMENTS")` + `Skill(skill="sp:code-improvement", args="<path> $ARGUMENTS")` (advisory SECUA quality + architectural depth; performs no task mutation)
32
- - `--fix`: Deprecated (no-op + warning message; route remediation to `/sp:dev-verify --fix`). **`--next` removed** (feature H8, 2026-07-31) — it was a deprecated no-op; route progression to `/sp:dev-next`. **was: `--next` deprecated no-op.**
39
+ - `--triage`: after the review returns, run the triage protocol in [dev-operations.md § 2. review](../skills/spur-dev/references/dev-operations.md#2-review) **inline in this session** — the review skills and `sp:super-reviewer` stay report-only.
40
+ - Without `--worktree`, every write lands in the current working tree on the current branch: no branch creation, no checkout, no commit.
41
+ - `--worktree [<name>]`: the [execution-batch.md § Worktree isolation](../skills/spur-dev/references/execution-batch.md#worktree-isolation---worktree-name) lifecycle as a run of one (marker `command` = `dev-review`, `selector` = the target). Rejected without `--triage`.
42
+ While it runs, fixes and filed tasks land in the worktree copy; the main tree shows them only after the FF-merge.
43
+ - `--fix`: Deprecated (still accepted: no-op + warning; route remediation to `--triage` or `/sp:dev-verify --fix`). **`--next` removed** (feature H8, 2026-07-31) — it was a deprecated no-op; route progression to `/sp:dev-next`. **was: `--next` deprecated no-op.**
@@ -40,10 +40,13 @@ See `sp:code-verification` § Shippable readiness gate.
40
40
 
41
41
  - Apply the [inline-default execution-surface contract](../skills/spur-dev/references/cross-cutting.md#inline-default-execution-surface).
42
42
  - `Skill(skill="sp:code-verification", args="verify $ARGUMENTS")`
43
- - **Residual sweep (F96 R1):** after the verify answer is written and `task verdict` derives
44
- `.spur/run/<wbs>-verdict.json`, run `residual-scan scan` + `fold` (observe-only; folds the
45
- verdict's `residual-sweep` check, PASS → PARTIAL when a blocking residual exists) — under every
46
- `--fix` mode — before `spur task record`. Resolve the script via `superskill script path sp
43
+ - **Residual sweep (F96 R1, post-record since 0987):** after `task verdict` derives
44
+ `.spur/run/<wbs>-verdict.json` and `spur task record` has flipped the verdict-proven R/AC boxes,
45
+ run `residual-scan scan` + `fold` (observe-only; folds the verdict's `residual-sweep` check,
46
+ PASS → PARTIAL when a blocking residual exists) — under every `--fix` mode — then, only on a
47
+ downgrade, re-run `spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json` so Testing
48
+ carries the final verdict. Same order as the pipeline `record` state (0983): scanning before
49
+ record reads unticked-but-proven boxes and folds a clean PASS to PARTIAL. Resolve the script via `superskill script path sp
47
50
  residual-scan.mjs`; never reference `plugins/sp/scripts/` in shipped surfaces
48
51
  (script-contract-check rule 4). When the task reaches `done` through `--next`, run
49
52
  `residual-scan settle` (best-effort). On a folded PARTIAL: the bounded remediation loop has
@@ -72,8 +72,9 @@ Without `--fix all`, shippable is not evaluated (optional note: use `--fix all`
72
72
  - Batch orchestration: `Skill(skill="sp:spur-dev", args="verifyall $ARGUMENTS")`
73
73
  - Per-task verification (inner): `Skill(skill="sp:code-verification", args="verify <wbs> $SHARED_FLAGS")`
74
74
  - Shippable gate: once after the batch (same skill § Shippable readiness gate)
75
- - **Residual sweep (F96 R1, per task):** same contract as `/sp:dev-verify` — after each task's
76
- verdict artifact is derived, run `residual-scan scan` + `fold` (under every `--fix` mode) before
77
- `spur task record`; on reaching `done` via `--next`, run `settle`. Script resolution:
75
+ - **Residual sweep (F96 R1, per task, post-record since 0987):** same contract as
76
+ `/sp:dev-verify` — after each task's verdict artifact is derived and `spur task record` has
77
+ flipped its verdict-proven boxes, run `residual-scan scan` + `fold` (under every `--fix` mode),
78
+ and re-record only on a downgrade; on reaching `done` via `--next`, run `settle`. Script resolution:
78
79
  `superskill script path sp residual-scan.mjs` (never `plugins/sp/scripts/` —
79
80
  script-contract-check rule 4).
@@ -17,7 +17,7 @@ Wraps the **wrapup-pipeline.yaml** workflow.
17
17
  | `--agent` `<inline\|auto\|name>` | Who runs the wrap's model-bearing steps. Wrap is workflow-backed (headless): `omit` and `--agent inline` resolve identically per task 0687 — tier substitution under objective trigger 3 (durable auditable run record required) with a warning naming the substituted executor; `auto` tier-resolves an executor; a name pins that executor into `vars.agent`. | inline |
18
18
  | `--auto` | Skip objective HITL gates. | off |
19
19
  | `--merge` | Merge the wrap branch. | off |
20
- | `--dry-run` | Render the wrap without writing. | off |
20
+ | `--dry-run` | Validate and walk transitions without executing actions. Conditional states therefore resolve to their declared failure edges (their guard files are never written), so a render ending in `failed` is **not** a run failure — it reports "this state's route cannot be decided without running it". Use a real run to preview a route. | off |
21
21
 
22
22
  For shared semantics, see the [flag glossary](../skills/spur-dev/references/flag-glossary.md).
23
23
 
@@ -34,7 +34,7 @@ export const REDACTION_CAP_BYTES = 4096;
34
34
  /** Max command/pattern summary length before ellipsis (chars). */
35
35
  export const SUMMARY_MAX_CHARS = 200;
36
36
 
37
- interface ToolPayload {
37
+ export interface ToolPayload {
38
38
  session_id?: string;
39
39
  tool_name?: string;
40
40
  tool_input?: {
@@ -115,7 +115,7 @@ export function redactText(text: string, capBytes: number = REDACTION_CAP_BYTES)
115
115
  if (bytes.length <= capBytes) return s;
116
116
  // Truncate on byte boundary then append marker
117
117
  let end = capBytes;
118
- while (end > 0 && (bytes[end] & 0xc0) === 0x80) end--;
118
+ while (end > 0 && ((bytes[end] ?? 0) & 0xc0) === 0x80) end--;
119
119
  return `${new TextDecoder().decode(bytes.subarray(0, end))}…[truncated]`;
120
120
  }
121
121
 
@@ -126,6 +126,7 @@ export function recordSessionStart(
126
126
  dir: string,
127
127
  env: NodeJS.ProcessEnv = getEnvVars(),
128
128
  now: () => Date = () => new Date(),
129
+ agentFallback?: string,
129
130
  ): string | null {
130
131
  try {
131
132
  mkdirSync(dir, { recursive: true });
@@ -140,7 +141,7 @@ export function recordSessionStart(
140
141
  const pad = (n: number) => String(n).padStart(2, '0');
141
142
  const sessionId = `session-${at.toISOString().slice(0, 10)}-${pad(at.getHours())}${pad(at.getMinutes())}`;
142
143
  const ts = at.toISOString();
143
- const agent = resolveAgentHint(env);
144
+ const agent = resolveAgentHint(env, agentFallback);
144
145
  const model = resolveModelHint(env);
145
146
 
146
147
  const sessionBody: Record<string, unknown> = {
@@ -49,30 +49,30 @@ function computeSessionTotals(
49
49
  return { reads, writes, tokens };
50
50
  }
51
51
 
52
- function exitOk(): never {
53
- process.exit(0);
54
- }
55
-
56
- async function main(): Promise<void> {
57
- const dir = join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context');
58
-
52
+ /**
53
+ * Finalize one session (task 0969 R3): read the `.session.json` pointer, compute the session's
54
+ * rollup totals, append the `session_end` row, remove the pointer (best-effort) and return the
55
+ * event. Shared by this hook's entrypoint and Pi's `session_shutdown` so both hosts roll up
56
+ * identically. Returns null on every fail-open path.
57
+ */
58
+ export function recordSessionEnd(dir: string, now: () => Date = () => new Date()): Record<string, unknown> | null {
59
59
  const sessionFile = join(dir, '.session.json');
60
- if (!existsSync(sessionFile)) exitOk();
60
+ if (!existsSync(sessionFile)) return null;
61
61
 
62
62
  let sessionId = '';
63
63
  try {
64
64
  const session = JSON.parse(readFileSync(sessionFile, 'utf-8')) as { session?: string };
65
65
  sessionId = session.session ?? '';
66
66
  } catch {
67
- exitOk();
67
+ return null;
68
68
  }
69
69
 
70
- if (!sessionId) exitOk();
70
+ if (!sessionId) return null;
71
71
 
72
72
  const totals = computeSessionTotals(join(dir, 'token-ledger.jsonl'), sessionId);
73
73
 
74
74
  const event = {
75
- ts: new Date().toISOString(),
75
+ ts: now().toISOString(),
76
76
  session: sessionId,
77
77
  type: 'session_end' as const,
78
78
  totals,
@@ -81,7 +81,7 @@ async function main(): Promise<void> {
81
81
  try {
82
82
  appendFileSync(join(dir, 'token-ledger.jsonl'), `${JSON.stringify(event)}\n`);
83
83
  } catch {
84
- exitOk();
84
+ return null;
85
85
  }
86
86
 
87
87
  try {
@@ -90,7 +90,15 @@ async function main(): Promise<void> {
90
90
  // cleanup is best-effort
91
91
  }
92
92
 
93
- exitOk();
93
+ return event;
94
94
  }
95
95
 
96
- void main().catch(exitOk);
96
+ // Entrypoint: run the shared core, then exit 0 (fail-open) as this hook always has.
97
+ if (import.meta.main) {
98
+ try {
99
+ recordSessionEnd(join(getEnvVar('CLAUDE_PROJECT_DIR') ?? process.cwd(), '.spur', 'context'));
100
+ } catch {
101
+ /* fail-open: every error path exits 0 */
102
+ }
103
+ process.exit(0);
104
+ }