test(FN-WF): treat a revoked merge gate as the deferral it is, and stop the engine before clearing globals

Two harness correctness fixes, both found chasing the S05 flake.

REVOKED GATE. `MergeGateRevokedError` escaped the graph dispatch and failed the
scenario. It is a DEFERRAL, not a failure — FN-180's own contract, and the reason
it carries a dedicated error type so callers cannot convert a gate lost mid-merge
into a retry or a failed park. It fires when a merge admitted on an earlier turn
reaches its ref-advance fence after a REVISE has already returned the card to
in-progress: the engine refusing correctly while the driver races it. The dispatch
now swallows ONLY that type, by NAME rather than by import, because importing
merger-errors.js would break the pre-FN-180 differential run the harness self-test
enforces.

TEARDOWN ORDER. `dispose` reset the shared mock registry and cleared
`activeSessionRegistry` BEFORE stopping the engine, stranding in-flight sessions on
default scripts mid-teardown and hiding them from the liveness checks the stop path
consults. Both are process-global, so the damage landed on whichever file ran next.

Together these took S05 on builtin:coding-ideas-v2 from failing 3 runs out of 3 to
2 out of 6 — a real reduction, and not zero, so it is still not shipped. S05 stays
on its original workflows, where the lane is green four consecutive full runs:
122.4s, 126.6s, 124.0s, 116.9s of the 150s budget.

builtin:coding-ideas-v2 remains at 18 of 19 scenarios plus the multi-repository
workspace drive.
This commit is contained in:
Fusion Agent
2026-08-25 05:38:46 +00:00
parent 19c3981a50
commit f22ebca62a

View File

@@ -354,11 +354,18 @@ export class PipelineSmokeHarness {
async dispose(): Promise<void> {
try {
/*
FNXC:PipelineSmoke 2026-08-24-23:10:
Stop the engine BEFORE clearing the shared registries. Resetting the mock registry while
sessions are still in flight strands them on default scripts mid-teardown, and clearing
`activeSessionRegistry` first hides those sessions from the liveness checks the stop path
consults. Both are process-global, so the damage lands on whichever file runs next.
*/
await this.engine.stop();
activeSessionRegistry.clear();
this.mockScriptStates.clear();
this.scriptedBranches.clear();
resetMockScripts();
await this.engine.stop();
this.guard.restore();
await this.centralCore.close();
// The shared PG harness owns taskStore.asyncLayer and closes it after this file.
@@ -1218,7 +1225,21 @@ export class PipelineSmokeHarness {
throw new Error("Pipeline smoke did not resolve the executor's authoritative workflow seams.");
}
this.authoritativeSeamsObserved = true;
await executor.executeAuthoritativeGraph(task);
/*
FNXC:PipelineSmoke 2026-08-24-23:10:
A revoked merge gate is a DEFERRAL, not a failure. That is FN-180's own contract and the reason
it carries a dedicated error type, so callers cannot convert a gate lost mid-merge into a retry
or a failed park. It surfaces here when a merge admitted on an earlier turn reaches its
ref-advance fence after a REVISE has already returned the card to in-progress: the engine is
refusing correctly, and the driver must simply take another turn. Swallowing ONLY this type
keeps every other merge failure fatal to the scenario.
*/
try {
await executor.executeAuthoritativeGraph(task);
} catch (error) {
/* Detected by NAME: importing merger-errors.js would break the pre-FN-180 differential run. */
if ((error as { name?: string } | undefined)?.name !== "MergeGateRevokedError") throw error;
}
const after = await this.freshTask(taskId);
if (after.mergeDetails?.mergeConfirmed === true) this.manualHoldTaskIds.delete(taskId);
return after;