@bongos/core 1.24.26 → 1.24.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.bongos-core.json +142 -92
  2. package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +12 -0
  3. package/docs/copy-inventory.md +67 -48
  4. package/docs/copy-registry.json +279 -78
  5. package/docs/module-api-changelog.md +2 -0
  6. package/docs/page-inventory.json +10 -4
  7. package/docs/page-readings.json +4335 -4314
  8. package/docs/recipes/ui-look-before-you-ship.md +1 -0
  9. package/modules/autonomy/fence.js +16 -1
  10. package/modules/government/protected-surfaces.json +2 -0
  11. package/modules/hall-ui/public/ai-studio.html +1 -1
  12. package/modules/hall-ui/public/atlas.html +1 -1
  13. package/modules/hall-ui/public/blockers.html +1 -1
  14. package/modules/hall-ui/public/board-room.html +1 -1
  15. package/modules/hall-ui/public/city.html +1 -1
  16. package/modules/hall-ui/public/collab.html +1 -1
  17. package/modules/hall-ui/public/commands.html +1 -1
  18. package/modules/hall-ui/public/copy-desk.html +1 -1
  19. package/modules/hall-ui/public/core-move-notice.js +9 -6
  20. package/modules/hall-ui/public/deploy-handoff.js +203 -0
  21. package/modules/hall-ui/public/deploy.html +4 -2
  22. package/modules/hall-ui/public/deploy.js +7 -1
  23. package/modules/hall-ui/public/diagrams.html +1 -1
  24. package/modules/hall-ui/public/drachmae.html +1 -1
  25. package/modules/hall-ui/public/fleet.html +1 -1
  26. package/modules/hall-ui/public/gate.html +1 -1
  27. package/modules/hall-ui/public/goal-map.html +1 -1
  28. package/modules/hall-ui/public/goals.html +1 -1
  29. package/modules/hall-ui/public/government.html +1 -1
  30. package/modules/hall-ui/public/idea.html +1 -1
  31. package/modules/hall-ui/public/ideas.html +1 -1
  32. package/modules/hall-ui/public/index.html +1 -1
  33. package/modules/hall-ui/public/market-watch-item.html +1 -1
  34. package/modules/hall-ui/public/market-watch-rules.html +1 -1
  35. package/modules/hall-ui/public/market-watch-settings.html +1 -1
  36. package/modules/hall-ui/public/market-watch.html +1 -1
  37. package/modules/hall-ui/public/modules.html +1 -1
  38. package/modules/hall-ui/public/my-agents.html +1 -1
  39. package/modules/hall-ui/public/primer.html +1 -1
  40. package/modules/hall-ui/public/profile.html +1 -1
  41. package/modules/hall-ui/public/project-settings.html +1 -1
  42. package/modules/hall-ui/public/ranks.html +1 -1
  43. package/modules/hall-ui/public/roadmap.html +1 -1
  44. package/modules/hall-ui/public/roster.html +1 -1
  45. package/modules/hall-ui/public/sessions.html +1 -1
  46. package/modules/hall-ui/public/settings-software-update.js +22 -8
  47. package/modules/hall-ui/public/settings.html +7 -5
  48. package/modules/hall-ui/public/shell.js +25 -73
  49. package/modules/hall-ui/public/street.html +1 -1
  50. package/modules/hall-ui/public/studio.html +1 -1
  51. package/modules/hall-ui/public/style.css +119 -39
  52. package/modules/hall-ui/public/task.html +1 -1
  53. package/modules/hall-ui/public/thinking.html +1 -1
  54. package/modules/hall-ui/public/tweak-editor.html +1 -1
  55. package/modules/hall-ui/public/update-pill.js +623 -0
  56. package/modules/hall-ui/public/watch.html +1 -1
  57. package/modules/hall-ui/public/work.html +1 -1
  58. package/modules/hall-ui/records/update-pill.md +12 -0
  59. package/modules/lifecycle/github-push.js +1 -0
  60. package/modules/lifecycle/routes/tasks.js +2 -2
  61. package/modules/lifecycle/runner-self-guard.js +122 -0
  62. package/modules/lifecycle/runner-self.js +154 -0
  63. package/modules/public-landing/public/account.html +3 -3
  64. package/modules/public-landing/public/assets/cosmos.css +45 -0
  65. package/modules/public-landing/public/community.html +4 -4
  66. package/modules/public-landing/public/connections.html +1 -1
  67. package/modules/public-landing/public/guild.html +1 -1
  68. package/modules/public-landing/public/invitations.html +4 -4
  69. package/modules/public-landing/public/projects.html +26 -26
  70. package/modules/status-ui/public/index.html +2 -2
  71. package/modules/status-ui/public/index.states.json +12 -0
  72. package/modules/status-ui/public/status.js +10 -2
  73. package/modules/status-ui/public/style.css +57 -0
  74. package/modules/ui-design/kit/README.md +1 -1
  75. package/modules/ui-design/kit/serve.js +8 -1
  76. package/package-lock.json +2 -2
  77. package/package.json +1 -1
  78. package/release-notes.json +14 -0
  79. package/scripts/bongos/autobongos-loop.js +4 -0
  80. package/scripts/bongos/autobongos-run.js +21 -4
  81. package/scripts/bongos/autobongos-tweak-lane.js +5 -3
  82. package/scripts/bongos/ship-flow.js +20 -0
  83. package/src/module-api.js +1 -1
  84. package/tests/autobongos_host_access.mjs +5 -3
  85. package/tests/autobongos_self_modification.mjs +420 -0
  86. package/tests/core_update_banner.mjs +6 -92
  87. package/tests/core_upgrade_owner_door.mjs +4 -56
  88. package/tests/deploy_handoff.mjs +206 -0
  89. package/tests/government_protected_surfaces.mjs +15 -0
  90. package/tests/hub_connections_page.mjs +1 -1
  91. package/tests/hub_invitations_page.mjs +1 -1
  92. package/tests/loading_mark.mjs +203 -0
  93. package/tests/nav_permission_atoms.mjs +3 -2
  94. package/tests/software_update.mjs +6 -2
  95. package/tests/update_pill.mjs +431 -0
