Files
xarmian b8b610812c feat: a per-user time zone for calendar days, backend (TASK-4101 PR1) (#2337)
* feat(users): users.timezone column, internal/usertz and the conditional set-if-unset write (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* feat(auth): timezone on /auth/me and the session user, PATCH with if_unset, tz-mismatch dismissal keys (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(server): compute overdue, standup and stamped dates in the viewer's zone, UTC when unset (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* test(server): regenerate the cloud contract goldens for the additive timezone field (TASK-4101)

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(store): add the Postgres twin of the users.timezone migration (TASK-4101)

pgmigrations has its own numbering; 160 alone left the column missing on
Postgres, and make test-pg failed on 'column "timezone" does not exist'.

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* feat(store): NUL invariant triggers for users.timezone (TASK-4101)

The census exists so no single validator is the only guard (ruling
1431c980 item 1): protect the column with its own post-160 trigger file
instead of excluding it.

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(server): decode a dismissal key once, and test the viewer zone through /me/needs (TASK-4101)

A double-encoded %252F reached the handler already decoded once (net/url
keeps no RawPath for it), and the second PathUnescape read it as a slash.
Unescape only when chi routed on RawPath. Adds the unescape cases, the
too_many_dismissals code assertion, an HTTP-level test that a zone ahead
of UTC makes an item overdue on /me/needs, and the api.md notes for the
cap and the additive dashboard/standup fields (ruling 1431c980 items 2-5).

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v

* fix(server): the standup test expects the UTC day, and the zone note names no PR2 surface (TASK-4101)

The standup date is the viewer's zone, UTC when unset; the test still
compared against the host's local day and failed on any non-UTC host
when the two dates differ. The note pointed at the web settings and
`pad auth timezone`, which land with PR2 (ruling 00877b60).

Claude-Session: https://claude.ai/code/session_01Pa22hHHb1atXYf1JmBYE1v
2026-10-28 14:51:33 -04:00
..