Skip to content

fix(macos): stable TCC anchor for uv-managed python interpreter - #85416

Closed
webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/85345-macos-tcc-anchor
Closed

webtecnica wants to merge 1 commit into
NousResearch:mainfrom
webtecnica:fix/85345-macos-tcc-anchor

Conversation

@webtecnica

Copy link
Copy Markdown

Summary

On macOS, TCC permission grants (microphone, screen recording, etc.) are keyed to the resolved client binary path. The uv-managed Python interpreter lives in ~/.local/share/uv/python/cpython-<patch>-macos-*/bin/python3.11 — every patch bump creates a new directory → orphaned grants → permission prompts return after every update. Symlinks don't help (TCC resolves through them).

Change (stable anchor — "copy to fixed path before invoking")

  • hermes_cli/macos_tcc_anchor.py (new, +277): replaces the venv/bin/python symlink (which resolves into the versioned uv store) with a real copy of the interpreter binary. The venv path is stable across updates and, being a regular file, there's no symlink for TCC to resolve → constant client path. pyvenv.cfg (home → uv store) untouched, so stdlib still loads as today. Self-healing: detects patch bump via .tcc-anchor-source marker (re-copies) and re-points python3/python3.11 aliases to the anchor. No-op on non-macOS and non-uv interpreters (Homebrew already has a stable path). Best-effort, never raises.
  • hermes_cli/doctor.py (+52): reports anchor state (healthy / stale / would-fix).
  • hermes_cli/update_cmd.py (+15): runs the anchor heal after update.
  • tests/hermes_cli/test_macos_tcc_anchor.py (new, +296): uv-store detection, patch-bump re-copy, symlink replacement, no-op cases (monkeypatched — real TCC behavior not testable on Linux CI).

Verification

  • tests/hermes_cli/test_macos_tcc_anchor.py: 20 passed. Real macOS validation left to review (CI is Linux).

Closes #85345

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard area/install-update Installer, updater, packaging, wheels, doctor labels Aug 13, 2026
@Enough1122

Copy link
Copy Markdown

AI code review — automated review for reference, author can ignore or act on any point.

fix(macos): stable TCC anchor for uv-managed python interpreter

Good design (real-file copy + pyvenv.cfg home keeps the stdlib source; atomic temp+rename; best-effort with doctor/update integration). Points to consider:

  • Test gap: tests/hermes_cli/test_macos_tcc_anchor.py exercises fake text "binaries" only. Real-binary concerns — code-signature preservation after shutil.copy2 (macOS requires at least ad-hoc signatures) and dylib/framework resolution from the new venv path — are untested. Recommend a smoke test on real macOS hardware (venv/bin/python -c "import sys" after anchoring, and again after a simulated patch bump).
  • _SIBLING_NAMES / _STORE_BIN_NAMES hardcode 3.11–3.13. A uv-managed 3.10 or 3.14+ interpreter leaves its python3.x alias symlink resolving into the versioned store — anything spawned through the alias still churns TCC. Consider deriving the version from the resolved interpreter instead of a fixed list.
  • ensure_tcc_anchor reads _interpreter_source and then os.replaces the copy; if a concurrent uv sync rewrites bin/python in between, the anchor can pin a stale copy. The marker-based self-heal on the next hermes update / doctor --fix covers it, but re-reading the source immediately before replace (or comparing the marker first) would close the window.

teknium1 added a commit that referenced this pull request Aug 26, 2026
Follow-ups on the #85416 salvage:
- _store_bin_names()/_sibling_names() derive the versioned interpreter name
  from the RUNNING interpreter instead of a hardcoded 3.11-3.13 list, with a
  sorted python3.* glob fallback for stores/aliases built by a different
  Python minor (major bumps, fixtures) — no future version bump can silently
  leave an alias resolving back into the versioned uv store.
- _repoint_alias_symlinks unions expected alias names with the versioned
  symlinks actually on disk before repointing.
teknium1 added a commit that referenced this pull request Aug 26, 2026
Follow-ups on the #85416 salvage:
- _store_bin_names()/_sibling_names() derive the versioned interpreter name
  from the RUNNING interpreter instead of a hardcoded 3.11-3.13 list, with a
  sorted python3.* glob fallback for stores/aliases built by a different
  Python minor (major bumps, fixtures) — no future version bump can silently
  leave an alias resolving back into the versioned uv store.
- _repoint_alias_symlinks unions expected alias names with the versioned
  symlinks actually on disk before repointing.
@teknium1

Copy link
Copy Markdown
Collaborator

Merged via #95131 (5259f56) with your commit cherry-picked intact — thanks @webtecnica! Follow-up on top: alias/store binary names are now derived from the running interpreter (with a glob fallback) instead of the hardcoded 3.11–3.13 lists, so future Python bumps can't leave an alias resolving back into the versioned store. The real-Mac smoke test (grant surviving a simulated patch bump) is flagged for maintainer verification on hardware.

and7777 pushed a commit to and7777/hermes-agent that referenced this pull request Aug 27, 2026
Follow-ups on the NousResearch#85416 salvage:
- _store_bin_names()/_sibling_names() derive the versioned interpreter name
  from the RUNNING interpreter instead of a hardcoded 3.11-3.13 list, with a
  sorted python3.* glob fallback for stores/aliases built by a different
  Python minor (major bumps, fixtures) — no future version bump can silently
  leave an alias resolving back into the versioned uv store.
- _repoint_alias_symlinks unions expected alias names with the versioned
  symlinks actually on disk before repointing.
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
Follow-ups on the NousResearch#85416 salvage:
- _store_bin_names()/_sibling_names() derive the versioned interpreter name
  from the RUNNING interpreter instead of a hardcoded 3.11-3.13 list, with a
  sorted python3.* glob fallback for stores/aliases built by a different
  Python minor (major bumps, fixtures) — no future version bump can silently
  leave an alias resolving back into the versioned uv store.
- _repoint_alias_symlinks unions expected alias names with the versioned
  symlinks actually on disk before repointing.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/install-update Installer, updater, packaging, wheels, doctor comp/cli CLI entry point, hermes_cli/, setup wizard P3 Low — cosmetic, nice to have type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: macOS permission prompts return after every update — TCC grants keyed to the uv-managed Python interpreter path, not the app bundle

4 participants