@lsctech/polaris 0.1.0

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 (316) hide show
  1. package/README.md +87 -0
  2. package/dist/__mocks__/tool-server-linear.js +34 -0
  3. package/dist/cli/adopt-approve.js +60 -0
  4. package/dist/cli/adopt-cognition.js +199 -0
  5. package/dist/cli/adopt-instructions.js +143 -0
  6. package/dist/cli/adopt-rules.js +169 -0
  7. package/dist/cli/adopt-scan.js +25 -0
  8. package/dist/cli/adopt-smartdocs.js +192 -0
  9. package/dist/cli/adoption-inventory.js +350 -0
  10. package/dist/cli/adoption-plan.js +222 -0
  11. package/dist/cli/args.js +28 -0
  12. package/dist/cli/args.test.js +30 -0
  13. package/dist/cli/branding.js +41 -0
  14. package/dist/cli/graph.js +358 -0
  15. package/dist/cli/index.js +127 -0
  16. package/dist/cli/index.test.js +162 -0
  17. package/dist/cli/init-detect.js +61 -0
  18. package/dist/cli/init.js +492 -0
  19. package/dist/cli/init.test.js +259 -0
  20. package/dist/cli/librarian.js +32 -0
  21. package/dist/cli/medic.js +96 -0
  22. package/dist/cli/startup.test.js +23 -0
  23. package/dist/cli/tracker.js +130 -0
  24. package/dist/cli/tracker.test.js +126 -0
  25. package/dist/cli/version.js +10 -0
  26. package/dist/cli/version.test.js +11 -0
  27. package/dist/cli/worker.js +287 -0
  28. package/dist/cluster-state/fixtures/test-clusters.json +19 -0
  29. package/dist/cluster-state/store.js +302 -0
  30. package/dist/cluster-state/store.test.js +145 -0
  31. package/dist/cluster-state/types.js +2 -0
  32. package/dist/cognition/archive.js +158 -0
  33. package/dist/cognition/closeout-librarian-types.js +161 -0
  34. package/dist/cognition/cognition.test.js +623 -0
  35. package/dist/cognition/index.js +41 -0
  36. package/dist/cognition/librarian-dispatch.js +391 -0
  37. package/dist/cognition/librarian-dispatch.test.js +247 -0
  38. package/dist/cognition/librarian-packet.js +313 -0
  39. package/dist/cognition/librarian-types.js +57 -0
  40. package/dist/cognition/route-cognition-delta.js +350 -0
  41. package/dist/cognition/summary-delta.js +279 -0
  42. package/dist/cognition/validate.js +162 -0
  43. package/dist/config/defaults.js +98 -0
  44. package/dist/config/doctor.js +273 -0
  45. package/dist/config/index.js +14 -0
  46. package/dist/config/loader.js +58 -0
  47. package/dist/config/provider-detect.js +60 -0
  48. package/dist/config/provider-detect.test.js +119 -0
  49. package/dist/config/schema.js +2 -0
  50. package/dist/config/show.js +44 -0
  51. package/dist/config/show.test.js +42 -0
  52. package/dist/config/validator.js +675 -0
  53. package/dist/config/validator.test.js +77 -0
  54. package/dist/docs/canon-check.js +334 -0
  55. package/dist/docs/canon-check.test.js +207 -0
  56. package/dist/docs/doctrine.js +133 -0
  57. package/dist/docs/doctrine.test.js +139 -0
  58. package/dist/docs/index.js +168 -0
  59. package/dist/docs/index.test.js +83 -0
  60. package/dist/docs/ingest.js +295 -0
  61. package/dist/docs/ingest.test.js +71 -0
  62. package/dist/docs/migrate.js +165 -0
  63. package/dist/docs/migrate.test.js +136 -0
  64. package/dist/docs/seed-instructions.js +147 -0
  65. package/dist/docs/seed-instructions.test.js +137 -0
  66. package/dist/docs/validate-instructions.js +267 -0
  67. package/dist/docs/validate-instructions.test.js +238 -0
  68. package/dist/finalize/artifact-policy.js +214 -0
  69. package/dist/finalize/artifact-policy.test.js +84 -0
  70. package/dist/finalize/delivery-integrity.js +141 -0
  71. package/dist/finalize/finalize.test.js +210 -0
  72. package/dist/finalize/github.js +41 -0
  73. package/dist/finalize/index.js +474 -0
  74. package/dist/finalize/linear.js +213 -0
  75. package/dist/finalize/run-report.js +47 -0
  76. package/dist/finalize/steps/01-map-update.js +11 -0
  77. package/dist/finalize/steps/02-map-validate.js +11 -0
  78. package/dist/finalize/steps/03-schema-validate.js +11 -0
  79. package/dist/finalize/steps/04-run-checks.js +118 -0
  80. package/dist/finalize/steps/05-generate-report.js +14 -0
  81. package/dist/finalize/steps/06-commit.js +39 -0
  82. package/dist/finalize/steps/07-push.js +10 -0
  83. package/dist/finalize/steps/08-create-pr.js +9 -0
  84. package/dist/finalize/steps/09-update-state.js +9 -0
  85. package/dist/finalize/steps/10-append-jsonl.js +23 -0
  86. package/dist/finalize/steps/11-update-linear.js +22 -0
  87. package/dist/finalize/steps/12-archive.js +21 -0
  88. package/dist/graph/adapter/c/adapter.js +45 -0
  89. package/dist/graph/adapter/c/extract.js +135 -0
  90. package/dist/graph/adapter/c/index.js +10 -0
  91. package/dist/graph/adapter/c/runtime.js +67 -0
  92. package/dist/graph/adapter/cpp/adapter.js +45 -0
  93. package/dist/graph/adapter/cpp/extract.js +159 -0
  94. package/dist/graph/adapter/cpp/index.js +10 -0
  95. package/dist/graph/adapter/cpp/runtime.js +61 -0
  96. package/dist/graph/adapter/csharp/adapter.js +45 -0
  97. package/dist/graph/adapter/csharp/extract.js +127 -0
  98. package/dist/graph/adapter/csharp/index.js +10 -0
  99. package/dist/graph/adapter/csharp/runtime.js +61 -0
  100. package/dist/graph/adapter/dart/adapter.js +56 -0
  101. package/dist/graph/adapter/dart/extract.js +130 -0
  102. package/dist/graph/adapter/dart/index.js +10 -0
  103. package/dist/graph/adapter/dart/runtime.js +61 -0
  104. package/dist/graph/adapter/go/adapter.js +45 -0
  105. package/dist/graph/adapter/go/extract.js +112 -0
  106. package/dist/graph/adapter/go/index.js +10 -0
  107. package/dist/graph/adapter/go/runtime.js +58 -0
  108. package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
  109. package/dist/graph/adapter/kotlin-java/index.js +15 -0
  110. package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
  111. package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
  112. package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
  113. package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
  114. package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
  115. package/dist/graph/adapter/python/adapter.js +45 -0
  116. package/dist/graph/adapter/python/extract.js +121 -0
  117. package/dist/graph/adapter/python/index.js +10 -0
  118. package/dist/graph/adapter/python/runtime.js +70 -0
  119. package/dist/graph/adapter/registry.js +82 -0
  120. package/dist/graph/adapter/rust/adapter.js +45 -0
  121. package/dist/graph/adapter/rust/extract.js +110 -0
  122. package/dist/graph/adapter/rust/index.js +10 -0
  123. package/dist/graph/adapter/rust/runtime.js +74 -0
  124. package/dist/graph/adapter/shell/adapter.js +34 -0
  125. package/dist/graph/adapter/shell/extract.js +98 -0
  126. package/dist/graph/adapter/shell/index.js +8 -0
  127. package/dist/graph/adapter/svelte/adapter.js +91 -0
  128. package/dist/graph/adapter/svelte/extract.js +41 -0
  129. package/dist/graph/adapter/svelte/index.js +8 -0
  130. package/dist/graph/adapter/swift/adapter.js +45 -0
  131. package/dist/graph/adapter/swift/extract.js +120 -0
  132. package/dist/graph/adapter/swift/index.js +10 -0
  133. package/dist/graph/adapter/swift/runtime.js +70 -0
  134. package/dist/graph/adapter/types.js +2 -0
  135. package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
  136. package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
  137. package/dist/graph/adapter/typescript-javascript/index.js +10 -0
  138. package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
  139. package/dist/graph/capability/index.js +105 -0
  140. package/dist/graph/governance.js +104 -0
  141. package/dist/graph/parser/extract.js +5 -0
  142. package/dist/graph/parser/loader.js +2 -0
  143. package/dist/graph/parser/pipeline.js +194 -0
  144. package/dist/graph/query/index.js +277 -0
  145. package/dist/graph/query/types.js +5 -0
  146. package/dist/graph/resolver/build-edges.js +211 -0
  147. package/dist/graph/resolver/index.js +23 -0
  148. package/dist/graph/resolver/resolve-imports.js +168 -0
  149. package/dist/graph/store/adapter.js +132 -0
  150. package/dist/graph/store/queries.js +124 -0
  151. package/dist/graph/store/types.js +4 -0
  152. package/dist/ignore/defaults.js +41 -0
  153. package/dist/ignore/parser.js +39 -0
  154. package/dist/lint/map-reference-validator.js +72 -0
  155. package/dist/loop/abort.js +188 -0
  156. package/dist/loop/abort.test.js +151 -0
  157. package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
  158. package/dist/loop/adapters/agent-subtask.js +137 -0
  159. package/dist/loop/adapters/agent-subtask.test.js +151 -0
  160. package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
  161. package/dist/loop/adapters/index.js +9 -0
  162. package/dist/loop/adapters/registry.js +17 -0
  163. package/dist/loop/adapters/terminal-cli.js +403 -0
  164. package/dist/loop/adapters/types.js +2 -0
  165. package/dist/loop/adapters/worker-instructions.js +85 -0
  166. package/dist/loop/body-parser.js +226 -0
  167. package/dist/loop/bootstrap-packet.js +97 -0
  168. package/dist/loop/bootstrap-packet.test.js +171 -0
  169. package/dist/loop/branch-governance.js +54 -0
  170. package/dist/loop/budget.js +63 -0
  171. package/dist/loop/budget.test.js +163 -0
  172. package/dist/loop/checkpoint.js +325 -0
  173. package/dist/loop/compact-return.js +59 -0
  174. package/dist/loop/continue.js +645 -0
  175. package/dist/loop/continue.test.js +337 -0
  176. package/dist/loop/dispatch-boundary.js +335 -0
  177. package/dist/loop/dispatch-state.js +223 -0
  178. package/dist/loop/dispatch-state.test.js +186 -0
  179. package/dist/loop/dispatch.js +1236 -0
  180. package/dist/loop/dispatch.test.js +499 -0
  181. package/dist/loop/ephemeral-smoke.test.js +160 -0
  182. package/dist/loop/evidence-backfill.js +216 -0
  183. package/dist/loop/execution-adapter.js +141 -0
  184. package/dist/loop/execution-adapter.test.js +66 -0
  185. package/dist/loop/finalize-evidence.js +169 -0
  186. package/dist/loop/git-custody.js +250 -0
  187. package/dist/loop/impl-packet-input.js +38 -0
  188. package/dist/loop/index.js +270 -0
  189. package/dist/loop/ledger.js +49 -0
  190. package/dist/loop/ledger.test.js +127 -0
  191. package/dist/loop/lifecycle-dispatch.js +348 -0
  192. package/dist/loop/lifecycle-dispatch.test.js +318 -0
  193. package/dist/loop/lifecycle.js +93 -0
  194. package/dist/loop/lifecycle.test.js +150 -0
  195. package/dist/loop/orphan-recovery.js +276 -0
  196. package/dist/loop/orphan-recovery.test.js +236 -0
  197. package/dist/loop/parent.js +1566 -0
  198. package/dist/loop/parent.test.js +738 -0
  199. package/dist/loop/provider-sealed-packet.test.js +164 -0
  200. package/dist/loop/resume.js +295 -0
  201. package/dist/loop/resume.test.js +172 -0
  202. package/dist/loop/run-bootstrap.js +234 -0
  203. package/dist/loop/run-preflight.js +203 -0
  204. package/dist/loop/status.js +665 -0
  205. package/dist/loop/status.test.js +235 -0
  206. package/dist/loop/wait.js +86 -0
  207. package/dist/loop/worker-packet.js +406 -0
  208. package/dist/loop/worker-packet.test.js +243 -0
  209. package/dist/loop/worker-prompt.js +260 -0
  210. package/dist/loop/worker-prompt.test.js +247 -0
  211. package/dist/loop/worker.js +498 -0
  212. package/dist/loop/worker.test.js +372 -0
  213. package/dist/map/atlas.js +77 -0
  214. package/dist/map/backfill.js +171 -0
  215. package/dist/map/backfill.test.js +120 -0
  216. package/dist/map/index.js +255 -0
  217. package/dist/map/inference.js +73 -0
  218. package/dist/map/inference.test.js +35 -0
  219. package/dist/map/query.js +157 -0
  220. package/dist/map/query.test.js +174 -0
  221. package/dist/map/update.js +355 -0
  222. package/dist/map/update.test.js +67 -0
  223. package/dist/map/validate.js +153 -0
  224. package/dist/map/validate.test.js +90 -0
  225. package/dist/map/welfare.js +86 -0
  226. package/dist/mcp/lib/invoke.js +35 -0
  227. package/dist/mcp/lib/redact.js +24 -0
  228. package/dist/mcp/lib/redact.test.js +112 -0
  229. package/dist/mcp/lib/root.js +41 -0
  230. package/dist/mcp/lib/root.test.js +114 -0
  231. package/dist/mcp/server.js +15 -0
  232. package/dist/mcp/tools/claim-child.js +119 -0
  233. package/dist/mcp/tools/claim-child.test.js +69 -0
  234. package/dist/mcp/tools/current-state.js +73 -0
  235. package/dist/mcp/tools/current-state.test.js +114 -0
  236. package/dist/mcp/tools/dispatch-result.js +186 -0
  237. package/dist/mcp/tools/dispatch-result.test.js +109 -0
  238. package/dist/mcp/tools/index.js +222 -0
  239. package/dist/mcp/tools/index.test.js +172 -0
  240. package/dist/mcp/tools/linear.js +74 -0
  241. package/dist/mcp/tools/loop-continue.js +159 -0
  242. package/dist/mcp/tools/loop-continue.test.js +186 -0
  243. package/dist/mcp/tools/loop-dry-run.js +15 -0
  244. package/dist/mcp/tools/status.js +65 -0
  245. package/dist/medic/chart-id.js +75 -0
  246. package/dist/medic/chart-schema.js +139 -0
  247. package/dist/runs/index.js +119 -0
  248. package/dist/runs/index.test.js +184 -0
  249. package/dist/runs/ledger-tail.js +15 -0
  250. package/dist/runs/list.js +29 -0
  251. package/dist/runs/reconcile.js +74 -0
  252. package/dist/runs/show.js +10 -0
  253. package/dist/runtime/audit/logger.js +15 -0
  254. package/dist/runtime/audit/reader.js +42 -0
  255. package/dist/runtime/checkpoint.js +89 -0
  256. package/dist/runtime/continuation/confirmed.js +225 -0
  257. package/dist/runtime/continuation/confirmed.test.js +421 -0
  258. package/dist/runtime/continuation/dry-run.js +74 -0
  259. package/dist/runtime/execution-window.js +106 -0
  260. package/dist/runtime/scheduling/child-selector.js +16 -0
  261. package/dist/runtime/state.js +43 -0
  262. package/dist/runtime/status.js +46 -0
  263. package/dist/runtime/verification/envelope.js +80 -0
  264. package/dist/runtime/verification/fingerprint.js +26 -0
  265. package/dist/skill-packet/cli.test.js +105 -0
  266. package/dist/skill-packet/generator.js +225 -0
  267. package/dist/skill-packet/generator.test.js +159 -0
  268. package/dist/skill-packet/index.js +49 -0
  269. package/dist/skill-packet/types.js +2 -0
  270. package/dist/smartdocs-engine/audit.js +194 -0
  271. package/dist/smartdocs-engine/audit.test.js +122 -0
  272. package/dist/smartdocs-engine/canon-check.js +340 -0
  273. package/dist/smartdocs-engine/canon-check.test.js +234 -0
  274. package/dist/smartdocs-engine/doctrine.js +633 -0
  275. package/dist/smartdocs-engine/doctrine.test.js +374 -0
  276. package/dist/smartdocs-engine/index.js +456 -0
  277. package/dist/smartdocs-engine/index.test.js +82 -0
  278. package/dist/smartdocs-engine/ingest.js +495 -0
  279. package/dist/smartdocs-engine/ingest.test.js +163 -0
  280. package/dist/smartdocs-engine/migrate.js +217 -0
  281. package/dist/smartdocs-engine/migrate.test.js +200 -0
  282. package/dist/smartdocs-engine/seed-instructions.js +335 -0
  283. package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
  284. package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
  285. package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
  286. package/dist/smartdocs-engine/validate-instructions.js +366 -0
  287. package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
  288. package/dist/tracker/adapters/github/index.js +305 -0
  289. package/dist/tracker/adapters/jira/index.js +293 -0
  290. package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
  291. package/dist/tracker/adapters/linear/index.js +621 -0
  292. package/dist/tracker/adapters/linear/index.test.js +238 -0
  293. package/dist/tracker/adapters/linear.js +50 -0
  294. package/dist/tracker/adapters/local/index.js +10 -0
  295. package/dist/tracker/adapters/local-file/index.js +159 -0
  296. package/dist/tracker/adapters/mcp-bridge.js +158 -0
  297. package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
  298. package/dist/tracker/adapters/spec/index.js +146 -0
  299. package/dist/tracker/capabilities.js +2 -0
  300. package/dist/tracker/index.js +49 -0
  301. package/dist/tracker/lifecycle-policy.js +139 -0
  302. package/dist/tracker/lifecycle-transition.js +164 -0
  303. package/dist/tracker/local-graph.js +98 -0
  304. package/dist/tracker/migration.js +56 -0
  305. package/dist/tracker/schema.js +86 -0
  306. package/dist/tracker/sync/index.js +404 -0
  307. package/dist/tracker/sync/queue-store.js +95 -0
  308. package/dist/tracker/sync/queue-store.test.js +104 -0
  309. package/dist/tracker/types.js +11 -0
  310. package/dist/types/linear.js +3 -0
  311. package/dist/types/result-packet.js +12 -0
  312. package/dist/types/runtime-state.js +2 -0
  313. package/dist/types/work-contract.js +2 -0
  314. package/dist/utils/monotonic-timestamp.js +13 -0
  315. package/dist/utils/monotonic-timestamp.test.js +19 -0
  316. package/package.json +64 -0
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateDeliveryIntegrity = validateDeliveryIntegrity;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const artifact_policy_js_1 = require("./artifact-policy.js");
6
+ const git_custody_js_1 = require("../loop/git-custody.js");
7
+ function getBranchDiff(repoRoot, baseBranch, currentBranch) {
8
+ if (baseBranch === currentBranch) {
9
+ return [];
10
+ }
11
+ try {
12
+ const output = (0, node_child_process_1.execFileSync)("git", ["diff", "--name-only", `${baseBranch}...${currentBranch}`], { cwd: repoRoot, encoding: "utf-8" }).trim();
13
+ return output ? output.split("\n").filter(Boolean) : [];
14
+ }
15
+ catch (err) {
16
+ throw new Error(`delivery-integrity: git diff failed for ${baseBranch}...${currentBranch}: ` +
17
+ (err instanceof Error ? err.message : String(err)));
18
+ }
19
+ }
20
+ function getStagedFiles(repoRoot) {
21
+ try {
22
+ const output = (0, node_child_process_1.execFileSync)("git", ["diff", "--cached", "--name-only"], { cwd: repoRoot, encoding: "utf-8" }).trim();
23
+ return output ? output.split("\n").filter(Boolean) : [];
24
+ }
25
+ catch {
26
+ return [];
27
+ }
28
+ }
29
+ function classifyAll(files, clusterId) {
30
+ const impl = [];
31
+ const map = [];
32
+ const telemetry = [];
33
+ const other = [];
34
+ for (const file of files) {
35
+ const cls = (0, artifact_policy_js_1.classifyArtifactPath)(file, clusterId);
36
+ if (cls === "non-artifact") {
37
+ impl.push(file);
38
+ }
39
+ else if (cls === "promoted-map-artifact") {
40
+ map.push(file);
41
+ }
42
+ else if (cls === "promoted-run-ledger") {
43
+ telemetry.push(file);
44
+ }
45
+ else {
46
+ other.push(file);
47
+ }
48
+ }
49
+ return { impl, map, telemetry, other };
50
+ }
51
+ /**
52
+ * Validates that a finalization delivery actually contains implementation work.
53
+ *
54
+ * Checks the combined view of committed branch diff (baseBranch...currentBranch) and
55
+ * currently staged files. This handles both the normal case (implementation already
56
+ * committed to delivery branch by workers) and the edge case where implementation is
57
+ * being staged as part of the finalize commit itself.
58
+ *
59
+ * Fails closed: if the check cannot confirm implementation delivery, it returns an
60
+ * error result instead of passing.
61
+ */
62
+ function validateDeliveryIntegrity(opts) {
63
+ const { repoRoot, currentBranch, baseBranch, clusterId, completedChildren, childCommits } = opts;
64
+ // Combine committed branch diff with staged files to capture the full delivery picture.
65
+ // Workers commit implementation during execution; the finalize commit adds artifacts.
66
+ // Either source of non-artifact files is valid evidence of real implementation work.
67
+ const committedFiles = getBranchDiff(repoRoot, baseBranch, currentBranch);
68
+ const stagedFiles = getStagedFiles(repoRoot);
69
+ const allFiles = [...new Set([...committedFiles, ...stagedFiles])];
70
+ const classified = classifyAll(allFiles, clusterId);
71
+ if (classified.impl.length > 0) {
72
+ return { ok: true };
73
+ }
74
+ // No implementation source files found. Determine whether this is because
75
+ // implementation is already on the base branch (PR #93 scenario) or because
76
+ // the branch genuinely contains no implementation work.
77
+ const childrenWithCommits = completedChildren.filter((id) => id in childCommits);
78
+ const childrenAlreadyOnBase = childrenWithCommits.filter((id) => {
79
+ const commit = childCommits[id];
80
+ return (0, git_custody_js_1.isCommitReachableFrom)(repoRoot, commit, baseBranch);
81
+ });
82
+ if (childrenWithCommits.length > 0 &&
83
+ childrenAlreadyOnBase.length === childrenWithCommits.length) {
84
+ return {
85
+ ok: false,
86
+ kind: "impl-already-on-base",
87
+ reason: `Implementation for completed children (${childrenAlreadyOnBase.join(", ")}) is already ` +
88
+ `reachable from base branch "${baseBranch}". ` +
89
+ `The delivery branch "${currentBranch}" contains no new implementation relative to "${baseBranch}". ` +
90
+ `This PR would not deliver any implementation work — only leftover metadata.`,
91
+ };
92
+ }
93
+ if (allFiles.length === 0) {
94
+ return {
95
+ ok: false,
96
+ kind: "empty-branch",
97
+ reason: `No changes found between "${baseBranch}" and "${currentBranch}" (including staged files). ` +
98
+ `Nothing to deliver.`,
99
+ };
100
+ }
101
+ // All changes are artifacts/metadata. Produce a specific error for each category
102
+ // to make the failure reason immediately actionable.
103
+ const hasMap = classified.map.length > 0;
104
+ const hasTelemetry = classified.telemetry.length > 0;
105
+ const hasOther = classified.other.length > 0;
106
+ const fileList = (files, max = 3) => files.slice(0, max).join(", ") + (files.length > max ? ` (and ${files.length - max} more)` : "");
107
+ if (hasMap && !hasTelemetry && !hasOther) {
108
+ return {
109
+ ok: false,
110
+ kind: "map-only",
111
+ reason: `The delivery branch "${currentBranch}" contains only Polaris map artifact changes ` +
112
+ `(${fileList(classified.map)}) relative to "${baseBranch}". ` +
113
+ `No implementation source files were found. This PR would deliver only metadata.`,
114
+ };
115
+ }
116
+ if (hasTelemetry && !hasMap && !hasOther) {
117
+ return {
118
+ ok: false,
119
+ kind: "telemetry-only",
120
+ reason: `The delivery branch "${currentBranch}" contains only telemetry/ledger changes ` +
121
+ `(${fileList(classified.telemetry)}) relative to "${baseBranch}". ` +
122
+ `No implementation source files were found. This PR would deliver only telemetry.`,
123
+ };
124
+ }
125
+ if (!hasOther) {
126
+ return {
127
+ ok: false,
128
+ kind: "runtime-artifacts-only",
129
+ reason: `The delivery branch "${currentBranch}" contains only runtime artifact changes ` +
130
+ `(map: ${classified.map.length}, telemetry: ${classified.telemetry.length} file(s)) ` +
131
+ `relative to "${baseBranch}". No implementation source files were found.`,
132
+ };
133
+ }
134
+ return {
135
+ ok: false,
136
+ kind: "artifact-only",
137
+ reason: `The delivery branch "${currentBranch}" contains only artifact/metadata changes ` +
138
+ `(${fileList(allFiles, 5)}) relative to "${baseBranch}". ` +
139
+ `No implementation source files were found. This PR would not deliver any implementation work.`,
140
+ };
141
+ }
@@ -0,0 +1,210 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const node_os_1 = require("node:os");
7
+ const node_child_process_1 = require("node:child_process");
8
+ // ---- helpers ----------------------------------------------------------------
9
+ function makeTestDir() {
10
+ const dir = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `polaris-finalize-test-${Date.now()}`);
11
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
12
+ (0, node_child_process_1.execFileSync)("git", ["init"], { cwd: dir, stdio: "pipe" });
13
+ (0, node_child_process_1.execFileSync)("git", ["config", "user.email", "test@test.com"], { cwd: dir, stdio: "pipe" });
14
+ (0, node_child_process_1.execFileSync)("git", ["config", "user.name", "Test"], { cwd: dir, stdio: "pipe" });
15
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "README.md"), "test\n");
16
+ (0, node_child_process_1.execFileSync)("git", ["add", "."], { cwd: dir, stdio: "pipe" });
17
+ (0, node_child_process_1.execFileSync)("git", ["commit", "-m", "init"], { cwd: dir, stdio: "pipe" });
18
+ return dir;
19
+ }
20
+ function writeState(dir, extra = {}) {
21
+ const stateFile = (0, node_path_1.join)(dir, ".polaris", "runs", "current-state.json");
22
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(dir, ".polaris", "runs"), { recursive: true });
23
+ const state = {
24
+ schema_version: "1.0",
25
+ run_id: "test-finalize-001",
26
+ cluster_id: "POL-6",
27
+ active_child: "",
28
+ completed_children: ["POL-9", "POL-10", "POL-11"],
29
+ open_children: [],
30
+ step_cursor: "CLUSTER-COMPLETE",
31
+ context_budget: { children_completed: 3 },
32
+ status: "complete",
33
+ next_open_child: null,
34
+ ...extra,
35
+ };
36
+ (0, node_fs_1.writeFileSync)(stateFile, JSON.stringify(state, null, 2));
37
+ return stateFile;
38
+ }
39
+ function writeEmptyAtlas(dir) {
40
+ const mapDir = (0, node_path_1.join)(dir, ".polaris", "map");
41
+ (0, node_fs_1.mkdirSync)(mapDir, { recursive: true });
42
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(mapDir, "file-routes.json"), "{}");
43
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(mapDir, "needs-review.json"), "{}");
44
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(mapDir, "exemptions.json"), "{}");
45
+ }
46
+ // ---- step 03: schema validate -----------------------------------------------
47
+ (0, vitest_1.describe)("stepSchemaValidate", () => {
48
+ (0, vitest_1.it)("passes for valid state", async () => {
49
+ const { stepSchemaValidate } = await import("./steps/03-schema-validate.js");
50
+ const validState = {
51
+ schema_version: "1.0",
52
+ run_id: "test-finalize-001",
53
+ cluster_id: "POL-6",
54
+ active_child: "",
55
+ completed_children: [],
56
+ open_children: [],
57
+ step_cursor: "CLUSTER-COMPLETE",
58
+ context_budget: { children_completed: 3 },
59
+ status: "complete",
60
+ };
61
+ (0, vitest_1.expect)(() => stepSchemaValidate(validState)).not.toThrow();
62
+ });
63
+ (0, vitest_1.it)("calls process.exit(1) for invalid state", async () => {
64
+ const { stepSchemaValidate } = await import("./steps/03-schema-validate.js");
65
+ const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation((() => {
66
+ throw new Error("process.exit called");
67
+ }));
68
+ (0, vitest_1.expect)(() => stepSchemaValidate({ schema_version: "1.0" })).toThrow("process.exit called");
69
+ exitSpy.mockRestore();
70
+ });
71
+ });
72
+ // ---- step 05: generate report -----------------------------------------------
73
+ (0, vitest_1.describe)("stepGenerateReport", () => {
74
+ let testDir;
75
+ (0, vitest_1.beforeEach)(() => { testDir = makeTestDir(); });
76
+ (0, vitest_1.afterEach)(() => { (0, node_fs_1.rmSync)(testDir, { recursive: true, force: true }); });
77
+ (0, vitest_1.it)("writes run-report.md with correct sections", async () => {
78
+ const { stepGenerateReport } = await import("./steps/05-generate-report.js");
79
+ const stateFile = writeState(testDir);
80
+ const { readState } = await import("../loop/checkpoint.js");
81
+ const state = readState(stateFile);
82
+ const reportPath = stepGenerateReport(testDir, state, "test/finalize-integration", true);
83
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)(reportPath)).toBe(true);
84
+ const content = (0, node_fs_1.readFileSync)(reportPath, "utf-8");
85
+ (0, vitest_1.expect)(content).toContain("# Run Report: test-finalize-001");
86
+ (0, vitest_1.expect)(content).toContain("**Branch:** test/finalize-integration");
87
+ (0, vitest_1.expect)(content).toContain("**Validation:** passed");
88
+ (0, vitest_1.expect)(content).toContain("POL-9");
89
+ (0, vitest_1.expect)(content).toContain("POL-10");
90
+ (0, vitest_1.expect)(content).toContain("POL-11");
91
+ });
92
+ });
93
+ // ---- step 09: update state --------------------------------------------------
94
+ (0, vitest_1.describe)("stepUpdateState", () => {
95
+ let testDir;
96
+ (0, vitest_1.beforeEach)(() => { testDir = makeTestDir(); });
97
+ (0, vitest_1.afterEach)(() => { (0, node_fs_1.rmSync)(testDir, { recursive: true, force: true }); });
98
+ (0, vitest_1.it)("writes pr_url to current-state.json", async () => {
99
+ const { stepUpdateState } = await import("./steps/09-update-state.js");
100
+ const { readState } = await import("../loop/checkpoint.js");
101
+ const stateFile = writeState(testDir);
102
+ const state = readState(stateFile);
103
+ stepUpdateState(stateFile, state, "https://github.com/test/repo/pull/42");
104
+ const updated = JSON.parse((0, node_fs_1.readFileSync)(stateFile, "utf-8"));
105
+ (0, vitest_1.expect)(updated["pr_url"]).toBe("https://github.com/test/repo/pull/42");
106
+ });
107
+ });
108
+ // ---- step 10: append jsonl --------------------------------------------------
109
+ (0, vitest_1.describe)("stepAppendJsonl", () => {
110
+ let testDir;
111
+ (0, vitest_1.beforeEach)(() => { testDir = makeTestDir(); });
112
+ (0, vitest_1.afterEach)(() => { (0, node_fs_1.rmSync)(testDir, { recursive: true, force: true }); });
113
+ (0, vitest_1.it)("appends pr-opened and run-complete events", async () => {
114
+ const { stepAppendJsonl } = await import("./steps/10-append-jsonl.js");
115
+ const { readState } = await import("../loop/checkpoint.js");
116
+ const stateFile = writeState(testDir);
117
+ const state = readState(stateFile);
118
+ const telemetryFile = (0, node_path_1.join)(testDir, "telemetry.jsonl");
119
+ const prUrl = "https://github.com/test/repo/pull/42";
120
+ stepAppendJsonl(telemetryFile, state, prUrl);
121
+ const lines = (0, node_fs_1.readFileSync)(telemetryFile, "utf-8")
122
+ .trim()
123
+ .split("\n")
124
+ .map((l) => JSON.parse(l));
125
+ (0, vitest_1.expect)(lines).toHaveLength(2);
126
+ (0, vitest_1.expect)(lines[0]["event"]).toBe("pr-opened");
127
+ (0, vitest_1.expect)(lines[0]["pr_url"]).toBe(prUrl);
128
+ (0, vitest_1.expect)(lines[0]["run_id"]).toBe("test-finalize-001");
129
+ (0, vitest_1.expect)(lines[1]["event"]).toBe("run-complete");
130
+ (0, vitest_1.expect)(lines[1]["children_completed"]).toBe(3);
131
+ });
132
+ });
133
+ // ---- step 11: update linear (disabled) --------------------------------------
134
+ (0, vitest_1.describe)("stepUpdateLinear", () => {
135
+ (0, vitest_1.it)("skips when linearEnabled is false", async () => {
136
+ const { stepUpdateLinear } = await import("./steps/11-update-linear.js");
137
+ const { readState } = await import("../loop/checkpoint.js");
138
+ const dir = makeTestDir();
139
+ const stateFile = writeState(dir);
140
+ const state = readState(stateFile);
141
+ // Should resolve without error when disabled
142
+ await (0, vitest_1.expect)(stepUpdateLinear(state, "test-branch", "https://example.com/pr/1", true, false)).resolves.toBeUndefined();
143
+ (0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
144
+ });
145
+ });
146
+ // ---- step 12: archive -------------------------------------------------------
147
+ (0, vitest_1.describe)("stepArchive", () => {
148
+ let testDir;
149
+ (0, vitest_1.beforeEach)(() => { testDir = makeTestDir(); });
150
+ (0, vitest_1.afterEach)(() => { (0, node_fs_1.rmSync)(testDir, { recursive: true, force: true }); });
151
+ (0, vitest_1.it)("copies state and report to .polaris/runs/<run_id>/", async () => {
152
+ const { stepArchive } = await import("./steps/12-archive.js");
153
+ const { readState } = await import("../loop/checkpoint.js");
154
+ const stateFile = writeState(testDir);
155
+ const state = readState(stateFile);
156
+ const reportPath = (0, node_path_1.join)(testDir, ".polaris", "runs", "run-report.md");
157
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(testDir, ".polaris", "runs"), { recursive: true });
158
+ (0, node_fs_1.writeFileSync)(reportPath, "# Run Report: test-finalize-001\n");
159
+ stepArchive(testDir, state, stateFile, reportPath);
160
+ const archiveDir = (0, node_path_1.join)(testDir, ".polaris", "runs", "test-finalize-001");
161
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(archiveDir, "current-state.json"))).toBe(true);
162
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(archiveDir, "run-report.md"))).toBe(true);
163
+ });
164
+ });
165
+ // ---- runFinalize steps 1–6 (skip-delivery) ----------------------------------
166
+ (0, vitest_1.describe)("runFinalize (steps 1–6, skip-delivery)", () => {
167
+ let testDir;
168
+ (0, vitest_1.beforeEach)(() => { testDir = makeTestDir(); });
169
+ (0, vitest_1.afterEach)(() => { (0, node_fs_1.rmSync)(testDir, { recursive: true, force: true }); });
170
+ (0, vitest_1.it)("runs steps 1–6 end-to-end and creates a commit", async () => {
171
+ const { runFinalize } = await import("./index.js");
172
+ const stateFile = writeState(testDir);
173
+ writeEmptyAtlas(testDir);
174
+ // Capture stdout
175
+ const logSpy = vitest_1.vi.spyOn(console, "log").mockImplementation(() => { });
176
+ try {
177
+ await runFinalize({ repoRoot: testDir, stateFile, skipDelivery: true });
178
+ }
179
+ finally {
180
+ logSpy.mockRestore();
181
+ }
182
+ // Step 5 verification: run-report.md written
183
+ const reportPath = (0, node_path_1.join)(testDir, ".polaris", "runs", "run-report.md");
184
+ (0, vitest_1.expect)((0, node_fs_1.existsSync)(reportPath)).toBe(true);
185
+ const report = (0, node_fs_1.readFileSync)(reportPath, "utf-8");
186
+ (0, vitest_1.expect)(report).toContain("# Run Report: test-finalize-001");
187
+ (0, vitest_1.expect)(report).toContain("**Validation:** passed");
188
+ // Step 6 verification: commit created
189
+ const log = (0, node_child_process_1.execFileSync)("git", ["log", "--oneline", "-1"], {
190
+ cwd: testDir,
191
+ encoding: "utf-8",
192
+ });
193
+ (0, vitest_1.expect)(log).toContain("polaris finalize: test-finalize-001");
194
+ });
195
+ (0, vitest_1.it)("aborts on missing state file", async () => {
196
+ const { runFinalize } = await import("./index.js");
197
+ writeEmptyAtlas(testDir);
198
+ const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation((() => {
199
+ throw new Error("process.exit called");
200
+ }));
201
+ const stderrSpy = vitest_1.vi.spyOn(process.stderr, "write").mockImplementation(() => true);
202
+ await (0, vitest_1.expect)(runFinalize({
203
+ repoRoot: testDir,
204
+ stateFile: (0, node_path_1.join)(testDir, "nonexistent.json"),
205
+ skipDelivery: true,
206
+ })).rejects.toThrow("process.exit called");
207
+ exitSpy.mockRestore();
208
+ stderrSpy.mockRestore();
209
+ });
210
+ });
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDraftPr = createDraftPr;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function createDraftPr(options) {
6
+ const { repoRoot, branch, state, draft } = options;
7
+ if (!state.cluster_id) {
8
+ throw new Error(`createDraftPr: state.cluster_id is empty — cannot create PR without a cluster identifier`);
9
+ }
10
+ const clusterSlug = state.cluster_id.toLowerCase().replace(/[^a-z0-9]/g, "-");
11
+ const branchNorm = branch.toLowerCase().replace(/[^a-z0-9]/g, "-");
12
+ // Boundary-aware: clusterSlug must match as a full token (surrounded by start/end or hyphens)
13
+ const slugPattern = new RegExp(`(^|-)${clusterSlug.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(-|$)`);
14
+ if (!slugPattern.test(branchNorm)) {
15
+ throw new Error(`createDraftPr: branch "${branch}" does not contain cluster ID slug "${clusterSlug}" — ` +
16
+ `branch/cluster mismatch, aborting PR creation`);
17
+ }
18
+ const title = `polaris finalize: ${state.cluster_id} (${state.run_id})`;
19
+ const body = [
20
+ `**Cluster ID:** ${state.cluster_id}`,
21
+ `**Run ID:** ${state.run_id}`,
22
+ `**Branch:** ${branch}`,
23
+ `**Children completed:** ${state.completed_children.length}`,
24
+ ``,
25
+ `_Generated by polaris finalize_`,
26
+ ].join("\n");
27
+ const args = [
28
+ "pr", "create",
29
+ "--title", title,
30
+ "--body", body,
31
+ "--head", branch,
32
+ ];
33
+ if (draft)
34
+ args.push("--draft");
35
+ const output = (0, node_child_process_1.execFileSync)("gh", args, {
36
+ cwd: repoRoot,
37
+ encoding: "utf-8",
38
+ stdio: ["pipe", "pipe", "inherit"],
39
+ });
40
+ return output.trim();
41
+ }