Repository navigation
view shell live code: repoint already-bound symbol pointers, so a patch changes what draws - #35850
Conversation
… so a patch changes what draws dyld_dynamic_interpose does not rewrite a lazy or GOT pointer dyld has already bound, so a view's body that SwiftUI called before a patch kept running the build's code: every generation of the headless measurement drew the same badge (the PNGs differed in bytes only). Interposer.rebind writes the patch's address into each __la_symbol_ptr and __got slot that names a replaced function, as fishhook does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-45fddab7397278d38551
|
cmux status (one comment, edited in place; each line is updated by the job that owns it) Review App of this head (app.zip, kept 1 day)
Shipped in:
|
|
Review (codex, router-review): SHIP at 267eac1 Reviewer: codex gpt-6-astra high, fast tier. Follow-ups: 2. They land as checklist items in this PR's one follow-up tracking issue in this repo ( SHIP at 267eac1 Visual verdict: no images; Leo tries the build
Follow-ups:
|
|
Pictures from the headless measurement (
|
…ve_apply finds @main among other attributes (#35861) Review follow-ups of #35850 (rebind ignored a refused mprotect) and #35705 (`@MainActor @main` was counted live). Agent-Session: https://cmux.com/deeplink/agent?id=aid-45fddab7397278d38551 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…G bytes (#35862) * live-code-shell measurement: judge a redraw by the badge's rendered width, not PNG bytes The same pixels encode to PNGs of different bytes, so cmp said "redrew" while every generation drew the build's badge (#35850). Also: refuse to run over an existing probe file, stop waiting on the new-file step once a refusal is logged, report an inject.sh failure as such, and record the widths in result.json. Review follow-ups of #35759. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-45fddab7397278d38551 * live-code-shell measurement: refuse a dangling probe link, report step 4's inject failure, name a missing width Review findings of #35862 at 803b764. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-45fddab7397278d38551 * live-code-shell measurement: restore only this run's Badge.swift backup; check the probe before building Review finding of #35862 at fd073c8: a reused OUT's older Badge.swift.orig was copied back on exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-45fddab7397278d38551 --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…oof; Debug and bench only) (#36118) * live code in the app: a patch of CmuxNextApp's Swift shows in a running bench build Leo, 2026-11-05: "if we're literally just trying to prove a thing works / please don't frontrun it with belt and suspenders / just get it working / THEN we go" The proof, before the trust check: the view shell's loader (#35314, #35512, #35850, #35861) in the app as CmuxNextLiveCode, started only by CMUX_LIVE_CODE_DIR; reload.sh's CMUX_LIVE_CODE_BUILD=1 builds CmuxNextApp unoptimized and testable and links the app -interposable with its symbols exported; scripts/live-code/patch.py compiles edited files with the frontend command of that build; scripts/measure/live-code-app.sh times two patches of the vault list's background end to end and counts their pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * arch: a reviewed exception for live code's one watcher instance (proof) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * live code: Observation, and the folder's disk work off the main thread through the checked seams Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * live code: only Debug and bench builds watch the live folder, until the signed pointer check Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * live code: import Observation publicly (LiveCode's @observable conformance is public) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * live code: patch AppKit drawing and redraw every window; the app delegate owns the one watcher No new SwiftUI file (the no-SwiftUI allowlist only shrinks): the proof patches HomeHostView's updateLayer, and after each patch every window's views are invalidated and drawn. The app delegate holds the instance, so there is no shared one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e * settings: CmuxConfigFile.launchURL() holds the app's settings-file bootstrap (moved out of AppDelegate) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Agent-Session: https://cmux.com/deeplink/agent?id=aid-0d93125da077eb6bbe1e --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>




Leo (2026-11-04): "Couldn't we architect stuff to make everything more amenable to hot swappage?"
The view shell's live code never changed what it drew. In the headless measurement (
scripts/measure/live-code-shell.sh), every generation's badge was the same picture, grey with padding 8. Each patch loaded and replaced its functions (functions=4), but the drawing still ran the build's code. The measurement's "second patch redrew: yes" came fromcmpon PNG bytes (2,036 vs 2,044 bytes for the same pixels).Why. The executable is linked
-interposable -no_fixup_chains, so its calls to its own Swift functions go through classic symbol pointers. One of them is__la_symbol_ptrforPreviewBadge.body(dyld_info -fixups:lazy-bind PreviewBadge.body). Generation 0 draws before any patch, so by then dyld has bound that pointer to the build'sbody, anddyld_dynamic_interposedoes not rewrite a pointer that is already bound.Fix. After the existing
dyld_dynamic_interposecall,Interposer.rebindwrites the patch's address into every__la_symbol_ptrand__gotslot of the executable that names a replaced function, as fishhook does.__DATA_CONSTis made writable for the write. The existing interpose call stays as it is.Measured on a fleet Mac at this fix plus #35705.
inject.shwas the only other change, to tar the PNGs out. The badge's rendered width, at 2× scale:@StateI checked the PNGs by eye as well. Edit to pixels: 1.45 s for the first patch and 1.14 s for the next.
Follow-up: the measurement should compare decoded pixels or widths, not bytes. It is a separate PR because the fleet runs only the reviewed bytes of a measurement script.
Part of manaflow-ai/cmuxterm-hq#7066.
🤖 Generated with Claude Code
Agent session: agent session
Open in [code]smith · Need help on this PR? Tag
@codesmith-botwith what you need.Summary by cubic
Fixes the view shell's live code so a patch actually changes what draws: previously every generation of the headless measurement drew the same badge, because
dyld_dynamic_interposedoes not rewrite a symbol pointer dyld has already bound (the build's view had been drawn before any patch).Interposer.rebind, which after the interpose call writes the patch's address into every__la_symbol_ptrand__gotslot that names a replaced function, as fishhook does.__DATA_CONSTwritable for the write viamprotect.Migration
-interposable -no_fixup_chainslinking is required for the rebind to be possible; that is already in place.Written for commit 267eac1. Summary will update on new commits.