Keep executor lifecycle transitions moving when run-audit sinks fail or stall. - Add a shared bounded, best-effort executor audit emission seam. - Route executor lifecycle audit writes through sink isolation. - Cover absent, throwing, rejecting, hanging, and late-settling sinks. - Document the emitter policy and publish a patch changeset. Files changed: .changeset/fn-9172-executor-run-audit-isolation.md | 7 + AGENTS.md | 7 + docs/run-audit.md | 6 +- .../src/__tests__/emit-bounded-run-audit.test.ts | 58 +++++ .../executor-run-audit-emitter-isolation.test.ts | 252 +++++++++++++++++++++ .../src/executor/acquire-session-registry-path.ts | 5 +- .../engine/src/executor/completion-finalization.ts | 3 +- .../engine/src/executor/create-task-done-tool.ts | 3 +- .../engine/src/executor/emit-bounded-run-audit.ts | 48 ++++ .../executor/emit-merge-boundary-unproven-audit.ts | 68 ++---- .../engine/src/executor/execute-workflow-graph.ts | 5 +- .../engine/src/executor/handle-graph-failure.ts | 18 +- ...dle-stale-in-review-parse-pause-abort-replay.ts | 7 +- ...ndle-stale-in-review-plan-pause-abort-replay.ts | 7 +- .../maybe-dispatch-workflow-work-engine.ts | 7 +- .../src/executor/no-merge-complete-column.ts | 3 +- .../reenter-paused-aborted-workflow-node.ts | 7 +- .../src/executor/required-artifact-recovery.ts | 3 +- packages/engine/src/executor/run-implementation.ts | 5 +- 19 files changed, 433 insertions(+), 86 deletions(-) Fusion-Task-Id: FN-9172 Fusion-Task-Lineage: 80c67c36-3bbb-4274-ac4c-9808c4cda3ce Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
60 lines
2.6 KiB
TypeScript
60 lines
2.6 KiB
TypeScript
/**
|
|
* FNXC:CodeOrganization 2026-08-03-17:30:
|
|
* acquireSessionRegistryPath peeled from TaskExecutor (U4).
|
|
*
|
|
* FNXC:SessionContention 2026-07-25-21:30 (contention prevention at the registration seam):
|
|
* Every executor session registration goes through acquireActiveSessionPath instead of the raw
|
|
* registerPath, so a LEAKED entry owned by a task with no live session surface in this process is
|
|
* RECLAIMED rather than throwing at the newcomer. That closes the second contention class (a dead
|
|
* holder can never release, so waiting on it is waiting forever). A genuinely live holder still throws
|
|
* the typed error — that case is real serialization, and callers classify it as a retryable contention
|
|
* hold (SESSION_CONTENTION_HOLD_VALUE), never as a provider/model failure.
|
|
* The probe reports LIVE on any uncertainty: an unknown holder with a fresh entry is treated as live by
|
|
* the staleness floor, so the reclaim only ever fires on proven-dead, aged entries.
|
|
*/
|
|
import type { TaskStore } from "@fusion/core";
|
|
import {
|
|
ActiveSessionPathHeldByForeignTaskError,
|
|
acquireActiveSessionPath,
|
|
activeSessionRegistry,
|
|
executingTaskLock,
|
|
type ActiveSessionKind,
|
|
} from "../agents/active-session-registry.js";
|
|
import { executorLog } from "../logger.js";
|
|
import { generateSyntheticRunId } from "../util/run-audit.js";
|
|
import { emitBoundedRunAudit } from "./emit-bounded-run-audit.js";
|
|
|
|
export type AcquireSessionRegistryPathDeps = {
|
|
store: TaskStore;
|
|
hasLiveTaskSessionSurface: (taskId: string) => boolean;
|
|
};
|
|
|
|
export function acquireSessionRegistryPath(
|
|
deps: AcquireSessionRegistryPathDeps,
|
|
taskId: string,
|
|
registryPath: string,
|
|
kind: ActiveSessionKind,
|
|
ownerKey: string,
|
|
): void {
|
|
const outcome = acquireActiveSessionPath(activeSessionRegistry, registryPath, { taskId, kind, ownerKey }, {
|
|
holderLiveProbe: (holderTaskId) => deps.hasLiveTaskSessionSurface(holderTaskId) || executingTaskLock.has(holderTaskId),
|
|
});
|
|
if (outcome.action === "contended") {
|
|
throw new ActiveSessionPathHeldByForeignTaskError(registryPath, outcome.holderTaskId, taskId);
|
|
}
|
|
if (outcome.action === "reclaimed-stale-foreign") {
|
|
executorLog.warn(
|
|
`${taskId}: reclaimed a stale active-session entry on ${registryPath} from dead task ${outcome.holderTaskId} (idle ${outcome.ageMs}ms)`,
|
|
);
|
|
void emitBoundedRunAudit(deps.store, {
|
|
taskId,
|
|
agentId: "executor",
|
|
runId: generateSyntheticRunId("session-path-reclaim", taskId),
|
|
domain: "database",
|
|
mutationType: "session:reclaim-stale-foreign-path",
|
|
target: taskId,
|
|
metadata: { taskId, holderTaskId: outcome.holderTaskId, kind, ageMs: outcome.ageMs },
|
|
});
|
|
}
|
|
}
|