@bongos/core 1.24.44 → 1.24.46

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 (145) hide show
  1. package/.bongos-core.json +179 -144
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +4 -0
  4. package/clients/bongos-client/index.cjs +4 -0
  5. package/clients/bongos-client/index.d.ts +8 -0
  6. package/clients/bongos-client/index.mjs +4 -0
  7. package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +15 -0
  8. package/docs/api/openapi.json +152 -3
  9. package/docs/api-reference.md +4 -2
  10. package/docs/copy-inventory.md +1111 -1111
  11. package/docs/copy-registry.json +1547 -1547
  12. package/docs/module-api-changelog.md +4 -0
  13. package/docs/onboarding/diagrams/assertions.json +2 -2
  14. package/docs/page-inventory.json +11 -0
  15. package/docs/page-readings.json +823 -823
  16. package/migrations/core_295_runner_logins.sql +50 -0
  17. package/modules/government/session-scopes.json +5 -0
  18. package/modules/hall-ui/public/ai-studio.html +1 -1
  19. package/modules/hall-ui/public/ai-studio.states.json +2 -0
  20. package/modules/hall-ui/public/atlas.html +1 -1
  21. package/modules/hall-ui/public/atlas.states.json +2 -0
  22. package/modules/hall-ui/public/blockers.html +1 -1
  23. package/modules/hall-ui/public/board-room.html +1 -1
  24. package/modules/hall-ui/public/board-room.states.json +2 -0
  25. package/modules/hall-ui/public/city.html +1 -1
  26. package/modules/hall-ui/public/city.states.json +2 -0
  27. package/modules/hall-ui/public/collab.html +1 -1
  28. package/modules/hall-ui/public/collab.states.json +2 -0
  29. package/modules/hall-ui/public/commands.html +1 -1
  30. package/modules/hall-ui/public/commands.states.json +2 -0
  31. package/modules/hall-ui/public/copy-desk.html +1 -1
  32. package/modules/hall-ui/public/copy-desk.states.json +2 -0
  33. package/modules/hall-ui/public/craft-mode.js +4 -1
  34. package/modules/hall-ui/public/deploy.html +1 -1
  35. package/modules/hall-ui/public/deploy.states.json +2 -0
  36. package/modules/hall-ui/public/goal-map.html +1 -1
  37. package/modules/hall-ui/public/goal-map.states.json +2 -0
  38. package/modules/hall-ui/public/goals.css +1 -0
  39. package/modules/hall-ui/public/goals.html +1 -1
  40. package/modules/hall-ui/public/goals.states.json +2 -0
  41. package/modules/hall-ui/public/government.html +1 -1
  42. package/modules/hall-ui/public/government.states.json +2 -0
  43. package/modules/hall-ui/public/idea.html +1 -1
  44. package/modules/hall-ui/public/idea.states.json +2 -0
  45. package/modules/hall-ui/public/ideas.html +1 -1
  46. package/modules/hall-ui/public/ideas.states.json +2 -0
  47. package/modules/hall-ui/public/index-founding.states.json +2 -0
  48. package/modules/hall-ui/public/index-genesis-decide.states.json +2 -0
  49. package/modules/hall-ui/public/index-genesis-demo.states.json +2 -0
  50. package/modules/hall-ui/public/index-genesis.states.json +2 -0
  51. package/modules/hall-ui/public/index.html +1 -1
  52. package/modules/hall-ui/public/index.states.json +5 -0
  53. package/modules/hall-ui/public/market-watch-item.html +1 -1
  54. package/modules/hall-ui/public/market-watch-item.states.json +2 -0
  55. package/modules/hall-ui/public/market-watch-rules.html +1 -1
  56. package/modules/hall-ui/public/market-watch-rules.states.json +2 -0
  57. package/modules/hall-ui/public/market-watch.html +1 -1
  58. package/modules/hall-ui/public/market-watch.states.json +2 -0
  59. package/modules/hall-ui/public/modules.html +1 -1
  60. package/modules/hall-ui/public/modules.states.json +2 -0
  61. package/modules/hall-ui/public/money.html +1 -1
  62. package/modules/hall-ui/public/money.states.json +2 -0
  63. package/modules/hall-ui/public/my-agents.html +1 -1
  64. package/modules/hall-ui/public/my-agents.states.json +2 -0
  65. package/modules/hall-ui/public/notes-panel.css +1 -1
  66. package/modules/hall-ui/public/primer.html +1 -1
  67. package/modules/hall-ui/public/primer.states.json +2 -0
  68. package/modules/hall-ui/public/profile.html +1 -1
  69. package/modules/hall-ui/public/profile.states.json +2 -0
  70. package/modules/hall-ui/public/project-settings.html +1 -1
  71. package/modules/hall-ui/public/roster.html +1 -1
  72. package/modules/hall-ui/public/roster.states.json +2 -0
  73. package/modules/hall-ui/public/settings.html +1 -1
  74. package/modules/hall-ui/public/settings.states.json +2 -0
  75. package/modules/hall-ui/public/shell.js +21 -1
  76. package/modules/hall-ui/public/street-card.states.json +2 -0
  77. package/modules/hall-ui/public/street.html +1 -1
  78. package/modules/hall-ui/public/street.states.json +2 -0
  79. package/modules/hall-ui/public/studio.html +1 -1
  80. package/modules/hall-ui/public/studio.states.json +2 -0
  81. package/modules/hall-ui/public/style.css +27 -24
  82. package/modules/hall-ui/public/task.html +1 -1
  83. package/modules/hall-ui/public/task.states.json +2 -0
  84. package/modules/hall-ui/public/thinking.html +1 -1
  85. package/modules/hall-ui/public/thinking.states.json +2 -0
  86. package/modules/hall-ui/public/tweak-editor.html +1 -1
  87. package/modules/hall-ui/public/tweak-editor.states.json +2 -0
  88. package/modules/hall-ui/public/watch.html +1 -1
  89. package/modules/hall-ui/public/watch.states.json +2 -0
  90. package/modules/hall-ui/public/welcome-boot.js +6 -2
  91. package/modules/hall-ui/public/work-founding.states.json +2 -0
  92. package/modules/hall-ui/public/work.html +1 -1
  93. package/modules/hall-ui/public/work.states.json +3 -1
  94. package/modules/hall-ui/records/hall-navigation.md +5 -0
  95. package/modules/lifecycle/db-claims.js +6 -3
  96. package/modules/lifecycle/routes/claims.js +10 -0
  97. package/modules/lifecycle/routes/tasks.js +1 -1
  98. package/modules/lifecycle/runner-self-guard.js +19 -10
  99. package/modules/platform-identity/platform-identity.js +1 -1
  100. package/modules/platform-identity/tests/platform-identity.mjs +13 -2
  101. package/modules/provisioning/core-upgrade-authz.js +8 -0
  102. package/modules/public-landing/public/account.html +1 -0
  103. package/modules/public-landing/public/community.html +1 -0
  104. package/modules/public-landing/public/connections.html +1 -0
  105. package/modules/public-landing/public/contact.html +1 -0
  106. package/modules/public-landing/public/guild.html +1 -0
  107. package/modules/public-landing/public/index.html +1 -0
  108. package/modules/public-landing/public/invitations.html +1 -0
  109. package/modules/public-landing/public/privacy.html +1 -0
  110. package/modules/public-landing/public/projects.html +1 -0
  111. package/modules/public-landing/public/terms.html +1 -0
  112. package/modules/raise/views/gate.html +1 -0
  113. package/modules/raise/views/nda.html +1 -0
  114. package/modules/raise/views/page.html +1 -0
  115. package/modules/status-ui/public/index.html +1 -0
  116. package/modules/ui-design/kit/README.md +1 -1
  117. package/modules/ui-design/kit/fixtures/versions.json +1 -0
  118. package/modules/ui-design/kit/lib.js +48 -3
  119. package/modules/ui-design/kit/probe.js +1 -0
  120. package/modules/ui-design/kit/render.js +2 -0
  121. package/modules/ui-design/kit/serve.js +1 -0
  122. package/modules/ui-design/module.json +1 -1
  123. package/modules/ui-design/public/tokens.css +125 -0
  124. package/package-lock.json +2 -2
  125. package/package.json +1 -1
  126. package/release-notes.json +20 -0
  127. package/scripts/bongos/fitness.js +1 -1
  128. package/scripts/bongos/gen-hall-sitemap.js +3 -1
  129. package/scripts/bongos/run-unit-tests.js +3 -0
  130. package/src/bongos/auth.js +25 -4
  131. package/src/bongos/route-rank-check.js +5 -0
  132. package/src/bongos/routes/auth.js +48 -0
  133. package/src/bongos/runner-login.js +151 -0
  134. package/src/bongos/serve-internal.js +6 -1
  135. package/src/module-api.js +1 -1
  136. package/tests/autobongos_runner_login.mjs +296 -0
  137. package/tests/autobongos_runner_login_db.mjs +105 -0
  138. package/tests/craft_mode_nav.mjs +51 -3
  139. package/tests/design_tokens_shared.mjs +183 -0
  140. package/tests/government_session_scope.mjs +2 -1
  141. package/tests/hall_reading_world.mjs +5 -1
  142. package/tests/hall_tokens.mjs +22 -3
  143. package/tests/nav_permission_atoms.mjs +2 -1
  144. package/tests/ui_design_kit.mjs +30 -0
  145. package/tests/welcome_boot_browser.mjs +4 -0
@@ -11,7 +11,7 @@
11
11
  .np-note__body { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
12
12
  .np-note__meta { margin: var(--sp-xs) 0 0; font-size: 12px; color: var(--ink-soft); display: flex; gap: var(--sp-md); flex-wrap: wrap; align-items: center; }
13
13
  .np-move select { font-size: 12px; }
14
- .np-link { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; font-size: 12px; text-decoration: underline; }
14
+ .np-link { background: none; border: 0; padding: 0; color: var(--accent); cursor: pointer; font: inherit; font-size: 12px; text-decoration: underline; min-height: 24px; min-width: 24px; display: inline-flex; align-items: center; } /* the 24px target floor (task 1005080) */
15
15
  .np-empty { color: var(--ink-soft); }
16
16
  .np-form { margin: var(--sp-lg) 0 0; display: grid; gap: var(--sp-sm); }
17
17
  .np-field { display: grid; gap: var(--sp-xs); font-size: 13px; }
@@ -113,7 +113,7 @@
113
113
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
114
114
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
115
115
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
116
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
116
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
117
117
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
118
118
  <script src="/builders/palette.js" defer></script>
119
119
  <!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The hall's primer through modules/ui-design/kit/render.js (task 1003306). Written with the reading-room re-skin so the room's failure state and its table of contents are MEASURED at 1440/390/320 in both modes, not just looked at.",
3
3
  "page": "/builders/primer",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -249,7 +249,7 @@
249
249
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
250
250
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
251
251
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
252
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
252
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
253
253
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
254
254
  <script src="/builders/palette.js" defer></script>
255
255
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The hall's PROFILE page through modules/ui-design/kit/render.js (task 1003308; four screens behind the section rail since task 1003489 — overview, works, activity, connections). The identity band, the charts, the works ledger and the activity ledgers only exist once a profile has LOADED, so the floors run against the hall-preview harness (see settings.states.json for the two commands; pass --base-in/--base-out at it). One rail key renders at a time, so each key is its own state. `out` is a real state here rather than a redirect: a signed-out visitor with no ?id= gets the empty state, not a bounce — under the harness's --fixture-me the /me read still answers, so `out` renders the own profile there and the empty state only under the kit's own stub.",
3
3
  "page": "/profile",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -77,7 +77,7 @@
77
77
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
78
78
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
79
79
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
80
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
80
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
81
81
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
82
82
  <script src="/builders/palette.js" defer></script>
83
83
  <script src="/builders/hall-kit.js"></script>
@@ -59,7 +59,7 @@
59
59
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
62
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "Builders — this project's OWN roster, ranked by credits earned (task 1003441, goal 1000075). Was the #leaderboard hash view on index.html; /#leaderboard now redirects here. Deliberately UNGATED, because its one data source (GET /leaderboard) is deliberately unauthenticated and mirrors the public status surface — so unlike a gated page's states file the `out` state below renders the real page rather than a seal. Populated look: the hall-preview harness (scripts/hall-preview/fixtures/public__leaderboard.json, since task 1004437 the list the page reads: fifteen builders with avatars, ranks, karma, tokens and each one's roles, including an artist and an ideator with no AI sessions).",
3
3
  "page": "/builders/roster",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -394,7 +394,7 @@
394
394
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
395
395
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
396
396
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
397
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
397
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
398
398
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
399
399
  <script src="/builders/palette.js" defer></script>
400
400
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The hall's SETTINGS page through modules/ui-design/kit/render.js (task 1003308). Every panel on it is a form the page script fills from a dozen /me/* reads, so the floors run against the hall-preview harness rather than the kit's own stub:\n\n node scripts/hall-preview/server.js --port 4621 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/settings.states.json --base-in http://builders.localhost:4621 --base-out http://builders.localhost:4621\n\nOne group renders at a time behind the section rail, so each rail group is its own state. There is no `out` state on purpose: settings.js answers a 401 by navigating to the sign-in flow, and a state whose page navigates away measures the destination, not this page (the not-signed-in gate panel is the non-401 failure render, pinned statically in tests/hall_settings_world.mjs).",
3
3
  "page": "/settings",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": { "prefix": "/builders/" },
6
8
  "modeQuery": false,
@@ -877,6 +877,23 @@
877
877
  // task 1004685: `positions` (seats held, null while unknown), `simRank` (?as=nobody's).
878
878
  var access = { authed: null, rank: null, graduated: false, showWork: false, permissions: null, needs: null, founding: null, positions: null, simRank: null };
879
879
 
880
+ // THE REMEMBERED NAV (task 1005070): pages are full loads and the gate waits on /me, so the rail booted empty. The last
881
+ // settled access paints at boot, /me settles the rest; cosmetic (ADR 0016), no needs roster; a 401/403 or sign-out forgets it.
882
+ var NAV_KEY = 'hall-nav-access', NAV_TTL = 24 * 60 * 60 * 1000;
883
+ function rememberAccess(b) {
884
+ if (access.authed !== true || !b || b.id == null) return;
885
+ var who = { id: b.id, rank: b.rank, display_name: b.display_name, github_login: b.github_login, avatar_url: b.avatar_url, preferred_disciplines: b.preferred_disciplines };
886
+ try { window.localStorage.setItem(NAV_KEY, JSON.stringify({ at: Date.now(), rank: access.rank, graduated: access.graduated, showWork: access.showWork, permissions: access.permissions, builder: who })); } catch (_) { /* private mode: wait for /me */ }
887
+ }
888
+ function forgetAccess() { try { window.localStorage.removeItem(NAV_KEY); } catch (_) { /* nothing kept */ } }
889
+ function recallAccess() {
890
+ var c; try { c = JSON.parse(window.localStorage.getItem(NAV_KEY) || 'null'); } catch (_) { return; }
891
+ if (!c || !c.builder || c.builder.id == null || !(Date.now() - c.at < NAV_TTL)) return;
892
+ access.authed = true; access.rank = c.rank ? String(c.rank).toLowerCase() : null; access.graduated = !!c.graduated; access.showWork = !!c.showWork;
893
+ access.permissions = (c.permissions && typeof c.permissions === 'object') ? c.permissions : null;
894
+ renderUser(c.builder); applyCraftEmphasis(c.builder);
895
+ }
896
+
880
897
  function gateAllows(gate) {
881
898
  var authed = access.authed === true;
882
899
  // ?as=nobody's stand-in rank only ever sits BELOW the real one (gov-positions.js).
@@ -1154,6 +1171,7 @@
1154
1171
  if (state.builder) { renderUser(state.builder); applyCraftEmphasis(state.builder); }
1155
1172
  // task 1004713: which seats this builder holds — /me's server-resolved block.
1156
1173
  if (state.builder && access.authed === true) { loadPositions(state.builder, state.positions); loadGithubChip(state.builder); }
1174
+ if (state.builder && access.authed === true) rememberAccess(state.builder); else if (access.authed === false) forgetAccess(); // task 1005070
1157
1175
  }
1158
1176
  document.querySelectorAll('#app-sidebar [data-gate]').forEach(function (el) {
1159
1177
  var ok = gateAllows(el.dataset.gate);
@@ -1301,6 +1319,7 @@
1301
1319
  // live here, not only on the index (builders.js used to own it alone). Convert
1302
1320
  // the click into the real POST, then bounce home.
1303
1321
  function doLogout() {
1322
+ forgetAccess(); // task 1005070: the next visitor's rail is not this builder's
1304
1323
  var finish = function () {
1305
1324
  // Mirror the SERVER logout (src/bongos/routes/auth.js): expire BOTH the
1306
1325
  // current gds_session and the legacy pms_session, each in BOTH the
@@ -1396,7 +1415,7 @@
1396
1415
  // the governor pages' living model reads /me too: it takes this answer (gov-feeds.js)
1397
1416
  HALL_READS['/me'] = meRead.then(function (res) { return res && res.ok ? res.data : (res && res.status === 403 ? false : null); }, function () { return null; });
1398
1417
  meRead.then(function (res) {
1399
- if (res.status === 401) { applyAccess({ authed: false }); return null; }
1418
+ if (res.status === 401 || res.status === 403) { applyAccess({ authed: false }); return null; }
1400
1419
  if (!res.ok) return null;
1401
1420
  return res.data;
1402
1421
  }).then(function (me) {
@@ -1442,6 +1461,7 @@
1442
1461
  applyZoom();
1443
1462
  if (CRAFT) CRAFT.mount();
1444
1463
  applyFullscreen(storedFullscreen());
1464
+ recallAccess(); // task 1005070: the last settled nav, so the rail paints before /me
1445
1465
  applyAccess(null); // default: everything gated until access is known
1446
1466
  wire();
1447
1467
  TRAIL.fillExits();
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "THE MATTER CARD THAT ACTS (task 1004528, goal 1000125) on the street page, through modules/ui-design/kit/render.js. Kept beside street.states.json rather than inside it, so the street and its interiors (task 1004526) and the card each own their states. The populated card needs the hall-preview harness, whose government__docket.json puts the review-seat amendment on the board source (board:47) and whose government__board__items.json carries its ballot (item 47), with a canned answer for a vote:\n\n node scripts/hall-preview/server.js --port 4625 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/street-card.states.json --base-in http://localhost:4625 --base-out http://localhost:4625\n\nTask 1004566 adds a card per kind the docket carries (blocker, red-team report, core update, module re-check, a Full Idea and the review queue), each compact and with \"more\" open; the harness fixture carries them. Against the kit stub there is no docket and no pin, so these states are for the harness.",
3
3
  "page": "/builders/street",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -231,7 +231,7 @@
231
231
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
232
232
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
233
233
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
234
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
234
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
235
235
  <script src="/builders/palette.js" defer></script>
236
236
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
237
237
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "THE STREET (task 1004525, goal 1000125) and inside every building and district (task 1004526) through modules/ui-design/kit/render.js. The page draws GET /government/docket, GET /goals, GET /goals/rollups and the viewer's GET /builders/:id/earnings; against the kit's own stub none exists, so every state renders the bare street (the nine civic buildings, no districts, no pins) with the readings saying the docket could not be read: a real state the floors must hold in. To look at the populated street, run it against the hall-preview harness, whose government__docket.json fixture carries the reference design's seven sample matters (3 need you, 2 moving, 2 settled) and whose goals fixtures carry the open goals:\n\n node scripts/hall-preview/server.js --port 4625 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/street.states.json --base-in http://localhost:4625 --base-out http://localhost:4625\n\nThe page follows the OS mode until the switch is pressed (then the hall's own 'hall-theme' preference), so the kit's dark/light emulation renders both modes.",
3
3
  "page": "/builders/street",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/",
@@ -225,7 +225,7 @@
225
225
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
226
226
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
227
227
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
228
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
228
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
229
229
  <script src="/builders/palette.js" defer></script>
230
230
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
231
231
  (task 1003829), which the older reviews still show. -->
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The studio home (task 1003971 / BV2.TW16), built 1:1 on docs/design/mocks/tweak-mode/Main.dc.html: the night room, the greeting and its sub-line (editable in place as a tweak of builders:studio), the tally, Resume, and three quick actions opening glass panels (the Tweak/CopyWrite Flow, the Approval queue, Artist Review Status). Behind a quiet door, the older reviews (R06 of goal 1000074, task 1003117; retired in TW14) still answer the reviews filed before Tweak Mode. Against the kit's own stub the page renders its unread states (no copy-desk feeds). TO SEE IT POPULATED, point the kit at the hall-preview harness: `node scripts/hall-preview/server.js --port 4636 --fixture-me` then `render.js --base-in http://builders.localhost:4636 --base-out http://builders.localhost:4636`. The harness answers the tally, the pages and the next page from copy-desk__tally.json, copy-desk__pages.json and copy-desk__next.json (built by the real composers), Resume's counter from copy-desk__pages__draft.json, the studio's own lines from copy-desk__pages__draft-studio.json, the claim and the draft save from its CANNED WRITES, and the older reviews from tasks.json (whose first row IS a cascade artist review).",
3
3
  "page": "/builders/studio",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -56,6 +56,10 @@
56
56
  here. It lives on the shell rules below and on a page's own body, because a
57
57
  body declaration beats an inherited :root value at every width and a token
58
58
  tier that carries a breakpoint silently defeats its own media query. */
59
+ /* The shared token sheet (task 1005081): the scales — type, spacing, corners,
60
+ shadows, motion, stacking — every surface loads. Imported FIRST so this
61
+ sheet's own rules follow it; the hall's scale names below are aliases of it. */
62
+ @import url("/modules/ui-design/public/tokens.css");
59
63
  @import url("page-notes.css");
60
64
  @import url("craft-review.css");
61
65
 
@@ -158,7 +162,7 @@
158
162
  thin on black, so light takes the mono and dark takes the body face.
159
163
  Take the token, never the family. ---- */
160
164
  --label-face: var(--font-mono);
161
- --label-size: 11px;
165
+ --label-size: var(--ds-text-2xs);
162
166
  --label-space: .12em;
163
167
  --label-case: uppercase;
164
168
 
@@ -166,11 +170,12 @@
166
170
  there is nothing behind it. Never a coloured glow. ---- */
167
171
  --type-halo: none;
168
172
 
169
- /* ---- structure. --line is a WIDTH (note c). ---- */
170
- --line: 1px;
171
- --radius: 16px;
172
- --radius-sm: 10px;
173
- --radius-pill: 999px;
173
+ /* ---- structure. --line is a WIDTH (note c). The values live in the shared
174
+ token sheet (task 1005081); these names are the hall's aliases. ---- */
175
+ --line: var(--ds-line);
176
+ --radius: var(--ds-radius-panel);
177
+ --radius-sm: var(--ds-radius-tile);
178
+ --radius-pill: var(--ds-radius-pill);
174
179
 
175
180
  /* ---- the shell's metrics. --topbar-h stays under the kit's 80px top-bar
176
181
  floor; --sidebar-w IS the rail, because v3 pins the rail at every
@@ -195,12 +200,12 @@
195
200
  /* ---- spacing: DESIGN.md's rhythm (6/10/14/22/34/52). The four-step
196
201
  --space-* scale task 2179 named aliases onto it, so no ported rule
197
202
  breaks; hairlines separate, padding does not. ---- */
198
- --sp-hair: 6px;
199
- --sp-xs: 10px;
200
- --sp-sm: 14px;
201
- --sp-md: 22px;
202
- --sp-lg: 34px;
203
- --sp-xl: 52px;
203
+ --sp-hair: var(--ds-space-hair);
204
+ --sp-xs: var(--ds-space-xs);
205
+ --sp-sm: var(--ds-space-sm);
206
+ --sp-md: var(--ds-space-md);
207
+ --sp-lg: var(--ds-space-lg);
208
+ --sp-xl: var(--ds-space-xl);
204
209
  --space-1: var(--sp-hair);
205
210
  --space-2: var(--sp-xs);
206
211
  --space-3: var(--sp-sm);
@@ -216,7 +221,7 @@
216
221
  /* The world has no shadow vocabulary for UI, so this is spent only where an
217
222
  element floats over a scrim (the phone drawer, the deep-link card). Pure
218
223
  black with alpha is the one literal the Fifteen Rule allows, as a scrim. */
219
- --shadow-pop: 0 18px 40px rgba(0, 0, 0, .28);
224
+ --shadow-pop: var(--ds-shadow-pop);
220
225
 
221
226
  /* ---- z ladder — content overlays (rank tips z-50, modals z-50, toast z-100,
222
227
  copy-toast z-1000, deeplink z-2000) must beat the chrome; the mobile drawer
@@ -225,17 +230,17 @@
225
230
  reading hairline sits UNDER the topbar so it reads as the article's
226
231
  progress and not as app chrome, and the diagram's fullscreen canvas is the
227
232
  top of the whole ladder because it covers the shell entirely. ---- */
228
- --z-progress: 25;
229
- --z-topbar: 30;
230
- --z-sidebar: 40;
231
- --z-scrim: 140;
232
- --z-drawer: 150;
233
+ --z-progress: var(--ds-z-progress);
234
+ --z-topbar: var(--ds-z-topbar);
235
+ --z-sidebar: var(--ds-z-sidebar);
236
+ --z-scrim: var(--ds-z-scrim);
237
+ --z-drawer: var(--ds-z-drawer);
233
238
  /* A confirm dialog is the one thing that must sit above the drawer: it is
234
239
  asking a question the drawer's content cannot answer. Added by task 1003307
235
240
  because two pages had each guessed a bare number for exactly this
236
241
  (200 and 50), which is what a missing layer looks like. */
237
- --z-modal: 160;
238
- --z-diagram-full: 9999;
242
+ --z-modal: var(--ds-z-modal);
243
+ --z-diagram-full: var(--ds-z-diagram-full);
239
244
 
240
245
  /* ---- the hall's own plate: the declared seam, deferred by the owner
241
246
  ("decide after the shell lands", 2026-08-30). The hall's plate was
@@ -286,11 +291,10 @@ html[data-theme="dark"] {
286
291
  --info-weak: color-mix(in srgb, var(--info) 14%, var(--bg-card));
287
292
  --focus: var(--accent);
288
293
  --label-face: var(--font-body);
289
- --label-size: 11.5px;
294
+ --label-size: var(--ds-text-2xs-plus);
290
295
  --label-space: .1em;
291
296
  --type-halo: 0 2px 18px rgba(0, 0, 0, .94), 0 0 54px rgba(0, 0, 0, .72);
292
297
  --surface-2: color-mix(in srgb, var(--ink) 6%, var(--bg-card));
293
- --shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
294
298
  --plate-veil: linear-gradient(to bottom, rgba(0, 0, 0, .82), rgba(0, 0, 0, .93));
295
299
  }
296
300
  html[data-theme="dark"] body { background: var(--bg); }
@@ -327,11 +331,10 @@ html[data-theme="dark"] body { background: var(--bg); }
327
331
  --info-weak: color-mix(in srgb, var(--info) 14%, var(--bg-card));
328
332
  --focus: var(--accent);
329
333
  --label-face: var(--font-body);
330
- --label-size: 11.5px;
334
+ --label-size: var(--ds-text-2xs-plus);
331
335
  --label-space: .1em;
332
336
  --type-halo: 0 2px 18px rgba(0, 0, 0, .94), 0 0 54px rgba(0, 0, 0, .72);
333
337
  --surface-2: color-mix(in srgb, var(--ink) 6%, var(--bg-card));
334
- --shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
335
338
  --plate-veil: linear-gradient(to bottom, rgba(0, 0, 0, .82), rgba(0, 0, 0, .93));
336
339
  }
337
340
  html:not([data-theme]) body { background: var(--bg); }
@@ -59,7 +59,7 @@
59
59
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
62
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The hall's TASK RECORD through modules/ui-design/kit/render.js (task 1003305). The record's own controls — the dependency links, the lifecycle buttons, the figure band, the way back — only exist once a task has LOADED, so the floors here are run against the hall-preview harness rather than the kit's own stub, which carries the public-landing feeds and not the hall's:\n\n node scripts/hall-preview/server.js --port 4612 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/task.states.json --base-in http://builders.localhost:4612 --base-out http://builders.localhost:4612\n\nRun against the kit's own stubs instead and every state renders `no such task`, which is a real state and is the `missing` one below — just not the one the re-skin changed.",
3
3
  "page": "/task/2166",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": { "prefix": "/builders/" },
6
8
  "modeQuery": false,
@@ -141,7 +141,7 @@
141
141
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
142
142
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
143
143
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
144
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
144
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
145
145
  <script src="/builders/palette.js" defer></script>
146
146
 
147
147
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "THE SKY (task 1004231 / sky part 3 of 5, goal 1000095; ADR 0331) through modules/ui-design/kit/render.js. The page draws GET /sky + GET /sky/mine; against the kit's own stub neither exists, so every state below renders the EMPTY sky — a real state the floors must hold in (the composer, the tour, the list drawer and the lenses all exist with no thought filed). To look at the populated sky, run it against the hall-preview harness, whose sky.json + sky__mine.json fixtures carry the wells and one of each of the five objects:\n\n node scripts/hall-preview/server.js --port 4615 --fixture-me\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/thinking.states.json --base-in http://localhost:4615 --base-out http://localhost:4615\n\nThe tour opens by itself on a first visit (sky-tour-seen), so states that want the bare sky skip it first.\n\nIN THE HALL (task 1004327): the page sits in the hall chrome, so it renders in BOTH modes — the chrome follows the hall theme and the sky's stage stays the void in both. The full-* states press the shell's full-screen switch: the sidebar and the top bar leave, and the one button that brings them back floats top right.",
3
3
  "page": "/builders/thinking",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": { "prefix": "/builders/" },
6
8
  "modeQuery": false,
@@ -145,7 +145,7 @@
145
145
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
146
146
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
147
147
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
148
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
148
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
149
149
  <script src="/builders/palette.js" defer></script>
150
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
151
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "The tweak editor (task 1004321 / BV2.TW10), built 1:1 on docs/design/mocks/tweak-mode/Editor.dc.html. Against the kit's own stub the page renders its could-not-read state (no /copy-desk feed), a state the floors must hold in. TO SEE IT POPULATED, point the kit at the hall-preview harness: `node scripts/hall-preview/server.js --port 4631 --fixture-me`, then `render.js --page '/builders/tweak-editor?page=landing:index' --base-in http://builders.localhost:4631 --base-out http://builders.localhost:4631`. The harness answers the read from fixtures/copy-desk__pages__draft.json (the real landing:index reading with the artboard's two rewrites as the saved draft), next up from copy-desk__next.json, and the autosave, the submit and the claim from its canned writes.",
3
3
  "page": "/builders/tweak-editor?page=landing:index",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -271,7 +271,7 @@
271
271
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
272
272
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
273
273
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
274
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
274
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
275
275
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
276
276
  <script src="/builders/palette.js" defer></script>
277
277
  <!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "Watch — the attention board behind requireGovernmentPage. The SEALED state (a viewer below the gate) is a real render, not an error page, so it is a state here. Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once.",
3
3
  "page": "/builders/watch",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/"
@@ -166,14 +166,18 @@
166
166
 
167
167
  var root = null, timers = [], returnFocus = null, shown = false, leaving = false;
168
168
 
169
+ function bannerTag(builder) { return builder ? 'div' : 'header'; }
169
170
  function overlayHtml(builder, now, when) {
170
171
  return '<div class="wb__art" aria-hidden="true"><div class="wb__pan">' +
171
172
  '<img class="wb__img" alt="" src="' + artFor(when) + '" decoding="async">' +
172
173
  '<div class="wb__fx" id="wb-fx"></div>' +
173
174
  '</div></div>' +
174
175
  '<div class="wb__cloud wb__cloud--a" aria-hidden="true"></div><div class="wb__cloud wb__cloud--b" aria-hidden="true"></div>' +
175
- '<div class="wb__banner">' + logoHtml(window.__BRANDING__) +
176
- '<span class="wb__version" id="wb-version" hidden></span></div>' +
176
+ // For a visitor the welcome IS the page (the shell draws no header for them), so its logo
177
+ // banner is the page's one <header>; over a signed-in hall the shell's header stands, and the
178
+ // banner stays a plain block (task 1005080).
179
+ '<' + bannerTag(builder) + ' class="wb__banner">' + logoHtml(window.__BRANDING__) +
180
+ '<span class="wb__version" id="wb-version" hidden></span></' + bannerTag(builder) + '>' +
177
181
  '<div class="wb__veil" aria-hidden="true"></div>' +
178
182
  '<button class="wb__skip" type="button" data-wb-skip>Skip</button>' +
179
183
  '<a class="wb__built" href="' + BUILT_ON.href + '" target="_blank" rel="noopener">Built on <b>' + BUILT_ON.label + '</b></a>' +
@@ -1,6 +1,8 @@
1
1
  {
2
2
  "_": "Project founding mode (task 1004422) on an ordinary hall page: the founding band above the topbar and today's nav items asleep, rendered from a /me that says founding (kit fixture me-founding.json, the real server shaper's output at stage strategize). The page itself is work.states.json's; only the identity differs. The no-founding case is every other hall states file.",
3
3
  "page": "/builders/work",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": {
6
8
  "prefix": "/builders/",
@@ -174,7 +174,7 @@
174
174
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
175
175
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
176
176
  <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
177
- <script src="/builders/shell.js?v=2026-11-05-goals"></script>
177
+ <script src="/builders/shell.js?v=2026-11-06-sidebar"></script>
178
178
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
179
179
  <script src="/builders/palette.js" defer></script>
180
180
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "_": "The hall's Work Board through modules/ui-design/kit/render.js (task 1003304). Written with the re-skin so the board's four tabs and its selection state are measured at 1440/390/320 in both modes, not just looked at. The populated look is the hall-preview harness (scripts/hall-preview/server.js); this file is the FLOORS.",
3
3
  "page": "/builders/work",
4
+ "_storage": "The hall asks a first-visit question (the session mode picker, craft-mode.js) over every page; seeding \"already asked\" renders the page a returning builder sees (task 1005080).",
5
+ "storage": { "session": { "hall-craft-asked": "1" } },
4
6
  "surface": "hall-ui",
5
7
  "stub": { "prefix": "/builders/" },
6
8
  "modeQuery": false,
7
9
  "_feeds": "The kit's stub carries a small fixture set, not the hall's task feeds, so the board renders its EMPTY and SKELETON states here — which is the point: an empty queue is a state the floors must hold in. The feed 404s are declared, never hidden.",
8
- "ignoreRequests": ["/api/(gds|bongos)/me$", "/api/(gds|bongos)/me/", "/api/(gds|bongos)/tasks", "/api/(gds|bongos)/claims", "/api/(gds|bongos)/goals", "/api/(gds|bongos)/inbox/", "/api/(gds|bongos)/versions"],
10
+ "ignoreRequests": ["/api/(gds|bongos)/live", "/api/(gds|bongos)/me$", "/api/(gds|bongos)/me/", "/api/(gds|bongos)/tasks", "/api/(gds|bongos)/claims", "/api/(gds|bongos)/goals", "/api/(gds|bongos)/inbox/", "/api/(gds|bongos)/versions"],
9
11
  "ignoreConsole": ["/api/(gds|bongos)/", "\\b401\\b", "\\b404\\b", "Unauthorized", "Not Found", "load failed"],
10
12
  "states": {
11
13
  "ready": { "auth": true, "expect": { "visible": ["#claim-scroll"], "hidden": ["#wip-scroll", "#backlog-scroll", "#history-scroll"] } },
@@ -28,3 +28,8 @@ Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html`
28
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).
29
29
 
30
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.
31
+
32
+ ## The rail paints at once, and Home is one button (task 1005070)
33
+
34
+ - Every hall page is a full load, and the gate waits on `/me`, so the rail used to boot with only Home and pop the rest in once `/me` answered — longest for the owner, whose `/me` is the heaviest. `shell.js` now keeps the last settled access in `localStorage['hall-nav-access']` (rank, graduated/showWork, the nav permission map, the user chip; never the needs roster, so no badge is stale) and paints from it at boot. `/me` still settles the real state. It is forgotten on any signed-out settle (a 401 or a refused 403) and by the sign-out link, and is not painted once a day old.
35
+ - In a craft the plate under the brand is the Home button (task 1005065), so no mode's tab list names `home` — Building used to, which drew two home icons. With no craft chosen there is no plate and the Home tab shows. Pinned in `tests/craft_mode_nav.mjs`.
@@ -108,7 +108,7 @@ function isRebaseGateExempt(task) {
108
108
  return Boolean(task) && task.kind === 'infra' && Number(task.priority) === 1;
109
109
  }
110
110
 
111
- async function claimTask({ taskId, builderId, worktreeName, headSha, creatorSessionId, enforceOverlap = false, autoPickup = false }) {
111
+ async function claimTask({ taskId, builderId, worktreeName, headSha, creatorSessionId, enforceOverlap = false, autoPickup = false, autonomous = false }) {
112
112
  const heldPermissions = await claimPermissionsFor(builderId); // BV1.R106 — before the tx opens
113
113
  // ADR 0371 D5: a marked claim needs the project's switch and cap, read before the
114
114
  // tx like the permissions above; the COUNT runs inside it, under the lock.
@@ -433,9 +433,12 @@ async function claimTask({ taskId, builderId, worktreeName, headSha, creatorSess
433
433
 
434
434
  // The column is named only on a marked claim, so an ordinary claim's insert is
435
435
  // byte-identical to before core_275.
436
+ // `autonomous` (core_295, task 1004547) is the server's mark that an Autobongos
437
+ // runner's own login made this claim — set by the claims route from the SESSION,
438
+ // never from the body. Like auto_pickup, the column is named only when true.
436
439
  const insertClaim = await client.query(
437
- `INSERT INTO claims (task_id, builder_id, worktree_name, requested_worktree_name, head_sha, reserved_migration_number, creator_session_id${autoPickup ? ', auto_pickup' : ''})
438
- VALUES ($1, $2, $3, $4, $5, $6, $7${autoPickup ? ', TRUE' : ''})
440
+ `INSERT INTO claims (task_id, builder_id, worktree_name, requested_worktree_name, head_sha, reserved_migration_number, creator_session_id${autoPickup ? ', auto_pickup' : ''}${autonomous ? ', autonomous' : ''})
441
+ VALUES ($1, $2, $3, $4, $5, $6, $7${autoPickup ? ', TRUE' : ''}${autonomous ? ', TRUE' : ''})
439
442
  RETURNING id, task_id, builder_id, worktree_name, requested_worktree_name, claimed_at, head_sha, reserved_migration_number`,
440
443
  [taskId, builderId, finalWorktreeName, requestedWorktreeName, headSha ?? null, reservedMigrationNumber, creatorSessionId ?? null]
441
444
  );
@@ -305,6 +305,10 @@ module.exports = function buildClaimsRouter() {
305
305
  creatorSessionId: resolveCreatorSessionId(bindSession, req.gdsSession?.session_id),
306
306
  enforceOverlap: enforceTouches === true,
307
307
  autoPickup: autoPickup === true,
308
+ // task 1004547: an Autobongos runner's own login marks its claim
309
+ // autonomous. Read from the SESSION (auth.requireBuilder), never the body,
310
+ // so a worker cannot claim its way out of the runner-only walls.
311
+ autonomous: !!(req.builder && req.builder.runner),
308
312
  });
309
313
  const { just_unlocked: justUnlocked, overlap_warning: overlapWarning, sibling_overlap_warning: siblingOverlapWarning, page_notice: pageNotice, ...claim } = claimResult;
310
314
  const task = await db.getTask(Number(taskId));
@@ -476,6 +480,12 @@ module.exports = function buildClaimsRouter() {
476
480
  if (taskIds.length === 0) {
477
481
  return res.fail('task_ids_required', 400);
478
482
  }
483
+ // task 1004547: a batch claim binds only its first row to the session, so it
484
+ // cannot carry the autonomous mark row by row. A runner works one task per
485
+ // worker and never batch-claims, so its own login is refused here outright.
486
+ if (req.builder && req.builder.runner) {
487
+ return res.fail('runner_batch_refused', { status: 403, message: 'An Autobongos runner claims one task at a time. Use POST /claims.' });
488
+ }
479
489
  if (taskIds.length > 20) {
480
490
  // Safety cap. The optimizer doesn't return more than a handful at a time,
481
491
  // and unlimited batches let one slow transaction lock up the table.
@@ -1184,7 +1184,7 @@ module.exports = function buildTasksRouter() {
1184
1184
  `Merging triggers .github/workflows/deploy-prod.yml -> ${branding().domains.publicOrigin}. ` +
1185
1185
  prMergeSentence(held);
1186
1186
  const pr = await githubPush.openOrFindPullRequest({ branch, title, body });
1187
- const prReads = sharedPrReads(); for (const [guard, refusalFor] of [[autoPickupFiles, autoPickupFiles.autoPickupFileRefusal], [runnerSelfGuard, runnerSelfGuard.runnerSelfEditRefusal]]) { const refusal = await refusalFor({ taskId: id, prNumber: pr.number, branch }, { getPullRequestFiles: prReads.getPullRequestFiles }); if (refusal) { await githubPrClose.closePullRequest({ number: pr.number }).catch(() => null); return res.fail(refusal.code, 409, { ...refusal, message: guard.refusalMessage(refusal) }); } } // the PR's own files: an auto-pickup round stays on its page (ADR 0371 D8, task 1004678); a runner never changes its own files (task 1004546)
1187
+ const prReads = sharedPrReads(); for (const [guard, refusalFor] of [[autoPickupFiles, autoPickupFiles.autoPickupFileRefusal], [runnerSelfGuard, runnerSelfGuard.runnerSelfEditRefusal]]) { const refusal = await refusalFor({ taskId: id, prNumber: pr.number, branch, publisherIsRunner: !!(req.builder && req.builder.runner) }, { getPullRequestFiles: prReads.getPullRequestFiles }); if (refusal) { await githubPrClose.closePullRequest({ number: pr.number }).catch(() => null); return res.fail(refusal.code, 409, { ...refusal, message: guard.refusalMessage(refusal) }); } } // the PR's own files: an auto-pickup round stays on its page (ADR 0371 D8, task 1004678); a runner never changes its own files (task 1004546), whoever's claim it is when a runner login publishes (task 1004547)
1188
1188
  // Best-effort by contract (pr-assign.js never rejects); the catch is for a future
1189
1189
  // edit that forgets — an assignment must not be able to fail a pushed ship.
1190
1190
  const assignment = await prAssign.assignPullRequest({ number: pr.number, login: builderLogin })