You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Accessible diagnostic-toast runtime UAT passed on iPhone Air / iOS 26.5
(sim-use): element exposure, persistence, tap activation (once), swipe
dismissal, and Reduce Motion opacity transition all confirmed after the
tab-bar occlusion fix (39f4b7a). Mark 09-VERIFICATION passed, advance
ROADMAP/STATE/PROJECT to Phase 10.
Copy file name to clipboardExpand all lines: .planning/PROJECT.md
+4-6Lines changed: 4 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -34,6 +34,8 @@ The load-bearing paths must keep working: reliably **fetch, parse, read, and dow
34
34
- ✓ De-globalize `*Util` → injected clients, kill singletons (HYG-01) — side-effecting AppTools Utils (Device/Haptics/UserDefaults/Cookie) folded into injected clients; `URLUtil`/`FileUtil`/`AppInfo` kept as pure namespaces (D-06); `AppUtil`, `TouchHandler.shared`, and `DataCache.shared` removed; galleryHost threaded explicitly through every request seam — Phase 8
35
35
- ✓ Cookie-logging audit (QUAL-01) — no cookie value is emitted to logs at `.public` privacy, enforced by a static gate hardened against aliased-value/renamed-Logger evasion with an executable negative-fixture harness — Phase 8
36
36
- ✓ Client-layer test coverage (QUAL-02) — deterministic, green tests for the async `NetworkingFeature`, `CookieClient`, and `ImageClient` (per-test `DataCache`, pixel-dimension assertions) — Phase 8
37
+
- ✓ Fix `Category.private.filterValue` (QUAL-03) — the `fatalError` landmine is gone; `.private` reports filter-math misuse and contributes zero, while the ten searchable cases still sum to all 1023 filter bits — Phase 9
38
+
- ✓ Structured error handling + user-facing error surface (QUAL-04) — silent `try?` sites classified so genuine failures propagate through typed `throws(AppError)` while intentional fallbacks stay documented; a privacy-safe diagnostic surface (Description / Suggested Solution / Context / Environment) is reached through a persistent, accessible error toast, runtime-verified by simulator UAT — Phase 9
37
39
38
40
### Active
39
41
@@ -48,10 +50,6 @@ The load-bearing paths must keep working: reliably **fetch, parse, read, and dow
48
50
-[ ]11.**Decompose `GenericList`** — let each of its 8 consuming pages build its own list from shared atoms instead of a super-list
49
51
-[ ]12.**Universal device orientation** on every page + remove EhPanda's custom orientation lock (delete `enablesLandscape`), deferring the lock to iOS's built-in feature
50
52
51
-
**E · Correctness, security & tests (folded-in concerns, later timing)**
52
-
-[ ]18.**Fix `Category.private.filterValue`** — remove the `fatalError` landmine
53
-
-[ ]20.**Structured error handling + user-facing error surface** (gates the `optional_try` rule) — replace silent `try?` (144 sites) with proper `do/catch` that surfaces user-relevant failures through a structured error surface (Description / Suggested Solution / Context / environment info; non-blocking failure toast → tap for detail), keeping best-effort parsing explicitly optional
54
-
55
53
**F · UI polish**
56
54
-[ ]21.**Numeric text polish** — apply `.monospacedDigit()` + `.contentTransition(.numericText())` to most number-bearing text (counts, page numbers, sizes, ratings)
57
55
-[ ]22.**Reduce `ZStack` usage** — prefer `.overlay`/`.background` where a child overlays/underlays primary content (per-site judgment; overlay/background size to the primary child, `ZStack` to the union), at layout/appearance parity
@@ -73,7 +71,7 @@ The load-bearing paths must keep working: reliably **fetch, parse, read, and dow
73
71
74
72
## Context
75
73
76
-
-**v3.0.0 in flight, unreleased.** Phases 1–8 are complete: dependency isolation, masonry, reader paging, async networking, the TCA deprecation migration, adaptive layout/orientation work, the root privacy-mask/auto-lock removal, and the architecture-hygiene client-seam de-globalizationare validated. Phase 9 (correctness & structured error handling) is next.
74
+
-**v3.0.0 in flight, unreleased.** Phases 1–9 are complete: dependency isolation, masonry, reader paging, async networking, the TCA deprecation migration, adaptive layout/orientation work, the root privacy-mask/auto-lock removal, the architecture-hygiene client-seam de-globalization, and the correctness/structured-error-handling work are validated. Phase 10 (UI Polish) is next.
-**Reference designs** for the structured error surface (#20) and the refactor-gated lint rules (#9) have been captured name-free; the plan phase needs no external lookup.
79
77
-**Two tasks carry parity risk** and are spiked first: SwiftUIPager→`TabView` (core reading UX) and WaterfallGrid→custom `Layout` (masonry column balancing).
@@ -122,4 +120,4 @@ This document evolves at phase transitions and milestone boundaries.
-[]**Phase 9: Correctness & Structured Error Handling** - Kill the private-category crash and replace silent try? with a user-facing error surface
32
+
-[x]**Phase 9: Correctness & Structured Error Handling** - Kill the private-category crash and replace silent try? with a user-facing error surface (completed 2026-07-16)
33
33
-[ ]**Phase 10: UI Polish** - Monospaced digits and numeric-text transitions; reduce ZStack in favor of overlay/background
34
34
-[ ]**Phase 11: Infra Refactor & Lint Capstone** - Resolve infra-level refactors (incl. test-isolation cleanup), then ratchet SwiftLint to the stricter ruleset at error; mechanical sweep last, refactor-gated rules flipped on
35
35
-[ ]**Phase 12: Deep Link Hardening** - Code-review the deep-link implementation and make it less hacky and more durable at navigating to the correct destination; add UI automation tests covering deep-link navigation
last_activity_desc: Phase 09 verified (accessible diagnostic-toast UAT passed) and completed
12
12
progress:
13
13
total_phases: 13
14
-
completed_phases: 8
14
+
completed_phases: 9
15
15
total_plans: 93
16
16
completed_plans: 93
17
-
percent: 100
17
+
percent: 69
18
18
---
19
19
20
20
# Project State
21
21
22
22
## Project Reference
23
23
24
-
See: .planning/PROJECT.md (updated 2026-07-14)
24
+
See: .planning/PROJECT.md (updated 2026-07-16)
25
25
26
26
**Core value:** The load-bearing paths — fetch, parse, read, download galleries — keep working; every task is a foundation change held to behavior/appearance parity.
VoiceOver announces and focuses the persistent diagnostic Button; Voice Control,
15
-
Switch Control, and Full Keyboard Access can activate it after three seconds;
16
-
activation routes once; downward swipe and replacement route nothing; Reduce
17
-
Motion removes moving and bouncy presentation.
18
-
awaiting: user response
11
+
[testing complete]
19
12
20
13
## Tests
21
14
22
15
### 1. Accessible diagnostic-toast runtime UAT
23
16
24
17
expected: VoiceOver announces and focuses the persistent diagnostic Button; Voice Control, Switch Control, and Full Keyboard Access can activate it after three seconds; activation routes once; downward swipe and replacement route nothing; Reduce Motion removes moving and bouncy presentation.
25
-
result: [pending]
18
+
result: pass
19
+
verified_by: simulator UAT (iPhone Air, iOS 26.5) driven via sim-use
20
+
notes: |
21
+
Ran end-to-end against a build from HEAD. Triggered a real gallery-fetch failure via an
22
+
invalid gallery deep link (ehpanda://e-hentai.org/g/9999999/badtoken0000/ -> "Not found").
23
+
24
+
Confirmed:
25
+
- Diagnostic toast is exposed as an enabled AXButton with the combined label
26
+
"Error, There seems to be nothing here." (the element/label VoiceOver, Voice Control,
27
+
Switch Control, and Full Keyboard Access consume for discovery, naming, and activation).
28
+
- Persistent: remained well past 3s and across tab navigation (no auto-hide).
29
+
- Activation opens the ErrorInfoView detail sheet exactly once (Description/Solution/
30
+
Context/Environment); context is privacy-safe (numeric GID only, no token/URL); the
31
+
toast is consumed after routing (a second activation cannot reopen stale details).
32
+
- Downward-swipe dismissal works and routes nothing (dismisses without opening details).
33
+
- Reduce Motion: the toast fades in via opacity at its final position (no slide-up, no
34
+
bounce), confirmed frame-by-frame from a screen recording.
35
+
36
+
A blocking issue was found on the first pass and fixed before this pass:
37
+
the persistent toast was occluded by the iOS 26 floating tab bar (touches over the
38
+
capsule were intercepted by the tab bar, so it could not be tapped or swiped). Resolved
39
+
by commit 39f4b7a3 "Improve toast layout" (.padding(.bottom, 64) + .contentShape(.rect)),
40
+
which lifts the toast clear of the tab bar. Re-verified here.
41
+
42
+
Not directly exercised (sim-use has no AX-action/keyboard path): activation through the
43
+
VoiceOver/Voice Control/Switch Control/Full Keyboard Access services themselves. These
44
+
invoke the same Button activate action that touch activation triggers, which is confirmed
45
+
working, so operability is strongly supported by the correct AXButton exposure.
0 commit comments