Repository navigation
time: stamps cross the bridge as UTC; local time is a display concern - #80
Conversation
Stamps arrived as the Mac's naive wall clock ("2026-09-07 14:33:12") and
were compared against the Linux clock — the same string only while both
machines sat in one timezone. A Mac an hour ahead put every read mark ahead
of every message, so nothing ever counted as unread; an hour behind and the
backlog re-toasted. And once a year, in the DST fall-back hour, the Mac's
own clock repeats: two messages an hour apart carried the SAME stamp, so
ordering, the watermark and "newer than the mark" stopped meaning anything
for that hour. Every `a.ts < b.ts` in the collector was already assuming
lexical order equals chronological order; only UTC actually provides it.
imsg's JSON now emits ISO-8601 UTC (fmt_ts); the plain-text renders keep the
Mac's clock (fmt_ts_local), because a person reading `imsg recent` wants the
time they remember. On the Linux side localNowTs() became nowTs() in the same
format, and every label converts to the READER's zone at render: localDay()
and formatStamp() in thread.ts, stampMs()/localDay()/fmtTime() in BlipView.
Day dividers no longer slice the date out of the string — that is UTC's day,
and the divider belongs at your midnight.
Upgrading is covered from both ends, and both halves are load-bearing
together: loadState() migrates the marks a pre-UTC release wrote, and stamps
are normalised at the two fetch doors so a Mac still on the old bridge keeps
working. Migrating the marks alone would have sorted every message below
every mark (" " < "T") and silently emptied the badge and the toasts.
The suite runs pinned to TZ=UTC, where all of this is invisible; the new
timezone.test.ts and bridge/mac/test_wire_time.py set their own zones and
are what actually cover it. Verified live against the gateway Mac on both
the old and the new bridge: 298 threads, unread unchanged, no toast storm
on migration, and 23:47:54Z rendering as 6:47 PM in CDT.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015xxS9WCT7ifHhbDjYyGnHS
Co-authored-by: zachwilke <zachwilke@users.noreply.github.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8da602a716
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // Pre-two-mark state files have no readMark. Inheriting the watermark is | ||
| // the safe migration: it reports zero unread rather than a fake backlog. | ||
| readMark: typeof s.readMark === "string" ? s.readMark : watermark, | ||
| readMark: toUtcStamp(typeof s.readMark === "string" ? s.readMark : watermark), |
There was a problem hiding this comment.
Avoid interpreting Mac-local marks in the Linux timezone
When upgrading a setup where the Mac and Linux machine use different timezones, this converts the old bridge's Mac-local readMark using the Linux timezone. For example, a UTC Mac's saved 18:00 mark becomes 22:00Z on a New York Linux host, so newly fetched UTC messages remain below the mark and produce no unread badge for four hours. Unlike watermark, this nonempty future readMark is persisted unchanged on every poll, so the problem does not correct itself after one fetch; migrate against the Mac's offset or reset/clamp legacy read marks when switching formats.
AGENTS.md reference: AGENTS.md:L23-L25
Useful? React with 👍 / 👎.
|
Merged, thanks @zachwilke! stamps now cross the bridge as UTC. It also fixed issue #86 (a send showing twice when Linux and the Mac are in different zones). Your fixes at both ends mean a Mac still on the old bridge keeps working, which made this safe to merge. It's in main as of 186b536 and deployed on my machine. I merged it together with nine other PRs, and all 526 tests pass on the combined tree. |
…cannot abort it WHAT: Step 4 of scripts/blip-setup replaced `scp -q "$here"/bridge/mac/*` with `find "$here/bridge/mac" -maxdepth 1 -type f -print0 | xargs -0 ... scp -q`. A test in collector.test.ts pins the regular-files-only copy. WHY: the glob also matches bridge/mac/__pycache__, which appears as soon as anyone runs the Mac-side Python tests. scp without -r exits 1 on a directory, and blip-setup runs under set -euo pipefail, so setup died right there, before install.sh ran, and the Mac silently kept its OLD bridge tools. Found 2026-09-15 while installing the 2.5.0+ bridge (UTC stamps, #80) on the gateway Mac after the PR Hunter merge batch: the copy failed exactly this way. VERIFIED: the new copy line run against a temp tree with a file, install.sh and a __pycache__/x.pyc under a stand-in scp copies the two files and skips the directory; bash -n clean; full bun suite green. TAGS: blip, blip-setup, scp, pycache, mac-install, set-e, bridge-tools, pr-hunter Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
WHAT: CHANGE.log is a running, newest-first log of every change since the last X post, each entry naming the person with real name, GitHub login and their X account as a https://x.com/<handle> link. X-POSTS.md is the queue of posts for @nixfred: Post 1 (CONTRIBUTORS-X-POST.md, 2026-09-11) marked posted, Post 2 drafted covering the 19 merged PRs since (#54 #71-#77 #80-#82 #84 #85 #87 #88 #92-#95) plus 4bad82d and 481e555. WHY: Fred's standing rule 2026-09-16. Handles were missed before; a thank-you without the @ never reaches the person, and a wrong @ tags a stranger. HANDLES: verified @jondkinney (GitHub profile), @zachwilke_1 (pinefall.dev), @bbishdotdev (bbish.dev + name), @mwhuss (name + Omarchy posts replying to @nixfred). @jefehoser likely (same login, Omarchy posts), flagged for Fred. Ian Swope and cjoh: none verified, recorded as checked. .gitignore: *.log swallowed CHANGE.log, so it is re-included explicitly. LR-T: blip, changelog, x-posts, contributors, handles LR-D: blip, docs, community LR-K: change-log-verified-x-handles, x-post-queue Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013ainpzHJ5F7nsV4hB6HpZy
Split out of #47 (first of five, as requested). Rebased onto current
main(2.5.0).What
Stamps cross the bridge as ISO-8601 UTC (
2026-09-07T18:33:12Z). Local time is a display concern: bubbles, day dividers, "Today"/"Yesterday" and read receipts convert to the reader's zone at render, so a day still breaks at your midnight.Why
Stamps arrived as the Mac's naive wall clock and were compared against the Linux clock — the same string only while both machines sat in one timezone. A Mac an hour ahead put every read mark ahead of every message (nothing unread); an hour behind and the backlog re-toasted. In the DST fall-back hour the Mac's own clock repeats, so two messages an hour apart carried the same stamp and lexical order stopped being chronological order. Every
a.ts < b.tsin the collector already assumed that property; only UTC actually provides it.imsg's JSON emitsfmt_ts(UTC). Plain-text CLI renders keepfmt_ts_local(the time a person readingimsg recentremembers).Upgrading is covered from both ends, and both halves are load-bearing together:
loadState()migrates marks a pre-UTC release wrote, and stamps are normalised at the two fetch doors (fetchMessages,loadThread, plusfetchChats'last) so a Mac still on the old bridge keeps working. Migrating the marks alone would sort every message below every mark (" "<"T") and silently empty the badge and the toasts.This closes the open ROADMAP item ("Timestamps are Mac-local wall-clock strings").
How it was verified
bun testis green (497 tests; CI-equivalent Mac-tool tests including newtest_wire_time.py)CLAUDE.md→ named below, and the doc updated if it changedinvariants touched: Every stamp inside Blip is UTC; local time is a DISPLAY concern. Marks migrate on load; stamps normalise at the fetch doors. The suite is pinned to
TZ=UTC(test-setup.ts);timezone.test.tsandbridge/mac/test_wire_time.pyset their own zones and are what actually cover the conversion.Rebase notes
mainmoved a long way since #47. Conflict resolutions kept current-main behaviour (chat-id sanitising, pin_name, failed-sendPendingSendfields,XDG_CONFIG_HOMEtest isolation) and appliedtoUtcStamponfetchChats().last. AselectIncomingLinksfixture added onmainafter #47 still used a naive stamp; it is now UTC so it does not sort below a UTC watermark (" "<"T").Supersedes the UTC slice of #47 (
688f2cb).