@gobing-ai/spur 0.4.2 → 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 (168) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/decisions/maker-liveness.yaml +39 -0
  3. package/config/plugin-scripts.json +0 -4
  4. package/config/rules/README.md +1 -0
  5. package/config/rules/docs/sourced-api-claims.yaml +56 -12
  6. package/config/rules/docs/verdict-confidence.yaml +68 -0
  7. package/config/rules/quality/coverage-gate.yaml +13 -0
  8. package/config/rules/recommended-pre-check.yaml +6 -2
  9. package/config/rules/strict/runtime-boundaries.yaml +10 -0
  10. package/config/rules/surface/check-cli-surface.yaml +1 -3
  11. package/config/script-placement-baseline.json +1 -19
  12. package/config/templates/AGENTS.md +7 -0
  13. package/config/workflows/feature-lifecycle.yaml +46 -1
  14. package/config/workflows/feature-verification.yaml +20 -1
  15. package/config/workflows/task-lifecycle.yaml +50 -0
  16. package/config/workflows/task-pipeline.yaml +15 -9
  17. package/package.json +10 -10
  18. package/plugins/sp/README.md +22 -16
  19. package/plugins/sp/agents/super-reviewer.md +46 -0
  20. package/plugins/sp/bunfig.toml +8 -5
  21. package/plugins/sp/commands/dev-changelog.md +1 -1
  22. package/plugins/sp/commands/dev-gitmsg.md +6 -2
  23. package/plugins/sp/commands/dev-handover.md +1 -0
  24. package/plugins/sp/commands/dev-job-dump.md +2 -1
  25. package/plugins/sp/commands/dev-refine.md +14 -2
  26. package/plugins/sp/commands/dev-refineall.md +7 -1
  27. package/plugins/sp/commands/dev-review.md +1 -0
  28. package/plugins/sp/hooks/careful-guard.ts +1 -1
  29. package/plugins/sp/hooks/context-post-tool.ts +7 -427
  30. package/plugins/sp/hooks/context-session-start.ts +5 -213
  31. package/plugins/sp/hooks/context-session-stop.ts +5 -191
  32. package/plugins/sp/hooks/pi/guard-extension.ts +6 -5
  33. package/plugins/sp/hooks/task-write-guard.ts +1 -1
  34. package/plugins/sp/{hooks → lib}/agent-hint.ts +1 -1
  35. package/plugins/sp/lib/context-post-tool.ts +432 -0
  36. package/plugins/sp/lib/context-session-start.ts +214 -0
  37. package/plugins/sp/lib/context-session-stop.ts +196 -0
  38. package/plugins/sp/lib/dogfood-pipeline.ts +265 -0
  39. package/plugins/sp/lib/dogfood-report.ts +278 -0
  40. package/plugins/sp/lib/history-anatomy.generated.mjs +18 -18
  41. package/plugins/sp/lib/idea-handoff.generated.mjs +91 -91
  42. package/plugins/sp/lib/inline-run-app.ts +40 -0
  43. package/plugins/sp/lib/inline-run.generated.d.mts +3 -3
  44. package/plugins/sp/lib/inline-run.generated.mjs +1425 -212
  45. package/plugins/sp/lib/plan-projection.generated.d.mts +150 -0
  46. package/plugins/sp/lib/plan-projection.generated.mjs +623 -9
  47. package/plugins/sp/lib/quality-gate.generated.d.mts +1 -0
  48. package/plugins/sp/lib/quality-gate.generated.mjs +13809 -13969
  49. package/plugins/sp/lib/residual-scan.generated.d.mts +20 -1
  50. package/plugins/sp/lib/residual-scan.generated.mjs +120 -25
  51. package/plugins/sp/lib/run-summary-core.ts +34 -370
  52. package/plugins/sp/lib/step-profile.generated.mjs +8 -8
  53. package/plugins/sp/lib/transcript.ts +37 -228
  54. package/plugins/sp/plugin.json +1 -1
  55. package/plugins/sp/scripts/batch-plan.mjs +619 -11
  56. package/plugins/sp/scripts/batch-preflight.mjs +57 -13
  57. package/plugins/sp/scripts/batch-preflight.ts +35 -295
  58. package/plugins/sp/scripts/commit-guard.mjs +6 -6
  59. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +127 -108
  60. package/plugins/sp/scripts/daily-summary/daily-summary.ts +111 -104
  61. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +13 -12
  62. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +7 -265
  63. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +7 -4
  64. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +6 -272
  65. package/plugins/sp/scripts/feature-verification-steps.mjs +20 -125
  66. package/plugins/sp/scripts/feature-verification-steps.ts +57 -181
  67. package/plugins/sp/scripts/history-anatomy-cache.mjs +19 -19
  68. package/plugins/sp/scripts/idea-handoff.mjs +2 -2
  69. package/plugins/sp/scripts/inline-run-dispatch.mjs +25 -24
  70. package/plugins/sp/scripts/inline-run-dispatch.ts +1 -21
  71. package/plugins/sp/scripts/inline-run-setup.mjs +164 -137
  72. package/plugins/sp/scripts/inline-run-setup.ts +1 -21
  73. package/plugins/sp/scripts/persist-out-check.mjs +5 -5
  74. package/plugins/sp/scripts/pr-reviewing.mjs +13 -13
  75. package/plugins/sp/scripts/quality-gate.mjs +13730 -13887
  76. package/plugins/sp/scripts/residual-scan.mjs +129 -31
  77. package/plugins/sp/scripts/residual-scan.ts +16 -13
  78. package/plugins/sp/scripts/run-summary.mjs +166 -112
  79. package/plugins/sp/scripts/run-summary.ts +7 -5
  80. package/plugins/sp/scripts/script-root.mjs +6 -6
  81. package/plugins/sp/scripts/session-timeline.mjs +62 -35
  82. package/plugins/sp/scripts/task-diffstat.mjs +19 -9
  83. package/plugins/sp/scripts/task-diffstat.ts +8 -39
  84. package/plugins/sp/scripts/workflow-step-profile.mjs +12 -12
  85. package/plugins/sp/scripts/wrapup-drift-probe.mjs +9 -9
  86. package/plugins/sp/scripts/wrapup-steps.mjs +25 -25
  87. package/plugins/sp/skills/code-implementation/SKILL.md +7 -1
  88. package/plugins/sp/skills/code-verification/references/secu-review.md +2 -1
  89. package/plugins/sp/skills/code-verification/references/verdict-schema.md +4 -0
  90. package/plugins/sp/skills/dogfood-testing/SKILL.md +2 -2
  91. package/plugins/sp/skills/next-feature/SKILL.md +1 -1
  92. package/plugins/sp/skills/next-router/SKILL.md +3 -1
  93. package/plugins/sp/skills/next-router/references/routing-table.md +9 -3
  94. package/plugins/sp/skills/parallel-execution/references/dispatch-surface.md +1 -1
  95. package/plugins/sp/skills/source-driven-development/SKILL.md +7 -4
  96. package/plugins/sp/skills/spur-cli/references/builder.md +3 -2
  97. package/plugins/sp/skills/spur-cli/references/decision.md +32 -7
  98. package/plugins/sp/skills/spur-cli/references/features.md +46 -5
  99. package/plugins/sp/skills/spur-cli/references/tasks.md +24 -4
  100. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +8 -1
  101. package/plugins/sp/skills/spur-dev/references/dev-operations.md +37 -11
  102. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +1 -1
  103. package/plugins/sp/skills/spur-dev/references/execution-batch.md +19 -6
  104. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +22 -0
  105. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +14 -0
  106. package/plugins/sp/skills/spur-dev/references/flag-glossary.md +4 -2
  107. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +49 -12
  108. package/schemas/section-matrix.schema.json +3 -2
  109. package/spur.js +66591 -73262
  110. package/web/_astro/{BoardApp.B3GQZvj8.js → BoardApp.Niubpe6z.js} +1 -1
  111. package/web/_astro/{BoardApp.BT7L2kSb.js → BoardApp.hF0RAIrg.js} +150 -150
  112. package/web/_astro/TaskDetail.DJpapt38.js +1 -0
  113. package/web/_astro/{arc.vaT7tPOB.js → arc.BgKcK0i8.js} +1 -1
  114. package/web/_astro/{architectureDiagram-3BPJPVTR.DRMmTjkN.js → architectureDiagram-3BPJPVTR.ChP-Ig0V.js} +1 -1
  115. package/web/_astro/{blockDiagram-GPEHLZMM.cla0LO_q.js → blockDiagram-GPEHLZMM.BVaCBoeN.js} +1 -1
  116. package/web/_astro/{c4Diagram-AAUBKEIU.D4YAIBKn.js → c4Diagram-AAUBKEIU.CHSDpEO5.js} +1 -1
  117. package/web/_astro/channel.DkpvN2tD.js +1 -0
  118. package/web/_astro/{chunk-2J33WTMH.BrSJbMRZ.js → chunk-2J33WTMH.MYRcYVRa.js} +1 -1
  119. package/web/_astro/{chunk-4BX2VUAB.CuLMgKF5.js → chunk-4BX2VUAB.S5j22Zqa.js} +1 -1
  120. package/web/_astro/{chunk-55IACEB6.zkvgu54F.js → chunk-55IACEB6.gciRPvmZ.js} +1 -1
  121. package/web/_astro/{chunk-727SXJPM.DxWkACh8.js → chunk-727SXJPM.CdqrSQkX.js} +1 -1
  122. package/web/_astro/{chunk-AQP2D5EJ.42XrmIlH.js → chunk-AQP2D5EJ.CyokdHT3.js} +1 -1
  123. package/web/_astro/{chunk-FMBD7UC4.Bao2wMK9.js → chunk-FMBD7UC4.Bg9KJhjW.js} +1 -1
  124. package/web/_astro/{chunk-ND2GUHAM.BhydojD1.js → chunk-ND2GUHAM.1gxxI-vD.js} +1 -1
  125. package/web/_astro/{chunk-QZHKN3VN.CmZ82g-H.js → chunk-QZHKN3VN.DDEYtCKU.js} +1 -1
  126. package/web/_astro/{classDiagram-4FO5ZUOK.Blr83Gt_.js → classDiagram-4FO5ZUOK.Ch3zi37T.js} +1 -1
  127. package/web/_astro/{classDiagram-v2-Q7XG4LA2.Blr83Gt_.js → classDiagram-v2-Q7XG4LA2.Ch3zi37T.js} +1 -1
  128. package/web/_astro/{cose-bilkent-S5V4N54A.qmfv0SPn.js → cose-bilkent-S5V4N54A.D2q5AINI.js} +1 -1
  129. package/web/_astro/{cynefin-OW5HDTMX.BtELhXPR.js → cynefin-OW5HDTMX.BA9kC2YT.js} +1 -1
  130. package/web/_astro/{dagre-BM42HDAG.AWmaAvXo.js → dagre-BM42HDAG.CHO08nRp.js} +1 -1
  131. package/web/_astro/{diagram-2AECGRRQ.DdH34KDi.js → diagram-2AECGRRQ.BfFQt0LW.js} +1 -1
  132. package/web/_astro/{diagram-5GNKFQAL.BmNPix1k.js → diagram-5GNKFQAL.CHMwMTe4.js} +1 -1
  133. package/web/_astro/{diagram-KO2AKTUF.BNQd_Ppo.js → diagram-KO2AKTUF.BJ-7gK-w.js} +1 -1
  134. package/web/_astro/{diagram-LMA3HP47.BAPgDi0e.js → diagram-LMA3HP47.C5xqcc0t.js} +1 -1
  135. package/web/_astro/{diagram-OG6HWLK6.DZ4zyLdC.js → diagram-OG6HWLK6.C2pQjfrC.js} +1 -1
  136. package/web/_astro/{erDiagram-TEJ5UH35.Q1TySMmw.js → erDiagram-TEJ5UH35._FoNyxgC.js} +1 -1
  137. package/web/_astro/{flowDiagram-I6XJVG4X.BXhFBicG.js → flowDiagram-I6XJVG4X.B_ENkT2I.js} +1 -1
  138. package/web/_astro/{ganttDiagram-6RSMTGT7.vHSgcuo2.js → ganttDiagram-6RSMTGT7.B3q8h77C.js} +1 -1
  139. package/web/_astro/{gitGraphDiagram-PVQCEYII.Bi1f73C7.js → gitGraphDiagram-PVQCEYII.QWZFDboZ.js} +1 -1
  140. package/web/_astro/index.BFyW13JJ.css +1 -0
  141. package/web/_astro/{infoDiagram-5YYISTIA.DPvXZEWm.js → infoDiagram-5YYISTIA.Cs6eEc92.js} +1 -1
  142. package/web/_astro/{ishikawaDiagram-YF4QCWOH.Cz829fsV.js → ishikawaDiagram-YF4QCWOH.BU9DJMHA.js} +1 -1
  143. package/web/_astro/{journeyDiagram-JHISSGLW.enRnB9Me.js → journeyDiagram-JHISSGLW.D35k6Jpe.js} +1 -1
  144. package/web/_astro/{kanban-definition-UN3LZRKU.DnTNk6d2.js → kanban-definition-UN3LZRKU.DBqcAT4m.js} +1 -1
  145. package/web/_astro/{linear.BzGnBwNL.js → linear.D-LSNK8W.js} +1 -1
  146. package/web/_astro/{mermaid.core.CSbTpIVz.js → mermaid.core.CSruWVOa.js} +4 -4
  147. package/web/_astro/{mindmap-definition-RKZ34NQL.DGJDy8zT.js → mindmap-definition-RKZ34NQL.BiFaUsfj.js} +1 -1
  148. package/web/_astro/{pieDiagram-4H26LBE5.HSPdi5_8.js → pieDiagram-4H26LBE5.D1KTKqb9.js} +1 -1
  149. package/web/_astro/{quadrantDiagram-W4KKPZXB.6Gv8mz2Y.js → quadrantDiagram-W4KKPZXB.DBHflsMG.js} +1 -1
  150. package/web/_astro/{requirementDiagram-4Y6WPE33.CnBCtFwa.js → requirementDiagram-4Y6WPE33.hwX9CNdk.js} +1 -1
  151. package/web/_astro/{sankeyDiagram-5OEKKPKP.5M8VWAu8.js → sankeyDiagram-5OEKKPKP.DkI8mJiE.js} +1 -1
  152. package/web/_astro/{sequenceDiagram-3UESZ5HK.DN41XN-U.js → sequenceDiagram-3UESZ5HK.DMz7jBDx.js} +1 -1
  153. package/web/_astro/{stateDiagram-AJRCARHV.ErOO3W8j.js → stateDiagram-AJRCARHV.B6Ef2xJ1.js} +1 -1
  154. package/web/_astro/{stateDiagram-v2-BHNVJYJU.B_ZHICM-.js → stateDiagram-v2-BHNVJYJU.CgoaufB9.js} +1 -1
  155. package/web/_astro/{timeline-definition-PNZ67QCA.CWdosr07.js → timeline-definition-PNZ67QCA.B3c8h1t-.js} +1 -1
  156. package/web/_astro/{vennDiagram-CIIHVFJN.BHzYjNV5.js → vennDiagram-CIIHVFJN.BdHPAxAb.js} +1 -1
  157. package/web/_astro/{wardleyDiagram-YWT4CUSO.h8x4IwPX.js → wardleyDiagram-YWT4CUSO.BrJXPPAj.js} +1 -1
  158. package/web/_astro/{xychartDiagram-2RQKCTM6.RTapW2X8.js → xychartDiagram-2RQKCTM6.CMH5yFsV.js} +1 -1
  159. package/web/index.html +2 -2
  160. package/web/spur_logo.svg +1 -1
  161. package/config/proportional-route-table.ts +0 -155
  162. package/plugins/sp/scripts/daily-summary/logger.ts +0 -28
  163. package/web/_astro/TaskDetail.Cxov1_N_.js +0 -1
  164. package/web/_astro/channel.D4nG9YLf.js +0 -1
  165. package/web/_astro/index.BhSwN8yY.css +0 -1
  166. /package/plugins/sp/{hooks → lib}/context-owner.ts +0 -0
  167. /package/plugins/sp/{hooks → lib}/destructive-policy.ts +0 -0
  168. /package/plugins/sp/{hooks → lib}/task-file-policy.ts +0 -0
