@bongos/core 1.19.922 → 1.19.924

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.
@@ -3,9 +3,9 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 1832,
7
- "unique_ids": 1589,
8
- "certain": 1832,
6
+ "strings": 1839,
7
+ "unique_ids": 1596,
8
+ "certain": 1839,
9
9
  "heuristic": 0,
10
10
  "duplicate_ids": 166
11
11
  },
@@ -13,7 +13,7 @@
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
15
  "files": 84,
16
- "strings": 1342
16
+ "strings": 1349
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -1011,8 +1011,8 @@
1011
1011
  "text": "Move to {…}",
1012
1012
  "occurrences": 2,
1013
1013
  "at": [
1014
- "modules/hall-ui/public/deploy.js:122",
1015
- "modules/hall-ui/public/deploy.js:217"
1014
+ "modules/hall-ui/public/deploy.js:218",
1015
+ "modules/hall-ui/public/deploy.js:314"
1016
1016
  ]
1017
1017
  },
1018
1018
  {
@@ -1840,6 +1840,15 @@
1840
1840
  "origin": "js-markup",
1841
1841
  "confidence": "certain"
1842
1842
  },
1843
+ {
1844
+ "id": "af615fa9f404",
1845
+ "surface": "builders-hall",
1846
+ "text": "(checked {…})",
1847
+ "file": "modules/hall-ui/public/deploy.js",
1848
+ "line": 152,
1849
+ "origin": "js-markup",
1850
+ "confidence": "certain"
1851
+ },
1843
1852
  {
1844
1853
  "id": "544794f1ec8f",
1845
1854
  "surface": "builders-hall",
@@ -3154,6 +3163,15 @@
3154
3163
  "origin": "js-markup",
3155
3164
  "confidence": "certain"
3156
3165
  },
3166
+ {
3167
+ "id": "f66f5cdaa732",
3168
+ "surface": "builders-hall",
3169
+ "text": "Checked against {…} — nothing blocks this move.{…}",
3170
+ "file": "modules/hall-ui/public/deploy.js",
3171
+ "line": 155,
3172
+ "origin": "js-markup",
3173
+ "confidence": "certain"
3174
+ },
3157
3175
  {
3158
3176
  "id": "f8725cf3ba62",
3159
3177
  "surface": "builders-hall",
@@ -3172,6 +3190,15 @@
3172
3190
  "origin": "js-text-assign",
3173
3191
  "confidence": "certain"
3174
3192
  },
3193
+ {
3194
+ "id": "69da40d030b0",
3195
+ "surface": "builders-hall",
3196
+ "text": "Checking what this would do…",
3197
+ "file": "modules/hall-ui/public/deploy.js",
3198
+ "line": 145,
3199
+ "origin": "js-markup",
3200
+ "confidence": "certain"
3201
+ },
3175
3202
  {
3176
3203
  "id": "aa3ba7d28a76",
3177
3204
  "surface": "builders-hall",
@@ -3942,7 +3969,7 @@
3942
3969
  "surface": "builders-hall",
3943
3970
  "text": "Could not read this project's deploy state: {…}",
3944
3971
  "file": "modules/hall-ui/public/deploy.js",
3945
- "line": 157,
3972
+ "line": 254,
3946
3973
  "origin": "js-markup",
3947
3974
  "confidence": "certain"
3948
3975
  },
@@ -6588,7 +6615,7 @@
6588
6615
  "surface": "builders-hall",
6589
6616
  "text": "Move to {…}",
6590
6617
  "file": "modules/hall-ui/public/deploy.js",
6591
- "line": 122,
6618
+ "line": 218,
6592
6619
  "origin": "js-markup",
6593
6620
  "confidence": "certain"
6594
6621
  },
@@ -6597,7 +6624,7 @@
6597
6624
  "surface": "builders-hall",
6598
6625
  "text": "Move to {…}",
6599
6626
  "file": "modules/hall-ui/public/deploy.js",
6600
- "line": 217,
6627
+ "line": 314,
6601
6628
  "origin": "js-text-assign",
6602
6629
  "confidence": "certain"
6603
6630
  },
@@ -6921,7 +6948,7 @@
6921
6948
  "surface": "builders-hall",
6922
6949
  "text": "No running box to deploy.",
6923
6950
  "file": "modules/hall-ui/public/deploy.js",
6924
- "line": 195,
6951
+ "line": 292,
6925
6952
  "origin": "js-markup",
6926
6953
  "confidence": "certain"
6927
6954
  },
@@ -7033,6 +7060,15 @@
7033
7060
  "origin": "js-markup",
7034
7061
  "confidence": "certain"
7035
7062
  },
7063
+ {
7064
+ "id": "428a62539050",
7065
+ "surface": "builders-hall",
7066
+ "text": "Not checked yet — the runner takes this reading on its own tick.",
7067
+ "file": "modules/hall-ui/public/deploy.js",
7068
+ "line": 150,
7069
+ "origin": "js-markup",
7070
+ "confidence": "certain"
7071
+ },
7036
7072
  {
7037
7073
  "id": "c72e0bdb834a",
7038
7074
  "surface": "builders-hall",
@@ -7200,7 +7236,7 @@
7200
7236
  "surface": "builders-hall",
7201
7237
  "text": "Nothing newer is published that this box can take. There is nothing to deploy.",
7202
7238
  "file": "modules/hall-ui/public/deploy.js",
7203
- "line": 140,
7239
+ "line": 237,
7204
7240
  "origin": "js-markup",
7205
7241
  "confidence": "certain"
7206
7242
  },
@@ -8775,7 +8811,7 @@
8775
8811
  "surface": "builders-hall",
8776
8812
  "text": "See what would change",
8777
8813
  "file": "modules/hall-ui/public/deploy.js",
8778
- "line": 108,
8814
+ "line": 121,
8779
8815
  "origin": "js-markup",
8780
8816
  "confidence": "certain"
8781
8817
  },
@@ -8847,7 +8883,7 @@
8847
8883
  "surface": "builders-hall",
8848
8884
  "text": "Serving {…} — read from the box itself, {…}.",
8849
8885
  "file": "modules/hall-ui/public/deploy.js",
8850
- "line": 142,
8886
+ "line": 239,
8851
8887
  "origin": "js-markup",
8852
8888
  "confidence": "certain"
8853
8889
  },
@@ -10120,6 +10156,15 @@
10120
10156
  "origin": "html-text",
10121
10157
  "confidence": "certain"
10122
10158
  },
10159
+ {
10160
+ "id": "8c991355687d",
10161
+ "surface": "builders-hall",
10162
+ "text": "The upgrade checked {…} and refused:{…}",
10163
+ "file": "modules/hall-ui/public/deploy.js",
10164
+ "line": 160,
10165
+ "origin": "js-markup",
10166
+ "confidence": "certain"
10167
+ },
10123
10168
  {
10124
10169
  "id": "a42a37c43bfb",
10125
10170
  "surface": "builders-hall",
@@ -10431,7 +10476,7 @@
10431
10476
  "surface": "builders-hall",
10432
10477
  "text": "This platform",
10433
10478
  "file": "modules/hall-ui/public/deploy.js",
10434
- "line": 187,
10479
+ "line": 284,
10435
10480
  "origin": "js-markup",
10436
10481
  "confidence": "certain"
10437
10482
  },
@@ -10440,7 +10485,7 @@
10440
10485
  "surface": "builders-hall",
10441
10486
  "text": "This restarts {…} onto core",
10442
10487
  "file": "modules/hall-ui/public/deploy.js",
10443
- "line": 122,
10488
+ "line": 218,
10444
10489
  "origin": "js-markup",
10445
10490
  "confidence": "certain"
10446
10491
  },
@@ -10480,6 +10525,15 @@
10480
10525
  "origin": "js-markup",
10481
10526
  "confidence": "certain"
10482
10527
  },
