[GH-ISSUE #5052] [Bug] Signature snapshot root cleanup can precede failed descriptor release #3097

Closed
opened 2026-10-31 00:36:37 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @morluto on GitHub (Oct 18, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/5052

Before submitting

  • Searched open/closed signature and descriptor/root cleanup issues and related PRs. #4734 concerns stale error ownership after release; #4735 concerns finalization cancellation.
  • Evidence uses authored inert files, exact owned descriptors and controlled cleanup failures; no credentials or private target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

At main 8307ea3717b9bbc6b5cf83d96a0f73d47708e586, signature inspection removes its private snapshot directory even while its snapshot descriptor remains open after close fails. The outer root lease and child descriptor leases have no dependency relation. A retry can likewise delete the root while the descriptor still fails cleanup.

Reproduction

Added one regression to src/native/SignatureTargetBinding.test.ts and executed npm run test:local -- src/native/SignatureTargetBinding.test.ts using Node 24.18.0 on macOS arm64: 1 failed, 24 passed.

The test creates an actual tiny input and private root, deliberately gives the input a mismatching registered digest so inspection never runs, and denies close of the exact snapshot OwnedFileHandle. It wraps the real root removal with a call counter. After production withSignatureTarget returns a cleanup failure, the counter is 1 although that descriptor still has no successful close. The regression expected no root removal. Teardown restores and closes the exact handle.

The existing test named tracks mixed snapshot descriptor and root cleanup through partial release and retry also explicitly expects the root to be gone while its snapshot descriptor lease is still pending; its green result protects the incorrect ordering.

Expected behavior

Follow docs/tool-design.md#own-the-lifecycle: release child descriptors before removing their containing root, and retain a composite owner through either failure. Block replacement admission while either stage is unresolved. Retry only live children, bind every error to exact owners, and preserve physical primary failure/cancellation facts.

Impact and scope

Verified at the production signature helper using real file admission/copy/hash and real directory removal, with injected descriptor close failure. No codesign/LLDB/target launch, ordinary OS close-failure prevalence, Windows/Linux execution, or public CLI/MCP fault-timing reproduction is claimed. External-user frequency: NOT_ENOUGH_INFO.

REA version / target / interface

Source 8307ea3717b9bbc6b5cf83d96a0f73d47708e586, package 6.7.0. Authored inert bytes, synthetic Mach-O metadata. Provider/helper boundary.

Earliest failure stage / relevant evidence

Cleanup, cancellation, or lifecycle. bindSignatureTarget retains failed descriptor leases in the caller's ArtifactResourceScope; withSignatureTarget then independently calls resources.release(rootOwner). The existing flat owner set preserves individual capability identity but cannot express containment ordering. ArtifactAnalysisResources and the ADB runtime-root lease provide existing dependency-ordering precedents; no new target authority is needed.

Relevant executed assertion: expected root-close counter 0, received 1. Local baseline transcript: .cache/codebase-audit/signature-root-order-baseline.log. A fix must also replace the incorrect mixed-outcome test expectation and retain cancellation/finalization coverage from #4734/#4735.

Originally created by @morluto on GitHub (Oct 18, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5052 ### Before submitting - [x] Searched open/closed signature and descriptor/root cleanup issues and related PRs. #4734 concerns stale error ownership after release; #4735 concerns finalization cancellation. - [x] Evidence uses authored inert files, exact owned descriptors and controlled cleanup failures; no credentials or private target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior At main `8307ea3717b9bbc6b5cf83d96a0f73d47708e586`, signature inspection removes its private snapshot directory even while its snapshot descriptor remains open after close fails. The outer root lease and child descriptor leases have no dependency relation. A retry can likewise delete the root while the descriptor still fails cleanup. ### Reproduction Added one regression to `src/native/SignatureTargetBinding.test.ts` and executed `npm run test:local -- src/native/SignatureTargetBinding.test.ts` using Node 24.18.0 on macOS arm64: **1 failed, 24 passed**. The test creates an actual tiny input and private root, deliberately gives the input a mismatching registered digest so inspection never runs, and denies close of the exact snapshot `OwnedFileHandle`. It wraps the real root removal with a call counter. After production `withSignatureTarget` returns a cleanup failure, the counter is 1 although that descriptor still has no successful close. The regression expected no root removal. Teardown restores and closes the exact handle. The existing test named `tracks mixed snapshot descriptor and root cleanup through partial release and retry` also explicitly expects the root to be gone while its snapshot descriptor lease is still pending; its green result protects the incorrect ordering. ### Expected behavior Follow `docs/tool-design.md#own-the-lifecycle`: release child descriptors before removing their containing root, and retain a composite owner through either failure. Block replacement admission while either stage is unresolved. Retry only live children, bind every error to exact owners, and preserve physical primary failure/cancellation facts. ### Impact and scope Verified at the production signature helper using real file admission/copy/hash and real directory removal, with injected descriptor close failure. No codesign/LLDB/target launch, ordinary OS close-failure prevalence, Windows/Linux execution, or public CLI/MCP fault-timing reproduction is claimed. External-user frequency: NOT_ENOUGH_INFO. ### REA version / target / interface Source `8307ea3717b9bbc6b5cf83d96a0f73d47708e586`, package 6.7.0. Authored inert bytes, synthetic Mach-O metadata. Provider/helper boundary. ### Earliest failure stage / relevant evidence Cleanup, cancellation, or lifecycle. `bindSignatureTarget` retains failed descriptor leases in the caller's `ArtifactResourceScope`; `withSignatureTarget` then independently calls `resources.release(rootOwner)`. The existing flat owner set preserves individual capability identity but cannot express containment ordering. `ArtifactAnalysisResources` and the ADB runtime-root lease provide existing dependency-ordering precedents; no new target authority is needed. Relevant executed assertion: expected root-close counter 0, received 1. Local baseline transcript: `.cache/codebase-audit/signature-root-order-baseline.log`. A fix must also replace the incorrect mixed-outcome test expectation and retain cancellation/finalization coverage from #4734/#4735.
bsakaguchi 2026-10-31 00:36:37 +00:00
  • closed this issue
  • added the
    triage
    bug
    labels
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3097
No description provided.