Skip to content

fix(daemon): dispose late durable capture authorities - #2107

Merged
thymikee merged 3 commits into
mainfrom
fix/durable-capture-late-reattach-cleanup
Aug 28, 2026
Merged

fix(daemon): dispose late durable capture authorities#2107
thymikee merged 3 commits into
mainfrom
fix/durable-capture-late-reattach-cleanup

Conversation

@thymikee

@thymikee thymikee commented Aug 27, 2026

Copy link
Copy Markdown
Member

Summary

Dispose late successful durable-capture reattachments when deadline or request cancellation wins the recovery race. Active reattachments dispose the live handle before the control; non-active outcomes dispose the control only. Both the detached race cleanup and the actual aborted-scope cleanup keep control disposal unconditional, while cleanup diagnostics cannot replace the primary timeout or cancellation error.

Closes #2099
Supersedes #2101

Validation

  • pnpm vitest run --project unit-core src/daemon/__tests__/durable-capture-recovery-authority.test.ts: 10 tests passed.
  • Red proof against both pre-fix paths: the outer late-resolution path and the real aborted-scope path left control undisposed when active-handle cleanup and its reporter failed; both regressions pass with this fix.
  • pnpm check:affected --run: format, lint, typecheck, layering, Fallow, and build passed. The related Vitest lane ran 172 files / 939 tests successfully but reported unrelated Apple EnvironmentTeardownError rejections during teardown on two local runs; exact-head GitHub lanes remain authoritative.
  • Touched files: 2. No docs or skills changed.

Residual timing risk is limited to the inherently detached late-cleanup continuation; native/device evidence remains GitHub-authoritative.

@github-actions

github-actions Bot commented Aug 27, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 2.48 MB 2.48 MB -216 B
JS gzip 834.3 kB 834.1 kB -131 B
npm tarball 957.5 kB 957.4 kB -117 B
npm unpacked 3.32 MB 3.32 MB -216 B

npm unpacked components

Component Base Current Diff
JS / dist source 2.64 MB 2.63 MB -216 B
Apple runner source/project 581.1 kB 581.1 kB 0 B
macOS helper source 54.8 kB 54.8 kB 0 B
Android helper artifacts 0 B 0 B 0 B
Other package files 45.3 kB 45.3 kB 0 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 28.5 ms 28.7 ms +0.2 ms
CLI --help 80.6 ms 78.7 ms -1.8 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/session2.js +268 B +105 B
dist/src/sdk-batch-runner.js -291 B -77 B
dist/src/app-inventory-contract.js -2 B -14 B
dist/src/perf-runtime-plan.js -2 B +8 B
dist/src/runtime4.js 0 B +6 B

Top changed packed files

Packed file Base Current Diff
dist/src/daemon-process.js 1.3 kB 642 B -617 B
dist/src/screen-recording-resource-recovery.js 19.3 kB 19.8 kB +509 B
dist/src/sdk-batch-runner.js 80.9 kB 80.6 kB -291 B
dist/src/session2.js 216.3 kB 216.6 kB +268 B
dist/src/daemon.js 3.7 kB 3.6 kB -83 B
dist/src/runtime.js 64.0 kB 64.0 kB +7 B
dist/src/internal/daemon.js 110.0 kB 110.0 kB -5 B
dist/src/app-inventory-contract.js 44.2 kB 44.2 kB -2 B
dist/src/perf-runtime-plan.js 63.6 kB 63.6 kB -2 B

@thymikee

Copy link
Copy Markdown
Member Author

Not ready. The terminal catch fixes #2101's unhandled rejection, but an active late authority can still leak control: if handle disposal fails and onLateCleanupFailure throws, cleanup exits before control disposal. Contain reporter failure per resource (or continue in finally) and plant active-handle-fails + reporter-throws proof that control still disposes and the primary error remains authoritative. Declare #2107 as replacement/supersession for duplicate #2101, record practical late-live-handle cleanup evidence, and finish exact-head CI.

@thymikee

Copy link
Copy Markdown
Member Author

P1 — control disposal is still skipped on the real aborted-scope path. The new finally protects only disposeLateRecoveryAuthority, but deadline/cancellation aborts scope.signal; after reattach resolves active, acquireRecoveryAuthority's throwIfAborted enters its catch. That catch disposes the handle first, and a throwing onLateCleanupFailure then skips control disposal because this catch still has no finally. The added regression mocks AbortSignal.any to a never-aborted signal, so it forces the outer path and cannot prove the real invariant. Add a planted-red test with an actually aborted scope, late active reattach, handle cleanup failure, and throwing reporter; then make control disposal unconditional in acquireRecoveryAuthority as well. #2101 remains open and superseded and should be marked obsolete once this replacement is accepted.

@thymikee

Copy link
Copy Markdown
Member Author

Re-reviewed 10c6e966aa: the prior aborted-scope leak is resolved. acquireRecoveryAuthority now guarantees control disposal in finally even when late active-handle cleanup reporting throws, and the new regression uses a real aborted combined scope, preserves the cancellation as primary, and proves control disposal. All exact-head checks are green. Code review clean; ready for human merge.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Aug 27, 2026
@thymikee
thymikee merged commit 782f4e2 into main Aug 28, 2026
18 checks passed
@thymikee
thymikee deleted the fix/durable-capture-late-reattach-cleanup branch August 28, 2026 05:47
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-28 05:47 UTC

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

daemon: durable-capture recovery Promise.race drops a late successful reattach, orphaning the handle

1 participant