10528
+ {
10529
+ "id": "4301ecd67891",
10530
+ "surface": "builders-hall",
10531
+ "text": "This would fail.",
10532
+ "file": "modules/hall-ui/public/deploy.js",
10533
+ "line": 160,
10534
+ "origin": "js-markup",
10535
+ "confidence": "certain"
10536
+ },
10483
10537
  {
10484
10538
  "id": "2ad5823d2cd7",
10485
10539
  "surface": "builders-hall",
@@ -11173,6 +11227,15 @@
11173
11227
  "origin": "js-notice",
11174
11228
  "confidence": "certain"
11175
11229
  },
11230
+ {
11231
+ "id": "921cb15a8188",
11232
+ "surface": "builders-hall",
11233
+ "text": "You can still press Move — the runner will refuse it the same way, and the box is left where it is.",
11234
+ "file": "modules/hall-ui/public/deploy.js",
11235
+ "line": 160,
11236
+ "origin": "js-markup",
11237
+ "confidence": "certain"
11238
+ },
11176
11239
  {
11177
11240
  "id": "b31fc908c962",
11178
11241
  "surface": "builders-hall",
@@ -12168,7 +12231,7 @@
12168
12231
  "surface": "builders-hall",
12169
12232
  "text": "is published and this box is not on it yet.",
12170
12233
  "file": "modules/hall-ui/public/deploy.js",
12171
- "line": 108,
12234
+ "line": 121,
12172
12235
  "origin": "js-markup",
12173
12236
  "confidence": "certain"
12174
12237
  },
@@ -12303,7 +12366,7 @@
12303
12366
  "surface": "builders-hall",
12304
12367
  "text": "no reading",
12305
12368
  "file": "modules/hall-ui/public/deploy.js",
12306
- "line": 91,
12369
+ "line": 104,
12307
12370
  "origin": "js-markup",
12308
12371
  "confidence": "certain"
12309
12372
  },
@@ -12555,7 +12618,7 @@
12555
12618
  "surface": "builders-hall",
12556
12619
  "text": "reads the control plane's own observation · ADR 0293 · every move is recorded with who asked",
12557
12620
  "file": "modules/hall-ui/public/deploy.js",
12558
- "line": 199,
12621
+ "line": 296,
12559
12622
  "origin": "js-markup",
12560
12623
  "confidence": "certain"
12561
12624
  },
@@ -2318,5 +2318,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2318
2318
  landed since 1.19.920 with no explicit bump. run 35534076467. (task 1002620)
2319
2319
  1.19.922 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2320
2320
  landed since 1.19.921 with no explicit bump. run 35536293721. (task 1002620)
2321
+ 1.19.923 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2322
+ landed since 1.19.922 with no explicit bump. run 35541278656. (task 1002620)
2323
+ 1.19.924 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2324
+ landed since 1.19.923 with no explicit bump. run 35541674047. (task 1002620)
2321
2325
  ---------------------------------------------------------------------------
