@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
@@ -287,17 +287,11 @@
287
287
  // task 1004713: the government positions this builder holds, server-resolved —
288
288
  // passed here too, or the index would keep the governing tabs for a seatless builder.
289
289
  positions: (ctx.lastMeState && ctx.lastMeState.positions) || null,
290
+ // task 1005076: incomplete setup holds the hall behind setup-gate.js.
291
+ setup: (onboarding && onboarding.setup) || null,
290
292
  builder,
291
293
  });
292
294
 
293
- // Re-open affordance: pre-graduation, when the welcome banner is dismissed
294
- // (hidden), a small link brings it back (reopenWelcomeFromNav).
295
- const reopen = document.getElementById('welcome-reopen');
296
- if (reopen) {
297
- const welcomeEl = document.getElementById('welcome-scroll');
298
- reopen.hidden = !(welcomeEl && welcomeEl.hidden && !graduated);
299
- }
300
-
301
295
  // Standing section rail: one link per group with a visible member.
302
296
  const rail = document.getElementById('standing-rail');
303
297
  if (rail) {
@@ -755,245 +749,6 @@
755
749
  h1.textContent = `${greetingWord(new Date().getHours())}, ${builder.display_name || builder.github_login || 'builder'}`;
756
750
  }
757
751
  }
758
- const ONBOARDING_STAGE_ORDER = [
759
- 'auth_complete',
760
- 'pick_disciplines',
761
- 'first_start',
762
- 'first_claim',
763
- 'ship_three',
764
- 'graduated',
765
- ];
766
-
767
- function stageIndex(stageId) {
768
- const i = ONBOARDING_STAGE_ORDER.indexOf(stageId);
769
- return i === -1 ? 0 : i;
770
- }
771
- function welcomeDismissalKey(builderId) {
772
- return `builders:welcome-dismissed:${builderId}`;
773
- }
774
- function readDismissedStageIndex(builderId) {
775
- try {
776
- const raw = window.localStorage.getItem(welcomeDismissalKey(builderId));
777
- if (raw === null) return null;
778
- const n = Number(raw);
779
- return Number.isInteger(n) ? n : null;
780
- } catch (_) {
781
- // localStorage can throw in private-browsing modes. Treat as "never
782
- // dismissed" — the panel renders, which is the right fail-open
783
- // behaviour for an onboarding affordance.
784
- return null;
785
- }
786
- }
787
- function renderWelcomeCore(builder, onboarding) {
788
- const scroll = $('#welcome-scroll');
789
- const body = $('#welcome-body');
790
- if (!scroll || !body) return;
791
-
792
- // No builder yet (unauth landing) → hide and exit.
793
- if (!builder || !builder.id) { scroll.hidden = true; return; }
794
-
795
- // Graduated → hide entirely. The checklist below will also hide itself.
796
- if (onboarding && onboarding.graduated) { scroll.hidden = true; return; }
797
-
798
- // Compute current stage index. When the /me payload didn't carry an
799
- // onboarding object (legacy), default to the first stage so the panel
800
- // still greets the builder rather than vanishing silently.
801
- const currentStageId = (onboarding && onboarding.current_stage) || ONBOARDING_STAGE_ORDER[0];
802
- const currentIdx = stageIndex(currentStageId);
803
-
804
- // Dismissal check. Reappears on stage regression (currentIdx < dismissed).
805
- const dismissedAt = readDismissedStageIndex(builder.id);
806
- if (dismissedAt !== null && currentIdx >= dismissedAt) {
807
- scroll.hidden = true;
808
- return;
809
- }
810
-
811
- const rankBadge = renderRankBadge(normalizeRank(builder.rank), { tip: true });
812
- const greeting = `Welcome, ${escapeHtml(builder.display_name || builder.github_login || 'builder')}.`;
813
-
814
- // #777: the welcome panel shows ONLY setup — the steps that get a newcomer
815
- // from "signed into the hall" to a working `claude` session on their own
816
- // machine. The "what to do next" arc lives in the Path checklist below — not
817
- // here.
818
- // The #support pointer assumes the Discord module; drop it when discord is off.
819
- const supportFoot = moduleOn('discord')
820
- ? `Stuck on any step? <a href="${API}/auth/discord/start">Join our Discord</a> and ask in <strong>#support</strong>.`
821
- : `Stuck on any step? See the project's README and docs.`;
822
- const setupSteps = `
823
- <p class="setup__intro">Build from your own machine — get a copy of the project, sign in once, then work in Claude.</p>
824
- <ol class="setup__steps">
825
- <li><strong>Clone the repository</strong> and install dependencies (<code>npm install</code>).</li>
826
- <li><strong>Run <code>npx bongos setup</code></strong> in that folder to sign this machine in.</li>
827
- <li><strong><a href="https://claude.com/download" target="_blank" rel="noopener noreferrer">Download Claude</a></strong> (or use the Claude Code CLI), open the project, and run <code>/builder-start</code> to claim your first task.</li>
828
- </ol>
829
- `;
830
- // task 469: a "Set up your machine / get the code" door. The steps above
831
- // get you a session running; this section answers the separate
832
- // question a first-timer always has — "where does the code come from, and
833
- // how do I get it onto my own laptop?". It's the hall mirror of the primer's
834
- // "Step 0 — get the project onto your machine". Collapsed by default so it
835
- // doesn't crowd the recommended path; the clone block is click-to-copy
836
- // (copy-on-click.js picks up .welcome__paste). Sign-in is `npx bongos setup`
837
- // (bin/bongos.js → scripts/bongos/setup.js), one line on every OS — bare
838
- // `bongos` is not on a fresh clone's PATH, `npx` finds the repo's own bin.
839
- // The clone commands come from the instance's own repo coords (loaded from
840
- // GET /api/bongos/instance), not a hardcoded repo, so a fresh instance's newcomer
841
- // clones ITS project (ADR 0062 §3, task 1705). Falls back to generic guidance
842
- // until the coords load (or if they're unavailable).
843
- const cloneBlock = ctx.instanceRepo
844
- ? `git clone ${escapeHtml(ctx.instanceRepo.url)}.git\ncd ${escapeHtml(ctx.instanceRepo.repo)}\nnpm install\nnpx bongos setup`
845
- : `# clone your instance's repository, cd into it, then run:\nnpm install\nnpx bongos setup`;
846
- const getCode = `
847
- <details class="setup__getcode">
848
- <summary class="setup__getcode-summary">Set up your machine / get the code</summary>
849
- <div class="setup__getcode-body">
850
- <p class="setup__hint">First install <a href="https://git-scm.com/downloads" target="_blank" rel="noopener noreferrer">Git</a>,
851
- <a href="https://nodejs.org" target="_blank" rel="noopener noreferrer">Node</a> (version 22 or newer)
852
- and <a href="https://claude.com/download" target="_blank" rel="noopener noreferrer">Claude Code</a> — that's all the tooling you need up front.</p>
853
- <p class="setup__getcode-lede">Then copy the project onto your machine, install its dependencies, and sign in:</p>
854
- <code class="welcome__paste">${cloneBlock}</code>
855
- <p class="setup__hint">The same commands work on Mac, Windows and Linux. New to the steps? The
856
- <a href="/primer">primer</a> walks the whole path in plain language.</p>
857
- </div>
858
- </details>`;
859
- body.innerHTML = `
860
- <div class="welcome__lede">${greeting} Your standing is ${rankBadge}.</div>
861
- <div class="setup">
862
- ${setupSteps}
863
- ${getCode}
864
- <p class="setup__foot">${supportFoot}</p>
865
- </div>
866
- `;
867
- // Remember the stage we rendered for, so the dismiss handler can record it.
868
- scroll.dataset.stageIdx = String(currentIdx);
869
- scroll.hidden = false;
870
- }
871
- function renderOnboarding(onboarding, discord) {
872
- const scroll = $('#onboarding-scroll');
873
- const list = $('#onboarding-list');
874
- if (!scroll || !list) return;
875
- if (!onboarding) {
876
- scroll.hidden = true;
877
- return;
878
- }
879
- // Hide the panel once the builder has graduated — no point lingering.
880
- if (onboarding.graduated) {
881
- scroll.hidden = true;
882
- return;
883
- }
884
- // task 2169 (banner budget: one at a time): while the welcome panel is
885
- // showing, the checklist waits its turn — it appears when the welcome is
886
- // dismissed or outgrown. renderWelcome runs first in the same loadAll pass
887
- // (and every SSE nudge re-runs both), so this stays current.
888
- const welcomeEl = document.getElementById('welcome-scroll');
889
- if (welcomeEl && !welcomeEl.hidden) {
890
- scroll.hidden = true;
891
- return;
892
- }
893
- scroll.hidden = false;
894
-
895
- const discordConfigured = !!(discord && discord.configured);
896
- const stages = Array.isArray(onboarding.stages) ? onboarding.stages : [];
897
- if (stages.length === 0) {
898
- list.innerHTML = `<li class="onboarding-item onboarding-item--placeholder">No onboarding steps recorded yet.</li>`;
899
- return;
900
- }
901
-
902
- // task 470: a static, INFORMATIONAL lead-in step that sits AHEAD of the
903
- // data-driven steps — the genuine first action into the city is "obtain the
904
- // repo" (clone it onto your machine).
905
- // It mirrors onboarding-state.js PRE_AUTH_STEP. It is NOT one of the tracked
906
- // checklist steps (it carries no engine stage, never gates graduation, and
907
- // isn't counted in the "N of M steps cleared" status line) — it just makes
908
- // the checklist a newcomer sees acknowledge the very first real action,
909
- // which the auto-ticking steps below silently assumed already happened.
910
- const preAuthLead = `
911
- <li class="onboarding-item onboarding-item--informational" data-stage="obtain_repo">
912
- <span class="onboarding-item__mark" aria-hidden="true">›</span>
913
- <div class="onboarding-item__body">
914
- <p class="onboarding-item__label">First — obtain the repo</p>
915
- <p class="onboarding-item__hint">Clone the project to your machine and run <code>npm install</code> in it. The build commands live inside the repo, so this comes first. The remaining steps below are tracked automatically.</p>
916
- </div>
917
- </li>`;
918
-
919
- list.innerHTML = preAuthLead + stages.map((s) => {
920
- const status = s.cleared ? 'cleared' : (s.is_current ? 'current' : 'locked');
921
- const mark = s.cleared ? '✓' : (s.is_current ? '◆' : '○');
922
- const hint = s.hint ? `<p class="onboarding-item__hint">${escapeHtml(s.hint)}</p>` : '';
923
- // #766: the terminal 'ship_three' stage carries a live "N of 3" counter
924
- // in its `detail` field — render it as a small progress line so the
925
- // builder watches themselves climb toward Thetes with each ship.
926
- const detail = s.detail
927
- ? `<p class="onboarding-item__detail">${escapeHtml(s.detail)}</p>`
928
- : '';
929
- // Per-step affordance, shown ONLY while the step is the current, uncleared
930
- // one — every step now behaves uniformly (Lars, #833): locked/future steps
931
- // and cleared steps carry no control, so the path reads as one action at a
932
- // time. (The Discord step already did this; the craft picker and the
933
- // See-the-works link used to render on locked/cleared steps too.) Every
934
- // step auto-detects its own completion server-side (#777): join_discord
935
- // clears on the Discord link, cli_setup once the Bongos CLI is authenticated,
936
- // ship_three at the third ship — no manual ticking.
937
- let action = '';
938
- if (s.is_current && !s.cleared) {
939
- if (s.id === 'pick_disciplines') {
940
- // V3 (2026-05-30): inline discipline picker → PATCH /me/disciplines.
941
- // Multi-select: each button is a toggle reflecting the saved prefs, so
942
- // a builder can pick engineer, artist, ideator, or any mix while the
943
- // step is current. (Settings can revise crafts later for graduated citizens.)
944
- const prefs = (ctx.lastMeState && ctx.lastMeState.builder && Array.isArray(ctx.lastMeState.builder.preferred_disciplines))
945
- ? ctx.lastMeState.builder.preferred_disciplines : [];
946
- const discBtn = (value, label) => {
947
- const on = prefs.includes(value);
948
- return `<button type="button" class="btn-ghost onboarding-item__disc${on ? ' onboarding-item__disc--on' : ''}" data-disc="${escapeHtml(value)}" aria-pressed="${on}">${escapeHtml(label)}</button>`;
949
- };
950
- // The offered crafts come from the server (task 1003487): window.__MODULES__
951
- // .disciplines IS enabledDisciplines(), the same list the PATCH validates
952
- // against, so this can never render a button the API would reject. Was a
953
- // hardcoded triple with a moduleOn('art-pipeline') branch for 'artist', so
954
- // a module-contributed craft (ui-design → ui) never appeared here at all.
955
- action = `
956
- <div class="onboarding-item__action onboarding-item__disc-pick">
957
- ${OTB.disciplines().map((d) => discBtn(d, OTB.roleLabel(d))).join('')}
958
- </div>`;
959
- } else if (s.id === 'ship_three') {
960
- action = `
961
- <div class="onboarding-item__action">
962
- <a class="btn-ghost" href="/work">See the work board →</a>
963
- </div>`;
964
- } else if (s.id === 'join_discord' && discordConfigured) {
965
- // #777: the Connect button runs the existing OAuth flow (GET
966
- // /auth/discord/start → auto-join + rank role). Full-page nav so the
967
- // server can set the CSRF state cookie before redirecting to Discord.
968
- // The step then clears itself once the link lands (discord.linked) —
969
- // no manual mark. Same control as /builders/settings.
970
- action = `
971
- <div class="onboarding-item__action">
972
- <a class="btn-ghost onboarding-item__discord" href="${API}/auth/discord/start">Connect Discord &amp; join the server</a>
973
- </div>`;
974
- }
975
- }
976
- return `
977
- <li class="onboarding-item onboarding-item--${status}" data-stage="${escapeHtml(s.id)}">
978
- <span class="onboarding-item__mark" aria-hidden="true">${mark}</span>
979
- <div class="onboarding-item__body">
980
- <p class="onboarding-item__label">${escapeHtml(s.label)}</p>
981
- ${hint}
982
- ${detail}
983
- </div>
984
- ${action}
985
- </li>
986
- `;
987
- }).join('');
988
-
989
- const cleared = onboarding.progress?.cleared ?? 0;
990
- const total = onboarding.progress?.total ?? stages.length;
991
- const status = $('#onboarding-status');
992
- if (status) {
993
- status.hidden = false;
994
- status.textContent = `${cleared} of ${total} steps cleared.`;
995
- }
996
- }
997
752
  // task 1004854: Home shows the newest few ships and a count, so loadAll (on
