From 7b41195620eadd54d25fa98dcd36735cba906ba3 Mon Sep 17 00:00:00 2001 From: Christian Cleberg Date: Wed, 22 Apr 2026 09:47:24 -0500 Subject: feat(v2.7.0): add provenance, confidence, and reproducibility metadata * add result provenance across major sections * introduce confidence levels for ambiguous outputs * distinguish observed facts from inferred summaries * expand snapshot metadata for reproducibility * improve error classification and partial snapshot handling * include provenance and confidence in export * add local notes for tracked domains and history --- DomainDig/DomainDiffService.swift | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'DomainDig/DomainDiffService.swift') diff --git a/DomainDig/DomainDiffService.swift b/DomainDig/DomainDiffService.swift index 340873d..528841d 100644 --- a/DomainDig/DomainDiffService.swift +++ b/DomainDig/DomainDiffService.swift @@ -67,16 +67,33 @@ enum DomainDiffService { let highlights = summaryHighlights(from: allChangedItems) let severity = allChangedItems.map(\.severity).max() ?? .low let message = summaryMessage(from: allChangedItems, highlights: highlights) + let observedFacts = observedFacts(from: allChangedItems) + let inferredConclusions = highlights.isEmpty ? [] : [message] + let contextNote = comparisonContextNote(from: oldSnapshot, to: newSnapshot) return DomainChangeSummary( hasChanges: !allChangedItems.isEmpty, changedSections: highlights, message: message, severity: severity, - generatedAt: generatedAt + generatedAt: generatedAt, + observedFacts: observedFacts, + inferredConclusions: inferredConclusions, + contextNote: contextNote ) } + static func comparisonContextNote(from oldSnapshot: LookupSnapshot, to newSnapshot: LookupSnapshot) -> String? { + var notes: [String] = [] + if oldSnapshot.resolverURLString != newSnapshot.resolverURLString { + notes.append("Compared snapshots used different DNS resolvers.") + } + if oldSnapshot.resultSource != newSnapshot.resultSource { + notes.append("Compared snapshots came from different collection modes.") + } + return notes.isEmpty ? nil : notes.joined(separator: " ") + } + static func certificateWarningLevel(for snapshot: LookupSnapshot) -> CertificateWarningLevel { guard let days = snapshot.sslInfo?.daysUntilExpiry else { return .none @@ -410,6 +427,14 @@ enum DomainDiffService { return "\(highlights[0]) and \(highlights[1].lowercased())" } + private static func observedFacts(from items: [DomainDiffItem]) -> [String] { + items.prefix(3).map { item in + let oldValue = item.oldValue ?? "none" + let newValue = item.newValue ?? "none" + return "\(item.label): \(oldValue) -> \(newValue)" + } + } + private static func normalized(_ value: String?) -> String? { guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines), !value.isEmpty else { return nil -- cgit v1.2.3