Run the accessibility verification pass

The automated half: AccessibilityAuditTests runs performAccessibilityAudit
over all eight surfaces DESIGN/10 names — board with trash hidden and
shown, card window in Preview/Edit/raw source, welcome, template chooser,
board popover. One audit per test, .all audit types, no issue handler —
nothing waived; a future false-positive excusal must match one element on
one surface with its reason beside it. Navigation is menu-bar titles and
the arrow grammar; no accessibility identifiers added to production code.

The suite launches with --ui-test-fixture-board: the sandbox forbids
handing the app a temp-folder path (no bookmark behind it), so the flag
carries no payload and the app builds a known board inside its own
container through the ordinary BoardWriter door — three lanes, six cards,
a rich Markdown body with attachment, one card already in .trash/ — with
the registry redirected to the same scratch directory so audit runs never
pollute real recents. LaunchPlan replaces the restore Bool (welcome /
restoreBoards / uiTestFixture, fixture wins outright), decided once in
KanbanApp.init and dispatched by RestoreBootstrapView; pure and pinned by
UITestLaunchTests, and the fixture itself is materialized and read back
through BoardLoader in units — the only proof available headlessly.

The manual half: KanbanUITests/AccessibilityVerification.md is the one
document — the audit suite at the top (it needs a real display and
Accessibility permission), the per-release VoiceOver smoke script with
expected utterances quoted from AccessibilityPhrases, and the
consolidated m11 checklist from all four implementation cards.

1588 unit tests green, UI target compiles, both schemes build. The audit
run and smoke script await a real display — the manual pass is the
user's.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-29 10:42:14 -04:00
parent 92a088fdd3
commit c5edcd8528
9 changed files with 989 additions and 20 deletions
+2
View File
@@ -66,3 +66,5 @@ scripts/verify-editions.sh
macOS 26+, Swift 6 (strict concurrency), SwiftUI, sandboxed. Internal codename `Kanban` (target, scheme, bundle id `dev.rzen.indie.Kanban`); the app ships under the display name **Lanework**.
Two app targets are built from one source tree (DESIGN/12-editions.md): **Lanework** compiles `Kanban/` alone, **Lanework Pro** (target `KanbanPro`, scheme `LaneworkPro`, bundle id `dev.rzen.indie.KanbanPro`) compiles `Kanban/` plus the Pro-only source root `KanbanPro/`. There is no edition flag and no `#if` in shared code — an edition difference is a file one target builds and the other does not — and the difference is checkable on the signed products: base carries no libgit2 and no network-client entitlement, which `scripts/verify-editions.sh` asserts against the built bundles. The unit suite is edition-agnostic and runs twice, once hosted by each app (`KanbanTests`, `KanbanProTests` — the same sources, bound to the Pro module by `-module-alias`). Both editions declare the same `dev.rzen.indie.kanban-board` UTI, so any board opens in either app.
**Accessibility is verified, not assumed** (DESIGN/10-accessibility.md § Verification). `KanbanUITests/AccessibilityAuditTests.swift` runs Xcode's accessibility audit over all eight surfaces the design names — the board with the trash shown and hidden, the card window in Preview, Edit and raw source, welcome, the template chooser, the board popover — and every violation is a test failure with nothing waived. Each test launches the app with `--ui-test-fixture-board`, a test-only argument that makes the app build a known three-lane board through its own `BoardWriter` inside its sandbox container, with its own scratch registry, so a run never touches real boards or real recents (`Kanban/App/UITestLaunch.swift`). The manual half — the per-release VoiceOver smoke script and the consolidated accessibility checklist — is `KanbanUITests/AccessibilityVerification.md`. Both halves need a real, unlocked display and Accessibility automation permission.