998
753
  // every load AND every live refresh) asks for HOME_SHIPS_LIMIT rows plus the
999
754
  // server's `total`. Only the Accomplishments ledger — its window/version
@@ -1365,6 +1120,6 @@
1365
1120
  }
1366
1121
 
1367
1122
 
1368
- return { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderOnboarding, renderProfile, renderRecentShips, renderWelcomeCore, HOME_SHIPS_LIMIT, loadShipLedger, populateShipVersionFilter, setRecentShips, shipState, shipsPager, welcomeDismissalKey };
1123
+ return { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderProfile, renderRecentShips, HOME_SHIPS_LIMIT, loadShipLedger, populateShipVersionFilter, setRecentShips, shipState, shipsPager };
1369
1124
  };
1370
1125
  })();
@@ -48,20 +48,9 @@
48
48
 
49
49
  /* ---------------- Home ---------------- */
50
50
 
51
- // 0. First-visit welcome banner — V3.R77 (#296). Pre-graduation only; dismissable.
52
- w('welcome-scroll', 'Welcome', 0,
53
- '<button type="button" class="welcome__minimize" id="welcome-minimize" aria-label="Minimize the welcome panel" title="Minimize">−</button>' +
54
- '<h2 id="welcome-h" class="scroll__h">Welcome</h2>' +
55
- '<div class="welcome" id="welcome-body"><div class="hall-loading" role="status">Loading…</div></div>',
56
- { view: 'home', zone: 'home-banner', welcomeChip: true, sectionClass: 'scroll scroll--welcome', labelledby: 'welcome-h', hiddenByDefault: true });
57
-
58
- // 0b. Onboarding checklist — V3.R75 (#294).
59
- w('onboarding-scroll', 'Getting started', 10,
60
- '<h2 id="onboarding-h" class="scroll__h">Getting started</h2>' +
61
- '<p class="scroll__lede">A short path to finding your feet. Tick the steps you’ve done; the rest are tracked for you.</p>' +
62
- '<ol class="onboarding-list" id="onboarding-list"><li class="hall-loading" role="status">Loading…</li></ol>' +
63
- '<p class="ledger__note" id="onboarding-status" hidden></p>',
64
- { view: 'home', zone: 'home-banner', labelledby: 'onboarding-h', hiddenByDefault: true });
51
+ // (task 1005076: the Welcome setup banner and the Getting started checklist left Home.
52
+ // Setup is a screen of its own that a new builder finishes once — setup-gate.js — and
53
+ // a set-up builder is never shown a setup banner again; owner, 2026-11-05.)
65
54
 
