krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

54c151e22f8a5d4d7a9c290e79fc7626415f7083

unsigned

author: Christian Cleberg <hello@cleberg.net> · 2026-07-17T15:47:27Z

v4.7.0: Bump version and mark shipped in roadmap

Bump AppVersion/marketing version to 4.7.0 and build number to 39 across the
app, widget, and share extension targets. Mark v4.7.0 (comparison, reputation,
tags/saved views) shipped in RELEASE_ROADMAP.md and escalate the overdue
XCTest-target gap ahead of v5.0.0.
 DomainDig.xcodeproj/project.pbxproj | 24 ++++++++++++------------
 DomainDig/AppVersion.swift          |  2 +-
 RELEASE_ROADMAP.md                  | 31 +++++++++++++++++++++----------
 3 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/DomainDig.xcodeproj/project.pbxproj b/DomainDig.xcodeproj/project.pbxproj
index 1b1aead..e51e7f8 100644
--- a/DomainDig.xcodeproj/project.pbxproj
+++ b/DomainDig.xcodeproj/project.pbxproj
@@ -495,7 +495,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -512,7 +512,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -532,7 +532,7 @@
 				ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
 				CODE_SIGN_ENTITLEMENTS = DomainDig/DomainDig.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				ENABLE_PREVIEWS = YES;
 				GENERATE_INFOPLIST_FILE = YES;
@@ -549,7 +549,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				STRING_CATALOG_GENERATE_SYMBOLS = YES;
@@ -568,7 +568,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -579,7 +579,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -597,7 +597,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = DomainDigWidget/DomainDigWidget.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = DomainDigWidget/Info.plist;
@@ -608,7 +608,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigWidget;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -626,7 +626,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -637,7 +637,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
@@ -655,7 +655,7 @@
 				APPLICATION_EXTENSION_API_ONLY = YES;
 				CODE_SIGN_ENTITLEMENTS = DomainDigShareExtension/DomainDigShareExtension.entitlements;
 				CODE_SIGN_STYLE = Automatic;
-				CURRENT_PROJECT_VERSION = 38;
+				CURRENT_PROJECT_VERSION = 39;
 				DEVELOPMENT_TEAM = ZCNAX3VL9D;
 				GENERATE_INFOPLIST_FILE = NO;
 				INFOPLIST_FILE = DomainDigShareExtension/Info.plist;
@@ -666,7 +666,7 @@
 					"@executable_path/Frameworks",
 					"@executable_path/../../Frameworks",
 				);
-				MARKETING_VERSION = 4.6.0;
+				MARKETING_VERSION = 4.7.0;
 				PRODUCT_BUNDLE_IDENTIFIER = net.cleberg.DomainDig.DomainDigShareExtension;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
diff --git a/DomainDig/AppVersion.swift b/DomainDig/AppVersion.swift
index 3a86f97..9865f5b 100644
--- a/DomainDig/AppVersion.swift
+++ b/DomainDig/AppVersion.swift
@@ -2,6 +2,6 @@ import Foundation
 
 enum AppVersion {
     nonisolated static var current: String {
-        "4.6.0"
+        "4.7.0"
     }
 }
diff --git a/RELEASE_ROADMAP.md b/RELEASE_ROADMAP.md
index c6dd564..dd07fa9 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.6.0`.
+Current version: `v4.7.0`.
 
 ## v4.4.1 Patch: Release Readiness — ✅ shipped
 
@@ -50,15 +50,23 @@ Goal: make monitoring and results feel first-class across contexts.
 Deferred: monitoring-alert Live Activities (only the sweep activity shipped) and
 Lock Screen accessory widget families (carried over from v4.5.0).
 
-## v4.7.0 Minor: Intelligence & Comparison
+## v4.7.0 Minor: Intelligence & Comparison — ✅ shipped
 
 Goal: help users interpret and organize, not just collect.
 
-- **Domain-vs-domain comparison** (side-by-side), extending the existing
-  time-based `DiffService` to compare two distinct domains.
-- **Reputation / blocklist signals** as a new optional data source (currently
-  absent); surfaced in the report and available to monitoring alerts.
-- **Tags / folders and saved views** for the watchlist to organize large sets.
+- **Domain-vs-domain comparison** — `DiffService.compare(domainA:domainB:)`
+  reuses the existing section-diff builders; `DomainCompareView` (Watchlist
+  toolbar → "Compare Domains") picks two tracked domains and renders the result
+  with the existing diff section UI.
+- **Reputation / blocklist signals** — a new pluggable data source
+  (`ExternalDataService.reputation(domain:)`, Pro+) mirroring the existing
+  ownership/DNS-history/pricing enrichment pattern. Ships with no bundled
+  third-party endpoint; folds a listed status into risk score/factors and
+  insights, so it rides the existing report and monitoring change-severity
+  pipeline rather than needing bespoke monitoring wiring.
+- **Tags and saved views** for the watchlist — freeform tags per tracked
+  domain, tag filter chips, and named saved filter/sort/tag presets
+  (UserDefaults-backed; not yet part of backup/restore).
 
 ## v4.8.0 Minor: Reporting & Sharing
 
@@ -88,6 +96,9 @@ feature releases above will accumulate.
 ## Cross-cutting note
 
 New feature surfaces (widgets, intents, extensions) each add a target and a
-persistence/entitlement seam. Add at least characterization tests for
-`DomainDataPortabilityService` and the report builders **before** v4.7.0, so the
-v5.0.0 contract and refactor work has a safety net rather than starting from zero.
+persistence/entitlement seam. This project still has **no XCTest target** —
+v4.5.0 through v4.7.0 all shipped without the characterization-test safety net
+originally recommended before v4.7.0. That gap is now larger (comparison,
+reputation, and tags/saved-views all touch persisted models with hand-written
+backward-compatible decoders) and should be the very first thing v5.0.0 does,
+not a later item within it.