@bongos/core 1.21.113 → 1.21.115
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 +608 -188
- package/.claude/skills/ask-for-help/SKILL.md +1 -1
- package/.claude/skills/bongos-feedback/SKILL.md +1 -1
- package/.claude/skills/builder-sequence/SKILL.md +1 -1
- package/.claude/skills/builder-ship/SKILL.md +1 -0
- package/GOVERNMENT.md +128 -11
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +28 -0
- package/clients/bongos-client/index.cjs +28 -0
- package/clients/bongos-client/index.d.ts +45 -2
- package/clients/bongos-client/index.mjs +28 -0
- package/docs/adr/0372-an-ask-is-stored-per-docket-matter-and-the-server-refuses-slop.md +33 -0
- package/docs/adr/0373-an-ai-session-files-a-blocker-with-an-ask.md +31 -0
- package/docs/adr/0374-a-building-lead-is-a-held-position-appointed-by-a-people-matter.md +30 -0
- package/docs/adr/0375-who-may-build-without-a-vote-comes-from-the-form-of-government.md +38 -0
- package/docs/adr/0376-every-ask-is-routed-to-a-real-seat-on-the-server.md +30 -0
- package/docs/adr/0377-governance-is-a-held-position-read-on-me.md +24 -0
- package/docs/adr/0378-a-leads-build-outs-are-rows-built-at-once-or-by-a-passed-vote.md +33 -0
- package/docs/adr/0379-the-treasury-is-pots-per-goal-a-reserve-and-borrowing-over-real-spend.md +32 -0
- package/docs/adr/0380-a-strictness-preset-is-a-bundle-of-laws-switched-by-one-laws-vote.md +26 -0
- package/docs/adr/README.md +10 -1
- package/docs/api/openapi.json +989 -87
- package/docs/api-reference.md +18 -4
- package/docs/copy-inventory.md +709 -626
- package/docs/copy-registry.json +1673 -858
- package/docs/file-map.md +2 -1
- package/docs/module-api-changelog.md +4 -0
- package/docs/packs/engineer.md +2 -1
- package/docs/page-inventory.json +91 -6
- package/docs/page-readings.json +4875 -4748
- package/modules/builder-settings/law-locks.js +202 -0
- package/modules/builder-settings/routes/settings.js +66 -6
- package/modules/builder-settings/settings.js +7 -0
- package/modules/discord/ship-broadcast.js +22 -13
- package/modules/government/asks-db.js +54 -0
- package/modules/government/asks.js +269 -0
- package/modules/government/board-idea-reads.js +68 -0
- package/modules/government/board.js +21 -57
- package/modules/government/build-rights.js +148 -0
- package/modules/government/buildouts.js +160 -0
- package/modules/government/counts.js +81 -0
- package/modules/government/db.js +242 -0
- package/modules/government/decision-board.js +234 -8
- package/modules/government/decision-rules.js +2 -0
- package/modules/government/docket.js +114 -6
- package/modules/government/law-board.js +131 -0
- package/modules/government/laws.js +241 -0
- package/modules/government/leads.js +170 -0
- package/modules/government/migrations/government_027_laws.sql +143 -0
- package/modules/government/migrations/government_028_asks.sql +57 -0
- package/modules/government/migrations/government_029_leads.sql +135 -0
- package/modules/government/migrations/government_030_buildouts.sql +111 -0
- package/modules/government/migrations/government_031_treasury.sql +107 -0
- package/modules/government/module.json +2 -1
- package/modules/government/pollers/treasury-borrow.js +62 -0
- package/modules/government/positions.js +107 -0
- package/modules/government/presets.js +149 -0
- package/modules/government/routes/government.js +425 -2
- package/modules/government/routing.js +154 -0
- package/modules/government/treasury-db.js +166 -0
- package/modules/government/treasury.js +241 -0
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/board-room.js +2 -0
- package/modules/hall-ui/public/city-draw.js +604 -11
- package/modules/hall-ui/public/city.css +157 -1
- package/modules/hall-ui/public/city.html +37 -7
- package/modules/hall-ui/public/city.js +441 -21
- package/modules/hall-ui/public/city.states.json +110 -5
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/craft-mode.js +112 -29
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/diagram-viewer.css +12 -0
- package/modules/hall-ui/public/diagram-viewer.js +4 -0
- package/modules/hall-ui/public/diagrams.html +2 -2
- package/modules/hall-ui/public/drachmae.html +2 -2
- package/modules/hall-ui/public/fleet.html +2 -2
- package/modules/hall-ui/public/gate.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/gov-feeds.js +141 -0
- package/modules/hall-ui/public/gov-living.js +1002 -0
- package/modules/hall-ui/public/gov-positions.js +97 -0
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +3 -0
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +3 -3
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/my-agents.css +334 -0
- package/modules/hall-ui/public/my-agents.html +72 -0
- package/modules/hall-ui/public/my-agents.js +603 -0
- package/modules/hall-ui/public/my-agents.states.json +37 -0
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile-connections.js +40 -28
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/ranks.html +2 -2
- package/modules/hall-ui/public/roadmap.html +2 -2
- package/modules/hall-ui/public/room.css +8 -0
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/sessions.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +116 -16
- package/modules/hall-ui/public/street-card.js +18 -1
- package/modules/hall-ui/public/street-deck.css +299 -0
- package/modules/hall-ui/public/street-deck.js +1145 -0
- package/modules/hall-ui/public/street-depth.js +712 -0
- package/modules/hall-ui/public/street-desk.js +5 -2
- package/modules/hall-ui/public/street-draw.js +109 -16
- package/modules/hall-ui/public/street-inside.js +137 -22
- package/modules/hall-ui/public/street-office.js +705 -0
- package/modules/hall-ui/public/street.css +161 -11
- package/modules/hall-ui/public/street.html +41 -11
- package/modules/hall-ui/public/street.js +78 -66
- package/modules/hall-ui/public/street.states.json +129 -21
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +5 -1
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/watch.js +14 -1
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/hall-ui/records/living-city.md +57 -0
- package/modules/hall-ui/records/my-agents.md +21 -0
- package/modules/hall-ui/records/oversight-family.md +1 -0
- package/modules/hall-ui/records/profile-connections.md +10 -0
- package/modules/hall-ui/records/reading-rooms.md +2 -0
- package/modules/ideas/routes/blockers.js +92 -3
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/moving.js +17 -1
- package/modules/lifecycle/routes/moving.js +6 -0
- package/modules/lifecycle/routes/project-settings.js +23 -0
- package/modules/onboarding/approval-broadcast.js +8 -0
- package/modules/onboarding/routes/access-requests.js +16 -6
- package/modules/platform-identity/connections.js +13 -2
- package/modules/platform-identity/routes/connections.js +7 -1
- package/modules/public-landing/public/account-private.states.json +2 -2
- package/modules/public-landing/public/account.html +65 -5
- package/modules/public-landing/public/account.states.json +3 -3
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-bongos-chrome-b.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blob-one.webp.json +13 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.jpg.json +1 -1
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp +0 -0
- package/modules/public-landing/public/assets/world/r2-chrome-blobs-set.webp.json +13 -0
- package/modules/public-landing/public/assets/world.css +4 -4
- package/modules/public-landing/public/connections-empty.states.json +14 -0
- package/modules/public-landing/public/connections-inactive.states.json +14 -0
- package/modules/public-landing/public/connections.html +450 -0
- package/modules/public-landing/public/connections.probes.json +414 -0
- package/modules/public-landing/public/connections.states.json +17 -0
- package/modules/public-landing/public/contact.html +2 -2
- package/modules/public-landing/public/guild.html +2 -2
- package/modules/public-landing/public/index.html +2 -2
- package/modules/public-landing/public/invitations.html +2 -2
- package/modules/public-landing/public/privacy.html +2 -2
- package/modules/public-landing/public/projects-hall-live.states.json +0 -5
- package/modules/public-landing/public/projects.html +169 -6
- package/modules/public-landing/public/projects.probes.json +10 -6
- package/modules/public-landing/public/projects.states.json +13 -13
- package/modules/public-landing/public/terms.html +2 -2
- package/modules/ui-design/kit/fixtures/connections-empty.json +12 -0
- package/modules/ui-design/kit/fixtures/connections-inactive.json +7 -0
- package/modules/ui-design/kit/fixtures/connections.json +22 -0
- package/modules/ui-design/kit/serve.js +95 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +128 -0
- package/scripts/discord/README.md +8 -5
- package/scripts/discord/post.js +12 -1
- package/scripts/gds/artifact-audit.js +20 -3
- package/scripts/gds/ask.js +164 -0
- package/scripts/gds/autobongos-verify.js +21 -2
- package/scripts/gds/government-walk.js +638 -4
- package/scripts/gds/pulse.js +9 -6
- package/scripts/gds/ship-escalation.js +22 -3
- package/scripts/gds/ship-finish.js +5 -4
- package/scripts/gds/ship-merge-gate.js +18 -6
- package/scripts/hall-preview/README.md +41 -1
- package/scripts/hall-preview/pref-writes.js +140 -0
- package/scripts/hall-preview/refresh-fixtures.js +101 -7
- package/scripts/hall-preview/scrub-identity.js +366 -7
- package/scripts/hall-preview/server.js +27 -1
- package/scripts/render-diagrams.sh +4 -2
- package/src/bongos/auth-idp.js +6 -1
- package/src/bongos/project-settings.js +41 -0
- package/src/bongos/route-rank-check.js +27 -0
- package/src/bongos/routes/me.js +68 -7
- package/src/bongos/serve-internal.js +5 -1
- package/src/module-api.js +7 -1
- package/src/platform-server.js +10 -0
- package/tests/application_lifecycle.mjs +47 -5
- package/tests/conductor_main_e2e.mjs +21 -0
- package/tests/connections_api.mjs +18 -0
- package/tests/craft_mode_nav.mjs +5 -3
- package/tests/craft_mode_tallies.mjs +52 -16
- package/tests/discord_approvals.mjs +11 -0
- package/tests/discord_channels.mjs +20 -0
- package/tests/fixtures/gov-seated.mjs +64 -0
- package/tests/fixtures/government-ask-cards.json +1284 -0
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/government_agent_asks.mjs +322 -0
- package/tests/government_asks.mjs +408 -0
- package/tests/government_build_rights.mjs +253 -0
- package/tests/government_buildouts.mjs +382 -0
- package/tests/government_counts.mjs +143 -0
- package/tests/government_decision_rules.mjs +5 -1
- package/tests/government_laws.mjs +488 -0
- package/tests/government_laws_session.mjs +321 -0
- package/tests/government_leads.mjs +375 -0
- package/tests/government_positions.mjs +127 -0
- package/tests/government_presets.mjs +215 -0
- package/tests/government_routes.mjs +8 -1
- package/tests/government_routing.mjs +118 -0
- package/tests/government_seed.mjs +5 -0
- package/tests/government_treasury.mjs +394 -0
- package/tests/governor_docket.mjs +26 -2
- package/tests/hall_buildouts.mjs +208 -0
- package/tests/hall_city.mjs +14 -6
- package/tests/hall_city_living.mjs +617 -0
- package/tests/hall_gov_counts.mjs +336 -0
- package/tests/hall_gov_living.mjs +636 -0
- package/tests/hall_gov_real_site.mjs +655 -0
- package/tests/hall_matter_card.mjs +6 -1
- package/tests/hall_my_agents.mjs +481 -0
- package/tests/hall_nav.mjs +107 -4
- package/tests/hall_page_gate_map.mjs +4 -0
- package/tests/hall_preview_governor_fixtures.mjs +30 -3
- package/tests/hall_preview_scrub.mjs +59 -1
- package/tests/hall_preview_security.mjs +169 -0
- package/tests/hall_reading_world.mjs +44 -0
- package/tests/hall_roster_world.mjs +6 -2
- package/tests/hall_street.mjs +18 -2
- package/tests/hall_street_amend.mjs +1 -1
- package/tests/hall_street_deck.mjs +609 -0
- package/tests/hall_street_depth.mjs +559 -0
- package/tests/hall_street_desk.mjs +1 -1
- package/tests/hall_street_inside.mjs +29 -5
- package/tests/hall_tweak_editor.mjs +4 -1
- package/tests/hub_account_page.mjs +94 -3
- package/tests/hub_connections_page.mjs +507 -0
- package/tests/hub_invite_badge.mjs +1 -1
- package/tests/idp_device_exchange.mjs +18 -0
- package/tests/landing_legal_pages.mjs +8 -5
- package/tests/landing_page.mjs +23 -4
- package/tests/nav_permission_atoms.mjs +4 -1
- package/tests/profile_connections_ui.mjs +88 -25
- package/tests/projects_hub.mjs +15 -0
- package/tests/projects_hub_pre_uat.mjs +4 -2
- package/tests/render_check.mjs +2 -2
- package/tests/ship_broadcast_routing.mjs +57 -16
- package/tests/ship_broadcast_server.mjs +40 -7
- package/tests/ship_merge_gate.mjs +19 -0
- package/tests/ui_design_kit.mjs +2 -2
- package/tests/watch_applications_queue.mjs +23 -0
- package/tests/wizard_physics.mjs +1 -1
- package/tests/wizard_physics_more.mjs +133 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
// tests/hall_buildouts.mjs — A LEAD'S BUILD-OUTS IN THE HALL (task 1004711, goal
|
|
2
|
+
// 1000111; ADR 0378). GET /government/leads carries each building's `structure`
|
|
3
|
+
// (tests/government_buildouts.mjs pins the server); this file pins what the hall
|
|
4
|
+
// draws from it:
|
|
5
|
+
// 1. THE MODEL (gov-living.js): a build-out going up is a live site at its real
|
|
6
|
+
// progress; one waiting on a vote is a ghost whose tally is its sitting's on
|
|
7
|
+
// the docket; a committee room is a real room; a floor that stands built is a
|
|
8
|
+
// real floor, and every one of these fields is labelled real or derived (an
|
|
9
|
+
// `overlay` handed in changes nothing: task 1004717 deleted it).
|
|
10
|
+
// 2. THE CITY (city-draw.js): the live site (scaffold, crane, rising floor) and
|
|
11
|
+
// the ghost (parked crane, dashed glass, tally arc) are drawn from it; the
|
|
12
|
+
// real rooms and floors plug into the core.
|
|
13
|
+
// 3. THE OFFICE (street-office.js): it stands where the real Leads are read; its works are the real build-outs; a tap goes to POST
|
|
14
|
+
// /government/buildouts; and THE PROPOSAL PICTURE RENDERS FROM THE REAL
|
|
15
|
+
// BUILDING — what stands is drawn solid, the change dashed on top of it.
|
|
16
|
+
//
|
|
17
|
+
// Run: node --test tests/hall_buildouts.mjs
|
|
18
|
+
|
|
19
|
+
import { test } from 'node:test';
|
|
20
|
+
import assert from 'node:assert/strict';
|
|
21
|
+
import fs from 'node:fs';
|
|
22
|
+
import path from 'node:path';
|
|
23
|
+
import { createRequire } from 'node:module';
|
|
24
|
+
import { fileURLToPath } from 'node:url';
|
|
25
|
+
|
|
26
|
+
const require = createRequire(import.meta.url);
|
|
27
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
28
|
+
const PUB = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
29
|
+
const FX = path.join(ROOT, 'scripts', 'hall-preview', 'fixtures');
|
|
30
|
+
const City = require(path.join(PUB, 'city-draw.js'));
|
|
31
|
+
require(path.join(PUB, 'street-desk.js'));
|
|
32
|
+
require(path.join(PUB, 'street-deck.js'));
|
|
33
|
+
const L = require(path.join(PUB, 'gov-living.js'));
|
|
34
|
+
const O = require(path.join(PUB, 'street-office.js'));
|
|
35
|
+
const fx = (n) => JSON.parse(fs.readFileSync(path.join(FX, n + '.json'), 'utf8'));
|
|
36
|
+
const j = (v) => JSON.parse(JSON.stringify(v));
|
|
37
|
+
const NOW = Date.parse('2026-10-10T06:00:00.000Z');
|
|
38
|
+
|
|
39
|
+
// the hub's leads read with a Lead at Release and the structure the server serves
|
|
40
|
+
const DOCKET_ID = 'board:91';
|
|
41
|
+
function leadsWith() {
|
|
42
|
+
const r = j(fx('government__leads'));
|
|
43
|
+
const at = (k) => r.buildings.find((b) => b.building === k);
|
|
44
|
+
const rel = at('release');
|
|
45
|
+
rel.lead = { id: '5', building: 'release', builder: { id: '777', login: 'quill', name: 'Quill' }, charter: 'Releases.', since: '2026-10-09T12:00:00.000Z', appointed_by: { item_id: '40' } };
|
|
46
|
+
rel.answers = { seat: 'lead:release', builder_ids: ['777'], fell_to_owner: false };
|
|
47
|
+
rel.structure = {
|
|
48
|
+
standing: [
|
|
49
|
+
{ id: '1', building: 'release', capsule: 'floor', name: 'Staging Deck', line: 'Try each release first.', state: 'building', progress: 0.4, started_at: '2026-10-10T00:00:00.000Z', builder: null, allowed: { path: 'at_once', ground: 'form', why: 'The founder decides alone.' } },
|
|
50
|
+
{ id: '2', building: 'release', capsule: 'committee', name: 'Gate Room', line: 'Who checks a release.', state: 'built', progress: 1, started_at: '2026-10-01T00:00:00.000Z', builder: null, allowed: { path: 'vote', item_id: '44' } },
|
|
51
|
+
{ id: '3', building: 'release', capsule: 'floor', name: 'Canary Floor', line: '', state: 'built', progress: 1, started_at: '2026-09-30T00:00:00.000Z', builder: null, allowed: { path: 'vote', item_id: '43' } },
|
|
52
|
+
],
|
|
53
|
+
proposed: [],
|
|
54
|
+
};
|
|
55
|
+
at('treasury').structure = {
|
|
56
|
+
standing: [],
|
|
57
|
+
proposed: [{ item_id: '91', matter_id: '9', docket_id: DOCKET_ID, building: 'treasury', capsule: 'annex', name: 'Reserve Wing', line: 'Where the reserve is kept.', opened_at: '2026-10-09T10:00:00.000Z', closes_at: null, href: '/board-room?item=91' }],
|
|
58
|
+
};
|
|
59
|
+
return r;
|
|
60
|
+
}
|
|
61
|
+
// the docket with the build-out's sitting open, 2 for and 1 against of 5 seats
|
|
62
|
+
function docketWith() {
|
|
63
|
+
const d = j(fx('government__docket'));
|
|
64
|
+
d.matters.push({ id: DOCKET_ID, source: 'board', building: 'treasury', rule: 'majority', state: 'needs', title: 'Add an annex to Treasury: Reserve Wing', short: 'matter', opened_at: '2026-10-09T10:00:00.000Z', tally: { for: 2, against: 1, open: 2, needs: 'x' }, ref: { type: 'board_item', id: '91' }, acts: [] });
|
|
65
|
+
return d;
|
|
66
|
+
}
|
|
67
|
+
function inputs(over = {}) {
|
|
68
|
+
return {
|
|
69
|
+
docket: docketWith(), constitution: fx('government__constitution'), goals: fx('goals'), rollups: fx('goals__rollups'), blockers: fx('blockers'),
|
|
70
|
+
claims: fx('tasks__claimable'), tasks: fx('tasks__status-active'), moving: fx('moving'), cost: fx('public__cost-summary'), roster: fx('builders__roster'),
|
|
71
|
+
me: fx('me'), leads: leadsWith(), now: NOW, ...over,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const bOf = (lv, k) => lv.buildings.find((b) => b.key === k);
|
|
75
|
+
|
|
76
|
+
// ── 1. the model ────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
test('the model: a build-out going up is a live site at its real progress; a vote is a ghost tied to its sitting', () => {
|
|
79
|
+
const lv = L.livingModel(inputs());
|
|
80
|
+
const rel = bOf(lv, 'release');
|
|
81
|
+
const site = rel.construction.find((c) => c.authorised);
|
|
82
|
+
assert.deepEqual(j([site.kind, site.capsule, site.id, site.label, site.progress, site.authorised_by]), ['buildout', 'floor', 'buildout:1', 'Staging Deck', 0.4, 'The founder decides alone.']);
|
|
83
|
+
assert.equal(rel.construction.indexOf(site), 0, 'drawn ahead of any other sitting there');
|
|
84
|
+
const tre = bOf(lv, 'treasury');
|
|
85
|
+
const ghost = tre.construction[0];
|
|
86
|
+
assert.deepEqual(j([ghost.kind, ghost.capsule, ghost.authorised, ghost.ask_id, ghost.sitting_id, ghost.needs]), ['scaffold', 'annex', false, DOCKET_ID, '91', 'a vote']);
|
|
87
|
+
assert.equal(tre.construction.filter((c) => c.id === DOCKET_ID).length, 1, 'its sitting is drawn once, as its ghost');
|
|
88
|
+
for (const c of [site, ghost]) for (const [k, v] of Object.entries(c._sources)) assert.ok(v === 'real' || v === 'derived', `${c.id}.${k}`);
|
|
89
|
+
assert.equal(rel._sources['counts.building'], 'derived');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('the model: a committee room is a real room, a floor that stands built a real floor; no deputies are invented', () => {
|
|
93
|
+
const lv = L.livingModel(inputs());
|
|
94
|
+
const rel = bOf(lv, 'release');
|
|
95
|
+
assert.deepEqual(j(rel.structure.committees), [{ key: 'b2', name: 'Gate Room', line: 'Who checks a release.', members: [], real: true, state: 'built' }]);
|
|
96
|
+
assert.deepEqual(j(rel.structure.built), [{ id: 'buildout:3', capsule: 'floor', name: 'Canary Floor', line: '' }]);
|
|
97
|
+
assert.deepEqual(Object.keys(rel.structure).sort(), ['built', 'committees'], 'no table holds deputies: none is drawn');
|
|
98
|
+
assert.equal(rel._sources.structure, 'real');
|
|
99
|
+
assert.equal(bOf(lv, 'treasury')._sources.structure, 'real', 'an unseated building\'s structure is real too');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('the model: an overlay handed in changes nothing (the prototype overlay is gone)', () => {
|
|
103
|
+
const a = L.livingModel(inputs());
|
|
104
|
+
const overlay = { buildouts: [{ id: 'x', building: 'release', authorised: true, label: 'Scrambled', progress: 0.9 }, { id: 'y', building: 'treasury', authorised: false, label: 'Other', ask_id: 'proto:x' }] };
|
|
105
|
+
const b = L.livingModel(inputs({ overlay }));
|
|
106
|
+
for (const k of ['release', 'treasury']) assert.deepEqual(j(bOf(a, k).construction), j(bOf(b, k).construction), k);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// ── 2. the city ─────────────────────────────────────────────────────────────
|
|
110
|
+
|
|
111
|
+
function cityOf(over = {}) {
|
|
112
|
+
const inp = inputs(over);
|
|
113
|
+
const living = L.livingModel(inp);
|
|
114
|
+
return City.cityModel({ docket: inp.docket, goals: inp.goals.goals || inp.goals, rollups: inp.rollups.rollups || inp.rollups, mode: 'dark', now: NOW, living, constitution: inp.constitution });
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
test('the city: a live site with scaffold, crane and a rising floor at the real progress, unmarked', () => {
|
|
118
|
+
const m = cityOf();
|
|
119
|
+
const rel = m.civic.find((c) => c.key === 'release');
|
|
120
|
+
assert.deepEqual(j([rel.site.kind, rel.site.progress, rel.site.built]), ['live', 0.4, 1]);
|
|
121
|
+
const parts = m.blds.find((b) => b.key === 'release').parts;
|
|
122
|
+
for (const cls of ['city-scaffold', 'city-crane', 'city-jib', 'city-rise', 'city-climb']) {
|
|
123
|
+
const p = parts.find((x) => x.cls === cls);
|
|
124
|
+
assert.ok(p, cls);
|
|
125
|
+
assert.equal('proto' in p, false, cls + ' carries no prototype mark');
|
|
126
|
+
}
|
|
127
|
+
// the structure plugs into the core: the real Lead, the real room and the real floor
|
|
128
|
+
assert.deepEqual(j(rel.floors.map((f) => [f.kind, f.name])), [['lead', 'Quill'], ['committee', 'Gate Room'], ['floor', 'Canary Floor']]);
|
|
129
|
+
const plugs = parts.filter((p) => /city-plug/.test(p.cls || '') && ['lead', 'committee', 'floor'].includes(p.floor));
|
|
130
|
+
assert.equal(plugs.length, 6, 'a shell and its windows for each of the three');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('the city: a build-out on a vote is a parked crane, a dashed ghost and its sitting\'s own tally', () => {
|
|
134
|
+
const m = cityOf();
|
|
135
|
+
const tre = m.civic.find((c) => c.key === 'treasury');
|
|
136
|
+
assert.deepEqual(j([tre.site.kind, tre.site.label, tre.site.ask_id]), ['pending', 'Reserve Wing', DOCKET_ID]);
|
|
137
|
+
assert.deepEqual(j(tre.tally), { seats: 5, yes: 2, no: 1 }, 'one dot a seat, filled as the docket counts them');
|
|
138
|
+
const parts = m.blds.find((b) => b.key === 'treasury').parts;
|
|
139
|
+
const ghost = parts.find((p) => p.cls === 'city-ghost');
|
|
140
|
+
assert.ok(ghost);
|
|
141
|
+
assert.ok(ghost.polys.filter((p) => p.f !== 'none').every((p) => p.dash === '4 3'), 'every face dashed');
|
|
142
|
+
assert.ok(parts.some((p) => p.cls === 'city-crane city-crane--parked'));
|
|
143
|
+
assert.ok(tre.shaft, 'its open sitting is a light shaft');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── 3. the office and the picture ───────────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
test('the office stands from the real Leads; its works are the real build-outs', () => {
|
|
149
|
+
const lv = L.livingModel(inputs());
|
|
150
|
+
const off = O.officeOf(lv, 'release');
|
|
151
|
+
assert.ok(off, 'the Leads were read: the office stands');
|
|
152
|
+
assert.deepEqual(j([off.seated, off.holders]), [true, ['777']]);
|
|
153
|
+
assert.deepEqual(j(off.rooms.map((r) => [r.name, r.proposed])), [['Gate Room', false]]);
|
|
154
|
+
assert.deepEqual(j(O.worksOf(lv, 'release').map((w) => [w.kind, w.state, w.label, w.progress])), [['floor', 'site', 'Staging Deck', 0.4]]);
|
|
155
|
+
assert.deepEqual(j(O.worksOf(lv, 'treasury').map((w) => [w.kind, w.state, w.label, w.ask])), [['annex', 'proposed', 'Reserve Wing', DOCKET_ID]]);
|
|
156
|
+
assert.equal(O.officeOf(L.livingModel(inputs({ leads: null })), 'release'), null, 'the Leads not read: no office is invented');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('THE PROPOSAL PICTURE RENDERS FROM THE REAL BUILDING: what stands is solid, the change dashed on top of it', () => {
|
|
160
|
+
const lv = L.livingModel(inputs());
|
|
161
|
+
const struct = bOf(lv, 'release').structure;
|
|
162
|
+
const bare = O.isoPicture('release', 'floor', 'dark', null);
|
|
163
|
+
const real = O.isoPicture('release', 'floor', 'dark', struct);
|
|
164
|
+
assert.deepEqual(j(real.stands), { floors: 1, rooms: 1, annex: false, district: false }, 'the real floor and room are drawn');
|
|
165
|
+
assert.deepEqual(j(bare.stands), { floors: 0, rooms: 0, annex: false, district: false });
|
|
166
|
+
const solid = (p) => p.paths.filter((x) => x.dash !== '4 3' && x.f !== 'none').length;
|
|
167
|
+
const dashed = (p) => p.paths.filter((x) => x.dash === '4 3');
|
|
168
|
+
assert.ok(solid(real) > solid(bare), 'more of the building stands solid');
|
|
169
|
+
assert.ok(dashed(real).length > 0 && dashed(bare).length > 0, 'the change, dashed, on both');
|
|
170
|
+
// the ghost floor sits on top of what stands: higher on the page than on the bare building
|
|
171
|
+
const topOf = (paths) => Math.min(...paths.flatMap((p) => [...p.d.matchAll(/[ML]\s*(-?[\d.]+)\s+(-?[\d.]+)/g)].map((m) => Number(m[2]))));
|
|
172
|
+
assert.ok(topOf(dashed(real)) < topOf(dashed(bare)), 'the change is drawn above the floors that stand');
|
|
173
|
+
// an annex beside a built annex stands further out
|
|
174
|
+
const withAnnex = O.isoPicture('release', 'annex', 'dark', { built: [{ capsule: 'annex' }], committees: [] });
|
|
175
|
+
const rightOf = (paths) => Math.max(...paths.flatMap((p) => [...p.d.matchAll(/[ML]\s*(-?[\d.]+)\s+(-?[\d.]+)/g)].map((m) => Number(m[1]))));
|
|
176
|
+
assert.ok(rightOf(dashed(withAnnex)) > rightOf(dashed(O.isoPicture('release', 'annex', 'dark', null))), 'the second annex is drawn beyond the first');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('a real build-out tap goes to the server: POST /government/buildouts, then the Leads and the docket are read again', async () => {
|
|
180
|
+
const sent = [];
|
|
181
|
+
const said = [];
|
|
182
|
+
let loads = 0;
|
|
183
|
+
const refreshed = [];
|
|
184
|
+
const saved = globalThis.GovFeeds;
|
|
185
|
+
globalThis.GovFeeds = { refresh: (k) => refreshed.push(k) };
|
|
186
|
+
const lv = L.livingModel(inputs());
|
|
187
|
+
const env = {
|
|
188
|
+
data: () => ({ docket: { viewer: { id: '777' } }, living: lv }), mode: () => 'dark', say: (t) => said.push(t), draw() {}, decided() {}, load: () => { loads += 1; },
|
|
189
|
+
send: async (req) => { sent.push(req); return { ok: true, status: 201, body: { ok: true, path: 'at_once', rights: { why: 'The founder decides alone.' } } }; },
|
|
190
|
+
};
|
|
191
|
+
try {
|
|
192
|
+
globalThis.GovDeck.page.living = () => lv;
|
|
193
|
+
O.page.mount(env);
|
|
194
|
+
const out = await O.page.buildOut('release', 'committee');
|
|
195
|
+
assert.deepEqual(j(sent), [{ method: 'POST', url: '/api/bongos/government/buildouts', body: { building: 'release', capsule: 'committee' } }]);
|
|
196
|
+
assert.equal(out.built, true);
|
|
197
|
+
assert.equal(out.path, 'at_once');
|
|
198
|
+
assert.match(said[0], /^Building a committee room now\. The founder decides alone\./);
|
|
199
|
+
assert.deepEqual(refreshed, [['leads', 'docket']]);
|
|
200
|
+
assert.equal(loads, 1);
|
|
201
|
+
assert.deepEqual(j(O.page.state), { presetFiled: {} }, 'nothing drawn for this visit only: the server\'s read draws it');
|
|
202
|
+
// a refusal is said, and nothing is drawn
|
|
203
|
+
env.send = async () => ({ ok: false, status: 403, body: { error: { code: 'not_the_holder', what_this_means: 'Release is led by Quill; only they grow its structure.' } } });
|
|
204
|
+
const no = await O.page.buildOut('release', 'floor');
|
|
205
|
+
assert.equal(no.refused, true);
|
|
206
|
+
assert.equal(said[said.length - 1], 'Release is led by Quill; only they grow its structure.');
|
|
207
|
+
} finally { globalThis.GovFeeds = saved; }
|
|
208
|
+
});
|
package/tests/hall_city.mjs
CHANGED
|
@@ -120,12 +120,15 @@ test('a pin per open matter, in its state, over its building', () => {
|
|
|
120
120
|
];
|
|
121
121
|
const m = City.cityModel({ docket: docket(ms), goals: [], rollups: [], mode: 'dark' });
|
|
122
122
|
assert.deepEqual(m.pins.map((p) => [p.key, p.building, p.state]), [
|
|
123
|
-
|
|
123
|
+
// back to front: the Charter stands in the back corner since task 1004718
|
|
124
|
+
['board:1', 'charter', 'needs'], ['blockers:2', 'planning', 'moving'], ['removal_proposals:3', 'disputes', 'settled'],
|
|
124
125
|
]);
|
|
125
|
-
// the prototype's own pin position for a matter at the Charter: P(
|
|
126
|
+
// the prototype's own pin position for a matter at the Charter: P(slot + 1, slot + 1, 158 + 8)
|
|
127
|
+
// (task 1004718 moved the Charter to the back corner, so Planning is not behind it)
|
|
126
128
|
const charter = m.pins.find((p) => p.building === 'charter');
|
|
127
129
|
const k = City.isoKit(60, 30, 720, 176, 30 / 28, City.isoTheme('dark'), false);
|
|
128
|
-
const
|
|
130
|
+
const cs = City.CIVIC_SLOTS.find((s) => s[0] === 'charter');
|
|
131
|
+
const top = k.P(cs[1] + 1, cs[2] + 1, 166);
|
|
129
132
|
assert.equal(charter.x, Math.round((top[0] - 15) * 10) / 10);
|
|
130
133
|
assert.equal(charter.y, Math.round((top[1] - 52) * 10) / 10);
|
|
131
134
|
});
|
|
@@ -154,7 +157,8 @@ test('one pin per building, in the tier of its most important matter, with the c
|
|
|
154
157
|
assert.deepEqual([m.pins[0].key, m.pins[0].state, m.pins[0].count], ['blockers:9', 'urgent', 4], 'urgent (needs you, due within a day); four open');
|
|
155
158
|
assert.deepEqual(m.pins[0].matters, ['blockers:9', 'blockers:1', 'blockers:0', 'blockers:2', 'blockers:3']);
|
|
156
159
|
const k = City.isoKit(60, 30, 720, 176, 30 / 28, City.isoTheme('dark'), false);
|
|
157
|
-
const
|
|
160
|
+
const ps = City.CIVIC_SLOTS.find((s) => s[0] === 'planning');
|
|
161
|
+
const top = k.P(ps[1] + 1, ps[2] + 1, City.ROOF.planning + 8);
|
|
158
162
|
assert.equal(m.pins[0].x, Math.round((top[0] - 15) * 10) / 10, 'where the prototype stands a single pin');
|
|
159
163
|
const tier = (list) => City.cityModel({ docket: docket(list), mode: 'dark', now: NOW }).pins[0].state;
|
|
160
164
|
assert.equal(tier([matter({ state: 'needs', deadline: '2026-10-05T12:00:00.000Z' })]), 'needs', 'due after a day');
|
|
@@ -280,13 +284,17 @@ test('the city sits in the hall chrome, behind the shell\'s full-screen switch',
|
|
|
280
284
|
for (const id of ['app-sidebar', 'app-scrim', 'app-topbar']) assert.match(html, new RegExp(`id="${id}"`), id);
|
|
281
285
|
assert.match(html, /<main id="hall-main" class="city-main">\s*<div class="city-top">/, 'the stage is the one <main>, the strip inside it');
|
|
282
286
|
const scripts = [...html.matchAll(/<script src="\/builders\/([a-z-]+)\.js/g)].map((m) => m[1]);
|
|
283
|
-
|
|
287
|
+
// task 1004685 (the living city): gov-living.js, the model under the drawing, and
|
|
288
|
+
// gov-feeds.js, its one reader per page (shared with the shell's tally), load before it
|
|
289
|
+
// task 1004702: the card answers its first ask in place, so the deck's card and sender (and the desk rows under them) load before the page
|
|
290
|
+
assert.deepEqual(scripts, ['shell-trail', 'craft-mode', 'shell', 'palette', 'gov-living', 'gov-feeds', 'city-draw', 'street-draw', 'street-card', 'street-inside', 'street-desk', 'street-deck', 'city'], 'the shell first, as on every hall page');
|
|
284
291
|
const css = read('city.css').replace(/\/\*[\s\S]*?\*\//g, '');
|
|
285
292
|
assert.match(css, /\.city-main \{ position: fixed; top: var\(--topbar-h, 56px\); left: var\(--sidebar-w-rail, 64px\);[^}]*contain: layout paint;/);
|
|
286
293
|
assert.match(css, /html\[data-fullscreen="on"\] \.city-main \{ top: 0; left: 0; \}/);
|
|
287
294
|
assert.match(css, /html\[data-fullscreen="on"\] \.city-top \{ right: 184px; \}/, 'the strip steps clear of the corner exit');
|
|
288
295
|
const shell = read('shell.js');
|
|
289
|
-
|
|
296
|
+
// (task 1004685's governing-as-a-seat pass marks the tab `position: true`; the gate is unchanged)
|
|
297
|
+
assert.match(shell, /\{ id: 'city', label: 'City', icon: 'city', href: '\/city', gate: 'perm:board\.vote\.cast'(, [^}]*)? \}/, 'the city is a tab, gated on its page\'s atom');
|
|
290
298
|
assert.doesNotMatch(shell, /href:\s*'(\/builders)?\/street'/, 'the street has no sidebar link: the way in is the city');
|
|
291
299
|
});
|
|
292
300
|
|