Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import Foundation

/// Counterfactual shadow evidence for snapshots that recur after a strong cumulative-token reset.
/// This analyzer cannot produce accounting rows or selector input and never changes ledger totals.
enum CodexLineageResetEpochDiagnostics {
struct Report: Equatable, Sendable {
let strongResetBoundaryCount: Int
let mixedRegressionCount: Int
let postResetRepeatedFingerprintCount: Int
let sameOwnerRepeatCount: Int
let crossOwnerRepeatCount: Int
let estimatedSuppressed: CodexLineageLedger.Totals
let estimatedSuppressedUTC: [String: CodexLineageLedger.Totals]
let sameOwnerEstimatedSuppressed: CodexLineageLedger.Totals
let sameOwnerEstimatedSuppressedUTC: [String: CodexLineageLedger.Totals]

static let empty = Self(
strongResetBoundaryCount: 0,
mixedRegressionCount: 0,
postResetRepeatedFingerprintCount: 0,
sameOwnerRepeatCount: 0,
crossOwnerRepeatCount: 0,
estimatedSuppressed: .zero,
estimatedSuppressedUTC: [:],
sameOwnerEstimatedSuppressed: .zero,
sameOwnerEstimatedSuppressedUTC: [:])
}

private struct Fingerprint: Hashable {
let last: CodexLineageLedger.Totals
let total: CodexLineageLedger.Totals
}

private struct CandidateKey: Hashable {
let owner: String
let epoch: Int
let fingerprint: Fingerprint
}

private struct Occurrence {
let owner: String
let stream: Int
let epoch: Int
let fingerprint: Fingerprint
let date: Date
let originalIndex: Int
}

static func analyze(
families: [CodexLineageEngine.PreparedFamily],
checkCancellation: CostUsageScanner.CancellationCheck? = nil) throws -> Report
{
var report = Report.empty
for family in families {
try checkCancellation?()
var occurrences: [Occurrence] = []
for (stream, document) in family.documents.enumerated() {
try checkCancellation?()
let owner = Self.ownerKey(document)
var dated: [(Int, Date, CodexLineageLedger.Observation)] = []
dated.reserveCapacity(document.observations.count)
for (index, observation) in document.observations.enumerated() {
try checkCancellation?()
guard let date = CostUsageScanner.dateFromTimestamp(observation.timestamp) else { continue }
dated.append((index, date, observation))
}
dated.sort {
if $0.1 != $1.1 {
return $0.1 < $1.1
}
return $0.0 < $1.0
}
var epoch = 0
var previous: CodexLineageLedger.Totals?
var seenInEpoch: Set<Fingerprint> = []
for (index, date, observation) in dated {
try checkCancellation?()
if let previous {
if Self.isStrongReset(from: previous, to: observation.total) {
epoch += 1
seenInEpoch.removeAll(keepingCapacity: true)
report = report.addingStrongReset()
} else if Self.isMixedRegression(from: previous, to: observation.total) {
report = report.addingMixedRegression()
}
}
previous = observation.total
let fingerprint = Fingerprint(last: observation.last, total: observation.total)
guard seenInEpoch.insert(fingerprint).inserted else { continue }
occurrences.append(.init(
owner: owner,
stream: stream,
epoch: epoch,
fingerprint: fingerprint,
date: date,
originalIndex: index))
}
}
occurrences.sort {
if $0.date != $1.date {
return $0.date < $1.date
}
if $0.owner != $1.owner {
return $0.owner < $1.owner
}
if $0.epoch != $1.epoch {
return $0.epoch < $1.epoch
}
return $0.originalIndex < $1.originalIndex
}
var familySeen: [Fingerprint: [String: Date]] = [:]
var streamEpochs: [Int: [Fingerprint: Set<Int>]] = [:]
var counted: Set<CandidateKey> = []
for occurrence in occurrences {
try checkCancellation?()
let priorOwnerEpochs = streamEpochs[occurrence.stream]?[occurrence.fingerprint] ?? []
let sameOwner = priorOwnerEpochs.contains { $0 < occurrence.epoch }
let crossOwner = familySeen[occurrence.fingerprint]?.contains { owner, date in
owner != occurrence.owner && date < occurrence.date
} ?? false
let key = CandidateKey(
owner: occurrence.owner,
epoch: occurrence.epoch,
fingerprint: occurrence.fingerprint)
if occurrence.epoch > 0, sameOwner || crossOwner, counted.insert(key).inserted {
report = report.addingCandidate(
occurrence.fingerprint.last,
day: Self.utcDayKey(from: occurrence.date),
sameOwner: sameOwner)
}
familySeen[occurrence.fingerprint, default: [:]][occurrence.owner] = min(
familySeen[occurrence.fingerprint]?[occurrence.owner] ?? occurrence.date,
occurrence.date)
streamEpochs[occurrence.stream, default: [:]][occurrence.fingerprint, default: []]
.insert(occurrence.epoch)
}
}
return report
}

private static func ownerKey(_ document: CodexLineageLedger.Document) -> String {
let owner = UUID(uuidString: document.ownerID)?.uuidString.lowercased() ?? document.ownerID
return document.scopeID + "\u{0}" + owner
}

private static func utcDayKey(from date: Date) -> String {
var calendar = Calendar(identifier: .gregorian)
calendar.timeZone = .gmt
let components = calendar.dateComponents([.year, .month, .day], from: date)
return String(format: "%04d-%02d-%02d", components.year ?? 0, components.month ?? 0, components.day ?? 0)
}

private static func isStrongReset(
from previous: CodexLineageLedger.Totals,
to current: CodexLineageLedger.Totals) -> Bool
{
let nonIncreasing = current.input <= previous.input && current.cached <= previous.cached
&& current.output <= previous.output
return nonIncreasing && current != previous
}

private static func isMixedRegression(
from previous: CodexLineageLedger.Totals,
to current: CodexLineageLedger.Totals) -> Bool
{
let regressed = current.input < previous.input || current.cached < previous.cached
|| current.output < previous.output
let increased = current.input > previous.input || current.cached > previous.cached
|| current.output > previous.output
return regressed && increased
}
}