66
55
  // (task 1005054: the quick-stats strip and the version tab left Home. The
67
56
  // zone they held, home-stats, is the project city's — home-city.js.)
@@ -53,7 +53,7 @@
53
53
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
54
54
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
55
55
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
56
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
56
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
57
57
  <script src="/builders/palette.js" defer></script>
58
58
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
59
59
  written inside an idea's own prose keeps resolving from this page. -->
@@ -228,7 +228,7 @@
228
228
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
229
229
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
230
230
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
231
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
231
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
232
232
  <script src="/builders/palette.js" defer></script>
233
233
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
234
234
  <script src="/builders/vote-control.js"></script>
@@ -74,10 +74,9 @@
74
74
  <!-- task 1005043: Home's loader, shown until a card mounts into a zone. -->
75
75
  <p class="hall-loading hall-loading--page hall-loading--zones" role="status">Loading…</p>
76
76
  <div class="view-zone" id="zone-home-banner">
77
- <!-- Landing card for signed-out visitors (builders.js renderLanding)
78
- + the re-open affordance for a dismissed welcome banner. -->
77
+ <!-- Landing card for signed-out visitors (builders.js renderLanding).
78
+ No setup banner: setup is a screen of its own (setup-gate.js, task 1005076). -->
79
79
  <div id="landing-root" hidden></div>
