Realign code with the 2026-07-29 accessibility rulings

Three ruled behavior changes (DESIGN/10, resolution session 2026-07-29):

- The board-change digest covers the trash while View > Show Trash is on:
  BoardDiff.between gains includingTrash, keying its card index by
  ItemPath so foreign purges, restores, and Empty Trash join the digest;
  crossings of the trash boundary still read deleted/restored, never
  moved, on both sides of the toggle. BoardStore.land passes the store's
  own isTrashVisible - no new injection seam.

- A vanished head with surviving co-selection is still named: naming and
  recovery are independent axes, so BoardAnnouncer's vanished-focus rung
  fires on all branches while the survivors-veto now gates only the
  recovery half (recovery implies vanished, no longer both-or-neither).

- Banner-row buttons are literal FKA Tab stops: BannerRow.controls is
  the row's testable button inventory, BannerRowView renders from it
  with .focusable() on each button; the combined VoiceOver element stays
  unconditional - custom actions and Tab stops are independent surfaces.

19 tests added, 2 expectations updated to the rulings. 1607 green on
both schemes.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-29 11:37:46 -04:00
parent 065c6f0678
commit 28ca2c3f50
8 changed files with 614 additions and 66 deletions
+11 -1
View File
@@ -666,7 +666,17 @@ public final class BoardStore {
// as cheap as it was.
let focus: BoardAnnouncer.FocusOutcome
if origin == .foreign, !endsWholesaleOperation {
facts.diff = BoardDiff.between(snapshot, result.model)
// **The digest covers the trash only while the trash lane is shown**
// (10-accessibility.md Live board announcements, ruled 2026-07-29). This is the
// seam that reading takes: visibility is view state on the board's own transient
// container one per store, shared by every window onto this board
// (`TransientBoardState.isTrashVisible`) so the store asks it here and the
// summarizer stays a pure function of two snapshots plus one fact.
facts.diff = BoardDiff.between(
snapshot,
result.model,
includingTrash: transient.isTrashVisible
)
focus = BoardAnnouncer.focusOutcome(
old: snapshot,
new: result.model,