@@ -87,7 +87,7 @@
87
87
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
88
88
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
89
89
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
90
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
90
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
91
91
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
92
92
  <script src="/builders/palette.js" defer></script>
93
93
  <script src="/builders/hall-kit.js"></script>
@@ -74,7 +74,7 @@
74
74
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
75
75
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
76
76
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
77
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
77
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
78
78
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
79
79
  <script src="/builders/palette.js" defer></script>
80
80
  <script src="/builders/founding-mark.js?v=2026-09-30-founding"></script>
@@ -44,7 +44,7 @@
44
44
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
45
45
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
46
46
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
47
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
47
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
48
48
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
49
49
  <script src="/builders/palette.js" defer></script>
50
50
  <!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
@@ -56,7 +56,7 @@
56
56
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
57
57
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
58
58
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
59
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
59
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
60
60
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
61
61
  <script src="/builders/palette.js" defer></script>
62
62
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -114,7 +114,7 @@
114
114
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
115
115
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
116
116
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
117
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
117
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
118
118
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
119
119
  <script src="/builders/palette.js" defer></script>
120
120
  <script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
@@ -7,11 +7,12 @@
7
7
  // update channel lets it take; what each version in between brings — and, for the people
8
8
  // the upgrade door lets through, an Update button.
9
9
  //
10
- // THE BUTTON LEADS TO THE DOOR; IT IS NOT THE DOOR. Taking an update is the deploy door's
11
- // two steps (see exactly what moves, then confirm that version) with its health check and
12
- // auto-rollback (ADR 0293), and on a hosted project that door is on the hub (ADR 0339 D3).
13
- // A second copy of that flow here would be a second place for its safety rules to drift, so
14
- // the server names where this project's door is (`door`) and the button goes there.
10
+ // THE BUTTON RUNS THE PILL'S FLOW; IT IS NOT A SECOND DOOR. Taking an update is the deploy
11
+ // door's act, with its health check and auto-rollback (ADR 0293), and on a hosted project that
12
+ // door is on the hub (ADR 0339 D3). Since task 1005041 the button hands this panel's reading to
13
+ // update-pill.js, which takes the update in place (this page names the version; the door moves
14
+ // it), so there is one copy of that flow, not two. The server still names the door (`door`),
15
+ // and the button's href is that door, for a page whose pill script did not load.
15
16
  //
