@bongos/core 1.24.27 → 1.24.29

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 (41) hide show
  1. package/.bongos-core.json +53 -58
  2. package/.claude/skills/builder-claim/SKILL.md +1 -1
  3. package/.claude/skills/builder-ship/SKILL.md +1 -1
  4. package/docs/copy-inventory.md +173 -203
  5. package/docs/copy-registry.json +350 -622
  6. package/docs/module-api-changelog.md +4 -0
  7. package/docs/page-inventory.json +8 -8
  8. package/docs/page-readings.json +346 -331
  9. package/modules/builder-settings/interaction-prefs.js +2 -2
  10. package/modules/hall-ui/public/my-agents.css +72 -1
  11. package/modules/hall-ui/public/my-agents.html +30 -6
  12. package/modules/hall-ui/public/my-agents.js +98 -56
  13. package/modules/hall-ui/public/my-agents.states.json +18 -0
  14. package/modules/hall-ui/public/palette.js +8 -0
  15. package/modules/hall-ui/public/settings-moved.js +41 -0
  16. package/modules/hall-ui/public/settings.css +4 -158
  17. package/modules/hall-ui/public/settings.html +19 -147
  18. package/modules/hall-ui/public/settings.js +20 -215
  19. package/modules/hall-ui/public/settings.states.json +2 -4
  20. package/modules/hall-ui/public/speciality-tools.css +140 -0
  21. package/modules/hall-ui/public/{settings-specialities.js → speciality-tools.js} +83 -6
  22. package/modules/hall-ui/records/my-agents.md +5 -2
  23. package/modules/hall-ui/records/panel-family.md +4 -0
  24. package/modules/lifecycle/ship-card.js +2 -2
  25. package/package-lock.json +2 -2
  26. package/package.json +1 -1
  27. package/release-notes.json +12 -0
  28. package/scripts/hall-preview/server.js +4 -0
  29. package/src/module-api.js +1 -1
  30. package/tests/government_tidy_up.mjs +21 -24
  31. package/tests/hall_loading.mjs +3 -1
  32. package/tests/hall_my_agents.mjs +190 -2
  33. package/tests/hall_palette.mjs +12 -0
  34. package/tests/hall_settings_world.mjs +64 -100
  35. package/tests/helpers.mjs +162 -0
  36. package/tests/interaction_posture.mjs +3 -1
  37. package/tests/speciality_offer_ui.mjs +183 -149
  38. package/tests/speciality_walkthrough_ui.mjs +182 -161
  39. package/modules/hall-ui/public/settings-interaction.js +0 -132
  40. package/modules/hall-ui/public/settings-law-locks.js +0 -53
  41. package/modules/hall-ui/public/settings-render.js +0 -138
@@ -1,6 +1,6 @@
1
1
  /* settings.css — what Settings has and the rest of the panel family does not
2
2
  (task 1003308, goal 1000081; the archetype is panel.css). The grouped layout
3
- behind the section rail, the group head, the token ladder, the voices grid,
3
+ behind the section rail, the group head, the token ladder,
4
4
  the volume line and the image-key banner. Every colour is a `var()` of the
5
5
  world's tier; the one literal pair is the Discord mark, named below.
6
6
 
@@ -98,31 +98,7 @@
98
98
  .sessions-row__agent { display: inline; margin-left: 8px; }
99
99
  .sessions-row__when { display: block; }
100
100
 
101
- /* ---- Voices (per-skill model picker) ------------------------------------- */
102
- .voices-wrap { margin-top: var(--sp-sm); overflow-x: auto; }
103
- table.voices { width: 100%; border-collapse: collapse; text-align: left; font-size: 14px; color: var(--ink); }
104
- table.voices th, table.voices td {
105
- padding: var(--sp-xs) var(--sp-xs) var(--sp-xs) 0;
106
- vertical-align: middle;
107
- border-top: var(--line) solid var(--rule-soft);
108
- }
109
- table.voices thead th {
110
- padding-top: 0;
111
- border-top: 0;
112
- border-bottom: var(--line) solid var(--rule);
113
- font-family: var(--label-face);
114
- font-size: var(--label-size);
115
- font-weight: 400;
116
- letter-spacing: var(--label-space);
117
- text-transform: var(--label-case);
118
- color: var(--ink-faint);
119
- white-space: nowrap;
120
- }
121
- .voices__skill { white-space: nowrap; }
122
- .voices__skill code { font-family: var(--font-mono); font-size: 12.5px; color: var(--ink-soft); }
123
- .voices__default { margin-left: 0; font-family: var(--font-body); font-size: 14px; letter-spacing: 0; text-transform: none; color: var(--ink-soft); }
124
- td.voices__choice { width: 1%; white-space: nowrap; }
125
- select.voices__select { min-width: 140px; }
101
+ /* ---- empty and error lines (the per-skill voices table left with task 1005044) -- */
126
102
  .voices-empty, .craft-empty { padding: var(--sp-xs) 0; font-size: 13.5px; color: var(--ink-faint); }
