@gobing-ai/spur 0.4.3 → 0.4.4

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 (117) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/rules/docs/verdict-confidence.yaml +68 -0
  3. package/config/rules/quality/coverage-gate.yaml +13 -0
  4. package/config/rules/recommended-pre-check.yaml +6 -2
  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 +15 -9
  11. package/package.json +10 -10
  12. package/plugins/sp/agents/super-reviewer.md +5 -2
  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.d.mts +13 -0
  27. package/plugins/sp/lib/residual-scan.generated.mjs +51 -21
  28. package/plugins/sp/lib/step-profile.generated.mjs +8 -8
  29. package/plugins/sp/plugin.json +1 -1
  30. package/plugins/sp/scripts/batch-plan.mjs +62 -40
  31. package/plugins/sp/scripts/batch-preflight.mjs +34 -12
  32. package/plugins/sp/scripts/commit-guard.mjs +6 -6
  33. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +26 -28
  34. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +11 -11
  35. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +4 -4
  36. package/plugins/sp/scripts/feature-verification-steps.mjs +15 -4
  37. package/plugins/sp/scripts/feature-verification-steps.ts +29 -1
  38. package/plugins/sp/scripts/history-anatomy-cache.mjs +19 -19
  39. package/plugins/sp/scripts/idea-handoff.mjs +2 -2
  40. package/plugins/sp/scripts/inline-run-dispatch.mjs +2 -2
  41. package/plugins/sp/scripts/inline-run-setup.mjs +20 -20
  42. package/plugins/sp/scripts/persist-out-check.mjs +5 -5
  43. package/plugins/sp/scripts/pr-reviewing.mjs +13 -13
  44. package/plugins/sp/scripts/quality-gate.mjs +13715 -13884
  45. package/plugins/sp/scripts/residual-scan.mjs +58 -27
  46. package/plugins/sp/scripts/residual-scan.ts +8 -8
  47. package/plugins/sp/scripts/run-summary.mjs +15 -15
  48. package/plugins/sp/scripts/script-root.mjs +6 -6
  49. package/plugins/sp/scripts/session-timeline.mjs +16 -16
  50. package/plugins/sp/scripts/task-diffstat.mjs +8 -8
  51. package/plugins/sp/scripts/workflow-step-profile.mjs +12 -12
  52. package/plugins/sp/scripts/wrapup-drift-probe.mjs +9 -9
  53. package/plugins/sp/scripts/wrapup-steps.mjs +25 -25
  54. package/plugins/sp/skills/next-router/SKILL.md +3 -1
  55. package/plugins/sp/skills/next-router/references/routing-table.md +9 -3
  56. package/plugins/sp/skills/spur-cli/references/tasks.md +24 -4
  57. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +8 -1
  58. package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -11
  59. package/plugins/sp/skills/spur-dev/references/execution-batch.md +6 -3
  60. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +4 -2
  61. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +22 -2
  62. package/schemas/section-matrix.schema.json +3 -2
  63. package/spur.js +24403 -32459
  64. package/web/_astro/{BoardApp.5IIl2O-z.js → BoardApp.Niubpe6z.js} +1 -1
  65. package/web/_astro/{BoardApp.Drnx-KeM.js → BoardApp.hF0RAIrg.js} +112 -112
  66. package/web/_astro/TaskDetail.DJpapt38.js +1 -0
  67. package/web/_astro/{arc.j2EVkYVd.js → arc.BgKcK0i8.js} +1 -1
  68. package/web/_astro/{architectureDiagram-3BPJPVTR.DZWSFqEG.js → architectureDiagram-3BPJPVTR.ChP-Ig0V.js} +1 -1
  69. package/web/_astro/{blockDiagram-GPEHLZMM.DE6GpXCN.js → blockDiagram-GPEHLZMM.BVaCBoeN.js} +1 -1
  70. package/web/_astro/{c4Diagram-AAUBKEIU.DoVR1G6B.js → c4Diagram-AAUBKEIU.CHSDpEO5.js} +1 -1
  71. package/web/_astro/channel.DkpvN2tD.js +1 -0
  72. package/web/_astro/{chunk-2J33WTMH.Do6-Gbyf.js → chunk-2J33WTMH.MYRcYVRa.js} +1 -1
  73. package/web/_astro/{chunk-4BX2VUAB.BYV1HGHx.js → chunk-4BX2VUAB.S5j22Zqa.js} +1 -1
  74. package/web/_astro/{chunk-55IACEB6.CvI1zkrc.js → chunk-55IACEB6.gciRPvmZ.js} +1 -1
  75. package/web/_astro/{chunk-727SXJPM.DNGSCRlX.js → chunk-727SXJPM.CdqrSQkX.js} +1 -1
  76. package/web/_astro/{chunk-AQP2D5EJ.DXhhgT5z.js → chunk-AQP2D5EJ.CyokdHT3.js} +1 -1
  77. package/web/_astro/{chunk-FMBD7UC4.dzkRKP2u.js → chunk-FMBD7UC4.Bg9KJhjW.js} +1 -1
  78. package/web/_astro/{chunk-ND2GUHAM.DujGLeEf.js → chunk-ND2GUHAM.1gxxI-vD.js} +1 -1
  79. package/web/_astro/{chunk-QZHKN3VN.BWtIT3ZU.js → chunk-QZHKN3VN.DDEYtCKU.js} +1 -1
  80. package/web/_astro/{classDiagram-4FO5ZUOK.DyApg0Nn.js → classDiagram-4FO5ZUOK.Ch3zi37T.js} +1 -1
  81. package/web/_astro/{classDiagram-v2-Q7XG4LA2.DyApg0Nn.js → classDiagram-v2-Q7XG4LA2.Ch3zi37T.js} +1 -1
  82. package/web/_astro/{cose-bilkent-S5V4N54A.DJQB-u_l.js → cose-bilkent-S5V4N54A.D2q5AINI.js} +1 -1
  83. package/web/_astro/{cynefin-OW5HDTMX.bi3D_siV.js → cynefin-OW5HDTMX.BA9kC2YT.js} +1 -1
  84. package/web/_astro/{dagre-BM42HDAG.C-f7TrRg.js → dagre-BM42HDAG.CHO08nRp.js} +1 -1
  85. package/web/_astro/{diagram-2AECGRRQ.DrPfjHQB.js → diagram-2AECGRRQ.BfFQt0LW.js} +1 -1
  86. package/web/_astro/{diagram-5GNKFQAL.C9S4WUHO.js → diagram-5GNKFQAL.CHMwMTe4.js} +1 -1
  87. package/web/_astro/{diagram-KO2AKTUF.Dzzye8QH.js → diagram-KO2AKTUF.BJ-7gK-w.js} +1 -1
  88. package/web/_astro/{diagram-LMA3HP47.DX-LRiof.js → diagram-LMA3HP47.C5xqcc0t.js} +1 -1
  89. package/web/_astro/{diagram-OG6HWLK6.Z-kAKwIz.js → diagram-OG6HWLK6.C2pQjfrC.js} +1 -1
  90. package/web/_astro/{erDiagram-TEJ5UH35.D6dyClSv.js → erDiagram-TEJ5UH35._FoNyxgC.js} +1 -1
  91. package/web/_astro/{flowDiagram-I6XJVG4X.Cx3q2cxD.js → flowDiagram-I6XJVG4X.B_ENkT2I.js} +1 -1
  92. package/web/_astro/{ganttDiagram-6RSMTGT7.DU3gH6CT.js → ganttDiagram-6RSMTGT7.B3q8h77C.js} +1 -1
  93. package/web/_astro/{gitGraphDiagram-PVQCEYII.ISmCnJ87.js → gitGraphDiagram-PVQCEYII.QWZFDboZ.js} +1 -1
  94. package/web/_astro/index.BFyW13JJ.css +1 -0
  95. package/web/_astro/{infoDiagram-5YYISTIA.DR6_iNOK.js → infoDiagram-5YYISTIA.Cs6eEc92.js} +1 -1
  96. package/web/_astro/{ishikawaDiagram-YF4QCWOH.D7wY-ZGA.js → ishikawaDiagram-YF4QCWOH.BU9DJMHA.js} +1 -1
  97. package/web/_astro/{journeyDiagram-JHISSGLW.DkP9-WK8.js → journeyDiagram-JHISSGLW.D35k6Jpe.js} +1 -1
  98. package/web/_astro/{kanban-definition-UN3LZRKU.wtgY98M2.js → kanban-definition-UN3LZRKU.DBqcAT4m.js} +1 -1
  99. package/web/_astro/{linear.CI6djNi7.js → linear.D-LSNK8W.js} +1 -1
  100. package/web/_astro/{mermaid.core.CuQPxS8b.js → mermaid.core.CSruWVOa.js} +4 -4
  101. package/web/_astro/{mindmap-definition-RKZ34NQL.5HNIEG7R.js → mindmap-definition-RKZ34NQL.BiFaUsfj.js} +1 -1
  102. package/web/_astro/{pieDiagram-4H26LBE5.CchhDFjE.js → pieDiagram-4H26LBE5.D1KTKqb9.js} +1 -1
  103. package/web/_astro/{quadrantDiagram-W4KKPZXB.PYXXJPSJ.js → quadrantDiagram-W4KKPZXB.DBHflsMG.js} +1 -1
  104. package/web/_astro/{requirementDiagram-4Y6WPE33.cUQuNAKU.js → requirementDiagram-4Y6WPE33.hwX9CNdk.js} +1 -1
  105. package/web/_astro/{sankeyDiagram-5OEKKPKP.BO7MnRIA.js → sankeyDiagram-5OEKKPKP.DkI8mJiE.js} +1 -1
  106. package/web/_astro/{sequenceDiagram-3UESZ5HK.D9bElyD6.js → sequenceDiagram-3UESZ5HK.DMz7jBDx.js} +1 -1
  107. package/web/_astro/{stateDiagram-AJRCARHV.DhGdRz7I.js → stateDiagram-AJRCARHV.B6Ef2xJ1.js} +1 -1
  108. package/web/_astro/{stateDiagram-v2-BHNVJYJU.5GvLeAoL.js → stateDiagram-v2-BHNVJYJU.CgoaufB9.js} +1 -1
  109. package/web/_astro/{timeline-definition-PNZ67QCA.LtAT9haM.js → timeline-definition-PNZ67QCA.B3c8h1t-.js} +1 -1
  110. package/web/_astro/{vennDiagram-CIIHVFJN.DRth1yLA.js → vennDiagram-CIIHVFJN.BdHPAxAb.js} +1 -1
  111. package/web/_astro/{wardleyDiagram-YWT4CUSO.h9MlZfcr.js → wardleyDiagram-YWT4CUSO.BrJXPPAj.js} +1 -1
  112. package/web/_astro/{xychartDiagram-2RQKCTM6.DHqy9DYA.js → xychartDiagram-2RQKCTM6.CMH5yFsV.js} +1 -1
  113. package/web/index.html +2 -2
  114. package/web/spur_logo.svg +1 -1
  115. package/web/_astro/TaskDetail.Ci4LNn9S.js +0 -1
  116. package/web/_astro/channel.DmdDxGsq.js +0 -1
  117. package/web/_astro/index.Do2lLdgo.css +0 -1
