[PR #1426] [MERGED] fix(hopper): treat a directory launcher path as missing #2878

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

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1426
Author: @Fazmin
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/10/2026
Merged by: @morluto

Base: main ← Head: fix/hopper-directory-launcher


📝 Commits (1)

  • db2214d fix(hopper): treat a directory launcher path as missing

📊 Changes

4 files changed (+34 additions, -3 deletions)

View changed files

📝 src/application/Doctor.systemHost.test.ts (+13 -1)
📝 src/application/Doctor.ts (+2 -1)
📝 src/hopper/HopperProvider.ts (+4 -1)
📝 src/hopper/HopperProviderDiscovery.test.ts (+15 -0)

📄 Description

Summary

If HOPPER_LAUNCHER_PATH points at a directory, such as the Hopper Disassembler.app bundle instead of the launcher inside it:

  • rea providers reports Hopper as available.
  • rea doctor reports it healthy.
  • Analysis then fails with a misleading version_unresolved rejection.

Both probes now require a regular file, as the Ghidra launcher probe already does, so the misconfiguration is reported where the user can fix it. No issue is linked because this is a small, self-contained fix.

Problem and expected behavior

  • Trigger: HOPPER_LAUNCHER_PATH="/Applications/Hopper Disassembler.app", an easy mistake on macOS. Any directory triggers it.
  • Violated invariant: availability means there is a launchable executable. HopperProvider.inspectAvailability and Doctor's executableAvailable relied only on access(path, X_OK), and a searchable directory passes that check. The Ghidra probe in GhidraInstallation.ts already adds statSync(path).isFile().
  • Observed on macOS (arm64), with an empty Hopper Disassembler.app directory:
    Command Before After
    rea providers --json, Hopper availability available unavailable, executable_missing: "Hopper launcher is missing or not executable: …/Hopper Disassembler.app"
    rea doctor --json, hopper check ok: true, healthy ok: false, config_drift, remediation "Unset or update HOPPER_LAUNCHER_PATH to an executable Hopper launcher."
    rea analyze /bin/ls --provider hopper --json, Hopper rejection version_unresolved: "Provider version or analysis profile could not be resolved" executable_missing, with the configured path

Change and scope

  • src/hopper/HopperProvider.ts: after the existing X_OK check, require statSync(path).isFile(). A directory takes the existing executable_missing path, with its existing reason and diagnostics.
  • src/application/Doctor.ts: executableAvailable returns false for a non-regular file before the Linux ldd probe.

Relative launcher paths, symlinks to a launcher (stat follows links), Linux behavior for real launchers, and all other availability codes are unchanged.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Hopper provider availability probe and Doctor's executable probe
  • CLI and MCP/tool-catalog contract: no schema change; a directory launcher now reports the existing executable_missing / config_drift outcomes
  • Provider, bridge, target-format, or platform compatibility: macOS and Linux Hopper hosts; real launcher files are unaffected
  • Evidence, artifact, provenance, or reconstruction contract: none
  • Process execution, authorization, cleanup, or containment impact: none; Hopper is not started by either probe
  • Generated metadata, package, or installation impact: none

Evidence and regression coverage

  • Tests added or updated:
    • src/hopper/HopperProviderDiscovery.test.ts: a directory launcher (dirname(process.execPath)) reports unavailable / executable_missing with the existing reason and diagnostics.
    • src/application/Doctor.systemHost.test.ts: systemDoctorHost(...).executable() returns false for a directory and true for process.execPath, without running any command.
  • Base reproduction: both new tests fail on the base revision and pass with this change. The CLI before/after table above was produced from the built CLI on each revision.
  • Remaining proof gaps: no real Hopper installation was used; neither probe starts Hopper.

Validation performed

All commands ran on macOS (arm64) with Node 24.18.0 and npm 11.16.0:

  • npm run test:local -- src/hopper/HopperProviderDiscovery.test.ts: 3 passed (new case fails on base).
  • npm run test:local -- src/application/Doctor.systemHost.test.ts: 6 passed (new case fails on base).
  • npm run test:local -- src/application/Doctor.test.ts: 24 passed.
  • npm run test:local -- src/hopper/HopperProvider.test.ts: 1 passed.
  • npm run check: passed.
  • npm run build:cached, then rea providers, rea doctor and rea analyze on the base and fixed revisions: output shown above.

Compatibility, safety, and release

  • Breaking changes or migration steps: none; a directory never worked as a launcher
  • Real Hopper/Ghidra, browser, or OS coverage: no real Hopper; macOS host only
  • Package or release metadata impact: none
  • Security, privacy, process, or containment review: none

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/1426 **Author:** [@Fazmin](https://github.com/Fazmin) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/10/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/hopper-directory-launcher` --- ### 📝 Commits (1) - [`db2214d`](https://github.com/morluto/rea/commit/db2214d12b8e0eb9a2e0f6dbdd76fe78e7e17ba4) fix(hopper): treat a directory launcher path as missing ### 📊 Changes **4 files changed** (+34 additions, -3 deletions) <details> <summary>View changed files</summary> 📝 `src/application/Doctor.systemHost.test.ts` (+13 -1) 📝 `src/application/Doctor.ts` (+2 -1) 📝 `src/hopper/HopperProvider.ts` (+4 -1) 📝 `src/hopper/HopperProviderDiscovery.test.ts` (+15 -0) </details> ### 📄 Description ## Summary If `HOPPER_LAUNCHER_PATH` points at a directory, such as the `Hopper Disassembler.app` bundle instead of the launcher inside it: - `rea providers` reports Hopper as **available**. - `rea doctor` reports it **healthy**. - Analysis then fails with a misleading `version_unresolved` rejection. Both probes now require a regular file, as the Ghidra launcher probe already does, so the misconfiguration is reported where the user can fix it. No issue is linked because this is a small, self-contained fix. ## Problem and expected behavior - **Trigger:** `HOPPER_LAUNCHER_PATH="/Applications/Hopper Disassembler.app"`, an easy mistake on macOS. Any directory triggers it. - **Violated invariant:** availability means there is a launchable executable. `HopperProvider.inspectAvailability` and Doctor's `executableAvailable` relied only on `access(path, X_OK)`, and a searchable directory passes that check. The Ghidra probe in `GhidraInstallation.ts` already adds `statSync(path).isFile()`. - **Observed on macOS (arm64), with an empty `Hopper Disassembler.app` directory:** | Command | Before | After | |---|---|---| | `rea providers --json`, Hopper availability | `available` | `unavailable`, `executable_missing`: "Hopper launcher is missing or not executable: …/Hopper Disassembler.app" | | `rea doctor --json`, `hopper` check | `ok: true`, `healthy` | `ok: false`, `config_drift`, remediation "Unset or update HOPPER_LAUNCHER_PATH to an executable Hopper launcher." | | `rea analyze /bin/ls --provider hopper --json`, Hopper rejection | `version_unresolved`: "Provider version or analysis profile could not be resolved" | `executable_missing`, with the configured path | ## Change and scope - **`src/hopper/HopperProvider.ts`:** after the existing `X_OK` check, require `statSync(path).isFile()`. A directory takes the existing `executable_missing` path, with its existing reason and diagnostics. - **`src/application/Doctor.ts`:** `executableAvailable` returns `false` for a non-regular file before the Linux `ldd` probe. Relative launcher paths, symlinks to a launcher (`stat` follows links), Linux behavior for real launchers, and all other availability codes are unchanged. ## Contract and boundary impact - Semantic owner and earliest changed stage: Hopper provider availability probe and Doctor's executable probe - CLI and MCP/tool-catalog contract: no schema change; a directory launcher now reports the existing `executable_missing` / `config_drift` outcomes - Provider, bridge, target-format, or platform compatibility: macOS and Linux Hopper hosts; real launcher files are unaffected - Evidence, artifact, provenance, or reconstruction contract: none - Process execution, authorization, cleanup, or containment impact: none; Hopper is not started by either probe - Generated metadata, package, or installation impact: none ## Evidence and regression coverage - **Tests added or updated:** - `src/hopper/HopperProviderDiscovery.test.ts`: a directory launcher (`dirname(process.execPath)`) reports `unavailable` / `executable_missing` with the existing reason and diagnostics. - `src/application/Doctor.systemHost.test.ts`: `systemDoctorHost(...).executable()` returns `false` for a directory and `true` for `process.execPath`, without running any command. - **Base reproduction:** both new tests fail on the base revision and pass with this change. The CLI before/after table above was produced from the built CLI on each revision. - **Remaining proof gaps:** no real Hopper installation was used; neither probe starts Hopper. ## Validation performed All commands ran on macOS (arm64) with Node 24.18.0 and npm 11.16.0: - `npm run test:local -- src/hopper/HopperProviderDiscovery.test.ts`: 3 passed (new case fails on base). - `npm run test:local -- src/application/Doctor.systemHost.test.ts`: 6 passed (new case fails on base). - `npm run test:local -- src/application/Doctor.test.ts`: 24 passed. - `npm run test:local -- src/hopper/HopperProvider.test.ts`: 1 passed. - `npm run check`: passed. - `npm run build:cached`, then `rea providers`, `rea doctor` and `rea analyze` on the base and fixed revisions: output shown above. ## Compatibility, safety, and release - Breaking changes or migration steps: none; a directory never worked as a launcher - Real Hopper/Ghidra, browser, or OS coverage: no real Hopper; macOS host only - Package or release metadata impact: none - Security, privacy, process, or containment review: none ## 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-17 21:11:19 +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#2878
No description provided.