From 333e650098027f95b5bd14ec54a8cf0251750aa4 Mon Sep 17 00:00:00 2001 From: omartelo Date: Sat, 24 Oct 2026 14:52:01 -0300 Subject: [PATCH 1/2] fix(ci): count Dock tiles in the macOS end-to-end, not applications --- .github/scripts/mac-e2e.sh | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/scripts/mac-e2e.sh b/.github/scripts/mac-e2e.sh index 079fcaba6..bf9f0de0b 100755 --- a/.github/scripts/mac-e2e.sh +++ b/.github/scripts/mac-e2e.sh @@ -1,12 +1,12 @@ #!/bin/bash # Runs a freshly bundled Lich.app end to end on the macOS runner, the Windows # job's check on the bundle: the seal verifies, lich finds the window beside -# itself in Contents/MacOS and opens it, macOS counts exactly one application -# in Lich.app (lsappinfo, which is what the Dock reads: a second entry is a -# process with a Dock tile of its own), the window closes on SIGTERM, a close -# request like the user's, lich keeps running without it, a launch opens a new -# window on it, and `lich quit` ends both. Its config lives under a HOME of its -# own. +# itself in Contents/MacOS and opens it, Lich.app has exactly one Dock tile +# (lsappinfo, which is what the Dock reads: the window is its one Foreground +# application, the backend a UIElement behind its menu-bar item), the window +# closes on SIGTERM, a close request like the user's, lich keeps running +# without it, a launch opens a new window on it, and `lich quit` ends both. Its +# config lives under a HOME of its own. # What a failure leaves behind goes to $RUNNER_TEMP/diag for the artifact the # workflow uploads. # @@ -38,11 +38,14 @@ fail() { exit 1 } -# One application in Lich.app however many of its processes run. +# One Dock tile in Lich.app however many of its processes run: the window is +# the one Foreground application, and the backend stays a UIElement. one_app() { lsappinfo list > "$RUNNER_TEMP/apps.txt" - apps=$(grep -c 'bundle path=.*Lich.app' "$RUNNER_TEMP/apps.txt" || true) - [ "$apps" = 1 ] || { grep -B1 -A3 'Lich.app' "$RUNNER_TEMP/apps.txt"; fail "macOS counts $apps applications in Lich.app, expected the window alone"; } + apps=$(awk '/^ *[0-9]+\) /{own=0} /bundle path=.*Lich.app/{own=1} own && /type="Foreground"/{n++} END{print n+0}' "$RUNNER_TEMP/apps.txt") + [ "$apps" = 1 ] || { grep -B1 -A3 'Lich.app' "$RUNNER_TEMP/apps.txt"; fail "macOS counts $apps Dock applications in Lich.app, expected the window alone"; } + grep -A1 'executable path=.*Lich.app/Contents/MacOS/lich"' "$RUNNER_TEMP/apps.txt" | grep -q 'type="UIElement"' \ + || { grep -B1 -A3 'Lich.app' "$RUNNER_TEMP/apps.txt"; fail "the backend is not a UIElement: it would take a Dock tile of its own"; } } codesign --verify --deep --strict --verbose=2 "$app" From cd4f28a5e9ddaa7a9c264e62ffa93360a73d1cd1 Mon Sep 17 00:00:00 2001 From: omartelo Date: Sat, 24 Oct 2026 15:16:48 -0300 Subject: [PATCH 2/2] test(native): wait for the review panel's first read before clicking the dock The dock tests settled while the review panel still showed Loading: its diff arrives from a goroutine, and the rig's virtual clock settles in microseconds. On a slow runner the read landed between the click's frames, which then differed for a reason the click never caused. The test asserted a frame the contract never promised; it now waits for the panel's read first. With the fake backend's diff reply delayed 0.5-1ms the two tests failed 9 of 800 runs before, 0 of 800 after. --- native/cmd/lich-native/stale_chrome_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/native/cmd/lich-native/stale_chrome_test.go b/native/cmd/lich-native/stale_chrome_test.go index 17ba99a57..c4d75d92f 100644 --- a/native/cmd/lich-native/stale_chrome_test.go +++ b/native/cmd/lich-native/stale_chrome_test.go @@ -41,7 +41,7 @@ func stageFrames(t *testing.T, tab dockTab) (*window, *frameRig) { func TestDockCloseHidesItInTheFrameAfterTheClick(t *testing.T) { u, r := stageFrames(t, dockReview) - r.settle(t) + r.waitFor(t, "No uncommitted changes") r.click(t, r.smallestClickIn(t, image.Rect(rigWindow.X-36, 0, rigWindow.X, 48))) if u.dock.tab != dockClosed { t.Fatalf("the click missed the ×: dock on %v", u.dock.tab) @@ -50,7 +50,7 @@ func TestDockCloseHidesItInTheFrameAfterTheClick(t *testing.T) { func TestDockFullScreenFillsTheRowInTheFrameAfterTheClick(t *testing.T) { u, r := stageFrames(t, dockReview) - r.settle(t) + r.waitFor(t, "No uncommitted changes") r.click(t, r.smallestClickIn(t, image.Rect(rigWindow.X-64, 0, rigWindow.X-36, 48))) if !u.dock.fullscreen { t.Fatal("the click missed full screen")