@bongos/core 1.19.461 → 1.19.463
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 +145 -40
- package/.claude/skills/design/SKILL.md +2 -1
- package/docs/adr/0221-the-impeccable-method-is-one-skill-with-sub-command-playbooks.md +36 -0
- package/docs/adr/README.md +1 -0
- package/docs/copy-inventory.md +122 -125
- package/docs/copy-registry.json +306 -322
- package/docs/file-map.md +2 -2
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/goals-lib.js +10 -6
- package/modules/hall-ui/public/goals-page.js +27 -12
- package/modules/hall-ui/public/goals-render.js +83 -52
- package/modules/hall-ui/public/goals.css +129 -120
- package/modules/hall-ui/public/goals.html +29 -11
- package/modules/hall-ui/public/goals.states.json +71 -3
- package/modules/hall-ui/public/people.css +27 -0
- package/modules/hall-ui/public/people.html +15 -7
- package/modules/hall-ui/public/people.js +111 -22
- package/modules/hall-ui/public/people.states.json +31 -1
- package/modules/hall-ui/public/scouting.html +30 -17
- package/modules/hall-ui/public/scouting.states.json +7 -2
- package/modules/hall-ui/public/sessions.css +18 -15
- package/modules/hall-ui/public/sessions.html +27 -22
- package/modules/hall-ui/public/sessions.js +32 -13
- package/modules/hall-ui/public/sessions.states.json +3 -2
- package/modules/ui-design/module.json +1 -1
- package/modules/ui-design/skills/README.md +4 -3
- package/modules/ui-design/skills/impeccable/SKILL.md +143 -0
- package/modules/ui-design/skills/impeccable/reference/adapt.md +25 -0
- package/modules/ui-design/skills/impeccable/reference/animate.md +27 -0
- package/modules/ui-design/skills/impeccable/reference/audit.md +19 -0
- package/modules/ui-design/skills/impeccable/reference/bolder.md +17 -0
- package/modules/ui-design/skills/impeccable/reference/clarify.md +25 -0
- package/modules/ui-design/skills/impeccable/reference/colorize.md +22 -0
- package/modules/ui-design/skills/impeccable/reference/critique.md +15 -0
- package/modules/ui-design/skills/impeccable/reference/distill.md +26 -0
- package/modules/ui-design/skills/impeccable/reference/document.md +25 -0
- package/modules/ui-design/skills/impeccable/reference/extract.md +20 -0
- package/modules/ui-design/skills/impeccable/reference/harden.md +21 -0
- package/modules/ui-design/skills/impeccable/reference/layout.md +20 -0
- package/modules/ui-design/skills/impeccable/reference/onboard.md +22 -0
- package/modules/ui-design/skills/impeccable/reference/optimize.md +22 -0
- package/modules/ui-design/skills/impeccable/reference/polish.md +22 -0
- package/modules/ui-design/skills/impeccable/reference/quieter.md +17 -0
- package/modules/ui-design/skills/impeccable/reference/shape.md +23 -0
- package/modules/ui-design/skills/impeccable/reference/typeset.md +24 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/hall_roster_world.mjs +287 -0
- package/tests/ui_design_skills.mjs +41 -0
- package/modules/hall-ui/public/scouting.js +0 -73
|
@@ -1,16 +1,29 @@
|
|
|
1
|
-
// people.js — the People page: the
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
1
|
+
// people.js — the People page: the cross-project builder directory (task
|
|
2
|
+
// 1002577; ADR 0141 §4 is the data's design of record), served at /people and
|
|
3
|
+
// at its legacy door /scouting — task 1003470 made the two ONE page, this
|
|
4
|
+
// script, because the same /api/gds/scouting rollup was being drawn three
|
|
5
|
+
// different ways (a kit ledger here, a hand-built table there, and the same
|
|
6
|
+
// columns in a different order). The endpoint is gated server-side on
|
|
7
|
+
// 'project.curate' OR being a project owner (R14, ADR 0210); both pages are
|
|
8
|
+
// gated in serve-internal.js on that same pair, so a visitor who is neither
|
|
9
|
+
// never sees this shell.
|
|
10
|
+
//
|
|
11
|
+
// What a directory row is (task 1003470): WHO — the avatar the API returns and
|
|
12
|
+
// both pages dropped, the display name, the login under it as the door (GitHub
|
|
13
|
+
// is the identity the platform account is keyed on, and the one page every
|
|
14
|
+
// listed builder has: the directory is cross-project, so no local profile id
|
|
15
|
+
// exists); then the four figures in tabular columns, the crafts as chips, and
|
|
16
|
+
// when they were last seen. Search, a sort and a craft lens are the kit's
|
|
17
|
+
// filter bar; the rows page through the kit's pager.
|
|
8
18
|
// All network values render through OTBKit.esc — never raw innerHTML.
|
|
9
19
|
(function () {
|
|
10
20
|
'use strict';
|
|
11
21
|
var kit = window.OTBKit;
|
|
22
|
+
var OTB = window.OTB;
|
|
12
23
|
var esc = kit.esc;
|
|
13
24
|
var body = document.getElementById('people-body');
|
|
25
|
+
var sub = document.getElementById('people-sub');
|
|
26
|
+
var countPill = document.getElementById('people-count');
|
|
14
27
|
|
|
15
28
|
function fmtRecency(iso) {
|
|
16
29
|
if (!iso) return '—';
|
|
@@ -22,38 +35,90 @@
|
|
|
22
35
|
if (days < 30) return days + ' days ago';
|
|
23
36
|
return Math.floor(days / 30) + ' mo ago';
|
|
24
37
|
}
|
|
38
|
+
var fmtInt = function (n) { return OTB && OTB.fmtInt ? OTB.fmtInt(n) : String(n == null ? 0 : n); };
|
|
39
|
+
var rankLabel = function (r) { return OTB && OTB.rankLabel ? OTB.rankLabel(r) : r; };
|
|
40
|
+
var currencyLabel = function () {
|
|
41
|
+
var b = window.OTBBranding;
|
|
42
|
+
return (b && b.currencyLabel && b.currencyLabel({ caps: true })) || 'Credits';
|
|
43
|
+
};
|
|
25
44
|
|
|
26
45
|
function builderName(b) {
|
|
27
|
-
return b.
|
|
46
|
+
return b.display_name || b.github_login || ('#' + b.github_id);
|
|
47
|
+
}
|
|
48
|
+
function initialOf(b) {
|
|
49
|
+
var n = String(builderName(b)).replace(/^[@#]/, '');
|
|
50
|
+
return (n.charAt(0) || '?').toUpperCase();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// The identity cell. The name is the shell's .lb-name (a door that reads as
|
|
54
|
+
// text until hovered) when a login exists to open; the login sits under it in
|
|
55
|
+
// mono so the door's destination is never a surprise.
|
|
56
|
+
function whoHtml(b) {
|
|
57
|
+
var name = esc(builderName(b));
|
|
58
|
+
var avatar = b.avatar_url
|
|
59
|
+
? '<img class="dir-avatar" src="' + esc(b.avatar_url) + '" alt="" loading="lazy">'
|
|
60
|
+
: '<span class="dir-avatar dir-avatar--initial" aria-hidden="true">' + esc(initialOf(b)) + '</span>';
|
|
61
|
+
var nameHtml = b.github_login
|
|
62
|
+
? '<a class="lb-name dir-name" href="https://github.com/' + encodeURIComponent(b.github_login) + '" rel="noopener">' + name + '</a>'
|
|
63
|
+
: '<span class="dir-name">' + name + '</span>';
|
|
64
|
+
var login = b.github_login ? '<span class="dir-login">@' + esc(b.github_login) + '</span>' : '';
|
|
65
|
+
return '<span class="dir-who">' + avatar + '<span class="dir-who__t">' + nameHtml + login + '</span></span>';
|
|
66
|
+
}
|
|
67
|
+
function craftsHtml(b) {
|
|
68
|
+
var list = Array.isArray(b.disciplines) ? b.disciplines : [];
|
|
69
|
+
if (!list.length) return '<span class="dir-none">—</span>';
|
|
70
|
+
return '<span class="dir-crafts">' + list.map(function (d) {
|
|
71
|
+
return '<span class="fact-chip">' + esc(String(d)) + '</span>';
|
|
72
|
+
}).join('') + '</span>';
|
|
28
73
|
}
|
|
29
74
|
|
|
30
75
|
var all = [];
|
|
31
76
|
var query = '';
|
|
77
|
+
var craft = '';
|
|
78
|
+
var sortBy = '';
|
|
32
79
|
|
|
33
80
|
function matches(b) {
|
|
81
|
+
if (craft && !(Array.isArray(b.disciplines) && b.disciplines.indexOf(craft) !== -1)) return false;
|
|
34
82
|
if (!query) return true;
|
|
35
|
-
var hay = (
|
|
83
|
+
var hay = ((b.github_login || '') + ' ' + (b.display_name || '') + ' ' +
|
|
36
84
|
(Array.isArray(b.disciplines) ? b.disciplines.join(' ') : '')).toLowerCase();
|
|
37
85
|
return hay.indexOf(query) !== -1;
|
|
38
86
|
}
|
|
87
|
+
// The server's order IS "track record" (ADR 0141 §4 ranks the rollup); the
|
|
88
|
+
// other lenses re-order the loaded set client-side, ties keeping that order.
|
|
89
|
+
var SORTS = {
|
|
90
|
+
'': null,
|
|
91
|
+
ships: function (b) { return -(b.total_tasks_shipped || 0); },
|
|
92
|
+
credits: function (b) { return -(b.total_credits || 0); },
|
|
93
|
+
karma: function (b) { return -(b.total_karma || 0); },
|
|
94
|
+
projects: function (b) { return -(b.project_count || 0); },
|
|
95
|
+
recent: function (b) { var t = Date.parse(b.last_active || ''); return isNaN(t) ? Infinity : -t; },
|
|
96
|
+
};
|
|
97
|
+
function ordered(rows) {
|
|
98
|
+
var key = SORTS[sortBy];
|
|
99
|
+
if (!key) return rows;
|
|
100
|
+
return rows.map(function (b, i) { return { b: b, i: i }; })
|
|
101
|
+
.sort(function (x, y) { return (key(x.b) - key(y.b)) || (x.i - y.i); })
|
|
102
|
+
.map(function (x) { return x.b; });
|
|
103
|
+
}
|
|
39
104
|
|
|
40
105
|
var pager = kit.makePager('people', 'hall-people-per-page');
|
|
41
106
|
var ledger = null;
|
|
42
107
|
|
|
43
108
|
function rowHtml(b) {
|
|
44
109
|
return '<tr>' +
|
|
45
|
-
'<td
|
|
46
|
-
'<td class="
|
|
47
|
-
'<td class="
|
|
48
|
-
'<td class="
|
|
49
|
-
'<td class="
|
|
50
|
-
'<td>' +
|
|
51
|
-
'<td>' + esc(fmtRecency(b.last_active)) + '</td>' +
|
|
110
|
+
'<td>' + whoHtml(b) + '</td>' +
|
|
111
|
+
'<td class="ledger__num">' + esc(fmtInt(b.project_count != null ? b.project_count : 0)) + '</td>' +
|
|
112
|
+
'<td class="ledger__num">' + esc(fmtInt(b.total_tasks_shipped != null ? b.total_tasks_shipped : 0)) + '</td>' +
|
|
113
|
+
'<td class="ledger__num">' + esc(fmtInt(b.total_credits != null ? b.total_credits : 0)) + '</td>' +
|
|
114
|
+
'<td class="ledger__num">' + esc(fmtInt(b.total_karma != null ? b.total_karma : 0)) + '</td>' +
|
|
115
|
+
'<td>' + craftsHtml(b) + '</td>' +
|
|
116
|
+
'<td class="ledger__date">' + esc(fmtRecency(b.last_active)) + '</td>' +
|
|
52
117
|
'</tr>';
|
|
53
118
|
}
|
|
54
119
|
|
|
55
120
|
function rerender() {
|
|
56
|
-
if (ledger) ledger.render(all.filter(matches));
|
|
121
|
+
if (ledger) ledger.render(ordered(all.filter(matches)));
|
|
57
122
|
}
|
|
58
123
|
|
|
59
124
|
function mountDirectory(builders) {
|
|
@@ -77,22 +142,44 @@
|
|
|
77
142
|
+ 'and none are listed without it. Their work still shows on each project’s own ledger.');
|
|
78
143
|
return;
|
|
79
144
|
}
|
|
145
|
+
if (countPill) { countPill.textContent = String(all.length); countPill.hidden = false; }
|
|
146
|
+
if (sub) {
|
|
147
|
+
sub.textContent = fmtInt(all.length) + ' builder' + (all.length === 1 ? '' : 's') + ' listed across hosted projects, ranked by track record. '
|
|
148
|
+
+ 'Descriptive only — cross-project numbers never carry authority here.';
|
|
149
|
+
}
|
|
150
|
+
var crafts = [];
|
|
151
|
+
all.forEach(function (b) {
|
|
152
|
+
(Array.isArray(b.disciplines) ? b.disciplines : []).forEach(function (d) {
|
|
153
|
+
if (crafts.indexOf(d) === -1) crafts.push(d);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
crafts.sort();
|
|
80
157
|
kit.makeFilterBar({
|
|
81
158
|
mount: document.getElementById('people-filters'),
|
|
82
|
-
search: { id: 'people-q', label: 'Search', placeholder: 'name or
|
|
159
|
+
search: { id: 'people-q', label: 'Search', placeholder: 'name or craft' },
|
|
160
|
+
primary: [
|
|
161
|
+
{ id: 'people-sort', label: 'Sort', options: [
|
|
162
|
+
{ value: '', label: 'track record' }, { value: 'ships', label: 'most ships' },
|
|
163
|
+
{ value: 'credits', label: 'most ' + currencyLabel().toLowerCase() }, { value: 'karma', label: 'most karma' },
|
|
164
|
+
{ value: 'projects', label: 'most projects' }, { value: 'recent', label: 'most recently active' }] },
|
|
165
|
+
{ id: 'people-craft', label: 'Craft', options: [{ value: '', label: '— any —' }].concat(crafts) },
|
|
166
|
+
],
|
|
83
167
|
onChange: function (vals) {
|
|
84
168
|
query = String(vals['people-q'] || '').trim().toLowerCase();
|
|
169
|
+
craft = String(vals['people-craft'] || '');
|
|
170
|
+
sortBy = String(vals['people-sort'] || '');
|
|
85
171
|
pager.page = 1;
|
|
86
172
|
rerender();
|
|
87
173
|
},
|
|
88
174
|
});
|
|
89
175
|
body.textContent = '';
|
|
176
|
+
body.className = '';
|
|
90
177
|
ledger = kit.makeLedger({
|
|
91
178
|
mount: body,
|
|
92
|
-
columns: ['Builder', { label: 'Projects', class: '
|
|
93
|
-
{ label:
|
|
179
|
+
columns: ['Builder', { label: 'Projects', class: 'ledger__num' }, { label: 'Ships', class: 'ledger__num' },
|
|
180
|
+
{ label: currencyLabel(), class: 'ledger__num' }, { label: 'Karma', class: 'ledger__num' }, 'Crafts', 'Last active'],
|
|
94
181
|
rowHtml: rowHtml,
|
|
95
|
-
// Reached only when the directory HAS builders and the
|
|
182
|
+
// Reached only when the directory HAS builders and the lenses excluded them
|
|
96
183
|
// all — the empty-API case returns above with its own explanation.
|
|
97
184
|
emptyMsg: 'No listed builder matches that search.',
|
|
98
185
|
pager: pager,
|
|
@@ -114,8 +201,10 @@
|
|
|
114
201
|
})
|
|
115
202
|
.then(function (data) { mountDirectory(data && data.builders); })
|
|
116
203
|
.catch(function (e) {
|
|
204
|
+
// The floor is the pack's label for metic, never a hardcoded rank name —
|
|
205
|
+
// and the page used to say "Archon-only", which was wrong since ADR 0210.
|
|
117
206
|
notice(e && e.gated
|
|
118
|
-
? 'The
|
|
207
|
+
? 'The directory opens at the ' + rankLabel('metic') + ' rank, or to a project’s owner.'
|
|
119
208
|
: 'Couldn’t load the directory right now.');
|
|
120
209
|
});
|
|
121
210
|
}());
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "People — the cross-project directory behind requireProjectCuratePage (metic+ via project.curate). Descriptive only; it grants no authority, and the page says so above the fold. Written with the re-skin (task 1003307)
|
|
2
|
+
"_": "People — the cross-project directory behind requireProjectCuratePage (metic+ via project.curate, or a project owner). Descriptive only; it grants no authority, and the page says so above the fold. Written with the re-skin (task 1003307); the redesign (task 1003470) added the identity cell, the sort and craft lenses, and the `search` state. /scouting is the legacy door to this same page (scouting.states.json measures it separately). Populated look: the hall-preview harness (scripts/hall-preview/fixtures/scouting.json carries five builders, three with avatars).",
|
|
3
3
|
"page": "/builders/people",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"/api/(gds|bongos)/inbox/",
|
|
15
15
|
"/api/(gds|bongos)/versions",
|
|
16
16
|
"/api/(gds|bongos)/builders",
|
|
17
|
+
"/api/(gds|bongos)/scouting",
|
|
17
18
|
"/api/(gds|bongos)/public/"
|
|
18
19
|
],
|
|
19
20
|
"ignoreConsole": [
|
|
@@ -43,6 +44,35 @@
|
|
|
43
44
|
]
|
|
44
45
|
}
|
|
45
46
|
},
|
|
47
|
+
"search": {
|
|
48
|
+
"auth": true,
|
|
49
|
+
"actions": [
|
|
50
|
+
[
|
|
51
|
+
"wait",
|
|
52
|
+
700
|
|
53
|
+
],
|
|
54
|
+
[
|
|
55
|
+
"fill",
|
|
56
|
+
"#people-q",
|
|
57
|
+
"hyp"
|
|
58
|
+
],
|
|
59
|
+
[
|
|
60
|
+
"wait",
|
|
61
|
+
200
|
|
62
|
+
]
|
|
63
|
+
],
|
|
64
|
+
"expect": {
|
|
65
|
+
"visible": [
|
|
66
|
+
"#people-scroll"
|
|
67
|
+
],
|
|
68
|
+
"value": [
|
|
69
|
+
[
|
|
70
|
+
"#people-q",
|
|
71
|
+
"hyp"
|
|
72
|
+
]
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
},
|
|
46
76
|
"out": {
|
|
47
77
|
"auth": false
|
|
48
78
|
}
|
|
@@ -3,17 +3,22 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>
|
|
7
|
-
<meta name="description" content="
|
|
6
|
+
<title>People — {{worldName}}</title>
|
|
7
|
+
<meta name="description" content="The builder directory: everyone building across {{worldName}} projects, searchable and ranked by track record.">
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
<!-- /scouting is the LEGACY DOOR to the People directory (task 1002577
|
|
13
|
+
graduated it into the hall; task 1002710 walled it on the same
|
|
14
|
+
project.curate gate). Task 1003470 made it the SAME PAGE: the body below
|
|
15
|
+
is byte-identical to people.html and runs people.js — the one
|
|
16
|
+
/api/gds/scouting rollup used to be drawn three different ways, and a
|
|
17
|
+
door that renders a different page from the same data is the drift.
|
|
18
|
+
tests/hall_roster_world.mjs holds the two bodies equal. -->
|
|
19
|
+
<link rel="stylesheet" href="/builders/people.css">
|
|
15
20
|
</head>
|
|
16
|
-
<body data-page="
|
|
21
|
+
<body data-page="people" data-page-title="People">
|
|
17
22
|
|
|
18
23
|
<div class="app">
|
|
19
24
|
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
@@ -21,20 +26,24 @@
|
|
|
21
26
|
<div class="app-body">
|
|
22
27
|
<header class="app-topbar" id="app-topbar"></header>
|
|
23
28
|
<div class="app-content">
|
|
24
|
-
<!--
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
is the LABEL voice, not a display line: on a working surface the
|
|
28
|
-
page's own name is the least interesting thing on it. -->
|
|
29
|
+
<!-- The board page head (task 1003307): the label voice, because on a
|
|
30
|
+
working surface the page's own name is the least interesting thing on
|
|
31
|
+
it. The sub is rewritten by people.js once the directory has loaded. -->
|
|
29
32
|
<div class="page-head page-head--board">
|
|
30
|
-
<h1 class="page-head__h">
|
|
31
|
-
<p class="page-head__sub">
|
|
33
|
+
<h1 class="page-head__h">People</h1>
|
|
34
|
+
<p class="page-head__sub" id="people-sub">Everyone building across hosted projects, ranked by track record. Descriptive only — cross-project numbers never carry authority here.</p>
|
|
32
35
|
</div>
|
|
33
36
|
|
|
34
37
|
<main id="hall-main">
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
+
<!-- task 1003470: ONE directory. /people and its legacy door /scouting
|
|
39
|
+
render this same page from the same script — the same
|
|
40
|
+
/api/gds/scouting rollup used to be drawn three different ways. -->
|
|
41
|
+
<section class="scroll" id="people-scroll" aria-labelledby="people-h">
|
|
42
|
+
<h2 id="people-h" class="scroll__h">Builder directory<span class="count-pill" id="people-count" hidden></span></h2>
|
|
43
|
+
<div id="people-filters"></div>
|
|
44
|
+
<div id="people-body">
|
|
45
|
+
<div class="profile__placeholder">Loading the directory…</div>
|
|
46
|
+
</div>
|
|
38
47
|
</section>
|
|
39
48
|
</main>
|
|
40
49
|
|
|
@@ -50,7 +59,11 @@
|
|
|
50
59
|
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
51
60
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
52
61
|
<script src="/builders/palette.js" defer></script>
|
|
62
|
+
<!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
|
|
63
|
+
branding pack's currency label, loaded BEFORE people.js. -->
|
|
64
|
+
<script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
|
|
53
65
|
<script src="/builders/brand-apply.js"></script>
|
|
54
|
-
<script src="/builders/
|
|
66
|
+
<script src="/builders/hall-kit.js"></script>
|
|
67
|
+
<script src="/builders/people.js"></script>
|
|
55
68
|
</body>
|
|
56
69
|
</html>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "Scouting — the
|
|
2
|
+
"_": "Scouting — the LEGACY DOOR to the People directory, on the same project.curate gate. Since task 1003470 it renders the People page itself (the same body, the same people.js), so this file measures the door, not a second design: the same floors at the same widths, with the People panel expected. The three-line inline <style> it used to carry went with the page it belonged to.",
|
|
3
3
|
"page": "/builders/scouting",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -36,7 +36,12 @@
|
|
|
36
36
|
"wait",
|
|
37
37
|
700
|
|
38
38
|
]
|
|
39
|
-
]
|
|
39
|
+
],
|
|
40
|
+
"expect": {
|
|
41
|
+
"visible": [
|
|
42
|
+
"#people-scroll"
|
|
43
|
+
]
|
|
44
|
+
}
|
|
40
45
|
},
|
|
41
46
|
"out": {
|
|
42
47
|
"auth": false
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
/* sessions.css —
|
|
2
|
-
|
|
3
|
-
the
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/* sessions.css — page-local styles for the session ledger (task 1002176;
|
|
2
|
+
redesigned in task 1003470, goal 1000081). The ledger, the pager and the
|
|
3
|
+
totals strip are the shell's; this holds only what the page adds. Gone with
|
|
4
|
+
the redesign: `white-space: nowrap` on every cell, and the .ledger-table-wrap
|
|
5
|
+
that existed only to give it a scrollport — a scrollport the shell's sticky
|
|
6
|
+
header then stuck to, so the column heads sat over the first row on desktop.
|
|
7
|
+
The shell's card strip handles a phone, the way it does on every other
|
|
8
|
+
ledger page. The session id is the family's .ov-mono (oversight.css). */
|
|
9
|
+
/* Six figures on the strip: the shell's auto-fit strands the sixth alone on a
|
|
10
|
+
second row at desktop width, so the strip fixes its columns — six across
|
|
11
|
+
from 1100px, three across under it, the shell's own pair on a phone. */
|
|
12
|
+
#stats-body { grid-template-columns: repeat(3, minmax(0, 1fr)); }
|
|
13
|
+
@media (min-width: 1100px) { #stats-body { grid-template-columns: repeat(6, minmax(0, 1fr)); } }
|
|
14
|
+
@media (max-width: 480px) { #stats-body { grid-template-columns: 1fr 1fr; } }
|
|
10
15
|
.ledger td.warn { color: var(--danger); font-weight: 600; }
|
|
11
|
-
.ledger-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
.ledger-rank { font-size: 12px; color: var(--ink-faint); }
|
|
16
|
+
.ledger-rank { font-size: 12px; color: var(--ink-faint); }
|
|
17
|
+
/* The task ids: chips that are doors (the family's a.fact-chip hover). */
|
|
18
|
+
.ledger-tasks { display: inline-flex; flex-wrap: wrap; gap: 6px; }
|
|
19
|
+
.ledger-tasks .fact-chip { text-decoration: none; }
|
|
@@ -4,11 +4,14 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>Sessions — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="The record of every work session —
|
|
7
|
+
<meta name="description" content="The record of every work session — who built, what it touched, what it cost.">
|
|
8
8
|
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
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-06-04-sessions">
|
|
12
|
+
<!-- task 1003470: the family sheet (the top-of-page strip, the mono fact,
|
|
13
|
+
the chip that is a door), linked before the page's own. -->
|
|
14
|
+
<link rel="stylesheet" href="/builders/oversight.css">
|
|
12
15
|
<link rel="stylesheet" href="/builders/sessions.css">
|
|
13
16
|
</head>
|
|
14
17
|
<body data-page="sessions" data-page-title="Sessions">
|
|
@@ -30,35 +33,37 @@
|
|
|
30
33
|
</div>
|
|
31
34
|
|
|
32
35
|
<main id="hall-main">
|
|
33
|
-
<!-- Access notice — shown by sessions.js when the viewer is
|
|
36
|
+
<!-- Access notice — shown by sessions.js when the viewer is below the
|
|
37
|
+
floor. The heading and the line are written from the pack's rank
|
|
38
|
+
label (ADR 0157: metic and up), so the static text never renders. -->
|
|
34
39
|
<section class="scroll" id="gate-scroll" aria-labelledby="gate-h" hidden>
|
|
35
|
-
<h2 id="gate-h" class="scroll__h">
|
|
36
|
-
<p class="scroll__lede" id="gate-body">The session log
|
|
40
|
+
<h2 id="gate-h" class="scroll__h">Higher ranks only</h2>
|
|
41
|
+
<p class="scroll__lede" id="gate-body">The session log opens from a higher rank.</p>
|
|
37
42
|
</section>
|
|
38
43
|
|
|
39
|
-
<!-- Totals —
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
44
|
+
<!-- Totals — the shell's glass strip at the top of the page (the way
|
|
45
|
+
Watch, Harbor and Gate carry theirs), not a strip inside a panel. -->
|
|
46
|
+
<section class="ov-summary" id="stats-scroll" aria-labelledby="stats-h" hidden>
|
|
47
|
+
<h2 id="stats-h" class="sr-only">Totals</h2>
|
|
43
48
|
<div class="stat-row" id="stats-body"></div>
|
|
44
49
|
</section>
|
|
45
50
|
|
|
46
|
-
<!-- Every recorded session — the corpus table.
|
|
51
|
+
<!-- Every recorded session — the corpus table. No wrapper: the shell's
|
|
52
|
+
card strip scrolls a wide ledger on a phone, and the old wrapper
|
|
53
|
+
turned the sticky header into a scrollport overlay on desktop. -->
|
|
47
54
|
<section class="scroll" id="sessions-scroll" aria-labelledby="sessions-h" hidden>
|
|
48
55
|
<h2 id="sessions-h" class="scroll__h">Recorded sessions</h2>
|
|
49
|
-
<p class="scroll__lede">Most recent first. Each row is one recorded work session.</p>
|
|
50
|
-
<
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</table>
|
|
61
|
-
</div>
|
|
56
|
+
<p class="scroll__lede">Most recent first. Each row is one recorded work session; every id on it opens its record.</p>
|
|
57
|
+
<table class="ledger" id="sessions-table">
|
|
58
|
+
<thead>
|
|
59
|
+
<tr>
|
|
60
|
+
<th>Session</th><th>Builder</th><th>Tasks</th>
|
|
61
|
+
<th class="ledger__num">Earned</th><th class="ledger__num">Turns</th><th class="ledger__num">Tokens</th><th class="ledger__num">Tool errors</th>
|
|
62
|
+
<th class="ledger__num">Duration</th><th>Recorded</th>
|
|
63
|
+
</tr>
|
|
64
|
+
</thead>
|
|
65
|
+
<tbody id="sessions-body"></tbody>
|
|
66
|
+
</table>
|
|
62
67
|
<nav class="pager" id="ledger-pager" hidden aria-label="Session pages">
|
|
63
68
|
<button type="button" class="btn-ghost" id="pager-prev">← Newer</button>
|
|
64
69
|
<span class="pager__range" id="pager-pos"></span>
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
// public-builders/sessions.js — the Session Ledger (/builders/sessions).
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
// Read-only. Fetches /api/bongos/me
|
|
5
|
-
//
|
|
6
|
-
// SERVER gates
|
|
7
|
-
// this page only reflects what that endpoint
|
|
8
|
-
// On a 401 it redirects into the web sign-in
|
|
3
|
+
// Metic+ monitoring view of the BFG session corpus (ADR 0027 / cluster 6D;
|
|
4
|
+
// the floor moved archon→metic in ADR 0157). Read-only. Fetches /api/bongos/me
|
|
5
|
+
// to learn the viewer's rank. The data itself comes from GET
|
|
6
|
+
// /api/bongos/sessions/search, which the SERVER gates and AUDITS (one audit_log
|
|
7
|
+
// row per load, by design) — this page only reflects what that endpoint
|
|
8
|
+
// returns; it is not the authority. On a 401 it redirects into the web sign-in
|
|
9
|
+
// flow, like the other hall pages. Redesigned in task 1003470 (goal 1000081):
|
|
10
|
+
// the totals on the shell's top strip, the ledger without its nowrap + wrapper
|
|
11
|
+
// pair, every id on a row a door, the floor named from the pack.
|
|
9
12
|
|
|
10
13
|
(() => {
|
|
11
14
|
'use strict';
|
|
@@ -62,26 +65,38 @@
|
|
|
62
65
|
.join('');
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
// task 1003470 — every id on the row is a door: the task ids are chips to the
|
|
69
|
+
// task page (`/builders/task/N`, which both hosts serve), the builder is the
|
|
70
|
+
// shell's .lb-name to their profile, and the session id is a mono fact with
|
|
71
|
+
// the full id on hover — it was painted in the info colour, which reads as a
|
|
72
|
+
// link to nowhere. The row also shows what the session EARNED, a fact the
|
|
73
|
+
// ledger had and dropped.
|
|
65
74
|
function renderTable(sessions) {
|
|
66
75
|
const body = $('#sessions-body');
|
|
67
76
|
if (!sessions.length) {
|
|
68
77
|
body.innerHTML =
|
|
69
|
-
'<tr><td colspan="
|
|
78
|
+
'<tr><td colspan="9" class="ledger__empty">No sessions recorded yet. They land here as builders ship and end sessions.</td></tr>';
|
|
70
79
|
return;
|
|
71
80
|
}
|
|
72
81
|
body.innerHTML = sessions
|
|
73
82
|
.map((s) => {
|
|
74
83
|
const tasks = Array.isArray(s.task_ids) && s.task_ids.length
|
|
75
|
-
? s.task_ids.map((t) =>
|
|
84
|
+
? `<span class="ledger-tasks">${s.task_ids.map((t) =>
|
|
85
|
+
`<a class="fact-chip" href="/builders/task/${encodeURIComponent(String(t))}">task ${escapeHtml(String(t))}</a>`).join('')}</span>`
|
|
76
86
|
: '—';
|
|
87
|
+
const who = s.github_login ? `@${escapeHtml(s.github_login)}` : `#${escapeHtml(String(s.builder_id ?? '?'))}`;
|
|
88
|
+
const whoHtml = s.builder_id != null
|
|
89
|
+
? `<a class="lb-name" href="/builders/profile?id=${encodeURIComponent(String(s.builder_id))}">${who}</a>`
|
|
90
|
+
: who;
|
|
77
91
|
const errs = num(s.tool_errors);
|
|
78
92
|
return (
|
|
79
93
|
'<tr>' +
|
|
80
|
-
`<td><span class="
|
|
81
|
-
`<td>${
|
|
82
|
-
(s.rank ? ` <span class="ledger-rank"
|
|
94
|
+
`<td><span class="ov-mono" title="${escapeHtml(s.session_id)}">${escapeHtml(shortSid(s.session_id))}</span></td>` +
|
|
95
|
+
`<td>${whoHtml}` +
|
|
96
|
+
(s.rank ? ` <span class="ledger-rank">${escapeHtml(rankLabel(s.rank))}</span>` : '') +
|
|
83
97
|
'</td>' +
|
|
84
98
|
`<td>${tasks}</td>` +
|
|
99
|
+
`<td class="ledger__num">${fmtInt(s.drachmae_earned)}</td>` +
|
|
85
100
|
`<td class="ledger__num">${fmtInt(s.assistant_turns)}</td>` +
|
|
86
101
|
`<td class="ledger__num" title="${fmtInt(s.total_tokens)} tokens">${escapeHtml(fmtCompact(s.total_tokens))}</td>` +
|
|
87
102
|
`<td class="ledger__num${errs > 0 ? ' warn' : ''}">${errs}</td>` +
|
|
@@ -93,9 +108,13 @@
|
|
|
93
108
|
.join('');
|
|
94
109
|
}
|
|
95
110
|
|
|
111
|
+
// The floor is written from the pack's rank label (ADR 0157: metic and up),
|
|
112
|
+
// never a hardcoded rank name — the page used to say "Archons only".
|
|
96
113
|
function showSealed(message) {
|
|
97
|
-
|
|
114
|
+
const floor = rankLabel('metic');
|
|
115
|
+
$('#ledger-sub').textContent = `${floor} and up.`;
|
|
98
116
|
const gate = $('#gate-scroll');
|
|
117
|
+
$('#gate-h').textContent = `${floor} and up`;
|
|
99
118
|
if (message) $('#gate-body').textContent = message;
|
|
100
119
|
gate.hidden = false;
|
|
101
120
|
$('#stats-scroll').hidden = true;
|
|
@@ -211,7 +230,7 @@
|
|
|
211
230
|
// ADR 0157: `session.search` moved archon→metic, so the session log is a
|
|
212
231
|
// Metic+ surface now. Cosmetic — the route gate is the wall.
|
|
213
232
|
if (!builder || (builder.rank !== 'metic' && builder.rank !== 'archon')) {
|
|
214
|
-
showSealed(
|
|
233
|
+
showSealed(`The session log opens at the ${rankLabel('metic')} rank. Your current rank does not have access.`);
|
|
215
234
|
return;
|
|
216
235
|
}
|
|
217
236
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "Sessions — the session ledger behind requireGovernmentPage. sessions.js pages SERVER-SIDE by offset and is the one sanctioned hand-rolled pager in the hall; this file measures it, it does not ask it to change. Written with the re-skin (task 1003307)
|
|
2
|
+
"_": "Sessions — the session ledger behind requireGovernmentPage. sessions.js pages SERVER-SIDE by offset and is the one sanctioned hand-rolled pager in the hall; this file measures it, it does not ask it to change. Written with the re-skin (task 1003307); the redesign (task 1003470) put the totals on the shell's top-of-page strip, dropped the nowrap + wrapper pair that broke the sticky header, and made every id on a row a door. Populated look: the hall-preview harness (five rows in sessions__search.json).",
|
|
3
3
|
"page": "/builders/sessions",
|
|
4
4
|
"surface": "hall-ui",
|
|
5
5
|
"stub": {
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
],
|
|
39
39
|
"expect": {
|
|
40
40
|
"visible": [
|
|
41
|
-
"#stats-scroll"
|
|
41
|
+
"#stats-scroll",
|
|
42
|
+
"#sessions-scroll"
|
|
42
43
|
]
|
|
43
44
|
}
|
|
44
45
|
},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"coreVersion": "^1.19.0",
|
|
6
6
|
"default": true,
|
|
7
7
|
"maintenance": { "status": "core-maintained" },
|
|
8
|
-
"contributes": { "skills": ["design", "design-sync", "figma-design-sync", "design-taste-frontend", "design-taste-frontend-v1", "high-end-visual-design", "redesign-existing-projects"] },
|
|
8
|
+
"contributes": { "skills": ["design", "design-sync", "figma-design-sync", "design-taste-frontend", "design-taste-frontend-v1", "high-end-visual-design", "redesign-existing-projects", "impeccable"] },
|
|
9
9
|
"provides": [],
|
|
10
10
|
"consumes": []
|
|
11
11
|
}
|