chore: initialize POM 2 repo (docs guards green; release gates recorded NOT RUN pending install)
This commit is contained in:
11
tests/command.test.ts
Normal file
11
tests/command.test.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { parseCommand } from "../src/parser";
|
||||
|
||||
test("command parser handles quotes, escapes, inline flags and terminator", () => {
|
||||
const parsed = parseCommand('new "The Clockwork Sea" --mode=strict-autopilot -- --literal \\ value');
|
||||
assert.equal(parsed.verb, "new");
|
||||
assert.deepEqual(parsed.args, ["The Clockwork Sea", "--literal", " value"]);
|
||||
assert.equal(parsed.flags.get("mode"), "strict-autopilot");
|
||||
assert.throws(() => parseCommand('new "unfinished'), /Unterminated quote/);
|
||||
});
|
||||
15
tests/events.test.ts
Normal file
15
tests/events.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { shouldBlockProjectCommand } from "../src/policy";
|
||||
|
||||
test("destructive-command policy blocks recursive deletion from or against project root", () => {
|
||||
const root = "/work/clockwork-sea";
|
||||
assert.equal(shouldBlockProjectCommand("rm -rf .", root, "clockwork-sea", root), true);
|
||||
assert.equal(shouldBlockProjectCommand("rm --recursive --force \"$PWD\"", root, "clockwork-sea", root), true);
|
||||
assert.equal(shouldBlockProjectCommand("find . -delete", root, "clockwork-sea", root), true);
|
||||
assert.equal(shouldBlockProjectCommand("git reset --hard HEAD~1", root, "clockwork-sea", root), true);
|
||||
assert.equal(shouldBlockProjectCommand("Remove-Item -Recurse -Force .", root, "clockwork-sea", root), true);
|
||||
assert.equal(shouldBlockProjectCommand("rm -rf /tmp/scratch", root, "clockwork-sea", "/tmp"), false);
|
||||
assert.equal(shouldBlockProjectCommand("rm -rf /work/clockwork-sea", root, "clockwork-sea", "/tmp"), true);
|
||||
assert.equal(shouldBlockProjectCommand("rm notes.txt", root, "clockwork-sea", root), false);
|
||||
});
|
||||
24
tests/paths.test.ts
Normal file
24
tests/paths.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, mkdir, rm, symlink, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { secureExistingPath, secureFuturePath } from "../src/paths";
|
||||
|
||||
test("canonical path guard rejects traversal and symlink escapes", async () => {
|
||||
const parent = await mkdtemp(join(tmpdir(), "pom-paths-"));
|
||||
const root = join(parent, "project");
|
||||
const outside = join(parent, "outside.txt");
|
||||
await mkdir(root);
|
||||
await writeFile(join(root, "inside.txt"), "safe");
|
||||
await writeFile(outside, "secret");
|
||||
await symlink(outside, join(root, "escape.txt"));
|
||||
try {
|
||||
assert.equal(await secureExistingPath(root, "inside.txt"), join(root, "inside.txt"));
|
||||
await assert.rejects(secureExistingPath(root, "../outside.txt"), /escapes project root/);
|
||||
await assert.rejects(secureExistingPath(root, "escape.txt"), /Symbolic links/);
|
||||
await assert.rejects(secureFuturePath(root, "../future.txt"), /escapes project root/);
|
||||
} finally {
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
30
tests/state.test.ts
Normal file
30
tests/state.test.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { bumpPatchVersion, createState, gotoStage, passStage, recordEvidence, startStage } from "../src/state";
|
||||
import { stage } from "../src/domain";
|
||||
|
||||
test("state machine rejects skipped prerequisites and evidence-free completion", async () => {
|
||||
assert.equal(bumpPatchVersion("1.2.9"), "1.2.10");
|
||||
assert.throws(() => bumpPatchVersion("latest"), /Invalid semantic version/);
|
||||
const root = await mkdtemp(join(tmpdir(), "pom-state-"));
|
||||
try {
|
||||
let state = createState("State Test", root);
|
||||
assert.throws(() => gotoStage(state, 1), /incomplete prerequisites/);
|
||||
state = startStage(state, 0);
|
||||
assert.equal(state.stageRuns["0"].status, "active");
|
||||
assert.throws(() => passStage(state, "report.json"), /lacks evidence/);
|
||||
|
||||
for (const criterion of stage(0).acceptance) {
|
||||
state = recordEvidence(state, criterion.id, `Verified ${criterion.label}`, ["01_planning/00_project_brief.md"], "Test");
|
||||
}
|
||||
state = passStage(state, "00_admin/validation/stage-0.json");
|
||||
assert.equal(state.stageRuns["0"].status, "complete");
|
||||
assert.equal(state.currentStage, 1);
|
||||
assert.deepEqual(state.completedStages, [0]);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
24
tests/studio.test.ts
Normal file
24
tests/studio.test.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { DEFAULT_CONFIG } from "../src/domain";
|
||||
import { createState } from "../src/state";
|
||||
import { buildHiveMission, missionToTaskBatch } from "../src/studio";
|
||||
|
||||
test("Hive mission is bounded, ownership-aware, and translated to one native task batch", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "pom-hive-"));
|
||||
try {
|
||||
const config = { ...DEFAULT_CONFIG, maxStudioAgents: 2 };
|
||||
const state = createState("Hive Test", root, config);
|
||||
const mission = buildHiveMission(state, config, "stage");
|
||||
assert.ok(mission.tasks.length > 0 && mission.tasks.length <= 2);
|
||||
const batch = missionToTaskBatch(state, mission);
|
||||
assert.equal(batch.tasks.length, mission.tasks.length);
|
||||
assert.match(batch.context, /native IRC/);
|
||||
assert.ok(batch.tasks.every((item) => item.task.includes("pom_hive op=signal")));
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
28
tests/swarm.test.ts
Normal file
28
tests/swarm.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { DEFAULT_CONFIG } from "../src/domain";
|
||||
import { bootstrapProject } from "../src/persistence";
|
||||
import { createState } from "../src/state";
|
||||
import { buildHiveMission } from "../src/studio";
|
||||
import { writeSwarmDefinition } from "../src/swarm";
|
||||
|
||||
test("optional OMP swarm definition creates a fan-out/fan-in DAG", async () => {
|
||||
const parent = await mkdtemp(join(tmpdir(), "pom-swarm-"));
|
||||
try {
|
||||
const state = createState("Swarm Test", parent, DEFAULT_CONFIG, true);
|
||||
await bootstrapProject(state);
|
||||
const mission = buildHiveMission(state, DEFAULT_CONFIG, "stage");
|
||||
const relative = await writeSwarmDefinition(state, mission, "parallel");
|
||||
const yaml = await readFile(join(state.projectRoot, relative), "utf8");
|
||||
assert.match(yaml, /^swarm:/);
|
||||
assert.match(yaml, /mode: parallel/);
|
||||
assert.match(yaml, /reports_to:\n\s+- integrator/);
|
||||
assert.match(yaml, /integrator:/);
|
||||
assert.match(yaml, /waits_for:/);
|
||||
} finally {
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
35
tests/validators.test.ts
Normal file
35
tests/validators.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { DEFAULT_CONFIG, stage } from "../src/domain";
|
||||
import { bootstrapProject } from "../src/persistence";
|
||||
import { createState, recordEvidence, startStage } from "../src/state";
|
||||
import { validatePom } from "../src/validators";
|
||||
|
||||
test("stage gate rejects starter shell then accepts substantive evidence-backed artifact", async () => {
|
||||
const parent = await mkdtemp(join(tmpdir(), "pom-validation-"));
|
||||
try {
|
||||
const config = { ...DEFAULT_CONFIG, projectRootMode: "child" as const };
|
||||
let state = createState("Validation Test", parent, config, true);
|
||||
await bootstrapProject(state);
|
||||
state = startStage(state, 0);
|
||||
const initial = await validatePom(state, "stage", { writeReport: false });
|
||||
assert.equal(initial.passed, false);
|
||||
assert.ok(initial.checks.some((item) => item.id.includes("bytes") && item.status === "fail"));
|
||||
|
||||
const sections = ["Vision", "Audience", "Constraints", "Success Criteria", "Open Questions"];
|
||||
const body = sections.map((heading) => `## ${heading}\n\n${("Specific, measurable, original production detail. ").repeat(18)}`).join("\n\n");
|
||||
const briefPath = join(state.projectRoot, "01_planning", "00_project_brief.md");
|
||||
const original = await readFile(briefPath, "utf8");
|
||||
await writeFile(briefPath, `${original.split("## Vision")[0]}${body}\n`);
|
||||
for (const criterion of stage(0).acceptance) {
|
||||
state = recordEvidence(state, criterion.id, `Verified ${criterion.description}`, ["01_planning/00_project_brief.md"], "Test");
|
||||
}
|
||||
const final = await validatePom(state, "stage", { writeReport: false });
|
||||
assert.equal(final.passed, true, final.checks.filter((item) => item.status === "fail").map((item) => item.message).join("\n"));
|
||||
} finally {
|
||||
await rm(parent, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
39
tests/zip.test.ts
Normal file
39
tests/zip.test.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
import { mkdtemp, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createZip, verifyZip } from "../src/zip";
|
||||
|
||||
test("ZIP creation verifies central directory, decompression, size, CRC and hash", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "pom-zip-"));
|
||||
try {
|
||||
const source = join(root, "artifact.md");
|
||||
const archive = join(root, "delivery.zip");
|
||||
await writeFile(source, "# Verified artifact\n\nCanonical content.\n");
|
||||
await createZip(archive, [{ absolutePath: source, archivePath: "docs/artifact.md" }]);
|
||||
const result = await verifyZip(archive);
|
||||
assert.equal(result.members.length, 1);
|
||||
assert.equal(result.members[0].path, "docs/artifact.md");
|
||||
assert.match(result.members[0].crc32, /^[0-9a-f]{8}$/);
|
||||
assert.match(result.sha256, /^[0-9a-f]{64}$/);
|
||||
await assert.rejects(createZip(join(root, "bad.zip"), [
|
||||
{ absolutePath: source, archivePath: "same.md" },
|
||||
{ absolutePath: source, archivePath: "same.md" },
|
||||
]), /Duplicate archive member/);
|
||||
await assert.rejects(createZip(join(root, "escape.zip"), [{ absolutePath: source, archivePath: "../escape.md" }]), /Unsafe archive member/);
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("malformed ZIP is rejected", async () => {
|
||||
const root = await mkdtemp(join(tmpdir(), "pom-bad-zip-"));
|
||||
try {
|
||||
const bad = join(root, "bad.zip");
|
||||
await writeFile(bad, Buffer.from("PK\\x03\\x04not-a-real-archive"));
|
||||
await assert.rejects(verifyZip(bad));
|
||||
} finally {
|
||||
await rm(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user