16
17
  // THE PAGE KNOWS NOTHING (deploy.js's rule, kept). Every version here is the server's reading
17
18
  // with the time it was taken. "Up to date" is drawn only from an ok reading with nothing
@@ -125,11 +126,14 @@
125
126
  if (!sameOrigin && !/^https?:\/\/[^\s"'<>]+$/i.test(url)) return '';
126
127
  let where = '';
127
128
  if (!sameOrigin) {
128
- try { where = ` on ${new URL(url).host}`; } catch (e) { return ''; }
129
+ try { where = new URL(url).host; } catch (e) { return ''; }
129
130
  }
131
+ // IN PLACE (task 1005041): the button runs the Update pill's flow (update-pill.js) and the
132
+ // page stays put; the pill in the top bar shows the progress. The href stays the door's,
133
+ // so a page whose pill script did not load still reaches the update the old way.
130
134
  return `<div class="row-end su-door">
131
- <a class="pbtn pbtn--save" id="su-update" href="${escapeHtml(url)}">Update to ${escapeHtml(offered)}${sameOrigin ? '' : ' ↗'}</a>
132
- <span class="panel-hint">Opens this project's upgrade${escapeHtml(where)}: you see what would move, confirm it, and it installs, checks itself, and rolls back if the project does not come back healthy.</span>
135
+ <a class="pbtn pbtn--save" id="su-update" href="${escapeHtml(url)}">Update to ${escapeHtml(offered)}</a>
136
+ <span class="panel-hint">Takes a few minutes. The project restarts once, checks itself, and goes back to the version it runs now if it does not come back healthy.${where ? ` A small window from ${escapeHtml(where)} opens for a moment to start it.` : ''}</span>
133
137
  </div>${door.kind === 'hub' && ask.available ? askHtml(ask, offered, { lead: false }) : ''}`;
134
138
  }
135
139
 
@@ -236,6 +240,16 @@
236
240
  const askBtn = byId('su-ask');
237
241
  if (askBtn) askBtn.addEventListener('click', () => askForUpgrade(data, askBtn));
238
242
 
243
+ // The same press as the pill's Update now; only when it took the update in place is the
244
+ // link's own navigation stopped.
245
+ const updateBtn = byId('su-update');
246
+ if (updateBtn) {
247
+ updateBtn.addEventListener('click', (e) => {
248
+ const pill = window.OTBUpdatePill;
249
+ if (pill && pill.updateNow(data, { api })) e.preventDefault();
250
+ });
251
+ }
252
+
239
253
  const more = byId('su-more');
240
254
  if (more) {
241
255
  more.addEventListener('click', () => {
@@ -475,9 +475,9 @@
475
475
  <!-- SOFTWARE UPDATE — this project's version, the newest release its update
476
476
  channel allows, and what the versions between them carry (task 1004296).
477
477
  Like a phone's Software Update screen: every builder reads it; the Update
478
- button is drawn only for those the upgrade door lets through, and it LEADS
479
- to that door (the two-step confirm and auto-rollback of ADR 0293) — the
480
- act never happens on this page. Rendered by settings-software-update.js
478
+ button is drawn only for those the upgrade door lets through, and it runs
479
+ the Update pill's in-place flow (update-pill.js, task 1005041): the act is
480
+ still the door's, with its auto-rollback (ADR 0293). Rendered by settings-software-update.js
481
481
  from GET /api/bongos/software-update, read only when this group opens:
482
482
  an update's notes come out of a package download. -->
483
483
  <section class="scroll" id="software-update-scroll" aria-labelledby="software-update-h" data-group="update" hidden>
@@ -514,7 +514,7 @@
514
514
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
515
515
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
516
516
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
517
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
517
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
518
518
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
519
519
  <script src="/builders/palette.js" defer></script>
520
520
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
@@ -540,7 +540,9 @@
540
540
  settings.js is against the 1500-line ratchet. -->
541
541
  <script src="/builders/settings-render.js?v=2026-09-16-render-levels"></script>
542
542
  <!-- task 1004296: Settings → Software update. Own file on the same precedent. -->
543
- <script src="/builders/settings-software-update.js?v=2026-10-18-history"></script>
543
+ <!-- the Update pill's flow, which the panel's Update button runs in place (task 1005041) -->
544
+ <script src="/builders/update-pill.js?v=2026-11-03-pill"></script>
545
+ <script src="/builders/settings-software-update.js?v=2026-11-03-pill"></script>
544
546
  <!-- task 1004501: Settings → Your Autobongos runner. Own file on the same precedent. -->
545
547
  <script src="/builders/settings-autobongos.js?v=2026-10-01-autobongos"></script>
546
548
  <!-- task 1003793: Settings → Module install reports. Own file on the same precedent. -->
@@ -751,8 +751,11 @@
751
751
  history.replaceState(null, '', window.location.pathname + rest + window.location.hash);
752
752
  }
753
753
 
754
- // task 1004045 — the core-update banner. The owner's ask: "a popup banner at the
755
- // top of every owners builders hall that a core update has been released".
754
+ // task 1004045 → task 1005041 — the Update pill. The owner's first ask was a banner at the top
755
+ // of every owner's hall when a core update is released; the second (2026-11-03) was that it
756
+ // was "way too obstructing and not obvious what to do", and should feel like the Claude
757
+ // app's "restart to update". So the news is a quiet pill in the top bar now, and the
758
+ // update is taken in place from it: update-pill.js owns all of it, this only asks.
756
759
  //
757
760
  // WHY THE SERVER IS ASKED RATHER THAN TOLD. The version this box runs is known
758
761
  // here, but what is PUBLISHED is not, and the fact lives behind an outbound
@@ -764,6 +767,11 @@
764
767
  // ASKED ONCE PER PAGE LOAD. applyAccess runs on every settle (the /me probe, a
765
768
  // page's own report), and re-firing a network call on each would be a request per
766
769
  // nav interaction for a fact with a six-hour life on the server.
770
+ //
771
+ // SAID FROM AN ANSWER, OR FROM AN UPDATE ALREADY UNDER WAY. The pill script loads only
772
+ // when the answer has something to say (behind, held, or just moved), or when this
773
+ // browser remembers an update it started: mid-restart the read itself may fail, and the
774
+ // pill must still follow the owner from page to page.
767
775
  var coreUpdateAsked = false;
768
776
  function maybeRenderCoreUpdate() {
769
777
  if (coreUpdateAsked) return;
@@ -771,10 +779,20 @@
771
779
  if (!perms || perms['core.pin.move'] !== true) return;
772
780
  coreUpdateAsked = true;
773
781
  api.request('GET', '/api/bongos/core-update').then(function (res) {
774
- if (!res || res.status !== 200 || !res.data) return;
775
- if (res.data.update) renderCoreUpdateBanner(res.data.update, res.data.held);
776
- if (res.data.moved) renderCoreMoveNotice(res.data.moved); // task 1004731: loads core-move-notice.js, which draws it
777
- }, function () { /* fail-soft: no banner is the correct answer to no answer */ });
782
+ mountUpdatePill(res && res.status === 200 && res.data ? res.data : null);
783
+ }, function () { mountUpdatePill(null); });
784
+ }
785
+ function updateUnderWay() {
786
+ try { return !!(window.localStorage && window.localStorage.getItem('bongos.update.run')); } catch (e) { return false; }
787
+ }
788
+ function mountUpdatePill(data) {
789
+ var u = data && data.update;
790
+ var news = !!(u && u.reading === 'ok' && u.behind) || !!(data && data.held);
791
+ if (news || updateUnderWay()) {
792
+ var go = function () { if (window.OTBUpdatePill) window.OTBUpdatePill.mount(data, { api: api }); };
793
+ if (window.OTBUpdatePill) go(); else loadHallScript('update-pill.js', go);
794
+ }
795
+ if (data && data.moved) renderCoreMoveNotice(data.moved); // task 1004731: loads core-move-notice.js, which draws it
778
796
  }
779
797
  // One hall script on demand, stamped with the shell's own ?v= (task 1004996 made it one helper).
780
798
  function loadHallScript(file, onload, onerror) {
@@ -785,7 +803,7 @@
785
803
  head.appendChild(tag);
786
804
  }
787
805
  function renderCoreMoveNotice(moved) {
788
- loadHallScript('core-move-notice.js', function () { if (window.CoreMoveNotice) window.CoreMoveNotice.render(moved, { api: api, href: SOFTWARE_UPDATE_HREF }); });
806
+ loadHallScript('core-move-notice.js', function () { if (window.CoreMoveNotice) window.CoreMoveNotice.render(moved, { api: api, href: '/settings#software-update' }); });
789
807
  }
790
808
  var githubChipAsked = false; // task 1004996: the Link GitHub chip (github-chip.js), asked once a page
791
809
  function loadGithubChip(builder) {
@@ -795,72 +813,6 @@
795
813
  loadHallScript('github-chip.js', function () { if (window.OTBGithubChip) window.OTBGithubChip.mount({ api: api, builder: builder, hub: hub }); });
796
814
  }
797
815
 
798
- // Where the banner's Upgrade goes (task 1004296). The platform's hall runs the deploy
799
- // door itself, so there it is /deploy. Every other hall — a hosted project, a
800
- // self-hosted instance — takes its update from Settings → Software update, which says
801
- // what the update carries and, on a hosted project, hands the owner to the hub's door
802
- // for this project (ADR 0339 D3). The banner no longer links off-site itself.
803
- var SOFTWARE_UPDATE_HREF = '/settings#software-update';
804
-
805
- // SAY NOTHING WHEN THERE IS NOTHING TO SAY. `behind` is false both when the box
806
- // is current AND when the registry could not be read; `reading` separates them,
807
- // and neither renders. A banner is a claim about being out of date, and a wrong
808
- // one on a project that is actually current is worse than silence.
809
- function renderCoreUpdateBanner(update, held) {
810
- if (!update || update.reading !== 'ok' || !update.behind || !update.latest) return;
811
- // A dismissal is remembered PER VERSION, not forever: the next release is new
812
- // news and must re-announce itself, or the surface is dead after one click.
813
- // localStorage throws in a private window and is absent in the unit tests' bare
814
- // vm context, so every touch is guarded and its loss only costs the memory.
815
- try {
816
- var seen = window.localStorage && window.localStorage.getItem('bongos.coreUpdate.dismissed');
817
- if (seen && seen === update.latest) return;
818
- } catch (e) { /* no storage: always show */ }
819
- var topbar = document.getElementById('app-topbar');
820
- if (!topbar) return;
821
- var el = document.createElement('div');
822
- el.className = 'core-update-notice';
823
- el.id = 'core-update-notice';
824
- el.setAttribute('role', 'status');
825
- // The action goes where the update can actually be taken. The deploy door is the
826
- // provisioning module's, and a hosted project does NOT run that module (probed:
827
- // /api/bongos/provisioning/health is 404 on a tenant, 200 on the platform) — so on
828
- // those it goes to Settings → Software update (SOFTWARE_UPDATE_HREF above).
829
- // NOT A LINK TO THE PAGE YOU ARE ALREADY ON (task 1004145). This reads as the action
830
- // button — it is labelled with the verb and styled like one — but it is a plain
831
- // navigation to /deploy. On /deploy itself that is a no-op, and the owner who
832
- // pressed it there reasonably reported "I clicked update, but I didn't see anything
833
- // happen": nothing had, and nothing had been asked for. The control is on that page
834
- // already, so on it the banner informs and points, and does not pretend to act.
835
- // The same rule on Settings → Software update: that panel IS the control there.
836
- var here = window.location ? String(window.location.pathname || '').replace(/\/+$/, '') : '';
837
- var onDeploy = here === '/deploy';
838
- var onUpdatePanel = here === '/settings' && String(window.location.hash || '') === '#software-update';
839
- var action = moduleOn('provisioning')
840
- ? (onDeploy
841
- ? '<span class="core-update-notice__hint">Use the control below to take it.</span>'
842
- : '<a class="core-update-notice__action" href="/deploy">Upgrade</a>')
843
- : (onUpdatePanel
844
- ? '<span class="core-update-notice__hint">Its details are below.</span>'
845
- : '<a class="core-update-notice__action" href="' + SOFTWARE_UPDATE_HREF + '">Upgrade</a>');
846
- // task 1004730: the newest version already failed here and was rolled back. Say so, never
847
- // "available" with a button to take it again; Software update carries the full sentence.
848
- var hold = held && held.version === update.latest ? held : null;
849
- el.innerHTML = (hold
850
- ? '<span>Core <strong>' + esc(hold.version) + '</strong> was tried on this project and rolled back. It is still on ' +
851
- esc(hold.still_on) + ' and working.</span><a class="core-update-notice__action" href="' + SOFTWARE_UPDATE_HREF + '">Details</a>'
852
- : '<span>Core <strong>' + esc(update.latest) + '</strong> is available — this project runs ' +
853
- esc(update.running || 'an unknown version') + '.</span>' + action) +
854
- '<button type="button" class="core-update-notice__close" aria-label="Dismiss">×</button>';
855
- el.querySelector('.core-update-notice__close').addEventListener('click', function () {
856
- try {
857
- if (window.localStorage) window.localStorage.setItem('bongos.coreUpdate.dismissed', update.latest);
858
- } catch (e) { /* dismissal is best-effort */ }
859
- el.remove();
860
- });
861
- topbar.insertAdjacentElement('afterend', el);
862
- }
863
-
864
816
  // task 1003303: the skip link, rendered once here rather than pasted into 27
865
817
  // heads. The hall had none, which is a platform floor (DESIGN.md
866
818
  // §Accessibility Floors) and, with the sidebar first in the DOM, meant a
@@ -231,7 +231,7 @@
231
231
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
232
232
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
233
233
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
234
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
234
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
235
235
  <script src="/builders/palette.js" defer></script>
236
236
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
237
237
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -219,7 +219,7 @@
219
219
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
220
220
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
221
221
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
222
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
222
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
223
223
  <script src="/builders/palette.js" defer></script>
224
224
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
225
225
  (task 1003829), which the older reviews still show. -->
@@ -1038,54 +1038,134 @@ html[data-theme="dark"] .gplate__light { display: none; }
1038
1038
  line-height: 1;
1039
1039
  cursor: pointer;
1040
1040
  }
