Skip to content

fix(macos): re-land dylib-complete TCC interpreter anchor (salvage #95605) - #96647

Merged
kshitijk4poor merged 6 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/95605-tcc-anchor-reland
Aug 28, 2026
Merged

kshitijk4poor merged 6 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/95605-tcc-anchor-reland

Conversation

@kshitijk4poor

@kshitijk4poor kshitijk4poor commented Aug 27, 2026 •

Copy link
Copy Markdown

Summary

Salvage of #95605 (@zengzheqing) onto current main with a hardening follow-up: the dylib-complete re-land of the macOS TCC interpreter anchor, live-verified on real macOS hardware (Apple Silicon), plus fixes for three review findings around alias-failure observability and concurrency.

The contributor's three commits are cherry-picked with authorship preserved and apply cleanly (zero conflicts — no churn on these files since the PR branched).

What the anchor does (from #95605)

Re-land of the mechanism reverted in #95563, against that revert's criteria (dylib-complete design, live-verified on macOS hardware):

  1. venv/bin/python is a real-file copy of the uv-store interpreter, identifier-DR signed (com.nousresearch.hermes.managed-python) — TCC grants survive Python patch bumps ([Bug]: macOS permission prompts return after every update — TCC grants keyed to the uv-managed Python interpreter path, not the app bundle #85345).
  2. python3 / python3.N are real-file copies, never symlinks — symlink aliases lose the venv prefix on python-build-standalone builds ([Bug]: macOS TCC anchor re-points venv python3 aliases to symlinks that cannot find the stdlib — CLI dies with ModuleNotFoundError: encodings #95541).
  3. Store libpython* is hardlinked into venv/lib/ so @executable_path/../lib resolves — no more dyld death ([Bug]: macOS TCC anchor copies the uv interpreter into venv/bin without libpython — every hermes command dies in dyld #95425).
  4. Pre-install boot gate: the staged copy is actually launched (import encodings + sys.prefix == <venv>, scrubbed env) before os.replace. A bad anchor can never brick update/doctor again.
  5. hermes doctor reports/installs the anchor; hermes update refreshes it. The revert-era heal (check_macos_tcc_anchor_removed) is removed — first-generation broken copies are repaired in place.

Follow-up hardening (our commit, review findings on the salvage)

  • _copy_alias no longer swallows OSError silently — it warns and reports failure (a leftover alias symlink is the exact [Bug]: macOS TCC anchor re-points venv python3 aliases to symlinks that cannot find the stdlib — CLI dies with ModuleNotFoundError: encodings #95541 crash shape; previously invisible).
  • Alias staging uses mkstemp unique names so concurrent ensures (update + doctor --fix) can never promote a truncated interim copy.
  • The anchor marker is written last and atomically (write-then-rename): it now asserts the whole layout (anchor + aliases) is complete. A partially-materialized alias set can no longer read "active" in doctor — the next ensure retries instead.
  • The /.hermes-runtime/python/ store marker is derived from managed_uv._RUNTIME_DIR_NAME instead of a drift-prone hardcoded string.
  • 5 new regression tests covering each behavior.

Validation (live on macOS arm64)

Check Result
pytest test_macos_tcc_anchor.py test_doctor.py 104 passed (39 anchor incl. real-interpreter macos_only E2E)
Fresh anchor, dynamic cpython-3.12 uv store python/python3/python3.12 boot, venv prefix, libpython hardlinked (same inode)
Fresh anchor, static cpython-3.11.15 managed-runtime (the #95541 interpreter class) 3/3 entry points boot, signed identifier pinned
Reproduced first-gen brick (dyld Library not loaded @executable_path/../lib/libpython) repaired in place by ensure_tcc_anchor; all entry points boot after
Idempotency second ensure leaves bin/python byte-identical
Forced boot-gate refusal live venv untouched, zero staging leftovers, next ensure recovers
Forced alias failure (new path) anchor left unmarked, doctor won't report active, retry completes
pip + console scripts in anchored venv work
Store deleted mid-patch-bump interpreter still boots (hardlink survives)
ruff check + git diff --check clean

Field confirmation of the alias-copy approach: #95551 comment from @fred0m (production install, #95541 signature, recovered with the same mechanism). Reviewer findings from @kokhlo on #95605 (gate env scrub, EACCES refusal, marker realpath) were already addressed by the contributor with regression tests.

Credit

Anchor design and implementation by @zengzheqing (#95605) — commits cherry-picked with authorship preserved. Builds on @kokhlo's review (#95754 closed as duplicate) and the failure-mode mapping in #95358 / #95476 / #95551.

Closes #95596. Closes #95605. Refs #85345, #95425, #95541, #95563.

Post-review fold (/simplify-code on the final stack)

  • refactor(macos): _write_marker now uses the shared utils.atomic_write_text (repo's single atomic-text-write implementation, adds fsync + cross-device fallbacks) instead of a bespoke mkstemp/replace block.
  • Final-stack mutation check: reverting macos_tcc_anchor.py to the contributor base fails the 3 new-behavior guards and passes the sync-contract test (correct split); full suite 39/39 green after restore.
  • Reviewed non-findings: _RUNTIME_DIR_NAME private import is the right coupling (no public bare-name accessor; sync test binds it); +13ms lazy-import cost on hermes doctor only — not material.

kshitijk4poor and others added 5 commits August 28, 2026 02:38
The first landing (NousResearch#95131/NousResearch#95478, reverted in NousResearch#95563) copied the
uv-store interpreter into venv/bin/python so TCC grants would stick
to a stable path. On real Macs that copy bricked every hermes command
two ways: dynamically-linked builds died in dyld because
@executable_path/../lib/libpython resolved into venv/lib/ (NousResearch#95425),
and alias symlinks to the copy made CPython getpath lose the venv
prefix (NousResearch#95541, ModuleNotFoundError: encodings).

Re-land:

- Keep the signed real-file copy of bin/python (identifier-pinned
  via _macos_sign_managed_python).
- Materialize python3 / python3.N as real-file copies, never
  symlinks. Copies boot on every build we could reproduce and keep
  the TCC identity.
- Hardlink store libpython* into venv/lib/ when present (copy across
  devices). Existing LC_RPATH already points there.
- Pre-install boot gate: launch the staged copy, demand encodings
  plus the venv prefix, abort and leave the live venv untouched
  on failure.

Doctor reports/installs the new anchor (the revert-era heal is
removed). Update refreshes it after a successful code swap. Tests
cover layout, idempotence, predecessor-symlink repair, libpython
hardlink, boot-gate refusal, and a macos_only real-interpreter E2E.

Closes NousResearch#95596.
The gate is the never-brick guarantee, so each direction gets its own
test: nonzero exit (dyld/encodings crash), build-time prefix leak,
timeout, and the deliberate OSError skip (a binary that cannot execute
here means the symlinked venv was equally dead — installing cannot make
things worse).
Review fixes from kokhlo's live-hardware review:

- The boot-gate probe now runs with PYTHONHOME / PYTHONPATH /
  PYTHONSTARTUP / __PYVENV_LAUNCHER__ scrubbed: an inherited
  PYTHONHOME=<venv> boots a staged copy that would otherwise die with
  "No module named 'encodings'", papering over the exact prefix
  failure the gate exists to catch.
- OSError is split by errno: ENOENT/ENOEXEC (fixtures, foreign-arch
  images) still skip; EACCES after our own chmod now refuses the
  install instead of silently accepting a broken copy.
- Marker writes and both marker comparisons go through os.path.realpath,
  so the managed-runtime layout (cpython-3.11-macos-* symlinked to
  cpython-3.11.15-macos-*) no longer reports stale on a fresh install.

Tests: +3 (env-scrub spy, EACCES refusal, symlinked-home state).
100 passed in the module + doctor neighborhoods.
…er-last

Follow-up to the NousResearch#95605 salvage, closing the review findings:

- _copy_alias no longer swallows OSError silently: it warns (a leftover
  alias symlink is the exact NousResearch#95541 crash shape) and reports failure.
- Alias staging uses mkstemp (unique names) so concurrent ensures
  (update + doctor --fix) can never promote a truncated interim copy.
- The anchor marker is written LAST and atomically (write-then-rename):
  it now asserts the whole layout (anchor + aliases) is complete, so a
  partially-materialized alias set can never read 'active' in doctor —
  the next ensure retries the install instead.
- /.hermes-runtime/python/ store marker is derived from
  managed_uv._RUNTIME_DIR_NAME instead of a hardcoded string.

5 new regression tests.
@kshitijk4poor
kshitijk4poor force-pushed the salvage/95605-tcc-anchor-reland branch from f3e20e2 to e447609 Compare August 27, 2026 21:09
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/cli CLI entry point, hermes_cli/, setup wizard area/install-update Installer, updater, packaging, wheels, doctor sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Aug 27, 2026
@zengzheqing

Copy link
Copy Markdown

Verified the hardening commit (e447609) — all four changes are sound. Making alias failures visible is right (a leftover symlink is exactly the #95541 crash shape); the atomic marker-last write closes the torn-read/partial-layout window; mkstemp staging removes the concurrent-ensure collision; deriving the store marker from _RUNTIME_DIR_NAME kills the drift risk. Thanks for the salvage — happy to see this land.

/simplify-code reuse finding: _write_marker reimplemented the
mkstemp->write->os.replace pattern that utils.atomic_write_text already
provides as the repo's shared atomic-text-write helper (and the shared
version adds fsync + cross-device/busy-file fallbacks).
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 28, 2026 03:30
@kshitijk4poor
kshitijk4poor merged commit 9c87a7c into NousResearch:main Aug 28, 2026
35 checks passed
melon-xf added a commit to melon-xf/hermes-agent that referenced this pull request Sep 3, 2026
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 P2 Medium — degraded but workaround exists sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

re-land: dylib-complete macOS TCC interpreter anchor (per #95563 criteria)

3 participants