Skip to content

Fix CI: unit-test failures reported green, change-detection dying on PRs#122

Merged
arzafran merged 21 commits into
mainfrom
fix/ci-guard-and-change-detection
Jul 13, 2026
Merged

Fix CI: unit-test failures reported green, change-detection dying on PRs#122
arzafran merged 21 commits into
mainfrom
fix/ci-guard-and-change-detection

Conversation

@arzafran

@arzafran arzafran commented Jul 10, 2026

Copy link
Copy Markdown
Member

What this does

Fixes CI so test failures actually fail the build again, then fixes everything that surfaced: 109 unit tests were failing silently — some for months — because the old workflow treated assertion failures as "expected" and reported green. Along the way this fixes ~25 real shipped bugs the greenwashing hid, including zsh shell integration never loading at all in shipped builds.

Summary

  • CI: run_suite exit-code capture, guard-test stub, change-detection --depth=1 "no merge base", serial unit-test scope
  • Shipped product bugs fixed: zsh integration filename + cmux CLI fallback (rebrand leftovers), use-after-free in surface inheritance, DevTools double-show + stuck transitions, goBack page skip, chord-shortcut stale cache, Cmd+N hijacked by leaked omnibar focus pointer, malformed PR filtering, closeWorkspace ownership, notification focus clobber + missing clear, palette typo scoring, telemetry report_tty fallback, portal hit-test bypass + lifecycle bugs, TerminalSurfaceRegistry memory leak (NSHashTable doesn't weak-store pure Swift), theme migration skipped via registration domain, shift-backquote tilde
  • Test repairs with evidence: rebrand-stale fixtures, retired nightly channel, AppKit teardown-order assumptions, cross-suite state leaks (AppDelegate.shared clobber, UserDefaults pollution, leaked surfaces)
  • Includes the NSGlassEffectView availability-guard modernization (cherry-picked from Use NSGlassEffectView directly behind availability guards #120) so macos-26 compat passes

Review attention

  • hitTest gating in both window portals inverted to a keyboard-only fast path (typing latency preserved; broader routing coverage) — the typing-lag CI job covers this
  • CLAUDE.md's pitfall wording about the pointer-event guard needs a follow-up edit to match

Test Plan

  • Full serial unit suite locally: 1446 tests, worst run 4 failures (focus/key-window timing family), all suites green scoped
  • CI green on fixed pipeline

arzafran added 21 commits July 10, 2026 15:55
run_suite read $? after a branchless 'if', which is always 0, so any
XCTest failure exited 0 and CI reported red suites as green. Capture
the status directly with '|| exit_code=$?'.

Also fix the guard test's xcodebuild stub: its split-stateful case
fabricated two log lines per invocation, so the runner's two real
passes logged four calls and the guard failed even on correct behavior.
Log the actual arguments once per invocation instead.
The --depth=1 fetch of the base and head SHAs grafts away their parent
history, so 'git diff BASE...HEAD' dies with 'no merge base' on every
pull request and all gated jobs skip. The checkout already uses
fetch-depth: 0, so a plain fetch is correct and effectively free.
The split-stateful parallel mode landed in the same commit as the
exit-code bug that reported failing suites as green, so it has never
had an honestly-reported passing run. With failures propagating again,
the parallel pass fails dozens of AppKit-adjacent tests across multiple
test runner processes on both macos-15 and macos-26. Return to the
serial scope that was green before the split; re-enable parallelism
separately once the suite is parallel-safe.
- .zshenv still sourced the pre-rebrand cmux-zsh-integration.zsh, so
  local zsh shell integration never loaded in shipped builds
- inheritedTerminalConfig read a raw surface pointer instead of the
  quarantining liveSurfaceForGhosttyAccess accessor (use-after-free risk)
- developer-tools reveal called show twice and left transition state
  in flight after synchronous settles, silently queueing later toggles
- goBack skipped a page when a live navigation moved past both restored
  history stacks
- the chord-shortcut action cache was refreshed on an async notification
  queue, so it was stale at the moment a just-configured chord was used
Rebrand leftovers (bash integration path, __cmuxFind* JS globals,
'cmux profiles' copy, half-renamed fixture path), intentional behavior
changes (no nightly channel, per-tag debug sockets, alpha rounding,
flash ring metrics), and CLI notify mocks that predate the v2
notification.create_for_target RPC.
Both shell-integration scripts still looked for a 'cmux' binary when
PROGRAMA_BUNDLED_CLI_PATH was unset, so relay RPCs (TTY reporting,
port-scan kicks) silently no-oped for any shell without the bundled
CLI env var.
- report-TTY expectation updated to the v2 surface.report_tty JSON the
  script actually sends (still asserting no panel scope)
- the TERM-preservation test's recorder ran on every precmd and
  truncated its own log; make it one-shot
- the bash preexec test polled until the log was merely non-empty and
  read it before the background ports_kick RPC landed; poll for the
  second RPC instead
- toggleDeveloperTools now keeps a short settle window in flight even
  when the transition completed synchronously, so rapid toggle bursts
  coalesce to the final intent instead of hitting the inspector per call
- createFloatingPopup copies the opener's WKProcessPool so popups share
  the opener's browsing context
BrowserProfileStore persists lastUsedProfileID into UserDefaults.standard,
so profile-mutating tests (and prior runs on the same machine) leaked a
non-default profile into BrowserPanelRemoteStoreTests, which then resolved
a profile-scoped data store instead of .default(). Add a DEBUG-only
replaceSharedForTesting seam and give the suite an ephemeral defaults
suite per test.
…r insert-at-end

- preferredPullRequest accepted 'not a url' because URL(string:) parses
  relative paths; require absolute http(s) with a host
- poll-candidate exclusion for in-flight probes wrongly filtered panels
  with confirmed metadata; scope it to directory rediscovery only
- closeWorkspace tore down workspaces it never owned; guard on tabs
  membership
- insert-at-end browser placement hit Bonsplit's insertion-index no-op
  short-circuit; use tabs.count as the insertion index
The install hop only writes and chmods the staged bootstrap script; a
login shell would source remote rc files as a side effect. Production
has sent /bin/sh -c here since before the CLI split.
…on close

- focusTabFromNotification now begins its own focus transition so an
  older pending FocusTransitionCoordinator completion no-ops instead of
  reapplying its stale captured panel on the next run-loop turn
- closePanelWithConfirmation clears the closed surface's notifications
  unconditionally; the previous gating skipped the clear whenever the
  last-surface close escalated to workspace/window close
…try fallback

- raise the tokenExtraCharacter penalty so a one-character omission from
  a real word outranks an extra character after a shorter command
- pending empty-state reuse assumed query monotonicity, which is false
  for a typo-tolerant matcher; only exact query matches may reuse it
- branch search tokens now include the last path segment with hyphens
  preserved, matching how directory tokens already index
- v2ScheduleTelemetryMutation gains the same self.tabManager fallback
  every other v2 resolution path has, so report_tty works for directly
  registered managers
- testRenameScoresHigherThanUnrelatedCommand asserted a fuzzy match that
  no strategy can produce (sibling test asserts nil for the same shape);
  assert nil explicitly
- hitTest in both host views now fast-paths only keyboard events and
  runs full divider/pass-through routing for everything else, so
  programmatic and non-pointer hit-tests stop silently bypassing it
- browser divider hits prefer hosted-inspector regions when an app-level
  divider coincidentally overlaps; terminal-side divider grab shrinks to
  2pt next to hosted terminal content so column-0 selection wins
- prune detaches anchorless hosted views; reveal marks WebKit rendering
  state for reattach without firing the heavy lifecycle pair; fully
  orphaned anchors hide immediately instead of inheriting the reparent
  grace period
- drag-handle capture resolves the window-level top hit (with per-window
  re-entrancy scoping) so a top-hit titlebar container blocks capture
- overlay focus no longer requires AppDelegate registration when none
  exists; keeps the cross-tab ownership protection
- deferred overlay mount strongly captures its surface for the closure's
  lifetime; keydown recovery clears desired focus when windowless
- shift-backquote ESC fallback checks the original event before Ghostty
  mods-translation destroys the signal
- test fixes with evidence: visibility-policy test contradicted its
  deterministic sibling since introduction; keydown test asserted a
  first-responder teardown order AppKit never provided; prune test now
  drains the anchor inside an autoreleasepool
Replace NSClassFromString lookups and the private setTintColor: selector
with compile-time NSGlassEffectView usage under #if compiler(>=6.2) +
@available(macOS 26.0, *), matching the pattern the ghostty submodule
already uses.

This also fixes window-level Liquid Glass never activating: the old code
cast NSGlassEffectView to NSVisualEffectView.Type, which fails at runtime
because NSGlassEffectView subclasses NSView, so the fallback path always
ran even on macOS 26.
…generation

- unregisterMainWindow clears the app-wide browserAddressBarFocusedPanelId
  when no live window owns the panel; the leaked pointer hijacked
  Cmd+N/Cmd+P in every other window through the omnibar-navigation path
- orphaned main-window contexts are pruned before context selection, not
  only when resolution fails
- closeWindowWithConfirmation uses close() (performClose is a no-op
  without a key window); titlebar accessory detaches in minimal mode
- setSearchOverlay only bumps the mutation generation when it actually
  mutates, so a redundant reactive call can't cancel the pending focus
  retry
- test-side: wire the existing key-window test override, correct an
  AppKit teardown-order assumption, bound-retry drift installation
  against in-flight focus reasserts, fix a decoy-overlay setup that
  never reached production code
- addWorkspace stopped consulting inheritedTerminalConfigForNewWorkspace
  long ago; the method had zero production callers and existed only for
  a test override. Delete it and point the sanitization test at the real
  fontSize-only inheritance path, preserving every assertion
- sidebar PR display has iterated all panels since its introduction and
  the poll-candidate machinery depends on background panels; rewrite the
  focused-only test to assert the actual contract (all panels in sidebar
  order, branch-mismatched PRs filtered)
- TerminalSurfaceRegistry used NSHashTable.weakObjects(), which does not
  reliably weak-store pure Swift classes — every registered surface was
  permanently retained. Track surfaces in Swift-native weak boxes
- BrowserPanelView's register(defaults:) landed in the process-wide
  registration domain, making BrowserThemeSettings.mode() skip the
  legacy forced-dark-mode migration for any defaults instance in the
  process, including real users'; consult the persistent domain instead
- save/restore AppDelegate.shared in suites that construct throwaway
  delegates; the clobbered singleton emptied the chord-action cache for
  every later suite
- release leaked ghostty test surfaces via the existing helper; replace
  fixed 50ms sleeps with bounded polling
- guard an unguarded removeFirst() that crashed the whole test process
  under timing contention
@arzafran arzafran merged commit 18b402b into main Jul 13, 2026
4 of 7 checks passed
@arzafran arzafran deleted the fix/ci-guard-and-change-detection branch July 13, 2026 20:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant