@bongos/core 1.24.41 → 1.24.43

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 (90) hide show
  1. package/.bongos-core.json +117 -92
  2. package/docs/copy-inventory.md +98 -137
  3. package/docs/copy-registry.json +244 -644
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/onboarding/primer.md +1 -1
  6. package/docs/page-inventory.json +3 -3
  7. package/docs/page-readings.json +494 -535
  8. package/modules/hall-ui/public/ai-studio.html +4 -4
  9. package/modules/hall-ui/public/atlas.html +4 -4
  10. package/modules/hall-ui/public/blockers.html +4 -4
  11. package/modules/hall-ui/public/board-room.html +4 -4
  12. package/modules/hall-ui/public/builders.js +35 -763
  13. package/modules/hall-ui/public/city.html +4 -4
  14. package/modules/hall-ui/public/collab.html +4 -4
  15. package/modules/hall-ui/public/commands.html +4 -4
  16. package/modules/hall-ui/public/copy-desk.html +4 -4
  17. package/modules/hall-ui/public/craft-mode.js +1 -1
  18. package/modules/hall-ui/public/deploy.html +4 -4
  19. package/modules/hall-ui/public/dom-utils.js +2 -1
  20. package/modules/hall-ui/public/goal-map.html +4 -4
  21. package/modules/hall-ui/public/goals.html +4 -4
  22. package/modules/hall-ui/public/government.html +4 -4
  23. package/modules/hall-ui/public/hall-render.js +25 -771
  24. package/modules/hall-ui/public/hall-widgets.js +14 -123
  25. package/modules/hall-ui/public/idea.html +4 -4
  26. package/modules/hall-ui/public/ideas.html +4 -4
  27. package/modules/hall-ui/public/index.html +8 -22
  28. package/modules/hall-ui/public/index.states.json +0 -33
  29. package/modules/hall-ui/public/market-watch-item.html +4 -4
  30. package/modules/hall-ui/public/market-watch-rules.html +4 -4
  31. package/modules/hall-ui/public/market-watch.html +4 -4
  32. package/modules/hall-ui/public/modules.html +4 -4
  33. package/modules/hall-ui/public/money.html +4 -4
  34. package/modules/hall-ui/public/my-agents.html +4 -4
  35. package/modules/hall-ui/public/palette.js +7 -6
  36. package/modules/hall-ui/public/primer.html +4 -4
  37. package/modules/hall-ui/public/profile-own.js +639 -0
  38. package/modules/hall-ui/public/profile.css +2 -1
  39. package/modules/hall-ui/public/profile.html +48 -5
  40. package/modules/hall-ui/public/profile.js +27 -2
  41. package/modules/hall-ui/public/profile.states.json +18 -0
  42. package/modules/hall-ui/public/project-settings.html +4 -4
  43. package/modules/hall-ui/public/roadmap.html +4 -4
  44. package/modules/hall-ui/public/roster.html +4 -4
  45. package/modules/hall-ui/public/settings.html +4 -4
  46. package/modules/hall-ui/public/setup-gate.js +156 -0
  47. package/modules/hall-ui/public/shell-trail.js +2 -1
  48. package/modules/hall-ui/public/shell.js +44 -26
  49. package/modules/hall-ui/public/street.html +4 -4
  50. package/modules/hall-ui/public/studio.html +4 -4
  51. package/modules/hall-ui/public/style.css +85 -117
  52. package/modules/hall-ui/public/task.html +4 -4
  53. package/modules/hall-ui/public/thinking.html +4 -4
  54. package/modules/hall-ui/public/tweak-editor.html +4 -4
  55. package/modules/hall-ui/public/watch.html +4 -4
  56. package/modules/hall-ui/public/work.html +4 -4
  57. package/modules/hall-ui/records/setup-gate.md +43 -0
  58. package/modules/lifecycle/routes/tasks.js +2 -2
  59. package/modules/onboarding/onboarding-state.js +41 -20
  60. package/package-lock.json +2 -2
  61. package/package.json +1 -1
  62. package/release-notes.json +12 -0
  63. package/src/bongos/routes/me.js +2 -2
  64. package/src/module-api.js +1 -1
  65. package/tests/curve_controls_delegation.mjs +9 -8
  66. package/tests/curve_kind_overlay.mjs +5 -5
  67. package/tests/hall_acclaim_vote_control.mjs +5 -3
  68. package/tests/hall_audit.mjs +10 -6
  69. package/tests/hall_board_world.mjs +41 -27
  70. package/tests/hall_builders_page_pass.mjs +14 -5
  71. package/tests/hall_home_ships_bounded.mjs +10 -21
  72. package/tests/hall_kit_charts.mjs +3 -2
  73. package/tests/hall_landing_boot.mjs +2 -3
  74. package/tests/hall_loading.mjs +2 -1
  75. package/tests/hall_nav.mjs +36 -37
  76. package/tests/hall_numbers_page.mjs +100 -0
  77. package/tests/hall_onboarding_bongos_setup.mjs +14 -14
  78. package/tests/hall_palette.mjs +19 -24
  79. package/tests/hall_profile_world.mjs +7 -2
  80. package/tests/hall_reading_room.mjs +3 -2
  81. package/tests/helpers.mjs +8 -2
  82. package/tests/leaderboard_roles.mjs +1 -1
  83. package/tests/module_contributions.mjs +10 -6
  84. package/tests/nav_permission_atoms.mjs +4 -2
  85. package/tests/onboarding_state.mjs +33 -0
  86. package/tests/page_tweak_artist.mjs +2 -1
  87. package/tests/rank_tier_single_source.mjs +3 -0
  88. package/tests/setup_flow.mjs +242 -0
  89. package/tests/task_visuals.mjs +3 -1
  90. package/tests/welcome_boot.mjs +3 -2
