fix: 2026-08-20 audit remediation — socket race, tmux targeting, hook fail-open, revocation, drag visuals - #312
Merged
Conversation
…er; sync notification.clear Audit 2026-08-20 H1/M7/M9. @mainactor on TerminalController is advisory under Swift 5 mode, so every off-main tabManager access must go through v2MainSync — seven read sites and one write placement (v2WindowCreate's setActiveTabManager ran on the connection thread after its lookup closure returned) did not. notification.clear replied ok before its async mutation ran, so clear-then-list raced. The per-connection line buffer had no size cap; one newline-less write could grow it without bound — now closes with payload_too_large at 8 MiB.
…rrors; honest CLI errors Audit 2026-08-20 H3/M2/L1/L2. A session-qualified tmux target (sess:2) silently discarded the session name and resolved against the flat workspace pool — now tries the full token as a title, then the session name for window 0/1, then errors explaining programa has no tmux sessions. Agent hooks failed CLOSED on session-start/prompt-submit for the same transport errors teardown tolerated — a quit Programa blocked the next prompt; all three agents now fail open consistently. focus/close-window no longer report every failure as 'Window not found', and layout apply sends the caller's cwd as its help text promised.
Audit 2026-08-20 H5. revoke() only removed the device from the trust store, so an in-progress relay kept full allowlisted access until the phone disconnected on its own. Admitted connections are now registered per endpointId (under the existing stateLock, unregistered via defer on every relay exit) and revoke closes them after clearing trust. Reconnects were already rejected.
…s boundedly Audit 2026-08-20 H2/M3. TabItemView's == excluded draggedTabId/dropIndicator while body reads both, so .equatable() froze drag dim and drop indicators mid-drag — the file's own documented failure mode. The autosave tick recorded its fingerprint even when the save layer declined (startup restore in flight, empty snapshot), suppressing up to 60s of identical-content saves after a save that never happened; declined saves now skip the fingerprint and retry after 1s, capped at 5 consecutive attempts so a windowless app doesn't poll.
…osed-panel shortcut Audit 2026-08-20 M10/M11. The spec marked M-009/RZ-* DONE while nothing in the app calls session.* — now DAEMON-ONLY with the integration deferred to the detached-sessions plan. ⌘⇧T (Reopen closed panel) shipped without a row in the shortcuts doc, violating the shortcut policy.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Remediates the actionable findings from today's correctness audit in one PR: closes a crash-capable threading race in the socket server, makes tmux-compat scripts fail loudly instead of acting on the wrong workspace, stops a quit Programa from blocking agents' next prompts, makes revoking a paired phone actually cut its live session, and unfreezes sidebar drag visuals.
Summary (finding IDs from docs/audits/codebase-audit-2026-08-20.md)
tabManageraccess routed throughv2MainSync(7 reads + 1 misplaced write);notification.clearnow synchronous; 8 MiB cap on the per-connection line buffer (payload_too_large+ close).focus/close-windowpropagate real errors;layout applysends the caller's cwd as documented.TabItemView.==compares the two drag bindings its body reads; declined autosaves no longer poison the fingerprint and retry boundedly (5 × 1s cap).Deliberately excluded (need decisions, not code): M4 (prompting on insecure-HTTP restore is a product call), M5 (reparent unification needs visual verification), M6's navigation-stale refs + M8 handle-map pruning (API/design changes), M12 (remote-host GC).
Review order: commit 1 (socket threading) is the load-bearing one; 2–5 are independent.
Test Plan
No regression tests were added for the threading fixes: races and hook transport failures have no unit seam (socket-integration CI exercises the handlers), and per test policy no shape-assert tests were written.