@bongos/core 1.21.26 → 1.21.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 (57) hide show
  1. package/.bongos-core.json +76 -61
  2. package/docs/copy-inventory.md +21 -13
  3. package/docs/copy-registry.json +88 -16
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/page-inventory.json +68 -0
  6. package/docs/page-readings.json +3504 -3354
  7. package/modules/hall-ui/public/atlas.html +3 -1
  8. package/modules/hall-ui/public/blockers.html +3 -1
  9. package/modules/hall-ui/public/board-room.html +3 -1
  10. package/modules/hall-ui/public/city.html +3 -1
  11. package/modules/hall-ui/public/collab.html +3 -1
  12. package/modules/hall-ui/public/commands.html +3 -1
  13. package/modules/hall-ui/public/copy-desk.html +3 -1
  14. package/modules/hall-ui/public/craft-mode.js +297 -0
  15. package/modules/hall-ui/public/deploy.html +3 -1
  16. package/modules/hall-ui/public/diagrams.html +3 -1
  17. package/modules/hall-ui/public/drachmae.html +3 -1
  18. package/modules/hall-ui/public/fleet.html +3 -1
  19. package/modules/hall-ui/public/gate.html +3 -1
  20. package/modules/hall-ui/public/goal-map.html +3 -1
  21. package/modules/hall-ui/public/goals.html +3 -1
  22. package/modules/hall-ui/public/government.html +3 -1
  23. package/modules/hall-ui/public/idea.html +3 -1
  24. package/modules/hall-ui/public/ideas.html +3 -1
  25. package/modules/hall-ui/public/index.html +3 -1
  26. package/modules/hall-ui/public/modules.html +3 -1
  27. package/modules/hall-ui/public/primer.html +3 -1
  28. package/modules/hall-ui/public/profile.html +3 -1
  29. package/modules/hall-ui/public/project-settings.html +3 -1
  30. package/modules/hall-ui/public/ranks.html +3 -1
  31. package/modules/hall-ui/public/roadmap.html +3 -1
  32. package/modules/hall-ui/public/roster.html +3 -1
  33. package/modules/hall-ui/public/sessions.html +3 -1
  34. package/modules/hall-ui/public/settings.html +3 -1
  35. package/modules/hall-ui/public/shell-trail.js +186 -0
  36. package/modules/hall-ui/public/shell.js +23 -141
  37. package/modules/hall-ui/public/street.html +3 -1
  38. package/modules/hall-ui/public/studio.html +3 -1
  39. package/modules/hall-ui/public/style.css +274 -0
  40. package/modules/hall-ui/public/task.html +3 -1
  41. package/modules/hall-ui/public/thinking.html +3 -1
  42. package/modules/hall-ui/public/tweak-editor.html +3 -1
  43. package/modules/hall-ui/public/watch.html +3 -1
  44. package/modules/hall-ui/public/work.html +3 -1
  45. package/modules/hall-ui/records/craft-mode.md +22 -0
  46. package/modules/hall-ui/records/hall-navigation.md +8 -3
  47. package/package-lock.json +2 -2
  48. package/package.json +1 -1
  49. package/release-notes.json +12 -0
  50. package/src/module-api.js +1 -1
  51. package/tests/craft_mode.mjs +267 -112
  52. package/tests/goal_map_page.mjs +2 -2
  53. package/tests/hall_city.mjs +1 -1
  54. package/tests/hall_fullscreen.mjs +125 -5
  55. package/tests/hall_street.mjs +1 -1
  56. package/tests/hall_tweak_editor.mjs +1 -1
  57. package/tests/nav_permission_atoms.mjs +1 -1
@@ -1201,6 +1201,234 @@ html[data-fullscreen="on"][data-fullscreen-plain="on"] .app-content { padding-to
1201
1201
  pointer-events: none;
1202
1202
  }
1203
1203
 