@@ -174,6 +174,47 @@
174
174
  </div>
175
175
  </section>
176
176
 
177
+ <!-- ============ yours: only on your own profile (task 1005050) ============ -->
178
+
179
+ <!-- Accomplishments folded into this page (owner decision 2026-11-03,
180
+ idea 1001315): what was only ever yours there lives here, and
181
+ profile-own.js draws it ONLY on the viewer's own profile. Every
182
+ section starts hidden, and so does the screen's rail key, so
183
+ someone else's profile never shows it. -->
184
+ <section class="scroll" id="rank-scroll" aria-labelledby="rank-standing-h" data-group="yours" hidden>
185
+ <h2 id="rank-standing-h" class="scroll__h">Where you stand</h2>
186
+ <div id="rank-standing-body"><div class="hall-loading" role="status">Loading…</div></div>
187
+ </section>
188
+ <section class="scroll" id="grades-scroll" aria-labelledby="marks-h" data-group="yours" hidden>
189
+ <h2 id="marks-h" class="scroll__h">Reviews</h2>
190
+ <table class="ledger" id="recent-grades"><thead><tr><th>№</th><th>Work</th><th>Score</th><th>Verdict</th><th>Reviewed</th></tr></thead>
191
+ <tbody><tr><td colspan="5" class="ledger__empty"><span class="hall-loading" role="status">Loading…</span></td></tr></tbody></table>
192
+ <p class="ledger__note" id="grades-rolling-note" hidden></p>
193
+ </section>
194
+ <section class="scroll" id="karma-scroll" aria-labelledby="karma-h" data-group="yours" hidden>
195
+ <h2 id="karma-h" class="scroll__h">Karma</h2>
196
+ <p class="scroll__lede">Ideas passed, peer votes and approvals earn karma. Shipped work earns <span data-currency-label>credits</span>.</p>
197
+ <table class="ledger" id="karma-feed"><thead><tr><th>When</th><th>Delta</th><th>Reason</th><th>Granted by</th></tr></thead>
198
+ <tbody><tr><td colspan="4" class="ledger__empty"><span class="hall-loading" role="status">Loading…</span></td></tr></tbody></table>
199
+ </section>
200
+ <section class="scroll" id="acclaim-scroll" aria-labelledby="acclaim-h" data-group="yours" hidden>
201
+ <h2 id="acclaim-h" class="scroll__h">Peer recognition</h2>
202
+ <p class="scroll__lede">Vote on others’ finished work. A vote counts toward their karma after a week.</p>
203
+ <table class="ledger" id="acclaim-feed"><thead><tr><th>Work</th><th>By</th><th>Votes</th></tr></thead>
204
+ <tbody><tr><td colspan="3" class="ledger__empty"><span class="hall-loading" role="status">Loading…</span></td></tr></tbody></table>
205
+ <div id="acclaim-pager-mount"></div>
206
+ </section>
207
+ <section class="scroll" id="analytics-scroll" aria-labelledby="curve-h" data-group="yours" hidden>
208
+ <h2 id="curve-h" class="scroll__h">Progress</h2>
209
+ <p class="scroll__lede">How your work has changed over time. <span id="curve-scope-note"></span></p>
210
+ <div class="curve" id="curve-body">
211
+ <div class="profile__placeholder" id="curve-empty" style="display:none;">Not enough shipped work yet to chart.</div>
212
+ <div class="curve__headline" id="curve-headline"></div>
213
+ <div class="curve__grid" id="curve-grid"></div>
214
+ <div class="curve__kinds" id="curve-kinds"></div>
215
+ </div>
216
+ </section>
217
+
177
218
  <!-- ============ connections: hub-only ============ -->
