20-Jun-2026 — memory-safe Google Cloud setup, radio per-role notify, and GitHub-run links in build he... #293
build 707 — commit f30c11a
Squashes two local fixes into one commit, with the full context of both preserved below.
Inbox (#293): refine the per-role notifications UI and fix the Google Cloud setup crash on the standard 512MB single-machine build; plus PWA title bar and deploy worker-change detection.
Release notes: link the GitHub run in build headings and back-fill the already-published notes.
Builds on the per-role inbox notifications, role-scoped unread badge and digest loop guard already shipped for #293. This refines that UI, fixes a Google Cloud setup crash on the standard 512MB single-machine build, and carries two smaller in-flight workstreams (PWA title bar, deploy worker-change detection).
Google Cloud setup - the 512MB crash fix (primary)
- The standard build runs as a single 512MB machine, and running Google Cloud setup crashed it: the provisioning loaded the heavyweight googleapis library, blowing the 259MB V8 heap cap, freezing the event loop and 524-ing every endpoint until Fly restarted the machine.
- googleapis is removed entirely (it was imported nowhere else). Provisioning now calls the Google REST endpoints directly through the existing lightweight OAuth2Client.request() - the same mechanism the Gmail reader uses - with a 20-second per-call timeout. Same five steps and status tracking, heap stays flat. Stays within the standard single machine and its 512MB; no scaling.
- The "Run Google Cloud setup" button now gives feedback: a busy/spinner state, a redirecting note, and a clear error if it can't start (no project, no topic, no consent URL, or a request failure) instead of doing nothing silently.
Per-role notifications - UI refinement
- The per-role notify control is now a three-way radio
- No notification / Notify the assigned member / Notify a different address - replacing the on/off toggle. The "member" option shows the member's actual email; "different address" reveals the override input.
- Edits are deferred: the radios and override email change in memory and only persist when the new "Save notification settings" button is pressed (with an "Unsaved changes" hint), so clicking a radio no longer writes to the database.
- The role-mailbox table columns are balanced (role / Gmail account share the space; the notify column is a fixed width).
Also in this commit
- PWA: dropped theme_color (#ffc107) from the web manifest so the installed Admin app uses the OS's normal title bar (which adapts to light/dark) instead of a bright yellow bar; background_color unchanged.
- Deploy tooling: detect-worker-changes now ignores type-only TypeScript edits by transpiling each side to JS and comparing the emitted output, so a deploy isn't triggered for changes that don't affect runtime behaviour.
Technical changes (inbox)
- inbox-google-setup: googleapis replaced with OAuth2Client.request REST calls (cloudresourcemanager / serviceusage / pubsub) plus per-call timeouts.
- system-gmail-inbox-settings: busy/error/status feedback on Run Google Cloud setup.
- inbox.model: InboxNotifyMode enum; assignedMemberEmail on the alias view.
- system-inbox-role-mailboxes: radio-based notify mode, deferred Save with a dirty-set, member email shown, balanced columns.
- inbox-aliases: assignedMembersByMemberId returns name + email.
- inbox-routes: role-mailbox views carry the assigned member email.
- manifest.webmanifest: theme_color removed.
- server/deploy/detect-worker-changes: type-only-change-aware diffing.
Server changes (Google Cloud setup) need a server restart to take effect.
Release notes - link GitHub run in build headings and back-fill
Release-note build headings rendered as plain text "GitHub #706" instead of linking to the workflow run. CI passed the run number but never the run URL, and the generator's fallback - looking the run up by commit SHA - failed because the run it needed was the in-progress build itself, not yet queryable by head_sha.
Fix at source
- New --build-url option on the generator, threaded into buildUrl, which skips the unreliable SHA lookup when the URL is already known.
- CI now passes --build-url .../actions/runs/
, so every future heading links without depending on the lookup.
Back-fill existing notes
- New refresh-build-links maintenance command rewrites plain-text headings in already-published notes into links. It resolves the URL by run number rather than commit SHA, because sibling pages from a single push share one run number while only the push tip is any run's head_sha - so a SHA lookup misses them.
- Supports both the CMS HTTP API and a direct MongoDB connection, is idempotent, and has a --dry-run mode.
- Applied across 119 published pages (0 unresolved).
Technical changes (release notes)
- content-generator: extractPlaintextBuildRef and linkPlaintextBuildLine helpers detect a plain-text build heading (pulling its run number and commit SHA) and rewrite it as a link, leaving already-linked headings untouched (+ specs).
- github-client: fetchWorkflowRunNumberMap lists the build/deploy workflow's runs and maps run_number to html_url, newest-first, stopping once every needed number is found.
- generate-release-notes / models: --build-url option threaded through generateMultipleReleaseNotes into ReleaseNotesData.buildUrl.
- refresh-build-links: new CLI with CMS and Mongo transports behind a PageStore.
- build-push-and-deploy workflow: pass --build-url built from github.run_id.
- package.json (root + server): release-notes:refresh-build-links script.