@bongos/core 1.19.725 → 1.19.727
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 +179 -494
- package/.devcontainer/Dockerfile +1 -1
- package/.devcontainer/devcontainer.json +2 -2
- package/.devcontainer/healthcheck.sh +1 -1
- package/.devcontainer/init-firewall.sh +1 -1
- package/.devcontainer/post-create.sh +2 -2
- package/.env.local.example +1 -1
- package/Dockerfile +1 -1
- package/docs/copy-inventory.md +1 -1
- package/docs/copy-registry.json +9 -9
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/devbox-app-release.md +4 -4
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +5 -5
- package/modules/dev-box/app/scripts/gen-brand.js +1 -1
- package/modules/dev-box/app/src/brand.js +1 -1
- package/modules/dev-box/app/test/unit.mjs +16 -16
- package/modules/dev-box/box-access.js +1 -1
- package/modules/dev-box/routes/box.js +1 -1
- package/modules/discord/discord-approvals.js +1 -1
- package/modules/grading/grader-model.js +1 -1
- package/modules/grading/grader-prompt.js +1 -1
- package/modules/grading/grader-workers/quality.js +1 -1
- package/modules/hall-ui/module.json +1 -1
- package/modules/hall-ui/public/brand-apply.js +1 -1
- package/modules/hall-ui/public/shell.js +1 -1
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/lifecycle/conflict-resolve.js +1 -1
- package/modules/lifecycle/db-analytics.js +1 -1
- package/modules/lifecycle/routes/analytics.js +1 -1
- package/modules/onboarding/approval-broadcast.js +1 -1
- package/modules/onboarding/onboarding.js +1 -1
- package/modules/provisioning/tests/liveness.mjs +1 -1
- package/modules/public-landing/public/index.html +1 -1
- package/modules/public-landing/public/projects-floor.states.json +2 -2
- package/modules/public-landing/public/projects.probes.json +4 -4
- package/modules/public-landing/public/projects.states.json +2 -2
- package/modules/status-ui/module.json +1 -1
- package/modules/status-ui/public/status.js +4 -4
- package/modules/status-ui/public/style.css +1 -1
- package/modules/ui-design/kit/fixtures/cost-summary.json +1 -1
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
- package/modules/ui-design/kit/fixtures/featured.json +1 -1
- package/modules/ui-design/kit/fixtures/leaderboard.json +1 -1
- package/modules/ui-design/kit/fixtures/my-projects.json +1 -1
- package/modules/ui-design/kit/fixtures/progress.json +1 -1
- package/modules/ui-design/kit/fixtures/recent-shipped.json +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/cron/style_guide_audit.sh +1 -1
- package/scripts/discord/lib.js +1 -1
- package/scripts/discord/post.js +1 -1
- package/scripts/gds/audit-authorship.sh +1 -1
- package/scripts/gds/audit-replay.sh +29 -2
- package/scripts/gds/audit-ships.js +1 -1
- package/scripts/gds/backfill-sessions.js +2 -2
- package/scripts/gds/cf-tunnel.js +4 -4
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +6 -6
- package/scripts/gds/cutover-codemod.js +1 -1
- package/scripts/gds/dev-lib.js +2 -2
- package/scripts/gds/diagnose-achievements.js +2 -2
- package/scripts/gds/fitness-checks-identity.js +6 -4
- package/scripts/gds/gate-review.js +14 -6
- package/scripts/gds/gen-diagrams.js +1 -1
- package/scripts/gds/leak-scan-allowlist.js +293 -0
- package/scripts/gds/learning-capture.js +1 -1
- package/scripts/gds/linkify-refs.js +2 -2
- package/scripts/gds/local-preview-lib.js +1 -1
- package/scripts/gds/mirror-lineage-check.js +1 -1
- package/scripts/gds/mirror-redact.js +17 -2
- package/scripts/gds/newcomer-floor.templates.json +1 -1
- package/scripts/gds/package-core.js +18 -1
- package/scripts/gds/paste-token.js +1 -1
- package/scripts/gds/provision-units.js +2 -2
- package/scripts/gds/publish-manifest.js +15 -0
- package/scripts/gds/repo-metrics.js +1 -1
- package/scripts/gds/sandbox-stage.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +1 -1
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +1 -1
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +1 -1
- package/scripts/gds/seed-bongos-tasks.js +2 -2
- package/scripts/gds/seed-gds-v4-tasks.js +1 -1
- package/scripts/gds/seed-medusa-tasks.js +10 -10
- package/scripts/gds/seed-module-upstreaming.js +1 -1
- package/scripts/gds/seed-productization-tasks.js +6 -6
- package/scripts/gds/seed-provisioning-tasks.js +2 -2
- package/scripts/gds/seed-staging.sh +19 -6
- package/scripts/gds/ship-deploy-target.js +1 -1
- package/scripts/gds/ship-flow.js +1 -1
- package/scripts/gds/smoke-gds.js +1 -1
- package/scripts/status-probe.mjs +2 -2
- package/src/bongos/auth-discord.js +2 -2
- package/src/bongos/live-channel.js +1 -1
- package/src/bongos/permission-path-check.js +80 -3
- package/src/bongos/pool.js +1 -1
- package/src/bongos/routes/auth.js +11 -11
- package/src/bongos/routes/security.js +1 -1
- package/src/bongos/routes.js +1 -1
- package/src/branding.js +1 -1
- package/src/devbox-downloads.js +1 -1
- package/src/instance-config.js +2 -2
- package/src/module-api.js +1 -1
- package/src/module-loader/provenance.js +2 -2
- package/tests/art_stage.mjs +2 -2
- package/tests/auth_anthropic_identity.mjs +1 -1
- package/tests/auth_duplicate_cookie.mjs +1 -1
- package/tests/auth_error_brand.mjs +1 -1
- package/tests/auth_inactive_builder.mjs +1 -1
- package/tests/backup_cli.mjs +4 -4
- package/tests/bongos_box_connect.mjs +2 -2
- package/tests/bongos_downloads.mjs +2 -2
- package/tests/bongos_login.mjs +5 -5
- package/tests/box_connect_e2e.mjs +2 -2
- package/tests/box_dns_repoint.mjs +10 -10
- package/tests/box_host_keys.mjs +1 -1
- package/tests/box_onboard.mjs +9 -9
- package/tests/box_scope_session.mjs +1 -1
- package/tests/box_terminal_ssrf.mjs +7 -7
- package/tests/boxes.mjs +9 -9
- package/tests/branding.mjs +2 -2
- package/tests/bump_version.mjs +2 -2
- package/tests/cf_tunnel.mjs +40 -40
- package/tests/cli_token_reissue.mjs +7 -7
- package/tests/cloud_bongos_pack.mjs +1 -1
- package/tests/currency_label.mjs +8 -8
- package/tests/dev_lib.mjs +4 -4
- package/tests/devbox_dmg_backdrop.mjs +1 -1
- package/tests/devbox_downloads.mjs +2 -2
- package/tests/devbox_icons.mjs +1 -1
- package/tests/discord_approvals.mjs +1 -1
- package/tests/discord_oauth.mjs +2 -2
- package/tests/executed_closure.mjs +6 -1
- package/tests/first_admin_bootstrap.mjs +1 -1
- package/tests/gate_approvals.mjs +5 -5
- package/tests/government_protected_surfaces.mjs +137 -0
- package/tests/grade_aggregates_unavailable.mjs +1 -1
- package/tests/hook_user_agents.mjs +2 -2
- package/tests/init.mjs +6 -6
- package/tests/instance_config.mjs +1 -1
- package/tests/instance_manifest.mjs +5 -5
- package/tests/instance_mismatch_guard.mjs +13 -13
- package/tests/leak_scan_allowlist.mjs +94 -0
- package/tests/migrate_root_resolution.mjs +4 -4
- package/tests/mirror_redact.mjs +28 -4
- package/tests/module_catalog.mjs +5 -5
- package/tests/module_submissions.mjs +3 -3
- package/tests/package_core.mjs +1 -1
- package/tests/parallel-load.mjs +6 -6
- package/tests/projects_hub.mjs +2 -2
- package/tests/provision.mjs +50 -50
- package/tests/provisioning_github_app_routes.mjs +1 -1
- package/tests/provisioning_onboard_mode.mjs +2 -2
- package/tests/publish_reconciler.mjs +4 -4
- package/tests/sandbox_stage.mjs +1 -1
- package/tests/scrubber_corpus.mjs +3 -3
- package/tests/search_isolation.mjs +1 -1
- package/tests/session_records.mjs +1 -1
- package/tests/ship_card.mjs +6 -6
- package/tests/ship_ci_deploy.mjs +1 -1
- package/tests/single_host_hall.mjs +9 -9
- package/tests/smoke_preflight.mjs +1 -1
- package/tests/smoke_target.mjs +1 -1
- package/tests/status-probe.mjs +1 -1
- package/tests/status_mirror_sync.mjs +1 -1
- package/docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md +0 -45
- package/docs/design/mocks/_schemes.css +0 -604
- package/docs/design/mocks/component-sheet.html +0 -428
- package/docs/design/mocks/hall/_tokens.css +0 -370
- package/docs/design/mocks/hall/board/v1.html +0 -846
- package/docs/design/mocks/hall/board/v2.html +0 -858
- package/docs/design/mocks/hall/board/v3.html +0 -884
- package/docs/design/mocks/hall/goals/v1.html +0 -1212
- package/docs/design/mocks/hall/goals/v2.html +0 -1192
- package/docs/design/mocks/hall/goals/v3.html +0 -1205
- package/docs/design/mocks/hall/hall-sheet.html +0 -244
- package/docs/design/mocks/hall/reading/v1.html +0 -1125
- package/docs/design/mocks/hall/reading/v2.html +0 -1177
- package/docs/design/mocks/hall/reading/v3.html +0 -1150
- package/docs/design/mocks/hall/settings/v1.html +0 -1238
- package/docs/design/mocks/hall/settings/v2.html +0 -1235
- package/docs/design/mocks/hall/settings/v3.html +0 -1299
- package/docs/design/mocks/hall/task/v1.html +0 -633
- package/docs/design/mocks/hall/task/v2.html +0 -646
- package/docs/design/mocks/hall/task/v3.html +0 -647
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg.json +0 -13
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg +0 -0
- package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg.json +0 -13
- package/docs/design/mocks/hub/hub-sheet.html +0 -192
- package/docs/design/mocks/hub/mine.html +0 -465
- package/docs/design/mocks/hub/picker.html +0 -421
- package/docs/design/mocks/hub/planets.html +0 -356
- package/docs/design/mocks/hub/v1.html +0 -1209
- package/docs/design/mocks/hub/v2.html +0 -1257
- package/docs/design/mocks/hub/v3.html +0 -1496
- package/docs/design/mocks/hub/wizard.html +0 -421
- package/docs/design/mocks/landing/landing-sheet.html +0 -131
- package/docs/design/mocks/landing/v1.html +0 -956
- package/docs/design/mocks/landing/v2.html +0 -708
- package/docs/design/mocks/landing/v3.html +0 -911
- package/docs/design/mocks/landing-page-mock.html +0 -687
- package/docs/design/mocks/landing-palettes.html +0 -103
- package/docs/design/mocks/projects-hub-mock.html +0 -991
- package/docs/design/mocks/projects-hub-variants.html +0 -146
- package/docs/design/mocks/projects-page-mock.html +0 -167
- package/docs/design/mocks/world/blue-hud/_tokens.css +0 -246
- package/docs/design/mocks/world/blue-hud/hall.html +0 -595
- package/docs/design/mocks/world/blue-hud/landing.html +0 -424
- package/docs/design/mocks/world/blue-hud/status.html +0 -410
- package/docs/design/mocks/world/chrome-blobs/_tokens.css +0 -479
- package/docs/design/mocks/world/chrome-blobs/hall.html +0 -553
- package/docs/design/mocks/world/chrome-blobs/landing-b.html +0 -581
- package/docs/design/mocks/world/chrome-blobs/landing.html +0 -328
- package/docs/design/mocks/world/chrome-blobs/status.html +0 -416
- package/docs/design/mocks/world/natgeo-editorial/_tokens.css +0 -377
- package/docs/design/mocks/world/natgeo-editorial/hall.html +0 -649
- package/docs/design/mocks/world/natgeo-editorial/landing-b.html +0 -743
- package/docs/design/mocks/world/natgeo-editorial/landing.html +0 -475
- package/docs/design/mocks/world/natgeo-editorial/status.html +0 -437
- package/docs/design/mocks/world/organic-cards/_tokens.css +0 -462
- package/docs/design/mocks/world/organic-cards/hall.html +0 -625
- package/docs/design/mocks/world/organic-cards/landing-b.html +0 -736
- package/docs/design/mocks/world/organic-cards/landing.html +0 -420
- package/docs/design/mocks/world/organic-cards/status.html +0 -458
- package/docs/design/mocks/world/world-sheet.html +0 -156
|
@@ -71,8 +71,8 @@ function serviceUnitPath(inst) { return `/etc/systemd/system/${inst.slug}.servic
|
|
|
71
71
|
// ── nightly DB backup: per-instance timer + service (task 2213) ─────────────
|
|
72
72
|
// PRODUCTIZES the manual task-2210 fix: a newly-provisioned instance was born with
|
|
73
73
|
// NO backups (dir absent, GDS_BACKUP_DB unset → the script fell back to the
|
|
74
|
-
// nonexistent `
|
|
75
|
-
// hardcode /home/lars/
|
|
74
|
+
// nonexistent `example` default, and the repo's global gds-db-backup units
|
|
75
|
+
// hardcode /home/lars/example — wrong for the consumer node_modules/@bongos/
|
|
76
76
|
// core layout). These generators emit a PER-INSTANCE unit pair that fixes all three:
|
|
77
77
|
// • name → `<slug>-db-backup.{service,timer}` so co-tenant instances on one box
|
|
78
78
|
// never collide (each dumps its OWN db; the script's retention prune is
|
|
@@ -281,12 +281,27 @@ const INSTANCE_EXCLUDE = [
|
|
|
281
281
|
'docs/design/container-exterior-tiles.md',
|
|
282
282
|
'docs/design/container-interior.md',
|
|
283
283
|
'docs/design/world-map-scroll.md',
|
|
284
|
+
// instance-specific UI MOCKUPS — iteration snapshots of THIS instance's own hub (hub v1/v2/v3,
|
|
285
|
+
// the wizard, the projects hub, the world chrome), branded end to end in markup a consumer of
|
|
286
|
+
// the portable core has no use for: they are our design history, not methodology. Same reason as
|
|
287
|
+
// the game-design carve-out above, and the same failure mode — redaction de-brands names in DOCS,
|
|
288
|
+
// but an .html is a FUNCTIONAL file that ships verbatim, so every brand string in the mock markup
|
|
289
|
+
// reached the published artifact. They were 117 of the 240 remaining leak-scan hits. Excluding the
|
|
290
|
+
// subtree rather than the six files is deliberate: it is the boundary that holds when someone adds
|
|
291
|
+
// mockup number seven. (task 1003869)
|
|
292
|
+
'docs/design/mocks/',
|
|
284
293
|
// instance-HISTORY ADRs (task 1002219 / audit H5). These record THIS platform-family's own
|
|
285
294
|
// migration + identity history (the amazonprimea→cloudbongos lift, the standalone reprovision,
|
|
286
295
|
// the cloudbongos identity hub) — they are inherently full of the prior instance's name/owner and
|
|
287
296
|
// genericize to nothing (unlike a portable methodology ADR). docs/adr/ is allowlisted wholesale for
|
|
288
297
|
// the portable decision records; these three stay in this repo and never ship in the core, mirroring
|
|
289
298
|
// the instance-recipe / game-design carve-outs above. (The portable ADRs + the README index still publish.)
|
|
299
|
+
// The instance's OWN name + trademark decision. Its prose redacts cleanly, but the FILENAME
|
|
300
|
+
// carries the brand and the publish manifest lists every path — so the literal shipped in
|
|
301
|
+
// .bongos-core.json no matter how well the body was redacted. It also genericizes to nothing:
|
|
302
|
+
// "accepting trademark risk on <our name>" is not a decision another instance inherits.
|
|
303
|
+
// (task 1003869 — the last literal in the artifact.)
|
|
304
|
+
'docs/adr/0004-amazonprimea-name-and-trademark-acceptance.md',
|
|
290
305
|
'docs/adr/0139-platform-history-migration-to-cloudbongos.md',
|
|
291
306
|
'docs/adr/0140-cloudbongos-standalone-reprovision.md',
|
|
292
307
|
'docs/adr/0141-cross-site-identity-federated-hub-idp.md',
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
// out to git / wc / knip and emits a JSON snapshot of repo health, then upserts
|
|
7
7
|
// it into repo_health_snapshots (idempotent on calendar date — rerun same day
|
|
8
8
|
// = upsert, not a new row). The latest row feeds /api/gds/public/repo-health,
|
|
9
|
-
// which renders a trend tile on status.
|
|
9
|
+
// which renders a trend tile on status.example.com.
|
|
10
10
|
//
|
|
11
11
|
// What the snapshot contains:
|
|
12
12
|
// - total_loc + loc_by_dir — tracked JS/MJS line counts, per watched dir
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
//
|
|
7
7
|
// Two preview contexts (resolvePreviewContext picks one):
|
|
8
8
|
// - BOX (ADR 0031): box-game-preview.service serves the working tree at
|
|
9
|
-
// https://sandbox-<login>.
|
|
9
|
+
// https://sandbox-<login>.example.com; "staging" restarts the service.
|
|
10
10
|
// - LOCAL (task 1056): a builder on a full local clone (laptop) runs the same
|
|
11
11
|
// ADR 0052 game-only entry privately at http://localhost:<port> via
|
|
12
12
|
// scripts/gds/local-preview.js; "staging" (re)starts that local process.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// Run: node scripts/gds/seed-bongos-coreB-tranche1-tasks.js
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const TOKEN = require(os.homedir() + '/.config/otb/gds-session.json').token;
|
|
10
|
-
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://
|
|
10
|
+
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://example.com';
|
|
11
11
|
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };
|
|
12
12
|
|
|
13
13
|
// ref → task spec. depends_on_refs are local R## refs resolved after create.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// Run: node scripts/gds/seed-bongos-coreB-tranche2-tasks.js
|
|
10
10
|
const os = require('os');
|
|
11
11
|
const TOKEN = require(os.homedir() + '/.config/otb/gds-session.json').token;
|
|
12
|
-
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://
|
|
12
|
+
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://example.com';
|
|
13
13
|
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };
|
|
14
14
|
|
|
15
15
|
// ref → task spec. deps are local R## refs resolved after create. Clean-slicing
|
|
@@ -47,7 +47,7 @@ const TASKS = [
|
|
|
47
47
|
{
|
|
48
48
|
key: 'G02',
|
|
49
49
|
title: 'config/hierarchy.json + hierarchy-config.js — the six-tier preset (onboarding-edited)',
|
|
50
|
-
description: `Phase 1 of the goal hierarchy (ADR 0086 §1 — the ADR 0062 core/host pattern applied to the work model). Add \`config/hierarchy.json\` (joins branding.json/modules.json as host config) declaring the tiers — names, order, and which tier carries scope, membership, and criteria. Ship the six-tier
|
|
50
|
+
description: `Phase 1 of the goal hierarchy (ADR 0086 §1 — the ADR 0062 core/host pattern applied to the work model). Add \`config/hierarchy.json\` (joins branding.json/modules.json as host config) declaring the tiers — names, order, and which tier carries scope, membership, and criteria. Ship the six-tier Example preset: Project → Version → Goal → Criteria → Task → Sub-task. Add \`src/bongos/hierarchy-config.js\` (mirrors src/modules.js) to load + validate it. v1 "editable" = owner-set labels, enable/disable, and which tier owns scope/criteria/membership; arbitrary custom tier graphs are explicitly DEFERRED. Owner fills/edits during onboarding (extend the scripts/gds/init.js interview).
|
|
51
51
|
|
|
52
52
|
**Done when:** hierarchy.json + hierarchy-config.js exist; the loader validates + exposes the tier set; the six-tier preset is the OTB default; onboarding can set labels/enable/scope-owner; unit test covers a malformed preset rejected.`,
|
|
53
53
|
kind: 'feature', discipline: 'engineer', priority: 5,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Seed the 6 net-new "Cloud Bongos CLI" tasks added to GDS-V4 (C8) — the
|
|
3
3
|
// conda/Miniconda-style terminal-first onboarding toolkit. `bongos` is the CLI
|
|
4
|
-
// command (Cloud Bongos = the standalone build platform;
|
|
4
|
+
// command (Cloud Bongos = the standalone build platform; Example =
|
|
5
5
|
// instance 1). Design doc: artifact 02bcca71 (the bongos toolkit).
|
|
6
6
|
//
|
|
7
7
|
// Shape mirrors seed-medusa-tasks.js exactly:
|
|
@@ -98,7 +98,7 @@ A small public, read-only endpoint returning the instance descriptor assembled f
|
|
|
98
98
|
|
|
99
99
|
\`bongos login <instance-url>\` fetches the P4A manifest, runs the GitHub device-flow (browser-click, no token paste — the UI-first rule), stores a per-instance scoped session, and brands the CLI for that instance. For someone NOT yet admitted, it files the public POST /api/gds/access-requests (their GitHub username) and polls — telling them "an Archon will approve you with a 👍 in Discord" — until admission flips, then completes sign-in. No GitHub collaborator invite is involved. Default admission is invite-only. Also folds the already-planned \`bongos init\` (scaffold a brand-new instance, R65) and \`bongos upgrade\` (R64) in as subcommands of the same CLI.
|
|
100
100
|
|
|
101
|
-
**Done when:** \`bongos login <any-instance-url>\` works end-to-end including the not-yet-admitted path (files the request, polls, completes once approved); the CLI is instance-agnostic (no hard-coded
|
|
101
|
+
**Done when:** \`bongos login <any-instance-url>\` works end-to-end including the not-yet-admitted path (files the request, polls, completes once approved); the CLI is instance-agnostic (no hard-coded example); \`init\`/\`upgrade\` are reachable subcommands.`,
|
|
102
102
|
kind: 'feature', discipline: 'engineer', priority: 3,
|
|
103
103
|
est_minutes: 360, manual_degree: 2, credits_reward: 60,
|
|
104
104
|
touches: ['bin/', 'scripts/gds/'], criteria: ['C8'], deps: ['P1', 'P4A'],
|
|
@@ -379,7 +379,7 @@ Reconcile shipped tasks against uploaded session records (BFG 6D follow-up to ta
|
|
|
379
379
|
title: 'Embedding provider decision + key provisioning (Archon go-ahead)',
|
|
380
380
|
description: `**Criterion:** one-call-recall (gated by V4.R24's go/no-go).
|
|
381
381
|
|
|
382
|
-
If the eval gate says GO: pick the provider (research recommendation: Voyage voyage-4-lite — $0.02/M tokens, 200M free tier, Matryoshka dims; fallback OpenAI text-embedding-3-small), create the account, provision EMBEDDINGS_API_KEY into /etc/
|
|
382
|
+
If the eval gate says GO: pick the provider (research recommendation: Voyage voyage-4-lite — $0.02/M tokens, 200M free tier, Matryoshka dims; fallback OpenAI text-embedding-3-small), create the account, provision EMBEDDINGS_API_KEY into /etc/example/secrets.env per ADR 0022. New external account = Lars's explicit go-ahead per the budget rules.
|
|
383
383
|
|
|
384
384
|
**Done when:** decision recorded (ADR addendum), key live on the droplet, cost expectations logged.`,
|
|
385
385
|
kind: 'decision', discipline: 'engineering', priority: 2,
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Seed the 24 net-new "Medusa" tasks (R50–R73) added to GDS-V4 in the
|
|
3
3
|
// 2026-06-17 Medusa Planning Session (meta task 1188 / claim 982).
|
|
4
4
|
//
|
|
5
|
-
// Medusa = decouple the GDS from the
|
|
5
|
+
// Medusa = decouple the GDS from the Example game into a standalone,
|
|
6
6
|
// configurable, self-hostable, AGPL open-source, non-profit, AI-first build
|
|
7
|
-
// platform.
|
|
7
|
+
// platform. Example becomes instance 1.
|
|
8
8
|
//
|
|
9
9
|
// Companion migration 110_medusa_planning_seed.sql handles:
|
|
10
10
|
// - the 2 done_when_criteria rows (C8 = medusa-standalone-instance,
|
|
@@ -66,7 +66,7 @@ A Medusa-only boot (sibling to src/preview-server.js) that builds the Express ap
|
|
|
66
66
|
title: 'GDS-owned DB pool + migration carve-out + drop the track enum',
|
|
67
67
|
description: `Part of Medusa (C8). Cross-ref: R50 design.
|
|
68
68
|
|
|
69
|
-
A GDS-owned pool reading DATABASE_URL/PG* (not hard-defaulting to the
|
|
69
|
+
A GDS-owned pool reading DATABASE_URL/PG* (not hard-defaulting to the example DB name); a GDS-only migration sequence (game migrations 001/002 simply not applied on a fresh non-game instance); drop the product/internal 'track' distinction. Live prod keeps its existing DB. Zero cross-FKs exist between game and GDS tables, so the split is clean.
|
|
70
70
|
|
|
71
71
|
**Done when:** a fresh-DB migration run produces only the build-system schema; a query asserts the game tables (world_events, players, seats) are absent; live prod unaffected.`,
|
|
72
72
|
kind: 'infra', discipline: 'engineer', priority: 5,
|
|
@@ -80,7 +80,7 @@ A GDS-owned pool reading DATABASE_URL/PG* (not hard-defaulting to the amazonprim
|
|
|
80
80
|
|
|
81
81
|
A per-instance config layer (config/branding.json + a documented schema + a neutral starter + a fill-it-in prompt an owner's own LLM uses) supplying product name, domain/cookie apex, sign-in (OAuth) origin, repo binding, currency label, copy, fonts, palette, icons/favicon/cursors. Core reads identity + branding from this layer; defaults preserve live OTB exactly. NOT a vanilla strip — branding becomes per-instance config; OTB's look becomes instance 1's pack (extracted in R57).
|
|
82
82
|
|
|
83
|
-
**Done when:** core resolves all identity + branding from config; booting under a non-
|
|
83
|
+
**Done when:** core resolves all identity + branding from config; booting under a non-example.com domain + a non-game name needs config only, no source edits. Gates most de-brand + self-host tasks.`,
|
|
84
84
|
kind: 'feature', discipline: 'engineer', priority: 5,
|
|
85
85
|
est_minutes: 360, manual_degree: 2, credits_reward: 60,
|
|
86
86
|
touches: ['config/', 'src/bongos/', 'public-builders/'], criteria: ['C8'], deps: ['R50'],
|
|
@@ -128,7 +128,7 @@ Make the reward currency a configurable label (neutral default; OTB's instance =
|
|
|
128
128
|
title: "Extract OTB's look into instance 1's branding pack (not a vanilla strip)",
|
|
129
129
|
description: `Part of Medusa (C8). Cross-ref: R53, R56.
|
|
130
130
|
|
|
131
|
-
Move the ~272 archaic strings, the Greek fonts, the parchment palette, favicon/cursors/SVG sigils, and the page copy out of the hall + status code and into instance 1's branding pack; core renders from the R53 branding layer with a neutral starter as the fallback. status.
|
|
131
|
+
Move the ~272 archaic strings, the Greek fonts, the parchment palette, favicon/cursors/SVG sigils, and the page copy out of the hall + status code and into instance 1's branding pack; core renders from the R53 branding layer with a neutral starter as the fallback. status.example.com + builders.example.com look IDENTICAL afterward — their look just comes from config now. Add a rank display-label map so the Greek rank LABELS are presentation (the enum stays Greek by decision).
|
|
132
132
|
|
|
133
133
|
**Done when:** no project branding is hard-coded in public-builders/ or public-status/; OTB's surfaces render unchanged from instance 1's pack; a fresh instance renders the neutral starter.`,
|
|
134
134
|
kind: 'refactor', discipline: 'engineer', priority: 4,
|
|
@@ -142,7 +142,7 @@ Move the ~272 archaic strings, the Greek fonts, the parchment palette, favicon/c
|
|
|
142
142
|
|
|
143
143
|
scripts/gds/gen-diagrams.js auto-generates the onboarding diagrams + assertions.json on every deploy. Make its template strings + assertions read the instance branding config so deploy-time regeneration emits neutral (instance-correct) branding. Rank names stay Greek by decision. This prevents the "we de-branded but Greek text reappeared after the next deploy" regression.
|
|
144
144
|
|
|
145
|
-
**Done when:** running gen-diagrams.js under a neutral instance config produces diagrams + assertions.json with no hard-coded OTB/
|
|
145
|
+
**Done when:** running gen-diagrams.js under a neutral instance config produces diagrams + assertions.json with no hard-coded OTB/Example tokens; OTB's instance still renders its own branding.`,
|
|
146
146
|
kind: 'refactor', discipline: 'engineer', priority: 4,
|
|
147
147
|
est_minutes: 180, manual_degree: 2, credits_reward: 30,
|
|
148
148
|
touches: ['scripts/gds/gen-diagrams.js'], criteria: ['C8'], deps: ['R53'],
|
|
@@ -190,7 +190,7 @@ The CLI config root (~/.config/otb/: gds-session.json, env, discord-webhooks.jso
|
|
|
190
190
|
title: 'Config-driven owner bootstrap + per-instance repo/OAuth binding',
|
|
191
191
|
description: `Part of Medusa (C8). Cross-ref: R52, R53.
|
|
192
192
|
|
|
193
|
-
Replace migration 023's hard-coded founding-Archon literal (UPDATE builders SET rank='archon' WHERE github_login='
|
|
193
|
+
Replace migration 023's hard-coded founding-Archon literal (UPDATE builders SET rank='archon' WHERE github_login='example-owner') with an install-time first-admin bootstrap from instance config. Make the GitHub repo binding (repo-info.js — the OTB_GITHUB_OWNER/REPO seam already exists) and the OAuth/App config per-instance. Single-project model keeps the global economy fine — only the owner literal + repo/OAuth need to generalize.
|
|
194
194
|
|
|
195
195
|
**Done when:** grep over migrations/ returns zero login literals; a fresh install seats the configured first admin; repo + OAuth resolve from instance config.`,
|
|
196
196
|
kind: 'feature', discipline: 'engineer', priority: 4,
|
|
@@ -202,7 +202,7 @@ Replace migration 023's hard-coded founding-Archon literal (UPDATE builders SET
|
|
|
202
202
|
title: 'Containerized self-host instance (Dockerfile + docker-compose)',
|
|
203
203
|
description: `Part of Medusa (C8). Cross-ref: R51, R52.
|
|
204
204
|
|
|
205
|
-
A Dockerfile + docker-compose (app + Postgres) that brings a Medusa instance up from clean state with ZERO hard-coded droplet IP
|
|
205
|
+
A Dockerfile + docker-compose (app + Postgres) that brings a Medusa instance up from clean state with ZERO hard-coded droplet IP, instance-specific systemd unit, or hand-maintained ~/deploy.sh. This is the self-host artifact; it does not touch prod's existing laptop/CI deploy path.
|
|
206
206
|
|
|
207
207
|
**Done when:** \`docker compose up\` on a machine that has never seen our droplet brings up Medusa + Postgres and the hall is reachable; no prod topology referenced anywhere in the path.`,
|
|
208
208
|
kind: 'infra', discipline: 'engineer', priority: 4,
|
|
@@ -248,7 +248,7 @@ Separate the portable methodology core Medusa ships (the working rules, the sess
|
|
|
248
248
|
{
|
|
249
249
|
ref: 'R67',
|
|
250
250
|
title: 'PROOF — Medusa dogfood: a second, differently-branded non-game instance ships a task',
|
|
251
|
-
description: `Part of Medusa (C8) — the proof-task that satisfies C8 ("the GDS builds itself"). Customer 0 = Medusa itself, running on the vanilla brand pack (R74); customer 1 =
|
|
251
|
+
description: `Part of Medusa (C8) — the proof-task that satisfies C8 ("the GDS builds itself"). Customer 0 = Medusa itself, running on the vanilla brand pack (R74); customer 1 = Example.
|
|
252
252
|
|
|
253
253
|
Stand Medusa up as customer 0 (its own database, vanilla brand, no game code) and take one real task through the full lifecycle on it: claim -> grade -> merge -> ship with credits awarded. Medusa building Medusa, on Medusa. (A separate non-game third project is an acceptable alternative proof, but customer-0 self-build is the truest.)
|
|
254
254
|
|
|
@@ -342,7 +342,7 @@ Verify the private and public lineages stay consistent: migration numbering (the
|
|
|
342
342
|
title: 'Medusa vanilla brand pack — the plain "customer 0" identity (Substack-like)',
|
|
343
343
|
description: `Part of Medusa (C8). Cross-ref: R53 (branding-contract layer), R57 (customer 1 / OTB pack).
|
|
344
344
|
|
|
345
|
-
Design + build the canonical Medusa VANILLA brand pack — the identity Medusa itself runs on as **customer 0** (
|
|
345
|
+
Design + build the canonical Medusa VANILLA brand pack — the identity Medusa itself runs on as **customer 0** (Example is customer 1). Deliberately PLAIN, clean, modern, professional — restrained palette, clean sans-serif type, minimal wordmark, plain-English product copy, simple favicon/icons. Aesthetic touchstone: Substack-plain; take INSPIRATION from planetscale.com's simplicity (do NOT copy). Delivered as a branding pack against the R53 contract; it is the default the hall + status render under when no other pack is supplied, and the brand Medusa ships on. Distinct from R57 (extracting customer 1 / OTB's Greek look) — this is customer 0's identity. No game/Greek flavor.
|
|
346
346
|
|
|
347
347
|
**Done when:** a complete Medusa vanilla brand pack (name/wordmark, restrained palette, clean type, plain copy, favicon/icons) renders the hall + status as a plain, professional dev toolkit; it is the pack customer 0 (Medusa) runs on; verified on the standalone boot.`,
|
|
348
348
|
kind: 'feature', discipline: 'artist', priority: 4,
|
|
@@ -19,7 +19,7 @@ const DRY = process.argv.includes('--dry-run');
|
|
|
19
19
|
const VERSION = 'BONGOS-V1';
|
|
20
20
|
const GOAL_TITLE = 'Module upstreaming & catalog — instances contribute modules into core';
|
|
21
21
|
const GOAL_DESC =
|
|
22
|
-
'ADR 0107. The reverse of the ADR 0100/0103 distribution arrow: instances (
|
|
22
|
+
'ADR 0107. The reverse of the ADR 0100/0103 distribution arrow: instances (Example, ' +
|
|
23
23
|
'Mercury) build feature modules and submit them for approval to be merged INTO the shared ' +
|
|
24
24
|
'Cloud Bongos core catalog, credited to author + origin project. Accepted modules land in ' +
|
|
25
25
|
'core modules/<key>/ default-off, added to the isPublishable() allowlist; every instance may ' +
|
|
@@ -52,7 +52,7 @@ const TASKS = [
|
|
|
52
52
|
title: 'ADR: instance docs-discovery contract (how an agent finds its hall/docs)',
|
|
53
53
|
description: `From the 2026-07-04 review (bullet 6). Today the only pointer to an instance's doc surface is \`domains.buildersOrigin\` in branding.json. A fresh self-hosted instance needs a DEFINED contract for how an agent reliably locates its builders' hall + rendered docs from config alone (standard docs location? discovery endpoint? manifest field?). Write an ADR that pins the contract, then CLAUDE.md's preamble references it instead of hand-waving.
|
|
54
54
|
|
|
55
|
-
**Done when:** an ADR records the docs-discovery contract; branding/config carries the needed field(s); CLAUDE.md §0 points at the contract; a second instance can resolve its docs surface with no hard-coded
|
|
55
|
+
**Done when:** an ADR records the docs-discovery contract; branding/config carries the needed field(s); CLAUDE.md §0 points at the contract; a second instance can resolve its docs surface with no hard-coded example.`,
|
|
56
56
|
kind: 'decision', discipline: 'engineer', priority: 3,
|
|
57
57
|
est_minutes: 90, manual_degree: 3, credits_reward: 20,
|
|
58
58
|
touches: ['docs/adr/', 'config/', 'CLAUDE.md'], criteria: ['C16'], deps: [],
|
|
@@ -80,12 +80,12 @@ const TASKS = [
|
|
|
80
80
|
{
|
|
81
81
|
key: 'NAV-HYGIENE',
|
|
82
82
|
title: 'Nav hygiene — reference the module, split instance-specifics',
|
|
83
|
-
description: `From the 2026-07-04 review (bullets 42, 57, 64, 66, 68, 94). Several nav rows (and §2 prose) INLINE content that belongs to a module or to instance-specifics. Convert them to module references, and split anything naming the
|
|
84
|
-
- **discord** (57, 64, 94): inbound #ideas, build-broadcast, channel layout → reference the discord module; the general mechanism lives there, anything naming the
|
|
83
|
+
description: `From the 2026-07-04 review (bullets 42, 57, 64, 66, 68, 94). Several nav rows (and §2 prose) INLINE content that belongs to a module or to instance-specifics. Convert them to module references, and split anything naming the example servers/assets into project-specific detail. By module:
|
|
84
|
+
- **discord** (57, 64, 94): inbound #ideas, build-broadcast, channel layout → reference the discord module; the general mechanism lives there, anything naming the example Discord server specifically → project-specific detail.
|
|
85
85
|
- **art-pipeline** (66): the pixel-art design-system row → reference the art module; the pixel/greek style choice itself is instance detail.
|
|
86
86
|
- **game** (68): world-cell model + paths-as-overlays rows → the game module / instance context (out of the portable core).
|
|
87
87
|
|
|
88
|
-
**Done when:** those nav/§2 items are thin references to their modules; no module-owned or
|
|
88
|
+
**Done when:** those nav/§2 items are thin references to their modules; no module-owned or example-server-specific detail is inlined in the core; the game-specific rows are out of core.`,
|
|
89
89
|
kind: 'cleanup', discipline: 'engineer', priority: 3,
|
|
90
90
|
est_minutes: 120, manual_degree: 3, credits_reward: 25,
|
|
91
91
|
touches: ['CLAUDE.md'], criteria: ['C32'], deps: [],
|
|
@@ -123,7 +123,7 @@ const TASKS = [
|
|
|
123
123
|
{
|
|
124
124
|
key: 'IDENTITY-ORG',
|
|
125
125
|
title: 'Rationalize the identity apparatus + structure project-context.md',
|
|
126
|
-
description: `From the 2026-07-04 review (bullets 109-111, 134/140, 187). Almost every identity section (§§1,3,5,6,7,9) is now a thin pointer to project-context.md, the §3 stub verbosely re-explains the core↔host split that §1 + the charter already cover, and the §6/§7 stubs still NAME the instance ("the
|
|
126
|
+
description: `From the 2026-07-04 review (bullets 109-111, 134/140, 187). Almost every identity section (§§1,3,5,6,7,9) is now a thin pointer to project-context.md, the §3 stub verbosely re-explains the core↔host split that §1 + the charter already cover, and the §6/§7 stubs still NAME the instance ("the Example world", "the \`otb-*\` art skills"). Meanwhile project-context.md is getting overloaded (bullet 187). Fix: collapse §§3/5/6/7 to a single minimal pointer (or one-liners), genericize instance names out of any surviving stub, and give project-context.md a real section structure.
|
|
127
127
|
|
|
128
128
|
**Done when:** the stub scaffolding is minimal + instance-name-free in the core; project-context.md has a clear, non-overloaded structure; anchors/cross-refs still resolve.`,
|
|
129
129
|
kind: 'cleanup', discipline: 'engineer', priority: 3,
|
|
@@ -148,7 +148,7 @@ const TASKS = [
|
|
|
148
148
|
title: 'Scheduled-routines portability to self-host + multi-builder',
|
|
149
149
|
description: `From the 2026-07-04 review (bullet 241). The ~17 autonomous cron routines under \`.claude/scheduled-tasks/\` (overnight-code-review, idea-triage-nightly, overnight-builder, bfg, security-review-weekly, …) are written for this instance. Define how they port to a self-hosted, multi-builder world: which run instance-wide vs per-builder, how a fresh instance inherits/configures them, and what a builder gets by default. Spike → a contract (and likely a config surface).
|
|
150
150
|
|
|
151
|
-
**Done when:** there's a documented model for which routines are instance-wide vs per-builder, how a fresh instance inherits them, and how a builder configures their own; no routine hard-assumes the
|
|
151
|
+
**Done when:** there's a documented model for which routines are instance-wide vs per-builder, how a fresh instance inherits them, and how a builder configures their own; no routine hard-assumes the example instance.`,
|
|
152
152
|
kind: 'spike', discipline: 'engineer', priority: 2,
|
|
153
153
|
est_minutes: 120, manual_degree: 3, credits_reward: 25,
|
|
154
154
|
touches: ['.claude/scheduled-tasks/', 'docs/'], criteria: ['C16'], deps: [],
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// Run: node scripts/gds/seed-provisioning-tasks.js
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const TOKEN = require(os.homedir() + '/.config/otb/gds-session.json').token;
|
|
10
|
-
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://
|
|
10
|
+
const BASE = require('../../src/instance-config').resolveEnv('BASE') || 'https://example.com';
|
|
11
11
|
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };
|
|
12
12
|
const GOAL = 26; // Cloud Bongos productization and self-host
|
|
13
13
|
|
|
@@ -27,7 +27,7 @@ const TASKS = [
|
|
|
27
27
|
deps: ['P1', 'P2'], extDeps: [] },
|
|
28
28
|
{ ref: 'P4', kind: 'infra', discipline: 'engineer', priority: 2, est: 300,
|
|
29
29
|
title: 'BV1 — Automated DNS record + Caddy on-demand ACME TLS for a provisioned host',
|
|
30
|
-
description: 'ADR 0111 §4. Cloudflare A-record UPSERT via the shared client (P2), plus Caddy on-demand ACME (Let\'s Encrypt) so a NEWLY provisioned host is TLS-ready with no manual Cloudflare Origin-Cert console step (the least-automated leg today). On-demand TLS MUST be allow-listed to hostnames the module actually provisioned (no cert-minting for arbitrary names). Existing
|
|
30
|
+
description: 'ADR 0111 §4. Cloudflare A-record UPSERT via the shared client (P2), plus Caddy on-demand ACME (Let\'s Encrypt) so a NEWLY provisioned host is TLS-ready with no manual Cloudflare Origin-Cert console step (the least-automated leg today). On-demand TLS MUST be allow-listed to hostnames the module actually provisioned (no cert-minting for arbitrary names). Existing example/cloudbongos apexes keep their Origin Certs unchanged. deps: P2, P3. (buildable now)',
|
|
31
31
|
deps: ['P2', 'P3'], extDeps: [] },
|
|
32
32
|
{ ref: 'P5', kind: 'feature', discipline: 'engineer', priority: 2, est: 240,
|
|
33
33
|
title: 'BV1 — Wire provisioning into `bongos init` (idea #345 — "configured via the API")',
|
|
@@ -16,19 +16,32 @@
|
|
|
16
16
|
# column that referenced a session, then deletes all sessions so NO real
|
|
17
17
|
# bearer token persists on staging.
|
|
18
18
|
#
|
|
19
|
-
# SAFETY: hard-refuses to target
|
|
19
|
+
# SAFETY: hard-refuses to target any database not named `*_staging`, so a
|
|
20
20
|
# fat-fingered env can never point the wipe at production. Everything runs at
|
|
21
|
-
# table-owner privilege (no superuser)
|
|
21
|
+
# table-owner privilege (no superuser) — the invoking role owns the staging objects.
|
|
22
22
|
#
|
|
23
23
|
# "What's real, what's faked on staging": see docs/recipes/staging-data.md.
|
|
24
24
|
|
|
25
25
|
set -euo pipefail
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
# No baked-in database names. A portable core cannot know an instance's DB, and the
|
|
28
|
+
# founder's two were hardcoded here until task 1003869 — including inside the safety
|
|
29
|
+
# check, which meant de-branding the names alone would have quietly disarmed it. So the
|
|
30
|
+
# guard is STRUCTURAL now instead of a name comparison: the target must be named
|
|
31
|
+
# <something>_staging. That keeps the original property ("a fat-fingered env can never
|
|
32
|
+
# point the wipe at production", since no production DB is called *_staging) and extends
|
|
33
|
+
# it to every instance rather than one. Both names must be set explicitly — this script
|
|
34
|
+
# WIPES its target, so there is no default worth guessing.
|
|
35
|
+
PROD_DB="${PROD_DB:-}"
|
|
36
|
+
STAGING_DB="${STAGING_DB:-}"
|
|
29
37
|
|
|
30
|
-
if [ "$STAGING_DB"
|
|
31
|
-
echo "REFUSING:
|
|
38
|
+
if [ -z "$STAGING_DB" ] || [ -z "$PROD_DB" ]; then
|
|
39
|
+
echo "REFUSING: set PROD_DB (the dump source) and STAGING_DB (the wipe target) explicitly." >&2
|
|
40
|
+
echo " e.g. PROD_DB=myapp STAGING_DB=myapp_staging $0" >&2
|
|
41
|
+
exit 2
|
|
42
|
+
fi
|
|
43
|
+
if ! printf '%s' "$STAGING_DB" | grep -qE '^[A-Za-z0-9_]+_staging$'; then
|
|
44
|
+
echo "REFUSING: STAGING_DB '$STAGING_DB' must be a bare identifier ending in '_staging'. This script wipes its target." >&2
|
|
32
45
|
exit 2
|
|
33
46
|
fi
|
|
34
47
|
if [ "$PROD_DB" = "$STAGING_DB" ]; then
|
|
@@ -171,7 +171,7 @@ function readSessionApiBase() {
|
|
|
171
171
|
// PR-body deploy note, and the manual-confirm curl guidance. Precedence mirrors the
|
|
172
172
|
// CLI's own base resolution: <PREFIX>_API_BASE env → the signed-in session's
|
|
173
173
|
// api_base → branding publicOrigin → null (unknowable). There is NO hardcoded
|
|
174
|
-
// founder host — 5 sites here baked in `https://
|
|
174
|
+
// founder host — 5 sites here baked in `https://example.com`, so a ci-land /
|
|
175
175
|
// manual-confirm on a migrated instance pointed the builder at the WRONG box; the
|
|
176
176
|
// ci-land/guidance sibling of resolveDeployTarget()'s laptop-mode fix (task 1002232
|
|
177
177
|
// ⊃ task 1002202). Pure over its injected inputs (env / sessionApiBase / branding)
|
package/scripts/gds/ship-flow.js
CHANGED
|
@@ -306,7 +306,7 @@ async function shipMain() {
|
|
|
306
306
|
|
|
307
307
|
// ---- Sandbox-first review gate (#927, ADR 0046) ----
|
|
308
308
|
// On a dev box, a diff that touches game surfaces must be STAGED on the
|
|
309
|
-
// builder's own live preview (sandbox-<login>.
|
|
309
|
+
// builder's own live preview (sandbox-<login>.example.com, ADR 0044)
|
|
310
310
|
// and reviewed in a browser before the claim resolves — see it running,
|
|
311
311
|
// get the chance to iterate, THEN decide to ship to the main game. Laptop
|
|
312
312
|
// sessions, non-game diffs, quick-tunnel boxes, and --skip-stage all pass
|
package/scripts/gds/smoke-gds.js
CHANGED
|
@@ -54,7 +54,7 @@ function matchedApiBaseVar(env) {
|
|
|
54
54
|
// null.
|
|
55
55
|
//
|
|
56
56
|
// There is deliberately NO hardcoded host. This file baked in
|
|
57
|
-
// `https://
|
|
57
|
+
// `https://example.com` as its fallback, so a builder signed into any OTHER
|
|
58
58
|
// instance who shipped without an env override ran the ship's verification smoke
|
|
59
59
|
// against the founder's box — which PASSED, because that box is healthy. The ship
|
|
60
60
|
// then reported "smoke=clean" on the strength of testing a system it never
|
package/scripts/status-probe.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// External uptime prober for
|
|
2
|
+
// External uptime prober for example.com (incident 2026-06-03, task #565).
|
|
3
3
|
//
|
|
4
4
|
// WHY THIS EXISTS, AND WHY IT IS NOT src/bongos/uptime-poller.js:
|
|
5
5
|
// The in-process poller (src/bongos/uptime-poller.js) runs INSIDE the Node
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
// the step output). Non-zero only on its own internal error.
|
|
28
28
|
//
|
|
29
29
|
// USAGE:
|
|
30
|
-
// node scripts/status-probe.mjs [--dir .] [--target https://
|
|
30
|
+
// node scripts/status-probe.mjs [--dir .] [--target https://example.com/healthz]
|
|
31
31
|
// [--attempts 3] [--timeout-ms 15000] [--gap-ms 10000]
|
|
32
32
|
|
|
33
33
|
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
@@ -167,9 +167,9 @@ async function addDiscordGuildMember({ guildId, discordUserId, accessToken, role
|
|
|
167
167
|
|
|
168
168
|
// Collect EVERY candidate auth token on the request, in priority order, with
|
|
169
169
|
// duplicates removed. A browser can legitimately carry MORE THAN ONE
|
|
170
|
-
// gds_session cookie: a host-only one (set on
|
|
170
|
+
// gds_session cookie: a host-only one (set on example.com before the
|
|
171
171
|
// #726/#734 apex-scoping rollout) AND the current apex-scoped one
|
|
172
|
-
// (.
|
|
172
|
+
// (.example.com). The browser sends both in the Cookie header. We must try
|
|
173
173
|
// them ALL — picking only the first (as the old single-value extractToken did)
|
|
174
174
|
// meant a stale/expired duplicate could shadow the live session and 401 the
|
|
175
175
|
// builder (hit on the Discord link callback for builder 25). Order: Bearer
|
|
@@ -35,7 +35,7 @@ let reconnectDelayMs = 2000; // backoff before re-LISTEN; test-tunable via _set
|
|
|
35
35
|
// exact same socket + database (PGHOST/PGDATABASE env overrides honored for
|
|
36
36
|
// non-droplet environments). The DB-name default resolves through pool.js's
|
|
37
37
|
// instanceDbName() — the SINGLE source for the instance's configured db.database
|
|
38
|
-
// (from the branding pack) — instead of a second hardcoded "
|
|
38
|
+
// (from the branding pack) — instead of a second hardcoded "example" literal
|
|
39
39
|
// (ADR 0062 §5). A non-OTB instance LISTENs on its OWN DB, and there is one place
|
|
40
40
|
// to change the default.
|
|
41
41
|
function pgConfig() {
|
|
@@ -140,13 +140,80 @@ function escapeRe(s) {
|
|
|
140
140
|
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
// task 1002874: the glob is normalised ONCE here, at build, not per candidate —
|
|
144
|
+
// there are 51 surfaces and `matchProtected` runs over every changed file on the
|
|
145
|
+
// push/claim/grade path. `s.glob` itself is kept VERBATIM on the entry, because it
|
|
146
|
+
// is what gets reported back to a refused builder and in the registry's own docs;
|
|
147
|
+
// only the compiled matcher is folded.
|
|
148
|
+
const COMPILED = PROTECTED_SURFACES.map((s) => ({ ...s, re: globToRegExp(foldForMatch(s.glob)) }));
|
|
144
149
|
|
|
145
150
|
// Normalize a path the way git reports it (forward slashes, no leading ./).
|
|
151
|
+
// normalize — put a path in the ONE spelling matching happens in.
|
|
152
|
+
//
|
|
153
|
+
// task 1002874 (ADR 0151 §4, the last piece of R101's scope): this used to do
|
|
154
|
+
// only backslash→slash and strip a leading './', so matching was exact-BYTES. On
|
|
155
|
+
// a case-insensitive filesystem — macOS by default, and Windows, i.e. the two
|
|
156
|
+
// platforms most builders use — `Src/Bongos/Auth.js` and `src/bongos/auth.js` name
|
|
157
|
+
// the SAME file, and only one of them matched the protected-surface glob. Unicode
|
|
158
|
+
// composition does the same thing invisibly: NFD (`e` + U+0301) and NFC (`é`) are
|
|
159
|
+
// different byte strings for one filename, and a checkout can hand you either.
|
|
160
|
+
// A protected-path wall you can step over by spelling is not a wall.
|
|
161
|
+
//
|
|
162
|
+
// BOTH SIDES go through this — the candidate here, and every registry glob ONCE at
|
|
163
|
+
// COMPILED build — so the transform is applied symmetrically and matching cannot
|
|
164
|
+
// become narrower. It is a strict widening: any pair that matched byte-for-byte
|
|
165
|
+
// still matches after both are folded the same way. The only regression shape is
|
|
166
|
+
// OVER-protection, surfacing as an unexpected claim/push refusal, never a silent
|
|
167
|
+
// un-protection.
|
|
168
|
+
//
|
|
169
|
+
// CASE FOLD: `toLowerCase()`, and that IS the invariant fold here. The Turkish-I
|
|
170
|
+
// hazard the ADR warns about belongs to `toLocaleLowerCase('tr')`, which maps
|
|
171
|
+
// 'I' → 'ı' (dotless) — measured. Plain `toLowerCase()` is Unicode Default Case
|
|
172
|
+
// Conversion and is locale-independent by specification, so it is the correct
|
|
173
|
+
// primitive and `toLocaleLowerCase` is the one that must never appear on this
|
|
174
|
+
// path. A test asserts it does not.
|
|
175
|
+
//
|
|
176
|
+
// ORDER: NFC first, then fold. Measured equivalent to folding first for the
|
|
177
|
+
// composed-accent cases, and NFC-first keeps the input in one canonical form
|
|
178
|
+
// before any other transform reasons about it.
|
|
146
179
|
function normalize(p) {
|
|
147
180
|
return String(p || '').replace(/\\/g, '/').replace(/^\.\//, '').trim();
|
|
148
181
|
}
|
|
149
182
|
|
|
183
|
+
// foldForMatch — the spelling MATCHING happens in. Separate from normalize() on
|
|
184
|
+
// purpose: normalize() still produces the path we REPORT, and that must keep its
|
|
185
|
+
// original case. A finding, a refusal message and a grader issue all carry
|
|
186
|
+
// `file`, and on a case-sensitive filesystem (Linux, i.e. CI) a lowercased path
|
|
187
|
+
// names nothing — a consumer that tried to read it would get ENOENT for a file
|
|
188
|
+
// that exists.
|
|
189
|
+
function foldForMatch(p) {
|
|
190
|
+
return normalize(p).normalize('NFC').toLowerCase();
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// compileGlobMatcher(globs) → (file) => boolean, with the fold applied to BOTH
|
|
194
|
+
// sides: each glob once here, the candidate on every call.
|
|
195
|
+
//
|
|
196
|
+
// task 1002874: this exists because `globToRegExp` + the raw `PROTECTED_GLOBS`
|
|
197
|
+
// are BOTH exported, and a consumer that compiles them itself gets a byte-exact
|
|
198
|
+
// matcher — which is precisely the wall this task removed, rebuilt by accident.
|
|
199
|
+
// gate-review.js had done exactly that (its ESCALATE / HARD_FLOOR classifier),
|
|
200
|
+
// so an uppercased spelling still slipped past CI's auto-merge gate after the
|
|
201
|
+
// matcher here was fixed. Anything matching a PROTECTED-surface list should
|
|
202
|
+
// compile through this, not through globToRegExp directly.
|
|
203
|
+
//
|
|
204
|
+
// NOT a blanket replacement for globToRegExp, deliberately. Folding widens what
|
|
205
|
+
// matches, and that is only safe when matching more means protecting more. On an
|
|
206
|
+
// ALLOW-list — publish-manifest's PUBLISH_ALLOWLIST, which decides what reaches
|
|
207
|
+
// the public OSS mirror — widening would publish MORE files. Those lists keep
|
|
208
|
+
// the exact matcher on purpose.
|
|
209
|
+
function compileGlobMatcher(globs) {
|
|
210
|
+
const compiled = (globs || []).map((g) => globToRegExp(foldForMatch(g)));
|
|
211
|
+
return (file) => {
|
|
212
|
+
const folded = foldForMatch(file);
|
|
213
|
+
return folded ? compiled.some((re) => re.test(folded)) : false;
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
150
217
|
// Which of `files` are permission-sensitive? Returns
|
|
151
218
|
// [{ file, glob, floor, permission }] — floor + permission come straight from the
|
|
152
219
|
// registry entry that matched, so a governance re-map flows through every caller.
|
|
@@ -155,7 +222,11 @@ function matchProtected(files) {
|
|
|
155
222
|
for (const raw of files || []) {
|
|
156
223
|
const f = normalize(raw);
|
|
157
224
|
if (!f) continue;
|
|
158
|
-
|
|
225
|
+
// Match on the FOLDED spelling, report the original one — see foldForMatch.
|
|
226
|
+
// Hoisted OUT of the predicate on purpose: inside it, the fold re-runs once
|
|
227
|
+
// per registry surface (51 today) for every candidate file.
|
|
228
|
+
const folded = foldForMatch(raw);
|
|
229
|
+
const hit = COMPILED.find((c) => c.re.test(folded));
|
|
159
230
|
if (hit) out.push({ file: f, glob: hit.glob, floor: hit.floor, permission: hit.permission });
|
|
160
231
|
}
|
|
161
232
|
return out;
|
|
@@ -164,7 +235,8 @@ function matchProtected(files) {
|
|
|
164
235
|
// The registry entry governing `file`, or null when the path is unprotected.
|
|
165
236
|
function surfaceFor(file) {
|
|
166
237
|
const f = normalize(file);
|
|
167
|
-
const
|
|
238
|
+
const folded = f ? foldForMatch(file) : '';
|
|
239
|
+
const hit = f ? COMPILED.find((c) => c.re.test(folded)) : null;
|
|
168
240
|
return hit ? { glob: hit.glob, floor: hit.floor, permission: hit.permission, group: hit.group, why: hit.why } : null;
|
|
169
241
|
}
|
|
170
242
|
|
|
@@ -271,6 +343,11 @@ module.exports = {
|
|
|
271
343
|
REGISTRY_PATH,
|
|
272
344
|
SPECIALTY,
|
|
273
345
|
globToRegExp,
|
|
346
|
+
compileGlobMatcher,
|
|
347
|
+
// foldForMatch is deliberately NOT exported: it is the internal spelling rule,
|
|
348
|
+
// and every caller should go through compileGlobMatcher so the fold is applied
|
|
349
|
+
// to BOTH sides. Handing it out invites a consumer to fold one side only, which
|
|
350
|
+
// is the asymmetry that breaks matching outright.
|
|
274
351
|
matchProtected,
|
|
275
352
|
surfaceFor,
|
|
276
353
|
floorFor,
|
package/src/bongos/pool.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// DB resolution (highest wins), NOT hard-defaulting to a host's DB name:
|
|
11
11
|
// DATABASE_URL -> PGHOST/PGDATABASE env -> the instance's configured db.database
|
|
12
12
|
// (config/branding.json, else config/branding.neutral.json)
|
|
13
|
-
// OTB's branding pack carries db.database='
|
|
13
|
+
// OTB's branding pack carries db.database='example', so prod (no DB env set) resolves to the
|
|
14
14
|
// exact same socket+database it used before this carve-out — live prod is unaffected. A fresh
|
|
15
15
|
// instance points this at its own DB via env or its own branding pack; the neutral starter has
|
|
16
16
|
// db.database=null so a misconfigured instance fails loudly rather than silently targeting OTB.
|