@bongos/core 1.19.185 → 1.19.187

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.
Files changed (58) hide show
  1. package/.bongos-core.json +90 -85
  2. package/clients/bongos-client/index.d.ts +2 -2
  3. package/config/government.neutral.json +6 -6
  4. package/docs/adr/0172-editable-rank-roles-substrate-axis.md +1 -1
  5. package/docs/api/openapi.json +12 -12
  6. package/docs/api-reference.md +7 -7
  7. package/modules/builder-settings/wandering-prefs.js +13 -13
  8. package/modules/government/catalog.js +79 -79
  9. package/modules/government/config.js +43 -33
  10. package/modules/government/db.js +58 -58
  11. package/modules/government/protected-surfaces.js +7 -7
  12. package/modules/government/protected-surfaces.json +4 -4
  13. package/modules/government/ranks.js +17 -17
  14. package/modules/government/resolver.js +24 -24
  15. package/modules/government/routes/government.js +88 -88
  16. package/modules/government/routes/rank-sync.js +8 -8
  17. package/modules/government/session-scopes.js +7 -7
  18. package/modules/government/session-scopes.json +5 -5
  19. package/modules/hall-ui/public/gate.js +2 -2
  20. package/modules/hall-ui/public/{governance.css → government.css} +11 -11
  21. package/modules/hall-ui/public/{governance.html → government.html} +14 -14
  22. package/modules/hall-ui/public/{governance.js → government.js} +108 -108
  23. package/modules/hall-ui/public/harbor.js +2 -2
  24. package/modules/hall-ui/public/shell.js +19 -15
  25. package/modules/hall-ui/public/watch.js +1 -1
  26. package/modules/lifecycle/claim-eligibility.js +1 -1
  27. package/modules/lifecycle/db.js +1 -1
  28. package/package-lock.json +2 -2
  29. package/package.json +1 -1
  30. package/scripts/gds/fitness.js +138 -0
  31. package/scripts/hall-preview/server.js +1 -1
  32. package/src/bongos/auth.js +11 -11
  33. package/src/bongos/serve-internal.js +8 -8
  34. package/src/module-api.js +5 -1
  35. package/tests/auth_page_gate.mjs +2 -2
  36. package/tests/auth_resolve_failure.mjs +4 -4
  37. package/tests/{governance_abuse_matrix.mjs → government_abuse_matrix.mjs} +39 -39
  38. package/tests/{governance_catalog.mjs → government_catalog.mjs} +11 -11
  39. package/tests/{governance_claim_eligibility.mjs → government_claim_eligibility.mjs} +2 -2
  40. package/tests/{governance_config.mjs → government_config.mjs} +54 -54
  41. package/tests/{governance_fold.mjs → government_fold.mjs} +6 -6
  42. package/tests/{governance_fold_bench.mjs → government_fold_bench.mjs} +3 -3
  43. package/tests/{governance_live_proof.mjs → government_live_proof.mjs} +5 -5
  44. package/tests/{governance_ownership_scope.mjs → government_ownership_scope.mjs} +16 -16
  45. package/tests/{governance_page_gates.mjs → government_page_gates.mjs} +14 -14
  46. package/tests/{governance_parity.mjs → government_parity.mjs} +3 -3
  47. package/tests/{governance_principals.mjs → government_principals.mjs} +14 -14
  48. package/tests/{governance_protected_surfaces.mjs → government_protected_surfaces.mjs} +1 -1
  49. package/tests/{governance_role_sync.mjs → government_rank_sync.mjs} +2 -2
  50. package/tests/{governance_require_permission.mjs → government_require_permission.mjs} +1 -1
  51. package/tests/{governance_resolver.mjs → government_resolver.mjs} +6 -6
  52. package/tests/{governance_routes.mjs → government_routes.mjs} +36 -36
  53. package/tests/{governance_seed.mjs → government_seed.mjs} +8 -8
  54. package/tests/{governance_session_scope.mjs → government_session_scope.mjs} +10 -10
  55. package/tests/{governance_vanilla_boot.mjs → government_vanilla_boot.mjs} +26 -26
  56. package/tests/government_vocabulary.mjs +94 -0
  57. package/tests/hall_nav.mjs +8 -7
  58. package/tests/hall_page_gate_map.mjs +9 -9
@@ -1,4 +1,4 @@
1
- // tests/governance_config.mjs — the governance config contract (BV1.R96, ADR 0151).
1
+ // tests/government_config.mjs — the governance config contract (BV1.R96, ADR 0151).
2
2
  //
3
3
  // DB-free: exercises the PURE resolver (all inputs injected) plus the disk loader
4
4
  // against the real neutral starter. The invariants that matter here are trust-boundary
@@ -18,7 +18,7 @@ const GOV = path.join(ROOT, 'modules', 'government');
18
18
  const config = require(path.join(GOV, 'config.js'));
19
19
  const catalog = require(path.join(GOV, 'catalog.js'));
20
20
  const { envPrefix } = require(path.join(ROOT, 'src', 'instance-config'));
21
- const { resolveGovernanceConfig, loadGovernanceConfig, grantableOnly, NEUTRAL_PATH, INSTANCE_PATH } = config;
21
+ const { resolveGovernmentConfig, loadGovernmentConfig, grantableOnly, NEUTRAL_PATH, INSTANCE_PATH } = config;
22
22
 
23
23
  const NEUTRAL = JSON.parse(fs.readFileSync(NEUTRAL_PATH, 'utf8'));
24
24
  // No env by default — every precedence test injects exactly the env it is asserting on.
@@ -27,73 +27,73 @@ const NO_ENV = {};
27
27
  // ── precedence: neutral -> instance -> env ──────────────────────────────────────
28
28
 