178
219
 
179
220
  <!-- Connections — section 6 of the profile-page spec (R20, task
@@ -205,10 +246,10 @@
205
246
 
206
247
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
207
248
  chrome exists before the page script runs. -->
208
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
209
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
249
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
250
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
251
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
252
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
212
253
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
213
254
  <script src="/builders/palette.js" defer></script>
214
255
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -237,6 +278,8 @@
237
278
  what the tabs draw. Both loaded BEFORE profile.js. -->
238
279
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
239
280
  <script src="/builders/profile-crafts.js?v=2026-10-01-crafts"></script>
240
- <script src="/builders/profile.js?v=2026-10-01-roles"></script>
281
+ <!-- task 1005050: the "Just you" screen, drawn only on your own profile. -->
282
+ <script src="/builders/profile-own.js?v=2026-11-05-own"></script>
283
+ <script src="/builders/profile.js?v=2026-11-05-own"></script>
241
284
  </body>
242
285
  </html>
@@ -862,13 +862,21 @@
862
862
  { id: 'works', label: 'Works' },
863
863
  { id: 'activity', label: 'Activity' },
864
864
  { id: 'connections', label: 'Connections' },
865
+ // task 1005050: Accomplishments' private half, offered on your own profile only.
866
+ { id: 'yours', label: 'Just you' },
865
867
  ];
866
868
  const RAIL_GROUPS = RAIL_ITEMS.map((i) => i.id);
867
869
  let rail = null;
868
870
 
871
+ // True once render() has drawn the "Just you" screen — the viewer's own profile.
872
+ let ownShown = false;
873
+ // Whether the profile asked for is the viewer's own: /profile with no id, or an
874
+ // ?id= equal to the viewer's. Settled in load() from the REQUEST, before any
875
+ // profile data is read, so someone else's profile can never count as yours.
876
+ let ownRequest = false;
869
877
  function railItems() {
870
878
  const conn = $('#conn-scroll');
871
- return RAIL_ITEMS.filter((i) => i.id !== 'connections' || (conn && !conn.hidden));
879
+ return RAIL_ITEMS.filter((i) => (i.id !== 'connections' || (conn && !conn.hidden)) && (i.id !== 'yours' || ownShown));
872
880
  }
873
881
 
874
882
  function activateGroup(group, opts) {
@@ -877,6 +885,8 @@
877
885
  if (!groups) return;
878
886
  groups.dataset.active = group;
879
887
  rail?.activate(group);
888
+ // task 1005050: the "Just you" screen's feeds load the first time it opens.
889
+ if (group === 'yours' && window.OTBProfileOwn && window.OTBProfileOwn.open) window.OTBProfileOwn.open();
880
890
  if (!opts || opts.scroll !== false) window.scrollTo({ top: 0 });
881
891
  }
882
892
 
@@ -963,6 +973,18 @@
963
973
  // reveals its own section or leaves it hidden. Optional by design: a page
964
974
  // that does not load it simply has no connections section.
965
975
  if (window.OTBConnections) window.OTBConnections.render(data, selfId);
976
+ // task 1005050: what only you see (Accomplishments, folded in) — your own
977
+ // profile only. Someone else's profile never calls it, so its screen and
978
+ // rail key stay hidden. The data is the viewer's own /me and public feeds.
979
+ if (ownRequest && selfId != null && selfMe && window.OTBProfileOwn) {
980
+ window.OTBProfileOwn.render({ api, API, me: selfMe });
981
+ ownShown = true;
982
+ if (rail) {
983
+ rail.refresh(railItems());
984
+ rail.activate($('#profile-groups')?.dataset.active || 'overview');
985
+ }
986
+ applyHash(); // an old Accomplishments link lands here as #yours
987
+ }
966
988
  }
967
989
 
