@bongos/core 1.24.41 → 1.24.42

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 (61) hide show
  1. package/.bongos-core.json +80 -65
  2. package/docs/copy-inventory.md +91 -112
  3. package/docs/copy-registry.json +177 -385
  4. package/docs/module-api-changelog.md +2 -0
  5. package/docs/page-readings.json +214 -213
  6. package/modules/hall-ui/public/ai-studio.html +1 -1
  7. package/modules/hall-ui/public/atlas.html +1 -1
  8. package/modules/hall-ui/public/blockers.html +1 -1
  9. package/modules/hall-ui/public/board-room.html +1 -1
  10. package/modules/hall-ui/public/builders.js +17 -262
  11. package/modules/hall-ui/public/city.html +1 -1
  12. package/modules/hall-ui/public/collab.html +1 -1
  13. package/modules/hall-ui/public/commands.html +1 -1
  14. package/modules/hall-ui/public/copy-desk.html +1 -1
  15. package/modules/hall-ui/public/deploy.html +1 -1
  16. package/modules/hall-ui/public/goal-map.html +1 -1
  17. package/modules/hall-ui/public/goals.html +1 -1
  18. package/modules/hall-ui/public/government.html +1 -1
  19. package/modules/hall-ui/public/hall-render.js +3 -248
  20. package/modules/hall-ui/public/hall-widgets.js +3 -14
  21. package/modules/hall-ui/public/idea.html +1 -1
  22. package/modules/hall-ui/public/ideas.html +1 -1
  23. package/modules/hall-ui/public/index.html +3 -4
  24. package/modules/hall-ui/public/market-watch-item.html +1 -1
  25. package/modules/hall-ui/public/market-watch-rules.html +1 -1
  26. package/modules/hall-ui/public/market-watch.html +1 -1
  27. package/modules/hall-ui/public/modules.html +1 -1
  28. package/modules/hall-ui/public/money.html +1 -1
  29. package/modules/hall-ui/public/my-agents.html +1 -1
  30. package/modules/hall-ui/public/primer.html +1 -1
  31. package/modules/hall-ui/public/profile.html +1 -1
  32. package/modules/hall-ui/public/project-settings.html +1 -1
  33. package/modules/hall-ui/public/roadmap.html +1 -1
  34. package/modules/hall-ui/public/roster.html +1 -1
  35. package/modules/hall-ui/public/settings.html +1 -1
  36. package/modules/hall-ui/public/setup-gate.js +156 -0
  37. package/modules/hall-ui/public/shell.js +16 -1
  38. package/modules/hall-ui/public/street.html +1 -1
  39. package/modules/hall-ui/public/studio.html +1 -1
  40. package/modules/hall-ui/public/style.css +85 -117
  41. package/modules/hall-ui/public/task.html +1 -1
  42. package/modules/hall-ui/public/thinking.html +1 -1
  43. package/modules/hall-ui/public/tweak-editor.html +1 -1
  44. package/modules/hall-ui/public/watch.html +1 -1
  45. package/modules/hall-ui/public/work.html +1 -1
  46. package/modules/hall-ui/records/setup-gate.md +43 -0
  47. package/modules/lifecycle/routes/tasks.js +2 -2
  48. package/modules/onboarding/onboarding-state.js +41 -20
  49. package/package-lock.json +2 -2
  50. package/package.json +1 -1
  51. package/release-notes.json +6 -0
  52. package/src/bongos/routes/me.js +2 -2
  53. package/src/module-api.js +1 -1
  54. package/tests/hall_board_world.mjs +1 -1
  55. package/tests/hall_landing_boot.mjs +2 -3
  56. package/tests/hall_onboarding_bongos_setup.mjs +14 -14
  57. package/tests/module_contributions.mjs +4 -2
  58. package/tests/nav_permission_atoms.mjs +3 -2
  59. package/tests/onboarding_state.mjs +33 -0
  60. package/tests/setup_flow.mjs +242 -0
  61. package/tests/welcome_boot.mjs +3 -2
@@ -804,6 +804,16 @@
804
804
  }
805
805
  if (data && data.moved) renderCoreMoveNotice(data.moved); // task 1004731: loads core-move-notice.js, which draws it
806
806
  }
807
+ // task 1005076: a builder whose setup is not complete sees only the setup screen (setup-gate.js),
808
+ // loaded once: applyAccess runs again before the first load lands, and a second copy drew a second screen.
809
+ var setupGateLoading = false;
810
+ function holdForSetup(builder) {
811
+ if (access.authed !== true || !access.setup || access.setup.complete !== false) return false;
812
+ var go = function () { if (window.OTBSetupGate) window.OTBSetupGate.show(access.setup, { api: api, builder: builder }); };
813
+ if (window.OTBSetupGate) go();
814
+ else if (!setupGateLoading) { setupGateLoading = true; loadHallScript('setup-gate.js', go); }
815
+ return true;
816
+ }
807
817
  // One hall script on demand, stamped with the shell's own ?v= (task 1004996 made it one helper).
