@gobing-ai/spur 0.4.3 → 0.4.5

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 (119) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/rules/docs/sourced-api-claims.yaml +3 -2
  3. package/config/rules/quality/coverage-gate.yaml +13 -0
  4. package/config/rules/recommended-pre-check.yaml +11 -5
  5. package/config/rules/strict/runtime-boundaries.yaml +3 -0
  6. package/config/rules/surface/check-cli-surface.yaml +1 -3
  7. package/config/templates/AGENTS.md +7 -0
  8. package/config/workflows/feature-verification.yaml +9 -1
  9. package/config/workflows/task-lifecycle.yaml +50 -0
  10. package/config/workflows/task-pipeline.yaml +31 -65
  11. package/package.json +10 -10
  12. package/plugins/sp/agents/super-reviewer.md +11 -24
  13. package/plugins/sp/commands/dev-changelog.md +1 -1
  14. package/plugins/sp/commands/dev-gitmsg.md +6 -2
  15. package/plugins/sp/commands/dev-handover.md +1 -0
  16. package/plugins/sp/commands/dev-job-dump.md +2 -1
  17. package/plugins/sp/commands/dev-refine.md +14 -2
  18. package/plugins/sp/commands/dev-refineall.md +7 -1
  19. package/plugins/sp/commands/dev-review.md +1 -0
  20. package/plugins/sp/lib/history-anatomy.generated.mjs +18 -18
  21. package/plugins/sp/lib/idea-handoff.generated.mjs +91 -91
  22. package/plugins/sp/lib/inline-run.generated.mjs +233 -277
  23. package/plugins/sp/lib/plan-projection.generated.mjs +62 -38
  24. package/plugins/sp/lib/quality-gate.generated.d.mts +1 -0
  25. package/plugins/sp/lib/quality-gate.generated.mjs +13793 -13966
  26. package/plugins/sp/lib/residual-scan.generated.mjs +20 -34
  27. package/plugins/sp/lib/step-profile.generated.mjs +8 -8
  28. package/plugins/sp/plugin.json +1 -1
  29. package/plugins/sp/scripts/batch-plan.mjs +62 -40
  30. package/plugins/sp/scripts/batch-preflight.mjs +34 -12
  31. package/plugins/sp/scripts/commit-guard.mjs +6 -6
  32. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +26 -28
  33. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +11 -11
  34. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +4 -4
  35. package/plugins/sp/scripts/feature-verification-steps.mjs +15 -4
  36. package/plugins/sp/scripts/feature-verification-steps.ts +29 -1
  37. package/plugins/sp/scripts/history-anatomy-cache.mjs +19 -19
  38. package/plugins/sp/scripts/idea-handoff.mjs +2 -2
  39. package/plugins/sp/scripts/inline-run-dispatch.mjs +2 -2
  40. package/plugins/sp/scripts/inline-run-setup.mjs +20 -20
  41. package/plugins/sp/scripts/persist-out-check.mjs +5 -5
  42. package/plugins/sp/scripts/pr-reviewing.mjs +13 -13
  43. package/plugins/sp/scripts/quality-gate.mjs +13715 -13884
  44. package/plugins/sp/scripts/residual-scan.mjs +24 -38
  45. package/plugins/sp/scripts/residual-scan.ts +4 -6
  46. package/plugins/sp/scripts/run-summary.mjs +15 -15
  47. package/plugins/sp/scripts/script-root.mjs +6 -6
  48. package/plugins/sp/scripts/session-timeline.mjs +16 -16
  49. package/plugins/sp/scripts/task-diffstat.mjs +8 -8
  50. package/plugins/sp/scripts/workflow-step-profile.mjs +12 -12
  51. package/plugins/sp/scripts/wrapup-drift-probe.mjs +9 -9
  52. package/plugins/sp/scripts/wrapup-steps.mjs +25 -25
  53. package/plugins/sp/skills/code-verification/SKILL.md +1 -2
  54. package/plugins/sp/skills/code-verification/references/verdict-schema.md +11 -32
  55. package/plugins/sp/skills/next-router/SKILL.md +3 -1
  56. package/plugins/sp/skills/next-router/references/routing-table.md +9 -3
  57. package/plugins/sp/skills/spur-cli/references/tasks.md +24 -4
  58. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +8 -1
  59. package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -11
  60. package/plugins/sp/skills/spur-dev/references/execution-batch.md +6 -3
  61. package/plugins/sp/skills/spur-dev/references/execution-workflow.md +7 -11
  62. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +4 -2
  63. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +29 -8
  64. package/schemas/section-matrix.schema.json +3 -2
  65. package/spur.js +24597 -32769
  66. package/web/_astro/{BoardApp.5IIl2O-z.js → BoardApp.Niubpe6z.js} +1 -1
  67. package/web/_astro/{BoardApp.Drnx-KeM.js → BoardApp.hF0RAIrg.js} +112 -112
  68. package/web/_astro/TaskDetail.DJpapt38.js +1 -0
  69. package/web/_astro/{arc.j2EVkYVd.js → arc.BgKcK0i8.js} +1 -1
  70. package/web/_astro/{architectureDiagram-3BPJPVTR.DZWSFqEG.js → architectureDiagram-3BPJPVTR.ChP-Ig0V.js} +1 -1
  71. package/web/_astro/{blockDiagram-GPEHLZMM.DE6GpXCN.js → blockDiagram-GPEHLZMM.BVaCBoeN.js} +1 -1
  72. package/web/_astro/{c4Diagram-AAUBKEIU.DoVR1G6B.js → c4Diagram-AAUBKEIU.CHSDpEO5.js} +1 -1
  73. package/web/_astro/channel.DkpvN2tD.js +1 -0
  74. package/web/_astro/{chunk-2J33WTMH.Do6-Gbyf.js → chunk-2J33WTMH.MYRcYVRa.js} +1 -1
  75. package/web/_astro/{chunk-4BX2VUAB.BYV1HGHx.js → chunk-4BX2VUAB.S5j22Zqa.js} +1 -1
  76. package/web/_astro/{chunk-55IACEB6.CvI1zkrc.js → chunk-55IACEB6.gciRPvmZ.js} +1 -1
  77. package/web/_astro/{chunk-727SXJPM.DNGSCRlX.js → chunk-727SXJPM.CdqrSQkX.js} +1 -1
  78. package/web/_astro/{chunk-AQP2D5EJ.DXhhgT5z.js → chunk-AQP2D5EJ.CyokdHT3.js} +1 -1
  79. package/web/_astro/{chunk-FMBD7UC4.dzkRKP2u.js → chunk-FMBD7UC4.Bg9KJhjW.js} +1 -1
  80. package/web/_astro/{chunk-ND2GUHAM.DujGLeEf.js → chunk-ND2GUHAM.1gxxI-vD.js} +1 -1
  81. package/web/_astro/{chunk-QZHKN3VN.BWtIT3ZU.js → chunk-QZHKN3VN.DDEYtCKU.js} +1 -1
  82. package/web/_astro/{classDiagram-4FO5ZUOK.DyApg0Nn.js → classDiagram-4FO5ZUOK.Ch3zi37T.js} +1 -1
  83. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DyApg0Nn.js → classDiagram-v2-Q7XG4LA2.Ch3zi37T.js} +1 -1
  84. package/web/_astro/{cose-bilkent-S5V4N54A.DJQB-u_l.js → cose-bilkent-S5V4N54A.D2q5AINI.js} +1 -1
  85. package/web/_astro/{cynefin-OW5HDTMX.bi3D_siV.js → cynefin-OW5HDTMX.BA9kC2YT.js} +1 -1
  86. package/web/_astro/{dagre-BM42HDAG.C-f7TrRg.js → dagre-BM42HDAG.CHO08nRp.js} +1 -1
  87. package/web/_astro/{diagram-2AECGRRQ.DrPfjHQB.js → diagram-2AECGRRQ.BfFQt0LW.js} +1 -1
  88. package/web/_astro/{diagram-5GNKFQAL.C9S4WUHO.js → diagram-5GNKFQAL.CHMwMTe4.js} +1 -1
  89. package/web/_astro/{diagram-KO2AKTUF.Dzzye8QH.js → diagram-KO2AKTUF.BJ-7gK-w.js} +1 -1
  90. package/web/_astro/{diagram-LMA3HP47.DX-LRiof.js → diagram-LMA3HP47.C5xqcc0t.js} +1 -1
  91. package/web/_astro/{diagram-OG6HWLK6.Z-kAKwIz.js → diagram-OG6HWLK6.C2pQjfrC.js} +1 -1
  92. package/web/_astro/{erDiagram-TEJ5UH35.D6dyClSv.js → erDiagram-TEJ5UH35._FoNyxgC.js} +1 -1
  93. package/web/_astro/{flowDiagram-I6XJVG4X.Cx3q2cxD.js → flowDiagram-I6XJVG4X.B_ENkT2I.js} +1 -1
  94. package/web/_astro/{ganttDiagram-6RSMTGT7.DU3gH6CT.js → ganttDiagram-6RSMTGT7.B3q8h77C.js} +1 -1
  95. package/web/_astro/{gitGraphDiagram-PVQCEYII.ISmCnJ87.js → gitGraphDiagram-PVQCEYII.QWZFDboZ.js} +1 -1
  96. package/web/_astro/index.BFyW13JJ.css +1 -0
  97. package/web/_astro/{infoDiagram-5YYISTIA.DR6_iNOK.js → infoDiagram-5YYISTIA.Cs6eEc92.js} +1 -1
  98. package/web/_astro/{ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js → ishikawaDiagram-YF4QCWOH.BU9DJMHA.js} +1 -1
  99. package/web/_astro/{journeyDiagram-JHISSGLW.DkP9-WK8.js → journeyDiagram-JHISSGLW.D35k6Jpe.js} +1 -1
  100. package/web/_astro/{kanban-definition-UN3LZRKU.wtgY98M2.js → kanban-definition-UN3LZRKU.DBqcAT4m.js} +1 -1
  101. package/web/_astro/{linear.CI6djNi7.js → linear.D-LSNK8W.js} +1 -1
  102. package/web/_astro/{mermaid.core.CuQPxS8b.js → mermaid.core.CSruWVOa.js} +4 -4
  103. package/web/_astro/{mindmap-definition-RKZ34NQL.5HNIEG7R.js → mindmap-definition-RKZ34NQL.BiFaUsfj.js} +1 -1
  104. package/web/_astro/{pieDiagram-4H26LBE5.CchhDFjE.js → pieDiagram-4H26LBE5.D1KTKqb9.js} +1 -1
  105. package/web/_astro/{quadrantDiagram-W4KKPZXB.PYXXJPSJ.js → quadrantDiagram-W4KKPZXB.DBHflsMG.js} +1 -1
  106. package/web/_astro/{requirementDiagram-4Y6WPE33.cUQuNAKU.js → requirementDiagram-4Y6WPE33.hwX9CNdk.js} +1 -1
  107. package/web/_astro/{sankeyDiagram-5OEKKPKP.BO7MnRIA.js → sankeyDiagram-5OEKKPKP.DkI8mJiE.js} +1 -1
  108. package/web/_astro/{sequenceDiagram-3UESZ5HK.D9bElyD6.js → sequenceDiagram-3UESZ5HK.DMz7jBDx.js} +1 -1
  109. package/web/_astro/{stateDiagram-AJRCARHV.DhGdRz7I.js → stateDiagram-AJRCARHV.B6Ef2xJ1.js} +1 -1
  110. package/web/_astro/{stateDiagram-v2-BHNVJYJU.5GvLeAoL.js → stateDiagram-v2-BHNVJYJU.CgoaufB9.js} +1 -1
  111. package/web/_astro/{timeline-definition-PNZ67QCA.LtAT9haM.js → timeline-definition-PNZ67QCA.B3c8h1t-.js} +1 -1
  112. package/web/_astro/{vennDiagram-CIIHVFJN.DRth1yLA.js → vennDiagram-CIIHVFJN.BdHPAxAb.js} +1 -1
  113. package/web/_astro/{wardleyDiagram-YWT4CUSO.h9MlZfcr.js → wardleyDiagram-YWT4CUSO.BrJXPPAj.js} +1 -1
  114. package/web/_astro/{xychartDiagram-2RQKCTM6.DHqy9DYA.js → xychartDiagram-2RQKCTM6.CMH5yFsV.js} +1 -1
  115. package/web/index.html +2 -2
  116. package/web/spur_logo.svg +1 -1
  117. package/web/_astro/TaskDetail.Ci4LNn9S.js +0 -1
  118. package/web/_astro/channel.DmdDxGsq.js +0 -1
  119. package/web/_astro/index.Do2lLdgo.css +0 -1