968
990
  // Render the friendly "no builder chosen" empty state for a signed-out visitor
@@ -1025,7 +1047,9 @@
1025
1047
  const idParam = new URLSearchParams(location.search).get('id');
1026
1048
  try {
1027
1049
  if (idParam) {
1028
- await fetchProfile(idParam, await resolveSelfId());
1050
+ const selfId = await resolveSelfId();
1051
+ ownRequest = selfId != null && String(idParam) === String(selfId);
1052
+ await fetchProfile(idParam, selfId);
1029
1053
  return;
1030
1054
  }
1031
1055
  // No id — soft-resolve the viewer's OWN id from /me (no hard redirect; a
@@ -1043,6 +1067,7 @@
1043
1067
  renderEmpty();
1044
1068
  return;
1045
1069
  }
1070
+ ownRequest = true;
1046
1071
  await fetchProfile(selfId, selfId);
1047
1072
  } catch (err) {
1048
1073
  console.error('[profile] failed to load', err);
@@ -73,6 +73,24 @@
73
73
  ]
74
74
  }
75
75
  },
76
+ "yours": {
77
+ "_": "Your own profile's \"Just you\" screen (task 1005050): Accomplishments' private half — where you stand, reviews, karma, peer recognition, progress.",
78
+ "auth": true,
79
+ "url": "/profile#yours",
80
+ "actions": [
81
+ [
82
+ "wait",
83
+ 1500
84
+ ]
85
+ ],
86
+ "expect": {
87
+ "visible": [
88
+ "#profile-rail",
89
+ "#rank-scroll",
90
+ "#grades-scroll"
91
+ ]
92
+ }
93
+ },
76
94
  "activity-artist": {
77
95
  "_": "The artist's Activity tab (task 1004435): approved pages, each with its before/after. Fixtures builders__2001__profile + copy-desk__artists__2001__pages.",
78
96
  "auth": true,
@@ -74,10 +74,10 @@
74
74
 
75
75
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
76
76
 
77
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
78
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
77
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
78
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
79
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
80
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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>
@@ -41,10 +41,10 @@
41
41
 
42
42
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
43
43
 
44
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
45
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
44
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
45
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
46
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
47
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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
@@ -56,10 +56,10 @@
56
56
 
57
57
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
58
58
  chrome exists before the page script runs. -->
59
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
60
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
59
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
60
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
62
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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. -->
@@ -391,10 +391,10 @@
391
391
 
392
392
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
393
393
  chrome exists before the page scripts run. -->
394
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
395
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
394
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
395
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
396
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
397
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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
+ })();
@@ -78,7 +78,8 @@
78
78
  function backLabel(path, hash) {
79
79
  if (path === '/') {
80
80
  var m = /^#\/(task|idea|blocker)\/(\d+)/.exec(hash || '');
81
- return m ? m[1] + ' ' + m[2] : (/^#(standing|accomplishments)/.test(hash || '') ? 'Accomplishments' : 'Home');
81
+ // task 1005050: no Accomplishments view any more; its hashes redirect to /profile.
82
+ return m ? m[1] + ' ' + m[2] : 'Home';
82
83
  }
83
84
  var rec = /^\/(task|idea)\/(\d+)$/.exec(path);
84
85
  if (rec) return rec[1] + ' ' + rec[2];
@@ -13,9 +13,9 @@
13
13
  // (#app-sidebar / #app-topbar / #app-foot) every hall page carries;
14
14
  // - rank-gate nav items COSMETICALLY (server-side page gates remain the real
15
15
  // access control — serve-internal.js requireGovernmentPage etc.);
16
- // - on the index (body[data-page="home"]) route the two views (#home,
17
- // #standing) plus the #achievements alias into Standing, and REDIRECT the
18
- // retired #leaderboard hash to the /roster page (task 1003441). The
16
+ // - on the index (body[data-page="home"]) route the #home view, and REDIRECT
17
+ // retired hashes: #leaderboard to the /roster page (task 1003441), and the
18
+ // Accomplishments hashes to your own /profile (task 1005050). The
19
19
  // `#/task|idea|blocker/N` deep-link hashes belong to builders.js and are
20
20
  // deliberately ignored here;
21
21
  // - expose window.OTBShell.applyAccess(...) so builders.js (index) or the
@@ -34,23 +34,12 @@
34
34
  // task 1003441: 'leaderboard' left this list — the Roll is the /roster PAGE
35
35
  // now. Its hash is not an alias (there is no host view to land on) but a
36
36
  // REDIRECT, declared in VIEW_REDIRECTS below.
37
- var VIEWS = ['home', 'standing'];
37
+ // task 1005050: 'standing' (Accomplishments) left too — it folded into /profile.
38
+ var VIEWS = ['home'];
38
39
  // A hash that lands on a view, scrolled to a section (route() ignores any
39
- // other). #achievements (task 2174) and each rail section (task 2178, keys =
40
- // STANDING_GROUPS, held by tests/hall_palette.mjs). Task 1004826 renamed the
41
- // 'standing' view Accomplishments: readers get #accomplishments*; old #standing* still lands.
42
- var VIEW_ALIASES = {
43
- accomplishments: { view: 'standing', anchor: null },
44
- 'accomplishments-work': { view: 'standing', anchor: 'standing-work' },
45
- 'accomplishments-karma': { view: 'standing', anchor: 'standing-karma' },
46
- 'accomplishments-progress': { view: 'standing', anchor: 'standing-progress' },
47
- 'accomplishments-achievements': { view: 'standing', anchor: 'standing-achievements' },
48
- achievements: { view: 'standing', anchor: 'standing-achievements' },
49
- 'standing-work': { view: 'standing', anchor: 'standing-work' },
50
- 'standing-karma': { view: 'standing', anchor: 'standing-karma' },
51
- 'standing-progress': { view: 'standing', anchor: 'standing-progress' },
52
- 'standing-achievements': { view: 'standing', anchor: 'standing-achievements' },
53
- };
40
+ // other). Empty since task 1005050: every alias named the 'standing' view,
41
+ // and those hashes are REDIRECTS now (below).
42
+ var VIEW_ALIASES = Object.create(null);
54
43
  // A retired view whose content moved to its OWN PAGE, so there is no host
55
44
  // view to alias onto. Every existing #leaderboard link — bookmarks, the
56
45
  // Standing card's stat tiles, profile.js's prose link — lands on the roster
@@ -58,8 +47,24 @@
58
47
  // Object.create(null): the key is an attacker-controlled hash string, so a
59
48
  // plain object would answer for "constructor"/"toString" as well as the real
60
49
  // entries. Harmless with one literal key, wrong to leave as the pattern.
50
+ // task 1005050 (owner decision 2026-11-03, idea 1001315): Accomplishments
51
+ // folded into the Profile page, so every #accomplishments / #standing /
52
+ // #achievements link lands on your OWN profile (/profile with no id is
53
+ // yours) — its "Just you" screen, its Works, or the overview with your
54
+ // achievements, whichever the old section was.
61
55
  var VIEW_REDIRECTS = Object.assign(Object.create(null), {
62
56
  leaderboard: '/roster',
57
+ accomplishments: '/profile#yours',
58
+ 'accomplishments-work': '/profile#works',
59
+ 'accomplishments-karma': '/profile#yours',
60
+ 'accomplishments-progress': '/profile#yours',
61
+ 'accomplishments-achievements': '/profile',
62
+ achievements: '/profile',
63
+ standing: '/profile#yours',
64
+ 'standing-work': '/profile#works',
65
+ 'standing-karma': '/profile#yours',
66
+ 'standing-progress': '/profile#yours',
67
+ 'standing-achievements': '/profile',
63
68
  });
64
69
  // builders.js owns these hashes (deep-link overlay) — never route them.
65
70
  var DEEPLINK_RE = /^#\/(task|idea|blocker)\/(\d+)$/;
@@ -288,11 +293,9 @@
288
293
  // call 2026-09-25, ADR 0337 D5). Cross-project discovery is the hub's, and
289
294
  // /people redirects to /roster.
290
295
  ] },
