@gobing-ai/spur 0.4.8 → 0.4.10

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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/config/pipeline-budgets.json +2 -2
  3. package/config/rules/README.md +1 -1
  4. package/config/rules/boundary/config-loading-ownership.yaml +1 -0
  5. package/config/rules/boundary/env-var-hygiene.yaml +0 -1
  6. package/config/rules/boundary/sp-plugin-standalone.yaml +1 -1
  7. package/config/rules/boundary/sp-script-placement.yaml +3 -3
  8. package/config/rules/docs/sourced-api-claims.yaml +29 -64
  9. package/config/rules/strict/runtime-boundaries.yaml +40 -1
  10. package/config/rules/structure/protected-files.yaml +1 -1
  11. package/config/rules/surface/check-cli-surface.yaml +33 -0
  12. package/config/rules/typescript/no-debugger.yaml +3 -0
  13. package/config/workflow-candidates.json +2 -2
  14. package/config/workflows/feature-verification.yaml +6 -19
  15. package/config/workflows/history-anatomy.yaml +14 -28
  16. package/config/workflows/idea-pipeline.yaml +6 -19
  17. package/config/workflows/pr-review.yaml +9 -16
  18. package/config/workflows/task-pipeline.yaml +18 -25
  19. package/config/workflows/wrapup-pipeline.yaml +21 -63
  20. package/package.json +3 -3
  21. package/plugins/sp/README.md +70 -37
  22. package/plugins/sp/agents/expert-spur.md +0 -1
  23. package/plugins/sp/agents/super-planner.md +5 -5
  24. package/plugins/sp/agents/super-reviewer.md +2 -2
  25. package/plugins/sp/commands/dev-changelog.md +1 -1
  26. package/plugins/sp/commands/dev-daily.md +1 -1
  27. package/plugins/sp/commands/dev-pr-review.md +1 -1
  28. package/plugins/sp/commands/dev-review-session.md +1 -1
  29. package/plugins/sp/commands/dev-runall.md +4 -4
  30. package/plugins/sp/commands/dev-verify.md +4 -6
  31. package/plugins/sp/commands/dev-verifyall.md +4 -5
  32. package/plugins/sp/hooks/hooks.json +11 -1
  33. package/plugins/sp/lib/context-post-tool.ts +1 -1
  34. package/plugins/sp/lib/context-session-start.ts +1 -1
  35. package/plugins/sp/lib/context-session-stop.ts +1 -1
  36. package/plugins/sp/lib/destructive-policy.ts +65 -1
  37. package/plugins/sp/plugin.json +2 -5
  38. package/plugins/sp/{hooks → scripts/hooks}/agent-lifecycle.ts +1 -1
  39. package/plugins/sp/scripts/hooks/careful-guard.ts +94 -0
  40. package/plugins/sp/{hooks → scripts/hooks}/context-post-tool.ts +2 -2
  41. package/plugins/sp/{hooks → scripts/hooks}/context-session-start.ts +1 -1
  42. package/plugins/sp/{hooks → scripts/hooks}/context-session-stop.ts +1 -1
  43. package/plugins/sp/{hooks → scripts/hooks}/fleet-guest-stop.ts +6 -4
  44. package/plugins/sp/{hooks → scripts/hooks}/task-write-guard.ts +38 -21
  45. package/plugins/sp/skills/brainstorm/SKILL.md +12 -11
  46. package/plugins/sp/skills/brainstorm/agents/openai.yaml +1 -1
  47. package/plugins/sp/skills/brainstorm/examples/ideation-example.md +0 -4
  48. package/plugins/sp/skills/brainstorm/metadata.openclaw +1 -1
  49. package/plugins/sp/skills/brainstorm/references/workflows.md +4 -14
  50. package/plugins/sp/skills/code-implementation/SKILL.md +1 -1
  51. package/plugins/sp/skills/code-verification/SKILL.md +8 -8
  52. package/plugins/sp/skills/code-verification/references/secu-review.md +4 -4
  53. package/plugins/sp/skills/code-verification/references/verdict-schema.md +9 -1
  54. package/plugins/sp/skills/daily-summary/SKILL.md +4 -5
  55. package/plugins/sp/skills/dogfood-testing/SKILL.md +9 -9
  56. package/plugins/sp/skills/dogfood-testing/references/report-template.md +1 -1
  57. package/plugins/sp/skills/history-anatomy/references/modes.md +2 -2
  58. package/plugins/sp/skills/history-anatomy/references/operations.md +3 -4
  59. package/plugins/sp/skills/history-anatomy/references/report-contract.md +2 -3
  60. package/plugins/sp/skills/issue-finding/SKILL.md +1 -1
  61. package/plugins/sp/skills/next-router/references/routing-table.md +1 -1
  62. package/plugins/sp/skills/parallel-execution/SKILL.md +2 -2
  63. package/plugins/sp/skills/parallel-execution/references/result-synthesis.md +9 -10
  64. package/plugins/sp/skills/pr-reviewing/SKILL.md +14 -15
  65. package/plugins/sp/skills/reverse-engineering/SKILL.md +4 -6
  66. package/plugins/sp/skills/session-review/SKILL.md +13 -29
  67. package/plugins/sp/skills/source-driven-development/SKILL.md +26 -27
  68. package/plugins/sp/skills/spur-check/SKILL.md +14 -13
  69. package/plugins/sp/skills/spur-cli/SKILL.md +1 -1
  70. package/plugins/sp/skills/spur-cli/references/agent.md +17 -2
  71. package/plugins/sp/skills/spur-cli/references/builder.md +27 -6
  72. package/plugins/sp/skills/spur-cli/references/features.md +6 -1
  73. package/plugins/sp/skills/spur-cli/references/history.md +3 -0
  74. package/plugins/sp/skills/spur-cli/references/self.md +5 -3
  75. package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +3 -0
  76. package/plugins/sp/skills/spur-cli/references/tasks.md +13 -3
  77. package/plugins/sp/skills/spur-cli/references/workflows/authoring-workflows.md +1 -1
  78. package/plugins/sp/skills/spur-cli/references/workflows.md +36 -1
  79. package/plugins/sp/skills/spur-dev/SKILL.md +1 -1
  80. package/plugins/sp/skills/spur-dev/references/cross-cutting.md +1 -1
  81. package/plugins/sp/skills/spur-dev/references/dev-operations.md +12 -13
  82. package/plugins/sp/skills/spur-dev/references/execution-batch-report.md +10 -11
  83. package/plugins/sp/skills/spur-dev/references/execution-batch.md +16 -17
  84. package/plugins/sp/skills/spur-dev/references/execution-parallel-isolation.md +1 -1
  85. package/plugins/sp/skills/spur-dev/references/execution-worktree-landing.md +9 -12
  86. package/plugins/sp/skills/spur-dev/references/execution-worktree-setup.md +6 -8
  87. package/plugins/sp/skills/spur-dev/references/gate-checklists.md +1 -1
  88. package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +33 -40
  89. package/plugins/sp/skills/spur-dev/references/structured-trace-emission.md +24 -24
  90. package/plugins/sp/skills/spur-doctor/SKILL.md +5 -4
  91. package/spur.js +12726 -1456
  92. package/web/_astro/{BoardApp.CaNvQBD_.js → BoardApp.B5rTVNd_.js} +1 -1
  93. package/web/_astro/{BoardApp.C5jnkkKP.js → BoardApp.BMprmPRc.js} +141 -141
  94. package/web/_astro/{TaskDetail.DGKndhI8.js → TaskDetail.Ddsur8qd.js} +1 -1
  95. package/web/_astro/{arc.DHrwdg3G.js → arc.DLqVVBxP.js} +1 -1
  96. package/web/_astro/{architectureDiagram-3BPJPVTR.pvKpK1s5.js → architectureDiagram-3BPJPVTR.C-Pk3uAi.js} +1 -1
  97. package/web/_astro/{blockDiagram-GPEHLZMM.BQmRcZsS.js → blockDiagram-GPEHLZMM.BRO1i_Nm.js} +1 -1
  98. package/web/_astro/{c4Diagram-AAUBKEIU.CCLjHeH1.js → c4Diagram-AAUBKEIU.BU_cWu6e.js} +1 -1
  99. package/web/_astro/channel.Bs3Illhy.js +1 -0
  100. package/web/_astro/{chunk-2J33WTMH.DL6Yuoma.js → chunk-2J33WTMH.D2VXiQ2t.js} +1 -1
  101. package/web/_astro/{chunk-4BX2VUAB.CzlyEI7l.js → chunk-4BX2VUAB.BcHT772J.js} +1 -1
  102. package/web/_astro/{chunk-55IACEB6.B60EWxp1.js → chunk-55IACEB6.DhDtICmo.js} +1 -1
  103. package/web/_astro/{chunk-727SXJPM.c7v_fRLo.js → chunk-727SXJPM.BoKHp7gV.js} +1 -1
  104. package/web/_astro/{chunk-AQP2D5EJ.8BxLWdDc.js → chunk-AQP2D5EJ.Chu4JRpE.js} +1 -1
  105. package/web/_astro/{chunk-FMBD7UC4.DmKuxGFT.js → chunk-FMBD7UC4.wHdwKPlq.js} +1 -1
  106. package/web/_astro/{chunk-ND2GUHAM.1Zwd4mqV.js → chunk-ND2GUHAM.ffN-M1JB.js} +1 -1
  107. package/web/_astro/{chunk-QZHKN3VN.BvYLu-A1.js → chunk-QZHKN3VN.FRubwg4S.js} +1 -1
  108. package/web/_astro/{classDiagram-4FO5ZUOK.CEqRt0WU.js → classDiagram-4FO5ZUOK.c22tQsc_.js} +1 -1
  109. package/web/_astro/{classDiagram-v2-Q7XG4LA2.CEqRt0WU.js → classDiagram-v2-Q7XG4LA2.c22tQsc_.js} +1 -1
  110. package/web/_astro/{cose-bilkent-S5V4N54A.C7JiUdj2.js → cose-bilkent-S5V4N54A.B9c6fiSj.js} +1 -1
  111. package/web/_astro/{cynefin-OW5HDTMX.B6mZ-1be.js → cynefin-OW5HDTMX.DrZsrUHl.js} +1 -1
  112. package/web/_astro/{dagre-BM42HDAG.BuryfDy6.js → dagre-BM42HDAG.DG1U4b3y.js} +1 -1
  113. package/web/_astro/{diagram-2AECGRRQ.dao39YQM.js → diagram-2AECGRRQ.CH15b-K3.js} +1 -1
  114. package/web/_astro/{diagram-5GNKFQAL.lnGZe4At.js → diagram-5GNKFQAL.BH1j7twR.js} +1 -1
  115. package/web/_astro/{diagram-KO2AKTUF.uWfhzEfT.js → diagram-KO2AKTUF.q2zCkbAj.js} +1 -1
  116. package/web/_astro/{diagram-LMA3HP47.ppq8-198.js → diagram-LMA3HP47.BLaaZnHV.js} +1 -1
  117. package/web/_astro/{diagram-OG6HWLK6.CKSCqDt5.js → diagram-OG6HWLK6.BcpRdQwI.js} +1 -1
  118. package/web/_astro/{erDiagram-TEJ5UH35.sFSf2yBN.js → erDiagram-TEJ5UH35.D7KBlqfM.js} +1 -1
  119. package/web/_astro/{flowDiagram-I6XJVG4X.BJQjKLbz.js → flowDiagram-I6XJVG4X.D_gmuRqg.js} +1 -1
  120. package/web/_astro/{ganttDiagram-6RSMTGT7.DAHKNj2N.js → ganttDiagram-6RSMTGT7.DzgWYniD.js} +1 -1
  121. package/web/_astro/{gitGraphDiagram-PVQCEYII.BlmzcW_O.js → gitGraphDiagram-PVQCEYII.576i3hm3.js} +1 -1
  122. package/web/_astro/index.DiC1a6lh.css +1 -0
  123. package/web/_astro/{infoDiagram-5YYISTIA.DGhjaqsi.js → infoDiagram-5YYISTIA.DMCclBiz.js} +1 -1
  124. package/web/_astro/{ishikawaDiagram-YF4QCWOH.gKIpgt6F.js → ishikawaDiagram-YF4QCWOH.9Kyca4SH.js} +1 -1
  125. package/web/_astro/{journeyDiagram-JHISSGLW.DG3tmP0R.js → journeyDiagram-JHISSGLW.chbdG-8q.js} +1 -1
  126. package/web/_astro/{kanban-definition-UN3LZRKU.CgHDSxkR.js → kanban-definition-UN3LZRKU.Bds54-e0.js} +1 -1
  127. package/web/_astro/{linear.BuGEpcuM.js → linear.D5ND9GN_.js} +1 -1
  128. package/web/_astro/{mermaid.core.DBVqkWBp.js → mermaid.core.D9N_PGbj.js} +4 -4
  129. package/web/_astro/{mindmap-definition-RKZ34NQL.D-kOcji9.js → mindmap-definition-RKZ34NQL.t0xop5vp.js} +1 -1
  130. package/web/_astro/{pieDiagram-4H26LBE5.BvGPwtZt.js → pieDiagram-4H26LBE5.DqgKh6hM.js} +1 -1
  131. package/web/_astro/{quadrantDiagram-W4KKPZXB.BKb_9FV6.js → quadrantDiagram-W4KKPZXB.DeuHVi2t.js} +1 -1
  132. package/web/_astro/{requirementDiagram-4Y6WPE33.Cx0fvD8v.js → requirementDiagram-4Y6WPE33.BdYFSG4W.js} +1 -1
  133. package/web/_astro/{sankeyDiagram-5OEKKPKP.fatULi0W.js → sankeyDiagram-5OEKKPKP.k1LPPETJ.js} +1 -1
  134. package/web/_astro/{sequenceDiagram-3UESZ5HK.ChHyKqco.js → sequenceDiagram-3UESZ5HK.DdyfpzoG.js} +1 -1
  135. package/web/_astro/{stateDiagram-AJRCARHV.BdUpKWyj.js → stateDiagram-AJRCARHV.DSEI8il0.js} +1 -1
  136. package/web/_astro/{stateDiagram-v2-BHNVJYJU.fmJxqIm5.js → stateDiagram-v2-BHNVJYJU.CoMnQRKo.js} +1 -1
  137. package/web/_astro/{timeline-definition-PNZ67QCA.BayDn5-V.js → timeline-definition-PNZ67QCA.DM1AHNm-.js} +1 -1
  138. package/web/_astro/{vennDiagram-CIIHVFJN.Bm2Aggwa.js → vennDiagram-CIIHVFJN.C-cJmVtl.js} +1 -1
  139. package/web/_astro/{wardleyDiagram-YWT4CUSO.DI75O-uG.js → wardleyDiagram-YWT4CUSO.D0xMzKPb.js} +1 -1
  140. package/web/_astro/{xychartDiagram-2RQKCTM6.DL90E7Q7.js → xychartDiagram-2RQKCTM6.BH5xJcEq.js} +1 -1
  141. package/web/index.html +2 -2
  142. package/config/plugin-scripts.json +0 -116
  143. package/config/script-placement-baseline.json +0 -33
  144. package/plugins/sp/hooks/careful-guard.ts +0 -74
  145. package/plugins/sp/hooks/pi/guard-extension.ts +0 -304
  146. package/plugins/sp/lib/dogfood-pipeline.ts +0 -265
  147. package/plugins/sp/lib/dogfood-report.ts +0 -278
  148. package/plugins/sp/lib/history-anatomy.generated.d.mts +0 -112
  149. package/plugins/sp/lib/history-anatomy.generated.mjs +0 -691
  150. package/plugins/sp/lib/idea-handoff.generated.d.mts +0 -17
  151. package/plugins/sp/lib/idea-handoff.generated.mjs +0 -1446
  152. package/plugins/sp/lib/inline-run-app.ts +0 -68
  153. package/plugins/sp/lib/inline-run.generated.d.mts +0 -37
  154. package/plugins/sp/lib/inline-run.generated.mjs +0 -3103
  155. package/plugins/sp/lib/plan-projection.generated.d.mts +0 -197
  156. package/plugins/sp/lib/plan-projection.generated.mjs +0 -982
  157. package/plugins/sp/lib/quality-gate.generated.d.mts +0 -139
  158. package/plugins/sp/lib/quality-gate.generated.mjs +0 -30510
  159. package/plugins/sp/lib/residual-scan.generated.d.mts +0 -93
  160. package/plugins/sp/lib/residual-scan.generated.mjs +0 -383
  161. package/plugins/sp/lib/run-summary-core.ts +0 -225
  162. package/plugins/sp/lib/spur-bin.ts +0 -36
  163. package/plugins/sp/lib/step-profile.generated.d.mts +0 -71
  164. package/plugins/sp/lib/step-profile.generated.mjs +0 -174
  165. package/plugins/sp/lib/transcript.ts +0 -112
  166. package/plugins/sp/scripts/batch-plan.mjs +0 -1096
  167. package/plugins/sp/scripts/batch-plan.ts +0 -145
  168. package/plugins/sp/scripts/batch-preflight.mjs +0 -423
  169. package/plugins/sp/scripts/batch-preflight.ts +0 -270
  170. package/plugins/sp/scripts/commit-guard.mjs +0 -226
  171. package/plugins/sp/scripts/commit-guard.ts +0 -248
  172. package/plugins/sp/scripts/daily-summary/daily-summary.mjs +0 -626
  173. package/plugins/sp/scripts/daily-summary/daily-summary.ts +0 -855
  174. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +0 -211
  175. package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +0 -110
  176. package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +0 -282
  177. package/plugins/sp/scripts/dogfood-testing/validate-report.ts +0 -98
  178. package/plugins/sp/scripts/feature-verification-steps.mjs +0 -82
  179. package/plugins/sp/scripts/feature-verification-steps.ts +0 -164
  180. package/plugins/sp/scripts/history-anatomy-cache.mjs +0 -931
  181. package/plugins/sp/scripts/history-anatomy-cache.ts +0 -247
  182. package/plugins/sp/scripts/idea-handoff.mjs +0 -32
  183. package/plugins/sp/scripts/idea-handoff.ts +0 -47
  184. package/plugins/sp/scripts/inline-run-dispatch.mjs +0 -131
  185. package/plugins/sp/scripts/inline-run-dispatch.ts +0 -115
  186. package/plugins/sp/scripts/inline-run-setup.mjs +0 -746
  187. package/plugins/sp/scripts/inline-run-setup.ts +0 -198
  188. package/plugins/sp/scripts/persist-out-check.mjs +0 -217
  189. package/plugins/sp/scripts/persist-out-check.ts +0 -249
  190. package/plugins/sp/scripts/pr-reviewing.mjs +0 -712
  191. package/plugins/sp/scripts/pr-reviewing.ts +0 -952
  192. package/plugins/sp/scripts/quality-gate.mjs +0 -30491
  193. package/plugins/sp/scripts/quality-gate.ts +0 -80
  194. package/plugins/sp/scripts/residual-scan.mjs +0 -634
  195. package/plugins/sp/scripts/residual-scan.ts +0 -248
  196. package/plugins/sp/scripts/run-summary.mjs +0 -758
  197. package/plugins/sp/scripts/run-summary.ts +0 -107
  198. package/plugins/sp/scripts/script-root.mjs +0 -129
  199. package/plugins/sp/scripts/script-root.ts +0 -199
  200. package/plugins/sp/scripts/session-timeline.mjs +0 -359
  201. package/plugins/sp/scripts/session-timeline.ts +0 -190
  202. package/plugins/sp/scripts/task-diffstat.mjs +0 -181
  203. package/plugins/sp/scripts/task-diffstat.ts +0 -219
  204. package/plugins/sp/scripts/workflow-step-profile.mjs +0 -338
  205. package/plugins/sp/scripts/workflow-step-profile.ts +0 -165
  206. package/plugins/sp/scripts/wrapup-drift-probe.mjs +0 -190
  207. package/plugins/sp/scripts/wrapup-drift-probe.ts +0 -261
  208. package/plugins/sp/scripts/wrapup-steps.mjs +0 -627
  209. package/plugins/sp/scripts/wrapup-steps.ts +0 -891
  210. package/web/_astro/channel.C7wG9RxA.js +0 -1
  211. package/web/_astro/index.BCY1JJHd.css +0 -1
