add persisted pi workflow extension

This commit is contained in:
jb
2026-08-25 16:07:44 -04:00
parent bfb07282d6
commit c5c3316e49
46 changed files with 20642 additions and 94 deletions
+178
View File
@@ -0,0 +1,178 @@
import assert from "node:assert/strict";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import * as path from "node:path";
import test from "node:test";
import { loadWorkflowConfig } from "./config.ts";
import { DEFAULT_WORKFLOW_CONFIG, MAX_CONCURRENCY } from "./domain.ts";
async function withTempDir(run: (directory: string) => Promise<void>) {
const directory = await mkdtemp(path.join(tmpdir(), "pi-workflow-config-"));
try {
await run(directory);
} finally {
await rm(directory, { recursive: true, force: true });
}
}
async function writeJson(file: string, value: unknown) {
await mkdir(path.dirname(file), { recursive: true });
await writeFile(file, `${JSON.stringify(value)}\n`);
}
test("configuration defaults to concurrency 8 with immutable ceiling 32", async () => {
await withTempDir(async (directory) => {
const config = await loadWorkflowConfig({
cwd: directory,
agentDir: path.join(directory, "agent"),
projectTrusted: false,
});
assert.deepEqual(config, DEFAULT_WORKFLOW_CONFIG);
assert.equal(config.concurrency.ceiling, MAX_CONCURRENCY);
});
});
test("a lowered ceiling clamps an inherited default", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { ceiling: 4 },
});
const globalOnly = await loadWorkflowConfig({
cwd: directory,
agentDir,
projectTrusted: false,
});
assert.deepEqual(globalOnly.concurrency, { default: 4, ceiling: 4 });
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { default: 6, ceiling: 16 },
});
await writeJson(path.join(directory, ".pi", "pi-workflow.json"), {
concurrency: { ceiling: 3 },
});
const project = await loadWorkflowConfig({
cwd: directory,
agentDir,
projectTrusted: true,
});
assert.deepEqual(project.concurrency, { default: 3, ceiling: 3 });
});
});
test("trusted project configuration layers over global configuration and may lower limits", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { default: 6, ceiling: 16 },
maxAssignmentsPerRun: 64,
analysisTools: ["read", "grep", "find"],
worktrees: { root: ".worktrees/global" },
});
await writeJson(path.join(directory, ".pi", "pi-workflow.json"), {
concurrency: { default: 4, ceiling: 8 },
maxReviewCycles: 5,
analysisTools: ["read", "find", "ls"],
worktrees: { root: ".worktrees/project" },
});
const config = await loadWorkflowConfig({
cwd: directory,
agentDir,
projectTrusted: true,
invocationConcurrency: 7,
});
assert.deepEqual(config.concurrency, { default: 7, ceiling: 8 });
assert.equal(config.maxAssignmentsPerRun, 64);
assert.equal(config.maxReviewCycles, 5);
assert.deepEqual(config.analysisTools, ["read", "find"]);
assert.equal(config.worktrees.root, path.normalize(".worktrees/project"));
});
});
test("untrusted project configuration is never applied", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(directory, ".pi", "pi-workflow.json"), {
concurrency: { default: 1, ceiling: 1 },
unknownField: true,
});
const config = await loadWorkflowConfig({
cwd: directory,
agentDir,
projectTrusted: false,
});
assert.deepEqual(config.concurrency, { default: 8, ceiling: 32 });
});
});
test("configuration cannot raise ceilings or activate unaudited tools", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { ceiling: 33 },
});
await assert.rejects(
loadWorkflowConfig({ cwd: directory, agentDir, projectTrusted: false }),
/between 1 and 32/,
);
await writeJson(path.join(agentDir, "pi-workflow.json"), {
analysisTools: ["read", "bash"],
});
await assert.rejects(
loadWorkflowConfig({ cwd: directory, agentDir, projectTrusted: false }),
/unsupported tools: bash/,
);
});
});
test("project configuration cannot raise a lowered global ceiling", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { default: 4, ceiling: 8 },
});
await writeJson(path.join(directory, ".pi", "pi-workflow.json"), {
concurrency: { ceiling: 16 },
});
await assert.rejects(
loadWorkflowConfig({ cwd: directory, agentDir, projectTrusted: true }),
/cannot raise the current ceiling/,
);
});
});
test("invalid numeric and path values fail before a run starts", async () => {
await withTempDir(async (directory) => {
const agentDir = path.join(directory, "agent");
await writeJson(path.join(agentDir, "pi-workflow.json"), {
maxAssignmentsPerRun: 1.5,
});
await assert.rejects(
loadWorkflowConfig({ cwd: directory, agentDir, projectTrusted: false }),
/must be an integer/,
);
await writeJson(path.join(agentDir, "pi-workflow.json"), {
worktrees: { root: "../outside" },
});
await assert.rejects(
loadWorkflowConfig({ cwd: directory, agentDir, projectTrusted: false }),
/must stay inside the repository/,
);
await writeJson(path.join(agentDir, "pi-workflow.json"), {
concurrency: { default: 8, ceiling: 8 },
});
await assert.rejects(
loadWorkflowConfig({
cwd: directory,
agentDir,
projectTrusted: false,
invocationConcurrency: 9,
}),
/between 1 and 8/,
);
});
});