[PR #2040] [MERGED] fix(native): select one slice when universal Mach-O slices share a CPU type #4983

Closed
opened 2026-10-31 00:57:40 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2040
Author: @hemanthrayuduu
Created: 10/12/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/macho-same-cpu-slices


📝 Commits (4)

  • 9c74fba fix(native): select one slice when universal Mach-O slices share a CPU type
  • 59d18f8 fix(native): share Mach-O slice preference across inspections
  • bf14a31 chore: merge main into Mach-O slice selection branch
  • 7456d81 chore: preserve upstream process capture test during merge

📊 Changes

9 files changed (+328 additions, -59 deletions)

View changed files

📝 src/native/AppleMachoSelection.test.ts (+47 -1)
📝 src/native/AppleMachoSelection.ts (+34 -23)
➕ src/native/MachoArchitectureSelection.ts (+54 -0)
📝 src/native/NativeMachoInspection.test.ts (+58 -0)
📝 src/native/NativeMachoInspection.ts (+18 -15)
📝 tests/boundary/providers/native/nativeSelectedArchitecture.test.ts (+64 -20)
➕ tests/fixtures/native-macos/lipo-same-cpu/README.md (+19 -0)
➕ tests/fixtures/native-macos/lipo-same-cpu/calculator.txt (+17 -0)
➕ tests/fixtures/native-macos/lipo-same-cpu/shortcut-droplet.txt (+17 -0)

📄 Description

Summary

On macOS 27, most system binaries are universal files whose slices all have CPU_TYPE_ARM64. Calculator and TextEdit are arm64e + arm64e.x1, /bin/ls is x86_64 + arm64e + arm64e.x1, and ShortcutDroplet is arm64 + arm64e.x1. Target admission names slices by CPU family, so these targets get availableArchitectures: ["arm64"] even though the file holds two arm64 slices. Two native tools treated that one-entry family list as if it were the slice list:

  • inspect_macho decided the file needed no -arch and ran otool, nm, dyld_info, dwarfdump and vtool against both slices. ShortcutDroplet fails outright, and Calculator returns both slices' data mixed into one result.
  • inspect_native_dispatch_metadata matched FAT slices by CPU type alone and refuses /bin/ls and TextEdit as "Ambiguous FAT architecture slice".

After this change, both tools pick exactly one slice, using the same preference order.

I didn't open an issue first. I found this while checking the other arm64e.x1 paths after #1765. That issue's naming fix landed in f598bab, and #1772 covers its dyld-resolution side. Neither touches the two functions changed here.

Problem and expected behavior

Smallest trigger: rea inspect-macho /System/Library/CoreServices/ShortcutDroplet.app/Contents/MacOS/ShortcutDroplet. lipo lists two slices, arm64 and arm64e.x1. Because the target has one CPU family, selectArchitecture returned "unfiltered" and otool printed both slices. The parser then rejected 40 load commands against ncmds 20.

The broken invariant: a slice tool must get -arch whenever the file holds more than one slice. One slice per CPU family isn't guaranteed. Likewise, selectMachoSlice should choose one slice when several share the requested CPU type, the way lipo's family names do, and report ambiguity only for slices it really can't tell apart.

Change and scope

  • src/native/NativeMachoInspection.ts: selectArchitecture now also filters when lipo listed more than one slice. Before, it only checked the family count. The existing preference is unchanged: the exact family name, then arm64e, then the first arm64e.<variant>. I kept the family check as well, so a file whose header has two families but whose lipo output lists fewer still gets today's "lipo did not list X" refusal. It doesn't fall back to an unfiltered run.
  • src/native/AppleMachoSelection.ts: selectMachoSlice ranks slices of the requested CPU type by base subtype, with the capability byte masked off. The order is arm64 (0, then 1) or x86_64 (3), then arm64e (2), then any other variant (arm64e.x1 is 12, x86_64h is 8) in FAT table order. It still throws "Ambiguous FAT architecture slice" when the top-ranked slices share a base subtype. So the identical-entry FAT64 test in AppleDispatchMetadata.test.ts passes unchanged.

On the four binaries above, both tools now read the same slice: arm64e for Calculator, TextEdit and /bin/ls, and arm64 for ShortcutDroplet.

I fixed the slice decision in these two functions rather than admission. BinaryTarget's family grouping is intentional (see the comment in selectArchitecture), and lipo and the FAT table are where the slice facts come from. Ghidra admission has the same root cause (below), so I've left it for a separate change.

Not included:

  • Ghidra admission (src/ghidra/GhidraProvider.ts, the availableArchitectures.length > 1 check). An arm64e + arm64e.x1 file reports one CPU family, so it gets past the "analyze a thinned Mach-O slice instead" refusal, which then never fires for it. Testing a fix needs a real Ghidra, which I don't have set up.
  • The codesign per-slice regex in NativeMacOSProvider.ts (around line 997) doesn't accept arm64e.x1.
  • A FAT file whose only x86_64 slice is x86_64h. inspect_macho's lipo-name matching has no rule for it.
  • endian is always null on real universal binaries, because file -b doesn't print an endianness for them.
  • #1772 adds a shared machoCpuSubtypeBase helper. This PR keeps a local mask so the two don't conflict. Whichever lands second can switch over.

Contract and boundary impact

  • Semantic owner and earliest changed stage: slice selection in the macOS native provider. That's selectArchitecture (tool arguments for inspect_macho) and selectMachoSlice (FAT selection for dispatch metadata). Admission and BinaryTarget are unchanged.
  • CLI and MCP/tool-catalog contract: no schema changes. For these files, inspect_macho now fills selected_architecture, and inspect_native_dispatch_metadata returns a result instead of an execution failure.
  • Provider, bridge, target-format, or platform compatibility: native macOS provider only. Thin files, one-slice FAT files and FAT files with one slice per family take the same path as before.
  • Evidence, artifact, provenance, or reconstruction contract: inspect_macho evidence for these files now describes one slice. The provenance shows the -arch arguments, and slice-based evidence locations now appear (Calculator has 7 locations, up from 2), with the "selected Mach-O slice is unknown" limitation gone.
  • Process execution, authorization, cleanup, or containment impact: same tools, now with -arch or --arch= added. Nothing else changes.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: none.

Evidence and regression coverage

  • Tests added or updated:

    • tests/boundary/providers/native/nativeSelectedArchitecture.test.ts: I moved the existing "fail any slice command without the expected -arch" runner into a shared helper and added two cases that use real lipo -detailed_info captures from Calculator and ShortcutDroplet, with the target set to availableArchitectures: ["arm64"] as admission produces. The captures and their provenance are in tests/fixtures/native-macos/lipo-same-cpu/.
    • src/native/AppleMachoSelection.test.ts: FAT fixtures with the real macOS 27 subtypes. arm64e (0x80000002) + arm64e.x1 (0x8000000c) and arm64 (0x0) + arm64e.x1 run in both orders, plus the /bin/ls layout and x86_64h + x86_64. An arm64e + arm64e duplicate must still be reported as ambiguous.
  • Base reproduction or other evidence: with only the tests applied on top of ee50ed1, npm run test:focused on the two test files gave 8 failed, 18 passed. The 6 selection cases threw "Ambiguous FAT architecture slice", and the 2 real-capture cases failed because otool ran without -arch. The duplicate case and the refactored existing cases passed.

  • User-visible CLI/MCP output: node scripts/rea.mjs <command> <path> --format json on macOS 27.0.1 (26A434), arm64, built from ee50ed1 before the change and from this branch after it:

    Target Before After
    inspect-macho ShortcutDroplet exit 1, unreadable_output: "Malformed otool load-command output" (2 Mach headers, 40 load commands, ncmds 20 each) exit 0, selected_architecture { name: "arm64", container_file_offset: 16384 }, 20 load commands, every slice tool ran with -arch arm64
    inspect-macho Calculator exit 0, but selected_architecture { name: null, container_file_offset: null }, 4138 imports, and 2 of 3 exports were nm's … (for architecture arm64e): / (… arm64e.x1): header lines exit 0, { name: "arm64e", container_file_offset: 16384 }, 2069 imports, 1 export (__mh_execute_header)
    inspect-native-dispatch-metadata /bin/ls exit 1: "Ambiguous FAT architecture slice (FAT header byte order: big-endian)" exit 0. No ObjC or Swift records, as you'd expect from a C tool. Chained fixups coverage DYLD_CHAINED_PTR_ARM64E
    inspect-native-dispatch-metadata TextEdit exit 1, same error exit 0: 38 ObjC classes, 7 protocols, 84 ivars, 341 dispatch implementations, 34 Swift dispatch slots, 728 relative pointers. All 3190 file offsets fall between 22060 and 217776, inside the arm64e slice (16384–427424) and not arm64e.x1 (from 442368)

    TextEdit's Swift conformance and vtable coverage is partial, because of decoder limits that have nothing to do with slice selection. It was never reached before this change, since the run failed earlier.

  • Remaining proof gaps:

    • I only ran this on one macOS 27.0.1 arm64 host. On an x64 host, admission picks the x86_64 slice, which this change doesn't affect, and I didn't test that.
    • Slices with subtype 1 (lipo calls them arm64v8) rank right after plain arm64 in the dispatch selector. inspect_macho's name matching has no arm64v8 rule, so the two selectors could still disagree on such a file. I didn't try to find one.
    • The Ghidra, codesign, x86_64h-only and endian items above remain open.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

All runs used Node 24.18.0 and npm 11.16.0 on macOS 27.0.1, arm64.

  • npm ci: installed.
  • npm run test:focused -- src/native/AppleMachoSelection.test.ts tests/boundary/providers/native/nativeSelectedArchitecture.test.ts with only the tests applied on ee50ed1: 8 failed, 18 passed (described above).
  • npm run test:focused -- src/native/AppleMachoSelection.test.ts tests/boundary/providers/native/nativeSelectedArchitecture.test.ts src/native/AppleDispatchMetadata.test.ts src/native/NativeMachoInspection.test.ts tests/boundary/providers/native/nativeProvider.test.ts tests/boundary/providers/native/nativeMachoHeader.golden.test.ts tests/boundary/cli/nativeMachoEchoedPathCli.test.ts: 7 files, 97 tests passed.
  • npx vitest run src/native tests/boundary/providers/native src/artifacts/apple src/domain/apple tests/boundary/cli/nativeMacho: 55 files, 518 tests passed.
  • npm run test:fast: 392 files, 5035 passed, 7 skipped.
  • npm run check:fast: passed.
  • npm run check: passed (typecheck, lint and module boundaries, format check, knip, metadata, architecture guards).
  • npm run verify:test-discovery: "Discovered all 901 test files exactly once."
  • git diff --check: clean.
  • npm run build:cached: passed.
  • The CLI runs in the table above, before and after.

I didn't run npm run docs:check or npm run check:pr, since no contracts, docs or generated metadata changed.

Compatibility, safety, and release

  • Breaking changes or migration steps: none. One behavior change sits outside the bug, and I got it from reading the code; no test covers it. A FAT file with several 32-bit slices of one CPU type (for example armv7 + armv7s) used to fail with "Ambiguous FAT architecture slice". Now the first slice is selected, and dispatch metadata fails with the existing "Unsupported 32-bit Mach-O metadata" error instead.
  • Real Hopper/Ghidra, browser, or OS coverage: real macOS 27.0.1 Apple tools via the CLI runs above. I didn't run Hopper or Ghidra, and neither is touched.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new processes or paths. The same Apple tools run with an added -arch argument.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🤖 Generated with Claude Code


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/2040 **Author:** [@hemanthrayuduu](https://github.com/hemanthrayuduu) **Created:** 10/12/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/macho-same-cpu-slices` --- ### 📝 Commits (4) - [`9c74fba`](https://github.com/morluto/rea/commit/9c74fba85e41b037ffd0f72063af60c9b460fc8f) fix(native): select one slice when universal Mach-O slices share a CPU type - [`59d18f8`](https://github.com/morluto/rea/commit/59d18f84d49ce4435efae659e44df709b01b2c54) fix(native): share Mach-O slice preference across inspections - [`bf14a31`](https://github.com/morluto/rea/commit/bf14a31c0113ec8a56fa5077c9db53779d2b360f) chore: merge main into Mach-O slice selection branch - [`7456d81`](https://github.com/morluto/rea/commit/7456d815903bf2349ee377cd1f5fec4008cbcc1d) chore: preserve upstream process capture test during merge ### 📊 Changes **9 files changed** (+328 additions, -59 deletions) <details> <summary>View changed files</summary> 📝 `src/native/AppleMachoSelection.test.ts` (+47 -1) 📝 `src/native/AppleMachoSelection.ts` (+34 -23) ➕ `src/native/MachoArchitectureSelection.ts` (+54 -0) 📝 `src/native/NativeMachoInspection.test.ts` (+58 -0) 📝 `src/native/NativeMachoInspection.ts` (+18 -15) 📝 `tests/boundary/providers/native/nativeSelectedArchitecture.test.ts` (+64 -20) ➕ `tests/fixtures/native-macos/lipo-same-cpu/README.md` (+19 -0) ➕ `tests/fixtures/native-macos/lipo-same-cpu/calculator.txt` (+17 -0) ➕ `tests/fixtures/native-macos/lipo-same-cpu/shortcut-droplet.txt` (+17 -0) </details> ### 📄 Description ## Summary On macOS 27, most system binaries are universal files whose slices all have `CPU_TYPE_ARM64`. Calculator and TextEdit are arm64e + arm64e.x1, `/bin/ls` is x86_64 + arm64e + arm64e.x1, and ShortcutDroplet is arm64 + arm64e.x1. Target admission names slices by CPU family, so these targets get `availableArchitectures: ["arm64"]` even though the file holds two arm64 slices. Two native tools treated that one-entry family list as if it were the slice list: - `inspect_macho` decided the file needed no `-arch` and ran otool, nm, dyld_info, dwarfdump and vtool against both slices. ShortcutDroplet fails outright, and Calculator returns both slices' data mixed into one result. - `inspect_native_dispatch_metadata` matched FAT slices by CPU type alone and refuses `/bin/ls` and TextEdit as "Ambiguous FAT architecture slice". After this change, both tools pick exactly one slice, using the same preference order. I didn't open an issue first. I found this while checking the other arm64e.x1 paths after #1765. That issue's naming fix landed in f598bab, and #1772 covers its dyld-resolution side. Neither touches the two functions changed here. ## Problem and expected behavior Smallest trigger: `rea inspect-macho /System/Library/CoreServices/ShortcutDroplet.app/Contents/MacOS/ShortcutDroplet`. lipo lists two slices, `arm64` and `arm64e.x1`. Because the target has one CPU family, `selectArchitecture` returned "unfiltered" and otool printed both slices. The parser then rejected 40 load commands against `ncmds 20`. The broken invariant: a slice tool must get `-arch` whenever the file holds more than one slice. One slice per CPU family isn't guaranteed. Likewise, `selectMachoSlice` should choose one slice when several share the requested CPU type, the way lipo's family names do, and report ambiguity only for slices it really can't tell apart. ## Change and scope - `src/native/NativeMachoInspection.ts`: `selectArchitecture` now also filters when lipo listed more than one slice. Before, it only checked the family count. The existing preference is unchanged: the exact family name, then `arm64e`, then the first `arm64e.<variant>`. I kept the family check as well, so a file whose header has two families but whose lipo output lists fewer still gets today's "lipo did not list X" refusal. It doesn't fall back to an unfiltered run. - `src/native/AppleMachoSelection.ts`: `selectMachoSlice` ranks slices of the requested CPU type by base subtype, with the capability byte masked off. The order is arm64 (0, then 1) or x86_64 (3), then arm64e (2), then any other variant (arm64e.x1 is 12, x86_64h is 8) in FAT table order. It still throws "Ambiguous FAT architecture slice" when the top-ranked slices share a base subtype. So the identical-entry FAT64 test in `AppleDispatchMetadata.test.ts` passes unchanged. On the four binaries above, both tools now read the same slice: arm64e for Calculator, TextEdit and `/bin/ls`, and arm64 for ShortcutDroplet. I fixed the slice decision in these two functions rather than admission. BinaryTarget's family grouping is intentional (see the comment in `selectArchitecture`), and lipo and the FAT table are where the slice facts come from. Ghidra admission has the same root cause (below), so I've left it for a separate change. Not included: - Ghidra admission (`src/ghidra/GhidraProvider.ts`, the `availableArchitectures.length > 1` check). An arm64e + arm64e.x1 file reports one CPU family, so it gets past the "analyze a thinned Mach-O slice instead" refusal, which then never fires for it. Testing a fix needs a real Ghidra, which I don't have set up. - The codesign per-slice regex in `NativeMacOSProvider.ts` (around line 997) doesn't accept `arm64e.x1`. - A FAT file whose only x86_64 slice is x86_64h. inspect_macho's lipo-name matching has no rule for it. - `endian` is always `null` on real universal binaries, because `file -b` doesn't print an endianness for them. - #1772 adds a shared `machoCpuSubtypeBase` helper. This PR keeps a local mask so the two don't conflict. Whichever lands second can switch over. ## Contract and boundary impact - Semantic owner and earliest changed stage: slice selection in the macOS native provider. That's `selectArchitecture` (tool arguments for `inspect_macho`) and `selectMachoSlice` (FAT selection for dispatch metadata). Admission and BinaryTarget are unchanged. - CLI and MCP/tool-catalog contract: no schema changes. For these files, `inspect_macho` now fills `selected_architecture`, and `inspect_native_dispatch_metadata` returns a result instead of an execution failure. - Provider, bridge, target-format, or platform compatibility: native macOS provider only. Thin files, one-slice FAT files and FAT files with one slice per family take the same path as before. - Evidence, artifact, provenance, or reconstruction contract: `inspect_macho` evidence for these files now describes one slice. The provenance shows the `-arch` arguments, and slice-based evidence locations now appear (Calculator has 7 locations, up from 2), with the "selected Mach-O slice is unknown" limitation gone. - Process execution, authorization, cleanup, or containment impact: same tools, now with `-arch` or `--arch=` added. Nothing else changes. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none. ## Evidence and regression coverage - Tests added or updated: - `tests/boundary/providers/native/nativeSelectedArchitecture.test.ts`: I moved the existing "fail any slice command without the expected `-arch`" runner into a shared helper and added two cases that use real `lipo -detailed_info` captures from Calculator and ShortcutDroplet, with the target set to `availableArchitectures: ["arm64"]` as admission produces. The captures and their provenance are in `tests/fixtures/native-macos/lipo-same-cpu/`. - `src/native/AppleMachoSelection.test.ts`: FAT fixtures with the real macOS 27 subtypes. arm64e (`0x80000002`) + arm64e.x1 (`0x8000000c`) and arm64 (`0x0`) + arm64e.x1 run in both orders, plus the `/bin/ls` layout and x86_64h + x86_64. An arm64e + arm64e duplicate must still be reported as ambiguous. - Base reproduction or other evidence: with only the tests applied on top of ee50ed1, `npm run test:focused` on the two test files gave 8 failed, 18 passed. The 6 selection cases threw "Ambiguous FAT architecture slice", and the 2 real-capture cases failed because otool ran without `-arch`. The duplicate case and the refactored existing cases passed. - User-visible CLI/MCP output: `node scripts/rea.mjs <command> <path> --format json` on macOS 27.0.1 (26A434), arm64, built from ee50ed1 before the change and from this branch after it: | Target | Before | After | | --- | --- | --- | | `inspect-macho` ShortcutDroplet | exit 1, `unreadable_output`: "Malformed otool load-command output" (2 Mach headers, 40 load commands, `ncmds` 20 each) | exit 0, `selected_architecture` `{ name: "arm64", container_file_offset: 16384 }`, 20 load commands, every slice tool ran with `-arch arm64` | | `inspect-macho` Calculator | exit 0, but `selected_architecture` `{ name: null, container_file_offset: null }`, 4138 imports, and 2 of 3 exports were nm's `… (for architecture arm64e):` / `(… arm64e.x1):` header lines | exit 0, `{ name: "arm64e", container_file_offset: 16384 }`, 2069 imports, 1 export (`__mh_execute_header`) | | `inspect-native-dispatch-metadata` /bin/ls | exit 1: "Ambiguous FAT architecture slice (FAT header byte order: big-endian)" | exit 0. No ObjC or Swift records, as you'd expect from a C tool. Chained fixups coverage `DYLD_CHAINED_PTR_ARM64E` | | `inspect-native-dispatch-metadata` TextEdit | exit 1, same error | exit 0: 38 ObjC classes, 7 protocols, 84 ivars, 341 dispatch implementations, 34 Swift dispatch slots, 728 relative pointers. All 3190 file offsets fall between 22060 and 217776, inside the arm64e slice (16384–427424) and not arm64e.x1 (from 442368) | TextEdit's Swift conformance and vtable coverage is `partial`, because of decoder limits that have nothing to do with slice selection. It was never reached before this change, since the run failed earlier. - Remaining proof gaps: - I only ran this on one macOS 27.0.1 arm64 host. On an x64 host, admission picks the x86_64 slice, which this change doesn't affect, and I didn't test that. - Slices with subtype 1 (lipo calls them `arm64v8`) rank right after plain arm64 in the dispatch selector. inspect_macho's name matching has no `arm64v8` rule, so the two selectors could still disagree on such a file. I didn't try to find one. - The Ghidra, codesign, x86_64h-only and `endian` items above remain open. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed All runs used Node 24.18.0 and npm 11.16.0 on macOS 27.0.1, arm64. - `npm ci`: installed. - `npm run test:focused -- src/native/AppleMachoSelection.test.ts tests/boundary/providers/native/nativeSelectedArchitecture.test.ts` with only the tests applied on ee50ed1: 8 failed, 18 passed (described above). - `npm run test:focused -- src/native/AppleMachoSelection.test.ts tests/boundary/providers/native/nativeSelectedArchitecture.test.ts src/native/AppleDispatchMetadata.test.ts src/native/NativeMachoInspection.test.ts tests/boundary/providers/native/nativeProvider.test.ts tests/boundary/providers/native/nativeMachoHeader.golden.test.ts tests/boundary/cli/nativeMachoEchoedPathCli.test.ts`: 7 files, 97 tests passed. - `npx vitest run src/native tests/boundary/providers/native src/artifacts/apple src/domain/apple tests/boundary/cli/nativeMacho`: 55 files, 518 tests passed. - `npm run test:fast`: 392 files, 5035 passed, 7 skipped. - `npm run check:fast`: passed. - `npm run check`: passed (typecheck, lint and module boundaries, format check, knip, metadata, architecture guards). - `npm run verify:test-discovery`: "Discovered all 901 test files exactly once." - `git diff --check`: clean. - `npm run build:cached`: passed. - The CLI runs in the table above, before and after. I didn't run `npm run docs:check` or `npm run check:pr`, since no contracts, docs or generated metadata changed. ## Compatibility, safety, and release - Breaking changes or migration steps: none. One behavior change sits outside the bug, and I got it from reading the code; no test covers it. A FAT file with several 32-bit slices of one CPU type (for example armv7 + armv7s) used to fail with "Ambiguous FAT architecture slice". Now the first slice is selected, and dispatch metadata fails with the existing "Unsupported 32-bit Mach-O metadata" error instead. - Real Hopper/Ghidra, browser, or OS coverage: real macOS 27.0.1 Apple tools via the CLI runs above. I didn't run Hopper or Ghidra, and neither is touched. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new processes or paths. The same Apple tools run with an added `-arch` argument. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:57:40 +00:00
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#4983
No description provided.