@bongos/core 1.19.224 → 1.19.225
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 +39 -24
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +3 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +82 -2
- package/docs/api-reference.md +3 -2
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
- package/modules/government/board.js +211 -0
- package/modules/government/db.js +125 -0
- package/modules/government/routes/government.js +54 -0
- package/modules/ideas/module.json +2 -1
- package/modules/ideas/routes/inbox.js +27 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/route-rank-check.js +4 -0
- package/src/module-api.js +3 -1
- package/tests/government_board_vote.mjs +247 -0
- package/tests/government_board_window.mjs +144 -0
- package/tests/government_routes.mjs +26 -5
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// tests/government_board_window.mjs — a window opens automatically when a Full
|
|
2
|
+
// Idea passes the grade (BV1.R07 of goal 1000069, ADR 0175 §5).
|
|
3
|
+
//
|
|
4
|
+
// DB-free: the pure bar gate is exercised directly; the open path runs against
|
|
5
|
+
// a monkeypatched db module (the government_board_membership.mjs idiom). The
|
|
6
|
+
// invariants that matter:
|
|
7
|
+
//
|
|
8
|
+
// 1. THE BAR IS THE SAME ONE THE CREDIT LANE USES — ADR 0175 §5 pins the
|
|
9
|
+
// window gate to economy's IDEA_FULL_PASS_BAR. board.js may not import
|
|
10
|
+
// economy (ADR 0083), so the two constants are held equal HERE — the
|
|
11
|
+
// no-second-list-that-drifts discipline, enforced by test.
|
|
12
|
+
// 2. A SUB-BAR IDEA NEVER REACHES THE BOARD — refused inside board.js, not
|
|
13
|
+
// by trusting the caller, and without touching the db.
|
|
14
|
+
// 3. THE CONSTITUTION IS SNAPSHOTTED — the open passes R05's sanitized board
|
|
15
|
+
// block, so an amendment cannot rewrite how an already-open item closes.
|
|
16
|
+
// 4. THE IDEMPOTENCY IS STRUCTURAL — the INSERT targets government_003's
|
|
17
|
+
// partial unique index; a losing racer gets the existing open item back.
|
|
18
|
+
import assert from 'node:assert/strict';
|
|
19
|
+
import { test } from 'node:test';
|
|
20
|
+
import { createRequire } from 'node:module';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { fileURLToPath } from 'node:url';
|
|
24
|
+
|
|
25
|
+
const require = createRequire(import.meta.url);
|
|
26
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
27
|
+
const GOV = path.join(ROOT, 'modules', 'government');
|
|
28
|
+
const board = require(path.join(GOV, 'board.js'));
|
|
29
|
+
const db = require(path.join(GOV, 'db.js'));
|
|
30
|
+
const { BOARD_DEFAULTS } = require(path.join(GOV, 'config.js'));
|
|
31
|
+
const credits = require(path.join(ROOT, 'modules', 'economy', 'credits.js'));
|
|
32
|
+
|
|
33
|
+
const { FULL_IDEA_PASS_BAR, ideaPassesBar, openWindowForFullIdea } = board;
|
|
34
|
+
|
|
35
|
+
// ── invariant 1: one bar, two modules, held equal by this line ──────────────
|
|
36
|
+
|
|
37
|
+
test('the window bar IS the credit-lane bar (ADR 0175 §5 — no second list that drifts)', () => {
|
|
38
|
+
assert.equal(FULL_IDEA_PASS_BAR, credits.IDEA_FULL_PASS_BAR,
|
|
39
|
+
'board.js and economy/credits.js must gate on the SAME completeness bar; '
|
|
40
|
+
+ 'if the bar becomes policy, move BOTH reads to one config surface in the same change');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
// ── invariant 2: the pure gate, fail-closed ─────────────────────────────────
|
|
44
|
+
|
|
45
|
+
test('ideaPassesBar: the bar passes at exactly 60, and every unscorable shape fails closed', () => {
|
|
46
|
+
assert.equal(ideaPassesBar(FULL_IDEA_PASS_BAR), true, 'the bar itself passes (>=)');
|
|
47
|
+
assert.equal(ideaPassesBar(100), true);
|
|
48
|
+
assert.equal(ideaPassesBar(FULL_IDEA_PASS_BAR - 1), false);
|
|
49
|
+
for (const bad of [null, undefined, NaN, 'sixty', '', {}, [], -1, Infinity]) {
|
|
50
|
+
assert.equal(ideaPassesBar(bad), Number.isFinite(Number(bad)) && Number(bad) >= FULL_IDEA_PASS_BAR ? true : false,
|
|
51
|
+
`${String(bad)} must not reach the board`);
|
|
52
|
+
}
|
|
53
|
+
// "not scored" and "could not be scored" look the same (R142) — both stay off the board.
|
|
54
|
+
assert.equal(ideaPassesBar(null), false);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// ── the open path, against a stubbed db ─────────────────────────────────────
|
|
58
|
+
|
|
59
|
+
async function withDb(stubs, fn) {
|
|
60
|
+
const originals = {};
|
|
61
|
+
for (const [k, v] of Object.entries(stubs)) { originals[k] = db[k]; db[k] = v; }
|
|
62
|
+
try { return await fn(); }
|
|
63
|
+
finally { for (const [k, v] of Object.entries(originals)) db[k] = v; }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
test('a sub-bar idea is refused WITHOUT touching the db', async () => {
|
|
67
|
+
await withDb({
|
|
68
|
+
openBoardItem: async () => { throw new Error('must not be called'); },
|
|
69
|
+
getOpenBoardItem: async () => { throw new Error('must not be called'); },
|
|
70
|
+
}, async () => {
|
|
71
|
+
assert.deepEqual(await openWindowForFullIdea({ ideaId: 7, completenessScore: 59 }),
|
|
72
|
+
{ opened: false, reason: 'below_bar' });
|
|
73
|
+
assert.deepEqual(await openWindowForFullIdea({ ideaId: 7, completenessScore: null }),
|
|
74
|
+
{ opened: false, reason: 'below_bar' });
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('an unidentifiable idea id is refused before the bar is even consulted', async () => {
|
|
79
|
+
await withDb({ openBoardItem: async () => { throw new Error('must not be called'); } }, async () => {
|
|
80
|
+
for (const bad of [undefined, null, 0, -3, 'seven', NaN]) {
|
|
81
|
+
assert.deepEqual(await openWindowForFullIdea({ ideaId: bad, completenessScore: 100 }),
|
|
82
|
+
{ opened: false, reason: 'bad_subject' }, `${String(bad)} names no subject`);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('a passing idea opens a window carrying the SANITIZED constitution snapshot', async () => {
|
|
88
|
+
let got = null;
|
|
89
|
+
await withDb({
|
|
90
|
+
openBoardItem: async (args) => { got = args; return { id: '11', subject_type: 'full_idea', subject_id: '7', closes_at: null }; },
|
|
91
|
+
}, async () => {
|
|
92
|
+
const out = await openWindowForFullIdea({ ideaId: 7, completenessScore: 82 });
|
|
93
|
+
assert.equal(out.opened, true);
|
|
94
|
+
assert.equal(out.item.id, '11');
|
|
95
|
+
});
|
|
96
|
+
assert.equal(got.subjectType, 'full_idea');
|
|
97
|
+
assert.equal(got.subjectId, 7);
|
|
98
|
+
assert.equal(got.openedBy, null, 'the grade opened this, nobody did (government_003)');
|
|
99
|
+
// This checkout has no config/government.json, so the snapshot is the neutral
|
|
100
|
+
// starter's board block — which R05 pins equal to BOARD_DEFAULTS (monarchy).
|
|
101
|
+
assert.deepEqual(got.constitution, BOARD_DEFAULTS,
|
|
102
|
+
'the snapshot is R05\'s sanitized board block, not raw file contents');
|
|
103
|
+
assert.equal(got.windowMinutes, BOARD_DEFAULTS.window_minutes,
|
|
104
|
+
'monarchy has no clock — closes_at stays NULL until a project configures a sitting');
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('a lost race returns the EXISTING open item, opened:false', async () => {
|
|
108
|
+
const existing = { id: '4', subject_type: 'full_idea', subject_id: '7', closed_at: null };
|
|
109
|
+
await withDb({
|
|
110
|
+
openBoardItem: async () => null, // the partial-unique-index conflict path
|
|
111
|
+
getOpenBoardItem: async (type, id) => {
|
|
112
|
+
assert.equal(type, 'full_idea'); assert.equal(id, 7);
|
|
113
|
+
return existing;
|
|
114
|
+
},
|
|
115
|
+
}, async () => {
|
|
116
|
+
const out = await openWindowForFullIdea({ ideaId: 7, completenessScore: 82 });
|
|
117
|
+
assert.deepEqual(out, { opened: false, reason: 'already_open', item: existing });
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
// ── the structural idempotency + clock, pinned as SQL source ────────────────
|
|
122
|
+
|
|
123
|
+
test('the INSERT targets the partial unique index and the DATABASE clock', () => {
|
|
124
|
+
const src = fs.readFileSync(path.join(GOV, 'db.js'), 'utf8');
|
|
125
|
+
const start = src.indexOf('async function openBoardItem');
|
|
126
|
+
assert.ok(start > -1);
|
|
127
|
+
const block = src.slice(start, src.indexOf('async function getOpenBoardItem'));
|
|
128
|
+
assert.match(block, /ON CONFLICT \(subject_type, subject_id\) WHERE closed_at IS NULL DO NOTHING/,
|
|
129
|
+
'idempotency must be the partial unique index, not a SELECT-then-INSERT race');
|
|
130
|
+
assert.match(block, /now\(\) \+ make_interval/,
|
|
131
|
+
'closes_at comes from the database clock — absolute, so a config change never moves an open deadline');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// ── the port carries it ──────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
test('the government port exposes openBoardWindowForIdea for the ideas capture path', () => {
|
|
137
|
+
const routes = require(path.join(GOV, 'routes', 'government.js'));
|
|
138
|
+
routes(); // registration is hasProvider-guarded — safe to call again
|
|
139
|
+
const api = require(path.join(ROOT, 'src', 'module-api.js'));
|
|
140
|
+
const port = api.resolveOptional('government');
|
|
141
|
+
assert.equal(typeof port.openBoardWindowForIdea, 'function');
|
|
142
|
+
assert.equal(port.openBoardWindowForIdea, board.openWindowForFullIdea,
|
|
143
|
+
'the port method IS board.js\'s — no wrapper that could drift the bar gate');
|
|
144
|
+
});
|
|
@@ -64,12 +64,34 @@ test('the routes file declares the full R110 surface', () => {
|
|
|
64
64
|
for (const key of wanted) assert.ok(declared.has(key), `missing route ${key}`);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
test('every /governance route gates on
|
|
67
|
+
test('every /governance route gates via REAL middleware on exactly its PINNED atom', () => {
|
|
68
|
+
// Until R08 every route here gated on government.manage and this test said so
|
|
69
|
+
// literally. The Board Room's vote route (R08 of goal 1000069) gates on the
|
|
70
|
+
// deliberately XENOS-floored board.vote.cast — the atom is the coarse gate,
|
|
71
|
+
// membership is the in-handler wall — so the invariant generalizes: each
|
|
72
|
+
// route's middleware must declare EXACTLY the atom its pin promises, and the
|
|
73
|
+
// rank each classifies to must be that atom's real catalog floor. This is
|
|
74
|
+
// STRICTER than the old literal: a new route cannot sneak in on a different
|
|
75
|
+
// atom without also changing the pin table, which is the reviewed surface.
|
|
68
76
|
assert.ok(classified.length > 0, 'no /government routes found to classify');
|
|
77
|
+
const pins = rrc.MODULE_EXPECTED_RANKS.government;
|
|
69
78
|
for (const r of classified) {
|
|
70
79
|
assert.equal(r.via, 'middleware', `${r.key}: gate must be middleware, not an annotation`);
|
|
71
|
-
|
|
72
|
-
assert.
|
|
80
|
+
const pinned = pins[r.key];
|
|
81
|
+
assert.ok(pinned && pinned.startsWith(rrc.PERM_PIN_PREFIX), `${r.key}: must carry a perm pin`);
|
|
82
|
+
const atom = pinned.slice(rrc.PERM_PIN_PREFIX.length);
|
|
83
|
+
assert.deepEqual(r.perms, [atom], `${r.key}: must gate on exactly its pinned atom (${atom})`);
|
|
84
|
+
// The auditor speaks rank LABELS, not raw floors (route-rank-check.js
|
|
85
|
+
// FLOOR_LABEL): both sub-Metic floors collapse to 'any-builder' — a label
|
|
86
|
+
// that may under-report strictness, never over-report it.
|
|
87
|
+
const FLOOR_LABEL = { xenos: 'any-builder', thetes: 'any-builder', metic: 'metic+archon', archon: 'archon' };
|
|
88
|
+
const floor = (catalog.byKey(atom) || {}).floor;
|
|
89
|
+
assert.equal(r.rank, FLOOR_LABEL[floor], `${r.key}: classifies to ${atom}'s floor label (${FLOOR_LABEL[floor]})`);
|
|
90
|
+
}
|
|
91
|
+
// The admin surface's own rule survives the generalization, stated directly:
|
|
92
|
+
// everything that is not a board route still gates on government.manage.
|
|
93
|
+
for (const r of classified.filter((x) => !x.key.includes('/board/'))) {
|
|
94
|
+
assert.deepEqual(r.perms, ['government.manage'], `${r.key}: the admin surface gates on the manage atom`);
|
|
73
95
|
}
|
|
74
96
|
});
|
|
75
97
|
|
|
@@ -78,8 +100,7 @@ test('every declared /governance route is pinned, and every pin exists (both dir
|
|
|
78
100
|
assert.ok(pins, 'MODULE_EXPECTED_RANKS has no government block');
|
|
79
101
|
const declared = new Set(classified.map((r) => r.key));
|
|
80
102
|
for (const key of declared) {
|
|
81
|
-
assert.
|
|
82
|
-
`${key} must be pinned to perm:government.manage`);
|
|
103
|
+
assert.ok(pins[key], `${key} must be pinned (atom equality is asserted above)`);
|
|
83
104
|
}
|
|
84
105
|
for (const key of Object.keys(pins)) {
|
|
85
106
|
assert.ok(declared.has(key), `pin ${key} names a route the file no longer declares`);
|