@bongos/core 1.24.72 → 1.24.74
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 +107 -77
- 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 +2 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +36 -2
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +5 -5
- package/docs/copy-registry.json +5 -5
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +33 -0
- package/docs/page-readings.json +33 -33
- package/modules/government/board.js +19 -7
- package/modules/government/config.js +21 -9
- package/modules/government/constitution.js +224 -0
- package/modules/government/db.js +28 -0
- package/modules/government/migrations/government_037_constitution.sql +33 -0
- package/modules/government/module.json +2 -1
- package/modules/government/pollers/constitution-restore.js +45 -0
- package/modules/government/routes/government.js +7 -7
- package/modules/hall-ui/public/ai-studio.html +2 -1
- package/modules/hall-ui/public/atlas.html +2 -1
- package/modules/hall-ui/public/blockers.html +2 -1
- package/modules/hall-ui/public/board-room.html +2 -1
- package/modules/hall-ui/public/board-room.js +7 -3
- package/modules/hall-ui/public/city.html +2 -1
- package/modules/hall-ui/public/collab.html +2 -1
- package/modules/hall-ui/public/commands.html +2 -1
- package/modules/hall-ui/public/copy-desk.html +2 -1
- package/modules/hall-ui/public/deploy.html +2 -1
- package/modules/hall-ui/public/goal-map.html +2 -1
- package/modules/hall-ui/public/goals.html +2 -1
- package/modules/hall-ui/public/government.html +2 -1
- package/modules/hall-ui/public/idea.html +2 -1
- package/modules/hall-ui/public/ideas.html +2 -1
- package/modules/hall-ui/public/index.html +2 -1
- package/modules/hall-ui/public/market-watch-item.html +2 -1
- package/modules/hall-ui/public/market-watch-rules.html +2 -1
- package/modules/hall-ui/public/market-watch.html +2 -1
- package/modules/hall-ui/public/modules.html +2 -1
- package/modules/hall-ui/public/money.html +2 -1
- package/modules/hall-ui/public/my-agents.html +2 -1
- package/modules/hall-ui/public/primer.html +2 -1
- package/modules/hall-ui/public/profile.html +2 -1
- package/modules/hall-ui/public/project-settings.html +2 -1
- package/modules/hall-ui/public/roster.html +2 -1
- package/modules/hall-ui/public/settings.html +2 -1
- package/modules/hall-ui/public/shell-game.js +50 -0
- package/modules/hall-ui/public/shell.js +11 -11
- package/modules/hall-ui/public/street-card.js +2 -0
- package/modules/hall-ui/public/street.html +2 -1
- package/modules/hall-ui/public/studio.html +2 -1
- package/modules/hall-ui/public/task.html +2 -1
- package/modules/hall-ui/public/thinking.html +2 -1
- package/modules/hall-ui/public/tweak-editor.html +2 -1
- package/modules/hall-ui/public/watch.html +2 -1
- package/modules/hall-ui/public/work.html +2 -1
- package/modules/hall-ui/records/game-mode.md +20 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/government-walk.js +7 -0
- package/src/bongos/route-rank-check.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/genesis_government.mjs +11 -0
- package/tests/goal_map_page.mjs +2 -2
- package/tests/government_board_amendment.mjs +10 -0
- package/tests/government_charter_forms.mjs +10 -0
- package/tests/government_constitution_durable.mjs +304 -0
- package/tests/government_seed.mjs +1 -0
- package/tests/hall_city.mjs +1 -1
- package/tests/hall_fullscreen.mjs +101 -0
- package/tests/hall_my_agents.mjs +1 -1
- package/tests/hall_street.mjs +1 -1
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -577,11 +577,11 @@
|
|
|
577
577
|
|
|
578
578
|
// ---------- full screen (task 1004327; standard since task 1004430) ----------
|
|
579
579
|
// One switch on EVERY shelled page hides the sidebar, top bar and foot; the same
|
|
580
|
-
// button
|
|
581
|
-
//
|
|
582
|
-
//
|
|
583
|
-
//
|
|
584
|
-
|
|
580
|
+
// button and Escape bring them back. Opt out with <body data-fullscreen-switch="off">;
|
|
581
|
+
// the bare attribute means the page lays itself out for it, else it is `plain`.
|
|
582
|
+
// State: html[data-fullscreen="on"], remembered PER PAGE (`hall-fullscreen:<data-page>`).
|
|
583
|
+
// task 1005142: in game mode (shell-game.js) EVERY page opens in it, whatever it remembers.
|
|
584
|
+
var GAME = window.OTBShellGame || { on: function () { return false; }, hint: function () { return true; } };
|
|
585
585
|
var FULLSCREEN_KEY = 'hall-fullscreen:';
|
|
586
586
|
function fullscreenAttr() {
|
|
587
587
|
var b = document.body;
|
|
@@ -615,7 +615,7 @@
|
|
|
615
615
|
}
|
|
616
616
|
function storedFullscreen() {
|
|
617
617
|
var page = fullscreenPage();
|
|
618
|
-
if (!page) return
|
|
618
|
+
if (!page || GAME.on()) return !!page;
|
|
619
619
|
try { return window.localStorage.getItem(FULLSCREEN_KEY + page) === 'on'; } catch (_) { return false; }
|
|
620
620
|
}
|
|
621
621
|
function isFullscreen() { return document.documentElement.dataset.fullscreen === 'on'; }
|
|
@@ -628,7 +628,7 @@
|
|
|
628
628
|
var root = document.documentElement;
|
|
629
629
|
if (on) root.dataset.fullscreen = 'on'; else delete root.dataset.fullscreen;
|
|
630
630
|
if (on && fullscreenAttr() === null) root.dataset.fullscreenPlain = 'on'; else delete root.dataset.fullscreenPlain;
|
|
631
|
-
if (on)
|
|
631
|
+
if (!on) hideFullscreenHint(); else if (GAME.hint()) showFullscreenHint();
|
|
632
632
|
var btn = document.getElementById('fullscreen-toggle');
|
|
633
633
|
if (btn) {
|
|
634
634
|
var label = on ? 'Exit full screen (Esc)' : 'Full screen — hide the sidebar and top bar';
|
|
@@ -646,10 +646,10 @@
|
|
|
646
646
|
function setFullscreen(on) {
|
|
647
647
|
var page = fullscreenPage();
|
|
648
648
|
if (!page) return;
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
//
|
|
649
|
+
// Game mode never rewrites a page's own memory; `null` re-reads what should show.
|
|
650
|
+
if (on != null && !GAME.on()) try { window.localStorage.setItem(FULLSCREEN_KEY + page, on ? 'on' : 'off'); } catch (_) { /* private mode */ }
|
|
651
|
+
applyFullscreen(on == null ? storedFullscreen() : on);
|
|
652
|
+
// A page that sizes itself to its box (the sky's canvas) re-measures on resize.
|
|
653
653
|
try { window.dispatchEvent(new Event('resize')); } catch (_) { /* old engine */ }
|
|
654
654
|
}
|
|
655
655
|
function inTextField(el) {
|
|
@@ -801,6 +801,8 @@
|
|
|
801
801
|
return { toast: 'Sent to the council. It is carried out when they pass it.', building: b, kind: 'light', close: false };
|
|
802
802
|
}
|
|
803
803
|
if (key === 'for') {
|
|
804
|
+
// Task 1005140: never say "in force" about a change that did not take.
|
|
805
|
+
if (r.item_closed && r.outcome === 'passed' && r.constitution_not_applied) return { toast: 'Passed, but it did not take effect. The old rules are still in force.', building: b, kind: 'settle', close: false };
|
|
804
806
|
if (r.item_closed && r.outcome === 'passed') return { toast: 'Passed. It is in force from today.', building: b, kind: 'rise', close: false };
|
|
805
807
|
if (r.item_closed) return { toast: 'The sitting closed: it went back to its author.', building: b, kind: 'settle', close: false };
|
|
806
808
|
return { toast: r.already_voted ? 'Your vote was already on the record.' : 'Your yes is on the record.', building: b, kind: 'light', close: false };
|
|
@@ -229,9 +229,10 @@
|
|
|
229
229
|
</div>
|
|
230
230
|
|
|
231
231
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
232
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
232
233
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
233
234
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
234
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
235
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
235
236
|
<script src="/builders/hall-dialog.js"></script>
|
|
236
237
|
<script src="/builders/palette.js" defer></script>
|
|
237
238
|
<script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
|
|
@@ -222,9 +222,10 @@
|
|
|
222
222
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
223
223
|
|
|
224
224
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
225
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
225
226
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
226
227
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
227
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
228
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
228
229
|
<script src="/builders/hall-dialog.js"></script>
|
|
229
230
|
<script src="/builders/palette.js" defer></script>
|
|
230
231
|
<!-- The kit, for its ONE renderer: the full-size ship-time visual figure
|
|
@@ -57,9 +57,10 @@
|
|
|
57
57
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
58
58
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
59
59
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
60
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
60
61
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
61
62
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
62
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
63
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
63
64
|
<script src="/builders/hall-dialog.js"></script>
|
|
64
65
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
65
66
|
<script src="/builders/palette.js" defer></script>
|
|
@@ -139,9 +139,10 @@
|
|
|
139
139
|
</div>
|
|
140
140
|
|
|
141
141
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
142
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
142
143
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
143
144
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
144
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
145
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
145
146
|
<script src="/builders/hall-dialog.js"></script>
|
|
146
147
|
<script src="/builders/palette.js" defer></script>
|
|
147
148
|
|
|
@@ -143,9 +143,10 @@
|
|
|
143
143
|
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
144
144
|
|
|
145
145
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
146
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
146
147
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
147
148
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
148
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
149
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
149
150
|
<script src="/builders/hall-dialog.js"></script>
|
|
150
151
|
<script src="/builders/palette.js" defer></script>
|
|
151
152
|
<script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
|
|
@@ -269,9 +269,10 @@
|
|
|
269
269
|
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
270
270
|
chrome exists before the page script runs. -->
|
|
271
271
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
272
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
272
273
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
273
274
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
274
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
275
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
275
276
|
<script src="/builders/hall-dialog.js"></script>
|
|
276
277
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
277
278
|
<script src="/builders/palette.js" defer></script>
|
|
@@ -172,9 +172,10 @@
|
|
|
172
172
|
<!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
|
|
173
173
|
time so the chrome exists before the deferred page scripts execute. -->
|
|
174
174
|
<script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
|
|
175
|
+
<script src="/builders/shell-game.js?v=2026-11-09-game-mode"></script>
|
|
175
176
|
<script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
|
|
176
177
|
<script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
|
|
177
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
178
|
+
<script src="/builders/shell.js?v=2026-11-09-game-mode"></script>
|
|
178
179
|
<script src="/builders/hall-dialog.js"></script>
|
|
179
180
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
180
181
|
<script src="/builders/palette.js" defer></script>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Game mode — every hall page opens without its bars (task 1005142, BONGOS-V3 R02)
|
|
2
|
+
|
|
3
|
+
Covers: every shelled hall page (`shell-game.js`, `shell.js`). Spec: `docs/specs/bongos-v3-game-conversion.md` §Reuse, do not rebuild. The switch itself is in `hall-navigation.md`.
|
|
4
|
+
|
|
5
|
+
## What it settled
|
|
6
|
+
|
|
7
|
+
- **Game mode is the existing full-screen switch, made hall-wide. There is no second full-screen mode.** The switch was already on every page (task 1004430) but remembered per page, so a reader had to turn it on 33 times. Game mode is one `localStorage` key, `hall-game`; while it is `on`, `shell.js` `storedFullscreen()` answers yes for every page, whatever that page remembers. What hides, the corner button and the Escape hand-off are the shell's, unchanged.
|
|
8
|
+
- **`shell-game.js` holds the mode and nothing else**: `window.OTBShellGame` = `on()`, `set(bool)`, `hint()`. It stamps `html[data-game="on"]` for a sheet or script that wants to read the mode. `shell.js` reads it through a fallback object, so a page without the file is never in game mode. It is its own file because `shell.js` sits on the 1,500-line ratchet; the start screen and options menu (R03, R04) are expected to grow here, not in `shell.js`.
|
|
9
|
+
- **Load order**: `shell-game.js` before `shell-trail.js` / `craft-mode.js` / `shell.js`, loaded the same way (deferred or not) as the shell on that page. Test-pinned.
|
|
10
|
+
- **Leaving is for this page load only.** In game mode the corner button and Escape bring the bars back, and nothing is written: the next page opens without them again, and a page's own `hall-fullscreen:<page>` memory is never overwritten. Turning game mode off (`set(false)`) hands each page back what it remembered. `OTBShell.setFullscreen(null)` is the "re-read what should show" call `set()` uses.
|
|
11
|
+
- **No page may opt out.** `<body data-fullscreen-switch="off">` would keep that page's sidebar in game mode; the test fails on one. A new hall page needs `#app-topbar` and the `shell-game.js` tag, like the rest.
|
|
12
|
+
- **The "how to leave" line shows once per tab in game mode** (`sessionStorage 'hall-game-hint'`), not on every page load. Outside game mode the shell's rule is unchanged.
|
|
13
|
+
- **How it is turned on today**: `?game=on` / `?game=off` on any hall URL, or `OTBShellGame.set()`. Nothing in the hall's interface sets it yet; the start screen (R03) is where that lands.
|
|
14
|
+
|
|
15
|
+
## For the tasks after this one
|
|
16
|
+
|
|
17
|
+
- A HUD or options menu that wants Escape must call `preventDefault()` on the press. `shell.js` `onFullscreenKey` waits one tick before leaving so a listener added after it still wins.
|
|
18
|
+
- Until the options menu exists, Escape is the only way to reach navigation in game mode. Do not remove it before R04 lands.
|
|
19
|
+
|
|
20
|
+
Tests: `tests/hall_fullscreen.mjs` (the eight `game mode:` cases run the real `shell-game.js` + `shell.js`), and the script lists in `tests/hall_city.mjs`, `tests/hall_street.mjs`, `tests/hall_my_agents.mjs`, `tests/goal_map_page.mjs`.
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.74",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.74",
|
|
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.24.
|
|
3
|
+
"version": "1.24.74",
|
|
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
|
@@ -11457,5 +11457,17 @@
|
|
|
11457
11457
|
"id": "1005103",
|
|
11458
11458
|
"text": "The current step number in the project-setup steps is now dark ink on the orange dot, so it's readable in light mode too."
|
|
11459
11459
|
}
|
|
11460
|
+
],
|
|
11461
|
+
"1.24.73": [
|
|
11462
|
+
{
|
|
11463
|
+
"id": "1005140",
|
|
11464
|
+
"text": "When a project's board votes in a new form of government, that choice now stays in force after the server restarts. Before, it was quietly lost on every restart and the project fell back to the original one-person rule. A proj"
|
|
11465
|
+
}
|
|
11466
|
+
],
|
|
11467
|
+
"1.24.74": [
|
|
11468
|
+
{
|
|
11469
|
+
"id": "1005142",
|
|
11470
|
+
"text": "The hall has a game mode: once it is on, every page opens full screen with no sidebar, and Escape brings the menus back when you need them."
|
|
11471
|
+
}
|
|
11460
11472
|
]
|
|
11461
11473
|
}
|
|
@@ -169,6 +169,7 @@ function makeStore(catalog) {
|
|
|
169
169
|
items: [],
|
|
170
170
|
votes: [],
|
|
171
171
|
amendments: new Map(),
|
|
172
|
+
constitutionRow: null,
|
|
172
173
|
matters: new Map(),
|
|
173
174
|
leads: [], // { id, building, builder_id, charter, appointed_by_item_id, removed_by_item_id, removed_at }
|
|
174
175
|
laws: [], // { id, text, effect_on_ai, locks, proposed_by, passed_by_item_id, passed_at, repealed_by_item_id, repealed_at }
|
|
@@ -298,6 +299,12 @@ function makeStore(catalog) {
|
|
|
298
299
|
return copy(row);
|
|
299
300
|
},
|
|
300
301
|
async getAmendment(id) { return copy(s.amendments.get(String(id))) || null; },
|
|
302
|
+
// The constitution in force as a durable row (task 1005140).
|
|
303
|
+
async getConstitutionRow() { return copy(s.constitutionRow) || null; },
|
|
304
|
+
async saveConstitutionRow({ board, source, itemId = null, updatedBy = null } = {}) {
|
|
305
|
+
s.constitutionRow = { board: copy(board), source, amendment_item_id: itemId, updated_by: updatedBy };
|
|
306
|
+
return copy(s.constitutionRow);
|
|
307
|
+
},
|
|
301
308
|
async listAmendmentHistory(limit = 50) {
|
|
302
309
|
return s.items
|
|
303
310
|
.filter((i) => i.subject_type === 'constitutional_amendment' && i.closed_at)
|
|
@@ -297,6 +297,10 @@ const GOVERNMENT_PINS = {
|
|
|
297
297
|
// R18: the Constitution view — the live rule + the amendment history; reading
|
|
298
298
|
// the rule you vote under is part of casting.
|
|
299
299
|
'GET /government/constitution': 'perm:board.vote.cast',
|
|
300
|
+
// task 1005140: put the newest PASSED constitutional amendment back into force
|
|
301
|
+
// (a container restart had erased it). Decides nothing — the board already did —
|
|
302
|
+
// so it is the owner's operational atom, not a board one.
|
|
303
|
+
'POST /government/constitution/reapply': 'perm:government.manage',
|
|
300
304
|
// task 1004429: the founding band — who each genesis stage is waiting on. A
|
|
301
305
|
// read of the open ballot, so the same atom as the room.
|
|
302
306
|
'GET /government/board/genesis-stages': 'perm:board.vote.cast',
|
package/src/module-api.js
CHANGED
|
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
|
|
|
81
81
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
82
82
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
83
83
|
|
|
84
|
-
const CORE_VERSION = '1.24.
|
|
84
|
+
const CORE_VERSION = '1.24.74'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
85
85
|
|
|
86
86
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
87
87
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -76,13 +76,23 @@ async function underMonarchy(fn) {
|
|
|
76
76
|
closeBoardItem: async ({ outcome }) => ({ id: '21', subject_type: 'constitutional_amendment', subject_id: '5', opened_by: FOUNDER, closed_at: 'T', outcome }),
|
|
77
77
|
insertAuthorRatifyKarma: async () => true,
|
|
78
78
|
getAmendment: async () => ({ id: '5', proposed: seen.filed.proposed }),
|
|
79
|
+
// The durable row (task 1005140) — written first, read back after.
|
|
80
|
+
saveConstitutionRow: async (args) => { seen.row = { board: args.board }; return seen.row; },
|
|
81
|
+
getConstitutionRow: async () => seen.row || null,
|
|
79
82
|
};
|
|
80
83
|
for (const [k, v] of Object.entries(stubs)) { originals[k] = db[k]; db[k] = v; }
|
|
81
84
|
const originalApply = config.applyBoardAmendment;
|
|
82
85
|
config.applyBoardAmendment = (proposed) => { seen.applied.push(proposed); return proposed; };
|
|
86
|
+
// The read-back after the apply (task 1005140) sees what was applied.
|
|
87
|
+
const originalLoad = config.loadGovernmentConfig;
|
|
88
|
+
config.loadGovernmentConfig = (opts = {}) => {
|
|
89
|
+
const out = originalLoad(opts);
|
|
90
|
+
return seen.applied.length ? { ...out, board: config.sanitizeBoard(seen.applied[seen.applied.length - 1]) } : out;
|
|
91
|
+
};
|
|
83
92
|
try { await fn(seen); } finally {
|
|
84
93
|
for (const [k, v] of Object.entries(originals)) db[k] = v;
|
|
85
94
|
config.applyBoardAmendment = originalApply;
|
|
95
|
+
config.loadGovernmentConfig = originalLoad;
|
|
86
96
|
}
|
|
87
97
|
return seen;
|
|
88
98
|
}
|
|
@@ -111,6 +121,7 @@ for (const form of ['timocratic-republic', 'direct-democracy']) {
|
|
|
111
121
|
assert.equal((await board.maybeCloseAfterVote(out.charter.item_id)).outcome, 'passed');
|
|
112
122
|
});
|
|
113
123
|
assert.equal(seen.applied.length, 1, 'the ratified constitution is applied once');
|
|
124
|
+
assert.equal(charter.readCharter(seen.row.board).form, form, 'and recorded in the durable row');
|
|
114
125
|
assert.equal(charter.readCharter(seen.applied[0]).form, form, 'the constitution in force is the government the founder picked');
|
|
115
126
|
assert.match(seen.filed.rationaleMd, /^Charter: /, 'the record says in words what was chosen');
|
|
116
127
|
});
|
package/tests/goal_map_page.mjs
CHANGED
|
@@ -434,9 +434,9 @@ await test('the page is the sky\'s own surface — the same sheet and the same s
|
|
|
434
434
|
assert.match(html, /<main id="hall-main" class="sky-stage">/, 'the sky is drawn in the stage, the skip link\'s target');
|
|
435
435
|
const scripts = (h) => [...h.matchAll(/<script src="\/builders\/([^"?]+)\?v=([^"]+)"/g)].map((m) => [m[1], m[2]]);
|
|
436
436
|
// criterion-uat-ui.js (task 1004402): the rings' UAT state words, shared with the goal page.
|
|
437
|
-
assert.deepEqual(scripts(html).filter((s) => !['dom-utils.js', 'shell-trail.js', 'craft-mode.js', 'shell.js'].includes(s[0])).map((s) => s[0]), ['idea-objects.js', 'criterion-uat-ui.js', 'sky-data.js', 'sky-layout.js', 'sky-draw.js', 'sky-interaction.js', 'sky-panel.js', 'sky-composer.js', 'sky-tour.js']);
|
|
437
|
+
assert.deepEqual(scripts(html).filter((s) => !['dom-utils.js', 'shell-game.js', 'shell-trail.js', 'craft-mode.js', 'shell.js'].includes(s[0])).map((s) => s[0]), ['idea-objects.js', 'criterion-uat-ui.js', 'sky-data.js', 'sky-layout.js', 'sky-draw.js', 'sky-interaction.js', 'sky-panel.js', 'sky-composer.js', 'sky-tour.js']);
|
|
438
438
|
// Both doors load the shell (tasks 1004327, 1004430); the SKY's files are the same.
|
|
439
|
-
const skyScripts = (h) => scripts(h).filter((s) => !['dom-utils.js', 'shell-trail.js', 'craft-mode.js', 'shell.js'].includes(s[0]));
|
|
439
|
+
const skyScripts = (h) => scripts(h).filter((s) => !['dom-utils.js', 'shell-game.js', 'shell-trail.js', 'craft-mode.js', 'shell.js'].includes(s[0]));
|
|
440
440
|
assert.deepEqual(skyScripts(html), skyScripts(thinking), 'the two doors load the same sky files at the same stamp');
|
|
441
441
|
assert.equal(html.match(/thinking\.css\?v=([^"]+)/)[1], thinking.match(/thinking\.css\?v=([^"]+)/)[1]);
|
|
442
442
|
assert.match(read('thinking.css'), /body\[data-page\] \.sky-stage \{/, 'the sheet dresses the one stage both doors draw in inside the hall');
|
|
@@ -207,10 +207,14 @@ test('the monarchy act end to end: the sole Archon ratifies their OWN amendment
|
|
|
207
207
|
// applyBoardAmendment by property lookup at call time for exactly this.
|
|
208
208
|
const origApply = config.applyBoardAmendment;
|
|
209
209
|
config.applyBoardAmendment = (proposed) => origApply(proposed, { instancePath: tmp });
|
|
210
|
+
// ...and the read-back after it (task 1005140) reads the same temp file.
|
|
211
|
+
const origLoad = config.loadGovernmentConfig;
|
|
212
|
+
config.loadGovernmentConfig = (opts = {}) => origLoad({ ...opts, instancePath: tmp });
|
|
210
213
|
try {
|
|
211
214
|
const karma = [];
|
|
212
215
|
let votes = [];
|
|
213
216
|
let authorAsked = 0;
|
|
217
|
+
let savedRow = null;
|
|
214
218
|
await withDb({
|
|
215
219
|
getBoardItemForVote: async () => amendmentItem(),
|
|
216
220
|
builderHoldsRankActive: async (builderId, rankKeys) => builderId === '42' && rankKeys.includes('archon'),
|
|
@@ -228,6 +232,9 @@ test('the monarchy act end to end: the sole Archon ratifies their OWN amendment
|
|
|
228
232
|
// reached ONLY by the post-close ratify karma, never by the vote's
|
|
229
233
|
// anti-self (the carve-out) — the counter proves the order.
|
|
230
234
|
getBoardItemAuthor: async () => { authorAsked += 1; return '42'; },
|
|
235
|
+
// The durable row (task 1005140) — written first, read back after.
|
|
236
|
+
saveConstitutionRow: async (args) => { savedRow = { board: args.board, source: args.source }; return savedRow; },
|
|
237
|
+
getConstitutionRow: async () => savedRow,
|
|
231
238
|
}, async () => {
|
|
232
239
|
// The PROPOSER casts on their own amendment — legal (§7) — and it closes
|
|
233
240
|
// immediately under first_ratifier.
|
|
@@ -240,13 +247,16 @@ test('the monarchy act end to end: the sole Archon ratifies their OWN amendment
|
|
|
240
247
|
assert.ok(closed.applied_constitution, 'the amendment was applied on close');
|
|
241
248
|
assert.equal(closed.applied_constitution.pass_rule, 'consent', 'the project now runs consent');
|
|
242
249
|
assert.equal(closed.applied_constitution.window_minutes, 90);
|
|
250
|
+
assert.equal(closed.constitution_not_applied, undefined, 'read back: it is in force');
|
|
243
251
|
assert.ok(authorAsked >= 1, 'the ratify karma DID resolve the author, after the close');
|
|
244
252
|
});
|
|
245
253
|
assert.equal(karma.length, 1, 'the proposer earned the ratification karma (+3, same act as an idea ratify)');
|
|
254
|
+
assert.equal(savedRow && savedRow.board.pass_rule, 'consent', 'the durable row carries it too, so a restart cannot lose it');
|
|
246
255
|
const onDisk = JSON.parse(fs.readFileSync(tmp, 'utf8'));
|
|
247
256
|
assert.equal(onDisk.board.pass_rule, 'consent', 'the TEMP instance pack carries the new constitution');
|
|
248
257
|
} finally {
|
|
249
258
|
config.applyBoardAmendment = origApply;
|
|
259
|
+
config.loadGovernmentConfig = origLoad;
|
|
250
260
|
try { fs.unlinkSync(tmp); } catch (_) { /* not created */ }
|
|
251
261
|
}
|
|
252
262
|
});
|
|
@@ -200,7 +200,11 @@ test('the founder picks Direct Democracy and ratifies it: the amendment path wri
|
|
|
200
200
|
const originalApply = config.applyBoardAmendment;
|
|
201
201
|
const applied = [];
|
|
202
202
|
config.applyBoardAmendment = (proposed) => { applied.push(proposed); fs.writeFileSync(target, JSON.stringify({ board: proposed })); return proposed; };
|
|
203
|
+
// The read-back after the apply (task 1005140) reads the same temp file.
|
|
204
|
+
const originalLoad = config.loadGovernmentConfig;
|
|
205
|
+
config.loadGovernmentConfig = (opts = {}) => originalLoad({ ...opts, instancePath: target });
|
|
203
206
|
let filed = null;
|
|
207
|
+
let savedRow = null;
|
|
204
208
|
const votes = [];
|
|
205
209
|
try {
|
|
206
210
|
await withDb({
|
|
@@ -216,6 +220,9 @@ test('the founder picks Direct Democracy and ratifies it: the amendment path wri
|
|
|
216
220
|
closeBoardItem: async ({ outcome }) => ({ id: '21', subject_type: 'constitutional_amendment', subject_id: '5', opened_by: '42', closed_at: 'T', outcome }),
|
|
217
221
|
insertAuthorRatifyKarma: async () => true,
|
|
218
222
|
getAmendment: async () => ({ id: '5', proposed: filed.proposed }),
|
|
223
|
+
// The durable row (task 1005140) — written first, read back after.
|
|
224
|
+
saveConstitutionRow: async (args) => { savedRow = { board: args.board }; return savedRow; },
|
|
225
|
+
getConstitutionRow: async () => savedRow,
|
|
219
226
|
}, async () => {
|
|
220
227
|
const out = await board.proposeCharter({ form: 'direct-democracy', proposedBy: '42' });
|
|
221
228
|
assert.equal(out.ok, true);
|
|
@@ -227,11 +234,14 @@ test('the founder picks Direct Democracy and ratifies it: the amendment path wri
|
|
|
227
234
|
await board.castVote({ itemId: '21', voterId: '42', direction: 'yes' });
|
|
228
235
|
const closed = await board.maybeCloseAfterVote('21');
|
|
229
236
|
assert.equal(closed.outcome, 'passed');
|
|
237
|
+
assert.equal(closed.constitution_not_applied, undefined, 'read back: it is in force');
|
|
230
238
|
});
|
|
231
239
|
assert.deepEqual(applied, [filed.proposed], 'the ratified text is exactly what was filed');
|
|
232
240
|
assert.equal(JSON.parse(fs.readFileSync(target, 'utf8')).board.membership, 'rank:xenos+');
|
|
241
|
+
assert.equal(savedRow && savedRow.board.membership, 'rank:xenos+', 'the durable row carries it too');
|
|
233
242
|
} finally {
|
|
234
243
|
config.applyBoardAmendment = originalApply;
|
|
244
|
+
config.loadGovernmentConfig = originalLoad;
|
|
235
245
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
236
246
|
}
|
|
237
247
|
});
|