80
- <p id="welcome-reopen" hidden><a href="#home" class="pill-link">Show the setup guide again</a></p>
81
80
  </div>
82
81
  <div class="view-zone" id="zone-home-stats"></div>
83
82
  <div class="view-zone view-zone--band" id="zone-home-band"></div>
@@ -123,7 +122,7 @@
123
122
  <script src="/builders/welcome-boot.js?v=2026-10-23-city1" defer></script>
124
123
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab" defer></script>
125
124
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight" defer></script>
126
- <script src="/builders/shell.js?v=2026-11-05-oversight" defer></script>
125
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate" defer></script>
127
126
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
128
127
  <script src="/builders/palette.js" defer></script>
129
128
  <!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
@@ -74,7 +74,7 @@
74
74
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
75
75
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
76
76
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
77
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
77
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
78
78
  <script src="/builders/palette.js" defer></script>
79
79
  <script src="/builders/hall-kit.js"></script>
80
80
  <script src="/builders/market-watch-lib.js"></script>
@@ -43,7 +43,7 @@
43
43
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
44
44
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
45
45
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
46
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
46
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
47
47
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
48
48
  <script src="/builders/palette.js" defer></script>
49
49
  <script src="/builders/hall-kit.js"></script>
@@ -188,7 +188,7 @@
188
188
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
189
189
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
190
190
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
191
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
191
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
192
192
  <script src="/builders/palette.js" defer></script>
