@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
@@ -5,6 +5,594 @@
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
+ }
14
+ var NON_ABORTING_PREFLIGHT_CODES = new Set([
15
+ "L4.scenario-unverified",
16
+ "L4.verifying-incomplete-tasks"
17
+ ]);
18
+ function classifyFeaturePreflightFindings(findings) {
19
+ const aborting = [];
20
+ const nonAborting = [];
21
+ for (const f of findings) {
22
+ if (f.severity === "error") {
23
+ if (NON_ABORTING_PREFLIGHT_CODES.has(f.code)) {
24
+ nonAborting.push(f);
25
+ } else {
26
+ aborting.push(f);
27
+ }
28
+ } else {
29
+ nonAborting.push(f);
30
+ }
31
+ }
32
+ return {
33
+ aborting,
34
+ nonAborting,
35
+ shouldAbort: aborting.length > 0
36
+ };
37
+ }
38
+ function quickReadiness(input) {
39
+ const status = (input.status ?? "").toLowerCase();
40
+ const operation = input.operation;
41
+ if (operation !== "run" && operation !== "refine" && operation !== "verify") {
42
+ return {
43
+ action: "invalid",
44
+ code: "IV",
45
+ reason: `quick-readiness: unknown operation '${operation}' (${input.wbs})`
46
+ };
47
+ }
48
+ if (input.filteredCount !== undefined) {
49
+ if (input.filteredCount < 0) {
50
+ return {
51
+ action: "invalid",
52
+ code: "IV",
53
+ reason: `quick-readiness: invalid negative filtered count '${input.filteredCount}' (${input.wbs})`
54
+ };
55
+ }
56
+ if (input.filteredCount === 0) {
57
+ return {
58
+ action: "skipped",
59
+ code: "EMPTY",
60
+ reason: `quick-readiness: empty status-filtered set — nothing to ${operation} (${input.wbs})`
61
+ };
62
+ }
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
+ }
71
+ if (status === "cancelled" || status === "done") {
72
+ if (operation === "verify" && input.force === true) {
73
+ return {
74
+ action: "runnable",
75
+ code: "FORCE",
76
+ reason: `quick-readiness: verify --force re-verification of ${status} task ${input.wbs}`
77
+ };
78
+ }
79
+ return {
80
+ action: "skipped",
81
+ code: status === "done" ? "DONE" : "CANCELLED",
82
+ reason: `quick-readiness: already ${status} — no ${operation} hop (${input.wbs})`
83
+ };
84
+ }
85
+ if (status === "blocked") {
86
+ return {
87
+ action: "blocked",
88
+ code: "BLK",
89
+ reason: `quick-readiness: blocked — human/handover first (${input.wbs})`
90
+ };
91
+ }
92
+ if (operation === "run" && input.dependencies && input.dependencies.length > 0) {
93
+ const unmet = input.dependencies.filter((d) => (input.depStatuses?.[d] ?? "missing").toLowerCase() !== "done");
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("; ")}` : "";
97
+ return {
98
+ action: "blocked",
99
+ code: "DEP",
100
+ reason: `quick-readiness: unmet deps — ${unmet.join(", ")} (${input.wbs})${hint}`,
101
+ unmetDeps: unmet
102
+ };
103
+ }
104
+ }
105
+ const required = input.requiredSections ?? [];
106
+ const present = input.presentSections ?? [];
107
+ const gaps = required.filter((s) => {
108
+ const finding = input.sectionFindings?.[s];
109
+ return !present.includes(s) || finding !== undefined && finding !== "";
110
+ });
111
+ if (operation === "refine") {
112
+ if (status !== "backlog" && status !== "todo") {
113
+ return {
114
+ action: "skipped",
115
+ code: "NONPLAN",
116
+ reason: `quick-readiness: refine targets backlog/todo only, not '${status}' (${input.wbs})`
117
+ };
118
+ }
119
+ return {
120
+ action: "runnable",
121
+ code: "OK",
122
+ reason: `quick-readiness: refine ready for ${input.wbs} (${gaps.length} planning gap(s) to fill)`
123
+ };
124
+ }
125
+ if (operation === "verify") {
126
+ if (status !== "testing" && status !== "wip") {
127
+ return {
128
+ action: "invalid",
129
+ code: "NOVERIFY",
130
+ reason: `quick-readiness: verify needs testing/wip, not '${status}' (${input.wbs})`
131
+ };
132
+ }
133
+ return {
134
+ action: "runnable",
135
+ code: "OK",
136
+ reason: `quick-readiness: verify ready for ${input.wbs}`
137
+ };
138
+ }
139
+ if (status !== "todo" && status !== "wip" && status !== "testing") {
140
+ return {
141
+ action: "invalid",
142
+ code: "NORUN",
143
+ reason: `quick-readiness: run needs todo/wip/testing, not '${status}' (${input.wbs})`
144
+ };
145
+ }
146
+ if (gaps.length > 0) {
147
+ return {
148
+ action: "needs-refinement",
149
+ code: "REFINE",
150
+ reason: `quick-readiness: implementation sections incomplete (${input.wbs})`,
151
+ gaps
152
+ };
153
+ }
154
+ return {
155
+ action: "runnable",
156
+ code: "OK",
157
+ reason: `quick-readiness: run ready for ${input.wbs}`
158
+ };
159
+ }
160
+ function preflightTask(input) {
161
+ const status = (input.status ?? "").toLowerCase();
162
+ const deps = input.dependencies ?? [];
163
+ const depStatuses = input.depStatuses ?? {};
164
+ if (status === "cancelled") {
165
+ return {
166
+ action: "skip",
167
+ code: "A9",
168
+ reason: `dev-next: cancelled — nothing to advance (${input.wbs})`
169
+ };
170
+ }
171
+ if (status === "done") {
172
+ return {
173
+ action: "skip",
174
+ code: "A8",
175
+ reason: `dev-next: already done — batch does not auto-wrap (${input.wbs})`
176
+ };
177
+ }
178
+ if (status === "blocked") {
179
+ return {
180
+ action: "skip",
181
+ code: "A7",
182
+ reason: `dev-next: blocked — do not launch pipeline; human/handover first (${input.wbs})`
183
+ };
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
+ }
192
+ if (status === "todo" || status === "backlog") {
193
+ const unmet = deps.filter((d) => {
194
+ const st = (depStatuses[d] ?? "missing").toLowerCase();
195
+ return st !== "done";
196
+ });
197
+ if (unmet.length > 0) {
198
+ return {
199
+ action: "skip",
200
+ code: "A2",
201
+ reason: `dev-next: blocked by deps — unmet: ${unmet.join(", ")} (${input.wbs})`,
202
+ unmetDeps: unmet
203
+ };
204
+ }
205
+ }
206
+ return { action: "run", code: "OK", reason: `preflight clear — launch task-pipeline for ${input.wbs}` };
207
+ }
208
+ var zeroTokens = () => ({ input: 0, cacheCreate: 0, cacheRead: 0, output: 0 });
209
+ function formatDuration(ms) {
210
+ const s = Math.round(ms / 1000);
211
+ const h = Math.floor(s / 3600);
212
+ const m = Math.floor(s % 3600 / 60);
213
+ const ss = String(s % 60).padStart(2, "0");
214
+ return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${ss}` : `${m}:${ss}`;
215
+ }
216
+ function formatTokens(n) {
217
+ if (n >= 999500)
218
+ return `${(n / 1e6).toFixed(1)}M`;
219
+ return n >= 1000 ? `${Math.round(n / 1000)}k` : String(n);
220
+ }
221
+ function formatTokenSplit(t) {
222
+ if (!t)
223
+ return "n/a";
224
+ const nonCached = t.input + t.cacheCreate + t.output;
225
+ return `${formatTokens(nonCached + t.cacheRead)} / ${formatTokens(nonCached)}`;
226
+ }
227
+ var OPERATOR_TOOLS = new Set(["AskUserQuestion", "ask_user_question"]);
228
+ function isCompaction(row) {
229
+ return row.type === "compaction" || row.type === "system" && row.subtype === "compact_boundary";
230
+ }
231
+ function firstTextContent(row) {
232
+ const content = row.message?.content;
233
+ if (typeof content === "string")
234
+ return content;
235
+ if (!Array.isArray(content))
236
+ return;
237
+ return content.find((b) => b.type === "text")?.text;
238
+ }
239
+ function skillInvocationPrompt(text) {
240
+ const name = /^\s*<skill name="([^"]+)"/.exec(text)?.[1];
241
+ if (name === undefined)
242
+ return;
243
+ const close = text.lastIndexOf("</skill>");
244
+ const args = close < 0 ? "" : text.slice(close + "</skill>".length).trim();
245
+ return args === "" ? `/${name}` : `/${name} ${args}`;
246
+ }
247
+ function promptText(row) {
248
+ if (row.type === "custom_message") {
249
+ if (row.display === false)
250
+ return;
251
+ const text = typeof row.content === "string" ? row.content.trim() : "";
252
+ return text === "" ? undefined : text;
253
+ }
254
+ if (row.type === "message") {
255
+ if (row.message?.role !== "user")
256
+ return;
257
+ const text = firstTextContent(row);
258
+ if (text === undefined)
259
+ return;
260
+ return skillInvocationPrompt(text) ?? text;
261
+ }
262
+ if (row.type !== "user" || row.isMeta || row.isCompactSummary)
263
+ return;
264
+ const content = row.message?.content;
265
+ if (typeof content === "string")
266
+ return content;
267
+ if (!Array.isArray(content) || content.some((b) => b.type === "tool_result"))
268
+ return;
269
+ return content.find((b) => b.type === "text")?.text;
270
+ }
271
+ function sumTokens(all) {
272
+ const total = zeroTokens();
273
+ for (const t of all)
274
+ for (const k of Object.keys(total))
275
+ total[k] += t[k];
276
+ return total;
277
+ }
278
+ function sumSpans(spans) {
279
+ return {
280
+ workMs: spans.reduce((n, s) => n + s.workMs, 0),
281
+ waitMs: spans.reduce((n, s) => n + s.waitMs, 0),
282
+ toolCalls: spans.reduce((n, s) => n + s.toolCalls, 0),
283
+ tokens: sumTokens(spans.map((s) => s.tokens))
284
+ };
285
+ }
286
+ var newAcc = (ts) => ({
287
+ last: ts,
288
+ askMs: 0,
289
+ asks: new Map,
290
+ toolIds: new Set,
291
+ messages: new Map,
292
+ compactions: 0
293
+ });
294
+ function accumulate(acc, row, ts) {
295
+ acc.last = Math.max(acc.last, ts);
296
+ if (row.type === "message") {
297
+ const m = row.message;
298
+ if (m?.role === "assistant") {
299
+ const blocks = Array.isArray(m.content) ? m.content : [];
300
+ for (const b of blocks) {
301
+ if (b.type === "toolCall" && b.id) {
302
+ acc.toolIds.add(b.id);
303
+ if (b.name && OPERATOR_TOOLS.has(b.name))
304
+ acc.asks.set(b.id, ts);
305
+ }
306
+ }
307
+ const u = m.usage;
308
+ if (u && row.id)
309
+ acc.messages.set(row.id, {
310
+ input: u.input ?? 0,
311
+ cacheCreate: u.cacheWrite ?? 0,
312
+ cacheRead: u.cacheRead ?? 0,
313
+ output: u.output ?? 0
314
+ });
315
+ } else if (m?.role === "toolResult" && m.toolCallId && acc.asks.has(m.toolCallId)) {
316
+ acc.askMs += ts - (acc.asks.get(m.toolCallId) ?? ts);
317
+ }
318
+ return;
319
+ }
320
+ if (row.type === "compaction")
321
+ acc.compactions++;
322
+ const blocks = Array.isArray(row.message?.content) ? row.message.content : [];
323
+ for (const b of blocks) {
324
+ if (b.type === "tool_use" && b.id) {
325
+ acc.toolIds.add(b.id);
326
+ if (b.name && OPERATOR_TOOLS.has(b.name))
327
+ acc.asks.set(b.id, ts);
328
+ } else if (b.type === "tool_result" && b.tool_use_id && acc.asks.has(b.tool_use_id)) {
329
+ acc.askMs += ts - (acc.asks.get(b.tool_use_id) ?? ts);
330
+ }
331
+ }
332
+ const u = row.message?.usage;
333
+ if (row.type === "assistant" && u && row.message?.id) {
334
+ acc.messages.set(row.message.id, {
335
+ input: u.input_tokens ?? 0,
336
+ cacheCreate: u.cache_creation_input_tokens ?? 0,
337
+ cacheRead: u.cache_read_input_tokens ?? 0,
338
+ output: u.output_tokens ?? 0
339
+ });
340
+ }
341
+ }
342
+ var IDLE_GAP_MS = 300000;
343
+ var SHELL_TOOLS = new Set(["bash", "shell", "exec", "exec_command", "run_command", "command"]);
344
+ var SUBAGENT_TOOLS = new Set(["agent", "task", "invoke_subagent", "subagent", "dispatch_agent"]);
345
+ var LOWERCASE_OPERATOR_TOOLS = new Set(["askuserquestion", "ask_user_question"]);
346
+ var ACTOR_PRECEDENCE = ["operator", "subagent", "shell", "other"];
347
+ function actorOf(tool) {
348
+ const t = tool.toLowerCase().replace(/[^a-z_]/g, "");
349
+ if (LOWERCASE_OPERATOR_TOOLS.has(t))
350
+ return "operator";
351
+ if (SUBAGENT_TOOLS.has(t))
352
+ return "subagent";
353
+ if (SHELL_TOOLS.has(t))
354
+ return "shell";
355
+ return "other";
356
+ }
357
+ function unionMs(spans, start, end) {
358
+ const clipped = spans.map(([a, b]) => [Math.max(a, start), Math.min(b, end)]).filter(([a, b]) => b > a).sort((x, y) => x[0] - y[0]);
359
+ let total = 0;
360
+ let cursor = Number.NaN;
361
+ let openUntil = Number.NaN;
362
+ for (const [a, b] of clipped) {
363
+ if (!Number.isFinite(cursor)) {
364
+ cursor = a;
365
+ openUntil = b;
366
+ continue;
367
+ }
368
+ if (a > openUntil) {
369
+ total += openUntil - cursor;
370
+ cursor = a;
371
+ openUntil = b;
372
+ } else {
373
+ openUntil = Math.max(openUntil, b);
374
+ }
375
+ }
376
+ if (Number.isFinite(cursor))
377
+ total += openUntil - cursor;
378
+ return total;
379
+ }
380
+ function subtract(spans, claimed) {
381
+ let free = spans;
382
+ for (const [ca, cb] of claimed) {
383
+ const next = [];
384
+ for (const [a, b] of free) {
385
+ if (cb <= a || ca >= b) {
386
+ next.push([a, b]);
387
+ continue;
388
+ }
389
+ if (ca > a)
390
+ next.push([a, ca]);
391
+ if (cb < b)
392
+ next.push([cb, b]);
393
+ }
394
+ free = next;
395
+ }
396
+ return free;
397
+ }
398
+ function actorSplit(rows, start, end) {
399
+ const empty = {
400
+ operatorMs: 0,
401
+ longestOperatorWaitMs: 0,
402
+ modelMs: 0,
403
+ shellMs: 0,
404
+ subagentMs: 0,
405
+ otherToolMs: 0,
406
+ idleMs: 0,
407
+ compactions: 0,
408
+ subagentErrors: 0
409
+ };
410
+ const window = end - start;
411
+ if (window <= 0)
412
+ return empty;
413
+ const calls = new Map;
414
+ const results = new Map;
415
+ const eventTs = [];
416
+ let compactions = 0;
417
+ for (const [row, ts] of rows) {
418
+ if (ts < start || ts > end)
419
+ continue;
420
+ eventTs.push(ts);
421
+ if (isCompaction(row))
422
+ compactions++;
423
+ const blocks = Array.isArray(row.message?.content) ? row.message?.content : [];
424
+ if (row.message?.role === "toolResult" && row.message.toolCallId !== undefined) {
425
+ results.set(row.message.toolCallId, { ts, isError: row.message.isError === true });
426
+ }
427
+ for (const b of blocks) {
428
+ if ((b.type === "tool_use" || b.type === "toolCall") && b.id !== undefined && b.name !== undefined) {
429
+ calls.set(b.id, { id: b.id, actor: actorOf(b.name), ts });
430
+ } else if (b.type === "tool_result" && b.tool_use_id !== undefined) {
431
+ results.set(b.tool_use_id, { ts, isError: b.is_error === true });
432
+ }
433
+ }
434
+ }
435
+ let subagentErrors = 0;
436
+ const resolved = [];
437
+ for (const call of calls.values()) {
438
+ const result = results.get(call.id);
439
+ if (result?.isError === true && call.actor === "subagent")
440
+ subagentErrors++;
441
+ resolved.push({ ...call, end: Math.min(result?.ts ?? end, end) });
442
+ }
443
+ const claimed = [];
444
+ const byActor = new Map;
445
+ for (const actor of ACTOR_PRECEDENCE) {
446
+ const mine = resolved.filter((r) => r.actor === actor).map((r) => [r.ts, r.end]);
447
+ const free = subtract(mine, claimed);
448
+ for (const span of free)
449
+ claimed.push(span);
450
+ const ms = unionMs(free, start, end);
451
+ byActor.set(actor, ms);
452
+ if (actor === "operator") {
453
+ empty.longestOperatorWaitMs = Math.max(0, ...mine.map(([a, b]) => b - a));
454
+ }
455
+ }
456
+ const boundaries = [...new Set([start, end, ...eventTs, ...claimed.flat()])].sort((a, b) => a - b);
457
+ const claimedSorted = [...claimed].sort((a, b) => a[0] - b[0]);
458
+ let idleMs = 0;
459
+ for (let i = 0;i < boundaries.length - 1; i++) {
460
+ const a = boundaries[i];
461
+ const b = boundaries[i + 1];
462
+ if (b - a < IDLE_GAP_MS)
463
+ continue;
464
+ if (claimedSorted.some(([ca, cb]) => ca < b && cb > a))
465
+ continue;
466
+ idleMs += b - a;
467
+ }
468
+ const accounted = (byActor.get("operator") ?? 0) + (byActor.get("subagent") ?? 0) + (byActor.get("shell") ?? 0) + (byActor.get("other") ?? 0) + idleMs;
469
+ return {
470
+ ...empty,
471
+ operatorMs: byActor.get("operator") ?? 0,
472
+ subagentMs: byActor.get("subagent") ?? 0,
473
+ shellMs: byActor.get("shell") ?? 0,
474
+ otherToolMs: byActor.get("other") ?? 0,
475
+ idleMs,
476
+ modelMs: Math.max(0, window - accounted),
477
+ compactions,
478
+ subagentErrors
479
+ };
480
+ }
481
+ function attemptWindow(attempts) {
482
+ const starts = attempts.map((a) => Date.parse(a.startedAt ?? "")).filter((n) => !Number.isNaN(n));
483
+ const ends = attempts.map((a) => Date.parse(a.completedAt ?? "")).filter((n) => !Number.isNaN(n));
484
+ return starts.length && ends.length ? [Math.min(...starts), Math.max(...ends)] : undefined;
485
+ }
486
+ function summaryRow(stage, status, span, measured) {
487
+ return {
488
+ stage,
489
+ status,
490
+ workMs: span.workMs,
491
+ waitMs: span.waitMs,
492
+ toolCalls: measured ? span.toolCalls : null,
493
+ tokens: measured ? span.tokens : null,
494
+ time: formatDuration(span.workMs),
495
+ wait: formatDuration(span.waitMs),
496
+ token: formatTokenSplit(measured ? span.tokens : null)
497
+ };
498
+ }
499
+ function buildRunSummary(rows, inputs, opts = {}) {
500
+ const { since, until } = opts;
501
+ const windows = [];
502
+ for (const { label, progress } of inputs) {
503
+ const states = (progress.states ?? []).flatMap((s) => {
504
+ const w = attemptWindow((s.actions ?? []).flatMap((a) => a.attempts ?? []));
505
+ return w ? [{ s, w }] : [];
506
+ });
507
+ if (!states.length)
508
+ throw new Error(`progress for run ${progress.runId} has no timed attempts`);
509
+ if (inputs.length === 1) {
510
+ for (const { s, w } of states) {
511
+ const stage = (s.visit ?? 1) > 1 ? `${s.state} (visit ${s.visit})` : s.state;
512
+ windows.push({ stage, status: s.status ?? "", start: w[0], end: w[1] });
513
+ }
514
+ } else {
515
+ const stage = label ?? `${progress.workflow ?? "run"} ${progress.runId.slice(0, 8)}`;
516
+ const start = Math.min(...states.map(({ w }) => w[0]));
517
+ windows.push({
518
+ stage,
519
+ status: progress.status ?? "",
520
+ start,
521
+ end: Math.max(...states.map(({ w }) => w[1]))
522
+ });
523
+ }
524
+ }
525
+ windows.sort((a, b) => a.start - b.start);
526
+ const measure = (start, end) => {
527
+ const acc = newAcc(start);
528
+ for (const [row, ts] of rows)
529
+ if (ts >= start && ts <= end && promptText(row) === undefined)
530
+ accumulate(acc, row, ts);
531
+ const span = {
532
+ workMs: end - start - acc.askMs,
533
+ waitMs: acc.askMs,
534
+ toolCalls: acc.toolIds.size,
535
+ tokens: sumTokens([...acc.messages.values()])
536
+ };
537
+ return { span, measured: acc.toolIds.size > 0 || acc.messages.size > 0 };
538
+ };
539
+ const sinceMs = since ? Date.parse(since) : Number.NaN;
540
+ const totalStart = Number.isNaN(sinceMs) ? Math.min(...windows.map((w) => w.start)) : sinceMs;
541
+ let totalEnd = Math.max(...windows.map((w) => w.end));
542
+ if (!Number.isNaN(sinceMs)) {
543
+ for (const [, ts] of rows)
544
+ if (ts >= sinceMs)
545
+ totalEnd = Math.max(totalEnd, ts);
546
+ }
547
+ const untilMs = until ? Date.parse(until) : Number.NaN;
548
+ if (!Number.isNaN(untilMs) && untilMs > totalStart)
549
+ totalEnd = untilMs;
550
+ const stageRows = windows.map((w) => {
551
+ const { span, measured } = measure(w.start, w.end);
552
+ return summaryRow(w.stage, w.status, span, measured);
553
+ });
554
+ const totalMeasure = measure(totalStart, totalEnd);
555
+ const statuses = [...new Set(inputs.map((i) => i.progress.status ?? ""))].join("/");
556
+ const total = summaryRow("Total", statuses, totalMeasure.span, totalMeasure.measured);
557
+ const overlaps = windows.some((w, i) => i > 0 && w.start < (windows[i - 1]?.end ?? 0));
558
+ const sum = sumSpans(stageRows.map((r) => ({ ...r, toolCalls: r.toolCalls ?? 0, tokens: r.tokens ?? zeroTokens() })));
559
+ const overhead = {
560
+ workMs: total.workMs - sum.workMs,
561
+ waitMs: total.waitMs - sum.waitMs,
562
+ toolCalls: (total.toolCalls ?? 0) - sum.toolCalls,
563
+ tokens: Object.fromEntries(Object.keys(sum.tokens).map((k) => [k, (total.tokens?.[k] ?? 0) - sum.tokens[k]]))
564
+ };
565
+ if (!overlaps && overhead.workMs > 0)
566
+ stageRows.push(summaryRow("driver overhead", "", overhead, totalMeasure.measured));
567
+ return {
568
+ available: true,
569
+ rows: stageRows,
570
+ total,
571
+ actors: actorSplit(rows, totalStart, totalEnd),
572
+ gate: { ms: null, reason: "no matching check receipt" },
573
+ skippedLines: opts.skippedLines ?? 0
574
+ };
575
+ }
576
+ var SENSITIVE_PREFIXES = ["drizzle/", "packages/config/", ".github/", "plugins/sp/hooks/"];
577
+ var SENSITIVE_GLOBS = ["apps/server/src/**/auth*", "**/*secret*", "**/*.sql"];
578
+ function globToRegex(glob) {
579
+ const escaped = glob.replace(/[.+^${}()|[\]\\]/g, "\\$&").replaceAll("**/", "\x00").replaceAll("**", "\x01").replaceAll("*", "[^/]*").replaceAll("\x00", "(?:.*/)?").replaceAll("\x01", ".*");
580
+ return new RegExp(`^${escaped}$`, "s");
581
+ }
582
+ var SENSITIVE_MATCHERS = [
583
+ ...SENSITIVE_PREFIXES.map((prefix) => ({
584
+ label: prefix,
585
+ test: (path) => path.startsWith(prefix)
586
+ })),
587
+ ...SENSITIVE_GLOBS.map((glob) => {
588
+ const regex = globToRegex(glob);
589
+ return { label: glob, test: (path) => regex.test(path) };
590
+ })
591
+ ];
592
+ function sensitiveReasonForPath(path) {
593
+ const match = SENSITIVE_MATCHERS.find((entry) => entry.test(path));
594
+ return match ? `matches sensitive pattern ${match.label}` : null;
595
+ }
8
596
  function planLetter(index) {
9
597
  if (!Number.isInteger(index) || index < 0 || index > 25) {
10
598
  throw new Error(`plan letter out of range: ${index} (expected 0..25)`);
@@ -473,16 +1061,36 @@ function main(argv) {
473
1061
  process.exit(main(process.argv.slice(2)));
474
1062
  }
475
1063
  export {
476
- validatePhaseTable,
477
- taskPhaseChildren,
478
- scheduleBatch,
479
- planLetter,
480
- planChild,
481
- main,
482
- insertOnEntry,
483
- hostText,
484
- hostStatus,
485
- buildPhasedPlan,
1064
+ BATCH_PLAN_USAGE,
1065
+ NON_ABORTING_PREFLIGHT_CODES,
1066
+ SENSITIVE_GLOBS,
1067
+ SENSITIVE_PREFIXES,
1068
+ accumulate,
1069
+ actorSplit,
486
1070
  buildBatchPlan,
487
- BATCH_PLAN_USAGE
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
488
1096
  };