[PR #927] [MERGED] feat(artifacts): trace Mach-O dylib resolution in bundles #2354

Closed
opened 2026-10-16 21:17:42 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/927
Author: @kaoru0822-kitauji
Created: 10/7/2026
Status: ✅ Merged
Merged: 10/8/2026
Merged by: @kaoru0822-kitauji

Base: main ← Head: feat/dylib-resolution


📝 Commits (10+)

  • 11b7f91 feat(artifacts): trace Mach-O dylib resolution in bundles
  • 71e5cb1 chore: merge main and regenerate combined catalog metadata
  • 63800ff Merge remote-tracking branch 'origin/main' into feat/dylib-resolution
  • a0ec3e2 fix(artifacts): follow dyld slice grading and report unparsed images
  • 9128039 fix(artifacts): defer lazy loads and tighten Mach-O validation
  • 19f01e2 Merge remote-tracking branch 'origin/main' into feat/dylib-resolution
  • ef245c0 fix(artifacts): trace .app-named files standalone and flag empty FAT tables
  • 9049620 Merge remote-tracking branch 'origin/main' into feat/dylib-resolution
  • 5e334a0 fix(artifacts): propagate conditional loads and bind digests to parsed images
  • e5c6fcc Merge origin/main into feat/dylib-resolution

📊 Changes

35 files changed (+3475 additions, -28 deletions)

View changed files

📝 README_ar.md (+1 -1)
📝 README_ja.md (+1 -1)
📝 README_ko.md (+1 -1)
📝 README_zh.md (+1 -1)
📝 docs/apple-application-analysis.md (+75 -0)
📝 docs/native-investigation.md (+7 -0)
📝 docs/product-catalog.json (+14 -11)
📝 docs/testing.md (+10 -1)
📝 docs/verification/managed-conformance-manifest.json (+2 -2)
📝 scripts/fixtures/apple/macos-bundle.mjs (+22 -2)
📝 scripts/verify-binary-layout.mjs (+1 -1)
📝 scripts/verify/apple/macos-bundle.mjs (+3 -0)
➕ scripts/verify/apple/macos-dylib-e2e.mjs (+209 -0)
📝 skills/reverse-engineer-anything/SKILL.md (+3 -3)
📝 skills/reverse-engineer-anything/references/native-and-artifacts.md (+7 -0)
📝 src/application/binary/BinarySession.ts (+4 -2)
📝 src/artifacts/ArtifactProvider.ts (+37 -0)
📝 src/artifacts/ArtifactProviderMetadata.ts (+2 -1)
➕ src/artifacts/apple/DylibResolutionReader.ts (+360 -0)
➕ src/artifacts/apple/MachoImage.fixture.ts (+152 -0)

...and 15 more files

📄 Description

Builds on #922, which itself builds on #921; this branch contains both. Once they merge, the diff here shrinks to the last commit.

Summary

REA could list a Mach-O's raw dependencies through inspect_macho, but nothing resolved @rpath, @loader_path or @executable_path. So it could not answer which file each dylib load actually reaches, or which loads are missing, weak, or depend on search order.

The new trace_dylib_resolution (CLI trace-dylib-resolution) answers that for the active Mach-O, or for every executable in the active .app: the main app, XPC services, extensions, login items, privileged helpers and helper tools. Each executable is a separate process root with its own @executable_path.

Change and scope

New tool. It lives in the artifact family because it is a distinct relationship question with no existing contract. It is execution-free and needs no subprocess or Apple tool, so it also runs off macOS.

Parser (src/artifacts/MachoLoadCommandReader.ts). Pure TypeScript with bounded reads of the FAT header, the Mach-O header and sizeofcmds.

  • Dependency commands: LC_LOAD/WEAK/REEXPORT/LAZY/UPWARD_DYLIB. This includes the newer dylib_use_command encoding from mach-o/loader.h, with its weak, re-export, upward and delayed-init flags, which the current ld emits for -delay_library.
  • Also LC_ID_DYLIB, LC_RPATH, LC_DYLD_ENVIRONMENT and code-signature presence.
  • Malformed tables are reported with the failing command index rather than guessed. Load commands above 16 MiB are refused as unsupported, a resource bound. A Java class file, which shares the FAT magic, is not mistaken for Mach-O.

Resolution (src/domain/dylibResolution.ts, src/domain/dyldPaths.ts). Pure logic over a lazily probed tree view.

  • @executable_path comes from the process root.
  • @loader_path comes from the loading image, or from the owner of the LC_RPATH entry being expanded.
  • @rpath searches the loading image's rpaths first, then up the load chain.
  • Dependencies load dependents-first in load-command order, once per process. A matching install name reuses an already loaded image.
  • Symlinks such as Versions/Current are followed inside the analyzed root, and .. or absolute escapes stop.
  • A standalone Mach-O is never resolved by walking its directory, so a binary in ~/Downloads stays cheap.

Result. Each edge lists every candidate path with an outcome (resolved, absent, not-mach-o, malformed, architecture-missing, outside-target, escapes-target, undetermined) and a resolution:

Status Meaning
resolved Resolved inside the root, with no undeterminable earlier candidate.
conditional Resolved inside the root, but an outside-root candidate such as /usr/lib/swift precedes it.
unresolved Every candidate is definitively unusable.
undetermined Nothing resolves inside the root, and a candidate lies outside it or depends on the environment.

Absolute and shared-cache system paths are never checked against the host.

Derived findings:

  • required-load-unresolved and weak-load-unresolved.
  • earlier-rpath-candidate-absent: library validation is reported as unknown and points to inspect_signature.
  • dyld-environment-present.

Not included: library-validation and hardened-runtime evaluation (planned with the inspect_signature CodeDirectory work), dyld shared-cache contents, and DMG/ZIP containers as trace targets. Those are inventoried by #922.

Contract and boundary impact

  • CLI and MCP: one new tool and command; the catalog goes from 131 to 132 tools, and the artifact family from 5 to 6 in all READMEs.
    • Input: roots? (normalized root-relative paths, as a JSON-Schema pattern) and architecture? (arm64, arm64e or x86_64).
    • CLI: --root (repeatable) and --architecture.
  • Effects: read-only evidence, with no process launch.
  • Snapshot cache: the operation is excluded, because the result depends on bundle bytes beyond the target digest.
  • Evidence: each image carries its sha256. A changed active target fails with integrity. A non-Mach-O target fails with unavailable. A non-normalized or non-Mach-O root fails with path or format.
  • Generated metadata: catalog, skill (version 28) and conformance digests are regenerated.

Sanitized MCP result for the lane fixture (trimmed):

{
  "roots": [{ "image": "Contents/MacOS/MacFixture", "architecture": "arm64" }, "…6 more"],
  "edges": [
    { "root": "Contents/MacOS/MacFixture", "loader": "Contents/MacOS/MacFixture",
      "command": "LC_LOAD_DYLIB", "encoding": "dylib_command",
      "install_name": "@rpath/Core.framework/Versions/A/Core",
      "candidates": [
        { "source": "rpath", "rpath": "@executable_path/../Overrides",
          "path": "Contents/MacOS/../Overrides/Core.framework/Versions/A/Core", "outcome": "absent" },
        { "source": "rpath", "rpath": "@executable_path/../Frameworks",
          "path": "Contents/MacOS/../Frameworks/Core.framework/Versions/A/Core", "outcome": "resolved",
          "resolved_path": "Contents/Frameworks/Core.framework/Versions/A/Core" }
      ],
      "resolution": { "status": "resolved", "image": "Contents/Frameworks/Core.framework/Versions/A/Core" } },
    { "install_name": "@rpath/libdelay.dylib", "encoding": "dylib_use_command", "delayed_init": true,
      "resolution": { "status": "resolved", "image": "Contents/Frameworks/libdelay.dylib" } },
    { "root": "Contents/XPCServices/Svc.xpc/Contents/MacOS/Svc",
      "install_name": "@rpath/Core.framework/Versions/A/Core",
      "candidates": [{ "path": "/opt/rea-fixture-absent/lib/Core.framework/Versions/A/Core", "outcome": "outside-target" }, "…"],
      "resolution": { "status": "conditional", "image": "Contents/Frameworks/Core.framework/Versions/A/Core" } },
    { "install_name": "/usr/lib/libSystem.B.dylib", "resolution": { "status": "undetermined", "image": null } }
  ],
  "findings": [
    { "kind": "earlier-rpath-candidate-absent", "basis": "derived", "edge_index": 0 },
    { "kind": "weak-load-unresolved", "basis": "derived" }
  ],
  "coverage": { "status": "complete", "unparsed_images": [], "roots_without_architecture": [] }
}

Evidence and regression coverage

Unit tests

  • src/artifacts/MachoLoadCommandReader.test.ts (8 cases):
    • every dependency command;
    • dylib_use_command flags;
    • FAT and FAT_64 containers;
    • i386, a Java class file and a script;
    • malformed cmdsize and ncmds;
    • unterminated, misplaced and non-UTF-8 strings;
    • truncated, oversized, big-endian and out-of-range images.
  • src/domain/dylibResolution.test.ts (10 cases):
    • loader-first rpaths, and @loader_path in an rpath relative to its owner;
    • @executable_path per root, Versions/Current, and escaping links;
    • outside-target versus conditional, and leaf names;
    • findings for weak and required loads;
    • not-mach-o, malformed and architecture-missing candidates;
    • load once per process with install-name reuse;
    • library roots, the architecture filter, and cancellation.

Boundary tests

  • CLI (tests/boundary/cli/dylibResolutionCli.test.ts):
    • app bundle roots and findings;
    • --root/--architecture, and rejection of ../ and non-Mach-O roots;
    • a standalone Mach-O.
  • MCP (tests/boundary/mcp/dylibResolutionMcp.test.ts): open_binary then the trace, an escaping root, a changed target (integrity), and a non-Mach-O target.

Real lane verify:macos-bundle, extended and run in the macOS CI job:

  • The clang-built fixture gains a missing Overrides rpath, an absolute rpath in the XPC service, and a delay-init library.
  • Every resolution status and finding is asserted, with CLI/MCP parity.
  • Every traced image's dependencies, rpaths and install name equal otool -l.
  • For the main app and the XPC service, the predicted load order equals what dyld loads under DYLD_PRINT_LIBRARIES, which confirms the loader-first rpath rule.

Parser conformance outside the lane. Against otool -l there were 0 mismatches across 1,654 slices in 853 images. The images came from Brave, OneDrive, Raycast, Discord, Telegram, /usr/bin (arm64e), Calculator, TextEdit, and the universal Command Line Tools binaries.

Real apps

App Time Size Notable result
Discord ≈1s 2.6 MB 34 earlier-rpath-candidate-absent: Electron Framework searches its own absent Libraries/ before the helper's rpath
OneDrive ≈1s 4 MB 4 unresolved weak loads from a nested app pointing at its own nonexistent Frameworks/
Raycast ≈1s 0.5 MB /usr/lib/swift precedes bundled frameworks, giving conditional
Brave ≈1s 1.8 MB 29 roots

Remaining proof gaps

  • The real-dyld load-order check covers two roots on arm64.
  • x86_64 and arm64e roots are covered by parser conformance, not by runtime runs.

Validation performed

  • npm run check:changed: 291 files passed.
  • npm run test:focused -- tests/boundary/cli/dylibResolutionCli.test.ts tests/boundary/mcp/dylibResolutionMcp.test.ts tests/boundary/cli/productCatalog.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/acceptance/contractPresentation.test.ts …: passed.
  • npm run docs:generate && npm run docs:check, npx oxfmt --check ., npm run lint: no new errors.
  • npm run verify:macos-bundle: {"ok":true,…,"dylib_resolution":{"roots":7,"edges":22,"otool_slices":11,"dyld_runtime_roots":2}}.

Compatibility, safety, and release

  • Breaking changes: none; this adds a tool.
  • Real Hopper/Ghidra coverage: not applicable, because no provider is touched.
  • Real OS coverage: macOS 26.6.2 arm64 locally, and the macOS 14 CI job.
  • Security and containment: read-only. Files are opened O_NOFOLLOW, symlinks are resolved without leaving the analyzed root, and the lane only runs its own fixture binaries.

🤖 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/927 **Author:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Created:** 10/7/2026 **Status:** ✅ Merged **Merged:** 10/8/2026 **Merged by:** [@kaoru0822-kitauji](https://github.com/kaoru0822-kitauji) **Base:** `main` ← **Head:** `feat/dylib-resolution` --- ### 📝 Commits (10+) - [`11b7f91`](https://github.com/morluto/rea/commit/11b7f918c2885fce09d3c7aed0c48c4dd6a4c4ce) feat(artifacts): trace Mach-O dylib resolution in bundles - [`71e5cb1`](https://github.com/morluto/rea/commit/71e5cb164024cd9b14a489d3d3c09568f9c3fc78) chore: merge main and regenerate combined catalog metadata - [`63800ff`](https://github.com/morluto/rea/commit/63800ff5a23854e29b94be2398459ec6e0622799) Merge remote-tracking branch 'origin/main' into feat/dylib-resolution - [`a0ec3e2`](https://github.com/morluto/rea/commit/a0ec3e20161a9a10ea463b7fb6824e1fd6348c2f) fix(artifacts): follow dyld slice grading and report unparsed images - [`9128039`](https://github.com/morluto/rea/commit/91280393cdc317cc43a14632399d5979a1349d07) fix(artifacts): defer lazy loads and tighten Mach-O validation - [`19f01e2`](https://github.com/morluto/rea/commit/19f01e2610752de57255cec220f61d418669ac1e) Merge remote-tracking branch 'origin/main' into feat/dylib-resolution - [`ef245c0`](https://github.com/morluto/rea/commit/ef245c0bd004cf568c66ede1ae00f92ae2855c96) fix(artifacts): trace .app-named files standalone and flag empty FAT tables - [`9049620`](https://github.com/morluto/rea/commit/9049620dfe42ee92f974cd8b586f9bff987087dd) Merge remote-tracking branch 'origin/main' into feat/dylib-resolution - [`5e334a0`](https://github.com/morluto/rea/commit/5e334a0e129a713892908e30fc52999b67aa3906) fix(artifacts): propagate conditional loads and bind digests to parsed images - [`e5c6fcc`](https://github.com/morluto/rea/commit/e5c6fccc35d7543db83ced1ee3a89b03846fed36) Merge origin/main into feat/dylib-resolution ### 📊 Changes **35 files changed** (+3475 additions, -28 deletions) <details> <summary>View changed files</summary> 📝 `README_ar.md` (+1 -1) 📝 `README_ja.md` (+1 -1) 📝 `README_ko.md` (+1 -1) 📝 `README_zh.md` (+1 -1) 📝 `docs/apple-application-analysis.md` (+75 -0) 📝 `docs/native-investigation.md` (+7 -0) 📝 `docs/product-catalog.json` (+14 -11) 📝 `docs/testing.md` (+10 -1) 📝 `docs/verification/managed-conformance-manifest.json` (+2 -2) 📝 `scripts/fixtures/apple/macos-bundle.mjs` (+22 -2) 📝 `scripts/verify-binary-layout.mjs` (+1 -1) 📝 `scripts/verify/apple/macos-bundle.mjs` (+3 -0) ➕ `scripts/verify/apple/macos-dylib-e2e.mjs` (+209 -0) 📝 `skills/reverse-engineer-anything/SKILL.md` (+3 -3) 📝 `skills/reverse-engineer-anything/references/native-and-artifacts.md` (+7 -0) 📝 `src/application/binary/BinarySession.ts` (+4 -2) 📝 `src/artifacts/ArtifactProvider.ts` (+37 -0) 📝 `src/artifacts/ArtifactProviderMetadata.ts` (+2 -1) ➕ `src/artifacts/apple/DylibResolutionReader.ts` (+360 -0) ➕ `src/artifacts/apple/MachoImage.fixture.ts` (+152 -0) _...and 15 more files_ </details> ### 📄 Description Builds on #922, which itself builds on #921; this branch contains both. Once they merge, the diff here shrinks to the last commit. ## Summary REA could list a Mach-O's raw dependencies through `inspect_macho`, but nothing resolved `@rpath`, `@loader_path` or `@executable_path`. So it could not answer which file each dylib load actually reaches, or which loads are missing, weak, or depend on search order. The new `trace_dylib_resolution` (CLI `trace-dylib-resolution`) answers that for the active Mach-O, or for every executable in the active `.app`: the main app, XPC services, extensions, login items, privileged helpers and helper tools. Each executable is a separate process root with its own `@executable_path`. ## Change and scope **New tool.** It lives in the artifact family because it is a distinct relationship question with no existing contract. It is execution-free and needs no subprocess or Apple tool, so it also runs off macOS. **Parser** (`src/artifacts/MachoLoadCommandReader.ts`). Pure TypeScript with bounded reads of the FAT header, the Mach-O header and `sizeofcmds`. - Dependency commands: `LC_LOAD/WEAK/REEXPORT/LAZY/UPWARD_DYLIB`. This includes the newer `dylib_use_command` encoding from `mach-o/loader.h`, with its weak, re-export, upward and delayed-init flags, which the current `ld` emits for `-delay_library`. - Also `LC_ID_DYLIB`, `LC_RPATH`, `LC_DYLD_ENVIRONMENT` and code-signature presence. - Malformed tables are reported with the failing command index rather than guessed. Load commands above 16 MiB are refused as unsupported, a resource bound. A Java class file, which shares the FAT magic, is not mistaken for Mach-O. **Resolution** (`src/domain/dylibResolution.ts`, `src/domain/dyldPaths.ts`). Pure logic over a lazily probed tree view. - `@executable_path` comes from the process root. - `@loader_path` comes from the loading image, or from the owner of the `LC_RPATH` entry being expanded. - `@rpath` searches the loading image's rpaths first, then up the load chain. - Dependencies load dependents-first in load-command order, once per process. A matching install name reuses an already loaded image. - Symlinks such as `Versions/Current` are followed inside the analyzed root, and `..` or absolute escapes stop. - A standalone Mach-O is never resolved by walking its directory, so a binary in `~/Downloads` stays cheap. **Result.** Each edge lists every candidate path with an outcome (`resolved`, `absent`, `not-mach-o`, `malformed`, `architecture-missing`, `outside-target`, `escapes-target`, `undetermined`) and a resolution: | Status | Meaning | | --- | --- | | `resolved` | Resolved inside the root, with no undeterminable earlier candidate. | | `conditional` | Resolved inside the root, but an outside-root candidate such as `/usr/lib/swift` precedes it. | | `unresolved` | Every candidate is definitively unusable. | | `undetermined` | Nothing resolves inside the root, and a candidate lies outside it or depends on the environment. | Absolute and shared-cache system paths are never checked against the host. **Derived findings:** - `required-load-unresolved` and `weak-load-unresolved`. - `earlier-rpath-candidate-absent`: library validation is reported as unknown and points to `inspect_signature`. - `dyld-environment-present`. Not included: library-validation and hardened-runtime evaluation (planned with the `inspect_signature` CodeDirectory work), dyld shared-cache contents, and DMG/ZIP containers as trace targets. Those are inventoried by #922. ## Contract and boundary impact - **CLI and MCP:** one new tool and command; the catalog goes from 131 to 132 tools, and the artifact family from 5 to 6 in all READMEs. - Input: `roots?` (normalized root-relative paths, as a JSON-Schema `pattern`) and `architecture?` (`arm64`, `arm64e` or `x86_64`). - CLI: `--root` (repeatable) and `--architecture`. - **Effects:** read-only `evidence`, with no process launch. - **Snapshot cache:** the operation is excluded, because the result depends on bundle bytes beyond the target digest. - **Evidence:** each image carries its sha256. A changed active target fails with `integrity`. A non-Mach-O target fails with `unavailable`. A non-normalized or non-Mach-O root fails with `path` or `format`. - **Generated metadata:** catalog, skill (version 28) and conformance digests are regenerated. Sanitized MCP result for the lane fixture (trimmed): ```json { "roots": [{ "image": "Contents/MacOS/MacFixture", "architecture": "arm64" }, "…6 more"], "edges": [ { "root": "Contents/MacOS/MacFixture", "loader": "Contents/MacOS/MacFixture", "command": "LC_LOAD_DYLIB", "encoding": "dylib_command", "install_name": "@rpath/Core.framework/Versions/A/Core", "candidates": [ { "source": "rpath", "rpath": "@executable_path/../Overrides", "path": "Contents/MacOS/../Overrides/Core.framework/Versions/A/Core", "outcome": "absent" }, { "source": "rpath", "rpath": "@executable_path/../Frameworks", "path": "Contents/MacOS/../Frameworks/Core.framework/Versions/A/Core", "outcome": "resolved", "resolved_path": "Contents/Frameworks/Core.framework/Versions/A/Core" } ], "resolution": { "status": "resolved", "image": "Contents/Frameworks/Core.framework/Versions/A/Core" } }, { "install_name": "@rpath/libdelay.dylib", "encoding": "dylib_use_command", "delayed_init": true, "resolution": { "status": "resolved", "image": "Contents/Frameworks/libdelay.dylib" } }, { "root": "Contents/XPCServices/Svc.xpc/Contents/MacOS/Svc", "install_name": "@rpath/Core.framework/Versions/A/Core", "candidates": [{ "path": "/opt/rea-fixture-absent/lib/Core.framework/Versions/A/Core", "outcome": "outside-target" }, "…"], "resolution": { "status": "conditional", "image": "Contents/Frameworks/Core.framework/Versions/A/Core" } }, { "install_name": "/usr/lib/libSystem.B.dylib", "resolution": { "status": "undetermined", "image": null } } ], "findings": [ { "kind": "earlier-rpath-candidate-absent", "basis": "derived", "edge_index": 0 }, { "kind": "weak-load-unresolved", "basis": "derived" } ], "coverage": { "status": "complete", "unparsed_images": [], "roots_without_architecture": [] } } ``` ## Evidence and regression coverage **Unit tests** - `src/artifacts/MachoLoadCommandReader.test.ts` (8 cases): - every dependency command; - `dylib_use_command` flags; - FAT and FAT_64 containers; - i386, a Java class file and a script; - malformed `cmdsize` and `ncmds`; - unterminated, misplaced and non-UTF-8 strings; - truncated, oversized, big-endian and out-of-range images. - `src/domain/dylibResolution.test.ts` (10 cases): - loader-first rpaths, and `@loader_path` in an rpath relative to its owner; - `@executable_path` per root, `Versions/Current`, and escaping links; - `outside-target` versus `conditional`, and leaf names; - findings for weak and required loads; - `not-mach-o`, `malformed` and `architecture-missing` candidates; - load once per process with install-name reuse; - library roots, the architecture filter, and cancellation. **Boundary tests** - CLI (`tests/boundary/cli/dylibResolutionCli.test.ts`): - app bundle roots and findings; - `--root`/`--architecture`, and rejection of `../` and non-Mach-O roots; - a standalone Mach-O. - MCP (`tests/boundary/mcp/dylibResolutionMcp.test.ts`): `open_binary` then the trace, an escaping root, a changed target (`integrity`), and a non-Mach-O target. **Real lane `verify:macos-bundle`**, extended and run in the macOS CI job: - The clang-built fixture gains a missing `Overrides` rpath, an absolute rpath in the XPC service, and a delay-init library. - Every resolution status and finding is asserted, with CLI/MCP parity. - Every traced image's dependencies, rpaths and install name equal `otool -l`. - For the main app and the XPC service, the predicted load order equals what dyld loads under `DYLD_PRINT_LIBRARIES`, which confirms the loader-first rpath rule. **Parser conformance outside the lane.** Against `otool -l` there were 0 mismatches across 1,654 slices in 853 images. The images came from Brave, OneDrive, Raycast, Discord, Telegram, `/usr/bin` (arm64e), Calculator, TextEdit, and the universal Command Line Tools binaries. **Real apps** | App | Time | Size | Notable result | | --- | --- | --- | --- | | Discord | ≈1s | 2.6 MB | 34 `earlier-rpath-candidate-absent`: Electron Framework searches its own absent `Libraries/` before the helper's rpath | | OneDrive | ≈1s | 4 MB | 4 unresolved weak loads from a nested app pointing at its own nonexistent `Frameworks/` | | Raycast | ≈1s | 0.5 MB | `/usr/lib/swift` precedes bundled frameworks, giving `conditional` | | Brave | ≈1s | 1.8 MB | 29 roots | **Remaining proof gaps** - The real-dyld load-order check covers two roots on arm64. - x86_64 and arm64e roots are covered by parser conformance, not by runtime runs. ## Validation performed - `npm run check:changed`: 291 files passed. - `npm run test:focused -- tests/boundary/cli/dylibResolutionCli.test.ts tests/boundary/mcp/dylibResolutionMcp.test.ts tests/boundary/cli/productCatalog.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/acceptance/contractPresentation.test.ts …`: passed. - `npm run docs:generate && npm run docs:check`, `npx oxfmt --check .`, `npm run lint`: no new errors. - `npm run verify:macos-bundle`: `{"ok":true,…,"dylib_resolution":{"roots":7,"edges":22,"otool_slices":11,"dyld_runtime_roots":2}}`. ## Compatibility, safety, and release - Breaking changes: none; this adds a tool. - Real Hopper/Ghidra coverage: not applicable, because no provider is touched. - Real OS coverage: macOS 26.6.2 arm64 locally, and the macOS 14 CI job. - Security and containment: read-only. Files are opened `O_NOFOLLOW`, symlinks are resolved without leaving the analyzed root, and the lane only runs its own fixture binaries. 🤖 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-16 21:17:42 +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#2354
No description provided.