aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-07-22 23:26:48 -0500
committerChristian Cleberg <[email protected]>2026-07-23 00:42:28 -0500
commit655d11b59a273c14720952478ccebec20a9627bd (patch)
tree77c2a5ae4ed2ab69970aae4fd25620224d24b1cb
parent872583eca8b4e7ae6ef85917604d4a5257b9d125 (diff)
downloaddomain-dig-655d11b59a273c14720952478ccebec20a9627bd.tar.gz
domain-dig-655d11b59a273c14720952478ccebec20a9627bd.tar.bz2
domain-dig-655d11b59a273c14720952478ccebec20a9627bd.zip
chore: bump to v4.9.0; record the release in the roadmapv4.9.0
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.
-rw-r--r--DomainDig.xcodeproj/project.pbxproj32
-rw-r--r--DomainDig/AppVersion.swift2
-rw-r--r--RELEASE_ROADMAP.md70
3 files changed, 82 insertions, 22 deletions
diff --git a/DomainDig.xcodeproj/project.pbxproj b/DomainDig.xcodeproj/project.pbxproj
index c132d2a..d3070e4 100644
--- a/DomainDig.xcodeproj/project.pbxproj
+++ b/DomainDig.xcodeproj/project.pbxproj
@@ -566,7 +566,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -583,7 +583,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -603,7 +603,7 @@
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
@@ -620,7 +620,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
PRODUCT_NAME = "$(TARGET_NAME)";
STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -639,7 +639,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -650,7 +650,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -668,7 +668,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -679,7 +679,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -697,7 +697,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -708,7 +708,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -726,7 +726,7 @@
APPLICATION_EXTENSION_API_ONLY = YES;
CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = NO;
INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -737,7 +737,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
@@ -753,11 +753,11 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDigUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
@@ -772,11 +772,11 @@
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
- CURRENT_PROJECT_VERSION = 43;
+ CURRENT_PROJECT_VERSION = 44;
DEVELOPMENT_TEAM = ZCNAX3VL9D;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
- MARKETING_VERSION = 4.8.3;
+ MARKETING_VERSION = 4.9.0;
PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDigUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_APPROACHABLE_CONCURRENCY = YES;
diff --git a/DomainDig/AppVersion.swift b/DomainDig/AppVersion.swift
index a0956df..4944a3f 100644
--- a/DomainDig/AppVersion.swift
+++ b/DomainDig/AppVersion.swift
@@ -2,6 +2,6 @@ import Foundation
enum AppVersion {
nonisolated static var current: String {
- "4.8.3"
+ "4.9.0"
}
}
diff --git a/RELEASE_ROADMAP.md b/RELEASE_ROADMAP.md
index 30158e7..d42540a 100644
--- a/RELEASE_ROADMAP.md
+++ b/RELEASE_ROADMAP.md
@@ -6,7 +6,7 @@ ports, geolocation, subdomains, availability). The next several releases invest
in *reach and surfacing* — getting that data onto more iOS surfaces and into more
workflows — rather than adding raw protocol checks.
-Current version: `v4.8.3`.
+Current version: `v4.9.0`.
## v4.4.1 Patch: Release Readiness — ✅ shipped
@@ -185,6 +185,65 @@ hits on SwiftUI `$binding` shorthand in `AuditModeView`, which cannot be
renamed. These want a *Won't Fix* / *Safe* resolution in SonarCloud rather than
a code change.
+## v4.9.0 Minor: Accessibility, Appearance & Engineering Health — ✅ shipped
+
+Goal: make the app usable by every iOS user — full accessibility pass (#21),
+light mode, and the engineering scaffolding to keep both from regressing.
+
+- **Semantic colour system** — every hard-coded colour replaced with adaptive
+ colorsets in `Shared/Colors.xcassets` (Any/Dark + High Contrast variants),
+ shared by app, widget, and share extension via the synchronized `Shared`
+ group. Every status colour clears WCAG AA on its page, its card, and its
+ badge surface, in both schemes; measured, not asserted. The accent is now
+ blue (`#0000FF` light / `#4DA3FF` dark), split into foreground
+ (`StatusInfo`), fill (`AccentFill`), and on-fill (`AppOnAccent`) roles
+ because one value cannot serve as both text-on-dark and fill-behind-white.
+ `AppStatusTone` pairs each status foreground with an authored surface.
+- **Light mode unlocked** — the 16 scattered `.preferredColorScheme(.dark)`
+ calls removed; appearance (System/Light/Dark) is applied once at the
+ `WindowGroup` and exposed under Settings → Display. `.secondary` (3.29:1 on a
+ light card) replaced with `AppTextSecondary` across 191 sites.
+- **Dynamic Type & reflow** — `Label`-clipped empty-state titles fixed, the
+ 44pt tap-target floor enforced (`AppCopyButton` was 30×30;
+ `controlMinHeight` was 42), `CardView`'s horizontal-scroll default flipped
+ to reflow, dense rows (`WatchlistRowView`, `BatchResultRowView`,
+ `PortfolioExpiryRow`) and the collapsible section headers rebuilt on
+ `ViewThatFits` so badges and buttons can never letter-wrap vertically, and
+ the widget clamped at `accessibility1` (fixed canvas, no scroll).
+- **VoiceOver** — labels on every icon-only control (label-in-name preserved
+ for Voice Control), selected-state on all toggles, badges read as one word,
+ heading-rotor navigation, dense rows collapsed to one element with the
+ detail on the More Content rotor (`accessibilityCustomContent`), technical
+ strings (DNS records, cipher suites) spoken with punctuation, and lookup/
+ sweep completion announcements. Widget rows read as a single phrase.
+- **Colour independence, motion, transparency** — widget status uses the badge
+ symbol vocabulary instead of colour-only dots; `differentiateWithoutColor`
+ adds symbols/borders on demand; all five animation sites honour
+ `reduceMotion`; the one material honours `reduceTransparency`.
+- **Accessibility audit harness** — `DomainDigUITests` runs
+ `performAccessibilityAudit()` over every primary screen at default and
+ AccessibilityXXXL, on CI (newest runtime, clean merge-result checkout) and
+ locally (`Scripts/audit-a11y.sh`, real floor runtime, wired to an opt-in
+ pre-push hook). `DOMAIN_DIG_SEED_FIXTURES` seeds deterministic in-memory
+ rows so the dense paths actually render under audit. The **enforcement
+ ratchet is engaged**: named findings in six categories fail CI, with
+ narrowly characterised, always-logged noise suppressions. Manual
+ verification checklist in `Docs/ACCESSIBILITY_VERIFICATION.md`; findings
+ burndown 20 → 11 with every remaining item characterised as system noise.
+- **Swift 6 language mode** (#27) — all three product targets build under
+ `SWIFT_VERSION = 6.0` with zero warnings. `SMTPChannel` became an actor
+ (fixing a real `CheckedContinuation` double-resume hazard),
+ `SweepActivityController` stores a Sendable activity id, and the remaining
+ isolation issues were resolved layer by layer. The UITests target stays on
+ Swift 5 (XCTest override isolation), recorded as a decision.
+- **Project hygiene** — the misleading project-level deployment target
+ (26.2 shadowing the real 17.6) reconciled; CI selects simulators
+ floor-aware instead of first-match.
+
+Deferred: the Phase 6 manual device passes (VoiceOver walkthrough, Voice
+Control, iPad Full Keyboard Access, Liquid Glass runtime check) are tracked in
+`Docs/ACCESSIBILITY_VERIFICATION.md` and #21 — they close on device, not in CI.
+
## v5.0.0 Major: Contract Stabilization & Engineering Health
Goal: earn long-term compatibility promises — and pay down the debt that the
@@ -194,10 +253,11 @@ feature releases above will accumulate.
and settings.
- Stabilize the public local API response contract; document compatibility
guarantees and planned deprecations.
-- **Establish a test target.** The project currently has no XCTest target and no
- tests; add one and cover the deterministic core first — `DomainReportBuilder`,
- `DomainReportExporter`, `DomainDataPortabilityService` (merge/replace dedup),
- and `DiffService` — before locking down external contracts.
+- **Extend the test net to the deterministic core.** v4.9.0 established
+ `DomainDigUITests` (accessibility audit + enforcement gate); unit coverage of
+ `DomainReportBuilder`, `DomainReportExporter`, `DomainDataPortabilityService`
+ (merge/replace dedup), and `DiffService` is still needed before locking down
+ external contracts.
- **Decompose the god-files** behind that test net: `DomainViewModel.swift`
(~4.7k lines) and `ContentView.swift` (~3.8k lines) into focused units
(audit, monitoring, workflows, portability).