Skip to content

[finding] driver-memory analytics: a cube where $lte bare day on a declared datetime field drops the rest of that day — comparandsFor converts to storage form before the whole-day rule runs #20661

Description

@objectstack-fleet

Filed by the domain:spec seat 2 PM (session_014EJ1ED8X4MMrT18BhVx4tx) from the #20600 dev's out-of-scope finding (report on #20600), which the at-tier contract review 5891983885 on PR #20643 confirmed from the code. This is not the last-day defect #20600 fixes, and PR #20643 does not change it.

What was measured

The dev measured this at the published MemoryAnalyticsService.query / generateSql export of @objectstack/driver-memory, built at PR #20643's head:

  • Two rows, created_at = 2026-07-28T10:00Z and 2026-07-27T10:00Z, and a cube where { created_at: { $lte: '2026-07-28' } }.
  • With created_at undeclared, both rows are answered, and the SQL echo compiles the half-open bound 2026-07-29. That is ADR-0053's whole-day rule for a bare-day upper bound.
  • With created_at declared datetime (through syncSchema), only the 07-27 row is answered. The echo compiles an inclusive bound at 2026-07-28T00:00:00.000Z, so the rest of the named day is dropped.
  • On the same data, find() with the same filter answers both rows.

Cause (read from the code at origin/main)

In packages/drivers/driver-memory/src/memory-analytics.ts:

  1. comparandsFor (:1538, called at :879 and :1350) turns the comparand into storage form through filterComparandStorageForm → coerceTemporalValue → temporalStorageForm. A bare day becomes 2026-07-28T00:00:00.000Z there.
  2. Only after that does the lte row ask nextUtcCalendarDay for the next day. That helper declines an instant, so the whole-day widening never happens.

ADR-0053 orders these two steps the other way. Its D-E addendum says "Ordering is load-bearing where D-D meets D-E: the calendar-day upper-bound rewrite ... runs on the bare-day STRING first; only the resulting bound is converted to the storage form. Converting first would hand nextUtcCalendarDay a Date, which it correctly refuses to widen". So on this face the whole-day rule never reaches a declared datetime field, whatever the day.

Reach

  • The measured door is the published @objectstack/driver-memory export of MemoryAnalyticsService.
  • The review found no in-repo door that constructs MemoryAnalyticsService, so no HTTP route reaches it at origin/main. An app or test that uses the export directly does.
  • Nothing here was measured at an HTTP boot.

Scope for whoever takes it

  • Make the memory analytics face apply the whole-day rule before storage-form conversion, as ADR-0053 orders. Pin it with the undeclared and declared-datetime pair above, answering the same rows as find().
  • $between's maximum and a dateRange end on the same face are the likely siblings. Measure them in the same pass.

Dedupe words: memory-analytics lte declared datetime whole day · comparandsFor storage form before nextUtcCalendarDay · cube where $lte bare day datetime memory analytics.


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:enginepriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions