Skip to content

Audit fixes: the Close timeout and the interrupted picture - #1

Merged
addhumal merged 8 commits into
mainfrom
audit-fixes
Oct 14, 2026
Merged

addhumal merged 8 commits into
mainfrom
audit-fixes

Conversation

@addhumal

@addhumal addhumal commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

Two fixes from a repository audit (2026-10-14), each with a check, plus the fixes from a review of them. Two of the audit's other findings were dropped from this PR: trackpad and wheel scrolling is Saffsanity#80's (Saffsanity#68), and moving CLAUDE.md's finished steps out would conflict with nearly every open PR.

  1. A window's Close waits 1 s at most (WindowSizer.perform). The close button is a new AX element on the system's default messaging timeout, pressed on the host's main actor, so an app whose close runs a modal alert, or hangs, held every device's input, picks and settings that long. Now type-checked and given 1 s, like every other AX element the host uses. New check: Scripts/menu-check/run.sh close, against menufixture's Slow Close window (its close holds the fixture 4 s); menufixture's label skips that window, whose title bar would read as the label. Built; not run: the machine this was made on has no Accessibility permission for its shell.
  2. An interrupted picture asks for a keyframe (HEVCDisplayView, new KeyframeRecovery, pbxproj AC01/FC01). The layer's requiresFlushToResumeDecoding was never checked and nothing asked the Mac for a keyframe, so a still window could stay frozen or black after the app was in the background. Both states now flush and re-pick the current source (kind 6, no wire change), at most once per 2 s and only while the app is active (in the background the layer reads interrupted at every frame, and a pick there would restart everyone's stream and, in regular mode, raise the window on the Mac); becoming active asks once if a flush has had no keyframe since. New check: Tests/checks/keyframe-recovery, 10 checks and 7 of 7 mutants, in CI's mutants matrix.

Verified: swift build -c release; Tests/checks/run-all.sh; CI on the fork (addhumal/sill#1).

For a device: leave Sill for a few seconds on a still window and come back: the picture returns, the Mac logs one restart, and nothing restarts while the iPad is away. Long-press a thumbnail › Close on a document with unsaved changes: the stream keeps answering.

…ault

The close button is a new AX element, on the default messaging timeout, and the host presses it on
its main actor: an app whose close runs a modal alert, or that hangs, held input, picks and
settings for every device that long. The button is now type-checked and given a 1 s timeout, as
every other AX element the host uses.

Check: Scripts/menu-check/run.sh close, against menufixture's new Slow Close window (its close
holds the fixture's main thread 4 s): the press must come back within 1.5 s. Built here; not run:
this shell has no Accessibility permission (AXIsProcessTrusted false), which menu-check needs.
The scroll pan took direct touches only, and no recognizer took scroll events, so a Magic
Keyboard's trackpad (and a Mac's, for the iPad app there) scrolled nothing on the stream. The pan
now takes continuous scrolls too, through the same handler and coast; a mouse wheel's discrete
clicks get a pan of their own (no touches) that never coasts, as a wheel never does on a Mac. The
portrait trackpad is unchanged: its pan reads fewer than two touches as pointer motion, and a
hardware scroll is the picture's job in both layouts.

Checked: Tests/touchrig against main, 72 of 72 (every one- and two-finger stroke the same events
as before, the gestures as before), which also compiles the file. Not checked here: a real scroll
event (the rig synthesizes touches only, and this Mac's Xcode 27.0 cannot build the app, which needs
the iOS 27.1 SDK): with this build, two fingers on the iPad simulator's window (the Mac trackpad)
or a wheel over the stream against SillHost --synthetic with SILL_TEST_INPUT_LOG=1 should print
scroll lines, a coast only after the trackpad's flick.
The display layer was only checked for `.failed`, and then waited for whatever keyframe came next:
a layer that needs a flush to decode again (iOS sets it after the app was in the background) was
never flushed, and a still window sends no keyframe, so the picture could stay frozen or black
until the window changed, plus up to the 4 s safety keyframe. Now both states flush the layer,
wait for a keyframe and ask for one by picking the source already streaming (kind 6), which every
Mac answers with a restart; at most once per 2 s (KeyframeRecovery, pure; pbxproj AC01/FC01). The
return to the foreground checks the layer too, as no frame may come to notice it. In regular mode
the re-pick brings the streamed window forward on the Mac, as any pick does.

Check: Tests/checks/keyframe-recovery, 10 checks and 7 of 7 mutants (red before the rule existed);
in run-all.sh by its folder and in CI's mutants matrix. The iOS app compiles here except for
DuoPostureReader.swift (this Mac's Xcode 27.0 lacks the iOS 27.1 SDK), StreamClient.swift and
HEVCDisplayView.swift without an error. Not run: the app on a simulator or device (background 2 s
and back against SillHost --synthetic should give one restart and the picture).
Every agent session loads CLAUDE.md, and 320 KB of its 407 KB were the Current step entries of
finished work. They are in docs/history.md now, word for word, newest first, a heading added to
each. CLAUDE.md keeps the header, the release entry (the current step) and the still-open stall
note, an Open device tests list linking the 32 steps whose untested items are still Noah's, the
rules those entries held (the encoder's operational rule, SillHostCore's rules, never reconfigure a
running SCStream), the two milestones in a line each, and Layout, Build and run, the compatibility
floor, Conventions and Decisions as they were: 92 KB. The plan's 60 KB is not reachable while
those sections stay whole (they are 82 KB on their own).

Check: every line of the old file is in one of the two, as many times as before (a multiset
comparison: 5,479 lines, none missing).
Review finding 1. In the background the display layer reads as needing a flush at every frame
until iOS suspends the app, so the iPad picked its source again every 2 s there: a restart of the
stream every device shares and, in regular mode, its window raised on the Mac over whatever its
user was doing. The ask now goes out only while the app is active, the foreground check moves to
didBecomeActive, and the view remembers a flush with no keyframe since (`interrupted`), so the
return to the front still asks once when the background's flush already cleared the layer's flag.
Also review finding 4: keyframeRecovery no longer sits between displayView and its doc comment.

Checked: the iOS app compiles but for DuoPostureReader.swift (Xcode 27.0 here); keyframe-recovery
as before.
Review finding 2. The label command took the first static text of the fixture's first window, and
Slow Close, ordered in front of the label's window, has its title bar's. The reader and mirror
checks read the label to see a press land. Compiled; not run here (no Accessibility).
Review finding 3. Twelve comments in nine files sent the reader to CLAUDE.md for the trackpad
stutter, the frozen stream, the 33 fps plateau and the stuck command after Spotlight, which moved
to docs/history.md with the rest of the finished steps. The old plan documents keep their dated
references.
Review finding 5. Away from home's and the iPhone Duo's entries, and away from home's P4–P13, are
in docs/history.md now.
@addhumal
addhumal merged commit 891d91b into main Oct 14, 2026
2 checks passed
@addhumal addhumal changed the title Audit fixes: the Close timeout, trackpad and wheel scrolling, the interrupted picture, CLAUDE.md slimmed Audit fixes: the Close timeout and the interrupted picture Oct 14, 2026
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