2322
2326
  ```
@@ -21,6 +21,7 @@ const cost = require('./cost');
21
21
  const streak = require('./streak');
22
22
  const ideaSizeLib = require('./idea-size');
23
23
  const ideaSizeHistorical = require('./idea-size-historical');
24
+ const ideaBrightnessLib = require('./idea-brightness');
24
25
 
25
26
  module.exports = {
26
27
  // credit allocation — transaction-participant: caller passes its tx `client`
@@ -110,6 +111,16 @@ module.exports = {
110
111
  // projection and quietly drops a field the pricing needs.
111
112
  descentRowsFromTrail: (...a) => ideaSizeHistorical.descentRowsFromTrail(...a),
112
113
  get IDEA_SIZE_SCALE_REF() { return ideaSizeLib.IDEA_SIZE_SCALE_REF; },
114
+ // BRIGHTNESS — the live share of that descent (task 1004099 / BV2.R16), on the
115
+ // port for the same reason size is. It is the SECOND of the two channels, and
116
+ // the pair is what BV2.R17 resolves the five states from: a thought that
117
+ // earned nothing (`hasWork: false`) and one whose work was all torn out
118
+ // (`share: 0`) are different objects in the sky, and only a caller holding
119
+ // BOTH fields can tell them apart. Shipping size without this left the ideas
120
+ // module able to draw how big a thought was and unable to say whether it was
121
+ // still alive.
122
+ ideaBrightness: (...a) => ideaBrightnessLib.ideaBrightness(...a),
123
+ ideaBrightnesses: (...a) => ideaBrightnessLib.ideaBrightnesses(...a),
113
124
  computeStreak: (...a) => streak.computeStreak(...a),
114
125
  wouldExtendStreak: (...a) => streak.wouldExtendStreak(...a),
115
126
  };
@@ -59,6 +59,6 @@
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-18-deploy2"></script>
62
+ <script src="/builders/deploy.js?v=2026-09-20-preflight2"></script>
63
63
  </body>
64
64
  </html>
@@ -50,6 +50,19 @@
50
50
  // instance_id -> 'preview' | 'confirm'. Held per box so opening the confirm on one does
51
51
  // not arm it on another.
52
52
  const steps = new Map();
53
+ // Rows whose dry run is in flight — per box, like `steps`, because a page-wide flag
54
+ // would spin every other project's row on one project's check.
55
+ const checking = new Set();
56
+ // How long the page waits for the runner to answer. Read off `window` when present so
57
+ // a harness can drive the loop without sleeping through it — a test that injects the
58
+ // real timer spends 30s of wall-clock per non-converging case and keeps repainting a
59
+ // sandbox that has already been torn down.
60
+ // `|| 2000` would be a bug here: an override of 0 — the one a harness wants — is
61
+ // falsy, so the fallback would swallow it and the test would sleep the full interval
62
+ // anyway while appearing to be driven.
63
+ const num = (v, dflt) => (Number.isFinite(v) ? v : dflt);
64
+ const POLL_MS = typeof window !== 'undefined' ? num(window.__deployPollMs, 2000) : 2000;
65
+ const POLL_TRIES = typeof window !== 'undefined' ? num(window.__deployPollTries, 15) : 15;
53
66
 
54
67
  async function fetchJson(path) {
55
68
  const res = await api.request('GET', `${API}${path}`);
@@ -117,10 +130,94 @@
117
130
  // to that button closes over this same preview object — so the owner confirms the thing
118
131
  // they were shown rather than "the newest", which is a different act by the time it is
119
132
  // resolved.
133
+ // What the dry run said, from the runner's own tick (task 1004121). Before this, step
134
+ // one made NO network call — it redrew a fixed sentence and called that a preview. The
135
+ // owner pressed it four times on 2026-09-20, restarted four live boxes, and learned
136
+ // three different pre-flight refusals one production restart at a time. Every one of
137
+ // them was in this verdict already.
138
+ //
139
+ // A REFUSAL DOES NOT DISABLE THE BUTTON. The runner refuses on its own, and this
140
+ // reading can be minutes old; a control that blocked on a stale ✖ would be lying in
141
+ // the other direction. Say what will happen and let the owner decide.
142
+ function preflightHtml(p) {
143
+ const pf = p.preflight;
144
+ if (checking.has(String(p.instance_id)) && !(pf && pf.target === p.recommended_version)) {
145
+ return `<p class="ov-row__sub dp-preflight dp-preflight--checking">Checking what this would do…</p>`;
146
+ }
147
+ // No verdict is its own state, and it is NOT "fine". A silent gap here is how the
148
+ // page used to promise a preview it never performed.
149
+ if (!pf) {
150
+ return `<p class="ov-row__sub dp-preflight dp-preflight--none">Not checked yet — the runner takes this reading on its own tick.</p>`;
151
+ }
152
+ const when = pf && p.preflight_at ? ` <span class="dp-preflight__age">(checked ${escapeHtml(ago(p.preflight_at))})</span>` : '';
153
+ if (pf.ok) {
154
+ const lines = (pf.lines || []).map((l) => `<li>${escapeHtml(String(l))}</li>`).join('');
155
+ return `<div class="dp-preflight dp-preflight--ok">
156
+ <p class="ov-row__sub">Checked against ${escapeHtml(String(pf.target))} — nothing blocks this move.${when}</p>
157
+ ${lines ? `<ul class="dp-preflight__list">${lines}</ul>` : ''}
158
+ </div>`;
159
+ }
160
+ return `<div class="dp-preflight dp-preflight--blocked">
161
+ <p class="ov-row__sub"><strong>This would fail.</strong> The upgrade checked ${escapeHtml(String(pf.target))} and refused:${when}</p>
162
+ <pre class="dp-preflight__error">${escapeHtml(String(pf.error || 'no reason given'))}</pre>
163
+ <p class="ov-row__sub">You can still press Move — the runner will refuse it the same way, and the box is left where it is.</p>
164
+ </div>`;
165
+ }
166
+
167
+ // Arming ASKS for the check (task 1004121). The web tier has no shell — it queues the
168
+ // dry run as an intent and the runner answers, which is the same division of labour as
169
+ // every other fact on this page. Then it re-reads the preview until the verdict lands.
170
+ //
171
+ // Only when there isn't already a fresh one: a verdict for THIS target is the answer,
172
+ // and re-asking would spend a subprocess to reprint it.
173
+ const asked = new Set();
174
+ async function askPreflight(p) {
175
+ const id = String(p.instance_id);
176
+ if (p.preflight && p.preflight.target === p.recommended_version) return;
177
+ if (asked.has(id)) return;
178
+ asked.add(id);
179
+ checking.add(id);
180
+ repaint();
181
+ try {
182
+ await sendJson('POST', `/provisioning/instances/${id}/core-upgrade`, {
183
+ to: p.recommended_version, dry_run: true,
184
+ });
185
+ } catch (e) {
186
+ if (e && e.kind === '401') return;
187
+ // A refused REQUEST is not a refused upgrade, and must not be painted as one:
188
+ // fall back to "not checked", which is honest about what is known.
189
+ checking.delete(id); asked.delete(id); repaint();
190
+ return;
191
+ }
192
+ // The runner drains on its own tick, so this waits rather than assuming. Bounded:
193
+ // a check that never lands leaves the row saying it was never checked, which is
194
+ // true, instead of spinning for ever.
195
+ //
196
+ // AND IT STOPS WHEN NOBODY IS LOOKING. The loop is started from a click and not
197
+ // awaited by it, so without this it outlives whatever closed the row — cancelling,
198
+ // confirming, or a reload — and goes on repainting a step the owner has left. It
199
+ // also made every test that never converges sit through the full ~30s in real time.
200
+ for (let i = 0; i < POLL_TRIES; i++) {
201
+ await new Promise((r) => setTimeout(r, POLL_MS));
202
+ if (steps.get(p.instance_id) !== 'confirm') break; // the row was closed; stop.
203
+ try {
204
+ await refreshOne(p, { keepStep: true });
205
+ const now = (view.self && String(view.self.instance_id) === id)
206
+ ? view.self
207
+ : view.boxes.find((b) => String(b.instance_id) === id);
208
+ if (now && now.preflight && now.preflight.target === now.recommended_version) break;
209
+ } catch (e) { break; }
210
+ }
211
+ checking.delete(id);
212
+ asked.delete(id);
213
+ repaint();
214
+ }
215
+
120
216
  function stepTwoHtml(p, isSelf) {
121
217
  const id = escapeHtml(String(p.instance_id));
122
218
  return `
