@gobing-ai/spur 0.3.94 → 0.3.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +42 -0
  3. package/board/index.d.ts +38 -0
  4. package/config/plugin-scripts.json +3 -52
  5. package/config/rules/boundary/sp-script-placement.yaml +19 -0
  6. package/config/rules/strict/runtime-boundaries.yaml +5 -0
  7. package/config/rules/structure/test-location.yaml +2 -0
  8. package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
  9. package/config/rules/typescript/output-boundaries.yaml +15 -2
  10. package/config/script-placement-baseline.json +51 -0
  11. package/config/workflows/feature-verification.yaml +7 -4
  12. package/config/workflows/history-anatomy.yaml +23 -11
  13. package/config/workflows/idea-pipeline.yaml +87 -86
  14. package/config/workflows/pr-review.yaml +18 -9
  15. package/config/workflows/task-pipeline.yaml +84 -109
  16. package/config/workflows/wrapup-pipeline.yaml +173 -46
  17. package/package.json +15 -11
  18. package/plugins/sp/README.md +8 -10
  19. package/plugins/sp/agents/super-reviewer.md +43 -18
  20. package/plugins/sp/commands/dev-fixgha.md +83 -0
  21. package/plugins/sp/commands/dev-gitmsg.md +8 -6
  22. package/plugins/sp/commands/dev-gtd.md +2 -2
  23. package/plugins/sp/commands/dev-idea.md +22 -12
  24. package/plugins/sp/commands/dev-plan.md +8 -9
  25. package/plugins/sp/commands/dev-review.md +22 -13
  26. package/plugins/sp/commands/dev-verify.md +7 -4
  27. package/plugins/sp/commands/dev-verifyall.md +5 -4
  28. package/plugins/sp/commands/dev-wrap.md +1 -1
  29. package/plugins/sp/commands/spur-init.md +2 -2
  30. package/plugins/sp/hooks/context-post-tool.ts +2 -2
  31. package/plugins/sp/hooks/context-session-start.ts +2 -1
  32. package/plugins/sp/hooks/context-session-stop.ts +22 -14
  33. package/plugins/sp/hooks/pi/guard-extension.ts +109 -150
  34. package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
  35. package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
  36. package/plugins/sp/lib/idea-handoff.generated.mjs +6 -5
  37. package/plugins/sp/lib/inline-run.generated.d.mts +13 -2
  38. package/plugins/sp/lib/inline-run.generated.mjs +33 -16
  39. package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
  40. package/plugins/sp/lib/quality-gate.generated.mjs +438 -0
  41. package/plugins/sp/lib/residual-scan.generated.d.mts +62 -0
  42. package/plugins/sp/lib/residual-scan.generated.mjs +210 -0
  43. package/plugins/sp/lib/spur-bin.ts +36 -0
  44. package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
  45. package/plugins/sp/lib/step-profile.generated.mjs +174 -0
  46. package/plugins/sp/plugin.json +1 -1
  47. package/plugins/sp/references/roles.md +1 -1
  48. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
  49. package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
  50. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
  51. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
  52. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
  53. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
  54. package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
  55. package/plugins/sp/scripts/history-anatomy-cache.mjs +23 -22
  56. package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
  57. package/plugins/sp/scripts/inline-run-setup.mjs +90 -315
  58. package/plugins/sp/scripts/inline-run-setup.ts +109 -639
  59. package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
  60. package/plugins/sp/scripts/pr-reviewing.ts +1 -0
  61. package/plugins/sp/scripts/quality-gate.mjs +38 -21
  62. package/plugins/sp/scripts/quality-gate.ts +20 -660
  63. package/plugins/sp/scripts/residual-scan.mjs +138 -159
  64. package/plugins/sp/scripts/residual-scan.ts +102 -499
  65. package/plugins/sp/scripts/script-root.mjs +129 -0
  66. package/plugins/sp/scripts/script-root.ts +199 -0
  67. package/plugins/sp/scripts/workflow-step-profile.mjs +27 -16
  68. package/plugins/sp/scripts/workflow-step-profile.ts +22 -314
  69. package/plugins/sp/scripts/wrapup-drift-probe.mjs +12 -3
  70. package/plugins/sp/scripts/wrapup-drift-probe.ts +6 -3
  71. package/plugins/sp/scripts/wrapup-steps.mjs +42 -40
  72. package/plugins/sp/scripts/wrapup-steps.ts +64 -46
  73. package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
  74. package/plugins/sp/skills/code-verification/SKILL.md +46 -16
  75. package/plugins/sp/skills/code-verification/references/verdict-schema.md +13 -2
  76. package/plugins/sp/skills/functional-review/SKILL.md +8 -5
  77. package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
  78. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
  79. package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
  80. package/plugins/sp/skills/next-router/references/routing-table.md +2 -2
  81. package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
  82. package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
  83. package/plugins/sp/skills/spur-cli/references/features.md +17 -6
  84. package/plugins/sp/skills/spur-cli/references/init.md +17 -16
  85. package/plugins/sp/skills/spur-cli/references/self.md +3 -2
  86. package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
  87. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +28 -5
  88. package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
  89. package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
  90. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +11 -9
  91. package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
  92. package/plugins/sp/skills/spur-dev/references/dev-operations.md +86 -70
  93. package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +3 -3
  94. package/plugins/sp/skills/spur-dev/references/execution-batch.md +107 -44
  95. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -9
  96. package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
  97. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +31 -21
  98. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
  99. package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
  100. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +35 -6
  101. package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
  102. package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
  103. package/plugins/sp/tsconfig.json +8 -0
  104. package/spur.js +6638 -4775
  105. package/web/_astro/BoardApp.BIjMatT1.js +1 -0
  106. package/web/_astro/{BoardApp.FTEs3-N8.js → BoardApp.GvjIe9Z6.js} +150 -153
  107. package/web/_astro/TaskDetail.BZM3EAFx.js +1 -0
  108. package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
  109. package/web/_astro/{arc.uuAf51IT.js → arc.HkRiZnoI.js} +1 -1
  110. package/web/_astro/architectureDiagram-3BPJPVTR.BDdK-tgZ.js +36 -0
  111. package/web/_astro/{blockDiagram-GPEHLZMM.D1mGCq3p.js → blockDiagram-GPEHLZMM.CZ9kOBxx.js} +1 -1
  112. package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
  113. package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
  114. package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
  115. package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
  116. package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
  117. package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
  118. package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
  119. package/web/_astro/{c4Diagram-AAUBKEIU.CMsolcde.js → c4Diagram-AAUBKEIU.DXK9qeRD.js} +1 -1
  120. package/web/_astro/channel.DlhX2MEt.js +1 -0
  121. package/web/_astro/{chunk-2J33WTMH.CrGA3fik.js → chunk-2J33WTMH.BTD2WeX5.js} +1 -1
  122. package/web/_astro/{chunk-4BX2VUAB.DsLVVla0.js → chunk-4BX2VUAB.Ci6Qbcvk.js} +1 -1
  123. package/web/_astro/{chunk-55IACEB6.Dxc59Tfi.js → chunk-55IACEB6.yl3zsj7p.js} +1 -1
  124. package/web/_astro/{chunk-727SXJPM.CaEVE2Wy.js → chunk-727SXJPM.ClXZsfyR.js} +1 -1
  125. package/web/_astro/{chunk-AQP2D5EJ.BiJ4HXeI.js → chunk-AQP2D5EJ.BmKWZBcP.js} +1 -1
  126. package/web/_astro/{chunk-FMBD7UC4.Mxf1fru5.js → chunk-FMBD7UC4.By30fcb7.js} +1 -1
  127. package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
  128. package/web/_astro/{chunk-ND2GUHAM.pyOWQixH.js → chunk-ND2GUHAM.24NmrD-k.js} +1 -1
  129. package/web/_astro/{chunk-QZHKN3VN.BmEsg4vr.js → chunk-QZHKN3VN.BN4sKdcS.js} +1 -1
  130. package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
  131. package/web/_astro/{classDiagram-4FO5ZUOK.BHhhMFTO.js → classDiagram-4FO5ZUOK.CJpoMPb5.js} +1 -1
  132. package/web/_astro/{classDiagram-v2-Q7XG4LA2.BHhhMFTO.js → classDiagram-v2-Q7XG4LA2.CJpoMPb5.js} +1 -1
  133. package/web/_astro/client.BhluLQqe.js +1 -0
  134. package/web/_astro/client.DBpSRLnx.js +9 -0
  135. package/web/_astro/cose-bilkent-S5V4N54A.BraxQ2Nt.js +1 -0
  136. package/web/_astro/{cynefin-OW5HDTMX.C2j1_lKL.js → cynefin-OW5HDTMX.gzoU73oL.js} +1 -1
  137. package/web/_astro/{dagre-BM42HDAG.hZ2NCTdT.js → dagre-BM42HDAG.wyDfWySU.js} +1 -1
  138. package/web/_astro/{diagram-2AECGRRQ.Bfw5_EzK.js → diagram-2AECGRRQ.h2_EbMxV.js} +1 -1
  139. package/web/_astro/{diagram-5GNKFQAL.Bt1V_Tmk.js → diagram-5GNKFQAL.Db6dcs89.js} +1 -1
  140. package/web/_astro/{diagram-KO2AKTUF.DEk-YFwp.js → diagram-KO2AKTUF.NVwzxu_U.js} +1 -1
  141. package/web/_astro/{diagram-LMA3HP47.CDjndtQm.js → diagram-LMA3HP47.B-eNgDQk.js} +1 -1
  142. package/web/_astro/{diagram-OG6HWLK6.cFnUHScG.js → diagram-OG6HWLK6.Bsa8ZRbJ.js} +1 -1
  143. package/web/_astro/{erDiagram-TEJ5UH35.DlhYp7NV.js → erDiagram-TEJ5UH35.BospRE5Q.js} +1 -1
  144. package/web/_astro/{flowDiagram-I6XJVG4X.DNTpsxfx.js → flowDiagram-I6XJVG4X.DMzvHwAA.js} +1 -1
  145. package/web/_astro/ganttDiagram-6RSMTGT7.Dmdxgd-M.js +292 -0
  146. package/web/_astro/{gitGraphDiagram-PVQCEYII.DkEqNI0P.js → gitGraphDiagram-PVQCEYII.CaqdO0rL.js} +1 -1
  147. package/web/_astro/index.5dn-Zm-2.js +1 -0
  148. package/web/_astro/index.C-IqPpW2.js +1 -0
  149. package/web/_astro/index.Cyc_GA4f.js +1 -0
  150. package/web/_astro/index.EoZlzLK-.css +1 -0
  151. package/web/_astro/index.tZfqwSut.js +1 -0
  152. package/web/_astro/{infoDiagram-5YYISTIA.CrjioCTG.js → infoDiagram-5YYISTIA.DDVD9Wn7.js} +1 -1
  153. package/web/_astro/{ishikawaDiagram-YF4QCWOH.BcK0CN8n.js → ishikawaDiagram-YF4QCWOH.EKyCUFsL.js} +1 -1
  154. package/web/_astro/{journeyDiagram-JHISSGLW.p0CbBDX1.js → journeyDiagram-JHISSGLW.By2-RkYB.js} +1 -1
  155. package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
  156. package/web/_astro/{kanban-definition-UN3LZRKU.puHIFt6J.js → kanban-definition-UN3LZRKU.ar-WmlyR.js} +1 -1
  157. package/web/_astro/{linear.Bb-3a1d7.js → linear.BnzMBgo_.js} +1 -1
  158. package/web/_astro/{mermaid.core.CJDgXOJs.js → mermaid.core.DSqeFu2Y.js} +4 -4
  159. package/web/_astro/{mindmap-definition-RKZ34NQL.BoAZEI9t.js → mindmap-definition-RKZ34NQL.1XcF8Wh2.js} +1 -1
  160. package/web/_astro/{pieDiagram-4H26LBE5.CJNSdqY7.js → pieDiagram-4H26LBE5.DnItHOdK.js} +1 -1
  161. package/web/_astro/{quadrantDiagram-W4KKPZXB.DtTy7_0Z.js → quadrantDiagram-W4KKPZXB.BQQyrnBt.js} +1 -1
  162. package/web/_astro/{requirementDiagram-4Y6WPE33.BNYV98Xg.js → requirementDiagram-4Y6WPE33.Dmzxun82.js} +1 -1
  163. package/web/_astro/{sankeyDiagram-5OEKKPKP.DRq9DGHp.js → sankeyDiagram-5OEKKPKP.BBIeRKYB.js} +1 -1
  164. package/web/_astro/{sequenceDiagram-3UESZ5HK.B7KdBFCm.js → sequenceDiagram-3UESZ5HK.CUNVdBBf.js} +1 -1
  165. package/web/_astro/{stateDiagram-AJRCARHV.CD62ZJ2H.js → stateDiagram-AJRCARHV.DlFf2sJm.js} +1 -1
  166. package/web/_astro/{stateDiagram-v2-BHNVJYJU.CeVk1TAj.js → stateDiagram-v2-BHNVJYJU.DXR7I1H5.js} +1 -1
  167. package/web/_astro/{timeline-definition-PNZ67QCA.C_ZPA5tT.js → timeline-definition-PNZ67QCA.BcO-GyX2.js} +1 -1
  168. package/web/_astro/{vennDiagram-CIIHVFJN.CrThO_FQ.js → vennDiagram-CIIHVFJN.ChCECmmW.js} +1 -1
  169. package/web/_astro/{wardleyDiagram-YWT4CUSO.DSZCA5nl.js → wardleyDiagram-YWT4CUSO.CRaT12mD.js} +1 -1
  170. package/web/_astro/{xychartDiagram-2RQKCTM6.KI7baTuk.js → xychartDiagram-2RQKCTM6.B3lsHVpj.js} +1 -1
  171. package/web/board-runtime.json +69 -0
  172. package/web/index.html +2 -2
  173. package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
  174. package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
  175. package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -298
  176. package/plugins/sp/scripts/feature-sync-bounded.ts +0 -479
  177. package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
  178. package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
  179. package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
  180. package/plugins/sp/scripts/record-feature-sync.ts +0 -84
  181. package/plugins/sp/scripts/script-contract-check.ts +0 -377
  182. package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
  183. package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
  184. package/plugins/sp/scripts/task-evidence-precheck.ts +0 -187
  185. package/plugins/sp/scripts/task-size-precheck.ts +0 -210
  186. package/plugins/sp/scripts/transition-shim-check.ts +0 -238
  187. package/plugins/sp/scripts/validate-commands.ts +0 -689
  188. package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
  189. package/plugins/sp/scripts/verify-answer-lint.ts +0 -547
  190. package/web/_astro/BoardApp.C02hAHPO.js +0 -1
  191. package/web/_astro/TaskDetail.C-GdsS-t.js +0 -1
  192. package/web/_astro/architectureDiagram-3BPJPVTR.CGe629A1.js +0 -36
  193. package/web/_astro/channel.fsgl7o5j.js +0 -1
  194. package/web/_astro/client.yhYJvxCU.js +0 -9
  195. package/web/_astro/cose-bilkent-S5V4N54A.2fH4YOlp.js +0 -1
  196. package/web/_astro/ganttDiagram-6RSMTGT7.DC_p36PI.js +0 -292
  197. package/web/_astro/index.De90oHcH.js +0 -1
  198. package/web/_astro/index.Hjbr15fG.css +0 -1
