@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,247 @@
|
|
|
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 librarian_dispatch_js_1 = require("./librarian-dispatch.js");
|
|
8
|
+
const summary_delta_js_1 = require("./summary-delta.js");
|
|
9
|
+
function makeTmp() {
|
|
10
|
+
const dir = (0, node_path_1.join)((0, node_os_1.tmpdir)(), `polaris-librarian-test-${Date.now()}`);
|
|
11
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
12
|
+
return dir;
|
|
13
|
+
}
|
|
14
|
+
function cleanup(dir) {
|
|
15
|
+
try {
|
|
16
|
+
(0, node_fs_1.rmSync)(dir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
// ignore
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function makePacket(overrides) {
|
|
23
|
+
return {
|
|
24
|
+
run_id: "test-run-001",
|
|
25
|
+
dispatch_id: "dispatch-001",
|
|
26
|
+
role: "cognition-librarian",
|
|
27
|
+
folder: "src/loop/",
|
|
28
|
+
folder_slug: "src-loop",
|
|
29
|
+
note_paths: [".polaris/cognition/pending/src-loop/note.md"],
|
|
30
|
+
polaris_md_path: "src/loop/POLARIS.md",
|
|
31
|
+
summary_md_path: "src/loop/SUMMARY.md",
|
|
32
|
+
cognition_index_path: ".polaris/cognition/archive/src-loop/cognition-index.json",
|
|
33
|
+
result_path: "/tmp/result.json",
|
|
34
|
+
constraints: {
|
|
35
|
+
max_polaris_addition_lines: 20,
|
|
36
|
+
max_summary_addition_lines: 30,
|
|
37
|
+
require_confidence_threshold: 0.80,
|
|
38
|
+
allowed_files: ["src/loop/POLARIS.md", "src/loop/SUMMARY.md"],
|
|
39
|
+
},
|
|
40
|
+
...overrides,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function makeResult(overrides) {
|
|
44
|
+
return {
|
|
45
|
+
run_id: "test-run-001",
|
|
46
|
+
dispatch_id: "dispatch-001",
|
|
47
|
+
role: "cognition-librarian",
|
|
48
|
+
folder: "src/loop/",
|
|
49
|
+
folder_slug: "src-loop",
|
|
50
|
+
notes_reconciled: [".polaris/cognition/pending/src-loop/note.md"],
|
|
51
|
+
confidence: 0.90,
|
|
52
|
+
proposed_patches: [],
|
|
53
|
+
archive_actions: [],
|
|
54
|
+
status: "success",
|
|
55
|
+
...overrides,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function makePatch(overrides) {
|
|
59
|
+
return {
|
|
60
|
+
file: "src/loop/POLARIS.md",
|
|
61
|
+
action: "update",
|
|
62
|
+
proposed_content: "# POLARIS\n\nShort update.",
|
|
63
|
+
change_summary: "minor update",
|
|
64
|
+
...overrides,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
(0, vitest_1.describe)("validateAndApplyLibrarianResult", () => {
|
|
68
|
+
let tmp;
|
|
69
|
+
(0, vitest_1.beforeEach)(() => {
|
|
70
|
+
tmp = makeTmp();
|
|
71
|
+
});
|
|
72
|
+
(0, vitest_1.afterEach)(() => {
|
|
73
|
+
cleanup(tmp);
|
|
74
|
+
});
|
|
75
|
+
// ── No-patch / success cases ─────────────────────────────────────────────
|
|
76
|
+
(0, vitest_1.it)("approves a valid result with no patches (no-change)", () => {
|
|
77
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ status: "no-change" }), tmp, makePacket());
|
|
78
|
+
(0, vitest_1.expect)(outcome.approved).toBe(true);
|
|
79
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
80
|
+
(0, vitest_1.expect)(outcome.patches_applied).toEqual([]);
|
|
81
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toEqual([]);
|
|
82
|
+
});
|
|
83
|
+
(0, vitest_1.it)("writes valid POLARIS.md patch to disk", () => {
|
|
84
|
+
const content = "# POLARIS\n\nNew content.";
|
|
85
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
86
|
+
confidence: 0.95,
|
|
87
|
+
proposed_patches: [makePatch({ proposed_content: content })],
|
|
88
|
+
}), tmp, makePacket());
|
|
89
|
+
(0, vitest_1.expect)(outcome.approved).toBe(true);
|
|
90
|
+
(0, vitest_1.expect)(outcome.patches_applied).toHaveLength(1);
|
|
91
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual(["src/loop/POLARIS.md"]);
|
|
92
|
+
(0, vitest_1.expect)((0, node_fs_1.readFileSync)((0, node_path_1.join)(tmp, "src/loop/POLARIS.md"), "utf-8")).toBe(content);
|
|
93
|
+
});
|
|
94
|
+
(0, vitest_1.it)("approves at exactly the confidence threshold boundary", () => {
|
|
95
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ confidence: 0.80 }), tmp, makePacket());
|
|
96
|
+
(0, vitest_1.expect)(outcome.approved).toBe(true);
|
|
97
|
+
});
|
|
98
|
+
// ── Schema validation (rejects entire result) ───────────────────────────
|
|
99
|
+
(0, vitest_1.it)("rejects result with invalid schema: missing run_id", () => {
|
|
100
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ run_id: "" }), tmp, makePacket());
|
|
101
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
102
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/SCHEMA_INVALID/);
|
|
103
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
104
|
+
});
|
|
105
|
+
(0, vitest_1.it)("rejects result with invalid schema: wrong role", () => {
|
|
106
|
+
const result = makeResult();
|
|
107
|
+
const bad = { ...result, role: "worker" };
|
|
108
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(bad, tmp, makePacket());
|
|
109
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
110
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/SCHEMA_INVALID/);
|
|
111
|
+
});
|
|
112
|
+
(0, vitest_1.it)("rejects result with invalid schema: confidence out of range", () => {
|
|
113
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ confidence: 1.5 }), tmp, makePacket());
|
|
114
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
115
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/SCHEMA_INVALID/);
|
|
116
|
+
});
|
|
117
|
+
// ── Confidence threshold §6.4 (rejects entire result) ──────────────────
|
|
118
|
+
(0, vitest_1.it)("rejects result below confidence threshold", () => {
|
|
119
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ confidence: 0.50 }), tmp, makePacket());
|
|
120
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
121
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/COGNITION_LOW_CONFIDENCE/);
|
|
122
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/0\.5/);
|
|
123
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
124
|
+
});
|
|
125
|
+
(0, vitest_1.it)("rejects result just below confidence threshold (0.799)", () => {
|
|
126
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({ confidence: 0.799 }), tmp, makePacket());
|
|
127
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
128
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/COGNITION_LOW_CONFIDENCE/);
|
|
129
|
+
});
|
|
130
|
+
// ── File scope check §6.1 (rejects entire result) ───────────────────────
|
|
131
|
+
(0, vitest_1.it)("rejects entire result when any patch targets an out-of-scope file", () => {
|
|
132
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
133
|
+
proposed_patches: [
|
|
134
|
+
makePatch({ file: "src/other/POLARIS.md" }),
|
|
135
|
+
],
|
|
136
|
+
}), tmp, makePacket());
|
|
137
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
138
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/COGNITION_SCOPE_VIOLATION/);
|
|
139
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/src\/other\/POLARIS\.md/);
|
|
140
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
141
|
+
});
|
|
142
|
+
(0, vitest_1.it)("rejects entire result when second patch is out-of-scope, even if first is valid", () => {
|
|
143
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
144
|
+
proposed_patches: [
|
|
145
|
+
makePatch({ file: "src/loop/POLARIS.md" }),
|
|
146
|
+
makePatch({ file: "src/other/POLARIS.md" }),
|
|
147
|
+
],
|
|
148
|
+
}), tmp, makePacket());
|
|
149
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
150
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toMatch(/COGNITION_SCOPE_VIOLATION/);
|
|
151
|
+
// No files written — entire result rejected
|
|
152
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
153
|
+
(0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(tmp, "src/loop/POLARIS.md"))).toBe(false);
|
|
154
|
+
});
|
|
155
|
+
// ── Doctrine bleed §6.2 (rejects specific patch, not entire result) ────
|
|
156
|
+
(0, vitest_1.it)("rejects SUMMARY.md patch containing doctrine bleed patterns", () => {
|
|
157
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
158
|
+
proposed_patches: [
|
|
159
|
+
makePatch({
|
|
160
|
+
file: "src/loop/SUMMARY.md",
|
|
161
|
+
proposed_content: "## Editing Rules\nDo not do X.",
|
|
162
|
+
}),
|
|
163
|
+
],
|
|
164
|
+
}), tmp, makePacket());
|
|
165
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(1);
|
|
166
|
+
(0, vitest_1.expect)(outcome.patches_rejected[0].reason).toMatch(/COGNITION_DOCTRINE_BLEED/);
|
|
167
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
168
|
+
});
|
|
169
|
+
(0, vitest_1.it)("rejects doctrine-bleeding SUMMARY.md patch but applies valid POLARIS.md patch", () => {
|
|
170
|
+
const polarisContent = "# Loop\n\nUpdated.";
|
|
171
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
172
|
+
proposed_patches: [
|
|
173
|
+
makePatch({ file: "src/loop/POLARIS.md", proposed_content: polarisContent }),
|
|
174
|
+
makePatch({
|
|
175
|
+
file: "src/loop/SUMMARY.md",
|
|
176
|
+
proposed_content: "## Constraints\nMust always use X.",
|
|
177
|
+
}),
|
|
178
|
+
],
|
|
179
|
+
}), tmp, makePacket());
|
|
180
|
+
(0, vitest_1.expect)(outcome.approved).toBe(true);
|
|
181
|
+
(0, vitest_1.expect)(outcome.patches_applied).toHaveLength(1);
|
|
182
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(1);
|
|
183
|
+
(0, vitest_1.expect)(outcome.patches_rejected[0].reason).toMatch(/COGNITION_DOCTRINE_BLEED/);
|
|
184
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual(["src/loop/POLARIS.md"]);
|
|
185
|
+
(0, vitest_1.expect)((0, node_fs_1.readFileSync)((0, node_path_1.join)(tmp, "src/loop/POLARIS.md"), "utf-8")).toBe(polarisContent);
|
|
186
|
+
});
|
|
187
|
+
(0, vitest_1.it)("does not apply doctrine-bleed check to POLARIS.md patches", () => {
|
|
188
|
+
// POLARIS.md can contain constraint-like language — only SUMMARY.md is checked
|
|
189
|
+
const content = "## Constraints\nMust always do X.";
|
|
190
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
191
|
+
proposed_patches: [makePatch({ proposed_content: content })],
|
|
192
|
+
}), tmp, makePacket());
|
|
193
|
+
// POLARIS.md lines check: "## Constraints\nMust always do X." = 2 lines ≤ 20
|
|
194
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(0);
|
|
195
|
+
(0, vitest_1.expect)(outcome.patches_applied).toHaveLength(1);
|
|
196
|
+
});
|
|
197
|
+
// ── Size guard §6.3 (rejects specific patch, not entire result) ─────────
|
|
198
|
+
(0, vitest_1.it)("rejects POLARIS.md patch exceeding max_polaris_addition_lines", () => {
|
|
199
|
+
const lines = Array.from({ length: 21 }, (_, i) => `line ${i + 1}`).join("\n");
|
|
200
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
201
|
+
proposed_patches: [makePatch({ proposed_content: lines })],
|
|
202
|
+
}), tmp, makePacket());
|
|
203
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(1);
|
|
204
|
+
(0, vitest_1.expect)(outcome.patches_rejected[0].reason).toMatch(/COGNITION_SIZE_GUARD/);
|
|
205
|
+
(0, vitest_1.expect)(outcome.patches_rejected[0].reason).toMatch(/21 lines/);
|
|
206
|
+
});
|
|
207
|
+
(0, vitest_1.it)("accepts POLARIS.md patch at exactly the line limit", () => {
|
|
208
|
+
const lines = Array.from({ length: 20 }, (_, i) => `line ${i + 1}`).join("\n");
|
|
209
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
210
|
+
proposed_patches: [makePatch({ proposed_content: lines })],
|
|
211
|
+
}), tmp, makePacket());
|
|
212
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(0);
|
|
213
|
+
(0, vitest_1.expect)(outcome.patches_applied).toHaveLength(1);
|
|
214
|
+
});
|
|
215
|
+
(0, vitest_1.it)("rejects SUMMARY.md patch exceeding SUMMARY_MAX_BYTES", () => {
|
|
216
|
+
const content = "x".repeat(summary_delta_js_1.SUMMARY_MAX_BYTES + 1);
|
|
217
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
218
|
+
proposed_patches: [
|
|
219
|
+
makePatch({ file: "src/loop/SUMMARY.md", proposed_content: content }),
|
|
220
|
+
],
|
|
221
|
+
}), tmp, makePacket());
|
|
222
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(1);
|
|
223
|
+
(0, vitest_1.expect)(outcome.patches_rejected[0].reason).toMatch(/COGNITION_SIZE_GUARD/);
|
|
224
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
(0, vitest_1.it)("accepts SUMMARY.md patch just under SUMMARY_MAX_BYTES", () => {
|
|
227
|
+
const content = "x".repeat(summary_delta_js_1.SUMMARY_MAX_BYTES - 1);
|
|
228
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
229
|
+
proposed_patches: [
|
|
230
|
+
makePatch({ file: "src/loop/SUMMARY.md", proposed_content: content }),
|
|
231
|
+
],
|
|
232
|
+
}), tmp, makePacket());
|
|
233
|
+
(0, vitest_1.expect)(outcome.patches_rejected).toHaveLength(0);
|
|
234
|
+
(0, vitest_1.expect)(outcome.patches_applied).toHaveLength(1);
|
|
235
|
+
});
|
|
236
|
+
// ── All-rejected edge case ───────────────────────────────────────────────
|
|
237
|
+
(0, vitest_1.it)("returns approved: false when all patches are rejected at patch level", () => {
|
|
238
|
+
const oversized = Array.from({ length: 21 }, (_, i) => `line ${i + 1}`).join("\n");
|
|
239
|
+
const outcome = (0, librarian_dispatch_js_1.validateAndApplyLibrarianResult)(makeResult({
|
|
240
|
+
proposed_patches: [makePatch({ proposed_content: oversized })],
|
|
241
|
+
}), tmp, makePacket());
|
|
242
|
+
(0, vitest_1.expect)(outcome.approved).toBe(false);
|
|
243
|
+
(0, vitest_1.expect)(outcome.rejection_reason).toBe("all patches rejected");
|
|
244
|
+
(0, vitest_1.expect)(outcome.files_written).toEqual([]);
|
|
245
|
+
(0, vitest_1.expect)((0, node_fs_1.existsSync)((0, node_path_1.join)(tmp, "src/loop/POLARIS.md"))).toBe(false);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generates a CloseoutLibrarianPacket for a completed cluster.
|
|
4
|
+
*
|
|
5
|
+
* Called by `polaris librarian packet <cluster-id>`.
|
|
6
|
+
* Writes the packet to .polaris/clusters/<cluster-id>/packets/librarian-packet-<dispatch-id>.json
|
|
7
|
+
* and prints the absolute path to stdout.
|
|
8
|
+
*/
|
|
9
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
10
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.generateLibrarianPacket = generateLibrarianPacket;
|
|
14
|
+
const node_crypto_1 = require("node:crypto");
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const node_child_process_1 = require("node:child_process");
|
|
18
|
+
const checkpoint_js_1 = require("../loop/checkpoint.js");
|
|
19
|
+
function generateLibrarianPacket(options) {
|
|
20
|
+
const repoRoot = node_path_1.default.resolve(options.repoRoot);
|
|
21
|
+
const { clusterId } = options;
|
|
22
|
+
let stateFile;
|
|
23
|
+
if (!options.stateFile) {
|
|
24
|
+
stateFile = resolveStateFile(repoRoot, clusterId);
|
|
25
|
+
}
|
|
26
|
+
else if (node_path_1.default.isAbsolute(options.stateFile)) {
|
|
27
|
+
stateFile = options.stateFile;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
stateFile = node_path_1.default.resolve(repoRoot, options.stateFile);
|
|
31
|
+
}
|
|
32
|
+
const state = (0, checkpoint_js_1.readState)(stateFile);
|
|
33
|
+
if (state.cluster_id !== clusterId) {
|
|
34
|
+
throw new Error(`State file cluster_id "${state.cluster_id}" does not match requested cluster "${clusterId}".`);
|
|
35
|
+
}
|
|
36
|
+
if (state.status !== "cluster-complete") {
|
|
37
|
+
throw new Error(`Cluster "${clusterId}" is not complete (status: "${state.status}"). ` +
|
|
38
|
+
`All children must be done before dispatching the Closeout Librarian.`);
|
|
39
|
+
}
|
|
40
|
+
if (state.completed_children.length === 0) {
|
|
41
|
+
throw new Error(`Cluster "${clusterId}" has no completed children.`);
|
|
42
|
+
}
|
|
43
|
+
const dispatchId = (0, node_crypto_1.randomUUID)();
|
|
44
|
+
const resultsDir = node_path_1.default.join(repoRoot, ".polaris", "clusters", clusterId, "results");
|
|
45
|
+
const artifactDir = state.artifact_dir ?? node_path_1.default.join(repoRoot, ".taskchain_artifacts", "polaris-run");
|
|
46
|
+
const telemetryFile = node_path_1.default.join(artifactDir, "runs", state.run_id, "telemetry.jsonl");
|
|
47
|
+
// Build per-child summaries
|
|
48
|
+
const childSummaries = state.completed_children.map((childId) => {
|
|
49
|
+
const resultFile = findResultFile(resultsDir, childId);
|
|
50
|
+
const result = resultFile ? readJsonFile(resultFile) : null;
|
|
51
|
+
const commitSha = typeof result?.["commit"] === "string" ? result["commit"] : null;
|
|
52
|
+
const changedFiles = commitSha ? getCommitFiles(repoRoot, commitSha) : [];
|
|
53
|
+
const cognitionNotePath = findCognitionNote(repoRoot, childId);
|
|
54
|
+
return {
|
|
55
|
+
child_id: childId,
|
|
56
|
+
title: state.open_children_meta?.[childId]?.title ?? childId,
|
|
57
|
+
commit_sha: commitSha,
|
|
58
|
+
changed_files: changedFiles,
|
|
59
|
+
compact_return_path: resultFile ? node_path_1.default.relative(repoRoot, resultFile) : null,
|
|
60
|
+
cognition_note_path: cognitionNotePath,
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
// Derive affected folders from all changed files across all children
|
|
64
|
+
const allChangedFiles = childSummaries.flatMap((s) => s.changed_files);
|
|
65
|
+
const affectedFolders = computeAffectedFolders(repoRoot, allChangedFiles);
|
|
66
|
+
// Build per-folder cognition paths
|
|
67
|
+
const polarisMdPaths = affectedFolders.map((folder) => {
|
|
68
|
+
const slug = folder.replace(/\/+$/, "").replace(/[^a-zA-Z0-9]+/g, "-");
|
|
69
|
+
const summaryAbs = node_path_1.default.join(repoRoot, folder, "SUMMARY.md");
|
|
70
|
+
const cognitionIndexAbs = node_path_1.default.join(repoRoot, ".polaris", "cognition", "index", `${slug}.json`);
|
|
71
|
+
return {
|
|
72
|
+
folder,
|
|
73
|
+
folder_slug: slug,
|
|
74
|
+
polaris_md: node_path_1.default.join(folder, "POLARIS.md"),
|
|
75
|
+
summary_md: node_fs_1.default.existsSync(summaryAbs) ? node_path_1.default.join(folder, "SUMMARY.md") : null,
|
|
76
|
+
cognition_index: node_fs_1.default.existsSync(cognitionIndexAbs)
|
|
77
|
+
? node_path_1.default.relative(repoRoot, cognitionIndexAbs)
|
|
78
|
+
: null,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
const cognitionNotes = findPendingCognitionNotes(repoRoot, state.completed_children);
|
|
82
|
+
const cognitionArchivePaths = findCognitionArchivePaths(repoRoot, affectedFolders);
|
|
83
|
+
const smartdocsRawPaths = findSmartdocsRaw(repoRoot);
|
|
84
|
+
const existingSpecs = findActiveSpecs(repoRoot);
|
|
85
|
+
const existingDoctrine = findActiveDoctrine(repoRoot);
|
|
86
|
+
const runReportPath = findRunReport(repoRoot, state.run_id);
|
|
87
|
+
const packetDir = node_path_1.default.join(repoRoot, ".polaris", "clusters", clusterId, "packets");
|
|
88
|
+
const resultDir = node_path_1.default.join(repoRoot, ".polaris", "clusters", clusterId, "results");
|
|
89
|
+
const resultPath = node_path_1.default.join(resultDir, `librarian-${dispatchId}.json`);
|
|
90
|
+
// Prohibited: anything that is not documentation or cognition
|
|
91
|
+
const prohibitedWritePaths = [
|
|
92
|
+
stateFile,
|
|
93
|
+
node_path_1.default.join(repoRoot, ".polaris", "clusters", clusterId, "state.json"),
|
|
94
|
+
node_path_1.default.join(repoRoot, ".taskchain_artifacts"),
|
|
95
|
+
node_path_1.default.join(repoRoot, ".polaris", "runs"),
|
|
96
|
+
node_path_1.default.join(repoRoot, ".polaris", "map"),
|
|
97
|
+
packetDir,
|
|
98
|
+
resultDir,
|
|
99
|
+
telemetryFile,
|
|
100
|
+
node_path_1.default.join(repoRoot, "src"),
|
|
101
|
+
node_path_1.default.join(repoRoot, "test"),
|
|
102
|
+
node_path_1.default.join(repoRoot, "scripts"),
|
|
103
|
+
];
|
|
104
|
+
// Allowed: documentation and cognition paths only
|
|
105
|
+
const allowedWritePaths = [
|
|
106
|
+
...polarisMdPaths.map((p) => node_path_1.default.join(repoRoot, p.polaris_md)),
|
|
107
|
+
...polarisMdPaths.filter((p) => p.summary_md).map((p) => node_path_1.default.join(repoRoot, p.summary_md)),
|
|
108
|
+
node_path_1.default.join(repoRoot, "smartdocs"),
|
|
109
|
+
node_path_1.default.join(repoRoot, ".polaris", "cognition", "archive"),
|
|
110
|
+
resultPath,
|
|
111
|
+
];
|
|
112
|
+
const packet = {
|
|
113
|
+
schema_version: "1.0",
|
|
114
|
+
role: "closeout-librarian",
|
|
115
|
+
run_id: state.run_id,
|
|
116
|
+
dispatch_id: dispatchId,
|
|
117
|
+
cluster_id: clusterId,
|
|
118
|
+
completed_children: state.completed_children,
|
|
119
|
+
child_summaries: childSummaries,
|
|
120
|
+
run_report_path: runReportPath,
|
|
121
|
+
current_state_path: node_path_1.default.relative(repoRoot, stateFile),
|
|
122
|
+
affected_folders: affectedFolders,
|
|
123
|
+
polaris_md_paths: polarisMdPaths,
|
|
124
|
+
cognition_notes: cognitionNotes,
|
|
125
|
+
cognition_archive_paths: cognitionArchivePaths,
|
|
126
|
+
smartdocs_raw_paths: smartdocsRawPaths,
|
|
127
|
+
existing_specs: existingSpecs,
|
|
128
|
+
existing_doctrine: existingDoctrine,
|
|
129
|
+
result_path: resultPath,
|
|
130
|
+
prohibited_write_paths: prohibitedWritePaths,
|
|
131
|
+
allowed_write_paths: allowedWritePaths,
|
|
132
|
+
constraints: {
|
|
133
|
+
max_summary_addition_lines: 50,
|
|
134
|
+
require_polaris_confidence_threshold: 0.8,
|
|
135
|
+
librarian_timeout_seconds: 600,
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
node_fs_1.default.mkdirSync(packetDir, { recursive: true });
|
|
139
|
+
const packetPath = node_path_1.default.join(packetDir, `librarian-packet-${dispatchId}.json`);
|
|
140
|
+
node_fs_1.default.writeFileSync(packetPath, JSON.stringify(packet, null, 2), "utf-8");
|
|
141
|
+
process.stdout.write(`${packetPath}\n`);
|
|
142
|
+
return packetPath;
|
|
143
|
+
}
|
|
144
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
145
|
+
function resolveStateFile(repoRoot, clusterId) {
|
|
146
|
+
const canonical = node_path_1.default.join(repoRoot, ".polaris", "clusters", clusterId, "state.json");
|
|
147
|
+
if (node_fs_1.default.existsSync(canonical))
|
|
148
|
+
return canonical;
|
|
149
|
+
const taskchain = node_path_1.default.join(repoRoot, ".taskchain_artifacts", "polaris-run", "current-state.json");
|
|
150
|
+
if (node_fs_1.default.existsSync(taskchain))
|
|
151
|
+
return taskchain;
|
|
152
|
+
const legacy = node_path_1.default.join(repoRoot, ".polaris", "runs", "current-state.json");
|
|
153
|
+
if (node_fs_1.default.existsSync(legacy))
|
|
154
|
+
return legacy;
|
|
155
|
+
return canonical;
|
|
156
|
+
}
|
|
157
|
+
function findResultFile(resultsDir, childId) {
|
|
158
|
+
try {
|
|
159
|
+
const files = node_fs_1.default
|
|
160
|
+
.readdirSync(resultsDir)
|
|
161
|
+
.filter((f) => f.startsWith(`${childId}-`) && f.endsWith(".json"))
|
|
162
|
+
.sort();
|
|
163
|
+
if (files.length === 0)
|
|
164
|
+
return null;
|
|
165
|
+
return node_path_1.default.join(resultsDir, files[files.length - 1]);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function readJsonFile(filePath) {
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(node_fs_1.default.readFileSync(filePath, "utf-8"));
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
function getCommitFiles(repoRoot, commitSha) {
|
|
180
|
+
try {
|
|
181
|
+
const output = (0, node_child_process_1.execFileSync)("git", ["show", "--name-only", "--format=", commitSha], { cwd: repoRoot, encoding: "utf-8" });
|
|
182
|
+
return output.trim().split("\n").filter(Boolean);
|
|
183
|
+
}
|
|
184
|
+
catch {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function findCognitionNote(repoRoot, childId) {
|
|
189
|
+
const pendingDir = node_path_1.default.join(repoRoot, ".polaris", "cognition", "pending");
|
|
190
|
+
const candidates = [
|
|
191
|
+
node_path_1.default.join(pendingDir, `${childId}.md`),
|
|
192
|
+
node_path_1.default.join(pendingDir, `${childId}.json`),
|
|
193
|
+
];
|
|
194
|
+
for (const candidate of candidates) {
|
|
195
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
196
|
+
return node_path_1.default.relative(repoRoot, candidate);
|
|
197
|
+
}
|
|
198
|
+
// Also try scanning for files that start with the child ID
|
|
199
|
+
try {
|
|
200
|
+
const found = node_fs_1.default
|
|
201
|
+
.readdirSync(pendingDir)
|
|
202
|
+
.find((f) => f.startsWith(childId + "-") || f.startsWith(childId + "."));
|
|
203
|
+
if (found)
|
|
204
|
+
return node_path_1.default.relative(repoRoot, node_path_1.default.join(pendingDir, found));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// ignore
|
|
208
|
+
}
|
|
209
|
+
return null;
|
|
210
|
+
}
|
|
211
|
+
function computeAffectedFolders(repoRoot, changedFiles) {
|
|
212
|
+
const skip = new Set([".polaris", "node_modules", ".taskchain_artifacts", ".github"]);
|
|
213
|
+
const folders = new Set();
|
|
214
|
+
for (const file of changedFiles) {
|
|
215
|
+
const parts = file.split("/");
|
|
216
|
+
if (skip.has(parts[0] ?? ""))
|
|
217
|
+
continue;
|
|
218
|
+
// Walk up from the file's directory toward root, collecting folders with POLARIS.md
|
|
219
|
+
for (let depth = 1; depth <= parts.length - 1; depth++) {
|
|
220
|
+
const folder = parts.slice(0, depth).join("/");
|
|
221
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(repoRoot, folder, "POLARIS.md"))) {
|
|
222
|
+
folders.add(folder + "/");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return [...folders].sort();
|
|
227
|
+
}
|
|
228
|
+
function findPendingCognitionNotes(repoRoot, childIds) {
|
|
229
|
+
const pendingDir = node_path_1.default.join(repoRoot, ".polaris", "cognition", "pending");
|
|
230
|
+
const results = [];
|
|
231
|
+
try {
|
|
232
|
+
const files = node_fs_1.default.readdirSync(pendingDir);
|
|
233
|
+
for (const file of files) {
|
|
234
|
+
const base = node_path_1.default.basename(file, node_path_1.default.extname(file));
|
|
235
|
+
if (childIds.some((id) => base === id || base.startsWith(id + "-"))) {
|
|
236
|
+
results.push(node_path_1.default.relative(repoRoot, node_path_1.default.join(pendingDir, file)));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch {
|
|
241
|
+
// no pending dir — normal
|
|
242
|
+
}
|
|
243
|
+
return results;
|
|
244
|
+
}
|
|
245
|
+
function findCognitionArchivePaths(repoRoot, affectedFolders) {
|
|
246
|
+
const archiveDir = node_path_1.default.join(repoRoot, ".polaris", "cognition", "archive");
|
|
247
|
+
const results = [];
|
|
248
|
+
try {
|
|
249
|
+
const slugs = new Set(affectedFolders.map((f) => f.replace(/\/+$/, "").replace(/[^a-zA-Z0-9]+/g, "-")));
|
|
250
|
+
const files = node_fs_1.default.readdirSync(archiveDir);
|
|
251
|
+
for (const file of files) {
|
|
252
|
+
const slug = node_path_1.default.basename(file, node_path_1.default.extname(file)).replace(/-\d{4}-\d{2}-.*$/, "");
|
|
253
|
+
if (slugs.has(slug)) {
|
|
254
|
+
results.push(node_path_1.default.relative(repoRoot, node_path_1.default.join(archiveDir, file)));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
catch {
|
|
259
|
+
// no archive dir — normal
|
|
260
|
+
}
|
|
261
|
+
return results;
|
|
262
|
+
}
|
|
263
|
+
function findSmartdocsRaw(repoRoot) {
|
|
264
|
+
const candidates = [
|
|
265
|
+
node_path_1.default.join(repoRoot, "smartdocs", "raw"),
|
|
266
|
+
node_path_1.default.join(repoRoot, "smartdocs", "docs", "raw"),
|
|
267
|
+
];
|
|
268
|
+
const results = [];
|
|
269
|
+
for (const dir of candidates) {
|
|
270
|
+
try {
|
|
271
|
+
const files = node_fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
272
|
+
for (const entry of files) {
|
|
273
|
+
if (entry.isFile()) {
|
|
274
|
+
results.push(node_path_1.default.relative(repoRoot, node_path_1.default.join(dir, entry.name)));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
// directory may not exist
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return results;
|
|
283
|
+
}
|
|
284
|
+
function findActiveSpecs(repoRoot) {
|
|
285
|
+
const specsDir = node_path_1.default.join(repoRoot, "smartdocs", "specs", "active");
|
|
286
|
+
return listMarkdownFiles(repoRoot, specsDir);
|
|
287
|
+
}
|
|
288
|
+
function findActiveDoctrine(repoRoot) {
|
|
289
|
+
const doctrineDir = node_path_1.default.join(repoRoot, "smartdocs", "doctrine", "active");
|
|
290
|
+
return listMarkdownFiles(repoRoot, doctrineDir);
|
|
291
|
+
}
|
|
292
|
+
function listMarkdownFiles(repoRoot, dir) {
|
|
293
|
+
try {
|
|
294
|
+
return node_fs_1.default
|
|
295
|
+
.readdirSync(dir)
|
|
296
|
+
.filter((f) => f.endsWith(".md"))
|
|
297
|
+
.map((f) => node_path_1.default.relative(repoRoot, node_path_1.default.join(dir, f)));
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
return [];
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function findRunReport(repoRoot, runId) {
|
|
304
|
+
const candidates = [
|
|
305
|
+
node_path_1.default.join(repoRoot, ".taskchain_artifacts", "polaris-run", "runs", runId, "run-report.md"),
|
|
306
|
+
node_path_1.default.join(repoRoot, ".polaris", "runs", runId, "run-report.md"),
|
|
307
|
+
];
|
|
308
|
+
for (const candidate of candidates) {
|
|
309
|
+
if (node_fs_1.default.existsSync(candidate))
|
|
310
|
+
return node_path_1.default.relative(repoRoot, candidate);
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Types and schema validator for the cognition-librarian role.
|
|
4
|
+
*
|
|
5
|
+
* The librarian is dispatched by the foreman after a worker completes. It
|
|
6
|
+
* reconciles staged work notes into durable folder cognition (POLARIS.md /
|
|
7
|
+
* SUMMARY.md) and returns a sealed proposed patch — it never writes directly.
|
|
8
|
+
*
|
|
9
|
+
* Spec: smartdocs/specs/active/folder-cognition-staging-librarian.md §3
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.validateCognitionLibrarianResult = validateCognitionLibrarianResult;
|
|
13
|
+
/**
|
|
14
|
+
* Validate an unknown value against the CognitionLibrarianResult schema.
|
|
15
|
+
* Returns an array of error strings; empty means valid.
|
|
16
|
+
*/
|
|
17
|
+
function validateCognitionLibrarianResult(value) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
if (typeof value !== "object" || value === null) {
|
|
20
|
+
return ["CognitionLibrarianResult must be a JSON object"];
|
|
21
|
+
}
|
|
22
|
+
const r = value;
|
|
23
|
+
if (r["role"] !== "cognition-librarian") {
|
|
24
|
+
errors.push('role must be "cognition-librarian"');
|
|
25
|
+
}
|
|
26
|
+
if (typeof r["run_id"] !== "string" || !r["run_id"]) {
|
|
27
|
+
errors.push("missing or empty run_id");
|
|
28
|
+
}
|
|
29
|
+
if (typeof r["dispatch_id"] !== "string" || !r["dispatch_id"]) {
|
|
30
|
+
errors.push("missing or empty dispatch_id");
|
|
31
|
+
}
|
|
32
|
+
if (typeof r["folder"] !== "string") {
|
|
33
|
+
errors.push("folder must be a string");
|
|
34
|
+
}
|
|
35
|
+
if (typeof r["folder_slug"] !== "string") {
|
|
36
|
+
errors.push("folder_slug must be a string");
|
|
37
|
+
}
|
|
38
|
+
if (!Array.isArray(r["notes_reconciled"])) {
|
|
39
|
+
errors.push("notes_reconciled must be an array");
|
|
40
|
+
}
|
|
41
|
+
if (typeof r["confidence"] !== "number" ||
|
|
42
|
+
r["confidence"] < 0 ||
|
|
43
|
+
r["confidence"] > 1) {
|
|
44
|
+
errors.push("confidence must be a number between 0.0 and 1.0");
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(r["proposed_patches"])) {
|
|
47
|
+
errors.push("proposed_patches must be an array");
|
|
48
|
+
}
|
|
49
|
+
if (!Array.isArray(r["archive_actions"])) {
|
|
50
|
+
errors.push("archive_actions must be an array");
|
|
51
|
+
}
|
|
52
|
+
const validStatuses = ["success", "no-change", "low-confidence", "failure"];
|
|
53
|
+
if (!validStatuses.includes(r["status"])) {
|
|
54
|
+
errors.push(`status must be one of: ${validStatuses.join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
return errors;
|
|
57
|
+
}
|