@bongos/core 1.24.26 → 1.24.28

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 (109) hide show
  1. package/.bongos-core.json +164 -109
  2. package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +12 -0
  3. package/docs/copy-inventory.md +242 -223
  4. package/docs/copy-registry.json +559 -358
  5. package/docs/module-api-changelog.md +4 -0
  6. package/docs/page-inventory.json +18 -6
  7. package/docs/page-readings.json +4399 -4315
  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.css +72 -1
  39. package/modules/hall-ui/public/my-agents.html +31 -7
  40. package/modules/hall-ui/public/my-agents.js +63 -54
  41. package/modules/hall-ui/public/my-agents.states.json +18 -0
  42. package/modules/hall-ui/public/primer.html +1 -1
  43. package/modules/hall-ui/public/profile.html +1 -1
  44. package/modules/hall-ui/public/project-settings.html +1 -1
  45. package/modules/hall-ui/public/ranks.html +1 -1
  46. package/modules/hall-ui/public/roadmap.html +1 -1
  47. package/modules/hall-ui/public/roster.html +1 -1
  48. package/modules/hall-ui/public/sessions.html +1 -1
  49. package/modules/hall-ui/public/settings-software-update.js +22 -8
  50. package/modules/hall-ui/public/settings.css +2 -132
  51. package/modules/hall-ui/public/settings.html +15 -53
  52. package/modules/hall-ui/public/shell.js +25 -73
  53. package/modules/hall-ui/public/speciality-tools.css +140 -0
  54. package/modules/hall-ui/public/{settings-specialities.js → speciality-tools.js} +83 -6
  55. package/modules/hall-ui/public/street.html +1 -1
  56. package/modules/hall-ui/public/studio.html +1 -1
  57. package/modules/hall-ui/public/style.css +119 -39
  58. package/modules/hall-ui/public/task.html +1 -1
  59. package/modules/hall-ui/public/thinking.html +1 -1
  60. package/modules/hall-ui/public/tweak-editor.html +1 -1
  61. package/modules/hall-ui/public/update-pill.js +623 -0
  62. package/modules/hall-ui/public/watch.html +1 -1
  63. package/modules/hall-ui/public/work.html +1 -1
  64. package/modules/hall-ui/records/my-agents.md +4 -2
  65. package/modules/hall-ui/records/panel-family.md +2 -0
  66. package/modules/hall-ui/records/update-pill.md +12 -0
  67. package/modules/lifecycle/github-push.js +1 -0
  68. package/modules/lifecycle/routes/tasks.js +2 -2
  69. package/modules/lifecycle/runner-self-guard.js +122 -0
  70. package/modules/lifecycle/runner-self.js +154 -0
  71. package/modules/public-landing/public/account.html +3 -3
  72. package/modules/public-landing/public/assets/cosmos.css +45 -0
  73. package/modules/public-landing/public/community.html +4 -4
  74. package/modules/public-landing/public/connections.html +1 -1
  75. package/modules/public-landing/public/guild.html +1 -1
  76. package/modules/public-landing/public/invitations.html +4 -4
  77. package/modules/public-landing/public/projects.html +26 -26
  78. package/modules/status-ui/public/index.html +2 -2
  79. package/modules/status-ui/public/index.states.json +12 -0
  80. package/modules/status-ui/public/status.js +10 -2
  81. package/modules/status-ui/public/style.css +57 -0
  82. package/modules/ui-design/kit/README.md +1 -1
  83. package/modules/ui-design/kit/serve.js +8 -1
  84. package/package-lock.json +2 -2
  85. package/package.json +1 -1
  86. package/release-notes.json +20 -0
  87. package/scripts/bongos/autobongos-loop.js +4 -0
  88. package/scripts/bongos/autobongos-run.js +21 -4
  89. package/scripts/bongos/autobongos-tweak-lane.js +5 -3
  90. package/scripts/bongos/ship-flow.js +20 -0
  91. package/scripts/hall-preview/server.js +4 -0
  92. package/src/module-api.js +1 -1
  93. package/tests/autobongos_host_access.mjs +5 -3
  94. package/tests/autobongos_self_modification.mjs +420 -0
  95. package/tests/core_update_banner.mjs +6 -92
  96. package/tests/core_upgrade_owner_door.mjs +4 -56
  97. package/tests/deploy_handoff.mjs +206 -0
  98. package/tests/government_protected_surfaces.mjs +15 -0
  99. package/tests/hall_loading.mjs +3 -1
  100. package/tests/hall_my_agents.mjs +104 -2
  101. package/tests/helpers.mjs +161 -0
  102. package/tests/hub_connections_page.mjs +1 -1
  103. package/tests/hub_invitations_page.mjs +1 -1
  104. package/tests/loading_mark.mjs +203 -0
  105. package/tests/nav_permission_atoms.mjs +3 -2
  106. package/tests/software_update.mjs +6 -2
  107. package/tests/speciality_offer_ui.mjs +182 -149
  108. package/tests/speciality_walkthrough_ui.mjs +180 -161
  109. package/tests/update_pill.mjs +431 -0
