@lsctech/polaris 0.5.12 → 0.5.14
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/adopt-canon.js +22 -8
- package/dist/finalize/artifact-policy.js +1 -24
- package/dist/finalize/index.js +13 -17
- package/dist/finalize/run-report.js +3 -3
- package/dist/finalize/steps/06-commit.js +1 -2
- package/dist/finalize/steps/12-archive.js +1 -1
- package/dist/loop/adapters/cli-subtask-bridge.js +2 -0
- package/dist/loop/adapters/terminal-cli.js +31 -3
- package/dist/loop/body-parser.js +4 -0
- package/dist/loop/continue.js +3 -3
- package/dist/loop/dispatch.js +1 -1
- package/dist/loop/orphan-recovery.js +2 -1
- package/dist/loop/parent.js +28 -12
- package/dist/loop/run-bootstrap.js +3 -1
- package/dist/map/inference.js +4 -1
- package/dist/medic/run-health-consult.js +5 -4
- package/dist/medic/treatment-packets.js +5 -1
- package/dist/qc/providers/coderabbit.js +2 -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/workspace/POLARIS.md +4 -0
- package/dist/workspace/SUMMARY.md +56 -0
- package/package.json +1 -1
package/dist/cli/adopt-canon.js
CHANGED
|
@@ -6,6 +6,7 @@ const node_path_1 = require("node:path");
|
|
|
6
6
|
const node_child_process_1 = require("node:child_process");
|
|
7
7
|
const loader_js_1 = require("../config/loader.js");
|
|
8
8
|
const librarian_dispatch_js_1 = require("../smartdocs-engine/librarian-dispatch.js");
|
|
9
|
+
const seed_instructions_js_1 = require("../smartdocs-engine/seed-instructions.js");
|
|
9
10
|
const SKIP_DIRS = new Set(["node_modules", ".git", "dist", "build", ".polaris", "smartdocs"]);
|
|
10
11
|
function loadInventoryCanonicalFolders(repoRoot) {
|
|
11
12
|
const inventoryPath = (0, node_path_1.join)(repoRoot, ".polaris", "adoption-inventory.json");
|
|
@@ -29,7 +30,7 @@ function scaffoldDraftSummaryFiles(repoRoot, canonicalFolders) {
|
|
|
29
30
|
if ((0, node_fs_1.existsSync)(summaryPath))
|
|
30
31
|
continue;
|
|
31
32
|
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
32
|
-
(0, node_fs_1.writeFileSync)(summaryPath,
|
|
33
|
+
(0, node_fs_1.writeFileSync)(summaryPath, (0, seed_instructions_js_1.generateSummaryDraft)(folder, repoRoot, {}), "utf-8");
|
|
33
34
|
console.log(` Scaffolded draft SUMMARY.md: ${folder}`);
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -90,13 +91,19 @@ function injectLinkedDocs(content, docs, summaryLines) {
|
|
|
90
91
|
const headingIdx = lines.findIndex((l) => l.startsWith("#"));
|
|
91
92
|
if (headingIdx === -1)
|
|
92
93
|
return content;
|
|
94
|
+
const heading = lines[headingIdx];
|
|
95
|
+
const hasSummaryContent = summaryLines.length > 0;
|
|
93
96
|
const yamlLines = ["", "---", ...buildLinkedDocsBlock(docs), "---"];
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
const bodyLines = hasSummaryContent
|
|
98
|
+
? [...yamlLines, "", ...summaryLines]
|
|
99
|
+
: [...yamlLines, ...lines.slice(headingIdx + 1).filter((l) => {
|
|
100
|
+
const t = l.trim();
|
|
101
|
+
return t !== seed_instructions_js_1.DRAFT_MARKER && t !== seed_instructions_js_1.GENERATED_START_MARKER && t !== seed_instructions_js_1.GENERATED_END_MARKER;
|
|
102
|
+
})];
|
|
103
|
+
const generatedRegion = [heading, ...bodyLines].join("\n");
|
|
104
|
+
return hasSummaryContent
|
|
105
|
+
? [seed_instructions_js_1.GENERATED_START_MARKER, generatedRegion, seed_instructions_js_1.GENERATED_END_MARKER].join("\n")
|
|
106
|
+
: [seed_instructions_js_1.DRAFT_MARKER, seed_instructions_js_1.GENERATED_START_MARKER, generatedRegion, seed_instructions_js_1.GENERATED_END_MARKER].join("\n");
|
|
100
107
|
}
|
|
101
108
|
function parseCanonResponse(text) {
|
|
102
109
|
const trimmed = text.trim();
|
|
@@ -249,12 +256,19 @@ async function enrichCanonFiles(repoRoot) {
|
|
|
249
256
|
if ((response.polaris_lines ?? []).length > 0) {
|
|
250
257
|
const polarisPath = (0, node_path_1.join)(dir, "POLARIS.md");
|
|
251
258
|
const polarisContent = [
|
|
259
|
+
seed_instructions_js_1.GENERATED_START_MARKER,
|
|
252
260
|
`# POLARIS — ${routeFolder}`,
|
|
253
261
|
"",
|
|
254
262
|
...response.polaris_lines,
|
|
255
263
|
"",
|
|
264
|
+
seed_instructions_js_1.GENERATED_END_MARKER,
|
|
256
265
|
].join("\n");
|
|
257
|
-
(0, node_fs_1.
|
|
266
|
+
if (!(0, node_fs_1.existsSync)(polarisPath) || (0, seed_instructions_js_1.hasDraftMarker)(polarisPath)) {
|
|
267
|
+
(0, node_fs_1.writeFileSync)(polarisPath, polarisContent, "utf-8");
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
console.log(` Skipping POLARIS.md (existing non-draft): ${routeFolder}`);
|
|
271
|
+
}
|
|
258
272
|
}
|
|
259
273
|
enrichedCount++;
|
|
260
274
|
console.log(` ✓ Enriched ${routeFolder} with ${response.relevant_docs.length} linked docs`);
|
|
@@ -24,23 +24,6 @@ const LEGACY_RUN_ARTIFACTS = new Set([
|
|
|
24
24
|
".polaris/runs/run-report.md",
|
|
25
25
|
".polaris/runs/current-state.pre-pol-198.json",
|
|
26
26
|
]);
|
|
27
|
-
function isPromotedRunArchivePath(relativePath) {
|
|
28
|
-
if (!relativePath.startsWith(".polaris/runs/")) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
const rest = relativePath.slice(".polaris/runs/".length);
|
|
32
|
-
if (rest === "" || rest === "evo-run-archive") {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
const slashIndex = rest.indexOf("/");
|
|
36
|
-
if (slashIndex === -1) {
|
|
37
|
-
// The path is a directory-style run archive path (e.g. `.polaris/runs/<run-id>`),
|
|
38
|
-
// not a top-level file like `ledger.jsonl`.
|
|
39
|
-
return !rest.includes(".") && !rest.startsWith(".");
|
|
40
|
-
}
|
|
41
|
-
const firstSegment = rest.slice(0, slashIndex);
|
|
42
|
-
return firstSegment !== "evo-run-archive";
|
|
43
|
-
}
|
|
44
27
|
function normalizeArtifactPath(filePath) {
|
|
45
28
|
const normalized = node_path_1.default.posix.normalize(filePath.replace(/\\/g, "/"));
|
|
46
29
|
return normalized.startsWith("./") ? normalized.slice(2) : normalized;
|
|
@@ -78,9 +61,6 @@ function classifyArtifactPath(filePath, activeClusterId) {
|
|
|
78
61
|
if (relativePath === PROMOTED_RUN_LEDGER) {
|
|
79
62
|
return "promoted-run-ledger";
|
|
80
63
|
}
|
|
81
|
-
if (isPromotedRunArchivePath(relativePath)) {
|
|
82
|
-
return "promoted-run-archive";
|
|
83
|
-
}
|
|
84
64
|
if (relativePath.startsWith(PROMOTED_COGNITION_ARCHIVE_PREFIX)) {
|
|
85
65
|
return "promoted-cognition-archive";
|
|
86
66
|
}
|
|
@@ -102,7 +82,6 @@ function isPromotedArtifactPath(filePath, activeClusterId) {
|
|
|
102
82
|
const classification = classifyArtifactPath(filePath, activeClusterId);
|
|
103
83
|
return (classification === "promoted-cluster-artifact"
|
|
104
84
|
|| classification === "promoted-run-ledger"
|
|
105
|
-
|| classification === "promoted-run-archive"
|
|
106
85
|
|| classification === "promoted-cognition-archive"
|
|
107
86
|
|| classification === "promoted-map-artifact");
|
|
108
87
|
}
|
|
@@ -114,8 +93,6 @@ function explainArtifactPolicy(filePath, activeClusterId) {
|
|
|
114
93
|
return "active cluster evidence is eligible for promotion into finalize commits";
|
|
115
94
|
case "promoted-run-ledger":
|
|
116
95
|
return "the run ledger is durable audit evidence and stays commit-eligible";
|
|
117
|
-
case "promoted-run-archive":
|
|
118
|
-
return "archived run snapshots under .polaris/runs/<run-id>/ are durable audit evidence and stay commit-eligible";
|
|
119
96
|
case "promoted-cognition-archive":
|
|
120
97
|
return "archived cognition reconciliation notes are durable provenance and stay commit-eligible";
|
|
121
98
|
case "promoted-map-artifact":
|
|
@@ -139,7 +116,6 @@ function findArtifactPromotionViolations(stagedPaths, activeClusterId) {
|
|
|
139
116
|
if (classification === "non-artifact"
|
|
140
117
|
|| classification === "promoted-cluster-artifact"
|
|
141
118
|
|| classification === "promoted-run-ledger"
|
|
142
|
-
|| classification === "promoted-run-archive"
|
|
143
119
|
|| classification === "promoted-cognition-archive"
|
|
144
120
|
|| classification === "promoted-map-artifact") {
|
|
145
121
|
continue;
|
|
@@ -194,6 +170,7 @@ function getGitignorePatterns() {
|
|
|
194
170
|
".polaris/runs/current-state.json",
|
|
195
171
|
".polaris/runs/run-report.md",
|
|
196
172
|
".polaris/runs/current-state.pre-pol-198.json",
|
|
173
|
+
".polaris/runs/*/",
|
|
197
174
|
".polaris/runs/evo-run-archive/**",
|
|
198
175
|
".polaris/bootstrap/**",
|
|
199
176
|
".polaris/session-type",
|
package/dist/finalize/index.js
CHANGED
|
@@ -246,7 +246,7 @@ function checkLibrarianGate(repoRoot, clusterId) {
|
|
|
246
246
|
}
|
|
247
247
|
function resolveQcTelemetryFile(state, repoRoot) {
|
|
248
248
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
249
|
-
return (0, node_path_1.
|
|
249
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
250
250
|
}
|
|
251
251
|
// ── QC repair-loop terminal state gate ────────────────────────────────────────
|
|
252
252
|
/**
|
|
@@ -628,7 +628,7 @@ async function runFinalize(options) {
|
|
|
628
628
|
throw new Error(`Canon check cannot proceed: git diff failed: ${msg}`);
|
|
629
629
|
}
|
|
630
630
|
const artifactDirForCheck = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
631
|
-
const telemetryFileForCheck = (0, node_path_1.
|
|
631
|
+
const telemetryFileForCheck = (0, node_path_1.resolve)(repoRoot, artifactDirForCheck, "runs", state.run_id, "telemetry.jsonl");
|
|
632
632
|
const canonResult = (0, canon_check_js_1.runCanonCheck)({
|
|
633
633
|
repoRoot,
|
|
634
634
|
changedFiles,
|
|
@@ -867,6 +867,13 @@ async function runFinalize(options) {
|
|
|
867
867
|
console.log("[8/14] Committing durable Polaris state + map..."); // Step count updated
|
|
868
868
|
const resolvedStateFile = (0, node_path_1.resolve)(stateFile);
|
|
869
869
|
(0, _06_commit_js_1.stepCommit)(repoRoot, state, resolvedStateFile, reportPath);
|
|
870
|
+
// The run's local state file is the authoritative place for post-PR state
|
|
871
|
+
// (pr_url, status: complete). When the state file passed to finalize is the
|
|
872
|
+
// tracked cluster snapshot, redirect post-PR writes to the untracked per-run
|
|
873
|
+
// archive so the cluster snapshot is never written after PR creation.
|
|
874
|
+
const runLocalStateFile = isClusterStateSnapshotFile(resolvedStateFile)
|
|
875
|
+
? (0, node_path_1.resolve)(repoRoot, ".polaris", "runs", state.run_id, "current-state.json")
|
|
876
|
+
: resolvedStateFile;
|
|
870
877
|
if (skipDelivery) {
|
|
871
878
|
console.log("[9–14/14] Delivery skipped (--skip-delivery).");
|
|
872
879
|
console.log("polaris finalize steps 1–8 complete.");
|
|
@@ -890,24 +897,13 @@ async function runFinalize(options) {
|
|
|
890
897
|
prUrl,
|
|
891
898
|
stepLabel: "[10.5/14]",
|
|
892
899
|
});
|
|
893
|
-
// Step 11: Write PR URL to
|
|
900
|
+
// Step 11: Write PR URL to the run's local state file
|
|
894
901
|
console.log("[11/14] Writing PR URL to state...");
|
|
895
|
-
state = (0, _09_update_state_js_1.stepUpdateState)(
|
|
896
|
-
// Promote the authoritative run state into the cluster snapshot so that
|
|
897
|
-
// `.polaris/clusters/<id>/state.json` preserves completed children, the
|
|
898
|
-
// dispatch boundary, QC repair-loop terminal state, and the PR URL.
|
|
899
|
-
try {
|
|
900
|
-
const clusterStateSnapshotPath = (0, node_path_1.join)(repoRoot, ".polaris", "clusters", state.cluster_id, "state.json");
|
|
901
|
-
(0, checkpoint_js_1.writeStateAtomic)(clusterStateSnapshotPath, state);
|
|
902
|
-
}
|
|
903
|
-
catch (snapshotError) {
|
|
904
|
-
process.stderr.write(`[11/14] WARNING: Failed to write cluster state snapshot: ${snapshotError instanceof Error ? snapshotError.message : String(snapshotError)}\n`);
|
|
905
|
-
process.stderr.write(`Delivery will proceed, but cluster state snapshot at .polaris/clusters/${state.cluster_id}/state.json may be incomplete.\n`);
|
|
906
|
-
}
|
|
902
|
+
state = (0, _09_update_state_js_1.stepUpdateState)(runLocalStateFile, state, prUrl);
|
|
907
903
|
// Step 12: Append JSONL events
|
|
908
904
|
console.log("[12/14] Appending JSONL events...");
|
|
909
905
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
910
|
-
const telemetryFile = (0, node_path_1.
|
|
906
|
+
const telemetryFile = (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
911
907
|
(0, _10_append_jsonl_js_1.stepAppendJsonl)(telemetryFile, state, prUrl);
|
|
912
908
|
// Step 13: Update Linear parent issue
|
|
913
909
|
console.log("[13/14] Updating Linear...");
|
|
@@ -916,7 +912,7 @@ async function runFinalize(options) {
|
|
|
916
912
|
await (0, _11_update_linear_js_1.stepUpdateLinear)(state, branch, prUrl, true, linearEnabled, state.cluster_id, lifecyclePolicy, authChildResult.authoritativeCount);
|
|
917
913
|
// Step 14: Archive run snapshot
|
|
918
914
|
console.log("[14/14] Archiving run snapshot...");
|
|
919
|
-
(0, _12_archive_js_1.stepArchive)(repoRoot, state,
|
|
915
|
+
(0, _12_archive_js_1.stepArchive)(repoRoot, state, runLocalStateFile, reportPath);
|
|
920
916
|
console.log("polaris finalize complete.");
|
|
921
917
|
}
|
|
922
918
|
function failMissingSubcommand(command, commandName) {
|
|
@@ -155,9 +155,9 @@ function renderRoutingSection(state, telemetryEvents) {
|
|
|
155
155
|
: "No routing anomalies or evidence gaps detected.";
|
|
156
156
|
return `### Provider distribution
|
|
157
157
|
|
|
158
|
-
| Child | Provider | Selection reason | Mode | Policy order | Candidates | Fallbacks | Status |
|
|
159
|
-
|
|
160
|
-
${rows || "| _No children recorded_ | — | — | — | — | — | — | — |"}
|
|
158
|
+
| Child | Title | Provider | Selection reason | Mode | Policy order | Candidates | Fallbacks | Status |
|
|
159
|
+
|---|---|---|---|---|---|---|---|---|
|
|
160
|
+
${rows || "| _No children recorded_ | — | — | — | — | — | — | — | — |"}
|
|
161
161
|
|
|
162
162
|
**Provider summary:** ${distribution}
|
|
163
163
|
|
|
@@ -7,8 +7,7 @@ const node_path_1 = require("node:path");
|
|
|
7
7
|
const artifact_policy_js_1 = require("../artifact-policy.js");
|
|
8
8
|
function stepCommit(repoRoot, state, _stateFile, _reportPath) {
|
|
9
9
|
const msg = `polaris finalize: ${state.run_id}\n\nChildren: ${state.completed_children.length} completed\nBranch: ${getBranch(repoRoot)}`;
|
|
10
|
-
const
|
|
11
|
-
const promotedTargets = [...(0, artifact_policy_js_1.getArtifactPromotionStageTargets)(state.cluster_id), runArchiveTarget]
|
|
10
|
+
const promotedTargets = (0, artifact_policy_js_1.getArtifactPromotionStageTargets)(state.cluster_id)
|
|
12
11
|
.filter((target) => (0, node_fs_1.existsSync)((0, node_path_1.join)(repoRoot, target)));
|
|
13
12
|
if (promotedTargets.length > 0) {
|
|
14
13
|
(0, node_child_process_1.execFileSync)("git", ["add", "--", ...promotedTargets], { cwd: repoRoot, stdio: "inherit" });
|
|
@@ -19,7 +19,7 @@ function stepArchive(repoRoot, state, stateFile, reportPath) {
|
|
|
19
19
|
}
|
|
20
20
|
const artifactDir = state.artifact_dir
|
|
21
21
|
?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
22
|
-
const telemetryFile = (0, node_path_1.
|
|
22
|
+
const telemetryFile = (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
23
23
|
if ((0, node_fs_1.existsSync)(telemetryFile)) {
|
|
24
24
|
(0, node_fs_1.copyFileSync)(telemetryFile, (0, node_path_1.join)(archiveDir, "telemetry.jsonl"));
|
|
25
25
|
}
|
|
@@ -61,10 +61,12 @@ function writeSealedResultFromSummary(packet, parsedSummary) {
|
|
|
61
61
|
}
|
|
62
62
|
const commit = (typeof parsedSummary["commit"] === "string" && parsedSummary["commit"]) ||
|
|
63
63
|
(typeof parsedSummary["commit_hash"] === "string" && parsedSummary["commit_hash"]) ||
|
|
64
|
+
(typeof parsedSummary["commit_sha"] === "string" && parsedSummary["commit_sha"]) ||
|
|
64
65
|
undefined;
|
|
65
66
|
const validation = parsedSummary["validation"] ?? parsedSummary["validation_summary"];
|
|
66
67
|
const sealedResult = {
|
|
67
68
|
run_id: packet.run_id,
|
|
69
|
+
cluster_id: packet.cluster_id,
|
|
68
70
|
child_id: packet.active_child,
|
|
69
71
|
status: normalizeSealedStatus(parsedSummary["status"]),
|
|
70
72
|
};
|
|
@@ -498,15 +498,42 @@ class TerminalCliAdapter {
|
|
|
498
498
|
const compact = buildCompactReturnForSealedResult(normalized, exitCode);
|
|
499
499
|
const compactReturnErrors = (0, compact_return_js_1.validateCompactReturn)(compact);
|
|
500
500
|
const isValidCompactReturn = compactReturnErrors.length === 0;
|
|
501
|
-
|
|
501
|
+
let finalStatus = isValidCompactReturn
|
|
502
502
|
? compact["status"]
|
|
503
503
|
: "failed";
|
|
504
|
-
|
|
504
|
+
let finalValidation = isValidCompactReturn
|
|
505
505
|
? compact["validation"]
|
|
506
506
|
: "failed";
|
|
507
|
-
|
|
507
|
+
let finalNext = isValidCompactReturn
|
|
508
508
|
? compact["next_recommended_action"]
|
|
509
509
|
: "stop";
|
|
510
|
+
// Reconcile validation/next_recommended_action with run-health symptoms.
|
|
511
|
+
// A validation-failed symptom overrides a passing validation string and
|
|
512
|
+
// prevents a continue recommendation when the build/tests did not pass.
|
|
513
|
+
const rawSymptoms = compact["run_health_symptoms"];
|
|
514
|
+
const hasValidationFailedSymptom = Array.isArray(rawSymptoms) &&
|
|
515
|
+
rawSymptoms.some((s) => typeof s === "object" &&
|
|
516
|
+
s !== null &&
|
|
517
|
+
s["category"] === "validation-failed");
|
|
518
|
+
if (hasValidationFailedSymptom) {
|
|
519
|
+
finalValidation = "failed";
|
|
520
|
+
finalNext = "stop";
|
|
521
|
+
}
|
|
522
|
+
// "continue" requires a passing validation result. If validation failed,
|
|
523
|
+
// the recommendation must be a terminal stop. If validation was skipped,
|
|
524
|
+
// treat the successful exit as a pass for the completion contract.
|
|
525
|
+
if (finalStatus === "done" && finalNext === "continue") {
|
|
526
|
+
if (finalValidation === "failed") {
|
|
527
|
+
finalNext = "stop";
|
|
528
|
+
}
|
|
529
|
+
else if (finalValidation === "skipped") {
|
|
530
|
+
finalValidation = "passed";
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
// A failed status should never recommend continuation.
|
|
534
|
+
if ((finalStatus === "failed" || finalStatus === "blocked") && finalNext === "continue") {
|
|
535
|
+
finalNext = "stop";
|
|
536
|
+
}
|
|
510
537
|
const errorMessage = finalStatus === "failed" || finalStatus === "blocked"
|
|
511
538
|
? isValidCompactReturn
|
|
512
539
|
? typeof compact["error_message"] === "string"
|
|
@@ -518,6 +545,7 @@ class TerminalCliAdapter {
|
|
|
518
545
|
: undefined;
|
|
519
546
|
const sealedResult = {
|
|
520
547
|
run_id: packet.run_id,
|
|
548
|
+
cluster_id: packet.cluster_id,
|
|
521
549
|
child_id: compact["child_id"],
|
|
522
550
|
status: finalStatus,
|
|
523
551
|
commit: compact["commit"],
|
package/dist/loop/body-parser.js
CHANGED
|
@@ -168,6 +168,10 @@ function normalizeDirectoryPattern(pattern) {
|
|
|
168
168
|
// Extract the final path segment to check for extensions
|
|
169
169
|
const lastSlashIndex = pattern.lastIndexOf('/');
|
|
170
170
|
const finalSegment = lastSlashIndex >= 0 ? pattern.slice(lastSlashIndex + 1) : pattern;
|
|
171
|
+
// Dot-files that are files, not directories, must not get a trailing `/**`.
|
|
172
|
+
if (finalSegment === '.gitignore' || finalSegment === '.gitattributes') {
|
|
173
|
+
return pattern;
|
|
174
|
+
}
|
|
171
175
|
// Check if final segment has a file extension (not counting leading dot in dot-directories)
|
|
172
176
|
const hasExtension = finalSegment.startsWith('.')
|
|
173
177
|
? finalSegment.slice(1).includes('.') // For .github, .vscode: no extension. For .env.local: has extension
|
package/dist/loop/continue.js
CHANGED
|
@@ -215,7 +215,7 @@ function appendTelemetryEvent(telemetryFile, event) {
|
|
|
215
215
|
}
|
|
216
216
|
function resolveTelemetryFilePath(state, repoRoot) {
|
|
217
217
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
218
|
-
return (0, node_path_1.
|
|
218
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
219
219
|
}
|
|
220
220
|
function hasValidationEvidence(validation) {
|
|
221
221
|
if (validation === undefined || validation === null)
|
|
@@ -383,7 +383,7 @@ function runLoopContinue(options) {
|
|
|
383
383
|
// If no dispatch was recorded (dispatch_epoch === continue_epoch),
|
|
384
384
|
// reject immediately and do NOT mutate any state.
|
|
385
385
|
const artifactDirForTelemetry = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
386
|
-
const telemetryFileForCheck = (0, node_path_1.
|
|
386
|
+
const telemetryFileForCheck = (0, node_path_1.resolve)(repoRoot, artifactDirForTelemetry, "runs", state.run_id, "telemetry.jsonl");
|
|
387
387
|
try {
|
|
388
388
|
(0, dispatch_boundary_js_1.assertContinueRequiresDispatch)(state, telemetryFileForCheck);
|
|
389
389
|
}
|
|
@@ -622,7 +622,7 @@ function runLoopContinue(options) {
|
|
|
622
622
|
appendContinueLedgerEvents(repoRoot, updatedState, completedChild);
|
|
623
623
|
// Step 2: Append JSONL checkpoint event
|
|
624
624
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
625
|
-
const telemetryFile = (0, node_path_1.
|
|
625
|
+
const telemetryFile = (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
626
626
|
(0, checkpoint_js_1.appendCheckpointEvent)(telemetryFile, {
|
|
627
627
|
event: "loop-checkpoint",
|
|
628
628
|
run_id: state.run_id,
|
package/dist/loop/dispatch.js
CHANGED
|
@@ -373,7 +373,7 @@ function appendTelemetry(telemetryFile, event) {
|
|
|
373
373
|
}
|
|
374
374
|
function resolveTelemetryFile(state, repoRoot) {
|
|
375
375
|
const artifactDir = state.artifact_dir ?? (0, node_path_1.join)(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
376
|
-
return (0, node_path_1.
|
|
376
|
+
return (0, node_path_1.resolve)(repoRoot, artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
377
377
|
}
|
|
378
378
|
function canonicalPath(path) {
|
|
379
379
|
try {
|
|
@@ -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);
|
|
@@ -685,6 +685,23 @@ async function runParentLoop(options) {
|
|
|
685
685
|
if (!dryRun) {
|
|
686
686
|
appendRunStartedLedgerEvent(repoRoot, state);
|
|
687
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
|
+
};
|
|
688
705
|
// Load config for adapter and provider resolution
|
|
689
706
|
const config = (0, loader_js_1.loadConfig)(repoRoot);
|
|
690
707
|
const legacyOrchestrationMode = state["orchestration_mode"];
|
|
@@ -1053,11 +1070,11 @@ async function runParentLoop(options) {
|
|
|
1053
1070
|
onStateUpdate: (loopState) => {
|
|
1054
1071
|
// Persist loop state to the state file on each mutation.
|
|
1055
1072
|
const stateWithLoop = { ...state, qc_repair_loop: loopState };
|
|
1056
|
-
|
|
1073
|
+
writeStateAtomic(stateFile, stateWithLoop);
|
|
1057
1074
|
},
|
|
1058
1075
|
});
|
|
1059
1076
|
state = { ...state, qc_repair_loop: repairLoopResult.loop_state };
|
|
1060
|
-
|
|
1077
|
+
writeStateAtomic(stateFile, state);
|
|
1061
1078
|
appendTelemetry(telemetryFile, {
|
|
1062
1079
|
event: "qc-repair-loop-finished",
|
|
1063
1080
|
run_id: state.run_id,
|
|
@@ -1179,8 +1196,7 @@ async function runParentLoop(options) {
|
|
|
1179
1196
|
if (!dryRun) {
|
|
1180
1197
|
logStatus(notificationFormat, "COMPLETE");
|
|
1181
1198
|
const clusterCompleteState = { ...state, status: "cluster-complete" };
|
|
1182
|
-
|
|
1183
|
-
(0, checkpoint_js_1.writeStateAtomic)((0, node_path_1.join)(repoRoot, '.polaris', 'clusters', state.cluster_id, 'state.json'), clusterCompleteState);
|
|
1199
|
+
writeStateAtomic(stateFile, clusterCompleteState);
|
|
1184
1200
|
appendTelemetry(telemetryFile, {
|
|
1185
1201
|
event: "cluster-complete",
|
|
1186
1202
|
run_id: state.run_id,
|
|
@@ -1240,7 +1256,7 @@ async function runParentLoop(options) {
|
|
|
1240
1256
|
if (budgetCheck.status === 'exhausted') {
|
|
1241
1257
|
// Write checkpoint before halting
|
|
1242
1258
|
if (!dryRun) {
|
|
1243
|
-
|
|
1259
|
+
writeStateAtomic(stateFile, {
|
|
1244
1260
|
...state,
|
|
1245
1261
|
status: "budget-exhausted",
|
|
1246
1262
|
step_cursor: "budget-check",
|
|
@@ -1514,7 +1530,7 @@ async function runParentLoop(options) {
|
|
|
1514
1530
|
step_cursor: "dispatch",
|
|
1515
1531
|
dispatch_boundary: (0, dispatch_boundary_js_1.advanceDispatchEpoch)(state.dispatch_boundary, nextChild),
|
|
1516
1532
|
};
|
|
1517
|
-
|
|
1533
|
+
writeStateAtomic(stateFile, stateWithDispatch);
|
|
1518
1534
|
state = stateWithDispatch;
|
|
1519
1535
|
stateBeforeDispatch = stateWithDispatch;
|
|
1520
1536
|
try {
|
|
@@ -1617,7 +1633,7 @@ async function runParentLoop(options) {
|
|
|
1617
1633
|
// No result file was written. Roll back dispatch state so the run is
|
|
1618
1634
|
// cleanly resumable without manual `loop abort` intervention.
|
|
1619
1635
|
if (dispatchResult.pre_dispatch_failure && !dryRun) {
|
|
1620
|
-
|
|
1636
|
+
writeStateAtomic(stateFile, statePreDispatch);
|
|
1621
1637
|
appendTelemetry(telemetryFile, {
|
|
1622
1638
|
event: "pre-dispatch-failure",
|
|
1623
1639
|
run_id: state.run_id,
|
|
@@ -1768,7 +1784,7 @@ async function runParentLoop(options) {
|
|
|
1768
1784
|
timestamp: new Date().toISOString(),
|
|
1769
1785
|
});
|
|
1770
1786
|
// Write checkpoint with blocker information
|
|
1771
|
-
|
|
1787
|
+
writeStateAtomic(stateFile, {
|
|
1772
1788
|
...state,
|
|
1773
1789
|
status: "blocked",
|
|
1774
1790
|
step_cursor: "blocked",
|
|
@@ -2153,7 +2169,7 @@ async function runParentLoop(options) {
|
|
|
2153
2169
|
childrenDispatched += 1;
|
|
2154
2170
|
// Worker did not write its own completion — orchestrator fills the gap.
|
|
2155
2171
|
if (!dryRun) {
|
|
2156
|
-
|
|
2172
|
+
writeStateAtomic(stateFile, state);
|
|
2157
2173
|
}
|
|
2158
2174
|
}
|
|
2159
2175
|
else {
|
|
@@ -2169,7 +2185,7 @@ async function runParentLoop(options) {
|
|
|
2169
2185
|
}, nextChild);
|
|
2170
2186
|
childrenDispatched += 1;
|
|
2171
2187
|
if (!dryRun) {
|
|
2172
|
-
|
|
2188
|
+
writeStateAtomic(stateFile, state);
|
|
2173
2189
|
}
|
|
2174
2190
|
}
|
|
2175
2191
|
// Orchestrator checkpoint event — always emitted after a successful child.
|
|
@@ -2269,7 +2285,7 @@ async function runParentLoop(options) {
|
|
|
2269
2285
|
if (postBudgetCheck.status === 'exhausted') {
|
|
2270
2286
|
const nextPending = state.open_children[0] ?? null;
|
|
2271
2287
|
if (!dryRun) {
|
|
2272
|
-
|
|
2288
|
+
writeStateAtomic(stateFile, {
|
|
2273
2289
|
...state,
|
|
2274
2290
|
status: "budget-exhausted",
|
|
2275
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
|
};
|
package/dist/map/inference.js
CHANGED
|
@@ -17,7 +17,10 @@ function inferRoute(filePath, repoRoot, config, knownRoutes, branchName) {
|
|
|
17
17
|
const subdir = rest.split("/")[0];
|
|
18
18
|
if (subdir && rest.includes("/")) {
|
|
19
19
|
domain = subdir;
|
|
20
|
-
|
|
20
|
+
// Route identity follows the file's own containing directory, not just the
|
|
21
|
+
// top-level domain subdir — otherwise nested folders (e.g. src/runtime/continuation)
|
|
22
|
+
// incorrectly report the parent domain's route (e.g. src/runtime).
|
|
23
|
+
route = (0, node_path_1.dirname)(filePath).replace(/\\/g, "/");
|
|
21
24
|
taskchain = `polaris-${subdir}`;
|
|
22
25
|
confidence += 0.85;
|
|
23
26
|
tags.push(subdir);
|
|
@@ -114,11 +114,12 @@ function markInProgress(report, chartRef, repoRoot) {
|
|
|
114
114
|
(0, index_js_1.markMedicDecision)(report.run_id, { status: "in-progress", chartRefs: [chartRef] }, repoRoot);
|
|
115
115
|
}
|
|
116
116
|
function markResolved(report, chartRef, treatmentRefs, outcome, repoRoot) {
|
|
117
|
+
const success = outcome === "treatment-success" || outcome === "no-treatment-needed";
|
|
117
118
|
(0, index_js_1.markMedicDecision)(report.run_id, {
|
|
118
|
-
status: "resolved",
|
|
119
|
+
status: success ? "resolved" : "in-progress",
|
|
119
120
|
chartRefs: [chartRef],
|
|
120
121
|
treatmentPacketRefs: treatmentRefs,
|
|
121
|
-
resolvedAt: new Date().toISOString(),
|
|
122
|
+
resolvedAt: success ? new Date().toISOString() : undefined,
|
|
122
123
|
resolutionNotes: `Terminal outcome: ${outcome}`,
|
|
123
124
|
}, repoRoot);
|
|
124
125
|
}
|
|
@@ -355,7 +356,7 @@ async function runMedicRunHealthConsult(input) {
|
|
|
355
356
|
run_id: packet.run_id,
|
|
356
357
|
cluster_id: packet.cluster_id,
|
|
357
358
|
dispatch_id: packet.dispatch_id,
|
|
358
|
-
status: "
|
|
359
|
+
status: "blocked",
|
|
359
360
|
chart_id: chart.chart_id,
|
|
360
361
|
decision: "terminal",
|
|
361
362
|
treatment_packet_refs: allTreatmentRefs,
|
|
@@ -371,7 +372,7 @@ async function runMedicRunHealthConsult(input) {
|
|
|
371
372
|
run_id: packet.run_id,
|
|
372
373
|
cluster_id: packet.cluster_id,
|
|
373
374
|
dispatch_id: packet.dispatch_id,
|
|
374
|
-
status: "resolved",
|
|
375
|
+
status: remainingSymptoms.length === 0 ? "resolved" : "blocked",
|
|
375
376
|
chart_id: chart.chart_id,
|
|
376
377
|
decision: "terminal",
|
|
377
378
|
treatment_packet_refs: allTreatmentRefs,
|
|
@@ -59,7 +59,7 @@ function buildTreatmentPackets(input) {
|
|
|
59
59
|
return treated.map((symptom) => {
|
|
60
60
|
const packetId = buildTreatmentPacketId(report.run_id, round, symptom.id);
|
|
61
61
|
const dispatchId = (0, node_crypto_1.randomUUID)();
|
|
62
|
-
const resultFile = (0, node_path_1.join)(getMedicDir(report.cluster_id, repoRoot), `${packetId}-result.json`);
|
|
62
|
+
const resultFile = (0, node_path_1.relative)(repoRoot, (0, node_path_1.join)(getMedicDir(report.cluster_id, repoRoot), `${packetId}-result.json`));
|
|
63
63
|
return {
|
|
64
64
|
packet_id: packetId,
|
|
65
65
|
run_id: report.run_id,
|
|
@@ -154,12 +154,16 @@ async function dispatchTreatmentWorker(input) {
|
|
|
154
154
|
const result = await dispatch(workerPacket);
|
|
155
155
|
const summary = parseTreatmentWorkerSummary(result.summary);
|
|
156
156
|
if (result.exit_code === 0 && summary?.status === "done") {
|
|
157
|
+
const completedTreatment = { ...treatment, status: "completed" };
|
|
158
|
+
writeTreatmentPacket({ treatment: completedTreatment, repoRoot });
|
|
157
159
|
return {
|
|
158
160
|
packet_id: treatment.packet_id,
|
|
159
161
|
status: "success",
|
|
160
162
|
commit_sha: summary.commit,
|
|
161
163
|
};
|
|
162
164
|
}
|
|
165
|
+
const failedTreatment = { ...treatment, status: "failed" };
|
|
166
|
+
writeTreatmentPacket({ treatment: failedTreatment, repoRoot });
|
|
163
167
|
return {
|
|
164
168
|
packet_id: treatment.packet_id,
|
|
165
169
|
status: "failure",
|
|
@@ -148,6 +148,8 @@ function isErrorRecord(record) {
|
|
|
148
148
|
return true;
|
|
149
149
|
if (record.error === true || record.error === "true")
|
|
150
150
|
return true;
|
|
151
|
+
if (typeof record.status === "string" && record.status.toLowerCase() === "error")
|
|
152
|
+
return true;
|
|
151
153
|
return false;
|
|
152
154
|
}
|
|
153
155
|
function errorTypeFromRecord(record) {
|
package/dist/run-health/index.js
CHANGED
|
@@ -150,6 +150,18 @@ function appendSymptom(runId, symptom, repoRoot) {
|
|
|
150
150
|
symptoms: [...existing.symptoms, symptom],
|
|
151
151
|
updated_at: new Date().toISOString(),
|
|
152
152
|
};
|
|
153
|
+
// A later high/critical symptom means a previously resolved Medic consult is
|
|
154
|
+
// no longer satisfied; reopen it so closeout/medic gates do not pass on a
|
|
155
|
+
// stale resolved decision.
|
|
156
|
+
if (existing.medic_consult?.status === "resolved" &&
|
|
157
|
+
(symptom.severity === "critical" || symptom.severity === "high")) {
|
|
158
|
+
updated.medic_consult = {
|
|
159
|
+
...existing.medic_consult,
|
|
160
|
+
status: "in-progress",
|
|
161
|
+
resolved_at: undefined,
|
|
162
|
+
resolution_notes: undefined,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
153
165
|
const validation = (0, schema_js_1.validateRunHealthReport)(updated);
|
|
154
166
|
if (!validation.valid) {
|
|
155
167
|
throw new Error(`Appended symptom produces invalid report:\n${validation.errors.join("\n")}`);
|