Skip to content

Implement v4.7.0: Intelligence & Comparison#6

Merged
ccleberg merged 4 commits into
mainfrom
feat/v4.7.0
Jul 17, 2026
Merged

Implement v4.7.0: Intelligence & Comparison#6
ccleberg merged 4 commits into
mainfrom
feat/v4.7.0

Conversation

@ccleberg

Copy link
Copy Markdown
Member

Delivers the v4.7.0 "Intelligence & Comparison" roadmap phase.

What

  • Domain-vs-domain comparisonDiffService.compare(domainA:domainB:) reuses the existing section-diff builders (previously only used for time-based comparison) to diff two distinct domains' latest reports. DomainCompareView (Watchlist toolbar → "Compare Domains") lets you pick two tracked domains and renders the result with the existing DomainDiffView.
  • Reputation / blocklist data sourceExternalDataService.reputation(domain:), a new pluggable-URL enrichment mirroring the existing ownership-history/DNS-history/pricing pattern (Pro+ gated). Ships with no bundled third-party endpoint — with nothing configured it resolves to unavailable, never fabricates "clean". A listed status folds into DomainInsightEngine's risk score/factors and top-level insights, so it's surfaced in the report UI and rides the existing risk/change-severity pipeline that monitoring already reacts to — no bespoke monitoring wiring needed. Threaded through LookupSnapshot/HistoryEntry (backward-compatible decode) so results persist, and exported in text/CSV/JSON.
  • Watchlist tags + saved viewsTrackedDomain.tags: [String] (backward-compatible decode), tag editing UI, tag filter chips, and named saved filter/sort/tag presets (WatchlistSavedView, UserDefaults-backed).

Version

Bumped to 4.7.0 (build 39) across app, widget, and share extension targets.

Verification

  • xcodebuild build (scheme DomainDig, iOS 26 sim) → BUILD SUCCEEDED after each commit.
  • Installed + launched on simulator after the reputation and tags/saved-views changes: no crash.

Honest gaps

  • The roadmap's cross-cutting note asked for characterization tests before v4.7.0 (there's still no XCTest target in this project). That didn't happen — v4.5.0 through v4.7.0 all shipped without it. I've escalated this in RELEASE_ROADMAP.md: it should be the first thing v5.0.0 does, since comparison/reputation/tags all touch persisted models with hand-written backward-compatible decoders now.
  • Saved views are intentionally not included in backup/restore (DomainDataPortabilityService) — they're a lightweight local preference, not user-authored content like tracked domains. Flagging in case that's wrong.
  • Reputation has no bundled data source. It's real, working plumbing (persistence, risk integration, exports, UI) but returns "unavailable" until someone points ExternalReputationURL at an endpoint — deliberately, to avoid taking on a third-party dependency/ToS decision unilaterally.

ccleberg added 4 commits July 17, 2026 10:25
- DiffService.compare(domainA:domainB:) reuses the existing section-diff
  builders to compare two distinct domains' latest reports, returning a new
  DomainComparisonResult (parallel to the time-based DomainDiff).
- DomainCompareView: pick two tracked domains and render the comparison with
  the existing DomainDiffView section renderer.
- Entry point: "Compare Domains" in the Watchlist toolbar menu, shown once at
  least two domains are tracked.
- New DomainReputationResult model (status: clean/listed/unknown, listed
  sources, checked-at) and a `reputation(domain:)` method on ExternalDataService,
  mirroring the existing pluggable-URL enrichment pattern (ownership history,
  DNS history, extended subdomains, pricing). With no endpoint configured
  (the default; DomainDig ships no bundled third-party reputation dependency)
  it resolves to unavailable rather than "clean".
- New .reputation FeatureCapability/DataCapability, gated Pro+ like domainPricing.
- Threaded reputation/reputationError through LookupSnapshot and HistoryEntry
  (backward-compatible decode) so results persist with history entries.
- Auto-fetched in performLookup alongside pricing; surfaced as a "Reputation"
  info row, folded into DomainInsightEngine's risk score/factors and top-level
  insights (a listed domain raises risk score and adds a factor/insight), and
  exported in text, CSV, and JSON report output.
- Reputation-driven risk changes ride the existing change-severity pipeline, so
  a listed status flip is visible to monitoring the same way any other risk
  delta is, without bespoke monitoring wiring.
- TrackedDomain.tags: [String] (backward-compatible custom decode), with
  updateTags(_:for:) and a normalized/deduplicated write path.
- Tag editing in TrackedDomainDetailView (comma-separated field, chip display).
- Tag filter chips in WatchlistView (TagFilterChipRowView), integrated into
  filteredTrackedDomains alongside the existing filter/search.
- Saved views: name + snapshot the current tag/filter/sort as a
  WatchlistSavedView preset (UserDefaults-backed, not part of backup/restore),
  with a management sheet to apply or delete presets.
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.
@sonarqubecloud

Copy link
Copy Markdown

@ccleberg ccleberg self-assigned this Jul 17, 2026
@ccleberg ccleberg added the enhancement New feature or request label Jul 17, 2026
@ccleberg
ccleberg merged commit 54c151e into main Jul 17, 2026
2 checks passed
@ccleberg
ccleberg deleted the feat/v4.7.0 branch July 17, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant