FN-8714: fix quality runner CLI argument passthrough

Allow dashboard aggregate quality commands through the pnpm script boundary.

- Normalize pnpm’s leading argument separator before validating runner options
- Add end-to-end aggregate and fail-fast quality-runner coverage with a deterministic fixture
- Document aggregate quality-runner invocation and failure reporting

Files changed:
 docs/testing.md                                    | 15 ++++
 packages/dashboard/scripts/__tests__/quality-runner-fixture.mjs | 18 +++++
 packages/dashboard/scripts/__tests__/run-quality-tests.test.ts | 88 ++++++++++++++++++++++
 packages/dashboard/scripts/run-quality-tests.mjs   | 23 ++++--
 4 files changed, 139 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-8714

Fusion-Task-Lineage: 48dbd434-a9e6-4283-8658-cbaa254a7dc7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-08-01 12:13:52 -07:00
parent e3fbd2ebf5
commit 0512334e86
4 changed files with 139 additions and 5 deletions

View File

@@ -209,6 +209,21 @@ was SIGKILLed by heap pressure under workspace worker budgeting. The top-level
`pretest` artifact bootstrap runs once before the orchestrator; lane subprocesses must
not re-run `scripts/ensure-test-artifacts.mjs`.
Use the exact aggregate command below to attempt all 15 app/API lanes, including after
one or more lanes fail:
```bash
pnpm --filter @fusion/dashboard test -- --all
```
pnpm forwards that invocation as `-- --all`; the quality runner deliberately removes
only the leading package-script separator and still rejects genuine unknown options.
`--no-fail-fast` is an equivalent aggregate alias. Without either alias, the default is
fail-fast for quick local feedback: remaining lanes are reported as **NOT RUN** with
**UNKNOWN** status, never as passing. Aggregate mode attempts every lane exactly once;
any failed or signal-terminated lane keeps the command nonzero and is named in the
final failure summary. It must never print an all-passed summary when any lane failed.
<!-- FNXC:TestInfrastructure 2026-06-21-12:21: FN-6854 applies the dashboard heap-runner pattern to the engine affected-package lane because a wide `vitest --changed` fan-out selected hundreds of real-git-heavy engine files and could be OS-SIGKILLed by heap pressure before Vitest returned a verdict. Keep the engine lane isolated, heap-capped, and lower-worker rather than raising concurrency or widening timeouts.
FNXC:TestInfrastructure 2026-06-21-16:28: FN-6877 applies the same changed-mode envelope to the dashboard scoped affected lane because FN-6874 showed App/jsdom changed runs could be OS-OOM-killed even with inbound test concurrency already set to 1. Keep the per-lane watchdog finite and outside the env; the envelope is a heap-pressure guard, not a hang-budget increase.

View File

@@ -0,0 +1,18 @@
#!/usr/bin/env node
import { appendFileSync } from "node:fs";
import process from "node:process";
const projectIndex = process.argv.indexOf("--project");
const project = process.argv[projectIndex + 1] ?? "unknown-project";
if (process.env.FUSION_DASHBOARD_QUALITY_LANE_LOG) {
appendFileSync(process.env.FUSION_DASHBOARD_QUALITY_LANE_LOG, `${project}\n`);
}
if (process.env.FUSION_DASHBOARD_QUALITY_SIGNAL_PROJECT === project) {
process.kill(process.pid, "SIGTERM");
}
const failedProjects = new Set((process.env.FUSION_DASHBOARD_QUALITY_FAIL_PROJECTS ?? "").split(",").filter(Boolean));
process.exitCode = failedProjects.has(project) ? 1 : 0;

View File