127
103
 
128
104
  /* ---- Sounds: the volume line ------------------------------------------- */
@@ -157,138 +133,8 @@ select.voices__select { min-width: 140px; }
157
133
  .settings-btn--discord { background: #5865F2; border-color: #4853c4; color: #ffffff; }
158
134
  .settings-btn--discord:hover:not(:disabled) { background: #4853c4; border-color: #4853c4; color: #ffffff; }
159
135
 
160
- /* Specialities panel (task 1003855, ADR 0296). A speciality is a named bundle of
161
- knowledge and terms under a craft; this styles the list, the train form and the
162
- create form. Tokens only — no literal colours, so light/dark follow the page. */
163
- .speciality-craft {
164
- font-size: 0.78rem;
165
- text-transform: lowercase;
166
- opacity: 0.72;
167
- margin-left: 0.4rem;
168
- }
169
- .speciality-badge {
170
- font-size: 0.7rem;
171
- padding: 0.05rem 0.4rem;
172
- border-radius: 0.5rem;
173
- margin-left: 0.4rem;
174
- border: 1px solid currentColor;
175
- }
176
- .speciality-counts { opacity: 0.82; }
177
- .speciality-card { flex-wrap: wrap; }
178
- .speciality-train,
179
- .speciality-create {
180
- flex-basis: 100%;
181
- margin-top: 0.75rem;
182
- padding-top: 0.75rem;
183
- border-top: 1px solid var(--rule, rgba(128, 128, 128, 0.25));
184
- display: flex;
185
- flex-direction: column;
186
- gap: 0.6rem;
187
- }
188
- .speciality-create__h { margin: 0; font-size: 0.95rem; }
189
- .speciality-field { display: flex; flex-direction: column; gap: 0.25rem; }
190
- .speciality-field > span { font-size: 0.85rem; }
191
- .speciality-field small { display: block; opacity: 0.7; font-weight: normal; }
192
- .speciality-field input[type='text'],
193
- .speciality-field select,
194
- .speciality-field textarea {
195
- width: 100%;
196
- box-sizing: border-box;
197
- }
198
- /* The checkbox row reads as one sentence, so it runs inline rather than stacked. */
199
- .speciality-field--check { flex-direction: row; align-items: flex-start; gap: 0.5rem; }
200
- .speciality-field--check input { margin-top: 0.2rem; }
201
- .speciality-actions { display: flex; gap: 0.5rem; }
202
- /* What a speciality OFFERS (task 1004404): the owner's pick-list of this
203
- instance's skills, each with its plain line. Sixty-odd rows, so the list
204
- scrolls inside its own box rather than pushing the form's buttons off-screen;
205
- the reading lines keep the measure. */
206
- .speciality-field input[type='search'] { width: 100%; box-sizing: border-box; }
207
- .speciality-offer,
208
- .speciality-offer-box { flex-basis: 100%; display: flex; flex-direction: column; gap: 0.6rem; }
209
- .speciality-offer { margin-top: 0.75rem; padding-top: 0.75rem; border-top: 1px solid var(--rule); }
210
- .speciality-offer > *, .speciality-offer-box > * { max-width: var(--measure); }
211
- .speciality-offer-box > summary { cursor: pointer; font-size: 0.85rem; min-height: 24px; padding-block: 2px; box-sizing: border-box; }
212
- .speciality-offer-box > summary small { opacity: 0.7; }
213
- .speciality-offer__choices, #speciality-create-skills { display: flex; flex-direction: column; gap: 0.6rem; }
214
- .speciality-offer__list {
215
- list-style: none;
216
- margin: 0;
217
- padding: 0 0.25rem 0 0;
218
- display: flex;
219
- flex-direction: column;
220
- gap: 0.5rem;
221
- max-height: 24rem;
222
- overflow-y: auto;
223
- overflow-x: hidden;
224
- }
225
- .speciality-offer__list li[hidden] { display: none; }
226
- .speciality-offer__list strong { font-family: var(--font-mono); font-weight: 600; }
227
- .speciality-offer__list .speciality-field--check > span { font-size: 0.85rem; line-height: 1.45; }
228
- /* The adopted contract, shown in full before "Use this" — another builder's prose
229
- reaching your sessions is the one thing here that must never be folded away. */
230
- .speciality-contract { flex-basis: 100%; margin-top: 0.5rem; font-size: 0.85rem; }
231
- /* 24px tall: the panel was never revealed until task 1004073, so the kit had
232
- never measured this toggle; it came back 21px. */
233
- .speciality-contract > summary { cursor: pointer; opacity: 0.85; min-height: 24px; padding-block: 2px; box-sizing: border-box; }
234
- .speciality-contract__body {
235
- white-space: pre-wrap;
236
- margin: 0.4rem 0 0;
237
- padding: 0.5rem 0.65rem;
238
- border-left: 2px solid var(--rule, rgba(128, 128, 128, 0.35));
239
- border-radius: 0;
240
- font-family: inherit;
241
- opacity: 0.92;
242
- }
243
- /* The skills walkthrough (task 1004073, ADR 0310 §3): one offered skill at a
244
- time, three answers as a definition list, then a decision. It sits inside the
245
- card like the train form, so it inherits the card's ground in both modes. */
246
- .skill-walk {
247
- flex-basis: 100%;
248
- margin-top: 0.75rem;
249
- padding-top: 0.75rem;
250
- border-top: 1px solid var(--rule);
251
- display: flex;
252
- flex-direction: column;
253
- gap: 0.6rem;
254
- }
255
- /* The measure caps the reading lines, never the section: a capped flex-basis
256
- lets the section fit beside the card's actions instead of wrapping under them. */
257
- .skill-walk > * { max-width: var(--measure); }
258
- .skill-walk__progress { margin: 0; font-size: 0.8rem; color: var(--ink-soft); }
259
- .skill-walk__name { margin: 0; font-family: var(--font-mono); font-size: 1rem; color: var(--ink); }
260
- .skill-walk__facts { margin: 0; display: grid; gap: 0.2rem 0; }
261
- .skill-walk__facts dt { font-size: 0.8rem; color: var(--ink-soft); margin-top: 0.4rem; }
262
- .skill-walk__facts dt:first-child { margin-top: 0; }
263
- .skill-walk__facts dd { margin: 0; color: var(--ink); line-height: 1.5; }
264
- .skill-walk__note { font-size: 0.8rem; }
265
- /* "Leave it off" and "Turn it on" are the panel family's plain pill at EQUAL
266
- weight: the default is off, so the page must not lean on the reader toward on.
267
- Only "Save my choices" takes the accent. A decision already made reads through
268
- aria-pressed when the person steps Back to it. */
269
- .skill-walk__actions { flex-wrap: wrap; }
270
- .skill-walk__actions .pbtn[aria-pressed="true"] { border-color: var(--ink-soft); }
271
- .skill-walk__recap { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
272
- .skill-walk__recap li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 0.4rem; }
273
- .skill-walk__recap .prow__hint { flex-basis: 100%; margin: 0; }
274
- .skill-walk__recap strong { font-family: var(--font-mono); font-weight: 600; }
275
- /* "Finish later" is a way out, not a choice about a skill, so it reads as a quiet
276
- link below the decision rather than a third button beside it. */
277
- .skill-walk__later {
278
- align-self: flex-start;
279
- background: none;
280
- border: 0;
281
- padding: 0 0.25rem;
282
- margin-inline-start: -0.25rem;
283
- min-height: 24px;
284
- font: inherit;
285
- font-size: 0.8rem;
286
- color: var(--ink-soft);
287
- text-decoration: underline;
288
- text-underline-offset: 3px;
289
- cursor: pointer;
290
- }
291
- .skill-walk__later:hover { color: var(--ink); }
136
+ /* The specialities panel's rules moved to speciality-tools.css (task 1005052);
137
+ only My agents links it since task 1005044. */
292
138
 
293
139
  /* ---- Software update (task 1004296) -------------------------------------- */
294
140
  /* A version is read digit by digit and compared against another, so it takes the mono
@@ -4,7 +4,7 @@
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <title>Settings — {{worldName}}</title>
7
- <meta name="description" content="Your preferences — connections, access keys, skill voices, and sounds.">
7
+ <meta name="description" content="Your preferences — connections, access keys, and sounds.">
8
8
  <!-- the hub's origin, stamped server-side (task 1004259): the Profile and
9
9
  privacy row links to its account page. Empty on an instance with no hub. -->
10
10
  <meta name="hub-origin" content="{{hubOrigin}}">
@@ -48,7 +48,7 @@
48
48
  <!-- Grouped layout (task 1723 → kit, task 2172): the sections sit in six
49
49
  groups behind a left sub-nav rail built by OTBKit.makeSectionRail
50
50
  (switch mode). settings.js wires the rail + per-panel anchor deep
51
- links (/settings#cli, /settings#voices, …); only the active group's
51
+ links (/settings#cli, /settings#art-key, …); only the active group's
52
52
  sections display. -->
53
53
  <div class="settings-layout" id="settings-layout">
54
54
  <nav class="section-rail" id="settings-rail" aria-label="Settings sections"></nav>
@@ -269,128 +269,19 @@
269
269
 
270
270
  <!-- ============ group: preferences ============ -->
271
271
 
272
- <!-- INTERACTION PROFILE — how replies are pitched for you (task 2010,
273
- delivered in task 1002514). GET/PATCH /api/bongos/me/interaction.
274
- Several independent fields, each its own single-select; the resolved
275
- contract is injected into every session by the conductor hook. No
276
- rank gate — this is ergonomics, not authority. -->
277
- <section class="scroll" id="interaction-scroll" aria-labelledby="interaction-h" data-group="preferences" hidden>
278
- <h2 id="interaction-h" class="scroll__h">How Claude talks to you</h2>
272
+ <!-- AGENT SETTINGS MOVED (task 1005044) — How Claude talks to you, your
273
+ specialities, Wandering and Rendering were edited here AND on My agents,
274
+ on the same routes. They live on My agents only now; this line is where
275
+ they were. Old deep links (#interaction, #wandering, #rendering, …) are
276
+ sent there by settings-moved.js. -->
277
+ <section class="scroll" id="agents-moved-preferences" aria-labelledby="agents-moved-preferences-h" data-group="preferences">
278
+ <h2 id="agents-moved-preferences-h" class="scroll__h">Your agents</h2>
279
279
  <p class="scroll__lede">
280
- How replies are pitched for you — the vocabulary assumed, how much
281
- hand-holding local tooling gets, how long an answer runs, and how much
282
- of the work behind it is narrated. These reach every session you start.
283
- The defaults assume no software-engineering background; change any of
284
- them and the rest stay as they are.
280
+ How Claude talks to you, wandering, session cards and your specialities are
281
+ set on <a href="/my-agents#talk">My agents</a>.
285
282
  </p>
286
- <div class="prows" id="interaction-rows"></div>
287
- <p class="settings-status" id="interaction-status" role="status" aria-live="polite"></p>
288
283
  </section>
289
284
 
290
- <!-- SPECIALITIES — a named bundle of knowledge + terms UNDER a craft
291
- (task 1003855, ADR 0296). GET/POST /api/bongos/specialities,
292
- POST /specialities/:id/{adopt,abandon,documents}. No rank gate:
293
- a speciality is how an expert's knowledge enters the platform,
294
- and gating that by rank would mean only trusted builders could
295
- be experts. -->
296
- <section class="scroll" id="specialities-scroll" aria-labelledby="specialities-h" data-group="preferences" hidden>
297
- <h2 id="specialities-h" class="scroll__h">Your specialities</h2>
298
- <p class="scroll__lede">
299
- A role says how you work. A speciality says what you work on — and how you,
300
- as someone who knows the subject, want to be engaged inside it. Whichever one
301
- you are using reaches every session where you hold a claim in that role.
302
- Train it by adding things you have written; it also picks things up on its own
303
- as you work.
304
- </p>
305
- <div id="speciality-suggestions"></div>
306
- <ul class="prows" id="specialities-rows"></ul>
307
-
308
- <form id="speciality-create" class="speciality-create">
309
- <h3 class="speciality-create__h">Create one</h3>
310
- <label class="speciality-field">
311
- <span>Name</span>
312
- <input type="text" name="name" maxlength="80" required placeholder="Economics" />
313
- </label>
314
- <label class="speciality-field">
315
- <span>Role it sits under</span>
316
- <select name="discipline" required>
317
- <option value="">Pick one…</option>
318
- <option value="engineer">Engineer</option>
319
- <option value="ideator">Ideator</option>
320
- <option value="artist">Artist</option>
321
- <option value="ui">UI design</option>
322
- </select>
323
- </label>
324
- <label class="speciality-field">
325
- <span>Summary <small>(optional)</small></span>
326
- <input type="text" name="summary" maxlength="500" placeholder="Markets, incentives, and how to price them." />
327
- </label>
328
- <label class="speciality-field">
329
- <span>How Claude should engage you in it <small>(optional)</small></span>
330
- <textarea name="contract_md" rows="4" maxlength="4000" placeholder="Argue from mechanism, not vibes. Always name the assumption a number rests on."></textarea>
331
- </label>
332
- <label class="speciality-field">
333
- <span>Who can use it</span>
334
- <select name="visibility">
335
- <option value="private">Only me</option>
336
- <option value="project">Anyone in the owning project</option>
337
- <option value="public">Anyone on this instance</option>
338
- </select>
339
- </label>
340
- <label class="speciality-field speciality-field--check">
341
- <input type="checkbox" name="sellable" />
342
- <span>
343
- May be offered for sale later
344
- <small>Records your decision only — there is no marketplace yet, and nothing is listed anywhere.</small>
345
- </span>
346
- </label>
347
- <details id="speciality-create-offer" class="speciality-offer-box">
348
- <summary>Skills it offers <small>(optional)</small></summary>
349
- <div id="speciality-create-skills"><p class="hall-loading hall-loading--inline" role="status">Loading this project’s skills…</p></div>
350
- </details>
351
- <div class="speciality-actions">
352
- <button type="submit" class="btn">Create speciality</button>
353
- </div>
354
- </form>
355
-
356
- <p class="settings-status" id="specialities-status" role="status" aria-live="polite"></p>
357
- </section>
358
-
359
- <!-- WANDERING — how readily the agent opens off-task threads (task 1268).
360
- GET/PATCH /api/bongos/me/wandering. Rank sets the default + ceiling; the
361
- server clamps, so options above the rank cap render disabled. -->
362
- <section class="scroll" id="wandering-scroll" aria-labelledby="wandering-h" data-group="preferences" hidden>
363
- <h2 id="wandering-h" class="scroll__h">Wandering</h2>
364
- <p class="scroll__lede">
365
- How readily Claude opens a <em>new thread</em> of work — a tangent, an
366
- investigation, an unprompted suggestion — instead of staying on your
367
- claimed task. It never limits the work on the task itself. Your rank
368
- sets the starting point; newcomers stay locked low.
369
- </p>
370
- <div class="prows" id="wander-rows" role="radiogroup" aria-labelledby="wandering-h"></div>
371
- <p class="settings-status" id="wandering-status" role="status" aria-live="polite"></p>
372
- <p class="section-note" id="wandering-note"></p>
373
- </section>
374
-
375
- <!-- RENDERING — how rich the session cards are (task 1279's toggle,
376
- a four-rung ladder since task 1002518). GET/PATCH
377
- /api/bongos/me/render; the rows are written by
378
- settings-render.js, not settings.js. The default rung is DERIVED
379
- from the builder's interaction profile, which is why the lede
380
- points at that panel rather than describing a fixed default. -->
381
- <section class="scroll" id="render-scroll" aria-labelledby="render-h" data-group="preferences" hidden>
382
- <h2 id="render-h" class="scroll__h">Rendering</h2>
383
- <p class="scroll__lede">
384
- How much your session cards — what <code>/builder-start</code>, claiming,
385
- and shipping show — spend on explaining themselves. Richer cards say more
386
- and cost more tokens; leaner ones carry the same facts for less. By default
387
- this follows how Claude talks to you, so you can set it once in
388
- <a href="#interaction">How Claude talks to you</a> and leave this alone.
389
- </p>
390
- <div class="sound-rows" id="render-rows"></div>
391
- <p class="settings-status" id="render-status" role="status" aria-live="polite"></p>
392
- <p class="section-note">Saves the moment you choose; applies from your next session.</p>
393
- </section>
394
285
 
395
286
  <!-- MODULE INSTALL REPORTS — the opt-out for counting your store-module
396
287
  installs in a module's Install and Reliability scores (task 1003793,
@@ -410,24 +301,13 @@
410
301
 
411
302
  <!-- ============ group: sound ============ -->
412
303
 
413
- <!-- VOICES — per-skill model picker (GET/PATCH /me/skill-prefs). -->
414
- <section class="scroll voices-scroll" id="voices-scroll" aria-labelledby="voices-h" data-group="sound" hidden>
415
- <h2 id="voices-h" class="scroll__h">Skill voices</h2>
304
+ <!-- SKILL VOICES MOVED (task 1005044) — the per-skill model picker lives on
305
+ My agents only now (Which models run → Per skill). -->
306
+ <section class="scroll" id="agents-moved-sound" aria-labelledby="agents-moved-sound-h" data-group="sound">
307
+ <h2 id="agents-moved-sound-h" class="scroll__h">Skill voices</h2>
416
308
  <p class="scroll__lede">
417
- Each slash-command can run on a different model. The system has a
418
- default for every skill; choose another to override it, or leave it
419
- on Default.
309
+ Which model each skill runs on is set on <a href="/my-agents#skills">My agents</a>.
420
310
  </p>
421
- <div class="voices-wrap">
422
- <table class="voices" id="voices-table">
423
- <thead>
424
- <tr><th scope="col">Skill</th><th scope="col">Default</th><th scope="col">Your choice</th></tr>
425
- </thead>
426
- <tbody id="voices-body"></tbody>
427
- </table>
428
- </div>
429
- <p class="settings-status" id="voices-status" role="status" aria-live="polite"></p>
430
- <p class="section-note">Changes save the moment you pick. “Default” clears your override and falls back to the system's choice.</p>
431
311
  </section>
432
312
 
433
313
  <!-- SOUNDS OFF — every notification sound is switched off for now (task 1004554);
@@ -522,23 +402,15 @@
522
402
  <!-- shared record overlay (#/idea|blocker/N) — every hall page carries it; this
523
403
  page was the one gap (task 2172). -->
524
404
  <script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
525
- <script src="/builders/settings-law-locks.js?v=2026-10-17-laws"></script>
526
- <script src="/builders/settings.js?v=2026-10-17-laws"></script>
405
+ <!-- task 1005044: the old deep links to the agent settings that moved to My agents -->
406
+ <script src="/builders/settings-moved.js?v=2026-11-03-moved"></script>
407
+ <script src="/builders/settings.js?v=2026-11-03-moved"></script>
527
408
  <!-- task 1003341: the Active sessions panel. Its own file rather than more of
528
409
  settings.js, which sits close to the 1500-line size ratchet. -->
529
410
  <script src="/builders/settings-sessions.js?v=2026-08-29-sessions"></script>
530
411
  <script src="/builders/settings-passkeys.js?v=2026-10-15-passkeys"></script>
531
412
  <script src="/builders/settings-age.js?v=2026-10-30-age"></script>
532
413
  <script src="/builders/settings-ai-keys.js?v=2026-10-28-ai-keys"></script>
533
- <!-- task 1002514: the interaction-profile panel ("How Claude talks to you"), on
534
- the same precedent and for the same reason — settings.js is against the
535
- 1500-line ratchet, and this panel shares nothing with the rest of the page. -->
536
- <script src="/builders/settings-interaction.js?v=2026-10-17-laws"></script>
537
- <script src="/builders/settings-specialities.js?v=2026-09-17-specialities"></script>
538
- <!-- task 1002518: the card-richness ladder. Own file on the same precedent —
539
- the panel grew from one switch to a rung picker with a derived default, and
540
- settings.js is against the 1500-line ratchet. -->
541
- <script src="/builders/settings-render.js?v=2026-09-16-render-levels"></script>
542
414
  <!-- task 1004296: Settings → Software update. Own file on the same precedent. -->
543
415
  <!-- the Update pill's flow, which the panel's Update button runs in place (task 1005041) -->
544
416
  <script src="/builders/update-pill.js?v=2026-11-03-pill"></script>