@@ -33,14 +33,6 @@ function hasClaimMarker(evidence) {
33
33
  function hasCitationForm(evidence) {
34
34
  return CITATION_FORMS.some((re) => re.test(evidence));
35
35
  }
36
- var CONFIDENCE_LEVELS = ["HIGH", "MEDIUM", "LOW"];
37
- function extractAnswerConfidence(text) {
38
- const matches = [...text.matchAll(/^\s*Confidence:\s*(\S+)\s*$/gim)];
39
- if (matches.length !== 1)
40
- return;
41
- const value = (matches[0]?.[1] ?? "").toUpperCase();
42
- return CONFIDENCE_LEVELS.includes(value) ? value : undefined;
43
- }
44
36
  var MARKER_PATTERN = /TODO|FIXME|XXX|HACK/;
45
37
  var PRIORITY_PATTERN = /^P[1-4]/;
46
38
  var RANGE_PRIORITY = /^P[1-4]\s*[\u2013\u2014-]\s*P?[1-4]/;
@@ -212,13 +204,6 @@ function blockingReviewFindings(taskContent, deferrals) {
212
204
  }
213
205
  function reviewContractViolations(taskContent) {
214
206
  const violations = [];
215
- const body = reviewBody(taskContent);
216
- if (body !== null && extractAnswerConfidence(body.replace(/\*\*/g, "")) === undefined) {
217
- violations.push({
218
- location: "task-file:Review",
219
- text: "Review report carries no single valid `**Confidence:** HIGH | MEDIUM | LOW` line — " + "state the level the report can defend (HIGH: every requirement row MET, every finding " + "dispositioned, evidence cited; MEDIUM/LOW: name the gap)."
220
- });
221
- }
222
207
  for (const row of parseReviewFindings(taskContent)) {
223
208
  if (hasClaimMarker(row.text) && !hasCitationForm(row.text)) {
224
209
  violations.push({
@@ -230,9 +215,10 @@ function reviewContractViolations(taskContent) {
230
215
  return violations;
231
216
  }
232
217
  function buildReviewGateArtifact(wbs, taskContent, deferrals) {
218
+ const contractViolations = reviewContractViolations(taskContent);
233
219
  const items = [
234
220
  ...blockingReviewFindings(taskContent, deferrals),
235
- ...reviewContractViolations(taskContent).map((v) => ({
221
+ ...contractViolations.map((v) => ({
236
222
  id: makeItemId("review-contract", v.location, v.text),
237
223
  category: "review-contract",
238
224
  class: "blocking",
@@ -622,27 +608,27 @@ function main(argv, env = getEnvVars(), options = {}) {
622
608
  }
623
609
  process.exit(main(process.argv.slice(2)));
624
610
  export {
625
- verdictDisagreementNote,
626
- scanResiduals2 as scanResiduals,
627
- reviewContractViolations,
628
- renderReport,
629
- recordedVerdictPath,
630
- parseScanArgs,
631
- parseReviewFindings,
632
- parseDiffMarkers,
633
- parseDeferralEntries,
634
- normalizeAnchor,
635
- makeItemId,
636
- main,
637
- locationOf,
638
- listStagingResidue,
639
- foldVerdict,
640
- findUncheckedBoxes,
641
- collectAddedLines,
642
- classify,
643
- buildReviewGateArtifact,
644
- blockingReviewFindings,
645
- blockingAnchors,
611
+ ALLOW_PRAGMA,
646
612
  RESIDUAL_SCAN_USAGE,
647
- ALLOW_PRAGMA
613
+ blockingAnchors,
614
+ blockingReviewFindings,
615
+ buildReviewGateArtifact,
616
+ classify,
617
+ collectAddedLines,
618
+ findUncheckedBoxes,
619
+ foldVerdict,
620
+ listStagingResidue,
621
+ locationOf,
622
+ main,
623
+ makeItemId,
624
+ normalizeAnchor,
625
+ parseDeferralEntries,
626
+ parseDiffMarkers,
627
+ parseReviewFindings,
628
+ parseScanArgs,
629
+ recordedVerdictPath,
630
+ renderReport,
631
+ reviewContractViolations,
632
+ scanResiduals2 as scanResiduals,
633
+ verdictDisagreementNote
648
634
  };
@@ -104,12 +104,10 @@ function scanMode(opts: ParsedArgs, env: ScanEnv, io: ScanIo): number {
104
104
  return 0;
105
105
  }
106
106
  /**
107
- * Task 1122 R2: the review-time gate — the record sweep's review-finding slice with the same
108
- * parse/classify/deferrals (core.buildReviewGateArtifact), applied at the review PASS edges
109
- * before verify spends the cycle. Writes the same `<wbs>-residuals.json` artifact the test-fix
110
- * hop already appends to the gate log (its remediation input); the record sweep stays the final
111
- * authority and overwrites it. Exit 1 on an open P1-P3 finding fails the review PASS edge closed
112
- * into review-fail-triage.
107
+ * Task 1122 R2: the review-time gate — the record sweep's review-finding slice
108
+ * (core.buildReviewGateArtifact) at the review PASS edges; writes the same `<wbs>-residuals.json`
109
+ * the test-fix hop reads. Exit 1 on an open P1-P3 finding. The answer-grade rule (1204) was
110
+ * retired with the asserted confidence machinery (F511); findings stay the gate.
113
111
  */
114
112
  function reviewGateMode(opts: ParsedArgs, env: ScanEnv, io: ScanIo): number {
115
113
  const runDir = join(opts.root, '.spur', 'run');
@@ -114,21 +114,21 @@ function accumulate(acc, row, ts) {
114
114
  if (row.type === "message") {
115
115
  const m = row.message;
116
116
  if (m?.role === "assistant") {
117
- const blocks2 = Array.isArray(m.content) ? m.content : [];
118
- for (const b of blocks2) {
117
+ const blocks = Array.isArray(m.content) ? m.content : [];
118
+ for (const b of blocks) {
119
119
  if (b.type === "toolCall" && b.id) {
120
120
  acc.toolIds.add(b.id);
121
121
  if (b.name && OPERATOR_TOOLS.has(b.name))
122
122
  acc.asks.set(b.id, ts);
123
123
  }
124
124
  }
125
- const u2 = m.usage;
126
- if (u2 && row.id)
125
+ const u = m.usage;
126
+ if (u && row.id)
127
127
  acc.messages.set(row.id, {
128
- input: u2.input ?? 0,
129
- cacheCreate: u2.cacheWrite ?? 0,
130
- cacheRead: u2.cacheRead ?? 0,
131
- output: u2.output ?? 0
128
+ input: u.input ?? 0,
129
+ cacheCreate: u.cacheWrite ?? 0,
130
+ cacheRead: u.cacheRead ?? 0,
131
+ output: u.output ?? 0
132
132
  });
133
133
  } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
134
134
  acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
@@ -747,12 +747,12 @@ function main(argv, env = getEnvVars(), write = (s) => process.stdout.write(s),
747
747
  }
748
748
  process.exit(main(process.argv.slice(2)));
749
749
  export {
750
- writeCloseSummary,
751
- renderSummaryMarkdown,
752
- renderRollupMarkdown,
753
- main,
754
- gateFromReceipt,
755
- buildRunSummary,
750
+ RUN_SUMMARY_USAGE,
756
751
  actorSplit,
757
- RUN_SUMMARY_USAGE
752
+ buildRunSummary,
753
+ gateFromReceipt,
754
+ main,
755
+ renderRollupMarkdown,
756
+ renderSummaryMarkdown,
757
+ writeCloseSummary
758
758
  };
@@ -119,11 +119,11 @@ function main(argv, env = getEnvVars(), options = {}) {
119
119
  process.exit(main(process.argv.slice(2)));
120
120
  }
121
121
  export {
122
- runScriptRoot,
123
- main,
124
- digestScriptSet,
125
- SOURCE_REPO_MARKER,
126
- SCRIPT_ROOT_USAGE,
122
+ PROJECT_SCRIPTS_DIR,
127
123
  SCRIPT_ROOT_TWIN_REL,
128
- PROJECT_SCRIPTS_DIR
124
+ SCRIPT_ROOT_USAGE,
125
+ SOURCE_REPO_MARKER,
126
+ digestScriptSet,
127
+ main,
128
+ runScriptRoot
129
129
  };
@@ -107,21 +107,21 @@ function accumulate(acc, row, ts) {
107
107
  if (row.type === "message") {
108
108
  const m = row.message;
109
109
  if (m?.role === "assistant") {
110
- const blocks2 = Array.isArray(m.content) ? m.content : [];
111
- for (const b of blocks2) {
110
+ const blocks = Array.isArray(m.content) ? m.content : [];
111
+ for (const b of blocks) {
112
112
  if (b.type === "toolCall" && b.id) {
113
113
  acc.toolIds.add(b.id);
114
114
  if (b.name && OPERATOR_TOOLS.has(b.name))
115
115
  acc.asks.set(b.id, ts);
116
116
  }
117
117
  }
118
- const u2 = m.usage;
119
- if (u2 && row.id)
118
+ const u = m.usage;
119
+ if (u && row.id)
120
120
  acc.messages.set(row.id, {
121
- input: u2.input ?? 0,
122
- cacheCreate: u2.cacheWrite ?? 0,
123
- cacheRead: u2.cacheRead ?? 0,
124
- output: u2.output ?? 0
121
+ input: u.input ?? 0,
122
+ cacheCreate: u.cacheWrite ?? 0,
123
+ cacheRead: u.cacheRead ?? 0,
124
+ output: u.output ?? 0
125
125
  });
126
126
  } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
127
127
  acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
@@ -347,13 +347,13 @@ function main(argv, env = getEnvVars(), write = (s) => process.stdout.write(s),
347
347
  }
348
348
  process.exit(main(process.argv.slice(2)));
349
349
  export {
350
- zeroSegmentReason,
351
- resolveTranscript,
352
- parseGroups,
353
- main,
354
- formatTokens,
355
- formatTokenSplit,
356
- formatDuration,
350
+ SESSION_TIMELINE_USAGE,
357
351
  buildTimeline,
358
- SESSION_TIMELINE_USAGE
352
+ formatDuration,
353
+ formatTokenSplit,
354
+ formatTokens,
355
+ main,
356
+ parseGroups,
357
+ resolveTranscript,
358
+ zeroSegmentReason
359
359
  };
@@ -84,9 +84,9 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
84
84
  const failSafe = (reason) => {
85
85
  process.stderr.write(`task-diffstat: ${reason} \u2014 failing safe (sensitive)
86
86
  `);
87
- const row2 = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
88
- writeArtifact(abs(relResult), row2);
89
- return { ...row2, resultFile: relResult, exitCode: 0 };
87
+ const row = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
88
+ writeArtifact(abs(relResult), row);
89
+ return { ...row, resultFile: relResult, exitCode: 0 };
90
90
  };
91
91
  let stdoutIsArtifact = false;
92
92
  try {
@@ -172,10 +172,10 @@ function main(argv, env = getEnvVars(), options = {}) {
172
172
  process.exit(main(process.argv.slice(2)));
173
173
  }
174
174
  export {
175
- sensitiveReasonForPath,
176
- runDiffstat,
177
- main,
178
- TASK_DIFFSTAT_USAGE,
175
+ SENSITIVE_GLOBS,
179
176
  SENSITIVE_PREFIXES,
180
- SENSITIVE_GLOBS
177
+ TASK_DIFFSTAT_USAGE,
178
+ main,
179
+ runDiffstat,
180
+ sensitiveReasonForPath
181
181
  };
@@ -322,17 +322,17 @@ function main(argv) {
322
322
  process.exit(main(process.argv.slice(2)));
323
323
  }
324
324
  export {
325
- runStepProfileCli,
326
- rowFlags,
327
- parseStepProfileCliArgs,
328
- nonDryRuns,
329
- nearestRankP50,
330
- main,
331
- formatStepProfileHuman,
332
- extractExecutions,
333
- buildStepProfile,
334
- buildRows,
335
- STEP_PROFILE_USAGE,
325
+ DEFAULT_LAST,
336
326
  DEFAULT_WINDOW_SEC,
337
- DEFAULT_LAST
327
+ STEP_PROFILE_USAGE,
328
+ buildRows,
329
+ buildStepProfile,
330
+ extractExecutions,
331
+ formatStepProfileHuman,
332
+ main,
333
+ nearestRankP50,
334
+ nonDryRuns,
335
+ parseStepProfileCliArgs,
336
+ rowFlags,
337
+ runStepProfileCli
338
338
  };
@@ -114,13 +114,13 @@ function runDriftProbe(env, options = {}, spur = defaultSpurRunner(env, options.
114
114
  mkdirSync(abs(join(".spur", "run")), { recursive: true });
115
115
  writeFileSync(abs(relModeFile), `
116
116
  `);
117
- const finish = (clean, reasons2, paths) => {
118
- writeFileSync(abs(relProbeFile), `${JSON.stringify({ clean, reasons: reasons2, paths })}
117
+ const finish = (clean, reasons, paths) => {
118
+ writeFileSync(abs(relProbeFile), `${JSON.stringify({ clean, reasons, paths })}
119
119
  `);
120
120
  writeFileSync(abs(relModeFile), clean ? `fast
121
121
  ` : `
122
122
  `);
123
- return { clean, reasons: reasons2, paths, probeFile: relProbeFile, modeFile: relModeFile, exitCode: 0 };
123
+ return { clean, reasons, paths, probeFile: relProbeFile, modeFile: relModeFile, exitCode: 0 };
124
124
  };
125
125
  let tasks;
126
126
  try {
@@ -180,11 +180,11 @@ function main(argv, env = getEnvVars(), options = {}) {
180
180
  process.exit(main(process.argv.slice(2)));
181
181
  }
182
182
  export {
183
- solutionSectionOf,
184
- runDriftProbe,
185
- main,
186
- driftReasonForPath,
187
- changedPathsOf,
183
+ DOC_OWNED_SURFACES,
188
184
  WRAPUP_DRIFT_PROBE_USAGE,
189
- DOC_OWNED_SURFACES
185
+ changedPathsOf,
186
+ driftReasonForPath,
187
+ main,
188
+ runDriftProbe,
189
+ solutionSectionOf
190
190
  };
@@ -296,7 +296,7 @@ function runMetrics(env, options = {}) {
296
296
  const parsed = JSON.parse(shown.stdout);
297
297
  const frontmatter = parsed.frontmatter !== null && typeof parsed.frontmatter === "object" ? parsed.frontmatter : {};
298
298
  const featureId = String(jqPick(frontmatter.feature_id, parsed.feature_id, ""));
299
- const status2 = String(jqPick(frontmatter.status, parsed.status, "unknown"));
299
+ const status = String(jqPick(frontmatter.status, parsed.status, "unknown"));
300
300
  const durable = join(".spur", "memory", "evidence", `${wbs}-verdict.json`);
301
301
  const hasDurable = existsSync(abs(durable));
302
302
  const verdictPath = hasDurable ? durable : join(".spur", "run", `${wbs}-verdict.json`);
@@ -308,7 +308,7 @@ function runMetrics(env, options = {}) {
308
308
  `);
309
309
  }
310
310
  const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
311
- const row = { wbs, feature_id: featureId, status: status2, verdict, timestamp };
311
+ const row = { wbs, feature_id: featureId, status, verdict, timestamp };
312
312
  try {
313
313
  appendFileSync(abs(relMetricsFile), `${JSON.stringify(row)}
314
314
  `);
@@ -600,28 +600,28 @@ function main(argv, env = getEnvVars(), options = {}) {
600
600
  process.exit(main(process.argv.slice(2)));
601
601
  }
602
602
  export {
603
- writeRouteReason,
604
- verdictFromTestingSection,
605
- taskStatusOf,
606
- runMetrics,
607
- runFeatureTransition,
608
- runDocSyncSnapshot,
609
- runDocSyncScope,
610
- runDocSupersessionCheck,
611
- resolveTasks,
612
- preflightFeature,
613
- porcelainPath,
614
- main,
615
- jqPick,
616
- isDocSyncAllowedPath,
617
- docSyncScopeViolations,
618
- classifySync,
619
- WRAPUP_STEPS_USAGE,
620
- WBS_PATTERN,
621
- SUPERSESSION_PIN,
622
- SNAPSHOT_MISSING,
623
- GIT_UNAVAILABLE_MARKER,
624
- DOC_SYNC_SUPERSESSION_STATUS_SUFFIX,
603
+ DOC_SYNC_SCOPE_STATUS_SUFFIX,
625
604
  DOC_SYNC_SNAPSHOT_SUFFIX,
626
- DOC_SYNC_SCOPE_STATUS_SUFFIX
605
+ DOC_SYNC_SUPERSESSION_STATUS_SUFFIX,
606
+ GIT_UNAVAILABLE_MARKER,
607
+ SNAPSHOT_MISSING,
608
+ SUPERSESSION_PIN,
609
+ WBS_PATTERN,
610
+ WRAPUP_STEPS_USAGE,
611
+ classifySync,
612
+ docSyncScopeViolations,
613
+ isDocSyncAllowedPath,
614
+ jqPick,
615
+ main,
616
+ porcelainPath,
617
+ preflightFeature,
618
+ resolveTasks,
619
+ runDocSupersessionCheck,
620
+ runDocSyncScope,
621
+ runDocSyncSnapshot,
622
+ runFeatureTransition,
623
+ runMetrics,
624
+ taskStatusOf,
625
+ verdictFromTestingSection,
626
+ writeRouteReason
627
627
  };
@@ -341,7 +341,6 @@ Verdict: PASS (or PARTIAL / FAIL)
341
341
 
342
342
  ```markdown
343
343
  Verdict: PASS
344
- Confidence: HIGH
345
344
 
346
345
  ### Per-Requirement Traceability
347
346
  | Req | Status | Evidence |
@@ -360,7 +359,7 @@ Confidence: HIGH
360
359
  ```
361
360
 
362
361
  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 |`.
363
- The answer MUST carry exactly one `Confidence: HIGH|MEDIUM|LOW` line (task 1068): the verifier's stated confidence in the verdict — HIGH = fresh executable evidence + re-read anchors this run; MEDIUM = partial reliance on prior evidence, external/delegated outcomes, or an unstable tree; LOW = key evidence not re-executed. A missing or out-of-vocabulary line fails lint (`confidence-missing` / `confidence-value`) and blocks derivation; the value lands in the verdict artifact's `confidence` field and the recorded `## Testing` section.
362
+ The answer carries no `Confidence:` line (F511, task 1207): model-asserted confidence is not verification — a verifier that can write a PASS can write `HIGH`. What the old level compressed now lives in the evidence cells themselves: cite fresh executable evidence and name residual gaps explicitly. Legacy answers that still carry a confidence line parse (the value is ignored); no lint code reads it and the artifact and recorded `## Testing` section carry no confidence value.
364
363
  The answer MUST cite a source for external API/library claims in Requirement/AC evidence cells: a cell asserting a scoped-package (`@scope/name`), `node_modules/`, semver-version (`x.y.z`), or backticked function-call (`` `name(args)` ``) claim is rejected with `evidence-citation` unless the SAME cell carries a citation — a repo `path:line(-end)` (backticked or bare), the external named-origin form (@origin `path` line N), or a URL. Cross-cell references ("receipt above") do not count; marker-free receipts ("bun test 398 pass / 0 fail") need no citation.
365
364
  **MUST NOT:** place a `Severity` column between `Req` and `Status`. The parser tolerates variants
366
365
  as defense-in-depth; this contract is canonical.
@@ -35,42 +35,21 @@ interface VerifyVerdict {
35
35
  status: 'pass' | 'fail' | 'warn';
36
36
  evidence: string;
37
37
  }>;
38
- /** Verifier confidence (task 1068 R3). REQUIRED for PASS — the done gate denies
39
- * a confidence-less PASS. Accepted case-insensitively, normalized to uppercase. */
40
- confidence?: 'HIGH' | 'MEDIUM' | 'LOW';
38
+ // F511 (task 1207): no asserted `confidence` field. Legacy artifacts that still carry one
39
+ // parse with the value ignored — unknown legacy keys are tolerated on read.
41
40
  }
42
41
  ```
43
42
 
44
- ## Confidence level (task 1068 R3)
43
+ ## No asserted confidence (F511, task 1207)
45
44
 
46
- A PASS verdict must state the verifier's confidence: `"confidence": "HIGH" | "MEDIUM" | "LOW"`.
47
- The value is case-insensitive on read (normalized to uppercase), but a present value outside the
48
- enum invalidates the artifact, and a PASS with the field absent is denied at the `* → done`
49
- transition with a remediation naming the field (`--force-done` remains the documented escape).
50
- The verify answer's `Confidence:` lint rule (producer side) already guarantees the field for
51
- pipeline-produced artifacts; this closes the hand-authored / pre-1068 artifact hole.
52
-
53
- ### Coherence: the level must be earned (task 1133)
54
-
55
- Presence and vocabulary are not enough — the level has to match the rows in the same answer, so the
56
- lint rejects a level that contradicts its own evidence. Two rejection classes, both addressed to the
57
- `Confidence:` line and both capped by the shared `ANSWER_LINT_MAX_FINDINGS` budget:
58
-
59
- - `confidence-unwarranted` — `HIGH` while any requirement row normalizes to `PARTIAL`/`UNMET`, any
60
- acceptance-criteria row does not normalize to `MET`, or any `MET` row's evidence is hedged.
61
- A `HIGH` claim says the evidence needs no re-check; a non-MET or hedged row says the opposite.
62
- - `confidence-understated` — `LOW` while at least one requirement row exists and every requirement
63
- and acceptance-criteria row normalizes to `MET`. The evidence already did the work, so `LOW`
64
- misreports the state as unproven.
65
-
66
- `MEDIUM` carries no coherence rule in either direction: it is the level a verifier uses for a caveat
67
- with no row to point at, and closing it would force either an overclaimed `HIGH` or an unusable
68
- `LOW`. The level is normalized to uppercase before the decision, so `high` is judged as `HIGH`.
69
-
70
- Set the level from the rows, not the mood: all rows `MET` with fresh evidence is `HIGH`; a
71
- `PARTIAL`/`UNMET` row or a hedge means `MEDIUM`, not `HIGH`. An unwarranted level does not merely
72
- warn — the lint rejects the answer and no verdict artifact is written, so the level is worth
73
- choosing deliberately before the verdict line is finalized.
45
+ Verdict artifacts carry no `confidence` field and verify answers no `Confidence:` line. The
46
+ 1068/1133 machinery that required, linted and coherence-checked a model-asserted
47
+ `HIGH | MEDIUM | LOW` was retired: a verifier that can write a PASS can write `HIGH`, so the grade
48
+ verifies nothing. What the level compressed belongs in the evidence cells — cite fresh executable
49
+ evidence and name residual gaps explicitly. The done gate no longer denies a confidence-less PASS:
50
+ it checks the proof digests, which are the part a hand-edited artifact cannot fake. The DERIVED
51
+ confidence family is a different thing and stays: feature-verification receipts (ADR-131, 1196),
52
+ the decision subsystem threshold, history-anatomy reading.
74
53
 
75
54
  ## Compatibility alias: `scenario` row key
76
55
 
@@ -142,7 +142,8 @@ but redundant. See the glossary entry for the disambiguation in full.
142
142
 
143
143
  Never: reimplement the pipeline inside the router; default `todo` to `--mode full`; infer targets
144
144
  from git/chat; auto-`--merge` on wrap; auto-author rules; use `dev-runall` as the feature default;
145
- bypass lifecycle guards (`--no-lifecycle`) to force progress.
145
+ bypass lifecycle guards (`--no-lifecycle`) to force progress; select a `deprecated` task for any
146
+ lifecycle hop (TABLE A A10 — frozen work has no forward step; un-deprecate or cancel it first).
146
147
 
147
148
  ## Common Rationalizations
148
149
 
@@ -158,6 +159,7 @@ bypass lifecycle guards (`--no-lifecycle`) to force progress.
158
159
  - Dispatching more than one primary command in a single invocation.
159
160
  - `--auto` used to choose among HITL candidates.
160
161
  - A route that is not in TABLE A/B/C (invented dispatch targets).
162
+ - A `deprecated` WBS routed into any lifecycle command instead of stopping (A10/ADR-137).
161
163
  - `--once` honored by merely skipping the re-invoke while leaving `--next` in the child argv.
162
164
  - Omitting the `dev-next:` prefix on stop/plan messages.
163
165
 
@@ -54,7 +54,7 @@ it or when chaining (chain links already use `--auto` per existing refine→run
54
54
 
55
55
  ## 1. TABLE A — Task primary routes (corpus status)
56
56
 
57
- Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done | cancelled.
57
+ Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done | cancelled | deprecated.
58
58
 
59
59
  | # | Precondition | Primary signal | Dispatch | probe | Chain on success? | Stop / notes |
60
60
  | --- | --- | --- | --- | --- | --- | --- |
@@ -67,8 +67,9 @@ Statuses from `TASK_STATUSES`: backlog | todo | wip | testing | blocked | done |
67
67
  | A7 | `status == blocked` | `task.show` + Notes/History for blocker text | `/sp:dev-handover "<blocker summary from Notes or status>"` | no | no | **STOP** after handover doc; human unblocks |
68
68
  | A8 | `status == done` | `task.show.status` | `/sp:dev-wrap <wbs>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | **no** (wrap's `--merge` is irreversible HITL; never auto) | Operator may re-invoke with `--merge` explicitly later |
69
69
  | A9 | `status == cancelled` | `task.show.status` | *(none)* | no | no | **STOP** — no-op; print "cancelled — nothing to advance" |
70
+ | A10 | `status == deprecated` | `task.show.status` | *(none)* | no | no | **STOP** — frozen and retained (ADR-137); nothing to advance. Print "deprecated — frozen; un-deprecate with `spur task update <wbs> todo`, or cancel it once removed". Never falls through to another row |
70
71
 
71
- **Priority when multiple TABLE A rows could match:** lowest row number wins (A1–A9 are mutually
72
+ **Priority when multiple TABLE A rows could match:** lowest row number wins (A1–A10 are mutually
72
73
  exclusive by status except A2/A3 which split `todo`).
73
74
 
74
75
  ## 2. TABLE B — Feature-ID path (rollup)
@@ -84,7 +85,7 @@ When input is a feature id:
84
85
  | B4 | No frontier tasks AND `feature.status == backlog` AND AC placeholder/invalid | `feature check` / AC body | `/sp:dev-plan` continuation is not WBS-shaped — **STOP** with: run `/sp:dev-plan --feature <id>` or fix AC then `spur feature check <id>` | no | no | Planning-half entry needs a description; do not invent idea text |
85
86
  | B5 | No frontier tasks AND feature has valid AC but zero tasks | feature + empty task roster | **STOP** with: `/sp:dev-plan --feature <id>` (decompose/batch-create) | no | no | Avoid auto-running plan without operator description confirmation in v1 |
86
87
  | B6 | No frontier tasks AND all child tasks `done` AND feature in `active`\|`verifying` | task list all done | `/sp:dev-wrapall --feature <id>` (`--agent <value>` preserved when the originating command supplied it; omission remains omission) | no | no | Advances feature via wrapall guards; `--merge` never auto |
87
- | B7 | No frontier tasks AND mixed cancelled/done only | statuses | **STOP** — print summary; suggest feature status update manually | no | no | |
88
+ | B7 | No frontier tasks AND mixed terminal only (any combination drawn from `cancelled`, `done`, `deprecated`) | statuses | **STOP** — print summary; suggest feature status update manually | no | no | |
88
89
  | B8 | `feature.status == blocked` | feature status | **STOP** — print feature blocked; do not pick tasks | no | no | |
89
90
 
90
91
  **Frontier selection algorithm (B3) — precise:**
@@ -101,6 +102,11 @@ if pick is null → TABLE B rows B4–B7
101
102
  else → TABLE A(pick)
102
103
  ```
103
104
 
105
+ The candidate set is exactly the open statuses: `done`, `cancelled` **and `deprecated`** are never
106
+ frontier candidates (ADR-137 — a frozen task is retained, not resumable), and a `deprecated`
107
+ dependency is not a satisfied one, so it keeps its dependents out of the frontier exactly like any
108
+ unfinished dep.
109
+
104
110
  ## 3. TABLE C — Light-gate short-circuit (probe=yes rows only)
105
111
 
106
112
  **Order is sequential short-circuit** (first match wins). Never parallel in v1 (simpler, lower
@@ -126,12 +126,32 @@ mutually exclusive.
126
126
  spur task update 0040 wip
127
127
  ```
128
128
 
129
- Valid statuses: `backlog · todo · wip · testing · blocked · done · cancelled` (the lifecycle engine
130
- enforces legal transitions). Two transitions are **guarded by `check`**: `wip→testing` runs
131
- `spur task check <wbs> --as testing`, and `testing→done` runs `spur task check <wbs> --as done`
132
- (F92 R3 — target-aware guards) — a failing
129
+ Valid statuses are the canonical `TaskStatus` vocabulary — **`TASK_STATUSES` in
130
+ `packages/domain/src/planning/schema.ts` is the only list**, and the lifecycle engine enforces legal
131
+ transitions against the shipped `task-lifecycle` graph (the engine's bundled workflow, resolved per
132
+ project).
133
+ Operator-facing semantics (the freeze, its guards, the exempt write set) live in the design
134
+ satellite [`task-deprecated-status.md`](../../../../../docs/design/task-deprecated-status.md) — read it
135
+ there rather than restating the vocabulary. Two transitions are **guarded by `check`**:
136
+ `wip→testing` runs `spur task check <wbs> --as testing`, and `testing→done` runs
137
+ `spur task check <wbs> --as done` (F92 R3 — target-aware guards) — a failing
133
138
  gate blocks the transition (§7.5).
134
139
 
140
+ **`deprecated` — frozen but retained (ADR-137).** Non-terminal: the record stays readable and
141
+ countable, but nothing new attaches to it and it never advances. Legal edges (from the shipped
142
+ graph): `backlog`, `todo`, `wip`, `testing`, `blocked` and `done` may move to `deprecated`, and it leaves only to `todo`
143
+ (un-deprecate, mandatory `History` entry) or `cancelled` (removed). Any other status write is
144
+ refused by the graph, and the refusal names the un-deprecate command.
145
+
146
+ Two guards carry the freeze past the graph: a **new** dependency edge onto a `deprecated` task is
147
+ refused (ready-set resolver and pipeline preflight), and a live dependent is reported by the
148
+ `L4.task-deprecated` warning. The freeze is prospective — an edge recorded before the deprecation is
149
+ reported, never rewritten.
150
+
151
+ Writes that stay **permitted** on a `deprecated` task (each edits the record rather than declaring
152
+ the work resumable): `spur task record`, `migrate-anchors` and verdict writes, and
153
+ `spur task update <wbs> --section <any>` — the section write lands and returns a freeze warning.
154
+
135
155
  **`--no-lifecycle`** suppresses lifecycle workflow *run record* creation (use during pipeline-driven
136
156
  transitions so nested lifecycle runs are not orphaned). **It is not a guard bypass** — the
137
157
  `wip→testing` / `testing→done` `check` gates still run, evaluated inline when the FSM guard does not.
@@ -572,7 +572,14 @@ does not earn another full-gate run:
572
572
  4. Give the test an explicit per-test timeout and a WHY comment (see
573
573
  `plugins/sp/tests/inline-run-close-reason.test.ts:100-102`) as soon as EITHER (a) the same test
574
574
  load-flakes in two separate runs, OR (b) it times out once **outside the change scope** and then
575
- passes green alone on three consecutive isolated reruns. Waiting for a second gate run to license
575
+ passes green alone on three consecutive isolated reruns. **Prefer making the test cheap or
576
+ hermetic first** — a test that hashes the live checkout transitively, or writes a probe file into
577
+ it, should build a `mkdtemp` fixture repo instead of buying a bigger budget
578
+ (`packages/app/tests/workflow/actions/proof-fingerprint.test.ts` is the pattern, 2026-10-13).
579
+ Only then hand the remaining repeat flakers an explicit budget: the suite-wide `--timeout` is
580
+ not the lever, and raising it was measured NOT to help (a 60s suite default still failed 7
581
+ spawn-heavy tests under a host shared with two sibling agent suites, while every one passed
582
+ alone in 0.15-0.3s — 2026-10-13). Waiting for a second gate run to license
576
583
  the fix cost 21 minutes on 2026-10-08 (task 1128, the 1058 two-tree canary: 5145 ms then 5967 ms
577
584
  against bun's 5000 ms default, green 3/3 alone). Never raise the global timeout, skip the test,
578
585
  or loop full-gate reruns.