codex-flow 2.1.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. codex_flow/__init__.py +28 -0
  2. codex_flow/__main__.py +9 -0
  3. codex_flow/cli.py +242 -0
  4. codex_flow/data/LICENSE +21 -0
  5. codex_flow/data/README.en.md +303 -0
  6. codex_flow/data/README.md +305 -0
  7. codex_flow/data/VERSION +1 -0
  8. codex_flow/data/apps/chatgpt-mcp/README.md +86 -0
  9. codex_flow/data/apps/chatgpt-mcp/__init__.py +1 -0
  10. codex_flow/data/apps/chatgpt-mcp/adapter.py +458 -0
  11. codex_flow/data/apps/chatgpt-mcp/server.py +358 -0
  12. codex_flow/data/apps/chatgpt-mcp/widget.html +927 -0
  13. codex_flow/data/apps/macos-overlay/README.en.md +121 -0
  14. codex_flow/data/apps/macos-overlay/README.md +123 -0
  15. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayRuntimeState.swift +126 -0
  16. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayScreenGeometry.swift +82 -0
  17. codex_flow/data/apps/macos-overlay/Sources/Controllers/OverlayWindowController.swift +1052 -0
  18. codex_flow/data/apps/macos-overlay/Sources/Localization.swift +197 -0
  19. codex_flow/data/apps/macos-overlay/Sources/Models/TelemetryData.swift +1557 -0
  20. codex_flow/data/apps/macos-overlay/Sources/Services/AccountSnapshotService.swift +1101 -0
  21. codex_flow/data/apps/macos-overlay/Sources/Services/FlowPilotInstanceLock.swift +153 -0
  22. codex_flow/data/apps/macos-overlay/Sources/Services/IPCServer.swift +298 -0
  23. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryQueryEngine.swift +800 -0
  24. codex_flow/data/apps/macos-overlay/Sources/Services/TelemetryWatcher.swift +135 -0
  25. codex_flow/data/apps/macos-overlay/Sources/Services/UpdateService.swift +610 -0
  26. codex_flow/data/apps/macos-overlay/Sources/Views/AccountView.swift +610 -0
  27. codex_flow/data/apps/macos-overlay/Sources/Views/AnalyticsView.swift +566 -0
  28. codex_flow/data/apps/macos-overlay/Sources/Views/AutostartView.swift +293 -0
  29. codex_flow/data/apps/macos-overlay/Sources/Views/BubbleView.swift +317 -0
  30. codex_flow/data/apps/macos-overlay/Sources/Views/HistoryView.swift +1124 -0
  31. codex_flow/data/apps/macos-overlay/Sources/Views/HoverRevealText.swift +165 -0
  32. codex_flow/data/apps/macos-overlay/Sources/Views/InspectorSkillsToolsView.swift +121 -0
  33. codex_flow/data/apps/macos-overlay/Sources/Views/LogoView.swift +182 -0
  34. codex_flow/data/apps/macos-overlay/Sources/Views/SleekSwitch.swift +117 -0
  35. codex_flow/data/apps/macos-overlay/Sources/Views/StrategyModeView.swift +561 -0
  36. codex_flow/data/apps/macos-overlay/Sources/Views/SummaryView.swift +1273 -0
  37. codex_flow/data/apps/macos-overlay/Sources/Views/UpdateView.swift +352 -0
  38. codex_flow/data/apps/macos-overlay/Sources/main.swift +340 -0
  39. codex_flow/data/apps/macos-overlay/Tests/OverlayScreenGeometryTests.swift +163 -0
  40. codex_flow/data/apps/macos-overlay/Tests/TelemetryPhase1ContractTests.swift +357 -0
  41. codex_flow/data/apps/macos-overlay/Tests/TelemetryQueryEngineConcurrencyTests.swift +221 -0
  42. codex_flow/data/apps/macos-overlay/Tests/TelemetryQuotaSelectionTests.swift +158 -0
  43. codex_flow/data/apps/macos-overlay/Tests/TelemetryWorkerTokenTests.swift +122 -0
  44. codex_flow/data/apps/macos-overlay/build.sh +75 -0
  45. codex_flow/data/benchmark/corpus.json +103 -0
  46. codex_flow/data/benchmark/manifest.example.json +41 -0
  47. codex_flow/data/benchmark/manifest.schema.json +137 -0
  48. codex_flow/data/benchmark/prices/gpt-5.6-2026-08-30.json +5 -0
  49. codex_flow/data/benchmark/profiles.json +90 -0
  50. codex_flow/data/benchmark/schema.json +77 -0
  51. codex_flow/data/benchmark/tasks.json +50 -0
  52. codex_flow/data/completions/codex-flow.bash +34 -0
  53. codex_flow/data/completions/codex-flow.zsh +52 -0
  54. codex_flow/data/glama.json +6 -0
  55. codex_flow/data/install-release.ps1 +126 -0
  56. codex_flow/data/install-release.sh +155 -0
  57. codex_flow/data/install.ps1 +349 -0
  58. codex_flow/data/install.sh +362 -0
  59. codex_flow/data/policy/benchmark.toml +49 -0
  60. codex_flow/data/policy/defaults.toml +70 -0
  61. codex_flow/data/scripts/analyze-benchmark.py +510 -0
  62. codex_flow/data/scripts/benchmark-local.py +171 -0
  63. codex_flow/data/scripts/check-recommendation.py +277 -0
  64. codex_flow/data/scripts/doctor.py +449 -0
  65. codex_flow/data/scripts/generate-release-manifest.py +74 -0
  66. codex_flow/data/scripts/localization.py +192 -0
  67. codex_flow/data/scripts/manage-hooks.py +448 -0
  68. codex_flow/data/scripts/manage-instructions.py +389 -0
  69. codex_flow/data/scripts/manage-shell.py +151 -0
  70. codex_flow/data/scripts/materialize-corpus.py +193 -0
  71. codex_flow/data/scripts/menu.py +646 -0
  72. codex_flow/data/scripts/migrations/0001_update_settings.py +80 -0
  73. codex_flow/data/scripts/package-release.py +132 -0
  74. codex_flow/data/scripts/render-benchmark-report.py +292 -0
  75. codex_flow/data/scripts/run-benchmark.py +829 -0
  76. codex_flow/data/scripts/strategies/__init__.py +28 -0
  77. codex_flow/data/scripts/strategies/balanced.py +115 -0
  78. codex_flow/data/scripts/strategies/base.py +363 -0
  79. codex_flow/data/scripts/strategies/efficient.py +158 -0
  80. codex_flow/data/scripts/strategies/lifecycle_runtime.py +590 -0
  81. codex_flow/data/scripts/strategies/quality.py +209 -0
  82. codex_flow/data/scripts/strategies/speed.py +108 -0
  83. codex_flow/data/scripts/strategies/task_budget_runtime.py +644 -0
  84. codex_flow/data/scripts/strategies/task_phase_runtime.py +341 -0
  85. codex_flow/data/scripts/strategies/work_unit_runtime.py +421 -0
  86. codex_flow/data/scripts/strategy_runtime.py +1091 -0
  87. codex_flow/data/scripts/telemetry.py +400 -0
  88. codex_flow/data/scripts/telemetry_core/__init__.py +192 -0
  89. codex_flow/data/scripts/telemetry_core/app_server.py +1192 -0
  90. codex_flow/data/scripts/telemetry_core/collector.py +1247 -0
  91. codex_flow/data/scripts/telemetry_core/common.py +421 -0
  92. codex_flow/data/scripts/telemetry_core/latency.py +593 -0
  93. codex_flow/data/scripts/telemetry_core/query.py +427 -0
  94. codex_flow/data/scripts/telemetry_core/quota_ledger.py +598 -0
  95. codex_flow/data/scripts/telemetry_core/render.py +460 -0
  96. codex_flow/data/scripts/telemetry_core/repair.py +223 -0
  97. codex_flow/data/scripts/ui.py +266 -0
  98. codex_flow/data/scripts/update-homebrew-formula.py +146 -0
  99. codex_flow/data/scripts/update_runtime_config.py +134 -0
  100. codex_flow/data/scripts/updater.py +1718 -0
  101. codex_flow/data/smithery.yaml +18 -0
  102. codex_flow/data/templates/agents/worker-explorer.toml +24 -0
  103. codex_flow/data/templates/agents/worker-implementer.toml +49 -0
  104. codex_flow/data/templates/agents/worker-reviewer.toml +25 -0
  105. codex_flow/data/templates/flow-pilot-instructions.md +35 -0
  106. codex_flow/data/templates/skills/flow-pilot/SKILL.md +577 -0
  107. codex_flow/mcp.py +35 -0
  108. codex_flow-2.1.13.dist-info/METADATA +342 -0
  109. codex_flow-2.1.13.dist-info/RECORD +113 -0
  110. codex_flow-2.1.13.dist-info/WHEEL +5 -0
  111. codex_flow-2.1.13.dist-info/entry_points.txt +3 -0
  112. codex_flow-2.1.13.dist-info/licenses/LICENSE +21 -0
  113. codex_flow-2.1.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ import Cocoa
