FN-8497: reduce merge gate wall time

Keep merge-gate coverage focused while running its independent test lanes concurrently.

- Limit PostgreSQL gate coverage to lifecycle and transactional-handoff canaries.
- Run engine and PostgreSQL gate lanes concurrently while preserving failure propagation.
- Enforce canary coverage policy and refresh velocity documentation and history.

Files changed:
 docs/test-velocity-baseline.md                     |  16 +--
 docs/testing.md                                    |   5 +-
 package.json                                       |   2 +-
 packages/core/package.json                         |   2 +-
 .../__tests__/engine-vitest-gate-policy.test.mjs   |  79 +++++++++++++-
 scripts/test-velocity-history.json                 | 115 +++++++++++++++++++++
 6 files changed, 204 insertions(+), 15 deletions(-)

Fusion-Task-Id: FN-8497
Fusion-Task-Lineage: 8777959c-6d8c-4686-a975-d91af2c169ea
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-22 15:47:24 -07:00
parent 55c0b7532c
commit 9002fca9de
6 changed files with 204 additions and 15 deletions

View File

@@ -1,6 +1,6 @@
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { readdirSync, readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
@@ -58,10 +58,17 @@ test("engine-core remains an explicit allow-listed merge gate", () => {
const includeEntries = [...engineCoreBlock.matchAll(/"src\/__tests__\/[^"\n]+\.test\.ts"/g)].map((match) => match[0]);
assert.equal(new Set(includeEntries).size, includeEntries.length, "engine-core allow-list must not contain duplicates");
assert.ok(includeEntries.length >= 18, "engine-core allow-list must not be gutted to avoid the runtime abort");
/*
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.
*/
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-task-runner.test.ts"'),
"engine-core must keep workflow graph gate coverage",
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"'),
@@ -78,6 +85,70 @@ test("root and package gate scripts still propagate real Vitest failures", () =>
"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.doesNotMatch(root.scripts?.["test"] ?? "", /NODE_NO_WARNINGS/);
});
/*
FNXC:MergeGatePerformance 2026-07-22-15:35:
FN-8497 keeps only lifecycle and transactional-handoff PostgreSQL canaries in
`test:pg-gate`: 23 independent PG files each create/copy a real database, so
putting the whole integration inventory on every PR made the sequential merge
gate take 26–45 seconds. The other PG files must remain ordinary enabled core
tests; this structural guard prevents a future package-script/config change
from silently converting the speed fix into lost coverage.
*/
test("pg gate canaries remain a subset of the enabled non-blocking PG suite", () => {
const core = readJson("packages/core/package.json");
const coreConfig = read("packages/core/vitest.config.ts");
const pgDirectory = path.join(repoRoot, "packages/core/src/__tests__/postgres");
const discoveredPgFiles = new Set(
readdirSync(pgDirectory)
.filter((file) => file.endsWith(".pg.test.ts"))
.map((file) => `src/__tests__/postgres/${file}`),
);
const gateMembers = core.scripts?.["test:pg-gate"]?.match(/src\/__tests__\/postgres\/[^ ]+\.pg\.test\.ts/g) ?? [];
const expectedCanaries = [
"src/__tests__/postgres/handoff-to-review-atomicity.pg.test.ts",
"src/__tests__/postgres/task-lifecycle-e2e.pg.test.ts",
];
const formerGateMembers = [
...expectedCanaries,
"src/__tests__/postgres/store-list.pg.test.ts",
"src/__tests__/postgres/soft-delete-resurrection-FN-5233.pg.test.ts",
"src/__tests__/postgres/agent-logs-and-monitor.pg.test.ts",
"src/__tests__/postgres/todo-store.pg.test.ts",
"src/__tests__/postgres/workflow-definitions.pg.test.ts",
"src/__tests__/postgres/message-store.pg.test.ts",
"src/__tests__/postgres/insight-store.pg.test.ts",
"src/__tests__/postgres/insight-run-execution.pg.test.ts",
"src/__tests__/postgres/research-store.pg.test.ts",
"src/__tests__/postgres/mission-store.pg.test.ts",
"src/__tests__/postgres/goal-store.pg.test.ts",
"src/__tests__/postgres/artifacts-documents-evals.pg.test.ts",
"src/__tests__/postgres/command-center-analytics.pg.test.ts",
"src/__tests__/postgres/command-center-remaining-analytics.pg.test.ts",
"src/__tests__/postgres/research-execution.pg.test.ts",
"src/__tests__/postgres/async-store-events.pg.test.ts",
"src/__tests__/postgres/signal-ingestion.pg.test.ts",
"src/__tests__/postgres/mission-autopilot.pg.test.ts",
"src/__tests__/postgres/workflow-create.pg.test.ts",
"src/__tests__/postgres/monitor-trait-storm-guard.pg.test.ts",
"src/__tests__/postgres/agent-wake-getagent.pg.test.ts",
];
assert.deepEqual(gateMembers, expectedCanaries, "the PG gate must stay a narrow, explicit canary list");
assert.match(core.scripts?.test ?? "", /^vitest run\b/, "the non-blocking core lane must execute Vitest");
assert.doesNotMatch(core.scripts?.test ?? "", /\s(?:--exclude|--include)\b/, "the non-blocking core lane must not narrow discovery");
assert.match(coreConfig, /include:\s*\["src\/\*\*\/\*.test\.ts"\]/, "the default core config must discover PG tests");
assert.match(coreConfig, /const quarantinedCoreTests: string\[\] = \[\]/, "no PG test may be hidden by quarantine exclusion");
for (const file of formerGateMembers) {
assert.ok(discoveredPgFiles.has(file), `former PG gate member must remain discovered: ${file}`);
}
const removedFromGate = formerGateMembers.filter((file) => !gateMembers.includes(file));
assert.equal(removedFromGate.length, 21, "all non-canary former gate members must remain in the non-blocking lane");
assert.ok(removedFromGate.every((file) => discoveredPgFiles.has(file)), "removed PG members must remain discoverable");
});

View File

@@ -1930,6 +1930,121 @@
"Timing snapshot is 25 days old; verify slowest-file attribution before treating the table as the current culprit.",
"9 of the listed slowest files no longer exist at the recorded paths: `packages/dashboard/src/__tests__/insights-routes.test.ts`, `packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts`, `packages/dashboard/src/__tests__/workflow-routes.test.ts`, `packages/core/src/__tests__/db.test.ts`, `packages/core/src/__tests__/mission-store.test.ts`, …. Refresh scripts/test-timings.json before choosing a slow-test rewrite."
]
},
{
"capturedAt": "2026-07-22T22:40:53.357Z",
"gateMs": 9144,
"bootSmokeMs": 18454,
"testMs": 14943,
"quarantineCount": 2,
"slowestTop20": [
{
"file": "packages/dashboard/src/__tests__/insights-routes.test.ts",
"ms": 26500,
"package": "@fusion/dashboard"
},
{
"file": "packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts",
"ms": 24700,
"package": "@fusion/engine"
},
{
"file": "packages/dashboard/src/__tests__/workflow-routes.test.ts",
"ms": 22000,
"package": "@fusion/dashboard"
},
{
"file": "packages/core/src/__tests__/db.test.ts",
"ms": 21200,
"package": "@fusion/core"
},
{
"file": "packages/dashboard/app/components/__tests__/GitManagerModal.test.tsx",
"ms": 16900,
"package": "@fusion/dashboard"
},
{
"file": "packages/core/src/__tests__/mission-store.test.ts",
"ms": 16000,
"package": "@fusion/core"
},
{
"file": "packages/cli/src/__tests__/extension.test.ts",
"ms": 15700,
"package": "@runfusion/fusion"
},
{
"file": "packages/dashboard/app/components/__tests__/AgentPromptsManager.test.tsx",
"ms": 14800,
"package": "@fusion/dashboard"
},
{
"file": "packages/dashboard/app/components/__tests__/App.test.tsx",
"ms": 14600,
"package": "@fusion/dashboard"
},
{
"file": "packages/dashboard/app/components/__tests__/TaskDetailModal.inline-editing-and-integrations.test.tsx",
"ms": 14100,
"package": "@fusion/dashboard"
},
{
"file": "packages/dashboard/app/components/__tests__/TaskDetailModal.rendering.test.tsx",
"ms": 13700,
"package": "@fusion/dashboard"
},
{
"file": "packages/dashboard/src/__tests__/routes-auth.test.ts",
"ms": 13600,
"package": "@fusion/dashboard"
},
{
"file": "packages/core/src/__tests__/agent-store.test.ts",
"ms": 13400,
"package": "@fusion/core"
},
{
"file": "packages/engine/src/__tests__/workspace-merger-idempotency.test.ts",
"ms": 12700,
"package": "@fusion/engine"
},
{
"file": "packages/engine/src/__tests__/self-healing-workspace.test.ts",
"ms": 11800,
"package": "@fusion/engine"
},
{
"file": "packages/engine/src/__tests__/pr-response-run.test.ts",
"ms": 11600,
"package": "@fusion/engine"
},
{
"file": "packages/dashboard/app/components/__tests__/ListView.test.tsx",
"ms": 11300,
"package": "@fusion/dashboard"
},
{
"file": "plugins/fusion-plugin-compound-engineering/src/__tests__/sync.test.ts",
"ms": 11000,
"package": "@fusion-plugin-examples/compound-engineering"
},
{
"file": "packages/dashboard/app/components/__tests__/AgentDetailView.settings.test.tsx",
"ms": 10700,
"package": "@fusion/dashboard"
},
{
"file": "packages/dashboard/app/components/__tests__/SecretsView.test.tsx",
"ms": 10700,
"package": "@fusion/dashboard"
}
],
"measurementFailures": [],
"timingSnapshotCapturedAt": "2026-06-27T05:41:42.568Z",
"timingNotes": [
"Timing snapshot is 25 days old; verify slowest-file attribution before treating the table as the current culprit.",
"9 of the listed slowest files no longer exist at the recorded paths: `packages/dashboard/src/__tests__/insights-routes.test.ts`, `packages/engine/src/runtimes/__tests__/in-process-runtime.test.ts`, `packages/dashboard/src/__tests__/workflow-routes.test.ts`, `packages/core/src/__tests__/db.test.ts`, `packages/core/src/__tests__/mission-store.test.ts`, …. Refresh scripts/test-timings.json before choosing a slow-test rewrite."
]
}
]
}