extension CodexLineageResetEpochDiagnostics.Report {
fileprivate func addingStrongReset() -> Self {
.init(
strongResetBoundaryCount: self.strongResetBoundaryCount + 1,
mixedRegressionCount: self.mixedRegressionCount,
postResetRepeatedFingerprintCount: self.postResetRepeatedFingerprintCount,
sameOwnerRepeatCount: self.sameOwnerRepeatCount,
crossOwnerRepeatCount: self.crossOwnerRepeatCount,
estimatedSuppressed: self.estimatedSuppressed,
estimatedSuppressedUTC: self.estimatedSuppressedUTC,
sameOwnerEstimatedSuppressed: self.sameOwnerEstimatedSuppressed,
sameOwnerEstimatedSuppressedUTC: self.sameOwnerEstimatedSuppressedUTC)
}

fileprivate func addingMixedRegression() -> Self {
.init(
strongResetBoundaryCount: self.strongResetBoundaryCount,
mixedRegressionCount: self.mixedRegressionCount + 1,
postResetRepeatedFingerprintCount: self.postResetRepeatedFingerprintCount,
sameOwnerRepeatCount: self.sameOwnerRepeatCount,
crossOwnerRepeatCount: self.crossOwnerRepeatCount,
estimatedSuppressed: self.estimatedSuppressed,
estimatedSuppressedUTC: self.estimatedSuppressedUTC,
sameOwnerEstimatedSuppressed: self.sameOwnerEstimatedSuppressed,
sameOwnerEstimatedSuppressedUTC: self.sameOwnerEstimatedSuppressedUTC)
}

fileprivate func addingCandidate(_ totals: CodexLineageLedger.Totals, day: String, sameOwner: Bool) -> Self {
var estimated = self.estimatedSuppressed
estimated.add(totals)
var days = self.estimatedSuppressedUTC
var dayTotals = days[day] ?? .zero
dayTotals.add(totals)
days[day] = dayTotals
var sameOwnerEstimated = self.sameOwnerEstimatedSuppressed
var sameOwnerDays = self.sameOwnerEstimatedSuppressedUTC
if sameOwner {
sameOwnerEstimated.add(totals)
var sameOwnerDay = sameOwnerDays[day] ?? .zero
sameOwnerDay.add(totals)
sameOwnerDays[day] = sameOwnerDay
}
return .init(
strongResetBoundaryCount: self.strongResetBoundaryCount,
mixedRegressionCount: self.mixedRegressionCount,
postResetRepeatedFingerprintCount: self.postResetRepeatedFingerprintCount + 1,
sameOwnerRepeatCount: self.sameOwnerRepeatCount + (sameOwner ? 1 : 0),
crossOwnerRepeatCount: self.crossOwnerRepeatCount + (sameOwner ? 0 : 1),
estimatedSuppressed: estimated,
estimatedSuppressedUTC: days,
sameOwnerEstimatedSuppressed: sameOwnerEstimated,
sameOwnerEstimatedSuppressedUTC: sameOwnerDays)
}
}
11 changes: 10 additions & 1 deletion Sources/CodexBarCore/Providers/Codex/CodexLineageShadow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ enum CodexLineageShadow {
let incompleteProvenanceFamilyCount: Int
let containedFamilyCount: Int
let containmentReasonCounts: [CodexLineageLedger.ContainmentReason: Int]
let resetEpochDiagnostics: CodexLineageResetEpochDiagnostics.Report
}

