@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
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
// GDS server-mediated branch publish (ADR 0055 / task 1025).
|
|
2
2
|
//
|
|
3
3
|
// WHY THIS EXISTS
|
|
4
|
-
// A
|
|
5
|
-
//
|
|
6
|
-
// GH_TOKEN — infra/builder-box-cloud-init.yaml scrubs the clone token), and its
|
|
7
|
-
// baked GDS session is box-scoped (deny-by-default, #919/ADR 0053). CI self-
|
|
4
|
+
// A checkout with NO GitHub push credential by design (credential-less HTTPS
|
|
5
|
+
// origin, no gh, no GH_TOKEN) cannot publish in `ci` deploy mode. CI self-
|
|
8
6
|
// deploy (#859/ADR 0042) fixed only the LAST leg (Actions deploys main->prod);
|
|
9
|
-
//
|
|
7
|
+
// such a checkout still can't get its branch onto GitHub to open the PR.
|
|
10
8
|
//
|
|
11
|
-
// THE FIX (owner-chosen: "the
|
|
12
|
-
// a THIN git bundle to the GDS over a RE-AUTHED
|
|
9
|
+
// THE FIX (owner-chosen: "the checkout holds nothing"). The checkout uploads its
|
|
10
|
+
// branch as a THIN git bundle to the GDS over a RE-AUTHED cli session; the server
|
|
13
11
|
// pushes the branch to GitHub + opens the PR + enables auto-merge using a SERVER-
|
|
14
|
-
// side push credential. The
|
|
15
|
-
// model is preserved. The credential is scoped to a single repo (Contents:write +
|
|
12
|
+
// side push credential. The checkout never holds a GitHub push credential, so the
|
|
13
|
+
// #919 model is preserved. The credential is scoped to a single repo (Contents:write +
|
|
16
14
|
// Pull-requests:write + Actions:read) and CANNOT push to branch-protected `main`
|
|
17
15
|
// nor bypass required checks — a leaked server credential can only land code on a
|
|
18
16
|
// feature branch behind the same checks every PR faces.
|
|
@@ -30,8 +28,8 @@
|
|
|
30
28
|
// This module is the server seam: push a bundle, open/find the PR, enable auto-
|
|
31
29
|
// merge, report status. All git runs through execFile arg-arrays (shell:false,
|
|
32
30
|
// the #681 injection discipline); the credentialed URL is built in-process and
|
|
33
|
-
// NEVER persisted to a remote nor echoed on an error path (the
|
|
34
|
-
//
|
|
31
|
+
// NEVER persisted to a remote nor echoed on an error path (the "one-shot URL,
|
|
32
|
+
// scrub the credential" pattern). The GitHub API calls are raw
|
|
35
33
|
// global fetch (no Octokit — fetch is used throughout auth.js), header shape
|
|
36
34
|
// mirrors auth.js fetchGithubUser.
|
|
37
35
|
|
|
@@ -574,7 +572,7 @@ async function enableAutoMergeDetailed({ nodeId }, deps = {}) {
|
|
|
574
572
|
}
|
|
575
573
|
|
|
576
574
|
// getPublishStatus — report the PR + prod-deploy state so ship.js can poll
|
|
577
|
-
// WITHOUT gh on the
|
|
575
|
+
// WITHOUT gh on the checkout. Returns:
|
|
578
576
|
// { pr_state, merged, merge_sha, deploy_status, deploy_probe } where
|
|
579
577
|
// pr_state ∈ open|closed|none,
|
|
580
578
|
// deploy_status ∈ pending|success|none|unconfigured|unavailable
|
|
@@ -811,7 +809,7 @@ function prReferencesTask(status, taskId) {
|
|
|
811
809
|
|
|
812
810
|
// task 1002645 — locate a task's PR by the "task <id>: …" TITLE convention, for when
|
|
813
811
|
// the reconciler cannot derive the branch. The sweep derives claude/<worktree_name>
|
|
814
|
-
// (the
|
|
812
|
+
// (the conventional name), but a laptop/wave ship pushes its own branch name
|
|
815
813
|
// (worktree-<worktree_name>), so the derived branch finds no PR and the sweep's whole
|
|
816
814
|
// PR path — the mergeIfGreen nudge (the ONLY green-PR merger where native auto-merge
|
|
817
815
|
// is repo-disallowed), conflict heal, land proof — never engages; the task strands at
|
|
@@ -972,7 +970,7 @@ function mergeBlockReason(s) {
|
|
|
972
970
|
const bad = s.checks.filter(_BAD_CHECK);
|
|
973
971
|
if (bad.length) return `failing_check:${bad[0].name}`;
|
|
974
972
|
// Red, and the failing check is one of the nulled nodes: say the list is blind
|
|
975
|
-
// rather than guess "review or pending", which sent a
|
|
973
|
+
// rather than guess "review or pending", which sent a ship retrying (task 1003988).
|
|
976
974
|
if (s.checks_unreadable && ['FAILURE', 'ERROR'].includes(s.rollup_state)) return 'failing_check_unreadable';
|
|
977
975
|
return 'blocked_required_review_or_pending_check';
|
|
978
976
|
}
|
|
@@ -107,10 +107,6 @@ function moduleKeySteps(spec) {
|
|
|
107
107
|
tasks.push({ title: 'Provide Discord bot token + webhooks', kind: 'infra',
|
|
108
108
|
description: 'The discord module needs a bot token + webhook URLs + DISCORD_BOT_ENABLED=true. Note: outbound/link surfaces fire on credentials even if the module flag is later turned off.' });
|
|
109
109
|
}
|
|
110
|
-
if (enabled.has('dev-box')) {
|
|
111
|
-
tasks.push({ title: 'Provide a DigitalOcean API token for dev boxes', kind: 'infra',
|
|
112
|
-
description: 'The dev-box module needs a DigitalOcean API token (and optionally Cloudflare) on the operator control plane before any builder box can be provisioned.' });
|
|
113
|
-
}
|
|
114
110
|
return tasks;
|
|
115
111
|
}
|
|
116
112
|
|
|
@@ -168,12 +168,4 @@ module.exports = {
|
|
|
168
168
|
// analytics.js (the AI-dev-curve): the builder + ecosystem analytics.
|
|
169
169
|
builderAnalytics: (...a) => db.builderAnalytics(...a),
|
|
170
170
|
ecosystemAnalytics: (...a) => db.ecosystemAnalytics(...a),
|
|
171
|
-
// dev-box (modules/dev-box/routes/box.js, via resolveOptional('lifecycle')): the
|
|
172
|
-
// module-key scope of a builder's active claims — the task-scoped source-access
|
|
173
|
-
// resolver (goal 1000051 / R92, ADR 0148 §2). Keys only; box-access maps them to
|
|
174
|
-
// git sparse-checkout globs through the doorway `moduleScopeMap`.
|
|
175
|
-
moduleScopeForActiveClaims: (...a) => db.moduleScopeForActiveClaims(...a),
|
|
176
|
-
// task 1002729 — the "is this builder actually working right now" signal that
|
|
177
|
-
// lets box-access tell an unscoped box from a crippled one (see db.js).
|
|
178
|
-
activeClaimCountForBuilder: (...a) => db.activeClaimCountForBuilder(...a),
|
|
179
171
|
};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "lifecycle",
|
|
3
3
|
"title": "Lifecycle (the build state machine)",
|
|
4
4
|
"description": "The build state machine \u2014 tasks \u00b7 claims \u00b7 versions \u00b7 done-when criteria \u00b7 goals \u00b7 dependencies \u00b7 the claim\u2192ship\u2192grade\u2192credit lifecycle \u00b7 override requests \u00b7 the ship/confirm/promote orchestration \u00b7 github-push + merge-lease + conflict-resolve + the publish reconciler. The LAST and most-coupled core domain carved out of the GDS core in BV1.R86 (ADR 0093 \u00a71,\u00a73, 02-module-map \u00a74 \u2014 kept whole, never split). default-on; every instance runs the lifecycle. Registers the `lifecycle` kernel PORT (createTask \u00b7 VOTABLE_TASK_STATUSES \u00b7 classifyTaskKind \u00b7 tallyPeerVotes + the work-tracking reads) that core read routes + the ideas/discord/autonomy modules resolve. As a transaction-participant it RESOLVES the `reward` + `onboarding` ports inside its claim/ship/confirm txns \u2014 but via resolveOptional (graceful, like sessions\u2192reward), so consumes is empty: a vanilla instance without those modules degrades the credit/onboarding writes to best-effort no-ops, it does not fail.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.12.0",
|
|
6
7
|
"default": true,
|
|
7
8
|
"maintenance": {
|
|
@@ -4,18 +4,18 @@
|
|
|
4
4
|
// "strands at confirmed" failure class.
|
|
5
5
|
//
|
|
6
6
|
// WHY THIS EXISTS
|
|
7
|
-
// A server-mediated publish (ADR 0055) is ASYNCHRONOUS: a
|
|
7
|
+
// A server-mediated publish (ADR 0055) is ASYNCHRONOUS: a checkout uploads its
|
|
8
8
|
// branch, the server pushes + opens the PR + arms auto-merge, then GitHub runs
|
|
9
|
-
// the required checks, merges, and Actions deploys to prod. The
|
|
9
|
+
// the required checks, merges, and Actions deploys to prod. The uploader's ship.js
|
|
10
10
|
// only POLLS for ~9 min (pollServerPublish) and then EXITS, printing "it flips to
|
|
11
11
|
// shipped on its own". But the confirmed→shipped transition lived in exactly ONE
|
|
12
12
|
// place — the GET /tasks/:id/publish-status reconcile-on-read (routes/tasks.js).
|
|
13
13
|
// That flip can ONLY happen while a client keeps issuing that GET. Real CI (check
|
|
14
|
-
// queue + merge queue + deploy) routinely finishes AFTER the
|
|
14
|
+
// queue + merge queue + deploy) routinely finishes AFTER the client has stopped
|
|
15
15
|
// polling, so nothing ever re-checks and the task row freezes at 'confirmed'
|
|
16
16
|
// forever — even though prod is live. Every prior point-fix (1025/1027/1037/1041/
|
|
17
17
|
// 1045/1046/1049/1050/1052/1068) compensated for a symptom of this one gap:
|
|
18
|
-
// "the
|
|
18
|
+
// "the client must stay alive and keep polling to finish its OWN ship."
|
|
19
19
|
//
|
|
20
20
|
// THE FIX. Move the last mile to the SERVER, where it belongs. A periodic sweep
|
|
21
21
|
// (this module) finds every 'confirmed' task, derives its branch from the latest
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// succeeded?), and on a confirmed land runs the EXACT db.shipTask +
|
|
24
24
|
// ship-broadcast.postShipBroadcast the publish-status route already runs. The land
|
|
25
25
|
// no longer depends on the shipper's CLI being alive — it works for EVERY builder,
|
|
26
|
-
// on a
|
|
26
|
+
// on a laptop or a desktop session, and retroactively heals strands left
|
|
27
27
|
// by a session that exited early. Idempotent: shipTask throws TASK_NOT_CONFIRMED
|
|
28
28
|
// if a client poll (or a prior sweep) already flipped it, so a sweep racing the
|
|
29
29
|
// route never double-ships nor double-broadcasts.
|
|
@@ -333,7 +333,7 @@ async function runSweep(deps = {}) {
|
|
|
333
333
|
|
|
334
334
|
// task 1002645 — the per-branch PR path (merge nudge → conflict/stale heal →
|
|
335
335
|
// publish-status), extracted so it can run TWICE for one task: first on the
|
|
336
|
-
// derived claude/<worktree_name> branch (the
|
|
336
|
+
// derived claude/<worktree_name> branch (the conventional name), then — when
|
|
337
337
|
// that finds no PR — on the REAL branch adopted from the task's own PR
|
|
338
338
|
// (findTaskPullRequest). Mutates the sweep counters via closure; returns
|
|
339
339
|
// { status, prNotLanded }.
|
|
@@ -605,7 +605,7 @@ async function runSweep(deps = {}) {
|
|
|
605
605
|
}
|
|
606
606
|
}
|
|
607
607
|
|
|
608
|
-
// task 1002645 — the claude/<worktree_name> derivation is
|
|
608
|
+
// task 1002645 — the claude/<worktree_name> derivation is only ONE convention;
|
|
609
609
|
// a laptop/wave ship pushes its own branch name (worktree-<worktree_name>), so the
|
|
610
610
|
// derived branch finds no PR and the merge nudge / conflict heal / land proof all
|
|
611
611
|
// sat idle while the task stranded at 'confirmed' (wave PRs 34/35/36 — native
|
|
@@ -768,8 +768,8 @@ async function runSweep(deps = {}) {
|
|
|
768
768
|
logFn(
|
|
769
769
|
`[publish-reconciler] task ${r.task_id} (${landNote}) landed → shipped`
|
|
770
770
|
);
|
|
771
|
-
// The
|
|
772
|
-
// secret
|
|
771
|
+
// The checkout that published cannot post the Discord broadcast (no webhook
|
|
772
|
+
// secret). The server holds the secrets, so it fires it here,
|
|
773
773
|
// exactly ONCE on the confirmed→shipped transition. Best-effort: never let
|
|
774
774
|
// a broadcast hiccup turn a successful reconcile into a failure.
|
|
775
775
|
try {
|
|
@@ -827,7 +827,7 @@ async function runSweep(deps = {}) {
|
|
|
827
827
|
|
|
828
828
|
// task 1366 — server-side session-cost reconcile, piggybacked on the same 5-min
|
|
829
829
|
// sweep. System 1's client cost hook silently skips the MAJORITY of sessions (a
|
|
830
|
-
//
|
|
830
|
+
// cowork VM / overnight agent runs with no GDS token, so the
|
|
831
831
|
// SessionEnd hook quiet-skips), so a server pass re-prices the model_usage we
|
|
832
832
|
// ALREADY stored on session_records into a cost_log row — keyed on the SAME
|
|
833
833
|
// (source, source_ref) the client would use, so logCost's ON CONFLICT DO NOTHING
|
|
@@ -624,7 +624,7 @@ module.exports = function buildTasksRouter() {
|
|
|
624
624
|
// confirmed→shipped flip and throws TASK_NOT_CONFIRMED on a repeat, so
|
|
625
625
|
// whichever entry point wins the transition is the only one that reaches
|
|
626
626
|
// this line — the emit fires exactly ONCE per ship across all three
|
|
627
|
-
// paths. The server is thus the SINGLE ship broadcaster (the
|
|
627
|
+
// paths. The server is thus the SINGLE ship broadcaster (the laptop
|
|
628
628
|
// local post in scripts/gds/ship.js was removed as a duplicate, task
|
|
629
629
|
// 1749). Best-effort: emitAsync isolates listener errors so a broadcast
|
|
630
630
|
// hiccup can never turn a successful ship into a 500.
|
|
@@ -1108,19 +1108,18 @@ module.exports = function buildTasksRouter() {
|
|
|
1108
1108
|
// ==========================================================================
|
|
1109
1109
|
// Server-mediated branch publish (ADR 0055 / task 1025)
|
|
1110
1110
|
// --------------------------------------------------------------------------
|
|
1111
|
-
// A
|
|
1112
|
-
//
|
|
1113
|
-
//
|
|
1111
|
+
// A checkout with NO GitHub push credential (credential-less origin, #919)
|
|
1112
|
+
// cannot `git push` + `gh pr create`, so in `ci` deploy mode ship.js uploads
|
|
1113
|
+
// its branch as a THIN
|
|
1114
1114
|
// git bundle here; the server pushes it + opens the PR + enables auto-merge
|
|
1115
1115
|
// using its own push credential — a GitHub App installation token (preferred,
|
|
1116
1116
|
// task 1028) or the GITHUB_PUSH_TOKEN PAT fallback; single repo, Contents:write
|
|
1117
1117
|
// + PR:write + Actions:read — it CANNOT push to branch-protected main nor bypass
|
|
1118
|
-
// required checks. The
|
|
1119
|
-
// preserved.
|
|
1118
|
+
// required checks. The uploading checkout holds NO GitHub credential, so the
|
|
1119
|
+
// #919 model is preserved.
|
|
1120
1120
|
//
|
|
1121
|
-
// AUTH: requireBuilder + gateTaskOwnership
|
|
1122
|
-
//
|
|
1123
|
-
// session first). The grade already ran client-side against this exact HEAD
|
|
1121
|
+
// AUTH: requireBuilder + gateTaskOwnership. The grade already ran client-side
|
|
1122
|
+
// against this exact HEAD
|
|
1124
1123
|
// (ship.js grades BEFORE this call), and the bundle-tip == claimed head_sha
|
|
1125
1124
|
// check below binds the pushed code to that graded sha.
|
|
1126
1125
|
const publishUpload = express.json({ limit: PUBLISH_BODY_LIMIT });
|
|
@@ -1256,7 +1255,7 @@ module.exports = function buildTasksRouter() {
|
|
|
1256
1255
|
|
|
1257
1256
|
// GET /tasks/:id/publish-status?branch=<branch> — poll the PR + prod-deploy
|
|
1258
1257
|
// state for a server-mediated publish so ship.js can confirm the land WITHOUT
|
|
1259
|
-
// gh on the
|
|
1258
|
+
// gh on the checkout. Ownership-gated like the POST. Derives state live from GitHub.
|
|
1260
1259
|
// `branch` is optional (task 1003764): without it the server finds the task's own PR
|
|
1261
1260
|
// (both publish paths title it "task <id>: …") and the answer names the `branch` it
|
|
1262
1261
|
// used and its `branch_source`, so the check agents are sent to instead of git probes
|
|
@@ -1303,9 +1302,9 @@ module.exports = function buildTasksRouter() {
|
|
|
1303
1302
|
try {
|
|
1304
1303
|
const shipped = await db.shipTask({ taskId: id, builderId: req.builder.id, landAssurance: RECONCILE_PROOF }); // ADR 0184
|
|
1305
1304
|
reconciled = true;
|
|
1306
|
-
// task 1045: the
|
|
1307
|
-
// it holds no webhook secret
|
|
1308
|
-
//
|
|
1305
|
+
// task 1045: the checkout that published can't post the Discord
|
|
1306
|
+
// broadcast — it holds no webhook secret, so such a ship was silently
|
|
1307
|
+
// skipping the public announcement. The server holds
|
|
1309
1308
|
// the secrets and just confirmed the deploy, so it fires the broadcast
|
|
1310
1309
|
// here — exactly ONCE, on the confirmed→shipped transition. shipTask
|
|
1311
1310
|
// throws TASK_NOT_CONFIRMED on a repeat poll, so this never re-posts.
|
|
@@ -1352,8 +1351,7 @@ module.exports = function buildTasksRouter() {
|
|
|
1352
1351
|
// ADR-0058 gate-review BLOCKS a Metic+ author's gate-surface PR shipped from a
|
|
1353
1352
|
// laptop and they can't self-approve their own PR (hit live on task 1162 / PR #131).
|
|
1354
1353
|
//
|
|
1355
|
-
// AUTH mirrors publish-branch EXACTLY: requireBuilder + gateTaskOwnership
|
|
1356
|
-
// deliberately NOT allowBoxScope (a box-scoped session 403s — it must reauth). The
|
|
1354
|
+
// AUTH mirrors publish-branch EXACTLY: requireBuilder + gateTaskOwnership. The
|
|
1357
1355
|
// attestation power is bounded: gate-author-trust clears a NEEDS_TRUST surface for
|
|
1358
1356
|
// a Metic+ author only and NEVER the hard floor (which always needs an owner
|
|
1359
1357
|
// review), so a Metic+ vouching for their own task's head stays within their tier.
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
//
|
|
10
10
|
// ONE generator, two render paths, both PURE:
|
|
11
11
|
// renderMonospaceCard(model) — alignment-proof left-rule text. The CLIs ALWAYS
|
|
12
|
-
// print this, so headless / cron
|
|
12
|
+
// print this, so headless / cron runs and feeds without the in-feed
|
|
13
13
|
// visualization tool still get a consistent summary.
|
|
14
14
|
// renderCardHtml(model) — the populated, branded HTML the skills render in
|
|
15
15
|
// the chat feed via the visualization tool (show_widget). Brand tokens come
|
|
@@ -181,8 +181,8 @@ function isPubliclyViewable(task) {
|
|
|
181
181
|
// The CANONICAL prefix (src/bongos/api-prefix.js) is hard-coded rather than
|
|
182
182
|
// imported: this lib is deliberately core-free so it unit-tests with a Buffer and
|
|
183
183
|
// a temp dir, and the doorway does not publish the prefix. It is safe to hard-code
|
|
184
|
-
// because BOTH prefixes are permanent — '/api/gds' is a forever alias for
|
|
185
|
-
//
|
|
184
|
+
// because BOTH prefixes are permanent — '/api/gds' is a forever alias for older
|
|
185
|
+
// CLIs and the off-box status mirror — so a URL stored under either
|
|
186
186
|
// keeps resolving, and every renderer's guard accepts both.
|
|
187
187
|
const SERVE_PREFIX = '/api/bongos/task-visuals';
|
|
188
188
|
function urlForName(name) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "memory",
|
|
3
3
|
"title": "Memory & recall",
|
|
4
4
|
"description": "Per-builder session memory, the lexical recall/search retrieval layer (ADR 0060), and durable learnings. A core-domain module carved out of the GDS core in BV1.R72 (ADR 0091); default-on because every instance needs the build system's memory.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.5.0",
|
|
6
7
|
"default": true,
|
|
7
8
|
"maintenance": {
|
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
//
|
|
33
33
|
// RENDER / STORED-XSS (#575, audit 2026-06-19) — memory content is stored RAW
|
|
34
34
|
// and is NEVER emitted as HTML. Every endpoint here returns it as JSON
|
|
35
|
-
// (res.json — content as a string field), and the only consumers are the CLI
|
|
36
|
-
//
|
|
35
|
+
// (res.json — content as a string field), and the only consumers are the CLI
|
|
36
|
+
// memory sync (it writes the strings to the local filesystem) and the
|
|
37
37
|
// Archon-only forensic reads (also JSON). An audit of every web surface
|
|
38
38
|
// (modules/hall-ui/public/, modules/status-ui/public/) found NO code path that interpolates
|
|
39
39
|
// memory content into the DOM: there is no markdown→HTML renderer in the client
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "npm-release",
|
|
3
3
|
"title": "npm release",
|
|
4
4
|
"description": "The deploy page for a project that publishes an npm package from its GitHub repo — where every recently shipped task has got to (not merged, merged, in a version, live, released) and each version's list of work, read from the release notes the package itself carries (task 1002622) — and the same line on a task's own page (task 1004301). Built for the Bongos hall, which publishes @bongos/core; off everywhere else.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.19.0",
|
|
6
7
|
"default": false,
|
|
7
8
|
"author": "Cloud Bongos",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "onboarding",
|
|
3
3
|
"title": "Onboarding (admission · setup · newcomer floor)",
|
|
4
4
|
"description": "The newcomer ADMISSION + ORIENTATION domain — the public access-request → Archon-approval gate, the server-ticked onboarding state machine (the 'Path into the City' checklist that graduates a Xenos to Thetes), the friction analyzer (feedback-without-asking on consented onboarding transcripts), and the evergreen newcomer-chore restock (a fresh first-builder task is always waiting). A core-domain module carved out of the GDS core in BV1.R81 (ADR 0093 §1); default-on because every instance onboards its own builders. The access-requests admission routes mount here; the core lifecycle (db.js claim/ship), auth callbacks, and the me/builders profile readers resolve the `onboarding` kernel PORT for the state-machine + restock surface they need — never importing the module.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.19.532",
|
|
6
7
|
"default": true,
|
|
7
8
|
"maintenance": { "status": "core-maintained" },
|
|
@@ -81,8 +81,8 @@ const ALL_STAGES = [...STAGES, GRADUATED];
|
|
|
81
81
|
|
|
82
82
|
// The pre-auth, INFORMATIONAL step that sits AHEAD of auth_complete (task 470).
|
|
83
83
|
// The state machine's first ENGINE stage is auth_complete, but the genuine
|
|
84
|
-
// first action a newcomer takes is "obtain the repo" — clone it locally
|
|
85
|
-
//
|
|
84
|
+
// first action a newcomer takes is "obtain the repo" — clone it locally. That
|
|
85
|
+
// step is manual/external
|
|
86
86
|
// (there is no server event for it) and intentionally NOT a member of STAGES, so
|
|
87
87
|
// it never gates graduation, never advances current_stage, and never appears in
|
|
88
88
|
// shapeForApi's tracked checklist. It exists purely so the system's own model of
|
|
@@ -92,7 +92,7 @@ const ALL_STAGES = [...STAGES, GRADUATED];
|
|
|
92
92
|
const PRE_AUTH_STEP = {
|
|
93
93
|
id: 'obtain_repo',
|
|
94
94
|
label: 'Obtain the repo',
|
|
95
|
-
hint: 'Clone the project to thy machine
|
|
95
|
+
hint: 'Clone the project to thy machine. The build commands live inside the repo, so this comes first.',
|
|
96
96
|
informational: true,
|
|
97
97
|
};
|
|
98
98
|
|
|
@@ -126,10 +126,10 @@ const STAGE_LABELS = {
|
|
|
126
126
|
// onboarding model never named that step, so the hint now points at it ahead of
|
|
127
127
|
// the sign-in itself. See PRE_AUTH_STEP below for the standalone representation.
|
|
128
128
|
const STAGE_HINTS = {
|
|
129
|
-
auth_complete: 'First obtain the repo — clone it
|
|
129
|
+
auth_complete: 'First obtain the repo — clone it — then sign in via GitHub Device Flow or the web sign-in.',
|
|
130
130
|
pick_disciplines: 'Pick the work that calls thee — engineer, artist, ideator, or any mix.',
|
|
131
131
|
first_start: 'Run /builder-start to see claimable works.',
|
|
132
|
-
first_claim: 'Claim a task with /builder-claim N —
|
|
132
|
+
first_claim: 'Claim a task with /builder-claim N — it is thine alone until thou shippest it.',
|
|
133
133
|
ship_three: 'Carry three tasks through with /builder-ship — the third makes thee a Thetes.',
|
|
134
134
|
graduated: '',
|
|
135
135
|
};
|
|
@@ -147,28 +147,16 @@ function isKnownStage(stage) {
|
|
|
147
147
|
// hall renders — a short, action-only orientation path for a brand-new Xenos.
|
|
148
148
|
// Every step auto-detects (#777, per Lars: no manual ticks — "the city watches"):
|
|
149
149
|
//
|
|
150
|
-
// 1.
|
|
151
|
-
// their dev box, i.e. a running `claude` process is seen
|
|
152
|
-
// on the box. Backed by builder_boxes.first_connected_at
|
|
153
|
-
// (stamped write-once by boxes.bumpActivity on the first
|
|
154
|
-
// claude_active=true heartbeat); passed in as
|
|
155
|
-
// `boxConnected` (boxes.hasEverConnected). FIRST because
|
|
156
|
-
// nothing else can happen until the box works — its hint
|
|
157
|
-
// points the newcomer at the welcome panel's setup steps
|
|
158
|
-
// (#823). `source: 'box'`.
|
|
159
|
-
// 2. join_discord — cleared by a live Discord link (discord.linked). The
|
|
150
|
+
// 1. join_discord — cleared by a live Discord link (discord.linked). The
|
|
160
151
|
// hall's "Connect Discord & join" button runs the
|
|
161
152
|
// existing OAuth flow (auto-join + rank role); linking
|
|
162
153
|
// clears the step. `source: 'discord'`.
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
// (ran /builder-setup,
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
// box; setup signs the GDS in so /builder-* works (Lars,
|
|
170
|
-
// #823: "both have a place"). `source: 'cli'`.
|
|
171
|
-
// 5. ship_three — the real engine stage. Auto, live "N of 3" counter,
|
|
154
|
+
// 2. pick_disciplines — the real engine stage, cleared by the craft picker.
|
|
155
|
+
// 3. cli_setup — cleared once the builder has authenticated the CLI
|
|
156
|
+
// (ran /builder-setup, which mints a source='cli'
|
|
157
|
+
// session). Detected via db.hasCliSession; passed in as
|
|
158
|
+
// `cliAuthed`. `source: 'cli'`.
|
|
159
|
+
// 4. ship_three — the real engine stage. Auto, live "N of 3" counter,
|
|
172
160
|
// clears at 3 ships (= the xenos->thetes promotion).
|
|
173
161
|
//
|
|
174
162
|
// The old 'auth_complete' (GitHub web sign-in) is intentionally NOT shown — a
|
|
@@ -181,7 +169,6 @@ function isKnownStage(stage) {
|
|
|
181
169
|
// derive `cleared` for each step. NONE of these are members of STAGES, so none
|
|
182
170
|
// affect nextStage/graduation — they are display-only.
|
|
183
171
|
const DISPLAY_STEPS = [
|
|
184
|
-
{ id: 'box_connect', source: 'box' },
|
|
185
172
|
{ id: 'join_discord', source: 'discord' },
|
|
186
173
|
{ id: 'pick_disciplines', source: 'stage' },
|
|
187
174
|
{ id: 'cli_setup', source: 'cli' },
|
|
@@ -189,18 +176,16 @@ const DISPLAY_STEPS = [
|
|
|
189
176
|
];
|
|
190
177
|
|
|
191
178
|
const DISPLAY_LABELS = {
|
|
192
|
-
box_connect: 'Connect to thy dev box',
|
|
193
179
|
join_discord: 'Join the Discord server',
|
|
194
180
|
pick_disciplines: 'Choose thy craft — engineer, artist, or ideator',
|
|
195
|
-
cli_setup: 'Run /builder-setup
|
|
181
|
+
cli_setup: 'Run /builder-setup',
|
|
196
182
|
ship_three: 'Ship three works — and rise to Thetes',
|
|
197
183
|
};
|
|
198
184
|
|
|
199
185
|
const DISPLAY_HINTS = {
|
|
200
|
-
box_connect: 'Follow the steps in the welcome scroll above to get Claude running on thy dev box — this ticks itself the moment thou art connected.',
|
|
201
186
|
join_discord: 'Connect Discord — thou art auto-joined to the city\'s server and given thy rank.',
|
|
202
187
|
pick_disciplines: 'Pick the work that calls thee — engineer, artist, ideator, or any mix.',
|
|
203
|
-
cli_setup: 'Sign the
|
|
188
|
+
cli_setup: 'Sign the CLI in with /builder-setup — this ticks itself once thou art signed in.',
|
|
204
189
|
ship_three: 'Carry three tasks through with /builder-ship — the third makes thee a Thetes.',
|
|
205
190
|
};
|
|
206
191
|
|
|
@@ -454,25 +439,21 @@ function stageDetail(stageId, shipCount) {
|
|
|
454
439
|
// → the counter isn't rendered.
|
|
455
440
|
// opts.discordLinked — whether the builder has a live Discord link. Clears the
|
|
456
441
|
// 'join_discord' step (the OAuth flow sets the link).
|
|
457
|
-
// opts.boxConnected — whether the builder's dev box has ever reported a live
|
|
458
|
-
// Claude process (boxes.hasEverConnected → first_connected_at).
|
|
459
|
-
// Clears the 'box_connect' step (#823).
|
|
460
442
|
// opts.cliAuthed — whether the builder has ever authenticated the GDS CLI
|
|
461
443
|
// (db.hasCliSession). Clears the 'cli_setup' step.
|
|
462
|
-
function shapeForApi(state, { shipCount = null, discordLinked = false,
|
|
444
|
+
function shapeForApi(state, { shipCount = null, discordLinked = false, cliAuthed = false } = {}) {
|
|
463
445
|
const completed = new Set((state && state.completed_stages) || []);
|
|
464
446
|
const currentReal = (state && state.current_stage) || STAGES[0];
|
|
465
447
|
const graduated = currentReal === GRADUATED;
|
|
466
448
|
|
|
467
449
|
// Per-step cleared resolution, by `source`. 'stage' mirrors hasCleared (in
|
|
468
|
-
// completed_stages, OR the engine advanced past it). 'discord'/'
|
|
469
|
-
//
|
|
450
|
+
// completed_stages, OR the engine advanced past it). 'discord'/'cli' derive
|
|
451
|
+
// from live signals passed by the route. A graduated builder has cleared
|
|
470
452
|
// everything by definition (keeps the displayed list coherent even though the
|
|
471
453
|
// panel hides on graduation — a roster/profile read could still surface this).
|
|
472
454
|
const stepCleared = (step) => {
|
|
473
455
|
if (graduated) return true;
|
|
474
456
|
if (step.source === 'discord') return !!discordLinked;
|
|
475
|
-
if (step.source === 'box') return !!boxConnected;
|
|
476
457
|
if (step.source === 'cli') return !!cliAuthed;
|
|
477
458
|
// 'stage'
|
|
478
459
|
if (completed.has(step.id)) return true;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/bongos/routes/access-requests.js — onboarding cold-start (2026-05-30).
|
|
2
2
|
//
|
|
3
3
|
// The hall is gated: a brand-new person can't enter until an Archon admits
|
|
4
|
-
// them. Admission is the ONLY gate
|
|
5
|
-
//
|
|
6
|
-
//
|
|
4
|
+
// them. Admission is the ONLY gate sign-in applies — there is no GitHub repo-
|
|
5
|
+
// access / collaborator check in it (2026-06-07). 'invited' just means
|
|
6
|
+
// "Archon-approved for the hall"; the web sign-in (auth.js) checks
|
|
7
7
|
// exactly that one flag. This router is how they ask and how the Archon
|
|
8
8
|
// answers:
|
|
9
9
|
//
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "platform-identity",
|
|
3
3
|
"title": "Platform identity (hub IdP)",
|
|
4
4
|
"description": "The Cloud Bongos hub identity-provider surface (ADR 0141): platform accounts, an SSO client registry, and an OAuth authorization-code login handoff so one platform account reaches any hosted project's builders hall with no second GitHub sign-in. Identity ONLY — the hub never mints or carries rank/credits/admission (the ADR 0016 trust boundary). Enabled ONLY on the hub instance (cloudbongos.com); ordinary hosted + self-hosted instances leave it off, so it adds zero attack surface for the common case.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.14.0",
|
|
6
7
|
"default": false,
|
|
7
8
|
"author": "Cloud Bongos",
|
|
@@ -87,14 +87,21 @@ function clientSecretValid(client, presentedSecret) {
|
|
|
87
87
|
// Decide whether a RESOLVED hub session may complete the SSO handoff. resolveSession
|
|
88
88
|
// (the raw resolver the browser leg uses so it can redirect-to-sign-in rather than
|
|
89
89
|
// 401) does NOT apply the deny gates requireBuilder does (src/bongos/auth.js) — so
|
|
90
|
-
// this replicates them: a deactivated account (status 'inactive'), a
|
|
91
|
-
//
|
|
92
|
-
//
|
|
93
|
-
//
|
|
90
|
+
// this replicates them: a deactivated account (status 'inactive'), a session whose
|
|
91
|
+
// source the kernel's session-source registry does not declare, and any non-human
|
|
92
|
+
// service principal (system_role set) must NOT federate. Pure + exported so the
|
|
93
|
+
// gate is unit-tested directly. Returns { ok, reason }.
|
|
94
|
+
//
|
|
95
|
+
// FEDERATION_SOURCES mirrors modules/government/session-scopes.json — a module may
|
|
96
|
+
// not read another module's files, and the doorway exports no lookup for it — and
|
|
97
|
+
// tests/platform-identity.mjs pins the two lists equal, so a source declared or
|
|
98
|
+
// retired there without the same edit here fails the build instead of drifting.
|
|
99
|
+
const FEDERATION_SOURCES = Object.freeze(['cli', 'web', null]);
|
|
94
100
|
function sessionUsableForFederation(session) {
|
|
95
101
|
if (!session) return { ok: false, reason: 'no_session' };
|
|
96
102
|
if (session.status === 'inactive') return { ok: false, reason: 'inactive' };
|
|
97
|
-
|
|
103
|
+
const source = session.source === undefined ? null : session.source;
|
|
104
|
+
if (!FEDERATION_SOURCES.includes(source)) return { ok: false, reason: 'session_scope' };
|
|
98
105
|
if (session.system_role) return { ok: false, reason: 'service_principal' };
|
|
99
106
|
return { ok: true, reason: null };
|
|
100
107
|
}
|
|
@@ -1395,6 +1402,7 @@ module.exports = {
|
|
|
1395
1402
|
clientSecretValid,
|
|
1396
1403
|
redirectUriAllowed,
|
|
1397
1404
|
sessionUsableForFederation,
|
|
1405
|
+
FEDERATION_SOURCES,
|
|
1398
1406
|
safeAvatarUrl,
|
|
1399
1407
|
// accounts
|
|
1400
1408
|
upsertAccount,
|
|
@@ -249,8 +249,9 @@ module.exports = function ssoRoutes() {
|
|
|
249
249
|
//
|
|
250
250
|
// resolveSession is the RAW resolver (so we can redirect rather than 401), so it
|
|
251
251
|
// does NOT apply requireBuilder's deny gates — we must. sessionUsableForFederation
|
|
252
|
-
// rejects a deactivated account, a
|
|
253
|
-
// trust boundary), and service principals: none may mint a
|
|
252
|
+
// rejects a deactivated account, a session of an undeclared source (outside
|
|
253
|
+
// the ADR 0016 trust boundary), and service principals: none may mint a
|
|
254
|
+
// federated identity code.
|
|
254
255
|
let session;
|
|
255
256
|
try {
|
|
256
257
|
session = await resolveSession(req);
|
|
@@ -265,8 +266,8 @@ module.exports = function ssoRoutes() {
|
|
|
265
266
|
}
|
|
266
267
|
const msg = usable.reason === 'inactive'
|
|
267
268
|
? 'This account is no longer active. Ask an Archon on the hub if you believe this is an error.'
|
|
268
|
-
: usable.reason === '
|
|
269
|
-
? '
|
|
269
|
+
: usable.reason === 'session_scope'
|
|
270
|
+
? 'This kind of session cannot be used for cross-project sign-in. Sign in from a browser or run /builder-reauth.'
|
|
270
271
|
: 'This session type cannot be used for cross-project sign-in.';
|
|
271
272
|
return res.status(403).type('text/html').send(errorPage(msg));
|
|
272
273
|
}
|
|
@@ -119,9 +119,23 @@ t('a deactivated account may NOT federate (mirrors requireBuilder status gate)',
|
|
|
119
119
|
assert.deepEqual(r, { ok: false, reason: 'inactive' });
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
t('a dev-box
|
|
123
|
-
|
|
124
|
-
|
|
122
|
+
t('a session of an undeclared source (a leftover dev-box token) may NOT federate', () => {
|
|
123
|
+
for (const source of ['box', 'not-a-source']) {
|
|
124
|
+
const r = P.sessionUsableForFederation({ github_id: 1, status: 'active', source });
|
|
125
|
+
assert.deepEqual(r, { ok: false, reason: 'session_scope' }, source);
|
|
126
|
+
}
|
|
127
|
+
// cli, web and the legacy sourceless row still federate.
|
|
128
|
+
for (const source of ['cli', 'web', null, undefined]) {
|
|
129
|
+
assert.equal(P.sessionUsableForFederation({ github_id: 1, status: 'active', source }).ok, true, String(source));
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
t('FEDERATION_SOURCES is exactly the kernel session-source registry', () => {
|
|
134
|
+
// platform-identity may not read the government module, so it mirrors the list;
|
|
135
|
+
// this pin is what stops the copy drifting from modules/government/session-scopes.json.
|
|
136
|
+
const reg = JSON.parse(readFileSync(new URL('../../government/session-scopes.json', import.meta.url), 'utf8'));
|
|
137
|
+
const declared = reg.scopes.map((r) => (r.source === undefined ? null : r.source));
|
|
138
|
+
assert.deepEqual([...P.FEDERATION_SOURCES].sort(), declared.sort());
|
|
125
139
|
});
|
|
126
140
|
|
|
127
141
|
t('a service principal (system_role set) may NOT federate', () => {
|
|
@@ -137,7 +137,7 @@ async function countPlaced(db, shapes, { excludeId = null } = {}) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
// readLocalMeminfo — THIS host's /proc/meminfo, for the surfaces that run on the
|
|
140
|
-
// control plane itself. Null (not a throw) where it does not exist, e.g. a
|
|
140
|
+
// control plane itself. Null (not a throw) where it does not exist, e.g. a laptop
|
|
141
141
|
// that is not Linux — the summary then reads 'default' and says so.
|
|
142
142
|
function readLocalMeminfo() {
|
|
143
143
|
try { return fs.readFileSync('/proc/meminfo', 'utf8'); } catch { return null; }
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"key": "provisioning",
|
|
3
3
|
"title": "Instance provisioning",
|
|
4
4
|
"description": "Stand up + operate a Cloud Bongos instance's own infrastructure (server, database, domain, DNS, TLS) via the API, so an owner does not manage infra by hand (ADR 0111). Owns the web-tier REQUEST + STATE surface only: it enqueues provisioning intents + reads instance state \u2014 it holds NO cloud-provider tokens and calls NO cloud API. A separate control-plane runner (scripts/gds/provision.js, task P3) is the sole token-holder that drains the intents. Distinct from the `hosting` module (ADR 0092), which brokers compute WORKLOADS.",
|
|
5
|
+
"version": "1.0.0",
|
|
5
6
|
"coreVersion": "^1.14.0",
|
|
6
7
|
"default": false,
|
|
7
8
|
"author": "Cloud Bongos",
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// intents. The control-plane runner (scripts/gds/provision.js, task P3) is the sole
|
|
8
8
|
// token-holder that drains provisioning_intents and does the real infra work.
|
|
9
9
|
//
|
|
10
|
-
// `db` is dependency-INJECTED (passed in as the first arg),
|
|
11
|
-
//
|
|
10
|
+
// `db` is dependency-INJECTED (passed in as the first arg), so this file has NO
|
|
11
|
+
// core require and is trivially
|
|
12
12
|
// unit-testable with a fake `{ query }`. The route factory passes the doorway pool.
|
|
13
13
|
'use strict';
|
|
14
14
|
|
|
@@ -351,8 +351,9 @@ function defaultDomainForSlug(slug, apex) {
|
|
|
351
351
|
}
|
|
352
352
|
|
|
353
353
|
// Labels the platform itself occupies (or has planned) directly under the instance
|
|
354
|
-
// apex, plus the
|
|
355
|
-
// so a project claiming one of these
|
|
354
|
+
// apex, plus the `term-` / `sandbox-` hostname prefixes older per-builder machines
|
|
355
|
+
// took. The runner's DNS write is an UPSERT, so a project claiming one of these
|
|
356
|
+
// would overwrite platform records — refuse at
|
|
356
357
|
// the API layer instead (task 1002704; the guard applies to auto-assigned AND
|
|
357
358
|
// bring-your-own hostnames, on create and on attach). Anything deeper than one
|
|
358
359
|
// label under the apex is also refused: it breaks the wildcard-cert rule (one
|
|
@@ -1133,7 +1134,7 @@ async function setInstanceStatus(db, id, status, patch = {}) {
|
|
|
1133
1134
|
|
|
1134
1135
|
// Enqueue a provision/teardown intent. Idempotent: the partial unique index
|
|
1135
1136
|
// ux_provisioning_intents_open makes a second open enqueue collide (23505); we
|
|
1136
|
-
// then return the already-open intent with created:false
|
|
1137
|
+
// then return the already-open intent with created:false.
|
|
1137
1138
|
async function enqueueIntent(db, instanceId, action, requestedBy, targetVersion = null) {
|
|
1138
1139
|
await require('./control-plane-guard').assertIntentAllowed(db, instanceId, action); // ADR 0293 D2; required HERE because this file is at the size ratchet
|
|
1139
1140
|
try {
|