@@ -4,9 +4,10 @@
4
4
  * (`--no-summary` skips it). Inputs: one or more `spur workflow progress <run-id> --json` files
5
5
  * (`--progress [<label>=]<file>`) and the host transcript (resolved like session-timeline, or
6
6
  * `--transcript`), OR the close-written per-run summary files (`--rollup [<label>=]<file>`, 1146 R3).
7
- * Measurement and rendering live in `../lib/run-summary-core` (ADR-130 glue budget); this script is
8
- * argv parsing and dispatch. Reporting only: no transcript or no timed attempts → an `n/a` result,
9
- * exit 0. Node builtins only.
7
+ * Measurement lives in `packages/app/src/workflow/run-summary.ts` (reached through the generated
8
+ * pure bundle entry); presentation and host IO live in `../lib/run-summary-core` (ADR-130 glue
9
+ * budget). This script is argv parsing and dispatch. Reporting only: no transcript or no timed
10
+ * attempts → an `n/a` result, exit 0. Node builtins only.
10
11
  */
11
12
  import { readFileSync } from 'node:fs';
12
13
  import { getEnvVars } from '../lib/env';
@@ -18,7 +19,7 @@ import {
18
19
  renderRollupMarkdown,
19
20
  renderSummaryMarkdown,
20
21
  } from '../lib/run-summary-core';