@@ -21,6 +21,7 @@ SURFACE=hall-ui PREFIX=/builders/ node modules/ui-design/kit/serve.js
21
21
  CHILD=1 node modules/ui-design/kit/serve.js # the child-instance shape (apex-only regions stripped)
22
22
  LIVE=1 node modules/ui-design/kit/serve.js # public feeds proxied to the pack's public origin
23
23
  FAIL=featured,progress node modules/ui-design/kit/serve.js # those feeds answer 500
24
+ LATENCY=4000 node modules/ui-design/kit/serve.js # every /api/ read held 4s, to look at a page's waiting state (the loader; task 1005053)
24
25
  STYLE=grove node modules/ui-design/kit/serve.js # under a look from the style library (modules/ui-design/styles/; task 1003323)
25
26
  ```
26
27
 
@@ -48,6 +48,7 @@ const REFUSALS = {
48
48
  no_allowlist: 'no goal is allowlisted, so there is nothing this runner may work on',
49
49
  goal_not_allowlisted: 'that goal is not on the allowlist',
50
50
  protected_path: 'the task declares work on a permission, migration, infrastructure or deploy-pipeline surface',
51
+ self_modification: "the task declares work on the runner's own supervisor, safety checks or kill switch",
51
52
  needs_host_access: 'the work needs shell access to a live host (SSH, root, systemd) that the runner machine does not have',
52
53
  grader_unavailable: 'the grader is bypassed, and it is the only review between an unattended worker and main',
53
54
  not_permitted: 'your rank does not hold autonomy.run, so your runner may not work on this project',
@@ -332,7 +333,13 @@ function needsHostAccess(task) {
332
333
  // in core and a module reaches core only through the doorway. Passing the hits in
333
334
  // also means the caller decides WHAT to match, which is what lets a future caller
334
335
  // run the same decision over a real diff instead of a declaration.
335
- function decideTask({ task, goals = [], protectedHits = [] } = {}) {
336
+ //
337
+ // `selfHits` is the same idea for the runner's OWN files (task 1004546): the output
338
+ // of modules/lifecycle/runner-self.js matchRunnerSelf over the same touches[]. The
339
+ // owner's rule is that the runner never edits its own supervisor, safety checks or
340
+ // kill switch, and none of those are on the ADR 0043 registry. Declared touches are
341
+ // a claim, so the ship-time check and the server's publish check read the real diff.
342
+ function decideTask({ task, goals = [], protectedHits = [], selfHits = [] } = {}) {
336
343
  if (!task || task.id === undefined || task.id === null) {
337
344
  return { go: false, code: 'fence_unreadable', reason: 'no task to decide about' };
338
345
  }
@@ -351,6 +358,14 @@ function decideTask({ task, goals = [], protectedHits = [] } = {}) {
351
358
  globs,
352
359
  };
353
360
  }
361
+ if (Array.isArray(selfHits) && selfHits.length) {
362
+ const files = [...new Set(selfHits.map((h) => h && h.file).filter(Boolean))];
363
+ return {
364
+ go: false, code: 'self_modification',
365
+ reason: `${REFUSALS.self_modification}${files.length ? ` (${files.join(', ')})` : ''}`,
366
+ files,
367
+ };
368
+ }
354
369
  const host = needsHostAccess(task);
355
370
  if (host.needs) {
356
371
  return {
@@ -109,6 +109,8 @@
109
109
  { "glob": "modules/lifecycle/github-push.js", "group": "pipeline", "why": "BV1.R86: the server-mediated branch publish (ADR 0055)" },
110
110
  { "glob": "modules/lifecycle/conflict-resolve.js", "group": "pipeline", "why": "BV1.R86: self-heals generated-file conflicts at land time" },
111
111
  { "glob": "modules/lifecycle/publish-reconciler.js", "group": "pipeline", "why": "BV1.R86: lands confirmed merges" },
112
+ { "glob": "modules/lifecycle/runner-self.js", "group": "pipeline", "why": "task 1004546: the one list of files an Autobongos runner may never change, and the shape of a runner's claim" },
113
+ { "glob": "modules/lifecycle/runner-self-guard.js", "group": "pipeline", "why": "task 1004546: the server refuses to publish a runner's change to its own supervisor, safety checks or kill switch" },
112
114
  { "glob": "migrations/", "group": "schema", "why": "schema migrations are never newcomer work" },
113
115
  { "glob": "infra/", "group": "substrate", "why": "the deploy substrate" },
114
116
  { "glob": ".github/", "group": "substrate", "why": "CI workflows (deploy-prod.yml, grade gate, secrets-scan) are the pipeline" },
@@ -89,7 +89,7 @@
89
89
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
90
90
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
91
91
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
92
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
92
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
93
93
  <script src="/builders/palette.js" defer></script>
94
94
  <script src="/builders/tweak-editor-lib.js?v=2026-09-28-tw23"></script>
95
95
  <script src="/builders/studio-lib.js?v=2026-10-09-day"></script>
@@ -92,7 +92,7 @@
92
92
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
93
93
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
94
94
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
95
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
95
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
96
96
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
97
97
  <script src="/builders/palette.js" defer></script>
98
98
  <script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
@@ -61,7 +61,7 @@
61
61
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
62
62
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
63
63
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
64
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
64
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
65
65
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
66
66
  <script src="/builders/palette.js" defer></script>
67
67
  <!-- the branding pack's currency label, loaded BEFORE blockers.js. -->
@@ -63,7 +63,7 @@
63
63
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
64
64
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
65
65
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
66
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
66
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
67
67
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
68
68
  <script src="/builders/palette.js" defer></script>
69
69
  <script src="/builders/hall-kit.js"></script>
@@ -108,7 +108,7 @@
108
108
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
109
109
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab" defer></script>
110
110
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch" defer></script>
111
- <script src="/builders/shell.js?v=2026-10-30-github-chip" defer></script>
111
+ <script src="/builders/shell.js?v=2026-11-03-update-pill" defer></script>
112
112
  <script src="/builders/palette.js" defer></script>
113
113
  <script src="/builders/gov-living.js?v=2026-10-09-positions1" defer></script>
114
114
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3" defer></script>
@@ -162,7 +162,7 @@
162
162
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
163
163
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
164
164
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
165
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
165
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
166
166
  <script src="/builders/palette.js" defer></script>
167
167
  <script src="/builders/collab-lib.js?v=2026-09-24-archive"></script>
168
168
  <script src="/builders/collab.js?v=2026-09-24-archive"></script>
@@ -54,7 +54,7 @@
54
54
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
55
55
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
56
56
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
57
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
57
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
58
58
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
59
59
  <script src="/builders/palette.js" defer></script>
60
60
  <script src="/builders/hall-kit.js"></script>
@@ -180,7 +180,7 @@
180
180
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
181
181
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
182
182
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
183
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
183
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
184
184
  <script src="/builders/palette.js" defer></script>
185
185
  <script src="/builders/hall-kit.js"></script>
186
186
  <script src="/builders/copy-desk.js?v=2026-09-01-redesign"></script>
@@ -32,19 +32,22 @@
32
32
  }
33
33
  var topbar = document.getElementById('app-topbar');
34
34
  if (!topbar) return;
35
+ // A small card in the top-right corner, the Update pill's own (task 1005041): the owner
36
+ // called the full-width strips "way too obstructing". It still sits after the top bar in
37
+ // the document, so a screen reader meets it where the strip was.
35
38
  var el = document.createElement('div');
36
- el.className = 'core-update-notice core-move-notice';
39
+ el.className = 'upd-card upd-card--note core-move-notice';
37
40
  el.id = 'core-move-notice';
38
41
  el.setAttribute('role', 'status');
39
42
  var entries = Array.isArray(moved.entries) ? moved.entries : [];
40
43
  var list = entries.length
41
- ? '<ul class="core-move-notice__list">' + entries.map(function (e) { return '<li>' + esc(e && e.text) + '</li>'; }).join('') +
44
+ ? '<ul class="upd-card__list core-move-notice__list">' + entries.map(function (e) { return '<li>' + esc(e && e.text) + '</li>'; }).join('') +
42
45
  (moved.more > 0 ? '<li>…and ' + esc(String(moved.more)) + ' more.</li>' : '') + '</ul>'
43
46
  : '';
44
- el.innerHTML = '<div class="core-move-notice__body"><span>' + esc(moved.message) +
45
- (entries.length ? ' What changed:' : '') + '</span>' + list + '</div>' +
46
- '<a class="core-update-notice__action core-move-notice__more" href="' + esc((opts && opts.href) || '/settings#software-update') + '">Update history</a>' +
47
- '<button type="button" class="core-update-notice__close core-move-notice__close" aria-label="Dismiss">×</button>';
47
+ el.innerHTML = '<button type="button" class="upd-card__close core-move-notice__close" aria-label="Dismiss">×</button>' +
48
+ '<h2 class="upd-card__h">Bongos is updated</h2>' +
49
+ '<p>' + esc(moved.message) + (entries.length ? ' What changed:' : '') + '</p>' + list +
50
+ '<div class="upd-card__actions"><a class="upd-btn core-move-notice__more" href="' + esc((opts && opts.href) || '/settings#software-update') + '">Update history</a></div>';
48
51
  function markSeen() {
49
52
  try {
50
53
  if (window.localStorage) window.localStorage.setItem(SEEN_KEY, String(moved.id));
@@ -0,0 +1,203 @@
1
+ // deploy-handoff.js — the hub's half of the in-place update (task 1005041).
2
+ //
3
+ // A hosted project's owner presses Update now in their own hall (update-pill.js). The act
4
+ // has to happen HERE, on the hub, with the owner's hub session: the project's own server
5
+ // may never move its own pin (ADR 0293 D1, ADR 0339 D3). So the hall opens this page in a
6
+ // small window as /deploy?project=<slug>&handoff=1, and the two windows say four things:
7
+ // hub → hall { tag, step: 'ready' } "I am loaded; which version?"
8
+ // hall → hub { tag, step: 'go', to } the version the owner was shown
9
+ // hub → hall { tag, step: 'started', to } the move is queued; the window closes
10
+ // hub → hall { tag, step: 'refused', message } the server's own sentence
11
+ //
12
+ // WHAT IS CHECKED BEFORE ANYTHING MOVES (judgeGo). The message must come from the window
13
+ // that opened this one, from that project's own registered address (provisioning's
14
+ // `domain`, which is where its hall is served), and name exactly the version this hub's
15
+ // own preview would move it to. Anything else moves nothing: a page that opens this
16
+ // window from elsewhere is talking to a window that will not listen, and a version this
17
+ // hub would not offer is shown to the owner instead of sent. The move itself is the same
18
+ // POST the deploy door makes, gated server-side exactly as before (owner or core.pin.move).
19
+ //
20
+ // ONE PRESS, NEVER ZERO, WHEN IT CANNOT HEAR. If no word comes from the hall (a browser
21
+ // that cuts the tie between the two windows, or this page opened by hand), it shows one
22
+ // button naming the version, the door's own confirm. It never moves on load by itself.
23
+ //
24
+ // Loaded by deploy.html; deploy.js hands over to run() when ?handoff=1 is present.
25
+ // Node-require-safe, so the tests run judgeGo and projectOrigin without a page.
26
+ (function (root) {
27
+ 'use strict';
28
+
29
+ var MSG = 'bongos-update'; // update-pill.js's tag
30
+ var VERSION_RE = /^\d+\.\d+\.\d+$/;
31
+ var HOST_RE = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+(?::\d{1,5})?$/;
32
+ var API = '/api/bongos';
33
+ // How long the window waits for the hall to say which version, before it offers the press.
34
+ var HEAR_MS = typeof root !== 'undefined' && root && typeof root.__handoffHearMs === 'number' ? root.__handoffHearMs : 5000;
35
+
36
+ /** Is this page the handoff window? Read defensively, as deploy.js reads its scope. */
37
+ function wanted(loc) {
38
+ try { return /[?&]handoff=1(?:&|$)/.test(String((loc && loc.search) || '')); } catch (e) { return false; }
39
+ }
40
+
41
+ /** The one origin allowed to ask for this project's move: its registered address, on the
42
+ * scheme this hub is served on (https in production; http only on a local harness). */
43
+ function projectOrigin(inst, protocol) {
44
+ var host = inst && typeof inst.domain === 'string' ? inst.domain.trim().toLowerCase() : '';
45
+ if (!host || !HOST_RE.test(host)) return null;
46
+ return (protocol === 'http:' ? 'http://' : 'https://') + host;
47
+ }
48
+
49
+ /** Why a message may NOT start the move, or null when it may. Each refusal names itself. */
50
+ function judgeGo(o) {
51
+ var data = o && o.data;
52
+ if (!data || data.tag !== MSG || data.step !== 'go') return 'not_go';
53
+ if (!o.opener || o.source !== o.opener) return 'not_opener';
54
+ if (!o.allowed || o.origin !== o.allowed) return 'wrong_origin';
55
+ var p = o.preview;
56
+ if (!p || p.reading !== 'ok' || !p.behind || !VERSION_RE.test(String(p.recommended_version || ''))) return 'nothing_to_move';
57
+ if (p.viewer_may_move === false) return 'not_allowed';
58
+ if (typeof data.to !== 'string' || data.to !== p.recommended_version) return 'wrong_version';
59
+ if (p.open_intent) return 'busy';
60
+ return null;
61
+ }
62
+
63
+ // ---------- the page ----------
64
+
65
+ function esc(s) { return root.OTB.escapeHtml(String(s == null ? '' : s)); }
66
+
67
+ function run(opts) {
68
+ var d = root.document;
69
+ var api = opts.api;
70
+ var main = d.getElementById('hall-main');
71
+ if (!main) return;
72
+ d.documentElement.setAttribute('data-handoff', 'on');
73
+ main.innerHTML = '<section class="upd-card upd-card--handoff" aria-labelledby="ho-h"><div id="ho-body"><p class="upd-card__note" role="status">Opening this project\'s update…</p></div></section>';
74
+ var body = d.getElementById('ho-body');
75
+ var slug = scopeSlug(root.location);
76
+ var opener = null;
77
+ try { opener = root.opener || null; } catch (e) { opener = null; }
78
+ var inst = null;
79
+ var preview = null;
80
+ var allowed = null;
81
+ var heard = false;
82
+ var moving = false;
83
+
84
+ function draw(html) { body.innerHTML = html; }
85
+ function title(t) { return '<h1 class="upd-card__h" id="ho-h">' + esc(t) + '</h1>'; }
86
+ function tell(msg) {
87
+ if (!opener || !allowed) return;
88
+ try { opener.postMessage(Object.assign({ tag: MSG }, msg), allowed); } catch (e) { /* the hall watches its own version anyway */ }
89
+ }
90
+ function refuse(message) {
91
+ tell({ step: 'refused', message: message });
92
+ draw(title('The update did not start') + '<p class="upd-card__err" role="alert">' + esc(message) + '</p>' +
93
+ '<div class="upd-card__actions"><button type="button" class="upd-btn" id="ho-close">Close</button></div>');
94
+ wireClose();
95
+ }
96
+ function wireClose() {
97
+ var c = d.getElementById('ho-close');
98
+ if (c) c.addEventListener('click', function () { root.close(); });
99
+ }
100
+
101
+ // The one press, naming the version THIS hub would move to: the door's own confirm.
102
+ function offerPress(note) {
103
+ if (moving) return;
104
+ var to = preview.recommended_version;
105
+ draw(title('Update ' + (slug || 'this project')) +
106
+ (note ? '<p class="upd-card__note">' + esc(note) + '</p>' : '') +
107
+ '<p>Moves it to version <span class="upd-ver">' + esc(to) + '</span>. It restarts once, and goes back to the version it runs now on its own if the new one is not healthy.</p>' +
108
+ '<div class="upd-card__actions"><button type="button" class="upd-btn upd-btn--go" id="ho-go">Update to ' + esc(to) + '</button>' +
109
+ '<button type="button" class="upd-btn" id="ho-close">Cancel</button></div>');
110
+ d.getElementById('ho-go').addEventListener('click', function () { move(to); });
111
+ wireClose();
112
+ }
113
+
114
+ function move(to) {
115
+ if (moving) return;
116
+ moving = true;
117
+ draw(title('Starting the update') + '<p class="upd-card__note" role="status">Asking the server to move ' + esc(slug) + ' to ' + esc(to) + '…</p>');
118
+ api.request('POST', API + '/provisioning/instances/' + encodeURIComponent(inst.id) + '/core-upgrade', { body: { to: to } }).then(function (res) {
119
+ if (res && res.status === 401) { signIn(); return; }
120
+ if (!res || !res.ok) {
121
+ moving = false;
122
+ var err = (res && res.data && res.data.error) || {};
123
+ refuse(err.message && err.message !== err.code ? err.message : 'The server refused the update (' + (res ? res.status : 'no answer') + ').');
124
+ return;
125
+ }
126
+ tell({ step: 'started', to: to });
127
+ draw(title('The update has started') +
128
+ '<p role="status">You can close this window. ' + (opener ? 'Your project\'s hall shows how it is going.' : 'Open your project\'s hall to see how it is going.') + '</p>' +
129
+ '<div class="upd-card__actions"><button type="button" class="upd-btn" id="ho-close">Close</button></div>');
130
+ wireClose();
131
+ if (opener) root.setTimeout(function () { root.close(); }, 1200);
132
+ }, function () { moving = false; refuse('The server did not answer. Nothing was changed.'); });
133
+ }
134
+
135
+ function onMessage(e) {
136
+ if (!e || !e.data || e.data.tag !== MSG) return;
137
+ var why = judgeGo({ data: e.data, source: e.source, opener: opener, origin: e.origin, allowed: allowed, preview: preview });
138
+ if (why === 'not_go' || why === 'not_opener' || why === 'wrong_origin') return; // not ours to answer
139
+ heard = true;
140
+ if (!why) { move(e.data.to); return; }
141
+ if (why === 'wrong_version') { offerPress('A different version is ready than the one your hall showed. Check it before updating.'); return; }
142
+ if (why === 'busy') { refuse('Another change is already running for this project. Try again once it settles.'); return; }
143
+ if (why === 'not_allowed') { refuse('Only the project\'s owner can update it.'); return; }
144
+ refuse('This project already runs the newest version it can take.');
145
+ }
146
+
147
+ function signIn() {
148
+ var back = String(root.location.pathname || '/deploy') + String(root.location.search || '');
149
+ root.location.assign(API + '/auth/web/start?return=' + encodeURIComponent(back));
150
+ }
151
+
152
+ function read(path) {
153
+ return api.request('GET', API + path).then(function (res) {
154
+ if (res && res.status === 401) { signIn(); throw { kind: '401' }; }
155
+ if (!res || !res.ok) {
156
+ var err = (res && res.data && res.data.error) || {};
157
+ var e = new Error(err.message && err.message !== err.code ? err.message : (err.code || path + ' → ' + (res && res.status)));
158
+ e.status = res && res.status;
159
+ throw e;
160
+ }
161
+ return res.data;
162
+ });
163
+ }
164
+
165
+ // The project by name: the whole roster where the viewer may read it, else their own
166
+ // projects (deploy.js findBySlug's order). A lookup, never a listing.
167
+ read('/provisioning/fleet').then(function (d) { return d.instances || []; }, function (e) {
168
+ if (e && e.status === 403) return read('/provisioning/instances').then(function (d) { return d.instances || []; });
169
+ throw e;
170
+ }).then(function (rows) {
171
+ for (var i = 0; i < rows.length; i++) if (rows[i] && String(rows[i].slug) === slug) inst = rows[i];
172
+ if (!inst) throw new Error('No project called ' + slug + ' is hosted here under your account.');
173
+ allowed = projectOrigin(inst, root.location.protocol);
174
+ return read('/provisioning/instances/' + encodeURIComponent(inst.id) + '/core-upgrade');
175
+ }).then(function (d) {
176
+ preview = d.preview || null;
177
+ if (!preview || preview.reading !== 'ok' || !preview.behind) {
178
+ refuse(preview && preview.reading === 'ok' ? 'This project already runs the newest version it can take.' : 'This project\'s version could not be read just now. Nothing was changed.');
179
+ return;
180
+ }
181
+ if (preview.open_intent) { refuse('Another change is already running for this project. Try again once it settles.'); return; }
182
+ if (!opener || !allowed) { offerPress(); return; }
183
+ root.addEventListener('message', onMessage);
184
+ tell({ step: 'ready' });
185
+ draw(title('Updating ' + slug) + '<p class="upd-card__note" role="status">Waiting for your hall…</p>');
186
+ root.setTimeout(function () { if (!heard && !moving) offerPress(); }, HEAR_MS);
187
+ }).then(null, function (e) {
188
+ if (e && e.kind === '401') return;
189
+ refuse(String((e && e.message) || 'This project\'s update could not be opened.'));
190
+ });
191
+ }
192
+
193
+ function scopeSlug(loc) {
194
+ try {
195
+ var m = /[?&]project=([^&#]+)/.exec(String((loc && loc.search) || ''));
196
+ return m ? decodeURIComponent(m[1]) : null;
197
+ } catch (e) { return null; }
198
+ }
199
+
200
+ var api = { wanted: wanted, run: run, judgeGo: judgeGo, projectOrigin: projectOrigin, MSG: MSG };
201
+ if (typeof module !== 'undefined' && module.exports) module.exports = api;
202
+ if (root) root.OTBDeployHandoff = api;
203
+ })(typeof window !== 'undefined' ? window : null);
@@ -81,11 +81,13 @@
81
81
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
82
82
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
83
83
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
84
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
84
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
85
85
  <script src="/builders/palette.js" defer></script>
86
86
  <script src="/builders/hall-kit.js"></script>
87
87
  <!-- the shared ask card the move is drawn with (task 1004829) -->
88
88
  <script src="/builders/hall-ask.js"></script>
89
- <script src="/builders/deploy.js?v=2026-09-23-changelist1"></script>
89
+ <!-- the in-place update window a hosted project's hall opens (task 1005041) -->
90
+ <script src="/builders/deploy-handoff.js?v=2026-11-03-handoff"></script>
91
+ <script src="/builders/deploy.js?v=2026-11-03-handoff"></script>
90
92
  </body>
91
93
  </html>
@@ -906,5 +906,11 @@
906
906
  }
907
907
  }
908
908
 
909
- document.addEventListener('DOMContentLoaded', () => { load(); loadModuleSections(); });
909
+ document.addEventListener('DOMContentLoaded', () => {
910
+ // task 1005041: the small window a hosted project's hall opens to update in place. It is
911
+ // this door, asked from the project's own site, and deploy-handoff.js draws it instead.
912
+ const handoff = window.OTBDeployHandoff;
913
+ if (handoff && handoff.wanted(window.location)) { handoff.run({ api }); return; }
914
+ load(); loadModuleSections();
915
+ });
910
916
  })();
@@ -97,7 +97,7 @@
97
97
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
98
98
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
99
99
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
100
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
100
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
101
101
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
102
102
  <script src="/builders/palette.js" defer></script>
103
103
  <script src="/builders/diagram-viewer.js?v=2026-06-14-diagviewer-1" defer></script>
@@ -97,7 +97,7 @@
97
97
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
98
98
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
99
99
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
100
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
100
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
101
101
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
102
102
  <script src="/builders/palette.js" defer></script>
103
103
  <script src="/builders/md-reader.js?v=2026-09-02-mdreader-2"></script>
@@ -60,7 +60,7 @@
60
60
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
61
61
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
62
62
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
63
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
63
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <script src="/builders/hall-kit.js"></script>
66
66
  <script src="/builders/fleet-costs.js"></script>
@@ -67,7 +67,7 @@
67
67
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
68
68
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
69
69
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
70
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
70
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
71
71
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
72
72
  <script src="/builders/palette.js" defer></script>
73
73
  <script src="/builders/hall-kit.js"></script>
@@ -140,7 +140,7 @@
140
140
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
141
141
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
142
142
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
143
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
143
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
144
144
  <script src="/builders/palette.js" defer></script>
145
145
 
146
146
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -244,7 +244,7 @@
244
244
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
245
245
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
246
246
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
247
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
247
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
248
248
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
249
249
  <script src="/builders/palette.js" defer></script>
250
250
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -157,7 +157,7 @@
157
157
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
158
158
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
159
159
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
160
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
160
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
161
161
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
162
162
  <script src="/builders/palette.js" defer></script>
163
163
  <script src="/builders/hall-kit.js"></script>
@@ -53,7 +53,7 @@
53
53
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
54
54
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
55
55
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
56
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
56
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
57
57
  <script src="/builders/palette.js" defer></script>
58
58
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
59
59
  written inside an idea's own prose keeps resolving from this page. -->
@@ -228,7 +228,7 @@
228
228
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
229
229
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
230
230
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
231
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
231
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
232
232
  <script src="/builders/palette.js" defer></script>
233
233
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
234
234
  <script src="/builders/vote-control.js"></script>
@@ -121,7 +121,7 @@
121
121
  <script src="/builders/welcome-boot.js?v=2026-10-23-city1" defer></script>
122
122
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab" defer></script>
123
123
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch" defer></script>
124
- <script src="/builders/shell.js?v=2026-10-30-github-chip" defer></script>
124
+ <script src="/builders/shell.js?v=2026-11-03-update-pill" defer></script>
125
125
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
126
126
  <script src="/builders/palette.js" defer></script>
127
127
  <!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
@@ -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-09-living3"></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
  <script src="/builders/palette.js" defer></script>
79
79
  <script src="/builders/hall-kit.js"></script>
80
80
  <script src="/builders/market-watch-lib.js"></script>
@@ -43,7 +43,7 @@
43
43
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
44
44
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
45
45
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
46
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
46
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
47
47
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
48
48
  <script src="/builders/palette.js" defer></script>
49
49
  <script src="/builders/hall-kit.js"></script>
@@ -107,7 +107,7 @@
107
107
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
108
108
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
109
109
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
110
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
110
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
111
111
  <script src="/builders/palette.js" defer></script>
112
112
  <script src="/builders/hall-kit.js"></script>
113
113
  <script src="/builders/market-watch-settings.js"></script>
@@ -102,7 +102,7 @@
102
102
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
103
103
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
104
104
  <script src="/builders/craft-mode.js?v=2026-10-09-living3"></script>
105
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
105
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
106
106
  <script src="/builders/palette.js" defer></script>
107
107
  <script src="/builders/hall-kit.js"></script>
108
108
  <script src="/builders/market-watch-lib.js"></script>
@@ -128,7 +128,7 @@
128
128
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
129
129
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
130
130
  <script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
131
- <script src="/builders/shell.js?v=2026-10-30-github-chip"></script>
131
+ <script src="/builders/shell.js?v=2026-11-03-update-pill"></script>
132
132
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
133
133
  <script src="/builders/palette.js" defer></script>
134
134
  <script src="/builders/brand-apply.js"></script>