@lsctech/polaris 0.5.4 → 0.5.5
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/dist/cli/index.js +18 -1
- package/dist/loop/dispatch.js +3 -1
- package/dist/loop/evidence-backfill.js +10 -3
- package/dist/loop/parent.js +46 -0
- package/dist/loop/run-bootstrap.js +13 -0
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -35,8 +35,25 @@ function resolveStateFile(repoRoot, explicit) {
|
|
|
35
35
|
return (0, node_path_1.resolve)(explicit);
|
|
36
36
|
const taskchainPath = (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run", "current-state.json");
|
|
37
37
|
const polarisPath = (0, node_path_1.join)(repoRoot, ".polaris", "runs", "current-state.json");
|
|
38
|
-
|
|
38
|
+
// Prefer canonical cluster-scoped state.json when it exists — finalize
|
|
39
|
+
// rejects the compatibility/debug taskchain path as the sole state source.
|
|
40
|
+
// Read cluster_id from the taskchain path if available, then check the
|
|
41
|
+
// canonical path bootstrapped alongside it.
|
|
42
|
+
if ((0, node_fs_1.existsSync)(taskchainPath)) {
|
|
43
|
+
try {
|
|
44
|
+
const raw = JSON.parse((0, node_fs_1.readFileSync)(taskchainPath, "utf-8"));
|
|
45
|
+
const clusterId = typeof raw["cluster_id"] === "string" ? raw["cluster_id"] : undefined;
|
|
46
|
+
if (clusterId) {
|
|
47
|
+
const canonicalPath = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", clusterId, "state.json");
|
|
48
|
+
if ((0, node_fs_1.existsSync)(canonicalPath))
|
|
49
|
+
return canonicalPath;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// Fall through to taskchain path
|
|
54
|
+
}
|
|
39
55
|
return taskchainPath;
|
|
56
|
+
}
|
|
40
57
|
if ((0, node_fs_1.existsSync)(polarisPath))
|
|
41
58
|
return polarisPath;
|
|
42
59
|
return taskchainPath;
|
package/dist/loop/dispatch.js
CHANGED
|
@@ -305,7 +305,9 @@ function detectPacketGenerationFailure(packet) {
|
|
|
305
305
|
if (allowedScope.length === 0) {
|
|
306
306
|
return {
|
|
307
307
|
missingField: "allowed_scope",
|
|
308
|
-
message: `Packet generation failed for ${childId}: missing actionable allowed_scope
|
|
308
|
+
message: `Packet generation failed for ${childId}: missing actionable allowed_scope. ` +
|
|
309
|
+
`Add a '## Scope' section to the child issue body in your tracker, ` +
|
|
310
|
+
`then run 'polaris tracker sync-in <cluster-id>' to pull the update.`,
|
|
309
311
|
};
|
|
310
312
|
}
|
|
311
313
|
const issueBody = packet.instructions.issue_context?.body?.trim() ?? "";
|
|
@@ -95,7 +95,12 @@ function backfillClusterStateEvidence(options) {
|
|
|
95
95
|
const packetsDir = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", clusterId, "packets");
|
|
96
96
|
const backfilled = [];
|
|
97
97
|
const skipped = [];
|
|
98
|
-
|
|
98
|
+
// Scan both completed and open children — recovery may have committed work
|
|
99
|
+
// for children that were never checkpointed into completed_children.
|
|
100
|
+
const seen = new Set();
|
|
101
|
+
const allChildren = [...state.completed_children, ...state.open_children].filter((id) => { if (seen.has(id))
|
|
102
|
+
return false; seen.add(id); return true; });
|
|
103
|
+
for (const childId of allChildren) {
|
|
99
104
|
// Resolve result file — mirrors continue.ts priority:
|
|
100
105
|
// 1. result_file on child meta (explicit override, e.g. --result-file flag)
|
|
101
106
|
// 2. dispatch_record.expected_result_path
|
|
@@ -132,10 +137,12 @@ function backfillClusterStateEvidence(options) {
|
|
|
132
137
|
continue;
|
|
133
138
|
}
|
|
134
139
|
// Guard: result must indicate completion.
|
|
135
|
-
|
|
140
|
+
// Accept "done" (backfill artifact) and "success" (SuccessResultPacket schema).
|
|
141
|
+
const resultStatus = result["status"];
|
|
142
|
+
if (resultStatus !== undefined && resultStatus !== "done" && resultStatus !== "success") {
|
|
136
143
|
skipped.push({
|
|
137
144
|
childId,
|
|
138
|
-
reason: `result status is not done: ${String(
|
|
145
|
+
reason: `result status is not done or success: ${String(resultStatus)}`,
|
|
139
146
|
});
|
|
140
147
|
continue;
|
|
141
148
|
}
|
package/dist/loop/parent.js
CHANGED
|
@@ -776,6 +776,52 @@ async function runParentLoop(options) {
|
|
|
776
776
|
// persisted to disk, so a fresh loop start always begins clean.
|
|
777
777
|
const lifecycle = new lifecycle_js_1.WorkerLifecycleManager(maxConcurrentWorkers);
|
|
778
778
|
lifecycle.forceReleaseAll();
|
|
779
|
+
// ── Auto-sync pre-flight: fetch missing issue bodies from tracker ────────
|
|
780
|
+
// When child issue bodies are absent or lack a ## Scope section, dispatch
|
|
781
|
+
// hard-fails with "empty allowed_scope". Attempt a silent tracker sync-in
|
|
782
|
+
// before the loop starts so operators don't need to run it manually.
|
|
783
|
+
if (!dryRun) {
|
|
784
|
+
const openChildrenNeedingScope = state.open_children.filter((childId) => {
|
|
785
|
+
// Skip analyze children — they never need scope (no impl packets)
|
|
786
|
+
if (isAnalyzeChild(childId, state)) {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
// Resolve child body: prefer cached meta body, then fall back to snapshot
|
|
790
|
+
const cachedChildBody = state.open_children_meta?.[childId]?.body;
|
|
791
|
+
const childBody = (cachedChildBody && cachedChildBody.trim().length > 0)
|
|
792
|
+
? cachedChildBody
|
|
793
|
+
: (0, checkpoint_js_1.readBodyFromClusterSnapshot)(state.cluster_id, childId, repoRoot) ?? '';
|
|
794
|
+
// If body is completely absent, treat as needing scope sync-in
|
|
795
|
+
if (childBody.trim().length === 0) {
|
|
796
|
+
return true;
|
|
797
|
+
}
|
|
798
|
+
// Parse child body for scope
|
|
799
|
+
const { scope: childScope } = (0, body_parser_js_1.parseIssueBody)(childBody);
|
|
800
|
+
if (childScope.length > 0) {
|
|
801
|
+
return false; // Child has scope, no sync needed
|
|
802
|
+
}
|
|
803
|
+
// Fallback: check parent/cluster-root body for scope
|
|
804
|
+
const cachedParentBody = state.open_children_meta?.[state.cluster_id]?.body;
|
|
805
|
+
const parentBody = (cachedParentBody && cachedParentBody.trim().length > 0)
|
|
806
|
+
? cachedParentBody
|
|
807
|
+
: (0, checkpoint_js_1.readBodyFromClusterSnapshot)(state.cluster_id, state.cluster_id, repoRoot) ?? '';
|
|
808
|
+
const { scope: parentScope } = (0, body_parser_js_1.parseIssueBody)(parentBody);
|
|
809
|
+
// If parent has scope, child can inherit — no sync needed
|
|
810
|
+
// Otherwise, child truly needs scope sync-in
|
|
811
|
+
return parentScope.length === 0;
|
|
812
|
+
});
|
|
813
|
+
if (openChildrenNeedingScope.length > 0) {
|
|
814
|
+
process.stderr.write(`[polaris] ${openChildrenNeedingScope.length} children missing scope — attempting tracker sync-in...\n`);
|
|
815
|
+
try {
|
|
816
|
+
await (0, index_js_2.loadTrackerGraph)(config, state.cluster_id);
|
|
817
|
+
process.stderr.write(`[polaris] sync-in complete.\n`);
|
|
818
|
+
}
|
|
819
|
+
catch (syncErr) {
|
|
820
|
+
process.stderr.write(`[polaris] sync-in failed (${syncErr instanceof Error ? syncErr.message : String(syncErr)}). ` +
|
|
821
|
+
`Run 'polaris tracker sync-in ${state.cluster_id}' manually and retry.\n`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
}
|
|
779
825
|
// ── Main dispatch loop ───────────────────────────────────────────────────
|
|
780
826
|
// eslint-disable-next-line no-constant-condition
|
|
781
827
|
while (true) {
|
|
@@ -216,6 +216,19 @@ function runLoopBootstrapInit(options) {
|
|
|
216
216
|
// avoid circular deps; validateState is called by dispatch which is downstream)
|
|
217
217
|
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(stateFile), { recursive: true });
|
|
218
218
|
const sha = (0, checkpoint_js_1.writeStateAtomic)(stateFile, initialState);
|
|
219
|
+
// Also write to the canonical cluster-scoped path so `polaris finalize`
|
|
220
|
+
// can locate it without requiring --state-file. finalize rejects the
|
|
221
|
+
// .taskchain_artifacts compatibility path as the sole state source.
|
|
222
|
+
const canonicalStatePath = (0, node_path_1.resolve)(repoRoot, ".polaris", "clusters", clusterId, "state.json");
|
|
223
|
+
if ((0, node_path_1.resolve)(stateFile) !== canonicalStatePath) {
|
|
224
|
+
try {
|
|
225
|
+
(0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(canonicalStatePath), { recursive: true });
|
|
226
|
+
(0, checkpoint_js_1.writeStateAtomic)(canonicalStatePath, initialState);
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
process.stderr.write(`Warning: Could not write canonical state to ${canonicalStatePath}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
219
232
|
const summary = {
|
|
220
233
|
run_id: runId,
|
|
221
234
|
cluster_id: clusterId,
|