2
+
3
+ @main
4
+ struct OverlayScreenGeometryTests {
5
+ static func main() {
6
+ testScreenGeometry()
7
+ testPresentationAnchorRouting()
8
+ testVisibleHitRegions()
9
+ testSyntheticHoverGate()
10
+ testSingleOwnerGeometrySequencing()
11
+ testDragThenPendingPresentation()
12
+ testRapidPresentationCoalescing()
13
+ print("Overlay runtime regression tests passed")
14
+ }
15
+
16
+ private static func testScreenGeometry() {
17
+ let primary = NSRect(x: 0, y: 0, width: 1440, height: 900)
18
+ let rightUpper = NSRect(x: 1440, y: 220, width: 1728, height: 1117)
19
+ let leftLower = NSRect(x: -1280, y: -300, width: 1280, height: 800)
20
+ let frames = [primary, rightUpper, leftLower]
21
+
22
+ let secondaryPanel = NSRect(x: 2680, y: 780, width: 384, height: 490)
23
+ precondition(
24
+ OverlayScreenGeometry.bestVisibleFrame(for: secondaryPanel, among: frames) == rightUpper,
25
+ "secondary panel should resolve to the right/upper display"
26
+ )
27
+
28
+ let partial = NSRect(x: -1210, y: -250, width: 76, height: 76)
29
+ precondition(
30
+ OverlayScreenGeometry.bestVisibleFrame(for: partial, among: frames) == leftLower,
31
+ "partially visible bubble should stay on the intersecting display"
32
+ )
33
+
34
+ precondition(
35
+ OverlayScreenGeometry.visibleFrame(containing: NSPoint(x: 2200, y: 700), among: frames) == rightUpper,
36
+ "pointer on secondary display should route drag geometry there"
37
+ )
38
+
39
+ let clamped = OverlayScreenGeometry.clamp(
40
+ NSPoint(x: 9999, y: -9999),
41
+ windowSize: NSSize(width: 76, height: 76),
42
+ to: rightUpper,
43
+ margin: 8
44
+ )
45
+ precondition(clamped.x == rightUpper.maxX - 76 - 8)
46
+ precondition(clamped.y == rightUpper.minY + 8)
47
+
48
+ let canonicalCollapsedX = rightUpper.maxX - 76
49
+ precondition(canonicalCollapsedX == 3092)
50
+ precondition(canonicalCollapsedX + 76 == rightUpper.maxX)
51
+ }
52
+
53
+ private static func testPresentationAnchorRouting() {
54
+ let left = NSRect(x: 0, y: 0, width: 1440, height: 900)
55
+ let right = NSRect(x: 1440, y: 0, width: 1728, height: 1117)
56
+ let frames = [left, right]
57
+
58
+ // Regression for the real failure mode: the 384pt panel straddles the
59
+ // display boundary, so its center is still on the left display while
60
+ // its top-trailing anchor (the compact bubble's anchor) is on the right.
61
+ let straddlingPanel = NSRect(x: 1200, y: 400, width: 384, height: 490)
62
+ precondition(
63
+ OverlayScreenGeometry.bestVisibleFrame(for: straddlingPanel, among: frames) == left,
64
+ "center-based routing demonstrates the old wrong-display choice"
65
+ )
66
+ precondition(
67
+ OverlayScreenGeometry.presentationVisibleFrame(for: straddlingPanel, among: frames) == right,
68
+ "presentation routing must follow the top-trailing anchor"
69
+ )
70
+
71
+ let anchor = OverlayScreenGeometry.topTrailingAnchor(for: straddlingPanel)
72
+ precondition(anchor.x == 1583)
73
+ precondition(anchor.y == 889)
74
+ }
75
+
76
+ private static func testVisibleHitRegions() {
77
+ let host = NSRect(x: 0, y: 0, width: 76, height: 76)
78
+
79
+ precondition(
80
+ OverlayCompactHitRegion.contains(NSPoint(x: 38, y: 38), in: host, expanded: false, docked: false),
81
+ "center of circular bubble must be interactive"
82
+ )
83
+ precondition(
84
+ !OverlayCompactHitRegion.contains(NSPoint(x: 2, y: 2), in: host, expanded: false, docked: false),
85
+ "transparent circular-host corner must not be interactive"
86
+ )
87
+
88
+ precondition(
89
+ OverlayCompactHitRegion.contains(NSPoint(x: 60, y: 38), in: host, expanded: false, docked: true),
90
+ "visible dock pill must be interactive"
91
+ )
92
+ precondition(
93
+ !OverlayCompactHitRegion.contains(NSPoint(x: 12, y: 38), in: host, expanded: false, docked: true),
94
+ "transparent area left of the dock pill must not untuck it"
95
+ )
96
+
97
+ precondition(
98
+ OverlayCompactHitRegion.contains(NSPoint(x: 2, y: 2), in: host, expanded: true, docked: false),
99
+ "expanded panel uses its full host bounds"
100
+ )
101
+ }
102
+
103
+ private static func testSyntheticHoverGate() {
104
+ var gate = OverlayHoverGate(rearmDistance: 6)
105
+ gate.suppress(at: NSPoint(x: 100, y: 100))
106
+
107
+ precondition(!gate.allowsHover(at: NSPoint(x: 100, y: 100)), "synthetic enter at the same pointer location must be ignored")
108
+ precondition(!gate.allowsHover(at: NSPoint(x: 103, y: 102)), "micro pointer jitter must not rearm hover")
109
+ precondition(gate.allowsHover(at: NSPoint(x: 107, y: 100)), "real pointer movement must rearm hover")
110
+ precondition(gate.allowsHover(at: NSPoint(x: 107, y: 100)), "hover remains armed after genuine movement")
111
+ }
112
+
113
+ private static func testSingleOwnerGeometrySequencing() {
114
+ var runtime = OverlayRuntimeState()
115
+
116
+ precondition(runtime.requestPresentationGeometry(), "collapse resize should acquire geometry ownership")
117
+ precondition(runtime.activeGeometry == .presentation)
118
+ precondition(!runtime.requestPresentationGeometry(), "a second presentation must be queued, never run concurrently")
119
+ precondition(runtime.pendingPresentationUpdate)
120
+
121
+ precondition(runtime.completeGeometry(), "queued presentation should acquire ownership after the first finishes")
122
+ precondition(runtime.activeGeometry == .presentation)
123
+ precondition(!runtime.pendingPresentationUpdate)
124
+ precondition(!runtime.beginSnapGeometry(), "snap cannot steal geometry from the active presentation")
125
+
126
+ precondition(!runtime.completeGeometry(), "no further presentation is pending")
127
+ precondition(runtime.activeGeometry == nil)
128
+ }
129
+
130
+ private static func testDragThenPendingPresentation() {
131
+ var runtime = OverlayRuntimeState()
132
+
133
+ precondition(runtime.beginPointerInteraction(), "drag should start while geometry is idle")
134
+ precondition(runtime.pointerInteractionActive)
135
+ precondition(!runtime.requestPresentationGeometry(), "resize requested during drag must be deferred")
136
+ precondition(runtime.pendingPresentationUpdate)
137
+
138
+ runtime.endPointerInteraction()
139
+ precondition(!runtime.pointerInteractionActive, "mouse-up must release interaction independently of any animation completion")
140
+ precondition(runtime.beginSnapGeometry(), "snap should acquire geometry immediately after pointer release")
141
+ precondition(runtime.activeGeometry == .snap)
142
+
143
+ precondition(runtime.completeGeometry(), "deferred presentation should run after snap finishes")
144
+ precondition(runtime.activeGeometry == .presentation)
145
+ precondition(!runtime.pointerInteractionActive, "superseding snap must never resurrect a stuck drag flag")
146
+ precondition(!runtime.completeGeometry())
147
+ precondition(runtime.activeGeometry == nil)
148
+ }
149
+
150
+ private static func testRapidPresentationCoalescing() {
151
+ var runtime = OverlayRuntimeState()
152
+
153
+ precondition(runtime.requestPresentationGeometry()) // collapse
154
+ precondition(!runtime.requestPresentationGeometry()) // expand
155
+ precondition(!runtime.requestPresentationGeometry()) // collapse again
156
+ precondition(runtime.pendingPresentationUpdate, "rapid updates coalesce to one replay")
157
+
158
+ precondition(runtime.completeGeometry(), "one replay should remain after the active transition")
159
+ precondition(runtime.activeGeometry == .presentation)
160
+ precondition(!runtime.pendingPresentationUpdate)
161
+ precondition(!runtime.completeGeometry(), "coalescing must not create an animation train")
162
+ }
163
+ }
@@ -0,0 +1,357 @@
1
+ import Foundation
2
+
3
+ func L(_ english: String, _ chinese: String) -> String { english }
4
+
5
+ @main
6
+ struct TelemetryPhase1ContractTests {
7
+ static func main() {
8
+ testAllocatedQuotaDeltaSeparation()
9
+ testChatSessionAttributionStatus()
10
+ testDailyUsageReadsSummarySnapshot()
11
+ testCrossMidnightPendingProducesNilQuotaDelta()
12
+ testMultiBucketDailyUsageSeparation()
13
+ testMissingAccountIdentityYieldsNil()
14
+ testExplicitMissingIdentityDoesNotUseCachedActiveAccount()
15
+ testMissingBucketWithMultipleBucketsYieldsNil()
16
+ print("Telemetry Phase 1 contract tests passed successfully!")
17
+ }
18
+
19
+ private static func testAllocatedQuotaDeltaSeparation() {
20
+ let runWithoutAlloc = TaskRun(
21
+ sessionId: "sess-1",
22
+ turnId: "turn-1",
23
+ status: "completed",
24
+ quotaChangeDuringRun: [
25
+ QuotaWindow(slot: "primary", usedPercent: 30.0, windowDurationMins: 10_080, deltaPercentagePoints: 24.0)
26
+ ]
27
+ )
28
+
29
+ // Must strictly separate observed vs canonical
30
+ precondition(runWithoutAlloc.observedAccountDelta == 24.0, "observedAccountDelta should be raw weekly delta")
31
+ precondition(runWithoutAlloc.allocatedQuotaDelta == nil, "allocatedQuotaDelta should be nil")
32
+ precondition(runWithoutAlloc.canonicalQuotaDelta == nil, "canonicalQuotaDelta MUST be nil when allocation missing, NO fallback to observedAccountDelta!")
33
+
34
+ let runWithAlloc = TaskRun(
35
+ sessionId: "sess-1",
36
+ turnId: "turn-1",
37
+ status: "completed",
38
+ quotaChangeDuringRun: [
39
+ QuotaWindow(slot: "primary", usedPercent: 30.0, windowDurationMins: 10_080, deltaPercentagePoints: 24.0)
40
+ ],
41
+ allocatedQuotaDelta: 17.5
42
+ )
43
+
44
+ precondition(runWithAlloc.observedAccountDelta == 24.0)
45
+ precondition(runWithAlloc.allocatedQuotaDelta == 17.5)
46
+ precondition(runWithAlloc.canonicalQuotaDelta == 17.5)
47
+ }
48
+
49
+ private static func testChatSessionAttributionStatus() {
50
+ let runA = TaskRun(
51
+ sessionId: "sess-partial",
52
+ turnId: "turn-a",
53
+ status: "completed",
54
+ quotaChangeDuringRun: [
55
+ QuotaWindow(slot: "primary", usedPercent: 5.0, windowDurationMins: 10_080, deltaPercentagePoints: 2.0)
56
+ ],
57
+ allocatedQuotaDelta: 2.0
58
+ )
59
+ let runB = TaskRun(
60
+ sessionId: "sess-partial",
61
+ turnId: "turn-b",
62
+ status: "completed",
63
+ quotaChangeDuringRun: [
64
+ QuotaWindow(slot: "primary", usedPercent: 30.0, windowDurationMins: 10_080, deltaPercentagePoints: 24.0)
65
+ ],
66
+ allocatedQuotaDelta: nil // Missing allocation
67
+ )
68
+
69
+ let partialChat = ChatSession(
70
+ sessionId: "sess-partial",
71
+ projectName: "test-proj",
72
+ title: "Partial chat",
73
+ lastActiveAtMs: 1000,
74
+ runs: [runA, runB]
75
+ )
76
+
77
+ precondition(partialChat.isPartiallyAttributed == true, "ChatSession should be partially attributed")
78
+ precondition(partialChat.isFullyAttributed == false, "ChatSession should NOT be fully attributed")
79
+ precondition(partialChat.totalQuotaDelta == 2.0, "totalQuotaDelta should sum only allocated runs")
80
+ precondition(partialChat.observedAccountDelta == 26.0, "observedAccountDelta should sum raw deltas")
81
+
82
+ let fullyAllocatedChat = ChatSession(
83
+ sessionId: "sess-full",
84
+ projectName: "test-proj",
85
+ title: "Full chat",
86
+ lastActiveAtMs: 1000,
87
+ runs: [runA]
88
+ )
89
+ precondition(fullyAllocatedChat.isPartiallyAttributed == false)
90
+ precondition(fullyAllocatedChat.isFullyAttributed == true)
91
+ precondition(fullyAllocatedChat.totalQuotaDelta == 2.0)
92
+ }
93
+
94
+ private static func testDailyUsageReadsSummarySnapshot() {
95
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
96
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
97
+ defer { try? FileManager.default.removeItem(at: tempDir) }
98
+
99
+ let runsDir = tempDir.appendingPathComponent("runs")
100
+ try! FileManager.default.createDirectory(at: runsDir, withIntermediateDirectories: true)
101
+
102
+ let todayFormatter = DateFormatter()
103
+ todayFormatter.dateFormat = "yyyy-MM-dd"
104
+ let todayStr = todayFormatter.string(from: Date())
105
+
106
+ let summaryURL = tempDir.appendingPathComponent("quota_summary.json")
107
+ let summaryJSON = """
108
+ {
109
+ "revision": 42,
110
+ "active_account_id": "acc-1",
111
+ "exported_at_ms": 1788855500000,
112
+ "timezone": "Asia/Shanghai",
113
+ "cycle_summaries": [],
114
+ "daily_usage": [
115
+ {
116
+ "account_id": "acc-1",
117
+ "bucket_id": "secondary",
118
+ "date": "\(todayStr)",
119
+ "observed_delta_pp": 10.0,
120
+ "coverage_status": "complete",
121
+ "cross_midnight_pending_pp": 0.0,
122
+ "unattributed_pp": 0.0
123
+ },
124
+ {
125
+ "account_id": "acc-2",
126
+ "bucket_id": "secondary",
127
+ "date": "\(todayStr)",
128
+ "observed_delta_pp": 20.0,
129
+ "coverage_status": "complete",
130
+ "cross_midnight_pending_pp": 0.0,
131
+ "unattributed_pp": 0.0
132
+ }
133
+ ],
134
+ "session_allocations": {}
135
+ }
136
+ """
137
+ try! summaryJSON.write(to: summaryURL, atomically: true, encoding: .utf8)
138
+
139
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
140
+ let summary = engine.loadQuotaSummary()
141
+ precondition(summary != nil)
142
+ precondition(summary?.revision == 42)
143
+ precondition(summary?.activeAccountId == "acc-1")
144
+ precondition(summary?.dailyUsage?.count == 2)
145
+
146
+ // Both dimensions must be explicit, even when the summary has an active account.
147
+ let dailyUsagesAcc1 = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: "secondary")
148
+ precondition(dailyUsagesAcc1.first?.quotaDelta == 10.0, "Should use explicit acc-1 secondary delta")
149
+
150
+ // Explicit accountId and bucketId matching active account
151
+ let dailyUsagesExplicitAcc1 = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: "secondary")
152
+ precondition(dailyUsagesExplicitAcc1.first?.quotaDelta == 10.0, "Should match explicit acc-1 delta")
153
+
154
+ // Inconsistent accountId (caller asks for acc-2 while active is acc-1) MUST return nil (unknown)
155
+ let dailyUsagesInconsistent = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-2", bucketId: "secondary")
156
+ precondition(dailyUsagesInconsistent.first?.quotaDelta == nil, "Inconsistent accountId must return nil to prevent showing wrong/stale account data")
157
+ }
158
+
159
+ private static func testCrossMidnightPendingProducesNilQuotaDelta() {
160
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
161
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
162
+ defer { try? FileManager.default.removeItem(at: tempDir) }
163
+
164
+ let runsDir = tempDir.appendingPathComponent("runs")
165
+ try! FileManager.default.createDirectory(at: runsDir, withIntermediateDirectories: true)
166
+
167
+ let todayFormatter = DateFormatter()
168
+ todayFormatter.dateFormat = "yyyy-MM-dd"
169
+ let todayStr = todayFormatter.string(from: Date())
170
+
171
+ let summaryURL = tempDir.appendingPathComponent("quota_summary.json")
172
+ let summaryJSON = """
173
+ {
174
+ "revision": 43,
175
+ "active_account_id": "acc-test",
176
+ "exported_at_ms": 1788855500000,
177
+ "daily_usage": [
178
+ {
179
+ "account_id": "acc-test",
180
+ "bucket_id": "secondary",
181
+ "date": "\(todayStr)",
182
+ "observed_delta_pp": 0.0,
183
+ "cross_midnight_pending_pp": 10.0,
184
+ "coverage_status": "has_cross_midnight_pending"
185
+ }
186
+ ]
187
+ }
188
+ """
189
+ try! summaryJSON.write(to: summaryURL, atomically: true, encoding: .utf8)
190
+
191
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
192
+ let dailyUsages = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-test", bucketId: "secondary")
193
+ let todayUsage = dailyUsages.first
194
+ precondition(todayUsage?.quotaDelta == nil, "quotaDelta MUST be nil when has_cross_midnight_pending with 0 intraday delta")
195
+ precondition(todayUsage?.crossMidnightPendingPp == 10.0, "crossMidnightPendingPp should be 10.0")
196
+ precondition(todayUsage?.coverageStatus == "has_cross_midnight_pending")
197
+ }
198
+
199
+ private static func testMultiBucketDailyUsageSeparation() {
200
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
201
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
202
+ defer { try? FileManager.default.removeItem(at: tempDir) }
203
+
204
+ let runsDir = tempDir.appendingPathComponent("runs")
205
+ try! FileManager.default.createDirectory(at: runsDir, withIntermediateDirectories: true)
206
+
207
+ let todayFormatter = DateFormatter()
208
+ todayFormatter.dateFormat = "yyyy-MM-dd"
209
+ let todayStr = todayFormatter.string(from: Date())
210
+
211
+ let summaryURL = tempDir.appendingPathComponent("quota_summary.json")
212
+ let summaryJSON = """
213
+ {
214
+ "revision": 44,
215
+ "active_account_id": "acc-1",
216
+ "exported_at_ms": 1788855500000,
217
+ "daily_usage": [
218
+ {
219
+ "account_id": "acc-1",
220
+ "bucket_id": "secondary",
221
+ "date": "\(todayStr)",
222
+ "observed_delta_pp": 12.0,
223
+ "coverage_status": "complete",
224
+ "cross_midnight_pending_pp": 0.0
225
+ },
226
+ {
227
+ "account_id": "acc-1",
228
+ "bucket_id": "primary",
229
+ "date": "\(todayStr)",
230
+ "observed_delta_pp": 18.0,
231
+ "coverage_status": "complete",
232
+ "cross_midnight_pending_pp": 0.0
233
+ }
234
+ ]
235
+ }
236
+ """
237
+ try! summaryJSON.write(to: summaryURL, atomically: true, encoding: .utf8)
238
+
239
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
240
+ // Request secondary bucket explicitly
241
+ let dailySec = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: "secondary")
242
+ precondition(dailySec.first?.quotaDelta == 12.0, "Should return secondary bucket delta 12.0")
243
+
244
+ // Request primary bucket explicitly
245
+ let dailyPri = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: "primary")
246
+ precondition(dailyPri.first?.quotaDelta == 18.0, "Should return primary bucket delta 18.0 without mixing or overwriting")
247
+ }
248
+
249
+ private static func testMissingAccountIdentityYieldsNil() {
250
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
251
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
252
+ defer { try? FileManager.default.removeItem(at: tempDir) }
253
+
254
+ let runsDir = tempDir.appendingPathComponent("runs")
255
+ try! FileManager.default.createDirectory(at: runsDir, withIntermediateDirectories: true)
256
+
257
+ let todayFormatter = DateFormatter()
258
+ todayFormatter.dateFormat = "yyyy-MM-dd"
259
+ let todayStr = todayFormatter.string(from: Date())
260
+
261
+ let summaryURL = tempDir.appendingPathComponent("quota_summary.json")
262
+ // No active_account_id in summary
263
+ let summaryJSON = """
264
+ {
265
+ "revision": 45,
266
+ "active_account_id": null,
267
+ "exported_at_ms": 1788855500000,
268
+ "daily_usage": [
269
+ {
270
+ "account_id": "acc-anon",
271
+ "bucket_id": "secondary",
272
+ "date": "\(todayStr)",
273
+ "observed_delta_pp": 10.0,
274
+ "coverage_status": "complete"
275
+ }
276
+ ]
277
+ }
278
+ """
279
+ try! summaryJSON.write(to: summaryURL, atomically: true, encoding: .utf8)
280
+
281
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
282
+ // No accountId provided, activeAccountId is null -> MUST NOT pick arbitrary account
283
+ let dailyUsages = engine.computeDailyQuotaUsage(days: 1, accountId: nil, bucketId: "secondary")
284
+ precondition(dailyUsages.first?.quotaDelta == nil, "Missing account identity must yield nil quotaDelta, NEVER arbitrary account data!")
285
+ }
286
+
287
+ private static func testExplicitMissingIdentityDoesNotUseCachedActiveAccount() {
288
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
289
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
290
+ defer { try? FileManager.default.removeItem(at: tempDir) }
291
+
292
+ let todayFormatter = DateFormatter()
293
+ todayFormatter.dateFormat = "yyyy-MM-dd"
294
+ let todayStr = todayFormatter.string(from: Date())
295
+ let summaryJSON = """
296
+ {
297
+ "revision": 46,
298
+ "active_account_id": "old-account",
299
+ "daily_usage": [
300
+ {
301
+ "account_id": "old-account",
302
+ "bucket_id": "secondary",
303
+ "date": "\(todayStr)",
304
+ "observed_delta_pp": 11.0,
305
+ "coverage_status": "complete"
306
+ }
307
+ ]
308
+ }
309
+ """
310
+ try! summaryJSON.write(to: tempDir.appendingPathComponent("quota_summary.json"), atomically: true, encoding: .utf8)
311
+
312
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
313
+ let nilIdentity = engine.computeDailyQuotaUsage(days: 1, accountId: nil, bucketId: "secondary")
314
+ let blankIdentity = engine.computeDailyQuotaUsage(days: 1, accountId: " \t", bucketId: "secondary")
315
+ precondition(nilIdentity.first?.quotaDelta == nil, "Nil snapshot identity must not use cached active account")
316
+ precondition(blankIdentity.first?.quotaDelta == nil, "Blank snapshot identity must not use cached active account")
317
+ }
318
+
319
+ private static func testMissingBucketWithMultipleBucketsYieldsNil() {
320
+ let tempDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
321
+ try! FileManager.default.createDirectory(at: tempDir, withIntermediateDirectories: true)
322
+ defer { try? FileManager.default.removeItem(at: tempDir) }
323
+
324
+ let todayFormatter = DateFormatter()
325
+ todayFormatter.dateFormat = "yyyy-MM-dd"
326
+ let todayStr = todayFormatter.string(from: Date())
327
+ let summaryJSON = """
328
+ {
329
+ "revision": 47,
330
+ "active_account_id": "acc-1",
331
+ "daily_usage": [
332
+ {
333
+ "account_id": "acc-1",
334
+ "bucket_id": "secondary",
335
+ "date": "\(todayStr)",
336
+ "observed_delta_pp": 11.0,
337
+ "coverage_status": "complete"
338
+ },
339
+ {
340
+ "account_id": "acc-1",
341
+ "bucket_id": "primary",
342
+ "date": "\(todayStr)",
343
+ "observed_delta_pp": 22.0,
344
+ "coverage_status": "complete"
345
+ }
346
+ ]
347
+ }
348
+ """
349
+ try! summaryJSON.write(to: tempDir.appendingPathComponent("quota_summary.json"), atomically: true, encoding: .utf8)
350
+
351
+ let engine = TelemetryQueryEngine(customRoot: tempDir)
352
+ let nilBucket = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: nil)
353
+ let blankBucket = engine.computeDailyQuotaUsage(days: 1, accountId: "acc-1", bucketId: " \t")
354
+ precondition(nilBucket.first?.quotaDelta == nil, "Missing bucket identity must not select secondary")
355
+ precondition(blankBucket.first?.quotaDelta == nil, "Blank bucket identity must not select secondary")
356
+ }
357
+ }