Files
NODEDC_DESIGN_GUIDELINE/scripts/spark-governance-contract.test.mjs
T

138 lines
7.4 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
function lineNumber(lines, value) {
return lines.findIndex((line) => line.includes(value));
}
function assertLine(lines, value, message) {
const index = lineNumber(lines, value);
assert.ok(index >= 0, `${message} (expected near line 1+, missing: ${value})`);
return index + 1;
}
function extractBlock(lines, key) {
const start = lines.findIndex((line) => line.trim() === `${key} = """`);
assert.ok(start >= 0, `missing ${key} block start`);
let end = start + 1;
while (end < lines.length && lines[end] !== "\"\"\"") {
end += 1;
}
assert.ok(end < lines.length, `unterminated ${key} block`);
return lines.slice(start + 1, end).join("\n").replace(/\s+/g, " ").trim();
}
test("global spark agent configuration matches governance contract", async () => {
const configLines = (await readFile(new URL("../.codex/config.toml", import.meta.url), "utf8")).split(/\r?\n/);
const agentsLine = assertLine(configLines, "[agents]", "global config should declare [agents]");
const enabledLine = assertLine(configLines, 'enabled = true', "global config should enable agents");
const threadLine = assertLine(configLines, "max_concurrent_threads_per_session = 2", "global config should cap threads at 2");
const defaultModelLine = assertLine(configLines, 'default_subagent_model = "gpt-5.6-terra"', "global config should set default subagent model");
const effortLine = assertLine(configLines, 'default_subagent_reasoning_effort = "low"', "global config should set default low reasoning for unpinned subagents");
const interruptLine = assertLine(configLines, "interrupt_message = true", "global config should keep interruption messages enabled");
assert.equal(configLines[agentsLine - 1].trim(), "[agents]", "[agents] block should be present");
assert.equal(configLines[enabledLine - 1].trim(), 'enabled = true');
assert.equal(configLines[threadLine - 1].trim(), "max_concurrent_threads_per_session = 2");
assert.equal(configLines[defaultModelLine - 1].trim(), 'default_subagent_model = "gpt-5.6-terra"');
assert.equal(configLines[effortLine - 1].trim(), 'default_subagent_reasoning_effort = "low"');
assert.equal(configLines[interruptLine - 1].trim(), "interrupt_message = true");
});
test("spark_explorer contract", async () => {
const explorerLines = (await readFile(new URL("../.codex/agents/spark-explorer.toml", import.meta.url), "utf8")).split(/\r?\n/);
const instructions = extractBlock(explorerLines, "developer_instructions");
const nameLine = assertLine(explorerLines, 'name = "spark_explorer"', "explorer name should be spark_explorer");
const modelLine = assertLine(explorerLines, 'model = "gpt-5.3-codex-spark"', "explorer should pin model gpt-5.3-codex-spark");
const effortLine = assertLine(explorerLines, 'model_reasoning_effort = "medium"', "explorer should use medium reasoning");
const sandboxLine = assertLine(explorerLines, 'sandbox_mode = "read-only"', "explorer should use read-only sandbox mode");
assert.equal(explorerLines[nameLine - 1].trim(), 'name = "spark_explorer"');
assert.equal(explorerLines[modelLine - 1].trim(), 'model = "gpt-5.3-codex-spark"');
assert.equal(explorerLines[effortLine - 1].trim(), 'model_reasoning_effort = "medium"');
assert.equal(explorerLines[sandboxLine - 1].trim(), 'sandbox_mode = "read-only"');
assert.ok(
instructions.includes("Never edit, create, move, or delete files."),
"explorer instructions must prohibit edits",
);
assert.ok(
instructions.includes("Never spawn another agent."),
"explorer instructions must prohibit nested agents",
);
});
test("spark_worker contract", async () => {
const workerLines = (await readFile(new URL("../.codex/agents/spark-worker.toml", import.meta.url), "utf8")).split(/\r?\n/);
const instructions = extractBlock(workerLines, "developer_instructions");
const nameLine = assertLine(workerLines, 'name = "spark_worker"', "worker name should be spark_worker");
const modelLine = assertLine(workerLines, 'model = "gpt-5.3-codex-spark"', "worker should pin model gpt-5.3-codex-spark");
const effortLine = assertLine(workerLines, 'model_reasoning_effort = "medium"', "worker should use medium reasoning");
const sandboxLine = assertLine(workerLines, 'sandbox_mode = "workspace-write"', "worker should use workspace-write sandbox mode");
assert.equal(workerLines[nameLine - 1].trim(), 'name = "spark_worker"');
assert.equal(workerLines[modelLine - 1].trim(), 'model = "gpt-5.3-codex-spark"');
assert.equal(workerLines[effortLine - 1].trim(), 'model_reasoning_effort = "medium"');
assert.equal(workerLines[sandboxLine - 1].trim(), 'sandbox_mode = "workspace-write"');
assert.ok(
instructions.includes("exact allowed file"),
"worker instructions must require exact allowed path allowlist",
);
assert.ok(instructions.includes("Never commit, push, deploy, install dependencies, or change external systems."), "worker must not commit, push, deploy, or install");
assert.ok(instructions.includes("Never spawn another agent."), "worker must prohibit nested agents");
assert.ok(instructions.includes("One corrective retry is allowed after a failed check; then stop and return the failure evidence."), "worker must limit corrective retries");
});
test("AGENTS and governance contract documents", async () => {
const agentsLines = (await readFile(new URL("../AGENTS.md", import.meta.url), "utf8")).split(/\r?\n/);
const governanceLines = (await readFile(new URL("../docs/CODEX_SUBAGENT_GOVERNANCE.md", import.meta.url), "utf8")).split(/\r?\n/);
assert.ok(
agentsLines.some((line) => line.includes("`docs/CODEX_SUBAGENT_GOVERNANCE.md`")),
"AGENTS.md must point to governance document",
);
assert.ok(
agentsLines.some((line) => line.toLowerCase().includes("at most two subagents")),
"AGENTS.md should state max two subagents",
);
assert.ok(
agentsLines.some((line) => line.toLowerCase().includes("one write-capable")),
"AGENTS.md should state max one writer subagent",
);
assert.ok(
governanceLines.some((line) => line.includes("## Mandatory task packet")),
"governance should define mandatory task packet",
);
assert.ok(
governanceLines.some((line) => line.includes("1. Objective: one concrete outcome.")),
"governance should include mandatory objective field",
);
assert.ok(
governanceLines.some((line) => line.includes("2. Allowed scope: exact files, directories, or read-only tools.")),
"governance should include allowed scope field",
);
assert.ok(
governanceLines.some((line) => line.includes("3. Forbidden actions: especially external writes, commits, pushes, and deploys.")),
"governance should include forbidden actions field",
);
assert.ok(
governanceLines.some((line) => line.includes("4. Acceptance criteria: observable evidence of completion.")),
"governance should include acceptance criteria field",
);
assert.ok(
governanceLines.some((line) => line.includes("5. Verification: exact checks the worker may run.")),
"governance should include verification field",
);
assert.ok(
governanceLines.some((line) => line.includes("6. Output: a short structured summary, not raw logs.")),
"governance should include output field",
);
assert.ok(
governanceLines.some((line) => line.includes("Reviews the diff and performs final verification itself.")),
"governance should retain final primary review and verification ownership",
);
});