static func run(
Expand Down Expand Up @@ -66,6 +67,13 @@ enum CodexLineageShadow {
unresolvedParents: discovery.unresolvedParents,
localTimeZone: localTimeZone,
checkCancellation: checkCancellation)
let preparedFamilies = try CodexLineageEngine.prepareFamilies(
documents: documents,
unresolvedParents: discovery.unresolvedParents,
checkCancellation: checkCancellation)
let resetEpochDiagnostics = try CodexLineageResetEpochDiagnostics.analyze(
families: preparedFamilies,
checkCancellation: checkCancellation)
let ledger = conservative.primary
let legacy = Self.legacyTotalsByDay(legacyDays)
let dayKeys = Set(legacy.keys).union(ledger.localDays.keys)
Expand All @@ -92,7 +100,8 @@ enum CodexLineageShadow {
}
return false
},
containmentReasonCounts: Self.containmentReasonCounts(conservative.families))
containmentReasonCounts: Self.containmentReasonCounts(conservative.families),
resetEpochDiagnostics: resetEpochDiagnostics)
}

private static func containmentReasonCounts(
Expand Down
82 changes: 82 additions & 0 deletions Tests/CodexBarTests/CodexLineageLocalValidationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,38 @@ struct CodexLineageLocalValidationTests {
let finalized: Bool
}

@Test
func `local reset epoch diagnostic`() throws {
guard ProcessInfo.processInfo.environment["CODEXBAR_VALIDATE_RESET_EPOCHS_ONLY"] == "1" else { return }
CodexBarLog.setLogLevel(.critical)
let root = try #require(ProcessInfo.processInfo.environment["CODEXBAR_LINEAGE_VALIDATION_ROOT"])
let snapshotHome = URL(fileURLWithPath: root, isDirectory: true)
.appendingPathComponent("codex-home", isDirectory: true)
let roots = [
snapshotHome.appendingPathComponent("sessions", isDirectory: true),
snapshotHome.appendingPathComponent("archived_sessions", isDirectory: true),
]
let included = Self.rollouts(roots: roots, days: Self.discoveryDays)
let report = try Self.resetEpochDiagnostics(includedFiles: included, roots: roots)
let output: [String: Any] = [
"strongResetBoundaries": report.strongResetBoundaryCount,
"mixedRegressions": report.mixedRegressionCount,
"postResetRepeatedFingerprints": report.postResetRepeatedFingerprintCount,
"sameOwnerRepeats": report.sameOwnerRepeatCount,
"crossOwnerRepeats": report.crossOwnerRepeatCount,
"estimatedSuppressedTokens": report.estimatedSuppressed.input + report.estimatedSuppressed.output,
"estimatedSuppressedUTC": report.estimatedSuppressedUTC.mapValues { $0.input + $0.output },
"sameOwnerEstimatedSuppressedTokens": report.sameOwnerEstimatedSuppressed.input
+ report.sameOwnerEstimatedSuppressed.output,
"sameOwnerEstimatedSuppressedUTC": report.sameOwnerEstimatedSuppressedUTC.mapValues {
$0.input + $0.output
},
]
let data = try JSONSerialization.data(withJSONObject: output, options: [.sortedKeys])
let encoded = try #require(String(bytes: data, encoding: .utf8))
print("CODEX_LINEAGE_RESET_EPOCHS " + encoded)
}

// The opt-in replay is intentionally linear so its immutable snapshot, scan, and comparison
// lifecycle stays auditable in one place.
// swiftlint:disable function_body_length
Expand Down Expand Up @@ -76,6 +108,14 @@ struct CodexLineageLocalValidationTests {
let references = try Self.referenceTotals()
Self.progress("legacy-ready")

let resetEpochDiagnostics: CodexLineageResetEpochDiagnostics.Report? = if ProcessInfo.processInfo
.environment["CODEXBAR_VALIDATE_RESET_EPOCHS"] == "1"
{
try Self.resetEpochDiagnostics(includedFiles: included, roots: [sessions, archived])
} else {
nil
}

let directModes: [CodexLineageAccountingMode] = switch ProcessInfo.processInfo
.environment["CODEXBAR_VALIDATE_SCANNER_MODES"]
{
Expand Down Expand Up @@ -234,6 +274,25 @@ struct CodexLineageLocalValidationTests {
"lineageMilliseconds": Self.milliseconds(lineageDuration),
],
"ordinaryDayDivergenceCount": ordinaryDivergenceCount,
"resetEpochDiagnostics": [
"strongResetBoundaries": resetEpochDiagnostics?.strongResetBoundaryCount ?? 0,
"mixedRegressions": resetEpochDiagnostics?.mixedRegressionCount ?? 0,
"postResetRepeatedFingerprints": resetEpochDiagnostics?.postResetRepeatedFingerprintCount ?? 0,
"sameOwnerRepeats": resetEpochDiagnostics?.sameOwnerRepeatCount ?? 0,
"crossOwnerRepeats": resetEpochDiagnostics?.crossOwnerRepeatCount ?? 0,
"estimatedSuppressedTokens": resetEpochDiagnostics.map {
$0.estimatedSuppressed.input + $0.estimatedSuppressed.output
} ?? 0,
"estimatedSuppressedUTC": resetEpochDiagnostics?.estimatedSuppressedUTC.mapValues {
$0.input + $0.output
} ?? [:],
"sameOwnerEstimatedSuppressedTokens": resetEpochDiagnostics.map {
$0.sameOwnerEstimatedSuppressed.input + $0.sameOwnerEstimatedSuppressed.output
} ?? 0,
"sameOwnerEstimatedSuppressedUTC": resetEpochDiagnostics?.sameOwnerEstimatedSuppressedUTC.mapValues {
$0.input + $0.output
} ?? [:],
],
"aggregateImproved": classification.improvesAggregateError,
// This replay is one validation artifact, not permission to remove the rollback path.
"supportsLegacyRemoval": false,
Expand Down Expand Up @@ -265,6 +324,29 @@ struct CodexLineageLocalValidationTests {
return references
}

private static func resetEpochDiagnostics(
includedFiles: [URL],
roots: [URL]) throws -> CodexLineageResetEpochDiagnostics.Report
{
self.progress("reset-epoch-start")
let discovery = try CodexLineageDiscovery.discover(includedFiles: includedFiles, roots: roots)
let documents = discovery.documents.map { document in
CodexLineageLedger.Document(
ownerID: document.ownerID,
metadataSessionID: document.metadataSessionID,
parentSessionID: document.parentSessionID,
observations: document.observations,
scopeID: document.scopeID,
incompleteObservationCount: document.incompleteObservationCount)
}
let families = try CodexLineageEngine.prepareFamilies(
documents: documents,
unresolvedParents: discovery.unresolvedParents)
let report = try CodexLineageResetEpochDiagnostics.analyze(families: families)
Self.progress("reset-epoch-ready")
return report
}

private static func rollouts(roots: [URL], days: Set<String>) -> [URL] {
roots.flatMap { root -> [URL] in
guard let enumerator = FileManager.default.enumerator(
Expand Down
Loading
Loading