@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
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/government/positions.js — THE POSITIONS A BUILDER HOLDS (task 1004713,
|
|
4
|
+
// goal 1000111; ADR 0377).
|
|
5
|
+
//
|
|
6
|
+
// THE OWNER'S RULING (2026-10-09). A seat in the government is HELD, not a picked
|
|
7
|
+
// craft: you hold a seat in the government or you don't. A position is
|
|
8
|
+
// owner the owner seat — every active Archon (leads.ownerOf)
|
|
9
|
+
// lead the Lead in office at a building (government_leads, by vote)
|
|
10
|
+
// seat a seat on a building's own sitting: its decision-rules row puts
|
|
11
|
+
// matters to a sitting of the people its predicate names
|
|
12
|
+
// board a member of the board under the constitution in force
|
|
13
|
+
//
|
|
14
|
+
// WHERE EACH ANSWER COMES FROM. Never a second rule: the owner and Lead seats are
|
|
15
|
+
// asked of routing.resolveSeat (which asks leads.answerFor), the board and each
|
|
16
|
+
// building's sitting of board-membership's membership predicate. This file only
|
|
17
|
+
// folds the answers into one list for one builder. GET /me carries it as
|
|
18
|
+
// `positions` (through the government port), the way it carries `permissions`,
|
|
19
|
+
// and the hall shows the governing surfaces and the Governing mode — named by the
|
|
20
|
+
// first seat — only to someone who holds one. Cosmetic there: every page and
|
|
21
|
+
// route keeps its own server-side gate (ADR 0016).
|
|
22
|
+
//
|
|
23
|
+
// WHAT A POSITION IS NOT. A craft (PATCH /me/disciplines is ungated, so a
|
|
24
|
+
// craft-derived seat would be self-appointment), and an authority: holding one
|
|
25
|
+
// grants no permission (leads.js: the Lead's office confers none).
|
|
26
|
+
//
|
|
27
|
+
// PURE: no db. readPositions() below takes the reads it needs as functions, so
|
|
28
|
+
// routes/government.js asks them per request, uncached, and a test hands fakes.
|
|
29
|
+
|
|
30
|
+
const { resolveSeat, BUILDING_NAMES } = require('./routing');
|
|
31
|
+
const { ownerOf } = require('./leads');
|
|
32
|
+
|
|
33
|
+
const BUILDINGS = Object.freeze(Object.keys(BUILDING_NAMES));
|
|
34
|
+
// Which seat names the Governing mode when a builder holds several.
|
|
35
|
+
const KIND_ORDER = Object.freeze({ lead: 0, owner: 1, seat: 2, board: 3 });
|
|
36
|
+
const KINDS = Object.freeze(Object.keys(KIND_ORDER));
|
|
37
|
+
// Decision-rules values that name no predicate of their own: the board's sitting
|
|
38
|
+
// is the board seat, and a fixed gate or nobody seats no one.
|
|
39
|
+
const NOT_A_PREDICATE = Object.freeze(['board', 'gate', 'nobody']);
|
|
40
|
+
|
|
41
|
+
function titleOf(kind, building) {
|
|
42
|
+
const name = BUILDING_NAMES[building] || '';
|
|
43
|
+
if (kind === 'lead') return `${name} Lead`;
|
|
44
|
+
if (kind === 'seat') return `${name} seat`;
|
|
45
|
+
if (kind === 'owner') return 'Owner';
|
|
46
|
+
return 'Board member';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// positionsOf(builderId, ctx) → { held[], title, has_position }
|
|
50
|
+
// ctx.leads Lead rows in office (db.listLeads)
|
|
51
|
+
// ctx.owner leads.ownerOf(active Archons)
|
|
52
|
+
// ctx.board true when the builder sits on the board in force
|
|
53
|
+
// ctx.sittings [building] — the buildings whose own sitting seats them
|
|
54
|
+
function positionsOf(builderId, ctx) {
|
|
55
|
+
const c = ctx || {};
|
|
56
|
+
const id = builderId == null || builderId === '' ? null : String(builderId);
|
|
57
|
+
const held = [];
|
|
58
|
+
if (id) {
|
|
59
|
+
const seats = { leads: c.leads || [], owner: c.owner || { builders: [] } };
|
|
60
|
+
if (resolveSeat('owner', seats).builder_ids.includes(id)) held.push({ kind: 'owner', seat: 'owner', building: null });
|
|
61
|
+
for (const b of BUILDINGS) {
|
|
62
|
+
const r = resolveSeat(`lead:${b}`, seats);
|
|
63
|
+
if (!r.fell_to_owner && r.builder_ids.includes(id)) held.push({ kind: 'lead', seat: `lead:${b}`, building: b });
|
|
64
|
+
}
|
|
65
|
+
for (const b of BUILDINGS) {
|
|
66
|
+
if ((c.sittings || []).includes(b)) held.push({ kind: 'seat', seat: `board:${b}`, building: b });
|
|
67
|
+
}
|
|
68
|
+
if (c.board === true) held.push({ kind: 'board', seat: 'board', building: null });
|
|
69
|
+
}
|
|
70
|
+
held.sort((a, b) => KIND_ORDER[a.kind] - KIND_ORDER[b.kind]);
|
|
71
|
+
const positions = held.map((p) => ({ ...p, title: titleOf(p.kind, p.building) }));
|
|
72
|
+
return { held: positions, title: positions.length ? positions[0].title : null, has_position: positions.length > 0 };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// readPositions(builderId, reads) → the /me `positions` block, or null when any
|
|
76
|
+
// read failed (unknown is never "holds none": the hall then keeps its nav).
|
|
77
|
+
// reads.listLeads() Lead rows in office
|
|
78
|
+
// reads.listActiveArchons() the owner seat's holders
|
|
79
|
+
// reads.board() the board block in force
|
|
80
|
+
// reads.decisions(board) the resolved decision-rules rows
|
|
81
|
+
// reads.sits(id, membership) does the builder satisfy this predicate (fail closed)
|
|
82
|
+
async function readPositions(builderId, reads) {
|
|
83
|
+
if (builderId == null || builderId === '') return null;
|
|
84
|
+
try {
|
|
85
|
+
const board = reads.board() || {};
|
|
86
|
+
const [leads, archons, decisions, onBoard] = await Promise.all([
|
|
87
|
+
reads.listLeads(), reads.listActiveArchons(), reads.decisions(board), reads.sits(builderId, board.membership),
|
|
88
|
+
]);
|
|
89
|
+
const sittings = [];
|
|
90
|
+
for (const d of decisions || []) {
|
|
91
|
+
if (!d || !d.sitting || !BUILDINGS.includes(d.area)) continue;
|
|
92
|
+
if (NOT_A_PREDICATE.includes(d.decided_by)) continue;
|
|
93
|
+
if (await reads.sits(builderId, d.decided_by)) sittings.push(d.area);
|
|
94
|
+
}
|
|
95
|
+
const p = positionsOf(builderId, { leads, owner: ownerOf(archons), board: onBoard === true, sittings });
|
|
96
|
+
return {
|
|
97
|
+
held: p.held,
|
|
98
|
+
title: p.title,
|
|
99
|
+
has_position: p.has_position,
|
|
100
|
+
from: 'the Leads in office, the owner seat (every active Archon), the board and each building\'s sitting under the constitution in force; never a craft',
|
|
101
|
+
};
|
|
102
|
+
} catch (_) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = { positionsOf, readPositions, titleOf, KINDS, KIND_ORDER };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/government/presets.js — HOW STRICT a project runs its AI agents
|
|
4
|
+
// (task 1004715, goal 1000111 "government is how a project steers its AI").
|
|
5
|
+
//
|
|
6
|
+
// A project picks how strict it is the way it picks a form of government. A
|
|
7
|
+
// STRICTNESS PRESET is a named bundle of laws (laws.js) and the locks they put on
|
|
8
|
+
// every builder's personal settings (builder-settings/law-locks.js):
|
|
9
|
+
//
|
|
10
|
+
// Open workshop few laws: land only, and going live waits for a person.
|
|
11
|
+
// Guild the standard set: Open's law, plus stuck agents skip on, and
|
|
12
|
+
// agents stay on the task (wandering capped at "focused").
|
|
13
|
+
// Factory strict: Guild's laws made tight (wandering fixed at "locked"),
|
|
14
|
+
// plus caps on every model slot, the ask format, and no
|
|
15
|
+
// unattended runs.
|
|
16
|
+
//
|
|
17
|
+
// NOTHING HERE WRITES A LAW, and nothing is seeded. A preset is a CATALOGUE: its
|
|
18
|
+
// laws are in force only once a vote passes them. Switching presets is ONE laws
|
|
19
|
+
// amendment (switchProposal → law-board.proposeLaws), filed and decided like any
|
|
20
|
+
// other: it enacts the preset's laws not yet in force and repeals the laws of the
|
|
21
|
+
// other presets that are, and it touches no law that belongs to no preset (a law
|
|
22
|
+
// the project passed on its own stays). The board passing it is the only write
|
|
23
|
+
// (board.closeItem → law-board.applyPassedLaws, guarded by government_027's
|
|
24
|
+
// trigger).
|
|
25
|
+
//
|
|
26
|
+
// WHICH PRESET IS IN FORCE is READ from the laws in force, never stored: a preset
|
|
27
|
+
// is in force when every one of its laws is (same words, same locks) and no other
|
|
28
|
+
// preset's law is. Anything else — a fresh project with no laws, or a book that
|
|
29
|
+
// mixes presets — is "no preset" (`current: null`), said plainly.
|
|
30
|
+
//
|
|
31
|
+
// WHAT EACH WOULD LOCK. The locks in force after a switch: the preset's own plus
|
|
32
|
+
// those of every law it would keep (the project's own laws). The hall counts them
|
|
33
|
+
// against the viewer's rows (My agents' preset strip, the Charter).
|
|
34
|
+
//
|
|
35
|
+
// ENFORCEMENT is the laws' own: a lock on a personal setting is enforced by the
|
|
36
|
+
// server on every read and write (law-locks.js); the ask format is already
|
|
37
|
+
// required of every AI session's blocker (ADR 0373); "No unattended runs" reaches
|
|
38
|
+
// every session as a law's words, and nothing yet refuses an unattended run.
|
|
39
|
+
//
|
|
40
|
+
// PURE: no db, no requires beyond laws.js. The route hands the laws in force in.
|
|
41
|
+
|
|
42
|
+
const laws = require('./laws');
|
|
43
|
+
|
|
44
|
+
// The laws a preset is made of. A law shared by two presets carries the same
|
|
45
|
+
// words AND the same locks in both, so a switch between them keeps it.
|
|
46
|
+
const LAW = {
|
|
47
|
+
land: { text: 'Land only. Ask before going live', effect_on_ai: 'Agents merge finished work; going live always waits for a person.', locks: [] },
|
|
48
|
+
stuck: { text: 'Stuck? Note it, skip it, keep going', effect_on_ai: 'A stuck agent leaves a note on the task and moves to the next one.', locks: [] },
|
|
49
|
+
focus: { text: 'Stay on the task; note side ideas', effect_on_ai: 'Agents finish the task in hand and leave one note for anything else.', locks: [{ setting: 'wandering', max: 'focused' }] },
|
|
50
|
+
tight: { text: 'Stay on the task; no side trips', effect_on_ai: 'Agents do the task in hand and nothing else.', locks: [{ setting: 'wandering', value: 'locked' }] },
|
|
51
|
+
ask: { text: 'Ask like a human', effect_on_ai: 'Every question to a person is one short card with tap answers, in the ask format.', locks: [] },
|
|
52
|
+
watched: { text: 'No unattended runs', effect_on_ai: 'A person starts and watches every agent run; nothing runs overnight or on a schedule.', locks: [] },
|
|
53
|
+
caps: {
|
|
54
|
+
text: 'Models stay under their caps',
|
|
55
|
+
effect_on_ai: 'The main session runs at most opus, helpers at most sonnet, routine work on haiku.',
|
|
56
|
+
locks: [{ setting: 'model.main', max: 'opus' }, { setting: 'model.subagentDefault', max: 'sonnet' }, { setting: 'model.subagentRoutine', max: 'haiku' }],
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const PRESETS = Object.freeze([
|
|
61
|
+
{ key: 'open', name: 'Open workshop', line: 'Few laws. Every builder keeps their own settings.', laws: [LAW.land] },
|
|
62
|
+
{ key: 'guild', name: 'Guild', line: 'The standard set. Laws lock the settings they name; the rest is yours.', laws: [LAW.land, LAW.stuck, LAW.focus] },
|
|
63
|
+
{ key: 'factory', name: 'Factory', line: 'Strict. Caps on, the ask format enforced, no unattended runs.', laws: [LAW.land, LAW.stuck, LAW.tight, LAW.ask, LAW.watched, LAW.caps] },
|
|
64
|
+
]);
|
|
65
|
+
const KEYS = PRESETS.map((p) => p.key);
|
|
66
|
+
|
|
67
|
+
const norm = (t) => String(t == null ? '' : t).trim().toLowerCase().replace(/\s+/g, ' ');
|
|
68
|
+
const lockKey = (l) => (Object.prototype.hasOwnProperty.call(l, 'value') ? `${l.setting}=${JSON.stringify(l.value)}` : `${l.setting}<=${JSON.stringify(l.max)}`);
|
|
69
|
+
const locksKey = (locks) => (Array.isArray(locks) ? locks : []).filter((l) => l && typeof l.setting === 'string').map(lockKey).sort().join('|');
|
|
70
|
+
// Every preset law's words, in any preset: the laws a switch may repeal.
|
|
71
|
+
const CATALOGUE = new Set(PRESETS.reduce((a, p) => a.concat(p.laws.map((l) => norm(l.text))), []));
|
|
72
|
+
|
|
73
|
+
const presetOf = (key) => PRESETS.find((p) => p.key === key) || null;
|
|
74
|
+
const inForceOnly = (rows) => (Array.isArray(rows) ? rows : []).filter((r) => r && r.id != null && r.repealed_at == null && typeof r.text === 'string');
|
|
75
|
+
const same = (row, law) => norm(row.text) === norm(law.text) && locksKey(row.locks) === locksKey(law.locks);
|
|
76
|
+
|
|
77
|
+
// The preset in force, read from the laws in force (`rows`: db.listLaws() rows).
|
|
78
|
+
// Returns a preset key, or null when none is.
|
|
79
|
+
function currentPreset(rows) {
|
|
80
|
+
const ours = inForceOnly(rows).filter((r) => CATALOGUE.has(norm(r.text)));
|
|
81
|
+
if (!ours.length) return null;
|
|
82
|
+
const hit = PRESETS.find((p) => p.laws.length === ours.length && p.laws.every((law) => ours.some((r) => same(r, law))));
|
|
83
|
+
return hit ? hit.key : null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// The ONE laws amendment a switch to `key` files: enact the preset's laws not in
|
|
87
|
+
// force (as written), repeal every other preset law in force (including one with
|
|
88
|
+
// the right words but other locks — the preset's own version replaces it). A law
|
|
89
|
+
// that is no preset's is never touched. Returns
|
|
90
|
+
// { ok: true, proposal: { enact, repeal }, rationale } or { ok: false, reason }.
|
|
91
|
+
function switchProposal(key, rows) {
|
|
92
|
+
const p = presetOf(key);
|
|
93
|
+
if (!p) return { ok: false, reason: 'unknown_preset', expected: KEYS.join(' | ') };
|
|
94
|
+
const inForce = inForceOnly(rows);
|
|
95
|
+
if (currentPreset(inForce) === key) return { ok: false, reason: 'preset_in_force' };
|
|
96
|
+
const kept = new Set();
|
|
97
|
+
const enact = [];
|
|
98
|
+
p.laws.forEach((law) => {
|
|
99
|
+
const row = inForce.find((r) => same(r, law));
|
|
100
|
+
if (row) kept.add(String(row.id));
|
|
101
|
+
else enact.push({ text: law.text, effect_on_ai: law.effect_on_ai, locks: law.locks.map((l) => ({ ...l })) });
|
|
102
|
+
});
|
|
103
|
+
const repeal = inForce.filter((r) => CATALOGUE.has(norm(r.text)) && !kept.has(String(r.id))).map((r) => String(r.id));
|
|
104
|
+
// the same strict check every laws proposal passes (shape, limits)
|
|
105
|
+
const v = laws.validateLawsProposal({ enact, repeal });
|
|
106
|
+
if (!v.ok) return v;
|
|
107
|
+
const from = currentPreset(inForce);
|
|
108
|
+
const fromName = from ? presetOf(from).name : 'no preset';
|
|
109
|
+
return { ok: true, proposal: v.proposal, rationale: `Switch from ${fromName} to ${p.name}: ${p.line}` };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Every lock in force after a switch to `key`: the preset's own, plus every lock
|
|
113
|
+
// of a law the switch keeps (one that is no preset's).
|
|
114
|
+
function locksAfter(key, rows) {
|
|
115
|
+
const p = presetOf(key);
|
|
116
|
+
if (!p) return [];
|
|
117
|
+
const out = [];
|
|
118
|
+
const add = (law) => (Array.isArray(law.locks) ? law.locks : []).forEach((l) => {
|
|
119
|
+
if (l && typeof l.setting === 'string' && !out.some((x) => x.setting === l.setting)) out.push({ ...l, law_text: law.text });
|
|
120
|
+
});
|
|
121
|
+
inForceOnly(rows).filter((r) => !CATALOGUE.has(norm(r.text))).forEach(add);
|
|
122
|
+
p.laws.forEach(add);
|
|
123
|
+
return out;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// GET /government/presets: the three presets, which one is in force, and for each
|
|
127
|
+
// its laws and the locks a switch to it would leave in force.
|
|
128
|
+
function presetsView(rows) {
|
|
129
|
+
const current = currentPreset(rows);
|
|
130
|
+
return {
|
|
131
|
+
current,
|
|
132
|
+
presets: PRESETS.map((p) => ({
|
|
133
|
+
key: p.key,
|
|
134
|
+
name: p.name,
|
|
135
|
+
line: p.line,
|
|
136
|
+
current: p.key === current,
|
|
137
|
+
laws: p.laws.map((l) => ({ text: l.text, effect_on_ai: l.effect_on_ai, locks: l.locks.map((x) => ({ ...x })) })),
|
|
138
|
+
locks: locksAfter(p.key, rows),
|
|
139
|
+
})),
|
|
140
|
+
changed_by: 'a passed laws vote only: POST /government/board/presets files one',
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
PRESETS,
|
|
146
|
+
currentPreset,
|
|
147
|
+
switchProposal,
|
|
148
|
+
presetsView,
|
|
149
|
+
};
|