808
818
  function loadHallScript(file, onload, onerror) {
809
819
  var head = document.head, tag = head && head.appendChild && document.createElement ? document.createElement('script') : null;
@@ -1136,6 +1146,8 @@
1136
1146
  if ('founding' in state) {
1137
1147
  access.founding = (state.founding && typeof state.founding === 'object') ? state.founding : null;
1138
1148
  }
1149
+ // task 1005076: /me's onboarding.setup; incomplete holds the hall behind setup-gate.js.
1150
+ if ('setup' in state) access.setup = (state.setup && typeof state.setup === 'object') ? state.setup : null;
1139
1151
  if (state.builder) { renderUser(state.builder); applyCraftEmphasis(state.builder); }
1140
1152
  // task 1004713: which seats this builder holds — /me's server-resolved block.
1141
1153
  if (state.builder && access.authed === true) { loadPositions(state.builder, state.positions); loadGithubChip(state.builder); }
@@ -1162,7 +1174,8 @@
1162
1174
  // permission map, which the loop above is what the shell has just been told.
1163
1175
  maybeRenderCoreUpdate();
1164
1176
  // task 1004492: a builder with no mode yet this session is asked once — unless Home's welcome (task 1004785, welcome-boot.js) greets the day's first visit instead; it marks the picker asked.
1165
- if (!(window.OTBWelcome && window.OTBWelcome.onAccess(access, state && state.builder)) && CRAFT) CRAFT.onAccess(access, state && state.builder);
1177
+ // task 1005076: neither is drawn over the setup screen; setup comes first.
1178
+ if (!holdForSetup(state && state.builder) && !(window.OTBWelcome && window.OTBWelcome.onAccess(access, state && state.builder)) && CRAFT) CRAFT.onAccess(access, state && state.builder);
1166
1179
  var signin = document.getElementById('shell-signin');
1167
1180
  if (signin) signin.hidden = access.authed !== false;
1168
1181
  var user = document.getElementById('shell-user');
@@ -1406,6 +1419,8 @@
1406
1419
  founding: me.founding_mode || null,
1407
1420
  // task 1004713: the government positions this builder holds (server-resolved).
1408
1421
  positions: me.positions || null,
1422
+ // task 1005076: whether setup is complete; incomplete shows setup-gate.js.
1423
+ setup: (me.onboarding && me.onboarding.setup) || null,
1409
1424
  builder: me.builder,
1410
1425
  });
1411
1426
  }).catch(function () { /* cosmetic only */ });
@@ -231,7 +231,7 @@
231
231
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
232
232
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
233
233
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
234
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
234
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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>
@@ -225,7 +225,7 @@
225
225
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
226
226
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
227
227
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
228
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
228
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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. -->
@@ -1356,6 +1356,88 @@ html[data-fullscreen="on"][data-fullscreen-plain="on"] .app-content { padding-to
1356
1356
  "ask me again" box. Each door carries its scene from the approved prototype,
1357
1357
  which is night-dark in either theme because every scene is a night one. */
1358
1358
  html.craft-picker-open { overflow: hidden; }
1359
+ /* The setup flow (setup-gate.js, task 1005076): the one screen a new builder sees until set up. */
1360
+ html.setup-gate-open { overflow: hidden; }
1361
+ .setup-gate {
1362
+ position: fixed;
1363
+ inset: 0;
1364
+ z-index: calc(var(--z-modal) + 1);
1365
+ display: flex;
1366
+ justify-content: center;
1367
+ align-items: flex-start;
1368
+ padding: max(24px, 6vh) 16px 24px;
1369
+ overflow-y: auto;
1370
+ background: var(--bg);
1371
+ }
1372
+ .setup-gate__panel {
1373
+ width: min(640px, 100%);
1374
+ box-sizing: border-box;
1375
+ padding: 36px 40px 28px;
1376
+ border-radius: 22px;
1377
+ background: var(--bg-card);
1378
+ color: var(--ink);
1379
+ }
1380
+ .setup-gate__count { margin: 0; font-size: 13px; color: var(--ink-faint); }
1381
+ .setup-gate__title { margin: 6px 0 8px; font-family: var(--font-display); font-size: clamp(26px, 4vw, 34px); font-weight: 300; letter-spacing: -0.02em; line-height: 1.2; }
1382
+ .setup-gate__lede { margin: 0 0 24px; font-size: 16px; line-height: 1.5; color: var(--ink-soft); }
1383
+ .setup-gate__steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
1384
+ .setup-gate__step { display: flex; gap: 14px; padding: 12px 0; border-top: 1px solid var(--rule-soft); }
1385
+ .setup-gate__step:first-child { border-top: 0; }
1386
+ .setup-gate__mark {
1387
+ flex: none;
1388
+ display: inline-flex;
1389
+ align-items: center;
1390
+ justify-content: center;
1391
+ width: 28px;
1392
+ height: 28px;
1393
+ border-radius: var(--radius-pill);
1394
+ border: 1px solid var(--rule);
1395
+ font-size: 13px;
1396
+ color: var(--ink-soft);
1397
+ }
1398
+ .setup-gate__mark svg { width: 16px; height: 16px; }
1399
+ .setup-gate__step.is-done .setup-gate__mark { border-color: transparent; background: var(--ok-weak); color: var(--ok); }
1400
+ .setup-gate__step.is-current .setup-gate__mark { border-color: var(--accent); color: var(--ink); }
1401
+ .setup-gate__body { min-width: 0; flex: 1; }
1402
+ .setup-gate__label { margin: 4px 0 0; font-size: 16px; font-weight: 500; }
1403
+ .setup-gate__step.is-done .setup-gate__label { color: var(--ink-soft); font-weight: 400; }
1404
+ .setup-gate__hint { margin: 6px 0 0; font-size: 15px; line-height: 1.5; color: var(--ink-soft); }
1405
+ .setup-gate__how { margin: 10px 0 0; padding-left: 20px; font-size: 15px; line-height: 1.6; }
1406
+ /* The clone block is several commands, one per line: `pre` keeps the breaks on screen and in
1407
+ copy-on-click (it copies innerText). Both axes named: the one-axis-strip rule. */
1408
+ .setup-gate__code {
1409
+ display: block;
1410
+ margin: 12px 0 0;
1411
+ padding: 10px 14px;
1412
+ border-radius: var(--radius-sm);
1413
+ background: var(--glass);
1414
+ font-family: var(--font-mono);
1415
+ font-size: 13px;
1416
+ white-space: pre;
1417
+ overflow-x: auto;
1418
+ overflow-y: hidden;
1419
+ }
1420
+ .setup-gate code { font-family: var(--font-mono); font-size: 14px; padding: 1px 6px; border-radius: 6px; background: var(--glass); }
1421
+ .setup-gate__crafts { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
1422
+ .setup-gate__craft,
1423
+ .setup-gate__save {
1424
+ min-height: 36px;
1425
+ padding: 0 16px;
1426
+ border-radius: var(--radius-pill);
1427
+ border: 1px solid var(--rule);
1428
+ background: none;
1429
+ color: var(--ink);
1430
+ font: inherit;
1431
+ font-size: 14px;
1432
+ cursor: pointer;
1433
+ }
1434
+ .setup-gate__craft[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-tint); }
1435
+ .setup-gate__save { margin-top: 12px; background: var(--accent); border-color: var(--accent); color: var(--accent-on); }
1436
+ .setup-gate__save:disabled { opacity: .5; cursor: default; }
1437
+ .setup-gate__err { margin: 8px 0 0; font-size: 14px; color: var(--danger); }
1438
+ .setup-gate__foot { margin: 24px 0 0; font-size: 14px; }
1439
+ .setup-gate__foot a { color: var(--ink-soft); }
1440
+ @media (max-width: 640px) { .setup-gate__panel { padding: 28px 20px 20px; border-radius: 18px; } }
1359
1441
  .craft-picker {
1360
1442
  position: fixed;
1361
1443
  inset: 0;
@@ -2116,8 +2198,7 @@ html.pb-editing main { padding-bottom: 180px; }
2116
2198
  text-align: center;
2117
2199
  }
2118
2200
 
2119
- .pill-link,
2120
- #welcome-reopen a {
2201
+ .pill-link {
2121
2202
  display: inline-flex;
2122
2203
  align-items: center;
2123
2204
  min-height: 28px;
@@ -2129,7 +2210,6 @@ html.pb-editing main { padding-bottom: 180px; }
2129
2210
  border-radius: var(--radius-pill);
2130
2211
  padding: 0 12px;
2131
2212
  }
2132
- #welcome-reopen { margin: 0; }
2133
2213
  .pill-link:hover { color: var(--ink); text-decoration: none; border-color: var(--ink-faint); }
2134
2214
 
2135
2215
  /* ---------- stat tiles (Home) ----------
@@ -3166,119 +3246,8 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
3166
3246
 
3167
3247
  .curve__kinds { display: flex; flex-direction: column; gap: 6px; margin-top: 16px; }
3168
3248
 
3169
- /* ---------- onboarding checklist ---------- */
3170
-
3171
- .onboarding-list {
3172
- list-style: none;
3173
- margin: 0;
3174
- padding: 0;
3175
- display: flex;
3176
- flex-direction: column;
3177
- }
3178
- .onboarding-item {
3179
- display: grid;
3180
- grid-template-columns: 26px minmax(0, 1fr) auto;
3181
- gap: 4px 12px;
3182
- align-items: start;
3183
- padding: 12px 4px;
3184
- border-bottom: 1px solid var(--rule-soft);
3185
- }
3186
- .onboarding-item:last-child { border-bottom: none; }
3187
- .onboarding-item--placeholder { color: var(--ink-faint); }
3188
- .onboarding-item__mark {
3189
- width: 22px; height: 22px;
3190
- border-radius: 50%;
3191
- border: 1.5px solid var(--border-strong);
3192
- display: flex;
3193
- align-items: center;
3194
- justify-content: center;
3195
- font-size: 12px;
3196
- color: var(--ink-faint);
3197
- background: var(--surface);
3198
- }
3199
- .onboarding-item--cleared .onboarding-item__mark {
3200
- background: var(--ok);
3201
- border-color: var(--ok);
3202
- color: #fff;
3203
- }
3204
- .onboarding-item--current .onboarding-item__mark { border-color: var(--accent); color: var(--accent-text); }
3205
- .onboarding-item--current { background: var(--accent-weak); border-radius: var(--radius-sm); padding-left: 10px; padding-right: 10px; border-bottom-color: transparent; }
3206
- .onboarding-item--locked { opacity: 0.6; }
3207
- .onboarding-item__body { min-width: 0; }
3208
- .onboarding-item__label { font-weight: 600; font-size: 13.5px; }
3209
- .onboarding-item--cleared .onboarding-item__label { color: var(--ink-soft); font-weight: 500; }
3210
- .onboarding-item__hint { font-size: 12.5px; color: var(--ink-soft); margin-top: 1px; }
3211
- .onboarding-item__detail { font-size: 12.5px; color: var(--ink-soft); margin-top: 6px; }
3212
- .onboarding-item__detail code { background: var(--surface-2); padding: 1px 5px; border-radius: 4px; }
3213
- .onboarding-item__action { align-self: center; }
3214
- .onboarding-item__disc-pick { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
3215
- .onboarding-item__disc {
3216
- border: 1px solid var(--border-strong);
3217
- background: var(--surface);
3218
- color: var(--ink-soft);
3219
- border-radius: 999px;
3220
- font-size: 12.5px;
3221
- font-weight: 500;
3222
- padding: 4px 12px;
3223
- cursor: pointer;
3224
- }
3225
- .onboarding-item__disc:hover { background: var(--surface-2); }
3226
- .onboarding-item__disc--on {
3227
- background: var(--accent-weak);
3228
- color: var(--accent-text);
3229
- border-color: color-mix(in srgb, var(--accent) 40%, transparent);
3230
- }
3231
- .onboarding-item__discord { font-weight: 600; }
3232
-
3233
- @media (max-width: 560px) {
3234
- .onboarding-item { grid-template-columns: 26px minmax(0, 1fr); }
3235
- .onboarding-item__action { grid-column: 2; justify-self: start; }
3236
- }
3237
-
3238
- /* ---------- welcome panel + setup selector ---------- */
3239
-
3240
- .scroll--welcome {
3241
- position: relative;
3242
- border-left: 3px solid var(--accent);
3243
- }
3244
- .scroll--welcome--minimized .welcome { display: none; }
3245
- .welcome__minimize {
3246
- position: absolute;
3247
- top: 12px; right: 12px;
3248
- width: 26px; height: 26px;
3249
- border: 1px solid var(--border);
3250
- border-radius: var(--radius-sm);
3251
- background: var(--surface);
3252
- color: var(--ink-soft);
3253
- font-size: 14px;
3254
- line-height: 1;
3255
- cursor: pointer;
3256
- }
3257
- .welcome__minimize:hover { background: var(--surface-2); }
3258
- .welcome__lede { font-size: 13.5px; color: var(--ink-soft); max-width: 70ch; }
3259
- .welcome__unlocks { margin: 8px 0; padding-left: 18px; font-size: 13px; color: var(--ink-soft); }
3260
- .welcome__cmd, .welcome__paste {
3261
- font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
3262
- font-size: 12.5px;
3263
- background: var(--surface-2);
3264
- border: 1px solid var(--border);
3265
- border-radius: var(--radius-sm);
3266
- padding: 8px 12px;
3267
- overflow-x: auto;
3268
- }
3269
- /* The get-the-code block is several commands, one per line. Without `pre` the
3270
- line breaks collapse on screen AND in copy-on-click (it copies innerText),
3271
- so a builder pasted every command onto one line. Both axes named: see the
3272
- one-axis-strip rule in this module's CLAUDE.md. */
3273
- .setup__getcode .welcome__paste {
3274
- display: block;
3275
- white-space: pre;
3276
- overflow-y: hidden;
3277
- }
3249
+ /* ---------- setup selector (the tab strip; .setup__tab is the kit's tab idiom) ---------- */
3278
3250
 
3279
- .setup { margin-top: 12px; }
3280
- .setup__intro { font-size: 13px; color: var(--ink-soft); }
3281
- .setup__toggle { display: flex; border: 0; border-bottom: 1px solid var(--rule); margin: 8px 0 12px; flex-wrap: wrap; }
3282
3251
  /* An open tab is the live thing in the strip, so it is what the accent lights:
3283
3252
  a 2px underline sitting on the strip’s own hairline. */
3284
3253
  .setup__tab {
@@ -3366,8 +3335,7 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
3366
3335
  html[data-door="out"] .app-sidebar,
3367
3336
  html[data-door="out"] .app-scrim,
3368
3337
  html[data-door="out"] .app-topbar,
3369
- html[data-door="out"] .app-foot,
3370
- html[data-door="out"] #welcome-reopen { display: none; }
3338
+ html[data-door="out"] .app-foot { display: none; }
3371
3339
  html[data-door="out"] .app-body { margin-left: 0; }
3372
3340
  /* body in the selector outranks Home's own body[data-page="home"] .page-head--hall. */
3373
3341
  html[data-door="out"] body .page-head {
@@ -59,7 +59,7 @@
59
59
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
61
61
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
62
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
62
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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
@@ -141,7 +141,7 @@
141
141
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
142
142
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
143
143
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
144
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
144
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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>
@@ -145,7 +145,7 @@
145
145
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
146
146
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
147
147
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
148
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
148
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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>
@@ -271,7 +271,7 @@
271
271
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
272
272
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
273
273
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
274
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
274
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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). -->
@@ -174,7 +174,7 @@
174
174
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
175
175
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
176
176
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
177
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
177
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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 -->
@@ -0,0 +1,43 @@
1
+ # The setup flow — one screen a new builder finishes once, and no setup banner on Home (task 1005076)
2
+
3
+ The owner, 2026-11-05: Home's Welcome banner (the clone / `npx bongos setup` / download Claude steps) and the
4
+ Getting started checklist below it stayed until a builder had shipped THREE tasks, so builders who were fully
5
+ set up kept seeing a setup banner across the top of the hall. The decision: a new builder is held on a setup
6
+ flow when they get in and goes through each step; once set up they are in for good, with no banner and no
7
+ reminders.
8
+
9
+ **What people see.** While `/me`'s `onboarding.setup.complete` is false, every hall page shows only
10
+ `setup-gate.js`'s screen: "N of 4 done", the four steps in order, the current one open with what to do, a Sign
11
+ out link. Each step ticks itself; the screen re-reads `/me` every 8 seconds and reloads into the hall the moment
12
+ setup is complete.
13
+
14
+ 1. **Sign in** — always done by the time the screen can draw.
15
+ 2. **Get the project and Claude on your machine, and link it** — a CLI session exists (`npx bongos setup`).
16
+ 3. **Choose your role** — chosen on the screen itself, saved through `PATCH /me/disciplines`.
17
+ 4. **Run /builder-start in Claude** — the `first_start` stage, which `GET /tasks/claimable` now marks only for
18
+ a CLI session (the hall's own pages read that feed from the browser and used to tick it for everyone).
19
+
20
+ **Who is never held.** Anyone graduated, anyone who has claimed a task, and any rank above xenos — every existing
21
+ builder goes straight in. An older server that sends no `setup` holds nobody. The rule is
22
+ `modules/onboarding/onboarding-state.js` `setupForApi`; the hall never re-derives it.
23
+
24
+ **How it is wired.** `shell.js` `holdForSetup` loads `setup-gate.js` on demand (`loadHallScript`, the
25
+ core-move-notice way), so a set-up builder never downloads it, and while it holds, neither the daily welcome
26
+ (`welcome-boot.js`) nor the craft picker is asked. Both `applyAccess` callers pass `setup` (`shell.js`'s own
27
+ `/me` probe and Home's `hall-render.js`). Its styles are `.setup-gate*` in `style.css`, on the hall tokens in
28
+ both modes; the overlay sits one above `--z-modal`.
29
+
30
+ **Removed.** The `welcome-scroll` and `onboarding-scroll` Home widgets, the "Show the setup guide again" link,
31
+ and everything that drew them: `renderWelcomeCore` / `renderOnboarding` and their helpers in `hall-render.js`;
32
+ `renderWelcome`, `reopenWelcomeFromNav`, `pickDiscipline`, the instance-repo loader and the checklist's /me poll
33
+ in `builders.js`; the `.welcome*` / `.onboarding-item*` styles. The clone block (this project's own repository
34
+ from `GET /instance`, never a hardcoded one) now lives on the setup screen's machine step, with Node and
35
+ `npm install` named before `npx bongos setup`, as the welcome panel had them. The role step says "role", the
36
+ word ADR 0174 fixed for that axis, through `OTB.roleAxisLabel` / `OTB.roleLabel`.
37
+
38
+ **Loaded once.** `applyAccess` can run several times before the script lands; `holdForSetup` asks for it once
39
+ (`setupGateLoading`), or two screens would stack.
40
+
41
+ **Tests.** `tests/setup_flow.mjs` (the claimable guard run for a CLI and a web session, the screen drawn and
42
+ driven, the shell loading it with nothing over it, Home with no banner), `tests/onboarding_state.mjs` (the
43
+ rule), `tests/welcome_boot.mjs` (the shell's ask order). Each was mutation-checked against its half of the change.
@@ -237,8 +237,8 @@ module.exports = function buildTasksRouter() {
237
237
  // and forget — the response should never wait on or fail because of the
238
238
  // onboarding write. safeMarkStage swallows errors and handles the
239
239
  // graduation broadcast. Through the `onboarding` kernel port (BV1.R81 / ADR
240
- // 0093 §1); ?. degrades to a no-op when the module is off.
241
- seams.resolveOptional('onboarding')?.safeMarkStage(pool, req.builder.id, 'first_start', req.builder);
240
+ // 0093 §1); ?. degrades to a no-op when the module is off. CLI only (task 1005076: the hall reads this feed too).
241
+ if (req.gdsSession && req.gdsSession.source === 'cli') seams.resolveOptional('onboarding')?.safeMarkStage(pool, req.builder.id, 'first_start', req.builder);
242
242
  // `claimable_total` keeps the limit honest: the length of the sliced array, never a
243
243
  // second count query (ADR 0197).
244
244
  res.json({ claimable, claimable_total: feed.length, active_claims: active });
@@ -79,23 +79,6 @@ const STAGES = [
79
79
  const GRADUATED = 'graduated';
80
80
  const ALL_STAGES = [...STAGES, GRADUATED];
81
81
 
82
- // The pre-auth, INFORMATIONAL step that sits AHEAD of auth_complete (task 470).
83
- // The state machine's first ENGINE stage is auth_complete, but the genuine
84
- // first action a newcomer takes is "obtain the repo" — clone it locally. That
85
- // step is manual/external
86
- // (there is no server event for it) and intentionally NOT a member of STAGES, so
87
- // it never gates graduation, never advances current_stage, and never appears in
88
- // shapeForApi's tracked checklist. It exists purely so the system's own model of
89
- // "the path into the city" begins where the path really begins. The hall renders
90
- // it as a static lead-in line on the displayed checklist; the auth_complete hint
91
- // above also surfaces the same clone instruction.
92
- const PRE_AUTH_STEP = {
93
- id: 'obtain_repo',
94
- label: 'Obtain the repo',
95
- hint: 'Clone the project to thy machine and run npm install in it. The build commands live inside the repo, so this comes first.',
96
- informational: true,
97
- };
98
-
99
82
  // How many shipped tasks the terminal 'ship_three' stage requires. Kept in
100
83
  // lockstep with db.THETES_GRADUATION_THRESHOLD (#692 / ADR 0034) so the
101
84
  // checklist's graduation coincides with the xenos→thetes promotion. Used only
@@ -124,7 +107,7 @@ const STAGE_LABELS = {
124
107
  // machine you'll build from — the /builder-* commands live INSIDE the repo, so
125
108
  // nothing (not even sign-in via the CLI) can happen before it exists. The
126
109
  // onboarding model never named that step, so the hint now points at it ahead of
127
- // the sign-in itself. See PRE_AUTH_STEP below for the standalone representation.
110
+ // the sign-in itself. The setup flow's cli_setup step (task 1005076) carries it too.
128
111
  const STAGE_HINTS = {
129
112
  auth_complete: 'First obtain the repo — clone it — then sign in with npx bongos setup in that folder.',
130
113
  pick_disciplines: 'Pick the work that calls thee — engineer, artist, ideator, or any mix.',
@@ -189,6 +172,42 @@ const DISPLAY_HINTS = {
189
172
  ship_three: 'Carry three tasks through with /builder-ship — the third makes thee a Thetes.',
190
173
  };
191
174
 
175
+ // ---- the setup flow (task 1005076) -------------------------------------------
176
+ //
177
+ // What a newcomer must finish before the hall opens to them (owner, 2026-11-05):
178
+ // the hall holds them on one setup screen until every step below is cleared, and
179
+ // after that they are in for good — no banner, no reminders. Each step ticks
180
+ // itself from a signal the server already records:
181
+ //
182
+ // signed_in — they are reading /me, so always cleared.
183
+ // cli_setup — a source='cli' session exists (ran `npx bongos setup`).
184
+ // pick_disciplines — the engine stage choosing a role marks (PATCH /me/disciplines).
185
+ // first_start — the engine stage, which GET /tasks/claimable marks only for a
186
+ // CLI session: proof /builder-start ran from Claude.
187
+ //
188
+ // Setup is also complete for anyone already past it: graduated, claimed a task
189
+ // (first_claim cleared), or holding any rank above xenos. Existing builders go
190
+ // straight in and are never held.
191
+ const SETUP_STEPS = [
192
+ { id: 'signed_in', label: 'Sign in', hint: 'Done — you are signed in.' },
193
+ { id: 'cli_setup', label: 'Get the project and Claude on your machine, and link it', hint: 'Three things on your own computer. This ticks itself once your machine is linked.' },
194
+ { id: 'pick_disciplines', label: 'Choose your role', hint: 'Pick the work you want to do — engineer, artist, ideator, or any mix.' },
195
+ { id: 'first_start', label: 'Run /builder-start in Claude', hint: 'Open the project in Claude and type /builder-start. This ticks itself the first time it runs, which proves everything works.' },
196
+ ];
197
+
198
+ function setupForApi(state, { cliAuthed = false, rank = null } = {}) {
199
+ const completed = new Set((state && state.completed_stages) || []);
200
+ const current = (state && state.current_stage) || STAGES[0];
201
+ const cleared = (stage) => current === GRADUATED || completed.has(stage)
202
+ || (STAGES.indexOf(current) > STAGES.indexOf(stage) && STAGES.indexOf(stage) >= 0);
203
+ const steps = SETUP_STEPS.map((s) => ({
204
+ ...s,
205
+ cleared: s.id === 'signed_in' ? true : s.id === 'cli_setup' ? !!cliAuthed : cleared(s.id),
206
+ }));
207
+ const pastIt = current === GRADUATED || cleared('first_claim') || (!!rank && rank !== 'xenos');
208
+ return { complete: pastIt || steps.every((s) => s.cleared), steps };
209
+ }
210
+
192
211
  // Pick the first uncompleted stage from STAGES (canonical order), or
193
212
  // GRADUATED if all six are done. Mirrors the migration 045 backfill CASE so
194
213
  // app-layer behaviour matches DB-layer reconstruction.
@@ -441,7 +460,9 @@ function stageDetail(stageId, shipCount) {
441
460
  // 'join_discord' step (the OAuth flow sets the link).
442
461
  // opts.cliAuthed — whether the builder has ever authenticated the GDS CLI
443
462
  // (db.hasCliSession). Clears the 'cli_setup' step.
444
- function shapeForApi(state, { shipCount = null, discordLinked = false, cliAuthed = false } = {}) {
463
+ // opts.rank — the builder's rank; any rank above xenos counts as set up
464
+ // (the `setup` block, task 1005076).
465
+ function shapeForApi(state, { shipCount = null, discordLinked = false, cliAuthed = false, rank = null } = {}) {
445
466
  const completed = new Set((state && state.completed_stages) || []);
446
467
  const currentReal = (state && state.current_stage) || STAGES[0];
447
468
  const graduated = currentReal === GRADUATED;
@@ -487,6 +508,7 @@ function shapeForApi(state, { shipCount = null, discordLinked = false, cliAuthed
487
508
  progress: { cleared: clearedCount, total: DISPLAY_STEPS.length },
488
509
  stages: display,
489
510
  graduated,
511
+ setup: setupForApi(state, { cliAuthed, rank }),
490
512
  };
491
513
  }
492
514
 
@@ -494,7 +516,6 @@ module.exports = {
494
516
  STAGES,
495
517
  GRADUATED,
496
518
  ALL_STAGES,
497
- PRE_AUTH_STEP,
498
519
  STAGE_HINTS,
499
520
  DISPLAY_LABELS,
500
521
  isKnownStage,
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.41",
3
+ "version": "1.24.42",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.41",
9
+ "version": "1.24.42",
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.24.41",
3
+ "version": "1.24.42",
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",
@@ -11209,5 +11209,11 @@
11209
11209
  "id": "1005049",
11210
11210
  "text": "Watch is now the one oversight page. Sessions and Autobongos moved off the sidebar and became two tabs on Watch, next to People, Operations and Security. Old links to /sessions and /gate open the right tab. Who can see and pre"
11211
11211
  }
11212
+ ],
11213
+ "1.24.42": [
11214
+ {
11215
+ "id": "1005076",
11216
+ "text": "New builders now get one clear setup screen they finish once, and everyone already set up no longer sees a setup banner across the top of Home."
11217
+ }
11212
11218
  ]
11213
11219
  }
@@ -535,7 +535,7 @@ module.exports = function buildMeRouter() {
535
535
  // returns a stage-1 "nothing cleared" view so the client renders the
536
536
  // checklist either way. Through the `onboarding` port (BV1.R81); null when
537
537
  // the module is off.
538
- onboarding: onboarding ? onboarding.shapeForApi(onboardingRow, { shipCount, discordLinked: !!discordLink, cliAuthed: !!cliAuthed }) : null,
538
+ onboarding: onboarding ? onboarding.shapeForApi(onboardingRow, { shipCount, discordLinked: !!discordLink, cliAuthed: !!cliAuthed, rank: req.builder.rank }) : null,
539
539
  // F7 (ADR 0033): Discord link state for the hall's Connect/Unlink UI.
540
540
  // `configured` tells the client whether the server even has Discord OAuth
541
541
  // creds — when false the hall renders nothing (the start route 503s).
@@ -714,7 +714,7 @@ module.exports = function buildMeRouter() {
714
714
  discordIsLinked(req.builder.id),
715
715
  db.hasCliSession(req.builder.id),
716
716
  ]);
717
- const onboarding = onboardingPort ? onboardingPort.shapeForApi(state, { shipCount, discordLinked: !!discordLink, cliAuthed: !!cliAuthed }) : null;
717
+ const onboarding = onboardingPort ? onboardingPort.shapeForApi(state, { shipCount, discordLinked: !!discordLink, cliAuthed: !!cliAuthed, rank: req.builder.rank }) : null;
718
718
  res.json({ builder, onboarding });
719
719
  } catch (err) {
720
720
  if (err.code === 'invalid_discipline' || err.code === 'invalid_main_discipline') {
package/src/module-api.js CHANGED
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
81
81
  // MAJOR (see allowBoxScope below): passes the request through untouched.
82
82
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
83
83
 
84
- const CORE_VERSION = '1.24.41'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.24.42'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
85
85
 
86
86
  // A namespaced logger so a module's log lines are attributable + consistent.
87
87
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -350,7 +350,7 @@ function bootHallRender({ me, progress = null, leaderboard = [], ctxExtra = {} }
350
350
  curLabel: (caps) => (caps ? 'Credits' : 'credits'), emptyRow: () => '', fmtDate: (d) => String(d).slice(0, 10),
351
351
  mins: (m) => `${m}m`, normalizeRank: (r) => String(r || '').toLowerCase(), pct: (n) => `${n}%`, postJSON: async () => ({}),
352
352
  rankLabel: (r) => r, rankWeight: () => 0, signClasses: () => '', toast() {}, usd: (n) => `$${n}`,
353
- instanceRepo: null, newcomerGated: false,
353
+ newcomerGated: false,
354
354
  get lastMeState() { return me; }, get lastCostState() { return me.cost || null; }, get lastKarmaState() { return me.karma || null; },
355
355
  get lastNeedsState() { return me.needs || null; }, get lastRebaseState() { return me.needs_rebase_tasks || null; },
356
356
  get lastProgressState() { return progress; },
@@ -92,15 +92,14 @@ async function boot({ meStatus, settle = false }) {
92
92
  },
93
93
  OTBHallRender: () => ({
94
94
  analyticsState: {}, lbState: {},
95
- shipsPager: { page: 1 }, welcomeDismissalKey: () => 'k',
95
+ shipsPager: { page: 1 },
96
96
  // task 1004854: Home asks for this many ships; the ledger's full read lives in hall-render.js.
97
97
  HOME_SHIPS_LIMIT: 5, setRecentShips: spy('setRecentShips'), populateShipVersionFilter: spy('populateShipVersionFilter'),
98
98
  mountHallWidgets: spy('mountHallWidgets'),
99
99
  renderLanding: spy('renderLanding'),
100
100
  renderCurveBody: spy('renderCurveBody'), renderGreeting: spy('renderGreeting'),
101
- renderNav: spy('renderNav'), renderOnboarding: spy('renderOnboarding'),
101
+ renderNav: spy('renderNav'),
102
102
  renderProfile: spy('renderProfile'), renderRecentShips: spy('renderRecentShips'),
103
- renderWelcomeCore: spy('renderWelcomeCore'),
104
103
  // task 1003459 — wireEvents() calls this unconditionally now (the Home
105
104
  // Next-up Claim button), so an incomplete stub throws mid-wire.
106
105
  wireNextUpClaim: spy('wireNextUpClaim'),