@bongos/core 1.19.951 → 1.19.953
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.
- package/.bongos-core.json +67 -62
- package/docs/copy-inventory.md +32 -24
- package/docs/copy-registry.json +100 -28
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +3 -3
- package/modules/hall-ui/public/deploy.js +97 -5
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/people.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.css +16 -0
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +12 -3
- package/modules/hall-ui/public/sky.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +6 -0
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/provisioning/core-upgrade.js +35 -3
- package/modules/provisioning/migrations/provisioning_022_last_core_move_index.sql +25 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/adopt-preflight.js +1 -1
- package/scripts/gds/device-flow-help.js +1 -1
- package/scripts/gds/init.js +5 -3
- package/scripts/gds/login.js +1 -1
- package/scripts/gds/setup.js +1 -1
- package/src/module-api.js +1 -1
- package/tests/adopt_preflight.mjs +1 -1
- package/tests/bongos_login.mjs +1 -1
- package/tests/core_update_banner.mjs +30 -2
- package/tests/core_upgrade_door.mjs +50 -0
- package/tests/deploy_page_projects.mjs +131 -7
- package/tests/discord_approvals.mjs +1 -1
- package/tests/module_api.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
|
|
178
178
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
179
179
|
|
|
180
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
180
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
181
181
|
<script src="/builders/palette.js" defer></script>
|
|
182
182
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
183
183
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-
|
|
11
|
+
<link rel="stylesheet" href="/builders/style.css?v=2026-09-22-movefeedback1">
|
|
12
12
|
<link rel="stylesheet" href="/builders/oversight.css">
|
|
13
13
|
<link rel="stylesheet" href="/builders/project-settings.css">
|
|
14
14
|
</head>
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
|
|
56
56
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
57
57
|
|
|
58
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
58
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
59
59
|
<script src="/builders/palette.js" defer></script>
|
|
60
60
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
61
61
|
<script src="/builders/hall-kit.js"></script>
|
|
62
|
-
<script src="/builders/deploy.js?v=2026-09-
|
|
62
|
+
<script src="/builders/deploy.js?v=2026-09-22-movefeedback1"></script>
|
|
63
63
|
</body>
|
|
64
64
|
</html>
|
|
@@ -63,6 +63,15 @@
|
|
|
63
63
|
const num = (v, dflt) => (Number.isFinite(v) ? v : dflt);
|
|
64
64
|
const POLL_MS = typeof window !== 'undefined' ? num(window.__deployPollMs, 2000) : 2000;
|
|
65
65
|
const POLL_TRIES = typeof window !== 'undefined' ? num(window.__deployPollTries, 15) : 15;
|
|
66
|
+
// A MOVE NEEDS A LONGER LEASH THAN A PREVIEW (task 1004145). The intent waits for the
|
|
67
|
+
// runner's 2-minute tick, and then the upgrade itself installs, migrates, restarts and
|
|
68
|
+
// health-checks — so the preview's 30s ceiling would time out on every healthy deploy
|
|
69
|
+
// and report "still running" as if something were wrong. 5 minutes covers the worst
|
|
70
|
+
// case with the tick included.
|
|
71
|
+
const MOVE_TRIES = typeof window !== 'undefined' ? num(window.__deployMoveTries, 150) : 150;
|
|
72
|
+
// Boxes with a move in flight, and what each was asked to move TO — so the row can say
|
|
73
|
+
// which version it is waiting for, and the settled check knows what "worked" means.
|
|
74
|
+
const moving = new Map();
|
|
66
75
|
|
|
67
76
|
async function fetchJson(path) {
|
|
68
77
|
const res = await api.request('GET', `${API}${path}`);
|
|
@@ -109,12 +118,54 @@
|
|
|
109
118
|
// Something is already running. The press is not offered at all — offering a button
|
|
110
119
|
// that the server will refuse with 'intent_conflict' is a worse experience than saying
|
|
111
120
|
// plainly that the box is busy.
|
|
121
|
+
// NOT "this page will notice when it does" any more (task 1004145). It said that and
|
|
122
|
+
// did not: only a move STARTED from this page polls, and even that stops at its bound.
|
|
123
|
+
// This branch is what an owner sees for someone else's press, or for their own after a
|
|
124
|
+
// reload, and neither is being watched — so it says what is true instead.
|
|
112
125
|
function busyHtml(p) {
|
|
113
126
|
const i = p.open_intent;
|
|
114
127
|
return `
|
|
115
128
|
<p class="ov-row__sub">A <strong>${escapeHtml(i.action)}</strong> is ${escapeHtml(i.state)} for this box${
|
|
116
129
|
i.target_version ? ` (to core ${escapeHtml(i.target_version)})` : ''}.
|
|
117
|
-
Nothing else can be queued until it settles
|
|
130
|
+
Nothing else can be queued until it settles. Reload to see where it got to.</p>`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// WHAT THE OWNER SEES WHILE IT RUNS. busyHtml already covered "something is running",
|
|
134
|
+
// but only when the server still reports an open intent — and it offered no version,
|
|
135
|
+
// no elapsed sense, and nothing when the intent had settled. This one is driven by the
|
|
136
|
+
// press itself, so it is on screen from the click rather than from the next poll.
|
|
137
|
+
function movingHtml(p) {
|
|
138
|
+
const to = moving.get(p.instance_id);
|
|
139
|
+
const state = (p.open_intent && p.open_intent.state) || 'queued';
|
|
140
|
+
return `
|
|
141
|
+
<p class="ov-row__sub dp-moving">Moving to core <strong>${escapeHtml(String(to))}</strong> — ${escapeHtml(state)}.
|
|
142
|
+
The project restarts as part of this, and puts itself back on
|
|
143
|
+
${escapeHtml(String(p.served_version))} if the new core does not answer.
|
|
144
|
+
This page is watching and will say when it lands.</p>`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// AND WHAT IT SEES AFTERWARDS. Three outcomes, and the failure is the one that had no
|
|
148
|
+
// surface at all before this: an errored intent is not an OPEN intent, so the payload
|
|
149
|
+
// after a rolled-back upgrade used to be identical to the payload before it.
|
|
150
|
+
function settledHtml(p) {
|
|
151
|
+
const m = p.last_move;
|
|
152
|
+
if (!m) return '';
|
|
153
|
+
if (m.state === 'error') {
|
|
154
|
+
return `
|
|
155
|
+
<div class="dp-settled dp-settled--failed">
|
|
156
|
+
<p class="ov-row__sub"><strong>The move to core ${escapeHtml(String(m.target_version))} did not land</strong>
|
|
157
|
+
— this project was put back on ${escapeHtml(String(p.served_version))} and is running.</p>
|
|
158
|
+
${m.error ? `<pre class="dp-preflight__error">${escapeHtml(String(m.error))}</pre>` : ''}
|
|
159
|
+
</div>`;
|
|
160
|
+
}
|
|
161
|
+
if (m.state === 'done' && m.target_version === p.served_version) {
|
|
162
|
+
return `
|
|
163
|
+
<div class="dp-settled dp-settled--ok">
|
|
164
|
+
<p class="ov-row__sub"><strong>Now running core ${escapeHtml(String(m.target_version))}</strong>
|
|
165
|
+
— moved ${escapeHtml(ago(m.at))}, and the box confirmed it.</p>
|
|
166
|
+
</div>`;
|
|
167
|
+
}
|
|
168
|
+
return '';
|
|
118
169
|
}
|
|
119
170
|
|
|
120
171
|
function stepOneHtml(p) {
|
|
@@ -232,10 +283,15 @@
|
|
|
232
283
|
function boxHtml(p, isSelf) {
|
|
233
284
|
const step = steps.get(p.instance_id) || 'preview';
|
|
234
285
|
let action;
|
|
235
|
-
|
|
286
|
+
// The owner's own press wins over every other state: it is true from the click,
|
|
287
|
+
// before the server has an intent to report, and it must not be replaced by
|
|
288
|
+
// "nothing to deploy" the moment served_version catches up mid-poll.
|
|
289
|
+
if (moving.has(p.instance_id)) action = movingHtml(p);
|
|
290
|
+
else if (p.open_intent) action = busyHtml(p);
|
|
236
291
|
else if (p.reading === 'no_answer') action = '';
|
|
237
292
|
else if (!p.behind) action = `<p class="ov-row__sub">Nothing newer is published that this box can take. There is nothing to deploy.</p>`;
|
|
238
293
|
else action = step === 'confirm' ? stepTwoHtml(p, isSelf) : stepOneHtml(p);
|
|
294
|
+
if (!moving.has(p.instance_id)) action += settledHtml(p);
|
|
239
295
|
return `
|
|
240
296
|
<li class="ov-row" id="dp-box-${escapeHtml(String(p.instance_id))}"><div class="ov-row__main">
|
|
241
297
|
<span class="ov-row__title">${escapeHtml(String(p.slug))}${isSelf ? ' — the box this platform runs on' : ''}</span>
|
|
@@ -302,17 +358,53 @@
|
|
|
302
358
|
async function move(p) {
|
|
303
359
|
const btn = $(`dp-confirm-${p.instance_id}`);
|
|
304
360
|
if (btn) { btn.disabled = true; btn.textContent = 'Queueing…'; }
|
|
361
|
+
const id = String(p.instance_id);
|
|
362
|
+
const to = p.recommended_version;
|
|
305
363
|
try {
|
|
306
364
|
// `p.recommended_version` — the version this owner was SHOWN for THIS box, not
|
|
307
365
|
// whatever is newest at the moment of the click.
|
|
308
|
-
const r = await sendJson('POST', `/provisioning/instances/${p.instance_id}/core-upgrade`, { to
|
|
366
|
+
const r = await sendJson('POST', `/provisioning/instances/${p.instance_id}/core-upgrade`, { to });
|
|
309
367
|
toast(r.message || 'Queued.');
|
|
310
|
-
await refreshOne(p);
|
|
311
368
|
} catch (e) {
|
|
312
369
|
if (e && e.kind === '401') return;
|
|
313
370
|
toast(e.message || 'Could not queue the move.');
|
|
314
|
-
if (btn) { btn.disabled = false; btn.textContent = `Move to ${
|
|
371
|
+
if (btn) { btn.disabled = false; btn.textContent = `Move to ${to}`; }
|
|
372
|
+
return; // nothing was queued, so there is nothing to watch
|
|
315
373
|
}
|
|
374
|
+
// WATCH IT (task 1004145). busyHtml has always promised "this page will notice when
|
|
375
|
+
// it does", and until now it did not: move() refreshed ONCE, immediately — before
|
|
376
|
+
// the runner's tick had even fired — and then fell silent for the whole upgrade. The
|
|
377
|
+
// owner got a toast, a row that snapped back to its pre-press state, and nothing
|
|
378
|
+
// else, which is indistinguishable from the press having been lost.
|
|
379
|
+
moving.set(p.instance_id, to);
|
|
380
|
+
steps.set(p.instance_id, 'preview'); // the two-step is spent; the row now reports
|
|
381
|
+
repaint();
|
|
382
|
+
let settled = false;
|
|
383
|
+
for (let i = 0; i < MOVE_TRIES; i++) {
|
|
384
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
385
|
+
if (moving.get(p.instance_id) !== to) return; // superseded or the row is gone
|
|
386
|
+
let now;
|
|
387
|
+
try {
|
|
388
|
+
// keepStep, for the reason the pre-flight poll documents: a refresh that resets
|
|
389
|
+
// the row would fight the state this loop is painting, every interval.
|
|
390
|
+
await refreshOne(p, { keepStep: true });
|
|
391
|
+
now = (view.self && String(view.self.instance_id) === id)
|
|
392
|
+
? view.self
|
|
393
|
+
: view.boxes.find((b) => String(b.instance_id) === id);
|
|
394
|
+
} catch (e) { break; }
|
|
395
|
+
if (!now) break;
|
|
396
|
+
// Settled two ways, and BOTH need the intent to be closed: served_version can
|
|
397
|
+
// read the new core while the runner is still committing the pin, and calling
|
|
398
|
+
// that "done" would drop the row out of its in-flight state early.
|
|
399
|
+
if (!now.open_intent && now.served_version === to) { settled = true; break; }
|
|
400
|
+
if (!now.open_intent && now.last_move && now.last_move.state === 'error') { settled = true; break; }
|
|
401
|
+
}
|
|
402
|
+
moving.delete(p.instance_id);
|
|
403
|
+
repaint();
|
|
404
|
+
// A poll that ran out is NOT a failure, and must not be painted as one. The upgrade
|
|
405
|
+
// is still the runner's to finish; saying so beats going quiet, which is the exact
|
|
406
|
+
// complaint this task came from.
|
|
407
|
+
if (!settled) toast(`Still working on core ${to}. It keeps going without this page — reload to see where it got to.`);
|
|
316
408
|
}
|
|
317
409
|
|
|
318
410
|
// Re-read ONLY the box that just changed. The full load() fans out one probe per
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
|
|
98
98
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
99
99
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
100
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
100
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></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>
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
|
|
99
99
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
100
100
|
|
|
101
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
101
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
102
102
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
103
103
|
<script src="/builders/palette.js" defer></script>
|
|
104
104
|
<script src="/builders/md-reader.js?v=2026-09-02-mdreader-2"></script>
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<div id="gate-modal-root"></div>
|
|
77
77
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
78
78
|
|
|
79
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
79
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
80
80
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
81
81
|
<script src="/builders/palette.js" defer></script>
|
|
82
82
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
|
|
57
57
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
58
58
|
|
|
59
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
59
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
60
60
|
<script src="/builders/palette.js" defer></script>
|
|
61
61
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
62
62
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -223,7 +223,7 @@
|
|
|
223
223
|
|
|
224
224
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
225
225
|
|
|
226
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
226
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
227
227
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
228
228
|
<script src="/builders/palette.js" defer></script>
|
|
229
229
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
|
|
135
135
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
136
136
|
|
|
137
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
137
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
138
138
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
139
139
|
<script src="/builders/palette.js" defer></script>
|
|
140
140
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
54
54
|
|
|
55
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
55
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
56
56
|
<script src="/builders/palette.js" defer></script>
|
|
57
57
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib" defer></script>
|
|
58
58
|
<!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
|
|
@@ -199,7 +199,7 @@
|
|
|
199
199
|
|
|
200
200
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
201
201
|
|
|
202
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
202
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
203
203
|
<script src="/builders/palette.js" defer></script>
|
|
204
204
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
205
205
|
<!-- task 1003069 (R08) — the quick-filing form's routed result links a task
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
<script src="/builders/brand-apply.js?v=2026-06-27-r75" defer></script>
|
|
103
103
|
<!-- The app shell (sidebar + top bar + view router). Dependency-free; loaded
|
|
104
104
|
BEFORE builders.js so the chrome ids exist when events are wired. -->
|
|
105
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
105
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1" defer></script>
|
|
106
106
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
107
107
|
<script src="/builders/palette.js" defer></script>
|
|
108
108
|
<!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
82
82
|
|
|
83
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
83
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
84
84
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
85
85
|
<script src="/builders/palette.js" defer></script>
|
|
86
86
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
|
|
56
56
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
57
57
|
chrome exists before the page script runs. -->
|
|
58
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
58
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
59
59
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
60
60
|
<script src="/builders/palette.js" defer></script>
|
|
61
61
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
|
|
116
116
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
117
117
|
|
|
118
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
118
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
119
119
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
120
120
|
<script src="/builders/palette.js" defer></script>
|
|
121
121
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
|
|
188
188
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
189
189
|
chrome exists before the page script runs. -->
|
|
190
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
190
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
191
191
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
192
192
|
<script src="/builders/palette.js" defer></script>
|
|
193
193
|
<!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
|
|
@@ -127,3 +127,19 @@
|
|
|
127
127
|
white-space: pre-wrap;
|
|
128
128
|
overflow-x: auto;
|
|
129
129
|
}
|
|
130
|
+
|
|
131
|
+
/* A move in flight, and how it ended (task 1004145). The owner pressed Upgrade and got
|
|
132
|
+
a toast and then silence; these are the two states that silence was hiding. */
|
|
133
|
+
.dp-moving {
|
|
134
|
+
border-left: 3px solid var(--accent);
|
|
135
|
+
padding-left: 0.7rem;
|
|
136
|
+
}
|
|
137
|
+
.dp-settled { margin: 0.4rem 0 0; }
|
|
138
|
+
.dp-settled--ok {
|
|
139
|
+
border-left: 3px solid var(--ok);
|
|
140
|
+
padding-left: 0.7rem;
|
|
141
|
+
}
|
|
142
|
+
.dp-settled--failed {
|
|
143
|
+
border-left: 3px solid var(--danger);
|
|
144
|
+
padding-left: 0.7rem;
|
|
145
|
+
}
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
|
|
59
59
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
60
60
|
|
|
61
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
61
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
62
62
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
63
63
|
<script src="/builders/palette.js" defer></script>
|
|
64
64
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
70
70
|
chrome exists before the page script runs. -->
|
|
71
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
71
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
72
72
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
73
73
|
<script src="/builders/palette.js" defer></script>
|
|
74
74
|
<!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
43
43
|
|
|
44
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
44
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
45
45
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
46
46
|
<script src="/builders/palette.js" defer></script>
|
|
47
47
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
52
52
|
chrome exists before the page script runs. -->
|
|
53
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
53
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
54
54
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
55
55
|
<script src="/builders/palette.js" defer></script>
|
|
56
56
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
|
|
100
100
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
101
101
|
chrome exists before the page script runs. -->
|
|
102
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
102
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
103
103
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
104
104
|
<script src="/builders/palette.js" defer></script>
|
|
105
105
|
<!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
|
|
@@ -416,7 +416,7 @@
|
|
|
416
416
|
|
|
417
417
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
418
418
|
chrome exists before the page scripts run. -->
|
|
419
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
419
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
420
420
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
421
421
|
<script src="/builders/palette.js" defer></script>
|
|
422
422
|
<!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
|
|
@@ -637,9 +637,18 @@
|
|
|
637
637
|
// (probed: /api/bongos/provisioning/health is 404 on a tenant, 200 on the
|
|
638
638
|
// platform) — so on those the banner informs and does not offer a button that
|
|
639
639
|
// would 404. Task 1004046 is what gives those halls a real action.
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
640
|
+
// NOT A LINK TO THE PAGE YOU ARE ALREADY ON (task 1004145). This reads as the action
|
|
641
|
+
// button — it is labelled with the verb and styled like one — but it is a plain
|
|
642
|
+
// navigation to /deploy. On /deploy itself that is a no-op, and the owner who
|
|
643
|
+
// pressed it there reasonably reported "I clicked update, but I didn't see anything
|
|
644
|
+
// happen": nothing had, and nothing had been asked for. The control is on that page
|
|
645
|
+
// already, so on it the banner informs and points, and does not pretend to act.
|
|
646
|
+
var onDeploy = window.location && String(window.location.pathname || '').replace(/\/+$/, '') === '/deploy';
|
|
647
|
+
var action = !moduleOn('provisioning')
|
|
648
|
+
? ''
|
|
649
|
+
: (onDeploy
|
|
650
|
+
? '<span class="core-update-notice__hint">Use the control below to take it.</span>'
|
|
651
|
+
: '<a class="core-update-notice__action" href="/deploy">Upgrade</a>');
|
|
643
652
|
el.innerHTML =
|
|
644
653
|
'<span>Core <strong>' + esc(update.latest) + '</strong> is available — this project runs ' +
|
|
645
654
|
esc(update.running || 'an unknown version') + '.</span>' +
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
|
|
66
66
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
67
67
|
|
|
68
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
68
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
69
69
|
<script src="/builders/palette.js" defer></script>
|
|
70
70
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
71
71
|
<!-- The SAME shared vocabulary the room loads, at the same version: the server
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
68
68
|
|
|
69
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
69
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
70
70
|
<script src="/builders/palette.js" defer></script>
|
|
71
71
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
72
72
|
<!-- The kit, for its ONE renderer: the full-size ship-time visual figure
|
|
@@ -897,6 +897,12 @@ main:focus { outline: none; }
|
|
|
897
897
|
font-weight: 600;
|
|
898
898
|
color: inherit;
|
|
899
899
|
}
|
|
900
|
+
/* The banner on /deploy itself: it still announces the release, but points at the
|
|
901
|
+
control instead of linking to the page the reader is already on (task 1004145). */
|
|
902
|
+
.core-update-notice__hint {
|
|
903
|
+
margin-left: auto;
|
|
904
|
+
color: var(--ink-soft);
|
|
905
|
+
}
|
|
900
906
|
.core-update-notice__close {
|
|
901
907
|
min-width: 24px;
|
|
902
908
|
min-height: 24px;
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
59
59
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
60
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
60
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
61
61
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
62
62
|
<script src="/builders/palette.js" defer></script>
|
|
63
63
|
<!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
|
|
63
63
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
64
64
|
|
|
65
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
65
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
66
66
|
<script src="/builders/palette.js" defer></script>
|
|
67
67
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
68
68
|
<!-- The filter strip is the kit's tab strip (makeTabs) rather than a fifth
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
|
|
163
163
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
164
164
|
chrome exists before the page script runs. -->
|
|
165
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
165
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
166
166
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
167
167
|
<script src="/builders/palette.js" defer></script>
|
|
168
168
|
<!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
|
|
152
152
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
153
153
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
154
|
-
<script src="/builders/shell.js?v=2026-09-
|
|
154
|
+
<script src="/builders/shell.js?v=2026-09-22-movefeedback1"></script>
|
|
155
155
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
156
156
|
<script src="/builders/palette.js" defer></script>
|
|
157
157
|
<!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
|
|
@@ -239,7 +239,7 @@ function makeCoreUpgradeHandler({ provisioning, pool, parseId, validateOrRespond
|
|
|
239
239
|
* empty one — a control that renders a cache as though it were live is how someone is
|
|
240
240
|
* told their box is on a version it left an hour ago.
|
|
241
241
|
*/
|
|
242
|
-
function previewFromSnapshot(inst, snap, openIntent) {
|
|
242
|
+
function previewFromSnapshot(inst, snap, openIntent, lastMove) {
|
|
243
243
|
const served = (snap && snap.served_version) || null;
|
|
244
244
|
const pf = (snap && snap.preflight) || null;
|
|
245
245
|
const available = (snap && Array.isArray(snap.available) ? snap.available : []);
|
|
@@ -266,13 +266,45 @@ function previewFromSnapshot(inst, snap, openIntent) {
|
|
|
266
266
|
preflight: pf && recommended && pf.target === recommended ? pf : null,
|
|
267
267
|
preflight_at: pf && recommended && pf.target === recommended ? (snap.preflight_at || null) : null,
|
|
268
268
|
open_intent: openIntent ? { action: openIntent.action, state: openIntent.state, target_version: openIntent.target_version || null } : null,
|
|
269
|
+
// THE LAST MOVE, WHATEVER BECAME OF IT (task 1004145).
|
|
270
|
+
//
|
|
271
|
+
// `open_intent` is `state IN ('pending','running')` — by the index's own definition.
|
|
272
|
+
// So an intent that reaches `error` stops being open, and until this field existed
|
|
273
|
+
// the payload after a FAILED upgrade was byte-identical to the payload before it:
|
|
274
|
+
// same served_version, open_intent null, no error anywhere. Intents 77/78/79 on
|
|
275
|
+
// 2026-09-20 all ended "failed and was rolled back" and the page could not say so;
|
|
276
|
+
// the owner's only signal was a number that never changed, which is indistinguishable
|
|
277
|
+
// from never having pressed the button. It is also what lets a SUCCESS be attributed:
|
|
278
|
+
// "the version is different now" and "what you asked for worked" are not the same
|
|
279
|
+
// sentence, and only one of them answers the press.
|
|
280
|
+
last_move: lastMove ? {
|
|
281
|
+
target_version: lastMove.target_version || null,
|
|
282
|
+
state: lastMove.state,
|
|
283
|
+
at: lastMove.resolved_at || lastMove.updated_at || null,
|
|
284
|
+
// Truncated because it reaches a DOM and upgrade.js's refusals can carry a whole
|
|
285
|
+
// `git status` (the dirty-tree one lists every file).
|
|
286
|
+
error: lastMove.last_error ? String(lastMove.last_error).slice(0, 600) : null,
|
|
287
|
+
} : null,
|
|
269
288
|
};
|
|
270
289
|
}
|
|
271
290
|
|
|
291
|
+
// The newest core-pin MOVE for this box, in any terminal or live state. Deliberately
|
|
292
|
+
// not `getOpenIntent`, which filters to pending|running, and deliberately not widened to
|
|
293
|
+
// `core-upgrade-preflight` — a preview already reports itself through `preflight`, and
|
|
294
|
+
// folding the two together would let a preview's refusal read as a failed deploy.
|
|
295
|
+
async function lastCoreMove(pool, instanceId) {
|
|
296
|
+
const { rows } = await pool.query(
|
|
297
|
+
`SELECT * FROM provisioning_intents
|
|
298
|
+
WHERE instance_id = $1 AND action = 'core-upgrade'
|
|
299
|
+
ORDER BY created_at DESC, id DESC LIMIT 1`,
|
|
300
|
+
[instanceId]);
|
|
301
|
+
return rows[0] || null;
|
|
302
|
+
}
|
|
303
|
+
|
|
272
304
|
async function loadPreview(pool, provisioning, inst) {
|
|
273
305
|
const { rows } = await pool.query('SELECT * FROM provisioning_core_version_snapshot WHERE instance_id = $1', [inst.id]);
|
|
274
306
|
const openIntent = provisioning.getOpenIntent ? await provisioning.getOpenIntent(pool, inst.id) : null;
|
|
275
|
-
return previewFromSnapshot(inst, rows[0], openIntent);
|
|
307
|
+
return previewFromSnapshot(inst, rows[0], openIntent, await lastCoreMove(pool, inst.id));
|
|
276
308
|
}
|
|
277
309
|
|
|
278
310
|
function makeCoreUpgradePreviewHandler({ provisioning, pool, parseId, log }) {
|
|
@@ -321,5 +353,5 @@ function makeControlPlaneHandler({ provisioning, pool, log }) {
|
|
|
321
353
|
};
|
|
322
354
|
}
|
|
323
355
|
|
|
324
|
-
module.exports = { CORE_UPGRADE_FROM, UPGRADE_SHAPES, VERSION_RE, isCoreVersion, coreUpgradeFault, enqueueCoreUpgrade, makeCoreUpgradeHandler, previewFromSnapshot, makeCoreUpgradePreviewHandler, makeControlPlaneHandler };
|
|
356
|
+
module.exports = { CORE_UPGRADE_FROM, lastCoreMove, UPGRADE_SHAPES, VERSION_RE, isCoreVersion, coreUpgradeFault, enqueueCoreUpgrade, makeCoreUpgradeHandler, previewFromSnapshot, makeCoreUpgradePreviewHandler, makeControlPlaneHandler };
|
|
325
357
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- provisioning_022 — an index for "the last core move on this box" (task 1004145).
|
|
2
|
+
--
|
|
3
|
+
-- Numbered 022, not 021: this branch and the byo-host shape change (task 1004141) each
|
|
4
|
+
-- allocated max+1 from their own branch point, and 1004141 landed first. migrate.sh
|
|
5
|
+
-- applies in BASENAME order, so two 021s would have their order decided alphabetically
|
|
6
|
+
-- and a fresh replay could differ from the order prod applied. The one that has NOT been
|
|
7
|
+
-- applied anywhere is the one that renumbers — renaming an applied migration re-runs it.
|
|
8
|
+
--
|
|
9
|
+
-- lastCoreMove() reads:
|
|
10
|
+
-- WHERE instance_id = $1 AND action = 'core-upgrade' ORDER BY created_at DESC LIMIT 1
|
|
11
|
+
--
|
|
12
|
+
-- and neither existing index serves it. `ux_provisioning_intents_open` is partial on
|
|
13
|
+
-- state IN ('pending','running') — this query is ABOUT the terminal rows it excludes —
|
|
14
|
+
-- and `idx_provisioning_intents_pending` is created_at under the same partial predicate.
|
|
15
|
+
-- So the read was a filter-and-sort over every intent for the instance.
|
|
16
|
+
--
|
|
17
|
+
-- It runs on EVERY preview of every box, including the fleet fan-out on /deploy, and
|
|
18
|
+
-- provisioning_intents only grows: one row per press, retained as the audit trail
|
|
19
|
+
-- (ADR 0293 D5). Small today at fewer than a hundred rows, so this is a cheap fix now
|
|
20
|
+
-- rather than a puzzling page later.
|
|
21
|
+
--
|
|
22
|
+
-- DESC on created_at so the LIMIT 1 is the leading edge; id is the tiebreak for two
|
|
23
|
+
-- presses inside the same clock tick, which a test-driven double-press can produce.
|
|
24
|
+
CREATE INDEX IF NOT EXISTS idx_provisioning_intents_last_action
|
|
25
|
+
ON provisioning_intents (instance_id, action, created_at DESC, id DESC);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.953",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.953",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.953",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|