fix(contracts): normalize rejected cleanup in durable resource disposal

Merged
YU
1h ago
+82-16

Summary

Fixes #4334.

createRetryableCleanup's [Symbol.asyncDispose] adapter let a rejection from the cleanup operation escape raw, which breaks the ADR 0019 §5 contract (resolve only on confirmed cleanup, otherwise reject with a normalized error). The adapter now treats a rejected cleanup as cleanup-pending / cleanup-unconfirmed (message taken from the rejection) and hands the original rejection to the facet's assertConfirmed, which passes it as the AppError cause. Retry is unchanged.

8 files: the shared adapter, the four assertConfirmed facets (app-log, screen recording, audio probe, perf) gaining a cause argument, and tests. Two existing tests pinned the raw rejection and now assert the normalized error with the original as cause. No scope expansion.

Validation

Base 093341a, head f8bc385.

  • New test app-log async disposal normalizes a rejected cleanup and keeps its cause: fails on base (raw Error: transport closed escapes), passes on head.
  • pnpm exec vitest run --project unit-core packages/contracts packages/capture-kit src/platform-runtime-perf-capture-host: 234 files / 1893 tests passed.
  • pnpm check:affected --run --head HEAD: format, lint passed; typecheck failed only on declarations from a node_modules outside the repo (@types/jsdom, terser) locally; all workspace projects built clean.
  • pnpm check:layering, pnpm check:fallow, pnpm build: passed.

Risk: a cleanup that rejects with an already-typed AppError (for example ownership-fence-lost) is now reported as cleanup-unconfirmed, with the typed error kept as cause. forceCleanup() itself still rejects raw; the issue scopes only disposal. No independent reviewer; I re-read the full diff.

🤖 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.