@bongos/core 1.24.42 → 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 (79) hide show
  1. package/.bongos-core.json +93 -83
  2. package/docs/copy-inventory.md +86 -104
  3. package/docs/copy-registry.json +159 -351
  4. package/docs/module-api-changelog.md +2 -0
  5. package/docs/onboarding/primer.md +1 -1
  6. package/docs/page-inventory.json +3 -3
  7. package/docs/page-readings.json +386 -428
  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 +22 -505
  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 +23 -524
  24. package/modules/hall-ui/public/hall-widgets.js +11 -109
  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 +6 -19
  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/shell-trail.js +2 -1
  47. package/modules/hall-ui/public/shell.js +28 -25
  48. package/modules/hall-ui/public/street.html +4 -4
  49. package/modules/hall-ui/public/studio.html +4 -4
  50. package/modules/hall-ui/public/task.html +4 -4
  51. package/modules/hall-ui/public/thinking.html +4 -4
  52. package/modules/hall-ui/public/tweak-editor.html +4 -4
  53. package/modules/hall-ui/public/watch.html +4 -4
  54. package/modules/hall-ui/public/work.html +4 -4
  55. package/package-lock.json +2 -2
  56. package/package.json +1 -1
  57. package/release-notes.json +6 -0
  58. package/src/module-api.js +1 -1
  59. package/tests/curve_controls_delegation.mjs +9 -8
  60. package/tests/curve_kind_overlay.mjs +5 -5
  61. package/tests/hall_acclaim_vote_control.mjs +5 -3
  62. package/tests/hall_audit.mjs +10 -6
  63. package/tests/hall_board_world.mjs +40 -26
  64. package/tests/hall_builders_page_pass.mjs +14 -5
  65. package/tests/hall_home_ships_bounded.mjs +10 -21
  66. package/tests/hall_kit_charts.mjs +3 -2
  67. package/tests/hall_loading.mjs +2 -1
  68. package/tests/hall_nav.mjs +36 -37
  69. package/tests/hall_numbers_page.mjs +100 -0
  70. package/tests/hall_palette.mjs +19 -24
  71. package/tests/hall_profile_world.mjs +7 -2
  72. package/tests/hall_reading_room.mjs +3 -2
  73. package/tests/helpers.mjs +8 -2
  74. package/tests/leaderboard_roles.mjs +1 -1
  75. package/tests/module_contributions.mjs +6 -4
  76. package/tests/nav_permission_atoms.mjs +3 -2
  77. package/tests/page_tweak_artist.mjs +2 -1
  78. package/tests/rank_tier_single_source.mjs +3 -0
  79. package/tests/task_visuals.mjs +3 -1
@@ -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-setup-gate"></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-setup-gate"></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-setup-gate"></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-setup-gate"></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-setup-gate"></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). -->
@@ -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
@@ -1241,8 +1244,8 @@
1241
1244
  }
1242
1245
 
1243
1246
  function route(opts) {
1244
- // The redirect runs BEFORE the view lookup: #leaderboard is no longer a
1245
- // 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
1246
1249
  // page on Home — a dead link that looks like it worked. replace() rather
1247
1250
  // than assign() so Back does not bounce the reader between the two.
1248
1251
  var redirect = IS_INDEX && VIEW_REDIRECTS[String(window.location.hash || "").replace(/^#/, "")];
@@ -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-setup-gate"></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-setup-gate"></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. -->
@@ -56,10 +56,10 @@
56
56
 
57
57
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
58
58
  time so the chrome exists before the deferred page scripts execute. -->
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-setup-gate"></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
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -138,10 +138,10 @@
138
138
  </div>
139
139
  </div>
140
140
 
141
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
142
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
143
- <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
144
- <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
141
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
142
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
143
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
144
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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>
@@ -142,10 +142,10 @@
142
142
 
143
143
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
144
144
 
145
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
146
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
147
- <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
148
- <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
145
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
146
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
147
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
148
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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>
@@ -268,10 +268,10 @@
268
268
 
269
269
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
270
270
  chrome exists before the page script runs. -->
271
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
272
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
273
- <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
274
- <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
271
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
272
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
273
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
274
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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). -->
@@ -171,10 +171,10 @@
171
171
 
172
172
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
- <script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
175
- <script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
176
- <script src="/builders/craft-mode.js?v=2026-11-05-oversight"></script>
177
- <script src="/builders/shell.js?v=2026-11-05-setup-gate"></script>
174
+ <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
175
+ <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
176
+ <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
177
+ <script src="/builders/shell.js?v=2026-11-05-numbers"></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 -->
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.42",
3
+ "version": "1.24.43",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.42",
9
+ "version": "1.24.43",
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.42",
3
+ "version": "1.24.43",
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",
@@ -11215,5 +11215,11 @@
11215
11215
  "id": "1005076",
11216
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
11217
  }
11218
+ ],
11219
+ "1.24.43": [
11220
+ {
11221
+ "id": "1005050",
11222
+ "text": "Your numbers are on one page now: your Profile. The separate Accomplishments page is gone from the sidebar. The parts only you should see — your rank and how to reach the next one, how your work was reviewed, your karma histor"
11223
+ }
11218
11224
  ]
