singularity-forge/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts
ace-pm 172753c3b2 refactor(forge): complete gsd → forge rebrand across native, logging, and build system
- Rename native Rust crates: gsd-engine → forge-engine, gsd-ast → forge-ast, gsd-grep → forge-grep
- Update all crate dependencies (Cargo.toml, .rs source) and N-API artifacts
- Mass rename log prefix [gsd] → [forge] across 81 files (scripts, src/, extensions, tests)
- Rename log prefix "gsd-db:" → "forge-db:" in template literals
- Update nix flake: add sf-run-native devShell with Rust toolchain for native addon builds
- Update CI workflow artifact names (build-native.yml)
- Verify only packages/native/* touched (no upstream pi-* packages renamed)

Rationale: Complete gsd-2 → singularity-forge rebrand (2026-04-15). Native addon is
sf-run-specific; all gsd-prefixed logging and crate names must align with new identity.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:11:45 +02:00

167 lines
5.4 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import { join, dirname } from "node:path";
import { tmpdir } from "node:os";
import { fileURLToPath } from "node:url";
import {
closeDatabase,
insertMilestone,
insertSlice,
insertTask,
openDatabase,
} from "../gsd-db.ts";
import type { GSDState, Phase } from "../types.ts";
import { ensurePlanV2Graph } from "../uok/plan-v2.ts";
const __dirname = dirname(fileURLToPath(import.meta.url));
const gsdDir = join(__dirname, "..");
const MILESTONE_ID = "M001";
const SLICE_ID = "S01";
const TASK_ID = "T01";
const tempDirs = new Set<string>();
function createBasePath(): string {
const basePath = mkdtempSync(join(tmpdir(), "gsd-uok-planv2-"));
mkdirSync(join(basePath, ".gsd", "milestones", MILESTONE_ID), { recursive: true });
tempDirs.add(basePath);
return basePath;
}
function writeMilestoneFile(basePath: string, suffix: string, content: string): void {
const milestoneDir = join(basePath, ".gsd", "milestones", MILESTONE_ID);
mkdirSync(milestoneDir, { recursive: true });
writeFileSync(join(milestoneDir, `${MILESTONE_ID}-${suffix}.md`), `${content}\n`, "utf-8");
}
function writeSliceFile(basePath: string, suffix: string, content: string): void {
const sliceDir = join(basePath, ".gsd", "milestones", MILESTONE_ID, "slices", SLICE_ID);
mkdirSync(sliceDir, { recursive: true });
writeFileSync(join(sliceDir, `${SLICE_ID}-${suffix}.md`), `${content}\n`, "utf-8");
}
function seedGraphRows(): void {
insertMilestone({ id: MILESTONE_ID, title: "Milestone", status: "active" });
insertSlice({
id: SLICE_ID,
milestoneId: MILESTONE_ID,
title: "Slice",
status: "in_progress",
sequence: 1,
});
insertTask({
id: TASK_ID,
milestoneId: MILESTONE_ID,
sliceId: SLICE_ID,
title: "Task",
status: "pending",
keyFiles: ["src/task.ts"],
sequence: 1,
});
}
function buildState(phase: Phase): GSDState {
return {
phase,
activeMilestone: { id: MILESTONE_ID, title: "Milestone" },
activeSlice: null,
activeTask: null,
recentDecisions: [],
blockers: [],
nextAction: "dispatch",
registry: [],
};
}
test.beforeEach(() => {
closeDatabase();
const opened = openDatabase(":memory:");
assert.equal(opened, true);
});
test.afterEach(() => {
closeDatabase();
for (const path of tempDirs) {
rmSync(path, { recursive: true, force: true });
}
tempDirs.clear();
});
test("guided flow enforces planning-flow gate before execution-oriented dispatch", () => {
const source = readFileSync(join(gsdDir, "guided-flow.ts"), "utf-8");
assert.ok(
source.includes("needsPlanningFlowGate") &&
source.includes("ensurePlanningFlowGraph") &&
source.includes("Plan gate failed-closed"),
"guided flow should fail-closed when planning-flow graph compilation fails",
);
});
test("planning-flow gate fails closed for execution phase when finalized context is missing", () => {
const basePath = createBasePath();
seedGraphRows();
writeMilestoneFile(basePath, "CONTEXT-DRAFT", "Draft context only.");
const compiled = ensurePlanV2Graph(basePath, buildState("executing"));
assert.equal(compiled.ok, false);
assert.match(compiled.reason ?? "", /CONTEXT\.md/i);
});
test("planning-flow compiler writes pipeline metadata for clarify/research/draft stages", () => {
const basePath = createBasePath();
seedGraphRows();
writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
writeMilestoneFile(basePath, "CONTEXT-DRAFT", "Draft context retained.");
writeMilestoneFile(basePath, "RESEARCH", "Milestone research synthesis.");
writeSliceFile(basePath, "RESEARCH", "Slice research detail.");
const compiled = ensurePlanV2Graph(basePath, buildState("executing"));
assert.equal(compiled.ok, true);
assert.equal(compiled.clarifyRoundLimit, 3);
assert.equal(compiled.researchSynthesized, true);
assert.equal(compiled.draftContextIncluded, true);
assert.equal(compiled.finalizedContextIncluded, true);
const graphPath = compiled.graphPath ?? "";
const graphRaw = readFileSync(graphPath, "utf-8");
const graph = JSON.parse(graphRaw) as {
pipeline?: Record<string, unknown>;
nodes?: unknown[];
};
assert.equal(graph.pipeline?.["clarifyRoundLimit"], 3);
assert.equal(graph.pipeline?.["researchSynthesized"], true);
assert.equal(graph.pipeline?.["draftContextIncluded"], true);
assert.equal(graph.pipeline?.["finalizedContextIncluded"], true);
assert.equal(Array.isArray(graph.nodes), true);
});
test("plan-v2 graph may compile during planning even without finalized context", () => {
const basePath = createBasePath();
seedGraphRows();
writeMilestoneFile(basePath, "CONTEXT-DRAFT", "Planning draft context.");
const compiled = ensurePlanV2Graph(basePath, buildState("planning"));
assert.equal(compiled.ok, true);
});
test("plan-v2 ensure rejects empty executable graph", () => {
const basePath = createBasePath();
writeMilestoneFile(basePath, "CONTEXT", "Finalized context.");
insertMilestone({ id: MILESTONE_ID, title: "Milestone", status: "active" });
insertSlice({
id: SLICE_ID,
milestoneId: MILESTONE_ID,
title: "Slice",
status: "pending",
sequence: 1,
});
const compiled = ensurePlanV2Graph(basePath, buildState("executing"));
assert.equal(compiled.ok, false);
assert.match(compiled.reason ?? "", /compiled graph is empty/i);
});