feat: P4 qdrant collection lifecycle (self-heal + guarded rebuild)
- shared TS collection manager: self-heal creates missing collection (1024/cosine) and missing keyword payload indexes; never mutates incompatible contracts (semantic_index_incompatible); async index visibility polled with bounded deadline - session admission (qdrantEnsure) uses the manager in self-heal mode; operator path keeps require_existing semantics - runtime lease exposes semanticQdrantUrl to the operator - operator commands vector-inspect/vector-rebuild with exact confirmation guards - thothctl workspace vector inspect|rebuild (Go) with --collection/--confirm/--destroy - p4 acceptance runner: real Qdrant (v1.18.2) lifecycle checks, 11/11 PASS - docs: CLI contract, manual walkthrough P4 (PENDING), PROJECT_STATE
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import test from "node:test";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
CHECK_IDS,
|
||||
canonicalIntegrationBase,
|
||||
cleanupOwnedRun,
|
||||
createOwnedRun,
|
||||
validateRunRoot,
|
||||
} from "./p4-acceptance.mjs";
|
||||
|
||||
const roots = [];
|
||||
async function fakeRepository() {
|
||||
const root = await mkdtemp(join(tmpdir(), "p4-acceptance-repo-"));
|
||||
roots.push(root);
|
||||
await mkdir(join(root, ".artifacts", "p4-integration"), { recursive: true });
|
||||
return root;
|
||||
}
|
||||
|
||||
test.afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
test("check ids are stable and unique", () => {
|
||||
assert.equal(new Set(CHECK_IDS).size, CHECK_IDS.length);
|
||||
assert.ok(CHECK_IDS.includes("self_heal_create_missing"));
|
||||
assert.ok(CHECK_IDS.includes("rebuild_recreates_contract"));
|
||||
});
|
||||
|
||||
test("run roots are only canonical direct p4 integration children", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const base = canonicalIntegrationBase(repositoryRoot);
|
||||
const id = `p4-${"a".repeat(32)}`;
|
||||
assert.equal(validateRunRoot(repositoryRoot, join(base, id), id), join(base, id));
|
||||
for (const candidate of [base, join(repositoryRoot, ".artifacts", "p1-integration", id), join(base, id, "nested")]) {
|
||||
assert.throws(() => validateRunRoot(repositoryRoot, candidate, id));
|
||||
}
|
||||
assert.throws(() => validateRunRoot(repositoryRoot, join(base, `p4-${"A".repeat(32)}`), `p4-${"A".repeat(32)}`));
|
||||
});
|
||||
|
||||
test("createOwnedRun writes a canonical marker and cleanup refuses foreign roots", async () => {
|
||||
const repositoryRoot = await fakeRepository();
|
||||
const { runId, runRoot } = createOwnedRun(repositoryRoot);
|
||||
assert.match(runId, /^p4-[0-9a-f]{32}$/);
|
||||
const marker = JSON.parse(await readFile(join(runRoot, "run.json"), "utf8"));
|
||||
assert.equal(marker.runId, runId);
|
||||
assert.throws(() => cleanupOwnedRun(repositoryRoot, join(repositoryRoot, "tmp"), runId));
|
||||
cleanupOwnedRun(repositoryRoot, runRoot, runId);
|
||||
});
|
||||
Reference in New Issue
Block a user