Skip to main content
Companion pages: the Event clock writeup is the narrative; the problem canvas is the one-page map; cohort alignment is the leftover SQL; availability and engagement semantics is the locked writer/accept contract. Ticket: ENG-580. This page is the 2026-08-17 DBeaver snapshot after the Instant-target retarget: how to read a writer interval, how many Events are still Active on the gigs list, which createdAt window those rows sit in, and what the migrations plus Phase 2 + Phase 4/5 branch Instant readers mean for those rows. Migrations are still not deployed on cited dumps; reader code on cursor/eng-580-timebound-clock-alignment-b9d9 is likewise branch-only.
Inventory ran from DBeaver with session America/Chicago, not the deployed app. Naive TIMESTAMP ::date / ::time / EXTRACT(HOUR) are session-safe. (timestamptz)::date is not.

Locked target

Naive TIMESTAMP(3) call clocks are UTC Instants. Event.timezone is a display IANA. Date-key columns stay calendar dates (UTC midnight of the venue’s calendar day), not venue-midnight Instants. Two formulas, never mixed: Do not run B on a row that is already Instant (startTime = show_start_at, or availability 05:00 → 04:xx next UTC day).

How to read an interval

A writer interval is UTC created_at / Event.createdAt, half-open: >= start AND < end. Both ends are named even when a side is indefinite (−∞ or +∞). The right end is the start of the next writer, not the last row of this one. created_at names a writer only if the row was never saved again. A later header save re-encodes call times. Flex quotes attached after create-time (25 in the gap, 1 in web-Z) are the usual lie. Calendar-day bounds are midnight UTC. PR / first-row bounds are merge or first-row seconds.

Event intervals

Noon calendar days (eventDate::time = 12:00:00) are not a writer interval. Both ends are indefinite; the predicate is the clock.

Offer, availability, and RoS intervals

Availability splits inside a_device_offset by clock shape, not by another created_at bound: hour-0 wall (625), 5→4 Instant pair (15, leave), timed leftovers (15).

Two meanings of active

Display-active is what the gigs list calls Active. A past stored-active gig already shows as Completed. The one UNARCHIVED row is stored-active but past, so it is in the 880 and not in the 156. Display-active is a calendar-day filter, not a writer interval. A March create for a September gig is still e_pre_mobile.

Active Events by createdAt interval

All 880 stored-active and 156 display-active rows, same half-open CASE as query 1. Display-active with a startTime: 22. Display-active with a real IANA (timezone not null and not 'UTC'): 17. Of the 156 display-active:
  • 131 are pre-mobile / mobile / gap (47 + 17 + 67). Almost no IANA (0 + 0 + 3).
  • 19 are Flex Instant overlap (14 starts, 12 with IANA).
  • 4 are web fake Z (2 starts, 0 IANA).
  • 2 are after #1328 (1 start, both have IANA).

What this file can rewrite of the 156

20260817180000_event_clock_alignment Instant-izes Event wall hours 10–23 only when a real IANA exists, and skips quote-equal Flex clocks. It also fills Flex metadata IANA and projects Flex Instant calendar days in the #1062 … #1292 window. It can touch the IANA slice of display-active — mainly Flex Instant overlap (19, 12 zoned) and the 2 after #1328 in that snapshot. Later #1387 Formula A walls restore from the quote Instant (metadata zone first). It cannot Instant-ize the 131 display-active rows in pre-mobile / mobile / gap without a zone ladder first. Mobile #379 display-active is 17 events with 0 starts. Same-release cutover. Do not teach every reader “these digits are UTC” until that row is already Instant or a proven IANA exists to Instant-ize it. A global reader flip on the 596 no-zone Events shows UTC hours and recreates Mapache in reverse.
  1. Same deploy: writers store a real Instant and a real IANA; readers format Instant → Event.timezone (Phase 2 + Phase 4/5 branch code); ship 20260817180000_event_clock_alignment and 20260819170000_event_schedule_item_formula_b as written. Schedule Formula B SQL covers hour 10–23, overnight inverted ends, and invalid Event IANA → org, but no schedule rows convert while convert_schedule_wall is empty — populate dump-proved ids before prod. Run the :5434 integration test before prod.
  2. Transitional read for leftovers: real IANA → Instant; startTime = show_start_at → Instant even if the zone was just filled; null / 'UTC' and not quote-equal → still wall.
  3. Zone ladder, display-active first. Fill a label, then formula B. Never use a CT prior as the convert zone. Order: real Event.timezone → Flex metadata.timezone (skip UTC) → Event / Venue lat/lng (skip 0,0) → sibling Event IANA → Venue.address.state → America/Chicago label only after venues agree. Then the 25+1 Flex-linked created before #1062 (query 11). Hours 0–9 stay Instant candidates.
  4. The same file Instant-izes offer Formula B (56) + crew_offer_days overrides, and avail same-UTC-day hour 10–23 evening walls (row IANA, fake-Z updated_at band). Avail hour-0 and 5→4 are nulled with a required IANA. RoS has 0 clocks. See cohort-alignment leftovers.
  5. Make Organization.timezone a real writer so new orgs stop landing in the leftover bucket.
Do not convert a whole created_at window. Do not run B on Flex quote-equal or avail 5→4. Do not default leftovers through America/Chicago. Do not mix LedgerItem.date (ENG-581). Do not edit applied 20260307.

Queries

Paste-safe. Same DBeaver session as cohort-alignment query 1.

Status rollup

Stored-active and display-active by interval