# 20-Jun-2026 — memory-safe Google Cloud setup, radio per-role notify, and GitHub-run links in build he... [#293](https://github.com/nbarrett/ngx-ramblers/issues/293)

## [build 707](https://github.com/nbarrett/ngx-ramblers/actions/runs/27886772780) — [commit f30c11a](https://github.com/nbarrett/ngx-ramblers/commit/f30c11a8903bd6364544b98ed6fd82df37ab3573)

_____

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/<run_id>, 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.