@bongos/core 1.19.655 → 1.19.657
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 +30 -25
- package/docs/copy-inventory.md +56 -56
- package/docs/copy-registry.json +57 -57
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/board-lib.js +165 -0
- package/modules/hall-ui/public/board-room.css +5 -16
- package/modules/hall-ui/public/board-room.html +3 -0
- package/modules/hall-ui/public/board-room.js +26 -86
- package/modules/hall-ui/public/government.css +10 -8
- package/modules/hall-ui/public/government.html +3 -0
- package/modules/hall-ui/public/government.js +31 -85
- package/modules/hall-ui/public/oversight.css +31 -0
- package/modules/lifecycle/db-rank-authz.js +30 -10
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/government_board_room_copy.mjs +30 -14
- package/tests/government_claim_eligibility.mjs +82 -0
- package/tests/government_constitution_view.mjs +7 -2
- package/tests/rank_tier_single_source.mjs +28 -16
|
@@ -37,6 +37,13 @@ const LIFECYCLE = path.join(ROOT, 'modules', 'lifecycle');
|
|
|
37
37
|
const eligibility = require(path.join(LIFECYCLE, 'claim-eligibility.js'));
|
|
38
38
|
const db = require(path.join(LIFECYCLE, 'db.js'));
|
|
39
39
|
const catalog = require(path.join(ROOT, 'modules', 'government', 'catalog.js'));
|
|
40
|
+
// The doorway, so section D can stand a stub `government` port up in front of the
|
|
41
|
+
// REAL claimPermissionsFor instead of re-implementing its branches in the test.
|
|
42
|
+
const moduleApi = require(path.join(ROOT, 'src', 'module-api.js'));
|
|
43
|
+
// claimPermissionsFor is NOT on the db.js facade — db-claims.js imports it from
|
|
44
|
+
// the authz module directly, so the test reads it from the same place the real
|
|
45
|
+
// caller does rather than from a re-export that does not exist.
|
|
46
|
+
const rankAuthz = require(path.join(LIFECYCLE, 'db-rank-authz.js'));
|
|
40
47
|
import { lifecycleDbSource } from './helpers.mjs';
|
|
41
48
|
|
|
42
49
|
const { CLAIM_ANY, CLAIM_NEWCOMER, permissionAllowsClaim, claimEligibilityAllows } = eligibility;
|
|
@@ -262,3 +269,78 @@ test('the abuse case stays blocked: a xenos cannot reach the general queue by an
|
|
|
262
269
|
assert.equal(permissionAllowsClaim(held, false), false);
|
|
263
270
|
assert.equal(permissionAllowsClaim(held, true), true);
|
|
264
271
|
});
|
|
272
|
+
|
|
273
|
+
// --- D. claimPermissionsFor's three-way answer (task 1003816) ---------------
|
|
274
|
+
//
|
|
275
|
+
// The function this section covers had NO test of its own: every case above
|
|
276
|
+
// drives the pure consumer (claimEligibilityAllows) with a hand-made
|
|
277
|
+
// heldPermissions value, so nothing proved which value the REAL reader produces.
|
|
278
|
+
// That is exactly where the bug lived — an empty permission set was folded in
|
|
279
|
+
// with "could not establish authority" and silently became a rank-rule fallback.
|
|
280
|
+
//
|
|
281
|
+
// The distinction is the whole point, so it is asserted as a distinction: null
|
|
282
|
+
// (fall back) for the two unestablished cases, a Set (a real verdict, denied when
|
|
283
|
+
// empty) whenever the resolver actually answered.
|
|
284
|
+
const realResolveOptional = moduleApi.resolveOptional;
|
|
285
|
+
async function withGovernance(stub, fn) {
|
|
286
|
+
moduleApi.resolveOptional = (name) => (name === 'government' ? stub : realResolveOptional.call(moduleApi, name));
|
|
287
|
+
try { return await fn(); } finally { moduleApi.resolveOptional = realResolveOptional; }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
test('no governance port — authority cannot be established, so the rank rule decides', async () => {
|
|
291
|
+
await withGovernance(undefined, async () => {
|
|
292
|
+
assert.equal(await rankAuthz.claimPermissionsFor('42'), null);
|
|
293
|
+
});
|
|
294
|
+
// A port object that does not implement the resolver is the same situation.
|
|
295
|
+
await withGovernance({}, async () => {
|
|
296
|
+
assert.equal(await rankAuthz.claimPermissionsFor('42'), null);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test('the resolver throwing is UNKNOWN, not empty — still the rank rule', async () => {
|
|
301
|
+
await withGovernance({ resolveBuilderPermissions: async () => { throw new Error('synthetic resolver outage'); } }, async () => {
|
|
302
|
+
assert.equal(await rankAuthz.claimPermissionsFor('42'), null);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test('a broken port contract (non-array) is unknown, never a deny', async () => {
|
|
307
|
+
for (const bad of [null, undefined, 'task.claim.any', 42, {}]) {
|
|
308
|
+
await withGovernance({ resolveBuilderPermissions: async () => bad }, async () => {
|
|
309
|
+
assert.equal(await rankAuthz.claimPermissionsFor('42'), null, `a ${typeof bad} return must not read as "denied"`);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test('ZERO permissions is a real verdict now, not a fallback (the 1003816 fix)', async () => {
|
|
315
|
+
await withGovernance({ resolveBuilderPermissions: async () => [] }, async () => {
|
|
316
|
+
const held = await rankAuthz.claimPermissionsFor('42');
|
|
317
|
+
assert.ok(held instanceof Set, 'an answered resolve returns a Set, so the rank rule is NOT consulted');
|
|
318
|
+
assert.equal(held.size, 0);
|
|
319
|
+
// And the consumer turns that into a deny for every queue, however permissive
|
|
320
|
+
// the incumbent rank rule would have been.
|
|
321
|
+
for (const nf of NF_VALUES) {
|
|
322
|
+
assert.equal(
|
|
323
|
+
claimEligibilityAllows({ heldPermissions: held, newcomerFriendly: nf, rankRuleAllows: true }),
|
|
324
|
+
false,
|
|
325
|
+
'a builder with no role row is denied even where the rank rule would allow',
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('a real permission set is passed through as the verdict', async () => {
|
|
332
|
+
await withGovernance({ resolveBuilderPermissions: async () => [CLAIM_ANY] }, async () => {
|
|
333
|
+
const held = await rankAuthz.claimPermissionsFor('42');
|
|
334
|
+
assert.ok(held instanceof Set);
|
|
335
|
+
assert.equal(held.has(CLAIM_ANY), true);
|
|
336
|
+
assert.equal(claimEligibilityAllows({ heldPermissions: held, newcomerFriendly: false, rankRuleAllows: false }), true);
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
test('the doc comment no longer argues for the fallback it describes retiring', () => {
|
|
341
|
+
const src = fs.readFileSync(path.join(LIFECYCLE, 'db-rank-authz.js'), 'utf8');
|
|
342
|
+
assert.ok(
|
|
343
|
+
!/nothing yet re-assigns a rank-role/.test(src),
|
|
344
|
+
'the stale "nothing yet re-assigns a rank-role" claim is false since BV1.R95b + task 1003193 — it must not survive the fix',
|
|
345
|
+
);
|
|
346
|
+
});
|
|
@@ -95,12 +95,17 @@ test('the read rides the vote atom, and the client renders the SERVER value (sou
|
|
|
95
95
|
// of a grammar is exactly the drift that produced the bug this task fixes, so
|
|
96
96
|
// they are held equal here over every form.
|
|
97
97
|
//
|
|
98
|
+
// The mirror lives in modules/hall-ui/public/board-lib.js since task 1003761.
|
|
99
|
+
// It was in government.js, and ADR 0266's carve left a SECOND copy in
|
|
100
|
+
// board-room.js — so this test lifted one of two implementations and proved
|
|
101
|
+
// nothing about the other. One copy, one lift.
|
|
102
|
+
//
|
|
98
103
|
// This matters more than a normal mirror: the Constitution panel is where an
|
|
99
104
|
// owner READS who governs before deciding whether to change it. A panel that
|
|
100
105
|
// misdescribes the franchise is the same failure as a resolver that mis-seats
|
|
101
106
|
// it, one layer up.
|
|
102
107
|
test('the hall renders every predicate form the server actually resolves', () => {
|
|
103
|
-
const src = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
108
|
+
const src = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/board-lib.js'), 'utf8');
|
|
104
109
|
|
|
105
110
|
// Lift the browser mirror out of the IIFE and run it.
|
|
106
111
|
const ladder = /const STANDARD_LADDER = (\[[^\]]*\]);/.exec(src);
|
|
@@ -131,7 +136,7 @@ test('the hall renders every predicate form the server actually resolves', () =>
|
|
|
131
136
|
});
|
|
132
137
|
|
|
133
138
|
test('the ladder the hall expands against IS the government ladder', () => {
|
|
134
|
-
const src = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/
|
|
139
|
+
const src = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/board-lib.js'), 'utf8');
|
|
135
140
|
const { RANK_ORDER } = require(path.join(ROOT, 'modules/government/catalog.js'));
|
|
136
141
|
const m = /const STANDARD_LADDER = \[([^\]]*)\]/.exec(src);
|
|
137
142
|
assert.ok(m);
|
|
@@ -107,23 +107,35 @@ const STATIC_LADDER_FILES = [
|
|
|
107
107
|
'modules/hall-ui/public/ranks.js',
|
|
108
108
|
'modules/hall-ui/public/profile.js',
|
|
109
109
|
'modules/hall-ui/public/watch.js',
|
|
110
|
-
// task 1003094: the Constitution panel
|
|
111
|
-
// form ("that rank and above") to render
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
// is what makes the copy provable
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
110
|
+
// task 1003094: the Constitution panel and the Board Room's sitting card both
|
|
111
|
+
// expand the `rank:<key>+` membership form ("that rank and above") to render
|
|
112
|
+
// who governs in plain language, so both need the ladder to expand against.
|
|
113
|
+
// Browser code cannot require the server module — the established mirror
|
|
114
|
+
// pattern here — and listing it in this array is what makes the copy provable
|
|
115
|
+
// rather than merely allowed.
|
|
116
|
+
//
|
|
117
|
+
// ONE ROW, not two, since task 1003761. ADR 0266 carved the Board Room onto
|
|
118
|
+
// its own page and the carve COPIED the ladder and the parser into both page
|
|
119
|
+
// files, so this array had to allowlist BOTH — which permitted a drift between
|
|
120
|
+
// them by construction: the exact-literal pin below proved each matched the
|
|
121
|
+
// canonical ladder, but nothing held the two parsers equal to EACH OTHER, and
|
|
122
|
+
// government_constitution_view.mjs lifted only one of them. board-lib.js is
|
|
123
|
+
// the single copy now. A second, government-specific pin lives in
|
|
124
|
+
// tests/government_constitution_view.mjs, which holds it against
|
|
125
|
+
// catalog.RANK_ORDER and runs the whole browser parser against the server one.
|
|
126
|
+
//
|
|
127
|
+
// This row used to claim tests/government_board_vote_ui.mjs pinned the
|
|
128
|
+
// board-room copy. It does not — that file asserts the sitting card, the
|
|
129
|
+
// ballot and the vote form, and has no parser assertion at all, so the second
|
|
130
|
+
// copy was guarded only by the exact-literal ladder match.
|
|
131
|
+
'modules/hall-ui/public/board-lib.js',
|
|
132
|
+
// government.js keeps a ladder literal of its own — the charter library's
|
|
133
|
+
// `seatsRanks` worked example for `rank:xenos+` (the full ladder, spelled out
|
|
134
|
+
// so a reader sees what the predicate resolves to). That is documentation of
|
|
135
|
+
// the grammar rather than a second implementation of it, and it must still
|
|
136
|
+
// match the canonical order or the example teaches a ladder that does not
|
|
137
|
+
// exist.
|
|
118
138
|
'modules/hall-ui/public/government.js',
|
|
119
|
-
// ADR 0266: the Board Room moved off the government page onto its own, and the
|
|
120
|
-
// sitting card's `decidedByHtml` expands the same `rank:<key>+` membership form
|
|
121
|
-
// to say who may vote on THAT sitting — so the carve took a copy of the ladder
|
|
122
|
-
// with it. Same reason as the row above (browser code cannot require the server
|
|
123
|
-
// module), and the same pinning: `tests/government_board_vote_ui.mjs` reads
|
|
124
|
-
// board-room.js and holds its predicate parser equal to the server's, and the
|
|
125
|
-
// pin below in this file holds the array itself against LIVE_RANK_LADDER.
|
|
126
|
-
'modules/hall-ui/public/board-room.js',
|
|
127
139
|
// task 1002487: the pure agent-definition validator applies the ADR 0043
|
|
128
140
|
// sub-Metic floor to an agent's declared scope, so it needs the ladder to
|
|
129
141
|
// compare against. It is one of the "dependency-free" cases this array exists
|