@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.
- package/.bongos-core.json +117 -92
- package/docs/copy-inventory.md +98 -137
- package/docs/copy-registry.json +244 -644
- package/docs/module-api-changelog.md +4 -0
- package/docs/onboarding/primer.md +1 -1
- package/docs/page-inventory.json +3 -3
- package/docs/page-readings.json +494 -535
- package/modules/hall-ui/public/ai-studio.html +4 -4
- package/modules/hall-ui/public/atlas.html +4 -4
- package/modules/hall-ui/public/blockers.html +4 -4
- package/modules/hall-ui/public/board-room.html +4 -4
- package/modules/hall-ui/public/builders.js +35 -763
- package/modules/hall-ui/public/city.html +4 -4
- package/modules/hall-ui/public/collab.html +4 -4
- package/modules/hall-ui/public/commands.html +4 -4
- package/modules/hall-ui/public/copy-desk.html +4 -4
- package/modules/hall-ui/public/craft-mode.js +1 -1
- package/modules/hall-ui/public/deploy.html +4 -4
- package/modules/hall-ui/public/dom-utils.js +2 -1
- package/modules/hall-ui/public/goal-map.html +4 -4
- package/modules/hall-ui/public/goals.html +4 -4
- package/modules/hall-ui/public/government.html +4 -4
- package/modules/hall-ui/public/hall-render.js +25 -771
- package/modules/hall-ui/public/hall-widgets.js +14 -123
- package/modules/hall-ui/public/idea.html +4 -4
- package/modules/hall-ui/public/ideas.html +4 -4
- package/modules/hall-ui/public/index.html +8 -22
- package/modules/hall-ui/public/index.states.json +0 -33
- package/modules/hall-ui/public/market-watch-item.html +4 -4
- package/modules/hall-ui/public/market-watch-rules.html +4 -4
- package/modules/hall-ui/public/market-watch.html +4 -4
- package/modules/hall-ui/public/modules.html +4 -4
- package/modules/hall-ui/public/money.html +4 -4
- package/modules/hall-ui/public/my-agents.html +4 -4
- package/modules/hall-ui/public/palette.js +7 -6
- package/modules/hall-ui/public/primer.html +4 -4
- package/modules/hall-ui/public/profile-own.js +639 -0
- package/modules/hall-ui/public/profile.css +2 -1
- package/modules/hall-ui/public/profile.html +48 -5
- package/modules/hall-ui/public/profile.js +27 -2
- package/modules/hall-ui/public/profile.states.json +18 -0
- package/modules/hall-ui/public/project-settings.html +4 -4
- package/modules/hall-ui/public/roadmap.html +4 -4
- package/modules/hall-ui/public/roster.html +4 -4
- package/modules/hall-ui/public/settings.html +4 -4
- package/modules/hall-ui/public/setup-gate.js +156 -0
- package/modules/hall-ui/public/shell-trail.js +2 -1
- package/modules/hall-ui/public/shell.js +44 -26
- package/modules/hall-ui/public/street.html +4 -4
- package/modules/hall-ui/public/studio.html +4 -4
- package/modules/hall-ui/public/style.css +85 -117
- package/modules/hall-ui/public/task.html +4 -4
- package/modules/hall-ui/public/thinking.html +4 -4
- package/modules/hall-ui/public/tweak-editor.html +4 -4
- package/modules/hall-ui/public/watch.html +4 -4
- package/modules/hall-ui/public/work.html +4 -4
- package/modules/hall-ui/records/setup-gate.md +43 -0
- package/modules/lifecycle/routes/tasks.js +2 -2
- package/modules/onboarding/onboarding-state.js +41 -20
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/bongos/routes/me.js +2 -2
- package/src/module-api.js +1 -1
- package/tests/curve_controls_delegation.mjs +9 -8
- package/tests/curve_kind_overlay.mjs +5 -5
- package/tests/hall_acclaim_vote_control.mjs +5 -3
- package/tests/hall_audit.mjs +10 -6
- package/tests/hall_board_world.mjs +41 -27
- package/tests/hall_builders_page_pass.mjs +14 -5
- package/tests/hall_home_ships_bounded.mjs +10 -21
- package/tests/hall_kit_charts.mjs +3 -2
- package/tests/hall_landing_boot.mjs +2 -3
- package/tests/hall_loading.mjs +2 -1
- package/tests/hall_nav.mjs +36 -37
- package/tests/hall_numbers_page.mjs +100 -0
- package/tests/hall_onboarding_bongos_setup.mjs +14 -14
- package/tests/hall_palette.mjs +19 -24
- package/tests/hall_profile_world.mjs +7 -2
- package/tests/hall_reading_room.mjs +3 -2
- package/tests/helpers.mjs +8 -2
- package/tests/leaderboard_roles.mjs +1 -1
- package/tests/module_contributions.mjs +10 -6
- package/tests/nav_permission_atoms.mjs +4 -2
- package/tests/onboarding_state.mjs +33 -0
- package/tests/page_tweak_artist.mjs +2 -1
- package/tests/rank_tier_single_source.mjs +3 -0
- package/tests/setup_flow.mjs +242 -0
- package/tests/task_visuals.mjs +3 -1
- 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-
|
|
209
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
210
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
211
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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
|
-
|
|
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
|
-
|
|
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-
|
|
78
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
79
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
80
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
45
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
46
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
47
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
60
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
61
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
62
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
395
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
396
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
397
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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
|
-
|
|
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
|
|
17
|
-
// #
|
|
18
|
-
//
|
|
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
|
-
|
|
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).
|
|
40
|
-
//
|
|
41
|
-
|
|
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
|
|
292
|
-
//
|
|
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
|
-
|
|
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
|
|
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-
|
|
232
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
233
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
234
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
226
|
-
<script src="/builders/shell-trail.js?v=2026-
|
|
227
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-
|
|
228
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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. -->
|