@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
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
# The roster and ledger pages — goals, sessions and people (task 1003470; scouting became /people in task 1003440)
|
|
1
|
+
# The roster and ledger pages — goals, sessions and people (task 1003470; scouting became /people in task 1003440; /people deleted in task 1003444)
|
|
2
2
|
|
|
3
3
|
**The ROSTER and LEDGER pages landed next (task 1003470)** — goals, sessions, people and scouting. Goals is the archetype page (`docs/design/mocks/hall/goals/v3.html`) and the page the owner was looking at when he called the re-skin a re-skin, so this pass changed its COMPOSITION rather than its tokens. Five things it settled:
|
|
4
4
|
|
|
5
5
|
- **The goals list is ONE panel.** The filter bar, the count line, the readiness band and the groups sit on one `.scroll` (`.goals-panel`); the bar of twelve controls on the ground over a `--bg-card` panel PER GROUP is gone. Three lenses stay in the open (version, state, sort) and the five rarely turned sit behind the kit's Filters disclosure, **which counts and chips whatever is active so a hidden filter can never narrow the board silently.** `rows|cards` is a VIEW, not a filter: a segmented toggle on the count line writes a hidden `#f-goal-view`, so task 1003136's fork in `renderList` and the `otb.goals.view` pref are unchanged; the pressed side takes `--accent-tint` (the tint the world gives a current thing), **never the accent fill.**
|
|
6
6
|
- **Three languages on a row, not four.** The ring is the goal's own done-when (satisfied over total, **the arc in INK — the accent lights the pressable and a progress arc is neither**); the facts line is every label a goal carries; the right column is ONE thing — `claim next` as the shell's `.btn-accent` (**the row's only accent**), `what's blocking` as a ghost pill, or a one-line state with a drawn check. The readiness is the family's `.ov-figs` band with the roll line's own `.gr-mark` dots (**classes, not inline styles**), so the band and the rows read as one language.
|
|
7
7
|
- **The card is the row folded.** The toggle stays because the mock keeps it (a card wall is the better read when scanning for a NAME), but a card inside the panel draws no box — a `--bg-card` tile on a `--bg-card` panel is the card-in-a-card v3 removed — so `.goal-grid` paints the hairline BETWEEN its rows (a 1px row gap over `--rule-soft`, each cell on the panel's ground) and every cell renders the same facts from `goalRenderFacts`.
|
|
8
|
-
- **People and scouting are ONE page.** `/scouting` was the legacy door to the same `/api/gds/scouting` rollup, drawn a third way; it now renders `people.html`'s body and `scouting.js` is deleted. **Task 1003440 then deleted the door itself**: `scouting.html` and `scouting.states.json` are gone, the `SCOUTING_PAGE_RE` page gate with them, and `/builders/scouting` now 301s to `/people` (`SCOUTING_LEGACY_RE` in `serve-internal.js`) so an old bookmark still lands on the directory. `tests/hall_roster_world.mjs` pins both halves — the files are absent, and the redirect exists with no dead gate behind it. On the row the name is the shell's `.lb-name` to the builder's **GitHub** page — the one door every listed builder has, since the directory is cross-project and no local profile id exists. The gate copy is written from `OTB.rankLabel('metic')`; it used to say "Archon-only", wrong since ADR 0210.
|
|
8
|
+
- **People and scouting are ONE page.** `/scouting` was the legacy door to the same `/api/gds/scouting` rollup, drawn a third way; it now renders `people.html`'s body and `scouting.js` is deleted. **Task 1003440 then deleted the door itself**: `scouting.html` and `scouting.states.json` are gone, the `SCOUTING_PAGE_RE` page gate with them, and `/builders/scouting` now 301s to `/people` (`SCOUTING_LEGACY_RE` in `serve-internal.js`) so an old bookmark still lands on the directory. `tests/hall_roster_world.mjs` pins both halves — the files are absent, and the redirect exists with no dead gate behind it. On the row the name is the shell's `.lb-name` to the builder's **GitHub** page — the one door every listed builder has, since the directory is cross-project and no local profile id exists. The gate copy is written from `OTB.rankLabel('metic')`; it used to say "Archon-only", wrong since ADR 0210. **Task 1003444 then deleted `/people` itself** (owner call, 2026-09-25 — [ADR 0337](../../../docs/adr/0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D5): cross-project discovery is the hub's, so `people.html`, `people.js`, `people.states.json` and the `scouting.json` harness fixture are gone, the identity cell `/roster` shared moved from `people.css` to `roster.css`, and the `requireProjectCuratePage` page gate (with its `alsoAdmits` door) went with the page. `/builders/people` and `/builders/scouting` now 302 to `/roster` (`RETIRED_DIRECTORY_RE`); GET `/scouting`'s own route gate is unchanged. Cross-project search and the recruiter name-only panel are absent until the hub community page (task 1003363) ships.
|
|
9
9
|
- **The session ledger's nowrap + wrapper pair was a live bug.** `sessions.css` forced `white-space: nowrap` on every cell, `.ledger-table-wrap` existed only to give that a scrollport, and the shell's sticky `th` then stuck to the WRAPPER — the column heads rendered over the first row on desktop. Both are gone; the shell's card strip handles a phone as on every other ledger. **Every id on a row is a door** (the session id was painted in `--info` — a link to nowhere); the row shows what the session EARNED; the totals ride the shell's top strip (`.ov-summary`) with **six fixed columns so the sixth tile is never stranded on its own row**. The hand-rolled server-side pager is untouched (the sanctioned exception below).
|
|
@@ -124,8 +124,8 @@ function isAccountActive(account) {
|
|
|
124
124
|
// The rollup axis: visible AND not hidden (task 1002967 — ADR 0171 D2 extended)
|
|
125
125
|
// -------------------------------------------------------------------------
|
|
126
126
|
|
|
127
|
-
// The predicate the two ROLLUP readers join — listScoutingRollup
|
|
128
|
-
// /scouting) and getPublicProfileExtras (the platform-identity.hub port behind
|
|
127
|
+
// The predicate the two ROLLUP readers join — listScoutingRollup
|
|
128
|
+
// (/scouting) and getPublicProfileExtras (the platform-identity.hub port behind
|
|
129
129
|
// core GET /builders/:id/profile). COMPOSED from accountVisibleSql, never
|
|
130
130
|
// restated, so changing what "visible" means moves this with it.
|
|
131
131
|
function rollupVisibleSql(alias) {
|
|
@@ -185,7 +185,7 @@ function recruiterReachSql(alias) {
|
|
|
185
185
|
|
|
186
186
|
// scoutingListableSql — THE predicate the recruiting directory joins: the rollup
|
|
187
187
|
// axis AND the reach axis (ADR 0210, task 1002290). listScoutingRollup is the
|
|
188
|
-
// one reader; /scouting
|
|
188
|
+
// one reader; GET /scouting is its surface (the hall's /people went, task 1003444).
|
|
189
189
|
//
|
|
190
190
|
// WHY REACH BELONGS HERE AND NOT IN rollupVisibleSql. The two axes answer
|
|
191
191
|
// different questions and only one surface asks both. `hide_stats` suppresses
|
|
@@ -701,8 +701,8 @@ async function upsertActivity({ clientId, githubId, credits = 0, tasksShipped =
|
|
|
701
701
|
//
|
|
702
702
|
// Consent by construction (ADR 0171 D2/D3, extended by tasks 1002967 + 1002290):
|
|
703
703
|
// only builders whose account passes scoutingListableSql — active, publicly
|
|
704
|
-
// visible, not hidden, AND not opted out of recruiting — appear. /
|
|
705
|
-
//
|
|
704
|
+
// visible, not hidden, AND not opted out of recruiting — appear. /scouting is a
|
|
705
|
+
// live cross-project surface, so the predicate lives IN the query,
|
|
706
706
|
// not in any caller's memory, and it is the SHARED one so this list can never
|
|
707
707
|
// disagree with /profiles/:handle about who is visible. Provisional, private,
|
|
708
708
|
// hidden and opted-out builders drop out entirely; the projection stays an
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// account_visibility + recruiter_discoverable (task
|
|
15
15
|
// 1002966, privacy spec D1). hide_stats takes effect
|
|
16
16
|
// immediately on every public surface (profile
|
|
17
|
-
// cross_project, /
|
|
17
|
+
// cross_project, /scouting): enforcement
|
|
18
18
|
// lives in the module's read functions (D2) and the
|
|
19
19
|
// hub profile response is no-store (D5), so there is
|
|
20
20
|
// no cache to outwait. The other two are STORED here
|
|
@@ -174,9 +174,10 @@ function registerSeams() {
|
|
|
174
174
|
// which a database error must never be allowed to say.
|
|
175
175
|
hiddenGithubIds: (githubIds) => pi.hiddenGithubIds(githubIds, { pool }),
|
|
176
176
|
// task 1002290 (ADR 0210): "does this hub account own a project?", the
|
|
177
|
-
// second audience
|
|
178
|
-
//
|
|
179
|
-
//
|
|
177
|
+
// second audience a page gate's `alsoAdmits` door admits — today the /deploy
|
|
178
|
+
// shell (requireCorePinPage, ADR 0339); /people, its first user, is deleted
|
|
179
|
+
// (task 1003444). The ownership fact lives in this module, so the answer does
|
|
180
|
+
// too; core asks through the port and treats an absent one as a denial.
|
|
180
181
|
isProjectOwner: (githubId) => scoutingAuthz.hasOwnerMembership(githubId, { pool }),
|
|
181
182
|
// task 1002347 (BV1.R28, ADR 0251): the names behind `invites.seeking` —
|
|
182
183
|
// the creation wizard's "who is worth inviting" list. Consent-filtered by
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// modules/platform-identity/scouting-authz.js — WHO may read the cross-project
|
|
2
|
-
// recruiting directory (GET /scouting; the hall's /people page
|
|
3
|
-
//
|
|
2
|
+
// recruiting directory (GET /scouting; the hall's /people page that rendered it
|
|
3
|
+
// was deleted in task 1003444). ADR 0210, task 1002290 — goal 1000045 C5: "project owners (not only
|
|
4
4
|
// Archons) browse builder profiles to recruit".
|
|
5
5
|
//
|
|
6
6
|
// TWO DOORS, DELIBERATELY DIFFERENT KINDS — the modules/lifecycle/goal-authz.js
|
|
@@ -680,7 +680,7 @@ await ta('listScoutingRollup scalar aggregation is fan-out-free (task 1002911)',
|
|
|
680
680
|
});
|
|
681
681
|
|
|
682
682
|
await ta('listScoutingRollup enforces consent IN the query (ADR 0171 D2/D3)', async () => {
|
|
683
|
-
// /
|
|
683
|
+
// /scouting must honor hide BY CONSTRUCTION: the predicate lives
|
|
684
684
|
// in the scalar-aggregation CTE, not in any caller. Semantics proven against
|
|
685
685
|
// real SQL by tests/profile_rollup_consent.mjs.
|
|
686
686
|
const pool = mockPool([{ rows: [] }]);
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1046",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.1046",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.1046",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -7565,5 +7565,17 @@
|
|
|
7565
7565
|
"id": "1003518",
|
|
7566
7566
|
"text": "A blocker that was linked to the wrong task — a duplicate, or a mistake — can now be unlinked, and the task goes back to being claimable if nothing else was holding it. Before this the only way out was to mark the blocker reso"
|
|
7567
7567
|
}
|
|
7568
|
+
],
|
|
7569
|
+
"1.19.1045": [
|
|
7570
|
+
{
|
|
7571
|
+
"id": "1003458",
|
|
7572
|
+
"text": "The Tasks board and the Goals page now claim and release work through one shared piece of code instead of two copies that had already drifted apart — the same refusal was worded differently on each page, and one had lost its \""
|
|
7573
|
+
}
|
|
7574
|
+
],
|
|
7575
|
+
"1.19.1046": [
|
|
7576
|
+
{
|
|
7577
|
+
"id": "1003444",
|
|
7578
|
+
"text": "The hall's People page is gone, as you asked. An old People link now opens the Builders page instead, and People is no longer in the side menu. Searching for builders across projects comes back when the hub's community page is"
|
|
7579
|
+
}
|
|
7568
7580
|
]
|
|
7569
7581
|
}
|
package/src/bongos/auth.js
CHANGED
|
@@ -425,17 +425,14 @@ function signInWithReturn(req) {
|
|
|
425
425
|
const PAGE_GATES = Object.freeze({
|
|
426
426
|
requireBuilderPage: Object.freeze({ permission: 'page.view.public', deniedTo: '/builders' }),
|
|
427
427
|
requireGovernmentPage: Object.freeze({ permission: 'page.view.government', deniedTo: '/builders' }),
|
|
428
|
-
// `alsoAdmits` — a SECOND, non-rank audience
|
|
429
|
-
// the page can follow it (ADR 0210
|
|
430
|
-
//
|
|
431
|
-
//
|
|
432
|
-
//
|
|
433
|
-
//
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
requireProjectCuratePage: Object.freeze({
|
|
437
|
-
permission: 'project.curate', deniedTo: '/builders', alsoAdmits: 'isProjectOwner',
|
|
438
|
-
}),
|
|
428
|
+
// `alsoAdmits` (on requireCorePinPage below) — a SECOND, non-rank audience a
|
|
429
|
+
// page's DATA ROUTE admits, so the page can follow it (ADR 0210). A page gate that
|
|
430
|
+
// could only ever ask about a permission would leave the widened audience staring
|
|
431
|
+
// at a redirect. The value names a function on the `platform-identity.hub` port —
|
|
432
|
+
// the module that owns the ownership fact — and is consulted ONLY after the
|
|
433
|
+
// permission check has already refused, so it can widen and never narrow. Absent
|
|
434
|
+
// port, absent function, or a throw: no admit. Its first user, the hall's /people
|
|
435
|
+
// page, was deleted by task 1003444 (ADR 0337 D5).
|
|
439
436
|
// The /board-room page (ADR 0266) — gated on `board.vote.cast`, the SAME atom
|
|
440
437
|
// the vote route checks, so the surface's reach equals the right to vote.
|
|
441
438
|
//
|
|
@@ -464,7 +461,7 @@ const PAGE_GATES = Object.freeze({
|
|
|
464
461
|
//
|
|
465
462
|
// SINCE ADR 0339 (task 1004174) its routes admit a SECOND audience — a hosted
|
|
466
463
|
// project's own owner, for that project only — so the shell follows through the same
|
|
467
|
-
// `alsoAdmits` door
|
|
464
|
+
// `alsoAdmits` door (ADR 0210). It admits the SHELL for an owner of any
|
|
468
465
|
// live project; which project they may read or move is still the routes' question,
|
|
469
466
|
// asked per request against that project's row.
|
|
470
467
|
requireCorePinPage: Object.freeze({ permission: 'core.pin.move', deniedTo: '/builders', alsoAdmits: 'isProjectOwner' }),
|
|
@@ -619,17 +616,6 @@ async function requireGovernmentPage(req, res, next) {
|
|
|
619
616
|
return runPageGate('requireGovernmentPage', req, res, next);
|
|
620
617
|
}
|
|
621
618
|
|
|
622
|
-
// The /people builder-directory page (task 1002577) — gated on the
|
|
623
|
-
// `project.curate` ATOM, the same key its one data route (GET /scouting,
|
|
624
|
-
// modules/platform-identity) checks, so the page layer can never drift wider
|
|
625
|
-
// than the API it fronts. R14 (goal 1000045, ADR 0210) widened that route to a
|
|
626
|
-
// SECOND audience that is not a rank at all — a project owner — so this gate
|
|
627
|
-
// carries the same OR through `alsoAdmits` above: the two doors of the page are
|
|
628
|
-
// exactly the two doors of the route, and neither layer can drift from it.
|
|
629
|
-
async function requireProjectCuratePage(req, res, next) {
|
|
630
|
-
return runPageGate('requireProjectCuratePage', req, res, next);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
619
|
// The /board-room page (ADR 0266) — gated on the `board.vote.cast` ATOM, the
|
|
634
620
|
// same key the vote route checks, so the page layer can never drift NARROWER
|
|
635
621
|
// than the API it fronts. That is the direction that matters here: the Board
|
|
@@ -940,7 +926,6 @@ module.exports = {
|
|
|
940
926
|
requireBuilder,
|
|
941
927
|
requireBuilderPage,
|
|
942
928
|
requireGovernmentPage,
|
|
943
|
-
requireProjectCuratePage,
|
|
944
929
|
requireBoardVotePage,
|
|
945
930
|
requireCorePinPage,
|
|
946
931
|
requireFleetPage,
|
|
@@ -936,17 +936,13 @@ function mountInternalSurfaces(app) {
|
|
|
936
936
|
// statically, and a comment in that gap drops the regex out of the map, which
|
|
937
937
|
// makes the page test as UNGATED while looking gated here. Prose goes above.
|
|
938
938
|
const BOARD_ROOM_PAGE_RE = /^\/builders\/board-room(?:\.html)?\/?$/;
|
|
939
|
-
// The /people builder directory (task 1002577): gated on `project.curate`, the
|
|
940
|
-
// exact permission its data route (GET /scouting) checks — same posture as the
|
|
941
|
-
// /government tab above (the page can never drift wider than the API it fronts).
|
|
942
|
-
const PEOPLE_PAGE_RE = /^\/builders\/people(?:\.html)?\/?$/;
|
|
943
939
|
// /deploy — the owner's deploy door (task 1003159, ADR 0293). Gated on the
|
|
944
940
|
// `core.pin.move` atom, the same key its preview and its move both check, so the
|
|
945
941
|
// shell's reach equals the right to press the button. Archon-floor and system:true,
|
|
946
942
|
// which makes this the narrowest page gate here — correct for the one surface whose
|
|
947
943
|
// button restarts the live service and changes what code the platform runs.
|
|
948
944
|
//
|
|
949
|
-
// DECLARED HERE, WITH THE OTHER GATES, AND NOT BELOW: the
|
|
945
|
+
// DECLARED HERE, WITH THE OTHER GATES, AND NOT BELOW: the RETIRED_DIRECTORY_RE block
|
|
950
946
|
// that follows is a matcher immediately followed by the redirect it feeds, and
|
|
951
947
|
// tests/hall_roster_world.mjs reads them as one span. Wedging a declaration between
|
|
952
948
|
// the two separates a matcher from its handler for a reader as well as for that test.
|
|
@@ -959,23 +955,24 @@ function mountInternalSurfaces(app) {
|
|
|
959
955
|
const DEPLOY_PAGE_RE = /^\/builders\/deploy(?:\.html)?\/?$/;
|
|
960
956
|
// The fleet cost page (task 1001949), on the atom its one read checks.
|
|
961
957
|
const FLEET_PAGE_RE = /^\/builders\/fleet(?:\.html)?\/?$/;
|
|
962
|
-
// /
|
|
963
|
-
// directory
|
|
964
|
-
//
|
|
965
|
-
//
|
|
966
|
-
//
|
|
967
|
-
//
|
|
968
|
-
//
|
|
969
|
-
// nothing, and /
|
|
970
|
-
|
|
958
|
+
// /people and its legacy twin /scouting were the hall's cross-project builder
|
|
959
|
+
// directory. Task 1003440 deleted the /scouting door; task 1003444 deleted
|
|
960
|
+
// /people itself (owner call, 2026-09-25 — ADR 0337 D5): cross-project
|
|
961
|
+
// discovery belongs to the hub, and /roster is the hall's directory. Both URLs
|
|
962
|
+
// survive as redirects to /roster so a bookmark lands somewhere instead of a
|
|
963
|
+
// 404. 302, not 301: /roster is interim until the hub community page (task
|
|
964
|
+
// 1003363) ships, and a browser caches a 301 past any re-point. No page gate:
|
|
965
|
+
// the redirect discloses nothing, and /roster is deliberately public.
|
|
966
|
+
// GET /scouting's own gate (platform-identity) is untouched by this.
|
|
967
|
+
const RETIRED_DIRECTORY_RE = /^\/builders\/(?:people|scouting)(?:\.html)?\/?$/;
|
|
971
968
|
app.use((req, res, next) => {
|
|
972
969
|
if (isModuleSurfaceHost(req)) return next();
|
|
973
970
|
if (req.method !== 'GET' && req.method !== 'HEAD') return next();
|
|
974
|
-
if (!
|
|
971
|
+
if (!RETIRED_DIRECTORY_RE.test(path.posix.normalize(req.path))) return next();
|
|
975
972
|
const qs = req.url.includes('?') ? req.url.slice(req.url.indexOf('?')) : '';
|
|
976
973
|
// On the builders subdomain the /builders prefix is redundant (the canonical
|
|
977
974
|
// redirect above strips it), so emit the clean path and save a second hop.
|
|
978
|
-
return res.redirect(
|
|
975
|
+
return res.redirect(302, `${isBuildersHost(req) ? '' : '/builders'}/roster${qs}`);
|
|
979
976
|
});
|
|
980
977
|
|
|
981
978
|
// /deploy EXISTS ONLY WHERE THE DOOR DOES (task 1004296). The owner, 2026-09-25: /deploy is
|
|
@@ -1019,9 +1016,6 @@ function mountInternalSurfaces(app) {
|
|
|
1019
1016
|
if (BOARD_ROOM_PAGE_RE.test(norm)) {
|
|
1020
1017
|
return gdsAuth.requireBoardVotePage(req, res, next);
|
|
1021
1018
|
}
|
|
1022
|
-
if (PEOPLE_PAGE_RE.test(norm)) {
|
|
1023
|
-
return gdsAuth.requireProjectCuratePage(req, res, next);
|
|
1024
|
-
}
|
|
1025
1019
|
// ADR 0293: the deploy door, on the same atom its two routes check.
|
|
1026
1020
|
if (DEPLOY_PAGE_RE.test(norm)) {
|
|
1027
1021
|
return gdsAuth.requireCorePinPage(req, res, next);
|
package/src/module-api.js
CHANGED
|
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
71
71
|
// there. scripts/gds/bump-version.js still rewrites the literal below; it appends
|
|
72
72
|
// the entry to that file. Look for a version's history there, not here.
|
|
73
73
|
// ---------------------------------------------------------------------------
|
|
74
|
-
const CORE_VERSION = '1.19.
|
|
74
|
+
const CORE_VERSION = '1.19.1046'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
75
75
|
|
|
76
76
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
77
77
|
// Usage: const log = api.logger('dev-box'); log.info('mounted');
|
|
@@ -418,7 +418,7 @@ test('the recruiting surface really is exactly two reads, and each one joins REA
|
|
|
418
418
|
const av = readFileSync(join(ROOT, 'modules/platform-identity/account-visibility.js'), 'utf8');
|
|
419
419
|
// The directory reads TWO tiers since task 1002291 — ranked, and the hidden
|
|
420
420
|
// builders it lists by name — and the opt-out must empty both, or "removes you
|
|
421
|
-
// from the
|
|
421
|
+
// from the recruiting directory" is false for a builder who also hid their record.
|
|
422
422
|
for (const composed of ['scoutingListableSql', 'scoutingNameOnlySql', 'recruiterSliverVisibleSql']) {
|
|
423
423
|
const body = av.slice(av.indexOf(`function ${composed}(`));
|
|
424
424
|
assert.match(body.slice(0, body.indexOf('\n}')), /recruiterReachSql\(alias\)/,
|
|
@@ -93,22 +93,22 @@ test('AUDIT: every async middleware mounted directly on a router owns its reject
|
|
|
93
93
|
// rot: a NEW bare `async function require*` added later fails this test.
|
|
94
94
|
// requireBuilder — the API gate. Guarded here (BV1.R109-follow-up).
|
|
95
95
|
// requireBuilderPage \
|
|
96
|
-
// requireGovernmentPage \ all
|
|
97
|
-
//
|
|
98
|
-
// requireBoardVotePage / ADR 0266 /board-room vote gate)
|
|
96
|
+
// requireGovernmentPage \ all delegate to runPageGate, guarded by
|
|
97
|
+
// requireBoardVotePage / BV1.R107 (and the ADR 0266 /board-room vote gate)
|
|
99
98
|
// requireCorePinPage / ADR 0293's /deploy door — the narrowest of them,
|
|
100
99
|
// on the archon-floor `core.pin.move` atom.
|
|
101
100
|
// requireFleetPage — task 1001949's /fleet cost page, same delegation.
|
|
102
101
|
// Task 1003450 removed two more — requireNonXenosPage and the R113
|
|
103
102
|
// requireGovernmentManagePage — which had both ended up dispatched from no
|
|
104
|
-
// route at all; tests/hall_page_gate_map.mjs now fails if that recurs.
|
|
103
|
+
// route at all; tests/hall_page_gate_map.mjs now fails if that recurs. Task
|
|
104
|
+
// 1003444 removed requireProjectCuratePage with the /people page it fronted.
|
|
105
105
|
// Anything else async and router-mounted must be added with its own try/catch.
|
|
106
106
|
const src = fs.readFileSync(path.join(ROOT, 'src', 'bongos', 'auth.js'), 'utf8');
|
|
107
107
|
const asyncMw = [...src.matchAll(/^async function (require\w+|allow\w+)\s*\(req, res, next\)/gm)].map((m) => m[1]);
|
|
108
|
-
assert.deepEqual(asyncMw.sort(), ['requireBoardVotePage', 'requireBuilder', 'requireBuilderPage', 'requireCorePinPage', 'requireFleetPage', 'requireGovernmentPage'
|
|
108
|
+
assert.deepEqual(asyncMw.sort(), ['requireBoardVotePage', 'requireBuilder', 'requireBuilderPage', 'requireCorePinPage', 'requireFleetPage', 'requireGovernmentPage'],
|
|
109
109
|
'a new async router middleware appeared — give it a try/catch and add it here');
|
|
110
110
|
// The page gates must actually delegate (that is WHY they are safe).
|
|
111
|
-
for (const name of ['requireBuilderPage', 'requireGovernmentPage', '
|
|
111
|
+
for (const name of ['requireBuilderPage', 'requireGovernmentPage', 'requireBoardVotePage', 'requireCorePinPage', 'requireFleetPage']) {
|
|
112
112
|
const fn = src.slice(src.indexOf(`async function ${name}`));
|
|
113
113
|
assert.match(sliceFn(fn), /runPageGate\(/, `${name} must delegate to the guarded runPageGate`);
|
|
114
114
|
}
|