diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift index fc57fc6..99a3430 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPageMapReconciliationCoordinator.swift @@ -28,8 +28,10 @@ final class RDEPUBPageMapReconciliationCoordinator { guard let currentWindow else { if let candidatePageMap { + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, fullReplace candidateChapters=\(candidatePageMap.totalChapters) candidatePages=\(candidatePageMap.totalPages)") return .fullReplace(candidatePageMap) } + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no currentWindow, no candidate") return .keepCurrentWindow } @@ -54,7 +56,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if coverageRatio < 0.8 { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: candidate doesn't cover protected area (coverage=\(coverageRatio))" + "evaluateTakeover: blocked by jumpSession protection coverage=\(String(format: "%.2f", coverageRatio)) currentSpine=\(currentSpineIndex) protected=\(protectedIndices)" ) return .keepCurrentWindow } @@ -72,7 +74,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if !hasPrev || !hasNext { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: missing adjacent chapter coverage" + "evaluateTakeover: blocked by missing adjacent coverage (segment) currentSpine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)" ) return .keepCurrentWindow } @@ -85,7 +87,7 @@ final class RDEPUBPageMapReconciliationCoordinator { if candidateSegment.renderSignature != currentRenderSignature { RDEPUBBackgroundTrace.log( "Reconciliation", - "blocked: render signature mismatch" + "evaluateTakeover: blocked by render signature mismatch" ) return .keepCurrentWindow } @@ -109,6 +111,7 @@ final class RDEPUBPageMapReconciliationCoordinator { ) } + RDEPUBBackgroundTrace.log("Reconciliation", "evaluateTakeover: no candidate, keepCurrentWindow") return .keepCurrentWindow } @@ -123,6 +126,10 @@ final class RDEPUBPageMapReconciliationCoordinator { if let currentSpineIndex { if !candidateIndices.contains(currentSpineIndex) { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateSegmentTakeover: keepCurrentWindow — currentSpine=\(currentSpineIndex) not in candidate" + ) return .keepCurrentWindow } } @@ -132,6 +139,10 @@ final class RDEPUBPageMapReconciliationCoordinator { let hasNext = candidateIndices.contains(currentSpineIndex + 1) || currentSpineIndex == lastBuildableSpineIndex if !hasPrev || !hasNext { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateSegmentTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext)" + ) return .keepCurrentWindow } } @@ -164,9 +175,14 @@ final class RDEPUBPageMapReconciliationCoordinator { lastBuildableSpineIndex: Int ) -> RDEPUBPageMapTakeoverDecision { let candidateIndices = Set(candidatePageMap.entries.map { $0.spineIndex }) + let currentEntries = currentWindow.entries.count if let currentSpineIndex { if !candidateIndices.contains(currentSpineIndex) { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — currentSpine=\(currentSpineIndex) not in candidate (candidateChapters=\(candidateIndices.count) currentChapters=\(currentEntries) candidatePages=\(candidatePageMap.totalPages) currentPages=\(currentWindow.totalPages))" + ) return .keepCurrentWindow } } @@ -176,15 +192,27 @@ final class RDEPUBPageMapReconciliationCoordinator { let hasNext = candidateIndices.contains(currentSpineIndex + 1) || currentSpineIndex == lastBuildableSpineIndex if !hasPrev || !hasNext { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — missing adjacent spine=\(currentSpineIndex) hasPrev=\(hasPrev) hasNext=\(hasNext) lastBuildable=\(lastBuildableSpineIndex)" + ) return .keepCurrentWindow } } - let isComplete = candidateIndices.count >= currentWindow.entries.count + let isComplete = candidateIndices.count >= currentEntries if isComplete { + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: fullReplace — candidateChapters=\(candidateIndices.count) currentChapters=\(currentEntries) candidatePages=\(candidatePageMap.totalPages) currentPages=\(currentWindow.totalPages)" + ) return .fullReplace(candidatePageMap) } + RDEPUBBackgroundTrace.log( + "Reconciliation", + "evaluateFullPageMapTakeover: keepCurrentWindow — candidateChapters=\(candidateIndices.count) < currentChapters=\(currentEntries)" + ) return .keepCurrentWindow } diff --git a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift index 433116c..c68f61e 100644 --- a/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift +++ b/Sources/RDReaderView/EPUBUI/ReaderController/RDEPUBPresentationRuntime.swift @@ -226,7 +226,8 @@ final class RDEPUBPresentationRuntime { switch decision { case .keepCurrentWindow: - RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow") + RDEPUBBackgroundTrace.log("Reconciliation", "decision: keepCurrentWindow — pending update removed to prevent indefinite retry") + removePendingPageMapUpdate(at: index) return false case .fullReplace(let newPageMap):