Skip to content

Commit 7ee95ee

Browse files
committed
fix(opencode): keep the pushed-down window a day wider than the report's
A cubic review pointed out that the push-down filters on `message.time_created` while the report filters on the payload's `time.created`, and the scale check only proves those share a unit, not a value. A column drifting from its payload by a few hours would therefore exclude a row near a window edge — silently, since SQL never hands it to the payload check. Widen only the pushed-down bounds by a day on each side. The exact window still runs per row against the payload, so the margin costs a few extra rows to scan and cannot change which entries survive. The new test fails without it. The remaining exposure is a column drifting by more than a day, or a column with mixed scales that the sampled rows do not reveal. Both would need a full-column scan to detect, which is the cost this push-down exists to avoid; the local install shows a maximum delta of 0 across 600 rows.
1 parent d72e237 commit 7ee95ee

1 file changed

Lines changed: 46 additions & 1 deletion

File tree

  • rust/crates/ccusage/src/adapter/opencode

‎rust/crates/ccusage/src/adapter/opencode/loader.rs‎

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ fn load_entries_from_database(
205205
// millisecond scale as the payload; the payload check in the loop stays
206206
// authoritative either way.
207207
let pushdown = if window.is_unbounded() || time_created_is_millis(&connection) {
208-
window
208+
window.widened_for_pushdown()
209209
} else {
210210
debug_log(
211211
shared,
@@ -367,6 +367,21 @@ impl DateWindow {
367367
self == Self::UNBOUNDED
368368
}
369369

370+
/// Same window, widened by a day on each side for the SQL push-down.
371+
///
372+
/// SQL filters on `message.time_created` while the report filters on the
373+
/// payload's `time.created`. They hold the same value on every OpenCode
374+
/// build checked here, but the column is only a proxy for the payload, so
375+
/// the pushed-down window is kept loose: a column that drifts from its
376+
/// payload then costs a few extra rows to scan instead of excluding a row
377+
/// the report wanted. The exact window is still applied per row below.
378+
fn widened_for_pushdown(self) -> Self {
379+
Self {
380+
start: self.start.map(|start| start - crate::MILLIS_PER_DAY),
381+
end: self.end.map(|end| end + crate::MILLIS_PER_DAY),
382+
}
383+
}
384+
370385
fn contains(self, millis: i64) -> bool {
371386
self.start.is_none_or(|start| millis >= start) && self.end.is_none_or(|end| millis < end)
372387
}
@@ -1031,6 +1046,36 @@ mod tests {
10311046
assert_eq!(entries[0].date, "2026-01-01");
10321047
}
10331048

1049+
#[test]
1050+
fn pushdown_margin_keeps_rows_whose_column_drifts_from_the_payload() {
1051+
let fixture = fs_fixture!({});
1052+
// Payload lands on 2026-01-02, but the column sits 26 hours later, which
1053+
// an exact SQL window would push past its upper bound.
1054+
create_db_message_with_time(
1055+
&fixture.path("opencode.db"),
1056+
"msg-drifted",
1057+
"session-a",
1058+
1_767_312_000_000 + 26 * 60 * 60 * 1000,
1059+
r#"{"providerID":"anthropic","modelID":"claude-sonnet-4-20250514","time":{"created":1767312000000},"tokens":{"input":1,"output":1}}"#,
1060+
);
1061+
1062+
let shared = SharedArgs {
1063+
mode: CostMode::Display,
1064+
timezone: Some("UTC".to_string()),
1065+
since: Some("20260102".to_string()),
1066+
until: Some("20260102".to_string()),
1067+
..SharedArgs::default()
1068+
};
1069+
let entries = load_entries_from_directory(fixture.root(), &shared).unwrap();
1070+
1071+
assert_eq!(
1072+
entries.len(),
1073+
1,
1074+
"the payload decides the window, so a drifting column must not exclude the row"
1075+
);
1076+
assert_eq!(entries[0].date, "2026-01-02");
1077+
}
1078+
10341079
#[test]
10351080
fn second_scale_time_created_disables_the_range_pushdown() {
10361081
let fixture = fs_fixture!({});

0 commit comments

Comments
 (0)