1204
+ /* THE CRAFT MODE (task 1004492, craft-mode.js). The top bar's switch: one pill
1205
+ holding the four crafts and Legacy, the current one lit. Narrower windows
1206
+ drop the words, and a phone shows only the current mode (or "Mode"), which
1207
+ opens the picker. The picker below asks once per session. Neither paints a
1208
+ mode's colour — the looks are their own task (1004495). */
1209
+ .craft-switch {
1210
+ display: inline-flex;
1211
+ align-items: center;
1212
+ gap: 2px;
1213
+ padding: 3px;
1214
+ border-radius: var(--radius-pill);
1215
+ box-shadow: inset 0 0 0 1px var(--glass-edge);
1216
+ flex-shrink: 0;
1217
+ }
1218
+ .craft-switch__opt,
1219
+ .craft-switch__open {
1220
+ display: inline-flex;
1221
+ align-items: center;
1222
+ gap: 6px;
1223
+ min-width: 28px;
1224
+ height: 28px;
1225
+ padding: 0 10px;
1226
+ border: 0;
1227
+ border-radius: var(--radius-pill);
1228
+ background: none;
1229
+ color: var(--ink-soft);
1230
+ font: inherit;
1231
+ font-size: 13px;
1232
+ cursor: pointer;
1233
+ white-space: nowrap;
1234
+ }
1235
+ .craft-switch__opt:hover,
1236
+ .craft-switch__open:hover { color: var(--ink); background: var(--glass); }
1237
+ .craft-switch__opt[aria-pressed="true"] { color: var(--ink); background: var(--glass-strong); }
1238
+ .craft-switch__opt:focus-visible,
1239
+ .craft-switch__open:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
1240
+ .craft-switch__icon { width: 15px; height: 15px; flex-shrink: 0; }
1241
+ .craft-switch__open { display: none; }
1242
+ @media (max-width: 1180px) {
1243
+ .craft-switch__opt { padding: 0 7px; }
1244
+ .craft-switch__opt .craft-switch__label { display: none; }
1245
+ }
1246
+ @media (max-width: 640px) {
1247
+ .craft-switch { padding: 2px; }
1248
+ .craft-switch__opt[aria-pressed="false"] { display: none; }
1249
+ .craft-switch--unset .craft-switch__open { display: inline-flex; padding: 0 7px; }
1250
+ }
1251
+
1252
+ /* The picker: a dialog over a scrim, four doors in a row and Legacy beside the
1253
+ "ask me again" box. Each door carries its scene from the approved prototype,
1254
+ which is night-dark in either theme because every scene is a night one. */
1255
+ html.craft-picker-open { overflow: hidden; }
1256
+ .craft-picker {
1257
+ position: fixed;
1258
+ inset: 0;
1259
+ z-index: var(--z-modal);
1260
+ display: flex;
1261
+ align-items: flex-start;
1262
+ justify-content: center;
1263
+ padding: max(24px, 8vh) 16px 24px;
1264
+ overflow-y: auto;
1265
+ background: var(--scrim);
1266
+ }
1267
+ .craft-picker__panel {
1268
+ position: relative;
1269
+ width: min(1120px, 100%);
1270
+ box-sizing: border-box;
1271
+ display: flex;
1272
+ flex-direction: column;
1273
+ gap: 12px;
1274
+ padding: 40px 44px 30px;
1275
+ border-radius: 22px;
1276
+ background: var(--bg-card);
1277
+ color: var(--ink);
1278
+ box-shadow: 0 0 0 1px var(--glass-edge), 0 24px 64px rgba(0, 0, 0, .35);
1279
+ }
1280
+ .craft-picker__close {
1281
+ position: absolute;
1282
+ top: 14px;
1283
+ right: 14px;
1284
+ display: inline-flex;
1285
+ align-items: center;
1286
+ justify-content: center;
1287
+ width: 36px;
1288
+ height: 36px;
1289
+ border: 0;
1290
+ border-radius: var(--radius-pill);
1291
+ background: none;
1292
+ color: var(--ink-soft);
1293
+ cursor: pointer;
1294
+ }
1295
+ .craft-picker__close svg { width: 18px; height: 18px; }
1296
+ .craft-picker__close:hover { color: var(--ink); background: var(--glass); }
1297
+ .craft-picker__eyebrow {
1298
+ margin: 0;
1299
+ font-family: var(--label-face);
1300
+ font-size: var(--label-size);
1301
+ letter-spacing: var(--label-space);
1302
+ text-transform: var(--label-case);
1303
+ color: var(--ink-faint);
1304
+ }
1305
+ .craft-picker__title {
1306
+ margin: 0;
1307
+ font-family: var(--font-display);
1308
+ font-size: clamp(28px, 4vw, 40px);
1309
+ font-weight: 300;
1310
+ letter-spacing: -0.03em;
1311
+ line-height: 1.15;
1312
+ }
1313
+ .craft-picker__lede { margin: 0 0 16px; font-size: 16px; line-height: 1.5; color: var(--ink-soft); }
1314
+ .craft-picker__doors {
1315
+ display: grid;
1316
+ grid-template-columns: repeat(4, minmax(0, 1fr));
1317
+ gap: 16px;
1318
+ }
1319
+ .craft-door {
1320
+ display: flex;
1321
+ flex-direction: column;
1322
+ align-items: stretch;
1323
+ gap: 8px;
1324
+ padding: 0 0 20px;
1325
+ overflow: hidden;
1326
+ border: 0;
1327
+ border-radius: 14px;
1328
+ background: var(--bg);
1329
+ color: var(--ink);
1330
+ font: inherit;
1331
+ text-align: left;
1332
+ cursor: pointer;
1333
+ box-shadow: inset 0 0 0 1px var(--glass-edge);
1334
+ transition: transform 200ms cubic-bezier(.2, .7, .3, 1), box-shadow 200ms;
1335
+ }
1336
+ .craft-door:hover { transform: translateY(-3px); box-shadow: inset 0 0 0 1px var(--rule); }
1337
+ .craft-door:focus-visible { outline: 2px solid var(--focus); outline-offset: 3px; }
1338
+ .craft-door--pre { box-shadow: inset 0 0 0 2px var(--accent); }
1339
+ .craft-door__head { display: flex; align-items: center; gap: 10px; padding: 10px 20px 0; }
1340
+ .craft-door__icon { width: 20px; height: 20px; flex-shrink: 0; }
1341
+ .craft-door__name { font-size: 20px; font-weight: 500; letter-spacing: -0.02em; }
1342
+ .craft-door__line { padding: 0 20px; font-size: 14px; line-height: 1.45; color: var(--ink-soft); }
1343
+ .craft-art {
1344
+ position: relative;
1345
+ display: block;
1346
+ height: 110px;
1347
+ overflow: hidden;
1348
+ background: #101012;
1349
+ }
1350
+ .craft-art--artistry { background: #0c1122; }
1351
+ .craft-art--ideating { background: #05060d; }
1352
+ .craft-art--governing { background: #000; }
1353
+ .craft-art__dot { position: absolute; border-radius: 999px; box-sizing: border-box; }
1354
+ .craft-art__dot--star { background: #d8dcf0; }
1355
+ .craft-art__dot--lit { background: var(--accent); border-radius: 0; }
1356
+ .craft-art__dot--gold { background: #f2d27a; }
1357
+ .craft-art__dot--glow { background: #f2d27a; box-shadow: 0 0 10px rgba(242, 210, 122, .7); }
1358
+ .craft-art__dot--proto { box-shadow: inset 0 0 0 1px rgba(242, 210, 122, .55); }
1359
+ .craft-art__bar { position: absolute; height: 10px; border-radius: 4px; background: rgba(255, 255, 255, .12); }
1360
+ .craft-art__bar--dim { background: rgba(255, 255, 255, .08); }
1361
+ .craft-art__pill { position: absolute; width: 84px; height: 26px; border-radius: 999px; background: var(--accent); }
1362
+ .craft-art__card {
1363
+ position: absolute; right: 20px; top: 20px; width: 60px; height: 72px; border-radius: 8px;
1364
+ background: rgba(255, 255, 255, .06); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .12);
1365
+ }
1366
+ .craft-art__moon {
1367
+ position: absolute; right: 30px; top: 18px; width: 28px; height: 28px; border-radius: 999px;
1368
+ background: #f1e6d2; box-shadow: 0 0 0 9px rgba(241, 230, 210, .10);
1369
+ }
1370
+ .craft-art__ground { position: absolute; left: 0; right: 0; bottom: 0; height: 32px; background: #090b16; }
1371
+ .craft-art__house { position: absolute; bottom: 0; background: #0a0d1a; }
1372
+ .craft-art__ring {
1373
+ position: absolute; left: 50%; top: 14px; width: 82px; height: 82px; margin-left: -41px;
1374
+ border-radius: 999px; box-shadow: inset 0 0 0 1px rgba(242, 210, 122, .22);
1375
+ }
1376
+ .craft-art__city { position: absolute; left: 50%; top: 0; width: 300px; height: 110px; margin-left: -150px; }
1377
+ .craft-picker__foot {
1378
+ display: flex;
1379
+ flex-wrap: wrap;
1380
+ align-items: center;
1381
+ justify-content: space-between;
1382
+ gap: 12px 24px;
1383
+ margin-top: 14px;
1384
+ padding-top: 18px;
1385
+ border-top: 1px solid var(--rule-soft);
1386
+ }
1387
+ .craft-picker__again {
1388
+ display: inline-flex;
1389
+ align-items: center;
1390
+ gap: 10px;
1391
+ min-height: 24px;
1392
+ font-size: 14px;
1393
+ color: var(--ink-soft);
1394
+ cursor: pointer;
1395
+ }
1396
+ .craft-picker__again input { width: 18px; height: 18px; margin: 0; accent-color: var(--accent); }
1397
+ .craft-picker__side { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; }
1398
+ .craft-picker__hint { font-size: 13px; color: var(--ink-faint); }
1399
+ .craft-picker__legacy {
1400
+ display: inline-flex;
1401
+ align-items: center;
1402
+ height: 40px;
1403
+ padding: 0 18px;
1404
+ border: 0;
1405
+ border-radius: var(--radius-pill);
1406
+ background: none;
1407
+ color: var(--ink);
1408
+ font: inherit;
1409
+ font-size: 14px;
1410
+ cursor: pointer;
1411
+ box-shadow: inset 0 0 0 1px var(--rule);
1412
+ }
1413
+ .craft-picker__legacy:hover { background: var(--glass); }
1414
+ .craft-picker__legacy:focus-visible,
1415
+ .craft-picker__close:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
1416
+ @media (max-width: 900px) {
1417
+ .craft-picker__doors { grid-template-columns: repeat(2, minmax(0, 1fr)); }
1418
+ }
1419
+ @media (max-width: 640px) {
1420
+ .craft-picker { padding: 12px; }
1421
+ .craft-picker__panel { padding: 28px 18px 20px; gap: 8px; border-radius: 18px; }
1422
+ .craft-picker__doors { grid-template-columns: minmax(0, 1fr); gap: 10px; }
1423
+ .craft-door { flex-direction: row; flex-wrap: wrap; align-items: center; gap: 4px 0; padding: 0 0 14px; }
1424
+ .craft-art { flex: 0 0 100%; height: 96px; }
1425
+ .craft-art__city { top: -8px; }
1426
+ .craft-door__head { padding: 10px 16px 0; }
1427
+ .craft-door__name { font-size: 18px; }
1428
+ .craft-door__line { flex: 0 0 100%; padding: 0 16px; box-sizing: border-box; }
1429
+ .craft-picker__legacy { height: auto; min-height: 40px; white-space: normal; text-align: left; }
1430
+ }
1431
+
1204
1432
  /* THE BACK LINK (task 1004430): at the head of the bar when the page was
1205
1433
  reached from inside another hall page. 24px floor on both axes. */
1206
1434
  .topbar-back {
@@ -1224,6 +1452,52 @@ html[data-fullscreen="on"][data-fullscreen-plain="on"] .app-content { padding-to
1224
1452
  .topbar-back:hover { color: var(--ink); background: var(--rule-soft); }
1225
1453
  .topbar-back svg { width: 16px; height: 16px; flex-shrink: 0; }
1226
1454
  @media (max-width: 480px) { .topbar-back__label { display: none; } .topbar-back { padding: 0 8px; } }
1455
+ /* The trail (task 1004476): "‹ Goals › goal 12 › task 340", the last crumb
1456
+ being the Back link. On a phone the middle collapses to "…", and on a narrow
1457
+ one the bar's page name gives the trail its room (the page heading below
1458
+ still names the page). */
1459
+ .topbar-trail {
1460
+ display: inline-flex;
1461
+ align-items: center;
1462
+ flex-shrink: 0;
1463
+ margin-right: -4px;
1464
+ color: var(--ink-soft);
1465
+ font-size: 13px;
1466
+ white-space: nowrap;
1467
+ }
1468
+ .topbar-trail__mark { width: 16px; height: 16px; flex-shrink: 0; margin: 0 2px 0 6px; }
1469
+ .topbar-trail__step, .topbar-trail__more { display: inline-flex; align-items: center; }
1470
+ .topbar-crumb {
1471
+ display: inline-flex;
1472
+ align-items: center;
1473
+ min-height: 32px;
1474
+ padding: 0 6px;
1475
+ border-radius: var(--radius-pill);
1476
+ color: inherit;
1477
+ text-decoration: none;
1478
+ }
1479
+ .topbar-crumb:hover { color: var(--ink); background: var(--rule-soft); }
1480
+ .topbar-trail__sep { padding: 0 2px; color: var(--ink-faint); }
1481
+ .topbar-trail .topbar-back { padding: 0 10px 0 6px; }
1482
+ .topbar-trail .topbar-back svg { display: none; }
1483
+ .topbar-trail__more { display: none; }
1484
+ @media (max-width: 640px) {
1485
+ .topbar-trail__step--mid { display: none; }
1486
+ .topbar-trail--long .topbar-trail__more { display: inline-flex; }
1487
+ }
1488
+ @media (max-width: 480px) {
1489
+ .topbar-trail__step--first { display: none; }
1490
+ .topbar-trail .topbar-trail__more { display: inline-flex; }
1491
+ .topbar-trail .topbar-back__label { display: inline; }
1492
+ .topbar-trail .topbar-back { padding: 0 8px 0 6px; }
1493
+ .topbar-trail ~ .topbar-title { display: none; }
1494
+ }
1495
+ @media (max-width: 360px) {
1496
+ .topbar-trail__mark, .topbar-trail .topbar-trail__more, .topbar-trail .topbar-back__label { display: none; }
1497
+ .topbar-trail .topbar-back svg { display: block; }
1498
+ .topbar-trail .topbar-back { padding: 0 8px; }
1499
+ .topbar-trail ~ .topbar-title { display: block; }
1500
+ }
1227
1501
  .zoom-toggle__pct {
1228
1502
  font-size: 12px;
1229
1503
  font-weight: 500;
@@ -54,7 +54,9 @@
54
54
 
55
55
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
56
56
  time so the chrome exists before the deferred page scripts execute. -->
57
- <script src="/builders/shell.js?v=2026-10-02-back"></script>
57
+ <script src="/builders/shell-trail.js?v=2026-10-02-craft"></script>
58
+ <script src="/builders/craft-mode.js?v=2026-10-02-craft"></script>
59
+ <script src="/builders/shell.js?v=2026-10-02-craft"></script>
58
60
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
59
61
  <script src="/builders/palette.js" defer></script>
60
62
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
@@ -138,7 +138,9 @@
138
138
  </div>
139
139
  </div>
140
140
 
141
- <script src="/builders/shell.js?v=2026-10-02-back"></script>
141
+ <script src="/builders/shell-trail.js?v=2026-10-02-craft"></script>
142
+ <script src="/builders/craft-mode.js?v=2026-10-02-craft"></script>
143
+ <script src="/builders/shell.js?v=2026-10-02-craft"></script>
142
144
  <script src="/builders/palette.js" defer></script>
143
145
 
144
146
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -142,7 +142,9 @@
142
142
 
143
143
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
144
144
 
145
- <script src="/builders/shell.js?v=2026-10-02-back"></script>
145
+ <script src="/builders/shell-trail.js?v=2026-10-02-craft"></script>
146
+ <script src="/builders/craft-mode.js?v=2026-10-02-craft"></script>
147
+ <script src="/builders/shell.js?v=2026-10-02-craft"></script>
146
148
  <script src="/builders/palette.js" defer></script>
147
149
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
148
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
@@ -162,7 +162,9 @@
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-10-02-back"></script>
165
+ <script src="/builders/shell-trail.js?v=2026-10-02-craft"></script>
166
+ <script src="/builders/craft-mode.js?v=2026-10-02-craft"></script>
167
+ <script src="/builders/shell.js?v=2026-10-02-craft"></script>
166
168
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
167
169
  <script src="/builders/palette.js" defer></script>
168
170
  <!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
@@ -171,7 +171,9 @@
171
171
 
172
172
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
- <script src="/builders/shell.js?v=2026-10-02-back"></script>
174
+ <script src="/builders/shell-trail.js?v=2026-10-02-craft"></script>
175
+ <script src="/builders/craft-mode.js?v=2026-10-02-craft"></script>
176
+ <script src="/builders/shell.js?v=2026-10-02-craft"></script>
175
177
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
176
178
  <script src="/builders/palette.js" defer></script>
177
179
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
@@ -0,0 +1,22 @@
1
+ # Craft mode — the entry picker and the top-bar switch (task 1004492, goal 1000095 WA6)
2
+
3
+ Covers: every shelled hall page (`craft-mode.js`, `shell.js`, `style.css`), and the carve of the Back link + trail into `shell-trail.js`.
4
+
5
+ ## What it is
6
+
7
+ - On entering the hall, once per browser session, a signed-in builder is asked **"What are you doing now?"**: Building, Artistry, Ideating, Governing, or **Legacy mode** (the owner's fifth choice, 2026-10-01: the hall exactly as it was). The approved prototype is the design canvas linked on the task; the doors carry its four scenes (the work desk, the night room, the sky, the governor city).
8
+ - The answer is ONE attribute, `html[data-craft="building|artistry|ideating|governing|legacy"]`, readable by every hall page. Every change also fires `otb:craft` on `document` with `{ detail: { mode } }`, so a page can react without polling.
9
+ - The top bar carries a switch with all five; pressing one changes the mode without a reload. Under 1180px it drops the words; at phone width only the current mode shows (or a "Choose your mode" button when there is none), and tapping it opens the picker.
10
+ - Artistry is pre-selected for a builder whose `main_discipline` is `artist` (or, with no main role, whose first craft is).
11
+
12
+ ## The rules an edit here must keep
13
+
14
+ - **It is a mode the hall wears, never a second hall** (the founding-mode precedent). This task records and shows the choice; what each mode changes — the nav (1004493), the look (1004494/1004495), the mode tools (1004496–1004498) — reads the attribute and is its own task.
15
+ - **No mode is the default and the fallback.** Closing the picker, storage that throws, and a page with `<body data-craft-mode="off">` all leave the attribute off, and the hall renders as it did before craft modes: `tests/craft_mode.mjs` boots the shell with and without `craft-mode.js` and requires the root attributes and the sidebar to be identical and the top bar to differ by the switch alone.
16
+ - **Once per session**: `sessionStorage['hall-craft']` holds the mode and `['hall-craft-asked']` that the picker was shown. Unticking "Ask me again next session" writes `localStorage['hall-craft-keep']`, which later sessions start from. A stored value that is not one of the five is ignored. A browser whose storage throws is never asked — it could not remember being asked.
17
+ - **The picker waits for a signed-in builder.** `shell.js` hands `onAccess(access, builder)` what `/me` said from `applyAccess`, so both callers (the index's `hall-render.js` and the shell's own probe) reach it, and a signed-out visitor is never asked.
18
+ - **Load order**: `shell-trail.js`, then `craft-mode.js`, then `shell.js`, deferred alike, on every hall page (test-pinned). `shell.js` reads both at boot and runs without either: the vm tests that boot `shell.js` alone still work.
19
+
20
+ ## The carve
21
+
22
+ `shell.js` sat at exactly 1,500 lines, the fitness ratchet, so the Back link + trail section (tasks 1004430/1004476/1004477) moved verbatim into `shell-trail.js` (`window.OTBShellTrail.make({ esc, nav })`). Behaviour is unchanged; `tests/hall_fullscreen.mjs` loads it before the shell and still drives the trail through the real top bar.
@@ -1,6 +1,6 @@
1
- # Hall navigation — the Back link and the standard full-screen switch (tasks 1004430, 1004477)
1
+ # Hall navigation — the Back link and its trail, and the standard full-screen switch (tasks 1004430, 1004477, 1004476)
2
2
 
3
- Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html` joining the chrome.
3
+ Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html` joining the chrome. Since task 1004492 the Back link and trail code lives in `shell-trail.js` (carved out of `shell.js` unchanged; see `craft-mode.md`).
4
4
 
5
5
  ## The Back link
6
6
 
@@ -11,6 +11,11 @@ Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html`
11
11
  - `<body data-back-fallback="/work">` names where Back goes when there is no origin (a deep-linked task falls back to Tasks, an idea to Your thinking, the tweak editor to Studio). It is honoured only as a path on this host; a sidebar hop still shows nothing.
12
12
  - A link marked `[data-shell-exit]` is a second exit a page places: the task record's actions row and its two failure renders, the primer and drachmae error notes, and the tweak editor's bar. The shell fills its `href` and its words ("Back to goal 12", "Back to tasks"), and with no way back at all it goes home. A page that renders one after boot calls `OTBShell.fillExits(root)`.
13
13
  - The origin's own `?from=` is dropped so back links never chain, and a `?from=` naming the page itself is refused.
14
+ - **The trail (task 1004476).** Two or more clicks deep the Back link grows into a breadcrumb: "‹ Goals › goal 12 › task 340", each crumb a link and the last one `#topbar-back` itself. One step back renders exactly the old Back link.
15
+ - It lives per tab in `sessionStorage['hall-nav-trail']`, a list of `{href,label}` crumbs ending with the current page. A drill-down (origin found in the trail) cuts after the origin and appends; returning to a crumb (this page is in the trail and the origin sits after it, or there is none) trims after it; an origin the trail does not know starts fresh, so `?from=`, referrer and `data-back-fallback` still decide the first crumb; a sidebar hop starts over with no trail.
16
+ - A referrer has no `#hash`, so a referrer origin matches the latest crumb on the same address: a task opened from `/goals#/goal/12` finds "goal 12", not "Goals". Hash routes (a goal opened on `/goals`, the hall's overlays) update the trail on `hashchange`: a record hash appends, a held crumb trims, any other view only renames the page's own crumb.
17
+ - Capped at four crumbs, oldest dropped. Stored crumbs pass the same host-path check as the fallback and are escaped when drawn. Storage that throws keeps the one-step Back.
18
+ - Width folds it: at 640px the middle becomes "…"; at 480px the first crumb joins it, the "…" becomes a link to the trail's start (titled with what it hides), and the bar's page name hides while a trail shows; at 360px only the Back arrow is left. Measured with no sideways scroll at 320, 390, 600 and 1280.
14
19
  - `/profile?id=N` highlights **Builders**, not your own Profile, unless `/me` says the id is yours.
15
20
 
16
21
  ## Full screen is standard
@@ -22,4 +27,4 @@ Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html`
22
27
 
23
28
  `goal-map.html` now uses thinking.html's structure: sidebar and top-bar mounts, `style.css`, `shell.js`, the sky inside `<main id="hall-main" class="sky-stage">`, and a `<div class="brand">` because the shell's bar is the page's one `<header>`. In `thinking.css` both pages share one stage rule, `body[data-page] .sky-stage`. No hall page is standalone any more (`tests/hall_audit.mjs` `STANDALONE_EXEMPT` is empty).
24
29
 
25
- Tests: `tests/hall_fullscreen.mjs` (the standard switch, `plain`, Back link, fallback and exit cases), `tests/hall_record_world.mjs` §10 (the record places its exit), `tests/goal_map_page.mjs`, `tests/hall_audit.mjs` §8.
30
+ Tests: `tests/hall_fullscreen.mjs` (the standard switch, `plain`, Back link, trail, fallback and exit cases), `tests/hall_record_world.mjs` §10 (the record places its exit), `tests/goal_map_page.mjs`, `tests/hall_audit.mjs` §8.
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.26",
3
+ "version": "1.21.28",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.26",
9
+ "version": "1.21.28",
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.21.26",
3
+ "version": "1.21.28",
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",
@@ -8912,5 +8912,17 @@
8912
8912
  "id": "1003623",
8913
8913
  "text": "Fixed idea-triage cluster handling: 'keep #N' and 'merge #N' now parse; skipped clusters count toward the summary; spark ideas excluded from batch clustering; stale Step 1.5 refs now point at 1.5b. Tests added in tests/idea_tr"
8914
8914
  }
8915
+ ],
8916
+ "1.21.27": [
8917
+ {
8918
+ "id": "1004476",
8919
+ "text": "In the builders' hall, a page you reach through several clicks now shows the whole way back at the top, like Goals > goal 12 > task 340, and each step is a link. Clicking an earlier step takes you straight there; the sidebar s"
8920
+ }
8921
+ ],
8922
+ "1.21.28": [
8923
+ {
8924
+ "id": "1004492",
8925
+ "text": "The Builders Hall now asks what you are doing when you arrive: Building, Artistry, Ideating or Governing, or Legacy mode for the hall exactly as it was. It asks once per session (or never again, if you untick the box), artists"
8926
+ }
8915
8927
  ]
8916
8928
  }
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.26'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.28'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');