Commit 655d11b59a

655d11b59a273c14720952478ccebec20a9627bd

parent: 872583eca8

Unsigned

cmc <hello@cleberg.net> · 2026-07-23 04:26 UTC

chore: bump to v4.9.0; record the release in the roadmap

MARKETING_VERSION 4.8.3 -> 4.9.0 and CURRENT_PROJECT_VERSION 43 -> 44
across all targets, with AppVersion.current aligned — the three-way
consistency v4.4.1 established.

The roadmap gains the v4.9.0 entry: the full accessibility pass (#21
phases 0-5 — semantic colours, light mode and the appearance setting,
Dynamic Type reflow and tap targets, VoiceOver, colour independence and
motion/transparency), the audit harness with seeded fixtures and the
engaged enforcement ratchet, the manual verification checklist, and
Swift 6 language mode adoption (#27). Deferred device passes are named
rather than implied.

The v5.0.0 "establish a test target" bullet is rewritten to match
reality: a UI test target now exists with an enforcement gate; what
remains for v5.0.0 is unit coverage of the deterministic core.

Layout: unified · split

DomainDig.xcodeproj/project.pbxproj +16 −16
@@ -566,7 +566,7 @@
566566 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
567567 CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
568568 CODE_SIGN_STYLE = Automatic;
569 CURRENT_PROJECT_VERSION = 43;
569 CURRENT_PROJECT_VERSION = 44;
570570 DEVELOPMENT_TEAM = ZCNAX3VL9D;
571571 ENABLE_PREVIEWS = YES;
572572 GENERATE_INFOPLIST_FILE = YES;
@@ -583,7 +583,7 @@
583583 "$(inherited)",
584584 "@executable_path/Frameworks",
585585 );
586 MARKETING_VERSION = 4.8.3;
586 MARKETING_VERSION = 4.9.0;
587587 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
588588 PRODUCT_NAME = "$(TARGET_NAME)";
589589 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -603,7 +603,7 @@
603603 ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
604604 CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
605605 CODE_SIGN_STYLE = Automatic;
606 CURRENT_PROJECT_VERSION = 43;
606 CURRENT_PROJECT_VERSION = 44;
607607 DEVELOPMENT_TEAM = ZCNAX3VL9D;
608608 ENABLE_PREVIEWS = YES;
609609 GENERATE_INFOPLIST_FILE = YES;
@@ -620,7 +620,7 @@
620620 "$(inherited)",
621621 "@executable_path/Frameworks",
622622 );
623 MARKETING_VERSION = 4.8.3;
623 MARKETING_VERSION = 4.9.0;
624624 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
625625 PRODUCT_NAME = "$(TARGET_NAME)";
626626 STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -639,7 +639,7 @@
639639 APPLICATION_EXTENSION_API_ONLY = YES;
640640 CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
641641 CODE_SIGN_STYLE = Automatic;
642 CURRENT_PROJECT_VERSION = 43;
642 CURRENT_PROJECT_VERSION = 44;
643643 DEVELOPMENT_TEAM = ZCNAX3VL9D;
644644 GENERATE_INFOPLIST_FILE = NO;
645645 INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -650,7 +650,7 @@
650650 "@executable_path/Frameworks",
651651 "@executable_path/../../Frameworks",
652652 );
653 MARKETING_VERSION = 4.8.3;
653 MARKETING_VERSION = 4.9.0;
654654 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
655655 PRODUCT_NAME = "$(TARGET_NAME)";
656656 SKIP_INSTALL = YES;
@@ -668,7 +668,7 @@
668668 APPLICATION_EXTENSION_API_ONLY = YES;
669669 CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
670670 CODE_SIGN_STYLE = Automatic;
671 CURRENT_PROJECT_VERSION = 43;
671 CURRENT_PROJECT_VERSION = 44;
672672 DEVELOPMENT_TEAM = ZCNAX3VL9D;
673673 GENERATE_INFOPLIST_FILE = NO;
674674 INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -679,7 +679,7 @@
679679 "@executable_path/Frameworks",
680680 "@executable_path/../../Frameworks",
681681 );
682 MARKETING_VERSION = 4.8.3;
682 MARKETING_VERSION = 4.9.0;
683683 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
684684 PRODUCT_NAME = "$(TARGET_NAME)";
685685 SKIP_INSTALL = YES;
@@ -697,7 +697,7 @@
697697 APPLICATION_EXTENSION_API_ONLY = YES;
698698 CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
699699 CODE_SIGN_STYLE = Automatic;
700 CURRENT_PROJECT_VERSION = 43;
700 CURRENT_PROJECT_VERSION = 44;
701701 DEVELOPMENT_TEAM = ZCNAX3VL9D;
702702 GENERATE_INFOPLIST_FILE = NO;
703703 INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -708,7 +708,7 @@
708708 "@executable_path/Frameworks",
709709 "@executable_path/../../Frameworks",
710710 );
711 MARKETING_VERSION = 4.8.3;
711 MARKETING_VERSION = 4.9.0;
712712 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
713713 PRODUCT_NAME = "$(TARGET_NAME)";
714714 SKIP_INSTALL = YES;
@@ -726,7 +726,7 @@
726726 APPLICATION_EXTENSION_API_ONLY = YES;
727727 CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
728728 CODE_SIGN_STYLE = Automatic;
729 CURRENT_PROJECT_VERSION = 43;
729 CURRENT_PROJECT_VERSION = 44;
730730 DEVELOPMENT_TEAM = ZCNAX3VL9D;
731731 GENERATE_INFOPLIST_FILE = NO;
732732 INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -737,7 +737,7 @@
737737 "@executable_path/Frameworks",
738738 "@executable_path/../../Frameworks",
739739 );
740 MARKETING_VERSION = 4.8.3;
740 MARKETING_VERSION = 4.9.0;
741741 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
742742 PRODUCT_NAME = "$(TARGET_NAME)";
743743 SKIP_INSTALL = YES;
@@ -753,11 +753,11 @@
753753 isa = XCBuildConfiguration;
754754 buildSettings = {
755755 CODE_SIGN_STYLE = Automatic;
756 CURRENT_PROJECT_VERSION = 43;
756 CURRENT_PROJECT_VERSION = 44;
757757 DEVELOPMENT_TEAM = ZCNAX3VL9D;
758758 GENERATE_INFOPLIST_FILE = YES;
759759 IPHONEOS_DEPLOYMENT_TARGET = 17.6;
760 MARKETING_VERSION = 4.8.3;
760 MARKETING_VERSION = 4.9.0;
761761 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDigUITests;
762762 PRODUCT_NAME = "$(TARGET_NAME)";
763763 SWIFT_APPROACHABLE_CONCURRENCY = YES;
@@ -772,11 +772,11 @@
772772 isa = XCBuildConfiguration;
773773 buildSettings = {
774774 CODE_SIGN_STYLE = Automatic;
775 CURRENT_PROJECT_VERSION = 43;
775 CURRENT_PROJECT_VERSION = 44;
776776 DEVELOPMENT_TEAM = ZCNAX3VL9D;
777777 GENERATE_INFOPLIST_FILE = YES;
778778 IPHONEOS_DEPLOYMENT_TARGET = 17.6;
779 MARKETING_VERSION = 4.8.3;
779 MARKETING_VERSION = 4.9.0;
780780 PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDigUITests;
781781 PRODUCT_NAME = "$(TARGET_NAME)";
782782 SWIFT_APPROACHABLE_CONCURRENCY = YES;
DomainDig/AppVersion.swift +1 −1
@@ -2,6 +2,6 @@ import Foundation
22
33enum AppVersion {
44 nonisolated static var current: String {
5 "4.8.3"
5 "4.9.0"
66 }
77}
RELEASE_ROADMAP.md +65 −5
@@ -6,7 +6,7 @@ ports, geolocation, subdomains, availability). The next several releases invest
66in *reach and surfacing* — getting that data onto more iOS surfaces and into more
77workflows — rather than adding raw protocol checks.
88
9Current version: `v4.8.3`.
9Current version: `v4.9.0`.
1010
1111## v4.4.1 Patch: Release Readiness — ✅ shipped
1212
@@ -185,6 +185,65 @@ hits on SwiftUI `$binding` shorthand in `AuditModeView`, which cannot be
185185renamed. These want a *Won't Fix* / *Safe* resolution in SonarCloud rather than
186186a code change.
187187
188## v4.9.0 Minor: Accessibility, Appearance & Engineering Health — ✅ shipped
189
190Goal: make the app usable by every iOS user — full accessibility pass (#21),
191light mode, and the engineering scaffolding to keep both from regressing.
192
193- **Semantic colour system** — every hard-coded colour replaced with adaptive
194 colorsets in `Shared/Colors.xcassets` (Any/Dark + High Contrast variants),
195 shared by app, widget, and share extension via the synchronized `Shared`
196 group. Every status colour clears WCAG AA on its page, its card, and its
197 badge surface, in both schemes; measured, not asserted. The accent is now
198 blue (`#0000FF` light / `#4DA3FF` dark), split into foreground
199 (`StatusInfo`), fill (`AccentFill`), and on-fill (`AppOnAccent`) roles
200 because one value cannot serve as both text-on-dark and fill-behind-white.
201 `AppStatusTone` pairs each status foreground with an authored surface.
202- **Light mode unlocked** — the 16 scattered `.preferredColorScheme(.dark)`
203 calls removed; appearance (System/Light/Dark) is applied once at the
204 `WindowGroup` and exposed under Settings → Display. `.secondary` (3.29:1 on a
205 light card) replaced with `AppTextSecondary` across 191 sites.
206- **Dynamic Type & reflow** — `Label`-clipped empty-state titles fixed, the
207 44pt tap-target floor enforced (`AppCopyButton` was 30×30;
208 `controlMinHeight` was 42), `CardView`'s horizontal-scroll default flipped
209 to reflow, dense rows (`WatchlistRowView`, `BatchResultRowView`,
210 `PortfolioExpiryRow`) and the collapsible section headers rebuilt on
211 `ViewThatFits` so badges and buttons can never letter-wrap vertically, and
212 the widget clamped at `accessibility1` (fixed canvas, no scroll).
213- **VoiceOver** — labels on every icon-only control (label-in-name preserved
214 for Voice Control), selected-state on all toggles, badges read as one word,
215 heading-rotor navigation, dense rows collapsed to one element with the
216 detail on the More Content rotor (`accessibilityCustomContent`), technical
217 strings (DNS records, cipher suites) spoken with punctuation, and lookup/
218 sweep completion announcements. Widget rows read as a single phrase.
219- **Colour independence, motion, transparency** — widget status uses the badge
220 symbol vocabulary instead of colour-only dots; `differentiateWithoutColor`
221 adds symbols/borders on demand; all five animation sites honour
222 `reduceMotion`; the one material honours `reduceTransparency`.
223- **Accessibility audit harness** — `DomainDigUITests` runs
224 `performAccessibilityAudit()` over every primary screen at default and
225 AccessibilityXXXL, on CI (newest runtime, clean merge-result checkout) and
226 locally (`Scripts/audit-a11y.sh`, real floor runtime, wired to an opt-in
227 pre-push hook). `DOMAIN_DIG_SEED_FIXTURES` seeds deterministic in-memory
228 rows so the dense paths actually render under audit. The **enforcement
229 ratchet is engaged**: named findings in six categories fail CI, with
230 narrowly characterised, always-logged noise suppressions. Manual
231 verification checklist in `Docs/ACCESSIBILITY_VERIFICATION.md`; findings
232 burndown 20 → 11 with every remaining item characterised as system noise.
233- **Swift 6 language mode** (#27) — all three product targets build under
234 `SWIFT_VERSION = 6.0` with zero warnings. `SMTPChannel` became an actor
235 (fixing a real `CheckedContinuation` double-resume hazard),
236 `SweepActivityController` stores a Sendable activity id, and the remaining
237 isolation issues were resolved layer by layer. The UITests target stays on
238 Swift 5 (XCTest override isolation), recorded as a decision.
239- **Project hygiene** — the misleading project-level deployment target
240 (26.2 shadowing the real 17.6) reconciled; CI selects simulators
241 floor-aware instead of first-match.
242
243Deferred: the Phase 6 manual device passes (VoiceOver walkthrough, Voice
244Control, iPad Full Keyboard Access, Liquid Glass runtime check) are tracked in
245`Docs/ACCESSIBILITY_VERIFICATION.md` and #21 — they close on device, not in CI.
246
188247## v5.0.0 Major: Contract Stabilization & Engineering Health
189248
190249Goal: earn long-term compatibility promises — and pay down the debt that the
@@ -194,10 +253,11 @@ feature releases above will accumulate.
194253 and settings.
195254- Stabilize the public local API response contract; document compatibility
196255 guarantees and planned deprecations.
197- **Establish a test target.** The project currently has no XCTest target and no
198 tests; add one and cover the deterministic core first — `DomainReportBuilder`,
199 `DomainReportExporter`, `DomainDataPortabilityService` (merge/replace dedup),
200 and `DiffService` — before locking down external contracts.
256- **Extend the test net to the deterministic core.** v4.9.0 established
257 `DomainDigUITests` (accessibility audit + enforcement gate); unit coverage of
258 `DomainReportBuilder`, `DomainReportExporter`, `DomainDataPortabilityService`
259 (merge/replace dedup), and `DiffService` is still needed before locking down
260 external contracts.
201261- **Decompose the god-files** behind that test net: `DomainViewModel.swift`
202262 (~4.7k lines) and `ContentView.swift` (~3.8k lines) into focused units
203263 (audit, monitoring, workflows, portability).