21
- import { resolveTranscript } from '../lib/transcript';
22
+ import { parseRows, resolveTranscript } from '../lib/transcript';
22
23
 
23
24
  // Re-exported so the suite and any consumer keep one import path for the whole surface.
24
25
  export * from '../lib/run-summary-core';
@@ -90,7 +91,8 @@ export function main(
90
91
  const [label, file] = eq > 0 ? [arg.slice(0, eq), arg.slice(eq + 1)] : [undefined, arg];
91
92
  return { label, progress: JSON.parse(readFileSync(file, 'utf8')) as Progress };
92
93
  });
93
- const summary = buildRunSummary(readFileSync(resolved.path, 'utf8').split('\n'), inputs, since);
94
+ const { rows, skippedLines } = parseRows(readFileSync(resolved.path, 'utf8').split('\n'));
95
+ const summary = buildRunSummary(rows, inputs, { since, skippedLines });
94
96
  summary.gate = gateFromReceipt(process.cwd(), inputs[0]?.progress.runId ?? '');
95
97
  write(
96
98
  `${markdown ? renderSummaryMarkdown(summary) : JSON.stringify({ ...summary, transcript: resolved.path })}\n`,
@@ -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
  };
@@ -13,9 +13,12 @@ function getEnvVars() {
13
13
  import { existsSync, readdirSync } from "node:fs";
14
14
  import { homedir } from "node:os";
15
15
  import { join } from "node:path";
16
- var SESSION_ID = /^[A-Za-z0-9_-]+$/;
17
- var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
18
- var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
16
+
17
+ // plugins/sp/lib/plan-projection.generated.mjs
18
+ var NON_ABORTING_PREFLIGHT_CODES = new Set([
19
+ "L4.scenario-unverified",
20
+ "L4.verifying-incomplete-tasks"
21
+ ]);
19
22
  var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
20
23
  function formatDuration(ms) {
21
24
  const s = Math.round(ms / 1000);
@@ -35,6 +38,23 @@ function formatTokenSplit(t) {
35
38
  const nonCached = t.input + t.cacheCreate + t.output;
36
39
  return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
37
40
  }
41
+ var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
42
+ function firstTextContent(row) {
43
+ const content = row.message?.content;
44
+ if (typeof content === "string")
45
+ return content;
46
+ if (!Array.isArray(content))
47
+ return;
48
+ return content.find((b) => b.type === "text")?.text;
49
+ }
50
+ function skillInvocationPrompt(text) {
51
+ const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
52
+ if (name === undefined)
53
+ return;
54
+ const close = text.lastIndexOf("</skill>");
55
+ const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
56
+ return args === "" ? `/${name}` : `/${name} ${args}`;
57
+ }
38
58
  function promptText(row) {
39
59
  if (row.type === "custom_message") {
40
60
  if (row.display === false)
@@ -59,22 +79,6 @@ function promptText(row) {
59
79
  return;
60
80
  return content.find((b) => b.type === "text")?.text;
61
81
  }
62
- function firstTextContent(row) {
63
- const content = row.message?.content;
64
- if (typeof content === "string")
65
- return content;
66
- if (!Array.isArray(content))
67
- return;
68
- return content.find((b) => b.type === "text")?.text;
69
- }
70
- function skillInvocationPrompt(text) {
71
- const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
72
- if (name === undefined)
73
- return;
74
- const close = text.lastIndexOf("</skill>");
75
- const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
76
- return args === "" ? `/${name}` : `/${name} ${args}`;
77
- }
78
82
  function sumTokens(all) {
79
83
  const total = zeroTokens();
80
84
  for (const t of all)
@@ -103,21 +107,21 @@ function accumulate(acc, row, ts) {
103
107
  if (row.type === "message") {
104
108
  const m = row.message;
105
109
  if (m?.role === "assistant") {
106
- const blocks2 = Array.isArray(m.content) ? m.content : [];
107
- for (const b of blocks2) {
110
+ const blocks = Array.isArray(m.content) ? m.content : [];
111
+ for (const b of blocks) {
108
112
  if (b.type === "toolCall" && b.id) {
109
113
  acc.toolIds.add(b.id);
110
114
  if (b.name && OPERATOR_TOOLS.has(b.name))
111
115
  acc.asks.set(b.id, ts);
112
116
  }
113
117
  }
114
- const u2 = m.usage;
115
- if (u2 && row.id)
118
+ const u = m.usage;
119
+ if (u && row.id)
116
120
  acc.messages.set(row.id, {
117
- input: u2.input ?? 0,
118
- cacheCreate: u2.cacheWrite ?? 0,
119
- cacheRead: u2.cacheRead ?? 0,
120
- output: u2.output ?? 0
121
+ input: u.input ?? 0,
122
+ cacheCreate: u.cacheWrite ?? 0,
123
+ cacheRead: u.cacheRead ?? 0,
124
+ output: u.output ?? 0
121
125
  });
122
126
  } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
123
127
  acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
@@ -146,6 +150,29 @@ function accumulate(acc, row, ts) {
146
150
  });
147
151
  }
148
152
  }
153
+ var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
154
+ var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
155
+ var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
156
+ var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
157
+ var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
158
+ function globToRegex(glob) {
159
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
160
+ return new RegExp(`^${escaped}$`, "s");
161
+ }
162
+ var SENSITIVE_MATCHERS = [
163
+ ...SENSITIVE_PREFIXES.map((prefix) => ({
164
+ label: prefix,
165
+ test: (path) => path.startsWith(prefix)
166
+ })),
167
+ ...SENSITIVE_GLOBS.map((glob) => {
168
+ const regex = globToRegex(glob);
169
+ return { label: glob, test: (path) => regex.test(path) };
170
+ })
171
+ ];
172
+
173
+ // plugins/sp/lib/transcript.ts
174
+ var SESSION_ID = /^[A-Za-z0-9_-]+$/;
175
+ var PI_ROLES = new Set(["user", "assistant", "toolResult"]);
149
176
  function sniffFormat(rows) {
150
177
  for (const row of rows) {
151
178
  if (row.type === "message" && PI_ROLES.has(row.message?.role ?? ""))
@@ -320,13 +347,13 @@ function main(argv, env = getEnvVars(), write = (s) => process.stdout.write(s),
320
347
  }
321
348
  process.exit(main(process.argv.slice(2)));
322
349
  export {
323
- zeroSegmentReason,
324
- resolveTranscript,
325
- parseGroups,
326
- main,
327
- formatTokens,
328
- formatTokenSplit,
329
- formatDuration,
350
+ SESSION_TIMELINE_USAGE,
330
351
  buildTimeline,
331
- SESSION_TIMELINE_USAGE
352
+ formatDuration,
353
+ formatTokenSplit,
354
+ formatTokens,
355
+ main,
356
+ parseGroups,
357
+ resolveTranscript,
358
+ zeroSegmentReason
332
359
  };
@@ -11,7 +11,15 @@ function getEnvVars() {
11
11
  return process.env;
12
12
  }
13
13
 
14
- // plugins/sp/scripts/task-diffstat.ts
14
+ // plugins/sp/lib/plan-projection.generated.mjs
15
+ var NON_ABORTING_PREFLIGHT_CODES = new Set([
16
+ "L4.scenario-unverified",
17
+ "L4.verifying-incomplete-tasks"
18
+ ]);
19
+ var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
20
+ var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
21
+ var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
22
+ var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
15
23
  var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
16
24
  var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
17
25
  function globToRegex(glob) {
@@ -32,6 +40,8 @@ function sensitiveReasonForPath(path) {
32
40
  const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
33
41
  return match ? `matches sensitive pattern ${match.label}` : null;
34
42
  }
43
+
44
+ // plugins/sp/scripts/task-diffstat.ts
35
45
  function defaultGitRunner(cwd) {
36
46
  return (args) => {
37
47
  const run = spawnSync("git", args, { cwd, encoding: "utf8" });
@@ -74,9 +84,9 @@ function runDiffstat(env, options = {}, git = defaultGitRunner(options.cwd)) {
74
84
  const failSafe = (reason) => {
75
85
  process.stderr.write(`task-diffstat: ${reason} \u2014 failing safe (sensitive)
76
86
  `);
77
- const row2 = { files: 0, insertions: 0, deletions: 0, paths: [], sensitive: true };
78
- writeArtifact(abs(relResult), row2);
79
- 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 };
80
90
  };
81
91
  let stdoutIsArtifact = false;
82
92
  try {
@@ -162,10 +172,10 @@ function main(argv, env = getEnvVars(), options = {}) {
162
172
  process.exit(main(process.argv.slice(2)));
163
173
  }
164
174
  export {
165
- sensitiveReasonForPath,
166
- runDiffstat,
167
- main,
168
- TASK_DIFFSTAT_USAGE,
175
+ SENSITIVE_GLOBS,
169
176
  SENSITIVE_PREFIXES,
170
- SENSITIVE_GLOBS
177
+ TASK_DIFFSTAT_USAGE,
178
+ main,
179
+ runDiffstat,
180
+ sensitiveReasonForPath
171
181
  };
@@ -65,45 +65,14 @@ export interface DiffstatResult extends Diffstat {
65
65
  exitCode: number;
66
66
  }
67
67
 
68
- /**
69
- * Sensitive paths (0943 R2c): any changed path under one of these forces the safety lane
70
- * before the `task-triage` decide runs — sensitive work is never lane-negotiated with a
71
- * model. Frozen list: `drizzle/`, `packages/config/`, auth-prefixed basenames anywhere
72
- * under the server src tree, `*secret*` basenames, `.github/`, `plugins/sp/hooks/`, and
73
- * any `*.sql` — spelled out in SENSITIVE_PREFIXES / SENSITIVE_GLOBS below (kept as data,
74
- * not prose, so the `*` / `/` sequences survive the doc comment).
75
- */
76
- export const SENSITIVE_PREFIXES = ['drizzle/', 'packages/config/', '.github/', 'plugins/sp/hooks/'] as const;
77
- export const SENSITIVE_GLOBS = ['apps/server/src/**/auth*', '**/*secret*', '**/*.sql'] as const;
78
-
79
- // Glob → RegExp: `**` + `/` matches any-or-no directories, bare `**` any characters, `*` within one segment.
80
- function globToRegex(glob: string): RegExp {
81
- const escaped = glob
82
- .replace(/[.+^${}()|[\]\\]/g, '\\$&')
83
- .replaceAll('**/', '\u0000')
84
- .replaceAll('**', '\u0001')
85
- .replaceAll('*', '[^/]*')
86
- .replaceAll('\u0000', '(?:.*/)?')
87
- .replaceAll('\u0001', '.*');
88
- return new RegExp(`^${escaped}$`, 's');
89
- }
90
-
91
- const SENSITIVE_MATCHERS: Array<{ label: string; test: (path: string) => boolean }> = [
92
- ...SENSITIVE_PREFIXES.map((prefix) => ({
93
- label: prefix,
94
- test: (path: string): boolean => path.startsWith(prefix),
95
- })),
96
- ...SENSITIVE_GLOBS.map((glob) => {
97
- const regex = globToRegex(glob);
98
- return { label: glob, test: (path: string): boolean => regex.test(path) };
99
- }),
100
- ];
101
-
102
- /** Sensitive-pattern match for one path, or null — mirrors wrapup-drift-probe's driftReasonForPath. */
103
- export function sensitiveReasonForPath(path: string): string | null {
104
- const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
105
- return match ? `matches sensitive pattern ${match.label}` : null;
106
- }
68
+ // Task 1176: the sensitive-path policy (0943 R2c) moved to the workflow/task owner —
69
+ // `packages/app/src/workflow/task-triage.ts`, reached through the generated pure bundle. That policy
70
+ // still forces the safety lane for any changed path under the frozen sensitive list before the
71
+ // `task-triage` decide runs, so sensitive work is never lane-negotiated with a model; this entry
72
+ // keeps git capture and the output artifact, and re-exports the policy for existing callers.
73
+ import { SENSITIVE_GLOBS, SENSITIVE_PREFIXES, sensitiveReasonForPath } from '../lib/plan-projection.generated.mjs';
74
+
75
+ export { SENSITIVE_GLOBS, SENSITIVE_PREFIXES, sensitiveReasonForPath };
107
76
 
108
77
  function defaultGitRunner(cwd?: string): GitRunner {
109
78
  return (args: string[]): GitRunResult => {
@@ -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
  };
@@ -82,6 +82,12 @@ not have and work you were not asked to do.
82
82
  - **Never implement a requirement belonging to another WBS**, even when it looks like a
83
83
  prerequisite. If the target genuinely cannot proceed without it, stop and say so — a blocked task
84
84
  is a cheap finding; a two-task diff costs the reverts.
85
+ - **Flip the target's `## Plan` boxes as each step completes.** They are this pass's progress
86
+ ledger, and the record-time residual sweep treats an *unchecked* Plan box as blocking: it
87
+ downgrades the verdict to PARTIAL and closes the `done` gate
88
+ (`docs/design/task-residual-sweep.md`), which costs a remediation cycle and a manual re-record.
89
+ Leave every finished step ticked through `spur task update <wbs> --section Plan --from-file`, and
90
+ if you deliberately skipped one, say so in the Solution change map.
85
91
 
86
92
  Why this is a rule and not a nicety: driving task 0486 lost several hours to exactly this. Task 0485
87
93
  was committed but still `todo` in the tree, and two different executors (omp run `ca130182`, claude
@@ -165,7 +171,7 @@ that single full gate.
165
171
  1. The **changed-path matrix** below (narrow behavior tests + affected consumers);
166
172
  2. the resolved `quality-gate` script in `light` mode (source-repo probe, else the installed twin) (biome on changed files, per-workspace typecheck, rule presets `rules:pre`/`rules:post`, related tests).
167
173
 
168
- Neither step takes the host-wide full-gate lock. The full gate is named only for the pipeline's `test` and `test-recheck` hops.
174
+ Neither step takes the full project gate. The full gate is named only for the pipeline's `test` and `test-recheck` hops.
169
175
 
170
176
  | Changed surface | Required targeted tests | Required typechecks |
171
177
  | --- | --- | --- |
@@ -92,7 +92,8 @@ Before declaring a task `done`, run this lightweight checklist. It catches the m
92
92
  - [ ] Solution section contains `file:line` citations for every changed file.
93
93
  - [ ] API/library claims in changed docs carry a source citation (URL or `path:line`) and an honest
94
94
  confidence level (HIGH/MEDIUM/LOW) per source-driven-development; the deterministic advisory backstop is
95
- the `cited-version-strings` / `cited-api-claims` rules (`.spur/rules/docs/sourced-api-claims.yaml`).
95
+ the `cited-version-strings` / `cited-api-claims` / `cited-api-claims-confidence` rules
96
+ (`.spur/rules/docs/sourced-api-claims.yaml`, run ad hoc — not wired into a preset).
96
97
  - [ ] Review section has P1–P4 findings table (even if all rows are empty — the table itself proves review happened).
97
98
 
98
99
  This checklist runs as part of the verify step when the task reaches `testing` status. It does not replace the formal gates — it augments them with between-pipeline hygiene checks.
@@ -202,6 +202,10 @@ citation in the SAME cell, or lint rejects the row with `evidence-citation` (blo
202
202
  - The rule is cell-local: a citation in another row or a "receipt above" cross-reference does not
203
203
  satisfy it. Marker-free receipts (test counts, reviewed-log notes) need no citation. The check is
204
204
  presence-only — which citation proves which claim is not deterministically decidable.
205
+ - The same two predicates (`hasClaimMarker` / `hasCitationForm` in
206
+ `packages/app/src/services/verify-answer-lint.ts`) gate the review report's findings table through
207
+ `residual-scan review-gate` as the blocking `review-contract` category — one definition of
208
+ "external claim" and one set of accepted citation forms for both paths.
205
209
 
206
210
  ## Hedged evidence is rejected on MET rows
207
211
 
@@ -82,11 +82,11 @@ The command forwards these via `$ARGUMENTS`:
82
82
  > <!-- pipeline-tokens:end -->
83
83
  > matched as a **distinct hyphen-word**
84
84
  > (machine-checked by
85
- > [`detectPipelineDriving`](../../scripts/dogfood-testing/detect-pipeline-driving.ts);
85
+ > [`detectPipelineDriving`](../../lib/dogfood-pipeline.ts);
86
86
  > see [§Pipeline-driving word-boundary contract](#pipeline-driving-word-boundary-contract))
87
87
  > → `⚠ pipeline-driving testee detected; pass --max-retry 0 (observe-only) or --max-retry N (fix mode, tree mutation acknowledged)`.
88
88
  > - **Mutating `--fix` modes** — `--fix all` / `--fix blockers-first`, boundary-guarded via
89
- > [`hasMutatingFixMode`](../../scripts/dogfood-testing/detect-pipeline-driving.ts) (never matches
89
+ > [`hasMutatingFixMode`](../../lib/dogfood-pipeline.ts) (never matches
90
90
  > `--fix none` / `--focus all` / `--prefix all`). No pipeline token required: a verify/review leg
91
91
  > with `--fix all` mutates the tree on its own (0280 dogfood P2, task 0293). Honesty note:
92
92
  > `--max-retry 0` here bounds **the driver only** — the testee still mutates the tree — so the
@@ -64,7 +64,7 @@ Run the steps in order. Each step's depth lives in its reference; this file is t
64
64
  change a frontier feature's status, the report **leads with the drift summary** ("sync first")
65
65
  and the ranking is computed over the *post-sync* status view. Details:
66
66
  [references/signal-derivation.md](references/signal-derivation.md) §0.
67
- 1. **Assemble the candidate set.** `spur feature list --json`; drop `done`/`cancelled`, drop
67
+ 1. **Assemble the candidate set.** `spur feature list --json`; drop `done`/`cancelled`/`deprecated`, drop
68
68
  structural containers (has children, no own open tasks — defect D1), drop the map/feature that
69
69
  owns this investigation if present. The `group` tag is **not** a reliable container marker (D3).
70
70
  2. **Gate on actionability.** Read the frontier predicate **at runtime** from
@@ -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
@@ -167,7 +167,7 @@ rule at the source, so no per-path shim is needed:
167
167
  | `spur agent loop` | `AgentService.run` per drained iteration | Same resolution path as `spur agent run`; inherits its own `SPUR_ROLE` |
168
168
  | `spur serve` team supervisor → member | spawns `spur agent loop` | Member inherits the supervisor's `SPUR_ROLE` (recursive by construction) |
169
169
  | Native subagent fan-out (this skill's default) | in-session `Task()`/`Skill()` | In-session subagents share the host session; when they themselves dispatch, the host's role is already in the session env — the rule holds at the next `spur agent run` boundary |
170
- | `plugins/sp/evals/run-eval.ts` | `spawnSync('spur agent run', …)` per scenario | Out of scope: a top-level eval harness, not a fan-out — no dispatcher role exists to inherit; each scenario is an independent top-level run (documented, no shim) |
170
+ | `scripts/commands/skill-eval.ts` | `spawnSync('spur agent run', …)` per scenario | Out of scope: a top-level eval harness, not a fan-out — no dispatcher role exists to inherit; each scenario is an independent top-level run (documented, no shim) |
171
171
 
172
172
  The inventory is recorded in `docs/04_DESIGN.md` § `spur agent run` envelope (`roleOrigin`).
173
173
  Paths that shell out to `spur agent run` without the rule would silently drop or double-attribute
@@ -101,10 +101,13 @@ line: `Verification: HIGH|MEDIUM|LOW — <first failing check and deviation, or
101
101
  - **Honest confidence** — the stated HIGH/MEDIUM/LOW matches the evidence: HIGH requires a check against the pinned source this session; MEDIUM/LOW items are labeled, and LOW is never presented as fact.
102
102
  - **HIGH** — all three checks pass. **MEDIUM** — coverage intact but some citations are off-version (latest docs for a pinned dependency) or secondary-source only. **LOW** — an API/library claim lacks any citation, or a LOW-confidence claim was asserted as fact.
103
103
 
104
- Deterministic backstop: the `cited-version-strings` / `cited-api-claims` rules
105
- (`.spur/rules/docs/sourced-api-claims.yaml`, run in `recommended-pre-check`) flag markdown lines that
106
- assert version strings or time-anchored API claims with no citation anchor on the same line. They
107
- are an info-severity advisory backstop for this contract (they do not gate; the checks above and the SECUA checklist are the enforcement points).
104
+ Deterministic backstop: the `cited-version-strings`, `cited-api-claims` and
105
+ `cited-api-claims-confidence` rules (`.spur/rules/docs/sourced-api-claims.yaml`) flag markdown lines
106
+ that assert a version string or a time-anchored API claim with no citation anchor on the same line,
107
+ and claim lines that carry no HIGH/MEDIUM/LOW level. They are an info-severity advisory backstop for
108
+ this contract and are deliberately NOT wired into `recommended-pre-check` — run them ad hoc with
109
+ `spur rule run --file .spur/rules/docs/sourced-api-claims.yaml`. The checks above and the SECUA
110
+ checklist remain the enforcement points.
108
111
 
109
112
  ## Common Rationalizations
110
113
 
@@ -8,8 +8,9 @@ see_also:
8
8
  # spur builder - release plumbing
9
9
 
10
10
  `spur builder` is the CLI for **version bumps and release tags**. It wraps the internal
11
- `spur-dev release` flow behind a public two-verb surface, promoted verbatim from
12
- `scripts/commands/release.ts` (now a thin forwarder to the same implementation). Package ids are
11
+ `release-ops` implementation behind a public two-verb surface. The repo-local forwarder
12
+ (`scripts/commands/release.ts`) and its `spur-dev` dispatcher cases were retired in task 1173; the
13
+ root `bun run bump-ver` / `bun run drop-tags` names route to `spur builder` directly. Package ids are
13
14
  the unscoped short names (`@gobing-ai/spur` → `spur`); the released set and the aggregate tag are
14
15
  discovered from the repo's own workspace manifests, so the same code serves any git+semver
15
16
  monorepo.