1041
- /* Core-update banner (task 1004045) — rendered by shell.js when this instance is
1042
- behind the newest published core. Deliberately the accent, not the warning
1043
- colour the denial notice uses: being a release behind is news, not a fault. */
1044
- .core-update-notice {
1045
- display: flex;
1046
- align-items: baseline;
1047
- gap: 12px;
1048
- padding: 10px 22px;
1049
- font-size: 13px;
1050
- color: var(--accent);
1051
- background: var(--accent-weak, var(--warn-weak));
1052
- border-bottom: 1px solid var(--rule-soft);
1041
+ /* ---------- the Update pill and its card (task 1005041, update-pill.js) ----------
1042
+ The owner: the full-width update banners were "way too obstructing"; it should feel
1043
+ like the Claude app's "restart to update". So the news is a quiet pill in the top bar
1044
+ (the Link GitHub chip's shape, one step louder: the accent dot), and everything else
1045
+ lives in a small card under it. The same card carries the one-time "Bongos is updated"
1046
+ note (core-move-notice.js), pinned to the same corner. */
1047
+ .upd-pill {
1048
+ display: inline-flex;
1049
+ align-items: center;
1050
+ gap: 6px;
1051
+ min-height: 28px;
1052
+ min-width: 28px;
1053
+ padding: 0 11px 0 9px;
1054
+ border: 1px solid var(--rule);
1055
+ border-radius: var(--radius-pill);
1056
+ background: var(--bg-card);
1057
+ color: var(--ink);
1058
+ font: inherit;
1059
+ font-size: 12px;
1060
+ font-weight: 500;
1061
+ line-height: 1;
1062
+ white-space: nowrap;
1063
+ flex-shrink: 0;
1064
+ cursor: pointer;
1053
1065
  }
1054
- .core-update-notice__action {
1055
- margin-left: auto;
1056
- font-weight: 600;
1057
- color: inherit;
1066
+ .upd-pill:hover { border-color: var(--ink-faint); }
1067
+ .upd-pill:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1068
+ .upd-pill__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex-shrink: 0; }
1069
+ .upd-pill[data-face="held"] .upd-pill__dot { background: var(--ink-faint); }
1070
+ .upd-pill[data-face="done"] { background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
1071
+ .upd-pill[data-face="done"] .upd-pill__dot { background: var(--accent-on); }
1072
+ .upd-pill[data-face="done"]:hover { background: var(--accent-lift); border-color: var(--accent-lift); }
1073
+ /* The running face turns a ring. The Kill Switch stops it, and the label still says
1074
+ "Updating…", so the rest frame is the whole message. */
1075
+ .upd-pill__spin {
1076
+ width: 10px; height: 10px;
1077
+ border: 1.5px solid var(--rule);
1078
+ border-top-color: var(--accent);
1079
+ border-radius: 50%;
1080
+ flex-shrink: 0;
1081
+ animation: upd-turn 0.9s linear infinite;
1058
1082
  }
1059
- /* The banner on /deploy itself: it still announces the release, but points at the
1060
- control instead of linking to the page the reader is already on (task 1004145). */
1061
- .core-update-notice__hint {
1062
- margin-left: auto;
1063
- color: var(--ink-soft);
1083
+ @keyframes upd-turn { to { transform: rotate(360deg); } }
1084
+
1085
+ .upd-card {
1086
+ position: fixed;
1087
+ top: 64px;
1088
+ right: 16px;
1089
+ z-index: calc(var(--z-sidebar) + 5);
1090
+ width: min(360px, calc(100vw - 32px));
1091
+ max-height: calc(100vh - 96px);
1092
+ overflow-y: auto;
1093
+ padding: 16px 18px 18px;
1094
+ border: 1px solid var(--rule-soft);
1095
+ border-radius: var(--radius);
1096
+ background: var(--bg-card);
1097
+ color: var(--ink);
1098
+ box-shadow: var(--shadow-pop);
1099
+ font-size: 13px;
1100
+ line-height: 1.5;
1064
1101
  }
1065
- .core-update-notice__close {
1066
- min-width: 24px;
1067
- min-height: 24px;
1102
+ .upd-card[hidden] { display: none; }
1103
+ .upd-card p { margin: 0 0 8px; }
1104
+ .upd-card__h { margin: 0 32px 8px 0; font-size: 15px; font-weight: 600; line-height: 1.3; }
1105
+ .upd-card__close {
1106
+ position: absolute;
1107
+ top: 10px; right: 10px;
1108
+ width: 28px; height: 28px;
1068
1109
  border: 0;
1110
+ border-radius: var(--radius-pill);
1069
1111
  background: none;
1070
- color: inherit;
1071
- font-size: 16px;
1112
+ color: var(--ink-soft);
1113
+ font-size: 18px;
1072
1114
  line-height: 1;
1073
1115
  cursor: pointer;
1074
1116
  }
1075
- /* With no action link the close button still sits hard right. */
1076
- .core-update-notice__action + .core-update-notice__close { margin-left: 0; }
1077
- .core-update-notice > span + .core-update-notice__close { margin-left: auto; }
1078
- /* "Upgraded to core X on <date>" and what changed (task 1004731): the same strip, with the
1079
- release-note lines under the sentence in the body ink; it wraps on a phone. */
1080
- .core-move-notice { flex-wrap: wrap; }
1081
- .core-move-notice__body { flex: 1 1 20rem; min-width: 0; }
1082
- .core-move-notice__list {
1083
- margin: 4px 0 0;
1084
- padding-left: 1.15rem;
1085
- max-width: var(--measure, 68ch);
1086
- color: var(--ink-soft);
1117
+ .upd-card__close:hover { background: var(--glass-strong); color: var(--ink); }
1118
+ .upd-card__list { margin: 0 0 10px; padding-left: 1.1rem; color: var(--ink-soft); }
1119
+ .upd-card__list li + li { margin-top: 4px; }
1120
+ .upd-card__note { color: var(--ink-soft); }
1121
+ .upd-card__ver { color: var(--ink-faint); font-size: 12px; }
1122
+ .upd-card__err { color: var(--danger); }
1123
+ .upd-ver { font-family: var(--font-mono); font-size: 12px; }
1124
+ .upd-card__actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
1125
+ .upd-btn {
1126
+ display: inline-flex;
1127
+ align-items: center;
1128
+ justify-content: center;
1129
+ min-height: 36px;
1130
+ padding: 0 14px;
1131
+ border: 1px solid var(--glass-edge);
1132
+ border-radius: var(--radius-pill);
1133
+ background: transparent;
1134
+ color: var(--ink);
1135
+ font: inherit;
1136
+ font-size: 13px;
1137
+ text-decoration: none;
1138
+ cursor: pointer;
1087
1139
  }
1088
- .core-move-notice__list li + li { margin-top: 2px; }
1140
+ .upd-btn:hover { background: var(--glass-strong); text-decoration: none; }
1141
+ .upd-btn:focus-visible, .upd-card__close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
1142
+ .upd-btn--go { background: var(--accent); border-color: var(--accent); color: var(--accent-on); font-weight: 500; }
1143
+ .upd-btn--go:hover { background: var(--accent-lift); border-color: var(--accent-lift); }
1144
+ .upd-btn:disabled { opacity: 1; color: var(--ink-faint); background: var(--glass); border-color: var(--glass-edge); cursor: default; }
1145
+ /* An indeterminate bar: how long an upgrade takes is not known here, so it never fills. */
1146
+ .upd-track { position: relative; height: 4px; margin: 4px 0 10px; border-radius: 2px; background: var(--glass-strong); overflow: hidden; }
1147
+ .upd-track span {
1148
+ position: absolute; top: 0; bottom: 0; left: 0;
1149
+ width: 40%;
1150
+ border-radius: 2px;
1151
+ background: var(--accent);
1152
+ animation: upd-slide 1.6s ease-in-out infinite;
1153
+ }
1154
+ @keyframes upd-slide { 0% { transform: translateX(-100%); } 100% { transform: translateX(250%); } }
1155
+ /* The one-time note sits under the top bar's right end too, on every page that loads it. */
1156
+ .upd-card--note { top: 64px; }
1157
+ @media (max-width: 640px) {
1158
+ .upd-card { right: 16px; left: 16px; width: auto; }
1159
+ }
1160
+ /* The hub's handoff window (deploy-handoff.js): a small popup with nothing in it but the card.
1161
+ The shell still runs (it signs the viewer in), so its chrome is hidden, not removed. */
1162
+ html[data-handoff="on"] .app-sidebar,
1163
+ html[data-handoff="on"] .app-topbar,
1164
+ html[data-handoff="on"] .app-foot,
1165
+ html[data-handoff="on"] .upd-pill,
1166
+ html[data-handoff="on"] .core-move-notice { display: none; }
1167
+ html[data-handoff="on"] .app-content { padding: 0; }
1168
+ .upd-card--handoff { position: static; width: auto; max-width: 420px; max-height: none; margin: 24px auto; box-shadow: none; }
1089
1169
  .topbar-menu {
1090
1170
  display: none;
1091
1171
  align-items: center;
@@ -59,7 +59,7 @@
59
59
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
62
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
62
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -141,7 +141,7 @@
141
141
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
142
142
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
143
143
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
144
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
144
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
145
145
  <script src="/builders/palette.js" defer></script>
146
146
 
147
147
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -145,7 +145,7 @@
145
145
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
146
146
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
147
147
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
148
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
148
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
149
149
  <script src="/builders/palette.js" defer></script>
150
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
151
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>