@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,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getVersion = getVersion;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ function getVersion() {
7
+ const packageJsonPath = (0, node_path_1.resolve)(__dirname, "../../package.json");
8
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, "utf-8"));
9
+ return packageJson.version;
10
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const version_js_1 = require("./version.js");
5
+ (0, vitest_1.describe)("getVersion", () => {
6
+ (0, vitest_1.it)("returns a version string", () => {
7
+ const version = (0, version_js_1.getVersion)();
8
+ (0, vitest_1.expect)(typeof version).toBe("string");
9
+ (0, vitest_1.expect)(version.length).toBeGreaterThan(0);
10
+ });
11
+ });
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWorkerCommand = createWorkerCommand;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const node_child_process_1 = require("node:child_process");
7
+ const node_crypto_1 = require("node:crypto");
8
+ const commander_1 = require("commander");
9
+ const worker_js_1 = require("../loop/worker.js");
10
+ const worker_packet_js_1 = require("../loop/worker-packet.js");
11
+ const git_custody_js_1 = require("../loop/git-custody.js");
12
+ const checkpoint_js_1 = require("../loop/checkpoint.js");
13
+ const store_js_1 = require("../cluster-state/store.js");
14
+ function appendTelemetry(telemetryFile, event) {
15
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(telemetryFile), { recursive: true });
16
+ (0, node_fs_1.appendFileSync)(telemetryFile, `${JSON.stringify(event)}\n`, "utf-8");
17
+ }
18
+ function failWithHelp(command, commandName) {
19
+ const subcommand = command.args[0];
20
+ const message = subcommand
21
+ ? `error: unknown command '${subcommand}' for '${commandName}'. Run '${commandName} --help'.`
22
+ : `error: missing command for '${commandName}'. Run '${commandName} --help'.`;
23
+ command.error(message, {
24
+ code: "commander.missingCommand",
25
+ exitCode: 1,
26
+ });
27
+ }
28
+ function readActiveWorkerPacket() {
29
+ const packet = (0, worker_js_1.readBootstrapPacket)(process.argv);
30
+ if (!(0, worker_packet_js_1.isWorkerPacket)(packet)) {
31
+ throw new Error("Active worker packet is missing or invalid");
32
+ }
33
+ return packet;
34
+ }
35
+ function getCommitHash(repoRoot) {
36
+ return (0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], {
37
+ cwd: repoRoot,
38
+ encoding: "utf-8",
39
+ }).trim();
40
+ }
41
+ function resolveRepoPath(repoRoot, path) {
42
+ return (0, node_path_1.isAbsolute)(path) ? path : (0, node_path_1.resolve)(repoRoot, path);
43
+ }
44
+ function validateWorkerCompletionResult(repoRoot, packet, resultFile) {
45
+ if (!resultFile) {
46
+ return { ok: false, reason: "missing result file path" };
47
+ }
48
+ if (!packet.active_child) {
49
+ return { ok: false, reason: "active worker packet has no active_child" };
50
+ }
51
+ const resolvedResultFile = resolveRepoPath(repoRoot, resultFile);
52
+ let parsed;
53
+ try {
54
+ const raw = JSON.parse((0, node_fs_1.readFileSync)(resolvedResultFile, "utf-8"));
55
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
56
+ return { ok: false, reason: "sealed result is not a JSON object" };
57
+ }
58
+ parsed = raw;
59
+ }
60
+ catch (error) {
61
+ return {
62
+ ok: false,
63
+ reason: `failed to read sealed result (${error instanceof Error ? error.message : String(error)})`,
64
+ };
65
+ }
66
+ const runId = String(parsed["run_id"] ?? "").trim();
67
+ if (runId !== packet.run_id) {
68
+ return { ok: false, reason: `sealed result run_id "${runId || "missing"}" does not match active packet` };
69
+ }
70
+ const childId = String(parsed["child_id"] ?? "").trim();
71
+ if (childId !== packet.active_child) {
72
+ return { ok: false, reason: `sealed result child_id "${childId || "missing"}" does not match active child "${packet.active_child}"` };
73
+ }
74
+ const status = String(parsed["status"] ?? "").trim().toLowerCase();
75
+ if (!["success", "done"].includes(status)) {
76
+ return { ok: false, reason: `sealed result status is "${status || "missing"}" (expected success)` };
77
+ }
78
+ const commit = String(parsed["commit"] ?? parsed["commit_hash"] ?? parsed["commit_sha"] ?? "").trim();
79
+ if (!commit) {
80
+ return { ok: false, reason: "sealed result is missing commit evidence" };
81
+ }
82
+ if (!/^[0-9a-f]{7,40}$/i.test(commit)) {
83
+ return { ok: false, reason: `sealed result commit "${commit}" is not a valid git hash` };
84
+ }
85
+ try {
86
+ (0, node_child_process_1.execFileSync)("git", ["rev-parse", "--verify", "--quiet", `${commit}^{commit}`], {
87
+ cwd: repoRoot,
88
+ stdio: "ignore",
89
+ });
90
+ }
91
+ catch {
92
+ return { ok: false, reason: `sealed result commit "${commit}" could not be verified in git` };
93
+ }
94
+ return {
95
+ ok: true,
96
+ result: {
97
+ ...parsed,
98
+ run_id: packet.run_id,
99
+ child_id: packet.active_child,
100
+ status: status === "done" ? "done" : "success",
101
+ commit,
102
+ },
103
+ };
104
+ }
105
+ function updateCompletionState(state, childId, commit) {
106
+ const completedChildren = Array.from(new Set([...state.completed_children, childId]));
107
+ const remainingOpenChildren = state.open_children.filter((child) => child !== childId);
108
+ const completedChildrenResults = {
109
+ ...(state.completed_children_results ?? {}),
110
+ [childId]: {
111
+ status: "done",
112
+ validation: "passed",
113
+ commit,
114
+ next_recommended_action: "continue",
115
+ },
116
+ };
117
+ return {
118
+ ...state,
119
+ active_child: "",
120
+ open_children: remainingOpenChildren,
121
+ completed_children: completedChildren,
122
+ completed_children_results: completedChildrenResults,
123
+ next_open_child: remainingOpenChildren[0] ?? null,
124
+ step_cursor: "checkpoint",
125
+ status: remainingOpenChildren.length > 0 ? "running" : "cluster-complete",
126
+ last_commit: commit,
127
+ context_budget: {
128
+ ...state.context_budget,
129
+ children_completed: completedChildren.length,
130
+ },
131
+ updated_at: new Date().toISOString(),
132
+ };
133
+ }
134
+ function emitWorkerCompletionTelemetry(telemetryFile, event) {
135
+ try {
136
+ appendTelemetry(telemetryFile, event);
137
+ }
138
+ catch (error) {
139
+ process.stderr.write(`[polaris-worker] telemetry write failed: ${error instanceof Error ? error.message : String(error)}\n`);
140
+ }
141
+ }
142
+ function createWorkerCommand(options) {
143
+ const worker = new commander_1.Command("worker")
144
+ .description("mutating: worker-owned commit operations")
145
+ .showHelpAfterError()
146
+ .showSuggestionAfterError();
147
+ worker.action(() => failWithHelp(worker, "polaris worker"));
148
+ worker
149
+ .command("commit")
150
+ .description("mutating: validate the staged git index against the active worker packet and create one commit")
151
+ .action(() => {
152
+ try {
153
+ const packet = readActiveWorkerPacket();
154
+ const { allowedScope, prohibitedWritePaths } = (0, worker_packet_js_1.getWorkerCommitPolicy)(packet);
155
+ const validation = (0, git_custody_js_1.validateWorkerCommitScope)(options.repoRoot, allowedScope, prohibitedWritePaths);
156
+ if (validation.staged_files.length === 0) {
157
+ appendTelemetry(packet.telemetry_file, {
158
+ event: "worker-commit-rejected",
159
+ event_id: (0, node_crypto_1.randomUUID)(),
160
+ run_id: packet.run_id,
161
+ child_id: packet.active_child,
162
+ reason: "no-staged-files",
163
+ allowed_scope: allowedScope,
164
+ prohibited_write_paths: prohibitedWritePaths,
165
+ staged_files: [],
166
+ violations: [],
167
+ timestamp: new Date().toISOString(),
168
+ });
169
+ process.stderr.write("worker commit rejected: no staged files\n");
170
+ process.exit(1);
171
+ }
172
+ if (validation.violations.length > 0) {
173
+ appendTelemetry(packet.telemetry_file, {
174
+ event: "worker-commit-rejected",
175
+ event_id: (0, node_crypto_1.randomUUID)(),
176
+ run_id: packet.run_id,
177
+ child_id: packet.active_child,
178
+ reason: "scope-violation",
179
+ allowed_scope: allowedScope,
180
+ prohibited_write_paths: prohibitedWritePaths,
181
+ staged_files: validation.staged_files,
182
+ violations: validation.violations,
183
+ timestamp: new Date().toISOString(),
184
+ });
185
+ const summary = validation.violations
186
+ .map((violation) => `${violation.kind}:${violation.path}`)
187
+ .join(", ");
188
+ process.stderr.write(`worker commit rejected: ${summary}\n`);
189
+ process.exit(1);
190
+ }
191
+ (0, node_child_process_1.execFileSync)("git", ["commit", "-m", `polaris worker commit: ${packet.active_child || packet.run_id}`], {
192
+ cwd: options.repoRoot,
193
+ stdio: "ignore",
194
+ });
195
+ process.stdout.write(`${getCommitHash(options.repoRoot)}\n`);
196
+ }
197
+ catch (error) {
198
+ if (error instanceof Error && error.message.startsWith("process.exit(")) {
199
+ throw error;
200
+ }
201
+ const message = error instanceof Error ? error.message : String(error);
202
+ process.stderr.write(`worker commit failed: ${message}\n`);
203
+ process.exit(1);
204
+ }
205
+ });
206
+ worker
207
+ .command("complete")
208
+ .description("mutating: validate a sealed worker result and update current-state.json")
209
+ .argument("<result-file>", "Path to the sealed worker result JSON")
210
+ .action((resultFile) => {
211
+ let packet;
212
+ try {
213
+ packet = readActiveWorkerPacket();
214
+ }
215
+ catch (error) {
216
+ process.stderr.write(`worker complete failed: ${error instanceof Error ? error.message : String(error)}\n`);
217
+ process.exit(1);
218
+ return;
219
+ }
220
+ try {
221
+ const stateFile = resolveRepoPath(options.repoRoot, packet.state_file);
222
+ const telemetryFile = resolveRepoPath(options.repoRoot, packet.telemetry_file);
223
+ const validation = validateWorkerCompletionResult(options.repoRoot, packet, resultFile);
224
+ if (!validation.ok) {
225
+ emitWorkerCompletionTelemetry(telemetryFile, {
226
+ event: "worker-complete-failed",
227
+ event_id: (0, node_crypto_1.randomUUID)(),
228
+ run_id: packet.run_id,
229
+ child_id: packet.active_child,
230
+ result_file: resolveRepoPath(options.repoRoot, resultFile),
231
+ reason: validation.reason,
232
+ timestamp: new Date().toISOString(),
233
+ });
234
+ process.stderr.write(`worker complete rejected: ${validation.reason}\n`);
235
+ process.exit(1);
236
+ return;
237
+ }
238
+ const state = (0, checkpoint_js_1.readState)(stateFile);
239
+ const updatedState = updateCompletionState(state, packet.active_child, validation.result.commit);
240
+ (0, checkpoint_js_1.writeStateAtomic)(stateFile, updatedState);
241
+ // Sync cluster-state.json so finalize and the parent loop see a consistent view.
242
+ try {
243
+ const clusterState = (0, store_js_1.readClusterStateSync)(packet.cluster_id, options.repoRoot);
244
+ if (clusterState) {
245
+ const childExists = clusterState.child_states.some((c) => c.id === packet.active_child);
246
+ const childStates = childExists
247
+ ? clusterState.child_states.map((c) => c.id === packet.active_child ? { ...c, status: "done", commit: validation.result.commit } : c)
248
+ : [...clusterState.child_states, { id: packet.active_child, status: "done", commit: validation.result.commit }];
249
+ (0, store_js_1.writeClusterStateSync)(packet.cluster_id, {
250
+ ...clusterState,
251
+ state_generation: clusterState.state_generation + 1,
252
+ child_states: childStates,
253
+ commits: { ...clusterState.commits, [packet.active_child]: validation.result.commit },
254
+ }, options.repoRoot);
255
+ }
256
+ }
257
+ catch (error) {
258
+ process.stderr.write(`[polaris-worker] cluster-state sync failed for ${packet.cluster_id} / ${packet.active_child}: ${error instanceof Error ? error.message : String(error)}\n`);
259
+ }
260
+ emitWorkerCompletionTelemetry(telemetryFile, {
261
+ event: "worker-complete",
262
+ event_id: (0, node_crypto_1.randomUUID)(),
263
+ run_id: packet.run_id,
264
+ child_id: packet.active_child,
265
+ result_file: resolveRepoPath(options.repoRoot, resultFile),
266
+ status: validation.result.status,
267
+ commit: validation.result.commit,
268
+ timestamp: new Date().toISOString(),
269
+ });
270
+ }
271
+ catch (error) {
272
+ const telemetryFile = resolveRepoPath(options.repoRoot, packet.telemetry_file);
273
+ emitWorkerCompletionTelemetry(telemetryFile, {
274
+ event: "worker-complete-failed",
275
+ event_id: (0, node_crypto_1.randomUUID)(),
276
+ run_id: packet.run_id,
277
+ child_id: packet.active_child,
278
+ result_file: resolveRepoPath(options.repoRoot, resultFile),
279
+ reason: error instanceof Error ? error.message : String(error),
280
+ timestamp: new Date().toISOString(),
281
+ });
282
+ process.stderr.write(`worker complete failed: ${error instanceof Error ? error.message : String(error)}\n`);
283
+ process.exit(1);
284
+ }
285
+ });
286
+ return worker;
287
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "schemaVersion": "2.0",
3
+ "activeCluster": "POL-TEST-1",
4
+ "clusters": {
5
+ "POL-TEST-1": {
6
+ "id": "POL-TEST-1",
7
+ "title": "Test Cluster",
8
+ "children": ["CHILD-1", "CHILD-2"]
9
+ }
10
+ },
11
+ "nodes": {
12
+ "POL-TEST-1": { "id": "POL-TEST-1", "type": "cluster" },
13
+ "CHILD-1": { "id": "CHILD-1", "type": "issue" },
14
+ "CHILD-2": { "id": "CHILD-2", "type": "issue" }
15
+ },
16
+ "dependencies": {
17
+ "CHILD-2": ["CHILD-1"]
18
+ }
19
+ }
@@ -0,0 +1,302 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initializeClusterState = exports.writeClusterStateSync = exports.writeClusterState = exports.readClusterStateSync = exports.readClusterState = void 0;
37
+ const fs_1 = require("fs");
38
+ const path = __importStar(require("path"));
39
+ const local_graph_1 = require("../tracker/local-graph");
40
+ const getClusterStatePath = (clusterId, repoRoot) => {
41
+ return path.join(repoRoot || process.cwd(), '.polaris', 'clusters', clusterId, 'cluster-state.json');
42
+ };
43
+ const normalizeClusterState = (state) => ({
44
+ ...state,
45
+ tracker_mutations: state.tracker_mutations ?? {},
46
+ });
47
+ const readClusterState = async (clusterId, repoRoot) => {
48
+ const filePath = getClusterStatePath(clusterId, repoRoot);
49
+ try {
50
+ const data = await fs_1.promises.readFile(filePath, 'utf-8');
51
+ if (!data) {
52
+ return null;
53
+ }
54
+ return normalizeClusterState(JSON.parse(data));
55
+ }
56
+ catch (error) {
57
+ if (error.code === 'ENOENT') {
58
+ return null;
59
+ }
60
+ throw error;
61
+ }
62
+ };
63
+ exports.readClusterState = readClusterState;
64
+ const readClusterStateSync = (clusterId, repoRoot) => {
65
+ const filePath = getClusterStatePath(clusterId, repoRoot);
66
+ try {
67
+ const data = (0, fs_1.readFileSync)(filePath, 'utf-8');
68
+ if (!data) {
69
+ return null;
70
+ }
71
+ return normalizeClusterState(JSON.parse(data));
72
+ }
73
+ catch (error) {
74
+ if (error.code === 'ENOENT') {
75
+ return null;
76
+ }
77
+ throw error;
78
+ }
79
+ };
80
+ exports.readClusterStateSync = readClusterStateSync;
81
+ const writeClusterState = async (clusterId, state, repoRoot) => {
82
+ const filePath = getClusterStatePath(clusterId, repoRoot);
83
+ const lockFilePath = filePath + '.lock';
84
+ const tempFilePath = `${filePath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
85
+ // Acquire lock (simple lock file approach with retry)
86
+ let lockAcquired = false;
87
+ const maxRetries = 50;
88
+ const retryDelayMs = 100;
89
+ const staleLockThresholdMs = 30000; // 30 seconds
90
+ for (let i = 0; i < maxRetries; i++) {
91
+ try {
92
+ // Try to create lock file exclusively (fails if it already exists)
93
+ await fs_1.promises.writeFile(lockFilePath, String(process.pid), { flag: 'wx' });
94
+ lockAcquired = true;
95
+ break;
96
+ }
97
+ catch (error) {
98
+ if (error.code === 'EEXIST') {
99
+ // Lock file exists, check if it's stale before retrying
100
+ try {
101
+ const lockStat = await fs_1.promises.stat(lockFilePath);
102
+ const lockAge = Date.now() - lockStat.mtimeMs;
103
+ if (lockAge > staleLockThresholdMs) {
104
+ // Lock is stale, try to read the PID
105
+ let isStale = true;
106
+ try {
107
+ const lockContent = await fs_1.promises.readFile(lockFilePath, 'utf-8');
108
+ const lockPid = parseInt(lockContent.trim(), 10);
109
+ // Check if the process is still running
110
+ if (!isNaN(lockPid)) {
111
+ try {
112
+ // process.kill with signal 0 checks existence without killing
113
+ process.kill(lockPid, 0);
114
+ // Process exists, lock is not stale
115
+ isStale = false;
116
+ }
117
+ catch (pidError) {
118
+ // Process doesn't exist (ESRCH) or no permission (EPERM)
119
+ // If EPERM, process exists but we can't signal it, so not stale
120
+ if (pidError.code === 'EPERM') {
121
+ isStale = false;
122
+ }
123
+ // Otherwise (ESRCH), process doesn't exist, lock is stale
124
+ }
125
+ }
126
+ }
127
+ catch {
128
+ // If we can't read the lock file or parse PID, consider it stale based on age
129
+ }
130
+ if (isStale) {
131
+ console.warn(`Removing stale lock file for cluster ${clusterId} (age: ${lockAge}ms)`);
132
+ try {
133
+ await fs_1.promises.unlink(lockFilePath);
134
+ // Retry immediately after removing stale lock
135
+ continue;
136
+ }
137
+ catch (unlinkError) {
138
+ // Race condition: another process may have removed it
139
+ if (unlinkError.code !== 'ENOENT') {
140
+ throw unlinkError;
141
+ }
142
+ }
143
+ }
144
+ }
145
+ }
146
+ catch (statError) {
147
+ // If lock file disappeared between EEXIST and stat, retry
148
+ if (statError.code === 'ENOENT') {
149
+ continue;
150
+ }
151
+ // Other errors during stat, just retry with delay
152
+ }
153
+ // Lock file exists and is not stale, wait and retry
154
+ await new Promise(resolve => setTimeout(resolve, retryDelayMs));
155
+ }
156
+ else {
157
+ throw error;
158
+ }
159
+ }
160
+ }
161
+ if (!lockAcquired) {
162
+ throw new Error(`Failed to acquire lock for cluster state ${clusterId} after ${maxRetries} attempts`);
163
+ }
164
+ try {
165
+ // Re-read current state under lock
166
+ const currentState = await (0, exports.readClusterState)(clusterId, repoRoot);
167
+ if (currentState && currentState.state_generation >= state.state_generation) {
168
+ throw new Error('Stale state: state_generation is not greater than current state.');
169
+ }
170
+ // Write to unique temp file and atomically rename
171
+ await fs_1.promises.writeFile(tempFilePath, JSON.stringify(state, null, 2));
172
+ await fs_1.promises.rename(tempFilePath, filePath);
173
+ }
174
+ finally {
175
+ // Release lock
176
+ try {
177
+ await fs_1.promises.unlink(lockFilePath);
178
+ }
179
+ catch {
180
+ // Ignore errors during lock cleanup
181
+ }
182
+ }
183
+ };
184
+ exports.writeClusterState = writeClusterState;
185
+ const writeClusterStateSync = (clusterId, state, repoRoot) => {
186
+ const filePath = getClusterStatePath(clusterId, repoRoot);
187
+ const lockFilePath = filePath + '.lock';
188
+ const tempFilePath = `${filePath}.tmp.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 8)}`;
189
+ let lockAcquired = false;
190
+ const maxRetries = 50;
191
+ const retryDelayMs = 100;
192
+ const staleLockThresholdMs = 30000;
193
+ for (let i = 0; i < maxRetries; i++) {
194
+ try {
195
+ (0, fs_1.mkdirSync)(path.dirname(lockFilePath), { recursive: true });
196
+ (0, fs_1.writeFileSync)(lockFilePath, String(process.pid), { flag: 'wx' });
197
+ lockAcquired = true;
198
+ break;
199
+ }
200
+ catch (error) {
201
+ if (error.code !== 'EEXIST') {
202
+ throw error;
203
+ }
204
+ try {
205
+ const lockStat = (0, fs_1.statSync)(lockFilePath);
206
+ const lockAge = Date.now() - lockStat.mtimeMs;
207
+ if (lockAge > staleLockThresholdMs) {
208
+ let isStale = true;
209
+ try {
210
+ const lockContent = (0, fs_1.readFileSync)(lockFilePath, 'utf-8');
211
+ const lockPid = parseInt(lockContent.trim(), 10);
212
+ if (!isNaN(lockPid)) {
213
+ try {
214
+ process.kill(lockPid, 0);
215
+ isStale = false;
216
+ }
217
+ catch (pidError) {
218
+ if (pidError.code === 'EPERM') {
219
+ isStale = false;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ catch {
225
+ // Ignore parse/read failure and fall back to age-based stale handling.
226
+ }
227
+ if (isStale) {
228
+ try {
229
+ (0, fs_1.unlinkSync)(lockFilePath);
230
+ continue;
231
+ }
232
+ catch (unlinkError) {
233
+ if (unlinkError.code !== 'ENOENT') {
234
+ throw unlinkError;
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+ catch (statError) {
241
+ if (statError.code === 'ENOENT') {
242
+ continue;
243
+ }
244
+ }
245
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, retryDelayMs);
246
+ }
247
+ }
248
+ if (!lockAcquired) {
249
+ throw new Error(`Failed to acquire lock for cluster state ${clusterId} after ${maxRetries} attempts`);
250
+ }
251
+ try {
252
+ const currentState = (0, exports.readClusterStateSync)(clusterId, repoRoot);
253
+ if (currentState && currentState.state_generation >= state.state_generation) {
254
+ throw new Error('Stale state: state_generation is not greater than current state.');
255
+ }
256
+ (0, fs_1.mkdirSync)(path.dirname(filePath), { recursive: true });
257
+ (0, fs_1.writeFileSync)(tempFilePath, JSON.stringify(state, null, 2));
258
+ (0, fs_1.renameSync)(tempFilePath, filePath);
259
+ }
260
+ finally {
261
+ try {
262
+ (0, fs_1.unlinkSync)(lockFilePath);
263
+ }
264
+ catch {
265
+ // Ignore errors during lock cleanup.
266
+ }
267
+ }
268
+ };
269
+ exports.writeClusterStateSync = writeClusterStateSync;
270
+ const initializeClusterState = async (clusterId, repoRoot) => {
271
+ const graph = await local_graph_1.LocalGraph.load(clusterId, repoRoot);
272
+ const activeCluster = graph.getActiveCluster();
273
+ if (!activeCluster) {
274
+ throw new Error(`Cluster ${clusterId} not found in graph.`);
275
+ }
276
+ // Exclude cluster root from child states — mirrors buildBootstrapPlan logic.
277
+ const clusterRoot = activeCluster.cluster_root;
278
+ const runnableChildren = clusterRoot
279
+ ? activeCluster.children.filter((id) => id !== clusterRoot)
280
+ : activeCluster.children;
281
+ const childrenToInitialize = runnableChildren.length > 0 ? runnableChildren : [clusterId];
282
+ const childStates = childrenToInitialize.map(childId => ({
283
+ id: childId,
284
+ status: 'ready',
285
+ }));
286
+ const initialState = {
287
+ schema_version: '1.0',
288
+ cluster_id: clusterId,
289
+ state_generation: 1,
290
+ child_states: childStates,
291
+ claim_metadata: {},
292
+ packet_pointers: {},
293
+ result_pointers: {},
294
+ validation_results: {},
295
+ commits: {},
296
+ tracker_mutations: {},
297
+ blockers: [],
298
+ };
299
+ await (0, exports.writeClusterState)(clusterId, initialState, repoRoot);
300
+ return initialState;
301
+ };
302
+ exports.initializeClusterState = initializeClusterState;