193
193
  <script src="/builders/hall-kit.js"></script>
194
194
  <script src="/builders/market-watch-lib.js"></script>
@@ -128,7 +128,7 @@
128
128
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
129
129
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
130
130
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
131
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
131
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
132
132
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
133
133
  <script src="/builders/palette.js" defer></script>
134
134
  <script src="/builders/brand-apply.js"></script>
@@ -82,7 +82,7 @@
82
82
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
83
83
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
84
84
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
85
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
85
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
86
86
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
87
87
  <script src="/builders/palette.js" defer></script>
88
88
  <script src="/builders/hall-kit.js"></script>
@@ -84,7 +84,7 @@
84
84
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
85
85
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
86
86
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
87
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
87
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
88
88
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
89
89
  <script src="/builders/palette.js" defer></script>
90
90
  <script src="/builders/gov-living.js?v=2026-10-10-real1"></script>
@@ -113,7 +113,7 @@
113
113
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
114
114
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
115
115
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
116
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
116
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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
@@ -208,7 +208,7 @@
208
208
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
209
209
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
210
210
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
211
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
211
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
212
212
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
213
213
  <script src="/builders/palette.js" defer></script>
214
214
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -77,7 +77,7 @@
77
77
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
78
78
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
79
79
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
80
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
80
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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>
@@ -44,7 +44,7 @@
44
44
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
45
45
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
46
46
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
47
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
47
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
48
48
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
49
49
  <script src="/builders/palette.js" defer></script>