123
219
  <div class="ps-controls">
220
+ ${preflightHtml(p)}
124
221
  <p class="ov-row__sub">This restarts ${isSelf ? 'the platform' : escapeHtml(String(p.slug))} onto core
125
222
  <strong>${escapeHtml(String(p.recommended_version))}</strong>. It waits for the box to answer and
126
223
  puts itself back on ${escapeHtml(String(p.served_version))} if it does not.</p>
@@ -166,7 +263,7 @@
166
263
  function wire(p) {
167
264
  const id = String(p.instance_id);
168
265
  const preview = $(`dp-preview-${id}`);
169
- if (preview) preview.addEventListener('click', () => { steps.set(p.instance_id, 'confirm'); repaint(); });
266
+ if (preview) preview.addEventListener('click', () => { steps.set(p.instance_id, 'confirm'); repaint(); askPreflight(p); });
170
267
  const cancel = $(`dp-cancel-${id}`);
171
268
  if (cancel) cancel.addEventListener('click', () => { steps.set(p.instance_id, 'preview'); repaint(); });
172
269
  const confirm = $(`dp-confirm-${id}`);
@@ -223,14 +320,17 @@
223
320
  // far wider than the change — it also re-probes boxes the owner never touched. A
224
321
  // failure here falls back to the full read, because a stale row on a deploy page is
225
322
  // the one thing worse than an extra request.
226
- async function refreshOne(p) {
323
+ // `keepStep` is for the pre-flight poll (task 1004121): a refresh AFTER a move should
324
+ // return the row to step one, but a refresh DURING the wait must not — it would close
325
+ // the panel the owner is reading, under them, every two seconds.
326
+ async function refreshOne(p, { keepStep = false } = {}) {
227
327
  const id = String(p.instance_id);
228
328
  const isSelf = view.self && String(view.self.instance_id) === id;
229
329
  try {
230
330
  const d = await fetchJson(isSelf ? '/provisioning/control-plane' : `/provisioning/instances/${id}/core-upgrade`);
231
331
  if (isSelf) view.self = d.preview;
232
332
  else view.boxes = view.boxes.map((b) => (String(b.instance_id) === id ? d.preview : b));
233
- steps.set(p.instance_id, 'preview');
333
+ if (!keepStep) steps.set(p.instance_id, 'preview');
234
334
  repaint();
235
335
  } catch (e) {
236
336
  if (e && e.kind === '401') return;
@@ -88,3 +88,42 @@
88
88
  color: var(--ink-soft);
89
89
  }
90
90
  .ps-sibling strong { color: inherit; font-weight: 600; }
91
+
92
+ /* The deploy control's pre-flight verdict (task 1004121) — what the upgrade says it
93
+ would do, taken by the runner's dry run. Three states, and "not checked" is one of
94
+ them: the page used to show a confident sentence where it had performed no check at
95
+ all, so silence here must look like silence, not like a pass. */
96
+ .dp-preflight { margin: 0 0 0.6rem; }
97
+ .dp-preflight--none,
98
+ .dp-preflight--checking { color: var(--ink-soft); font-style: italic; }
99
+ /* The pass state earns a mark of its own. Without one, "nothing blocks this" and "not
100
+ checked" differ only in wording, and the three-state legibility this file claims is
101
+ two states and a sentence. */
102
+ .dp-preflight--ok {
103
+ border-left: 3px solid var(--ok);
104
+ padding-left: 0.7rem;
105
+ }
106
+ .dp-preflight__age { color: var(--ink-soft); font-weight: normal; }
107
+ .dp-preflight__list {
108
+ margin: 0.25rem 0 0;
109
+ padding-left: 1.1rem;
110
+ color: var(--ink-soft);
111
+ font-size: 0.86rem;
112
+ line-height: 1.5;
113
+ }
114
+ .dp-preflight--blocked {
115
+ border-left: 3px solid var(--danger);
116
+ padding-left: 0.7rem;
117
+ }
118
+ /* The refusal is upgrade.js's own words, multi-line and already indented to name the
119
+ fix — preserved rather than reflowed, because the wording IS the instruction. */
120
+ .dp-preflight__error {
121
+ margin: 0.35rem 0;
122
+ padding: 0.5rem 0.6rem;
123
+ background: var(--surface-2);
124
+ border-radius: 6px;
125
+ font-size: 0.82rem;
126
+ line-height: 1.45;
127
+ white-space: pre-wrap;
128
+ overflow-x: auto;
129
+ }