11219
11225
  }
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.42'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.24.43'; // 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');
@@ -23,18 +23,19 @@ import { fileURLToPath } from 'node:url';
23
23
 
24
24
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
25
25
  const PUB = path.join(ROOT, 'modules/hall-ui/public');
26
- const widgetsSrc = fs.readFileSync(path.join(PUB, 'hall-widgets.js'), 'utf8');
27
- const buildersSrc = fs.readFileSync(path.join(PUB, 'builders.js'), 'utf8');
26
+ // task 1005050: the Progress section moved with Accomplishments to the Profile
27
+ // page's "Just you" screen — its markup is profile.html's, its wiring profile-own.js's.
28
+ const pageSrc = fs.readFileSync(path.join(PUB, 'profile.html'), 'utf8');
29
+ const buildersSrc = fs.readFileSync(path.join(PUB, 'profile-own.js'), 'utf8');
28
30
 
29
31
  const SECTION_ID = 'analytics-scroll';
30
32
 
31
33
  // The Progress widget's markup: the concatenated string literals of its w() call.
32
34
  function progressMarkup() {
33
- const start = widgetsSrc.indexOf(`w('${SECTION_ID}'`);
34
- assert.ok(start >= 0, `hall-widgets.js no longer registers '${SECTION_ID}'`);
35
- const end = widgetsSrc.indexOf("{ view: 'standing'", start);
36
- const call = widgetsSrc.slice(start, end);
37
- return [...call.matchAll(/'((?:[^'\\]|\\.)*)'/g)].map((m) => m[1]).slice(2).join('');
35
+ const start = pageSrc.indexOf(`id="${SECTION_ID}"`);
36
+ assert.ok(start >= 0, `profile.html no longer carries '${SECTION_ID}'`);
37
+ const open = pageSrc.lastIndexOf('<section', start);
38
+ return pageSrc.slice(open, pageSrc.indexOf('</section>', start) + '</section>'.length);
38
39
  }
39
40
 
40
41
  // True if the element with id=`outer` encloses the element with id=`inner`.
