@bongos/core 1.19.1044 → 1.19.1046
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 +106 -121
- package/docs/adr/0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md +14 -2
- package/docs/adr/README.md +1 -1
- package/docs/copy-inventory.md +142 -158
- package/docs/copy-registry.json +173 -360
- package/docs/file-map.md +2 -3
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/windows-builders.md +1 -1
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/claim-action.js +250 -0
- package/modules/hall-ui/public/collab.css +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goals-page.js +28 -44
- package/modules/hall-ui/public/goals-render.js +7 -28
- package/modules/hall-ui/public/goals.html +6 -3
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/{people.css → roster.css} +7 -11
- package/modules/hall-ui/public/roster.html +5 -5
- package/modules/hall-ui/public/roster.js +6 -7
- package/modules/hall-ui/public/roster.states.json +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/settings.js +2 -2
- package/modules/hall-ui/public/shell.js +3 -10
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +2 -9
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +5 -2
- package/modules/hall-ui/public/work.js +35 -123
- package/modules/hall-ui/records/builders-roster.md +1 -1
- package/modules/hall-ui/records/roster-and-ledger.md +2 -2
- package/modules/platform-identity/account-visibility.js +3 -3
- package/modules/platform-identity/platform-identity.js +2 -2
- package/modules/platform-identity/routes/profile.js +1 -1
- package/modules/platform-identity/routes/sso.js +4 -3
- package/modules/platform-identity/scouting-authz.js +2 -2
- package/modules/platform-identity/tests/platform-identity.mjs +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/src/bongos/auth.js +9 -24
- package/src/bongos/serve-internal.js +13 -19
- package/src/module-api.js +1 -1
- package/tests/account_privacy_flags.mjs +1 -1
- package/tests/auth_resolve_failure.mjs +6 -6
- package/tests/claim_action.mjs +417 -0
- package/tests/core_upgrade_owner_door.mjs +1 -1
- package/tests/edit_hints.mjs +8 -8
- package/tests/effective_visibility_predicate.mjs +1 -1
- package/tests/goal_category_ui.mjs +3 -0
- package/tests/goal_criterion_confirm_ui.mjs +3 -0
- package/tests/goal_forest_page.mjs +3 -0
- package/tests/goal_invite_consent.mjs +3 -1
- package/tests/goal_mine_lens.mjs +3 -0
- package/tests/government_parity.mjs +1 -1
- package/tests/hall_builders_page_pass.mjs +2 -2
- package/tests/hall_mine_lens.mjs +3 -0
- package/tests/hall_nav.mjs +8 -8
- package/tests/hall_page_gate_map.mjs +14 -11
- package/tests/hall_roster_world.mjs +47 -78
- package/tests/module_contributions.mjs +0 -1
- package/tests/nav_permission_atoms.mjs +1 -1
- package/tests/{scouting_page_gate.mjs → page_gate_second_door.mjs} +34 -29
- package/tests/profile_rollup_consent.mjs +4 -4
- package/tests/profile_route.mjs +3 -26
- package/tests/scouting_owner_access_and_reach.mjs +4 -8
- package/modules/hall-ui/public/people.html +0 -79
- package/modules/hall-ui/public/people.js +0 -321
- package/modules/hall-ui/public/people.states.json +0 -89
- package/tests/people_directory_clip.mjs +0 -269
- package/tests/people_name_only.mjs +0 -263
|
@@ -235,7 +235,7 @@
|
|
|
235
235
|
|
|
236
236
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
237
237
|
|
|
238
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
238
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
239
239
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
240
240
|
<script src="/builders/palette.js" defer></script>
|
|
241
241
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -243,6 +243,9 @@
|
|
|
243
243
|
<script src="/builders/hall-kit.js"></script>
|
|
244
244
|
<!-- shared TASK-DOMAIN helpers (window.OTBTaskLib) — priority/touches/prompt/refusal-copy, one copy for both boards (task 1002463). -->
|
|
245
245
|
<script src="/builders/task-lib.js"></script>
|
|
246
|
+
<!-- task 1003458: the hall's ONE claim/release action + refusal copy (window.OTBClaimAction).
|
|
247
|
+
Before goals-render.js, which destructures errCode/errDetails/friendlyClaimError from it. -->
|
|
248
|
+
<script src="/builders/claim-action.js?v=2026-09-26-claimaction1"></script>
|
|
246
249
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
|
|
247
250
|
<script src="/builders/brand-apply.js?v=2026-06-27-r75"></script>
|
|
248
251
|
<script src="/builders/goals-lib.js?v=2026-08-21-goalslib1"></script>
|
|
@@ -258,7 +261,7 @@
|
|
|
258
261
|
<!-- task 1003206: the page RENDERERS, the second half of the carve goals-lib.js
|
|
259
262
|
began — goals-lib took the pure helpers, this takes the view painters.
|
|
260
263
|
Loaded BEFORE goals-page.js, which calls window.OTBGoalsRender(ctx). -->
|
|
261
|
-
<script src="/builders/goals-render.js?v=2026-
|
|
262
|
-
<script src="/builders/goals-page.js?v=2026-
|
|
264
|
+
<script src="/builders/goals-render.js?v=2026-09-26-claimaction1"></script>
|
|
265
|
+
<script src="/builders/goals-page.js?v=2026-09-26-claimaction1"></script>
|
|
263
266
|
</body>
|
|
264
267
|
</html>
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
|
|
135
135
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
136
136
|
|
|
137
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
137
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
138
138
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
139
139
|
<script src="/builders/palette.js" defer></script>
|
|
140
140
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
|
|
53
53
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
54
54
|
|
|
55
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
55
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
56
56
|
<script src="/builders/palette.js" defer></script>
|
|
57
57
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib" defer></script>
|
|
58
58
|
<!-- task-detail.js still owns the #/idea/N and #/blocker/N overlay, so a ref
|
|
@@ -225,7 +225,7 @@
|
|
|
225
225
|
|
|
226
226
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
227
227
|
|
|
228
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
228
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
229
229
|
<script src="/builders/palette.js" defer></script>
|
|
230
230
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
231
231
|
<!-- task 1003069 (R08) — the quick-filing form's routed result links a task
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
<script src="/builders/brand-apply.js?v=2026-06-27-r75" defer></script>
|
|
103
103
|
<!-- The app shell (sidebar + top bar + view router). Dependency-free; loaded
|
|
104
104
|
BEFORE builders.js so the chrome ids exist when events are wired. -->
|
|
105
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
105
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople" defer></script>
|
|
106
106
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
107
107
|
<script src="/builders/palette.js" defer></script>
|
|
108
108
|
<!-- task 2166: the shared component kit (window.OTBKit) — pager/ledger/rail.
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
|
|
81
81
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
82
82
|
|
|
83
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
83
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
84
84
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
85
85
|
<script src="/builders/palette.js" defer></script>
|
|
86
86
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
|
|
116
116
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
117
117
|
|
|
118
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
118
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
119
119
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
120
120
|
<script src="/builders/palette.js" defer></script>
|
|
121
121
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
|
|
188
188
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
189
189
|
chrome exists before the page script runs. -->
|
|
190
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
190
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
191
191
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
192
192
|
<script src="/builders/palette.js" defer></script>
|
|
193
193
|
<!-- shared frontend toolkit (window.OTB) — loaded before profile.js, which pulls from it. -->
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
|
|
59
59
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
60
60
|
|
|
61
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
61
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
62
62
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
63
63
|
<script src="/builders/palette.js" defer></script>
|
|
64
64
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
|
|
69
69
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
70
70
|
chrome exists before the page script runs. -->
|
|
71
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
71
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
72
72
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
73
73
|
<script src="/builders/palette.js" defer></script>
|
|
74
74
|
<!-- shared frontend toolkit (window.OTB) — loaded before ranks.js, which pulls from it. -->
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
|
|
42
42
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
43
43
|
|
|
44
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
44
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
45
45
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
46
46
|
<script src="/builders/palette.js" defer></script>
|
|
47
47
|
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
/*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
here: the
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
/* roster.css — page-local styles for the Builders page (/roster). The identity
|
|
2
|
+
cell was shared with the cross-project directory at /people until task
|
|
3
|
+
1003444 deleted that page from the hall; it lives here now. Only the
|
|
4
|
+
identity cell and the place/id cell live here: the ledger, the filter bar,
|
|
5
|
+
the pager and the empty state are the shell's and the kit's. Tokens are the
|
|
6
|
+
world's real names — no compat alias. */
|
|
7
7
|
|
|
8
|
-
/* The who cell: avatar, then the name over the login.
|
|
9
|
-
avatar centres its figures on it, so the numbers line up with the name. */
|
|
10
|
-
#people-body .ledger td { vertical-align: middle; }
|
|
8
|
+
/* The who cell: avatar, then the name over the login. */
|
|
11
9
|
.dir-who { display: inline-flex; align-items: center; gap: 11px; min-width: 0; }
|
|
12
10
|
.dir-avatar {
|
|
13
11
|
width: 30px; height: 30px; border-radius: 50%; flex: none;
|
|
@@ -33,5 +31,3 @@
|
|
|
33
31
|
font-size: 11.5px;
|
|
34
32
|
color: var(--ink-faint);
|
|
35
33
|
}
|
|
36
|
-
.dir-crafts { display: inline-flex; flex-wrap: wrap; gap: 6px; }
|
|
37
|
-
.dir-none { color: var(--ink-faint); }
|
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
12
|
-
<!-- task 1003441: the identity cell (avatar, name, login)
|
|
13
|
-
cross-project
|
|
14
|
-
<link rel="stylesheet" href="/builders/
|
|
12
|
+
<!-- task 1003441: the identity cell (avatar, name, login). It was shared with
|
|
13
|
+
the cross-project /people page until task 1003444 deleted it. -->
|
|
14
|
+
<link rel="stylesheet" href="/builders/roster.css">
|
|
15
15
|
</head>
|
|
16
16
|
<body data-page="roster" data-page-title="Builders">
|
|
17
17
|
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
rewritten by roster.js once the roster has loaded. -->
|
|
26
26
|
<div class="page-head page-head--board">
|
|
27
27
|
<h1 class="page-head__h">Builders</h1>
|
|
28
|
-
<p class="page-head__sub" id="roster-sub">Who builds on this project, ranked by work shipped. This project's own ledger —
|
|
28
|
+
<p class="page-head__sub" id="roster-sub">Who builds on this project, ranked by work shipped. This project's own ledger — everyone who builds here is listed.</p>
|
|
29
29
|
</div>
|
|
30
30
|
|
|
31
31
|
<main id="hall-main">
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
|
|
51
51
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
52
52
|
chrome exists before the page script runs. -->
|
|
53
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
53
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
54
54
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
55
55
|
<script src="/builders/palette.js" defer></script>
|
|
56
56
|
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
// Community nav group read as one real page plus a link into the home page. It
|
|
6
6
|
// is a page now, and `/#leaderboard` redirects here (shell.js VIEW_REDIRECTS).
|
|
7
7
|
//
|
|
8
|
-
// PROJECT-OWN, not cross-project
|
|
9
|
-
//
|
|
10
|
-
// ungated, because its one data source — GET /leaderboard
|
|
8
|
+
// PROJECT-OWN, not cross-project: the hall's only builder directory since task
|
|
9
|
+
// 1003444 deleted /people (cross-project discovery is the hub's, ADR 0337). It
|
|
10
|
+
// is deliberately ungated, because its one data source — GET /leaderboard
|
|
11
11
|
// (modules/economy/routes/leaderboard.js) — is deliberately unauthenticated and
|
|
12
12
|
// mirrors the public status surface. No visibility predicate is applied and that
|
|
13
13
|
// is CORRECT: privacy suppresses cross-project surfaces only, so a builder
|
|
@@ -77,10 +77,9 @@
|
|
|
77
77
|
function httpUrl(u) {
|
|
78
78
|
return /^https?:\/\//i.test(String(u || '')) ? String(u) : null;
|
|
79
79
|
}
|
|
80
|
-
// The identity cell
|
|
81
|
-
//
|
|
82
|
-
//
|
|
83
|
-
// offer GitHub. Guarded on b.id so a row without one degrades to plain text
|
|
80
|
+
// The identity cell (roster.css). The name opens the builder's profile PAGE:
|
|
81
|
+
// this is a project surface, so a local profile id exists. Guarded on b.id
|
|
82
|
+
// so a row without one degrades to plain text
|
|
84
83
|
// rather than a broken /profile?id=undefined link (task 1467).
|
|
85
84
|
function whoHtml(b) {
|
|
86
85
|
var name = esc(builderName(b));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "Builders — this project's OWN roster, ranked by credits earned (task 1003441, goal 1000075). Was the #leaderboard hash view on index.html; /#leaderboard now redirects here. Deliberately UNGATED, because its one data source (GET /leaderboard) is deliberately unauthenticated and mirrors the public status surface — so unlike
|
|
2
|
+
"_": "Builders — this project's OWN roster, ranked by credits earned (task 1003441, goal 1000075). Was the #leaderboard hash view on index.html; /#leaderboard now redirects here. Deliberately UNGATED, because its one data source (GET /leaderboard) is deliberately unauthenticated and mirrors the public status surface — so unlike a gated page's states file the `out` state below renders the real page rather than a seal. Populated look: the hall-preview harness (scripts/hall-preview/fixtures/leaderboard.json carries thirteen builders with avatars, ranks, karma and tokens).",
|
|
3
3
|
"page": "/builders/roster",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -99,7 +99,7 @@
|
|
|
99
99
|
|
|
100
100
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
101
101
|
chrome exists before the page script runs. -->
|
|
102
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
102
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
103
103
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
104
104
|
<script src="/builders/palette.js" defer></script>
|
|
105
105
|
<!-- shared frontend toolkit (window.OTB) — loaded BEFORE sessions.js so it can pull from it. -->
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
<p class="scroll__lede">
|
|
97
97
|
Your track record across every project you build on, shown on your
|
|
98
98
|
public profile. Hiding it takes effect immediately everywhere —
|
|
99
|
-
your profile, the
|
|
99
|
+
your profile, the recruiting directory, and a recruiter's look-up of one builder.
|
|
100
100
|
</p>
|
|
101
101
|
<div id="cross-project-rows"></div>
|
|
102
102
|
<p class="settings-status" id="cross-project-status" role="status" aria-live="polite"></p>
|
|
@@ -436,7 +436,7 @@
|
|
|
436
436
|
|
|
437
437
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
438
438
|
chrome exists before the page scripts run. -->
|
|
439
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
439
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
440
440
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
441
441
|
<script src="/builders/palette.js" defer></script>
|
|
442
442
|
<!-- shared frontend toolkit (window.OTB) — loaded before consuming IIFEs. -->
|
|
@@ -490,7 +490,7 @@
|
|
|
490
490
|
// GET/PATCH /api/bongos/me/cross-project (platform-identity module — hub only).
|
|
491
491
|
// One on/off toggle: "show my cross-project record on my public profile".
|
|
492
492
|
// Enforcement is server-side in the module's read functions, so flipping it
|
|
493
|
-
// takes effect immediately on the profile and /
|
|
493
|
+
// takes effect immediately on the profile and /scouting. The section
|
|
494
494
|
// reveals ONLY when the module's endpoint answers (404 on a vanilla instance →
|
|
495
495
|
// stays hidden, no error state). P04 later re-homes this in the Profile rail
|
|
496
496
|
// group; storage is account-global on the hub (not behavior_prefs).
|
|
@@ -562,7 +562,7 @@
|
|
|
562
562
|
privacyRow('visibility', 'Public profile page', isPublic ? 'Public' : 'Private', isPublic,
|
|
563
563
|
'Public means anyone can open your profile page and find you by name. Private hides the page from everyone — including you: it answers "not found" to every visitor, and you read your own record here in Settings instead.', '') +
|
|
564
564
|
privacyRow('discoverable', 'Discoverable by recruiters', discoverable ? 'On' : 'Off', discoverable,
|
|
565
|
-
`Recruiters looking for builders can find you. ${grant}${despitePrivate} Turning this off removes you from both recruiting surfaces — the
|
|
565
|
+
`Recruiters looking for builders can find you. ${grant}${despitePrivate} Turning this off removes you from both recruiting surfaces — the recruiting directory, and a recruiter's look-up of one builder — straight away.`,
|
|
566
566
|
follows ? ' <span class="voices__default">· follows your profile setting</span>' : '');
|
|
567
567
|
}
|
|
568
568
|
|
|
@@ -92,7 +92,6 @@
|
|
|
92
92
|
goals: '<circle cx="9" cy="9" r="6.5"/><circle cx="9" cy="9" r="3.5"/><circle cx="9" cy="9" r="0.8" fill="currentColor"/>',
|
|
93
93
|
roadmap: '<path d="M3 15.5V3.5l4 2 4-2 4 2v10l-4-2-4 2-4-2Z"/><path d="M7 3.5v10M11 5.5v10"/>',
|
|
94
94
|
leaderboard: '<path d="M3.5 15.5v-6M9 15.5V3.5m5.5 12v-9"/>',
|
|
95
|
-
people: '<circle cx="6.5" cy="6.5" r="2.5"/><path d="M2.5 15c.5-2.4 2-3.7 4-3.7s3.5 1.3 4 3.7"/><circle cx="12.5" cy="7" r="2"/><path d="M12 11.5c1.8.1 3 1.3 3.5 3.3"/>',
|
|
96
95
|
docs: '<path d="M4 3.5h7l3 3v10H4v-13Z"/><path d="M11 3.5v3h3"/>',
|
|
97
96
|
// task 1003117 (R06 of goal 1000074): the Studio (artist mode). A sprout —
|
|
98
97
|
// the zen-garden reading of "a place an artist wants to be", so it does not
|
|
@@ -293,15 +292,9 @@
|
|
|
293
292
|
// page, so no `view` key. Labelled Builders — 'roster' is the word
|
|
294
293
|
// modules/hall-ui/CLAUDE.md uses, but Builders is what a reader scans for.
|
|
295
294
|
{ id: 'roster', label: 'Builders', icon: 'leaderboard', href: '/roster', gate: 'signed' },
|
|
296
|
-
// task
|
|
297
|
-
//
|
|
298
|
-
//
|
|
299
|
-
// task 1002710: the nav was stricter than the wall, so a Metic could open
|
|
300
|
-
// /people by URL but never saw the link. Cosmetic either way —
|
|
301
|
-
// requireProjectCuratePage is the wall — but a nav that disagrees with the
|
|
302
|
-
// gate teaches the ladder wrong. The module gate hides the tab on
|
|
303
|
-
// instances without the platform-identity hub.
|
|
304
|
-
{ id: 'people', label: 'People', icon: 'people', href: '/people', gate: 'government', module: 'platform-identity' },
|
|
295
|
+
// task 1003444: 'People', the cross-project directory, was deleted (owner
|
|
296
|
+
// call 2026-09-25, ADR 0337 D5). Cross-project discovery is the hub's, and
|
|
297
|
+
// /people redirects to /roster.
|
|
305
298
|
] },
|
|
306
299
|
{ group: 'Docs', gate: 'signed', items: [
|
|
307
300
|
// task 1002710: primer + diagrams were gate:'member' (rank !== xenos) to
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
|
|
67
67
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
68
68
|
|
|
69
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
69
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
70
70
|
<script src="/builders/palette.js" defer></script>
|
|
71
71
|
<script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
|
|
72
72
|
<!-- The kit, for its ONE renderer: the full-size ship-time visual figure
|
|
@@ -3391,15 +3391,8 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
|
|
|
3391
3391
|
does not exist, so "Someone helped" rendered full-size on every row. */
|
|
3392
3392
|
.btn--sm { min-height: 26px; padding: 0 11px; font-size: 12.5px; }
|
|
3393
3393
|
|
|
3394
|
-
/*
|
|
3395
|
-
|
|
3396
|
-
Scouting and plain on People. It lives here now, and scouting.html itself
|
|
3397
|
-
went with task 1003440. */
|
|
3398
|
-
.who { font-weight: 600; }
|
|
3399
|
-
|
|
3400
|
-
/* people.js:105 — notice() sets body.className='note'. .note lives in
|
|
3401
|
-
my-projects.css, which People never loads, so every gated / empty / error
|
|
3402
|
-
message on that page rendered unstyled. */
|
|
3394
|
+
/* The notice line: roster.js and blockers.js notice() set body.className='note'
|
|
3395
|
+
for an empty / error message, and neither page's sheet defines it. */
|
|
3403
3396
|
.note { margin: 0; font-size: 13.5px; color: var(--ink-soft); }
|
|
3404
3397
|
|
|
3405
3398
|
.empty-state { padding: 8px 0; }
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
59
59
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
60
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
60
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
61
61
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
62
62
|
<script src="/builders/palette.js" defer></script>
|
|
63
63
|
<!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
|
|
163
163
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
164
164
|
chrome exists before the page script runs. -->
|
|
165
|
-
<script src="/builders/shell.js?v=2026-09-26-
|
|
165
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
166
166
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
167
167
|
<script src="/builders/palette.js" defer></script>
|
|
168
168
|
<!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
|
|
@@ -171,7 +171,7 @@
|
|
|
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/shell.js?v=2026-09-26-
|
|
174
|
+
<script src="/builders/shell.js?v=2026-09-26-nopeople"></script>
|
|
175
175
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
176
176
|
<script src="/builders/palette.js" defer></script>
|
|
177
177
|
<!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
|
|
@@ -180,13 +180,16 @@
|
|
|
180
180
|
<script src="/builders/hall-kit.js" defer></script>
|
|
181
181
|
<!-- shared TASK-DOMAIN helpers (window.OTBTaskLib) — priority/touches/prompt/refusal-copy, one copy for both boards (task 1002463). -->
|
|
182
182
|
<script src="/builders/task-lib.js" defer></script>
|
|
183
|
+
<!-- task 1003458: the hall's ONE claim/release action + refusal copy (window.OTBClaimAction).
|
|
184
|
+
Before work.js, which destructures friendlyClaimError from it. -->
|
|
185
|
+
<script src="/builders/claim-action.js?v=2026-09-26-claimaction1" defer></script>
|
|
183
186
|
<!-- the hall's one definition of "mine" (window.OTBMineLens) — goals you are on, claims you hold, ships you made; shared with the goal board. -->
|
|
184
187
|
<script src="/builders/mine-lens.js" defer></script>
|
|
185
188
|
<!-- the Completed tab's project-wide momentum card (window.OTBWorkSummary), carved out of work.js. -->
|
|
186
189
|
<script src="/builders/work-summary.js" defer></script>
|
|
187
190
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail" defer></script>
|
|
188
191
|
<script src="/builders/brand-apply.js?v=2026-06-19-r56" defer></script>
|
|
189
|
-
<script src="/builders/work.js?v=2026-
|
|
192
|
+
<script src="/builders/work.js?v=2026-09-26-claimaction1" defer></script>
|
|
190
193
|
<script src="/builders/copy-on-click.js?v=2026-06-01-copy-1" defer></script>
|
|
191
194
|
</body>
|
|
192
195
|
</html>
|
|
@@ -36,6 +36,10 @@
|
|
|
36
36
|
// file's exact variants (date-only fmtDate; a 2200ms clearing toast).
|
|
37
37
|
// fmtAgo / toAuth / getJSON / postJSON stay local below.
|
|
38
38
|
const { escapeHtml, $, fmtNum, errCode, errDetails } = window.OTB; // task 1002462: envelope readers
|
|
39
|
+
// The hall's ONE claim/release refusal copy (task 1003458). This board's own
|
|
40
|
+
// copy is gone: a second surface that wants a claim button consumes this
|
|
41
|
+
// instead of lifting the switch, which is how the two drifted apart before.
|
|
42
|
+
const { friendlyClaimError, makeClaimAction } = window.OTBClaimAction;
|
|
39
43
|
// Shared task-domain helpers (task 1002463). These lived in two copies here and
|
|
40
44
|
// in the other board and had already drifted — goals-page.js had silently lost
|
|
41
45
|
// friendlyBatchReason's MODULE_CONFLICT case. Destructured so every call site
|
|
@@ -180,6 +184,18 @@
|
|
|
180
184
|
return data;
|
|
181
185
|
}
|
|
182
186
|
|
|
187
|
+
// The hall's ONE claim/release action (task 1003458). Everything this board
|
|
188
|
+
// used to hand-roll four times — the POST, the refusal sentence, the
|
|
189
|
+
// stale-view refresh, the all-or-nothing batch report — lives in
|
|
190
|
+
// claim-action.js now; doClaim/doRelease/doBulkClaim/doBulkRelease below keep
|
|
191
|
+
// only what is genuinely this page: button text, the re-entrancy guard, the
|
|
192
|
+
// confirm() wording and the selection. friendlyBatchReason is injected rather
|
|
193
|
+
// than imported there, so claim-action.js stays dependency-free.
|
|
194
|
+
// refreshLive is a hoisted declaration; toast and API are already bound above.
|
|
195
|
+
const claimAction = makeClaimAction({
|
|
196
|
+
postJSON, toast, API, refresh: refreshLive, describeBatchReason: friendlyBatchReason,
|
|
197
|
+
});
|
|
198
|
+
|
|
183
199
|
// ---- paginated lists (task 1931/1955 → kit, task 2170) --------------------
|
|
184
200
|
// All four Work-page lists paginate through the shared OTBKit pager (the
|
|
185
201
|
// canonical copy of the helper that used to live here). localStorage keys are
|
|
@@ -627,51 +643,6 @@
|
|
|
627
643
|
}
|
|
628
644
|
}
|
|
629
645
|
}
|
|
630
|
-
|
|
631
|
-
// ---- claim / release actions ---------------------------------------------
|
|
632
|
-
|
|
633
|
-
// Map the server's typed error code to a builder-readable line. The codes are
|
|
634
|
-
// exactly those POST /claims and POST /claims/:id/resolve can return.
|
|
635
|
-
function friendlyActionError(status, data) {
|
|
636
|
-
// task 1002462: read the ADR 0116 envelope. `data.error` is an OBJECT, so the
|
|
637
|
-
// old `const code = data && data.error` compared a string to an object and
|
|
638
|
-
// EVERY case below fell through to the generic default — a wrong-but-plausible
|
|
639
|
-
// toast on every claim/release refusal.
|
|
640
|
-
const det = errDetails(data);
|
|
641
|
-
switch (errCode(data)) {
|
|
642
|
-
case 'ALREADY_CLAIMED':
|
|
643
|
-
return 'Someone else claimed that task first. The board will refresh.';
|
|
644
|
-
case 'TASK_NOT_READY':
|
|
645
|
-
return 'That task is no longer ready to be claimed.';
|
|
646
|
-
case 'TOUCHES_CONFLICT': {
|
|
647
|
-
const titles = Array.isArray(det.conflicts) ? det.conflicts.map((c) => c.title).filter(Boolean) : [];
|
|
648
|
-
return titles.length
|
|
649
|
-
? `That task overlaps an active claim: ${titles.join('; ')}. Wait for it to ship.`
|
|
650
|
-
: 'That task overlaps another active claim. Wait for it to ship.';
|
|
651
|
-
}
|
|
652
|
-
case 'DEPS_NOT_SHIPPED':
|
|
653
|
-
return 'That task waits on unshipped dependencies. Pick another for now.';
|
|
654
|
-
case 'rank_too_low_for_task':
|
|
655
|
-
case 'XENOS_RESTRICTED':
|
|
656
|
-
case 'INSUFFICIENT_RANK':
|
|
657
|
-
return 'Your rank is not yet high enough for that task.';
|
|
658
|
-
case 'newcomer_must_ship_first':
|
|
659
|
-
return det.what_to_do || 'Ship your current claim before opening another.';
|
|
660
|
-
case 'NOT_YOUR_CLAIM':
|
|
661
|
-
return 'That claim is not yours to release.';
|
|
662
|
-
case 'ALREADY_RESOLVED':
|
|
663
|
-
return 'That claim was already resolved. The board will refresh.';
|
|
664
|
-
case 'BUILDER_INACTIVE':
|
|
665
|
-
return 'Your account is inactive. Contact an Archon.';
|
|
666
|
-
default:
|
|
667
|
-
return `The board could not complete that (${status}). Try again.`;
|
|
668
|
-
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// Maps a single batch-item failure reason (POST /claims/batch,
|
|
672
|
-
// /claims/batch/validate) to a builder-readable fragment. Distinct from
|
|
673
|
-
// friendlyActionError above because a batch item's shape differs (reason,
|
|
674
|
-
// not error; extra fields like conflictsWithTaskId/module_key).
|
|
675
646
|
async function copyText(text) {
|
|
676
647
|
try { await navigator.clipboard.writeText(text); return true; }
|
|
677
648
|
catch (_) {
|
|
@@ -735,18 +706,15 @@
|
|
|
735
706
|
async function scheduleValidate(key, ids) {
|
|
736
707
|
if (validateDebounce) clearTimeout(validateDebounce);
|
|
737
708
|
validateDebounce = setTimeout(async () => {
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
validateCache.parallelSafe = false;
|
|
748
|
-
validateCache.byId = new Map();
|
|
749
|
-
}
|
|
709
|
+
// The dry run lives in claim-action.js with the rest of the claim path; it
|
|
710
|
+
// is best-effort there, so a failure lands as parallelSafe:false with an
|
|
711
|
+
// empty map rather than a throw — Parallel stays disabled with a generic
|
|
712
|
+
// title instead of copying a prompt for tasks nobody verified. The
|
|
713
|
+
// debounce, the cache key and the re-render stay here: page furniture.
|
|
714
|
+
const out = await claimAction.validateMany(ids);
|
|
715
|
+
validateCache.key = key;
|
|
716
|
+
validateCache.parallelSafe = out.parallelSafe;
|
|
717
|
+
validateCache.byId = out.byId;
|
|
750
718
|
renderBulkToolbar();
|
|
751
719
|
}, 350);
|
|
752
720
|
}
|
|
@@ -911,33 +879,8 @@
|
|
|
911
879
|
const btn = $('#bulk-claim-btn');
|
|
912
880
|
if (btn) { btn.disabled = true; btn.textContent = 'Claiming…'; }
|
|
913
881
|
try {
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
toast(`You claimed #${ids[0]}. Open a terminal to start work.`);
|
|
917
|
-
} else {
|
|
918
|
-
await postJSON(`${API}/claims/batch`, { task_ids: ids, bind_session: false });
|
|
919
|
-
toast(`Claimed ${ids.length} tasks. Open a terminal to start work.`);
|
|
920
|
-
}
|
|
921
|
-
selection.claim.clear();
|
|
922
|
-
await refreshLive();
|
|
923
|
-
} catch (err) {
|
|
924
|
-
if (err && err.message === 'redirecting to auth') return;
|
|
925
|
-
// task 1002462: same envelope defect — this tested `err.data.error === 'batch_failed'`
|
|
926
|
-
// against an object, so an all-or-nothing batch refusal rendered as the generic toast
|
|
927
|
-
// and the per-task reasons were dropped on the floor.
|
|
928
|
-
if (err.status === 409 && errCode(err.data) === 'batch_failed') {
|
|
929
|
-
// One read covers both envelopes — errDetails falls back to the body (task 1003657).
|
|
930
|
-
const bdet = errDetails(err.data);
|
|
931
|
-
const failures = Array.isArray(bdet.failures) ? bdet.failures : [];
|
|
932
|
-
const lines = failures.map((f) => `#${f.task_id} (${friendlyBatchReason(f)})`);
|
|
933
|
-
toast(lines.length
|
|
934
|
-
? `The batch could not be claimed — ${lines.join('; ')}.`
|
|
935
|
-
: 'The batch could not be claimed. It was left untouched (all-or-nothing).');
|
|
936
|
-
await refreshLive().catch(() => {});
|
|
937
|
-
} else {
|
|
938
|
-
toast(friendlyActionError(err.status, err.data));
|
|
939
|
-
if (err.status === 409) await refreshLive().catch(() => {});
|
|
940
|
-
}
|
|
882
|
+
const out = await claimAction.claimMany(ids);
|
|
883
|
+
if (out.ok) selection.claim.clear();
|
|
941
884
|
} finally {
|
|
942
885
|
renderBulkToolbar();
|
|
943
886
|
}
|
|
@@ -952,22 +895,8 @@
|
|
|
952
895
|
const btn = $('#bulk-release-btn');
|
|
953
896
|
if (btn) { btn.disabled = true; btn.textContent = 'Releasing…'; }
|
|
954
897
|
try {
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
toast(`Released #${entries[0][1]}. It returns to the queue.`);
|
|
958
|
-
} else {
|
|
959
|
-
const result = await postJSON(`${API}/claims/batch/release`, { claim_ids: entries.map(([cid]) => Number(cid)) });
|
|
960
|
-
const released = Array.isArray(result.released) ? result.released.length : 0;
|
|
961
|
-
const failed = Array.isArray(result.failures) ? result.failures.length : 0;
|
|
962
|
-
toast(failed
|
|
963
|
-
? `Released ${released} of ${entries.length} — ${failed} could not be released.`
|
|
964
|
-
: `Released ${released} tasks. They return to the queue.`);
|
|
965
|
-
}
|
|
966
|
-
selection.release.clear();
|
|
967
|
-
await refreshLive();
|
|
968
|
-
} catch (err) {
|
|
969
|
-
if (err && err.message === 'redirecting to auth') return;
|
|
970
|
-
toast(friendlyActionError(err.status, err.data));
|
|
898
|
+
const out = await claimAction.releaseMany(entries);
|
|
899
|
+
if (out.ok) selection.release.clear();
|
|
971
900
|
} finally {
|
|
972
901
|
renderBulkToolbar();
|
|
973
902
|
}
|
|
@@ -979,18 +908,10 @@
|
|
|
979
908
|
pending.add(key);
|
|
980
909
|
if (btn) { btn.disabled = true; btn.textContent = 'Claiming…'; }
|
|
981
910
|
try {
|
|
982
|
-
|
|
983
|
-
//
|
|
984
|
-
// it
|
|
985
|
-
|
|
986
|
-
toast(`You claimed #${taskId}. Open a terminal to start work.`);
|
|
987
|
-
await refreshLive();
|
|
988
|
-
} catch (err) {
|
|
989
|
-
if (err && err.message === 'redirecting to auth') return;
|
|
990
|
-
toast(friendlyActionError(err.status, err.data));
|
|
991
|
-
// A conflict/already-claimed means our view is stale — pull fresh truth.
|
|
992
|
-
if (err.status === 409) await refreshLive().catch(() => {});
|
|
993
|
-
if (btn) { btn.disabled = false; btn.textContent = 'Claim'; }
|
|
911
|
+
const out = await claimAction.claim(taskId);
|
|
912
|
+
// Leave the button disabled while the page is navigating to sign-in —
|
|
913
|
+
// re-enabling it invites a click that cannot land.
|
|
914
|
+
if (!out.ok && !out.redirected && btn) { btn.disabled = false; btn.textContent = 'Claim'; }
|
|
994
915
|
} finally {
|
|
995
916
|
pending.delete(key);
|
|
996
917
|
}
|
|
@@ -1003,17 +924,8 @@
|
|
|
1003
924
|
pending.add(key);
|
|
1004
925
|
if (btn) { btn.disabled = true; btn.textContent = 'Releasing…'; }
|
|
1005
926
|
try {
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
// server-side (NOT_YOUR_CLAIM).
|
|
1009
|
-
await postJSON(`${API}/claims/${encodeURIComponent(claimId)}/resolve`, { outcome: 'abandoned' });
|
|
1010
|
-
toast(`Released #${taskId}. It returns to the queue.`);
|
|
1011
|
-
await refreshLive();
|
|
1012
|
-
} catch (err) {
|
|
1013
|
-
if (err && err.message === 'redirecting to auth') return;
|
|
1014
|
-
toast(friendlyActionError(err.status, err.data));
|
|
1015
|
-
if (err.status === 409) await refreshLive().catch(() => {});
|
|
1016
|
-
if (btn) { btn.disabled = false; btn.textContent = 'Release'; }
|
|
927
|
+
const out = await claimAction.release(claimId, taskId);
|
|
928
|
+
if (!out.ok && !out.redirected && btn) { btn.disabled = false; btn.textContent = 'Release'; }
|
|
1017
929
|
} finally {
|
|
1018
930
|
pending.delete(key);
|
|
1019
931
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**The BUILDERS page landed next (task 1003441, goal 1000075)** — the in-project roster, at `/roster`. It is not a redesign but a MOVE: the Roll was a `#leaderboard` HASH VIEW on `index.html`, which is why the Community group read as one real page plus a link into the home page. Four things it settled:
|
|
4
4
|
|
|
5
|
-
- **The load-bearing part was not the markup.** `shell.js` `VIEWS`, `dom-utils.js` `HALL_VIEWS` and `hall-widgets.js` all declared `leaderboard`; the table, its pager, its sort lenses (`LB_SORT_HEADERS`, `sortedLeaderboardRows`) and the achievement laurels (`ACH_EMOJI`) lived in `hall-render.js`. All of it MOVED to `roster.js` rather than being copied, so Home carries no dead renderer. `roster.html` is the board archetype at people.html’s shape and reuses `people.css` for the identity cell — one definition, both rosters.
|
|
5
|
+
- **The load-bearing part was not the markup.** `shell.js` `VIEWS`, `dom-utils.js` `HALL_VIEWS` and `hall-widgets.js` all declared `leaderboard`; the table, its pager, its sort lenses (`LB_SORT_HEADERS`, `sortedLeaderboardRows`) and the achievement laurels (`ACH_EMOJI`) lived in `hall-render.js`. All of it MOVED to `roster.js` rather than being copied, so Home carries no dead renderer. `roster.html` is the board archetype at people.html’s shape and reuses `people.css` for the identity cell — one definition, both rosters, until task 1003444 deleted `/people` and the sheet became `roster.css`.
|
|
6
6
|
- **`lbState` and `leaderboardPlace()` STAYED on Home**, shrunk to `{ rows: [] }`. The Standing card still says "#N on the leaderboard", and it needs the same rows; the fetch stays too. Verified live: the card reads `#4 on the leaderboard` with the table gone.
|
|
7
7
|
- **The hash REDIRECTS, it does not alias.** `#achievements` could fold into Standing because Standing exists on that page; there is no host view for a Roll that left. So `VIEW_REDIRECTS` in `shell.js` sends `#leaderboard` to `/roster` via `location.replace`, guarded to the index — without it `viewFromHash` returns nothing and `route()` silently leaves the reader on Home, a dead link that looks like it worked.
|
|
8
8
|
- **It is deliberately UNGATED,** and `tests/hall_page_gate_map.mjs` spells that out rather than defaulting. Its one data source, `GET /leaderboard`, is deliberately unauthenticated and mirrors the public status surface; a page must not be gated tighter than the API it fronts, and it was already reachable to a stranger as a view on the ungated index. No visibility predicate is applied and that is correct — privacy suppresses cross-project surfaces only, so a `private` builder still appears on the roster of a project they build on (ADR 0225).
|