@@ -1,7 +1,16 @@
// @vitest-environment node
import { spawnSync } from "node:child_process";
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
const __dirname = dirname(fileURLToPath(import.meta.url));
const workspaceRoot = resolve(__dirname, "../../../..");
const fixtureRunnerPath = join(__dirname, "quality-runner-fixture.mjs");
interface QualityLane {
name: string;
group: "app" | "api";
@@ -17,6 +26,7 @@ interface LaneResult {
interface RunQualityTestsModule {
qualityLanes: QualityLane[];
parseArgs(argv: string[]): { group: "all" | "app" | "api"; list: boolean; allLanes: boolean };
resolveConcurrency(env?: Record<string, string | undefined>): number;
runQualityTests(options?: {
failFast?: boolean;
@@ -133,6 +143,84 @@ describe("dashboard quality orchestrator", () => {
expect(result).toMatchObject({ ok: false, skipped: 2 });
});
it("parses direct and pnpm-forwarded aggregate aliases while rejecting genuine unknown arguments", async () => {
const { parseArgs } = await loadModule();
expect(parseArgs([])).toEqual({ group: "all", list: false, allLanes: false });
expect(parseArgs(["--all"])).toEqual({ group: "all", list: false, allLanes: true });
expect(parseArgs(["--no-fail-fast"])).toEqual({ group: "all", list: false, allLanes: true });
expect(parseArgs(["--", "--all"])).toEqual({ group: "all", list: false, allLanes: true });
expect(parseArgs(["--", "--no-fail-fast"])).toEqual({ group: "all", list: false, allLanes: true });
expect(parseArgs(["--group", "app", "--list"])).toEqual({ group: "app", list: true, allLanes: false });
expect(() => parseArgs(["--", "--not-a-quality-option"])).toThrow("Unknown argument: --not-a-quality-option");
});
it.each(["--all", "--no-fail-fast"])("runs package aggregate alias %s through all 15 lanes without hiding failures", async (aggregateAlias) => {
const { qualityLanes } = await loadModule();
const temporaryDirectory = mkdtempSync(join(tmpdir(), "fn-8714-quality-runner-"));
const laneLogPath = join(temporaryDirectory, "lanes.log");
try {
const result = spawnSync("pnpm", ["--filter", "@fusion/dashboard", "test", "--", aggregateAlias], {
cwd: workspaceRoot,
encoding: "utf8",
env: {
...process.env,
npm_config_ignore_scripts: "true",
FUSION_DASHBOARD_TEST_CONCURRENCY: "1",
FUSION_DASHBOARD_QUALITY_TEST_MODE: "1",
FUSION_DASHBOARD_QUALITY_RUNNER: fixtureRunnerPath,
FUSION_DASHBOARD_QUALITY_LANE_LOG: laneLogPath,
FUSION_DASHBOARD_QUALITY_FAIL_PROJECTS: "dashboard-app-quality-foundation-api,dashboard-api-quality",
},
});
const output = `${result.stdout}\n${result.stderr}`;
const startedLanes = [...output.matchAll(/^\[dashboard-quality\] start ([^:]+:[^:]+):/gm)].map((match) => match[1]);
const launchedProjects = existsSync(laneLogPath) ? readFileSync(laneLogPath, "utf8").trim().split("\n") : [];
expect(result.status, output).toBe(1);
expect(output).not.toContain("Unknown argument: --");
expect(startedLanes).toEqual(qualityLanes.map((qualityLane) => qualityLane.name));
expect(new Set(startedLanes).size).toBe(15);
expect(launchedProjects).toHaveLength(15);
expect(output).toContain("failed lane(s): app:foundation-api, api:curated");
expect(output).not.toContain("NOT RUN");
expect(output).not.toContain("all 15 lane(s) passed");
} finally {
rmSync(temporaryDirectory, { recursive: true, force: true });
}
});
it("keeps the no-argument package command fail-fast and labels unrun lanes unknown", async () => {
const temporaryDirectory = mkdtempSync(join(tmpdir(), "fn-8714-default-quality-runner-"));
const laneLogPath = join(temporaryDirectory, "lanes.log");
try {
const result = spawnSync("pnpm", ["--filter", "@fusion/dashboard", "test"], {
cwd: workspaceRoot,
encoding: "utf8",
env: {
...process.env,
npm_config_ignore_scripts: "true",
FUSION_DASHBOARD_TEST_CONCURRENCY: "1",
FUSION_DASHBOARD_QUALITY_TEST_MODE: "1",
FUSION_DASHBOARD_QUALITY_RUNNER: fixtureRunnerPath,
FUSION_DASHBOARD_QUALITY_LANE_LOG: laneLogPath,
FUSION_DASHBOARD_QUALITY_FAIL_PROJECTS: "dashboard-app-quality-foundation-api",
},
});
const output = `${result.stdout}\n${result.stderr}`;
const launchedProjects = readFileSync(laneLogPath, "utf8").trim().split("\n");
expect(result.status, output).toBe(1);
expect(launchedProjects).toEqual(["dashboard-app-quality-foundation-api"]);
expect(output).toContain("14 lane(s) were NOT RUN after the first failure — their status is UNKNOWN, not passing.");
expect(output).not.toContain("all 1 lane(s) passed");
} finally {
rmSync(temporaryDirectory, { recursive: true, force: true });
}
});
it("failFast:false runs EVERY lane and reports every failure, not just the first", async () => {
const { runQualityTests } = await loadModule();
const launched: string[] = [];

View File

@@ -8,6 +8,8 @@ const HEAP_MB = 6144;
const DEFAULT_CONCURRENCY = 2;
const MAX_CONCURRENCY = 2;
const VITEST_WRAPPER = "scripts/run-vitest-with-heap.mjs";
const TEST_RUNNER_ENV = "FUSION_DASHBOARD_QUALITY_RUNNER";
const TEST_MODE_ENV = "FUSION_DASHBOARD_QUALITY_TEST_MODE";
const EXCLUDE_BUILD_OUTPUT = ["--exclude", "**/build-output.test.ts"];
export const qualityLanes = [
@@ -79,15 +81,23 @@ export function resolveConcurrency(env = process.env) {
return Math.min(requested, MAX_CONCURRENCY);
}
function parseArgs(argv) {
/*
FNXC:DashboardQualityLanes 2026-08-01-17:15:
FN-8714 follows FN-8699/#2784: pnpm forwards package-script arguments as a leading standalone
`--`, so remove only that boundary marker before validating the runner's own options. Remaining
arguments still fail closed; aggregate mode must not turn unknown options or failed lanes into a
false passing quality report.
*/
export function parseArgs(argv) {
const normalizedArgv = argv[0] === "--" ? argv.slice(1) : argv;
let group = "all";
let list = false;
let allLanes = false;
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index];
for (let index = 0; index < normalizedArgv.length; index += 1) {
const arg = normalizedArgv[index];
if (arg === "--group") {
group = argv[index + 1] ?? "";
group = normalizedArgv[index + 1] ?? "";
index += 1;
continue;
}
@@ -124,9 +134,12 @@ function formatLaneCommand(lane) {
function runLane(lane) {
return new Promise((resolve) => {
const startedAt = Date.now();
// FNXC:DashboardQualityLanes 2026-08-01-17:15: The child-process regression runs the exact pnpm
// command with a deterministic lane fixture; production always retains the heap wrapper.
const runnerPath = process.env[TEST_MODE_ENV] === "1" ? process.env[TEST_RUNNER_ENV] || VITEST_WRAPPER : VITEST_WRAPPER;
console.log(`[dashboard-quality] start ${lane.name}: ${formatLaneCommand(lane)}`);
// process-supervisor-allowlist: foreground test orchestrator runs bounded child processes and waits for each to finish
const child = spawn(process.execPath, [VITEST_WRAPPER, ...lane.args], {
const child = spawn(process.execPath, [runnerPath, ...lane.args], {
cwd: new URL("..", import.meta.url),
stdio: "inherit",
env: process.env,