@bongos/core 1.19.467 → 1.19.469
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 +86 -36
- package/docs/adr/0225-privacy-suppresses-cross-project-surfaces-only.md +84 -0
- package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +169 -0
- package/docs/adr/README.md +2 -0
- package/docs/copy-inventory.md +5 -10
- package/docs/copy-registry.json +15 -92
- package/docs/file-map.md +5 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/research/task-network-map/a-every-task-p2.png +0 -0
- package/docs/research/task-network-map/a-every-task-p40.png +0 -0
- package/docs/research/task-network-map/b-dependency-forest-p2.png +0 -0
- package/docs/research/task-network-map/b-dependency-forest-p40.png +0 -0
- package/docs/research/task-network-map/c-goals-p2.png +0 -0
- package/docs/research/task-network-map/c-goals-p40.png +0 -0
- package/docs/research/task-network-map/d-joined-by-goal.png +0 -0
- package/docs/research/task-network-map-readability.md +191 -0
- package/modules/hall-ui/public/people.css +3 -2
- package/modules/hall-ui/public/people.html +2 -2
- package/modules/hall-ui/public/people.js +5 -4
- package/modules/hall-ui/public/people.states.json +1 -1
- package/modules/hall-ui/public/settings.js +1 -1
- package/modules/hall-ui/public/style.css +4 -3
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/spike-task-map.js +730 -0
- package/src/bongos/serve-internal.js +20 -8
- package/src/module-api.js +1 -1
- package/tests/hall_page_gate_map.mjs +4 -2
- package/tests/hall_reading_world.mjs +2 -2
- package/tests/hall_roster_world.mjs +40 -12
- package/tests/spike_task_map.mjs +0 -0
- package/modules/hall-ui/public/scouting.html +0 -69
- package/modules/hall-ui/public/scouting.states.json +0 -59
|
@@ -837,13 +837,25 @@ function mountInternalSurfaces(app) {
|
|
|
837
837
|
// exact permission its data route (GET /scouting) checks — same posture as the
|
|
838
838
|
// /government tab above (the page can never drift wider than the API it fronts).
|
|
839
839
|
const PEOPLE_PAGE_RE = /^\/builders\/people(?:\.html)?\/?$/;
|
|
840
|
-
// /scouting
|
|
841
|
-
// left behind when task 1002577 graduated it into the hall.
|
|
842
|
-
//
|
|
843
|
-
//
|
|
844
|
-
//
|
|
845
|
-
//
|
|
846
|
-
|
|
840
|
+
// /scouting WAS the legacy twin of /people — the same /api/gds/scouting
|
|
841
|
+
// directory, left behind when task 1002577 graduated it into the hall. Task
|
|
842
|
+
// 1003470 had already made the two one page (scouting.html rendered people's
|
|
843
|
+
// body byte-for-byte and scouting.js was deleted); task 1003440 removes the
|
|
844
|
+
// door itself, because a second URL onto one directory is a surface to keep in
|
|
845
|
+
// sync for no reader. The URL survives as a 301 so an old bookmark lands on the
|
|
846
|
+
// directory instead of a 404. It carries no page gate: the redirect discloses
|
|
847
|
+
// nothing, and /people's wall applies the instant the browser follows it.
|
|
848
|
+
const SCOUTING_LEGACY_RE = /^\/builders\/scouting(?:\.html)?\/?$/;
|
|
849
|
+
app.use((req, res, next) => {
|
|
850
|
+
if (isModuleSurfaceHost(req)) return next();
|
|
851
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
852
|
+
if (!SCOUTING_LEGACY_RE.test(path.posix.normalize(req.path))) return next();
|
|
853
|
+
const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
|
|
854
|
+
// On the builders subdomain the /builders prefix is redundant (the canonical
|
|
855
|
+
// redirect above strips it), so emit the clean path and save a second hop.
|
|
856
|
+
return res.redirect(301, `${isBuildersHost(req) ? '' : '/builders'}/people${qs}`);
|
|
857
|
+
});
|
|
858
|
+
|
|
847
859
|
app.use((req, res, next) => {
|
|
848
860
|
if (isModuleSurfaceHost(req)) return next();
|
|
849
861
|
if (!req.path.startsWith('/builders')) return next();
|
|
@@ -862,7 +874,7 @@ function mountInternalSurfaces(app) {
|
|
|
862
874
|
if (GOVERNMENT_PAGE_RE.test(norm)) {
|
|
863
875
|
return gdsAuth.requireGovernmentPage(req, res, next);
|
|
864
876
|
}
|
|
865
|
-
if (PEOPLE_PAGE_RE.test(norm)
|
|
877
|
+
if (PEOPLE_PAGE_RE.test(norm)) {
|
|
866
878
|
return gdsAuth.requireProjectCuratePage(req, res, next);
|
|
867
879
|
}
|
|
868
880
|
next();
|
package/src/module-api.js
CHANGED
|
@@ -33,7 +33,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
|
|
|
33
33
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
34
34
|
// the entry to that file. Look for a version's history there, not here.
|
|
35
35
|
// ---------------------------------------------------------------------------
|
|
36
|
-
const CORE_VERSION = '1.19.
|
|
36
|
+
const CORE_VERSION = '1.19.469'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
37
37
|
|
|
38
38
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
39
39
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -63,9 +63,11 @@ const ORACLE = {
|
|
|
63
63
|
'/builders/sessions': 'requireGovernmentPage',
|
|
64
64
|
|
|
65
65
|
// Metic+ via the `project.curate` atom — the exact permission GET /scouting
|
|
66
|
-
// checks, so
|
|
66
|
+
// checks, so the page can never drift wider than the API it fronts. Its
|
|
67
|
+
// legacy twin /builders/scouting is gone (task 1003440): the URL now 301s to
|
|
68
|
+
// /people before this dispatch runs, so it holds no gate and has no row —
|
|
69
|
+
// tests/hall_roster_world.mjs owns the proof that the redirect is there.
|
|
67
70
|
'/builders/people': 'requireProjectCuratePage',
|
|
68
|
-
'/builders/scouting': 'requireProjectCuratePage',
|
|
69
71
|
|
|
70
72
|
// Metic+ since R16 (goal 1000069): the Government page is three rooms —
|
|
71
73
|
// Permissions · Board Room · Constitution (reordered, task 1003269) — and only
|
|
@@ -364,8 +364,8 @@ test('no reading room carries an inline <style>, and none needs an audit excepti
|
|
|
364
364
|
assert.ok(budgets, 'hall_audit.mjs still declares the ratchet');
|
|
365
365
|
assert.doesNotMatch(budgets[1].replace(/\/\/[^\n]*/g, ''), /['"][a-z-]+\.html['"]\s*:/,
|
|
366
366
|
'no page carries an inline-style exception any more');
|
|
367
|
-
// (
|
|
368
|
-
//
|
|
367
|
+
// (an exception is what this asserts is gone, not every <style>. scouting.html
|
|
368
|
+
// was the last page carrying a one-line block; task 1003440 deleted the page.)
|
|
369
369
|
});
|
|
370
370
|
|
|
371
371
|
test('roadmap\'s two sub-24px controls clear the floor, on both axes', () => {
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
// in the open and the rest behind the kit's disclosure, the readiness is the
|
|
6
6
|
// family's figure band in the same dot language as the roll line, the view
|
|
7
7
|
// toggle is a view and not a filter, the accent lands on the one live door; the
|
|
8
|
-
//
|
|
9
|
-
//
|
|
8
|
+
// legacy /scouting door is deleted and its URL redirects onto the one directory
|
|
9
|
+
// page (task 1003440), which shows the avatar, the name and the login
|
|
10
10
|
// the API returns; the session ledger drops the nowrap + wrapper pair that broke
|
|
11
11
|
// its sticky header, makes every id a door, and names its floor from the pack.
|
|
12
12
|
import test from 'node:test';
|
|
@@ -165,17 +165,47 @@ test('goals.css and people.css spend only the fifteen, and every token they name
|
|
|
165
165
|
});
|
|
166
166
|
|
|
167
167
|
// =========================================================================
|
|
168
|
-
// PEOPLE
|
|
168
|
+
// PEOPLE — one directory, one door
|
|
169
169
|
// =========================================================================
|
|
170
170
|
|
|
171
|
-
|
|
171
|
+
// Task 1003470 made /scouting render people.html byte-for-byte; task 1003440
|
|
172
|
+
// removes the door itself. Two URLs onto one directory is a surface to keep in
|
|
173
|
+
// sync that no reader ever asked for — but the URL still has to answer, or a
|
|
174
|
+
// bookmark on the old door 404s.
|
|
175
|
+
test('the legacy /scouting door is deleted, and nothing scouting-shaped is left in the hall', () => {
|
|
176
|
+
for (const f of ['scouting.html', 'scouting.js', 'scouting.states.json', 'scouting.css']) {
|
|
177
|
+
assert.ok(!fs.existsSync(path.join(PUB, f)), f + ' is gone — one directory, one door');
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('the /scouting URL redirects to /people, and no dead gate dispatches to it', () => {
|
|
182
|
+
const serve = fs.readFileSync(path.join(ROOT, 'src', 'bongos', 'serve-internal.js'), 'utf8');
|
|
183
|
+
const i = serve.indexOf('const SCOUTING_LEGACY_RE');
|
|
184
|
+
assert.ok(i > 0, 'the retired URL keeps a matcher, so the bookmark still lands somewhere');
|
|
185
|
+
const block = serve.slice(i, i + 1200);
|
|
186
|
+
assert.ok(block.includes('res.redirect(301,'), 'and answers with a permanent redirect');
|
|
187
|
+
assert.ok(block.includes("/people"), 'pointing at the directory that replaced it');
|
|
188
|
+
// RUN the shipped matcher, rather than trusting that it reads right. Anchoring
|
|
189
|
+
// is the half that goes wrong: an unanchored twin would swallow the API route.
|
|
190
|
+
const decl = 'const SCOUTING_LEGACY_RE = ';
|
|
191
|
+
const j = serve.indexOf(decl);
|
|
192
|
+
const rx = eval(serve.slice(j + decl.length, serve.indexOf(';', j))); // eslint-disable-line no-eval
|
|
193
|
+
for (const p of ['/builders/scouting', '/builders/scouting/', '/builders/scouting.html']) {
|
|
194
|
+
assert.ok(rx.test(p), p + ' must reach the redirect');
|
|
195
|
+
}
|
|
196
|
+
for (const p of ['/api/gds/scouting', '/api/bongos/scouting', '/builders/people',
|
|
197
|
+
'/builders/scoutingfoo', '/builders/scouting/extra']) {
|
|
198
|
+
assert.ok(!rx.test(p), p + ' must NOT be redirected — the data route and the directory are not the door');
|
|
199
|
+
}
|
|
200
|
+
// The gate is the half that would rot silently: a page-gate branch still
|
|
201
|
+
// naming a deleted page is a wall guarding nothing.
|
|
202
|
+
assert.ok(!decomment(serve).includes('SCOUTING_PAGE_RE'),
|
|
203
|
+
'no page gate is left dispatching to a page that no longer exists');
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('the directory page loads its own script, sheet and toolkit', () => {
|
|
172
207
|
const people = read('people.html');
|
|
173
|
-
const
|
|
174
|
-
const bodyOf = (html) => html.slice(html.indexOf('<body'));
|
|
175
|
-
assert.equal(bodyOf(scouting), bodyOf(people), 'the two bodies are byte-identical');
|
|
176
|
-
assert.ok(!fs.existsSync(path.join(PUB, 'scouting.js')), 'scouting.js is gone — the third rendering of one endpoint');
|
|
177
|
-
assert.doesNotMatch(scouting, /<style[^>]*>/, 'the legacy door carries no inline style');
|
|
178
|
-
for (const html of [people, scouting]) {
|
|
208
|
+
for (const html of [people]) {
|
|
179
209
|
assert.match(html, /\/builders\/people\.js/);
|
|
180
210
|
assert.match(html, /\/builders\/people\.css/);
|
|
181
211
|
assert.match(html, /\/builders\/dom-utils\.js/, 'the toolkit the rank label and the number formats come from');
|
|
@@ -280,8 +310,6 @@ test('the states files exercise the redesign\'s new states', () => {
|
|
|
280
310
|
assert.deepEqual(goals.states.cards.expect.value, [['#f-goal-view', 'cards']]);
|
|
281
311
|
const people = JSON.parse(read('people.states.json'));
|
|
282
312
|
assert.ok(people.states.search, 'people: a search is measured');
|
|
283
|
-
const scouting = JSON.parse(read('scouting.states.json'));
|
|
284
|
-
assert.deepEqual(scouting.states.board.expect.visible, ['#people-scroll'], 'scouting expects the People panel');
|
|
285
313
|
const sessions = JSON.parse(read('sessions.states.json'));
|
|
286
314
|
assert.deepEqual(sessions.states.ledger.expect.visible, ['#stats-scroll', '#sessions-scroll']);
|
|
287
315
|
});
|
|
Binary file
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
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
|
-
<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
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
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">
|
|
20
|
-
</head>
|
|
21
|
-
<body data-page="people" data-page-title="People">
|
|
22
|
-
|
|
23
|
-
<div class="app">
|
|
24
|
-
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
25
|
-
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
26
|
-
<div class="app-body">
|
|
27
|
-
<header class="app-topbar" id="app-topbar"></header>
|
|
28
|
-
<div class="app-content">
|
|
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. -->
|
|
32
|
-
<div class="page-head page-head--board">
|
|
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>
|
|
35
|
-
</div>
|
|
36
|
-
|
|
37
|
-
<main id="hall-main">
|
|
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>
|
|
47
|
-
</section>
|
|
48
|
-
</main>
|
|
49
|
-
|
|
50
|
-
<footer class="app-foot" id="app-foot"></footer>
|
|
51
|
-
</div>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
54
|
-
|
|
55
|
-
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
56
|
-
|
|
57
|
-
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
58
|
-
chrome exists before the page script runs. -->
|
|
59
|
-
<script src="/builders/shell.js?v=2026-07-02-shell1"></script>
|
|
60
|
-
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
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>
|
|
65
|
-
<script src="/builders/brand-apply.js"></script>
|
|
66
|
-
<script src="/builders/hall-kit.js"></script>
|
|
67
|
-
<script src="/builders/people.js"></script>
|
|
68
|
-
</body>
|
|
69
|
-
</html>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
{
|
|
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
|
-
"page": "/builders/scouting",
|
|
4
|
-
"surface": "hall-ui",
|
|
5
|
-
"stub": {
|
|
6
|
-
"prefix": "/builders/"
|
|
7
|
-
},
|
|
8
|
-
"modeQuery": false,
|
|
9
|
-
"_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
|
|
10
|
-
"ignoreRequests": [
|
|
11
|
-
"/api/(gds|bongos)/me$",
|
|
12
|
-
"/api/(gds|bongos)/me/",
|
|
13
|
-
"/api/(gds|bongos)/goals",
|
|
14
|
-
"/api/(gds|bongos)/inbox/",
|
|
15
|
-
"/api/(gds|bongos)/versions",
|
|
16
|
-
"/api/(gds|bongos)/scouting",
|
|
17
|
-
"/api/(gds|bongos)/builders"
|
|
18
|
-
],
|
|
19
|
-
"ignoreConsole": [
|
|
20
|
-
"/api/(gds|bongos)/",
|
|
21
|
-
"\\b401\\b",
|
|
22
|
-
"\\b403\\b",
|
|
23
|
-
"\\b404\\b",
|
|
24
|
-
"\\b501\\b",
|
|
25
|
-
"Unauthorized",
|
|
26
|
-
"Forbidden",
|
|
27
|
-
"Not Found",
|
|
28
|
-
"Not Implemented",
|
|
29
|
-
"load failed"
|
|
30
|
-
],
|
|
31
|
-
"states": {
|
|
32
|
-
"board": {
|
|
33
|
-
"auth": true,
|
|
34
|
-
"actions": [
|
|
35
|
-
[
|
|
36
|
-
"wait",
|
|
37
|
-
700
|
|
38
|
-
]
|
|
39
|
-
],
|
|
40
|
-
"expect": {
|
|
41
|
-
"visible": [
|
|
42
|
-
"#people-scroll"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"out": {
|
|
47
|
-
"auth": false
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"reducedMotion": {
|
|
51
|
-
"auth": true,
|
|
52
|
-
"actions": [
|
|
53
|
-
[
|
|
54
|
-
"wait",
|
|
55
|
-
600
|
|
56
|
-
]
|
|
57
|
-
]
|
|
58
|
-
}
|
|
59
|
-
}
|