@lsctech/polaris 0.5.11 → 0.5.13
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/autoresearch/proposal.js +61 -0
- package/dist/autoresearch/score.js +124 -0
- package/dist/cli/adopt-canon.js +22 -8
- package/dist/cli/index.js +4 -0
- package/dist/cli/qc.js +97 -0
- package/dist/config/validator.js +16 -6
- package/dist/finalize/artifact-policy.js +12 -3
- package/dist/finalize/index.js +36 -21
- package/dist/finalize/run-report.js +189 -19
- package/dist/finalize/steps/05-generate-report.js +5 -1
- package/dist/finalize/steps/12-archive.js +6 -0
- package/dist/loop/adapters/terminal-cli.js +159 -25
- package/dist/loop/body-parser.js +73 -6
- package/dist/loop/continue.js +22 -4
- package/dist/loop/dispatch.js +103 -68
- package/dist/loop/orphan-recovery.js +2 -1
- package/dist/loop/parent.js +50 -15
- package/dist/loop/router/engine.js +1 -0
- package/dist/loop/run-bootstrap.js +3 -1
- package/dist/loop/run-preflight.js +4 -1
- package/dist/loop/worker-packet.js +81 -2
- package/dist/map/inference.js +4 -1
- package/dist/medic/routing-signals.js +60 -0
- package/dist/medic/run-health-consult.js +5 -4
- package/dist/medic/treatment-packets.js +5 -1
- package/dist/qc/policy.js +2 -0
- package/dist/qc/providers/coderabbit.js +140 -10
- package/dist/qc/repair-loop.js +147 -1
- package/dist/qc/repair-packets.js +16 -3
- package/dist/qc/routing.js +6 -0
- package/dist/qc/types.js +3 -0
- package/dist/run-health/index.js +12 -0
- package/dist/skill-packet/generator.js +234 -3
- package/dist/smartdocs-engine/canon-check.js +5 -5
- package/dist/smartdocs-engine/index.js +54 -0
- package/dist/smartdocs-engine/seed-instructions.js +159 -4
- package/dist/smartdocs-engine/validate-instructions.js +46 -4
- package/dist/tracker/local-graph.js +106 -3
- package/dist/workspace/POLARIS.md +4 -0
- package/dist/workspace/SUMMARY.md +56 -0
- package/package.json +1 -1
package/dist/loop/continue.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveTelemetryFilePath = resolveTelemetryFilePath;
|
|
3
4
|
exports.runLoopContinue = runLoopContinue;
|
|
4
5
|
const node_path_1 = require("node:path");
|
|
5
6
|
const node_fs_1 = require("node:fs");
|
|
@@ -214,7 +215,7 @@ function appendTelemetryEvent(telemetryFile, event) {
|
|
|
214
215
|
}
|
|
215
216
|
function resolveTelemetryFilePath(state, repoRoot) {
|
|
216
217
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
217
|
-
return (0, node_path_1.
|
|
218
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
218
219
|
}
|
|
219
220
|
function hasValidationEvidence(validation) {
|
|
220
221
|
if (validation === undefined || validation === null)
|
|
@@ -280,12 +281,15 @@ function bridgeEvidenceToClusterState(repoRoot, clusterId, childId, commit, rawV
|
|
|
280
281
|
: [...existing.child_states, { id: childId, status: "done", commit: commit || undefined }];
|
|
281
282
|
// Evict any stale commit entry for this child before conditionally re-adding
|
|
282
283
|
const { [childId]: _staleCommit, ...remainingCommits } = existing.commits;
|
|
284
|
+
const relativeResultFile = resultFile
|
|
285
|
+
? (0, node_path_1.relative)(repoRoot, (0, node_path_1.isAbsolute)(resultFile) ? resultFile : (0, node_path_1.resolve)(repoRoot, resultFile))
|
|
286
|
+
: resultFile;
|
|
283
287
|
const updated = {
|
|
284
288
|
...existing,
|
|
285
289
|
state_generation: existing.state_generation + 1,
|
|
286
290
|
child_states: updatedChildStates,
|
|
287
291
|
commits: commit ? { ...remainingCommits, [childId]: commit } : remainingCommits,
|
|
288
|
-
result_pointers:
|
|
292
|
+
result_pointers: relativeResultFile ? { ...existing.result_pointers, [childId]: relativeResultFile } : existing.result_pointers,
|
|
289
293
|
validation_results: { ...existing.validation_results, [childId]: toValidationResult(rawValidation) },
|
|
290
294
|
};
|
|
291
295
|
(0, store_js_1.writeClusterStateSync)(clusterId, updated, repoRoot);
|
|
@@ -379,7 +383,7 @@ function runLoopContinue(options) {
|
|
|
379
383
|
// If no dispatch was recorded (dispatch_epoch === continue_epoch),
|
|
380
384
|
// reject immediately and do NOT mutate any state.
|
|
381
385
|
const artifactDirForTelemetry = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
382
|
-
const telemetryFileForCheck = (0, node_path_1.
|
|
386
|
+
const telemetryFileForCheck = (0, node_path_1.resolve)(repoRoot, artifactDirForTelemetry, "runs", state.run_id, "telemetry.jsonl");
|
|
383
387
|
try {
|
|
384
388
|
(0, dispatch_boundary_js_1.assertContinueRequiresDispatch)(state, telemetryFileForCheck);
|
|
385
389
|
}
|
|
@@ -601,10 +605,24 @@ function runLoopContinue(options) {
|
|
|
601
605
|
}
|
|
602
606
|
// Step 1 (cont): Atomic write of updated current-state.json (open_children_meta pruned after bridge)
|
|
603
607
|
const sha = (0, checkpoint_js_1.writeStateAtomic)(stateFile, updatedState);
|
|
608
|
+
// Also keep the canonical cluster state snapshot in sync so finalize and
|
|
609
|
+
// direct cluster-state reads see the same completed children.
|
|
610
|
+
const canonicalStatePath = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", updatedState.cluster_id, "state.json");
|
|
611
|
+
if ((0, node_path_1.resolve)(stateFile) !== (0, node_path_1.resolve)(canonicalStatePath)) {
|
|
612
|
+
try {
|
|
613
|
+
(0, checkpoint_js_1.writeStateAtomic)(canonicalStatePath, updatedState);
|
|
614
|
+
}
|
|
615
|
+
catch (error) {
|
|
616
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
617
|
+
console.error(`Error: primary state for cluster ${updatedState.cluster_id} was persisted, but the canonical ` +
|
|
618
|
+
`cluster state snapshot at ${canonicalStatePath} failed to update and remains stale: ${msg}`);
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
|
621
|
+
}
|
|
604
622
|
appendContinueLedgerEvents(repoRoot, updatedState, completedChild);
|
|
605
623
|
// Step 2: Append JSONL checkpoint event
|
|
606
624
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
607
|
-
const telemetryFile = (0, node_path_1.
|
|
625
|
+
const telemetryFile = (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
608
626
|
(0, checkpoint_js_1.appendCheckpointEvent)(telemetryFile, {
|
|
609
627
|
event: "loop-checkpoint",
|
|
610
628
|
run_id: state.run_id,
|
package/dist/loop/dispatch.js
CHANGED
|
@@ -139,6 +139,18 @@ function resolveProviderAndMode(options, role, config, routerContext) {
|
|
|
139
139
|
},
|
|
140
140
|
compatibilityMode: false,
|
|
141
141
|
});
|
|
142
|
+
const workerPolicy = exec?.providerPolicy?.[role];
|
|
143
|
+
const noFallback = workerPolicy?.noFallback === true;
|
|
144
|
+
const fallbackEligible = !noFallback && decision.providersTried.length > 1;
|
|
145
|
+
const routingSummary = {
|
|
146
|
+
selected_provider: decision.selectedProvider ?? null,
|
|
147
|
+
selected_adapter: adapter,
|
|
148
|
+
selection_reason: decision.selectionReason,
|
|
149
|
+
effective_policy_order: decision.providersTried,
|
|
150
|
+
compatibility_mode: false,
|
|
151
|
+
registry_present: true,
|
|
152
|
+
fallback_eligible: fallbackEligible,
|
|
153
|
+
};
|
|
142
154
|
return {
|
|
143
155
|
provider: decision.selectedProvider,
|
|
144
156
|
mode: decision.mode,
|
|
@@ -157,91 +169,108 @@ function resolveProviderAndMode(options, role, config, routerContext) {
|
|
|
157
169
|
activeSlotsByProvider: routerContext?.activeSlotsByProvider,
|
|
158
170
|
quotaAvailableByProvider: routerContext?.quotaAvailableByProvider,
|
|
159
171
|
},
|
|
172
|
+
routingSummary,
|
|
160
173
|
};
|
|
161
174
|
}
|
|
162
175
|
function resolveProviderAndModeLegacy(options, role, adapter, exec) {
|
|
163
|
-
if (options.provider) {
|
|
164
|
-
return {
|
|
165
|
-
provider: options.provider,
|
|
166
|
-
mode: "direct-worker",
|
|
167
|
-
adapter,
|
|
168
|
-
selectionReason: "cli-provider-override",
|
|
169
|
-
overrideSource: "dispatch-flag",
|
|
170
|
-
providersTried: [options.provider],
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
176
|
const rolePolicy = exec?.providerPolicy?.[role];
|
|
174
177
|
const roleProviders = rolePolicy?.providers ?? [];
|
|
178
|
+
const noFallback = rolePolicy?.noFallback === true;
|
|
175
179
|
const rotation = exec?.rotation ?? [];
|
|
176
|
-
|
|
180
|
+
const configuredProviders = new Set(Object.keys(exec?.providers ?? {}));
|
|
181
|
+
let provider;
|
|
182
|
+
let selectionReason;
|
|
183
|
+
let overrideSource;
|
|
184
|
+
let fallbackFrom;
|
|
185
|
+
let fallbackReason;
|
|
186
|
+
let exhaustedReason;
|
|
187
|
+
let effectivePolicyOrder;
|
|
188
|
+
if (options.provider) {
|
|
189
|
+
provider = options.provider;
|
|
190
|
+
selectionReason = "cli-provider-override";
|
|
191
|
+
overrideSource = "dispatch-flag";
|
|
192
|
+
effectivePolicyOrder = [options.provider];
|
|
193
|
+
}
|
|
194
|
+
else if (roleProviders.length > 0) {
|
|
177
195
|
// When a role policy is configured, filter the rotation to only include
|
|
178
196
|
// providers that are allowed by the policy. This ensures that a provider
|
|
179
197
|
// present in rotation but absent from the policy is never selected.
|
|
180
198
|
const allowedByPolicy = new Set(roleProviders);
|
|
181
|
-
const configuredProviders = new Set(Object.keys(exec?.providers ?? {}));
|
|
182
199
|
const filteredRotation = rotation.filter((p) => allowedByPolicy.has(p));
|
|
200
|
+
const availablePolicyProviders = roleProviders.filter((p) => configuredProviders.has(p));
|
|
183
201
|
if (filteredRotation.length > 0 && filteredRotation[0]) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
202
|
+
provider = filteredRotation[0];
|
|
203
|
+
selectionReason = "policy-filtered-rotation";
|
|
204
|
+
effectivePolicyOrder = filteredRotation.slice();
|
|
205
|
+
// Surface remaining configured policy providers as fallback alternatives
|
|
206
|
+
for (const p of availablePolicyProviders) {
|
|
207
|
+
if (!effectivePolicyOrder.includes(p)) {
|
|
208
|
+
effectivePolicyOrder.push(p);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
191
211
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
selectionReason: "role-policy",
|
|
201
|
-
providersTried: [availablePolicyProviders[0]],
|
|
202
|
-
};
|
|
212
|
+
else if (availablePolicyProviders.length > 0 && availablePolicyProviders[0]) {
|
|
213
|
+
provider = availablePolicyProviders[0];
|
|
214
|
+
selectionReason = "role-policy";
|
|
215
|
+
effectivePolicyOrder = availablePolicyProviders.slice();
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// No configured provider satisfies the policy — fail clearly before dispatch.
|
|
219
|
+
throw new Error(`provider dispatch forbidden: no configured provider satisfies the "${role}" role policy (policy=${roleProviders.join(",")})`);
|
|
203
220
|
}
|
|
204
|
-
// No configured provider satisfies the policy — fail clearly before dispatch.
|
|
205
|
-
throw new Error(`provider dispatch forbidden: no configured provider satisfies the "${role}" role policy (policy=${roleProviders.join(",")})`);
|
|
206
221
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
222
|
+
else if (exec?.roles?.[role]?.provider) {
|
|
223
|
+
provider = exec.roles[role].provider;
|
|
224
|
+
selectionReason = "role-config";
|
|
225
|
+
effectivePolicyOrder = [provider];
|
|
226
|
+
// If a role policy exists for this role, include its other providers as alternatives
|
|
227
|
+
for (const p of roleProviders) {
|
|
228
|
+
if (p !== provider && !effectivePolicyOrder.includes(p)) {
|
|
229
|
+
effectivePolicyOrder.push(p);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
216
232
|
}
|
|
217
|
-
if (rotation.length > 0 && rotation[0]) {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
adapter,
|
|
222
|
-
selectionReason: "config-rotation",
|
|
223
|
-
providersTried: [rotation[0]],
|
|
224
|
-
};
|
|
233
|
+
else if (rotation.length > 0 && rotation[0]) {
|
|
234
|
+
provider = rotation[0];
|
|
235
|
+
selectionReason = "config-rotation";
|
|
236
|
+
effectivePolicyOrder = rotation.slice();
|
|
225
237
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
provider
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
238
|
+
else {
|
|
239
|
+
const providers = Object.keys(exec?.providers ?? {});
|
|
240
|
+
if (providers.length > 0 && providers[0]) {
|
|
241
|
+
provider = providers[0];
|
|
242
|
+
selectionReason = "config-first-provider";
|
|
243
|
+
fallbackFrom = "rotation";
|
|
244
|
+
fallbackReason = "rotation-empty";
|
|
245
|
+
effectivePolicyOrder = providers.slice();
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
selectionReason = "delegated-no-provider";
|
|
249
|
+
exhaustedReason = "no-configured-provider";
|
|
250
|
+
effectivePolicyOrder = [];
|
|
251
|
+
}
|
|
237
252
|
}
|
|
253
|
+
const fallbackEligible = !noFallback && effectivePolicyOrder.length > 1;
|
|
254
|
+
const routingSummary = {
|
|
255
|
+
selected_provider: provider ?? null,
|
|
256
|
+
selected_adapter: adapter,
|
|
257
|
+
selection_reason: selectionReason,
|
|
258
|
+
effective_policy_order: effectivePolicyOrder,
|
|
259
|
+
compatibility_mode: true,
|
|
260
|
+
registry_present: false,
|
|
261
|
+
fallback_eligible: fallbackEligible,
|
|
262
|
+
};
|
|
238
263
|
return {
|
|
239
|
-
provider
|
|
240
|
-
mode: "delegated",
|
|
264
|
+
provider,
|
|
265
|
+
mode: provider ? "direct-worker" : "delegated",
|
|
241
266
|
adapter,
|
|
242
|
-
selectionReason
|
|
243
|
-
|
|
244
|
-
|
|
267
|
+
selectionReason,
|
|
268
|
+
overrideSource,
|
|
269
|
+
fallbackFrom,
|
|
270
|
+
fallbackReason,
|
|
271
|
+
providersTried: provider ? [provider] : [],
|
|
272
|
+
exhaustedReason,
|
|
273
|
+
routingSummary,
|
|
245
274
|
};
|
|
246
275
|
}
|
|
247
276
|
function fail(message) {
|
|
@@ -344,7 +373,7 @@ function appendTelemetry(telemetryFile, event) {
|
|
|
344
373
|
}
|
|
345
374
|
function resolveTelemetryFile(state, repoRoot) {
|
|
346
375
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
347
|
-
return (0, node_path_1.
|
|
376
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
348
377
|
}
|
|
349
378
|
function canonicalPath(path) {
|
|
350
379
|
try {
|
|
@@ -464,6 +493,7 @@ function emitProviderSelected(telemetryFile, dispatchId, runId, childId, decisio
|
|
|
464
493
|
...(decision.fallbackReason ? { fallback_reason: decision.fallbackReason } : {}),
|
|
465
494
|
...(decision.providersTried.length > 0 ? { providers_tried: decision.providersTried } : {}),
|
|
466
495
|
...(decision.exhaustedReason ? { router_exhausted_reason: decision.exhaustedReason } : {}),
|
|
496
|
+
...(decision.routingSummary ? { routing_summary: decision.routingSummary } : {}),
|
|
467
497
|
...(decision.routerEvidence?.candidates?.length
|
|
468
498
|
? {
|
|
469
499
|
router_candidates: decision.routerEvidence.candidates.map((candidate) => ({
|
|
@@ -484,6 +514,7 @@ function emitProviderSelected(telemetryFile, dispatchId, runId, childId, decisio
|
|
|
484
514
|
active_slots: candidate.evidence.activeSlots,
|
|
485
515
|
slot_limit: candidate.evidence.slotLimit,
|
|
486
516
|
policy_matched: candidate.evidence.policyMatched,
|
|
517
|
+
fallback_eligible: candidate.evidence.fallbackEligible,
|
|
487
518
|
},
|
|
488
519
|
})),
|
|
489
520
|
}
|
|
@@ -574,7 +605,7 @@ function assertProviderAllowedForRole(role, provider, policy, telemetryFile, dis
|
|
|
574
605
|
/**
|
|
575
606
|
* Create a dispatch record with all required evidence.
|
|
576
607
|
*/
|
|
577
|
-
function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath, roleContext, provider, providerSelectionReason, providerOverrideSource, providersTried) {
|
|
608
|
+
function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath, roleContext, provider, providerSelectionReason, providerOverrideSource, providersTried, routingSummary) {
|
|
578
609
|
const dispatchMode = determineDispatchMode(provider);
|
|
579
610
|
const runtimeState = determineRuntimeState(dispatchMode);
|
|
580
611
|
return {
|
|
@@ -588,6 +619,7 @@ function createDispatchRecord(runId, clusterId, childId, packetPath, resultPath,
|
|
|
588
619
|
provider_selection_reason: providerSelectionReason,
|
|
589
620
|
provider_override_source: providerOverrideSource,
|
|
590
621
|
providers_tried: providersTried && providersTried.length > 0 ? providersTried : undefined,
|
|
622
|
+
routing_summary: routingSummary,
|
|
591
623
|
dispatched_at: new Date().toISOString(),
|
|
592
624
|
status: "dispatched",
|
|
593
625
|
dispatch_mode: dispatchMode,
|
|
@@ -755,6 +787,7 @@ function ledgerLastCommit(state) {
|
|
|
755
787
|
return state.last_commit && state.last_commit.length > 0 ? state.last_commit : null;
|
|
756
788
|
}
|
|
757
789
|
function appendDispatchLedgerEvent(repoRoot, state, childId) {
|
|
790
|
+
const routingSummary = state.open_children_meta?.[childId]?.dispatch_record?.routing_summary;
|
|
758
791
|
new ledger_js_1.LedgerWriter((0, node_path_1.join)(repoRoot, ledger_js_1.DEFAULT_LEDGER_PATH)).append({
|
|
759
792
|
schema_version: 1,
|
|
760
793
|
event_id: (0, node_crypto_1.randomUUID)(),
|
|
@@ -772,6 +805,7 @@ function appendDispatchLedgerEvent(repoRoot, state, childId) {
|
|
|
772
805
|
pr_url: null,
|
|
773
806
|
timestamp: new Date().toISOString(),
|
|
774
807
|
dispatch_epoch: state.dispatch_boundary?.dispatch_epoch ?? 0,
|
|
808
|
+
...(routingSummary ? { routing_summary: routingSummary } : {}),
|
|
775
809
|
});
|
|
776
810
|
}
|
|
777
811
|
function selectChild(state, requestedChild) {
|
|
@@ -1189,7 +1223,7 @@ function runLoopDispatch(options) {
|
|
|
1189
1223
|
fail(`Failed to write packet artifact to ${packetPath}: ${errorMsg}`);
|
|
1190
1224
|
}
|
|
1191
1225
|
// Create dispatch record with all evidence
|
|
1192
|
-
const dispatchRecord = createDispatchRecord(state.run_id, state.cluster_id, childId, relativePacketPath, relativeEffectiveResultPath, packet.role_context, resolvedProvider, providerDecision.selectionReason, providerDecision.overrideSource, providerDecision.providersTried);
|
|
1226
|
+
const dispatchRecord = createDispatchRecord(state.run_id, state.cluster_id, childId, relativePacketPath, relativeEffectiveResultPath, packet.role_context, resolvedProvider, providerDecision.selectionReason, providerDecision.overrideSource, providerDecision.providersTried, providerDecision.routingSummary);
|
|
1193
1227
|
emitProviderFallbackAttempted(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
|
|
1194
1228
|
emitProviderExhausted(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
|
|
1195
1229
|
emitProviderSelected(telemetryFile, dispatchRecord.dispatch_id, state.run_id, childId, providerDecision);
|
|
@@ -1245,6 +1279,7 @@ function runLoopDispatch(options) {
|
|
|
1245
1279
|
dispatch_mode: dispatchRecord.dispatch_mode,
|
|
1246
1280
|
runtime_state: dispatchRecord.runtime_state,
|
|
1247
1281
|
provider: dispatchRecord.provider ?? null,
|
|
1282
|
+
routing_summary: dispatchRecord.routing_summary ?? null,
|
|
1248
1283
|
timestamp: new Date().toISOString(),
|
|
1249
1284
|
});
|
|
1250
1285
|
// ── Apply lifecycle transition for child-dispatch event ────────────────────
|
|
@@ -177,12 +177,13 @@ function resetForRedispatch(stateFile, state, childId) {
|
|
|
177
177
|
if (!childMeta?.dispatch_record)
|
|
178
178
|
return (0, node_crypto_1.randomUUID)();
|
|
179
179
|
const newDispatchId = (0, node_crypto_1.randomUUID)();
|
|
180
|
-
// Clear dispatch record
|
|
180
|
+
// Clear dispatch record and result_file to prevent stale artifact reads
|
|
181
181
|
const updatedMeta = {
|
|
182
182
|
...state.open_children_meta,
|
|
183
183
|
[childId]: {
|
|
184
184
|
...childMeta,
|
|
185
185
|
dispatch_record: undefined,
|
|
186
|
+
result_file: undefined,
|
|
186
187
|
},
|
|
187
188
|
};
|
|
188
189
|
// Reset active_child so a new dispatch can proceed
|
package/dist/loop/parent.js
CHANGED
|
@@ -120,7 +120,7 @@ function appendTelemetry(telemetryFile, event) {
|
|
|
120
120
|
}
|
|
121
121
|
function resolveTelemetryFile(state, repoRoot) {
|
|
122
122
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
123
|
-
return (0, node_path_1.
|
|
123
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
124
124
|
}
|
|
125
125
|
function estimateTokensFromBytes(bytes) {
|
|
126
126
|
return Math.round(bytes / 4);
|
|
@@ -254,6 +254,7 @@ function appendChildCompletedLedgerEvent(repoRoot, state, completedChild, lastCo
|
|
|
254
254
|
...(completion?.providersTried?.length
|
|
255
255
|
? { providers_tried: completion.providersTried }
|
|
256
256
|
: {}),
|
|
257
|
+
...(completion?.routingSummary ? { routing_summary: completion.routingSummary } : {}),
|
|
257
258
|
});
|
|
258
259
|
}
|
|
259
260
|
function appendClusterCompletedLedgerEvent(repoRoot, state) {
|
|
@@ -435,6 +436,7 @@ function buildParentDispatchRecord(args) {
|
|
|
435
436
|
provider: args.provider,
|
|
436
437
|
provider_selection_reason: args.providerSelectionReason,
|
|
437
438
|
providers_tried: args.providersTried,
|
|
439
|
+
routing_summary: args.routingSummary,
|
|
438
440
|
dispatched_at: args.dispatchedAt,
|
|
439
441
|
status: "dispatched",
|
|
440
442
|
dispatch_mode: "direct-worker",
|
|
@@ -557,15 +559,16 @@ async function syncClusterCompletion(args) {
|
|
|
557
559
|
output: String(args.validationSummary),
|
|
558
560
|
},
|
|
559
561
|
};
|
|
562
|
+
const relativeResultFile = args.resultFile ? (0, node_path_1.relative)(args.repoRoot, args.resultFile) : args.resultFile;
|
|
560
563
|
await (0, store_js_1.writeClusterState)(args.clusterId, {
|
|
561
564
|
...clusterState,
|
|
562
565
|
state_generation: clusterState.state_generation + 1,
|
|
563
566
|
child_states: childStates,
|
|
564
567
|
claim_metadata: Object.fromEntries(Object.entries(clusterState.claim_metadata).filter(([childId]) => childId !== args.childId)),
|
|
565
|
-
result_pointers:
|
|
568
|
+
result_pointers: relativeResultFile
|
|
566
569
|
? {
|
|
567
570
|
...clusterState.result_pointers,
|
|
568
|
-
[args.childId]:
|
|
571
|
+
[args.childId]: relativeResultFile,
|
|
569
572
|
}
|
|
570
573
|
: clusterState.result_pointers,
|
|
571
574
|
validation_results: nextValidationResults,
|
|
@@ -594,6 +597,7 @@ async function syncClusterDispatch(args) {
|
|
|
594
597
|
}
|
|
595
598
|
: child)
|
|
596
599
|
: [...clusterState.child_states, { id: args.childId, status: "dispatched" }];
|
|
600
|
+
const relativePacketPath = (0, node_path_1.relative)(args.repoRoot, args.packetPath);
|
|
597
601
|
await (0, store_js_1.writeClusterState)(args.clusterId, {
|
|
598
602
|
...clusterState,
|
|
599
603
|
state_generation: clusterState.state_generation + 1,
|
|
@@ -608,7 +612,7 @@ async function syncClusterDispatch(args) {
|
|
|
608
612
|
},
|
|
609
613
|
packet_pointers: {
|
|
610
614
|
...clusterState.packet_pointers,
|
|
611
|
-
[args.childId]:
|
|
615
|
+
[args.childId]: relativePacketPath,
|
|
612
616
|
},
|
|
613
617
|
}, args.repoRoot);
|
|
614
618
|
return true;
|
|
@@ -681,6 +685,23 @@ async function runParentLoop(options) {
|
|
|
681
685
|
if (!dryRun) {
|
|
682
686
|
appendRunStartedLedgerEvent(repoRoot, state);
|
|
683
687
|
}
|
|
688
|
+
// Keep the canonical cluster snapshot in sync with the taskchain state so
|
|
689
|
+
// `polaris status`, `polaris continue`, and `polaris finalize` always see
|
|
690
|
+
// the freshest state, even if the parent loop is interrupted before it
|
|
691
|
+
// reaches the final cluster-complete write.
|
|
692
|
+
const canonicalStatePath = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", state.cluster_id, "state.json");
|
|
693
|
+
const writeStateAtomic = (filePath, stateToWrite) => {
|
|
694
|
+
(0, checkpoint_js_1.writeStateAtomic)(filePath, stateToWrite);
|
|
695
|
+
if ((0, node_path_1.resolve)(filePath) !== (0, node_path_1.resolve)(canonicalStatePath)) {
|
|
696
|
+
try {
|
|
697
|
+
(0, checkpoint_js_1.writeStateAtomic)(canonicalStatePath, stateToWrite);
|
|
698
|
+
}
|
|
699
|
+
catch (err) {
|
|
700
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
701
|
+
process.stderr.write(`[polaris] canonical cluster state snapshot at ${canonicalStatePath} failed to update and remains stale: ${msg}\n`);
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
};
|
|
684
705
|
// Load config for adapter and provider resolution
|
|
685
706
|
const config = (0, loader_js_1.loadConfig)(repoRoot);
|
|
686
707
|
const legacyOrchestrationMode = state["orchestration_mode"];
|
|
@@ -692,10 +713,20 @@ async function runParentLoop(options) {
|
|
|
692
713
|
let providerName;
|
|
693
714
|
let providerSelectionReason;
|
|
694
715
|
let providersTried;
|
|
716
|
+
let routingSummary;
|
|
695
717
|
if (adapterName === "agent-subtask") {
|
|
696
718
|
providerName = "agent-subtask";
|
|
697
719
|
providerSelectionReason = "agent-subtask-adapter";
|
|
698
720
|
providersTried = ["agent-subtask"];
|
|
721
|
+
routingSummary = {
|
|
722
|
+
selected_provider: "agent-subtask",
|
|
723
|
+
selected_adapter: "agent-subtask",
|
|
724
|
+
selection_reason: "agent-subtask-adapter",
|
|
725
|
+
effective_policy_order: ["agent-subtask"],
|
|
726
|
+
compatibility_mode: false,
|
|
727
|
+
registry_present: false,
|
|
728
|
+
fallback_eligible: false,
|
|
729
|
+
};
|
|
699
730
|
}
|
|
700
731
|
else {
|
|
701
732
|
let evidence;
|
|
@@ -713,6 +744,7 @@ async function runParentLoop(options) {
|
|
|
713
744
|
providerName = evidence.provider ?? "default";
|
|
714
745
|
providerSelectionReason = evidence.selectionReason;
|
|
715
746
|
providersTried = evidence.providersTried;
|
|
747
|
+
routingSummary = evidence.routingSummary;
|
|
716
748
|
}
|
|
717
749
|
const executionConfig = adapterName === "agent-subtask"
|
|
718
750
|
? { ...(config.execution ?? { providers: {} }), adapter: "agent-subtask" }
|
|
@@ -1038,11 +1070,11 @@ async function runParentLoop(options) {
|
|
|
1038
1070
|
onStateUpdate: (loopState) => {
|
|
1039
1071
|
// Persist loop state to the state file on each mutation.
|
|
1040
1072
|
const stateWithLoop = { ...state, qc_repair_loop: loopState };
|
|
1041
|
-
|
|
1073
|
+
writeStateAtomic(stateFile, stateWithLoop);
|
|
1042
1074
|
},
|
|
1043
1075
|
});
|
|
1044
1076
|
state = { ...state, qc_repair_loop: repairLoopResult.loop_state };
|
|
1045
|
-
|
|
1077
|
+
writeStateAtomic(stateFile, state);
|
|
1046
1078
|
appendTelemetry(telemetryFile, {
|
|
1047
1079
|
event: "qc-repair-loop-finished",
|
|
1048
1080
|
run_id: state.run_id,
|
|
@@ -1164,8 +1196,7 @@ async function runParentLoop(options) {
|
|
|
1164
1196
|
if (!dryRun) {
|
|
1165
1197
|
logStatus(notificationFormat, "COMPLETE");
|
|
1166
1198
|
const clusterCompleteState = { ...state, status: "cluster-complete" };
|
|
1167
|
-
|
|
1168
|
-
(0, checkpoint_js_1.writeStateAtomic)((0, node_path_1.join)(repoRoot, '.polaris', 'clusters', state.cluster_id, 'state.json'), clusterCompleteState);
|
|
1199
|
+
writeStateAtomic(stateFile, clusterCompleteState);
|
|
1169
1200
|
appendTelemetry(telemetryFile, {
|
|
1170
1201
|
event: "cluster-complete",
|
|
1171
1202
|
run_id: state.run_id,
|
|
@@ -1225,7 +1256,7 @@ async function runParentLoop(options) {
|
|
|
1225
1256
|
if (budgetCheck.status === 'exhausted') {
|
|
1226
1257
|
// Write checkpoint before halting
|
|
1227
1258
|
if (!dryRun) {
|
|
1228
|
-
|
|
1259
|
+
writeStateAtomic(stateFile, {
|
|
1229
1260
|
...state,
|
|
1230
1261
|
status: "budget-exhausted",
|
|
1231
1262
|
step_cursor: "budget-check",
|
|
@@ -1489,6 +1520,7 @@ async function runParentLoop(options) {
|
|
|
1489
1520
|
provider: providerName,
|
|
1490
1521
|
providerSelectionReason,
|
|
1491
1522
|
providersTried,
|
|
1523
|
+
routingSummary,
|
|
1492
1524
|
workerId,
|
|
1493
1525
|
dispatchedAt,
|
|
1494
1526
|
});
|
|
@@ -1498,7 +1530,7 @@ async function runParentLoop(options) {
|
|
|
1498
1530
|
step_cursor: "dispatch",
|
|
1499
1531
|
dispatch_boundary: (0, dispatch_boundary_js_1.advanceDispatchEpoch)(state.dispatch_boundary, nextChild),
|
|
1500
1532
|
};
|
|
1501
|
-
|
|
1533
|
+
writeStateAtomic(stateFile, stateWithDispatch);
|
|
1502
1534
|
state = stateWithDispatch;
|
|
1503
1535
|
stateBeforeDispatch = stateWithDispatch;
|
|
1504
1536
|
try {
|
|
@@ -1563,6 +1595,7 @@ async function runParentLoop(options) {
|
|
|
1563
1595
|
dry_run: dryRun,
|
|
1564
1596
|
selected_slot_claim: selectedSlotClaim ?? null,
|
|
1565
1597
|
slot_claims: nextSlotClaims,
|
|
1598
|
+
routing_summary: routingSummary ?? null,
|
|
1566
1599
|
timestamp: new Date().toISOString(),
|
|
1567
1600
|
});
|
|
1568
1601
|
emitBootstrapContextSize(telemetryFile, state.run_id, nextChild, stateFile, packet);
|
|
@@ -1600,7 +1633,7 @@ async function runParentLoop(options) {
|
|
|
1600
1633
|
// No result file was written. Roll back dispatch state so the run is
|
|
1601
1634
|
// cleanly resumable without manual `loop abort` intervention.
|
|
1602
1635
|
if (dispatchResult.pre_dispatch_failure && !dryRun) {
|
|
1603
|
-
|
|
1636
|
+
writeStateAtomic(stateFile, statePreDispatch);
|
|
1604
1637
|
appendTelemetry(telemetryFile, {
|
|
1605
1638
|
event: "pre-dispatch-failure",
|
|
1606
1639
|
run_id: state.run_id,
|
|
@@ -1751,7 +1784,7 @@ async function runParentLoop(options) {
|
|
|
1751
1784
|
timestamp: new Date().toISOString(),
|
|
1752
1785
|
});
|
|
1753
1786
|
// Write checkpoint with blocker information
|
|
1754
|
-
|
|
1787
|
+
writeStateAtomic(stateFile, {
|
|
1755
1788
|
...state,
|
|
1756
1789
|
status: "blocked",
|
|
1757
1790
|
step_cursor: "blocked",
|
|
@@ -2136,7 +2169,7 @@ async function runParentLoop(options) {
|
|
|
2136
2169
|
childrenDispatched += 1;
|
|
2137
2170
|
// Worker did not write its own completion — orchestrator fills the gap.
|
|
2138
2171
|
if (!dryRun) {
|
|
2139
|
-
|
|
2172
|
+
writeStateAtomic(stateFile, state);
|
|
2140
2173
|
}
|
|
2141
2174
|
}
|
|
2142
2175
|
else {
|
|
@@ -2152,7 +2185,7 @@ async function runParentLoop(options) {
|
|
|
2152
2185
|
}, nextChild);
|
|
2153
2186
|
childrenDispatched += 1;
|
|
2154
2187
|
if (!dryRun) {
|
|
2155
|
-
|
|
2188
|
+
writeStateAtomic(stateFile, state);
|
|
2156
2189
|
}
|
|
2157
2190
|
}
|
|
2158
2191
|
// Orchestrator checkpoint event — always emitted after a successful child.
|
|
@@ -2213,6 +2246,7 @@ async function runParentLoop(options) {
|
|
|
2213
2246
|
model: modelUsed,
|
|
2214
2247
|
router_selection_reason: dispatchRecord?.provider_selection_reason,
|
|
2215
2248
|
providers_tried: dispatchRecord?.providers_tried,
|
|
2249
|
+
routing_summary: dispatchRecord?.routing_summary ?? null,
|
|
2216
2250
|
timestamp: new Date().toISOString(),
|
|
2217
2251
|
};
|
|
2218
2252
|
if (elapsedSeconds !== undefined) {
|
|
@@ -2228,6 +2262,7 @@ async function runParentLoop(options) {
|
|
|
2228
2262
|
completionStatus: workerStatus,
|
|
2229
2263
|
routerSelectionReason: dispatchRecord?.provider_selection_reason,
|
|
2230
2264
|
providersTried: dispatchRecord?.providers_tried,
|
|
2265
|
+
routingSummary: dispatchRecord?.routing_summary,
|
|
2231
2266
|
});
|
|
2232
2267
|
}
|
|
2233
2268
|
if (orchestrationMode === 'supervised') {
|
|
@@ -2250,7 +2285,7 @@ async function runParentLoop(options) {
|
|
|
2250
2285
|
if (postBudgetCheck.status === 'exhausted') {
|
|
2251
2286
|
const nextPending = state.open_children[0] ?? null;
|
|
2252
2287
|
if (!dryRun) {
|
|
2253
|
-
|
|
2288
|
+
writeStateAtomic(stateFile, {
|
|
2254
2289
|
...state,
|
|
2255
2290
|
status: "budget-exhausted",
|
|
2256
2291
|
step_cursor: "budget-check",
|
|
@@ -141,6 +141,8 @@ function runLoopBootstrapInit(options) {
|
|
|
141
141
|
const runId = options.runId ?? deriveRunId(clusterId);
|
|
142
142
|
const seal = createBootstrapSeal(runId, clusterId, openChildren);
|
|
143
143
|
const deliveryBranch = branch ?? (0, git_custody_js_1.buildDeliveryBranchName)(clusterId);
|
|
144
|
+
const resolvedArtifactDir = (0, node_path_1.resolve)(repoRoot, artifactDir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run"));
|
|
145
|
+
const artifactDirValue = (0, node_path_1.relative)(repoRoot, resolvedArtifactDir);
|
|
144
146
|
const initialState = {
|
|
145
147
|
schema_version: "1.0",
|
|
146
148
|
run_id: runId,
|
|
@@ -159,7 +161,7 @@ function runLoopBootstrapInit(options) {
|
|
|
159
161
|
},
|
|
160
162
|
status: "running",
|
|
161
163
|
next_open_child: openChildren[0] ?? null,
|
|
162
|
-
artifact_dir:
|
|
164
|
+
artifact_dir: artifactDirValue,
|
|
163
165
|
dispatch_boundary: (0, dispatch_boundary_js_2.initialDispatchBoundary)(),
|
|
164
166
|
run_bootstrap_seal: seal,
|
|
165
167
|
};
|
|
@@ -220,7 +220,10 @@ async function ensureClusterRunState(options) {
|
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
-
const
|
|
223
|
+
const graph = await loadOrSyncGraph(clusterId, repoRoot);
|
|
224
|
+
const { openChildren, openChildrenMeta } = buildBootstrapPlan(clusterId, graph, repoRoot);
|
|
225
|
+
// Persist canonical child ordering and any body-derived ordering dependencies.
|
|
226
|
+
await graph.save(clusterId, repoRoot);
|
|
224
227
|
await bootstrapHandler({
|
|
225
228
|
clusterId,
|
|
226
229
|
openChildren,
|