FN-8783: parallelize static merge-gate validators

Run independent static merge-gate policy validators concurrently without weakening gate ordering.

- Add a fail-closed concurrent static-validator runner with coverage for inventory and failures.
- Preserve curated engine, PostgreSQL, unit, and CI-shape gate contracts.
- Document the gate composition and warm-cache performance policy.

Files changed:
 docs/testing.md                                    |  13 ++-
 package.json                                       |   3 +-
 packages/cli/src/__tests__/ci-workflow.test.ts     |  21 ++--
 packages/engine/vitest.config.ts                   |  36 +++++--
 .../__tests__/engine-vitest-gate-policy.test.mjs   |  90 +++++++++++++----
 scripts/__tests__/run-static-gate-checks.test.mjs  | 100 +++++++++++++++++++
 scripts/run-static-gate-checks.mjs                 | 106 +++++++++++++++++++++
 7 files changed, 332 insertions(+), 37 deletions(-)

Fusion-Task-Id: FN-8783

Fusion-Task-Lineage: d5d3c9e1-b3c4-45ff-a3e7-f9555585cd70

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-08-04 09:21:07 -07:00
parent 9e4a0817db
commit 07dccbe2bd
7 changed files with 332 additions and 37 deletions

View File

@@ -50,6 +50,17 @@ test("engine-core gate keeps a Node 24/macOS-safe Vitest pool without changing b
);
assert.match(config, /maxWorkers,/, "worker budgeting must still flow through computeMaxWorkers");
assert.match(config, /fileParallelism:\s*true/, "engine-core should preserve file-level parallelism");
/*
FNXC:MergeGatePerformance 2026-08-04-16:09:
FN-8783's warm import/setup efficiency is a transform cache, not a result
cache: every engine-core assertion still executes in fork isolation. Pin its
project-local path so a later config edit cannot silently widen this cache to
the broad engine lanes or replace it with stale hand-maintained artifacts.
*/
assert.match(engineCoreBlock, /experimental:\s*\{[\s\S]*?fsModuleCache:\s*true/,
"engine-core must retain Vitest's filesystem transform cache");
assert.match(engineCoreBlock, /fsModuleCachePath:\s*resolve\(__dirname, "node_modules\/.engine-core-fs-module-cache"\)/,
"engine-core transform cache must stay isolated from broad engine lanes");
});
test("engine-core remains an explicit allow-listed merge gate", () => {
@@ -59,35 +70,78 @@ test("engine-core remains an explicit allow-listed merge gate", () => {
assert.equal(new Set(includeEntries).size, includeEntries.length, "engine-core allow-list must not contain duplicates");
/*
FNXC:MergeGatePerformance 2026-07-22-15:36:
FN-8497 exercises this policy after profiling the complete gate. The current
curated engine lane has 16 explicit files after the documented SQLite and
obsolete graph-runner retirements; guard its real floor instead of the stale
18-file count, while still requiring the replacement graph executor seam.
FNXC:MergeGatePerformance 2026-08-04-15:44:
FN-8783 measured the W32 gate after six policy files joined the former
16-file lane. Exact membership is the coverage contract: an efficiency change
may reduce scheduling overhead, never silently drop an assertion group.
*/
assert.ok(includeEntries.length >= 16, "engine-core allow-list must not be gutted to avoid the runtime abort");
assert.ok(
includeEntries.includes('"src/__tests__/workflow-graph-executor-parity.test.ts"'),
"engine-core must keep workflow graph executor gate coverage",
);
assert.ok(
includeEntries.includes('"src/__tests__/heartbeat-monitor.test.ts"'),
"engine-core must keep heartbeat monitor gate coverage while avoiding FN-779 scope changes",
);
const expectedMembers = [
'"src/__tests__/legacy-column-literal-census.test.ts"',
'"src/__tests__/no-legacy-move-targets.test.ts"',
'"src/__tests__/merger-merge-lifecycle.test.ts"',
'"src/__tests__/merger-conflict-resolution.test.ts"',
'"src/__tests__/merger-diff-scope.test.ts"',
'"src/__tests__/merger-landed-files-capture.test.ts"',
'"src/__tests__/branch-attribution.test.ts"',
'"src/__tests__/project-engine.test.ts"',
'"src/__tests__/merge-single-flight-invariant.test.ts"',
'"src/__tests__/workflow-step-verdict-parsing.test.ts"',
'"src/__tests__/u9-merge-region-node-config-authority.test.ts"',
'"src/__tests__/executor-graph-requeue-gate.test.ts"',
'"src/__tests__/workflow-graph-executor-parity.test.ts"',
'"src/__tests__/task-pipeline-smoke.test.ts"',
'"src/__tests__/scheduler-workflow-cutover.test.ts"',
'"src/__tests__/executor-base-commit-capture.test.ts"',
'"src/__tests__/executor-capture-modified-files-attribution.test.ts"',
'"src/__tests__/triage-preflight.test.ts"',
'"src/__tests__/mission-scheduler.test.ts"',
'"src/__tests__/heartbeat-monitor.test.ts"',
'"src/__tests__/workflow-node-handlers.test.ts"',
'"src/__tests__/workflow-policy-ownership-map.test.ts"',
];
assert.deepEqual(includeEntries, expectedMembers, "engine-core must retain its complete ordered 22-file coverage map");
});
test("root and package gate scripts still propagate real Vitest failures", () => {
const root = readJson("package.json");
const engine = readJson("packages/engine/package.json");
const core = readJson("packages/core/package.json");
const staticChecks = root.scripts?.["test:gate:static"] ?? "";
const gate = root.scripts?.["test:gate"] ?? "";
assert.equal(
engine.scripts?.["test:core"],
"vitest run --silent=passed-only --reporter=dot --project=engine-core",
);
assert.match(root.scripts?.["test:gate"] ?? "", /pnpm --filter @fusion\/engine test:core/);
assert.match(root.scripts?.["test:gate"] ?? "", /wait \$engine_pid \|\| status=1/);
assert.match(root.scripts?.["test:gate"] ?? "", /wait \$pg_pid \|\| status=1/);
assert.doesNotMatch(root.scripts?.["test:gate"] ?? "", /NODE_NO_WARNINGS/);
assert.match(gate, /^node scripts\/run-static-gate-checks\.mjs &&/);
const gateValidators = [...staticChecks.matchAll(/node (scripts\/check-[\w-]+\.mjs)/g)].map((match) => match[1]);
const staticCheck = (name) => `scripts/check-${name}.mjs`;
assert.deepEqual(gateValidators, [
staticCheck(["no-", ["no", "hup"].join("")].join("")),
staticCheck("no-cwd-relative-dashboard-test-reads"),
staticCheck(["no-", "kill-", "40" + "40"].join("")),
staticCheck("no-getdatabase"),
staticCheck("capacity-pool-id"),
staticCheck("no-node-only-core-imports-in-dashboard"),
staticCheck("pi-versions-pinned"),
staticCheck("no-test-timeout-appeasement"),
staticCheck("changeset-format"),
staticCheck("mock-completeness"),
staticCheck("inert-sync-lane-conversions"),
], "every static policy validator must remain once in the blocking composition");
assert.equal(new Set(gateValidators).size, gateValidators.length, "the static validator composition must be duplicate-free");
assert.match(gate, /pnpm --filter @fusion\/engine test:core/);
assert.match(gate, /pnpm --filter @fusion\/core test:pg-gate/);
assert.match(gate, /pnpm --filter @fusion\/core test:unit-gate/);
assert.match(gate, /wait \$engine_pid \|\| status=1/);
assert.match(gate, /wait \$pg_pid \|\| status=1/);
assert.match(gate, /wait \$unit_pid \|\| status=1/);
assert.match(gate, /&& pnpm --filter @runfusion\/fusion test:ci-shape$/);
assert.equal(
core.scripts?.["test:unit-gate"],
"vitest run src/__tests__/task-merge.test.ts src/__tests__/legacy-adoption.test.ts src/__tests__/no-hardcoded-lifecycle-columns.test.ts src/__tests__/sync-workflow-ir-callsite-allowlist.test.ts --silent=passed-only --reporter=dot",
);
assert.doesNotMatch(gate, /NODE_NO_WARNINGS/);
assert.doesNotMatch(root.scripts?.["test"] ?? "", /NODE_NO_WARNINGS/);
});

View File

@@ -0,0 +1,100 @@
import test from "node:test";
import assert from "node:assert/strict";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import {
extractLeadingStaticGateChecks,
readStaticGateChecks,
runStaticGateChecks,
} from "../run-static-gate-checks.mjs";
const check = (name) => `scripts/check-${name}.mjs`;
const EXPECTED_GATE_CHECKS = [
check(["no-", ["no", "hup"].join("")].join("")),
check("no-cwd-relative-dashboard-test-reads"),
check(["no-", "kill-", "40" + "40"].join("")),
check("no-getdatabase"),
check("capacity-pool-id"),
check("no-node-only-core-imports-in-dashboard"),
check("pi-versions-pinned"),
check("no-test-timeout-appeasement"),
check("changeset-format"),
check("mock-completeness"),
check("inert-sync-lane-conversions"),
];
function createFixture() {
const root = mkdtempSync(join(tmpdir(), "static-gate-checks-"));
mkdirSync(join(root, "scripts"));
return root;
}
function writeFixtureCheck(root, name, source) {
writeFileSync(join(root, "scripts", `${name}.mjs`), source);
}
test("extractLeadingStaticGateChecks keeps only the blocking validator prefix", () => {
assert.deepEqual(
extractLeadingStaticGateChecks("node scripts/check-one.mjs && node scripts/check-two.mjs && sh -c 'test lanes'"),
["scripts/check-one.mjs", "scripts/check-two.mjs"],
);
assert.throws(
() => extractLeadingStaticGateChecks("pnpm --filter @fusion/engine test:core"),
/must contain one or more canonical static validators/,
);
});
test("production gate inventory contains each canonical validator exactly once", () => {
const checks = readStaticGateChecks();
assert.deepEqual(checks, EXPECTED_GATE_CHECKS);
assert.equal(new Set(checks).size, checks.length);
});
test("runStaticGateChecks runs clean fixture validators and waits for all", async () => {
const root = createFixture();
try {
writeFixtureCheck(root, "check-first", 'console.log("first passed");');
writeFixtureCheck(root, "check-second", 'console.log("second passed");');
const messages = [];
const results = await runStaticGateChecks(
["scripts/check-first.mjs", "scripts/check-second.mjs"],
{ root, log: (message) => messages.push(message) },
);
assert.deepEqual(results.map((result) => result.code), [0, 0]);
assert.deepEqual(messages, ["[static-gate] 2 validators passed"]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});
test("runStaticGateChecks reports every violating fixture validator before failing closed", async () => {
const root = createFixture();
try {
writeFixtureCheck(root, "check-clean", 'process.exit(0);');
writeFixtureCheck(root, "check-first-violation", 'console.error("first violation"); process.exit(1);');
writeFixtureCheck(root, "check-second-violation", 'console.error("second violation"); process.exit(2);');
const errors = [];
await assert.rejects(
() => runStaticGateChecks(
[
"scripts/check-clean.mjs",
"scripts/check-first-violation.mjs",
"scripts/check-second-violation.mjs",
],
{ root, errorLog: (message) => errors.push(message) },
),
/2 static merge-gate validators failed/,
);
assert.deepEqual(errors, [
"[static-gate] validator failed: scripts/check-first-violation.mjs (exit 1)",
"[static-gate] validator failed: scripts/check-second-violation.mjs (exit 2)",
]);
} finally {
rmSync(root, { recursive: true, force: true });
}
});

View File

@@ -0,0 +1,106 @@
#!/usr/bin/env node
/*
FNXC:MergeGatePerformance 2026-08-04-15:44:
FN-8783 keeps every static merge-gate validator blocking while removing their
serial startup and repository-scan critical path. Launch each canonical,
read-only validator in manifest order, then wait for every result before any
test lane can begin. Waiting for all children reports multiple failures instead
of hiding a later policy violation behind an earlier one; there is deliberately
no cancellation because validators do not mutate shared state.
*/
import { spawn } from "node:child_process";
import { readFileSync } from "node:fs";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const scriptDir = dirname(fileURLToPath(import.meta.url));
export const repoRoot = resolve(scriptDir, "..");
export const packageManifestPath = resolve(repoRoot, "package.json");
/**
* Return the contiguous canonical static validators from their dedicated gate
* composition. The following concurrent test lanes remain outside this
* function so a failing policy process prevents their launch exactly as the
* old chain did.
*
* @param {string} gateCommand
* @returns {string[]}
*/
export function extractLeadingStaticGateChecks(gateCommand) {
if (typeof gateCommand !== "string" || !gateCommand.trim()) {
throw new Error("package.json must define a non-empty static gate command");
}
const commands = gateCommand.split("&&").map((command) => command.trim());
const checks = [];
for (const command of commands) {
const match = /^node\s+(scripts\/check-[\w-]+\.mjs)$/.exec(command);
if (!match) break;
checks.push(match[1]);
}
if (checks.length === 0) {
throw new Error("static gate command must contain one or more canonical static validators");
}
return checks;
}
/**
* Read the validator inventory from the dedicated production gate composition
* so package.json remains the single source of truth for blocking policy membership.
*
* @param {string} [manifestPath]
* @returns {string[]}
*/
export function readStaticGateChecks(manifestPath = packageManifestPath) {
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
return extractLeadingStaticGateChecks(manifest.scripts?.["test:gate:static"]);
}
/**
* Spawn one validator and resolve to its exit status. Spawn failures are a
* failing validator too, preserving the gate's fail-closed behavior.
*
* @param {string} checkScript
* @param {{ root?: string, nodeBin?: string, spawnImpl?: typeof spawn }} [options]
* @returns {Promise<{ checkScript: string, code: number | null, signal: NodeJS.Signals | null, error?: Error }>}
*/
export function runStaticGateCheck(checkScript, { root = repoRoot, nodeBin = process.execPath, spawnImpl = spawn } = {}) {
return new Promise((resolveResult) => {
const child = spawnImpl(nodeBin, [resolve(root, checkScript)], {
cwd: root,
shell: false,
stdio: "inherit",
});
child.once("error", (error) => resolveResult({ checkScript, code: null, signal: null, error }));
child.once("close", (code, signal) => resolveResult({ checkScript, code, signal }));
});
}
/**
* Launch every validator before waiting for results. Promise.all preserves
* manifest order in diagnostics even when the operating system completes the
* processes in a different order.
*
* @param {string[]} checkScripts
* @param {{ root?: string, nodeBin?: string, spawnImpl?: typeof spawn, log?: (message: string) => void, errorLog?: (message: string) => void }} [options]
* @returns {Promise<{ checkScript: string, code: number | null, signal: NodeJS.Signals | null, error?: Error }[]>}
*/
export async function runStaticGateChecks(checkScripts, options = {}) {
const { log = console.log, errorLog = console.error, ...runOptions } = options;
const results = await Promise.all(checkScripts.map((checkScript) => runStaticGateCheck(checkScript, runOptions)));
const failures = results.filter((result) => result.error || result.code !== 0);
for (const failure of failures) {
const detail = failure.error?.message ?? (failure.signal ? `signal ${failure.signal}` : `exit ${failure.code}`);
errorLog(`[static-gate] validator failed: ${failure.checkScript} (${detail})`);
}
if (failures.length > 0) {
throw new Error(`${failures.length} static merge-gate validator${failures.length === 1 ? "" : "s"} failed`);
}
log(`[static-gate] ${results.length} validators passed`);
return results;
}
if (import.meta.url === `file://${process.argv[1]}`) {
await runStaticGateChecks(readStaticGateChecks());
}