@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.
- package/README.md +87 -0
- package/dist/__mocks__/tool-server-linear.js +34 -0
- package/dist/cli/adopt-approve.js +60 -0
- package/dist/cli/adopt-cognition.js +199 -0
- package/dist/cli/adopt-instructions.js +143 -0
- package/dist/cli/adopt-rules.js +169 -0
- package/dist/cli/adopt-scan.js +25 -0
- package/dist/cli/adopt-smartdocs.js +192 -0
- package/dist/cli/adoption-inventory.js +350 -0
- package/dist/cli/adoption-plan.js +222 -0
- package/dist/cli/args.js +28 -0
- package/dist/cli/args.test.js +30 -0
- package/dist/cli/branding.js +41 -0
- package/dist/cli/graph.js +358 -0
- package/dist/cli/index.js +127 -0
- package/dist/cli/index.test.js +162 -0
- package/dist/cli/init-detect.js +61 -0
- package/dist/cli/init.js +492 -0
- package/dist/cli/init.test.js +259 -0
- package/dist/cli/librarian.js +32 -0
- package/dist/cli/medic.js +96 -0
- package/dist/cli/startup.test.js +23 -0
- package/dist/cli/tracker.js +130 -0
- package/dist/cli/tracker.test.js +126 -0
- package/dist/cli/version.js +10 -0
- package/dist/cli/version.test.js +11 -0
- package/dist/cli/worker.js +287 -0
- package/dist/cluster-state/fixtures/test-clusters.json +19 -0
- package/dist/cluster-state/store.js +302 -0
- package/dist/cluster-state/store.test.js +145 -0
- package/dist/cluster-state/types.js +2 -0
- package/dist/cognition/archive.js +158 -0
- package/dist/cognition/closeout-librarian-types.js +161 -0
- package/dist/cognition/cognition.test.js +623 -0
- package/dist/cognition/index.js +41 -0
- package/dist/cognition/librarian-dispatch.js +391 -0
- package/dist/cognition/librarian-dispatch.test.js +247 -0
- package/dist/cognition/librarian-packet.js +313 -0
- package/dist/cognition/librarian-types.js +57 -0
- package/dist/cognition/route-cognition-delta.js +350 -0
- package/dist/cognition/summary-delta.js +279 -0
- package/dist/cognition/validate.js +162 -0
- package/dist/config/defaults.js +98 -0
- package/dist/config/doctor.js +273 -0
- package/dist/config/index.js +14 -0
- package/dist/config/loader.js +58 -0
- package/dist/config/provider-detect.js +60 -0
- package/dist/config/provider-detect.test.js +119 -0
- package/dist/config/schema.js +2 -0
- package/dist/config/show.js +44 -0
- package/dist/config/show.test.js +42 -0
- package/dist/config/validator.js +675 -0
- package/dist/config/validator.test.js +77 -0
- package/dist/docs/canon-check.js +334 -0
- package/dist/docs/canon-check.test.js +207 -0
- package/dist/docs/doctrine.js +133 -0
- package/dist/docs/doctrine.test.js +139 -0
- package/dist/docs/index.js +168 -0
- package/dist/docs/index.test.js +83 -0
- package/dist/docs/ingest.js +295 -0
- package/dist/docs/ingest.test.js +71 -0
- package/dist/docs/migrate.js +165 -0
- package/dist/docs/migrate.test.js +136 -0
- package/dist/docs/seed-instructions.js +147 -0
- package/dist/docs/seed-instructions.test.js +137 -0
- package/dist/docs/validate-instructions.js +267 -0
- package/dist/docs/validate-instructions.test.js +238 -0
- package/dist/finalize/artifact-policy.js +214 -0
- package/dist/finalize/artifact-policy.test.js +84 -0
- package/dist/finalize/delivery-integrity.js +141 -0
- package/dist/finalize/finalize.test.js +210 -0
- package/dist/finalize/github.js +41 -0
- package/dist/finalize/index.js +474 -0
- package/dist/finalize/linear.js +213 -0
- package/dist/finalize/run-report.js +47 -0
- package/dist/finalize/steps/01-map-update.js +11 -0
- package/dist/finalize/steps/02-map-validate.js +11 -0
- package/dist/finalize/steps/03-schema-validate.js +11 -0
- package/dist/finalize/steps/04-run-checks.js +118 -0
- package/dist/finalize/steps/05-generate-report.js +14 -0
- package/dist/finalize/steps/06-commit.js +39 -0
- package/dist/finalize/steps/07-push.js +10 -0
- package/dist/finalize/steps/08-create-pr.js +9 -0
- package/dist/finalize/steps/09-update-state.js +9 -0
- package/dist/finalize/steps/10-append-jsonl.js +23 -0
- package/dist/finalize/steps/11-update-linear.js +22 -0
- package/dist/finalize/steps/12-archive.js +21 -0
- package/dist/graph/adapter/c/adapter.js +45 -0
- package/dist/graph/adapter/c/extract.js +135 -0
- package/dist/graph/adapter/c/index.js +10 -0
- package/dist/graph/adapter/c/runtime.js +67 -0
- package/dist/graph/adapter/cpp/adapter.js +45 -0
- package/dist/graph/adapter/cpp/extract.js +159 -0
- package/dist/graph/adapter/cpp/index.js +10 -0
- package/dist/graph/adapter/cpp/runtime.js +61 -0
- package/dist/graph/adapter/csharp/adapter.js +45 -0
- package/dist/graph/adapter/csharp/extract.js +127 -0
- package/dist/graph/adapter/csharp/index.js +10 -0
- package/dist/graph/adapter/csharp/runtime.js +61 -0
- package/dist/graph/adapter/dart/adapter.js +56 -0
- package/dist/graph/adapter/dart/extract.js +130 -0
- package/dist/graph/adapter/dart/index.js +10 -0
- package/dist/graph/adapter/dart/runtime.js +61 -0
- package/dist/graph/adapter/go/adapter.js +45 -0
- package/dist/graph/adapter/go/extract.js +112 -0
- package/dist/graph/adapter/go/index.js +10 -0
- package/dist/graph/adapter/go/runtime.js +58 -0
- package/dist/graph/adapter/kotlin-java/extract-common.js +83 -0
- package/dist/graph/adapter/kotlin-java/index.js +15 -0
- package/dist/graph/adapter/kotlin-java/java-adapter.js +45 -0
- package/dist/graph/adapter/kotlin-java/java-extract.js +42 -0
- package/dist/graph/adapter/kotlin-java/kotlin-adapter.js +48 -0
- package/dist/graph/adapter/kotlin-java/kotlin-extract.js +58 -0
- package/dist/graph/adapter/kotlin-java/runtime.js +75 -0
- package/dist/graph/adapter/python/adapter.js +45 -0
- package/dist/graph/adapter/python/extract.js +121 -0
- package/dist/graph/adapter/python/index.js +10 -0
- package/dist/graph/adapter/python/runtime.js +70 -0
- package/dist/graph/adapter/registry.js +82 -0
- package/dist/graph/adapter/rust/adapter.js +45 -0
- package/dist/graph/adapter/rust/extract.js +110 -0
- package/dist/graph/adapter/rust/index.js +10 -0
- package/dist/graph/adapter/rust/runtime.js +74 -0
- package/dist/graph/adapter/shell/adapter.js +34 -0
- package/dist/graph/adapter/shell/extract.js +98 -0
- package/dist/graph/adapter/shell/index.js +8 -0
- package/dist/graph/adapter/svelte/adapter.js +91 -0
- package/dist/graph/adapter/svelte/extract.js +41 -0
- package/dist/graph/adapter/svelte/index.js +8 -0
- package/dist/graph/adapter/swift/adapter.js +45 -0
- package/dist/graph/adapter/swift/extract.js +120 -0
- package/dist/graph/adapter/swift/index.js +10 -0
- package/dist/graph/adapter/swift/runtime.js +70 -0
- package/dist/graph/adapter/types.js +2 -0
- package/dist/graph/adapter/typescript-javascript/adapter.js +72 -0
- package/dist/graph/adapter/typescript-javascript/extract.js +115 -0
- package/dist/graph/adapter/typescript-javascript/index.js +10 -0
- package/dist/graph/adapter/typescript-javascript/runtime.js +97 -0
- package/dist/graph/capability/index.js +105 -0
- package/dist/graph/governance.js +104 -0
- package/dist/graph/parser/extract.js +5 -0
- package/dist/graph/parser/loader.js +2 -0
- package/dist/graph/parser/pipeline.js +194 -0
- package/dist/graph/query/index.js +277 -0
- package/dist/graph/query/types.js +5 -0
- package/dist/graph/resolver/build-edges.js +211 -0
- package/dist/graph/resolver/index.js +23 -0
- package/dist/graph/resolver/resolve-imports.js +168 -0
- package/dist/graph/store/adapter.js +132 -0
- package/dist/graph/store/queries.js +124 -0
- package/dist/graph/store/types.js +4 -0
- package/dist/ignore/defaults.js +41 -0
- package/dist/ignore/parser.js +39 -0
- package/dist/lint/map-reference-validator.js +72 -0
- package/dist/loop/abort.js +188 -0
- package/dist/loop/abort.test.js +151 -0
- package/dist/loop/adapters/__tests__/terminal-cli.test.js +92 -0
- package/dist/loop/adapters/agent-subtask.js +137 -0
- package/dist/loop/adapters/agent-subtask.test.js +151 -0
- package/dist/loop/adapters/cli-subtask-bridge.js +108 -0
- package/dist/loop/adapters/index.js +9 -0
- package/dist/loop/adapters/registry.js +17 -0
- package/dist/loop/adapters/terminal-cli.js +403 -0
- package/dist/loop/adapters/types.js +2 -0
- package/dist/loop/adapters/worker-instructions.js +85 -0
- package/dist/loop/body-parser.js +226 -0
- package/dist/loop/bootstrap-packet.js +97 -0
- package/dist/loop/bootstrap-packet.test.js +171 -0
- package/dist/loop/branch-governance.js +54 -0
- package/dist/loop/budget.js +63 -0
- package/dist/loop/budget.test.js +163 -0
- package/dist/loop/checkpoint.js +325 -0
- package/dist/loop/compact-return.js +59 -0
- package/dist/loop/continue.js +645 -0
- package/dist/loop/continue.test.js +337 -0
- package/dist/loop/dispatch-boundary.js +335 -0
- package/dist/loop/dispatch-state.js +223 -0
- package/dist/loop/dispatch-state.test.js +186 -0
- package/dist/loop/dispatch.js +1236 -0
- package/dist/loop/dispatch.test.js +499 -0
- package/dist/loop/ephemeral-smoke.test.js +160 -0
- package/dist/loop/evidence-backfill.js +216 -0
- package/dist/loop/execution-adapter.js +141 -0
- package/dist/loop/execution-adapter.test.js +66 -0
- package/dist/loop/finalize-evidence.js +169 -0
- package/dist/loop/git-custody.js +250 -0
- package/dist/loop/impl-packet-input.js +38 -0
- package/dist/loop/index.js +270 -0
- package/dist/loop/ledger.js +49 -0
- package/dist/loop/ledger.test.js +127 -0
- package/dist/loop/lifecycle-dispatch.js +348 -0
- package/dist/loop/lifecycle-dispatch.test.js +318 -0
- package/dist/loop/lifecycle.js +93 -0
- package/dist/loop/lifecycle.test.js +150 -0
- package/dist/loop/orphan-recovery.js +276 -0
- package/dist/loop/orphan-recovery.test.js +236 -0
- package/dist/loop/parent.js +1566 -0
- package/dist/loop/parent.test.js +738 -0
- package/dist/loop/provider-sealed-packet.test.js +164 -0
- package/dist/loop/resume.js +295 -0
- package/dist/loop/resume.test.js +172 -0
- package/dist/loop/run-bootstrap.js +234 -0
- package/dist/loop/run-preflight.js +203 -0
- package/dist/loop/status.js +665 -0
- package/dist/loop/status.test.js +235 -0
- package/dist/loop/wait.js +86 -0
- package/dist/loop/worker-packet.js +406 -0
- package/dist/loop/worker-packet.test.js +243 -0
- package/dist/loop/worker-prompt.js +260 -0
- package/dist/loop/worker-prompt.test.js +247 -0
- package/dist/loop/worker.js +498 -0
- package/dist/loop/worker.test.js +372 -0
- package/dist/map/atlas.js +77 -0
- package/dist/map/backfill.js +171 -0
- package/dist/map/backfill.test.js +120 -0
- package/dist/map/index.js +255 -0
- package/dist/map/inference.js +73 -0
- package/dist/map/inference.test.js +35 -0
- package/dist/map/query.js +157 -0
- package/dist/map/query.test.js +174 -0
- package/dist/map/update.js +355 -0
- package/dist/map/update.test.js +67 -0
- package/dist/map/validate.js +153 -0
- package/dist/map/validate.test.js +90 -0
- package/dist/map/welfare.js +86 -0
- package/dist/mcp/lib/invoke.js +35 -0
- package/dist/mcp/lib/redact.js +24 -0
- package/dist/mcp/lib/redact.test.js +112 -0
- package/dist/mcp/lib/root.js +41 -0
- package/dist/mcp/lib/root.test.js +114 -0
- package/dist/mcp/server.js +15 -0
- package/dist/mcp/tools/claim-child.js +119 -0
- package/dist/mcp/tools/claim-child.test.js +69 -0
- package/dist/mcp/tools/current-state.js +73 -0
- package/dist/mcp/tools/current-state.test.js +114 -0
- package/dist/mcp/tools/dispatch-result.js +186 -0
- package/dist/mcp/tools/dispatch-result.test.js +109 -0
- package/dist/mcp/tools/index.js +222 -0
- package/dist/mcp/tools/index.test.js +172 -0
- package/dist/mcp/tools/linear.js +74 -0
- package/dist/mcp/tools/loop-continue.js +159 -0
- package/dist/mcp/tools/loop-continue.test.js +186 -0
- package/dist/mcp/tools/loop-dry-run.js +15 -0
- package/dist/mcp/tools/status.js +65 -0
- package/dist/medic/chart-id.js +75 -0
- package/dist/medic/chart-schema.js +139 -0
- package/dist/runs/index.js +119 -0
- package/dist/runs/index.test.js +184 -0
- package/dist/runs/ledger-tail.js +15 -0
- package/dist/runs/list.js +29 -0
- package/dist/runs/reconcile.js +74 -0
- package/dist/runs/show.js +10 -0
- package/dist/runtime/audit/logger.js +15 -0
- package/dist/runtime/audit/reader.js +42 -0
- package/dist/runtime/checkpoint.js +89 -0
- package/dist/runtime/continuation/confirmed.js +225 -0
- package/dist/runtime/continuation/confirmed.test.js +421 -0
- package/dist/runtime/continuation/dry-run.js +74 -0
- package/dist/runtime/execution-window.js +106 -0
- package/dist/runtime/scheduling/child-selector.js +16 -0
- package/dist/runtime/state.js +43 -0
- package/dist/runtime/status.js +46 -0
- package/dist/runtime/verification/envelope.js +80 -0
- package/dist/runtime/verification/fingerprint.js +26 -0
- package/dist/skill-packet/cli.test.js +105 -0
- package/dist/skill-packet/generator.js +225 -0
- package/dist/skill-packet/generator.test.js +159 -0
- package/dist/skill-packet/index.js +49 -0
- package/dist/skill-packet/types.js +2 -0
- package/dist/smartdocs-engine/audit.js +194 -0
- package/dist/smartdocs-engine/audit.test.js +122 -0
- package/dist/smartdocs-engine/canon-check.js +340 -0
- package/dist/smartdocs-engine/canon-check.test.js +234 -0
- package/dist/smartdocs-engine/doctrine.js +633 -0
- package/dist/smartdocs-engine/doctrine.test.js +374 -0
- package/dist/smartdocs-engine/index.js +456 -0
- package/dist/smartdocs-engine/index.test.js +82 -0
- package/dist/smartdocs-engine/ingest.js +495 -0
- package/dist/smartdocs-engine/ingest.test.js +163 -0
- package/dist/smartdocs-engine/migrate.js +217 -0
- package/dist/smartdocs-engine/migrate.test.js +200 -0
- package/dist/smartdocs-engine/seed-instructions.js +335 -0
- package/dist/smartdocs-engine/seed-instructions.test.js +275 -0
- package/dist/smartdocs-engine/smartdoc-ignore.js +258 -0
- package/dist/smartdocs-engine/smartdoc-ignore.test.js +146 -0
- package/dist/smartdocs-engine/validate-instructions.js +366 -0
- package/dist/smartdocs-engine/validate-instructions.test.js +283 -0
- package/dist/tracker/adapters/github/index.js +305 -0
- package/dist/tracker/adapters/jira/index.js +293 -0
- package/dist/tracker/adapters/linear/http-errors.test.js +51 -0
- package/dist/tracker/adapters/linear/index.js +621 -0
- package/dist/tracker/adapters/linear/index.test.js +238 -0
- package/dist/tracker/adapters/linear.js +50 -0
- package/dist/tracker/adapters/local/index.js +10 -0
- package/dist/tracker/adapters/local-file/index.js +159 -0
- package/dist/tracker/adapters/mcp-bridge.js +158 -0
- package/dist/tracker/adapters/mcp-bridge.test.js +12 -0
- package/dist/tracker/adapters/spec/index.js +146 -0
- package/dist/tracker/capabilities.js +2 -0
- package/dist/tracker/index.js +49 -0
- package/dist/tracker/lifecycle-policy.js +139 -0
- package/dist/tracker/lifecycle-transition.js +164 -0
- package/dist/tracker/local-graph.js +98 -0
- package/dist/tracker/migration.js +56 -0
- package/dist/tracker/schema.js +86 -0
- package/dist/tracker/sync/index.js +404 -0
- package/dist/tracker/sync/queue-store.js +95 -0
- package/dist/tracker/sync/queue-store.test.js +104 -0
- package/dist/tracker/types.js +11 -0
- package/dist/types/linear.js +3 -0
- package/dist/types/result-packet.js +12 -0
- package/dist/types/runtime-state.js +2 -0
- package/dist/types/work-contract.js +2 -0
- package/dist/utils/monotonic-timestamp.js +13 -0
- package/dist/utils/monotonic-timestamp.test.js +19 -0
- package/package.json +64 -0
|
@@ -0,0 +1,164 @@
|
|
|
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 parent_js_1 = require("./parent.js");
|
|
8
|
+
const run_bootstrap_js_1 = require("./run-bootstrap.js");
|
|
9
|
+
vitest_1.vi.mock("./adapters/registry.js", () => ({
|
|
10
|
+
createAdapter: vitest_1.vi.fn(),
|
|
11
|
+
}));
|
|
12
|
+
vitest_1.vi.mock("../config/loader.js", () => ({
|
|
13
|
+
loadConfig: vitest_1.vi.fn(() => ({
|
|
14
|
+
execution: {
|
|
15
|
+
adapter: "terminal-cli",
|
|
16
|
+
providers: { "agent-subtask": { command: "native-subtask" } },
|
|
17
|
+
rotation: ["terminal-cli"],
|
|
18
|
+
},
|
|
19
|
+
budget: {
|
|
20
|
+
max_children_per_session: 5,
|
|
21
|
+
allow_analyze_children: false,
|
|
22
|
+
},
|
|
23
|
+
})),
|
|
24
|
+
}));
|
|
25
|
+
const registry_js_1 = require("./adapters/registry.js");
|
|
26
|
+
function writeState(dir, overrides) {
|
|
27
|
+
const stateFile = (0, node_path_1.join)(dir, "current-state.json");
|
|
28
|
+
const runId = "sealed-packet-smoke-run";
|
|
29
|
+
const clusterId = overrides["cluster_id"] ?? "POL-197";
|
|
30
|
+
const openChildren = overrides["open_children"] ?? [];
|
|
31
|
+
const state = {
|
|
32
|
+
schema_version: "1.0",
|
|
33
|
+
run_id: runId,
|
|
34
|
+
cluster_id: clusterId,
|
|
35
|
+
skill: "polaris-run",
|
|
36
|
+
artifact_dir: dir,
|
|
37
|
+
branch: "feature/pol-197",
|
|
38
|
+
current_step_id: "03-execute-child",
|
|
39
|
+
step_cursor: "dispatching",
|
|
40
|
+
status: "executing",
|
|
41
|
+
session_type: "implementation",
|
|
42
|
+
active_child: "",
|
|
43
|
+
last_commit: "",
|
|
44
|
+
next_open_child: null,
|
|
45
|
+
completed_children: [],
|
|
46
|
+
open_children: [],
|
|
47
|
+
open_children_meta: {},
|
|
48
|
+
context_budget: {
|
|
49
|
+
children_completed: 0,
|
|
50
|
+
files_touched_total: 0,
|
|
51
|
+
max_children_per_session: 5,
|
|
52
|
+
},
|
|
53
|
+
dispatch_boundary: { dispatch_epoch: 0, continue_epoch: 0, last_dispatched_child: null },
|
|
54
|
+
run_bootstrap_seal: (0, run_bootstrap_js_1.createBootstrapSeal)(runId, clusterId, openChildren),
|
|
55
|
+
updated_at: "2026-05-26T00:00:00.000Z",
|
|
56
|
+
...overrides,
|
|
57
|
+
};
|
|
58
|
+
(0, node_fs_1.writeFileSync)(stateFile, JSON.stringify(state, null, 2), "utf-8");
|
|
59
|
+
return stateFile;
|
|
60
|
+
}
|
|
61
|
+
function readJsonLines(path) {
|
|
62
|
+
return (0, node_fs_1.readFileSync)(path, "utf-8")
|
|
63
|
+
.trim()
|
|
64
|
+
.split("\n")
|
|
65
|
+
.filter(Boolean)
|
|
66
|
+
.map((line) => JSON.parse(line));
|
|
67
|
+
}
|
|
68
|
+
function makeSealedPacketAdapter(calls, resultFilePath) {
|
|
69
|
+
return {
|
|
70
|
+
name: "agent-subtask",
|
|
71
|
+
async dispatch(packet, options) {
|
|
72
|
+
calls.push({ packet, options });
|
|
73
|
+
// Simulate the worker writing its sealed result to the file
|
|
74
|
+
const workerPacket = packet;
|
|
75
|
+
if (workerPacket.result_file_contract?.result_file) {
|
|
76
|
+
const sealedResult = {
|
|
77
|
+
run_id: workerPacket.run_id,
|
|
78
|
+
child_id: workerPacket.active_child,
|
|
79
|
+
status: "success",
|
|
80
|
+
commit: "sealed-commit-123",
|
|
81
|
+
validation: { message: "sealed packet validation passed" },
|
|
82
|
+
};
|
|
83
|
+
(0, node_fs_1.writeFileSync)(workerPacket.result_file_contract.result_file, JSON.stringify(sealedResult, null, 2), "utf-8");
|
|
84
|
+
}
|
|
85
|
+
const dispatchResult = {
|
|
86
|
+
exit_code: 0,
|
|
87
|
+
provider_used: "agent-subtask",
|
|
88
|
+
command_run: `agent-subtask:${packet.active_child}`,
|
|
89
|
+
// No summary returned for sealed packets, result is in file
|
|
90
|
+
summary: "",
|
|
91
|
+
};
|
|
92
|
+
return dispatchResult;
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
(0, vitest_1.describe)("provider smoke tests for sealed local packets", () => {
|
|
97
|
+
let tmpDir;
|
|
98
|
+
(0, vitest_1.beforeEach)(() => {
|
|
99
|
+
tmpDir = (0, node_fs_1.mkdtempSync)((0, node_path_1.join)((0, node_os_1.tmpdir)(), "polaris-sealed-packet-smoke-"));
|
|
100
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(tmpDir, "runs", "sealed-packet-smoke-run"), { recursive: true });
|
|
101
|
+
vitest_1.vi.clearAllMocks();
|
|
102
|
+
});
|
|
103
|
+
(0, vitest_1.afterEach)(() => {
|
|
104
|
+
(0, node_fs_1.rmSync)(tmpDir, { recursive: true, force: true });
|
|
105
|
+
});
|
|
106
|
+
(0, vitest_1.it)("dispatches a child with a sealed packet contract and processes its result file", async () => {
|
|
107
|
+
const calls = [];
|
|
108
|
+
const childId = "POL-200";
|
|
109
|
+
const resultFilePath = (0, node_path_1.join)(tmpDir, "runs", "sealed-packet-smoke-run", `${childId}-result.json`);
|
|
110
|
+
vitest_1.vi.mocked(registry_js_1.createAdapter).mockReturnValue(makeSealedPacketAdapter(calls, resultFilePath));
|
|
111
|
+
const stateFile = writeState(tmpDir, {
|
|
112
|
+
orchestration_mode: "ephemeral",
|
|
113
|
+
open_children: [childId],
|
|
114
|
+
next_open_child: childId,
|
|
115
|
+
open_children_meta: {
|
|
116
|
+
[childId]: {
|
|
117
|
+
title: "IMPLEMENT: Sealed Packet Test Child",
|
|
118
|
+
result_file: resultFilePath,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const result = await (0, parent_js_1.runParentLoop)({ stateFile, repoRoot: tmpDir });
|
|
123
|
+
(0, vitest_1.expect)(result.haltReason).toBe("cluster-complete");
|
|
124
|
+
(0, vitest_1.expect)(result.childrenDispatched).toBe(1);
|
|
125
|
+
(0, vitest_1.expect)(registry_js_1.createAdapter).toHaveBeenCalledWith("agent-subtask", vitest_1.expect.objectContaining({ adapter: "agent-subtask" }));
|
|
126
|
+
(0, vitest_1.expect)(calls).toHaveLength(1);
|
|
127
|
+
const dispatchedPacket = calls[0].packet;
|
|
128
|
+
(0, vitest_1.expect)(dispatchedPacket.active_child).toBe(childId);
|
|
129
|
+
(0, vitest_1.expect)(dispatchedPacket.result_file_contract?.result_file).toBe(resultFilePath);
|
|
130
|
+
// Verify the mock worker wrote the sealed result file
|
|
131
|
+
(0, vitest_1.expect)((0, node_fs_1.readFileSync)(resultFilePath, "utf-8")).toBe(JSON.stringify({
|
|
132
|
+
run_id: "sealed-packet-smoke-run",
|
|
133
|
+
child_id: childId,
|
|
134
|
+
status: "success",
|
|
135
|
+
commit: "sealed-commit-123",
|
|
136
|
+
validation: { message: "sealed packet validation passed" },
|
|
137
|
+
}, null, 2));
|
|
138
|
+
// Verify parent processed the sealed result and updated state and telemetry
|
|
139
|
+
const updatedState = JSON.parse((0, node_fs_1.readFileSync)(stateFile, "utf-8"));
|
|
140
|
+
(0, vitest_1.expect)(updatedState.status).toBe("cluster-complete");
|
|
141
|
+
(0, vitest_1.expect)(updatedState.completed_children).toEqual([childId]);
|
|
142
|
+
(0, vitest_1.expect)(updatedState.open_children).toEqual([]);
|
|
143
|
+
(0, vitest_1.expect)(updatedState.next_open_child).toBeNull();
|
|
144
|
+
(0, vitest_1.expect)(updatedState.last_commit).toBe("sealed-commit-123");
|
|
145
|
+
(0, vitest_1.expect)(updatedState.context_budget).toMatchObject({ children_completed: 1 });
|
|
146
|
+
const telemetry = readJsonLines((0, node_path_1.join)(tmpDir, "runs", "sealed-packet-smoke-run", "telemetry.jsonl"));
|
|
147
|
+
(0, vitest_1.expect)(telemetry).toEqual(vitest_1.expect.arrayContaining([
|
|
148
|
+
vitest_1.expect.objectContaining({
|
|
149
|
+
event: "child-dispatched",
|
|
150
|
+
child_id: childId,
|
|
151
|
+
adapter: "agent-subtask",
|
|
152
|
+
orchestration_mode: "ephemeral",
|
|
153
|
+
provider: "agent-subtask",
|
|
154
|
+
}),
|
|
155
|
+
vitest_1.expect.objectContaining({
|
|
156
|
+
event: "child-complete",
|
|
157
|
+
child_id: childId,
|
|
158
|
+
children_completed: 1,
|
|
159
|
+
validation_summary: { message: "sealed packet validation passed" },
|
|
160
|
+
commit_hash: "sealed-commit-123",
|
|
161
|
+
}),
|
|
162
|
+
]));
|
|
163
|
+
});
|
|
164
|
+
});
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runLoopResume = runLoopResume;
|
|
4
|
+
const node_fs_1 = require("node:fs");
|
|
5
|
+
const node_path_1 = require("node:path");
|
|
6
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const loader_js_1 = require("../config/loader.js");
|
|
9
|
+
const checkpoint_js_1 = require("./checkpoint.js");
|
|
10
|
+
const store_js_1 = require("../cluster-state/store.js");
|
|
11
|
+
const ledger_js_1 = require("./ledger.js");
|
|
12
|
+
function readPacket(bootstrapDir, runId) {
|
|
13
|
+
let entries;
|
|
14
|
+
try {
|
|
15
|
+
entries = (0, node_fs_1.readdirSync)(bootstrapDir).filter((f) => f.endsWith(".json"));
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new Error(`Cannot read bootstrap directory: ${bootstrapDir}`);
|
|
19
|
+
}
|
|
20
|
+
if (entries.length === 0) {
|
|
21
|
+
throw new Error(`No bootstrap packets found in ${bootstrapDir}`);
|
|
22
|
+
}
|
|
23
|
+
let target;
|
|
24
|
+
if (runId) {
|
|
25
|
+
const matches = entries.filter((f) => f.startsWith(`${runId}-`)).sort();
|
|
26
|
+
if (matches.length === 0) {
|
|
27
|
+
throw new Error(`No bootstrap packet found for run_id "${runId}" in ${bootstrapDir}`);
|
|
28
|
+
}
|
|
29
|
+
target = matches.at(-1);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Pick the most recently written packet (last alphabetically by timestamp suffix)
|
|
33
|
+
target = entries.sort().at(-1);
|
|
34
|
+
}
|
|
35
|
+
const raw = (0, node_fs_1.readFileSync)((0, node_path_1.join)(bootstrapDir, target), "utf-8");
|
|
36
|
+
return JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
function verifyBranch(branch, repoRoot) {
|
|
39
|
+
try {
|
|
40
|
+
(0, node_child_process_1.execFileSync)("git", ["rev-parse", "--verify", branch], {
|
|
41
|
+
cwd: repoRoot,
|
|
42
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`branch not found: ${branch}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function computeStateSha(stateFile) {
|
|
50
|
+
const content = (0, node_fs_1.readFileSync)(stateFile, "utf-8");
|
|
51
|
+
return (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
|
|
52
|
+
}
|
|
53
|
+
const COMPLETED_CHILD_STATUSES = new Set(["done", "finalized"]);
|
|
54
|
+
const OPEN_CHILD_STATUSES = new Set([
|
|
55
|
+
"ready",
|
|
56
|
+
"claimed",
|
|
57
|
+
"dispatched",
|
|
58
|
+
"running",
|
|
59
|
+
"blocked",
|
|
60
|
+
"reviewed",
|
|
61
|
+
]);
|
|
62
|
+
function findClusterStateForPacket(repoRoot, packet) {
|
|
63
|
+
const clustersDir = (0, node_path_1.resolve)(repoRoot, ".polaris", "clusters");
|
|
64
|
+
const hints = new Set([packet.last_completed_child, ...packet.open_children].filter((value) => typeof value === "string" && value.length > 0));
|
|
65
|
+
let entries;
|
|
66
|
+
try {
|
|
67
|
+
entries = (0, node_fs_1.readdirSync)(clustersDir, { withFileTypes: true })
|
|
68
|
+
.filter((entry) => entry.isDirectory())
|
|
69
|
+
.map((entry) => entry.name);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
throw new Error(`cannot reconstruct state: no cluster-state.json found under ${clustersDir}`);
|
|
73
|
+
}
|
|
74
|
+
const matches = entries
|
|
75
|
+
.map((clusterId) => ({
|
|
76
|
+
state: (0, store_js_1.readClusterStateSync)(clusterId, repoRoot),
|
|
77
|
+
score: 0,
|
|
78
|
+
}))
|
|
79
|
+
.filter((candidate) => candidate.state !== null)
|
|
80
|
+
.map((candidate) => ({
|
|
81
|
+
...candidate,
|
|
82
|
+
score: candidate.state.child_states.filter((child) => hints.has(child.id)).length,
|
|
83
|
+
}))
|
|
84
|
+
.filter((candidate) => candidate.score > 0)
|
|
85
|
+
.sort((a, b) => b.score - a.score);
|
|
86
|
+
if (matches.length === 0) {
|
|
87
|
+
throw new Error("cannot reconstruct state: no cluster-state.json matches the bootstrap packet children");
|
|
88
|
+
}
|
|
89
|
+
if (matches.length > 1 && matches[0].score === matches[1].score) {
|
|
90
|
+
throw new Error("cannot reconstruct state: multiple cluster-state.json files match the bootstrap packet children");
|
|
91
|
+
}
|
|
92
|
+
return matches[0].state;
|
|
93
|
+
}
|
|
94
|
+
function rebuildLoopStateFromClusterState(packet, clusterState, repoRoot) {
|
|
95
|
+
const clusterChildren = new Set(clusterState.child_states.map((child) => child.id));
|
|
96
|
+
const completedChildren = clusterState.child_states
|
|
97
|
+
.filter((child) => COMPLETED_CHILD_STATUSES.has(child.status))
|
|
98
|
+
.map((child) => child.id);
|
|
99
|
+
const openChildren = packet.open_children.filter((childId) => clusterChildren.has(childId));
|
|
100
|
+
const fallbackOpenChildren = openChildren.length > 0
|
|
101
|
+
? openChildren
|
|
102
|
+
: clusterState.child_states
|
|
103
|
+
.filter((child) => OPEN_CHILD_STATUSES.has(child.status))
|
|
104
|
+
.map((child) => child.id);
|
|
105
|
+
const activeChild = fallbackOpenChildren.find((childId) => {
|
|
106
|
+
const status = clusterState.child_states.find((child) => child.id === childId)?.status;
|
|
107
|
+
return status === "claimed" || status === "dispatched" || status === "running";
|
|
108
|
+
}) ??
|
|
109
|
+
fallbackOpenChildren[0] ??
|
|
110
|
+
packet.last_completed_child;
|
|
111
|
+
const lastCommit = clusterState.commits[packet.last_completed_child] ??
|
|
112
|
+
Object.values(clusterState.commits).at(-1);
|
|
113
|
+
return {
|
|
114
|
+
schema_version: "1.0",
|
|
115
|
+
run_id: packet.run_id,
|
|
116
|
+
cluster_id: clusterState.cluster_id,
|
|
117
|
+
branch: packet.branch,
|
|
118
|
+
session_type: "implement",
|
|
119
|
+
active_child: activeChild,
|
|
120
|
+
completed_children: completedChildren,
|
|
121
|
+
open_children: fallbackOpenChildren,
|
|
122
|
+
step_cursor: packet.last_completed_step,
|
|
123
|
+
context_budget: {
|
|
124
|
+
children_completed: packet.context_budget.children_completed,
|
|
125
|
+
},
|
|
126
|
+
status: "running",
|
|
127
|
+
last_commit: lastCommit,
|
|
128
|
+
next_open_child: fallbackOpenChildren[0] ?? null,
|
|
129
|
+
artifact_dir: (0, node_path_1.resolve)(repoRoot, ".taskchain_artifacts", "polaris-run"),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function resolveResumeState(repoRoot, packet, stateFile) {
|
|
133
|
+
if ((0, node_fs_1.existsSync)(stateFile)) {
|
|
134
|
+
return {
|
|
135
|
+
packet,
|
|
136
|
+
state: (0, checkpoint_js_1.readState)(stateFile),
|
|
137
|
+
stateFile,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
const clusterState = findClusterStateForPacket(repoRoot, packet);
|
|
141
|
+
const rebuiltState = rebuildLoopStateFromClusterState(packet, clusterState, repoRoot);
|
|
142
|
+
const rebuiltSha = (0, checkpoint_js_1.writeStateAtomic)(stateFile, rebuiltState);
|
|
143
|
+
const emittedPacket = {
|
|
144
|
+
...packet,
|
|
145
|
+
artifact_pointers: {
|
|
146
|
+
...packet.artifact_pointers,
|
|
147
|
+
current_state: (0, node_path_1.relative)(repoRoot, stateFile),
|
|
148
|
+
},
|
|
149
|
+
current_state_sha: rebuiltSha,
|
|
150
|
+
};
|
|
151
|
+
return {
|
|
152
|
+
packet: emittedPacket,
|
|
153
|
+
state: rebuiltState,
|
|
154
|
+
stateFile,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function getHeadSha(repoRoot) {
|
|
158
|
+
try {
|
|
159
|
+
return (0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], {
|
|
160
|
+
cwd: repoRoot,
|
|
161
|
+
encoding: "utf-8",
|
|
162
|
+
}).trim();
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return "";
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function normalizeRunType(sessionType) {
|
|
169
|
+
return sessionType === "analyze" ? "analyze" : "implement";
|
|
170
|
+
}
|
|
171
|
+
function ledgerLastCommit(state) {
|
|
172
|
+
return state.last_commit && state.last_commit.length > 0 ? state.last_commit : null;
|
|
173
|
+
}
|
|
174
|
+
function appendResumedLedgerEvent(repoRoot, packet, state) {
|
|
175
|
+
const completedChildren = Array.isArray(state.completed_children)
|
|
176
|
+
? state.completed_children
|
|
177
|
+
: [];
|
|
178
|
+
const openChildren = Array.isArray(state.open_children)
|
|
179
|
+
? state.open_children
|
|
180
|
+
: packet.open_children;
|
|
181
|
+
new ledger_js_1.LedgerWriter((0, node_path_1.join)(repoRoot, ledger_js_1.DEFAULT_LEDGER_PATH)).append({
|
|
182
|
+
schema_version: 1,
|
|
183
|
+
event_id: (0, node_crypto_1.randomUUID)(),
|
|
184
|
+
event: "run-resumed",
|
|
185
|
+
run_id: packet.run_id,
|
|
186
|
+
run_type: normalizeRunType(state.session_type),
|
|
187
|
+
cluster_id: state.cluster_id ?? null,
|
|
188
|
+
issue_id: state.active_child || null,
|
|
189
|
+
branch: state.branch ?? packet.branch,
|
|
190
|
+
status: "running",
|
|
191
|
+
completed_children: completedChildren,
|
|
192
|
+
open_children: openChildren,
|
|
193
|
+
next_child: state.next_open_child ?? openChildren[0] ?? null,
|
|
194
|
+
last_commit: ledgerLastCommit(state),
|
|
195
|
+
pr_url: null,
|
|
196
|
+
timestamp: new Date().toISOString(),
|
|
197
|
+
resume_source: "bootstrap",
|
|
198
|
+
resume_reason: "polaris loop resume selected bootstrap packet",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function runLoopResume(options) {
|
|
202
|
+
const { runId, repoRoot } = options;
|
|
203
|
+
const config = (0, loader_js_1.loadConfig)(repoRoot);
|
|
204
|
+
const bootstrapDir = (0, node_path_1.resolve)(repoRoot, config.loop.bootstrapOutputPath ?? ".polaris/bootstrap");
|
|
205
|
+
// Step 1: Read bootstrap packet
|
|
206
|
+
let packet;
|
|
207
|
+
try {
|
|
208
|
+
packet = readPacket(bootstrapDir, runId);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
// Step 2: Verify branch exists
|
|
215
|
+
try {
|
|
216
|
+
verifyBranch(packet.branch, repoRoot);
|
|
217
|
+
}
|
|
218
|
+
catch (err) {
|
|
219
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
// Step 3: Verify current-state SHA
|
|
223
|
+
const stateFile = options.stateFile ??
|
|
224
|
+
(packet.artifact_pointers.current_state.startsWith("/")
|
|
225
|
+
? packet.artifact_pointers.current_state
|
|
226
|
+
: (0, node_path_1.resolve)(repoRoot, packet.artifact_pointers.current_state));
|
|
227
|
+
let resumePacket = packet;
|
|
228
|
+
let resumeState;
|
|
229
|
+
try {
|
|
230
|
+
const resolved = resolveResumeState(repoRoot, packet, stateFile);
|
|
231
|
+
resumePacket = resolved.packet;
|
|
232
|
+
resumeState = resolved.state;
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
console.error(`Error: ${err instanceof Error ? err.message : String(err)}`);
|
|
236
|
+
process.exit(1);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
const actualSha = computeStateSha(stateFile);
|
|
240
|
+
if (actualSha !== resumePacket.current_state_sha) {
|
|
241
|
+
// A SHA mismatch normally means the state drifted after the bootstrap
|
|
242
|
+
// packet was written — "state packet stale".
|
|
243
|
+
//
|
|
244
|
+
// Exception: `loop abort` legitimately rewrites current-state.json after
|
|
245
|
+
// the packet is recorded (it clears active_child, balances dispatch epochs,
|
|
246
|
+
// and sets status to "blocked"). Resume is safe when the state is
|
|
247
|
+
// blocked-idle: no active dispatch and no outstanding worker.
|
|
248
|
+
if (resumeState.status === "blocked") {
|
|
249
|
+
if (resumeState.active_child && resumeState.active_child !== "") {
|
|
250
|
+
console.error("cannot resume: status is blocked with active_child set — resolve the active dispatch first");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const boundary = resumeState.dispatch_boundary;
|
|
254
|
+
if (!boundary) {
|
|
255
|
+
console.error("cannot resume: status is blocked but dispatch_boundary is missing — state is inconsistent");
|
|
256
|
+
process.exit(1);
|
|
257
|
+
}
|
|
258
|
+
if (boundary.dispatch_epoch !== boundary.continue_epoch) {
|
|
259
|
+
console.error("cannot resume: status is blocked with unbalanced dispatch boundary — a dispatch is still pending");
|
|
260
|
+
process.exit(1);
|
|
261
|
+
}
|
|
262
|
+
// Blocked-idle: safe to proceed. Step 5 below will clear the blocked status.
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
console.error("state packet stale — re-run `polaris loop status` to verify current state");
|
|
266
|
+
process.exit(1);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
// Step 4: Check allowBranchDivergence
|
|
270
|
+
const allowDivergence = config.loop.allowBranchDivergence ?? false;
|
|
271
|
+
if (!allowDivergence && packet.base_commit_sha) {
|
|
272
|
+
const headSha = getHeadSha(repoRoot);
|
|
273
|
+
if (headSha && headSha !== packet.base_commit_sha) {
|
|
274
|
+
console.error(`branch HEAD changed since checkpoint (expected ${packet.base_commit_sha}, got ${headSha})`);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Step 5: Clear blocked status if state was blocked
|
|
279
|
+
let ledgerState = resumeState;
|
|
280
|
+
if (resumeState.status === "blocked") {
|
|
281
|
+
ledgerState = {
|
|
282
|
+
...resumeState,
|
|
283
|
+
status: "running",
|
|
284
|
+
blocker: undefined,
|
|
285
|
+
};
|
|
286
|
+
const updatedSha = (0, checkpoint_js_1.writeStateAtomic)(stateFile, ledgerState);
|
|
287
|
+
resumePacket = {
|
|
288
|
+
...resumePacket,
|
|
289
|
+
current_state_sha: updatedSha,
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
appendResumedLedgerEvent(repoRoot, resumePacket, ledgerState);
|
|
293
|
+
// Step 6: Emit bootstrap packet to stdout, exit 0
|
|
294
|
+
console.log(JSON.stringify(resumePacket, null, 2));
|
|
295
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
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_crypto_1 = require("node:crypto");
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const resume_js_1 = require("./resume.js");
|
|
10
|
+
function getHeadSha(dir) {
|
|
11
|
+
try {
|
|
12
|
+
return (0, node_child_process_1.execFileSync)("git", ["rev-parse", "HEAD"], { cwd: dir, encoding: "utf-8" }).trim();
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function getCurrentBranch(dir) {
|
|
19
|
+
try {
|
|
20
|
+
return (0, node_child_process_1.execFileSync)("git", ["branch", "--show-current"], { cwd: dir, encoding: "utf-8" }).trim();
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return "main";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function makeTestDir() {
|
|
27
|
+
const dir = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `polaris-resume-test-${Date.now()}`);
|
|
28
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
29
|
+
(0, node_child_process_1.execFileSync)("git", ["init"], { cwd: dir });
|
|
30
|
+
(0, node_child_process_1.execFileSync)("git", ["config", "user.email", "test@test.com"], { cwd: dir });
|
|
31
|
+
(0, node_child_process_1.execFileSync)("git", ["config", "user.name", "Test"], { cwd: dir });
|
|
32
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, "README.md"), "test\n");
|
|
33
|
+
(0, node_child_process_1.execFileSync)("git", ["add", "."], { cwd: dir });
|
|
34
|
+
(0, node_child_process_1.execFileSync)("git", ["commit", "-m", "init"], { cwd: dir });
|
|
35
|
+
return dir;
|
|
36
|
+
}
|
|
37
|
+
function writeState(dir, state) {
|
|
38
|
+
const stateFile = (0, node_path_1.join)(dir, ".polaris", "runs", "current-state.json");
|
|
39
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(dir, ".polaris", "runs"), { recursive: true });
|
|
40
|
+
const content = JSON.stringify(state, null, 2);
|
|
41
|
+
(0, node_fs_1.writeFileSync)(stateFile, content);
|
|
42
|
+
return stateFile;
|
|
43
|
+
}
|
|
44
|
+
function shaOf(content) {
|
|
45
|
+
return (0, node_crypto_1.createHash)("sha256").update(content).digest("hex");
|
|
46
|
+
}
|
|
47
|
+
function writePacket(dir, packet) {
|
|
48
|
+
const bootstrapDir = (0, node_path_1.join)(dir, ".polaris", "bootstrap");
|
|
49
|
+
(0, node_fs_1.mkdirSync)(bootstrapDir, { recursive: true });
|
|
50
|
+
const filename = `${packet.run_id}-2026-01-01T00-00-00-000Z.json`;
|
|
51
|
+
const path = (0, node_path_1.join)(bootstrapDir, filename);
|
|
52
|
+
(0, node_fs_1.writeFileSync)(path, JSON.stringify(packet, null, 2));
|
|
53
|
+
return path;
|
|
54
|
+
}
|
|
55
|
+
function makePacket(stateFile, stateContent, testDir, overrides = {}) {
|
|
56
|
+
const sha = shaOf(JSON.stringify(stateContent, null, 2));
|
|
57
|
+
return {
|
|
58
|
+
run_id: "pol-5-session-1",
|
|
59
|
+
skill: "bootstrap-run",
|
|
60
|
+
branch: getCurrentBranch(testDir),
|
|
61
|
+
base_commit_sha: getHeadSha(testDir),
|
|
62
|
+
last_completed_step: "checkpoint",
|
|
63
|
+
last_completed_child: "POL-23",
|
|
64
|
+
next_step: "03-execute-child",
|
|
65
|
+
open_children: ["POL-24"],
|
|
66
|
+
artifact_pointers: {
|
|
67
|
+
current_state: stateFile,
|
|
68
|
+
telemetry: "/tmp/telemetry.jsonl",
|
|
69
|
+
},
|
|
70
|
+
context_budget: { children_completed: 1, files_touched_total: 0, stop_threshold_remaining: 2 },
|
|
71
|
+
current_state_sha: sha,
|
|
72
|
+
resume_instructions: "Run polaris loop resume pol-5-session-1",
|
|
73
|
+
...overrides,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
(0, vitest_1.describe)("runLoopResume", () => {
|
|
77
|
+
let testDir;
|
|
78
|
+
(0, vitest_1.beforeEach)(() => {
|
|
79
|
+
testDir = makeTestDir();
|
|
80
|
+
});
|
|
81
|
+
(0, vitest_1.afterEach)(() => {
|
|
82
|
+
(0, node_fs_1.rmSync)(testDir, { recursive: true, force: true });
|
|
83
|
+
});
|
|
84
|
+
(0, vitest_1.it)("emits bootstrap packet JSON to stdout when all checks pass", () => {
|
|
85
|
+
const stateContent = { schema_version: "1.0", run_id: "pol-5-session-1" };
|
|
86
|
+
const stateFile = writeState(testDir, stateContent);
|
|
87
|
+
const packet = makePacket(stateFile, stateContent, testDir);
|
|
88
|
+
writePacket(testDir, packet);
|
|
89
|
+
const logs = [];
|
|
90
|
+
const origLog = console.log;
|
|
91
|
+
console.log = (...args) => logs.push(args.map(String).join(" "));
|
|
92
|
+
try {
|
|
93
|
+
(0, resume_js_1.runLoopResume)({ repoRoot: testDir, stateFile });
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
console.log = origLog;
|
|
97
|
+
}
|
|
98
|
+
const emitted = JSON.parse(logs.join("\n"));
|
|
99
|
+
(0, vitest_1.expect)(emitted.run_id).toBe("pol-5-session-1");
|
|
100
|
+
(0, vitest_1.expect)(emitted.current_state_sha).toBeTruthy();
|
|
101
|
+
});
|
|
102
|
+
(0, vitest_1.it)("selects packet by run_id when provided", () => {
|
|
103
|
+
const stateContent = { schema_version: "1.0", run_id: "pol-5-session-1" };
|
|
104
|
+
const stateFile = writeState(testDir, stateContent);
|
|
105
|
+
const packet = makePacket(stateFile, stateContent, testDir);
|
|
106
|
+
writePacket(testDir, packet);
|
|
107
|
+
const logs = [];
|
|
108
|
+
const origLog = console.log;
|
|
109
|
+
console.log = (...args) => logs.push(args.map(String).join(" "));
|
|
110
|
+
try {
|
|
111
|
+
(0, resume_js_1.runLoopResume)({ runId: "pol-5-session-1", repoRoot: testDir, stateFile });
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
console.log = origLog;
|
|
115
|
+
}
|
|
116
|
+
(0, vitest_1.expect)(logs.join("")).toContain("pol-5-session-1");
|
|
117
|
+
});
|
|
118
|
+
(0, vitest_1.it)("halts with exit 1 when current-state SHA does not match packet", () => {
|
|
119
|
+
const stateContent = { schema_version: "1.0", run_id: "pol-5-session-1" };
|
|
120
|
+
const stateFile = writeState(testDir, stateContent);
|
|
121
|
+
// Use a wrong SHA in the packet
|
|
122
|
+
const packet = makePacket(stateFile, stateContent, testDir, { current_state_sha: "wrongsha" });
|
|
123
|
+
writePacket(testDir, packet);
|
|
124
|
+
const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation(() => {
|
|
125
|
+
throw new Error("process.exit called");
|
|
126
|
+
});
|
|
127
|
+
const errSpy = vitest_1.vi.spyOn(console, "error").mockImplementation(() => { });
|
|
128
|
+
try {
|
|
129
|
+
(0, vitest_1.expect)(() => (0, resume_js_1.runLoopResume)({ repoRoot: testDir, stateFile })).toThrow();
|
|
130
|
+
(0, vitest_1.expect)(errSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("state packet stale"));
|
|
131
|
+
}
|
|
132
|
+
finally {
|
|
133
|
+
exitSpy.mockRestore();
|
|
134
|
+
errSpy.mockRestore();
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
(0, vitest_1.it)("halts when no bootstrap packets exist", () => {
|
|
138
|
+
const stateContent = { schema_version: "1.0", run_id: "pol-5-session-1" };
|
|
139
|
+
const stateFile = writeState(testDir, stateContent);
|
|
140
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.join)(testDir, ".polaris", "bootstrap"), { recursive: true });
|
|
141
|
+
const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation(() => {
|
|
142
|
+
throw new Error("process.exit called");
|
|
143
|
+
});
|
|
144
|
+
const errSpy = vitest_1.vi.spyOn(console, "error").mockImplementation(() => { });
|
|
145
|
+
try {
|
|
146
|
+
(0, vitest_1.expect)(() => (0, resume_js_1.runLoopResume)({ repoRoot: testDir, stateFile })).toThrow();
|
|
147
|
+
(0, vitest_1.expect)(errSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("No bootstrap packets found"));
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
exitSpy.mockRestore();
|
|
151
|
+
errSpy.mockRestore();
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
(0, vitest_1.it)("halts when run_id packet not found", () => {
|
|
155
|
+
const stateContent = { schema_version: "1.0", run_id: "pol-5-session-1" };
|
|
156
|
+
const stateFile = writeState(testDir, stateContent);
|
|
157
|
+
const packet = makePacket(stateFile, stateContent, testDir);
|
|
158
|
+
writePacket(testDir, packet);
|
|
159
|
+
const exitSpy = vitest_1.vi.spyOn(process, "exit").mockImplementation(() => {
|
|
160
|
+
throw new Error("process.exit called");
|
|
161
|
+
});
|
|
162
|
+
const errSpy = vitest_1.vi.spyOn(console, "error").mockImplementation(() => { });
|
|
163
|
+
try {
|
|
164
|
+
(0, vitest_1.expect)(() => (0, resume_js_1.runLoopResume)({ runId: "nonexistent-run", repoRoot: testDir, stateFile })).toThrow();
|
|
165
|
+
(0, vitest_1.expect)(errSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining("nonexistent-run"));
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
exitSpy.mockRestore();
|
|
169
|
+
errSpy.mockRestore();
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
});
|