Dates: reads + insight context on the new contract (mixed-row safe) #119
Notifications
Due Date
No due date set.
Blocks
Depends on
Reference: Apps/ImageApi#119
Reference in new issue
Block a user
Parent
ImageApi #113 — SPEC: true-instant date contract (ADR-0001).
What to build
The server speaks the new date contract on every read and in every AI context.
/photos,/memories, and the image metadata responses carry the new fields (true instant, camera offset, offset-source) beside the frozen, byte-identical legacy field. Day grouping (memories buckets, day-level queries) prefers the instant in the display zone; rows not yet drained fall back through their known per-source convention — so a converted row and an unconverted row mid-drain both land in the right day, and the long-standing double-shift of container/filesystem rows in/memoriesis fixed before the drain ever runs.The insight context follows the same rule as the UI: the chat bootstrap and the photo summary format the camera wall clock (instant minus camera offset, or the display zone when the row has no offset), so the date the AI is told is the date the owner sees. The SMS, location, and calendar context windows are anchored on the true instant with no shift — a 9pm shot pulls 9pm messages and 9pm location regardless of the camera's zone.
Acceptance criteria
/photos,/memories, and metadata responses include the new fields for converted rows (null when unconverted); the legacy field is byte-identical for every row throughout./memoriesdouble-shift is gone pre-drain).Blocked by
Resolved — PR #125 (branch
feature/date-reads-insights, commitaf87765).All seven acceptance criteria met; 1,410 tests pass,
cargo fmt --check+cargo clippy --all-targetsclean. Two-axis review (spec + standards) done in-branch; spec's four test-gap notes and standards' duplicated-code + naming findings fixed before handoff.What landed:
src/date_view.rs(new, pure — no DB/IO):row_true_instant(window anchor),row_display_wall(prompt/UI date),row_display_day(grouping key — always the owner's day),filename_true_instant. Converted rows read the triple directly; unconverted rows fall back to the per-source convention table in the module docs (exif/filename-wall/manual = wall in server or display zone; exiftool-video = one server-zone round-trip read back as UTC; exiftool-photo/ts/fs_time = legacy as-is or its render)./memoriesitems,GpsPhotoSummary, and the photos/EXIF response paths carrydate_utc/camera_offset_minutes/date_taken_tz_source— null while unconverted./memoriesgroups byrender(true_instant, owner_display_zone)with exact MM-DD/%W/month matching in Rust (day_in_span+sqlite_w_week), over a full years-back SQL pre-filter (memories_span_windows). The container double-shift is gone — the mixed-fixture seam test (memories::api::memories_mixed_rows_group_into_owner_month) seeds converted + unconverted-exif + unconverted-container rows and asserts all three land in the owner's month with the correct per-sourcecreated.row_display_wall(camera wall when the row carries an offset — a Japan photo tells the AI the Japan day, with a midnight rollover relative to the owner's zone asserted injapan_offset_row_ai_date_is_camera_wall_and_anchor_unshifted); metadata/UI and prompt now agree by construction (same function).row_true_instant— zero offset math (InsightGenerator::resolve_date_anchorshared by both agentic and non-agentic paths).unconverted_video_row_ai_date_matches_display_wallpins the container convention at the chat seam.REEL_PREGEN_DISPLAY_TZ) instead of a fixed offset;fixed_tz_offsetdeleted.date_takencolumn is untouched by this PR (no migrations, no writes) — the #120 cutover flips the remaining consumers.Deferred (P2 judgement calls, noted for #120/follow-up): bundling the
(display_zone, server_zone)data clump into aDateContexttype;date_from/date_torange filters andtool_store_fact::valid_fromstay on legacy until the big-bang flip.Note: merge is pending the Jenkins CI infra fix (
Core Repos/ImageApi/pipeline/pr-masteris failing for unrelated reasons); local gates are all green.