chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
import Foundation
|
||||
|
||||
/// The event kinds a trace records. `decision` events capture a full policy tick (the
|
||||
/// signals it saw plus what it chose). `menuOpen` and `refreshCompleted` capture the two
|
||||
/// ground-truth events the replay engine anchors a simulation to, independent of any candidate
|
||||
/// policy. `timerAdvanced` captures the one place live behavior *isn't* a plain tick loop: when
|
||||
/// opening the menu makes `UsageStore.noteMenuOpened(at:)` pull the next adaptive refresh forward
|
||||
/// (see `UsageStore.shouldAdvanceAdaptiveTimer(scheduledAt:candidate:)`). Recording it separately
|
||||
/// from `decision` lets a trace answer "did an advance happen, and to when" without relying on
|
||||
/// fragile inference from decision-timestamp gaps.
|
||||
public enum AdaptiveRefreshTraceEventKind: String, Sendable, Codable {
|
||||
case decision
|
||||
case menuOpen
|
||||
case refreshCompleted
|
||||
case timerAdvanced
|
||||
/// Every live advance comparison, including the cases correctly rejected because the current
|
||||
/// timer was already earlier. This is distinct from counterfactual replay advances.
|
||||
case timerAdvanceEvaluated
|
||||
}
|
||||
|
||||
/// One line of a JSONL adaptive-refresh trace. Field presence depends on `kind`: `decision`
|
||||
/// records populate `menuAgeSeconds`, `lowPowerModeEnabled`, `thermalState`, `reason`, and
|
||||
/// `delaySeconds`, plus optional activity observations supplied by the input trace
|
||||
/// (`codexActivitySeconds`/`claudeActivitySeconds`, the seconds-since-newest-transcript fields,
|
||||
/// and the per-file intensity fields alongside them); timer advance records populate
|
||||
/// `previousScheduledAt`, `candidateScheduledAt`, `reason`, and `delaySeconds`; `menuOpen` and
|
||||
/// `refreshCompleted` carry only `kind` and `timestamp`.
|
||||
public struct AdaptiveRefreshTraceRecord: Sendable, Codable, Equatable {
|
||||
public let kind: AdaptiveRefreshTraceEventKind
|
||||
public let timestamp: Date
|
||||
public let menuAgeSeconds: TimeInterval?
|
||||
public let lowPowerModeEnabled: Bool?
|
||||
public let thermalState: ReplayThermalState?
|
||||
public let reason: String?
|
||||
public let delaySeconds: TimeInterval?
|
||||
/// Timer advance records only: the adaptive timer's scheduled refresh time before the
|
||||
/// comparison, or `nil` when no refresh had been scheduled yet (matches
|
||||
/// `UsageStore.shouldAdvanceAdaptiveTimer`'s "always advance when nothing is scheduled" rule).
|
||||
public let previousScheduledAt: Date?
|
||||
/// Timer advance records only: the candidate refresh time, i.e. the menu-open timestamp plus
|
||||
/// the freshly computed decision's delay.
|
||||
public let candidateScheduledAt: Date?
|
||||
/// `timerAdvanceEvaluated` only: whether the live schedule comparison accepted the candidate.
|
||||
public let timerAdvanceAccepted: Bool?
|
||||
/// `timerAdvanceEvaluated` only: `previousScheduledAt - candidateScheduledAt`, captured before
|
||||
/// whole-second ISO-8601 serialization. Positive means the candidate was earlier. Optional for
|
||||
/// compatibility with traces recorded before exact comparison deltas were added.
|
||||
public let scheduleLeadSeconds: TimeInterval?
|
||||
/// `timerAdvanceEvaluated` only: whether another refresh was in flight at comparison time.
|
||||
public let refreshInFlight: Bool?
|
||||
/// `decision` only: seconds since the newest observed Codex session transcript modification,
|
||||
/// or `nil` when unavailable. Optional so old trace lines without this field keep decoding.
|
||||
public let codexActivitySeconds: TimeInterval?
|
||||
/// `decision` only: the Claude Code counterpart of `codexActivitySeconds`.
|
||||
public let claudeActivitySeconds: TimeInterval?
|
||||
/// `decision` only: how long the newest Codex transcript has been
|
||||
/// growing (its mtime minus its creationDate), or `nil` when unavailable. Not a separate
|
||||
/// session-age field — age is `codexActivitySeconds` + `codexSessionDurationSeconds`.
|
||||
public let codexSessionDurationSeconds: TimeInterval?
|
||||
/// `decision` only: the Claude Code counterpart of `codexSessionDurationSeconds`.
|
||||
public let claudeSessionDurationSeconds: TimeInterval?
|
||||
/// `decision` only: size in bytes of the newest Codex transcript, as a stateless raw value.
|
||||
public let codexTranscriptBytes: Int64?
|
||||
/// `decision` only: the Claude Code counterpart of `codexTranscriptBytes`.
|
||||
public let claudeTranscriptBytes: Int64?
|
||||
/// `decision` only: count of Codex `.jsonl` transcripts modified in the observation window.
|
||||
public let codexActiveTranscriptCount: Int?
|
||||
/// `decision` only: the Claude Code counterpart of `codexActiveTranscriptCount`.
|
||||
public let claudeActiveTranscriptCount: Int?
|
||||
|
||||
public init(
|
||||
kind: AdaptiveRefreshTraceEventKind,
|
||||
timestamp: Date,
|
||||
menuAgeSeconds: TimeInterval? = nil,
|
||||
lowPowerModeEnabled: Bool? = nil,
|
||||
thermalState: ReplayThermalState? = nil,
|
||||
reason: String? = nil,
|
||||
delaySeconds: TimeInterval? = nil,
|
||||
previousScheduledAt: Date? = nil,
|
||||
candidateScheduledAt: Date? = nil,
|
||||
timerAdvanceAccepted: Bool? = nil,
|
||||
scheduleLeadSeconds: TimeInterval? = nil,
|
||||
refreshInFlight: Bool? = nil,
|
||||
codexActivitySeconds: TimeInterval? = nil,
|
||||
claudeActivitySeconds: TimeInterval? = nil,
|
||||
codexSessionDurationSeconds: TimeInterval? = nil,
|
||||
claudeSessionDurationSeconds: TimeInterval? = nil,
|
||||
codexTranscriptBytes: Int64? = nil,
|
||||
claudeTranscriptBytes: Int64? = nil,
|
||||
codexActiveTranscriptCount: Int? = nil,
|
||||
claudeActiveTranscriptCount: Int? = nil)
|
||||
{
|
||||
self.kind = kind
|
||||
self.timestamp = timestamp
|
||||
self.menuAgeSeconds = menuAgeSeconds
|
||||
self.lowPowerModeEnabled = lowPowerModeEnabled
|
||||
self.thermalState = thermalState
|
||||
self.reason = reason
|
||||
self.delaySeconds = delaySeconds
|
||||
self.previousScheduledAt = previousScheduledAt
|
||||
self.candidateScheduledAt = candidateScheduledAt
|
||||
self.timerAdvanceAccepted = timerAdvanceAccepted
|
||||
self.scheduleLeadSeconds = scheduleLeadSeconds
|
||||
self.refreshInFlight = refreshInFlight
|
||||
self.codexActivitySeconds = codexActivitySeconds
|
||||
self.claudeActivitySeconds = claudeActivitySeconds
|
||||
self.codexSessionDurationSeconds = codexSessionDurationSeconds
|
||||
self.claudeSessionDurationSeconds = claudeSessionDurationSeconds
|
||||
self.codexTranscriptBytes = codexTranscriptBytes
|
||||
self.claudeTranscriptBytes = claudeTranscriptBytes
|
||||
self.codexActiveTranscriptCount = codexActiveTranscriptCount
|
||||
self.claudeActiveTranscriptCount = claudeActiveTranscriptCount
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
public static func decision(
|
||||
timestamp: Date,
|
||||
menuAgeSeconds: TimeInterval?,
|
||||
lowPowerModeEnabled: Bool,
|
||||
thermalState: ReplayThermalState,
|
||||
reason: String,
|
||||
delaySeconds: TimeInterval,
|
||||
codexActivitySeconds: TimeInterval? = nil,
|
||||
claudeActivitySeconds: TimeInterval? = nil,
|
||||
codexSessionDurationSeconds: TimeInterval? = nil,
|
||||
claudeSessionDurationSeconds: TimeInterval? = nil,
|
||||
codexTranscriptBytes: Int64? = nil,
|
||||
claudeTranscriptBytes: Int64? = nil,
|
||||
codexActiveTranscriptCount: Int? = nil,
|
||||
claudeActiveTranscriptCount: Int? = nil) -> Self
|
||||
{
|
||||
Self(
|
||||
kind: .decision,
|
||||
timestamp: timestamp,
|
||||
menuAgeSeconds: menuAgeSeconds,
|
||||
lowPowerModeEnabled: lowPowerModeEnabled,
|
||||
thermalState: thermalState,
|
||||
reason: reason,
|
||||
delaySeconds: delaySeconds,
|
||||
codexActivitySeconds: codexActivitySeconds,
|
||||
claudeActivitySeconds: claudeActivitySeconds,
|
||||
codexSessionDurationSeconds: codexSessionDurationSeconds,
|
||||
claudeSessionDurationSeconds: claudeSessionDurationSeconds,
|
||||
codexTranscriptBytes: codexTranscriptBytes,
|
||||
claudeTranscriptBytes: claudeTranscriptBytes,
|
||||
codexActiveTranscriptCount: codexActiveTranscriptCount,
|
||||
claudeActiveTranscriptCount: claudeActiveTranscriptCount)
|
||||
}
|
||||
|
||||
public static func menuOpen(timestamp: Date) -> Self {
|
||||
Self(kind: .menuOpen, timestamp: timestamp)
|
||||
}
|
||||
|
||||
public static func refreshCompleted(timestamp: Date) -> Self {
|
||||
Self(kind: .refreshCompleted, timestamp: timestamp)
|
||||
}
|
||||
|
||||
/// - Parameters:
|
||||
/// - timestamp: When the menu open that triggered the advance occurred.
|
||||
/// - previousScheduledAt: The timer's scheduled refresh time immediately before the advance.
|
||||
/// - candidateScheduledAt: The refresh time the timer advanced to (`timestamp + delaySeconds`).
|
||||
/// - reason: The freshly computed decision's reason (e.g. `"recentInteraction"`).
|
||||
/// - delaySeconds: The freshly computed decision's delay.
|
||||
public static func timerAdvanced(
|
||||
timestamp: Date,
|
||||
previousScheduledAt: Date?,
|
||||
candidateScheduledAt: Date,
|
||||
reason: String,
|
||||
delaySeconds: TimeInterval) -> Self
|
||||
{
|
||||
Self(
|
||||
kind: .timerAdvanced,
|
||||
timestamp: timestamp,
|
||||
reason: reason,
|
||||
delaySeconds: delaySeconds,
|
||||
previousScheduledAt: previousScheduledAt,
|
||||
candidateScheduledAt: candidateScheduledAt)
|
||||
}
|
||||
|
||||
// swiftlint:disable:next function_parameter_count
|
||||
public static func timerAdvanceEvaluated(
|
||||
timestamp: Date,
|
||||
previousScheduledAt: Date?,
|
||||
candidateScheduledAt: Date,
|
||||
reason: String,
|
||||
delaySeconds: TimeInterval,
|
||||
accepted: Bool,
|
||||
refreshInFlight: Bool) -> Self
|
||||
{
|
||||
Self(
|
||||
kind: .timerAdvanceEvaluated,
|
||||
timestamp: timestamp,
|
||||
reason: reason,
|
||||
delaySeconds: delaySeconds,
|
||||
previousScheduledAt: previousScheduledAt,
|
||||
candidateScheduledAt: candidateScheduledAt,
|
||||
timerAdvanceAccepted: accepted,
|
||||
scheduleLeadSeconds: previousScheduledAt.map { $0.timeIntervalSince(candidateScheduledAt) },
|
||||
refreshInFlight: refreshInFlight)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user