291
- // Your own numbers. task 2174 folded Achievements into Standing and this task
292
- // retires its nav entry; VIEW_ALIASES still routes #achievements here, so old
293
- // bookmarks and the stat tiles keep landing on the section.
296
+ // Your own numbers. task 1005050: Accomplishments folded into Profile — one
297
+ // page for your numbers — and its old links redirect there (VIEW_REDIRECTS).
294
298
  { group: 'You', gate: 'signed', items: [
295
- { id: 'standing', label: 'Accomplishments', icon: 'accomplishments', href: '/#accomplishments', view: 'standing', gate: 'signed' },
296
299
  // task 1467: the viewable, shareable builder profile. The nav link points at
297
300
  // the viewer's OWN profile (profile.js defaults to self when no ?id=). gate is
298
301
  // cosmetic only — the /profile PAGE is public (a portfolio others can open via
@@ -804,6 +807,16 @@
804
807
  }
805
808
  if (data && data.moved) renderCoreMoveNotice(data.moved); // task 1004731: loads core-move-notice.js, which draws it
806
809
  }
810
+ // task 1005076: a builder whose setup is not complete sees only the setup screen (setup-gate.js),
811
+ // loaded once: applyAccess runs again before the first load lands, and a second copy drew a second screen.
812
+ var setupGateLoading = false;
813
+ function holdForSetup(builder) {
814
+ if (access.authed !== true || !access.setup || access.setup.complete !== false) return false;
815
+ var go = function () { if (window.OTBSetupGate) window.OTBSetupGate.show(access.setup, { api: api, builder: builder }); };
816
+ if (window.OTBSetupGate) go();
817
+ else if (!setupGateLoading) { setupGateLoading = true; loadHallScript('setup-gate.js', go); }
818
+ return true;
819
+ }
807
820
  // One hall script on demand, stamped with the shell's own ?v= (task 1004996 made it one helper).