@@ -47,6 +47,8 @@ export declare interface ParsedScanArgs {
47
47
  spurBin?: string;
48
48
  root: string;
49
49
  tmpDir: string;
50
+ /** The --review-answer <path> flag: the review answer the review-gate mode grades (task 1204). */
51
+ reviewAnswer?: string;
50
52
  }
51
53
  export declare const ALLOW_PRAGMA: 'residual-scan:allow';
52
54
  export declare function recordedVerdictPath(runDir: string, wbs: string, fs: Pick<typeof import('node:fs'), 'lstatSync' | 'existsSync'>): string;
@@ -68,10 +70,21 @@ export declare function scanResiduals(inputs: ResidualScanInputs): ResidualArtif
68
70
  export declare function parseDeferralEntries(raw: unknown): Deferral[];
69
71
  export declare function blockingReviewFindings(taskContent: string, deferrals: Deferral[]): ResidualItem[];
70
72
  export declare function reviewContractViolations(taskContent: string): Array<{ location: string; text: string }>;
73
+ export declare function reviewConfidenceViolation(
74
+ reviewAnswer: string,
75
+ ackLowConfidence: string | undefined,
76
+ ): { location: string; text: string } | null;
77
+ export declare function reviewGradingInput(
78
+ root: string,
79
+ reviewAnswer: string | undefined,
80
+ ackLowConfidence: string | undefined,
81
+ fs: Pick<typeof import('node:fs'), 'readFileSync' | 'statSync'>,
82
+ ): { answer: string; ackLowConfidence: string | undefined } | undefined;
71
83
  export declare function buildReviewGateArtifact(
72
84
  wbs: string,
73
85
  taskContent: string,
74
86
  deferrals: Deferral[],
87
+ review?: { answer: string; ackLowConfidence: string | undefined },
75
88
  ): { artifact: ResidualArtifact; note: string };