@@ -1,627 +0,0 @@
1
- #!/usr/bin/env node
2
- // @bun
3
-
4
- // plugins/sp/scripts/wrapup-steps.ts
5
- import { spawnSync } from "child_process";
6
- import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
7
- import { join } from "path";
8
-
9
- // plugins/sp/lib/env.ts
10
- function getEnvVars() {
11
- return process.env;
12
- }
13
-
14
- // plugins/sp/lib/spur-bin.ts
15
- function spurCommand(spurBin) {
16
- const parts = (spurBin ?? "spur").trim().split(/\s+/).filter((p) => p.length > 0);
17
- return { cmd: parts[0] ?? "spur", prefix: parts.slice(1) };
18
- }
19
-
20
- // plugins/sp/scripts/wrapup-steps.ts
21
- function jqPick(...values) {
22
- for (const value of values) {
23
- if (value !== null && value !== undefined && value !== false)
24
- return value;
25
- }
26
- return values[values.length - 1];
27
- }
28
- function jqText(value) {
29
- return typeof value === "string" ? value : JSON.stringify(value);
30
- }
31
- var WBS_PATTERN = /^[0-9]{4}$/;
32
- function spur(env, args, options = {}) {
33
- const { cmd, prefix } = spurCommand(env.spurBin);
34
- const result = spawnSync(cmd, [...prefix, ...args], {
35
- cwd: options.cwd,
36
- encoding: "utf8",
37
- ...options.stderr === "inherit" ? { stdio: ["ignore", "pipe", "inherit"] } : {}
38
- });
39
- if (result.error !== undefined)
40
- return { status: result.status ?? 1, stdout: "" };
41
- return { status: result.status ?? 1, stdout: result.stdout ?? "" };
42
- }
43
- function taskStatusOf(taskJson) {
44
- let parsed;
45
- try {
46
- parsed = JSON.parse(taskJson);
47
- } catch {
48
- return { resolved: null, present: false };
49
- }
50
- if (parsed === null || typeof parsed !== "object")
51
- return { resolved: null, present: false };
52
- const frontmatter = parsed.frontmatter;
53
- const fmStatus = frontmatter !== null && typeof frontmatter === "object" ? frontmatter.status : undefined;
54
- const status = parsed.status;
55
- if (fmStatus === null || fmStatus === undefined) {
56
- return { resolved: status ?? null, present: status !== undefined && status !== null };
57
- }
58
- return { resolved: fmStatus, present: true };
59
- }
60
- function statusDisplay(taskJson) {
61
- const { resolved } = taskStatusOf(taskJson);
62
- if (resolved === null || resolved === undefined || resolved === false)
63
- return "unresolved";
64
- return jqText(resolved);
65
- }
66
- function resolveTasks(env, options = {}) {
67
- const cwd = options.cwd;
68
- const runId = env.__runId ?? "";
69
- if (runId.length === 0) {
70
- process.stderr.write(`task-resolve: __runId is empty \u2014 refusing the legacy fixed-path fallback
71
- `);
72
- return { status: "FAIL", statusFile: "", tasksFile: "", exitCode: 1 };
73
- }
74
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
75
- const relTasksFile = join(".spur", "run", `${runId}-wrapup-tasks.json`);
76
- const relReasonFile = join(".spur", "run", `${runId}-route-reason.txt`);
77
- const relStatusFile = join(".spur", "run", `${runId}-wrapup-resolve.status`);
78
- const abs = (p) => cwd ? join(cwd, p) : p;
79
- const writeFail = (reason) => {
80
- writeFileSync(abs(relReasonFile), reason);
81
- writeFileSync(abs(relStatusFile), `FAIL
82
- `);
83
- return { status: "FAIL", statusFile: relStatusFile, tasksFile: relTasksFile, exitCode: 0 };
84
- };
85
- let parsedTasks;
86
- try {
87
- parsedTasks = JSON.parse(env.tasks ?? "");
88
- } catch {
89
- parsedTasks = undefined;
90
- }
91
- const validArray = Array.isArray(parsedTasks) && parsedTasks.every((w) => typeof w === "string" && WBS_PATTERN.test(w));
92
- if (!validArray) {
93
- process.stderr.write(`task-resolve: tasks must be a JSON array of canonical four-digit WBS strings (whitespace is rejected, not trimmed)
94
- `);
95
- return writeFail("failed:tasks is not a JSON array of canonical four-digit WBS strings");
96
- }
97
- const deduped = [];
98
- for (const wbs of parsedTasks) {
99
- if (!deduped.includes(wbs))
100
- deduped.push(wbs);
101
- }
102
- writeFileSync(abs(relTasksFile), `${JSON.stringify(deduped)}
103
- `);
104
- let unresolved = false;
105
- for (const wbs of deduped) {
106
- const shown = spur(env, ["task", "show", wbs, "--json"], { cwd });
107
- const status = shown.status === 0 ? statusDisplay(shown.stdout) : "unresolved";
108
- if (status !== "done" && status !== "cancelled") {
109
- process.stderr.write(`task-resolve: task ${wbs} did not resolve to a completed status (status=${status})
110
- `);
111
- unresolved = true;
112
- }
113
- }
114
- if (unresolved) {
115
- return writeFail(`failed:unresolved or non-completed task (see ${relTasksFile})`);
116
- }
117
- if ((env.feature ?? "") !== "") {
118
- const pf = preflightFeature(env, cwd);
119
- if (pf)
120
- return writeFail(pf);
121
- }
122
- writeFileSync(abs(relStatusFile), `PASS
123
- `);
124
- return { status: "PASS", statusFile: relStatusFile, tasksFile: relTasksFile, exitCode: 0 };
125
- }
126
- function isFindingList(value) {
127
- return Array.isArray(value) && value.every((f) => f !== null && typeof f === "object" && typeof f.severity === "string" && typeof f.code === "string" && f.code.length > 0);
128
- }
129
- function sortedUniqueCodes(findings) {
130
- const codes = findings.filter((f) => f.severity === "error").map((f) => String(f.code ?? "")).filter((c) => c.length > 0);
131
- return [...new Set(codes)].sort();
132
- }
133
- function preflightFeature(env, cwd) {
134
- const feature = env.feature ?? "";
135
- const abs = (p) => cwd ? join(cwd, p) : p;
136
- const syncOut = spur(env, ["feature", "sync", feature, "--dry-run", "--json"], { cwd });
137
- let sync;
138
- if (syncOut.status === 0) {
139
- try {
140
- const parsed = JSON.parse(syncOut.stdout);
141
- if (parsed !== null && typeof parsed === "object")
142
- sync = parsed;
143
- } catch {}
144
- }
145
- if (sync === undefined)
146
- return "failed:preflight:sync-unreadable";
147
- const proposal = sync.proposal;
148
- if (proposal === null || typeof proposal !== "object" || Array.isArray(proposal) || proposal.gateBlocked !== undefined && typeof proposal.gateBlocked !== "boolean" || proposal.gateFindings !== undefined && !isFindingList(proposal.gateFindings) || proposal.hops !== undefined && (!Array.isArray(proposal.hops) || !proposal.hops.every((hop) => typeof hop === "string")) || proposal.gateBlocked !== true && (typeof proposal.from !== "string" || typeof proposal.to !== "string")) {
149
- return "failed:preflight:sync-unreadable";
150
- }
151
- if (proposal.gateBlocked === true) {
152
- return `failed:preflight:gate-blocked ${sortedUniqueCodes(proposal.gateFindings ?? []).join(",")}`;
153
- }
154
- const reachesDone = proposal.to === "done" || (proposal.hops ?? []).includes("done");
155
- if (!reachesDone)
156
- return null;
157
- const checkOut = spur(env, ["feature", "check", feature, "--strict", "--as", "done", "--json"], { cwd });
158
- let check;
159
- try {
160
- check = JSON.parse(checkOut.stdout);
161
- } catch {
162
- return "failed:preflight:check-unreadable";
163
- }
164
- const payload = Array.isArray(check) && check.length === 1 ? check[0] : check;
165
- const findings = payload !== null && typeof payload === "object" ? payload.findings : undefined;
166
- if (!isFindingList(findings) || checkOut.status !== 0 && (checkOut.status !== 1 || !findings.some((f) => f.severity === "error"))) {
167
- return "failed:preflight:check-unreadable";
168
- }
169
- const eligible = proposal.from === "verifying" ? findings : findings.filter((f) => !String(f.code ?? "").startsWith("L4.feature-receipt-"));
170
- const runId = env.__runId ?? "";
171
- if (runId.length > 0) {
172
- mkdirSync(abs(join(".spur", "run")), { recursive: true });
173
- writeFileSync(abs(join(".spur", "run", `${runId}-wrapup-preflight.json`)), `${JSON.stringify(check)}
174
- `);
175
- }
176
- const codes = sortedUniqueCodes(eligible);
177
- return codes.length > 0 ? `failed:preflight:done-gate ${codes.join(",")}` : null;
178
- }
179
- var ROUTE_REASON_TABLE = {
180
- fast: "fast:evidence complete+consistent",
181
- "": "safety:missing evidence (mode empty)",
182
- unknown: "safety:unknown evidence quality",
183
- conflict: "safety:conflicting evidence",
184
- safety: "safety:operator-forced doc-sync"
185
- };
186
- function writeRouteReason(env, options = {}) {
187
- const cwd = options.cwd;
188
- const runId = env.__runId ?? "";
189
- if (runId.length === 0) {
190
- process.stderr.write(`task-resolve: __runId is empty \u2014 refusing to write a route reason
191
- `);
192
- return { reason: "", reasonFile: "", exitCode: 1 };
193
- }
194
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
195
- mkdirSync(cwd ? join(cwd, ".spur", "memory") : join(".spur", "memory"), { recursive: true });
196
- const relReasonFile = join(".spur", "run", `${runId}-route-reason.txt`);
197
- const abs = (p) => cwd ? join(cwd, p) : p;
198
- const statusFile = join(".spur", "run", `${runId}-wrapup-resolve.status`);
199
- if (readFileSyncSafe(abs(statusFile))?.trim() === "FAIL") {
200
- return { reason: "", reasonFile: relReasonFile, exitCode: 0 };
201
- }
202
- let taskCount = -1;
203
- try {
204
- const parsed = JSON.parse(readFileSync(abs(join(".spur", "run", `${runId}-wrapup-tasks.json`)), "utf8"));
205
- if (Array.isArray(parsed))
206
- taskCount = parsed.length;
207
- } catch {}
208
- let probeClean = false;
209
- try {
210
- const probe = JSON.parse(readFileSync(abs(join(".spur", "run", `${runId}-drift-probe.json`)), "utf8"));
211
- probeClean = probe?.clean === true;
212
- } catch {}
213
- const mode = env.mode ?? "";
214
- let reason;
215
- if (taskCount === 0) {
216
- reason = "skipped:empty task list";
217
- } else if (mode === "fast" && probeClean) {
218
- reason = "fast:drift-probe-clean";
219
- } else {
220
- reason = ROUTE_REASON_TABLE[mode] ?? `safety:unrecognized evidence (mode=${mode})`;
221
- }
222
- writeFileSync(abs(relReasonFile), `${reason}
223
- `);
224
- appendFileSync(abs(join(".spur", "memory", "wrapup-routes.log")), `${runId} ${reason}
225
- `);
226
- return { reason, reasonFile: relReasonFile, exitCode: 0 };
227
- }
228
- function readFileSyncSafe(path) {
229
- try {
230
- return readFileSync(path, "utf8");
231
- } catch {
232
- return null;
233
- }
234
- }
235
- function verdictFromTestingSection(content) {
236
- const heading = /^#{1,6}\s+Testing\s*$/m.exec(content);
237
- if (!heading)
238
- return null;
239
- const level = heading[0].match(/^#+/)?.[0]?.length ?? 2;
240
- const rest = content.slice(heading.index + heading[0].length);
241
- const next = new RegExp(`^#{1,${level}}\\s+\\S`, "m").exec(rest);
242
- const section = next ? rest.slice(0, next.index) : rest;
243
- for (const line of section.split(`
244
- `)) {
245
- const m = /^(?:-\s*|\*\*)?Verdict:\s*(PASS|PARTIAL|FAIL|UNKNOWN)\b/i.exec(line.trim());
246
- if (m?.[1] !== undefined)
247
- return m[1].toUpperCase();
248
- }
249
- return null;
250
- }
251
- function verdictOfArtifact(verdictPath) {
252
- try {
253
- const raw = jqPick(JSON.parse(readFileSync(verdictPath, "utf8")).verdict, "");
254
- const text = jqText(raw);
255
- return text.length > 0 ? text : null;
256
- } catch {
257
- return null;
258
- }
259
- }
260
- function runMetrics(env, options = {}) {
261
- const cwd = options.cwd;
262
- const runId = env.__runId ?? "";
263
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
264
- mkdirSync(cwd ? join(cwd, ".spur", "memory") : join(".spur", "memory"), { recursive: true });
265
- const relStatusFile = join(".spur", "run", `${runId}-wrapup-metrics.status`);
266
- const relTasksFile = join(".spur", "run", `${runId}-wrapup-tasks.json`);
267
- const relMetricsFile = join(".spur", "memory", "wrapup-metrics.jsonl");
268
- const abs = (p) => cwd ? join(cwd, p) : p;
269
- const fail = () => {
270
- writeFileSync(abs(relStatusFile), `FAIL
271
- `);
272
- return { status: "FAIL", statusFile: relStatusFile };
273
- };
274
- let captured;
275
- try {
276
- captured = JSON.parse(readFileSync(abs(relTasksFile), "utf8"));
277
- } catch {
278
- captured = undefined;
279
- }
280
- const validCapture = Array.isArray(captured) && captured.every((w) => typeof w === "string" && WBS_PATTERN.test(w));
281
- if (!validCapture) {
282
- process.stderr.write(`metrics-record: run-scoped task capture missing, corrupted or non-canonical \u2014 refusing to record metrics
283
- `);
284
- return fail();
285
- }
286
- let metricsRc = 0;
287
- for (const wbs of captured) {
288
- const shown = spur(env, ["task", "show", wbs, "--json"], { cwd });
289
- const lookup = shown.status === 0 ? taskStatusOf(shown.stdout) : { resolved: null, present: false };
290
- if (!lookup.present) {
291
- process.stderr.write(`metrics-record: task ${wbs} lookup failed or was malformed \u2014 recording FAIL instead of silently omitting its metrics row
292
- `);
293
- metricsRc = 1;
294
- continue;
295
- }
296
- const parsed = JSON.parse(shown.stdout);
297
- const frontmatter = parsed.frontmatter !== null && typeof parsed.frontmatter === "object" ? parsed.frontmatter : {};
298
- const featureId = String(jqPick(frontmatter.feature_id, parsed.feature_id, ""));
299
- const status = String(jqPick(frontmatter.status, parsed.status, "unknown"));
300
- const durable = join(".spur", "memory", "evidence", `${wbs}-verdict.json`);
301
- const hasDurable = existsSync(abs(durable));
302
- const verdictPath = hasDurable ? durable : join(".spur", "run", `${wbs}-verdict.json`);
303
- const artifactVerdict = verdictOfArtifact(abs(verdictPath)) ?? (hasDurable ? "UNKNOWN" : null);
304
- const trackedVerdict = verdictFromTestingSection(typeof parsed.content === "string" ? parsed.content : "");
305
- const verdict = artifactVerdict ?? trackedVerdict ?? "UNKNOWN";
306
- if (verdict === "UNKNOWN") {
307
- process.stderr.write(`metrics-record: task ${wbs} has no certifying verdict \u2014 ${verdictPath}: ${artifactVerdict ?? "missing or carries none"}, tracked Testing: ${trackedVerdict ?? "no Verdict: line"} \u2014 recording UNKNOWN telemetry
308
- `);
309
- }
310
- const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
311
- const row = { wbs, feature_id: featureId, status, verdict, timestamp };
312
- try {
313
- appendFileSync(abs(relMetricsFile), `${JSON.stringify(row)}
314
- `);
315
- } catch {
316
- process.stderr.write(`metrics-record: metrics append failed for task ${wbs} \u2014 recording FAIL instead of claiming the row landed
317
- `);
318
- metricsRc = 1;
319
- }
320
- }
321
- const status = metricsRc === 0 ? "PASS" : "FAIL";
322
- writeFileSync(abs(relStatusFile), `${status}
323
- `);
324
- return { status, statusFile: relStatusFile };
325
- }
326
- function classifySync(syncOutput, syncRc, feature, observed) {
327
- if (syncRc !== 0) {
328
- return { reason: `sync exited nonzero (rc=${syncRc})`, applied: "unreadable", syncOk: false };
329
- }
330
- let parsed;
331
- try {
332
- parsed = JSON.parse(syncOutput);
333
- } catch {
334
- parsed = undefined;
335
- }
336
- const obj = parsed !== null && typeof parsed === "object" ? parsed : undefined;
337
- const proposal = obj?.proposal !== null && typeof obj?.proposal === "object" ? obj.proposal : undefined;
338
- const shapeOk = obj !== undefined && proposal !== undefined && typeof proposal.featureId === "string" && typeof proposal.from === "string" && typeof proposal.to === "string" && typeof obj.applied === "boolean";
339
- if (!shapeOk) {
340
- return { reason: "malformed or unreadable sync result", applied: "unreadable", syncOk: false };
341
- }
342
- const applied = obj.applied === true ? "true" : "false";
343
- const pFrom = String(jqPick(proposal.from, ""));
344
- const pTo = String(jqPick(proposal.to, ""));
345
- if (String(jqPick(proposal.featureId, "")) !== feature) {
346
- return { reason: `sync proposal does not match feature ${feature}`, applied, syncOk: false };
347
- }
348
- if (proposal.gateBlocked === true) {
349
- return {
350
- reason: "sync proposal is gate-blocked \u2014 a blocked sync is not a no-change success",
351
- applied,
352
- syncOk: false
353
- };
354
- }
355
- if (proposal.requiresConfirm === true) {
356
- return { reason: "sync proposal requires operator confirmation", applied, syncOk: false };
357
- }
358
- if (applied === "true" && observed !== pTo) {
359
- return {
360
- reason: `applied sync did not land on the proposal target (observed=${observed}, to=${pTo})`,
361
- applied,
362
- syncOk: false
363
- };
364
- }
365
- if (applied === "false" && (pFrom !== pTo || observed !== pTo)) {
366
- return {
367
- reason: `sync applied nothing without a from==to observed no-op (from=${pFrom}, to=${pTo}, observed=${observed})`,
368
- applied,
369
- syncOk: false
370
- };
371
- }
372
- return { reason: "", applied, syncOk: true };
373
- }
374
- function runFeatureTransition(env, options = {}) {
375
- const cwd = options.cwd;
376
- const runId = env.__runId ?? "";
377
- const feature = env.feature ?? "";
378
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
379
- const relStatusFile = join(".spur", "run", `${runId}-wrapup-sync.status`);
380
- const abs = (p) => cwd ? join(cwd, p) : p;
381
- if (feature.length === 0) {
382
- process.stderr.write(`feature-transition: vars.feature is empty \u2014 refusing no-op feature sync (mis-invocation, not a blocked sync)
383
- `);
384
- return { status: "FAIL", statusFile: relStatusFile, exitCode: 1 };
385
- }
386
- let syncOutput = "";
387
- let syncRc = 1;
388
- const sync = spur(env, ["feature", "sync", feature, "--json"], { cwd, stderr: "inherit" });
389
- syncOutput = sync.stdout;
390
- syncRc = sync.status;
391
- process.stdout.write(`${syncOutput}
392
- `);
393
- const shown = spur(env, ["feature", "show", feature, "--json"], { cwd });
394
- let observed = "";
395
- if (shown.status === 0) {
396
- try {
397
- const parsed = JSON.parse(shown.stdout);
398
- const frontmatter = parsed.frontmatter !== null && typeof parsed.frontmatter === "object" ? parsed.frontmatter : {};
399
- const picked = jqPick(parsed.status, frontmatter.status, "");
400
- observed = picked === "" ? "" : jqText(picked);
401
- } catch {
402
- observed = "";
403
- }
404
- }
405
- if (observed.length === 0)
406
- observed = "unreadable";
407
- const classified = classifySync(syncOutput, syncRc, feature, observed);
408
- const { reason, applied } = classified;
409
- const syncOk = classified.syncOk;
410
- let gate = "skipped";
411
- if (applied === "true" || syncRc !== 0) {
412
- process.stdout.write(`feature-transition: sync applied or failed after a possible partial transition for ${feature} \u2014 running feature gate: ${env.featureGateCmd ?? ""}
413
- `);
414
- const gateResult = spawnSync("sh", ["-c", env.featureGateCmd ?? ""], { cwd, stdio: "inherit" });
415
- if ((gateResult.status ?? 1) === 0) {
416
- gate = "PASS";
417
- process.stdout.write(`feature-transition: feature gate PASS for feature ${feature}
418
- `);
419
- } else {
420
- gate = "FAIL";
421
- process.stderr.write(`feature-transition: feature gate FAIL for feature ${feature} \u2014 inspect findings before reporting the transition complete
422
- `);
423
- }
424
- } else {
425
- process.stdout.write(`feature-transition: sync did not apply a transition (rc=${syncRc}, applied=${applied}) \u2014 feature gate skipped
426
- `);
427
- }
428
- let syncStatus;
429
- if (!syncOk || gate === "FAIL") {
430
- syncStatus = "FAIL";
431
- process.stderr.write(`feature-transition: required synchronization failed for ${feature} \u2014 ${reason}; gate=${gate}
432
- `);
433
- } else if (applied === "false") {
434
- syncStatus = "PASS";
435
- process.stdout.write(`feature-transition: feature sync verified for ${feature} (from==to observed at ${observed}, gate=${gate}) \u2014 explicit no-change
436
- `);
437
- } else {
438
- syncStatus = "PASS";
439
- process.stdout.write(`feature-transition: feature sync verified for ${feature} (applied, observed=${observed}, gate=${gate})
440
- `);
441
- }
442
- writeFileSync(abs(relStatusFile), `${syncStatus}
443
- `);
444
- return { status: syncStatus, statusFile: relStatusFile, exitCode: 0 };
445
- }
446
- var DOC_SYNC_SNAPSHOT_SUFFIX = "wrapup-doc-sync-snapshot.txt";
447
- var DOC_SYNC_SCOPE_STATUS_SUFFIX = "wrapup-doc-sync-scope.status";
448
- var DOC_SYNC_SUPERSESSION_STATUS_SUFFIX = "wrapup-doc-supersession.status";
449
- var SUPERSESSION_PIN = "repo-wide-tests/adr-supersession.test.ts";
450
- var GIT_UNAVAILABLE_MARKER = "# git-unavailable:";
451
- var SNAPSHOT_MISSING = "snapshot-missing";
452
- function porcelainPath(line) {
453
- const body = line.length >= 3 ? line.slice(3) : line;
454
- const arrow = body.indexOf(" -> ");
455
- const target = arrow >= 0 ? body.slice(arrow + 4) : body;
456
- const trimmed = target.trim();
457
- return trimmed.startsWith('"') && trimmed.endsWith('"') && trimmed.length >= 2 ? trimmed.slice(1, -1) : trimmed;
458
- }
459
- function isDocSyncAllowedPath(path, allowedExtra) {
460
- if (allowedExtra.includes(path))
461
- return true;
462
- if (!path.startsWith("docs/"))
463
- return false;
464
- return !path.startsWith("docs/tasks") && !path.startsWith("docs/features");
465
- }
466
- function docSyncScopeViolations(snapshot, current, allowedExtra) {
467
- const before = new Set(snapshot.split(`
468
- `).filter((line) => line.trim() !== ""));
469
- const paths = new Set;
470
- for (const line of current.split(`
471
- `)) {
472
- if (line.trim() === "" || before.has(line))
473
- continue;
474
- const path = porcelainPath(line);
475
- if (!isDocSyncAllowedPath(path, allowedExtra))
476
- paths.add(path);
477
- }
478
- return [...paths].sort();
479
- }
480
- function gitPorcelain(cwd) {
481
- const result = spawnSync("git", ["status", "--porcelain", "-uall"], {
482
- cwd,
483
- encoding: "utf8"
484
- });
485
- if (result.error !== undefined)
486
- return { ok: false, listing: "", detail: String(result.error) };
487
- if (result.status !== 0)
488
- return { ok: false, listing: "", detail: (result.stderr ?? "").trim() };
489
- return { ok: true, listing: result.stdout ?? "", detail: "" };
490
- }
491
- function wrapupRunPaths(env, cwd) {
492
- const runId = env.__runId ?? "";
493
- const rel = (suffix) => join(".spur", "run", `${runId}-${suffix}`);
494
- return { rel, abs: (suffix) => cwd ? join(cwd, rel(suffix)) : rel(suffix) };
495
- }
496
- function docSyncAllowedExtra(env) {
497
- const runId = env.__runId ?? "";
498
- return [
499
- join(".spur", "run", `${runId}-wrapup-learnings.md`),
500
- join(".spur", "run", `${runId}-${DOC_SYNC_SNAPSHOT_SUFFIX}`),
501
- join(".spur", "run", `${runId}-${DOC_SYNC_SCOPE_STATUS_SUFFIX}`)
502
- ];
503
- }
504
- function runDocSyncSnapshot(env, options = {}) {
505
- const cwd = options.cwd;
506
- const paths = wrapupRunPaths(env, cwd);
507
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
508
- writeFileSync(paths.abs(DOC_SYNC_SNAPSHOT_SUFFIX), "");
509
- const listing = gitPorcelain(cwd);
510
- if (!listing.ok) {
511
- writeFileSync(paths.abs(DOC_SYNC_SNAPSHOT_SUFFIX), `${GIT_UNAVAILABLE_MARKER} ${listing.detail}
512
- `);
513
- process.stderr.write(`doc-sync-snapshot: git status unavailable \u2014 scope check skips (${listing.detail})
514
- `);
515
- return { statusFile: paths.rel(DOC_SYNC_SNAPSHOT_SUFFIX), captured: false, exitCode: 0 };
516
- }
517
- writeFileSync(paths.abs(DOC_SYNC_SNAPSHOT_SUFFIX), listing.listing);
518
- return { statusFile: paths.rel(DOC_SYNC_SNAPSHOT_SUFFIX), captured: true, exitCode: 0 };
519
- }
520
- function runDocSyncScope(env, options = {}) {
521
- const cwd = options.cwd;
522
- const paths = wrapupRunPaths(env, cwd);
523
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
524
- const statusFile = paths.rel(DOC_SYNC_SCOPE_STATUS_SUFFIX);
525
- const write = (status, detail) => {
526
- writeFileSync(paths.abs(DOC_SYNC_SCOPE_STATUS_SUFFIX), `${detail}
527
- `);
528
- if (status !== "PASS")
529
- process.stderr.write(`doc-sync-scope: ${detail}
530
- `);
531
- return { status, statusFile, violations: status === "PASS" ? [] : [detail], exitCode: 0 };
532
- };
533
- const snapshot = readFileSyncSafe(paths.abs(DOC_SYNC_SNAPSHOT_SUFFIX));
534
- if (snapshot === null) {
535
- return write("scope-violation", `scope-violation: ${SNAPSHOT_MISSING}`);
536
- }
537
- if (snapshot.startsWith(GIT_UNAVAILABLE_MARKER)) {
538
- process.stderr.write(`doc-sync-scope: entry snapshot recorded no git listing \u2014 scope check skipped
539
- `);
540
- return write("PASS", "PASS");
541
- }
542
- const current = gitPorcelain(cwd);
543
- if (!current.ok) {
544
- return write("scope-violation", `scope-violation: git-unavailable (${current.detail})`);
545
- }
546
- const violations = docSyncScopeViolations(snapshot, current.listing, docSyncAllowedExtra(env));
547
- if (violations.length === 0)
548
- return write("PASS", "PASS");
549
- return write("scope-violation", `scope-violation: ${violations.join(" ")}`);
550
- }
551
- function runDocSupersessionCheck(env, options = {}) {
552
- const cwd = options.cwd;
553
- const paths = wrapupRunPaths(env, cwd);
554
- mkdirSync(cwd ? join(cwd, ".spur", "run") : join(".spur", "run"), { recursive: true });
555
- const statusFile = paths.rel(DOC_SYNC_SUPERSESSION_STATUS_SUFFIX);
556
- const pinPath = cwd ? join(cwd, SUPERSESSION_PIN) : SUPERSESSION_PIN;
557
- if (!existsSync(pinPath)) {
558
- writeFileSync(paths.abs(DOC_SYNC_SUPERSESSION_STATUS_SUFFIX), `PASS
559
- `);
560
- return { status: "PASS", statusFile, exitCode: 0 };
561
- }
562
- const pin = spawnSync("bun", ["test", SUPERSESSION_PIN], { cwd, encoding: "utf8" });
563
- if (pin.status === 0) {
564
- writeFileSync(paths.abs(DOC_SYNC_SUPERSESSION_STATUS_SUFFIX), `PASS
565
- `);
566
- return { status: "PASS", statusFile, exitCode: 0 };
567
- }
568
- writeFileSync(paths.abs(DOC_SYNC_SUPERSESSION_STATUS_SUFFIX), `supersession-pin-failed: ${SUPERSESSION_PIN}
569
- `);
570
- const detail = `${pin.stdout ?? ""}${pin.stderr ?? ""}`.trim();
571
- process.stderr.write(`doc-supersession: ${SUPERSESSION_PIN} failed (exit ${String(pin.status ?? 1)}) \u2014 the doc-sync step introduced or kept superseded index content:
572
- ${detail.slice(-2000)}
573
- `);
574
- return { status: "supersession-pin-failed", statusFile, exitCode: 0 };
575
- }
576
- var WRAPUP_STEPS_USAGE = "usage: wrapup-steps.ts <resolve|route-reason|metrics|feature-transition|doc-sync-snapshot|doc-sync-scope|doc-supersession> (env: __runId, tasks, mode, feature, featureGateCmd, spurBin)";
577
- function main(argv, env = getEnvVars(), options = {}) {
578
- const sub = argv[0];
579
- if (sub === "resolve")
580
- return resolveTasks(env, options).exitCode;
581
- if (sub === "route-reason")
582
- return writeRouteReason(env, options).exitCode;
583
- if (sub === "metrics") {
584
- runMetrics(env, options);
585
- return 0;
586
- }
587
- if (sub === "feature-transition")
588
- return runFeatureTransition(env, options).exitCode;
589
- if (sub === "doc-sync-snapshot")
590
- return runDocSyncSnapshot(env, options).exitCode;
591
- if (sub === "doc-sync-scope")
592
- return runDocSyncScope(env, options).exitCode;
593
- if (sub === "doc-supersession")
594
- return runDocSupersessionCheck(env, options).exitCode;
595
- process.stderr.write(`${WRAPUP_STEPS_USAGE}
596
- `);
597
- return 2;
598
- }
599
- {
600
- process.exit(main(process.argv.slice(2)));
601
- }
602
- export {
603
- DOC_SYNC_SCOPE_STATUS_SUFFIX,
604
- DOC_SYNC_SNAPSHOT_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
- };