808
821
  function loadHallScript(file, onload, onerror) {
809
822
  var head = document.head, tag = head && head.appendChild && document.createElement ? document.createElement('script') : null;
@@ -1136,6 +1149,8 @@
1136
1149
  if ('founding' in state) {
1137
1150
  access.founding = (state.founding && typeof state.founding === 'object') ? state.founding : null;
1138
1151
  }
1152
+ // task 1005076: /me's onboarding.setup; incomplete holds the hall behind setup-gate.js.
1153
+ if ('setup' in state) access.setup = (state.setup && typeof state.setup === 'object') ? state.setup : null;
1139
1154
  if (state.builder) { renderUser(state.builder); applyCraftEmphasis(state.builder); }
1140
1155
  // task 1004713: which seats this builder holds — /me's server-resolved block.
1141
1156
  if (state.builder && access.authed === true) { loadPositions(state.builder, state.positions); loadGithubChip(state.builder); }
@@ -1162,7 +1177,8 @@
1162
1177
  // permission map, which the loop above is what the shell has just been told.
1163
1178
  maybeRenderCoreUpdate();
1164
1179
  // 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);
1180
+ // task 1005076: neither is drawn over the setup screen; setup comes first.
1181
+ if (!holdForSetup(state && state.builder) && !(window.OTBWelcome && window.OTBWelcome.onAccess(access, state && state.builder)) && CRAFT) CRAFT.onAccess(access, state && state.builder);
1166
1182
  var signin = document.getElementById('shell-signin');
1167
1183
  if (signin) signin.hidden = access.authed !== false;
1168
1184
  var user = document.getElementById('shell-user');
@@ -1228,8 +1244,8 @@
1228
1244
  }
1229
1245
 
1230
1246
  function route(opts) {
1231
- // The redirect runs BEFORE the view lookup: #leaderboard is no longer a
1232
- // view, so viewFromHash would return nothing and route() would leave the
1247
+ // The redirect runs BEFORE the view lookup: #leaderboard (and since task
1248
+ // 1005050 #accomplishments and #standing) is no longer a view, so viewFromHash would return nothing and route() would leave the
1233
1249
  // page on Home — a dead link that looks like it worked. replace() rather
1234
1250
  // than assign() so Back does not bounce the reader between the two.
1235
1251
  var redirect = IS_INDEX && VIEW_REDIRECTS[String(window.location.hash || "").replace(/^#/, "")];
@@ -1406,6 +1422,8 @@
1406
1422
  founding: me.founding_mode || null,
1407
1423
  // task 1004713: the government positions this builder holds (server-resolved).
1408
1424
  positions: me.positions || null,
1425
+ // task 1005076: whether setup is complete; incomplete shows setup-gate.js.
1426
+ setup: (me.onboarding && me.onboarding.setup) || null,
1409
1427
  builder: me.builder,
1410
1428
  });
1411
1429
  }).catch(function () { /* cosmetic only */ });
@@ -228,10 +228,10 @@
228
228
  </div>
229
229
  </div>
230
230
 
231
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
232
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
231
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
232
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
233
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
234
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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>
@@ -222,10 +222,10 @@
222
222
 
223
223
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
224
224
 
225
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
226
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
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>
225
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
226
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
227
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
228
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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. -->