76
89
  export declare function parseScanArgs(
77
90
  argv: readonly (string | undefined)[],
@@ -219,10 +219,35 @@ function reviewContractViolations(taskContent) {
219
219
  }
220
220
  return violations;
221
221
  }
222
- function buildReviewGateArtifact(wbs, taskContent, deferrals) {
222
+ function reviewConfidenceViolation(reviewAnswer, ackLowConfidence) {
223
+ if (ackLowConfidence === "true")
224
+ return null;
225
+ const level = extractAnswerConfidence(reviewAnswer.replace(/\*\*/g, ""));
226
+ if (level === "HIGH" || level === "MEDIUM")
227
+ return null;
228
+ return {
229
+ location: "review-answer:Confidence",
230
+ text: level === undefined ? "The review answer carries no valid `**Confidence:** HIGH | MEDIUM | LOW` line — a " + "PASS must be graded, or the operator must set ackLowConfidence" : `The review answer graded its PASS ${level} — HIGH/MEDIUM cross unadorned, ` + "LOW/absent needs the operator’s ackLowConfidence"
231
+ };
232
+ }
233
+ function reviewGradingInput(root, reviewAnswer, ackLowConfidence, fs) {
234
+ if (reviewAnswer === undefined)
235
+ return;
236
+ const path = join(root, reviewAnswer);
237
+ let isFile = false;
238
+ try {
239
+ isFile = fs.statSync(path).isFile();
240
+ } catch {
241
+ isFile = false;
242
+ }
243
+ return { answer: isFile ? fs.readFileSync(path, "utf8") : "", ackLowConfidence };
244
+ }
245
+ function buildReviewGateArtifact(wbs, taskContent, deferrals, review) {
246
+ const confidence = review === undefined ? null : reviewConfidenceViolation(review.answer, review.ackLowConfidence);
247
+ const contractViolations = [...reviewContractViolations(taskContent), ...confidence === null ? [] : [confidence]];
223
248
  const items = [
224
249
  ...blockingReviewFindings(taskContent, deferrals),
225
- ...reviewContractViolations(taskContent).map((v) => ({
250
+ ...contractViolations.map((v) => ({
226
251
  id: makeItemId("review-contract", v.location, v.text),
227
252
  category: "review-contract",
228
253
  class: "blocking",
@@ -355,6 +380,7 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
355
380
  let spurBin;
356
381
  let root = cwd;
357
382
  let tmpDir = defaultTmpDir;
383
+ let reviewAnswer;
358
384
  for (let i = 0;i < argv.length; i++) {
359
385
  const a = argv[i];
360
386
  if (a === undefined)
@@ -365,6 +391,8 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
365
391
  root = argv[++i] ?? root;
366
392
  else if (a === "--tmp-dir")
367
393
  tmpDir = argv[++i] ?? tmpDir;
394
+ else if (a === "--review-answer")
395
+ reviewAnswer = argv[++i];
368
396
  else if (a === "--help" || a === "-h")
369
397
  return null;
370
398
  else if (mode === "")
@@ -372,26 +400,28 @@ function parseScanArgs(argv, cwd, defaultTmpDir) {
372
400
  else if (wbs === "")
373
401
  wbs = a;
374
402
  }
375
- return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir };
403
+ return mode === "" || wbs === "" ? null : { mode, wbs, spurBin, root, tmpDir, reviewAnswer };
376
404
  }
377
405
  export {
378
- verdictDisagreementNote,
379
- scanResiduals,
380
- reviewContractViolations,
381
- renderReport,
382
- recordedVerdictPath,
383
- parseScanArgs,
384
- parseReviewFindings,
385
- parseDiffMarkers,
386
- parseDeferralEntries,
387
- normalizeAnchor,
388
- makeItemId,
389
- locationOf,
390
- foldVerdict,
391
- findUncheckedBoxes,
392
- classify,
393
- buildReviewGateArtifact,
394
- blockingReviewFindings,
406
+ ALLOW_PRAGMA,
395
407
  blockingAnchors,
396
- ALLOW_PRAGMA
408
+ blockingReviewFindings,
409
+ buildReviewGateArtifact,
410
+ classify,
411
+ findUncheckedBoxes,
412
+ foldVerdict,
413
+ locationOf,
414
+ makeItemId,
415
+ normalizeAnchor,
416
+ parseDeferralEntries,
417
+ parseDiffMarkers,
418
+ parseReviewFindings,
419
+ parseScanArgs,
420
+ recordedVerdictPath,
421
+ renderReport,
422
+ reviewConfidenceViolation,
423
+ reviewContractViolations,
424
+ reviewGradingInput,
425
+ scanResiduals,
426
+ verdictDisagreementNote
397
427
  };
@@ -162,13 +162,13 @@ function formatStepProfileHuman(profile) {
162
162
  `;
163
163
  }
164
164
  export {
165
- rowFlags,
166
- nonDryRuns,
167
- nearestRankP50,
168
- formatStepProfileHuman,
169
- extractExecutions,
170
- buildStepProfile,
171
- buildRows,
165
+ DEFAULT_LAST,
172
166
  DEFAULT_WINDOW_SEC,
173
- DEFAULT_LAST
167
+ buildRows,
168
+ buildStepProfile,
169
+ extractExecutions,
170
+ formatStepProfileHuman,
171
+ nearestRankP50,
172
+ nonDryRuns,
173
+ rowFlags
174
174
  };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "Spur — a local-first harness engineering toolkit that wraps mainstream coding agents with constraint checking, workflow orchestration, and history analytics.",
5
5
  "extensions": {
6
6
  "pi": ["./hooks/pi/guard-extension.ts"]
@@ -5,6 +5,12 @@
5
5
  import { readFileSync } from "fs";
6
6
 
7
7
  // plugins/sp/lib/plan-projection.generated.mjs
8
+ function isTaskFrozen(status) {
9
+ return status?.toLowerCase() === "deprecated";
10
+ }
11
+ function undeprecateTaskCommand(wbs) {
12
+ return `spur task update ${wbs} todo`;
13
+ }
8
14
  var NON_ABORTING_PREFLIGHT_CODES = new Set([
9
15
  "L4.scenario-unverified",
10
16
  "L4.verifying-incomplete-tasks"
@@ -55,6 +61,13 @@ function quickReadiness(input) {
55
61
  };
56
62
  }
57
63
  }
64
+ if (isTaskFrozen(status)) {
65
+ return {
66
+ action: "skipped",
67
+ code: "DEPRECATED",
68
+ reason: `quick-readiness: deprecated (frozen) — no ${operation} hop; un-deprecate with \`${undeprecateTaskCommand(input.wbs)}\` (${input.wbs})`
69
+ };
70
+ }
58
71
  if (status === "cancelled" || status === "done") {
59
72
  if (operation === "verify" && input.force === true) {
60
73
  return {
@@ -79,10 +92,12 @@ function quickReadiness(input) {
79
92
  if (operation === "run" && input.dependencies && input.dependencies.length > 0) {
80
93
  const unmet = input.dependencies.filter((d) => (input.depStatuses?.[d] ?? "missing").toLowerCase() !== "done");
81
94
  if (unmet.length > 0) {
95
+ const frozen = unmet.filter((d) => isTaskFrozen(input.depStatuses?.[d]));
96
+ const hint = frozen.length > 0 ? ` — ${frozen.map((d) => `${d} deprecated (frozen); re-point the edge or un-deprecate: \`${undeprecateTaskCommand(d)}\``).join("; ")}` : "";
82
97
  return {
83
98
  action: "blocked",
84
99
  code: "DEP",
85
- reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})`,
100
+ reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})${hint}`,
86
101
  unmetDeps: unmet
87
102
  };
88
103
  }
@@ -167,6 +182,13 @@ function preflightTask(input) {
167
182
  reason: `dev-next: blocked — do not launch pipeline; human/handover first (${input.wbs})`
168
183
  };
169
184
  }
185
+ if (isTaskFrozen(status)) {
186
+ return {
187
+ action: "skip",
188
+ code: "DEPRECATED",
189
+ reason: `dev-next: deprecated (frozen) — un-deprecate first: ${undeprecateTaskCommand(input.wbs)} (${input.wbs})`
190
+ };
191
+ }
170
192
  if (status === "todo" || status === "backlog") {
171
193
  const unmet = deps.filter((d) => {
172
194
  const st = (depStatuses[d] ?? "missing").toLowerCase();
@@ -274,21 +296,21 @@ function accumulate(acc, row, ts) {
274
296
  if (row.type === "message") {
275
297
  const m = row.message;
276
298
  if (m?.role === "assistant") {
277
- const blocks2 = Array.isArray(m.content) ? m.content : [];
278
- for (const b of blocks2) {
299
+ const blocks = Array.isArray(m.content) ? m.content : [];
300
+ for (const b of blocks) {
279
301
  if (b.type === "toolCall" && b.id) {
280
302
  acc.toolIds.add(b.id);
281
303
  if (b.name && OPERATOR_TOOLS.has(b.name))
282
304
  acc.asks.set(b.id, ts);
283
305
  }
284
306
  }
285
- const u2 = m.usage;
286
- if (u2 && row.id)
307
+ const u = m.usage;
308
+ if (u && row.id)
287
309
  acc.messages.set(row.id, {
288
- input: u2.input ?? 0,
289
- cacheCreate: u2.cacheWrite ?? 0,
290
- cacheRead: u2.cacheRead ?? 0,
291
- output: u2.output ?? 0
310
+ input: u.input ?? 0,
311
+ cacheCreate: u.cacheWrite ?? 0,
312
+ cacheRead: u.cacheRead ?? 0,
313
+ output: u.output ?? 0
292
314
  });
293
315
  } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
294
316
  acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
@@ -1039,36 +1061,36 @@ function main(argv) {
1039
1061
  process.exit(main(process.argv.slice(2)));
1040
1062
  }
1041
1063
  export {
1042
- zeroTokens,
1043
- validatePhaseTable,
1044
- taskPhaseChildren,
1045
- sumTokens,
1046
- sumSpans,
1047
- skillInvocationPrompt,
1048
- sensitiveReasonForPath,
1049
- scheduleBatch,
1050
- quickReadiness,
1051
- promptText,
1052
- preflightTask,
1053
- planLetter,
1054
- planChild,
1055
- newAcc,
1056
- main,
1057
- isCompaction,
1058
- insertOnEntry,
1059
- hostText,
1060
- hostStatus,
1061
- formatTokens,
1062
- formatTokenSplit,
1063
- formatDuration,
1064
- classifyFeaturePreflightFindings,
1065
- buildRunSummary,
1066
- buildPhasedPlan,
1067
- buildBatchPlan,
1068
- actorSplit,
1069
- accumulate,
1070
- SENSITIVE_PREFIXES,
1071
- SENSITIVE_GLOBS,
1064
+ BATCH_PLAN_USAGE,
1072
1065
  NON_ABORTING_PREFLIGHT_CODES,
1073
- BATCH_PLAN_USAGE
1066
+ SENSITIVE_GLOBS,
1067
+ SENSITIVE_PREFIXES,
1068
+ accumulate,
1069
+ actorSplit,
1070
+ buildBatchPlan,
1071
+ buildPhasedPlan,
1072
+ buildRunSummary,
1073
+ classifyFeaturePreflightFindings,
1074
+ formatDuration,
1075
+ formatTokenSplit,
1076
+ formatTokens,
1077
+ hostStatus,
1078
+ hostText,
1079
+ insertOnEntry,
1080
+ isCompaction,
1081
+ main,
1082
+ newAcc,
1083
+ planChild,
1084
+ planLetter,
1085
+ preflightTask,
1086
+ promptText,
1087
+ quickReadiness,
1088
+ scheduleBatch,
1089
+ sensitiveReasonForPath,
1090
+ skillInvocationPrompt,
1091
+ sumSpans,
1092
+ sumTokens,
1093
+ taskPhaseChildren,
1094
+ validatePhaseTable,
1095
+ zeroTokens
1074
1096
  };
@@ -3,6 +3,12 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // plugins/sp/lib/plan-projection.generated.mjs
6
+ function isTaskFrozen(status) {
7
+ return status?.toLowerCase() === "deprecated";
8
+ }
9
+ function undeprecateTaskCommand(wbs) {
10
+ return `spur task update ${wbs} todo`;
11
+ }
6
12
  var NON_ABORTING_PREFLIGHT_CODES = new Set([
7
13
  "L4.scenario-unverified",
8
14
  "L4.verifying-incomplete-tasks"
@@ -53,6 +59,13 @@ function quickReadiness(input) {
53
59
  };
54
60
  }
55
61
  }
62
+ if (isTaskFrozen(status)) {
63
+ return {
64
+ action: "skipped",
65
+ code: "DEPRECATED",
66
+ reason: `quick-readiness: deprecated (frozen) — no ${operation} hop; un-deprecate with \`${undeprecateTaskCommand(input.wbs)}\` (${input.wbs})`
67
+ };
68
+ }
56
69
  if (status === "cancelled" || status === "done") {
57
70
  if (operation === "verify" && input.force === true) {
58
71
  return {
@@ -77,10 +90,12 @@ function quickReadiness(input) {
77
90
  if (operation === "run" && input.dependencies && input.dependencies.length > 0) {
78
91
  const unmet = input.dependencies.filter((d) => (input.depStatuses?.[d] ?? "missing").toLowerCase() !== "done");
79
92
  if (unmet.length > 0) {
93
+ const frozen = unmet.filter((d) => isTaskFrozen(input.depStatuses?.[d]));
94
+ const hint = frozen.length > 0 ? ` — ${frozen.map((d) => `${d} deprecated (frozen); re-point the edge or un-deprecate: \`${undeprecateTaskCommand(d)}\``).join("; ")}` : "";
80
95
  return {
81
96
  action: "blocked",
82
97
  code: "DEP",
83
- reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})`,
98
+ reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})${hint}`,
84
99
  unmetDeps: unmet
85
100
  };
86
101
  }
@@ -165,6 +180,13 @@ function preflightTask(input) {
165
180
  reason: `dev-next: blocked — do not launch pipeline; human/handover first (${input.wbs})`
166
181
  };
167
182
  }
183
+ if (isTaskFrozen(status)) {
184
+ return {
185
+ action: "skip",
186
+ code: "DEPRECATED",
187
+ reason: `dev-next: deprecated (frozen) — un-deprecate first: ${undeprecateTaskCommand(input.wbs)} (${input.wbs})`
188
+ };
189
+ }
168
190
  if (status === "todo" || status === "backlog") {
169
191
  const unmet = deps.filter((d) => {
170
192
  const st = (depStatuses[d] ?? "missing").toLowerCase();
@@ -332,7 +354,7 @@ function runPreflightCli(argv) {
332
354
  return { exitCode: 0, stdout: body, stderr: "" };
333
355
  }
334
356
  if (args.operation !== null) {
335
- const result2 = quickReadiness({
357
+ const result = quickReadiness({
336
358
  wbs: args.wbs,
337
359
  status: args.status,
338
360
  operation: args.operation,
@@ -343,14 +365,14 @@ function runPreflightCli(argv) {
343
365
  ...args.presentSections.length > 0 ? { presentSections: args.presentSections } : {},
344
366
  ...args.force ? { force: true } : {}
345
367
  });
346
- const runnable = result2.action === "runnable" || result2.action === "needs-refinement";
368
+ const runnable = result.action === "runnable" || result.action === "needs-refinement";
347
369
  if (args.json) {
348
- return { exitCode: runnable ? 0 : 2, stdout: `${JSON.stringify(result2, null, 2)}
370
+ return { exitCode: runnable ? 0 : 2, stdout: `${JSON.stringify(result, null, 2)}
349
371
  `, stderr: "" };
350
372
  }
351
373
  return {
352
374
  exitCode: runnable ? 0 : 2,
353
- stdout: `${result2.action}${result2.code ? ` ${result2.code}` : ""}: ${result2.reason}
375
+ stdout: `${result.action}${result.code ? ` ${result.code}` : ""}: ${result.reason}
354
376
  `,
355
377
  stderr: ""
356
378
  };
@@ -390,12 +412,12 @@ function runPreflightCli(argv) {
390
412
  process.exit(exitCode);
391
413
  }
392
414
  export {
393
- runPreflightCli,
394
- recoveryHint,
395
- quickReadiness,
396
- preflightTask,
397
- parsePreflightCliArgs,
398
- classifyFeaturePreflightFindings,
415
+ NON_ABORTING_PREFLIGHT_CODES,
399
416
  PREFLIGHT_CLI_USAGE,
400
- NON_ABORTING_PREFLIGHT_CODES
417
+ classifyFeaturePreflightFindings,
418
+ parsePreflightCliArgs,
419
+ preflightTask,
420
+ quickReadiness,
421
+ recoveryHint,
422
+ runPreflightCli
401
423
  };
@@ -206,11 +206,11 @@ function main(argv, options = {}) {
206
206
  process.exit(main(process.argv.slice(2)));
207
207
  }
208
208
  export {
209
- runStart,
210
- runStage,
211
- runCheck,
212
- parsePorcelainZ,
213
- main,
209
+ COMMIT_GUARD_USAGE,
214
210
  conflictMarkerLine,
215
- COMMIT_GUARD_USAGE
211
+ main,
212
+ parsePorcelainZ,
213
+ runCheck,
214
+ runStage,
215
+ runStart
216
216
  };
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  // @bun
3
- import { createRequire } from "node:module";
4
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
3
 
6
4
  // plugins/sp/scripts/daily-summary/daily-summary.ts
7
5
  import { spawn, spawnSync } from "child_process";
@@ -41,8 +39,8 @@ function resolveAppEntry(spurBin) {
41
39
  }
42
40
  return { entry, portable: true };
43
41
  }
44
- async function loadInlineApp(spurBin, resolveAppEntry2, required) {
45
- const { entry, portable } = resolveAppEntry2(spurBin);
42
+ async function loadInlineApp(spurBin, resolveAppEntry, required) {
43
+ const { entry, portable } = resolveAppEntry(spurBin);
46
44
  const app = await import(entry);
47
45
  const missing = portable ? required.filter((name) => typeof app[name] !== "function") : [];
48
46
  if (missing.length > 0) {
@@ -143,7 +141,7 @@ function getDateRange(dateStr) {
143
141
  return { start, end };
144
142
  }
145
143
  var defaultProcessSpawner = (cmd, args, env) => {
146
- return new Promise((resolve3, reject) => {
144
+ return new Promise((resolve, reject) => {
147
145
  try {
148
146
  const proc = spawn(cmd, args, {
149
147
  stdio: ["ignore", "pipe", "pipe"],
@@ -154,7 +152,7 @@ var defaultProcessSpawner = (cmd, args, env) => {
154
152
  proc.stdout.on("data", (chunk) => stdoutChunks.push(Buffer.from(chunk)));
155
153
  proc.stderr.on("data", (chunk) => stderrChunks.push(Buffer.from(chunk)));
156
154
  proc.on("close", (exitCode) => {
157
- resolve3({
155
+ resolve({
158
156
  stdout: Buffer.concat(stdoutChunks).toString("utf8"),
159
157
  stderr: Buffer.concat(stderrChunks).toString("utf8"),
160
158
  exitCode: exitCode ?? 0
@@ -319,12 +317,12 @@ Please provide the following (press Enter to skip):
319
317
  chunks.push(chunk);
320
318
  }
321
319
  const buffered = Buffer.concat(chunks).toString("utf-8");
322
- const [learnings2 = "", issuesFixed2 = "", pending2 = ""] = buffered.split(`
320
+ const [learnings = "", issuesFixed = "", pending = ""] = buffered.split(`
323
321
  `);
324
322
  return {
325
- learnings: learnings2.trim(),
326
- issuesFixed: issuesFixed2.trim(),
327
- pending: pending2.trim()
323
+ learnings: learnings.trim(),
324
+ issuesFixed: issuesFixed.trim(),
325
+ pending: pending.trim()
328
326
  };
329
327
  }
330
328
  const readline = await import("readline");
@@ -332,9 +330,9 @@ Please provide the following (press Enter to skip):
332
330
  input: process.stdin,
333
331
  output: process.stdout
334
332
  });
335
- const question = (prompt) => new Promise((resolve3) => {
333
+ const question = (prompt) => new Promise((resolve) => {
336
334
  rl.question(prompt, (answer) => {
337
- resolve3(answer.trim());
335
+ resolve(answer.trim());
338
336
  });
339
337
  });
340
338
  const learnings = await question(`1. What did you learn today? (optional)
@@ -608,21 +606,21 @@ ${markdown}
608
606
  });
609
607
  }
610
608
  export {
611
- yesterdayLocal,
612
- writeSummary,
613
- todayLocal,
614
- setProcessSpawner,
615
- promptUser,
616
- printUsage,
617
- parseArgs,
618
- main,
619
- getSpurHistoryHealth,
620
- getGitCommits,
621
- getDateRange,
622
- getCcusageData,
623
- generateMarkdown,
624
- ensureDir,
625
- enableLogger,
609
+ buildDailySummary,
626
610
  defaultProcessSpawner,
627
- buildDailySummary
611
+ enableLogger,
612
+ ensureDir,
613
+ generateMarkdown,
614
+ getCcusageData,
615
+ getDateRange,
616
+ getGitCommits,
617
+ getSpurHistoryHealth,
618
+ main,
619
+ parseArgs,
620
+ printUsage,
621
+ promptUser,
622
+ setProcessSpawner,
623
+ todayLocal,
624
+ writeSummary,
625
+ yesterdayLocal
628
626
  };
@@ -196,16 +196,16 @@ function runCli(argv) {
196
196
  process.exit(exitCode);
197
197
  }
198
198
  export {
199
- runCli,
200
- parseCliArgs,
201
- isImplementHeavyStep,
202
- hasMutatingFixMode,
203
- evaluateDogfoodGate,
204
- detectPipelineDriving,
205
- detectImplementHeavy,
206
- PIPELINE_TOKENS,
207
- PIPELINE_DRIVING_REFUSE_MESSAGE,
208
- MUTATING_FIX_REFUSE_MESSAGE,
199
+ CLI_USAGE,
209
200
  IMPLEMENT_HEAVY_ADVISORY_MESSAGE,
210
- CLI_USAGE
201
+ MUTATING_FIX_REFUSE_MESSAGE,
202
+ PIPELINE_DRIVING_REFUSE_MESSAGE,
203
+ PIPELINE_TOKENS,
204
+ detectImplementHeavy,
205
+ detectPipelineDriving,
206
+ evaluateDogfoodGate,
207
+ hasMutatingFixMode,
208
+ isImplementHeavyStep,
209
+ parseCliArgs,
210
+ runCli
211
211
  };
@@ -274,9 +274,9 @@ function mainCli(argv = process.argv.slice(2)) {
274
274
  process.exit(mainCli());
275
275
  }
276
276
  export {
277
- validateReport,
278
- runValidateCli,
279
- parseValidateCliArgs,
277
+ VALIDATE_CLI_USAGE,
280
278
  mainCli,
281
- VALIDATE_CLI_USAGE
279
+ parseValidateCliArgs,
280
+ runValidateCli,
281
+ validateReport
282
282
  };
@@ -2,6 +2,7 @@
2
2
  // @bun
3
3
 
4
4
  // plugins/sp/scripts/feature-verification-steps.ts
5
+ import { spawnSync } from "child_process";
5
6
  import { existsSync } from "fs";
6
7
  import { fileURLToPath } from "url";
7
8
 
@@ -42,17 +43,27 @@ async function requireModule(entry, mode) {
42
43
  }
43
44
  return mod;
44
45
  }
45
- async function verify(featureId, runId, cmdOverride, spurBin, mode) {
46
+ async function verify(featureId, runId, cmdOverride, spurBin, mode, confidenceOverride, ackLowConfidence) {
46
47
  const mod = await loadModule(spurBin, mode);
47
48
  const result = await mod.runFeatureVerification({
48
49
  workdir: process.cwd(),
49
50
  featureId,
50
51
  runId,
51
- cmdOverride
52
+ cmdOverride,
53
+ confidenceOverride,
54
+ ackLowConfidence
52
55
  });
53
56
  console.log(`feature-verification-steps: ${featureId} ${result.message}`);
54
57
  }
55
58
  function main() {
59
+ if (!process.versions.bun) {
60
+ const child = spawnSync("bun", [fileURLToPath(import.meta.url), ...process.argv.slice(2)], {
61
+ stdio: "inherit"
62
+ });
63
+ if (child.error)
64
+ console.error(`feature-verification-steps requires Bun on PATH: ${child.error.message}`);
65
+ process.exit(child.status ?? 1);
66
+ }
56
67
  const args = process.argv.slice(2);
57
68
  const command = args[0];
58
69
  const flag = (name) => {
@@ -60,10 +71,10 @@ function main() {
60
71
  return i >= 0 && i + 1 < args.length ? args[i + 1] ?? "" : "";
61
72
  };
62
73
  if (command !== "verify") {
63
- console.error("Usage: feature-verification-steps.ts verify --feature-id <id> --run-id <id> [--cmd <command>]");
74
+ console.error("Usage: feature-verification-steps.ts verify --feature-id <id> --run-id <id> [--cmd <command>] [--confidence HIGH|MEDIUM|LOW] [--ack-low-confidence]");
64
75
  process.exit(2);
65
76
  }
66
- verify(flag("--feature-id") || getEnvVar("featureId") || "", flag("--run-id") || getEnvVar("__runId") || "", flag("--cmd") || getEnvVar("verificationCmd") || "", flag("--spur-bin") || getEnvVar("spurBin") || "", resolveModuleMode(flag("--module-mode"))).then(() => process.exit(0)).catch((err) => {
77
+ verify(flag("--feature-id") || getEnvVar("featureId") || "", flag("--run-id") || getEnvVar("__runId") || "", flag("--cmd") || getEnvVar("verificationCmd") || "", flag("--spur-bin") || getEnvVar("spurBin") || "", resolveModuleMode(flag("--module-mode")), flag("--confidence") || getEnvVar("confidence") || "", args.includes("--ack-low-confidence") || getEnvVar("ackLowConfidence") === "true").then(() => process.exit(0)).catch((err) => {
67
78
  console.error(`feature-verification-steps: ${String(err)}`);
68
79
  process.exit(1);
69
80
  });