29
29
  test('neutral-only resolution yields sane defaults (the vanilla boot R102 proves)', () => {
30
- const out = resolveGovernanceConfig({ neutral: NEUTRAL, env: NO_ENV });
31
- assert.deepEqual(Object.keys(out.roles).sort(), [...catalog.RANK_ORDER].sort());
32
- assert.equal(out.roles.metic.label, 'Metic');
33
- assert.equal(out.roles.archon.seeded, true);
30
+ const out = resolveGovernmentConfig({ neutral: NEUTRAL, env: NO_ENV });
31
+ assert.deepEqual(Object.keys(out.ranks).sort(), [...catalog.RANK_ORDER].sort());
32
+ assert.equal(out.ranks.metic.label, 'Metic');
33
+ assert.equal(out.ranks.archon.seeded, true);
34
34
  for (const rank of catalog.RANK_ORDER) {
35
- assert.ok(out.roles[rank].description, `${rank} template must carry a description`);
35
+ assert.ok(out.ranks[rank].description, `${rank} template must carry a description`);
36
36
  }
37
37
  });
38
38
 
39
39
  test('an empty config still resolves the four rank-roles to their canonical names', () => {
40
40
  // governance_002 seeds them unconditionally, so a missing/mangled config must fall
41
41
  // back rather than drop a role out of the set.
42
- const out = resolveGovernanceConfig({ env: NO_ENV });
43
- assert.deepEqual(Object.keys(out.roles).sort(), [...catalog.RANK_ORDER].sort());
44
- assert.equal(out.roles.xenos.label, 'xenos');
42
+ const out = resolveGovernmentConfig({ env: NO_ENV });
43
+ assert.deepEqual(Object.keys(out.ranks).sort(), [...catalog.RANK_ORDER].sort());
44
+ assert.equal(out.ranks.xenos.label, 'xenos');
45
45
  });
46
46
 