@@ -30,9 +30,10 @@
30
30
  */
31
31
 
32
32
  import { spawnSync } from 'node:child_process';
33
- import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
33
+ import { appendFileSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
34
34
  import { join } from 'node:path';
35
35
  import { getEnvVars } from '../lib/env';
36
+ import { spurCommand } from '../lib/spur-bin';
36
37
 
37
38
  export interface WrapupStepsEnv {
38
39
  __runId?: string;
@@ -64,15 +65,6 @@ function jqText(value: unknown): string {
64
65
  /** Canonical four-digit WBS string: whitespace is rejected, not trimmed (0783 R1). */
65
66
  export const WBS_PATTERN = /^[0-9]{4}$/;
66
67
 
67
- /** `spurBin` splits on whitespace into a command plus prefix args (so `bun x.ts` works). */
68
- export function spurCommand(spurBin: string | undefined): { cmd: string; prefix: string[] } {
69
- const parts = (spurBin ?? 'spur')
70
- .trim()
71
- .split(/\s+/)
72
- .filter((p) => p.length > 0);
73
- return { cmd: parts[0] ?? 'spur', prefix: parts.slice(1) };
74
- }
75
-
76
68
  function spur(
77
69
  env: WrapupStepsEnv,
78
70
  args: string[],
@@ -269,6 +261,49 @@ function readFileSyncSafe(path: string): string | null {
269
261
  }
270
262
  }
271
263
 
264
+ /**
265
+ * Verdict from a task record's tracked `Testing` section — the durable copy that outlives a
266
+ * worktree teardown or clone (F93). Local copy of `parseVerdictLine` and of the `Testing` slice
267
+ * in `extractTestingSection` (`packages/app/src/services/task-record.ts`) because ADR-065 keeps
268
+ * plugin scripts on builtin and relative imports only; keep the three literals in step by hand
269
+ * (the parity guard in `plugins/sp/tests/wrapup-steps.test.ts` reads the canonical literals).
270
+ *
271
+ * The slice rule is the canonical one verbatim — `#{1,6}` heading, end at the next
272
+ * same-or-higher heading — so an `# Testing` or `#### Testing` heading, and an h4 subheading
273
+ * inside a `## Testing` section, read the same here and in the app. One deliberate difference:
274
+ * with no `Testing` heading at all this returns null instead of falling back to the whole
275
+ * document, so a `Verdict:` token in another section cannot be misread as this task's verdict.
276
+ */
277
+ export function verdictFromTestingSection(content: string): string | null {
278
+ const heading = /^#{1,6}\s+Testing\s*$/m.exec(content);
279
+ if (!heading) return null;
280
+ const level = heading[0].match(/^#+/)?.[0]?.length ?? 2;
281
+ const rest = content.slice(heading.index + heading[0].length);
282
+ const next = new RegExp(`^#{1,${level}}\\s+\\S`, 'm').exec(rest);
283
+ const section = next ? rest.slice(0, next.index) : rest;
284
+ for (const line of section.split('\n')) {
285
+ // Line-anchored (optionally after `- ` bullet or `**` bold) so evidence text
286
+ // containing a mid-line "Verdict:" token cannot be misread as the section verdict.
287
+ const m = /^(?:-\s*|\*\*)?Verdict:\s*(PASS|PARTIAL|FAIL|UNKNOWN)\b/i.exec(line.trim());
288
+ if (m?.[1] !== undefined) return m[1].toUpperCase();
289
+ }
290
+ return null;
291
+ }
292
+
293
+ /**
294
+ * `verdict` field of a verdict artifact; null when the file is absent, unreadable, malformed or
295
+ * carries no usable verdict (jq `//` semantics: null, undefined and false all count as missing).
296
+ */
297
+ function verdictOfArtifact(verdictPath: string): string | null {
298
+ try {
299
+ const raw = jqPick((JSON.parse(readFileSync(verdictPath, 'utf8')) as { verdict?: unknown }).verdict, '');
300
+ const text = jqText(raw);
301
+ return text.length > 0 ? text : null;
302
+ } catch {
303
+ return null;
304
+ }
305
+ }
306
+
272
307
  export interface MetricsResult {
273
308
  status: 'PASS' | 'FAIL';
274
309
  statusFile: string;
@@ -323,17 +358,20 @@ export function runMetrics(env: WrapupStepsEnv, options: WrapupStepsOptions = {}
323
358
  const featureId = String(jqPick(frontmatter.feature_id, parsed.feature_id, ''));
324
359
  const status = String(jqPick(frontmatter.status, parsed.status, 'unknown'));
325
360
 
326
- // jq `//` semantics: null and false count as missing; an empty string result stays UNKNOWN.
327
- let verdict = 'UNKNOWN';
361
+ // The verdict artifact stays the first source (F93 R2); when it is gone — a worktree run
362
+ // fast-forwards and removes the tree, taking the gitignored artifact with it — the tracked
363
+ // `Testing` line `task record` already wrote into the task file is the durable copy. Honest
364
+ // UNKNOWN is the last resort, and it is telemetry, never proof of completion.
328
365
  const verdictPath = join('.spur', 'run', `${wbs}-verdict.json`);
329
- if (existsSync(abs(verdictPath))) {
330
- try {
331
- const raw = jqPick(JSON.parse(readFileSync(abs(verdictPath), 'utf8')).verdict, 'UNKNOWN');
332
- const text = raw === 'UNKNOWN' ? 'UNKNOWN' : jqText(raw);
333
- if (text.length > 0) verdict = text;
334
- } catch {
335
- // unreadable verdict file keeps UNKNOWN telemetry
336
- }
366
+ const artifactVerdict = verdictOfArtifact(abs(verdictPath));
367
+ const trackedVerdict = verdictFromTestingSection(typeof parsed.content === 'string' ? parsed.content : '');
368
+ const verdict = artifactVerdict ?? trackedVerdict ?? 'UNKNOWN';
369
+ if (verdict === 'UNKNOWN') {
370
+ // R3: name the task, the artifact path and the tracked state. An explicit UNKNOWN on
371
+ // either source is still an uncertified row, so it reports the same way as a miss.
372
+ process.stderr.write(
373
+ `metrics-record: task ${wbs} has no certifying verdict — ${verdictPath}: ${artifactVerdict ?? 'missing or carries none'}, tracked Testing: ${trackedVerdict ?? 'no Verdict: line'} — recording UNKNOWN telemetry\n`,
374
+ );
337
375
  }
338
376
 
339
377
  const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
@@ -438,34 +476,14 @@ export function runFeatureTransition(env: WrapupStepsEnv, options: WrapupStepsOp
438
476
  return { status: 'FAIL', statusFile: relStatusFile, exitCode: 1 };
439
477
  }
440
478
 
441
- // Three branches, first that exists relative to the cwd; current arguments including --spur-bin.
479
+ // 1004 R5: one direct service sync (stderr streams through, stdout is the JSON payload).
480
+ // Repeated-BLOCKED suppression (0411) lives in the feature sync service itself (1004 R3),
481
+ // so the bounded-wrapper resolution branches are gone.
442
482
  let syncOutput = '';
443
483
  let syncRc = 1;
444
- const boundedTs = join('plugins', 'sp', 'scripts', 'feature-sync-bounded.ts');
445
- const boundedArgs = [feature, '--spur-bin', env.spurBin ?? 'spur', '--json'];
446
- if (existsSync(cwd ? join(cwd, boundedTs) : boundedTs)) {
447
- const result = spawnSync('bun', [boundedTs, ...boundedArgs], { cwd, encoding: 'utf8' });
448
- syncOutput = result.stdout ?? '';
449
- syncRc = result.status ?? 1;
450
- if (result.stderr !== null && result.stderr.length > 0) process.stderr.write(result.stderr);
451
- } else {
452
- const probe = spawnSync('superskill', ['script', 'path', 'sp', 'feature-sync-bounded.mjs'], {
453
- cwd,
454
- encoding: 'utf8',
455
- });
456
- const twin = probe.status === 0 ? (probe.stdout ?? '').trim() : '';
457
- if (twin.length > 0 && existsSync(twin)) {
458
- const result = spawnSync('node', [twin, ...boundedArgs], { cwd, encoding: 'utf8' });
459
- syncOutput = result.stdout ?? '';
460
- syncRc = result.status ?? 1;
461
- if (result.stderr !== null && result.stderr.length > 0) process.stderr.write(result.stderr);
462
- } else {
463
- // Last branch: stderr streams through (visible), stdout is the JSON payload.
464
- const result = spur(env, ['feature', 'sync', feature, '--json'], { cwd, stderr: 'inherit' });
465
- syncOutput = result.stdout;
466
- syncRc = result.status;
467
- }
468
- }
484
+ const sync = spur(env, ['feature', 'sync', feature, '--json'], { cwd, stderr: 'inherit' });
485
+ syncOutput = sync.stdout;
486
+ syncRc = sync.status;
469
487
  process.stdout.write(`${syncOutput}\n`);
470
488
 
471
489
  const shown = spur(env, ['feature', 'show', feature, '--json'], { cwd });
@@ -105,9 +105,9 @@ spur task show <wbs> --json # for a pipeline run
105
105
  # scope = 'src/api/' | 'packages/domain/' | 'plugins/sp/'
106
106
  ```
107
107
 
108
- For a pipeline run, derive the diff scope the same way `sp:code-verification` Step 3 does (the
109
- task's last commit → changed `*.ts/*.tsx/*.js/*.jsx`). For standalone, the `path` argument is the
110
- scope.
108
+ For a pipeline run, derive the diff scope the same way `sp:code-verification` Step 3 does (the SSOT
109
+ recipe: tagged-commit task diff — defined there, not restated here). For standalone, the `path`
110
+ argument is the scope.
111
111
 
112
112
  ### Step 2 — Explore (read the map)
113
113
 
@@ -188,7 +188,8 @@ When invoked as the `--focus architecture` dimension of `/sp:dev-review`:
188
188
  - `blocker`/`major` candidates block the `approve(HITL)` gate alongside any SECUA blockers from
189
189
  `sp:code-verification`.
190
190
  - The candidate list is returned as a **review fragment**; the review coordinator
191
- (`sp:super-reviewer` under `/sp:dev-review`) merges it into the combined `## Review` section —
191
+ (`sp:super-reviewer` — the invoking session under `/sp:dev-review`) merges it into the combined
192
+ `## Review` section —
192
193
  it is never written by `record`, which backfills `## Review` only when the section is bare
193
194
  (fallback-only, F92 0593 R1).
194
195
  - This skill does **not** write to the task file directly — the coordinator (or the operator) does.
@@ -36,7 +36,7 @@ It backs two commands:
36
36
  | Command | Mode | Input | Output |
37
37
  |---------|------|-------|--------|
38
38
  | `/sp:dev-verify <wbs>` | **verify** | a task WBS | `.spur/run/<wbs>-verdict.json`; `record` transcribes `## Testing` |
39
- | `/sp:dev-review <wbs>` | **review** (coordinator) | a task WBS (diff scope) | merged three-dimensional findings → `## Review` |
39
+ | `/sp:dev-review --tasks <wbs>` | **review** (coordinator) | a task WBS (diff scope) | merged three-dimensional findings → `## Review` |
40
40
 
41
41
  The verify mode is the **completion gate's evidence source**: it emits a machine verdict the
42
42
  `task-pipeline.yaml` workflow reads before allowing `record → done`. A `PASS` clears the gate; a
@@ -96,16 +96,39 @@ no-op there; `--force` matters for re-auditing completed tasks.)
96
96
 
97
97
  ### Step 3 — Establish the change scope
98
98
 
99
- Determine which files the task changed (the evidence surface):
99
+ Determine which files the task changed. This step is the SSOT scope recipe:
100
+ `sp:functional-review` Step 3, `sp:code-improvement` Step 1, and `sp:super-reviewer` "Establish
101
+ scope first" defer here and must not restate it.
102
+
103
+ WBS mode — the union of files changed by the task's implementation commits, identified by the
104
+ `(<wbs>)` subject tag, excluding the task file itself:
100
105
 
101
106
  ```bash
102
- TASK_FILE=$(spur task show <wbs> --json | jq -r .filePath)
103
- COMMIT=$(git log -1 --format=%H -- "$TASK_FILE")
104
- git diff --name-only "${COMMIT}~1"..HEAD -- '*.ts' '*.tsx' '*.js' '*.jsx'
105
- # Fallback when the task file is uncommitted: use the working-tree diff.
107
+ ROOT=$(git rev-parse --show-toplevel)
108
+ TASK_FILE=$(cd "$ROOT" && spur task show <wbs> --json | jq -r .filePath)
109
+ COMMITS=$(git log --format='%H %s' | grep -F "(<wbs>)" | cut -d' ' -f1)
110
+ for c in $COMMITS; do git diff-tree --no-commit-id --name-only -r "$c"; done \
111
+ | sort -u | grep -vxF "${TASK_FILE#$ROOT/}"
112
+ # Empty scope → working-tree diff.
106
113
  git status --porcelain
107
114
  ```
108
115
 
116
+ - No file-extension filter — `.md`/`.yaml` are first-class harness surfaces. Generated/lock files
117
+ (`*.generated.mjs`, `bun.lock`) may be excluded by name, stated in the Scope line.
118
+ - Empty scope (no tagged commit, or tagged commits touch only the task file) degrades visibly:
119
+ state `working tree` and the reason in the Scope line.
120
+
121
+ ### Step 3p — Path scope (review mode only)
122
+
123
+ When the review target is a path, not a WBS, Step 3 does not apply — derive the scope from the
124
+ path: the tracked files under it, with the file count reported in the Scope line.
125
+
126
+ ```bash
127
+ git ls-files -- <path>
128
+ ```
129
+
130
+ Large scopes are fanned out by the review coordinator, not here.
131
+
109
132
  ### Step 4 — Requirements traceability gate (Phase 8)
110
133
 
111
134
  For each `R{n}` in `## Requirements`, find implementation evidence in the changed files / tests and
@@ -259,17 +282,22 @@ the deterministic Testing writer `spur task record` (section authorship never ha
259
282
 
260
283
  ```bash
261
284
  # write .spur/run/<wbs>-verdict.json (shape in references/verdict-schema.md), then:
262
- # F96 residual sweep (observe-only): scan + fold BEFORE record, under every --fix mode.
285
+ spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json # renders ## Testing, flips proven boxes
286
+ # F96 residual sweep (observe-only): scan + fold AFTER record, under every --fix mode (0987).
263
287
  RESIDUAL=$(superskill script path sp residual-scan.mjs)
264
288
  node "$RESIDUAL" scan <wbs>
265
289
  node "$RESIDUAL" fold <wbs>
266
- spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json # renders ## Testing
290
+ # Downgrade only: re-record so Testing carries the final (folded) verdict.
291
+ [ "$(jq -r .verdict .spur/run/<wbs>-verdict.json)" = PASS ] \
292
+ || spur task record <wbs> --verdict-file .spur/run/<wbs>-verdict.json
267
293
  ```
268
294
 
269
- > **Residual fold (F96).** `scan` reads `.spur/run/` artifacts and writes `residuals.json` +
270
- > `residual-report.md`; `fold` rewrites the **just-written** verdict artifact's `residual-sweep`
271
- > check in place (PASS → PARTIAL when a blocking residual exists), so `record` transcribes the
272
- > downgraded verdict — a manual verify cannot certify what the pipeline would reject. Resolve the
295
+ > **Residual fold (F96).** `scan` reads `.spur/run/` artifacts and the post-record task file and
296
+ > writes `residuals.json` + `residual-report.md`; `fold` rewrites the verdict artifact's
297
+ > `residual-sweep` check in place (PASS → PARTIAL when a blocking residual exists), and the
298
+ > downgrade-only re-record transcribes it — a manual verify cannot certify what the pipeline would
299
+ > reject. The sweep runs after `record` (same order as the pipeline, 0983/0987): before it, a
300
+ > verdict-proven box is still unticked and a clean PASS would fold to PARTIAL. Resolve the
273
301
  > script via `superskill script path sp residual-scan.mjs`; shipped surfaces never reference
274
302
  > `plugins/sp/scripts/` directly (script-contract-check rule 4). When the task reaches `done`
275
303
  > through `--next`, run `residual-scan settle` (links follow-up tasks; best-effort).
@@ -322,7 +350,7 @@ Verdict: PASS
322
350
  ### SECUA Review
323
351
  | Priority | Dimension | Location | Finding |
324
352
  | --- | --- | --- | --- |
325
- | P4 | — | — | No P1–P3 findings; verify verdict PASS |
353
+ | P4 | — | — | No findings (verify verdict PASS) |
326
354
  ```
327
355
 
328
356
  The per-requirement traceability table MUST use `| Req | Status | Evidence |` (exactly this header, no `R#`/`R`/`Requirement` variant — `Status` in column 2 — and no extra columns between Req and Status). The Acceptance Criteria table MUST use `| AC | Status | Evidence Type | Evidence |`.
@@ -333,7 +361,8 @@ canonical.
333
361
  Under the pipeline, the verifier owns the answer file `.spur/run/<wbs>-verify-answer.txt` (0726
334
362
  R3): `Verdict: PARTIAL` first, append one row at a time, replace the verdict line only when all
335
363
  rows are certified — interruptions leave lintable partial rows; retries fill only missing IDs.
336
- Host: `expectFile` → `verify-answer-lint.ts` → verdict + `spur task check` (R9). Vocabularies,
364
+ Host: `spur task verdict --from-answer` lints the answer file, then derives the verdict +
365
+ `spur task check` (R9). Vocabularies,
337
366
  rejection classes, and the AC identity rule: `references/verdict-schema.md`. Sections follow the
338
367
  Step 10 contract.
339
368
 
@@ -476,10 +505,11 @@ re-audit is never misread as a successful `testing -> done` (dev-verify.md `--ne
476
505
  ## Mode: review (`/sp:dev-review`)
477
506
 
478
507
  The source-oriented path: SECUA review of a task's diff without the full traceability verdict. Runs
479
- Steps 3 + 7 and returns a **review fragment** — no verdict artifact, no section write, no `done`
508
+ Step 3 (WBS target) or Step 3p (path target — a path target never derives scope from Step 3) plus
509
+ Step 7, and returns a **review fragment** — no verdict artifact, no section write, no `done`
480
510
  gate (F92 0593 R1); the coordinator (`sp:super-reviewer`) merges fragments into `## Review`.
481
511
 
482
- Flags: `--agent <inline|auto|name>` (execution surface — inline default, with named escalation triggers taking precedence), `--auto` (no confirmations), `--fix <none|blockers-first|all>` (post-review repair), and `--focus <all|security|efficiency|correctness|usability|architecture>` (SECUA dimensions). Apply the [central contract](../spur-dev/references/cross-cutting.md#inline-default-execution-surface) before starting the review.
512
+ Flags: `--agent <inline|auto|name>` (execution surface — inline default, with named escalation triggers taking precedence), `--auto` (no confirmations), and `--focus <all|functional|security|efficiency|correctness|usability|architecture>` (review dimensions — **the single review `--focus` vocabulary is declared here (SSOT)**: `functional` routes to `sp:functional-review`, `architecture` to `sp:code-improvement` + SECUA-A, the SECUA lenses to Step 7; `dev-review.md`, `dev-operations.md` §2 and `flag-glossary.md#flag-focus` link here instead of restating a second list). Apply the [central contract](../spur-dev/references/cross-cutting.md#inline-default-execution-surface) before starting the review.
483
513
 
484
514
  ---
485
515
 
@@ -108,8 +108,8 @@ For answer files, emit a matching parseable table:
108
108
 
109
109
  **Authoring contract under the pipeline (0726 R3).** The verifier owns the answer file: write
110
110
  `Verdict: PARTIAL` first, append one complete row at a time, and replace the first verdict line only
111
- after every row is certified. `verify-answer-lint.ts` gates the file before `spur task verdict
112
- --from-answer` and rejects, with row-level diagnostics: missing/duplicate/unknown requirement IDs,
111
+ after every row is certified. `spur task verdict --from-answer` lints the file before deriving the
112
+ verdict and rejects, with row-level diagnostics: missing/duplicate/unknown requirement IDs,
113
113
  AC ids that do not resolve to one accepted identity — a task AC checklist label or its declared
114
114
  `AC-N`/checklist-token alias, or a linked feature scenario title, in the ac-style-guide forms
115
115
  (exact/bare title, `Scenario:` prefix, bracket tags, `AC-N`); paraphrases and ambiguous aliases
@@ -118,6 +118,17 @@ fail — invalid status (`MET | PARTIAL | UNMET` for requirements;
118
118
  manual-review | llm-judge | n/a`, or a `+` compound), and empty evidence. Interrupted runs keep the
119
119
  rows that pass the lint and complete only the missing IDs on retry.
120
120
 
121
+ **Feature-credited AC ids are narrower than the lint's accepted set (task 0966 finding).** A row keyed
122
+ by a bare checklist ordinal (`AC1`) passes the `spur task verdict` answer lint — it resolves to the task's AC
123
+ checklist label — but `feature-check.rowMatchesScenario` credits a linked feature scenario only by the
124
+ scenario's normalized title or its `AC-N` ordinal alias. Such a row therefore carries a PASS verdict
125
+ while crediting no scenario, and the feature's `verifying → done` gate denies with
126
+ `L4.scenario-unverified` / `L4.verdict-rows-match-no-scenario`. When the task has a linked feature,
127
+ key each AC row `AC-N` (1-based scenario ordinal, hyphenated) or by the exact scenario title.
128
+ A scenario-keyed row (`R3 — <title>`) ticks the task AC box whose line aliases that scenario
129
+ (`- [ ] AC1 — R3 — <title>`) during `spur task record` (0996); a scenario key no AC line aliases
130
+ ticks nothing, so alias every graduated scenario on its AC line.
131
+
121
132
  **Concrete anchors only (task 0804 R9).** An evidence anchor must be a concrete existing `file:line`
122
133
  (or `file:start-end`) path. A glob or directory summary (`src/services/*.ts`, `the retry
123
134
  classifiers in task-pipeline.yaml`) is not an anchor: expand it into the specific cited files/ranges
@@ -11,6 +11,7 @@ metadata:
11
11
  - pipeline
12
12
  modes:
13
13
  - verify
14
+ - review
14
15
  verdicts:
15
16
  - PASS
16
17
  - PARTIAL
@@ -214,8 +215,8 @@ return `verdict = PASS` with "No requirements to verify" (zero-requirements case
214
215
  ### Step 3 — Establish the change scope
215
216
 
216
217
  If `--source-paths` is given, use it. Otherwise derive the diff scope exactly as
217
- `sp:code-verification` Step 3 does (the task file's last commit → `git diff --name-only` for
218
- `*.ts`/`*.tsx`/`*.js`/`*.jsx`, fallback to working-tree diff).
218
+ `sp:code-verification` Step 3 does (the SSOT recipe: the `(<wbs>)`-tagged implementation commits'
219
+ changed files, fallback to the working-tree diff — defined there, not restated here).
219
220
 
220
221
  ### Step 4 — Track A: BDD mapping (if `--bdd-report`)
221
222
 
@@ -253,7 +254,7 @@ shape, e.g. `| Req | Status | Evidence |` alone, is structurally rejected and de
253
254
  ```markdown
254
255
  | Priority | Dimension | Location | Finding |
255
256
  | --- | --- | --- | --- |
256
- | P4 | — | — | No P1–P3 findings; functional verdict PASS |
257
+ | P4 | — | — | No findings (functional verdict PASS) |
257
258
 
258
259
  | Req | Status | Evidence |
259
260
  | --- | --- | --- |
@@ -268,7 +269,8 @@ The priority table leads; the traceability table follows for per-requirement det
268
269
  **Fragment-only discipline (F92 0593 R1).** In coordinated mode (dispatched by `/sp:dev-review` /
269
270
  `sp:super-reviewer`), do **not** write `## Review` — return the fragment to the coordinator, which
270
271
  merges the functional + SECUA + architecture fragments into the combined `## Review` section. Only
271
- `sp:super-reviewer` (the review coordinator) writes `## Review`; direct component-skill use is
272
+ `sp:super-reviewer` (the invoking session under `/sp:dev-review` acting as review coordinator)
273
+ writes `## Review`; direct component-skill use is
272
274
  advisory output. `spur task record` backfills a **bare** `## Review` from the verdict artifact as a
273
275
  standalone compatibility fallback only and never overwrites authored Review (F92 0593 R1).
274
276
 
@@ -291,7 +293,8 @@ Include the per-requirement traceability table in the report:
291
293
  ```
292
294
 
293
295
  **Under the pipeline**, `sp:functional-review` is a component of `/sp:dev-review`: it returns its
294
- fragment to the coordinator (`sp:super-reviewer`), which writes the combined `## Review`. The
296
+ fragment to the coordinator (`sp:super-reviewer` — the invoking session under `/sp:dev-review`),
297
+ which writes the combined `## Review`. The
295
298
  `record` step transcribes only `## Testing` from the verdict artifact and backfills `## Review`
296
299
  only when the section is bare (`sectionIsBare` guard, `task-service.ts`); it never overwrites the
297
300
  coordinator's authored Review. Keep the priority-table lead stable in the fragment so the L3 gate
@@ -27,7 +27,7 @@ interface FunctionalVerdict {
27
27
  bddReportPath: string | null;
28
28
  /**
29
29
  * Explicit source scope (if --source-paths given) or derived diff scope
30
- * (the changed *.ts/*.tsx/*.js/*.jsx files for the task's last commit).
30
+ * (tagged-commit recipe; SSOT = code-verification SKILL Step 3/3p).
31
31
  */
32
32
  sourcePaths: string[];
33
33
  }
@@ -3,7 +3,8 @@
3
3
  The skill resolves exactly two modes. Everything else fails loud. This matrix is the enforcement
4
4
  surface the workflow (0660) and the skill share; keep the vocabulary frozen. Execution note
5
5
  (0920): argument validation runs deterministically in the `history-anatomy-cache` helper `paths`
6
- command before the workflow starts; the model hop no longer performs it.
6
+ command (logic core: `packages/app/src/services/history-anatomy.ts`, task 1005) before the
7
+ workflow starts; the model hop no longer performs it.
7
8
 
8
9
  ## Mode vocabulary (frozen)
9
10
 
@@ -78,7 +78,7 @@ survivors would make its primary case unreachable. Offer the rank-1 ranked candi
78
78
  **Why this is not a Principle #5 taste auto-click.** The ranking already produced the recommendation;
79
79
  `--auto` only skips the *proceed-with-offer* pause. Overriding the ranking (picking a non-offered
80
80
  candidate) still requires the interactive confirm path. Architecture / design-approval taste gates
81
- inside dispatched children remain governed by their own contracts (`--approve-taste` where applicable).
81
+ inside dispatched children remain governed by their own contracts (`--auto` where applicable).
82
82
 
83
83
  ### What `--task` does not change
84
84
 
@@ -113,7 +113,7 @@ token, deterministic).
113
113
  | C3 | A5/A6 when Testing empty/N/A **and** verify would fail for missing tests — only if prior implement claims code exists | Coverage/test signal: `bun test` fail attributed to task paths OR explicit "insufficient tests" in prior verify verdict artifact `.spur/run/<wbs>-verdict.json` | test fail / coverage gap | `/sp:dev-unit <wbs> --auto` | continue |
114
114
  | C4 | A3/A5/A6 when operator or task tags mention rules, OR `spur rule run` last report dirty in `.spur/` if present | `spur rule run` (default project preset) non-zero with findings | rule findings | **HITL STOP** — print rule summary; suggest `/sp:rule-scan` or `rule-add`/`rule-refine` (do not auto-author rules) | continue |
115
115
  | C5 | A6 only | Existing `.spur/run/<wbs>-verdict.json` with FAIL and findings pointing at coverage | verdict artifact | `/sp:dev-unit <wbs>` then re-verify on next invocation (`--once` friendly) | `/sp:dev-verify …` |
116
- | C6 | A4/A5 | `.spur/run/<wbs>-verdict.json` has a failing (PARTIAL/FAIL) `residual-sweep` check — the bounded remediation loop already ran and failed (F96) | folded verdict artifact | **HITL STOP** — print `.spur/run/<wbs>-residual-report.md` and the recovery command `/sp:dev-run <wbs>`; never auto-dispatch a fix (repeating the loop unattended burns quota without new information) | continue |
116
+ | C6 | A4/A5 | `.spur/run/<wbs>-verdict.json` has a failing (PARTIAL/FAIL) `residual-sweep` check — residual blockers survived the pipeline sweep (F96) | folded verdict artifact | **HITL STOP** — print `.spur/run/<wbs>-residual-report.md` and the recovery command `/sp:dev-run <wbs>`; never auto-dispatch a fix (repeating the loop unattended burns quota without new information) | continue |
117
117
 
118
118
  **C-row precedence note (F96):** C6 outranks C2/C3/C5 for the same task — a residual-sweep failure
119
119
  means the workspace holds unfinished task residue, so fixall/unit reruns would either clean it
@@ -131,7 +131,7 @@ call; no git dirtiness as a route (optional advisory print only).
131
131
  | C2 and C3 both true | lint vs tests both red | (1) fixall (2) unit (3) abort |
132
132
  | Feature B3 pick ambiguous because two `todo` same rank — **should not happen** after WBS sort | — | N/A — WBS tie-break is total |
133
133
  | Task `todo` but also feature-level AC invalid when invoked via feature id | feature health vs task progress | (1) fix feature AC (2) proceed with task A3 |
134
- | `testing` with open P1 in Review section | verify vs review-fix | (1) `/sp:dev-review <wbs> --fix blockers-first` (2) verify anyway |
134
+ | `testing` with open P1 in Review section | verify vs review-fix | (1) `/sp:dev-review --tasks <wbs> --triage` (2) verify anyway |
135
135
  | `wip` with both checkpoint and dirty Solution L3 | resume vs re-implement | (1) `--continue` (2) implement `--next` |
136
136
 
137
137
  When HITL STOP fires: print decision-brief (question, stakes, recommended option, alternatives).
@@ -46,7 +46,7 @@ Pick the noun, read its reference. Each Tier A and Tier B reference owns that no
46
46
  | **Tier A** | **builder** | Release plumbing: bump a package (or the `workspace:`-pinned set) with `bump-ver`, delete release tags with `drop-tags`, commit + tag + optional push | [references/builder.md](references/builder.md) |
47
47
  | **Tier B** | **agent** | Coding-agent execution surface: run prompts via detected/named agents, list agent specs, start/stop supervised processes, readiness check | [references/agent.md](references/agent.md) |
48
48
  | **Tier B** | **message** | Durable inter-agent messaging: send, inbox, reply, watch | [references/message.md](references/message.md) |
49
- | **Tier B** | **self** | Self-management verbs: scaffold (`init`), schema migrations (`migrate`), local web server (`serve`), status overview (`status`); `self init` runs post-scaffold validation probes & layout classification | [references/self.md](references/self.md) |
49
+ | **Tier B** | **self** | Self-management verbs: scaffold (`init`), database maintenance (`maintain`), schema migrations (`migrate`), local web server (`serve`), status overview (`status`); `self init` runs post-scaffold validation probes & layout classification | [references/self.md](references/self.md) |
50
50
  | **Tier B** | **history** | Import agent histories, aggregate forensic artifacts, render reports, and run the checkpoint-resumed daily pipeline | [references/history.md](references/history.md) |
51
51
  | **Tier B** | **projects** | Manage the local multi-project registry and start/stop project servers | [references/projects.md](references/projects.md) |
52
52
  | **Tier C** | **help** | Commander-generated help command; not a Spur noun | Generated `--help` |
@@ -141,8 +141,8 @@ and spreading it; full contract in `docs/04_DESIGN.md` §1.0.1.
141
141
  [dispatch-surface rule](../parallel-execution/references/dispatch-surface.md).
142
142
  - **[references/message.md](references/message.md)** - durable inter-agent messaging (`send`,
143
143
  `inbox`, `reply`, `watch`).
144
- - **[references/self.md](references/self.md)** - `spur self init|migrate|serve|status` CLI verbs
145
- (the four legacy top-level nouns remain hidden aliases). `self init` runs post-scaffold init
144
+ - **[references/self.md](references/self.md)** - `spur self init|maintain|migrate|serve|status` CLI verbs
145
+ (the five legacy top-level nouns remain hidden aliases). `self init` runs post-scaffold init
146
146
  validation (Phase 1.5/1.6 probes).
147
147
  - **[references/history.md](references/history.md)** - history import, forensic artifact analysis,
148
148
  pure report rendering, and the daily pipeline.
@@ -23,12 +23,12 @@ that before using `run` for fan-out dispatch.
23
23
  | ---- | ------- | --------- |
24
24
  | `run <prompt>` | Execute a prompt or slash command via a coding agent | `--agent <name>` `--spec <id>` `--model <name>` `--mode <mode>` `--continue` `--cwd <path>` `--drain` `--json` |
25
25
  | `wait [<specId>]` | Identity-pinned wait for an occupant run to reach a lifecycle state (G4 wave 2; `--role` selector per 0685) | `--role <name>` `--run <runId>` `--until <state>...` `--timeout <ms>` `--json` |
26
- | `list` | List detected coding agents, or agent specs with `--specs` (live run status + member session merged from `spur serve`) | `--specs` `--server <url>` `--json` |
27
- | `status` | Agent specs with live process status and member session (requires `spur serve`) | `--server <url>` `--json` |
26
+ | `list` | List detected coding agents, or agent specs with `--specs` (live run status + member session merged from `spur self serve`) | `--specs` `--server <url>` `--json` |
27
+ | `status` | Agent specs with live process status and member session (requires `spur self serve`) | `--server <url>` `--json` |
28
28
  | `doctor [agent]` | Check agent readiness | `--json` `--probe-health` `--force-refresh` |
29
29
  | `usage` | Run-once provider usage capture (codexbar) → quota-owned availability refresh; scheduled externally | `--dry-run` `--source <name>` `--json` |
30
- | `start <spec-id>` | Start a supervised agent process (requires `spur serve`) | `--server <url>` `--json` |
31
- | `stop <spec-id>` | Stop a supervised agent process (requires `spur serve`) | `--server <url>` `--json` |
30
+ | `start <spec-id>` | Start a supervised agent process (requires `spur self serve`) | `--server <url>` `--json` |
31
+ | `stop <spec-id>` | Stop a supervised agent process (requires `spur self serve`) | `--server <url>` `--json` |
32
32
 
33
33
  `list`, `status`, `doctor`, `run`, `wait`, `start`, and `stop` accept `--json` plus `--json-envelope`. The hidden
34
34
  `loop` is a supervisor-internal process surface. **Exit codes:** `0` success, `1` failure, and `2`
@@ -85,7 +85,7 @@ justify it - but ensure the run executes in a context that can write the target
85
85
 
86
86
  ## `loop` - supervisor-internal self-draining wrapper (hidden)
87
87
 
88
- `spur agent loop --spec <id> [--poll <ms>]` is spawned by the `spur serve` supervisor for each
88
+ `spur agent loop --spec <id> [--poll <ms>]` is spawned by the `spur self serve` supervisor for each
89
89
  materialized agent spec; it is hidden from `--help` and not an operator verb (use `spur agent start`).
90
90
  It waits for a wake on the `system_events` ledger — a human request (`message.sent`), a strategy
91
91
  change (`strategy.changed`), a capacity change (`fleet.capacity.changed`), or a completion receipt
@@ -145,7 +145,7 @@ lists agent specs (`.spur/agents/*.yaml`) **with live run status merged from the
145
145
  supervisor**: each row carries a trailing status column
146
146
  (`running` / `stopped` / `errored` / `unknown`), `pid=<n>` where a process exists, and the member
147
147
  session (0897): the session mode plus a shortened resume id (`resume id=3f9c2a1d`), or `-` when the
148
- member has no recorded session. When `spur serve` is unreachable, the listing falls back to all
148
+ member has no recorded session. When `spur self serve` is unreachable, the listing falls back to all
149
149
  `stopped` with a stderr warning. `--server <url>`
150
150
  (default `http://localhost:3000/api`) targets the supervisor API.
151
151
 
@@ -200,9 +200,9 @@ spur agent start worker-1
200
200
  spur agent start worker-1 --json
201
201
  ```
202
202
 
203
- Posts to the `spur serve` supervisor API
203
+ Posts to the `spur self serve` supervisor API
204
204
  (`POST /api/agents/:id/start`) and prints `started <id> (pid=<n>, status=<s>)`. Requires a
205
- reachable `spur serve`; `--server <url>` (default `http://localhost:3000/api`) targets it. Exit `1`
205
+ reachable `spur self serve`; `--server <url>` (default `http://localhost:3000/api`) targets it. Exit `1`
206
206
  when the server is unreachable or the start fails.
207
207
 
208
208
  ## `stop` - stop a supervised process
@@ -250,7 +250,7 @@ unconfirmed or failed. For `skipped`/`pending` the printed target is intent only
250
250
  0 * * * * /opt/homebrew/bin/spur agent usage >> /tmp/spur-agent-usage.log 2>&1
251
251
  ```
252
252
 
253
- `spur serve` never invokes the producer (asserted by a test, design R4).
253
+ `spur self serve` never invokes the producer (asserted by a test, design R4).
254
254
 
255
255
  ### Flags
256
256
 
@@ -285,7 +285,7 @@ one never re-sends settled work; only never-started deliveries release and redel
285
285
  - **Not the dispatch decision.** *When* to use `spur agent run` vs a native subagent is the
286
286
  **[dispatch-surface rule](../../parallel-execution/references/dispatch-surface.md)**, not this
287
287
  reference. This reference documents the verbs; that rule decides which surface carries a dispatch.
288
- - **Not the fleet orchestrator.** The `spur serve` supervisor drives the lifecycle: `spur agent
288
+ - **Not the fleet orchestrator.** The `spur self serve` supervisor drives the lifecycle: `spur agent
289
289
  start` / `stop` manage supervised processes and `agent list --specs` reports live state.
290
290
 
291
291
  ## See also
@@ -181,11 +181,16 @@ planning reference, then apply accepted deterministic changes through `spur feat
181
181
  ## The gate — `check --json`
182
182
 
183
183
  ```bash
184
- spur feature check H2 --json # one feature
185
- spur feature check --json # whole tree
186
- spur feature check --strict --json # warnings → failures
184
+ spur feature check H2 --json # one feature
185
+ spur feature check --json # whole tree
186
+ spur feature check --strict --json # warnings → failures
187
+ spur feature check H2 --inventory <eval-report.md> # also cross-check AC ↔ requirement inventory
187
188
  ```
188
189
 
190
+ With `--inventory <report>`, the `inventory-coverage` finding (1004 R1, unsuppressible error layer)
191
+ fails the check when a `## Requirement inventory` item has no covering scenario (`# covers: I<n>`)
192
+ and is not `[deferred: ...]`; a missing/empty inventory section is itself an error.
193
+
189
194
  The 4-layer validator (frontmatter, AC syntax, children-limit/structure, L4 traceability) emits its
190
195
  verdict and findings as a JSON **array**, one entry per feature (`jq '.[0].pass'`, `.[0].findings[].code`),
191
196
  like `spur task check --json`. Gherkin AC must keep its `Feature:` line (`L3.ac-bdd-error`) and
@@ -207,15 +212,21 @@ see the proposed status hop.
207
212
  spur feature sync H2 --json # one feature
208
213
  spur feature sync H2 --dry-run --json # propose only, no write
209
214
  spur feature sync --all --json # every feature with linked tasks
210
- spur feature sync H2 --force # apply a reopen proposal without confirmation
215
+ spur feature sync H2 --force # re-derive live: bypass replay + apply a reopen without confirmation
211
216
  spur feature sync H2 --folder docs/custom-tasks --json # non-default tasks folder
212
217
  ```
213
218
 
214
219
  - **`[id]`** syncs one feature; **`--all`** syncs every feature with linked tasks. One of the two is
215
220
  required - exit `2` if neither is given.
216
221
  - **`--dry-run`** reports proposed transitions without applying. **`--force`** applies a *reopen*
217
- proposal (status moving backward) without interactive confirmation.
218
- - **`--json`** single-feature emits `{ proposal, applied, appliedHops[] }`; `--all` emits
222
+ proposal (status moving backward) without interactive confirmation **and** bypasses blocked-sync
223
+ replay (below).
224
+ - **Blocked-sync suppression (1004 R3):** a BLOCKED outcome is persisted at
225
+ `.spur/run/feature-sync-blocked-<id>.json` with an input fingerprint (feature content, linked
226
+ task statuses, verdict mtimes); an identical next call replays the prior result
227
+ (`suppressed: true`) instead of re-deriving. A changed input, `--force`, or a non-blocked
228
+ outcome re-derives/clears. Dry-run never reads or writes the state.
229
+ - **`--json`** single-feature emits `{ proposal, applied, appliedHops[], suppressed? }`; `--all` emits
219
230
  `{ totalFeatures, evaluated, updatedCount, results[] }` where each result is the single-feature
220
231
  shape. `proposal` is
221
232
  `{ featureId, from, to, reason, requiresConfirm?, gateBlocked?, gateFindings?, hops? }`.