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