47
47
  test('the instance file overrides the neutral starter, per role and per field', () => {
48
- const out = resolveGovernanceConfig({
48
+ const out = resolveGovernmentConfig({
49
49
  neutral: NEUTRAL,
50
- instance: { roles: { metic: { label: 'Trusted' } } },
50
+ instance: { ranks: { metic: { label: 'Trusted' } } },
51
51
  env: NO_ENV,
52
52
  });
53
- assert.equal(out.roles.metic.label, 'Trusted');
53
+ assert.equal(out.ranks.metic.label, 'Trusted');
54
54
  // The untouched field survives the merge, and so do the other three roles.
55
- assert.equal(out.roles.metic.description, NEUTRAL.roles.metic.description);
56
- assert.equal(out.roles.archon.label, 'Archon');
55
+ assert.equal(out.ranks.metic.description, NEUTRAL.ranks.metic.description);
56
+ assert.equal(out.ranks.archon.label, 'Archon');
57
57
  });
58
58
 
59
59
  test('the instance file may add a custom role template the neutral starter has not', () => {
60
- const out = resolveGovernanceConfig({
60
+ const out = resolveGovernmentConfig({
61
61
  neutral: NEUTRAL,
62
- instance: { roles: { triager: { label: 'Triager', permissions: ['idea.triage', 'blocker.review'] } } },
62
+ instance: { ranks: { triager: { label: 'Triager', permissions: ['idea.triage', 'blocker.review'] } } },
63
63
  env: NO_ENV,
64
64
  });
65
- assert.equal(out.roles.triager.seeded, false);
66
- assert.deepEqual(out.roles.triager.permissions, ['blocker.review', 'idea.triage']);
65
+ assert.equal(out.ranks.triager.seeded, false);
66
+ assert.deepEqual(out.ranks.triager.permissions, ['blocker.review', 'idea.triage']);
67
67
  });
68
68
 
69
69
  test('an env override beats both the instance file and the neutral starter', () => {
70
70
  const env = { [`${envPrefix()}_GOVERNANCE_ROLE_LABELS`]: 'metic=Crew,archon=Captain' };
71
- const out = resolveGovernanceConfig({
71
+ const out = resolveGovernmentConfig({
72
72
  neutral: NEUTRAL,
73
- instance: { roles: { metic: { label: 'Trusted' } } },
73
+ instance: { ranks: { metic: { label: 'Trusted' } } },
74
74
  env,
75
75
  });
76
- assert.equal(out.roles.metic.label, 'Crew');
77
- assert.equal(out.roles.archon.label, 'Captain');
78
- assert.equal(out.roles.xenos.label, 'Xenos', 'an unnamed role keeps its resolved label');
76
+ assert.equal(out.ranks.metic.label, 'Crew');
77
+ assert.equal(out.ranks.archon.label, 'Captain');
78
+ assert.equal(out.ranks.xenos.label, 'Xenos', 'an unnamed role keeps its resolved label');
79
79
  });
80
80
 
81
81
  test('the env layer can relabel a role but can never grant it a permission', () => {
82
82
  // The whole point of restricting env to labels: an env var must not widen authority.
83
83
  const env = { [`${envPrefix()}_GOVERNANCE_ROLE_LABELS`]: 'triager=Renamed' };
84
- const out = resolveGovernanceConfig({
84
+ const out = resolveGovernmentConfig({
85
85
  neutral: NEUTRAL,
86
- instance: { roles: { triager: { label: 'Triager', permissions: ['idea.triage'] } } },
86
+ instance: { ranks: { triager: { label: 'Triager', permissions: ['idea.triage'] } } },
87
87
  env,
88
88
  });
89
- assert.equal(out.roles.triager.label, 'Renamed');
90
- assert.deepEqual(out.roles.triager.permissions, ['idea.triage']);
89
+ assert.equal(out.ranks.triager.label, 'Renamed');
90
+ assert.deepEqual(out.ranks.triager.permissions, ['idea.triage']);
91
91
  });
92
92
 
93
93
  test('an env label naming a role no config defines is inert (a typo cannot grow the role set)', () => {
94
94
  const env = { [`${envPrefix()}_GOVERNANCE_ROLE_LABELS`]: 'metics=Typo' };
95
- const out = resolveGovernanceConfig({ neutral: NEUTRAL, env });
96
- assert.deepEqual(Object.keys(out.roles).sort(), [...catalog.RANK_ORDER].sort());
95
+ const out = resolveGovernmentConfig({ neutral: NEUTRAL, env });
96
+ assert.deepEqual(Object.keys(out.ranks).sort(), [...catalog.RANK_ORDER].sort());
97
97
  });
98
98
 
99
99
  // ── fail-closed: config is not a trust boundary ─────────────────────────────────
@@ -101,41 +101,41 @@ test('an env label naming a role no config defines is inert (a typo cannot grow
101
101
  test('a template naming a system permission is stripped (config cannot grant system power)', () => {
102
102
  const systemKeys = catalog.systemKeys();
103
103
  assert.ok(systemKeys.includes('deploy.push_main'), 'fixture guard: the catalog still flags deploy.push_main');
104
- const out = resolveGovernanceConfig({
104
+ const out = resolveGovernmentConfig({
105
105
  neutral: NEUTRAL,
106
- instance: { roles: { sneaky: { label: 'Sneaky', permissions: ['deploy.push_main', 'builder.rank.set', 'idea.triage'] } } },
106
+ instance: { ranks: { sneaky: { label: 'Sneaky', permissions: ['deploy.push_main', 'builder.rank.set', 'idea.triage'] } } },
107
107
  env: NO_ENV,
108
108
  });
109
- assert.deepEqual(out.roles.sneaky.permissions, ['idea.triage']);
109
+ assert.deepEqual(out.ranks.sneaky.permissions, ['idea.triage']);
110
110
  // Belt and braces: NO system key survives a template, whichever one is named.
111
- const all = resolveGovernanceConfig({
111
+ const all = resolveGovernmentConfig({
112
112
  neutral: NEUTRAL,
113
- instance: { roles: { everything: { permissions: catalog.permissionKeys() } } },
113
+ instance: { ranks: { everything: { permissions: catalog.permissionKeys() } } },
114
114
  env: NO_ENV,
115
115
  });
116
116
  for (const k of systemKeys) {
117
- assert.ok(!all.roles.everything.permissions.includes(k), `config must not be able to grant system permission "${k}"`);
117
+ assert.ok(!all.ranks.everything.permissions.includes(k), `config must not be able to grant system permission "${k}"`);
118
118
  }
119
- assert.deepEqual(all.roles.everything.permissions, [...catalog.grantableKeys()].sort());
119
+ assert.deepEqual(all.ranks.everything.permissions, [...catalog.grantableKeys()].sort());
120
120
  });
121
121
 
122
122
  test('a template naming an unknown permission key is dropped (the catalog is the truth)', () => {
123
- const out = resolveGovernanceConfig({
123
+ const out = resolveGovernmentConfig({
124
124
  neutral: NEUTRAL,
125
- instance: { roles: { odd: { permissions: ['idea.triage', 'not.a.real.permission', 'deploy.everything'] } } },
125
+ instance: { ranks: { odd: { permissions: ['idea.triage', 'not.a.real.permission', 'deploy.everything'] } } },
126
126
  env: NO_ENV,
127
127
  });
128
- assert.deepEqual(out.roles.odd.permissions, ['idea.triage']);
128
+ assert.deepEqual(out.ranks.odd.permissions, ['idea.triage']);
129
129
  });
130
130
 
131
131
  test('permissions on a SEEDED rank-role are dropped wholesale (grants derive from floors)', () => {
132
- const out = resolveGovernanceConfig({
132
+ const out = resolveGovernmentConfig({
133
133
  neutral: NEUTRAL,
134
- instance: { roles: { xenos: { label: 'Newcomer', permissions: ['task.create', 'audit_log.read'] } } },
134
+ instance: { ranks: { xenos: { label: 'Newcomer', permissions: ['task.create', 'audit_log.read'] } } },
135
135
  env: NO_ENV,
136
136
  });
137
- assert.equal(out.roles.xenos.label, 'Newcomer', 'a seeded role may be relabelled');
138
- assert.deepEqual(out.roles.xenos.permissions, [], 'a seeded role may never be re-granted from config');
137
+ assert.equal(out.ranks.xenos.label, 'Newcomer', 'a seeded role may be relabelled');
138
+ assert.deepEqual(out.ranks.xenos.permissions, [], 'a seeded role may never be re-granted from config');
139
139
  });
140
140
 
141
141
  test('grantableOnly de-duplicates, sorts, and rejects non-string entries', () => {
@@ -145,9 +145,9 @@ test('grantableOnly de-duplicates, sorts, and rejects non-string entries', () =>
145
145
  });
146
146
 
147
147
  test('the resolved config is frozen — a caller cannot mutate it into a grant', () => {
148
- const out = resolveGovernanceConfig({ neutral: NEUTRAL, env: NO_ENV });
149
- assert.ok(Object.isFrozen(out) && Object.isFrozen(out.roles) && Object.isFrozen(out.roles.metic));
150
- assert.throws(() => out.roles.metic.permissions.push('deploy.push_main'), TypeError);
148
+ const out = resolveGovernmentConfig({ neutral: NEUTRAL, env: NO_ENV });
149
+ assert.ok(Object.isFrozen(out) && Object.isFrozen(out.ranks) && Object.isFrozen(out.ranks.metic));
150
+ assert.throws(() => out.ranks.metic.permissions.push('deploy.push_main'), TypeError);
151
151
  });
152
152
 
153
153
  // ── the neutral starter itself ──────────────────────────────────────────────────
@@ -159,9 +159,9 @@ test('the neutral starter carries no host-specific identity (a vanilla instance
159
159
  const raw = fs.readFileSync(NEUTRAL_PATH, 'utf8');
160
160
  assert.deepEqual(scanPromptIdentity('config/government.neutral.json', raw, '.json'), []);
161
161
  // And it carries only templates — never a live per-builder assignment.
162
- assert.deepEqual(Object.keys(NEUTRAL).filter((k) => k !== '_note'), ['roles']);
163
- for (const role of Object.values(NEUTRAL.roles)) {
164
- assert.ok(!('permissions' in role), 'the neutral seeded roles carry labels only, never grants');
162
+ assert.deepEqual(Object.keys(NEUTRAL).filter((k) => k !== '_note'), ['ranks']);
163
+ for (const rank of Object.values(NEUTRAL.ranks)) {
164
+ assert.ok(!('permissions' in rank), 'the neutral seeded ranks carry labels only, never grants');
165
165
  }
166
166
  });
167
167
 
@@ -171,13 +171,13 @@ test('the two config paths are the branding-shaped neutral/instance pair under c
171
171
  assert.ok(fs.existsSync(NEUTRAL_PATH), 'the neutral starter must ship — it is the vanilla default');
172
172
  });
173
173
 
174
- test('loadGovernanceConfig reads the real neutral starter and tolerates a missing instance pack', () => {
174
+ test('loadGovernmentConfig reads the real neutral starter and tolerates a missing instance pack', () => {
175
175
  // A vanilla instance has no config/government.json at all — the default INSTANCE_PATH
176
176
  // is genuinely absent in this checkout, so the default call exercises that path.
177
177
  assert.equal(fs.existsSync(INSTANCE_PATH), false, 'fixture guard: this checkout is the vanilla case');
178
- const out = loadGovernanceConfig({ env: NO_ENV });
179
- assert.equal(out.roles.archon.label, 'Archon');
180
- assert.deepEqual(Object.keys(out.roles).sort(), [...catalog.RANK_ORDER].sort());
178
+ const out = loadGovernmentConfig({ env: NO_ENV });
179
+ assert.equal(out.ranks.archon.label, 'Archon');
180
+ assert.deepEqual(Object.keys(out.ranks).sort(), [...catalog.RANK_ORDER].sort());
181
181
  });
182
182
 
183
183
  test('the neutral starter publishes with the core (a packaged vanilla instance needs it)', () => {
@@ -1,4 +1,4 @@
1
- // tests/governance_fold.mjs — the session-load permission FOLD (task 1002556).
1
+ // tests/government_fold.mjs — the session-load permission FOLD (task 1002556).
2
2
  //
3
3
  // DB-free. requirePermission used to cost a second round-trip on every gated
4
4
  // request (requireBuilder loads the session, then resolveHeldPermissions hits
@@ -15,7 +15,7 @@
15
15
  // • fail-closed posture is untouched: a missing/malformed port still denies
16
16
  // even when raw keys are present (authority must be ESTABLISHED, ADR 0016).
17
17
  //
18
- // The real-DB / real-HTTP complement is tests/governance_live_proof.mjs, which
18
+ // The real-DB / real-HTTP complement is tests/government_live_proof.mjs, which
19
19
  // exercises the folded lookupSession end-to-end on a live stack.
20
20
  import assert from 'node:assert/strict';
21
21
  import { test } from 'node:test';
@@ -121,8 +121,8 @@ test('the kernel shape guard: the real fragment embeds; a malformed one degrades
121
121
  assert.equal(dbKernel.sessionPermsSelect(), '', 'a fragment with a statement separator is refused');
122
122
  // The REAL registered fragment passes the guard verbatim.
123
123
  seams._reset();
124
- const buildGovernanceRouter = require(path.join(ROOT, 'modules', 'government', 'routes', 'government.js'));
125
- buildGovernanceRouter();
124
+ const buildGovernmentRouter = require(path.join(ROOT, 'modules', 'government', 'routes', 'government.js'));
125
+ buildGovernmentRouter();
126
126
  const real = seams.resolveOptional('session-grants-sql').selectFragment;
127
127
  assert.equal(dbKernel.sessionPermsSelect(), real, 'the shipped fragment embeds unchanged');
128
128
  seams._reset();
@@ -133,8 +133,8 @@ test('the module registers the capability-named SQL seam the kernel embeds', ()
133
133
  // §Decision #4); the governance route factory is what registers it. Pin the
134
134
  // registration and the column contract the kernel query depends on.
135
135
  seams._reset();
136
- const buildGovernanceRouter = require(path.join(ROOT, 'modules', 'government', 'routes', 'government.js'));
137
- buildGovernanceRouter();
136
+ const buildGovernmentRouter = require(path.join(ROOT, 'modules', 'government', 'routes', 'government.js'));
137
+ buildGovernmentRouter();
138
138
  const provider = seams.resolveOptional('session-grants-sql');
139
139
  assert.ok(provider && typeof provider.selectFragment === 'string', 'fragment provider registered');
140
140
  assert.match(provider.selectFragment, /AS raw_permission_keys/, 'yields the column auth.js reads');
@@ -1,4 +1,4 @@
1
- // tests/governance_fold_bench.mjs — the task-1002556 benchmark, re-runnable.
1
+ // tests/government_fold_bench.mjs — the task-1002556 benchmark, re-runnable.
2
2
  //
3
3
  // Measures end-to-end latency of a requireBuilder + requirePermission route
4
4
  // (GET /government/ranks) against a LIVE server, the way the fold was graded:
@@ -19,7 +19,7 @@
19
19
  // set so the DB-free lane never runs it.
20
20
  //
21
21
  // Run: GDS_BASE_URL=http://localhost:3000 BENCH_TOKEN=<archon session token> \
22
- // node tests/governance_fold_bench.mjs
22
+ // node tests/government_fold_bench.mjs
23
23
  // BENCH_N (default 300) / BENCH_WARMUP (default 30) tune the sample.
24
24
 
25
25
  import { strict as assert } from 'node:assert';
@@ -27,7 +27,7 @@ import { strict as assert } from 'node:assert';
27
27
  const BASE = (process.env.GDS_BASE_URL || '').replace(/\/$/, '');
28
28
  const TOKEN = process.env.BENCH_TOKEN || '';
29
29
  if (!BASE || !TOKEN) {
30
- console.log('governance_fold_bench.mjs SKIPPED — set GDS_BASE_URL and BENCH_TOKEN (a session token whose builder holds government.manage).');
30
+ console.log('government_fold_bench.mjs SKIPPED — set GDS_BASE_URL and BENCH_TOKEN (a session token whose builder holds government.manage).');
31
31
  process.exit(0);
32
32
  }
33
33
  const N = Number(process.env.BENCH_N) || 300;
@@ -1,4 +1,4 @@
1
- // tests/governance_live_proof.mjs — the C6 governance LIVE proof (BV1.R114, ADR 0151).
1
+ // tests/government_live_proof.mjs — the C6 governance LIVE proof (BV1.R114, ADR 0151).
2
2
  //
3
3
  // C6's live-demonstration clause: an Archon creates a CUSTOM role granting an
4
4
  // OPERATIONAL permission, assigns it to a low-trust builder, and that builder can
@@ -7,7 +7,7 @@
7
7
  // substrate: the deploy/protected-path attempt is still blocked.
8
8
  //
9
9
  // This is the goal_wall_proof.mjs pattern: the abuse MATRIX
10
- // (tests/governance_abuse_matrix.mjs, R112) already proves every vector DB-free
10
+ // (tests/government_abuse_matrix.mjs, R112) already proves every vector DB-free
11
11
  // with stubs; THIS file proves the same story END-TO-END on the real stack —
12
12
  // real Postgres rows, real sessions, real HTTP through the real router, the real
13
13
  // resolver behind the real `governance` kernel port, no stubs, no monkeypatches.
@@ -18,7 +18,7 @@
18
18
  // and is listed in run-unit-tests.js's INTEGRATION set so the DB-free lane
19
19
  // never runs it.
20
20
  //
21
- // Run: DATABASE_URL=... node tests/governance_live_proof.mjs
21
+ // Run: DATABASE_URL=... node tests/government_live_proof.mjs
22
22
  // GDS_BASE_URL=http://localhost:3000 — optional: drive an already-running
23
23
  // sandbox server (sharing that DATABASE_URL) instead of booting the router
24
24
  // in-process. Same routes either way; this is how the live sandbox transcript
@@ -32,7 +32,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'test';
32
32
  const require = createRequire(import.meta.url);
33
33
 
34
34
  if (!process.env.DATABASE_URL) {
35
- console.log('governance_live_proof.mjs SKIPPED — DATABASE_URL not set.');
35
+ console.log('government_live_proof.mjs SKIPPED — DATABASE_URL not set.');
36
36
  process.exit(0);
37
37
  }
38
38
 
@@ -41,7 +41,7 @@ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
41
41
  try {
42
42
  await pool.query('SELECT 1 FROM governance_roles LIMIT 1');
43
43
  } catch (e) {
44
- console.log(`governance_live_proof.mjs SKIPPED — no migrated Postgres (${e.code || e.message}).`);
44
+ console.log(`government_live_proof.mjs SKIPPED — no migrated Postgres (${e.code || e.message}).`);
45
45
  await pool.end().catch(() => {});
46
46
  process.exit(0);
47
47
  }
@@ -1,4 +1,4 @@
1
- // tests/governance_ownership_scope.mjs — ownership-scoped permissions
1
+ // tests/government_ownership_scope.mjs — ownership-scoped permissions
2
2
  // ("act on OWN X") as a first-class axis (BV1.R100, ADR 0151 §3). DB-free.
3
3
  //
4
4
  // The thing being proved is a CONJUNCTION. `box.manage.own` is not a grant that
@@ -37,7 +37,7 @@ const db = require(path.join(GOV, 'db.js'));
37
37
  const auth = require(path.join(ROOT, 'src', 'bongos', 'auth.js'));
38
38
 
39
39
  const {
40
- PERMISSIONS, RANK_ORDER, RANK_ROLE_SEED, RESOURCE_FAMILIES, permissionKeys, byKey,
40
+ PERMISSIONS, RANK_ORDER, RANK_SEED, RESOURCE_FAMILIES, permissionKeys, byKey,
41
41
  scopeOf, resourceOf, isOwnershipScoped, isCrossOwner, ownershipScopedKeys, crossOwnerKeys,
42
42
  crossOwnerKeysFor, isPrincipalKey, seedForRank,
43
43
  } = catalog;
@@ -153,16 +153,16 @@ test('B1: for every paired family some rank reaches its OWN row but not everyone
153
153
  test('B2: the seed grants the own key to ranks it withholds the cross-owner key from', () => {
154
154
  for (const [resource, fam] of PAIRED) {
155
155
  for (const ownKey of fam.own) {
156
- const ownOnly = RANK_ORDER.filter((r) => RANK_ROLE_SEED[r].includes(ownKey) && !fam.any.some((a) => RANK_ROLE_SEED[r].includes(a)));
156
+ const ownOnly = RANK_ORDER.filter((r) => RANK_SEED[r].includes(ownKey) && !fam.any.some((a) => RANK_SEED[r].includes(a)));
157
157
  assert.ok(ownOnly.length > 0, `${resource}: no rank holds ${ownKey} without also holding ${fam.any.join('/')}`);
158
158
  }
159
159
  }
160
160
  // The concrete claim, spelled out: a xenos may act on their own claim and their
161
161
  // own box; they may not release another builder's claim or close another's box.
162
- assert.ok(RANK_ROLE_SEED.xenos.includes('task.act.own'));
163
- assert.ok(!RANK_ROLE_SEED.xenos.includes('claim.release.any'));
164
- assert.ok(RANK_ROLE_SEED.xenos.includes('box.manage.own'));
165
- assert.ok(!RANK_ROLE_SEED.xenos.includes('box.fleet.manage'));
162
+ assert.ok(RANK_SEED.xenos.includes('task.act.own'));
163
+ assert.ok(!RANK_SEED.xenos.includes('claim.release.any'));
164
+ assert.ok(RANK_SEED.xenos.includes('box.manage.own'));
165
+ assert.ok(!RANK_SEED.xenos.includes('box.fleet.manage'));
166
166
  });
167
167
 
168
168
  test('B3: cross-owner authority is never CHEAPER than own-only authority', () => {
@@ -372,13 +372,13 @@ test('F4: a cross-owner key with no ownership-scoped counterpart throws at modul
372
372
 
373
373
  test('G1: the seed is still floor-derived and cumulative — the ownership axis adds no grants', () => {
374
374
  for (const rank of RANK_ORDER) {
375
- const held = new Set(RANK_ROLE_SEED[rank]);
375
+ const held = new Set(RANK_SEED[rank]);
376
376
  const tier = RANK_ORDER.indexOf(rank);
377
377
  for (const p of PERMISSIONS) {
378
378
  assert.equal(held.has(p.key), p.floor !== null && RANK_ORDER.indexOf(p.floor) <= tier,
379
379
  `${rank} vs ${p.key}: scope must not touch the floor derivation`);
380
380
  }
381
- assert.deepEqual([...seedForRank(rank)].sort(), [...RANK_ROLE_SEED[rank]].sort());
381
+ assert.deepEqual([...seedForRank(rank)].sort(), [...RANK_SEED[rank]].sort());
382
382
  }
383
383
  });
384
384
 
@@ -397,14 +397,14 @@ test('G2: the role resolve is untouched — own-scoped keys still resolve, princ
397
397
  });
398
398
 
399
399
  test('G3: the kernel port carries the ownership axis on the ONE governance port', () => {
400
- const { governancePort } = require(path.join(GOV, 'routes', 'government.js'));
400
+ const { governmentPort } = require(path.join(GOV, 'routes', 'government.js'));
401
401
  for (const name of ['authorizeOwned', 'authorizeOwnedOrAny', 'sameOwner', 'isOwnershipScoped', 'crossOwnerKeysFor']) {
402
- assert.equal(typeof governancePort[name], 'function', `the port must expose ${name}`);
402
+ assert.equal(typeof governmentPort[name], 'function', `the port must expose ${name}`);
403
403
  }
404
404
  // still the same one object port the earlier slices registered
405
- assert.equal(typeof governancePort.resolveBuilderPermissions, 'function');
406
- assert.equal(typeof governancePort.resolvePrincipalPermissions, 'function');
407
- assert.equal(governancePort.isOwnershipScoped('box.manage.own'), true);
408
- assert.equal(governancePort.isOwnershipScoped('claim.release.any'), false);
409
- assert.equal(governancePort.isOwnershipScoped('nope.not.a.key'), false);
405
+ assert.equal(typeof governmentPort.resolveBuilderPermissions, 'function');
406
+ assert.equal(typeof governmentPort.resolvePrincipalPermissions, 'function');
407
+ assert.equal(governmentPort.isOwnershipScoped('box.manage.own'), true);
408
+ assert.equal(governmentPort.isOwnershipScoped('claim.release.any'), false);
409
+ assert.equal(governmentPort.isOwnershipScoped('nope.not.a.key'), false);
410
410
  });
@@ -1,4 +1,4 @@
1
- // tests/governance_page_gates.mjs — the three HTML PAGE GATES + the wandering
1
+ // tests/government_page_gates.mjs — the three HTML PAGE GATES + the wandering
2
2
  // CEILING, re-expressed on the governance model (BV1.R107, ADR 0151).
3
3
  //
4
4
  // DB-free. Two doubles, both installed the way the existing suites do it:
@@ -6,7 +6,7 @@
6
6
  // auth.js destructures it at load time (tests/auth_page_gate.mjs precedent).
7
7
  // • the `governance` KERNEL PORT is a seam stub, so the gates' authority half
8
8
  // needs no Postgres, no router, no live module
9
- // (tests/governance_require_permission.mjs precedent).
9
+ // (tests/government_require_permission.mjs precedent).
10
10
  //
11
11
  // WHAT THIS FILE HAS TO PROVE, and why the oracles are split.
12
12
  // A page gate that resolves its audience from catalog.floor cannot be checked
@@ -26,7 +26,7 @@
26
26
  // installed on the response double precisely so a regression to JSON shows up
27
27
  // as an assertion rather than a TypeError.
28
28
  //
29
- // Run: node --test tests/governance_page_gates.mjs
29
+ // Run: node --test tests/government_page_gates.mjs
30
30
 
31
31
  import assert from 'node:assert/strict';
32
32
  import { test } from 'node:test';
@@ -50,13 +50,13 @@ const resolver = require('../modules/government/resolver.js');
50
50
  const wandering = require('../modules/builder-settings/wandering-prefs.js');
51
51
  const { attachFail } = require('../src/bongos/middleware/error-envelope.js');
52
52
 
53
- const GATES = ['requireBuilderPage', 'requireNonXenosPage', 'requireGovernancePage'];
53
+ const GATES = ['requireBuilderPage', 'requireNonXenosPage', 'requireGovernmentPage'];
54
54
  const LIVE_RANKS = ['xenos', 'thetes', 'metic', 'archon'];
55
55
 
56
56
  // ═══ the ORACLE — hand-written, from the rank tests R107 deleted ═════════════
57
57
  // requireBuilderPage admitted any session at all → every live rank
58
58
  // requireNonXenosPage admitted rank !== 'xenos' → thetes and up
59
- // requireGovernancePage admitted rank === 'archon' → archon only …until ADR 0157
59
+ // requireGovernmentPage admitted rank === 'archon' → archon only …until ADR 0157
60
60
  //
61
61
  // ADR 0157 (owner decision, 2026-08-05) lowered `page.view.government` archon→metic, so
62
62
  // the monitoring pages (/watch, /harbor, /gate) now open to the working rank. That gate
@@ -71,7 +71,7 @@ const LIVE_RANKS = ['xenos', 'thetes', 'metic', 'archon'];
71
71
  const PRE_R107_AUDIENCE = {
72
72
  requireBuilderPage: { xenos: true, thetes: true, metic: true, archon: true },
73
73
  requireNonXenosPage: { xenos: false, thetes: true, metic: true, archon: true },
74
- requireGovernancePage: { xenos: false, thetes: false, metic: true, archon: true },
74
+ requireGovernmentPage: { xenos: false, thetes: false, metic: true, archon: true },
75
75
  };
76
76
  // Where a SIGNED-IN builder who fails the gate is sent. Never the sign-in flow —
77
77
  // they already have a session, so that would loop. Since task 1002613 (map N-1)
@@ -81,12 +81,12 @@ const PRE_R107_AUDIENCE = {
81
81
  const DENIED_TO = {
82
82
  requireBuilderPage: '/builders',
83
83
  requireNonXenosPage: '/builders/not-ready',
84
- requireGovernancePage: '/builders',
84
+ requireGovernmentPage: '/builders',
85
85
  };
86
86
  const GATE_PERMISSION = {
87
87
  requireBuilderPage: 'page.view.public',
88
88
  requireNonXenosPage: 'page.view.builder',
89
- requireGovernancePage: 'page.view.government',
89
+ requireGovernmentPage: 'page.view.government',
90
90
  };
91
91
  const deniedLoc = (gate, { unavailable = false } = {}) =>
92
92
  `${DENIED_TO[gate]}?denied=${GATE_PERMISSION[gate]}${unavailable ? '&reason=unavailable' : ''}`;
@@ -95,7 +95,7 @@ const GATE_FLOOR = {
95
95
  requireBuilderPage: 'xenos',
96
96
  requireNonXenosPage: 'thetes',
97
97
  // ADR 0157: `page.view.government` floor archon→metic.
98
- requireGovernancePage: 'metic',
98
+ requireGovernmentPage: 'metic',
99
99
  };
100
100
  const SIGN_IN = '/api/gds/auth/web/start';
101
101
 
@@ -249,16 +249,16 @@ test('B4: the page gates resolve per request — no memoised authority (ADR 0016
249
249
  });
250
250
  const req = { headers: { cookie: 'gds_session=validtoken' } };
251
251
  let admitted = false;
252
- await auth.requireGovernancePage(req, fakeRes(), () => { admitted = true; });
252
+ await auth.requireGovernmentPage(req, fakeRes(), () => { admitted = true; });
253
253
  assert.equal(admitted, true, 'an Archon should reach the page');
254
254
  keys = []; // the role is revoked between requests
255
255
  const second = fakeRes();
256
256
  let admittedAgain = false;
257
- await auth.requireGovernancePage(req, second, () => { admittedAgain = true; });
257
+ await auth.requireGovernmentPage(req, second, () => { admittedAgain = true; });
258
258
  seams._reset();
259
259
  assert.equal(calls, 2, 'the resolver must be consulted on every request');
260
260
  assert.equal(admittedAgain, false, 'a revoked role must deny on the very next request');
261
- assert.equal(second._redirect.loc, deniedLoc('requireGovernancePage'));
261
+ assert.equal(second._redirect.loc, deniedLoc('requireGovernmentPage'));
262
262
  });
263
263
 
264
264
  test('B5: every permission a page gate names is a real catalog key', async () => {
@@ -286,7 +286,7 @@ test('C1: the per-role wandering default + ceiling are unchanged', () => {
286
286
  assert.equal(wandering.defaultLevelForRank(role), expected.default, `${role} default`);
287
287
  assert.equal(wandering.maxLevelForRank(role), expected.ceiling, `${role} ceiling`);
288
288
  assert.deepEqual(
289
- { default: wandering.roleWandering(role).default, ceiling: wandering.roleWandering(role).ceiling },
289
+ { default: wandering.rankWandering(role).default, ceiling: wandering.rankWandering(role).ceiling },
290
290
  expected,
291
291
  `${role} role metadata`,
292
292
  );
@@ -339,7 +339,7 @@ test('C4: the ceiling is NOT answerable through the boolean permission axis', ()
339
339
  });
340
340
 
341
341
  test('C5: the derived rank projections cannot drift from the role metadata', () => {
342
- for (const [role, meta] of Object.entries(wandering.ROLE_WANDERING)) {
342
+ for (const [role, meta] of Object.entries(wandering.RANK_WANDERING)) {
343
343
  assert.equal(wandering.RANK_DEFAULT[role], meta.default, `RANK_DEFAULT.${role}`);
344
344
  assert.equal(wandering.RANK_MAX[role], meta.ceiling, `RANK_MAX.${role}`);
345
345
  assert.ok(meta.default <= meta.ceiling, `${role}: default must not exceed the ceiling`);
@@ -1,4 +1,4 @@
1
- // tests/governance_parity.mjs — the compat-seed PARITY proof (BV1.R103, ADR 0151).
1
+ // tests/government_parity.mjs — the compat-seed PARITY proof (BV1.R103, ADR 0151).
2
2
  //
3
3
  // The promise the whole governance cutover rests on: once governance_002 seeds the
4
4
  // four rank-roles and backfills every builder the role matching their rank, each
@@ -24,10 +24,10 @@
24
24
  // R104-R107 migration shifts coverage between oracles instead of erasing it.
25
25
  // PART C — the two REAL middlewares, requirePermission vs requireRank, over the
26
26
  // whole catalog rather than the four hand-picked keys in
27
- // tests/governance_require_permission.mjs.
27
+ // tests/government_require_permission.mjs.
28
28
  // PART D — the deliberate fail-closed divergence governance_002 documents.
29
29
  //
30
- // A third oracle already exists and is NOT duplicated here: tests/governance_seed.mjs
30
+ // A third oracle already exists and is NOT duplicated here: tests/government_seed.mjs
31
31
  // pins the catalog against the materialised migration SQL, so a floor edit that
32
32
  // reaches no new migration reds there.
33
33
  //
@@ -1,4 +1,4 @@
1
- // tests/governance_principals.mjs — machine principals as a first-class type
1
+ // tests/government_principals.mjs — machine principals as a first-class type
2
2
  // (BV1.R98, ADR 0151 §3). DB-free.
3
3
  //
4
4
  // A builder's authority is the UNION of their ROLES. A machine principal's authority
@@ -26,7 +26,7 @@ const GOV = path.join(ROOT, 'modules', 'government');
26
26
  const catalog = require(path.join(GOV, 'catalog.js'));
27
27
  const resolver = require(path.join(GOV, 'resolver.js'));
28
28
 
29
- const { PERMISSIONS, RANK_ORDER, RANK_ROLE_SEED, PRINCIPALS, principalKeys, principalRoles, isPrincipalKey, principalByRole, seedForRank, isGrantable, isSystem, systemKeys, grantableKeys, permissionKeys } = catalog;
29
+ const { PERMISSIONS, RANK_ORDER, RANK_SEED, PRINCIPALS, principalKeys, principalRoles, isPrincipalKey, principalByRole, seedForRank, isGrantable, isSystem, systemKeys, grantableKeys, permissionKeys } = catalog;
30
30
  const { effectivePermissions, resolvePrincipalPermissions, principalHasPermission } = resolver;
31
31
 
32
32
  // ── the catalog side: principals are a distinct, well-formed type ───────────────
@@ -71,7 +71,7 @@ test('isPrincipalKey separates the axes; an unknown key fails closed', () => {
71
71
 
72
72
  test('no rank-role holds a principal permission — an Archon must not hold principal.bfg', () => {
73
73
  for (const rank of RANK_ORDER) {
74
- const held = new Set(RANK_ROLE_SEED[rank]);
74
+ const held = new Set(RANK_SEED[rank]);
75
75
  for (const key of principalKeys()) {
76
76
  assert.ok(!held.has(key), `the ${rank} rank-role must not hold ${key}`);
77
77
  }
@@ -81,7 +81,7 @@ test('no rank-role holds a principal permission — an Archon must not hold prin
81
81
  assert.ok(!derived.has(key), `seedForRank('${rank}') must not derive ${key}`);
82
82
  }
83
83
  }
84
- assert.ok(!RANK_ROLE_SEED.archon.includes('principal.bfg'),
84
+ assert.ok(!RANK_SEED.archon.includes('principal.bfg'),
85
85
  'the top rank is the case that matters: identity is not seniority');
86
86
  });
87
87
 
@@ -149,10 +149,10 @@ test('resolved principal sets are frozen — a caller cannot widen one', () => {
149
149
  // ── show, never grant ───────────────────────────────────────────────────────────
150
150
 
151
151
  test('the tab surface is a read-only projection: shown, flagged non-grantable', () => {
152
- const { governancePort } = require(path.join(GOV, 'routes', 'government.js'));
153
- assert.equal(typeof governancePort.listPrincipals, 'function');
154
- assert.equal(typeof governancePort.resolvePrincipalPermissions, 'function');
155
- const listed = governancePort.listPrincipals();
152
+ const { governmentPort } = require(path.join(GOV, 'routes', 'government.js'));
153
+ assert.equal(typeof governmentPort.listPrincipals, 'function');
154
+ assert.equal(typeof governmentPort.resolvePrincipalPermissions, 'function');
155
+ const listed = governmentPort.listPrincipals();
156
156
  assert.deepEqual(listed.map((p) => p.system_role).sort(), ['bfg', 'discord_bot']);
157
157
  for (const p of listed) {
158
158
  assert.equal(p.grantable, false, `${p.system_role} must be rendered non-grantable`);
@@ -162,9 +162,9 @@ test('the tab surface is a read-only projection: shown, flagged non-grantable',
162
162
  });
163
163
 
164
164
  test('the governance port exposes no way to create, grant, or assign a principal', () => {
165
- const { governancePort } = require(path.join(GOV, 'routes', 'government.js'));
165
+ const { governmentPort } = require(path.join(GOV, 'routes', 'government.js'));
166
166
  const mutator = /(create|grant|assign|add|set|write|provision|revoke|delete|update)/i;
167
- for (const name of Object.keys(governancePort)) {
167
+ for (const name of Object.keys(governmentPort)) {
168
168
  if (!/principal/i.test(name)) continue;
169
169
  assert.ok(!mutator.test(name), `port method "${name}" looks like a principal mutator — principals are provisioned out-of-band only`);
170
170
  }
@@ -208,14 +208,14 @@ test('the catalog models exactly the principals the substrate can provision', ()
208
208
 
209
209
  test('the rank-role seed is still cumulative and floor-derived (R94 invariants hold)', () => {
210
210
  for (let i = 1; i < RANK_ORDER.length; i++) {
211
- const lower = new Set(RANK_ROLE_SEED[RANK_ORDER[i - 1]]);
212
- for (const k of RANK_ROLE_SEED[RANK_ORDER[i]]) assert.ok(typeof k === 'string');
211
+ const lower = new Set(RANK_SEED[RANK_ORDER[i - 1]]);
212
+ for (const k of RANK_SEED[RANK_ORDER[i]]) assert.ok(typeof k === 'string');
213
213
  for (const k of lower) {
214
- assert.ok(RANK_ROLE_SEED[RANK_ORDER[i]].includes(k), `${RANK_ORDER[i]} lost ${k}`);
214
+ assert.ok(RANK_SEED[RANK_ORDER[i]].includes(k), `${RANK_ORDER[i]} lost ${k}`);
215
215
  }
216
216
  }
217
217
  for (const rank of RANK_ORDER) {
218
- const held = new Set(RANK_ROLE_SEED[rank]);
218
+ const held = new Set(RANK_SEED[rank]);
219
219
  const tier = RANK_ORDER.indexOf(rank);
220
220
  for (const p of PERMISSIONS) {
221
221
  assert.equal(held.has(p.key), p.floor !== null && RANK_ORDER.indexOf(p.floor) <= tier,
@@ -1,4 +1,4 @@
1
- // tests/governance_protected_surfaces.mjs — the protected-surface registry
1
+ // tests/government_protected_surfaces.mjs — the protected-surface registry
2
2
  // (BV1.R101, ADR 0151 §4).
3
3
  //
4
4
  // DB-free. The registry is the data the OUT-OF-BAND substrate reads (a git
@@ -1,4 +1,4 @@
1
- // tests/governance_role_sync.mjs — rank→role assignment stays in sync (BV1.R95b, ADR 0151).
1
+ // tests/government_rank_sync.mjs — rank→role assignment stays in sync (BV1.R95b, ADR 0151).
2
2
  //
3
3
  // DB-free. The decision the writer executes is a pure function (modules/government/
4
4
  // ranks.js planRankSync), so these exercise PRODUCTION logic directly rather
@@ -214,7 +214,7 @@ test('the repair migration removes stale seeded roles and is idempotent', () =>
214
214
  });
215
215
 
216
216
  test('the repair migration touches ASSIGNMENTS, never GRANTS', () => {
217
- // tests/governance_seed.mjs builds RANK_ROLE_SEED as the UNION of every grant
217
+ // tests/government_seed.mjs builds RANK_SEED as the UNION of every grant
218
218
  // migration and fails loudly if one ever revokes. This migration must stay out of
219
219
  // that model entirely: it never writes governance_role_permissions.
220
220
  const code = stripSqlComments(repairSql);
@@ -1,4 +1,4 @@
1
- // tests/governance_require_permission.mjs — the requirePermission gate (BV1.R97, ADR 0151).
1
+ // tests/government_require_permission.mjs — the requirePermission gate (BV1.R97, ADR 0151).
2
2
  //
3
3
  // DB-free. The gate reaches the resolver through the `governance` KERNEL PORT
4
4
  // (the one-way rule — core never requires modules/government/), so a stub