@bongos/core 1.17.1 → 1.17.2
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 +244 -264
- package/.claude/skills/blocker-review/SKILL.md +1 -1
- package/.claude/skills/builder-claim/SKILL.md +2 -0
- package/.claude/skills/builder-exit/SKILL.md +2 -2
- package/.claude/skills/builder-redteam/SKILL.md +3 -3
- package/.claude/skills/builder-setup/SKILL.md +2 -2
- package/.claude/skills/builder-start/SKILL.md +1 -1
- package/.claude/skills/dev/SKILL.md +1 -1
- package/.claude/skills/idea-triage/SKILL.md +2 -2
- package/.claude/skills/ideate/SKILL.md +1 -1
- package/.claude/skills/merge-mode/SKILL.md +1 -1
- package/.claude/skills/new-project/SKILL.md +3 -0
- package/.claude/skills/paint/SKILL.md +1 -1
- package/GOVERNANCE.md +1 -1
- package/README.md +1 -1
- package/docs/adr/0001-stack-choice-single-vps.md +1 -1
- package/docs/adr/0008-google-chat-oauth-user-auth.md +1 -1
- package/docs/adr/0019-red-team-bounty-payout-table.md +3 -3
- package/docs/adr/0020-security-model-public-stub.md +1 -1
- package/docs/adr/0023-kind-multipliers-and-idea-bonus.md +1 -1
- package/docs/adr/0024-cloneable-repo-local-first-memory.md +1 -1
- package/docs/adr/0026-bfg-memory-hygiene-and-cross-builder-learning.md +4 -4
- package/docs/adr/0027-bfg-session-inefficiency-evaluator.md +3 -3
- package/docs/adr/0030-builders-hall-live-updates.md +1 -1
- package/docs/adr/0031-cloud-dev-environments-for-builders.md +4 -4
- package/docs/adr/0032-discord-mirror-of-gds.md +2 -2
- package/docs/adr/0034-thetes-graduated-newcomer-rank.md +1 -1
- package/docs/adr/0044-mediterranean-palette-replacement.md +1 -1
- package/docs/adr/0044-per-box-live-game-preview.md +1 -1
- package/docs/adr/0054-session-token-reward-cost-plus.md +10 -10
- package/docs/adr/0057-container-cost-ledger.md +3 -3
- package/docs/adr/0062-medusa-instance-model-decoupling.md +4 -4
- package/docs/adr/0065-agpl-nonprofit-ai-first-governance.md +2 -2
- package/docs/adr/0085-remove-default-per-builder-spend-cap.md +1 -1
- package/docs/adr/0086-goal-scoped-work-hierarchy.md +1 -1
- package/docs/adr/0096-require-reward-before-workable.md +9 -9
- package/docs/adr/0097-retroactive-reward-backfill.md +3 -3
- package/docs/adr/0100-two-repo-instance-model.md +1 -1
- package/docs/adr/0101-goal-hierarchy-phase-5-judge-membership-reward.md +3 -3
- package/docs/adr/0103-core-first-extraction-cloud-bongos-trunk.md +3 -3
- package/docs/adr/0107-module-upstreaming-contribution-to-core-catalog.md +1 -1
- package/docs/adr/0120-pay-on-land-and-builder-owned-rebase-gate.md +6 -6
- package/docs/adr/0136-update-channel-subscription-policy.md +51 -0
- package/docs/adr/0137-upstream-publish-policy-manual-owner-gated.md +40 -0
- package/docs/adr/0138-bundled-module-deps-declared-per-module.md +45 -0
- package/docs/adr/README.md +9 -6
- package/docs/architecture.md +2 -2
- package/docs/branding-contract.md +5 -4
- package/docs/canonical-permissions.md +1 -1
- package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
- package/docs/file-map.md +18 -17
- package/docs/handoff-template.md +2 -2
- package/docs/modules-contract.md +6 -1
- package/docs/onboarding/diagrams/README.md +7 -7
- package/docs/onboarding/drachmae-and-karma.md +18 -18
- package/docs/onboarding/primer.md +9 -9
- package/docs/recipes/audit-replay.md +2 -2
- package/docs/recipes/builder-box-lifecycle.md +2 -2
- package/docs/recipes/claude-design-loop.md +1 -1
- package/docs/recipes/ops-gotchas.md +1 -1
- package/docs/recipes/private-npm-distribution.md +17 -0
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/upgrading-the-core.md +55 -0
- package/docs/research/cursor-competitive-analysis.md +1 -1
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +1 -1
- package/modules/discord/module.json +2 -1
- package/modules/economy/achievements.js +9 -1
- package/modules/economy/credits.js +10 -1
- package/modules/hall-ui/public/builders.js +71 -4
- package/modules/hall-ui/public/goal-inbox.js +191 -0
- package/modules/hall-ui/public/hall-widgets.js +14 -1
- package/modules/hall-ui/public/index.html +3 -0
- package/modules/hall-ui/public/new-project-wizard.js +1 -0
- package/modules/hall-ui/public/style.css +43 -0
- package/modules/provisioning/CLAUDE.md +15 -0
- package/modules/provisioning/migrations/provisioning_001_tables.sql +160 -0
- package/modules/provisioning/migrations/provisioning_002_standalone_shape.sql +11 -0
- package/modules/provisioning/migrations/provisioning_003_github_app_manifest.sql +95 -0
- package/modules/provisioning/module.json +17 -0
- package/modules/provisioning/onboard-plan.js +37 -0
- package/modules/provisioning/provisioning.js +539 -0
- package/modules/provisioning/rate-limit.js +35 -0
- package/modules/provisioning/routes/provisioning.js +607 -0
- package/modules/provisioning/tests/provisioning.mjs +232 -0
- package/modules/provisioning/ui/.gitkeep +0 -0
- package/modules/security/secret-scrub.js +10 -0
- package/package-lock.json +2 -2
- package/package.json +7 -2
- package/scripts/discord/README.md +1 -1
- package/scripts/gds/claim.js +98 -48
- package/scripts/gds/claude-materialize.js +30 -9
- package/scripts/gds/cutover-codemod.js +241 -0
- package/scripts/gds/fitness.js +54 -0
- package/scripts/gds/init.js +59 -3
- package/scripts/gds/lint-infra-exec.js +70 -0
- package/scripts/gds/lint-workflow-exec.js +63 -0
- package/scripts/gds/mirror-lineage-check.js +14 -5
- package/scripts/gds/mirror-redact.js +8 -0
- package/scripts/gds/onboard.js +4 -0
- package/scripts/gds/package-core.js +18 -3
- package/scripts/gds/publish-manifest.js +60 -2
- package/scripts/gds/run-routine.js +51 -13
- package/scripts/gds/run-unit-tests.js +4 -0
- package/scripts/gds/update-channel.js +186 -0
- package/scripts/gds/upgrade.js +116 -9
- package/src/bongos/auth.js +14 -1
- package/src/bongos/serve-internal.js +10 -1
- package/src/branding.js +1 -0
- package/src/module-api.js +12 -1
- package/src/module-loader/loader.js +36 -4
- package/src/module-loader/manifest-schema.js +16 -1
- package/tests/branding.mjs +11 -0
- package/tests/claim_error_surface.mjs +107 -0
- package/tests/claude_materialize.mjs +20 -0
- package/tests/consumer_layout_boot.mjs +302 -0
- package/tests/cutover_codemod.mjs +184 -0
- package/tests/device_flow_start.mjs +25 -2
- package/tests/hook_core_resolve.mjs +125 -0
- package/tests/infra_exec_paths.mjs +104 -0
- package/tests/init.mjs +47 -0
- package/tests/mirror_lineage_proof.mjs +14 -0
- package/tests/module_loader.mjs +42 -0
- package/tests/module_manifest.mjs +19 -0
- package/tests/onboard.mjs +13 -0
- package/tests/package_core.mjs +8 -0
- package/tests/provisioning_github_app_routes.mjs +15 -0
- package/tests/publish_manifest.mjs +52 -1
- package/tests/run_routine.mjs +26 -1
- package/tests/run_routine_two_root.mjs +77 -0
- package/tests/scrubber_corpus.mjs +17 -0
- package/tests/update_channel.mjs +186 -0
- package/tests/upgrade.mjs +131 -0
- package/tests/workflow_exec_paths.mjs +98 -0
- package/docs/design/container-exterior-tiles.md +0 -49
- package/docs/design/container-exterior.md +0 -81
- package/docs/design/container-interior.md +0 -77
- package/docs/design/world-map-scroll.md +0 -41
- package/docs/recipes/claude-gemini-bongos-doors.md +0 -155
- package/docs/recipes/cloudbongos-staging-gate.md +0 -139
- package/docs/recipes/cloudbongos-standup.md +0 -111
- package/docs/recipes/parallel-load-results-2026-05-30.md +0 -63
- package/docs/recipes/parallel-load-test.md +0 -32
- package/docs/recipes/pixel-art-pipeline.md +0 -411
- package/docs/recipes/staging-data.md +0 -50
- package/migrations/instance/018_pms_v3_planning_seed.sql +0 -131
- package/migrations/instance/080_gds_v4_planning_seed.sql +0 -75
- package/migrations/instance/110_medusa_planning_seed.sql +0 -70
- package/migrations/instance/120_bongos_v1_planning_seed.sql +0 -69
- package/migrations/instance/144_bongos_v1_module_system_criterion.sql +0 -26
- package/migrations/instance/159_bongos_v1_goal_hierarchy_criterion.sql +0 -29
- package/migrations/instance/172_v2_world_planning_seed.sql +0 -163
- package/migrations/instance/173_bongos_v1_c7_core_modularized.sql +0 -33
- package/migrations/instance/182_gdsv4_bongos_consolidation.sql +0 -132
- package/migrations/instance/183_gdsv4_carryover_themed_goals.sql +0 -116
- package/migrations/instance/186_bongos_v1_governance_planning_seed.sql +0 -69
- package/scripts/gds/art-stage.js +0 -152
- package/server.js +0 -212
- package/tests/art_key.mjs +0 -121
- package/tests/day_cycle.mjs +0 -197
- package/tests/snapshot_picker.mjs +0 -233
- package/tests/sprite_facing.mjs +0 -35
- package/tests/terrain_picker.mjs +0 -298
- package/tests/turn_in_place.mjs +0 -280
- package/tests/walkability.mjs +0 -188
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
-- Migration 183 — Five themed goals for the GDS-V4 carryover (BONGOS-V1)
|
|
2
|
-
-- ============================================================================
|
|
3
|
-
-- Follow-up to ADR 0103 (migration 182): the ~72 still-relevant GDS-V4 tasks
|
|
4
|
-
-- carried into BONGOS-V1 were left goal-less. This gives the platform debt a
|
|
5
|
-
-- spine — five OUTCOME goals (ADR 0086 goal tier), each with done-when criteria,
|
|
6
|
-
-- and reparents the tasks under them. Evergreen chores + grader/economy/misc +
|
|
7
|
-
-- recent non-debt work are deliberately LEFT goal-less (they are not carryover
|
|
8
|
-
-- outcomes). Owner-directed 2026-07-02 (with Lars).
|
|
9
|
-
--
|
|
10
|
-
-- Goals are Archon-owned (created_by = 1), scope_modules empty (grouping goals,
|
|
11
|
-
-- not scope-walled; tasks attach here via this migration, not the API route).
|
|
12
|
-
-- Criteria start satisfied=false, so each goal stays 'open' until its work lands.
|
|
13
|
-
--
|
|
14
|
-
-- REVERSIBLE: only INSERTs (goals + criteria) and task goal_id UPDATEs. To undo,
|
|
15
|
-
-- NULL the goal_id on the affected tasks and DELETE the 5 goals + their criteria.
|
|
16
|
-
-- Idempotent: goal INSERTs are title-guarded, criteria use ON CONFLICT DO NOTHING,
|
|
17
|
-
-- task UPDATEs only touch still-goal-less rows.
|
|
18
|
-
-- ============================================================================
|
|
19
|
-
|
|
20
|
-
BEGIN;
|
|
21
|
-
|
|
22
|
-
DO $$
|
|
23
|
-
DECLARE gid bigint;
|
|
24
|
-
BEGIN
|
|
25
|
-
-- ---- G1: Build-pipeline integrity --------------------------------------
|
|
26
|
-
SELECT id INTO gid FROM goals WHERE version_id='BONGOS-V1' AND title='Build-pipeline integrity' LIMIT 1;
|
|
27
|
-
IF gid IS NULL THEN
|
|
28
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
29
|
-
VALUES ('BONGOS-V1', 'Build-pipeline integrity',
|
|
30
|
-
'GDS-V4 carryover (ADR 0103): the system that builds Cloud Bongos never fails silently or carries known integrity holes — ship/merge/deploy, CI, the nightly backup, and the SEC-863 deploy-gate.',
|
|
31
|
-
'{}', 1, 'open')
|
|
32
|
-
RETURNING id INTO gid;
|
|
33
|
-
END IF;
|
|
34
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
35
|
-
('BONGOS-V1','gcarry-pipe-loud','Every stage of ship->merge->deploy and the nightly DB backup fails LOUDLY (surfaces in CI or journald), never silently.',gid,10),
|
|
36
|
-
('BONGOS-V1','gcarry-pipe-sec863','The SEC-863 deploy-gate has no known bypass: audit range anchored to last-successful-deploy, sub-Metic protected-path claims blocked at claim time, and each protected-path commit tied to a task in the main audit.',gid,20),
|
|
37
|
-
('BONGOS-V1','gcarry-pipe-smoke','A full-stack integration smoke (server + Postgres) runs as a required PR check, and the flaky unit harness is isolated so a green run is trustworthy.',gid,30)
|
|
38
|
-
ON CONFLICT (version_id, criterion_id) DO NOTHING;
|
|
39
|
-
UPDATE tasks SET goal_id=gid, updated_at=now()
|
|
40
|
-
WHERE version_id='BONGOS-V1' AND goal_id IS NULL
|
|
41
|
-
AND id IN (1426,1427,1429,1431,1432,1433,1435,1436,1462,1479,1641,1703,1219,861,862,1140,1437,1440,1461,1434,723);
|
|
42
|
-
|
|
43
|
-
-- ---- G2: Builder experience — hall, dev box, and skills ----------------
|
|
44
|
-
SELECT id INTO gid FROM goals WHERE version_id='BONGOS-V1' AND title='Builder experience — hall, dev box, and skills' LIMIT 1;
|
|
45
|
-
IF gid IS NULL THEN
|
|
46
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
47
|
-
VALUES ('BONGOS-V1', 'Builder experience — hall, dev box, and skills',
|
|
48
|
-
'GDS-V4 carryover (ADR 0103): everything a builder directly touches — the builders hall UI, the dev box + sandbox, and the day-to-day skills — works without needing Claude for routine actions.',
|
|
49
|
-
'{}', 1, 'open')
|
|
50
|
-
RETURNING id INTO gid;
|
|
51
|
-
END IF;
|
|
52
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
53
|
-
('BONGOS-V1','gcarry-bx-hall','Core task actions (claim, release, ship-trigger, triage) and a task board work in the builders hall WITHOUT invoking the LLM.',gid,10),
|
|
54
|
-
('BONGOS-V1','gcarry-bx-identity','A builder can view and edit their own profile, see works-completed, and read an efficiency-aware leaderboard.',gid,20),
|
|
55
|
-
('BONGOS-V1','gcarry-bx-onramp','Newcomers never dead-end: auth/re-auth is browser-clickable and self-explanatory, and the dev box + sandbox are discoverable.',gid,30),
|
|
56
|
-
('BONGOS-V1','gcarry-bx-box','A builder dev box + sandbox is robust: uncommitted work is not lost on reclaim, host-keys survive rebuilds, and staging is cross-platform.',gid,40)
|
|
57
|
-
ON CONFLICT (version_id, criterion_id) DO NOTHING;
|
|
58
|
-
UPDATE tasks SET goal_id=gid, updated_at=now()
|
|
59
|
-
WHERE version_id='BONGOS-V1' AND goal_id IS NULL
|
|
60
|
-
AND id IN (758,751,759,424,1466,1467,1468,1469,1471,1473,1474,1475,1476,1481,1482,594,1472,1715,1696,1650,1470,1391,1165,837,843,885,1477,1493,1430,1665);
|
|
61
|
-
|
|
62
|
-
-- ---- G3: Cloud Bongos productization and self-host ---------------------
|
|
63
|
-
SELECT id INTO gid FROM goals WHERE version_id='BONGOS-V1' AND title='Cloud Bongos productization and self-host' LIMIT 1;
|
|
64
|
-
IF gid IS NULL THEN
|
|
65
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
66
|
-
VALUES ('BONGOS-V1', 'Cloud Bongos productization and self-host',
|
|
67
|
-
'GDS-V4 carryover (ADR 0103): someone other than the owner can run Cloud Bongos — a second instance, a real update channel, a clean vanilla boot, and hosting as a module.',
|
|
68
|
-
'{}', 1, 'open')
|
|
69
|
-
RETURNING id INTO gid;
|
|
70
|
-
END IF;
|
|
71
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
72
|
-
('BONGOS-V1','gcarry-prod-customer2','A second instance (Mercury) runs live on a pinned Cloud Bongos core, with its own repo and GDS.',gid,10),
|
|
73
|
-
('BONGOS-V1','gcarry-prod-upgrade','bongos upgrade is a real update-consumption channel for a two-repo consumer (not a stub).',gid,20),
|
|
74
|
-
('BONGOS-V1','gcarry-prod-vanilla','A vanilla docker compose up boots clean: no instance/product seed data runs and no host-brand leaks, both gated in CI.',gid,30),
|
|
75
|
-
('BONGOS-V1','gcarry-prod-hosting','Instance hosting (provision, manage, cost read-out) is available as a Cloud Bongos module.',gid,40)
|
|
76
|
-
ON CONFLICT (version_id, criterion_id) DO NOTHING;
|
|
77
|
-
UPDATE tasks SET goal_id=gid, updated_at=now()
|
|
78
|
-
WHERE version_id='BONGOS-V1' AND goal_id IS NULL
|
|
79
|
-
AND id IN (1689,1690,1326,1704,1706,1485,1619,1620,1621,1388,1439);
|
|
80
|
-
|
|
81
|
-
-- ---- G4: Findability — the grep path (ADR 0095) ------------------------
|
|
82
|
-
SELECT id INTO gid FROM goals WHERE version_id='BONGOS-V1' AND title='Findability — the grep path (ADR 0095)' LIMIT 1;
|
|
83
|
-
IF gid IS NULL THEN
|
|
84
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
85
|
-
VALUES ('BONGOS-V1', 'Findability — the grep path (ADR 0095)',
|
|
86
|
-
'GDS-V4 carryover (ADR 0103 / ADR 0095): a repo agents navigate by literal search, with meaning written into the source rather than an embedding index.',
|
|
87
|
-
'{}', 1, 'open')
|
|
88
|
-
RETURNING id INTO gid;
|
|
89
|
-
END IF;
|
|
90
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
91
|
-
('BONGOS-V1','gcarry-find-docstrings','The docstring / module-header convention is rolled out across the codebase and guarded by a fitness/density check so it cannot rot.',gid,10),
|
|
92
|
-
('BONGOS-V1','gcarry-find-why','Code rationale (the why) is surfaced forward-facing, and the AST/tree-sitter repo-map upgrade is decided.',gid,20)
|
|
93
|
-
ON CONFLICT (version_id, criterion_id) DO NOTHING;
|
|
94
|
-
UPDATE tasks SET goal_id=gid, updated_at=now()
|
|
95
|
-
WHERE version_id='BONGOS-V1' AND goal_id IS NULL
|
|
96
|
-
AND id IN (1654,1655,1656,1657,1659,848);
|
|
97
|
-
|
|
98
|
-
-- ---- G5: Autonomy groundwork -------------------------------------------
|
|
99
|
-
SELECT id INTO gid FROM goals WHERE version_id='BONGOS-V1' AND title='Autonomy groundwork' LIMIT 1;
|
|
100
|
-
IF gid IS NULL THEN
|
|
101
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
102
|
-
VALUES ('BONGOS-V1', 'Autonomy groundwork',
|
|
103
|
-
'GDS-V4 carryover (ADR 0103): safe unattended routines — guardrails first, then arm the safe set.',
|
|
104
|
-
'{}', 1, 'open')
|
|
105
|
-
RETURNING id INTO gid;
|
|
106
|
-
END IF;
|
|
107
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
108
|
-
('BONGOS-V1','gcarry-auto-guardrails','An off-droplet spend ceiling, a scoped file-only automation token, and a hall kill-switch all exist.',gid,10),
|
|
109
|
-
('BONGOS-V1','gcarry-auto-armed','The cloud-routine execution environment is verified and the safe routine set is armed behind the autonomy flag.',gid,20)
|
|
110
|
-
ON CONFLICT (version_id, criterion_id) DO NOTHING;
|
|
111
|
-
UPDATE tasks SET goal_id=gid, updated_at=now()
|
|
112
|
-
WHERE version_id='BONGOS-V1' AND goal_id IS NULL
|
|
113
|
-
AND id IN (1393,1394,1395,1396);
|
|
114
|
-
END $$;
|
|
115
|
-
|
|
116
|
-
COMMIT;
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
-- 186_bongos_v1_governance_planning_seed.sql (migrations/instance/ — OTB scope-truth)
|
|
2
|
-
-- Planning session (2026-07-03): the BONGOS-V1 "Governance — configurable roles &
|
|
3
|
-
-- permissions" goal + its six done-when criteria.
|
|
4
|
-
--
|
|
5
|
-
-- WHY THIS LIVES IN migrations/instance/ (ADR 0105 / selfhost-boot gate):
|
|
6
|
-
-- versions/goals/done_when_criteria are OTB scope-truth, NOT platform-universal
|
|
7
|
-
-- schema. In migrations/ root they would seed a vanilla third-party self-host with
|
|
8
|
-
-- OTB's governance criteria (the selfhost-boot VANILLA assertion: a fresh DB seeds
|
|
9
|
-
-- ZERO scope-truth). Here, migrate.sh applies the file only when the OWNING instance
|
|
10
|
-
-- opts in (GDS_APPLY_INSTANCE_SEEDS=1); a vanilla self-host skips the whole dir.
|
|
11
|
-
--
|
|
12
|
-
-- DR-SAFE: the goal was first created via the API (a runtime row), so a from-scratch
|
|
13
|
-
-- owning-instance replay would have no goal to parent the criteria under. This
|
|
14
|
-
-- migration recreates the goal idempotently (title-guard) so replay stays whole and
|
|
15
|
-
-- so it is a no-op on prod (where goal 30 already exists). Mirrors migration 183.
|
|
16
|
-
--
|
|
17
|
-
-- The 25 tasks (BV1.R90-R114) + dependency graph are seeded by
|
|
18
|
-
-- scripts/gds/seed-governance-tasks.js after this deploys (so the slugs resolve).
|
|
19
|
-
|
|
20
|
-
BEGIN;
|
|
21
|
-
|
|
22
|
-
DO $do$
|
|
23
|
-
DECLARE gid bigint;
|
|
24
|
-
BEGIN
|
|
25
|
-
SELECT id INTO gid FROM goals
|
|
26
|
-
WHERE version_id = 'BONGOS-V1'
|
|
27
|
-
AND title = 'Governance — configurable roles & permissions'
|
|
28
|
-
LIMIT 1;
|
|
29
|
-
|
|
30
|
-
IF gid IS NULL THEN
|
|
31
|
-
INSERT INTO goals (version_id, title, description, scope_modules, created_by, status)
|
|
32
|
-
VALUES (
|
|
33
|
-
'BONGOS-V1',
|
|
34
|
-
'Governance — configurable roles & permissions',
|
|
35
|
-
$desc$A core, Discord-style roles-and-permissions system where PERMISSIONS are the atomic primitive and ranks become seeded roles. Adds first-class principals (machine identities), session-scopes (box tokens), ownership-scoped permissions, and a governance-owned protected-surface registry (policy owns / substrate enforces). Capability lives in core (modules/governance/, default:true); per-project settings split config/governance.json (over neutral) + governance_* DB tables. Ships an Archon-only Governance hall tab (assign + toggle; system permissions locked). Preserves the ADR 0016 trust boundary end-to-end. Protected-scope (kernel/lifecycle) -> Archon-mediated.$desc$,
|
|
36
|
-
'{kernel,lifecycle,hall-ui}',
|
|
37
|
-
1,
|
|
38
|
-
'open'
|
|
39
|
-
)
|
|
40
|
-
RETURNING id INTO gid;
|
|
41
|
-
END IF;
|
|
42
|
-
|
|
43
|
-
INSERT INTO done_when_criteria (version_id, criterion_id, criterion_md, goal_id, sort_order) VALUES
|
|
44
|
-
('BONGOS-V1', 'gov-perms-atom',
|
|
45
|
-
$md$**Permissions become the atom; ranks become seeded roles — with zero day-one behavior change.** Authority stops being a fixed rank threshold and becomes a resolved set of granular permissions. Done when: a code-defined permission catalog exists (each permission flagged `system` or not, since permissions map to real routes and cannot be UI-invented); a per-request, uncached resolver returns a builder's effective permission set as the union of their roles (preserving ADR 0016); four seeded roles (`xenos`/`thetes`/`metic`/`archon`) reproduce today's thresholds exactly; a `requirePermission()` gate exists alongside `requireRank`; and a parity test proves every builder resolves to exactly today's powers (behavior-identical at ship).$md$,
|
|
46
|
-
gid, 20),
|
|
47
|
-
('BONGOS-V1', 'gov-gates-permissioned',
|
|
48
|
-
$md$**Every DB-resolvable authority gate reads permissions, not rank.** Rank stops being special everywhere the server decides access. Done when: the ~60 HTTP route gates migrate `requireRank(...)` -> `requirePermission(...)`; claim eligibility (`xenosClaimAllowed`/`rankAllowsTask`/per-task floor), the page-level gates, and the wandering-prefs clamp are re-expressed as permissions; the ~6 duplicated numeric rank-tier maps collapse into the single resolver; and the `route-rank-check` fitness function is updated to assert permission gates so nothing silently regresses.$md$,
|
|
49
|
-
gid, 21),
|
|
50
|
-
('BONGOS-V1', 'gov-full-model',
|
|
51
|
-
$md$**The complete authority model is represented — not just role -> permission.** The 2026-07-03 authority sweep proved Cloud Bongos authority runs on ~7 mechanisms, not one. Done when: first-class **principals** (machine identities like the BFG memory-writer and the Discord bot, keyed on `system_role`, never UI-grantable) are modeled; **session-scopes** (box-scoped tokens, deny-by-default) are a first-class capability distinct from a builder's roles; **ownership-scoped** permissions ("act on *own* X" — own box/memory/credentials/claim) are expressible; and the **protected-surface registry** (today's `PROTECTED_GLOBS` / `EXPECTED_RANKS`) becomes governance-owned *data* while the out-of-band substrate (git hooks, CI, GitHub branch protection, SSH) stays the *enforcer* — policy owns, substrate enforces.$md$,
|
|
52
|
-
gid, 22),
|
|
53
|
-
('BONGOS-V1', 'gov-core-project-split',
|
|
54
|
-
$md$**Capability lives in core; settings live per project.** The governance engine is portable to any Cloud Bongos instance; each project keeps its own governance data. Done when: `modules/governance/` is a `default:true` core module with its own `governance_*` additive migrations and a resolver exposed through a kernel port; role *templates/defaults* ship as `config/governance.json` resolved over a `config/governance.neutral.json` (the branding pattern); live role/permission/assignment *data* lives in the DB; and a fresh vanilla instance boots with sane neutral defaults and no OTB identity.$md$,
|
|
55
|
-
gid, 23),
|
|
56
|
-
('BONGOS-V1', 'gov-archon-tab',
|
|
57
|
-
$md$**The Archon Governance tab — assign + toggle.** The owner can configure governance from the builders' hall, Discord-style. Done when: an Archon-only Governance tab lets you view/edit the ~12 operational permissions per role, create custom roles, and assign roles to builders; `system` permissions render **locked** (visible but not grantable); and the tab is gated in depth at the page layer (`serve-internal`), the API layer (`governance.manage`), and pinned in `route-rank-check`.$md$,
|
|
58
|
-
gid, 24),
|
|
59
|
-
('BONGOS-V1', 'gov-boundary-proof',
|
|
60
|
-
$md$**The trust boundary provably holds — with a live proof and the docs corrected.** This criterion de-risks the whole goal: no amount of governance configuration can weaken production safety. Done when: an abuse-matrix test proves no configurable role/permission can grant a low-trust builder deploy or protected-path access; a live demonstration creates a custom role granting an operational permission (it works) while a `system` permission stays enforced by substrate; an ADR records the "permissions-as-atom + principals + session-scopes + protected-surface registry, policy-owns/substrate-enforces" decision; and the stale ADR 0043 / canonical-permissions.md branch-protection claims are fixed (GitHub branch protection is now live).$md$,
|
|
61
|
-
gid, 25)
|
|
62
|
-
ON CONFLICT (version_id, criterion_id) DO UPDATE
|
|
63
|
-
SET criterion_md = EXCLUDED.criterion_md,
|
|
64
|
-
goal_id = EXCLUDED.goal_id,
|
|
65
|
-
sort_order = EXCLUDED.sort_order;
|
|
66
|
-
END
|
|
67
|
-
$do$;
|
|
68
|
-
|
|
69
|
-
COMMIT;
|
package/scripts/gds/art-stage.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// scripts/gds/art-stage.js — after the art pipeline writes modules/art-pipeline/generated/tiles/,
|
|
3
|
-
// rebuild the atlas the game client loads and surface it on the builder's own
|
|
4
|
-
// sandbox live preview (ADR 0044/0052) in ONE command (#1067). Today the loop is
|
|
5
|
-
// generate -> manually rebuild the atlas -> manually restart -> manually refresh;
|
|
6
|
-
// this collapses the last three into one step (and #1065's auto-reload then
|
|
7
|
-
// refreshes the open tab on its own).
|
|
8
|
-
//
|
|
9
|
-
// Internal-track tooling. It does NOT regenerate art (generation costs $ and is a
|
|
10
|
-
// separate, reviewed step), does NOT touch prod, and does NOT commit anything.
|
|
11
|
-
//
|
|
12
|
-
// node scripts/gds/art-stage.js # rebuild atlas + surface on the sandbox
|
|
13
|
-
// node scripts/gds/art-stage.js --no-build # skip the atlas rebuild, just restart + print
|
|
14
|
-
// node scripts/gds/art-stage.js --tiles "a b c" # label the affected tiles explicitly
|
|
15
|
-
//
|
|
16
|
-
// On a BOX the preview serves /workspace (box-game-preview.service), but a Claude
|
|
17
|
-
// session edits a git worktree, so we copy the rebuilt public/assets/tiles into
|
|
18
|
-
// /workspace before restarting (self-contained; does not depend on the #1068 ship
|
|
19
|
-
// gate's worktree sync). It deliberately does NOT write the sandbox-stage REVIEW
|
|
20
|
-
// marker — surfacing art for a look is not the same as recording a ship sign-off.
|
|
21
|
-
|
|
22
|
-
const path = require('node:path');
|
|
23
|
-
const fs = require('node:fs');
|
|
24
|
-
const { execFileSync } = require('node:child_process');
|
|
25
|
-
const sandbox = require('./sandbox-stage');
|
|
26
|
-
|
|
27
|
-
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
28
|
-
const BOX_SERVED_ROOT = '/workspace';
|
|
29
|
-
const PY = process.env.PYTHON || 'python3';
|
|
30
|
-
const HEALTH_TIMEOUT_MS = 90_000;
|
|
31
|
-
const HEALTH_POLL_MS = 2_000;
|
|
32
|
-
|
|
33
|
-
// Rebuild public/assets/tiles + the content-hashed atlas from modules/art-pipeline/generated/tiles/.
|
|
34
|
-
// build_tilesheet.py copies the per-tile PNGs and calls build_atlas at its tail.
|
|
35
|
-
function rebuildAtlas(deps = {}) {
|
|
36
|
-
const run = deps.execFileSync || execFileSync;
|
|
37
|
-
run(PY, [path.join(REPO_ROOT, 'modules/art-pipeline/pipeline/build_tilesheet.py')], { cwd: REPO_ROOT, stdio: 'inherit' });
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// On a box, the preview serves /workspace/public, not the session worktree. Copy
|
|
41
|
-
// the rebuilt tiles tree across so the sandbox shows the new art. No-op when the
|
|
42
|
-
// session checkout IS the served root (running directly in /workspace, or a
|
|
43
|
-
// laptop where the local preview serves the worktree). Best-effort: returns
|
|
44
|
-
// { copied, reason? }.
|
|
45
|
-
function copyAtlasToServed(deps = {}) {
|
|
46
|
-
const f = deps.fs || fs;
|
|
47
|
-
const servedRoot = deps.servedRoot || BOX_SERVED_ROOT;
|
|
48
|
-
const repoRoot = deps.repoRoot || REPO_ROOT;
|
|
49
|
-
if (path.resolve(repoRoot) === path.resolve(servedRoot)) return { copied: false, reason: 'session checkout is the served checkout' };
|
|
50
|
-
const from = path.join(repoRoot, 'public/assets/tiles');
|
|
51
|
-
const to = path.join(servedRoot, 'public/assets/tiles');
|
|
52
|
-
if (!f.existsSync(from) || !f.existsSync(servedRoot)) return { copied: false, reason: 'no served checkout / no tiles to copy' };
|
|
53
|
-
try {
|
|
54
|
-
f.cpSync(from, to, { recursive: true });
|
|
55
|
-
return { copied: true, to };
|
|
56
|
-
} catch (e) {
|
|
57
|
-
return { copied: false, reason: e.message };
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// The tiles this run touched: an explicit --tiles list (the orchestrator knows
|
|
62
|
-
// the family members) or a git diff of public/assets/tiles (standalone runs).
|
|
63
|
-
function affectedTiles(explicit, deps = {}) {
|
|
64
|
-
if (explicit && explicit.trim()) return explicit.trim().split(/\s+/);
|
|
65
|
-
const run = deps.execFileSync || execFileSync;
|
|
66
|
-
try {
|
|
67
|
-
const out = run('git', ['status', '--porcelain', '--', 'public/assets/tiles'], { cwd: deps.repoRoot || REPO_ROOT, encoding: 'utf8' });
|
|
68
|
-
return out.split('\n').map((l) => l.slice(3).trim()).filter(Boolean);
|
|
69
|
-
} catch {
|
|
70
|
-
return [];
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
async function waitHealthy(healthUrl, deps = {}) {
|
|
75
|
-
const fetchImpl = deps.fetchImpl || fetch;
|
|
76
|
-
const deadline = Date.now() + (deps.timeoutMs || HEALTH_TIMEOUT_MS);
|
|
77
|
-
while (Date.now() < deadline) {
|
|
78
|
-
try {
|
|
79
|
-
const res = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5_000) });
|
|
80
|
-
if (res.status < 500) return true;
|
|
81
|
-
} catch { /* keep polling */ }
|
|
82
|
-
await new Promise((r) => setTimeout(r, deps.pollMs || HEALTH_POLL_MS));
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function printSurfacedBlock(url, tiles, log = console.log) {
|
|
88
|
-
const bar = '─'.repeat(64);
|
|
89
|
-
log('');
|
|
90
|
-
log(bar);
|
|
91
|
-
log(' 🎨 NEW ART IS LIVE ON YOUR SANDBOX');
|
|
92
|
-
log('');
|
|
93
|
-
log(` ${url}`);
|
|
94
|
-
log('');
|
|
95
|
-
if (tiles.length) log(` Affected: ${tiles.join(', ')}`);
|
|
96
|
-
log(' Open the link to see it in-game. (With the auto-reload from task 1065,');
|
|
97
|
-
log(' an already-open tab refreshes itself.)');
|
|
98
|
-
log(bar);
|
|
99
|
-
log('');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
async function main(argv = process.argv.slice(2), deps = {}) {
|
|
103
|
-
const noBuild = argv.includes('--no-build');
|
|
104
|
-
const tilesArg = (() => { const i = argv.indexOf('--tiles'); return i >= 0 ? argv[i + 1] : ''; })();
|
|
105
|
-
const log = deps.log || console.log;
|
|
106
|
-
|
|
107
|
-
if (!noBuild) {
|
|
108
|
-
log('Rebuilding the atlas from modules/art-pipeline/generated/tiles/ ...');
|
|
109
|
-
rebuildAtlas(deps);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const ctx = (deps.resolvePreviewContext || sandbox.resolvePreviewContext)(deps);
|
|
113
|
-
if (!ctx) {
|
|
114
|
-
log('Atlas rebuilt into public/assets/tiles/. No sandbox here to surface it on');
|
|
115
|
-
log('(not a dev box, and this clone has no local game preview).');
|
|
116
|
-
return { ok: true, surfaced: false, reason: 'no sandbox' };
|
|
117
|
-
}
|
|
118
|
-
if (!ctx.url) {
|
|
119
|
-
log('Atlas rebuilt. This box is on the quick tunnel (no preview hostname) — no sandbox URL to open (ADR 0044).');
|
|
120
|
-
return { ok: true, surfaced: false, reason: 'no preview URL' };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
if (ctx.kind === 'box') {
|
|
124
|
-
const c = copyAtlasToServed(deps);
|
|
125
|
-
if (c.copied) log(` copied the rebuilt tiles into ${deps.servedRoot || BOX_SERVED_ROOT} so the sandbox serves them`);
|
|
126
|
-
else log(` (atlas copy skipped: ${c.reason})`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
log(`Restarting the ${ctx.kind} preview to pick up the new atlas...`);
|
|
130
|
-
try {
|
|
131
|
-
ctx.restart(deps);
|
|
132
|
-
} catch (e) {
|
|
133
|
-
log(` could not restart the preview (${e.message}); the atlas is rebuilt — restart manually to see it.`);
|
|
134
|
-
return { ok: false, surfaced: false, reason: e.message };
|
|
135
|
-
}
|
|
136
|
-
const healthy = await waitHealthy(ctx.healthUrl, deps);
|
|
137
|
-
if (!healthy) {
|
|
138
|
-
log(' preview did not come back healthy in time — check its logs; the atlas is rebuilt.');
|
|
139
|
-
return { ok: false, surfaced: false, reason: 'preview unhealthy' };
|
|
140
|
-
}
|
|
141
|
-
printSurfacedBlock(ctx.url, affectedTiles(tilesArg, deps), log);
|
|
142
|
-
return { ok: true, surfaced: true, url: ctx.url };
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
module.exports = { rebuildAtlas, copyAtlasToServed, affectedTiles, waitHealthy, main };
|
|
146
|
-
|
|
147
|
-
if (require.main === module) {
|
|
148
|
-
main().then((r) => process.exit(r && r.ok === false ? 1 : 0)).catch((e) => {
|
|
149
|
-
console.error('art-stage error:', e.message);
|
|
150
|
-
process.exit(1);
|
|
151
|
-
});
|
|
152
|
-
}
|
package/server.js
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
const http = require('http');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const express = require('express');
|
|
4
|
-
const { Server, matchMaker } = require('colyseus');
|
|
5
|
-
const { WebSocketTransport } = require('@colyseus/ws-transport');
|
|
6
|
-
const { isApiShapedPath, apiNotFoundBody } = require('./src/bongos/api-path-404');
|
|
7
|
-
const uptimePoller = require('./src/bongos/uptime-poller');
|
|
8
|
-
const moduleLoader = require('./src/module-loader/loader');
|
|
9
|
-
const { mountInternalSurfaces, isStatusHost } = require('./src/bongos/serve-internal');
|
|
10
|
-
const { isModuleEnabled } = require('./src/modules');
|
|
11
|
-
const seams = require('./src/module-seams');
|
|
12
|
-
|
|
13
|
-
const PORT = Number(process.env.PORT) || 3000;
|
|
14
|
-
const HOST = process.env.HOST || '127.0.0.1';
|
|
15
|
-
|
|
16
|
-
const app = express();
|
|
17
|
-
app.disable('x-powered-by');
|
|
18
|
-
// Behind Cloudflare → Caddy → Node. Trust the immediate proxy so
|
|
19
|
-
// req.secure / x-forwarded-proto reflect reality for OAuth callbacks.
|
|
20
|
-
app.set('trust proxy', 1);
|
|
21
|
-
|
|
22
|
-
// All non-game surfaces — /healthz, /version, /api/gds, the Dev Box download
|
|
23
|
-
// redirects, and the status + builders hosts — are served by the shared internal
|
|
24
|
-
// surface module (ADR 0062 R51, task 1190). This also calls mountModuleRoutes,
|
|
25
|
-
// which runs each enabled module's route factory — including the game module's,
|
|
26
|
-
// which registers the game.staticRoot / game.registerRooms / game.precreateWorldRoom
|
|
27
|
-
// seam ports before the code below resolves them (ADR 0083 / BV1.R49).
|
|
28
|
-
// Mounted before the game tail below so /builders/* + the status host
|
|
29
|
-
// short-circuit ahead of the game static middleware.
|
|
30
|
-
mountInternalSurfaces(app);
|
|
31
|
-
|
|
32
|
-
app.use((req, res, next) => {
|
|
33
|
-
// From here down: only the game host. Skip if request was for the status
|
|
34
|
-
// host (it should already be handled, but defense in depth).
|
|
35
|
-
if (isStatusHost(req)) return res.status(404).type('text/plain').send('not found');
|
|
36
|
-
next();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
app.use(
|
|
40
|
-
'/vendor/phaser.min.js',
|
|
41
|
-
express.static(path.join(__dirname, 'node_modules/phaser/dist/phaser.min.js'), {
|
|
42
|
-
maxAge: '1d',
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
|
|
46
|
-
app.use(
|
|
47
|
-
'/vendor/colyseus.js',
|
|
48
|
-
express.static(path.join(__dirname, 'node_modules/colyseus.js/dist/colyseus.js'), {
|
|
49
|
-
maxAge: '1d',
|
|
50
|
-
})
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
// Game static files — served from the game module's public/ dir (modules/game/public/).
|
|
54
|
-
// The path is provided by the game module via the game.staticRoot seam port, registered
|
|
55
|
-
// by modules/game/routes/game.js when the loader runs mountModuleRoutes above.
|
|
56
|
-
// If the game module is disabled (vanilla Cloud Bongos boot) these blocks are simply
|
|
57
|
-
// skipped — no game assets served, no 404.html, no client JS. The platform API still
|
|
58
|
-
// works via mountInternalSurfaces above.
|
|
59
|
-
const _gameStaticRoot = seams.resolveOptional('game.staticRoot');
|
|
60
|
-
if (_gameStaticRoot) {
|
|
61
|
-
const gamePublic = _gameStaticRoot();
|
|
62
|
-
|
|
63
|
-
// Tile-asset cache strategy (task #55 + #160 + #176).
|
|
64
|
-
//
|
|
65
|
-
// Three buckets:
|
|
66
|
-
// 1. Content-hashed atlas (atlas-<sha8>.png + atlas-<sha8>.json if any)
|
|
67
|
-
// Filename changes with content → safe to cache forever. Marked
|
|
68
|
-
// immutable so browsers / CF skip revalidation entirely.
|
|
69
|
-
// 2. Stable-URL files paired with bucket 1 (atlas-index.json, manifest.json,
|
|
70
|
-
// atlas.json). URLs don't change across deploys but contents do, so
|
|
71
|
-
// they get a short TTL — a deploy is visible within ~1 minute. atlas.json
|
|
72
|
-
// lives here because tilePalette.js still hardcodes its URL; until the
|
|
73
|
-
// build hashes it (idea_inbox follow-up), the short TTL is what prevents
|
|
74
|
-
// the post-deploy "new PNG + stale frame coords" mismatch window.
|
|
75
|
-
// 3. Legacy unhashed per-tile PNGs + everything else under /assets/tiles
|
|
76
|
-
// Short TTL (5 min) until they migrate to content-hashed names. The
|
|
77
|
-
// old 1-day TTL is what caused the 2026-05-10 stale-atlas incident.
|
|
78
|
-
app.use(
|
|
79
|
-
'/assets/tiles',
|
|
80
|
-
express.static(path.join(gamePublic, 'assets/tiles'), {
|
|
81
|
-
setHeaders: (res, filePath) => {
|
|
82
|
-
const name = path.basename(filePath);
|
|
83
|
-
if (/^atlas-[a-f0-9]{8,}\.(png|json)$/.test(name)) {
|
|
84
|
-
res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
|
|
85
|
-
} else if (
|
|
86
|
-
name === 'atlas-index.json' ||
|
|
87
|
-
name === 'manifest.json' ||
|
|
88
|
-
name === 'atlas.json'
|
|
89
|
-
) {
|
|
90
|
-
res.setHeader('Cache-Control', 'public, max-age=60');
|
|
91
|
-
} else {
|
|
92
|
-
res.setHeader('Cache-Control', 'public, max-age=300');
|
|
93
|
-
}
|
|
94
|
-
},
|
|
95
|
-
})
|
|
96
|
-
);
|
|
97
|
-
|
|
98
|
-
// HTML + game JS — short TTL so deploys are visible quickly. .js files in
|
|
99
|
-
// game/ are not content-hashed today (V1 shortcut), so a long TTL would
|
|
100
|
-
// compound the stale-art class of bugs into stale-code bugs.
|
|
101
|
-
app.use(
|
|
102
|
-
express.static(gamePublic, {
|
|
103
|
-
extensions: ['html'],
|
|
104
|
-
setHeaders: (res, filePath) => {
|
|
105
|
-
if (/\.(html|js|json)$/.test(filePath)) {
|
|
106
|
-
res.setHeader('Cache-Control', 'public, max-age=60');
|
|
107
|
-
} else {
|
|
108
|
-
res.setHeader('Cache-Control', 'public, max-age=300');
|
|
109
|
-
}
|
|
110
|
-
},
|
|
111
|
-
})
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Themed 404 for the game host. Anything that didn't resolve to an API route,
|
|
116
|
-
// the status/builders hosts, or a static file lands here.
|
|
117
|
-
// The status host has its own plain-text 404 above; we don't poach it.
|
|
118
|
-
// API-SHAPED paths get a JSON 404 with a corrective hint — never the HTML island
|
|
119
|
-
// page — so a caller that missed the /api/gds mount (curl /tasks, /gds/*, an
|
|
120
|
-
// /api/* typo) gets a self-describing error instead of HTML that JSON.parse-faults
|
|
121
|
-
// on the client (V4.R14 / #952). The match is broader than the old `/api/` test:
|
|
122
|
-
// it also catches the bare GDS resource roots a human hits without the prefix.
|
|
123
|
-
app.use((req, res) => {
|
|
124
|
-
if (isStatusHost(req)) return res.status(404).type('text/plain').send('not found');
|
|
125
|
-
res.status(404);
|
|
126
|
-
if (isApiShapedPath(req.path)) {
|
|
127
|
-
return res.json(apiNotFoundBody(req.path));
|
|
128
|
-
}
|
|
129
|
-
if (req.method === 'GET' && req.accepts('html')) {
|
|
130
|
-
res.setHeader('Cache-Control', 'public, max-age=60');
|
|
131
|
-
const gameStaticRoot = seams.resolveOptional('game.staticRoot');
|
|
132
|
-
if (gameStaticRoot) {
|
|
133
|
-
return res.sendFile(path.join(gameStaticRoot(), '404.html'));
|
|
134
|
-
}
|
|
135
|
-
return res.type('text/plain').send('404 — not found');
|
|
136
|
-
}
|
|
137
|
-
return res.type('text/plain').send('404 — you have washed ashore on the wrong island');
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
const httpServer = http.createServer(app);
|
|
141
|
-
|
|
142
|
-
const gameServer = new Server({
|
|
143
|
-
transport: new WebSocketTransport({ server: httpServer }),
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// The PRODUCT/game rooms (world + queue) — registered via the game module's seam
|
|
147
|
-
// port (game.registerRooms), wired by modules/game/routes/game.js at loader mount
|
|
148
|
-
// time. Only called when the game module is enabled (OTB does; vanilla skips).
|
|
149
|
-
// The game-less platform entrypoint (src/platform-server.js) never constructs a
|
|
150
|
-
// Colyseus server at all. The builders' hall live channel is NOT a Colyseus room —
|
|
151
|
-
// it rides the SSE endpoint (src/bongos/routes/live.js, ADR 0069).
|
|
152
|
-
const _registerRooms = seams.resolveOptional('game.registerRooms');
|
|
153
|
-
if (_registerRooms) {
|
|
154
|
-
_registerRooms(gameServer);
|
|
155
|
-
} else {
|
|
156
|
-
console.log('[game] game module disabled — no Colyseus rooms registered');
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
uptimePoller.start();
|
|
160
|
-
|
|
161
|
-
// task 1134 — the server-owned publish reconciler (drives 'confirmed' tasks whose
|
|
162
|
-
// branch has merged + deployed to 'shipped' on a timer, ADR 0055). CARVED into the
|
|
163
|
-
// lifecycle module at BV1.R86 (ADR 0093 §1): it now starts via the loader's
|
|
164
|
-
// startModulePollers (a declared poller — modules/lifecycle/pollers/publish-
|
|
165
|
-
// reconciler.js), in the listen callback below alongside the Discord bot + routine
|
|
166
|
-
// timers — so server.js no longer reaches into the lifecycle module to start it.
|
|
167
|
-
// No-op without a push credential (local dev/tests); kill-switch via
|
|
168
|
-
// PUBLISH_RECONCILER_DISABLED. Unref'd timers, best-effort — never takes the
|
|
169
|
-
// game/API down.
|
|
170
|
-
|
|
171
|
-
// task 1382 (ADR 0078 amendment) — server-internal routine timers (peer-vote-tally,
|
|
172
|
-
// weekly, in-process; no model, no external cron). CARVED into the autonomy module
|
|
173
|
-
// at BV1.R83 (ADR 0093 §1): it now starts via the loader's startModulePollers (a
|
|
174
|
-
// declared poller — modules/autonomy/pollers/routine-timers.js), in the listen
|
|
175
|
-
// callback below alongside the Discord bot poller. server.js is still the only
|
|
176
|
-
// startModulePollers caller (not the game-less platform entrypoint), so a second
|
|
177
|
-
// always-on instance can't double-run. Kill-switch ROUTINE_TIMERS_DISABLED stays.
|
|
178
|
-
|
|
179
|
-
httpServer.listen(PORT, HOST, async () => {
|
|
180
|
-
console.log(`Amazonprimea listening on ${HOST}:${PORT}`);
|
|
181
|
-
// Pre-create the singleton WorldRoom so clients can use
|
|
182
|
-
// `client.join('world')` (existing-only, no auto-create on overflow). The
|
|
183
|
-
// precreate function is provided by the game module via the seam port
|
|
184
|
-
// game.precreateWorldRoom; idempotent + failure-tolerant.
|
|
185
|
-
const _precreate = seams.resolveOptional('game.precreateWorldRoom');
|
|
186
|
-
if (_precreate) {
|
|
187
|
-
await _precreate(matchMaker);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Start each ENABLED module's web-process background pollers through the loader
|
|
191
|
-
// — module-agnostic: it requires modules/<key>/pollers/<name>.js and fires
|
|
192
|
-
// start(). The Discord bot (ADR 0033) is one such poller; a vanilla instance
|
|
193
|
-
// with discord off simply has no poller to start, and never reads a webhook/bot
|
|
194
|
-
// token. Non-blocking + failure-isolated by the loader (a poller problem must
|
|
195
|
-
// never take the game/API down) — the module-agnostic successor to the
|
|
196
|
-
// hardcoded discordBot.start() block (ADR 0083 / BV1.R47, task 1416).
|
|
197
|
-
moduleLoader.startModulePollers({}, { isEnabled: isModuleEnabled });
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Graceful shutdown — stop the in-process pollers, then stop each module poller
|
|
201
|
-
// (e.g. close the Discord gateway socket cleanly on a systemd restart / Ctrl-C so
|
|
202
|
-
// we don't leave a zombie session for Discord to reap). stopModulePollers awaits
|
|
203
|
-
// each stop and isolates errors, so a stuck poller never blocks the exit (BV1.R47).
|
|
204
|
-
for (const sig of ['SIGTERM', 'SIGINT']) {
|
|
205
|
-
process.on(sig, () => {
|
|
206
|
-
// The publish reconciler + routine timers now stop via
|
|
207
|
-
// moduleLoader.stopModulePollers() — the lifecycle module's publish-reconciler
|
|
208
|
-
// poller (carved BV1.R86) + the autonomy module's routine-timers poller
|
|
209
|
-
// (carved BV1.R83 / ADR 0093 §1).
|
|
210
|
-
moduleLoader.stopModulePollers().finally(() => process.exit(0));
|
|
211
|
-
});
|
|
212
|
-
}
|