From 5134a55b77f5f4f314a897d52a3b6b8e2145db06 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 11 Aug 2026 15:20:19 -0700 Subject: [PATCH] FN-8958: fence orphaned merge-body writes Prevent cancelled merge generations from writing stale task state. - Add a signal-aware merge write fence with orphan audit reporting. - Fence merge finalization, post-push metadata, and recovery-branch task logs. - Cover durable write callsites and cancellation behavior with tests and guidance. Files changed: .changeset/fn-8958-orphan-merge-write-fence.md | 7 + AGENTS.md | 1 + .../reliability/orphan-merge-body-write-fence.md | 68 ++ .../__tests__/_merge-durable-write-callsites.ts | 4 + .../merge-orphan-durable-write-inventory.json | 982 +++++++++++---------- .../merge-orphan-body-durable-writes.test.ts | 38 +- .../engine/src/__tests__/merge-write-fence.test.ts | 39 + .../engine/src/merge/auto-merge-finalization.ts | 9 + packages/engine/src/merge/merge-write-fence.ts | 92 ++ packages/engine/src/merge/merger-ai.ts | 173 ++-- 10 files changed, 875 insertions(+), 538 deletions(-) Fusion-Task-Id: FN-8958 Fusion-Task-Lineage: 5f398c44-4320-4f0c-be15-707184f66aa8 Co-authored-by: Fusion (runfusion.ai) --- .../fn-8958-orphan-merge-write-fence.md | 7 + AGENTS.md | 1 + .../orphan-merge-body-write-fence.md | 68 ++ .../_merge-durable-write-callsites.ts | 4 + .../merge-orphan-durable-write-inventory.json | 982 +++++++++--------- .../merge-orphan-body-durable-writes.test.ts | 38 +- .../src/__tests__/merge-write-fence.test.ts | 39 + .../src/merge/auto-merge-finalization.ts | 9 + .../engine/src/merge/merge-write-fence.ts | 92 ++ packages/engine/src/merge/merger-ai.ts | 173 ++- 10 files changed, 875 insertions(+), 538 deletions(-) create mode 100644 .changeset/fn-8958-orphan-merge-write-fence.md create mode 100644 docs/solutions/reliability/orphan-merge-body-write-fence.md create mode 100644 packages/engine/src/__tests__/merge-write-fence.test.ts create mode 100644 packages/engine/src/merge/merge-write-fence.ts diff --git a/.changeset/fn-8958-orphan-merge-write-fence.md b/.changeset/fn-8958-orphan-merge-write-fence.md new file mode 100644 index 0000000000..6df94ca9d9 --- /dev/null +++ b/.changeset/fn-8958-orphan-merge-write-fence.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Prevent canceled AI merge bodies from overwriting successor merge state. +category: fix +dev: Adds `merge-write-fence` with per-mutation ownership checks, optional squash-landing signals and ref-advance checkpoints. Aborts rethrow as `MergeAbortedError`; the injected `merge:orphan-write-fenced` audit emits once at first interaction with an emit-time suppression count. diff --git a/AGENTS.md b/AGENTS.md index c7dfbfe2aa..2c6772ec9c 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -276,6 +276,7 @@ Scoped exception (FN-5819/FN-8823): while project auto-merge is On, shared-branc `testMode?: boolean` is now available in both project and global settings. If project `testMode === true` (or the resolved default provider is `"mock"` at any tier), every AI lane is forced to `mock/scripted`, overriding per-task and per-lane model selections. The dashboard exposes this via the Settings Modal "Enable test mode" toggle and a persistent "Test mode — no real AI calls" banner. ### Run Audit +- FN-8958: `merge:orphan-write-fenced` is emitted once per orphan merge body at its fence's first interaction. Metadata is ids/counts/outcomes-only: `{ taskId, category, interaction, suppressedCount }`; `suppressedCount` is the emit-time count (`1` for `interaction:"suppressed"`, `0` for `interaction:"rejected"`), never a cumulative body total. - Store-open provenance: every `TaskStore.init()` emits `store:open` with ids/paths-only metadata (`pid`, `ppid`, `execPath`, `entry`, `cwd`, `nodeVersion`). Purpose: attribute shared-DB mutations to the process that opened the store (the FN-7910 Ideas-evacuation writer was unidentifiable without it). Tests reading unfiltered `runAuditEvents` must filter out `store:open` rather than assert exact counts. - FN-8948: `mission:reconcile-pass` records a bounded automatic reconciliation result. Metadata contains optional mission ID, source enum, and scan/write/skip/conflict/failure counters only; never roadmap prose, titles, reasons, or secrets. diff --git a/docs/solutions/reliability/orphan-merge-body-write-fence.md b/docs/solutions/reliability/orphan-merge-body-write-fence.md new file mode 100644 index 0000000000..cf3fdbe950 --- /dev/null +++ b/docs/solutions/reliability/orphan-merge-body-write-fence.md @@ -0,0 +1,68 @@ +--- +category: reliability +module: "@fusion/engine" +tags: [merge, cancellation, task-store, git] +problem_type: race-condition +applies_when: "A merge generation can outlive cancellation while a successor owns the same task." +--- + +# Fence orphan merge-body writes + +An abort signal is write authority for a claimed merge generation. An aborted signal means that body no longer owns the task row; the successor owns a fresh signal. Ownership must be read immediately before each mutation or irreversible action. A function-entry, closure, loop-head, or shared adjacent-writer check is unsound because an abort may arrive during the preceding await. + +`merge-write-fence.ts` supplies one fence per merge body. Diagnostic and rebound writes use `fence.write`, which silently suppresses an orphaned call. Finalization, task completion, commit association, branch-group updates, group-PR synchronization, and cleanup use `assertOwned`, which throws `MergeAbortedError` so the body unwinds instead of leaving a partially finalized successor row. Every catch on the merge path rethrows that error; it is never classified as a transient failure, partial workspace land, rebound result, or group-PR sync failure. + +## Boundary policy mapping + +Each entry is a single action at its own boundary: + +| Boundary/action | Bucket | Guard | +| --- | --- | --- | +| `runAiMerge` log entry | suppress | `fence.write("log")` | +| `runAiMerge` agent log append | suppress | `fence.write("log")` | +| `landWorkspaceTask` log entry | suppress | `fence.write("log")` | +| `landWorkspaceTask` agent log append | suppress | `fence.write("log")` | +| transient merge status update | suppress | `fence.write("lifecycle")` | +| single-repo no-commits rebound error update | suppress | `fence.write("lifecycle")` | +| single-repo no-commits rebound diagnostic | suppress | `fence.write("log")` | +| single-repo no-commits rebound move | suppress | `fence.write("lifecycle")` | +| single-repo missing-proof rebound error update | suppress | `fence.write("lifecycle")` | +| single-repo missing-proof rebound diagnostic | suppress | `fence.write("log")` | +| single-repo missing-proof rebound move | suppress | `fence.write("lifecycle")` | +| single-repo overseer-veto rebound error update | suppress | `fence.write("lifecycle")` | +| single-repo overseer-veto rebound diagnostic | suppress | `fence.write("log")` | +| single-repo overseer-veto rebound move | suppress | `fence.write("lifecycle")` | +| workspace missing-proof rebound error update | suppress | `fence.write("lifecycle")` | +| workspace missing-proof rebound diagnostic | suppress | `fence.write("log")` | +| workspace missing-proof rebound move | suppress | `fence.write("lifecycle")` | +| auto-finalization hard-blocker update | throw | `fence.assertOwned()` | +| workspace final `mergeDetails` update | throw | `fence.assertOwned()` | +| workspace finalization hand-off | throw | `fence.assertOwned()` | +| final merge-details/files update | throw | `fence.assertOwned()` | +| commit association | throw | `fence.assertOwned()` | +| cleanup merge-details update | throw | `fence.assertOwned()` | +| branch deletion | throw | `fence.assertOwned()` | +| worktree removal | throw | `fence.assertOwned()` | +| worktree-clear update | throw | `fence.assertOwned()` | +| branch-group member landing | throw | `fence.assertOwned()` | +| managed group-PR sync | throw | `fence.assertOwned()` | +| auto-finalization task update | throw | `fence.assertOwned()` | +| auto-finalization column move | throw | `fence.assertOwned()` | +| auto-finalization tail log | throw | `fence.assertOwned()` | +| `task:merged` emit | throw | `fence.assertOwned()` | +| squash case-B CAS ref advance | throw | `assertMergeGenerationOwned()` | +| squash dirty-checkout CAS ref advance | throw | `assertMergeGenerationOwned()` | +| squash case-A fast-forward merge | throw | `assertMergeGenerationOwned()` | +| push-after-merge ref advance | throw | `assertMergeGenerationOwned()` | +| `persistRepoLandedSha` | deliberately unfenced | Records an already-completed advance; suppressing it can recreate double-squash landing. | +| append-only run audit | deliberately unfenced | Forensic data does not mutate task ownership; aborts are excluded from sync-failure classification. | + +The squash helper receives an optional signal. Existing direct callers without a signal retain prior behavior. Its loop-head cancellation checks remain cheap early-outs, but each actual ref advance has its own immediate ownership check. + +A non-abort failure in an already-orphaned rebound may have its next rebound write suppressed; no compensating mutation is made because the successor owns the row. Similarly, an abort at branch/worktree cleanup may leave cleanup for the successor or existing self-healing. Completion announcement remains last, preventing an abandoned body from announcing a merge. + +## Observability + +The fence gets an injected `recordRunAuditEvent` recorder; no ambient or module-global state is used. It emits one `merge:orphan-write-fenced` event on the fence's first interaction with `{ taskId, category, interaction, suppressedCount }`. The emit-time count is `1` for a suppression-first interaction and `0` for a rejection-first interaction. Later suppressions only increase the in-process counter: no end-of-body cumulative event is attempted because unwinding cannot reliably flush one. The rejection-first case is therefore unit-tested at the fence level. + +This completes the progression from FN-8912's transient-status fence and FN-8923's call-site inventory. The regression suite drives real single-repository and workspace bodies so an orphan rejects with `MergeAbortedError` while successor-owned task state remains independent. diff --git a/packages/engine/src/__tests__/_merge-durable-write-callsites.ts b/packages/engine/src/__tests__/_merge-durable-write-callsites.ts index 85c9c6d23a..fc8237092d 100644 --- a/packages/engine/src/__tests__/_merge-durable-write-callsites.ts +++ b/packages/engine/src/__tests__/_merge-durable-write-callsites.ts @@ -67,6 +67,10 @@ const STORE_METHOD_CLASSIFICATION: RecordcreatedTask", "writer": "store.createTask", "ordinal": 1, - "lineHint": 1539, + "lineHint": 1541, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -520,7 +526,7 @@ "enclosingSymbolPath": "createChatTaskDocumentTools>document", "writer": "store.upsertTaskDocument", "ordinal": 1, - "lineHint": 2256, + "lineHint": 2258, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -538,7 +544,7 @@ "enclosingSymbolPath": "createTaskAddDepTool", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 3398, + "lineHint": 3400, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -558,7 +564,7 @@ "enclosingSymbolPath": "createTaskArchiveTool>task", "writer": "store.archiveTask", "ordinal": 1, - "lineHint": 3140, + "lineHint": 3142, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -576,7 +582,7 @@ "enclosingSymbolPath": "createTaskDeleteTool>task", "writer": "store.deleteTask", "ordinal": 1, - "lineHint": 3187, + "lineHint": 3189, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -594,7 +600,7 @@ "enclosingSymbolPath": "createTaskDocumentWriteTool>document", "writer": "store.upsertTaskDocument", "ordinal": 1, - "lineHint": 2086, + "lineHint": 2088, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -612,7 +618,7 @@ "enclosingSymbolPath": "createTaskDuplicateTool>task", "writer": "store.duplicateTask", "ordinal": 1, - "lineHint": 3277, + "lineHint": 3279, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -630,7 +636,7 @@ "enclosingSymbolPath": "createTaskFileScopeAddTool", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 2213, + "lineHint": 2215, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -650,7 +656,7 @@ "enclosingSymbolPath": "createTaskFileScopeAddTool", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2212, + "lineHint": 2214, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -670,7 +676,7 @@ "enclosingSymbolPath": "createTaskLogTool", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1899, + "lineHint": 1901, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -690,7 +696,7 @@ "enclosingSymbolPath": "createTaskLogToolWithContext", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1937, + "lineHint": 1939, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -710,7 +716,7 @@ "enclosingSymbolPath": "createTaskPauseTool>task", "writer": "store.pauseTask", "ordinal": 1, - "lineHint": 3243, + "lineHint": 3245, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -728,7 +734,7 @@ "enclosingSymbolPath": "createTaskPromptWriteTool", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2127, + "lineHint": 2129, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -748,7 +754,7 @@ "enclosingSymbolPath": "createTaskRetryTool", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 3226, + "lineHint": 3228, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -768,7 +774,7 @@ "enclosingSymbolPath": "createTaskRetryTool", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 3225, + "lineHint": 3227, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -788,7 +794,7 @@ "enclosingSymbolPath": "createTaskRetryTool", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 3212, + "lineHint": 3214, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -808,7 +814,7 @@ "enclosingSymbolPath": "createTaskUnarchiveTool>task", "writer": "store.unarchiveTask", "ordinal": 1, - "lineHint": 3164, + "lineHint": 3166, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -826,7 +832,7 @@ "enclosingSymbolPath": "createTaskUnpauseTool>task", "writer": "store.pauseTask", "ordinal": 1, - "lineHint": 3260, + "lineHint": 3262, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -844,7 +850,7 @@ "enclosingSymbolPath": "createTaskUpdateTool", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 3344, + "lineHint": 3346, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -864,7 +870,7 @@ "enclosingSymbolPath": "createTaskUpdateTool>res", "writer": "store.updateTaskCustomFields", "ordinal": 1, - "lineHint": 3323, + "lineHint": 3325, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -882,7 +888,7 @@ "enclosingSymbolPath": "createTaskUpdateTool>task", "writer": "store.updateStep", "ordinal": 1, - "lineHint": 3347, + "lineHint": 3349, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -900,7 +906,7 @@ "enclosingSymbolPath": "createWorkflowCreateTool>created", "writer": "store.createWorkflowDefinition", "ordinal": 1, - "lineHint": 3614, + "lineHint": 3616, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -918,7 +924,7 @@ "enclosingSymbolPath": "createWorkflowDeleteTool", "writer": "store.deleteWorkflowDefinition", "ordinal": 1, - "lineHint": 3742, + "lineHint": 3744, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -936,7 +942,7 @@ "enclosingSymbolPath": "createWorkflowSelectTool", "writer": "store.selectTaskWorkflowAndReconcile", "ordinal": 1, - "lineHint": 2998, + "lineHint": 3000, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -954,7 +960,7 @@ "enclosingSymbolPath": "createWorkflowSettingsTool>next", "writer": "store.updateWorkflowSettingValues", "ordinal": 1, - "lineHint": 3880, + "lineHint": 3882, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -972,7 +978,7 @@ "enclosingSymbolPath": "createWorkflowUpdateTool>updated", "writer": "store.updateWorkflowDefinition", "ordinal": 1, - "lineHint": 3682, + "lineHint": 3684, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -990,7 +996,7 @@ "enclosingSymbolPath": "registerArtifactForAgent>artifact", "writer": "store.registerArtifact", "ordinal": 1, - "lineHint": 2428, + "lineHint": 2430, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -1158,7 +1164,7 @@ "enclosingSymbolPath": "issueRelease>result", "writer": "store.moveTaskIf", "ordinal": 1, - "lineHint": 907, + "lineHint": 883, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -1176,7 +1182,7 @@ "enclosingSymbolPath": "promoteHeldTask", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 1016, + "lineHint": 992, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -1196,7 +1202,7 @@ "enclosingSymbolPath": "promoteHeldTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 1005, + "lineHint": 981, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -1216,7 +1222,7 @@ "enclosingSymbolPath": "releaseHeldTaskByEvent", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 1068, + "lineHint": 1044, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -1496,18 +1502,18 @@ "enclosingSymbolPath": "finalizeProvenAutoMergeTask", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 368, + "lineHint": 377, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/auto-merge-finalization.ts::finalizeProvenAutoMergeTask::store.updateTask::#1", @@ -1516,18 +1522,18 @@ "enclosingSymbolPath": "finalizeProvenAutoMergeTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 301, + "lineHint": 307, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/auto-merge-finalization.ts::finalizeProvenAutoMergeTask::store.updateTask::#2", @@ -1536,18 +1542,18 @@ "enclosingSymbolPath": "finalizeProvenAutoMergeTask", "writer": "store.updateTask", "ordinal": 2, - "lineHint": 336, + "lineHint": 343, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/auto-merge-finalization.ts::finalizeProvenAutoMergeTask>moved::store.moveTask::#1", @@ -1556,18 +1562,18 @@ "enclosingSymbolPath": "finalizeProvenAutoMergeTask>moved", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 354, + "lineHint": 362, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.recordBranchGroupMemberLanded::#1", @@ -1576,20 +1582,20 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.recordBranchGroupMemberLanded", "ordinal": 1, - "lineHint": 2670, + "lineHint": 2724, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "mid-review", "post-ref-advance", "post-mergeDetails" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The real mid-review, integration-ref-advance, and mergeDetails abort-at-boundary fixtures continue through branch-group landing without an intervening throwIfAborted; the recorder observes this write after abort." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.recordRunAuditEvent::#1", @@ -1598,7 +1604,7 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 2687, + "lineHint": 2744, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1607,13 +1613,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.updateTask::#1", @@ -1622,7 +1628,7 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2604, + "lineHint": 2655, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1631,13 +1637,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.updateTask::#2", @@ -1646,7 +1652,7 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.updateTask", "ordinal": 2, - "lineHint": 2622, + "lineHint": 2675, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1655,13 +1661,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.updateTask::#3", @@ -1670,7 +1676,7 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.updateTask", "ordinal": 3, - "lineHint": 2637, + "lineHint": 2693, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1679,13 +1685,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.upsertTaskCommitAssociation::#1", @@ -1694,7 +1700,7 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "store.upsertTaskCommitAssociation", "ordinal": 1, - "lineHint": 2608, + "lineHint": 2660, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1703,13 +1709,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeMerged::syncGroupPrOnLanding::#1", @@ -1718,18 +1724,18 @@ "enclosingSymbolPath": "finalizeMerged", "writer": "syncGroupPrOnLanding", "ordinal": 1, - "lineHint": 2679, + "lineHint": 2735, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeTask::store.emit::#1", @@ -1738,7 +1744,7 @@ "enclosingSymbolPath": "finalizeTask", "writer": "store.emit", "ordinal": 1, - "lineHint": 2733, + "lineHint": 2796, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1747,33 +1753,33 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeTask>finalization::finalizeProvenAutoMergeTask::#1", - "callSiteFingerprint": "finalizeProvenAutoMergeTask({?,?,?,?,auditAgentId,auditPhase,source,?,?})", + "callSiteFingerprint": "finalizeProvenAutoMergeTask({?,?,?,?,auditAgentId,auditPhase,source,?,?,?})", "file": "packages/engine/src/merge/merger-ai.ts", "enclosingSymbolPath": "finalizeTask>finalization", "writer": "finalizeProvenAutoMergeTask", "ordinal": 1, - "lineHint": 2715, + "lineHint": 2776, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeWorkspaceTask::store.logEntry::#1", @@ -1782,7 +1788,7 @@ "enclosingSymbolPath": "finalizeWorkspaceTask", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 2229, + "lineHint": 2276, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1791,13 +1797,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::finalizeWorkspaceTask::store.updateTask::#1", @@ -1806,7 +1812,7 @@ "enclosingSymbolPath": "finalizeWorkspaceTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2214, + "lineHint": 2261, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1815,13 +1821,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask::store.logEntry::#1", @@ -1830,7 +1836,7 @@ "enclosingSymbolPath": "landWorkspaceTask", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 2118, + "lineHint": 2163, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1839,13 +1845,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask::store.moveTask::#1", @@ -1854,7 +1860,7 @@ "enclosingSymbolPath": "landWorkspaceTask", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 2128, + "lineHint": 2173, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1863,13 +1869,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask::store.updateTask::#1", @@ -1878,7 +1884,7 @@ "enclosingSymbolPath": "landWorkspaceTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2116, + "lineHint": 2160, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1887,13 +1893,33 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." + }, + { + "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask>fence::store.recordRunAuditEvent::#1", + "callSiteFingerprint": "store.recordRunAuditEvent({?,agentId,runId,domain,mutationType,target,metadata})", + "file": "packages/engine/src/merge/merger-ai.ts", + "enclosingSymbolPath": "landWorkspaceTask>fence", + "writer": "store.recordRunAuditEvent", + "ordinal": 1, + "lineHint": 1913, + "owningEntryPoint": "landWorkspaceTask", + "reachableDataStates": [ + "post-abort" + ], + "axis1": "checkpoint-covered", + "axis2Provisional": "benign-unfenced", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", + "executionProof": "positive-observation", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask>log::store.appendAgentLog::#1", @@ -1902,7 +1928,7 @@ "enclosingSymbolPath": "landWorkspaceTask>log", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 1878, + "lineHint": 1921, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1911,13 +1937,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::landWorkspaceTask>log::store.logEntry::#1", @@ -1926,7 +1952,7 @@ "enclosingSymbolPath": "landWorkspaceTask>log", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1877, + "lineHint": 1920, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1935,13 +1961,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::persistRepoLandedSha::store.updateTask::#1", @@ -1950,7 +1976,7 @@ "enclosingSymbolPath": "persistRepoLandedSha", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 2170, + "lineHint": 2215, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1959,13 +1985,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::pushAfterMergeToRemote>recordRecoveryBranch::store.logEntry::#1", @@ -1974,7 +2000,7 @@ "enclosingSymbolPath": "pushAfterMergeToRemote>recordRecoveryBranch", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 2417, + "lineHint": 2465, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -1983,13 +2009,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.logEntry::#1", @@ -1998,7 +2024,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1421, + "lineHint": 1447, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2007,13 +2033,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.logEntry::#2", @@ -2022,7 +2048,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 1481, + "lineHint": 1511, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2031,13 +2057,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.logEntry::#3", @@ -2046,7 +2072,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 1547, + "lineHint": 1581, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2055,13 +2081,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.moveTask::#1", @@ -2070,7 +2096,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 1444, + "lineHint": 1470, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2079,13 +2105,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.moveTask::#2", @@ -2094,7 +2120,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.moveTask", "ordinal": 2, - "lineHint": 1498, + "lineHint": 1528, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2103,13 +2129,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.moveTask::#3", @@ -2118,7 +2144,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.moveTask", "ordinal": 3, - "lineHint": 1570, + "lineHint": 1604, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2127,13 +2153,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.updateTask::#1", @@ -2142,7 +2168,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 1419, + "lineHint": 1441, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2151,13 +2177,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.updateTask::#2", @@ -2166,7 +2192,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.updateTask", "ordinal": 2, - "lineHint": 1479, + "lineHint": 1505, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2175,13 +2201,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge::store.updateTask::#3", @@ -2190,7 +2216,7 @@ "enclosingSymbolPath": "runAiMerge", "writer": "store.updateTask", "ordinal": 3, - "lineHint": 1545, + "lineHint": 1575, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2199,13 +2225,33 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", - "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "axis2Final": "already-fenced", + "observedInSuite": "layerB-observed", + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." + }, + { + "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge>fence::store.recordRunAuditEvent::#1", + "callSiteFingerprint": "store.recordRunAuditEvent({?,agentId,runId,domain,mutationType,target,metadata})", + "file": "packages/engine/src/merge/merger-ai.ts", + "enclosingSymbolPath": "runAiMerge>fence", + "writer": "store.recordRunAuditEvent", + "ordinal": 1, + "lineHint": 1339, + "owningEntryPoint": "runAiMerge", + "reachableDataStates": [ + "post-abort" + ], + "axis1": "checkpoint-covered", + "axis2Provisional": "benign-unfenced", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", + "executionProof": "positive-observation", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge>log::store.appendAgentLog::#1", @@ -2214,7 +2260,7 @@ "enclosingSymbolPath": "runAiMerge>log", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 1326, + "lineHint": 1348, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2223,13 +2269,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runAiMerge>log::store.logEntry::#1", @@ -2238,7 +2284,7 @@ "enclosingSymbolPath": "runAiMerge>log", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1325, + "lineHint": 1347, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2247,13 +2293,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "checkpoint-gap", + "axis1": "checkpoint-covered", "axis2Provisional": "must-be-fenced", - "axis2Final": "must-be-fenced", + "axis2Final": "already-fenced", "observedInSuite": "layerB-observed", - "executionProof": "positive-observation", - "followUpTaskId": "FN-8958", - "axis1Evidence": "The abort-at-clean-room-review fixture aborts after review returns; production code has no throwIfAborted checkpoint before landing, mergeDetails persistence, complete-column movement, or final log emission." + "executionProof": "static:per-call-site merge write fence or ownership checkpoint", + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "FN-8958 applies a fresh per-boundary fence or ownership checkpoint at this call site." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runPushAfterMergeStep::store.logEntry::#1", @@ -2262,7 +2308,7 @@ "enclosingSymbolPath": "runPushAfterMergeStep", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1666, + "lineHint": 1700, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2271,13 +2317,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runPushAfterMergeStep::store.logEntry::#2", @@ -2286,7 +2332,7 @@ "enclosingSymbolPath": "runPushAfterMergeStep", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 1687, + "lineHint": 1721, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2295,13 +2341,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runPushAfterMergeStep::store.logEntry::#3", @@ -2310,7 +2356,7 @@ "enclosingSymbolPath": "runPushAfterMergeStep", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 1699, + "lineHint": 1733, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2319,13 +2365,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::runPushAfterMergeStep::store.updateTask::#1", @@ -2334,7 +2380,7 @@ "enclosingSymbolPath": "runPushAfterMergeStep", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 1656, + "lineHint": 1690, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2343,13 +2389,13 @@ "post-mergeDetails", "workspace-partial-land" ], - "axis1": "indeterminate", + "axis1": "checkpoint-covered", "axis2Provisional": "unresolved", - "axis2Final": "unresolved", - "observedInSuite": "unobservable:production-body harness cannot isolate this call site without a test-only export", + "axis2Final": "benign-unfenced", + "observedInSuite": "layerB-observed", "executionProof": "none:production-body harness cannot isolate this call site without a test-only export", - "followUpTaskId": "FN-8958", - "axis1Evidence": "Direct merge-body module call site; deep abort-at-boundary production-path isolation remains unresolved without a test-only export." + "followUpTaskId": "none:no-follow-up-required", + "axis1Evidence": "Deliberately unfenced append-only audit or post-finalization bookkeeping surface; it does not clobber a successor task row." }, { "callSiteId": "packages/engine/src/merge/merger-ai.ts::writeTransientMergeStatus::store.updateTask::#1", @@ -2358,7 +2404,7 @@ "enclosingSymbolPath": "writeTransientMergeStatus", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 144, + "lineHint": 151, "owningEntryPoint": "runAiMerge", "reachableDataStates": [ "pre-land", @@ -2600,7 +2646,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 8072, + "lineHint": 8073, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2620,7 +2666,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 8207, + "lineHint": 8208, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2640,7 +2686,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 8543, + "lineHint": 8544, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2660,7 +2706,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 4, - "lineHint": 8613, + "lineHint": 8614, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2680,7 +2726,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 5, - "lineHint": 9364, + "lineHint": 9365, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2700,7 +2746,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 6, - "lineHint": 9373, + "lineHint": 9374, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2720,7 +2766,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.appendAgentLog", "ordinal": 7, - "lineHint": 9550, + "lineHint": 9551, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2740,7 +2786,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.enqueueMergeQueue", "ordinal": 1, - "lineHint": 7384, + "lineHint": 7385, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -2758,7 +2804,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 7547, + "lineHint": 7548, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2778,7 +2824,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 10, - "lineHint": 7938, + "lineHint": 7939, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2798,7 +2844,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 11, - "lineHint": 8332, + "lineHint": 8333, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2818,7 +2864,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 12, - "lineHint": 8440, + "lineHint": 8441, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2838,7 +2884,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 13, - "lineHint": 8475, + "lineHint": 8476, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2858,7 +2904,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 14, - "lineHint": 8544, + "lineHint": 8545, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2878,7 +2924,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 15, - "lineHint": 8629, + "lineHint": 8630, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2898,7 +2944,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 16, - "lineHint": 8634, + "lineHint": 8635, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2918,7 +2964,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 17, - "lineHint": 9236, + "lineHint": 9237, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2938,7 +2984,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 18, - "lineHint": 9890, + "lineHint": 9891, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2958,7 +3004,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 19, - "lineHint": 9914, + "lineHint": 9915, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2978,7 +3024,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 7580, + "lineHint": 7581, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -2998,7 +3044,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 7621, + "lineHint": 7622, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3018,7 +3064,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 4, - "lineHint": 7663, + "lineHint": 7664, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3038,7 +3084,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 5, - "lineHint": 7690, + "lineHint": 7691, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3058,7 +3104,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 6, - "lineHint": 7738, + "lineHint": 7739, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3078,7 +3124,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 7, - "lineHint": 7758, + "lineHint": 7759, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3098,7 +3144,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 8, - "lineHint": 7825, + "lineHint": 7826, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3118,7 +3164,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.logEntry", "ordinal": 9, - "lineHint": 7887, + "lineHint": 7888, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3138,7 +3184,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 7604, + "lineHint": 7605, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3158,7 +3204,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 2, - "lineHint": 7638, + "lineHint": 7639, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3178,7 +3224,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 3, - "lineHint": 7701, + "lineHint": 7702, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3198,7 +3244,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 4, - "lineHint": 7836, + "lineHint": 7837, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3218,7 +3264,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 5, - "lineHint": 7911, + "lineHint": 7912, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3238,7 +3284,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.moveTask", "ordinal": 6, - "lineHint": 9466, + "lineHint": 9467, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3258,7 +3304,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 6738, + "lineHint": 6739, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3278,7 +3324,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 2, - "lineHint": 6881, + "lineHint": 6882, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3298,7 +3344,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 3, - "lineHint": 6909, + "lineHint": 6910, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3318,7 +3364,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 4, - "lineHint": 7591, + "lineHint": 7592, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3338,7 +3384,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 5, - "lineHint": 7629, + "lineHint": 7630, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3358,7 +3404,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 6, - "lineHint": 7695, + "lineHint": 7696, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3378,7 +3424,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 7, - "lineHint": 7830, + "lineHint": 7831, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3398,7 +3444,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.recordRunAuditEvent", "ordinal": 8, - "lineHint": 7898, + "lineHint": 7899, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3418,7 +3464,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.transitionMergeRequestState", "ordinal": 1, - "lineHint": 7360, + "lineHint": 7361, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -3436,7 +3482,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.transitionMergeRequestState", "ordinal": 2, - "lineHint": 7361, + "lineHint": 7362, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -3454,7 +3500,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.transitionMergeRequestState", "ordinal": 3, - "lineHint": 7363, + "lineHint": 7364, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -3472,7 +3518,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 7546, + "lineHint": 7547, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3492,7 +3538,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 10, - "lineHint": 7916, + "lineHint": 7917, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3512,7 +3558,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 11, - "lineHint": 8649, + "lineHint": 8650, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3532,7 +3578,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 12, - "lineHint": 9462, + "lineHint": 9463, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3552,7 +3598,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 13, - "lineHint": 9513, + "lineHint": 9514, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3572,7 +3618,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 14, - "lineHint": 9627, + "lineHint": 9628, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3592,7 +3638,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 15, - "lineHint": 9751, + "lineHint": 9752, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3612,7 +3658,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 16, - "lineHint": 9784, + "lineHint": 9785, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3632,7 +3678,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 17, - "lineHint": 9861, + "lineHint": 9862, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3652,7 +3698,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 2, - "lineHint": 7579, + "lineHint": 7580, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3672,7 +3718,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 3, - "lineHint": 7620, + "lineHint": 7621, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3692,7 +3738,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 4, - "lineHint": 7662, + "lineHint": 7663, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3712,7 +3758,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 5, - "lineHint": 7689, + "lineHint": 7690, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3732,7 +3778,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 6, - "lineHint": 7739, + "lineHint": 7740, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3752,7 +3798,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 7, - "lineHint": 7824, + "lineHint": 7825, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3772,7 +3818,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 8, - "lineHint": 7850, + "lineHint": 7851, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3792,7 +3838,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.updateTask", "ordinal": 9, - "lineHint": 7886, + "lineHint": 7887, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3812,7 +3858,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.upsertMergeRequestRecord", "ordinal": 1, - "lineHint": 7355, + "lineHint": 7356, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -3830,7 +3876,7 @@ "enclosingSymbolPath": "aiMergeTask", "writer": "store.upsertTaskCommitAssociation", "ordinal": 1, - "lineHint": 9520, + "lineHint": 9521, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3850,7 +3896,7 @@ "enclosingSymbolPath": "aiMergeTask>capture", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 9405, + "lineHint": 9406, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3870,7 +3916,7 @@ "enclosingSymbolPath": "aiMergeTask>cleared", "writer": "store.clearStaleExecutionStartBranchReferences", "ordinal": 1, - "lineHint": 9705, + "lineHint": 9706, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -3888,7 +3934,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 8691, + "lineHint": 8692, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3908,7 +3954,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 10, - "lineHint": 9053, + "lineHint": 9054, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3928,7 +3974,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 11, - "lineHint": 9088, + "lineHint": 9089, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3948,7 +3994,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 8830, + "lineHint": 8831, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3968,7 +4014,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 8855, + "lineHint": 8856, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -3988,7 +4034,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 4, - "lineHint": 8889, + "lineHint": 8890, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4008,7 +4054,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 5, - "lineHint": 8901, + "lineHint": 8902, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4028,7 +4074,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 6, - "lineHint": 8939, + "lineHint": 8940, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4048,7 +4094,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 7, - "lineHint": 8990, + "lineHint": 8991, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4068,7 +4114,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 8, - "lineHint": 9009, + "lineHint": 9010, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4088,7 +4134,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.appendAgentLog", "ordinal": 9, - "lineHint": 9043, + "lineHint": 9044, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4108,7 +4154,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 8808, + "lineHint": 8809, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4128,7 +4174,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 10, - "lineHint": 9052, + "lineHint": 9053, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4148,7 +4194,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 11, - "lineHint": 9095, + "lineHint": 9096, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4168,7 +4214,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 12, - "lineHint": 9131, + "lineHint": 9132, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4188,7 +4234,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 8829, + "lineHint": 8830, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4208,7 +4254,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 8854, + "lineHint": 8855, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4228,7 +4274,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 4, - "lineHint": 8888, + "lineHint": 8889, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4248,7 +4294,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 5, - "lineHint": 8900, + "lineHint": 8901, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4268,7 +4314,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 6, - "lineHint": 8946, + "lineHint": 8947, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4288,7 +4334,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 7, - "lineHint": 8989, + "lineHint": 8990, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4308,7 +4354,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 8, - "lineHint": 9008, + "lineHint": 9009, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4328,7 +4374,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.logEntry", "ordinal": 9, - "lineHint": 9042, + "lineHint": 9043, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4348,7 +4394,7 @@ "enclosingSymbolPath": "aiMergeTask>mergeAttempt", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 8804, + "lineHint": 8805, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4368,7 +4414,7 @@ "enclosingSymbolPath": "aiMergeTask>reacquireReuseIntegrationWorktree", "writer": "store.recordActivity", "ordinal": 1, - "lineHint": 7167, + "lineHint": 7168, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -4386,7 +4432,7 @@ "enclosingSymbolPath": "aiMergeTask>reacquireReuseIntegrationWorktree", "writer": "store.recordActivity", "ordinal": 2, - "lineHint": 7268, + "lineHint": 7269, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -4404,7 +4450,7 @@ "enclosingSymbolPath": "aiMergeTask>reacquireReuseIntegrationWorktree", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 7151, + "lineHint": 7152, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4424,7 +4470,7 @@ "enclosingSymbolPath": "aiMergeTask>recordBranchGroupMemberLanding", "writer": "store.recordBranchGroupMemberLanded", "ordinal": 1, - "lineHint": 6805, + "lineHint": 6806, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4444,7 +4490,7 @@ "enclosingSymbolPath": "aiMergeTask>recordBranchGroupMemberLanding", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 6818, + "lineHint": 6819, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4464,7 +4510,7 @@ "enclosingSymbolPath": "aiMergeTask>recordBranchGroupMemberLanding>settled", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 6859, + "lineHint": 6860, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4484,7 +4530,7 @@ "enclosingSymbolPath": "aiMergeTask>recordBranchGroupMemberLanding>settled", "writer": "syncGroupPrOnLanding", "ordinal": 1, - "lineHint": 6851, + "lineHint": 6852, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4504,7 +4550,7 @@ "enclosingSymbolPath": "aiMergeTask>runLocalBaseRebase", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 8167, + "lineHint": 8168, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4524,7 +4570,7 @@ "enclosingSymbolPath": "applyBranchCommitsPreservingHistory", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 4796, + "lineHint": 4797, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4544,7 +4590,7 @@ "enclosingSymbolPath": "applyBranchCommitsPreservingHistory", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 4798, + "lineHint": 4799, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4564,7 +4610,7 @@ "enclosingSymbolPath": "applyLayer3ConflictScopePartition", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 4202, + "lineHint": 4203, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4584,7 +4630,7 @@ "enclosingSymbolPath": "applyLayer3ConflictScopePartition", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 4264, + "lineHint": 4265, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4604,7 +4650,7 @@ "enclosingSymbolPath": "applyLayer3ConflictScopePartition", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 4299, + "lineHint": 4300, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4624,7 +4670,7 @@ "enclosingSymbolPath": "applyLayer3ConflictScopePartition", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 4300, + "lineHint": 4301, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4644,7 +4690,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 1248, + "lineHint": 1249, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4664,7 +4710,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 1315, + "lineHint": 1316, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4684,7 +4730,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 1352, + "lineHint": 1353, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4704,7 +4750,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.appendAgentLog", "ordinal": 4, - "lineHint": 1367, + "lineHint": 1368, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4724,7 +4770,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.appendAgentLog", "ordinal": 5, - "lineHint": 1420, + "lineHint": 1421, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4744,7 +4790,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1244, + "lineHint": 1245, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4764,7 +4810,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 1311, + "lineHint": 1312, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4784,7 +4830,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 1351, + "lineHint": 1352, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4804,7 +4850,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.logEntry", "ordinal": 4, - "lineHint": 1363, + "lineHint": 1364, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4824,7 +4870,7 @@ "enclosingSymbolPath": "attemptInMergeVerificationFix", "writer": "store.logEntry", "ordinal": 5, - "lineHint": 1419, + "lineHint": 1420, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4844,7 +4890,7 @@ "enclosingSymbolPath": "completeTask", "writer": "store.emit", "ordinal": 1, - "lineHint": 11478, + "lineHint": 11485, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4864,7 +4910,7 @@ "enclosingSymbolPath": "completeTask", "writer": "store.recordAgentActivity", "ordinal": 1, - "lineHint": 11476, + "lineHint": 11483, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -4882,7 +4928,7 @@ "enclosingSymbolPath": "completeTask", "writer": "store.transitionMergeRequestState", "ordinal": 1, - "lineHint": 11467, + "lineHint": 11474, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -4900,7 +4946,7 @@ "enclosingSymbolPath": "completeTask", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 11460, + "lineHint": 11467, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4920,7 +4966,7 @@ "enclosingSymbolPath": "completeTask>task", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 11462, + "lineHint": 11469, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4940,7 +4986,7 @@ "enclosingSymbolPath": "dropAutostashHandle", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 2350, + "lineHint": 2351, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4960,7 +5006,7 @@ "enclosingSymbolPath": "executeMergeAttempt", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 10536, + "lineHint": 10537, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -4980,7 +5026,7 @@ "enclosingSymbolPath": "notifyAutostashOrphans", "writer": "store.emit", "ordinal": 1, - "lineHint": 1797, + "lineHint": 1798, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5000,7 +5046,7 @@ "enclosingSymbolPath": "persistFinalizeResetLeftovers", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 3595, + "lineHint": 3596, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5020,7 +5066,7 @@ "enclosingSymbolPath": "recordCommitAssociationFromHead", "writer": "store.upsertTaskCommitAssociation", "ordinal": 1, - "lineHint": 3400, + "lineHint": 3401, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5040,7 +5086,7 @@ "enclosingSymbolPath": "runAiAgentForAutostashConflict", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 2531, + "lineHint": 2532, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5060,7 +5106,7 @@ "enclosingSymbolPath": "runAiAgentForAutostashConflict", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 2555, + "lineHint": 2556, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5080,7 +5126,7 @@ "enclosingSymbolPath": "runAiAgentForAutostashHardFail", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 2979, + "lineHint": 2980, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5100,7 +5146,7 @@ "enclosingSymbolPath": "runAiAgentForAutostashHardFail", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 3003, + "lineHint": 3004, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5120,7 +5166,7 @@ "enclosingSymbolPath": "runAiAgentForCommit", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 11125, + "lineHint": 11132, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5140,7 +5186,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 816, + "lineHint": 817, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5160,7 +5206,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 847, + "lineHint": 848, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5180,7 +5226,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 855, + "lineHint": 856, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5200,7 +5246,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 4, - "lineHint": 859, + "lineHint": 860, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5220,7 +5266,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 5, - "lineHint": 870, + "lineHint": 871, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5240,7 +5286,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 6, - "lineHint": 888, + "lineHint": 889, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5260,7 +5306,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 7, - "lineHint": 990, + "lineHint": 991, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5280,7 +5326,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 8, - "lineHint": 1019, + "lineHint": 1020, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5300,7 +5346,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.appendAgentLog", "ordinal": 9, - "lineHint": 1036, + "lineHint": 1037, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5320,7 +5366,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 815, + "lineHint": 816, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5340,7 +5386,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 10, - "lineHint": 1043, + "lineHint": 1044, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5360,7 +5406,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 846, + "lineHint": 847, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5380,7 +5426,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 854, + "lineHint": 855, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5400,7 +5446,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 4, - "lineHint": 858, + "lineHint": 859, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5420,7 +5466,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 5, - "lineHint": 869, + "lineHint": 870, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5440,7 +5486,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 6, - "lineHint": 883, + "lineHint": 884, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5460,7 +5506,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 7, - "lineHint": 985, + "lineHint": 986, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5480,7 +5526,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 8, - "lineHint": 1014, + "lineHint": 1015, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5500,7 +5546,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.logEntry", "ordinal": 9, - "lineHint": 1035, + "lineHint": 1036, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5520,7 +5566,7 @@ "enclosingSymbolPath": "runDeterministicVerification", "writer": "store.recordVerificationCachePass", "ordinal": 1, - "lineHint": 1041, + "lineHint": 1042, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -5538,7 +5584,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.appendAgentLog", "ordinal": 1, - "lineHint": 926, + "lineHint": 927, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5558,7 +5604,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.appendAgentLog", "ordinal": 2, - "lineHint": 940, + "lineHint": 941, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5578,7 +5624,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.appendAgentLog", "ordinal": 3, - "lineHint": 954, + "lineHint": 955, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5598,7 +5644,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.appendAgentLog", "ordinal": 4, - "lineHint": 966, + "lineHint": 967, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5618,7 +5664,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.appendAgentLog", "ordinal": 5, - "lineHint": 971, + "lineHint": 972, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5638,7 +5684,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 925, + "lineHint": 926, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5658,7 +5704,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 939, + "lineHint": 940, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5678,7 +5724,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 953, + "lineHint": 954, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5698,7 +5744,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.logEntry", "ordinal": 4, - "lineHint": 965, + "lineHint": 966, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5718,7 +5764,7 @@ "enclosingSymbolPath": "runDeterministicVerification>executeVerificationWithRetry", "writer": "store.logEntry", "ordinal": 5, - "lineHint": 970, + "lineHint": 971, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5738,7 +5784,7 @@ "enclosingSymbolPath": "sweepAutostashOrphans", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 1998, + "lineHint": 1999, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5758,7 +5804,7 @@ "enclosingSymbolPath": "sweepAutostashOrphans", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 2012, + "lineHint": 2013, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5778,7 +5824,7 @@ "enclosingSymbolPath": "syncDependenciesForMerge", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 659, + "lineHint": 660, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5798,7 +5844,7 @@ "enclosingSymbolPath": "syncGroupPrOnLanding", "writer": "store.updateBranchGroup", "ordinal": 1, - "lineHint": 6694, + "lineHint": 6695, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -5816,7 +5862,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 6460, + "lineHint": 6461, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5836,7 +5882,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 6511, + "lineHint": 6512, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5856,7 +5902,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 6483, + "lineHint": 6484, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5876,7 +5922,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 6459, + "lineHint": 6460, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5896,7 +5942,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.updateTask", "ordinal": 2, - "lineHint": 6510, + "lineHint": 6511, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -5916,7 +5962,7 @@ "enclosingSymbolPath": "tryEarlyEmptyOwnDiffFinalize", "writer": "store.updateTask", "ordinal": 3, - "lineHint": 6619, + "lineHint": 6620, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6010,7 +6056,7 @@ "enclosingSymbolPath": "archiveAsGhostBug", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 253, + "lineHint": 260, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6030,7 +6076,7 @@ "enclosingSymbolPath": "archiveAsGhostBug", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 270, + "lineHint": 277, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6050,7 +6096,7 @@ "enclosingSymbolPath": "archiveAsGhostBug", "writer": "store.recordActivity", "ordinal": 1, - "lineHint": 264, + "lineHint": 265, "owningEntryPoint": "none:out-of-frontier", "reachableDataStates": [], "axis1": "out-of-frontier", @@ -6068,7 +6114,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.logEntry", "ordinal": 1, - "lineHint": 629, + "lineHint": 636, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6088,7 +6134,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.logEntry", "ordinal": 2, - "lineHint": 647, + "lineHint": 654, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6108,7 +6154,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.logEntry", "ordinal": 3, - "lineHint": 765, + "lineHint": 772, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6128,7 +6174,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.moveTask", "ordinal": 1, - "lineHint": 786, + "lineHint": 793, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6148,7 +6194,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.moveTask", "ordinal": 2, - "lineHint": 788, + "lineHint": 795, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6168,7 +6214,7 @@ "enclosingSymbolPath": "autoRecoverWorktreeSessionStartFailure", "writer": "store.updateTask", "ordinal": 1, - "lineHint": 715, + "lineHint": 722, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6188,7 +6234,7 @@ "enclosingSymbolPath": "recoverApprovedStrandedAiMergeCommit>finalized", "writer": "finalizeProvenAutoMergeTask", "ordinal": 1, - "lineHint": 11978, + "lineHint": 12194, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6208,7 +6254,7 @@ "enclosingSymbolPath": "recoverMergedReviewTasks>finalization", "writer": "finalizeProvenAutoMergeTask", "ordinal": 1, - "lineHint": 10850, + "lineHint": 11066, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6248,7 +6294,7 @@ "enclosingSymbolPath": "createRunAuditor", "writer": "store.recordRunAuditEvent", "ordinal": 1, - "lineHint": 1124, + "lineHint": 1154, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6268,7 +6314,7 @@ "enclosingSymbolPath": "createRunAuditor", "writer": "store.recordRunAuditEvent", "ordinal": 2, - "lineHint": 1149, + "lineHint": 1179, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6288,7 +6334,7 @@ "enclosingSymbolPath": "createRunAuditor", "writer": "store.recordRunAuditEvent", "ordinal": 3, - "lineHint": 1167, + "lineHint": 1197, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -6308,7 +6354,7 @@ "enclosingSymbolPath": "createRunAuditor", "writer": "store.recordRunAuditEvent", "ordinal": 4, - "lineHint": 1185, + "lineHint": 1215, "owningEntryPoint": "other", "reachableDataStates": [ "unobservable:closure-wide production fixture pending" @@ -7751,6 +7797,11 @@ "kind": "non-writer", "reason": "reviewed public TaskStore operation; not a durable writer reached by this merge frontier" }, + { + "method": "clearTaskWedgeNotificationPending", + "kind": "writer", + "reason": "persists or mutates TaskStore state" + }, { "method": "clearTaskWorkflowSelection", "kind": "writer", @@ -8856,6 +8907,11 @@ "kind": "writer", "reason": "persists or mutates TaskStore state" }, + { + "method": "markTaskWedgeNotificationPending", + "kind": "writer", + "reason": "persists or mutates TaskStore state" + }, { "method": "markTerminalFailureAutoRecoveryBudgetExhausted", "kind": "writer", diff --git a/packages/engine/src/__tests__/merge-orphan-body-durable-writes.test.ts b/packages/engine/src/__tests__/merge-orphan-body-durable-writes.test.ts index 450e671c61..2e99f4e003 100644 --- a/packages/engine/src/__tests__/merge-orphan-body-durable-writes.test.ts +++ b/packages/engine/src/__tests__/merge-orphan-body-durable-writes.test.ts @@ -162,12 +162,12 @@ describe("FN-8923 orphan merge-body durable writes", () => { expect(store.getTask).toHaveBeenCalledOnce(); }); - it("aborts from the real mid-review seam and records orphan finalization writes", async () => { + it("aborts from the real mid-review seam before orphan finalization writes", async () => { const directory = createMergeRepo(); const controller = new AbortController(); const { store, records } = createRecordingStore(controller, { sharedGroup: true }); let recordsAtAbort = -1; - const result = await runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { + await expect(runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { mergeAgent: async (cwd) => { git(cwd, "merge --squash fusion/FN-8923 && git commit -q -m squash"); }, // This is the abort-at-boundary fixture: review completed, the squash exists, and the // production body is about to enter its landing/finalization stretch. @@ -176,23 +176,22 @@ describe("FN-8923 orphan merge-body durable writes", () => { recordsAtAbort = records.length; return "REVIEW_VERDICT: approve"; }, - }); + })).rejects.toMatchObject({ name: "MergeAbortedError" }); const orphanWrites = records.slice(recordsAtAbort); expect(recordsAtAbort).toBeGreaterThanOrEqual(0); - expect(result.merged).toBe(true); // Positive writes are their own execution proof and demonstrate that this is a body that // outlived its abort rather than a pre-aborted fixture that stopped at an entry checkpoint. // FN-8923 follow-up: FN-8958 flips packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.updateTask::#1. - expect(orphanWrites.some(({ writer, args }) => writer === "updateTask" && (args[1] as Record).mergeDetails)).toBe(true); + expect(orphanWrites.some(({ writer, args }) => writer === "updateTask" && (args[1] as Record).mergeDetails)).toBe(false); // FN-8923 follow-up: FN-8958 flips packages/engine/src/merge/auto-merge-finalization.ts::finalizeProvenAutoMergeTask>moved::store.moveTask::#1. - expect(orphanWrites.some(({ writer }) => writer === "moveTask")).toBe(true); + expect(orphanWrites.some(({ writer }) => writer === "moveTask")).toBe(false); // FN-8923 follow-up: FN-8958 flips packages/engine/src/merge/merger-ai.ts::finalizeTask::store.emit::#1. - expect(orphanWrites.some(({ writer, args }) => writer === "emit" && args[0] === "task:merged")).toBe(true); + expect(orphanWrites.some(({ writer, args }) => writer === "emit" && args[0] === "task:merged")).toBe(false); // FN-8923 follow-up: FN-8958 flips packages/engine/src/merge/merger-ai.ts::finalizeMerged::store.recordBranchGroupMemberLanded::#1. - expect(orphanWrites.some(({ writer }) => writer === "recordBranchGroupMemberLanded")).toBe(true); + expect(orphanWrites.some(({ writer }) => writer === "recordBranchGroupMemberLanded")).toBe(false); // FN-8923 follow-up: FN-8958 flips packages/engine/src/merge/merger-ai.ts::runAiMerge>log::store.logEntry::#1. - expect(orphanWrites.some(({ writer }) => writer === "logEntry")).toBe(true); - expect(orphanWrites.some(({ writer }) => writer === "appendAgentLog")).toBe(true); + expect(orphanWrites.some(({ writer }) => writer === "logEntry")).toBe(false); + expect(orphanWrites.some(({ writer }) => writer === "appendAgentLog")).toBe(false); }); it("attempts the real landOneRepo pre-land boundary", async () => { @@ -213,14 +212,13 @@ describe("FN-8923 orphan merge-body durable writes", () => { // `advanceIntegrationBranchRef` emits this durable audit only after its CAS ref write. onIntegrationRefAdvance: () => controller.abort("orphaned after integration ref advance"), }); - const result = await runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { + await expect(runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { mergeAgent: async (cwd) => { git(cwd, "merge --squash fusion/FN-8923 && git commit -q -m squash"); }, reviewAgent: async () => "REVIEW_VERDICT: approve", - }); + })).rejects.toMatchObject({ name: "MergeAbortedError" }); expect(controller.signal.aborted).toBe(true); - expect(result.merged).toBe(true); // The post-boundary mergeDetails write proves the production body passed the ref-advance seam. - expect(records.some(({ writer, args }) => writer === "updateTask" && (args[1] as Record).mergeDetails)).toBe(true); + expect(records.some(({ writer, args }) => writer === "updateTask" && (args[1] as Record).mergeDetails)).toBe(false); }); it("aborts from the mergeDetails persistence seam before the private finalization tail", async () => { @@ -230,14 +228,13 @@ describe("FN-8923 orphan merge-body durable writes", () => { sharedGroup: true, onMergeDetailsPersist: () => controller.abort("orphaned after mergeDetails persistence"), }); - const result = await runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { + await expect(runAiMerge(store as never, directory, "FN-8923", { manual: true, signal: controller.signal }, { mergeAgent: async (cwd) => { git(cwd, "merge --squash fusion/FN-8923 && git commit -q -m squash"); }, reviewAgent: async () => "REVIEW_VERDICT: approve", - }); + })).rejects.toMatchObject({ name: "MergeAbortedError" }); expect(controller.signal.aborted).toBe(true); - expect(result.merged).toBe(true); // `task:merged` is emitted after mergeDetails, proving the private tail was reached through runAiMerge. - expect(records.some(({ writer, args }) => writer === "emit" && args[0] === "task:merged")).toBe(true); + expect(records.some(({ writer, args }) => writer === "emit" && args[0] === "task:merged")).toBe(false); }); it("aborts during a real second workspace-repository land and partitions successor writes", async () => { @@ -328,13 +325,12 @@ describe("FN-8923 orphan merge-body durable writes", () => { expect(successorIsHeldAtProductionSeam).toBe(true); releaseOrphan(); // FNXC:MergeReliability 2026-08-11-00:37: This assertion proves the bodies overlap at a real production seam. - const result = await body; + await expect(body).rejects.toMatchObject({ name: "MergeAbortedError" }); expect(successorIsHeldAtProductionSeam).toBe(true); releaseSuccessor(); await successorBody; expect(mergeCount).toBe(2); expect(successorSignal).toBeDefined(); - expect(result.repos[0]).toMatchObject({ repo: "repo-a", status: "landed" }); expect((task.workspaceWorktrees as Record) ["repo-a"].landedSha).toEqual(expect.any(String)); const postBoundary = records.slice(recordCountAtAbort); const orphanWrites = postBoundary.filter((record) => record.generation === "orphan"); @@ -342,7 +338,7 @@ describe("FN-8923 orphan merge-body durable writes", () => { // FNXC:MergeReliability 2026-08-11-00:37: Separate facades prevent successor writes from satisfying orphan assertions. expect(successorWrites).toContainEqual(expect.objectContaining({ writer: "updateTask", args: ["FN-8923", { status: "merging" }] })); expect(orphanWrites).not.toContainEqual(expect.objectContaining({ writer: "updateTask", args: ["FN-8923", { status: "merging" }] })); - expect(orphanWrites.some(({ writer }) => writer === "logEntry")).toBe(true); + expect(orphanWrites.some(({ writer }) => writer === "logEntry")).toBe(false); }); }); }); diff --git a/packages/engine/src/__tests__/merge-write-fence.test.ts b/packages/engine/src/__tests__/merge-write-fence.test.ts new file mode 100644 index 0000000000..a88d97e6e1 --- /dev/null +++ b/packages/engine/src/__tests__/merge-write-fence.test.ts @@ -0,0 +1,39 @@ +import { describe, expect, it, vi } from "vitest"; +import { + assertMergeGenerationOwned, + createMergeWriteFence, + isMergeAbortedError, +} from "../merge/merge-write-fence.js"; + +describe("merge write fence", () => { + it("reads ownership at every individual boundary", async () => { + const controller = new AbortController(); + const fence = createMergeWriteFence({ taskId: "FN-8958", signal: controller.signal }); + expect(fence.isOrphaned()).toBe(false); + await fence.write("log", () => controller.abort()); + expect(fence.isOrphaned()).toBe(true); + await expect(fence.write("log", () => { throw new Error("must not run"); })).resolves.toBeUndefined(); + expect(fence.suppressedCount).toBe(1); + }); + + it("emits once at first suppression and keeps an in-process count", async () => { + const record = vi.fn(); + const controller = new AbortController(); controller.abort(); + const fence = createMergeWriteFence({ taskId: "FN-8958", signal: controller.signal, recordAudit: record }); + await fence.write("log", () => undefined); + await fence.write("lifecycle", () => undefined); + expect(record).toHaveBeenCalledTimes(1); + expect(record.mock.calls[0]).toEqual(["log", "suppressed", 1]); + expect(fence.suppressedCount).toBe(2); + }); + + it("emits rejection-first with count zero and has a common abort predicate", () => { + const record = vi.fn(); const controller = new AbortController(); controller.abort(); + const fence = createMergeWriteFence({ taskId: "FN-8958", signal: controller.signal, recordAudit: record }); + let error: unknown; + try { fence.assertOwned(); } catch (caught) { error = caught; } + expect(isMergeAbortedError(error)).toBe(true); + expect(record.mock.calls[0]).toEqual(["finalization", "rejected", 0]); + expect(() => assertMergeGenerationOwned(controller.signal, "FN-8958")).toThrow(/AI merge aborted/); + }); +}); diff --git a/packages/engine/src/merge/auto-merge-finalization.ts b/packages/engine/src/merge/auto-merge-finalization.ts index 674ecbefa3..9d0d7ba08e 100644 --- a/packages/engine/src/merge/auto-merge-finalization.ts +++ b/packages/engine/src/merge/auto-merge-finalization.ts @@ -11,6 +11,7 @@ import { type TaskStore, } from "@fusion/core"; import { createRunAuditor, generateSyntheticRunId, type DatabaseMutationType, type RunAuditor } from "../util/run-audit.js"; +import type { MergeWriteFence } from "./merge-write-fence.js"; /* FNXC:WorkflowMergeFinalization 2026-07-19-07:20 (U7 / R2/R3/KTD-1): @@ -82,6 +83,7 @@ export interface FinalizeProvenAutoMergeTaskOptions { auditPhase?: string; source: "direct-ai-merge" | "merge-confirmed-fast-path" | "self-healing" | "workflow-graph-merge-finalize"; log?: (message: string) => void | Promise; + fence?: MergeWriteFence; } export type WorkflowDoneMergeProofVerdict = @@ -232,6 +234,7 @@ export async function finalizeProvenAutoMergeTask({ auditPhase, source, log, + fence, }: FinalizeProvenAutoMergeTaskOptions): Promise { const latest = await store.getTask(taskId).catch(() => null); if (!latest) { @@ -298,6 +301,9 @@ export async function finalizeProvenAutoMergeTask({ error: undefined, }); if (hardBlocker) { + // FNXC:MergeReliability 2026-08-11-21:39: A blocker discovered before finalization + // still writes task lifecycle state, so an orphan must reject rather than return a blocked result. + fence?.assertOwned("finalization"); await store.updateTask(taskId, { status: "failed", error: `Merge confirmed but finalization blocked: ${hardBlocker}`, @@ -333,6 +339,7 @@ export async function finalizeProvenAutoMergeTask({ return { outcome: "blocked", task: latest, previousColumn: latest.column, reason: proofVerdict.reason }; } + fence?.assertOwned("finalization"); await store.updateTask(taskId, { paused: false, status: null, @@ -351,6 +358,7 @@ export async function finalizeProvenAutoMergeTask({ } try { + fence?.assertOwned("finalization"); const moved = await store.moveTask(taskId, completeColumn, shouldRecoveryRehome ? { moveSource: "engine", recoveryRehome: true, preserveProgress: true } : { moveSource: "engine", preserveProgress: true }); @@ -365,6 +373,7 @@ export async function finalizeProvenAutoMergeTask({ auditAgentId, auditPhase, }); + fence?.assertOwned("finalization"); await store.logEntry( taskId, `Auto-merge finalization repaired column mismatch: ${latest.column} → ${completeColumn} after proven merge; cleared stale status/blockers`, diff --git a/packages/engine/src/merge/merge-write-fence.ts b/packages/engine/src/merge/merge-write-fence.ts new file mode 100644 index 0000000000..493d00ba77 --- /dev/null +++ b/packages/engine/src/merge/merge-write-fence.ts @@ -0,0 +1,92 @@ +/* +FNXC:MergeReliability 2026-08-11-21:12: +An aborted per-claim signal means its merge body no longer owns the task row: a successor owns a +fresh signal. Abort is asynchronous, so ownership is read immediately before each individual +mutation or irreversible action; a closure, loop, or function-entry check cannot cover a later +write, and one checkpoint never covers adjacent writers. Call sites choose their bucket: diagnostic +writes suppress while finalization writes reject and unwind. + +The recorder is injected so orphan observability has no ambient state. One best-effort audit row is +emitted at the first fence interaction: suppression emits count 1 and rejection emits count 0. +The running count is intentionally in-process only because unwinding cannot guarantee an end flush. +*/ + +export type MergeWriteCategory = "log" | "lifecycle" | "finalization" | "audit"; +export type MergeFenceInteraction = "suppressed" | "rejected"; + +export interface OrphanFenceAuditRecorder { + recordRunAuditEvent(event: { + type: "merge:orphan-write-fenced"; + taskId: string; + metadata: { taskId: string; category: MergeWriteCategory; interaction: MergeFenceInteraction; suppressedCount: number }; + }): Promise | unknown; +} + +export interface MergeWriteFence { + readonly taskId: string; + readonly signal: AbortSignal | undefined; + readonly suppressedCount: number; + isOrphaned(): boolean; + write(category: MergeWriteCategory, write: () => Promise | T): Promise; + assertOwned(category?: MergeWriteCategory): void; +} + +export function createMergeAbortedError(taskId: string): Error { + const error = new Error(`AI merge aborted for ${taskId}`); + error.name = "MergeAbortedError"; + return error; +} + +export function isMergeAbortedError(error: unknown): boolean { + return error instanceof Error && error.name === "MergeAbortedError"; +} + +export function assertMergeGenerationOwned(signal: AbortSignal | undefined, taskId: string): void { + if (signal?.aborted === true) throw createMergeAbortedError(taskId); +} + +export function createMergeWriteFence(options: { + taskId: string; + signal?: AbortSignal; + recordAudit?: OrphanFenceAuditRecorder | ((category: MergeWriteCategory, interaction: MergeFenceInteraction, suppressedCount: number) => Promise | unknown); +}): MergeWriteFence { + let suppressedCount = 0; + let auditEmitted = false; + const emit = (category: MergeWriteCategory, interaction: MergeFenceInteraction): void => { + if (auditEmitted) return; + auditEmitted = true; + try { + const recorder = options.recordAudit; + const result = typeof recorder === "function" + ? recorder(category, interaction, suppressedCount) + : recorder?.recordRunAuditEvent({ + type: "merge:orphan-write-fenced", + taskId: options.taskId, + metadata: { taskId: options.taskId, category, interaction, suppressedCount }, + }); + void Promise.resolve(result).catch(() => undefined); + } catch { + // Observability must never replace the merge's original outcome. + } + }; + return { + taskId: options.taskId, + signal: options.signal, + get suppressedCount() { return suppressedCount; }, + isOrphaned: () => options.signal?.aborted === true, + async write(category: MergeWriteCategory, write: () => Promise | T): Promise { + if (options.signal?.aborted === true) { + suppressedCount += 1; + emit(category, "suppressed"); + return undefined; + } + return await write(); + }, + assertOwned(category: MergeWriteCategory = "finalization"): void { + if (options.signal?.aborted === true) { + emit(category, "rejected"); + throw createMergeAbortedError(options.taskId); + } + }, + }; +} diff --git a/packages/engine/src/merge/merger-ai.ts b/packages/engine/src/merge/merger-ai.ts index 51cf6d8b40..32f1aefcd5 100644 --- a/packages/engine/src/merge/merger-ai.ts +++ b/packages/engine/src/merge/merger-ai.ts @@ -66,6 +66,12 @@ import { selectUserCommentsForAgentContext } from "../agents/agent-user-comments import { resolveTaskWorkingBranch } from "../worktree/worktree-names.js"; import { resolveIntegrationBranch } from "./integration-branch.js"; import { advanceIntegrationBranchRef } from "./merger-ref-update-advance.js"; +import { + assertMergeGenerationOwned, + createMergeWriteFence, + isMergeAbortedError, + type MergeWriteFence, +} from "./merge-write-fence.js"; import { createResolvedAgentSession, resolveMergerSessionModel, resolveMergerThinkingLevel, resolveMergerFallbackThinkingLevel, resolveValidatorThinkingLevel } from "../agents/agent-session-helpers.js"; import { promptWithFallback } from "../pi.js"; import { AgentLogger } from "../agents/agent-logger.js"; @@ -141,7 +147,8 @@ export function writeTransientMergeStatus( signal: AbortSignal | undefined, status: string | null, ): Promise { - return signal?.aborted ? Promise.resolve(undefined) : store.updateTask(taskId, { status }).catch(() => undefined); + const fence = createMergeWriteFence({ taskId, signal }); + return fence.write("lifecycle", () => store.updateTask(taskId, { status }).catch(() => undefined)); } async function git(args: string[], cwd: string, opts: { timeout?: number } = {}): Promise { @@ -310,6 +317,7 @@ async function recoverApprovedPreexistingAiMergeWorktree( audit, resolveConflicts: stashResolveAgent, allowDirtyLocalCheckoutSync, + signal, }); if (land.outcome !== "advanced") return null; await log(`AI merge: recovered approved pre-existing clean-room commit ${short(selected.squashSha)} before pruning`); @@ -628,8 +636,9 @@ export async function landSquash(input: { * Resolved project settings default merger.allowDirtyLocalCheckoutSync to true for legacy operator UX, but this helper's parameter default intentionally remains false so direct/programmatic callers and tests fail closed unless they make the dirty-checkout sync policy explicit. */ allowDirtyLocalCheckoutSync?: boolean; + signal?: AbortSignal; }): Promise { - const { projectRootDir, mergeRoot, integrationBranch, tipSha, squashSha, taskId, audit, resolveConflicts, allowDirtyLocalCheckoutSync = false } = input; + const { projectRootDir, mergeRoot, integrationBranch, tipSha, squashSha, taskId, audit, resolveConflicts, allowDirtyLocalCheckoutSync = false, signal } = input; const emit = (outcome: LocalSyncOutcome, extra: Record = {}) => audit.git({ type: "merge:ai-local-sync", target: integrationBranch, metadata: { taskId, outcome, squashSha, ...extra } }).catch(() => undefined); @@ -637,6 +646,7 @@ export async function landSquash(input: { // Case B — target not checked out here: bare CAS ref advance. if (currentBranch !== integrationBranch) { + assertMergeGenerationOwned(signal, taskId); const adv = await advanceIntegrationBranchRef({ rootDir: mergeRoot, projectRootDir, integrationBranch, newSha: squashSha, expectedCurrentSha: tipSha, taskId, audit, @@ -688,6 +698,7 @@ export async function landSquash(input: { // The dirty state couldn't be stashed (e.g. untracked/tracked collision or a // stash hook failure). Don't risk `merge --ff-only` aborting/clobbering: // advance the ref atomically and leave the user's working tree as-is. + assertMergeGenerationOwned(signal, taskId); const adv = await advanceIntegrationBranchRef({ rootDir: mergeRoot, projectRootDir, integrationBranch, newSha: squashSha, expectedCurrentSha: tipSha, taskId, audit, @@ -704,6 +715,7 @@ export async function landSquash(input: { } // Fast-forward the checkout (and the branch ref) to the squash. + assertMergeGenerationOwned(signal, taskId); if (!(await gitOk(["merge", "--ff-only", squashSha], projectRootDir))) { if (stashed) await gitOk(["stash", "pop"], projectRootDir); // restore the user's edits return { outcome: "concurrent", localSync: "skipped-other-branch" }; @@ -1060,6 +1072,7 @@ export async function landOneRepo( projectRootDir: repoRootDir, mergeRoot, integrationBranch, tipSha, squashSha, taskId, audit, resolveConflicts: stashResolveAgent, allowDirtyLocalCheckoutSync: ctx.allowDirtyLocalCheckoutSync === true, + signal, }); if (landed.outcome === "concurrent") { if (advanceRetries < MAX_CONCURRENT_ADVANCE_RETRIES) { @@ -1320,18 +1333,27 @@ export async function runAiMerge( phase: "merge", }); + const fence = createMergeWriteFence({ + taskId, + signal: options.signal, + recordAudit: (category, interaction, suppressedCount) => store.recordRunAuditEvent?.({ + taskId, agentId: "merger", runId: `merge-${taskId}`, domain: "git", + mutationType: "merge:orphan-write-fenced", target: taskId, + metadata: { taskId, category, interaction, suppressedCount }, + }), + }); // Surface progress on the task detail (status pill) + the task log stream. const log = async (message: string): Promise => { - await store.logEntry(taskId, message, "AiMerge").catch(() => undefined); - await store.appendAgentLog(taskId, message, "status", undefined, "merger").catch(() => undefined); + await fence.write("log", () => store.logEntry(taskId, message, "AiMerge").catch(() => undefined)); + await fence.write("log", () => store.appendAgentLog(taskId, message, "status", undefined, "merger").catch(() => undefined)); }; /* FNXC:MergeReliability 2026-08-09-22:35: `raceMergeWithAbort` rejects only the race; a body can outlive the bounded settle latch while a successor generation owns this task. Its per-claim signal remains aborted, so suppressing this status-only write prevents it from re-stamping `merging` (issue #3395) or clearing a successor's - live stamp. Keep diagnostics unfenced and make this a no-op, not a throw, because finally paths - must preserve the original failure. + live stamp. Diagnostics use the same suppress-and-no-op policy rather than throwing, because + finally paths must preserve the original failure. */ const setStatus = (status: string | null): Promise => writeTransientMergeStatus(store, taskId, options.signal, status); @@ -1366,7 +1388,7 @@ export async function runAiMerge( target: branch, metadata: { taskId, kind: alreadyMerged ? "already-merged" : "never-executed" }, }); - return await finalizeTask(store, taskId, noOpResult(task, branch, alreadyMerged ? "already-merged" : "no-branch"), undefined, undefined, projectRootDir); + return await finalizeTask(store, taskId, noOpResult(task, branch, alreadyMerged ? "already-merged" : "no-branch"), undefined, undefined, projectRootDir, fence); } // The target branch must exist as a LOCAL ref to merge into it — surface a @@ -1416,9 +1438,13 @@ export async function runAiMerge( * FNXC:Lifecycle 2026-06-14-20:02: * FN-6461/FN-6455 requires the AI empty-merge lane to demote no-commits tasks whose skipped/incomplete steps outweigh done steps instead of finalizing the operational work as done. */ - await store.updateTask(taskId, { error: reason }); + await fence.write("lifecycle", () => store.updateTask(taskId, { error: reason })); + if (fence.isOrphaned()) return { + task, branch, merged: false, noOp: false, ok: true, reason, error: reason, + worktreeRemoved: false, branchDeleted: false, + }; const reboundColumn = await resolveFinalizeReboundColumn(store, taskId); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Finalize blocked (no-commits incomplete-work guard): ${reason} — moving back to ${reboundColumn} with progress preserved`, JSON.stringify({ @@ -1428,7 +1454,7 @@ export async function runAiMerge( integrationBranch, lane: "ai-empty-merge", }, null, 2), - ); + )); await audit.database({ type: "task:no-commits-finalize-blocked-incomplete-steps" as Parameters[0]["type"], target: taskId, @@ -1441,7 +1467,7 @@ export async function runAiMerge( lane: "ai-empty-merge", }, }); - await store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2]); + await fence.write("lifecycle", () => store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2])); return { task, branch, @@ -1476,13 +1502,17 @@ export async function runAiMerge( if (!landedProof) { const reason = "branch had no net changes vs main — work may have been reverted or lost; operator review required"; - await store.updateTask(taskId, { error: reason }); + await fence.write("lifecycle", () => store.updateTask(taskId, { error: reason })); + if (fence.isOrphaned()) return { + task, branch, merged: false, noOp: false, ok: true, reason, error: reason, + worktreeRemoved: false, branchDeleted: false, + }; const reboundColumn = await resolveFinalizeReboundColumn(store, taskId); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Finalize blocked (empty-merge no-landed-proof guard): ${reason} — moving back to ${reboundColumn} with progress preserved`, JSON.stringify({ branch, integrationBranch, lane: "ai-empty-merge", baseCommitSha: task.baseCommitSha }, null, 2), - ); + )); await audit.database({ type: "task:empty-merge-finalize-blocked-no-landed-proof" as Parameters[0]["type"], target: taskId, @@ -1495,7 +1525,7 @@ export async function runAiMerge( hadPriorNoOpProof: false, }, }); - await store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2]); + await fence.write("lifecycle", () => store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2])); return { task, branch, @@ -1542,9 +1572,13 @@ export async function runAiMerge( const executorVeto = evaluateNoOpFinalizeExecutorVeto({ mergeIsEmpty: true, task, memory: executorMemory, settings }); if (executorVeto.veto) { const vetoReason = executorVeto.reason ?? "overseer failed-executor no-op-finalize veto"; - await store.updateTask(taskId, { error: vetoReason }); + await fence.write("lifecycle", () => store.updateTask(taskId, { error: vetoReason })); + if (fence.isOrphaned()) return { + task, branch, merged: false, noOp: false, ok: true, reason: vetoReason, error: vetoReason, + worktreeRemoved: false, branchDeleted: false, + }; const reboundColumn = await resolveFinalizeReboundColumn(store, taskId); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Finalize blocked (overseer failed-executor veto): ${vetoReason} — moving back to ${reboundColumn} with progress preserved`, JSON.stringify({ @@ -1554,7 +1588,7 @@ export async function runAiMerge( integrationBranch, lane: "ai-empty-merge", }, null, 2), - ); + )); await audit.database({ type: "overseer:no-op-finalize-vetoed-failed-executor" as Parameters[0]["type"], target: taskId, @@ -1567,7 +1601,7 @@ export async function runAiMerge( lane: "ai-empty-merge", }, }); - await store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2]); + await fence.write("lifecycle", () => store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2])); return { task, branch, @@ -1582,13 +1616,13 @@ export async function runAiMerge( } await log(`AI merge: ${branch} had no net changes vs ${integrationBranch} — finalizing as no-op`); - const noOpFinalized = await finalizeMerged(store, projectRootDir, taskId, task, branch, integrationBranch, landResult.tipSha, audit, log, { empty: true }, mergeTarget, groupRouting, options.syncGroupPr); - await runPushAfterMergeStep({ store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result: noOpFinalized }); + const noOpFinalized = await finalizeMerged(store, projectRootDir, taskId, task, branch, integrationBranch, landResult.tipSha, audit, log, { empty: true }, mergeTarget, groupRouting, options.syncGroupPr, fence); + await runPushAfterMergeStep({ store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result: noOpFinalized, fence }); return noOpFinalized; } - const finalized = await finalizeMerged(store, projectRootDir, taskId, task, branch, integrationBranch, landResult.squashSha, audit, log, { empty: false }, mergeTarget, groupRouting, options.syncGroupPr); - await runPushAfterMergeStep({ store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result: finalized }); + const finalized = await finalizeMerged(store, projectRootDir, taskId, task, branch, integrationBranch, landResult.squashSha, audit, log, { empty: false }, mergeTarget, groupRouting, options.syncGroupPr, fence); + await runPushAfterMergeStep({ store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result: finalized, fence }); return finalized; } @@ -1612,8 +1646,9 @@ async function runPushAfterMergeStep(input: { log: (message: string) => Promise; options: MergerOptions; result: MergeResult; + fence: MergeWriteFence; }): Promise { - const { store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result } = input; + const { store, projectRootDir, taskId, settings, integrationBranch, audit, log, options, result, fence } = input; if (settings.pushAfterMerge !== true || settings.mergeStrategy === "pull-request") return; try { const pushOutcome = await pushAfterMergeToRemote({ @@ -1627,6 +1662,7 @@ async function runPushAfterMergeStep(input: { signal: options.signal, onAgentText: options.onAgentText, onSession: options.onSession, + fence, }); result.pushedToRemote = pushOutcome.pushed; if (pushOutcome.error) result.pushError = pushOutcome.error; @@ -1653,9 +1689,9 @@ async function runPushAfterMergeStep(input: { const details = latest?.mergeDetails; if (details?.commitSha && details.commitSha !== pushOutcome.rebasedSha) { const { filesChanged, insertions, deletions } = await captureSingleCommitLandedMetadata(projectRootDir, pushOutcome.rebasedSha); - await store.updateTask(taskId, { + await fence.write("lifecycle", () => store.updateTask(taskId, { mergeDetails: { ...details, commitSha: pushOutcome.rebasedSha, filesChanged, insertions, deletions }, - }); + })); } } catch (refreshErr: unknown) { aiMergeLog.warn(`${taskId}: post-push mergeDetails refresh failed: ${getErrorMessage(refreshErr)}`); @@ -1663,11 +1699,11 @@ async function runPushAfterMergeStep(input: { } } else { aiMergeLog.warn(`${taskId}: push to remote failed: ${pushOutcome.error}`); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Push to remote failed after merge — task finalized anyway; local ${integrationBranch} may diverge from ${pushOutcome.remote ?? "origin"}: ${pushOutcome.error}`, "PushToRemoteFailed", - ).catch(() => undefined); + ).catch(() => undefined)); } } catch (err: unknown) { if (err instanceof Error && err.name === "MergeAbortedError") { @@ -1684,7 +1720,7 @@ async function runPushAfterMergeStep(input: { target: taskId, metadata: { integrationBranch, remote: settings.pushRemote ?? "origin", outcome: "aborted" }, }).catch(() => undefined); - await store.logEntry(taskId, message, "PushToRemoteFailed").catch(() => undefined); + await fence.write("log", () => store.logEntry(taskId, message, "PushToRemoteFailed").catch(() => undefined)); return; } const message = getErrorMessage(err); @@ -1696,11 +1732,11 @@ async function runPushAfterMergeStep(input: { target: taskId, metadata: { integrationBranch, remote: settings.pushRemote ?? "origin", outcome: "failed", stderrPreview: message.slice(0, 500) }, }).catch(() => undefined); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Push to remote threw after merge — task finalized anyway; local ${integrationBranch} may diverge from origin: ${message}`, "PushToRemoteFailed", - ).catch(() => undefined); + ).catch(() => undefined)); } } @@ -1873,9 +1909,18 @@ export async function landWorkspaceTask( taskId, phase: "merge", }); + const fence = createMergeWriteFence({ + taskId, + signal: options.signal, + recordAudit: (category, interaction, suppressedCount) => store.recordRunAuditEvent?.({ + taskId, agentId: "merger", runId: `merge-${taskId}`, domain: "git", + mutationType: "merge:orphan-write-fenced", target: taskId, + metadata: { taskId, category, interaction, suppressedCount }, + }), + }); const log = async (message: string): Promise => { - await store.logEntry(taskId, message, "AiMerge").catch(() => undefined); - await store.appendAgentLog(taskId, message, "status", undefined, "merger").catch(() => undefined); + await fence.write("log", () => store.logEntry(taskId, message, "AiMerge").catch(() => undefined)); + await fence.write("log", () => store.appendAgentLog(taskId, message, "status", undefined, "merger").catch(() => undefined)); }; /* FNXC:MergeReliability 2026-08-09-22:35: @@ -2046,6 +2091,7 @@ export async function landWorkspaceTask( repos.push({ repo: repoRel, repoRootDir, integrationBranch, branch: entry.branch, status: "empty" }); } } catch (err: unknown) { + if (isMergeAbortedError(err)) throw err; // A WorkspacePartialLandError from the persist-failure window above must PROPAGATE // (the engine parks/retries). The outer try/finally below resets status first (A3). if (err instanceof WorkspacePartialLandError) throw err; @@ -2113,22 +2159,23 @@ export async function landWorkspaceTask( if (hasRevertedEmptyRepo) { const reason = "branch had no net changes vs main — work may have been reverted or lost; operator review required"; - await store.updateTask(taskId, { error: reason }); + await fence.write("lifecycle", () => store.updateTask(taskId, { error: reason })); + if (fence.isOrphaned()) return { taskId, repos, allLanded, finalized: false }; const reboundColumn = await resolveFinalizeReboundColumn(store, taskId); - await store.logEntry( + await fence.write("log", () => store.logEntry( taskId, `Finalize blocked (empty-merge no-landed-proof guard, workspace): ${reason} — moving back to ${reboundColumn} with progress preserved`, JSON.stringify({ lane: "ai-empty-merge-workspace", repoCount: repos.length, landedCount, repos: repos.map((r) => r.repo) }, null, 2), - ).catch(() => undefined); + ).catch(() => undefined)); await audit.database({ type: "task:empty-merge-finalize-blocked-no-landed-proof" as Parameters[0]["type"], target: taskId, metadata: { reason, lane: "ai-empty-merge-workspace", repoCount: repos.length, landedCount, hadPriorNoOpProof: false }, }).catch(() => undefined); - await store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2]); + await fence.write("lifecycle", () => store.moveTask(taskId, reboundColumn, { preserveProgress: true, moveSource: "engine" } as Parameters[2])); return { taskId, repos, allLanded, finalized: false }; } - const finalized = await finalizeWorkspaceTask(store, taskId, task, repos); + const finalized = await finalizeWorkspaceTask(store, taskId, task, repos, fence); return { taskId, repos, allLanded, finalized }; } return { taskId, repos, allLanded, finalized: false }; @@ -2183,6 +2230,7 @@ async function finalizeWorkspaceTask( taskId: string, task: Task, repos: WorkspaceRepoLandResult[], + fence?: MergeWriteFence, ): Promise { const landed = repos.filter((r) => r.status === "landed" && r.landedSha); const workspaceLandedShas: Record = {}; @@ -2211,6 +2259,7 @@ async function finalizeWorkspaceTask( ...(anyLanded ? { workspaceLandedShas } : {}), mergeConfirmed: anyLanded, }; + fence?.assertOwned("finalization"); await store.updateTask(taskId, { mergeDetails }); task.mergeDetails = mergeDetails; @@ -2226,8 +2275,9 @@ async function finalizeWorkspaceTask( worktreeRemoved: false, branchDeleted: false, }; - await store.logEntry(taskId, `AI merge (workspace): all ${repos.length} sub-repo(s) landed — task → done`, "AiMerge").catch(() => undefined); - await finalizeTask(store, taskId, result); + await fence?.write("log", () => store.logEntry(taskId, `AI merge (workspace): all ${repos.length} sub-repo(s) landed — task → done`, "AiMerge").catch(() => undefined)); + fence?.assertOwned("finalization"); + await finalizeTask(store, taskId, result, undefined, undefined, undefined, fence); return true; } @@ -2363,8 +2413,12 @@ export async function pushAfterMergeToRemote(input: { signal?: AbortSignal; onAgentText?: (delta: string) => void; onSession?: (session: { dispose: () => void }) => void; + fence?: MergeWriteFence; }): Promise<{ pushed: boolean; remote?: string; targetBranch?: string; refAdvanced?: boolean; rebasedSha?: string; error?: string }> { const { store, projectRootDir, taskId, settings, integrationBranch, audit, log, signal } = input; + // FNXC:MergeReliability 2026-08-11-22:17: Post-push recovery diagnostics can outlive + // cancellation, so direct callers construct the same per-generation write fence. + const fence = input.fence ?? createMergeWriteFence({ taskId, signal }); let remote: string; let targetBranch: string; @@ -2414,7 +2468,7 @@ export async function pushAfterMergeToRemote(input: { target: taskId, metadata: { taskId, remote, recoveryBranch, sha: localSha, outcome }, }).catch(() => undefined); - await store.logEntry(taskId, logMessage, logAction).catch(() => undefined); + await fence.write("log", () => store.logEntry(taskId, logMessage, logAction).catch(() => undefined)); }; try { await git(["push", "--force", remote, `${localSha}:${recoveryRef}`], projectRootDir, { timeout: 120_000 }); @@ -2500,6 +2554,7 @@ export async function pushAfterMergeToRemote(input: { if (!rebasedSha || rebasedSha === localSha) { return { pushed: true, remote, targetBranch }; } + assertMergeGenerationOwned(signal, taskId); const adv = await advanceIntegrationBranchRef({ rootDir: canonicalPushRoot, projectRootDir, @@ -2568,6 +2623,7 @@ async function finalizeMerged( mergeTarget?: MergeTargetResolution, groupRouting?: BranchGroupMergeRouting | null, syncGroupPr?: SyncGroupPrFn, + fence?: MergeWriteFence, ): Promise { /* FNXC:BranchGroupCompletion 2026-07-04-00:00: @@ -2601,10 +2657,12 @@ async function finalizeMerged( ...mergeTargetPatch, }; modifiedFiles = landedFiles.length > 0 ? landedFiles : undefined; + fence?.assertOwned("finalization"); await store.updateTask(taskId, { mergeDetails, modifiedFiles }); task.mergeDetails = mergeDetails; task.modifiedFiles = modifiedFiles; if (task.lineageId && typeof (store as Partial).upsertTaskCommitAssociation === "function") { + fence?.assertOwned("finalization"); await store.upsertTaskCommitAssociation({ taskLineageId: task.lineageId, taskIdSnapshot: task.id, @@ -2619,6 +2677,7 @@ async function finalizeMerged( } } else if (mergeTargetPatch) { mergeDetails = { ...(task.mergeDetails ?? {}), ...mergeTargetPatch }; + fence?.assertOwned("finalization"); await store.updateTask(taskId, { mergeDetails }); task.mergeDetails = mergeDetails; } @@ -2626,6 +2685,7 @@ async function finalizeMerged( // NEVER delete the integration branch itself — a task whose branch name // coincides with the target (or merges into its own branch) must not have the // just-advanced integration ref force-deleted out from under it. + fence?.assertOwned("finalization"); if (branch !== integrationBranch && await gitOk(["branch", "-D", branch], projectRootDir)) { branchDeleted = true; await audit.git({ type: "branch:delete", target: branch, metadata: { taskId, force: true } }).catch(() => undefined); @@ -2633,7 +2693,9 @@ async function finalizeMerged( // Remove the task's own worktree if it still exists. let worktreeRemoved = false; if (task.worktree) { + fence?.assertOwned("finalization"); worktreeRemoved = await gitOk(["worktree", "remove", "--force", task.worktree], projectRootDir); + fence?.assertOwned("finalization"); await store.updateTask(taskId, { worktree: null }).catch(() => undefined); } @@ -2655,27 +2717,27 @@ async function finalizeMerged( }; await audit.git({ type: "merge:ai-landed", target: integrationBranch, metadata: { taskId, landedSha, empty: opts.empty } }).catch(() => undefined); await log(opts.empty ? `AI merge: finalized ${taskId} (no-op), finalizing task row` : `AI merge: landed ${short(landedSha)}, finalizing task row`); - const finalized = await finalizeTask(store, taskId, result, audit, log, projectRootDir); - await log(opts.empty ? `AI merge: finalized ${taskId} (no-op) → done` : `AI merge: landed ${short(landedSha)}, task → done`); /* - FNXC:BranchGroupCompletion 2026-07-04-00:00: - FN-7532: mirror the legacy merger.ts executeMergeAttempt's shared-group landing bookkeeping so a - member merged via the (now sole) runAiMerge path also updates the group row (worktreePath/status) - and pushes the up-to-date checklist body onto any already-open managed group PR. Both are - best-effort — a failure here must never fail an otherwise-successful merge. + FNXC:MergeReliability 2026-08-11-21:39: + Group bookkeeping is a finalization writer, so it must finish before the done-column move and + `task:merged` announcement. An abort here rejects before external consumers see an announced + merge whose managed-group state is still incomplete; each adjacent writer keeps its own fence. */ if (groupRouting) { try { + fence?.assertOwned("finalization"); await Promise.resolve((store as { recordBranchGroupMemberLanded?: TaskStore["recordBranchGroupMemberLanded"] }).recordBranchGroupMemberLanded?.(groupRouting.branchGroup.id, { worktreePath: task.worktree ?? null, status: "open", })); - } catch { + } catch (err) { + if (isMergeAbortedError(err)) throw err; // best-effort persistence } if (syncGroupPr) { try { + fence?.assertOwned("finalization"); await syncGroupPrOnLanding({ store, groupId: groupRouting.branchGroup.id, @@ -2683,6 +2745,7 @@ async function finalizeMerged( syncGroupPr, }); } catch (err) { + if (isMergeAbortedError(err)) throw err; try { store.recordRunAuditEvent?.({ taskId, @@ -2700,6 +2763,9 @@ async function finalizeMerged( } } + fence?.assertOwned("finalization"); + const finalized = await finalizeTask(store, taskId, result, audit, log, projectRootDir, fence); + await log(opts.empty ? `AI merge: finalized ${taskId} (no-op) → done` : `AI merge: landed ${short(landedSha)}, task → done`); return finalized; } @@ -2711,6 +2777,7 @@ async function finalizeTask( audit?: RunAuditor, log?: (message: string) => Promise, rootDir?: string, + fence?: MergeWriteFence, ): Promise { const finalization = await finalizeProvenAutoMergeTask({ store, @@ -2722,6 +2789,7 @@ async function finalizeTask( source: "direct-ai-merge", rootDir, log, + fence, }); if (finalization.outcome === "blocked") { throw new Error(`AI merge finalization blocked for ${taskId}: ${finalization.reason ?? "unknown"}`); @@ -2730,14 +2798,11 @@ async function finalizeTask( throw new Error(`AI merge finalization could not find task ${taskId}`); } result.task = finalization.task; + fence?.assertOwned("finalization"); store.emit("task:merged", result); return result; } function throwIfAborted(signal: AbortSignal | undefined, taskId: string): void { - if (signal?.aborted) { - const err = new Error(`AI merge aborted for ${taskId}`); - err.name = "MergeAbortedError"; - throw err; - } + assertMergeGenerationOwned(signal, taskId); }