@bongos/core 1.19.1073 → 1.19.1075
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 +404 -549
- package/.claude/skills/feedback/SKILL.md +2 -2
- package/bin/bongos.js +1 -3
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -60
- package/clients/bongos-client/index.cjs +4 -60
- package/clients/bongos-client/index.d.ts +5 -90
- package/clients/bongos-client/index.mjs +4 -60
- package/config/branding.neutral.json +1 -5
- package/config/modules.neutral.json +2 -3
- package/config/scheduled-routines.json +0 -2
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +1 -0
- package/docs/adr/0285-a-shared-box-holds-about-twelve-projects-per-gb-and-memory-is-the-wall.md +1 -0
- package/docs/adr/0323-hosting-is-three-shapes-and-we-are-not-the-landlord.md +1 -1
- package/docs/adr/0327-a-cloud-host-runs-on-the-owners-account-and-the-key-is-borrowed.md +1 -1
- package/docs/adr/0341-the-page-is-the-unit-of-tweak-mode.md +2 -0
- package/docs/adr/0345-we-host-every-projects-hall-and-its-app-deploys-where-the-owner-chooses.md +94 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +175 -1665
- package/docs/api-reference.md +9 -41
- package/docs/architecture.md +16 -1
- package/docs/branding-contract.md +0 -3
- package/docs/copy-inventory.md +454 -479
- package/docs/copy-registry.json +746 -978
- package/docs/file-map.md +5 -34
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +13 -1
- package/docs/onboarding/diagrams/04-architecture.mmd +11 -22
- package/docs/onboarding/diagrams/README.md +3 -3
- package/docs/onboarding/diagrams/assertions.json +2 -22
- package/docs/onboarding/primer.md +9 -28
- package/docs/page-inventory.json +4 -1
- package/docs/page-readings.json +1032 -1077
- package/docs/recipes/render-pilot.md +29 -10
- package/migrations/core_257_module_store_registry.sql +87 -0
- package/migrations/core_258_drop_dev_box_tables.sql +55 -0
- package/modules/agents/module.json +1 -0
- package/modules/autonomy/module.json +1 -0
- package/modules/builder-settings/module.json +1 -0
- package/modules/builder-settings/render-prefs.js +4 -5
- package/modules/copy-desk/docx.js +78 -5
- package/modules/copy-desk/module.json +1 -0
- package/modules/copy-desk/routes/copy-desk.js +10 -10
- package/modules/copy-desk/tests/copy_docx.mjs +107 -1
- package/modules/copy-desk/tests/fixtures/word-docx.mjs +30 -0
- package/modules/discord/module.json +1 -0
- package/modules/discord/ship-broadcast.js +3 -3
- package/modules/economy/credits.js +2 -2
- package/modules/economy/module.json +1 -0
- package/modules/economy/routes/credits.js +1 -2
- package/modules/government/catalog.js +13 -17
- package/modules/government/migrations/government_019_retire_box_permissions.sql +33 -0
- package/modules/government/module.json +1 -0
- package/modules/government/protected-surfaces.json +3 -11
- package/modules/government/resolver.js +3 -46
- package/modules/government/routes/government.js +0 -2
- package/modules/government/session-scopes.js +43 -83
- package/modules/government/session-scopes.json +8 -18
- package/modules/grading/module.json +1 -0
- package/modules/hall-ui/module.json +1 -0
- package/modules/hall-ui/public/board-lib.js +2 -2
- package/modules/hall-ui/public/builders.js +29 -40
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/collab.js +1 -1
- package/modules/hall-ui/public/diagrams.html +3 -3
- package/modules/hall-ui/public/dom-utils.js +1 -1
- package/modules/hall-ui/public/gate.js +2 -2
- package/modules/hall-ui/public/gate.states.json +1 -1
- package/modules/hall-ui/public/hall-render.js +43 -79
- package/modules/hall-ui/public/index.html +5 -1
- package/modules/hall-ui/public/modules.js +1 -0
- package/modules/hall-ui/public/oversight.css +5 -5
- package/modules/hall-ui/public/palette.js +0 -11
- package/modules/hall-ui/public/primer.js +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings-sessions.js +1 -1
- package/modules/hall-ui/public/settings.css +0 -1
- package/modules/hall-ui/public/settings.html +4 -33
- package/modules/hall-ui/public/settings.js +3 -33
- package/modules/hall-ui/public/settings.states.json +2 -2
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/style.css +10 -13
- package/modules/hall-ui/public/task.html +4 -1
- package/modules/hall-ui/public/task.js +36 -1
- package/modules/hall-ui/public/tweak-editor-lib.js +18 -1
- package/modules/hall-ui/public/tweak-editor.css +16 -0
- package/modules/hall-ui/public/tweak-editor.html +26 -3
- package/modules/hall-ui/public/tweak-editor.js +32 -1
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/hall-ui/records/tweak-editor.md +1 -0
- package/modules/ideas/module.json +1 -0
- package/modules/ideas/projection.js +1 -1
- package/modules/lifecycle/db-claim-reads.js +1 -58
- package/modules/lifecycle/db-ship.js +4 -9
- package/modules/lifecycle/db.js +0 -4
- package/modules/lifecycle/github-push.js +12 -14
- package/modules/lifecycle/kickoff-checklist.js +0 -4
- package/modules/lifecycle/lifecycle.js +0 -8
- package/modules/lifecycle/module.json +1 -0
- package/modules/lifecycle/publish-reconciler.js +10 -10
- package/modules/lifecycle/routes/tasks.js +13 -15
- package/modules/lifecycle/ship-card.js +1 -1
- package/modules/lifecycle/task-visuals.js +2 -2
- package/modules/memory/module.json +1 -0
- package/modules/memory/routes/memory.js +2 -2
- package/modules/npm-release/module.json +1 -0
- package/modules/onboarding/module.json +1 -0
- package/modules/onboarding/onboarding-state.js +17 -36
- package/modules/onboarding/routes/access-requests.js +3 -3
- package/modules/platform-identity/module.json +1 -0
- package/modules/platform-identity/platform-identity.js +13 -5
- package/modules/platform-identity/routes/sso.js +5 -4
- package/modules/platform-identity/tests/platform-identity.mjs +17 -3
- package/modules/provisioning/capacity.js +1 -1
- package/modules/provisioning/module.json +1 -0
- package/modules/provisioning/provisioning.js +6 -5
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/provisioning/starter-bundles.js +5 -25
- package/modules/public-landing/module.json +1 -0
- package/modules/public-landing/public/assets/cosmos.css +1 -1
- package/modules/public-landing/public/contact.html +1 -1
- package/modules/public-landing/public/privacy.html +1 -1
- package/modules/public-landing/public/projects.html +101 -32
- package/modules/public-landing/public/projects.probes.json +2 -2
- package/modules/public-landing/public/projects.states.json +4 -3
- package/modules/public-landing/public/terms.html +1 -1
- package/modules/security/module.json +1 -0
- package/modules/sessions/db.js +1 -1
- package/modules/sessions/module.json +1 -0
- package/modules/specialities/module.json +1 -0
- package/modules/status-ui/module.json +1 -0
- package/modules/ui-design/module.json +1 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +84 -0
- package/scripts/gds/artifact-format.js +203 -0
- package/scripts/gds/artifact-staleness.js +2 -2
- package/scripts/gds/audit-authorship.sh +1 -13
- package/scripts/gds/backfill-task-rewards.js +5 -5
- package/scripts/gds/claim.js +0 -15
- package/scripts/gds/claude-materialize.js +2 -2
- package/scripts/gds/cli-lib.js +15 -15
- package/scripts/gds/codemod-rename-src-gds.js +1 -1
- package/scripts/gds/context-pack.js +5 -7
- package/scripts/gds/diagram-facts.js +12 -23
- package/scripts/gds/do-api.js +13 -80
- package/scripts/gds/doc-cli-guard.js +2 -0
- package/scripts/gds/doctor.js +4 -4
- package/scripts/gds/feedback-latest.js +139 -9
- package/scripts/gds/fitness-checks-identity.js +2 -7
- package/scripts/gds/fitness-checks-packaging.js +4 -4
- package/scripts/gds/fitness-checks-write-validation.js +4 -0
- package/scripts/gds/fitness-ratchets.js +5 -5
- package/scripts/gds/fitness.js +10 -10
- package/scripts/gds/gen-api-client.js +4 -4
- package/scripts/gds/gen-api-docs.js +1 -1
- package/scripts/gds/gen-diagrams.js +49 -71
- package/scripts/gds/gen-repo-map.js +13 -14
- package/scripts/gds/gen-session-index.js +8 -8
- package/scripts/gds/http-api-client.js +12 -19
- package/scripts/gds/init.js +1 -1
- package/scripts/gds/leak-scan-allowlist.js +1 -1
- package/scripts/gds/lint-infra-exec.js +1 -1
- package/scripts/gds/local-preview-lib.js +8 -39
- package/scripts/gds/local-preview.js +2 -15
- package/scripts/gds/migration-namespace.js +3 -3
- package/scripts/gds/module-artifact.js +212 -0
- package/scripts/gds/module.js +95 -6
- package/scripts/gds/package-core.js +5 -116
- package/scripts/gds/plain-cards.js +1 -1
- package/scripts/gds/provision-config.js +2 -2
- package/scripts/gds/provision-net.js +2 -3
- package/scripts/gds/provision.js +9 -9
- package/scripts/gds/publish-manifest.js +1 -2
- package/scripts/gds/push-path-brief.js +10 -10
- package/scripts/gds/regen-instance-docs.js +1 -1
- package/scripts/gds/rename-history-check.js +1 -1
- package/scripts/gds/run-unit-tests.js +2 -16
- package/scripts/gds/sandbox-stage.js +39 -341
- package/scripts/gds/session-digest-build.js +2 -2
- package/scripts/gds/ship-deploy-target.js +13 -13
- package/scripts/gds/ship-flow.js +7 -7
- package/scripts/gds/ship-land.js +9 -10
- package/scripts/gds/ship-merge.js +4 -4
- package/scripts/gds/ship-preflight-steps.js +4 -4
- package/scripts/gds/ship-regen.js +15 -49
- package/scripts/gds/ship.js +13 -33
- package/scripts/gds/skill-preflight.js +9 -8
- package/scripts/gds/start.js +0 -15
- package/scripts/hall-preview/README.md +5 -5
- package/scripts/hall-preview/server.js +6 -6
- package/scripts/render-diagrams.sh +1 -1
- package/src/bongos/api-errors.js +1 -1
- package/src/bongos/api-prefix.js +4 -4
- package/src/bongos/auth-github.js +2 -3
- package/src/bongos/auth.js +51 -126
- package/src/bongos/db-kernel.js +2 -3
- package/src/bongos/db.js +5 -28
- package/src/bongos/module-scope-map.js +14 -29
- package/src/bongos/module-store.js +141 -0
- package/src/bongos/platform-visibility-gate.js +1 -1
- package/src/bongos/route-rank-check.js +3 -16
- package/src/bongos/routes/auth.js +0 -116
- package/src/bongos/routes/builders.js +5 -15
- package/src/bongos/routes/instance.js +8 -14
- package/src/bongos/routes/me.js +7 -23
- package/src/bongos/routes/modules.js +83 -0
- package/src/bongos/routes/security.js +3 -3
- package/src/bongos/routes.js +7 -2
- package/src/bongos/serve-internal.js +5 -6
- package/src/bongos-downloads.js +1 -6
- package/src/branding.js +2 -2
- package/src/build-info.js +4 -6
- package/src/instance-config.js +2 -2
- package/src/module-api.js +13 -6
- package/src/module-loader/catalog.js +3 -0
- package/src/module-loader/loader.js +4 -4
- package/src/module-loader/manifest-schema.js +16 -8
- package/src/module-seams.js +2 -3
- package/src/modules.js +42 -14
- package/tests/agents_spend_guard.mjs +1 -1
- package/tests/api_alias_caller_ratchet.mjs +1 -1
- package/tests/api_alias_exceptions.mjs +1 -1
- package/tests/api_client.mjs +1 -16
- package/tests/api_path_404.mjs +1 -1
- package/tests/auth_page_gate.mjs +10 -10
- package/tests/backfill_task_rewards.mjs +1 -1
- package/tests/canonical_profile_url.mjs +1 -3
- package/tests/claim_action.mjs +4 -1
- package/tests/claim_from_task_and_home.mjs +127 -0
- package/tests/{box_ship_permissions.mjs → cli_allowlist_permissions.mjs} +5 -5
- package/tests/cli_sessions.mjs +1 -1
- package/tests/consumer_layout_boot.mjs +2 -0
- package/tests/context_pack.mjs +13 -13
- package/tests/copy_desk_page_docx.mjs +33 -2
- package/tests/copy_inventory.mjs +7 -7
- package/tests/core_upgrade_runner.mjs +6 -6
- package/tests/credit_grant.mjs +2 -2
- package/tests/criterion_suggest.mjs +1 -1
- package/tests/deploy_divergence_line.mjs +11 -11
- package/tests/design_tokens_sync.mjs +3 -2
- package/tests/diagram_facts_offset.mjs +1 -1
- package/tests/do_api.mjs +106 -0
- package/tests/feedback_latest.mjs +122 -0
- package/tests/fitness.mjs +44 -42
- package/tests/fitness_ratchets.mjs +2 -2
- package/tests/gate_approvals.mjs +0 -1
- package/tests/github_push_land_proof.mjs +1 -1
- package/tests/go_live.mjs +3 -3
- package/tests/goal_advisory.mjs +3 -3
- package/tests/goal_suggest.mjs +3 -3
- package/tests/government_abuse_matrix.mjs +21 -19
- package/tests/government_ownership_scope.mjs +33 -35
- package/tests/government_parity.mjs +2 -2
- package/tests/government_protected_surfaces.mjs +56 -37
- package/tests/government_require_permission.mjs +3 -3
- package/tests/government_seed.mjs +37 -4
- package/tests/government_session_scope.mjs +92 -366
- package/tests/hall_audit.mjs +1 -1
- package/tests/hall_error_envelope.mjs +4 -0
- package/tests/hall_landing_boot.mjs +4 -1
- package/tests/hall_palette.mjs +1 -16
- package/tests/hall_record_world.mjs +6 -0
- package/tests/hall_tweak_editor.mjs +52 -1
- package/tests/helpers.mjs +1 -1
- package/tests/host_topology_skips.mjs +3 -4
- package/tests/html_comment_nesting.mjs +137 -0
- package/tests/infra_exec_paths.mjs +9 -10
- package/tests/init.mjs +4 -5
- package/tests/instance_manifest.mjs +18 -25
- package/tests/kickoff_checklist.mjs +1 -1
- package/tests/lib_sh_resolution.mjs +0 -221
- package/tests/local_preview.mjs +0 -12
- package/tests/migration_namespace.mjs +6 -6
- package/tests/module-scope-map.mjs +9 -9
- package/tests/module_api.mjs +2 -2
- package/tests/module_catalog.mjs +1 -1
- package/tests/module_cli.mjs +20 -20
- package/tests/module_contributions.mjs +6 -6
- package/tests/module_loader.mjs +6 -6
- package/tests/module_manifest.mjs +26 -17
- package/tests/module_route_rank.mjs +2 -2
- package/tests/module_store_publish.mjs +307 -0
- package/tests/module_store_publish_route.mjs +121 -0
- package/tests/module_store_registry_migration.mjs +70 -0
- package/tests/modules.mjs +56 -26
- package/tests/modules_route_wiring.mjs +1 -1
- package/tests/onboarding_route_signals.mjs +21 -31
- package/tests/onboarding_state.mjs +35 -49
- package/tests/paste_token_session_store.mjs +1 -1
- package/tests/permission_path.mjs +0 -3
- package/tests/platform_boot.mjs +1 -2
- package/tests/platform_visibility_gate.mjs +1 -1
- package/tests/profile_route.mjs +1 -6
- package/tests/project_modules_ui.mjs +17 -17
- package/tests/projects_hub.mjs +5 -5
- package/tests/projects_hub_app_step.mjs +138 -0
- package/tests/projects_hub_module_picker.mjs +77 -76
- package/tests/projects_hub_pre_uat.mjs +4 -5
- package/tests/provision.mjs +7 -7
- package/tests/provisioning_capacity.mjs +1 -1
- package/tests/provisioning_recommendations.mjs +2 -1
- package/tests/provisioning_starter_bundles.mjs +0 -22
- package/tests/publish_branch_route.mjs +11 -19
- package/tests/publish_manifest.mjs +2 -3
- package/tests/publish_reconciler.mjs +8 -8
- package/tests/push_path_brief.mjs +10 -9
- package/tests/rank_tier_single_source.mjs +1 -13
- package/tests/regrade_eligibility.mjs +1 -1
- package/tests/rename_history_restraint.mjs +1 -1
- package/tests/repo_map.mjs +12 -13
- package/tests/runner_drift.mjs +3 -21
- package/tests/sandbox_stage.mjs +57 -390
- package/tests/seam_wiring_guard.mjs +11 -11
- package/tests/search_isolation.mjs +3 -3
- package/tests/session_records.mjs +1 -1
- package/tests/session_rename_fallback.mjs +1 -22
- package/tests/session_start_freshness.mjs +3 -3
- package/tests/session_token_hash_db.mjs +1 -1
- package/tests/ship_card.mjs +1 -1
- package/tests/ship_ci_deploy.mjs +17 -17
- package/tests/ship_error_shape.mjs +1 -1
- package/tests/ship_premerge.mjs +1 -1
- package/tests/ship_resume.mjs +1 -1
- package/tests/skill_preflight.mjs +2 -2
- package/tests/skip_is_not_pass.mjs +1 -1
- package/tests/task_visual_slots.mjs +31 -0
- package/tests/terms_acceptance.mjs +2 -2
- package/tests/upgrade.mjs +4 -4
- package/tests/watch_sealed_floor.mjs +0 -1
- package/tests/wizard_draft_resume.mjs +2 -2
- package/tests/wizard_intent_resume.mjs +21 -10
- package/tests/wizard_preselect_why.mjs +18 -18
- package/docs/onboarding/browser-terminal-guide.md +0 -73
- package/docs/recipes/managed-settings-remote-control.md +0 -416
- package/modules/dev-box/CLAUDE.md +0 -15
- package/modules/dev-box/box-access.js +0 -623
- package/modules/dev-box/box-credential.js +0 -94
- package/modules/dev-box/box-onboard.js +0 -486
- package/modules/dev-box/boxes.js +0 -1038
- package/modules/dev-box/db.js +0 -39
- package/modules/dev-box/module.json +0 -19
- package/modules/dev-box/routes/box.js +0 -1223
- package/scripts/gds/box-auth-check.js +0 -160
- package/scripts/gds/box-infra.js +0 -321
- package/scripts/gds/box-sync.js +0 -216
- package/scripts/gds/box.js +0 -1332
- package/scripts/gds/cf-tunnel.js +0 -558
- package/scripts/gds/smoke-box.sh +0 -150
- package/scripts/gds/tree-preflight.js +0 -241
- package/src/bongos/app-pair.js +0 -172
- package/tests/app_pair.mjs +0 -168
- package/tests/box_access.mjs +0 -578
- package/tests/box_auth_check.mjs +0 -114
- package/tests/box_code_staleness.mjs +0 -129
- package/tests/box_connect_e2e.mjs +0 -196
- package/tests/box_cost.mjs +0 -70
- package/tests/box_credential.mjs +0 -125
- package/tests/box_dns_repoint.mjs +0 -153
- package/tests/box_env.mjs +0 -64
- package/tests/box_host_keys.mjs +0 -126
- package/tests/box_infra_resolve.mjs +0 -246
- package/tests/box_onboard.mjs +0 -317
- package/tests/box_scope_session.mjs +0 -119
- package/tests/box_sweep_docs.mjs +0 -119
- package/tests/box_sync.mjs +0 -55
- package/tests/box_sync_scope_report.mjs +0 -126
- package/tests/box_task_scope.mjs +0 -169
- package/tests/box_task_scope_pause.mjs +0 -66
- package/tests/box_terminal_ssrf.mjs +0 -96
- package/tests/boxes.mjs +0 -1585
- package/tests/cf_ruleset_preflight.mjs +0 -142
- package/tests/cf_tunnel.mjs +0 -496
- package/tests/docs_scrubber_damage.mjs +0 -111
- package/tests/module_scope_active_claims.mjs +0 -94
- package/tests/ship_api_client_pathspec.mjs +0 -100
- package/tests/tree_preflight.mjs +0 -243
package/scripts/gds/do-api.js
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
// scripts/gds/do-api.js — a thin DigitalOcean API v2 client for the control plane.
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// (
|
|
6
|
-
//
|
|
7
|
-
// tier alongside its callers — and a module may not import a sibling module, so two
|
|
8
|
-
// CLIs can only share it from here. Originally #598 (box lifecycle).
|
|
3
|
+
// Control-plane client (ADR 0111 §2, task #1943): the DO client the
|
|
4
|
+
// instance-provisioning runner (scripts/gds/provision.js) and its teardown
|
|
5
|
+
// (provision-teardown.js) use. It is operator-CLI code (never the web tier), so it
|
|
6
|
+
// lives in the control-plane tier alongside its callers. Originally #598.
|
|
9
7
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// box.js orchestration) stays testable without a live account.
|
|
8
|
+
// Everything that touches DO goes through here so the provisioning code stays
|
|
9
|
+
// testable without a live account.
|
|
13
10
|
//
|
|
14
11
|
// Design:
|
|
15
12
|
// - Injectable transport (`fetchImpl`) so tests pass a fake fetch and assert
|
|
@@ -23,10 +20,8 @@
|
|
|
23
20
|
// - Live mode requires a token (DO_API_TOKEN); the constructor throws clearly
|
|
24
21
|
// if asked to run live without one — it never silently no-ops a real call.
|
|
25
22
|
//
|
|
26
|
-
// We implement only the calls
|
|
27
|
-
// createDroplet · getDroplet · deleteDroplet ·
|
|
28
|
-
// snapshotDroplet (action) · getAction (poll) · deleteSnapshot
|
|
29
|
-
// createDropletFromSnapshot
|
|
23
|
+
// We implement only the calls provisioning needs:
|
|
24
|
+
// createDroplet · getDroplet · deleteDroplet · settleDroplet (poll to active)
|
|
30
25
|
|
|
31
26
|
const { JsonHttpApi, makeApiError } = require('./http-api-client');
|
|
32
27
|
|
|
@@ -54,7 +49,7 @@ class DoApi extends JsonHttpApi {
|
|
|
54
49
|
|
|
55
50
|
// ---- droplets --------------------------------------------------------------
|
|
56
51
|
|
|
57
|
-
// image:
|
|
52
|
+
// image: a base image slug (e.g. 'ubuntu-24-04-x64').
|
|
58
53
|
async createDroplet({ name, region, size, image, userData, tags, sshKeys }) {
|
|
59
54
|
const body = {
|
|
60
55
|
name, region, size, image,
|
|
@@ -79,53 +74,9 @@ class DoApi extends JsonHttpApi {
|
|
|
79
74
|
return this._req('DELETE', `/droplets/${dropletId}`, null, { dryRunResult: { deleted: true } });
|
|
80
75
|
}
|
|
81
76
|
|
|
82
|
-
//
|
|
83
|
-
//
|
|
84
|
-
// the
|
|
85
|
-
// box row whose droplet vanished). per_page=200 covers the whole fleet in one
|
|
86
|
-
// page at our scale (<100 boxes); a future larger fleet would follow
|
|
87
|
-
// links.pages.next. Returns { droplets: [...] }; dry-run returns an empty list
|
|
88
|
-
// (the reconcile builds its OWN live read client so planning still sees drift).
|
|
89
|
-
async listDroplets({ tag } = {}) {
|
|
90
|
-
const qs = tag ? `?tag_name=${encodeURIComponent(tag)}&per_page=200` : '?per_page=200';
|
|
91
|
-
return this._req('GET', `/droplets${qs}`, null, { dryRunResult: { droplets: [] } });
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// ---- snapshots (via droplet actions) --------------------------------------
|
|
95
|
-
|
|
96
|
-
// Snapshot a running droplet. DO recommends powering off first for a clean
|
|
97
|
-
// snapshot; the orchestrator powers off then calls this. Returns an ACTION
|
|
98
|
-
// whose completion must be polled (getAction) before the droplet is destroyed.
|
|
99
|
-
async snapshotDroplet(dropletId, snapshotName) {
|
|
100
|
-
return this._req('POST', `/droplets/${dropletId}/actions`, { type: 'snapshot', name: snapshotName }, {
|
|
101
|
-
dryRunResult: { action: { id: `dryrun-action-${dropletId}`, status: 'completed', type: 'snapshot' } },
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
async powerOffDroplet(dropletId) {
|
|
106
|
-
return this._req('POST', `/droplets/${dropletId}/actions`, { type: 'power_off' }, {
|
|
107
|
-
dryRunResult: { action: { id: `dryrun-poweroff-${dropletId}`, status: 'completed' } },
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async getAction(actionId) {
|
|
112
|
-
return this._req('GET', `/actions/${actionId}`, null, {
|
|
113
|
-
dryRunResult: { action: { id: actionId, status: 'completed' } },
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async deleteSnapshot(snapshotId) {
|
|
118
|
-
return this._req('DELETE', `/snapshots/${snapshotId}`, null, { dryRunResult: { deleted: true } });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async createDropletFromSnapshot({ name, region, size, snapshotId, userData, tags, sshKeys }) {
|
|
122
|
-
return this.createDroplet({ name, region, size, image: snapshotId, userData, tags, sshKeys });
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Shared poll primitive behind waitForAction + settleDroplet (the box
|
|
126
|
-
// subsystem's two DO poll loops, unified per the #1092 cleanup). Calls step(i)
|
|
127
|
-
// each round; step returns { done:true, value } to stop, or a falsy value to keep
|
|
128
|
-
// polling. Returns the stopping value, or { timedOut:true } after `tries` rounds.
|
|
77
|
+
// Poll primitive behind settleDroplet (#1092). Calls step(i) each round; step
|
|
78
|
+
// returns { done:true, value } to stop, or a falsy value to keep polling.
|
|
79
|
+
// Returns the stopping value, or { timedOut:true } after `tries` rounds.
|
|
129
80
|
// `sleepFn` lets tests drive the loop with no real clock.
|
|
130
81
|
async _poll({ tries = 60, sleepMs = 5000, sleepFn } = {}, step) {
|
|
131
82
|
const sleep = sleepFn || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
@@ -137,26 +88,8 @@ class DoApi extends JsonHttpApi {
|
|
|
137
88
|
return { timedOut: true };
|
|
138
89
|
}
|
|
139
90
|
|
|
140
|
-
// Poll an action to completion. Returns true if 'completed', throws on 'errored'.
|
|
141
|
-
// sleepFn lets tests drive the loop deterministically (no real sleeping).
|
|
142
|
-
async waitForAction(actionId, { tries = 60, sleepMs = 5000, sleepFn } = {}) {
|
|
143
|
-
if (this.dryRun) return true;
|
|
144
|
-
const out = await this._poll({ tries, sleepMs, sleepFn }, async () => {
|
|
145
|
-
const { action } = await this.getAction(actionId);
|
|
146
|
-
if (action.status === 'completed') return { done: true, value: true };
|
|
147
|
-
if (action.status === 'errored') {
|
|
148
|
-
throw new DoApiError(`DO action ${actionId} errored`, { body: action });
|
|
149
|
-
}
|
|
150
|
-
return null;
|
|
151
|
-
});
|
|
152
|
-
if (out && out.timedOut) throw new DoApiError(`DO action ${actionId} did not complete in time`);
|
|
153
|
-
return out;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
91
|
// Wait until a freshly-created droplet is 'active' and return its public IPv4.
|
|
157
|
-
//
|
|
158
|
-
// share one primitive; scripts/gds/box.js calls it as a method. dry-run returns
|
|
159
|
-
// no IP (it never created a real droplet).
|
|
92
|
+
// dry-run returns no IP (it never created a real droplet).
|
|
160
93
|
async settleDroplet(dropletId, { tries = 60, sleepMs = 5000, sleepFn } = {}) {
|
|
161
94
|
if (this.dryRun) return { ip: null };
|
|
162
95
|
const out = await this._poll({ tries, sleepMs, sleepFn }, async () => {
|
|
@@ -41,6 +41,8 @@ const DOC_CLI_ALLOW = new Map([
|
|
|
41
41
|
'art-stage.js ships with the art-pipeline HOST module, which publish-manifest.js excludes from core — absent from a core-only checkout by design; the skill says so inline.'],
|
|
42
42
|
['.claude/skills/otb-tile-generate/SKILL.md::scripts/gds/art-stage.js',
|
|
43
43
|
'same as paint/SKILL.md — the art staging CLI travels with the excluded art-pipeline module, not the portable core.'],
|
|
44
|
+
['docs/session-logs/2026-09-10-task-1003507-the-box-that-could-not-be-reaped.md::scripts/gds/box.js',
|
|
45
|
+
'a dated session log — the historical record, never rewritten — of work on a CLI the dev-box removal deleted (task 1003893); it describes what was run then, not what to run now.'],
|
|
44
46
|
]);
|
|
45
47
|
|
|
46
48
|
// `node scripts/gds/foo.js` … up to the end of the line/segment, so the flags that
|
package/scripts/gds/doctor.js
CHANGED
|
@@ -167,7 +167,7 @@ function checkToolchain() {
|
|
|
167
167
|
// Check 3 — Git plumbing
|
|
168
168
|
// --------------------------------------------------------------------------
|
|
169
169
|
// Pure verdict for the hooks gate, extracted so the truth table is testable
|
|
170
|
-
// (tests/doctor_hooks.mjs)
|
|
170
|
+
// (tests/doctor_hooks.mjs).
|
|
171
171
|
//
|
|
172
172
|
// `.husky/` is INSTANCE topology, not shipped in the portable core (README
|
|
173
173
|
// "Secrets scanning"). So its absence is not a builder error: it means ADR 0043
|
|
@@ -371,7 +371,7 @@ function checkDeployTooling() {
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// ADR 0055 / task 1025: in ci mode there are two publish paths. A machine with
|
|
374
|
-
// no GitHub push credential
|
|
374
|
+
// no GitHub push credential ships SERVER-MEDIATED — it uploads its
|
|
375
375
|
// branch to the GDS, which pushes + opens the PR with the server's credential —
|
|
376
376
|
// so gh is NOT needed here. Resolve the same path ship.js will take and skip the
|
|
377
377
|
// gh requirement when it's server-mediated.
|
|
@@ -382,7 +382,7 @@ function checkDeployTooling() {
|
|
|
382
382
|
/* keep 'local'; the gh checks below are the safe, stricter default */
|
|
383
383
|
}
|
|
384
384
|
if (via === 'server') {
|
|
385
|
-
ok('deploy', `Deploy mode: ci (server-mediated publish) — this machine has no GitHub push credential, so the GDS pushes your branch + opens the PR for you. gh is not required here
|
|
385
|
+
ok('deploy', `Deploy mode: ci (server-mediated publish) — this machine has no GitHub push credential, so the GDS pushes your branch + opens the PR for you. gh is not required here.`);
|
|
386
386
|
return;
|
|
387
387
|
}
|
|
388
388
|
|
|
@@ -425,7 +425,7 @@ function checkDeployTooling() {
|
|
|
425
425
|
|
|
426
426
|
// Intent-runner drift (task 1002722): a runner on a stale core once turned a domain-attach
|
|
427
427
|
// into a teardown (task 1002716). Only a host with *intent-runner* units shows this section,
|
|
428
|
-
// so a builder laptop
|
|
428
|
+
// so a builder laptop sees nothing.
|
|
429
429
|
function runnerDriftRows(results) {
|
|
430
430
|
return (results || []).map((r) => {
|
|
431
431
|
const msg = `${r.name}: ${r.reason}`;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// /feedback Claude Code slash command.
|
|
4
4
|
//
|
|
5
5
|
// Resolves the LATEST feedback bundle the Bongos app wrote to
|
|
6
|
-
// ~/CloudBongos/feedback/ (via the shared `latest` pointer
|
|
7
|
-
//
|
|
6
|
+
// ~/CloudBongos/feedback/ (via the shared `latest` pointer, ADR 0087) and
|
|
7
|
+
// prints it for a Claude Code session:
|
|
8
8
|
//
|
|
9
9
|
// 1. the full prompt.md body (the model-agnostic transcript + ask, R11), and
|
|
10
10
|
// 2. the ABSOLUTE path of every attached frame, on its own line, so Claude's
|
|
@@ -20,13 +20,139 @@
|
|
|
20
20
|
//
|
|
21
21
|
// Local + read-only: it reads files under your home dir. No GDS session, no
|
|
22
22
|
// network — so it works on any machine the Bongos app runs on.
|
|
23
|
+
//
|
|
24
|
+
// resolveLatest + feedbackBaseDir are ported here from the app's
|
|
25
|
+
// bongos-app/src/handoff.js, which no longer lives in this repo (the desktop
|
|
26
|
+
// apps publish off-repo, ADR 0098 — task 1004356). The contract is ADR 0087's:
|
|
27
|
+
// `latest.json` is authoritative, the `latest/` symlink is a convenience that
|
|
28
|
+
// may be absent, and with neither the reader self-heals by scanning for the
|
|
29
|
+
// newest bundle dir holding a prompt.md.
|
|
23
30
|
|
|
24
31
|
'use strict';
|
|
25
32
|
|
|
33
|
+
const fs = require('node:fs/promises');
|
|
34
|
+
const os = require('node:os');
|
|
26
35
|
const path = require('node:path');
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
|
|
37
|
+
const IMAGE_EXT = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);
|
|
38
|
+
|
|
39
|
+
/** Where the Bongos app writes bundles: ~/CloudBongos/feedback. */
|
|
40
|
+
function feedbackBaseDir() {
|
|
41
|
+
return path.join(os.homedir(), 'CloudBongos', 'feedback');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function isBundleDir(dir) {
|
|
45
|
+
try {
|
|
46
|
+
return (await fs.stat(path.join(dir, 'prompt.md'))).isFile();
|
|
47
|
+
} catch {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The dir `latest.json` names, or null if it is missing, unreadable or stale.
|
|
53
|
+
// Accepts the pointer by `dir` (absolute) or by `id` (a child of baseDir).
|
|
54
|
+
async function fromPointer(baseDir) {
|
|
55
|
+
let ptr;
|
|
56
|
+
try {
|
|
57
|
+
ptr = JSON.parse(await fs.readFile(path.join(baseDir, 'latest.json'), 'utf8'));
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
if (!ptr || typeof ptr !== 'object') return null;
|
|
62
|
+
const candidates = [];
|
|
63
|
+
if (typeof ptr.dir === 'string' && ptr.dir) candidates.push(path.resolve(baseDir, ptr.dir));
|
|
64
|
+
if (typeof ptr.id === 'string' && ptr.id && !/[\\/]/.test(ptr.id)) candidates.push(path.join(baseDir, ptr.id));
|
|
65
|
+
for (const dir of candidates) if (await isBundleDir(dir)) return dir;
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function fromSymlink(baseDir) {
|
|
70
|
+
const link = path.join(baseDir, 'latest');
|
|
71
|
+
if (!(await isBundleDir(link))) return null;
|
|
72
|
+
try {
|
|
73
|
+
return await fs.realpath(link);
|
|
74
|
+
} catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Self-heal: the newest child dir (by prompt.md mtime, then name) with a prompt.md.
|
|
80
|
+
async function fromScan(baseDir) {
|
|
81
|
+
let entries;
|
|
82
|
+
try {
|
|
83
|
+
entries = await fs.readdir(baseDir, { withFileTypes: true });
|
|
84
|
+
} catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
let best = null;
|
|
88
|
+
for (const e of entries) {
|
|
89
|
+
if (!e.isDirectory() || e.name === 'latest') continue;
|
|
90
|
+
const dir = path.join(baseDir, e.name);
|
|
91
|
+
let st;
|
|
92
|
+
try {
|
|
93
|
+
st = await fs.stat(path.join(dir, 'prompt.md'));
|
|
94
|
+
} catch {
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!st.isFile()) continue;
|
|
98
|
+
if (!best || st.mtimeMs > best.mtimeMs || (st.mtimeMs === best.mtimeMs && e.name > best.name)) {
|
|
99
|
+
best = { dir, name: e.name, mtimeMs: st.mtimeMs };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return best && best.dir;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// The bundle's frames as absolute paths: those prompt.md references (in the
|
|
106
|
+
// order the walkthrough cites them) that exist, then any other image in the dir.
|
|
107
|
+
async function collectImages(dir, promptMarkdown) {
|
|
108
|
+
const out = [];
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
const add = (p) => {
|
|
111
|
+
const key = process.platform === 'win32' ? p.toLowerCase() : p;
|
|
112
|
+
if (!seen.has(key)) { seen.add(key); out.push(p); }
|
|
113
|
+
};
|
|
114
|
+
for (const m of promptMarkdown.matchAll(/!\[[^\]]*\]\(\s*<?([^)>]+?)>?\s*\)/g)) {
|
|
115
|
+
const p = path.resolve(dir, m[1]);
|
|
116
|
+
if (!IMAGE_EXT.has(path.extname(p).toLowerCase())) continue;
|
|
117
|
+
try {
|
|
118
|
+
if ((await fs.stat(p)).isFile()) add(p);
|
|
119
|
+
} catch { /* referenced but absent — skip */ }
|
|
120
|
+
}
|
|
121
|
+
const walk = async (d, depth) => {
|
|
122
|
+
let entries;
|
|
123
|
+
try {
|
|
124
|
+
entries = await fs.readdir(d, { withFileTypes: true });
|
|
125
|
+
} catch {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
entries.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
129
|
+
for (const e of entries) {
|
|
130
|
+
const p = path.join(d, e.name);
|
|
131
|
+
if (e.isDirectory() && depth > 0) await walk(p, depth - 1);
|
|
132
|
+
else if (e.isFile() && IMAGE_EXT.has(path.extname(e.name).toLowerCase())) add(p);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
await walk(dir, 1);
|
|
136
|
+
return out;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* The newest feedback bundle under baseDir, or null if there is none.
|
|
141
|
+
* @returns {Promise<null|{id:string,dir:string,promptPath:string,promptMarkdown:string,imagePaths:string[]}>}
|
|
142
|
+
*/
|
|
143
|
+
async function resolveLatest(baseDir = feedbackBaseDir()) {
|
|
144
|
+
const dir = (await fromPointer(baseDir)) || (await fromSymlink(baseDir)) || (await fromScan(baseDir));
|
|
145
|
+
if (!dir) return null;
|
|
146
|
+
const promptPath = path.join(dir, 'prompt.md');
|
|
147
|
+
const promptMarkdown = await fs.readFile(promptPath, 'utf8');
|
|
148
|
+
return {
|
|
149
|
+
id: path.basename(dir),
|
|
150
|
+
dir,
|
|
151
|
+
promptPath,
|
|
152
|
+
promptMarkdown,
|
|
153
|
+
imagePaths: await collectImages(dir, promptMarkdown),
|
|
154
|
+
};
|
|
155
|
+
}
|
|
30
156
|
|
|
31
157
|
function parseArgs(argv) {
|
|
32
158
|
const out = { json: false, base: undefined };
|
|
@@ -88,7 +214,11 @@ async function main() {
|
|
|
88
214
|
process.stdout.write(`${lines.join('\n')}\n`);
|
|
89
215
|
}
|
|
90
216
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
217
|
+
module.exports = { resolveLatest, feedbackBaseDir };
|
|
218
|
+
|
|
219
|
+
if (require.main === module) {
|
|
220
|
+
main().catch((err) => {
|
|
221
|
+
process.stderr.write(`feedback-latest: ${err.message}\n`);
|
|
222
|
+
process.exitCode = 1;
|
|
223
|
+
});
|
|
224
|
+
}
|
|
@@ -71,10 +71,6 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
|
|
|
71
71
|
// one, and any file NOT listed must have zero hits. Getting better never fails.
|
|
72
72
|
//
|
|
73
73
|
// Why each is legitimate:
|
|
74
|
-
// * box.js — REAL Cloudflare/DNS zone names for the operator plane
|
|
75
|
-
// (`dev.<host>`, the terminal zone). These are infrastructure identifiers,
|
|
76
|
-
// not an instance URL; routing them through branding would be wrong. (Its
|
|
77
|
-
// two genuine API-base defaults WERE fixed in 1002428.)
|
|
78
74
|
// * publish-manifest.js — the literal FILENAMES of three real committed docs
|
|
79
75
|
// (the two decouple recipes, plus the instance's own name/trademark ADR that
|
|
80
76
|
// task 1003869 excluded: its prose redacts cleanly but the FILENAME rides in
|
|
@@ -91,8 +87,8 @@ const IDENTITY_SCAN_EXTRA_FILES = [];
|
|
|
91
87
|
// 1002567). Each is a HOST-PINNED OPS script, not portable code: they name the
|
|
92
88
|
// live box's actual Postgres database/role and its actual /etc secrets dir, and
|
|
93
89
|
// every one of those literals is already only the DEFAULT behind an env override
|
|
94
|
-
// (GDS_BACKUP_DB, PROD_DB/STAGING_DB, PG_SOCKET).
|
|
95
|
-
//
|
|
90
|
+
// (GDS_BACKUP_DB, PROD_DB/STAGING_DB, PG_SOCKET). Each is an infrastructure
|
|
91
|
+
// identifier, not an instance URL, and the systemd
|
|
96
92
|
// units on that box invoke these by path. Re-pointing an instance's backups is
|
|
97
93
|
// deployment work, not a branding read. Their COUNT is still frozen here, so a
|
|
98
94
|
// NEW literal in any of them fails exactly like one in an unlisted file.
|
|
@@ -116,7 +112,6 @@ const IDENTITY_SCAN_ALLOWED = {
|
|
|
116
112
|
// instance it declines to run instead of destroying the wrong database.
|
|
117
113
|
'scripts/gds/seed-staging.sh': 4,
|
|
118
114
|
'scripts/gds/backfill-sessions.js': 2,
|
|
119
|
-
'scripts/gds/box.js': 3,
|
|
120
115
|
'scripts/gds/publish-manifest.js': 3,
|
|
121
116
|
'scripts/gds/seed-bongos-coreB-tranche1-tasks.js': 1,
|
|
122
117
|
'scripts/gds/seed-bongos-coreB-tranche2-tasks.js': 1,
|
|
@@ -24,7 +24,7 @@ const { CORE_ROOT, rel, walkJs, walkJsAllRoots } = require('./fitness-lib.js');
|
|
|
24
24
|
// ============================================================================
|
|
25
25
|
//
|
|
26
26
|
// A vanilla self-host boot ships only the PUBLISHABLE tree (publish-manifest.js). An
|
|
27
|
-
// instance-only config (config/
|
|
27
|
+
// instance-only config (config/bongos-cli.json, config/branding.json,
|
|
28
28
|
// config/modules.json, …) is boundary-EXCLUDED, so a bare `require('../../config/<excluded>.json')`
|
|
29
29
|
// in core code throws MODULE_NOT_FOUND and CRASHES the boot on a fresh instance — the exact
|
|
30
30
|
// class that shipped in R88 (two hard-requires in src/bongos/serve-internal.js; task 1824). The
|
|
@@ -81,9 +81,9 @@ function checkCoreRequiresExcludedConfig() {
|
|
|
81
81
|
// require()s a boundary-EXCLUDED module (modules/game, modules/art-pipeline,
|
|
82
82
|
// modules/character-anim, …) throws MODULE_NOT_FOUND once that module lives in the
|
|
83
83
|
// consumer's instance repo rather than the core package (R89 split). The cure is
|
|
84
|
-
// either to reclassify the module INTO core (if it belongs there — e.g. provisioning
|
|
85
|
-
//
|
|
86
|
-
//
|
|
84
|
+
// either to reclassify the module INTO core (if it belongs there — e.g. provisioning)
|
|
85
|
+
// or to exclude the requiring file with the feature it serves (art tooling).
|
|
86
|
+
// Catches BOTH require forms: a direct literal
|
|
87
87
|
// require('../modules/<key>/…') AND a require(path.join(<root>, 'modules/<key>/…')).
|
|
88
88
|
// Only an EXCLUDED module required from a PUBLISHABLE file is a violation; requiring a
|
|
89
89
|
// publishable (core) module is fine, and the loader's DYNAMIC dispatch
|
|
@@ -46,6 +46,10 @@ const WRITE_VALIDATION_EXEMPT = new Set([
|
|
|
46
46
|
// the route's own express.raw parser and read by docx.readUpload, which refuses
|
|
47
47
|
// anything that is not a bounded, readable Word file (not_a_docx / upload_too_large).
|
|
48
48
|
'POST /copy-desk/pages/:pageId/draft.docx',
|
|
49
|
+
// Not a JSON body either: the raw module tarball (task 1004271, ADR 0338 D1), capped by
|
|
50
|
+
// the route's express.raw parser and read by module-artifact.verifyModuleArtifact, which
|
|
51
|
+
// recomputes every hash and refuses anything that is not a well-formed module version.
|
|
52
|
+
'POST /store/modules/:key/versions',
|
|
49
53
|
]);
|
|
50
54
|
|
|
51
55
|
// KNOWN GAPS, NOT EXEMPTIONS (task 1002551 / BV1.R109b → tracked by task 1002552).
|
|
@@ -284,9 +284,9 @@ function knipIssueCount() {
|
|
|
284
284
|
// baseline map leaves every `floor === undefined`, and compareToBaselines classifies
|
|
285
285
|
// an absent floor as a WARNING — so a checkout with no config/ reported the ratchet
|
|
286
286
|
// as PASSED. Measured: with the baselines gone, a planted 5,000-line and 94-cycle
|
|
287
|
-
// regression produced 0 violations. That
|
|
288
|
-
//
|
|
289
|
-
//
|
|
287
|
+
// regression produced 0 violations. That was not a hypothetical checkout — a sparse
|
|
288
|
+
// checkout without `config/` once left the builders who most needed the gate
|
|
289
|
+
// silently exempt from it, learning from red CI instead.
|
|
290
290
|
//
|
|
291
291
|
// Distinguish the two absences, because they are not the same fact:
|
|
292
292
|
// - the FILE is missing/malformed -> cannot verify anything -> hard fail (here);
|
|
@@ -315,7 +315,7 @@ function readBaselines(baselinePath = BASELINE_PATH) {
|
|
|
315
315
|
metrics: {},
|
|
316
316
|
error: missing
|
|
317
317
|
? `config/fitness-baselines.json is MISSING — the quality ratchet cannot verify anything, so it fails CLOSED rather than reporting a pass it did not check. `
|
|
318
|
-
+ `If this is a
|
|
318
|
+
+ `If this is a sparse or partial checkout, take the whole tree (\`git sparse-checkout disable\`). `
|
|
319
319
|
+ `To bootstrap a fresh baseline deliberately: node scripts/gds/fitness-ratchets.js --update-baselines`
|
|
320
320
|
: `config/fitness-baselines.json is unreadable (${e && e.message}) — failing CLOSED.`,
|
|
321
321
|
};
|
|
@@ -350,7 +350,7 @@ const METRIC_HINTS = {
|
|
|
350
350
|
legacy_api_gds_callers:
|
|
351
351
|
' — a NEW in-repo caller was written against the permanent `/api/gds` alias (task 1004064, '
|
|
352
352
|
+ 'idea 1001030). Ask for `/api/bongos` instead: the alias exists for callers we CANNOT update '
|
|
353
|
-
+ '(shipped
|
|
353
|
+
+ '(shipped binaries, live instances), not for code in this repo. Run '
|
|
354
354
|
+ '`node scripts/gds/api-alias-caller-scan.js --files` to see every counted line. The alias '
|
|
355
355
|
+ 'contract, the pinned OAuth callbacks, the uptime poller + smoke, the rename guardrails, '
|
|
356
356
|
+ 'tests/, docs/, migrations/ and captured fixtures are excluded by construction — and so is '
|
package/scripts/gds/fitness.js
CHANGED
|
@@ -343,11 +343,11 @@ function checkDeterministicRoutines() {
|
|
|
343
343
|
// published doorway); the core NEVER imports a module; a module NEVER imports a
|
|
344
344
|
// sibling module. These two checks enforce it.
|
|
345
345
|
//
|
|
346
|
-
// Both were GREEN through the strangler:
|
|
347
|
-
//
|
|
346
|
+
// Both were GREEN through the strangler: the first two moved modules (M07/M08)
|
|
347
|
+
// imported only the doorway, and the core imported neither. The boundary
|
|
348
348
|
// shipped in WARN mode so a half-moved module mid-migration couldn't red the
|
|
349
349
|
// build; BV1.R45 (task 1414) flips it to ENFORCING now that the first move
|
|
350
|
-
// (
|
|
350
|
+
// (M07) proved clean — the monotonic ratchet (ADR 0062 §9 / design
|
|
351
351
|
// 04§3). A NEW cross-boundary import is now a red build; warn-mode is gone.
|
|
352
352
|
let MODULE_BOUNDARY_ENFORCING = true;
|
|
353
353
|
const MODULE_API_REL = 'src/module-api.js';
|
|
@@ -497,7 +497,7 @@ function checkScopeMapImportsProtected() {
|
|
|
497
497
|
// module-AGNOSTIC: it discovers, validates, and mounts modules uniformly and may
|
|
498
498
|
// never special-case one by name. The import checks (Check 7/8) stop a module's
|
|
499
499
|
// CODE from being reached by `require`; this stops a module's IDENTITY from being
|
|
500
|
-
// hardcoded into the kernel (`if (key === '
|
|
500
|
+
// hardcoded into the kernel (`if (key === 'art-pipeline') …`, a copied module helper,
|
|
501
501
|
// a registry entry that should have moved out). "No module-specific code" is
|
|
502
502
|
// operationalized as the one drift-free signal — the kernel names no discovered
|
|
503
503
|
// module KEY (the canonical id from modules/<key>/module.json). Domain terms are
|
|
@@ -606,14 +606,14 @@ const KERNEL_FILES = [
|
|
|
606
606
|
// Kernel files exempt from the no-module-keys scan (Check 10) because naming a
|
|
607
607
|
// module is, for them, the JOB — they stay in KERNEL_FILES for the import check
|
|
608
608
|
// (Check 12), which guards dependency *direction* (a much stronger guarantee than
|
|
609
|
-
// "never write the string '
|
|
609
|
+
// "never write the string 'art-pipeline'"). Two legitimate reasons a kernel file names
|
|
610
610
|
// a module:
|
|
611
611
|
// • the REGISTRY — module-scope-map.js IS the module→path map (and
|
|
612
612
|
// checkScopeMapImportsProtected already guards how it derives `protected`).
|
|
613
613
|
// • the TRUST BOUNDARY — auth.js carries the Discord OAuth account-LINK flow
|
|
614
614
|
// (core by design — see modules/discord/CLAUDE.md "NOT in this module"); and
|
|
615
615
|
// permission-path-check.js / route-rank-check.js MUST name module path
|
|
616
|
-
// subtrees ('modules/
|
|
616
|
+
// subtrees ('modules/grading/', …) to protect + rank-gate them. Forbidding
|
|
617
617
|
// these the string would force the trust layer to stop protecting module
|
|
618
618
|
// surfaces — exactly backwards. The generic machinery + data + config files
|
|
619
619
|
// (module-api, the loader, db-kernel, secret-box, pool, _helpers, branding,
|
|
@@ -683,8 +683,8 @@ function moduleDir(key) {
|
|
|
683
683
|
return path.join(CORE_ROOT, 'modules', key);
|
|
684
684
|
}
|
|
685
685
|
|
|
686
|
-
// Match a module key as a DELIMITED token so `
|
|
687
|
-
// `mount('
|
|
686
|
+
// Match a module key as a DELIMITED token so `art-pipeline` hits in `'art-pipeline'` /
|
|
687
|
+
// `mount('art-pipeline')` but never inside `art-pipelines` or `xart-pipeline`. Hyphens are
|
|
688
688
|
// non-word chars, so the boundary guards against word-chars AND hyphens on both
|
|
689
689
|
// sides (no lookbehind — portable on every node the unit gate runs).
|
|
690
690
|
function keyTokenRe(key) {
|
|
@@ -816,7 +816,7 @@ function checkKernelImportsNoDomain() {
|
|
|
816
816
|
// migrations," never "drop its tables" (ADR 0083 §Module-owned migrations);
|
|
817
817
|
// there is no down path, so a DROP/TRUNCATE can only be an accident.
|
|
818
818
|
// • NAMESPACED — every object it CREATEs lives under the <key>_ prefix
|
|
819
|
-
// (
|
|
819
|
+
// (art-pipeline → art_pipeline_), and it may only ALTER its own namespaced tables;
|
|
820
820
|
// core/sibling state is reached through the doorway/seams, never raw DDL.
|
|
821
821
|
//
|
|
822
822
|
// LEGACY_NAMESPACED: tables that predate the <key>_-prefix convention (created before the module
|
|
@@ -848,7 +848,7 @@ const CREATE_OBJ_RE = /\bcreate\s+(?:or\s+replace\s+)?(?:unique\s+)?(?:materiali
|
|
|
848
848
|
const CREATE_INDEX_RE = /\bcreate\s+(?:unique\s+)?index\s+(?:concurrently\s+)?(?:if\s+not\s+exists\s+)?(?:"?[\w.]+"?)\s+on\s+(?:only\s+)?("?[\w.]+"?)/gi;
|
|
849
849
|
const ALTER_TABLE_RE = /\balter\s+table\s+(?:if\s+exists\s+)?(?:only\s+)?("?[\w.]+"?)/gi;
|
|
850
850
|
|
|
851
|
-
// SQL object-namespace prefix for a module key:
|
|
851
|
+
// SQL object-namespace prefix for a module key: art-pipeline → art_pipeline (SQL-safe).
|
|
852
852
|
function moduleNamespace(key) { return key.replace(/-/g, '_'); }
|
|
853
853
|
|
|
854
854
|
// strip SQL comments so a commented-out statement is never scanned
|
|
@@ -208,8 +208,8 @@ function stripEsmExports(esm) {
|
|
|
208
208
|
.replace(/^export default createClient;\n?/m, '');
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
// index.cjs — for Node consumers that use require() (e.g.
|
|
212
|
-
//
|
|
211
|
+
// index.cjs — for Node consumers that use require() (e.g. a zero-dep CJS subproject
|
|
212
|
+
// that vendors this file at package time).
|
|
213
213
|
function renderClientCjs(esm) {
|
|
214
214
|
const header =
|
|
215
215
|
'// @bongos/client (CommonJS build) — GENERATED from index.mjs by\n' +
|
|
@@ -394,8 +394,8 @@ function renderPackageJson(spec) {
|
|
|
394
394
|
// updated while the API it is generated FROM moved 500+ core versions. It had zero consumers
|
|
395
395
|
// the whole time: nothing across the core or any instance repo ever declared it as a
|
|
396
396
|
// dependency — every consumer VENDORS a copy instead. That was two places when this was
|
|
397
|
-
// written;
|
|
398
|
-
// @cloudbongos/cli, so there is now no vendoring consumer left at all. The
|
|
397
|
+
// written; both have since been deleted or retired (ADR 0324 retired
|
|
398
|
+
// @cloudbongos/cli), so there is now no vendoring consumer left at all. The
|
|
399
399
|
// owner removed it from the registry on 2026-09-09.
|
|
400
400
|
//
|
|
401
401
|
// The manifest itself stays: `main`/`types`/`exports` are what make the vendored copy and a
|
|
@@ -837,7 +837,7 @@ function buildOpenapi(model) {
|
|
|
837
837
|
license: { name: 'AGPL-3.0-or-later', url: 'https://www.gnu.org/licenses/agpl-3.0.html' },
|
|
838
838
|
description: [
|
|
839
839
|
`The Bongos API — the build-methodology backend`,
|
|
840
|
-
`(builders, tasks, claims, credits, grading, memory
|
|
840
|
+
`(builders, tasks, claims, credits, grading, memory).`,
|
|
841
841
|
``,
|
|
842
842
|
`**API version:** \`${API_VERSION}\` — served at \`${VERSIONED_API_PREFIX}\`. The`,
|
|
843
843
|
`unversioned \`${API_PREFIX}\` and legacy \`/api/gds\` are permanent aliases`,
|