import { test, expect } from "vitest";
import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { executeKernelCommand } from "../runtime.ts";
/*
RFC-0267: proves the executor's IO-adapter selection is load-bearing, not
just the adapters in isolation. A command that (mistakenly) declares
mutatesState: false but attempts a write via context.io is caught by the
executor's read-only adapter and fails loudly with KERNEL-META-01 — this is
the "test harness runs every mutatesState: false command under read-only
IO" mechanism the RFC requires, demonstrated end-to-end against a fixture
command standing in for "a previously mislabeled command found".
- Not a contract source of truth — the module under test owns the authoritative behavior; fixtures are minimal scaffolding only.
- Fixtures stay inline and minimal so each case states its own setup.
- RFC-1311: Compass header sweep — declares the workspace suite's contract for test-file scaffold coverage.
*/
// @ai-invariant fixtures must not couple to internals beyond the public surface under test
async function fixtureWorkspace(): Promise<{ root: string }> {
const root = await mkdtemp(join(tmpdir(), "workspace-io-executor-"));
const toolsRoot = join(root, "tools");
await mkdir(toolsRoot, { recursive: true });
await writeFile(join(root, "package.json"), "{}\n", "utf8");
await writeFile(join(root, "pnpm-workspace.yaml"), "packages:\n - apps/*\n", "utf8");
await writeFile(
join(toolsRoot, "kernel.config.mjs"),
`
export default {
modules: [{
name: "fixture",
version: "0.0.0",
declarations: [],
commands: [
{
name: "fixture.mislabeled.command",
modulePath: "test",
description: "declares mutatesState: false but actually writes — the bug this test finds",
scope: "workspace",
mutatesState: false,
async execute(_input, context) {
await context.io.writeFile("${join(root, "should-not-exist.txt").replace(/\\/g, "\\\\")}", "oops");
return { exitCode: 0 };
},
},
{
name: "fixture.honest.readonly.command",
modulePath: "test",
description: "correctly declares mutatesState: false and never writes",
scope: "workspace",
mutatesState: false,
async execute(_input, context) {
const exists = await context.io.exists("${join(root, "package.json").replace(/\\/g, "\\\\")}");
return { exitCode: 0, data: { exists } };
},
},
{
name: "fixture.writes.file.command",
modulePath: "test",
description: "mutating command that writes via context.io, for RFC-0326 filesModified tests",
scope: "workspace",
mutatesState: true,
async execute(_input, context) {
const docs = "${join(root, "docs").replace(/\\/g, "\\\\")}";
const target = "${join(root, "docs", "generated.txt").replace(/\\/g, "\\\\")}";
await context.io.writeFile(target, "generated content");
return { exitCode: 0, data: { wrote: target } };
},
},
{
name: "fixture.manual.rendered.command",
modulePath: "test",
description: "emits its own renderedFiles — manual emission must win over the auto-collected map",
scope: "workspace",
mutatesState: true,
async execute(_input, context) {
const target = "${join(root, "docs", "generated.txt").replace(/\\/g, "\\\\")}";
await context.io.writeFile(target, "auto content");
return {
exitCode: 0,
data: { renderedFiles: { "docs/generated.txt": "manual", "docs/virtual.txt": "never written" } },
};
},
},
{
name: "fixture.append.file.command",
modulePath: "test",
description: "writes then appends via context.io — rendered content must be the accumulated file",
scope: "workspace",
mutatesState: true,
async execute(_input, context) {
const target = "${join(root, "docs", "appended.txt").replace(/\\/g, "\\\\")}";
await context.io.writeFile(target, "a");
await context.io.appendFile(target, "b");
return { exitCode: 0 };
},
},
{
name: "fixture.exec-readonly.command",
modulePath: "test",
description: "RFC-1176: execOnReadOnly command — io.exec passes through, fs writes still throw",
scope: "workspace",
mutatesState: false,
execOnReadOnly: true,
async execute(_input, context) {
const r = await context.io.exec("echo", ["probe"]);
return { exitCode: 0, data: { out: r.stdout.trim(), readonly: context.io.readonly } };
},
},
{
name: "fixture.writebaseline.command",
modulePath: "test",
description: "RFC-1176: mutatingFlags command — writes only under --write-baseline",
scope: "workspace",
mutatesState: false,
mutatingFlags: ["write-baseline"],
flags: {
"write-baseline": { kind: "boolean", description: "Write the baseline file" },
},
async execute(input, context) {
if (input.flags["write-baseline"] === true) {
const target = "${join(root, "baseline.txt").replace(/\\/g, "\\\\")}";
await context.io.writeFile(target, "baseline");
return { exitCode: 0, data: { wrote: true, readonly: context.io.readonly } };
}
return { exitCode: 0, data: { wrote: false, readonly: context.io.readonly } };
},
},
{
name: "fixture.echo.flag",
modulePath: "test",
description: "RFC-1306: echoes input.flags — proves invoke flag serialization for all kinds",
scope: "workspace",
mutatesState: false,
flags: {
msg: { kind: "string", description: "Echo this value" },
flag: { kind: "boolean", description: "Boolean true round-trip" },
off: { kind: "boolean", description: "Boolean false round-trip" },
list: { kind: "string[]", description: "Array round-trip" },
skip: { kind: "string", description: "Must stay absent when undefined" },
},
async execute(input) {
return { exitCode: 0, data: { flags: input.flags } };
},
},
{
name: "fixture.invokes.child",
modulePath: "test",
description: "RFC-1306: calls context.invoke on a sibling — proves the executor-populated port",
scope: "workspace",
mutatesState: false,
async execute(_input, context) {
if (!context.invoke) return { exitCode: 1, data: { invokeMissing: true } };
const report = await context.invoke("fixture.echo.flag", {
msg: "a=b c",
flag: true,
off: false,
list: ["x", "y"],
skip: undefined,
});
return {
exitCode: 0,
data: { childOk: report.ok, childExit: report.exitCode, flags: report.data?.flags },
};
},
},
],
pipelines: [],
}],
};
`,
"utf8",
);
return { root };
}
test(
"executor: a mislabeled mutatesState: false command that writes via context.io fails with KERNEL-META-01",
{ timeout: 60_000 },
async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.mislabeled.command",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(false);
expect(single.exitCode).toBe(1);
expect(single.summary ?? "").toMatch(/KERNEL-META-01/);
expect(single.summary ?? "").toMatch(/fixture\.mislabeled\.command/);
} finally {
await rm(root, { recursive: true, force: true });
}
},
);
test("executor: an honestly-declared mutatesState: false command runs cleanly under the read-only adapter", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.honest.readonly.command",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
expect((single.data as { exists: boolean })?.exists).toBe(true);
} finally {
await rm(root, { recursive: true, force: true });
}
});
// RFC-0326: end-to-end tests that the executor surfaces filesModified on the
// execution report for both real and dry runs.
test("executor: real mutating command reports filesModified as workspace-root-relative paths", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.writes.file.command",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
expect(single.filesModified).toEqual(["docs/generated.txt"]);
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("executor: dry-run mutating command reports filesModified without touching disk", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.writes.file.command",
outputFormat: "json",
dryRun: true,
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
expect(single.filesModified).toEqual(["docs/generated.txt"]);
// File must NOT have been written to disk during dry-run.
let fileExists = false;
try {
await import("node:fs/promises").then((fs) => fs.access(join(root, "docs", "generated.txt")));
fileExists = true;
} catch {
fileExists = false;
}
expect(fileExists).toBe(false);
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("executor: mutatesState: false command reports empty filesModified", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.honest.readonly.command",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
expect(single.filesModified).toEqual([]);
} finally {
await rm(root, { recursive: true, force: true });
}
});
// RFC-1149 follow-up: the recording adapter captures full text content, and
// the executor surfaces it as data.renderedFiles under dryRun — this is what
// makes every io-migrated generator verifiable by generated.drift.validate
// (DRIFT-02) without per-command emission blocks.
test("executor: dry-run mutating command reports renderedFiles with full content", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.writes.file.command",
outputFormat: "json",
dryRun: true,
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
const rendered = (single.data as { renderedFiles?: Record })?.renderedFiles;
expect(rendered).toEqual({ "docs/generated.txt": "generated content" });
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("executor: command-emitted renderedFiles win over the auto-collected map", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.manual.rendered.command",
outputFormat: "json",
dryRun: true,
});
const single = Array.isArray(report) ? report[0]! : report;
const rendered = (single.data as { renderedFiles?: Record })?.renderedFiles;
// Manual emission overrides the same key and keeps its extra entries.
expect(rendered?.["docs/generated.txt"]).toBe("manual");
expect(rendered?.["docs/virtual.txt"]).toBe("never written");
} finally {
await rm(root, { recursive: true, force: true });
}
});
// RFC-1176: execOnReadOnly lets a declared mutatesState:false command shell
// out for read-only probes while fs mutations still throw KERNEL-META-01.
test("executor: execOnReadOnly command runs io.exec under the read-only adapter", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.exec-readonly.command",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
const data = single.data as { out: string; readonly: boolean };
expect(data.out).toBe("probe");
expect(data.readonly).toBe(true);
} finally {
await rm(root, { recursive: true, force: true });
}
});
// RFC-1176: mutatingFlags — the declaration stays read-only for scheduling;
// a declared flag in argv upgrades the invocation to the real adapter.
test("executor: mutatingFlags command stays read-only without the flag, writes with it", async () => {
const { root } = await fixtureWorkspace();
try {
const plain = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.writebaseline.command",
outputFormat: "json",
});
const single = Array.isArray(plain) ? plain[0]! : plain;
expect(single.ok).toBe(true);
expect((single.data as { readonly: boolean }).readonly).toBe(true);
const flagged = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.writebaseline.command",
outputFormat: "json",
argv: ["--write-baseline"],
});
const flaggedSingle = Array.isArray(flagged) ? flagged[0]! : flagged;
expect(flaggedSingle.ok).toBe(true);
expect((flaggedSingle.data as { wrote: boolean }).wrote).toBe(true);
await expect(
import("node:fs/promises").then((fs) => fs.readFile(join(root, "baseline.txt"), "utf8")),
).resolves.toBe("baseline");
} finally {
await rm(root, { recursive: true, force: true });
}
});
test("executor: dry-run renderedFiles carries accumulated append content", async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.append.file.command",
outputFormat: "json",
dryRun: true,
});
const single = Array.isArray(report) ? report[0]! : report;
const rendered = (single.data as { renderedFiles?: Record })?.renderedFiles;
expect(rendered?.["docs/appended.txt"]).toBe("ab");
} finally {
await rm(root, { recursive: true, force: true });
}
});
test(
"RFC-1306: context.invoke delegates to a sibling command — flags serialize to the child's resolved input",
{ timeout: 60_000 },
async () => {
const { root } = await fixtureWorkspace();
try {
const report = await executeKernelCommand({
workspaceRoot: root,
commandName: "fixture.invokes.child",
outputFormat: "json",
});
const single = Array.isArray(report) ? report[0]! : report;
expect(single.ok).toBe(true);
const data = single.data as {
childOk: boolean;
childExit: number;
flags: Record;
};
expect(data.childOk).toBe(true);
expect(data.childExit).toBe(0);
// Every flag kind round-trips through argv serialization: string with
// "=" and a space, boolean true/false, string[] accumulation, and
// undefined dropped entirely.
expect(data.flags.msg).toBe("a=b c");
expect(data.flags.flag).toBe(true);
expect(data.flags.off).toBe(false);
expect(data.flags.list).toEqual(["x", "y"]);
expect("skip" in data.flags).toBe(false);
} finally {
await rm(root, { recursive: true, force: true });
}
},
);