[GH-ISSUE #5546] [Bug] inspect-artifact discards root observations when a DMG is already mounted #3378

Closed
opened 2026-10-31 00:39:43 +00:00 by bsakaguchi · 3 comments
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

On macOS, node scripts/rea.mjs inspect-artifact "$WORK/fixture.dmg" --json exits 1 when the valid DMG is already mounted read-only. It returns artifact_operation_failed, category unavailable, and retryable: false, with no root inventory Evidence or partial observation. The top-level message and remediation suggest checking artifact support and required tools. The nested diagnostic correctly retains hdiutil: attach failed - Resource busy.

After detaching the fixture's own device, the same command on the same bytes exits 0 with complete container-entry inventory. One mounted attempt and one detached control were independently reproduced.

Reproduction

From the built source checkout on macOS, the verifier ran this sequence of inputs and options using a self-created one-file image and isolated writable directories:

WORK=$(mktemp -d)
DEVICE=''
trap 'if [ -n "$DEVICE" ]; then hdiutil detach "$DEVICE"; fi' EXIT
mkdir -p "$WORK/src" "$WORK/mnt" "$WORK/home" "$WORK/config" \
  "$WORK/data" "$WORK/cache" "$WORK/state" "$WORK/temp"
printf 'fixture\n' > "$WORK/src/file.txt"
hdiutil create -quiet -srcfolder "$WORK/src" -volname VerifyBusyFixture \
  -size 20m -format UDZO "$WORK/fixture.dmg"
hdiutil attach -readonly -nobrowse -plist -mountroot "$WORK/mnt" \
  "$WORK/fixture.dmg" > "$WORK/attach.plist"
DEVICE=$(python3 -c 'import plistlib,sys; print(plistlib.load(open(sys.argv[1], "rb"))["system-entities"][0]["dev-entry"])' "$WORK/attach.plist")
run_rea() {
  env HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" \
    XDG_DATA_HOME="$WORK/data" XDG_CACHE_HOME="$WORK/cache" \
    XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/temp" \
    node scripts/rea.mjs inspect-artifact "$WORK/fixture.dmg" --json
}
run_rea > "$WORK/busy.json" 2> "$WORK/busy.stderr"
printf 'busy exit: %s\n' "$?"
cat "$WORK/busy.json"
hdiutil detach "$DEVICE" && DEVICE=''
run_rea > "$WORK/control.json" 2> "$WORK/control.stderr"
printf 'control exit: %s\n' "$?"

The mounted call exited 1; the detached control exited 0. The verifier then checked hdiutil info -plist, confirmed no attachment remained for this fixture, and removed the generated image and source fixture. No other owner's image was detached. No external analysis engine or persistent database was needed.

Expected behavior

The artifact guide, .agents/skills/reverse-engineer-anything/references/native-and-artifacts.md:315-319 at the tested revision, documents read-only DMG traversal on supported macOS hosts and explicit mount-failure limitations without claiming unobserved children. src/contracts/artifactToolContracts.ts:83 includes root/integrity observations, limitations and unexplored branches in inspect_artifact.

Retain available root identity with truthful unknown or incomplete child coverage and a busy-attachment limitation. A safely obtained complete inventory also satisfies the contract. Preserve mount ownership boundaries and give recovery guidance that reflects the busy attachment.

Impact and scope

This bounded mounted-image case prevents supported inspection from returning available root observations and gives misleading recovery advice. Detaching the verifier-owned mount restored complete inventory of the same image, so callers able to release their existing mount have a demonstrated workaround. Behavior while another application must keep a mount active was not tested. No data corruption or security boundary crossing was observed; broader frequency is unknown.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

Node v26.12.0; macOS 27.0.1, build 26A434; arm64. Working system hdiutil; no Hopper or Ghidra involved.

Target and artifact context

A self-created read-only UDZO DMG containing one text file, already attached read-only under a fixture-owned mount root.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Selected fields from the mounted CLI result, with command diagnostics reduced to exclude local paths:

code: artifact_operation_failed
category: unavailable
retryable: false
message: Artifact processing is unavailable for the current target or host. Check artifact support and required tools.
remediation.action: Artifact processing is unavailable for the current target or host. Check artifact support and required tools.
details.operation: inspect_artifact
details.reason: unavailable

details.detail retains an hdiutil attach failure with exit code 1 and hdiutil: attach failed - Resource busy. No root inventory Evidence or partial observation appears in this result.

The detached control's inventory substep reports coverage: {"scope":"container-entries","status":"complete"} and root format dmg. The outer inspection reports complete-within-substeps.

Typed outcome or recovery expected

Return the available root observations and an explicit busy-attachment child-traversal limitation with truthful coverage, or a safely obtained complete inventory. Advise retry after the caller can release its mount rather than directing it toward host support or required tools. Do not detach or borrow another owner's mount.

Analysis or suspected cause

Source-derived reasoning at the tested revision: src/artifacts/inventory/scanCanonical.ts:69-80,104-135 classifies and hashes the root before traversal, but propagates a traversal failure without constructing an inventory snapshot. src/artifacts/NativeDmgArtifactReader.ts:759-768 maps numeric non-verify command failures to unavailable; verification has separate classification. src/domain/analysisErrorPresentation.ts:439-440 supplies the generic host/tool message. The original Resource busy diagnostic is preserved, so the defect concerns loss of available observations and misleading top-level recovery guidance.

Additional context

Prepared with AI assistance and independently reproduced through the public CLI. The submitter checked current origin/main at 57e76599c035a420181b665f4f2a3edad71628fb; subsequent changes in the cited files do not alter DMG scanning, attachment classification or this unavailable message. The latest published release is still 6.7.0. Related PRs #3290 and #4146 concern verification diagnostics, #679 concerns mount-root and detach ownership, and #5230 concerns unreadable directory entries; none reports this already-mounted-image failure.

NOT_ENOUGH_INFO: the last known good version is unknown. MCP behavior, unsupported-host/missing-tool outcomes and integrity-failure handling were not exercised by this reproduction.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5546 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Observed behavior On macOS, `node scripts/rea.mjs inspect-artifact "$WORK/fixture.dmg" --json` exits 1 when the valid DMG is already mounted read-only. It returns `artifact_operation_failed`, category `unavailable`, and `retryable: false`, with no root inventory Evidence or partial observation. The top-level message and remediation suggest checking artifact support and required tools. The nested diagnostic correctly retains `hdiutil: attach failed - Resource busy`. After detaching the fixture's own device, the same command on the same bytes exits 0 with complete container-entry inventory. One mounted attempt and one detached control were independently reproduced. ### Reproduction From the built source checkout on macOS, the verifier ran this sequence of inputs and options using a self-created one-file image and isolated writable directories: ```sh WORK=$(mktemp -d) DEVICE='' trap 'if [ -n "$DEVICE" ]; then hdiutil detach "$DEVICE"; fi' EXIT mkdir -p "$WORK/src" "$WORK/mnt" "$WORK/home" "$WORK/config" \ "$WORK/data" "$WORK/cache" "$WORK/state" "$WORK/temp" printf 'fixture\n' > "$WORK/src/file.txt" hdiutil create -quiet -srcfolder "$WORK/src" -volname VerifyBusyFixture \ -size 20m -format UDZO "$WORK/fixture.dmg" hdiutil attach -readonly -nobrowse -plist -mountroot "$WORK/mnt" \ "$WORK/fixture.dmg" > "$WORK/attach.plist" DEVICE=$(python3 -c 'import plistlib,sys; print(plistlib.load(open(sys.argv[1], "rb"))["system-entities"][0]["dev-entry"])' "$WORK/attach.plist") run_rea() { env HOME="$WORK/home" XDG_CONFIG_HOME="$WORK/config" \ XDG_DATA_HOME="$WORK/data" XDG_CACHE_HOME="$WORK/cache" \ XDG_STATE_HOME="$WORK/state" TMPDIR="$WORK/temp" \ node scripts/rea.mjs inspect-artifact "$WORK/fixture.dmg" --json } run_rea > "$WORK/busy.json" 2> "$WORK/busy.stderr" printf 'busy exit: %s\n' "$?" cat "$WORK/busy.json" hdiutil detach "$DEVICE" && DEVICE='' run_rea > "$WORK/control.json" 2> "$WORK/control.stderr" printf 'control exit: %s\n' "$?" ``` The mounted call exited 1; the detached control exited 0. The verifier then checked `hdiutil info -plist`, confirmed no attachment remained for this fixture, and removed the generated image and source fixture. No other owner's image was detached. No external analysis engine or persistent database was needed. ### Expected behavior The artifact guide, `.agents/skills/reverse-engineer-anything/references/native-and-artifacts.md:315-319` at the tested revision, documents read-only DMG traversal on supported macOS hosts and explicit mount-failure limitations without claiming unobserved children. `src/contracts/artifactToolContracts.ts:83` includes root/integrity observations, limitations and unexplored branches in `inspect_artifact`. Retain available root identity with truthful unknown or incomplete child coverage and a busy-attachment limitation. A safely obtained complete inventory also satisfies the contract. Preserve mount ownership boundaries and give recovery guidance that reflects the busy attachment. ### Impact and scope This bounded mounted-image case prevents supported inspection from returning available root observations and gives misleading recovery advice. Detaching the verifier-owned mount restored complete inventory of the same image, so callers able to release their existing mount have a demonstrated workaround. Behavior while another application must keep a mount active was not tested. No data corruption or security boundary crossing was observed; broader frequency is unknown. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform Node v26.12.0; macOS 27.0.1, build 26A434; arm64. Working system `hdiutil`; no Hopper or Ghidra involved. ### Target and artifact context A self-created read-only UDZO DMG containing one text file, already attached read-only under a fixture-owned mount root. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence Selected fields from the mounted CLI result, with command diagnostics reduced to exclude local paths: ```text code: artifact_operation_failed category: unavailable retryable: false message: Artifact processing is unavailable for the current target or host. Check artifact support and required tools. remediation.action: Artifact processing is unavailable for the current target or host. Check artifact support and required tools. details.operation: inspect_artifact details.reason: unavailable ``` `details.detail` retains an `hdiutil attach` failure with exit code 1 and `hdiutil: attach failed - Resource busy`. No root inventory Evidence or partial observation appears in this result. The detached control's inventory substep reports `coverage: {"scope":"container-entries","status":"complete"}` and root format `dmg`. The outer inspection reports `complete-within-substeps`. ### Typed outcome or recovery expected Return the available root observations and an explicit busy-attachment child-traversal limitation with truthful coverage, or a safely obtained complete inventory. Advise retry after the caller can release its mount rather than directing it toward host support or required tools. Do not detach or borrow another owner's mount. ### Analysis or suspected cause Source-derived reasoning at the tested revision: `src/artifacts/inventory/scanCanonical.ts:69-80,104-135` classifies and hashes the root before traversal, but propagates a traversal failure without constructing an inventory snapshot. `src/artifacts/NativeDmgArtifactReader.ts:759-768` maps numeric non-verify command failures to `unavailable`; verification has separate classification. `src/domain/analysisErrorPresentation.ts:439-440` supplies the generic host/tool message. The original Resource busy diagnostic is preserved, so the defect concerns loss of available observations and misleading top-level recovery guidance. ### Additional context Prepared with AI assistance and independently reproduced through the public CLI. The submitter checked current `origin/main` at `57e76599c035a420181b665f4f2a3edad71628fb`; subsequent changes in the cited files do not alter DMG scanning, attachment classification or this unavailable message. The latest published release is still 6.7.0. Related PRs #3290 and #4146 concern verification diagnostics, #679 concerns mount-root and detach ownership, and #5230 concerns unreadable directory entries; none reports this already-mounted-image failure. NOT_ENOUGH_INFO: the last known good version is unknown. MCP behavior, unsupported-host/missing-tool outcomes and integrity-failure handling were not exercised by this reproduction.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

The root-observation loss has a concrete source fix on d87e73f: inventory failure handling now retains the classified/hashed DMG carrier in partial_observation, with unknown child coverage. The busy-attach regression injects Resource busy and checks root digest/format, the root occurrence, unknown coverage and verify/attach provenance through public error projection. That test is simulated; I have not run it or repeated the mounted-image CLI reproduction.

The remaining point is recovery specificity. Numeric attach failures still become generic unavailable; busy attachment has no distinct recovery information. The public message has improved: it now asks callers to resolve the reported availability condition rather than blaming host support/tools. It still does not explain that an already-mounted image can be retried after its owner can release that mount. The regression asserts retained observations, not that recovery instruction, and its info response contains no existing attachment.

To close the remaining requirement, cover busy-specific guidance at the producing classification and public projection boundaries, then repeat the already-mounted read-only image and detached control on macOS. Verify retained root facts, explicit unexamined children, useful retry guidance, and that the pre-existing mount survives REA cleanup. Returning a failure with truthful partial observations is acceptable; successful child traversal is not required. Recovery must remain owner-controlled: REA should not detach or borrow another owner's mount.

<!-- gh-comment-id:6380895890 --> @coygeek commented on GitHub (Oct 24, 2026): The root-observation loss has a concrete source fix on `d87e73f`: [inventory failure handling](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/inventory/scanCanonical.ts#L122-L188) now retains the classified/hashed DMG carrier in `partial_observation`, with unknown child coverage. The [busy-attach regression](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/NativeDmgRootObservation.test.ts#L15-L75) injects `Resource busy` and checks root digest/format, the root occurrence, unknown coverage and verify/attach provenance through public error projection. That test is simulated; I have not run it or repeated the mounted-image CLI reproduction. The remaining point is recovery specificity. [Numeric attach failures](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/artifacts/NativeDmgArtifactReader.ts#L765-L778) still become generic `unavailable`; busy attachment has no distinct recovery information. The [public message](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/analysisErrorPresentation.ts#L487-L495) has improved: it now asks callers to resolve the reported availability condition rather than blaming host support/tools. It still does not explain that an already-mounted image can be retried after its owner can release that mount. The regression asserts retained observations, not that recovery instruction, and its `info` response contains no existing attachment. To close the remaining requirement, cover busy-specific guidance at the producing classification and public projection boundaries, then repeat the already-mounted read-only image and detached control on macOS. Verify retained root facts, explicit unexamined children, useful retry guidance, and that the pre-existing mount survives REA cleanup. Returning a failure with truthful partial observations is acceptable; successful child traversal is not required. Recovery must remain owner-controlled: REA should not detach or borrow another owner's mount.
Author
Owner

@hichipli commented on GitHub (Oct 26, 2026):

Replayed this on current main eecadf35b with a self-created UDZO image on macOS 26.5.2 / Node 24.22.1. The mounted case now retains the root digest and unknown child coverage, but still gives generic availability guidance. The pre-existing mount survives REA cleanup; after releasing that fixture-owned mount, the same image inventories successfully.

I'll take the remaining recovery hint in a small follow-up: use the pre-attach hdiutil info fact when it lists the same resolved image path, keep the failure and partial observations, and advise releasing the attachment through its owner when safe. No borrowing or detaching existing mounts, and no inference from localized stderr alone. I'll include the real mounted/detached CLI control.

<!-- gh-comment-id:6412781824 --> @hichipli commented on GitHub (Oct 26, 2026): Replayed this on current main `eecadf35b` with a self-created UDZO image on macOS 26.5.2 / Node 24.22.1. The mounted case now retains the root digest and unknown child coverage, but still gives generic availability guidance. The pre-existing mount survives REA cleanup; after releasing that fixture-owned mount, the same image inventories successfully. I'll take the remaining recovery hint in a small follow-up: use the pre-attach `hdiutil info` fact when it lists the same resolved image path, keep the failure and partial observations, and advise releasing the attachment through its owner when safe. No borrowing or detaching existing mounts, and no inference from localized stderr alone. I'll include the real mounted/detached CLI control.
Author
Owner

@hichipli commented on GitHub (Oct 26, 2026):

Follow-up in #7221. The real macOS CLI regression checks that the existing mount survives the failure, root facts and unknown child coverage remain visible, and the detached retry succeeds. Different or unresolved image paths keep the generic guidance.

It's a draft pending the human owner's final review; local checks and all 67 focused tests passed.

<!-- gh-comment-id:6413098261 --> @hichipli commented on GitHub (Oct 26, 2026): Follow-up in #7221. The real macOS CLI regression checks that the existing mount survives the failure, root facts and unknown child coverage remain visible, and the detached retry succeeds. Different or unresolved image paths keep the generic guidance. It's a draft pending the human owner's final review; local checks and all 67 focused tests passed.
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#3378
No description provided.