commit 51dea575b98467630bb0174fe041bd62943a3938
parent f851ea71b5e384b526455128cb1f2b299ce513bd
Author: Michael Camilleri <[email protected]>
Date: Sat, 23 May 2026 18:12:32 +0900
Increase diagnostic detail
Diffstat:
2 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/Crossmate/CrossmateApp.swift b/Crossmate/CrossmateApp.swift
@@ -458,8 +458,8 @@ private struct PuzzleDisplayView: View {
switch outcome {
case .upgraded:
services.eventLog.note("[upgrade NYT \(gameID.uuidString.prefix(8))] applied")
- case .mismatched:
- services.eventLog.note("[upgrade NYT \(gameID.uuidString.prefix(8))] structural mismatch — clue not updated", level: "warn")
+ case .mismatched(let reason):
+ services.eventLog.note("[upgrade NYT \(gameID.uuidString.prefix(8))] structural mismatch — \(reason)", level: "warn")
case .failed(let error):
services.eventLog.note("[upgrade NYT \(gameID.uuidString.prefix(8))] fetch failed: \(error)", level: "error")
}
diff --git a/Crossmate/Services/NYTPuzzleUpgrader.swift b/Crossmate/Services/NYTPuzzleUpgrader.swift
@@ -16,8 +16,9 @@ enum NYTPuzzleUpgrader {
case upgraded(newSource: String)
/// The new XD's grid differs from the persisted one. Per the upgrade
/// policy, the caller should stamp the new CmVer but keep the old
- /// source so the player's moves stay valid.
- case mismatched
+ /// source so the player's moves stay valid. `reason` describes the
+ /// first divergence found so diagnostics can pinpoint the cell.
+ case mismatched(reason: String)
/// The fetch or a parse step failed. Caller should leave both the
/// source and the CmVer untouched so the upgrade is retried on the
/// next launch.
@@ -45,7 +46,9 @@ enum NYTPuzzleUpgrader {
return .failed(error)
}
- guard structurallyEquivalent(oldXD, newXD) else { return .mismatched }
+ if let reason = structuralDivergence(oldXD, newXD) {
+ return .mismatched(reason: reason)
+ }
return .upgraded(newSource: newSource)
}
@@ -114,19 +117,34 @@ enum NYTPuzzleUpgrader {
/// cell. Special markers, accepted variants, clues, and headers may all
/// differ.
static func structurallyEquivalent(_ a: XD, _ b: XD) -> Bool {
- guard a.width == b.width, a.height == b.height else { return false }
+ structuralDivergence(a, b) == nil
+ }
+
+ /// Walks the grid in row-major order and returns a short description of
+ /// the first cell that disagrees, or nil when the two are equivalent.
+ /// Used to populate `.mismatched(reason:)` for diagnostic logging.
+ static func structuralDivergence(_ a: XD, _ b: XD) -> String? {
+ if a.width != b.width || a.height != b.height {
+ return "dims old=\(a.width)x\(a.height) new=\(b.width)x\(b.height)"
+ }
for row in 0..<a.height {
for col in 0..<a.width {
switch (a.cells[row][col], b.cells[row][col]) {
case (.block, .block):
continue
case let (.open(left, _, _), .open(right, _, _)):
- if left != right { return false }
+ if left != right {
+ return "cell(r=\(row),c=\(col)) old=\(left ?? "nil") new=\(right ?? "nil")"
+ }
+ case (.block, .open):
+ return "cell(r=\(row),c=\(col)) old=block new=open"
+ case (.open, .block):
+ return "cell(r=\(row),c=\(col)) old=open new=block"
default:
- return false
+ return "cell(r=\(row),c=\(col)) kind mismatch"
}
}
}
- return true
+ return nil
}
}