cmux NEXT: the Updated card names the build by its time and commit, and how far it is from the newest

Merged
TE
10d ago
+876-26

Summary

Leo, on the cmux NEXT Updated card: "Like with this new update, I don't know what update this is, but, like, it just says updated to nightly, but that doesn't mean anything." The card said "Updated to Nightly · Oct 26", and the build ("1.0.0-nightly.4037004604601") showed only in a tooltip.

The card now says which build you are on:

  • When it was made, in your time zone and locale: "Updated to Nightly · Oct 26, 7:38 PM" (en-GB reads "26 Oct, 19:38"). Before, it showed only a UTC day. The full version stays in the tooltip. In a narrow sidebar the line wraps between its parts and nothing is cut off.
  • Which build, and how far it is from the newest: a second line, "Build a9d1434 · 2 builds behind newest", "· 1 build behind newest" or "· Newest". It names the build by the short commit it was cut from, or by the build number when the notes don't give the commit.

Where the data comes from:

  • release-notes.py now writes builtAt (UTC, to the second) into each build's notes. Swift ReleaseNotes decodes head and builtAt. UpdateHighlights carries both to the page; when the notes lack builtAt, it uses the appcast item's date. The date the notes give is only a day (UTC midnight), so it is never shown as a time. An update record saved before this change takes the time from its staged appcast date.
  • "Behind newest" comes from the notes index (cmux.changelog.list), which the card already loads to sum up the update's span. This adds no network call. The line shows the position only once the index has loaded and lists the build; until then, or offline, it shows only "Build a9d1434".
  • 4 new strings in webviews/src/pages/changelog/Localizable.xcstrings, in all 21 languages, like their neighbours. This catalog has no plural forms, so languages with number agreement use a "Newer builds: N" form.

Proof

Gallery pages.changelog.update-surfaces: before is the merge-base and after is this head. Fixture data only.

BeforeAfter
beforeafter: 2 builds behind newest
before, darkafter, dark

The newest build: after: newest

The gallery renders in CI's DejaVu Sans at the card's minimum width, so its lines wrap more than they do in the app's system font.

No capture: no native scene or tour shows the Updated card with published notes. debug.updater updated on a capture build has no notes for its own build. The card is a web view, and the gallery renders the same component.

Testing

  • Red, at the tests-only commit 8d9a5d13b72: the new card, notes and UpdateHighlights tests fail.
  • Green, at the head (cmux-ci test --setup webviews): updated-card-lists-changes passes under TZ=UTC, Asia/Kolkata, Pacific/Kiritimati and America/Los_Angeles. 46 tests pass across the other card and changelog suites (updated-card-summary, update-ready-popover, changelog-model, changelog-readable, whats-new-by-area, changelog-first-paint, whats-new-no-hover-previews, whats-new-text-selection). Also clean: vp fmt --check, tsc, check:catalog-keys, check:design-tokens, check:type-scale, and tests/test_cmux_next_release_notes.py (18).
  • Swift: UpdateHighlightsBuildTimeTests covers the time and commit from the notes, the appcast time as fallback, a day from the notes that is not taken as a time, and older notes and records still decoding. The app compiles on the fleet (cmux-ci compile-check --app); the Swift tests run in this PR's CI.

Observed / Not run

  • Not run: the real app on an updated nightly. Notes published before this change have no builtAt, so until a new nightly publishes, the time comes from the staged appcast item.

Changelog

Changed: The Updated card says when the build was made, in your time zone, which build it is, and how far it is from the newest

Risk: normal. Display only, plus two new optional fields in the notes and in UpdateHighlights. Older notes and saved records decode unchanged.

Agent session: agent session

🤖 Generated with Claude Code