@bongos/core 1.21.112 → 1.21.114
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 +616 -186
- 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 +343 -262
- package/docs/copy-registry.json +1223 -426
- 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 +5017 -4458
- 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.html +2 -2
- package/modules/public-landing/public/projects.probes.json +6 -2
- 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-run.js +20 -5
- package/scripts/gds/autobongos-tweak-lane.js +262 -0
- package/scripts/gds/autobongos-verify.js +21 -2
- package/scripts/gds/claim.js +2 -0
- 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/gds/tweak-halt.js +1 -0
- 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/autobongos_tweak_lane.mjs +208 -0
- 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/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
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
// tests/craft_mode_tallies.mjs — Governing mode's header tallies (task 1004563, goal
|
|
2
|
-
// 1000095 WA6): "
|
|
2
|
+
// 1000095 WA6): "to do N · moving M" beside the eyebrow, on the city and the
|
|
3
|
+
// street only (task 1004701, owner ruling 2026-10-09).
|
|
3
4
|
//
|
|
4
5
|
// Runs the REAL craft-mode.js over a small DOM with a stubbed fetch, and the REAL
|
|
5
6
|
// server count (modules/lifecycle/moving.js) over a fake pool, so the two halves are
|
|
6
7
|
// pinned where they actually decide: which statuses "moving" counts (owner decision
|
|
7
8
|
// 2026-10-08: active + confirmed), and that a figure the page could not read is left
|
|
8
|
-
// out rather than drawn as a zero that says nothing waits on you.
|
|
9
|
+
// out rather than drawn as a zero that says nothing waits on you. Since the living
|
|
10
|
+
// city (task 1004685) both figures are the city's own (gov-living.js countsOf over
|
|
11
|
+
// gov-feeds.js), so the real gov-living.js and gov-feeds.js run beside it here;
|
|
12
|
+
// tests/hall_gov_counts.mjs holds the tally equal to every other surface.
|
|
9
13
|
import assert from 'node:assert/strict';
|
|
10
14
|
import { test } from 'node:test';
|
|
11
15
|
import fs from 'node:fs';
|
|
@@ -16,6 +20,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
16
20
|
|
|
17
21
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
18
22
|
const CRAFT = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/craft-mode.js'), 'utf8');
|
|
23
|
+
const LIVING = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/gov-living.js'), 'utf8');
|
|
24
|
+
const FEEDS = fs.readFileSync(path.join(ROOT, 'modules/hall-ui/public/gov-feeds.js'), 'utf8');
|
|
19
25
|
const require = createRequire(import.meta.url);
|
|
20
26
|
process.env.NODE_ENV = 'test';
|
|
21
27
|
const { countMoving, movingHandler, MOVING_STATUSES } = require('../modules/lifecycle/moving.js');
|
|
@@ -44,7 +50,8 @@ function node(tag, id, cls) {
|
|
|
44
50
|
function all(root) { return root.children.flatMap((c) => [c, ...all(c)]); }
|
|
45
51
|
|
|
46
52
|
// answers: path → JSON body, or a number (an HTTP failure status), or 'throw'.
|
|
47
|
-
|
|
53
|
+
// page: the body's data-page (the tally stands on the city and the street only)
|
|
54
|
+
function boot(mode, answers, page = 'city') {
|
|
48
55
|
const sidebar = node('NAV', 'app-sidebar');
|
|
49
56
|
sidebar.appendChild(node('DIV', '', 'app-sidebar__brand'));
|
|
50
57
|
const topbar = node('DIV', 'app-topbar');
|
|
@@ -52,7 +59,7 @@ function boot(mode, answers) {
|
|
|
52
59
|
const root = node('HTML');
|
|
53
60
|
const asked = [];
|
|
54
61
|
const document = {
|
|
55
|
-
documentElement: root, body: node('BODY'), activeElement: null,
|
|
62
|
+
documentElement: root, body: (() => { const b = node('BODY'); if (page) b.setAttribute('data-page', page); return b; })(), activeElement: null,
|
|
56
63
|
getElementById: (id) => [sidebar, topbar, ...all(sidebar), ...all(topbar)].find((n) => n.id === id) || null,
|
|
57
64
|
querySelectorAll: () => [],
|
|
58
65
|
createElement: (t) => node(t.toUpperCase()),
|
|
@@ -68,20 +75,34 @@ function boot(mode, answers) {
|
|
|
68
75
|
if (typeof a === 'number') return Promise.resolve({ ok: false, status: a, json: () => Promise.resolve({}) });
|
|
69
76
|
return Promise.resolve({ ok: true, status: 200, json: () => Promise.resolve(a) });
|
|
70
77
|
};
|
|
71
|
-
|
|
78
|
+
window.fetch = fetch;
|
|
79
|
+
const sandbox = { window, document, fetch, Date, CustomEvent: class { constructor(t, i) { this.type = t; Object.assign(this, i); } } };
|
|
72
80
|
vm.createContext(sandbox);
|
|
81
|
+
// the living city's one counter and one reader, as the city and the street carry them
|
|
82
|
+
// (task 1004685): the tally prints their numbers, read through gov-feeds.js
|
|
83
|
+
vm.runInContext(LIVING, sandbox, { filename: 'gov-living.js' });
|
|
84
|
+
vm.runInContext(FEEDS, sandbox, { filename: 'gov-feeds.js' });
|
|
73
85
|
vm.runInContext(CRAFT, sandbox, { filename: 'craft-mode.js' });
|
|
74
86
|
const craft = window.OTBCraftMode;
|
|
75
87
|
craft.mount();
|
|
76
88
|
const box = () => all(topbar).find((n) => n.id === 'craft-tallies') || null;
|
|
77
89
|
return { craft, topbar, asked, box };
|
|
78
90
|
}
|
|
79
|
-
const settle = () => new Promise((r) => setImmediate(r));
|
|
91
|
+
const settle = async () => { for (let i = 0; i < 20; i++) await new Promise((r) => setImmediate(r)); };
|
|
80
92
|
const text = (html) => html.replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim();
|
|
81
93
|
|
|
82
|
-
|
|
94
|
+
// Since the living city (task 1004685) the tally prints the city's own counts:
|
|
95
|
+
// gov-living.js countsOf over gov-feeds.js COUNT_FEEDS, and since task 1004716 those
|
|
96
|
+
// stand on ONE server read, GET /government/counts. Here three matters on the
|
|
97
|
+
// viewer, no agent at work, twelve moving.
|
|
98
|
+
const COUNTS = { viewer: { id: '1' }, on_you: 3, on_others: 0, open: 3, agents_at_work: 0, moving: 12, routed: true, yours: ['b:1', 'b:2', 'b:3'], others: [] };
|
|
99
|
+
const BOTH = {
|
|
100
|
+
'/government/counts': COUNTS,
|
|
101
|
+
'/me': { builder: { id: '1', github_login: 'builder-1', rank: 'archon' } },
|
|
102
|
+
};
|
|
103
|
+
const COUNT_PATHS = ['/government/counts', '/me'];
|
|
83
104
|
|
|
84
|
-
test('Governing reads
|
|
105
|
+
test('Governing reads the city\'s counts and draws "to do 3 · moving 12" right after the eyebrow', async () => {
|
|
85
106
|
const { topbar, box, asked } = boot('governing', BOTH);
|
|
86
107
|
await settle();
|
|
87
108
|
const b = box();
|
|
@@ -89,24 +110,25 @@ test('Governing reads both counts and draws "needs you 3 · moving 12" right aft
|
|
|
89
110
|
const eyebrow = all(topbar).find((n) => n.id === 'craft-eyebrow');
|
|
90
111
|
assert.equal(eyebrow.nextSibling, b, 'they sit directly after the mode words');
|
|
91
112
|
assert.equal(b.getAttribute('hidden'), null);
|
|
92
|
-
assert.equal(text(b.innerHTML), '
|
|
113
|
+
assert.equal(text(b.innerHTML), 'to do 3 · moving 12');
|
|
93
114
|
assert.match(b.innerHTML, /craft-tally craft-tally--due/, 'something waits, so needs-you carries the warm dot');
|
|
94
|
-
assert.deepEqual(asked.map((u) => u.replace('/api/bongos', '')).sort(),
|
|
115
|
+
assert.deepEqual(asked.map((u) => u.replace('/api/bongos', '')).sort(), COUNT_PATHS,
|
|
116
|
+
'one server count, once, beside the viewer the shell already reads — never a task list');
|
|
95
117
|
});
|
|
96
118
|
|
|
97
|
-
test('nothing waiting draws "
|
|
98
|
-
const { box } = boot('governing', { ...BOTH, '/
|
|
119
|
+
test('nothing waiting draws "to do 0" without the dot', async () => {
|
|
120
|
+
const { box } = boot('governing', { ...BOTH, '/government/counts': { ...COUNTS, on_you: 0, open: 0, yours: [] } });
|
|
99
121
|
await settle();
|
|
100
|
-
assert.equal(text(box().innerHTML), '
|
|
122
|
+
assert.equal(text(box().innerHTML), 'to do 0 · moving 12');
|
|
101
123
|
assert.doesNotMatch(box().innerHTML, /craft-tally--due/);
|
|
102
124
|
});
|
|
103
125
|
|
|
104
126
|
test('a count that cannot be read is LEFT OUT, never drawn as zero', async () => {
|
|
105
|
-
const noMoving = boot('governing', { ...BOTH, '/
|
|
127
|
+
const noMoving = boot('governing', { ...BOTH, '/government/counts': { ...COUNTS, moving: null } });
|
|
106
128
|
await settle();
|
|
107
|
-
assert.equal(text(noMoving.box().innerHTML), '
|
|
129
|
+
assert.equal(text(noMoving.box().innerHTML), 'to do 3', 'a moving count the server could not read drops only its own figure');
|
|
108
130
|
|
|
109
|
-
const offline = boot('governing', { '/
|
|
131
|
+
const offline = boot('governing', { '/government/counts': 'throw' });
|
|
110
132
|
await settle();
|
|
111
133
|
assert.equal(offline.box().getAttribute('hidden'), '', 'nothing readable: the slot stays hidden, no zeros');
|
|
112
134
|
assert.equal(offline.box().innerHTML, '');
|
|
@@ -126,6 +148,20 @@ test('only Governing wears the tallies, and leaving it removes them', async () =
|
|
|
126
148
|
assert.equal(box(), null, 'switching away takes them down');
|
|
127
149
|
});
|
|
128
150
|
|
|
151
|
+
test('the top bar carries the tally on the city and the street only (owner ruling, task 1004701)', async () => {
|
|
152
|
+
for (const page of ['city', 'street']) {
|
|
153
|
+
const { box } = boot('governing', BOTH, page);
|
|
154
|
+
await settle();
|
|
155
|
+
assert.ok(box(), `${page} carries the tally`);
|
|
156
|
+
}
|
|
157
|
+
for (const page of ['my-agents', 'board', 'home', null]) {
|
|
158
|
+
const { box, asked } = boot('governing', BOTH, page);
|
|
159
|
+
await settle();
|
|
160
|
+
assert.equal(box(), null, `${page || 'a page with no name'} draws no tally`);
|
|
161
|
+
assert.deepEqual(asked, [], `${page || 'a page with no name'} asks the server for nothing`);
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
129
165
|
test('the server counts active + confirmed only, and sums them', async () => {
|
|
130
166
|
assert.deepEqual([...MOVING_STATUSES], ['active', 'confirmed']);
|
|
131
167
|
let seen;
|
|
@@ -72,6 +72,17 @@ await t('memberApprovalLine round-trips through parseApprovalMarker', () => {
|
|
|
72
72
|
assert.deepEqual(ap.parseApprovalMarker(line), { kind: 'member', id: 99 });
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
// A vouched takeover (ADR 0333 D4) re-posts the request with the row's words now; it
|
|
76
|
+
// must still lead with the SAME marker, or a 👍 on it would resolve nothing.
|
|
77
|
+
await t('the refiled line carries the new words and round-trips to the same request', () => {
|
|
78
|
+
const line = bcast.memberApprovalLine({ id: 99, github_login: 'carol', display_name: 'Carol', note: 'the real me', refiled: true });
|
|
79
|
+
assert.deepEqual(ap.parseApprovalMarker(line), { kind: 'member', id: 99 });
|
|
80
|
+
assert.match(line, /Carol/);
|
|
81
|
+
assert.match(line, /the real me/);
|
|
82
|
+
assert.match(line, /replace the earlier line for #99/);
|
|
83
|
+
assert.doesNotMatch(bcast.memberApprovalLine({ id: 99, github_login: 'carol' }), /replace/, 'a first line says nothing about replacing');
|
|
84
|
+
});
|
|
85
|
+
|
|
75
86
|
// ---- recordApprovalReaction (orchestration via fakes) ----------------------
|
|
76
87
|
const GOOD_ID = '123456789012345678';
|
|
77
88
|
const archon = { id: 1, rank: 'archon', github_login: 'example-owner' };
|
|
@@ -361,6 +361,26 @@ await t('the real config declares the board-room channel, visible to every build
|
|
|
361
361
|
assert.ok(board.topic && board.topic.length > 0, 'board-room needs a topic');
|
|
362
362
|
});
|
|
363
363
|
|
|
364
|
+
// task 1002536: the AI-news feed is the ZERO-CODE form the owner chose at the
|
|
365
|
+
// 2026-07-29 triage — an off-the-shelf RSS service posts into one channel, and
|
|
366
|
+
// nothing on our host polls anything. The channel is the whole repo surface, so
|
|
367
|
+
// pin it: builders-only by INHERITING the BUILDERS category (spelling `view`
|
|
368
|
+
// out would repeat the category and drift), and a topic that names #ideas,
|
|
369
|
+
// because pasting a link there is the article → idea path (the inbound handler
|
|
370
|
+
// reads only typed text, never a forwarded post, so Forward files no link).
|
|
371
|
+
await t('the real config declares ai-news, builders-only, pointing readers at #ideas', () => {
|
|
372
|
+
const cfg = ch.loadConfig();
|
|
373
|
+
const cat = cfg.categories.find((c) => (c.channels || []).some((x) => x.key === 'ai-news'));
|
|
374
|
+
assert.ok(cat, 'expected a channel keyed ai-news');
|
|
375
|
+
const news = cat.channels.find((x) => x.key === 'ai-news');
|
|
376
|
+
assert.equal(ch.slugify(news.name), 'ai-news', 'the name must slug to its key, or apply creates a duplicate');
|
|
377
|
+
assert.equal(cat.name, 'BUILDERS');
|
|
378
|
+
assert.equal(news.view, undefined, 'ai-news must inherit, not repeat, its category view');
|
|
379
|
+
assert.equal(cat.view, 'xenos', 'so every builder, and only builders, can read it');
|
|
380
|
+
assert.ok(news.topic && news.topic.length <= 1024, 'Discord refuses a topic over 1024 characters');
|
|
381
|
+
assert.match(news.topic, /#ideas/, 'the topic must say where an article becomes an idea');
|
|
382
|
+
});
|
|
383
|
+
|
|
364
384
|
await t('the real config plans cleanly against an empty guild and never deletes', () => {
|
|
365
385
|
const cfg = ch.loadConfig();
|
|
366
386
|
const { ops } = ch.planChannelReconcile({ categories: [], channels: [] }, cfg, CTX_BOT);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// tests/fixtures/gov-seated.mjs — A SEATED GOVERNMENT, as test data for the
|
|
2
|
+
// hall's living-city tests (task 1004717). The hub has seated no Lead and built
|
|
3
|
+
// nothing, so the harness fixtures alone cannot show a Lead's floor, a build-out
|
|
4
|
+
// going up or one waiting on its vote. This builds them in the exact shapes the
|
|
5
|
+
// routes answer — GET /government/leads with five Leads appointed by People votes,
|
|
6
|
+
// Review's Testers room standing, a floor going up at Release and an annex
|
|
7
|
+
// waiting on its sitting at Treasury — and routes the docket to those seats by
|
|
8
|
+
// the server's own rule (modules/government/routing.js). The hall never reads it.
|
|
9
|
+
|
|
10
|
+
import fs from 'node:fs';
|
|
11
|
+
import path from 'node:path';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
|
|
17
|
+
const routing = require(path.join(ROOT, 'modules', 'government', 'routing.js'));
|
|
18
|
+
const fx = (name) => JSON.parse(fs.readFileSync(path.join(ROOT, 'scripts', 'hall-preview', 'fixtures', name + '.json'), 'utf8'));
|
|
19
|
+
|
|
20
|
+
// five Leads (the fixture's viewer, 1000025, leads Planning)
|
|
21
|
+
export const SEATED = Object.freeze([
|
|
22
|
+
{ building: 'planning', lead: '1000025', since: '2026-09-12', scope: 'Goals, priorities, the order of work.' },
|
|
23
|
+
{ building: 'release', lead: '1000027', since: '2026-09-05', scope: 'Release floors and the path to going live.' },
|
|
24
|
+
{ building: 'review', lead: '1000095', since: '2026-09-20', scope: 'Who checks finished work, and how.' },
|
|
25
|
+
{ building: 'treasury', lead: '1000094', since: '2026-09-28', scope: 'Pots and the reserve.' },
|
|
26
|
+
{ building: 'people', lead: '1000070', since: '2026-10-02', scope: 'Who joins, and the ranks they hold.' },
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// GET /government/leads with them in office and the three build-outs on the record
|
|
30
|
+
export function leadsRead() {
|
|
31
|
+
const r = fx('government__leads');
|
|
32
|
+
const roster = fx('builders__roster').builders;
|
|
33
|
+
const at = (k) => r.buildings.find((b) => b.building === k);
|
|
34
|
+
for (const l of SEATED) {
|
|
35
|
+
const p = roster.find((x) => String(x.id) === l.lead) || { id: l.lead };
|
|
36
|
+
at(l.building).lead = { id: 'L' + l.building, building: l.building, builder: { id: String(p.id), login: p.github_login || null, name: p.display_name || null }, charter: l.scope, since: l.since, appointed_by: { item_id: '1' } };
|
|
37
|
+
at(l.building).answers = { seat: 'lead:' + l.building, builder_ids: [String(p.id)], fell_to_owner: false };
|
|
38
|
+
}
|
|
39
|
+
at('review').structure = { standing: [{ id: 2, capsule: 'committee', name: 'Testers', line: 'Who checks finished work.', state: 'built', progress: 1 }], proposed: [] };
|
|
40
|
+
at('release').structure = { standing: [{ id: 1, capsule: 'floor', name: 'Staging floor', line: '', state: 'building', progress: 0.55, allowed: { path: 'at_once', why: 'Its holder builds it out at once.' } }], proposed: [] };
|
|
41
|
+
at('treasury').structure = { standing: [], proposed: [{ capsule: 'annex', name: 'Investments arm', line: 'A wing for the reserve.', docket_id: 'board:77', item_id: '77' }] };
|
|
42
|
+
return r;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// the annex's sitting, open on the docket: nobody has voted yet
|
|
46
|
+
function sitting(seats) {
|
|
47
|
+
return {
|
|
48
|
+
id: 'board:77', source: 'board', building: 'treasury', rule: 'majority', state: 'moving', title: 'Add an annex to Treasury: Investments arm', short: 'an annex',
|
|
49
|
+
opened_at: '2026-10-08T10:00:00.000Z', deadline: null, tally: { for: 0, against: 0, open: seats }, ref: { type: 'board_item', id: '77' }, acts: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// GET /government/docket with the sitting, every matter routed to those seats
|
|
54
|
+
export function docketRouted() {
|
|
55
|
+
const d = fx('government__docket');
|
|
56
|
+
d.matters.push(sitting(d.buildings.find((b) => b.key === 'charter').holders.length));
|
|
57
|
+
const read = leadsRead();
|
|
58
|
+
const seats = { leads: read.buildings.filter((b) => b.lead).map((b) => ({ building: b.building, builder_id: b.lead.builder.id })), owner: read.owner, holders: {} };
|
|
59
|
+
d.matters.forEach((m) => {
|
|
60
|
+
const { route, ...bare } = m;
|
|
61
|
+
m.route = routing.routeMatter(bare, d.viewer.id, seats, Date.parse(d.generated_at));
|
|
62
|
+
});
|
|
63
|
+
return d;
|
|
64
|
+
}
|