[PR #2795] [MERGED] fix(hopper): recover from a Spotlight not-found launch #5399

Closed
opened 2026-10-31 01:06:20 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/2795
Author: @bruno-candia
Created: 10/13/2026
Status: ✅ Merged
Merged: 10/13/2026
Merged by: @morluto

Base: main ← Head: fix/hopper-spotlight-not-found


📝 Commits (3)

  • 044c0b7 fix(hopper): recover from a Spotlight not-found launch
  • e073417 fix(hopper): require empty stdout for native launch signatures
  • 03f907d fix(hopper): retain ownership after unverified Spotlight reports

📊 Changes

5 files changed (+104 additions, -9 deletions)

View changed files

📝 src/hopper/HopperClient.ts (+8 -0)
📝 src/hopper/HopperProcessDiagnostic.test.ts (+47 -1)
📝 src/hopper/HopperProcessDiagnostic.ts (+25 -2)
📝 tests/acceptance/providers/hopperStartupRecovery.test.ts (+18 -6)
📝 tests/fixtures/hopperLaunchAdmission.mjs (+6 -0)

📄 Description

Summary

Hopper's native launcher locates the application through a Spotlight query before sending any Apple event. When Spotlight indexing is disabled for the volume, the launcher exits 3 with Hopper has not been found…. REA treated that external document as unconfirmed, kept the runtime, and every later open_binary failed until the MCP server restarted. Refs #1808: same family (a failed launch leaves the session unusable), but #1808 covers a different exit, so this does not close it. This follows the recovery pattern of #1817 and completes #1105, which made the Spotlight error visible but still retained the runtime.

Problem and expected behavior

  • Trigger: open_binary with Spotlight indexing off (mdutil -s / reports disabled).
  • Violated invariant: a launch that fails before handoff must leave the session reusable.
  • New behavior: REA recognizes the complete not-found report as a pre-handoff failure, confirms that no document exists, and returns a message asking the caller to enable Spotlight indexing (mdutil -i on). A later open_binary in the same session works once Hopper can be found.

Change and scope

  • nativeHopperApplicationWasNotFound in src/hopper/HopperProcessDiagnostic.ts.
  • New branch in HopperClient.#launcherStartupFailure that marks shutdown as confirmed.
  • Not included: locating Hopper without Spotlight.

Ordinary fix within existing boundaries; maintainer scope decision: not applicable.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Hopper launcher diagnostics (src/hopper).

  • Contribution scope fit and linked maintainer decision: bug fix; not applicable.

  • CLI and MCP/tool-catalog contract: no schema change; new error message text for this case.

  • Provider, bridge, target-format, or platform compatibility: macOS Hopper launcher only.

  • Evidence, artifact, provenance, or reconstruction contract: none.

  • Target selection, authority, upstream behavior, and cleanup details: cleanup now completes for this failure, so the runtime is not retained.

  • Generated metadata, package, or installation impact: none.

  • Launch or attach to a process.

Evidence and regression coverage

  • Tests added or updated: unit cases in src/hopper/HopperProcessDiagnostic.test.ts; acceptance test recovers a Spotlight not-found Hopper launch without restarting the MCP session in tests/acceptance/providers/hopperStartupRecovery.test.ts, using a new not-found mode in tests/fixtures/hopperLaunchAdmission.mjs.

  • Base reproduction: with HopperClient.ts and HopperProcessDiagnostic.ts checked out from main, the new acceptance test fails; with the change it passes.

  • Observed on a real machine: with indexing disabled, every open_binary failed until the server restarted; after enabling indexing, Hopper was found.

  • Remaining proof gaps: retains a denial-shaped failure when caller arguments can alter the helper's AppleScript in the same acceptance file fails locally on main as well (ENOENT on loader-target during teardown). It is unrelated to this change, but I could not confirm whether it is specific to my environment.

  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm run test:focused -- src/hopper/HopperProcessDiagnostic.test.ts tests/acceptance/providers/hopperStartupRecovery.test.ts — 18 passed, 1 failed (the pre-existing test above)
  • Same command with the source files from main — the new acceptance test fails as well
  • npm run check:fast — typecheck and lint passed
  • npm run verify:hopper (macOS, Hopper Disassembler 6.6.0-demo) — launch and conformance fixtures verified ("verified":7), then the run stops at AssertionError: fixture omitted its real long literal in scripts/lib/real-hopper-search.mjs:127. The same assertion fails on main (d85374496) on this host, so it is pre-existing and appears tied to string recognition in the demo build, not to the launcher path changed here.

Compatibility, safety, and release

  • Breaking changes or migration steps: none.
  • Real Hopper/Ghidra, browser, or OS coverage: macOS with Hopper Disassembler; launcher behavior reproduced with Spotlight off and on.
  • Package or release metadata impact: none.
  • Security, privacy, process, or containment review: no new process interaction; the change only classifies an existing launcher exit.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • 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/2795 **Author:** [@bruno-candia](https://github.com/bruno-candia) **Created:** 10/13/2026 **Status:** ✅ Merged **Merged:** 10/13/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/hopper-spotlight-not-found` --- ### 📝 Commits (3) - [`044c0b7`](https://github.com/morluto/rea/commit/044c0b72256c61c2ffea4b0af36d94ed95e08c55) fix(hopper): recover from a Spotlight not-found launch - [`e073417`](https://github.com/morluto/rea/commit/e0734170091cfaa22dcdf29adf7dcf6f2fc5195d) fix(hopper): require empty stdout for native launch signatures - [`03f907d`](https://github.com/morluto/rea/commit/03f907df680042789c1956996cf7845ff850776a) fix(hopper): retain ownership after unverified Spotlight reports ### 📊 Changes **5 files changed** (+104 additions, -9 deletions) <details> <summary>View changed files</summary> 📝 `src/hopper/HopperClient.ts` (+8 -0) 📝 `src/hopper/HopperProcessDiagnostic.test.ts` (+47 -1) 📝 `src/hopper/HopperProcessDiagnostic.ts` (+25 -2) 📝 `tests/acceptance/providers/hopperStartupRecovery.test.ts` (+18 -6) 📝 `tests/fixtures/hopperLaunchAdmission.mjs` (+6 -0) </details> ### 📄 Description ## Summary Hopper's native launcher locates the application through a Spotlight query before sending any Apple event. When Spotlight indexing is disabled for the volume, the launcher exits 3 with `Hopper has not been found…`. REA treated that external document as unconfirmed, kept the runtime, and every later `open_binary` failed until the MCP server restarted. Refs #1808: same family (a failed launch leaves the session unusable), but #1808 covers a different exit, so this does not close it. This follows the recovery pattern of #1817 and completes #1105, which made the Spotlight error visible but still retained the runtime. ## Problem and expected behavior - Trigger: `open_binary` with Spotlight indexing off (`mdutil -s /` reports disabled). - Violated invariant: a launch that fails before handoff must leave the session reusable. - New behavior: REA recognizes the complete not-found report as a pre-handoff failure, confirms that no document exists, and returns a message asking the caller to enable Spotlight indexing (`mdutil -i on`). A later `open_binary` in the same session works once Hopper can be found. ## Change and scope - `nativeHopperApplicationWasNotFound` in `src/hopper/HopperProcessDiagnostic.ts`. - New branch in `HopperClient.#launcherStartupFailure` that marks shutdown as confirmed. - Not included: locating Hopper without Spotlight. Ordinary fix within existing boundaries; maintainer scope decision: not applicable. ## Contract and boundary impact - Semantic owner and earliest changed stage: Hopper launcher diagnostics (`src/hopper`). - Contribution scope fit and linked maintainer decision: bug fix; not applicable. - CLI and MCP/tool-catalog contract: no schema change; new error message text for this case. - Provider, bridge, target-format, or platform compatibility: macOS Hopper launcher only. - Evidence, artifact, provenance, or reconstruction contract: none. - Target selection, authority, upstream behavior, and cleanup details: cleanup now completes for this failure, so the runtime is not retained. - Generated metadata, package, or installation impact: none. - [x] Launch or attach to a process. ## Evidence and regression coverage - Tests added or updated: unit cases in `src/hopper/HopperProcessDiagnostic.test.ts`; acceptance test `recovers a Spotlight not-found Hopper launch without restarting the MCP session` in `tests/acceptance/providers/hopperStartupRecovery.test.ts`, using a new `not-found` mode in `tests/fixtures/hopperLaunchAdmission.mjs`. - Base reproduction: with `HopperClient.ts` and `HopperProcessDiagnostic.ts` checked out from `main`, the new acceptance test fails; with the change it passes. - Observed on a real machine: with indexing disabled, every `open_binary` failed until the server restarted; after enabling indexing, Hopper was found. - Remaining proof gaps: `retains a denial-shaped failure when caller arguments can alter the helper's AppleScript` in the same acceptance file fails locally on `main` as well (ENOENT on `loader-target` during teardown). It is unrelated to this change, but I could not confirm whether it is specific to my environment. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm run test:focused -- src/hopper/HopperProcessDiagnostic.test.ts tests/acceptance/providers/hopperStartupRecovery.test.ts` — 18 passed, 1 failed (the pre-existing test above) - Same command with the source files from `main` — the new acceptance test fails as well - `npm run check:fast` — typecheck and lint passed - `npm run verify:hopper` (macOS, Hopper Disassembler 6.6.0-demo) — launch and conformance fixtures verified (`"verified":7`), then the run stops at `AssertionError: fixture omitted its real long literal` in `scripts/lib/real-hopper-search.mjs:127`. The same assertion fails on `main` (d85374496) on this host, so it is pre-existing and appears tied to string recognition in the demo build, not to the launcher path changed here. ## Compatibility, safety, and release - Breaking changes or migration steps: none. - Real Hopper/Ghidra, browser, or OS coverage: macOS with Hopper Disassembler; launcher behavior reproduced with Spotlight off and on. - Package or release metadata impact: none. - Security, privacy, process, or containment review: no new process interaction; the change only classifies an existing launcher exit. ## Before merge - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [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 01:06:20 +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#5399
No description provided.