@@ -57,7 +58,7 @@ function encloses(html, outer, inner) {
57
58
  // The selector each curve-control listener is attached to in builders.js.
58
59
  function delegationTarget(marker) {
59
60
  const at = buildersSrc.indexOf(marker);
60
- assert.ok(at >= 0, `builders.js no longer handles ${marker}`);
61
+ assert.ok(at >= 0, `profile-own.js no longer handles ${marker}`);
61
62
  const before = buildersSrc.slice(0, at);
62
63
  const all = [...before.matchAll(/\$\('#([\w-]+)'\)\?\.addEventListener\('(click|change)'/g)];
63
64
  assert.ok(all.length, `no delegated listener found before ${marker}`);
@@ -254,7 +254,7 @@ test('cost is WITHHELD under a kind filter, not faked', () => {
254
254
  });
255
255
 
256
256
  test('the client hides the cost chart and stat rather than printing $0.00', () => {
257
- const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/hall-render.js'), 'utf8');
257
+ const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
258
258
  assert.match(render, /costUnavailable = Array\.isArray\(data\.unavailable\) && data\.unavailable\.includes\('cost_trend'\)/);
259
259
  assert.match(render, /costUnavailable\s*\?\s*statCard\('Cost \/ work', '—'/,
260
260
  'a withheld cost must render as —, never as a price');
@@ -264,7 +264,7 @@ test('the controls are written BEFORE every early return', () => {
264
264
  // A kind with nothing shipped in the window takes the totalShipped === 0
265
265
  // branch, which blanks the body. If the filter were drawn after that, the
266
266
  // reader would be stranded at "no data" with no control to undo it.
267
- const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/hall-render.js'), 'utf8');
267
+ const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
268
268
  const fn = render.slice(render.indexOf('function renderCurveBody()'));
269
269
  const controls = fn.indexOf('kindFilterHtml()');
270
270
  const firstReturn = fn.indexOf('if (!data) {');
@@ -275,8 +275,8 @@ test('the controls are written BEFORE every early return', () => {
275
275
  });
276
276
 
277
277
  test('the scope toggle is no longer rank-gated', () => {
278
- const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/hall-render.js'), 'utf8');
279
- const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/builders.js'), 'utf8');
278
+ const render = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
279
+ const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
280
280
  assert.ok(!/analyticsState\.isArchon\s*&&/.test(render),
281
281
  'the scope toggle is still gated on rank');
282
282
  // The field is REMOVED, not merely unread — a write-only state flag is how a
@@ -288,7 +288,7 @@ test('the scope toggle is no longer rank-gated', () => {
288
288
  });
289
289
 
290
290
  test('a kind change re-fetches BOTH series, and the newest answer wins', () => {
291
- const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/builders.js'), 'utf8');
291
+ const builders = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/profile-own.js'), 'utf8');
292
292
  const fn = builders.slice(builders.indexOf('async function reloadCurveForKind'));
293
293
  const body = fn.slice(0, fn.indexOf('\n }') + 4);
294
294
  assert.match(body, /analytics\/builder\/\$\{id\}\?days=90\$\{q\}/);
@@ -19,7 +19,9 @@ import { fileURLToPath } from 'node:url';
19
19
  const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
20
20
  const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
21
21
  const read = (f) => fs.readFileSync(path.join(HALL, f), 'utf8').replace(/\r\n/g, '\n');
22
- const builders = read('builders.js');
22
+ // task 1005050: the peer-recognition feed moved with Accomplishments to the
23
+ // Profile page's "Just you" screen (profile-own.js; its markup is profile.html's).
24
+ const builders = read('profile-own.js');
23
25
 
24
26
  // A small DOM: enough for the control's cast() and event dispatch.
25
27
  function el(attrs = {}) {
@@ -71,7 +73,7 @@ function bootControl(fetchImpl) {
71
73
  // The feed's real handler, sliced from builders.js and run over a held list.
72
74
  function feedHandler(tasks) {
73
75
  const start = builders.indexOf('function onAcclaimVote(');
74
- assert.ok(start > 0, 'builders.js defines onAcclaimVote');
76
+ assert.ok(start > 0, 'profile-own.js defines onAcclaimVote');
75
77
  const end = builders.indexOf('\n }\n', start) + 4;
76
78
  const ctx = { acclaimState: { tasks } };
77
79
  vm.createContext(ctx);
@@ -109,5 +111,5 @@ test('a vote through the real control lands in the held, paged rows', async () =
109
111
  test('the old vote markup, handler and CSS are gone', () => {
110
112
  assert.doesNotMatch(builders, /acclaim__btn|acclaim__vote|data-vote-task|onAcclaimClick|data-acclaim-net/);
111
113
  assert.doesNotMatch(read('style.css'), /\.acclaim__(btn|vote|readonly|net)/);
112
- assert.match(read('hall-widgets.js'), /<th>Work<\/th><th>By<\/th><th>Votes<\/th><\/tr>/);
114
+ assert.match(read('profile.html'), /<th>Work<\/th><th>By<\/th><th>Votes<\/th><\/tr>/);
113
115
  });