50
50
  <!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
@@ -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
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -394,7 +394,7 @@
394
394
  <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
395
395
  <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
396
396
  <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
397
- <script src="/builders/shell.js?v=2026-11-05-oversight"></script>
397
+ <script src="/builders/shell.js?v=2026-11-05-setup-gate"></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). -->
@@ -0,0 +1,156 @@
1
+ // modules/hall-ui/public/setup-gate.js — the setup flow a new builder finishes once (task 1005076).
2
+ //
3
+ // THE SERVER DECIDES, THIS ONLY DRAWS. GET /me carries `onboarding.setup`
4
+ // ({ complete, steps[] }, modules/onboarding/onboarding-state.js setupForApi). While
5
+ // `complete` is false the hall is held behind this one screen on every page: the steps
6
+ // in order, each ticking itself from a signal the server records (a linked machine, a
7
+ // chosen role, /builder-start run from Claude). Once complete the builder is in for
8
+ // good: the screen never draws again, and Home carries no setup banner (owner,
9
+ // 2026-11-05: "once they're in, they're in").
10
+ //
11
+ // Its own file, loaded by shell.js only when setup is not complete (the core-move-notice.js
12
+ // way): shell.js sits at the 1500-line ratchet, and a set-up builder never pays for it.
13
+ // The role step saves through PATCH /me/disciplines, the route Settings' preferred roles use.
14
+
15
+ (function () {
16
+ 'use strict';
17
+
18
+ var POLL_MS = 8000;
19
+ var el = null, timer = null, api = null, state = null, picked = [], repo = null;
20
+
21
+ function esc(s) { return window.OTB.escapeHtml(s); }
22
+
23
+ var CHECK = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12.5l4.5 4.5L19 7.5" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>';
24
+
25
+ function stepDetail(step) {
26
+ if (step.id === 'cli_setup') {
27
+ // This project's own repository (GET /instance), never a hardcoded one (ADR 0062 §3).
28
+ var getCode = repo
29
+ ? '<pre class="setup-gate__code">git clone ' + esc(repo.url) + '.git\ncd ' + esc(repo.repo) + '\nnpm install\nnpx bongos setup</pre>'
30
+ : '';
31
+ return '<ol class="setup-gate__how">' +
32
+ '<li>Install <a href="https://nodejs.org" target="_blank" rel="noopener">Node</a> (version 22 or newer) and ' +
33
+ '<a href="https://claude.com/download" target="_blank" rel="noopener">Claude</a> (the desktop app, or Claude Code in a terminal).</li>' +
34
+ '<li>Clone the project to your machine and run <code>npm install</code> in it.' + (repo ? '' : ' The project’s README says where it lives.') + '</li>' +
35
+ '<li>In the project folder, run <code>npx bongos setup</code> and sign in when it asks.</li>' +
36
+ '</ol>' + getCode;
37
+ }
38
+ if (step.id === 'pick_disciplines') {
39
+ var crafts = window.OTB.disciplines();
40
+ return '<div class="setup-gate__crafts" role="group" aria-label="' + esc(window.OTB.roleAxisLabel()) + '">' +
41
+ crafts.map(function (c) {
42
+ var on = picked.indexOf(c) !== -1;
43
+ return '<button type="button" class="setup-gate__craft" data-craft="' + esc(c) + '" aria-pressed="' + on + '">' + esc(window.OTB.roleLabel(c)) + '</button>';
44
+ }).join('') +
45
+ '</div>' +
46
+ '<button type="button" class="setup-gate__save" data-save-crafts' + (picked.length ? '' : ' disabled') + '>Save my role</button>' +
47
+ '<p class="setup-gate__err" data-craft-err hidden></p>';
48
+ }
49
+ if (step.id === 'first_start') {
50
+ return '<p class="setup-gate__hint">In Claude, open the project folder and type <code>/builder-start</code>.</p>';
51
+ }
52
+ return '';
53
+ }
54
+
55
+ function render() {
56
+ if (!el || !state) return;
57
+ var steps = state.steps || [];
58
+ var current = steps.filter(function (s) { return !s.cleared; })[0] || null;
59
+ var done = steps.filter(function (s) { return s.cleared; }).length;
60
+ el.querySelector('.setup-gate__count').textContent = done + ' of ' + steps.length + ' done';
61
+ el.querySelector('.setup-gate__steps').innerHTML = steps.map(function (s, i) {
62
+ var isCurrent = current && s.id === current.id;
63
+ var cls = 'setup-gate__step' + (s.cleared ? ' is-done' : '') + (isCurrent ? ' is-current' : '');
64
+ return '<li class="' + cls + '"' + (isCurrent ? ' aria-current="step"' : '') + '>' +
65
+ '<span class="setup-gate__mark">' + (s.cleared ? CHECK : String(i + 1)) + '</span>' +
66
+ '<div class="setup-gate__body">' +
67
+ '<h3 class="setup-gate__label">' + esc(s.label) + '</h3>' +
68
+ (isCurrent ? '<p class="setup-gate__hint">' + esc(s.hint) + '</p>' + stepDetail(s) : '') +
69
+ '</div></li>';
70
+ }).join('');
71
+ }
72
+
73
+ function check() {
74
+ return api.request('GET', '/api/bongos/me').then(function (res) {
75
+ var setup = res && res.ok && res.data && res.data.onboarding && res.data.onboarding.setup;
76
+ if (!setup) return;
77
+ if (setup.complete) { finish(); return; }
78
+ state = setup;
79
+ if (!el.contains(document.activeElement) || !document.activeElement.closest('.setup-gate__step.is-current')) render();
80
+ }, function () { /* offline a moment: keep the screen, try again */ });
81
+ }
82
+
83
+ function saveCrafts(btn) {
84
+ var err = el.querySelector('[data-craft-err]');
85
+ btn.disabled = true;
86
+ api.request('PATCH', '/api/bongos/me/disciplines', { body: { preferred_disciplines: picked.slice() } }).then(function (res) {
87
+ if (res && res.ok) { check(); return; }
88
+ btn.disabled = false;
89
+ if (err) { err.textContent = 'That did not save. Try again.'; err.hidden = false; }
90
+ }, function () {
91
+ btn.disabled = false;
92
+ if (err) { err.textContent = 'That did not save. Check your connection and try again.'; err.hidden = false; }
93
+ });
94
+ }
95
+
96
+ function onClick(e) {
97
+ var craft = e.target.closest('[data-craft]');
98
+ if (craft) {
99
+ var id = craft.getAttribute('data-craft');
100
+ var at = picked.indexOf(id);
101
+ if (at === -1) picked.push(id); else picked.splice(at, 1);
102
+ craft.setAttribute('aria-pressed', String(at === -1));
103
+ var save = el.querySelector('[data-save-crafts]');
104
+ if (save) save.disabled = !picked.length;
105
+ return;
106
+ }
107
+ var save2 = e.target.closest('[data-save-crafts]');
108
+ if (save2) saveCrafts(save2);
109
+ }
110
+
111
+ function finish() {
112
+ if (timer) { clearInterval(timer); timer = null; }
113
+ if (el) el.remove();
114
+ el = null;
115
+ document.documentElement.classList.remove('setup-gate-open');
116
+ window.location.reload();
117
+ }
118
+
119
+ /** Hold the hall behind the setup screen. `setup` is /me's onboarding.setup; `opts.api` the shell's client. */
120
+ function show(setup, opts) {
121
+ if (!setup || setup.complete) return;
122
+ api = opts && opts.api;
123
+ state = setup;
124
+ if (el) { render(); return; }
125
+ var name = opts && opts.builder && (opts.builder.display_name || opts.builder.github_login);
126
+ el = document.createElement('div');
127
+ el.className = 'setup-gate';
128
+ el.id = 'setup-gate';
129
+ el.setAttribute('role', 'dialog');
130
+ el.setAttribute('aria-modal', 'true');
131
+ el.setAttribute('aria-labelledby', 'setup-gate-h');
132
+ el.innerHTML =
133
+ '<div class="setup-gate__panel">' +
134
+ '<p class="setup-gate__count" aria-live="polite"></p>' +
135
+ '<h2 class="setup-gate__title" id="setup-gate-h">' + (name ? 'Welcome, ' + esc(name) + '. ' : '') + 'Let’s get you set up</h2>' +
136
+ '<p class="setup-gate__lede">Finish these steps once and you’re in. Each one ticks itself as you go.</p>' +
137
+ '<ol class="setup-gate__steps"></ol>' +
138
+ '<p class="setup-gate__foot"><a href="/api/bongos/auth/logout" id="setup-gate-signout">Sign out</a></p>' +
139
+ '</div>';
140
+ el.addEventListener('click', onClick);
141
+ document.body.appendChild(el);
142
+ document.documentElement.classList.add('setup-gate-open');
143
+ render();
144
+ var h = document.getElementById('setup-gate-h');
145
+ if (h) { h.setAttribute('tabindex', '-1'); h.focus(); }
146
+ if (api) {
147
+ timer = setInterval(check, POLL_MS);
148
+ api.request('GET', '/api/bongos/instance').then(function (res) {
149
+ var r = res && res.ok && res.data && res.data.repo;
150
+ if (r && !r.error && r.url) { repo = r; render(); }
151
+ }, function () { /* no repo coords: the README line stands in */ });
152
+ }
153
+ }
154
+
155
+ window.OTBSetupGate = { show: show };
156
+ })();