@bongos/core 1.18.3 → 1.18.18
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 +1575 -285
- package/.claude/skills/builder-box/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +97 -0
- package/.claude/skills/builder-ship/SKILL.md +8 -2
- package/.claude/skills/builder-start/SKILL.md +11 -0
- package/.claude/skills/builder-sync/SKILL.md +41 -0
- package/.claude/skills/fix-task/SKILL.md +64 -0
- package/.claude/skills/goal-create/SKILL.md +158 -0
- package/.claude/skills/goal-review/SKILL.md +3 -1
- package/.claude/skills/idea-triage/SKILL.md +14 -8
- package/.claude/skills/merge-mode/SKILL.md +2 -2
- package/.claude/skills/new-project/SKILL.md +1 -0
- package/.claude/skills/planning-session/SKILL.md +45 -18
- package/.env.local.example +4 -3
- package/README.md +4 -2
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +82 -18
- package/clients/bongos-client/index.cjs +82 -18
- package/clients/bongos-client/index.d.ts +131 -29
- package/clients/bongos-client/index.mjs +82 -18
- package/config/branding.neutral.json +6 -1
- package/config/governance.neutral.json +21 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
- package/docs/adr/0090-metic-task-authoring.md +1 -1
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
- package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
- package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
- package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
- package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
- package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
- package/docs/adr/0152-metic-task-abandonment.md +67 -0
- package/docs/adr/0153-sequential-chain-runner.md +51 -0
- package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
- package/docs/adr/README.md +10 -0
- package/docs/api/openapi.json +843 -17
- package/docs/api-reference.md +23 -10
- package/docs/architecture.md +4 -2
- package/docs/branding-contract.md +3 -1
- package/docs/canonical-permissions.md +21 -8
- package/docs/file-map.md +8 -0
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
- package/docs/onboarding/diagrams/assertions.json +4 -7
- package/docs/onboarding/drachmae-and-karma.md +3 -3
- package/docs/onboarding/primer.md +17 -16
- package/docs/onboarding/slash-commands.md +124 -0
- package/docs/recipes/builder-box-lifecycle.md +23 -0
- package/docs/recipes/devbox-app-release.md +14 -4
- package/docs/recipes/instance-first-run.md +16 -0
- package/docs/recipes/local-dev.md +81 -0
- package/docs/recipes/ops-gotchas.md +19 -1
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/parallel-goal-waves.md +174 -0
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +48 -0
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
- package/migrations/003_pms.sql +6 -7
- package/migrations/006_pms_v2.sql +3 -31
- package/migrations/core_193_task_needs_rebase.sql +28 -0
- package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
- package/migrations/core_195_idea_triage_provenance.sql +57 -0
- package/migrations/core_196_session_reward_topup.sql +69 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -8
- package/modules/builder-settings/wandering-prefs.js +61 -18
- package/modules/dev-box/app/README.md +73 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
- package/modules/dev-box/app/electron-builder.yml +95 -0
- package/modules/dev-box/app/package-lock.json +5291 -0
- package/modules/dev-box/app/package.json +30 -0
- package/modules/dev-box/app/renderer/app.js +319 -0
- package/modules/dev-box/app/renderer/index.html +45 -0
- package/modules/dev-box/app/renderer/styles.css +246 -0
- package/modules/dev-box/app/renderer/theme.generated.css +16 -0
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
- package/modules/dev-box/app/scripts/gen-brand.js +138 -0
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
- package/modules/dev-box/app/scripts/gen-icons.js +286 -0
- package/modules/dev-box/app/scripts/vendor-client.js +23 -0
- package/modules/dev-box/app/src/box-controller.js +287 -0
- package/modules/dev-box/app/src/brand.generated.json +13 -0
- package/modules/dev-box/app/src/brand.js +16 -0
- package/modules/dev-box/app/src/config.js +19 -0
- package/modules/dev-box/app/src/gds-client.js +90 -0
- package/modules/dev-box/app/src/main.js +610 -0
- package/modules/dev-box/app/src/pairing.js +57 -0
- package/modules/dev-box/app/src/preload.js +29 -0
- package/modules/dev-box/app/src/session-store.js +58 -0
- package/modules/dev-box/app/src/ssh-setup.js +569 -0
- package/modules/dev-box/app/src/task-picker.js +70 -0
- package/modules/dev-box/app/src/tray.js +99 -0
- package/modules/dev-box/app/src/updater.js +55 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
- package/modules/dev-box/app/test/unit.mjs +526 -0
- package/modules/dev-box/box-access.js +121 -4
- package/modules/dev-box/boxes.js +20 -0
- package/modules/dev-box/module.json +1 -1
- package/modules/dev-box/routes/box.js +87 -3
- package/modules/economy/credits.js +116 -25
- package/modules/economy/reward.js +8 -0
- package/modules/economy/streak.js +2 -1
- package/modules/governance/CLAUDE.md +15 -0
- package/modules/governance/catalog.js +419 -0
- package/modules/governance/config.js +174 -0
- package/modules/governance/db.js +125 -0
- package/modules/governance/migrations/governance_001_tables.sql +81 -0
- package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
- package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
- package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
- package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
- package/modules/governance/module.json +10 -0
- package/modules/governance/protected-surfaces.js +88 -0
- package/modules/governance/protected-surfaces.json +80 -0
- package/modules/governance/rank-roles.js +83 -0
- package/modules/governance/resolver.js +221 -0
- package/modules/governance/routes/governance.js +89 -0
- package/modules/governance/routes/role-sync.js +49 -0
- package/modules/governance/session-scopes.js +127 -0
- package/modules/governance/session-scopes.json +38 -0
- package/modules/hall-ui/public/atlas.css +117 -0
- package/modules/hall-ui/public/atlas.html +3 -114
- package/modules/hall-ui/public/brand-apply.js +34 -6
- package/modules/hall-ui/public/builders.js +187 -219
- package/modules/hall-ui/public/diagrams.html +2 -0
- package/modules/hall-ui/public/dom-utils.js +27 -0
- package/modules/hall-ui/public/drachmae.html +2 -0
- package/modules/hall-ui/public/drachmae.js +54 -1
- package/modules/hall-ui/public/gate.css +98 -0
- package/modules/hall-ui/public/gate.html +3 -95
- package/modules/hall-ui/public/goal-inbox.js +4 -1
- package/modules/hall-ui/public/goals-page.js +626 -114
- package/modules/hall-ui/public/goals.css +79 -1
- package/modules/hall-ui/public/goals.html +38 -11
- package/modules/hall-ui/public/hall-widgets.js +62 -52
- package/modules/hall-ui/public/harbor.css +123 -0
- package/modules/hall-ui/public/harbor.html +3 -120
- package/modules/hall-ui/public/index.html +11 -4
- package/modules/hall-ui/public/instances-panel.js +4 -1
- package/modules/hall-ui/public/modules.css +63 -0
- package/modules/hall-ui/public/modules.html +3 -60
- package/modules/hall-ui/public/new-project-wizard.js +7 -3
- package/modules/hall-ui/public/not-ready.html +2 -0
- package/modules/hall-ui/public/pair.css +73 -0
- package/modules/hall-ui/public/pair.html +3 -70
- package/modules/hall-ui/public/palette.js +302 -0
- package/modules/hall-ui/public/primer.css +13 -0
- package/modules/hall-ui/public/primer.html +2 -0
- package/modules/hall-ui/public/profile.html +2 -0
- package/modules/hall-ui/public/ranks.html +2 -0
- package/modules/hall-ui/public/roadmap.css +179 -0
- package/modules/hall-ui/public/roadmap.html +9 -172
- package/modules/hall-ui/public/roadmap.js +329 -316
- package/modules/hall-ui/public/sessions.css +16 -0
- package/modules/hall-ui/public/sessions.html +3 -13
- package/modules/hall-ui/public/settings.html +2 -0
- package/modules/hall-ui/public/settings.js +4 -4
- package/modules/hall-ui/public/shell.js +143 -18
- package/modules/hall-ui/public/style.css +231 -39
- package/modules/hall-ui/public/task-detail.js +347 -26
- package/modules/hall-ui/public/task.html +6 -1
- package/modules/hall-ui/public/watch.html +2 -0
- package/modules/hall-ui/public/work.html +8 -6
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/ideas/inbox.js +61 -11
- package/modules/ideas/routes/inbox.js +23 -0
- package/modules/lifecycle/claim-eligibility.js +72 -0
- package/modules/lifecycle/db.js +519 -139
- package/modules/lifecycle/done-when.js +18 -3
- package/modules/lifecycle/github-push.js +19 -2
- package/modules/lifecycle/goal-advisory.js +114 -0
- package/modules/lifecycle/goal-authz.js +62 -0
- package/modules/lifecycle/lifecycle.js +9 -0
- package/modules/lifecycle/publish-reconciler.js +109 -6
- package/modules/lifecycle/routes/claims.js +68 -23
- package/modules/lifecycle/routes/done-when.js +110 -15
- package/modules/lifecycle/routes/goals.js +8 -38
- package/modules/lifecycle/routes/tasks.js +38 -6
- package/modules/lifecycle/ship-card.js +6 -0
- package/modules/onboarding/onboarding-state.js +1 -1
- package/modules/platform-identity/CLAUDE.md +15 -0
- package/modules/platform-identity/hub-device.js +147 -0
- package/modules/platform-identity/hub-keys.js +151 -0
- package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
- package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
- package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
- package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
- package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
- package/modules/platform-identity/module.json +18 -0
- package/modules/platform-identity/platform-identity.js +531 -0
- package/modules/platform-identity/pollers/code-sweep.js +43 -0
- package/modules/platform-identity/routes/my-projects.js +140 -0
- package/modules/platform-identity/routes/projects.js +107 -0
- package/modules/platform-identity/routes/scouting.js +35 -0
- package/modules/platform-identity/routes/sso.js +402 -0
- package/modules/platform-identity/tests/platform-identity.mjs +388 -0
- package/modules/platform-identity/ui/.gitkeep +0 -0
- package/modules/security/routes/reports.js +10 -0
- package/modules/sessions/db.js +166 -21
- package/modules/sessions/module.json +1 -1
- package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
- package/modules/sessions/routes/sessions.js +33 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/anim/idle/frame_00.webp +0 -0
- package/public-landing/assets/anim/idle/frame_01.webp +0 -0
- package/public-landing/assets/anim/idle/frame_02.webp +0 -0
- package/public-landing/assets/anim/idle/frame_03.webp +0 -0
- package/public-landing/assets/anim/idle/frame_04.webp +0 -0
- package/public-landing/assets/anim/idle/frame_05.webp +0 -0
- package/public-landing/assets/anim/idle/frame_06.webp +0 -0
- package/public-landing/assets/anim/idle/frame_07.webp +0 -0
- package/public-landing/assets/anim/idle/frame_08.webp +0 -0
- package/public-landing/assets/anim/idle/frame_09.webp +0 -0
- package/public-landing/assets/anim/idle/frame_10.webp +0 -0
- package/public-landing/assets/anim/idle/frame_11.webp +0 -0
- package/public-landing/assets/anim/idle/frame_12.webp +0 -0
- package/public-landing/assets/anim/idle/frame_13.webp +0 -0
- package/public-landing/assets/anim/idle/frame_14.webp +0 -0
- package/public-landing/assets/anim/idle/frame_15.webp +0 -0
- package/public-landing/assets/anim/idle/frame_16.webp +0 -0
- package/public-landing/assets/anim/idle/frame_17.webp +0 -0
- package/public-landing/assets/anim/idle/frame_18.webp +0 -0
- package/public-landing/assets/anim/idle/frame_19.webp +0 -0
- package/public-landing/assets/anim/idle/frame_20.webp +0 -0
- package/public-landing/assets/anim/idle/frame_21.webp +0 -0
- package/public-landing/assets/anim/idle/frame_22.webp +0 -0
- package/public-landing/assets/anim/idle/frame_23.webp +0 -0
- package/public-landing/assets/anim/idle/frame_24.webp +0 -0
- package/public-landing/assets/anim/idle/frame_25.webp +0 -0
- package/public-landing/assets/anim/idle/frame_26.webp +0 -0
- package/public-landing/assets/anim/idle/frame_27.webp +0 -0
- package/public-landing/assets/anim/idle/frame_28.webp +0 -0
- package/public-landing/assets/anim/idle/frame_29.webp +0 -0
- package/public-landing/assets/anim/idle/frame_30.webp +0 -0
- package/public-landing/assets/anim/idle/frame_31.webp +0 -0
- package/public-landing/assets/anim/idle/frame_32.webp +0 -0
- package/public-landing/assets/anim/idle/frame_33.webp +0 -0
- package/public-landing/assets/anim/idle/frame_34.webp +0 -0
- package/public-landing/assets/anim/idle/frame_35.webp +0 -0
- package/public-landing/assets/anim/idle/frame_36.webp +0 -0
- package/public-landing/assets/anim/idle/frame_37.webp +0 -0
- package/public-landing/assets/anim/idle/frame_38.webp +0 -0
- package/public-landing/assets/anim/idle/frame_39.webp +0 -0
- package/public-landing/assets/anim/idle/frame_40.webp +0 -0
- package/public-landing/assets/anim/idle/frame_41.webp +0 -0
- package/public-landing/assets/anim/idle/frame_42.webp +0 -0
- package/public-landing/assets/anim/idle/frame_43.webp +0 -0
- package/public-landing/create.html +51 -58
- package/public-landing/gate/README.md +137 -0
- package/public-landing/gate/dispmaps/bar.png +0 -0
- package/public-landing/gate/dispmaps/flat.png +0 -0
- package/public-landing/gate/dispmaps/key.png +0 -0
- package/public-landing/gate/index.html +587 -0
- package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
- package/public-landing/gate/previews/off-the-boats.jpg +0 -0
- package/public-landing/gate/space.css +799 -0
- package/public-landing/gate/space.js +1826 -0
- package/public-landing/gate/sprites/enter.png +0 -0
- package/public-landing/gate/sprites/search.png +0 -0
- package/public-landing/gate/sprites/terminal-blank.png +0 -0
- package/public-landing/gate/sprites/tile.png +0 -0
- package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
- package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
- package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
- package/public-landing/world/assets/plates/scene-establishing.png +0 -0
- package/scripts/gds/abandon.js +6 -4
- package/scripts/gds/api.js +10 -5
- package/scripts/gds/api.ps1 +26 -0
- package/scripts/gds/api.sh +6 -0
- package/scripts/gds/audit-ships.js +7 -1
- package/scripts/gds/backfill-sessions.js +78 -18
- package/scripts/gds/backup.js +23 -1
- package/scripts/gds/box-connect-lib.js +124 -31
- package/scripts/gds/box-sync.js +66 -0
- package/scripts/gds/box.js +225 -53
- package/scripts/gds/cf-tunnel.js +145 -6
- package/scripts/gds/claim.js +34 -3
- package/scripts/gds/cli-lib.js +161 -27
- package/scripts/gds/code.js +6 -2
- package/scripts/gds/deploy-divergence.js +209 -0
- package/scripts/gds/dev-lib.js +309 -0
- package/scripts/gds/dev.js +322 -0
- package/scripts/gds/doctor.js +90 -61
- package/scripts/gds/fetch-art-key.js +1 -1
- package/scripts/gds/fetch-sound-prefs.js +2 -2
- package/scripts/gds/fitness.js +228 -12
- package/scripts/gds/gds-db-backup.service +16 -1
- package/scripts/gds/gds-db-offsite.service +9 -1
- package/scripts/gds/gen-api-client.js +1 -1
- package/scripts/gds/gen-api-docs.js +36 -14
- package/scripts/gds/gen-diagrams.js +45 -28
- package/scripts/gds/gen-file-map.js +2 -1
- package/scripts/gds/gen-freshness.js +32 -0
- package/scripts/gds/gen-repo-map.js +3 -2
- package/scripts/gds/gen-session-index.js +14 -5
- package/scripts/gds/hooks/pre-push.js +2 -2
- package/scripts/gds/init.js +60 -3
- package/scripts/gds/install-git-hooks.js +1 -1
- package/scripts/gds/key-card.js +15 -6
- package/scripts/gds/key-status.js +1 -1
- package/scripts/gds/linkify-refs.js +35 -9
- package/scripts/gds/login.js +16 -1
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +123 -12
- package/scripts/gds/publish-manifest.js +7 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +4 -2
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
- package/scripts/gds/sequence.js +425 -0
- package/scripts/gds/session-cost.js +10 -1
- package/scripts/gds/setup.js +31 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +319 -29
- package/scripts/gds/smoke-gds.js +139 -8
- package/scripts/gds/start.js +171 -22
- package/scripts/gds/status.js +1 -1
- package/scripts/gds/triage.js +10 -9
- package/scripts/gds/upgrade.js +124 -2
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +447 -102
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +39 -0
- package/src/bongos/module-scope-map.js +27 -1
- package/src/bongos/permission-path-check.js +136 -102
- package/src/bongos/repo-info.js +47 -11
- package/src/bongos/route-rank-check.js +429 -46
- package/src/bongos/routes/_helpers.js +12 -1
- package/src/bongos/routes/auth.js +219 -2
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +32 -0
- package/src/instance-config.js +30 -0
- package/src/module-api.js +112 -1
- package/src/platform-server.js +86 -9
- package/tests/abandon_task.mjs +59 -3
- package/tests/api_cli_flags.mjs +53 -0
- package/tests/auth_page_gate.mjs +13 -0
- package/tests/backfill_sessions.mjs +127 -1
- package/tests/backup_cli.mjs +54 -0
- package/tests/bongos_box_connect.mjs +77 -0
- package/tests/box_access.mjs +130 -1
- package/tests/box_credential.mjs +125 -0
- package/tests/box_host_keys.mjs +24 -0
- package/tests/box_infra_resolve.mjs +149 -0
- package/tests/box_scope_session.mjs +6 -2
- package/tests/box_sync.mjs +55 -0
- package/tests/box_task_scope.mjs +99 -0
- package/tests/branding.mjs +12 -0
- package/tests/cf_ruleset_preflight.mjs +142 -0
- package/tests/cf_tunnel.mjs +37 -0
- package/tests/claim_gate_rebase.mjs +98 -0
- package/tests/clawback_confirmed_backlog.mjs +103 -0
- package/tests/cli_lib_formatters.mjs +20 -0
- package/tests/confirm_attribution.mjs +30 -46
- package/tests/credit_grant.mjs +198 -10
- package/tests/criterion_authoring_gate.mjs +272 -0
- package/tests/criterion_progress.mjs +33 -1
- package/tests/dev_lib.mjs +253 -0
- package/tests/devbox_artifact_gate.mjs +75 -0
- package/tests/devbox_dmg_backdrop.mjs +137 -0
- package/tests/devbox_icons.mjs +304 -0
- package/tests/devbox_installer_brand.mjs +232 -0
- package/tests/devbox_key_register.mjs +234 -0
- package/tests/docs_openapi_route.mjs +110 -0
- package/tests/doctor_hooks.mjs +141 -0
- package/tests/extract_json.mjs +140 -0
- package/tests/fitness.mjs +89 -0
- package/tests/gen_freshness.mjs +96 -0
- package/tests/goal_advisory.mjs +251 -0
- package/tests/goal_criterion_confirm_ui.mjs +269 -0
- package/tests/goal_routes.mjs +59 -4
- package/tests/goal_tier.mjs +12 -3
- package/tests/governance_catalog.mjs +129 -0
- package/tests/governance_claim_eligibility.mjs +262 -0
- package/tests/governance_config.mjs +187 -0
- package/tests/governance_ownership_scope.mjs +410 -0
- package/tests/governance_page_gates.mjs +324 -0
- package/tests/governance_parity.mjs +336 -0
- package/tests/governance_principals.mjs +232 -0
- package/tests/governance_protected_surfaces.mjs +208 -0
- package/tests/governance_require_permission.mjs +223 -0
- package/tests/governance_resolver.mjs +84 -0
- package/tests/governance_role_sync.mjs +231 -0
- package/tests/governance_seed.mjs +127 -0
- package/tests/governance_session_scope.mjs +434 -0
- package/tests/governance_vanilla_boot.mjs +203 -0
- package/tests/grade_attribution.mjs +7 -7
- package/tests/hall_audit.mjs +223 -0
- package/tests/hall_nav.mjs +191 -0
- package/tests/hall_palette.mjs +206 -0
- package/tests/hall_signout.mjs +124 -0
- package/tests/hook_user_agents.mjs +10 -0
- package/tests/idea_triage.mjs +162 -0
- package/tests/idp_device_exchange.mjs +93 -0
- package/tests/init.mjs +51 -0
- package/tests/instance_config.mjs +65 -0
- package/tests/instance_mismatch_guard.mjs +23 -6
- package/tests/instance_release_config.mjs +104 -0
- package/tests/linkify_refs.mjs +15 -2
- package/tests/module_contributions.mjs +55 -0
- package/tests/module_loader.mjs +10 -5
- package/tests/module_scope_active_claims.mjs +94 -0
- package/tests/needs_rebase.mjs +92 -0
- package/tests/one_active_claim_per_session.mjs +76 -0
- package/tests/override_attribution.mjs +10 -8
- package/tests/override_request_ownership.mjs +8 -4
- package/tests/parse_cookie_decode.mjs +42 -0
- package/tests/pay_on_land.mjs +129 -0
- package/tests/platform_boot.mjs +100 -1
- package/tests/post_login_landing.mjs +42 -0
- package/tests/preflight_checks.mjs +72 -0
- package/tests/provision.mjs +132 -7
- package/tests/publish_branch_route.mjs +53 -0
- package/tests/publish_reconciler.mjs +187 -2
- package/tests/repo_info_binding.mjs +248 -0
- package/tests/reward_gate.mjs +11 -2
- package/tests/reward_mode_cost_plus_only.mjs +116 -0
- package/tests/reward_mode_surfacing.mjs +168 -0
- package/tests/route_permission_check.mjs +301 -0
- package/tests/route_rank_check.mjs +254 -12
- package/tests/route_validation.mjs +30 -0
- package/tests/sequence.mjs +267 -0
- package/tests/session_digest.mjs +44 -0
- package/tests/session_index.mjs +18 -6
- package/tests/session_records.mjs +39 -1
- package/tests/session_reward_on_land.mjs +381 -0
- package/tests/session_reward_reconcile.mjs +229 -0
- package/tests/ship_api_client_pathspec.mjs +100 -0
- package/tests/ship_base_flag.mjs +93 -0
- package/tests/ship_ci_deploy.mjs +80 -3
- package/tests/ship_upstream_deletion.mjs +57 -0
- package/tests/smoke_target.mjs +233 -0
- package/tests/start_env_guard.mjs +87 -0
- package/tests/start_rebase_warning.mjs +103 -0
- package/tests/status_mirror_sync.mjs +122 -0
- package/tests/stop_refcheck.mjs +19 -1
- package/tests/task_goal_link.mjs +15 -6
- package/tests/upgrade.mjs +139 -0
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/sessions/pollers/session-reward-reconcile.js — the loader poller entry
|
|
4
|
+
// for the STATE-BASED session-token-reward safety net (ADR 0120).
|
|
5
|
+
//
|
|
6
|
+
// The deferred cost-plus session reward books via two LIVE triggers only: the
|
|
7
|
+
// upload-time gate and the in-process `task.shipped` listener. Both miss a task
|
|
8
|
+
// that reaches status='shipped' OUT OF BAND — a direct-SQL / hotpatch flip, or a
|
|
9
|
+
// ship in another process — leaving that session's drachmae stranded forever even
|
|
10
|
+
// though the code landed. This poller periodically calls
|
|
11
|
+
// db.reconcileUnbookedSessionRewards(), which books the reward for any already-
|
|
12
|
+
// shipped session that has no session.token_reward row yet. It is the sessions-
|
|
13
|
+
// domain twin of the publish-reconciler poller (the same loader inversion), and
|
|
14
|
+
// mirrors the task 1679 achievements reconcile (reconcile from durable STATE, not
|
|
15
|
+
// the event). Idempotent — a re-sweep never double-pays.
|
|
16
|
+
//
|
|
17
|
+
// Deliberately independent of any GitHub push credential (unlike the confirmed→
|
|
18
|
+
// shipped publish-reconciler): this sweep touches no git and its whole purpose is
|
|
19
|
+
// to run on a credential-less box and recover already-shipped-but-unbooked work.
|
|
20
|
+
|
|
21
|
+
const db = require('../db');
|
|
22
|
+
|
|
23
|
+
// Sweep cadence. A safety net, not a hot path — the live triggers do the real-time
|
|
24
|
+
// work; this only catches out-of-band ships, so a relaxed interval is fine.
|
|
25
|
+
const INTERVAL_MS = Number(process.env.SESSION_REWARD_RECONCILE_MS) || 10 * 60_000;
|
|
26
|
+
const FIRST_SWEEP_DELAY_MS = Number(process.env.SESSION_REWARD_RECONCILE_FIRST_MS) || 90_000;
|
|
27
|
+
|
|
28
|
+
let intervalTimer = null;
|
|
29
|
+
let firstTimer = null;
|
|
30
|
+
|
|
31
|
+
async function runSweep() {
|
|
32
|
+
const res = await db.reconcileUnbookedSessionRewards({ limit: 200 });
|
|
33
|
+
if (res && res.awarded > 0) {
|
|
34
|
+
console.log(
|
|
35
|
+
`[session-reward-reconcile] booked ${res.awarded} stranded session reward(s) (scanned ${res.scanned})`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return res;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Start the periodic sweep. Idempotent (a second call is a no-op). Kill-switched by
|
|
42
|
+
// SESSION_REWARD_RECONCILE_DISABLED=1. Timers are unref'd so they never hold the
|
|
43
|
+
// process open on a systemd restart (the publish-reconciler / discord-bot precedent).
|
|
44
|
+
function start() {
|
|
45
|
+
if (process.env.SESSION_REWARD_RECONCILE_DISABLED === '1') {
|
|
46
|
+
console.log('[session-reward-reconcile] disabled (SESSION_REWARD_RECONCILE_DISABLED=1)');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (intervalTimer) return; // already started
|
|
50
|
+
|
|
51
|
+
const tick = () => {
|
|
52
|
+
runSweep().catch((e) =>
|
|
53
|
+
console.error(`[session-reward-reconcile] sweep crashed: ${e && e.message ? e.message : e}`)
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
firstTimer = setTimeout(tick, FIRST_SWEEP_DELAY_MS);
|
|
58
|
+
if (firstTimer.unref) firstTimer.unref();
|
|
59
|
+
intervalTimer = setInterval(tick, INTERVAL_MS);
|
|
60
|
+
if (intervalTimer.unref) intervalTimer.unref();
|
|
61
|
+
|
|
62
|
+
console.log(
|
|
63
|
+
`[session-reward-reconcile] started — recovering out-of-band-ship rewards every ${Math.round(INTERVAL_MS / 1000)}s`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function stop() {
|
|
68
|
+
if (firstTimer) {
|
|
69
|
+
clearTimeout(firstTimer);
|
|
70
|
+
firstTimer = null;
|
|
71
|
+
}
|
|
72
|
+
if (intervalTimer) {
|
|
73
|
+
clearInterval(intervalTimer);
|
|
74
|
+
intervalTimer = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = { start, stop, runSweep, INTERVAL_MS };
|
|
@@ -133,7 +133,40 @@ function recordSessionRead(req, { route, targetBuilderId, target }) {
|
|
|
133
133
|
});
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
// The module's kernel seam wiring, registered when this factory runs (the loader
|
|
137
|
+
// runs it at boot, already gated to enabled instances — the discord precedent).
|
|
138
|
+
// This is the LAND-side half of ADR 0120 "pay on land": core emits `task.shipped`
|
|
139
|
+
// on EVERY land path (publish-reconciler + routes/tasks) with the shipped task
|
|
140
|
+
// row; we react by booking the cost-plus SESSION token reward for any session
|
|
141
|
+
// that shipped that task and DEFERRED its reward at upload time (the ci/async
|
|
142
|
+
// order, where the digest upload precedes the land). The upload-side gate in
|
|
143
|
+
// db.upsertSessionRecordWithReward covers the sync/laptop order; both call the
|
|
144
|
+
// SAME idempotent award, so re-firing is a safe per-session_id no-op.
|
|
145
|
+
//
|
|
146
|
+
// sessions registers no PORT (provides: []) — this is an EVENT listener, not a
|
|
147
|
+
// port, so module.json is unchanged. off→on guarded so a test re-instantiating
|
|
148
|
+
// the router never stacks duplicate listeners (the discord listener precedent).
|
|
149
|
+
let _shipRewardListenerOff = null;
|
|
150
|
+
function registerSeams() {
|
|
151
|
+
if (_shipRewardListenerOff) _shipRewardListenerOff();
|
|
152
|
+
_shipRewardListenerOff = api.on('task.shipped', (task) => {
|
|
153
|
+
const id = task && task.id;
|
|
154
|
+
if (id == null) return;
|
|
155
|
+
// Best-effort: awardDeferredSessionRewardForTask is async, so a rejection
|
|
156
|
+
// (e.g. a transient DB error) surfaces as a rejected promise — .catch keeps
|
|
157
|
+
// it from escaping the listener. This matters because the dominant ci/async
|
|
158
|
+
// land path emits SYNCHRONOUSLY (publish-reconciler's emit()), which is
|
|
159
|
+
// fire-and-forget and would otherwise turn a throw here into an
|
|
160
|
+
// unhandledRejection. A missed reward is re-attempted idempotently on the
|
|
161
|
+
// next land/upload, so a best-effort miss never double-pays or loses money.
|
|
162
|
+
db.awardDeferredSessionRewardForTask(id).catch((e) =>
|
|
163
|
+
console.error('[sessions] deferred session-reward on task.shipped (non-blocking):',
|
|
164
|
+
e && e.message ? e.message : e));
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
136
168
|
module.exports = function buildSessionsRouter() {
|
|
169
|
+
registerSeams();
|
|
137
170
|
const router = express.Router();
|
|
138
171
|
const uploadJson = express.json({ limit: UPLOAD_BODY_LIMIT });
|
|
139
172
|
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.18",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.18.
|
|
9
|
+
"version": "1.18.18",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.18.
|
|
3
|
+
"version": "1.18.18",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -82,11 +82,6 @@ body{
|
|
|
82
82
|
.exit-wizard kbd{font-family:var(--font-mono);font-size:10px;line-height:1;background:rgba(42,31,23,0.08);border-radius:4px;padding:2px 5px;color:var(--ink-faint);}
|
|
83
83
|
.who{display:flex;align-items:center;gap:10px;font-size:14px;font-weight:600;color:var(--ink-soft);min-height:34px;}
|
|
84
84
|
.who img{width:30px;height:30px;border-radius:50%;border:2px solid #fff;box-shadow:0 2px 8px rgba(42,31,23,0.12);}
|
|
85
|
-
.who a{color:var(--accent-deep);text-decoration:none;font-weight:700;}
|
|
86
|
-
.who a:hover{text-decoration:underline;}
|
|
87
|
-
/* switch to a different GitHub account from inside the wizard (task 2098) */
|
|
88
|
-
.who .switch-acct{font-size:12px;font-weight:600;color:var(--ink-faint);padding-left:10px;margin-left:2px;border-left:1px solid rgba(42,31,23,0.16);}
|
|
89
|
-
.who .switch-acct:hover{color:var(--accent-deep);}
|
|
90
85
|
|
|
91
86
|
/* ── layout ── */
|
|
92
87
|
.wrap{
|
|
@@ -202,10 +197,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
202
197
|
.ghbtn:hover{background:#1b1f24;transform:translateY(-1px);}
|
|
203
198
|
.ghbtn:active{transform:translateY(0) scale(.98);}
|
|
204
199
|
.ghbtn .gh{width:18px;height:18px;fill:currentColor;}
|
|
205
|
-
.ghcreate{display:none;margin-bottom:18px;}
|
|
206
|
-
.ghcreate.on{display:block;}
|
|
207
|
-
.ghcreate-note{font-size:13px;font-weight:500;color:var(--ink-faint);line-height:1.5;margin-top:9px;max-width:52ch;}
|
|
208
|
-
.ghcreate-note strong{color:var(--ink-soft);}
|
|
209
200
|
.steps .ghbtn{font-size:14px;padding:9px 16px;margin-top:10px;}
|
|
210
201
|
|
|
211
202
|
/* buttons */
|
|
@@ -380,7 +371,11 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
380
371
|
<section class="panel" id="panel-gate" aria-labelledby="gate-h">
|
|
381
372
|
<h1 id="gate-h">Create a project</h1>
|
|
382
373
|
<p class="lede">Your own <strong data-brand-product-inline>Cloud Bongos</strong> project: its own repository, its own home on the web, and a hall of AI builders who do the building. You set the direction; agents lay down the code. Sign in with GitHub so the project belongs to <em>you</em> from the very first beat.</p>
|
|
383
|
-
|
|
374
|
+
<!-- The wizard's own sign-in opts into repo scope (read:user + public_repo) up
|
|
375
|
+
front (&repos=1) — anyone here intends to create a project, which needs repo
|
|
376
|
+
access, so we ask once at sign-in instead of a second "Connect" step at Home.
|
|
377
|
+
Hall + CLI sign-in stay identity-only (ADR 0143 least-privilege default). -->
|
|
378
|
+
<a class="btn big" id="signin" href="/api/bongos/auth/web/start?return=/create&repos=1">
|
|
384
379
|
<svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
385
380
|
Sign in with GitHub
|
|
386
381
|
</a>
|
|
@@ -430,7 +425,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
430
425
|
to the manual owner/repo inputs below (#manualHome), which stay authoritative. -->
|
|
431
426
|
<div id="ghConnect" class="reassure">
|
|
432
427
|
<p id="ghConnectMsg"> </p>
|
|
433
|
-
<button type="button" class="btn" id="ghConnectBtn">
|
|
428
|
+
<button type="button" class="btn" id="ghConnectBtn">Allow repo access</button>
|
|
434
429
|
<button type="button" class="linklike" id="ghManualToggle">or enter it manually</button>
|
|
435
430
|
</div>
|
|
436
431
|
<div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
|
|
@@ -454,13 +449,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
454
449
|
<input type="text" id="f-repo" placeholder="mercury" autocomplete="off" maxlength="120">
|
|
455
450
|
</label>
|
|
456
451
|
</div>
|
|
457
|
-
<div class="ghcreate" id="ghCreate">
|
|
458
|
-
<a class="ghbtn" id="ghCreateBtn" href="https://github.com/new" target="_blank" rel="noopener">
|
|
459
|
-
<svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
460
|
-
Create this repo on GitHub
|
|
461
|
-
</a>
|
|
462
|
-
<p class="ghcreate-note">Opens GitHub with the name already filled in — just click <strong>Create repository</strong> and leave it empty, then come back and press Next. Already made it? Just press Next.</p>
|
|
463
|
-
</div>
|
|
464
452
|
<div class="hint" id="h-home"> </div>
|
|
465
453
|
<div class="nav">
|
|
466
454
|
<button type="button" class="btn ghost" data-back="1">← Back</button>
|
|
@@ -607,10 +595,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
607
595
|
if (owner) u.searchParams.set('owner', owner);
|
|
608
596
|
return u.toString();
|
|
609
597
|
}
|
|
610
|
-
function updateGhCreateHref() {
|
|
611
|
-
var a = $('ghCreateBtn');
|
|
612
|
-
if (a) a.href = githubNewRepoUrl(state.owner, state.repo);
|
|
613
|
-
}
|
|
614
598
|
function esc(s) {
|
|
615
599
|
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
616
600
|
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
@@ -664,7 +648,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
664
648
|
if (!state.repoTouched) {
|
|
665
649
|
state.repo = deriveRepo(state.name);
|
|
666
650
|
$('f-repo').value = state.repo;
|
|
667
|
-
updateGhCreateHref();
|
|
668
651
|
}
|
|
669
652
|
var h = $('h-name');
|
|
670
653
|
if (state.slug) { h.innerHTML = 'its handle on the platform: <code>' + esc(state.slug) + '</code>'; h.className = 'hint'; }
|
|
@@ -678,10 +661,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
678
661
|
$('c-green').setAttribute('aria-pressed', String(mode === 'greenfield'));
|
|
679
662
|
$('c-adopt').setAttribute('aria-pressed', String(mode === 'adopt'));
|
|
680
663
|
$('adoptNote').classList.toggle('on', mode === 'adopt');
|
|
681
|
-
// The "create the repo on GitHub" button only makes sense when starting fresh —
|
|
682
|
-
// an adopted repo already exists.
|
|
683
|
-
$('ghCreate').classList.toggle('on', mode === 'greenfield');
|
|
684
|
-
updateGhCreateHref();
|
|
685
664
|
saveDraft();
|
|
686
665
|
syncHome();
|
|
687
666
|
}
|
|
@@ -692,7 +671,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
692
671
|
/* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
|
|
693
672
|
Progressive enhancement: probe GET /github/repos once we reach step 2.
|
|
694
673
|
200 → we hold repo access → show the picker (adopt) / name field (fresh);
|
|
695
|
-
403 → offer
|
|
674
|
+
403 → offer to grant repo access (a ?repos=1 re-sign-in);
|
|
696
675
|
503 (server can't store the token) or any error → the manual inputs.
|
|
697
676
|
#manualHome (the classic owner/repo fields) is the always-safe fallback. ── */
|
|
698
677
|
var repoAccess = null; // null=unknown/loading · true · 'need-connect' · 'unavailable' · 'manual'
|
|
@@ -722,10 +701,10 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
722
701
|
$('manualHome').hidden = !isManualHome();
|
|
723
702
|
if (need) {
|
|
724
703
|
$('ghConnectMsg').textContent = adopt
|
|
725
|
-
? '
|
|
726
|
-
: '
|
|
704
|
+
? 'You’re signed in — creating your project needs one more GitHub permission. Allow repo access to pick the repository to adopt.'
|
|
705
|
+
: 'You’re signed in — creating your project needs one more GitHub permission. Allow repo access and we’ll create the empty repo for you.';
|
|
727
706
|
}
|
|
728
|
-
if (ok && adopt)
|
|
707
|
+
if (ok && adopt) initAdoptPicker();
|
|
729
708
|
if (ok && !adopt) syncFresh();
|
|
730
709
|
}
|
|
731
710
|
|
|
@@ -749,25 +728,57 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
749
728
|
}
|
|
750
729
|
$('ghNewName').addEventListener('input', function () { state.repo = this.value.trim(); updateFreshPreview(); saveDraft(); });
|
|
751
730
|
|
|
731
|
+
// The adopt picker behaves like a standard combobox (owner design review, task
|
|
732
|
+
// 1002247): the dropdown is open only while the visitor is choosing. Selecting a
|
|
733
|
+
// repo fills the search box with its full_name and collapses the list; focusing or
|
|
734
|
+
// typing reopens it.
|
|
735
|
+
function openRepoList() { $('ghList').hidden = false; renderRepoList(); }
|
|
736
|
+
function closeRepoList() { $('ghList').hidden = true; }
|
|
752
737
|
function renderRepoList() {
|
|
753
738
|
var q = ($('ghSearch').value || '').trim().toLowerCase();
|
|
754
|
-
var rows = repoList.filter(function (r) { return !q || String(r.full_name).toLowerCase().indexOf(q) >= 0; }).slice(0, 100);
|
|
755
739
|
var sel = state.owner && state.repo ? (state.owner + '/' + state.repo) : '';
|
|
740
|
+
// When the box just shows the current selection verbatim, list everything (so the
|
|
741
|
+
// visitor can switch) instead of filtering down to the one selected repo.
|
|
742
|
+
var effectiveQ = q === sel.toLowerCase() ? '' : q;
|
|
743
|
+
var rows = repoList.filter(function (r) { return !effectiveQ || String(r.full_name).toLowerCase().indexOf(effectiveQ) >= 0; }).slice(0, 100);
|
|
756
744
|
$('ghList').innerHTML = rows.length
|
|
757
745
|
? rows.map(function (r) {
|
|
758
746
|
return '<button type="button" class="ghrow" role="option" data-full="' + esc(r.full_name) + '" aria-selected="' + (r.full_name === sel) + '">' +
|
|
759
747
|
'<span>' + esc(r.full_name) + '</span>' + (r.private ? '<span class="priv">Private</span>' : '') + '</button>';
|
|
760
748
|
}).join('')
|
|
761
|
-
: '<div class="hint" style="padding:10px 12px">No repositories match “' + esc(
|
|
749
|
+
: '<div class="hint" style="padding:10px 12px">No repositories match “' + esc(effectiveQ) + '”.</div>';
|
|
750
|
+
}
|
|
751
|
+
// Fill the search box with the current selection (if any) and collapse the list;
|
|
752
|
+
// with nothing chosen yet, open it so the visitor sees their repos right away.
|
|
753
|
+
function initAdoptPicker() {
|
|
754
|
+
var sel = state.owner && state.repo ? state.owner + '/' + state.repo : '';
|
|
755
|
+
// Only treat it as a confirmed pick if it's actually one of the visitor's repos.
|
|
756
|
+
// owner/repo are auto-derived from the project name, so a bare derived value must
|
|
757
|
+
// NOT masquerade as a chosen repo — that would collapse the list onto a repo that
|
|
758
|
+
// may not even exist.
|
|
759
|
+
var picked = sel && repoList.some(function (r) { return String(r.full_name).toLowerCase() === sel.toLowerCase(); });
|
|
760
|
+
var h = $('ghAdoptHint');
|
|
761
|
+
if (picked) {
|
|
762
|
+
$('ghSearch').value = sel;
|
|
763
|
+
h.className = 'hint ok'; h.innerHTML = 'Adopting <strong>' + esc(sel) + '</strong>.';
|
|
764
|
+
closeRepoList();
|
|
765
|
+
} else {
|
|
766
|
+
$('ghSearch').value = '';
|
|
767
|
+
h.className = 'hint'; h.innerHTML = ' ';
|
|
768
|
+
openRepoList();
|
|
769
|
+
}
|
|
762
770
|
}
|
|
763
|
-
$('ghSearch').addEventListener('
|
|
771
|
+
$('ghSearch').addEventListener('focus', function () { this.select(); openRepoList(); });
|
|
772
|
+
$('ghSearch').addEventListener('input', openRepoList);
|
|
764
773
|
$('ghList').addEventListener('click', function (ev) {
|
|
765
774
|
var row = ev.target.closest('.ghrow'); if (!row) return;
|
|
766
775
|
var full = row.getAttribute('data-full'); var slash = full.indexOf('/');
|
|
767
776
|
state.owner = full.slice(0, slash); state.repo = full.slice(slash + 1);
|
|
777
|
+
state.ownerTouched = true; state.repoTouched = true; // a real pick — don't let name-derive clobber it
|
|
768
778
|
saveDraft();
|
|
769
|
-
|
|
779
|
+
$('ghSearch').value = full; // pull the choice into the search box …
|
|
770
780
|
var h = $('ghAdoptHint'); h.className = 'hint ok'; h.innerHTML = 'Adopting <strong>' + esc(full) + '</strong>.';
|
|
781
|
+
closeRepoList(); // … and collapse the dropdown (typical UX)
|
|
771
782
|
});
|
|
772
783
|
|
|
773
784
|
function ensureRepoAccess() {
|
|
@@ -785,8 +796,8 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
785
796
|
.then(syncHome);
|
|
786
797
|
}
|
|
787
798
|
|
|
788
|
-
$('f-owner').addEventListener('input', function () { state.owner = this.value.trim(); state.ownerTouched = true;
|
|
789
|
-
$('f-repo').addEventListener('input', function () { state.repo = this.value.trim(); state.repoTouched = true;
|
|
799
|
+
$('f-owner').addEventListener('input', function () { state.owner = this.value.trim(); state.ownerTouched = true; saveDraft(); });
|
|
800
|
+
$('f-repo').addEventListener('input', function () { state.repo = this.value.trim(); state.repoTouched = true; saveDraft(); });
|
|
790
801
|
|
|
791
802
|
$('f-slug').addEventListener('input', function () {
|
|
792
803
|
state.slugTouched = true;
|
|
@@ -815,7 +826,7 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
815
826
|
}
|
|
816
827
|
if (step === 2) {
|
|
817
828
|
var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
|
|
818
|
-
if (repoAccess === 'need-connect') return homeErr('
|
|
829
|
+
if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
|
|
819
830
|
if (repoAccess === true && state.mode !== 'adopt') {
|
|
820
831
|
// Fresh with access: just the new repo NAME (owner is the signed-in account).
|
|
821
832
|
if (!validRepoName(state.repo)) return homeErr('Name the new repository (letters, numbers, and . _ -).');
|
|
@@ -1114,31 +1125,14 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1114
1125
|
if (e.key === 'Escape') { e.preventDefault(); exitWizard(); }
|
|
1115
1126
|
});
|
|
1116
1127
|
|
|
1117
|
-
/* ── switch GitHub account from inside the wizard (task 2098) ── you are not
|
|
1118
|
-
locked to the account you entered with: sign out of the Cloud Bongos session,
|
|
1119
|
-
then restart GitHub sign-in so the wizard reloads under whichever account you
|
|
1120
|
-
pick. (To land on a *different* GitHub account, switch accounts on GitHub's
|
|
1121
|
-
side; this un-locks our session so the next sign-in is honored.) */
|
|
1122
|
-
function switchAccount(e) {
|
|
1123
|
-
if (e) e.preventDefault();
|
|
1124
|
-
var back = API + '/auth/web/start?return=/create';
|
|
1125
|
-
fetch(API + '/auth/logout', { method: 'POST', credentials: 'same-origin' })
|
|
1126
|
-
.then(function () { window.location.assign(back); })
|
|
1127
|
-
.catch(function () { window.location.assign(back); });
|
|
1128
|
-
}
|
|
1129
|
-
|
|
1130
1128
|
/* ── boot: who am I? ── (task 2094: the wizard shows the signed-in identity
|
|
1131
1129
|
ONLY — never a link into cloud bongos's own builders hall. A project
|
|
1132
|
-
creator enters a hall from their own project's landing, not from here.
|
|
1133
|
-
task 2098: + a "switch account" action so you're not locked in.) ── */
|
|
1130
|
+
creator enters a hall from their own project's landing, not from here.) ── */
|
|
1134
1131
|
function renderWho() {
|
|
1135
1132
|
var w = $('who');
|
|
1136
1133
|
if (me) {
|
|
1137
1134
|
w.innerHTML = (me.avatar_url ? '<img src="' + esc(me.avatar_url) + '" alt="">' : '') +
|
|
1138
|
-
'<span>' + esc(me.github_login || 'signed in') + '</span>'
|
|
1139
|
-
'<a href="#" class="switch-acct" id="switchAcct">switch account</a>';
|
|
1140
|
-
var sw = $('switchAcct');
|
|
1141
|
-
if (sw) sw.addEventListener('click', switchAccount);
|
|
1135
|
+
'<span>' + esc(me.github_login || 'signed in') + '</span>';
|
|
1142
1136
|
} else {
|
|
1143
1137
|
w.innerHTML = '';
|
|
1144
1138
|
}
|
|
@@ -1154,7 +1148,6 @@ input[type=text].bad{border-color:var(--err);}
|
|
|
1154
1148
|
if (!state.ownerTouched && !state.owner && me && me.github_login) {
|
|
1155
1149
|
state.owner = me.github_login;
|
|
1156
1150
|
$('f-owner').value = state.owner;
|
|
1157
|
-
updateGhCreateHref();
|
|
1158
1151
|
saveDraft();
|
|
1159
1152
|
}
|
|
1160
1153
|
// returning after a create? resume watching that instance instead of a blank wizard
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Cloud Bongos — landing gate (interactive)
|
|
2
|
+
|
|
3
|
+
`index.html` is the Cloud Bongos landing experience — a small state machine over one
|
|
4
|
+
self-contained page (`space.css` + `space.js` for the space side; the download scene + its
|
|
5
|
+
drag physics live in the inline `<script>`), built from **drawn ATLA-style sprites**
|
|
6
|
+
(`sprites/*.png` — a `> cloud bongos` terminal, an enter key, a search bar, and download tiles)
|
|
7
|
+
refracting the backdrop through SVG displacement maps.
|
|
8
|
+
|
|
9
|
+
## The flow (tasks 1721, 1722)
|
|
10
|
+
|
|
11
|
+
Four body-class views: `view-entry` → `view-space` → `view-download` / `view-detail`.
|
|
12
|
+
|
|
13
|
+
1. **Entry — type `bongos`, in space** (`#entry`, `view-entry`). Every visit lands here: the
|
|
14
|
+
`> cloud ▊` terminal floating in the **space sky** (deep-blue → sunrise gradient, drifting
|
|
15
|
+
clouds), *not* the temple world. Typing `bongos` is the ritual on every visit; the enter key
|
|
16
|
+
lights, and pressing enter flashes through into the circles. Driven by `space.js`
|
|
17
|
+
(`entryKey` / `enterSite`), reusing the terminal + enter CSS.
|
|
18
|
+
2. **Space — the circles** (`#space` / `#space-canvas`, `view-space`). A pannable, zoomable sky
|
|
19
|
+
of project orbs, sized by number of builders, moving as a **living gravity cluster** — every
|
|
20
|
+
orb attracts every other by its mass (∝ size³), so the field swirls, bodies sling past one
|
|
21
|
+
another, and a small orb is sometimes briefly caught circling a big one like a moon (task
|
|
22
|
+
1733; see "The gravity" below). Project data is **inlined** in `space.js` (`PROJECTS`) — Caddy
|
|
23
|
+
404s `*.json` on staging — so swap in a real registry by changing `PROJECTS` + `layout()`.
|
|
24
|
+
- **Drag** empty sky to pan · **scroll** to zoom (toward the cursor).
|
|
25
|
+
- **Hover** an orb for an info card; **click** it to dive in.
|
|
26
|
+
- **Space bar** opens a search scoped to the orbs **currently in view** (client-side match
|
|
27
|
+
over name/tagline/tags now; the Claude-backed answer is a dormant seam, `aiAnswer()`, per
|
|
28
|
+
the "interaction now, AI wired next" decision).
|
|
29
|
+
- **Click empty sky** drops a new project circle.
|
|
30
|
+
3. **Download — Cloud Bongos' install page** (`#land` in `.stage`, `view-download`). Clicking the
|
|
31
|
+
Cloud Bongos orb zooms **straight** into the search + download-tiles scene over the
|
|
32
|
+
temple-world backdrop — **no type-`bongos` gate in between** (that already happened at entry).
|
|
33
|
+
`space.js` fires `cb:show-download`; the inline script reveals `#land` and runs its drag.
|
|
34
|
+
4. **Detail — other projects** (`#detail`, `view-detail`). Clicking any non-self orb opens a
|
|
35
|
+
themed "full story — coming soon" page with a link out to its live site.
|
|
36
|
+
|
|
37
|
+
**ESC** rises from the download or detail page back to the circles (`space.js` → `cb:hide-download`).
|
|
38
|
+
|
|
39
|
+
## The download scene's elastic-tether drag (task 1534)
|
|
40
|
+
|
|
41
|
+
Every download sprite (search bar, tiles, wordmark) is draggable. Grab one and it resists like
|
|
42
|
+
it's anchored; pull past the threshold and it snaps free, hovers, then drifts slowly home and
|
|
43
|
+
re-docks, carrying a faint idle bob at rest. The physics is scoped to `.stage`, so it never
|
|
44
|
+
grabs the entry terminal (which `space.js` owns). **This scene is separate from the space
|
|
45
|
+
gravity below — the download interaction is unchanged by task 1733.**
|
|
46
|
+
|
|
47
|
+
## The space gravity — a living n-body cluster (task 1733)
|
|
48
|
+
|
|
49
|
+
The project orbs on the **space** view (`view-space`) are a real gravitational simulation, all
|
|
50
|
+
in `space.js`. Each orb is a body with **mass ∝ size³** (more builders → bigger → heavier →
|
|
51
|
+
pulls harder), so the sky behaves like planets, suns and the occasional fleeting moon rather
|
|
52
|
+
than a fixed scatter. Under the hood:
|
|
53
|
+
|
|
54
|
+
- **Softened Newtonian gravity**, every pair, so a close pass *slings* instead of blowing up
|
|
55
|
+
(no singularities).
|
|
56
|
+
- A **fixed-timestep leapfrog integrator** (`physStep`) for accurate, energy-stable motion; the
|
|
57
|
+
frame loop (`tick` → `render`) interpolates between steps for smoothness.
|
|
58
|
+
- **Soft collisions** so discs bounce rather than overlap; a **soft containment well** plus a
|
|
59
|
+
**band thermostat** keep the swirl bounded and lively *forever* — energy is free to ebb and
|
|
60
|
+
flow inside a band, and only a runaway collapse or blow-up gets a gentle nudge back.
|
|
61
|
+
- Net drift is removed each step, so the cluster stays centred on screen (and stays put when a
|
|
62
|
+
visitor adds a new orb, which is flung into the swirl).
|
|
63
|
+
|
|
64
|
+
Verified stable over a 60-minute headless simulation (no NaN, never off-screen, never
|
|
65
|
+
interpenetrating). It replaced the earlier "drift around fixed anchors" motion.
|
|
66
|
+
|
|
67
|
+
### Tuning the gravity
|
|
68
|
+
|
|
69
|
+
All knobs live in the `GRAV` block near the top of `space.js`:
|
|
70
|
+
|
|
71
|
+
| You want… | Edit (in `GRAV`) |
|
|
72
|
+
|--------------------------------------|---------------------------------------------------|
|
|
73
|
+
| Livelier / faster swirl | `SWIRL` ↑ (or `THERMO_LO` ↑) |
|
|
74
|
+
| Calmer, slower motion | `SWIRL` ↓ / `THERMO_LO` ↓ |
|
|
75
|
+
| Tighter cluster (less panning) | `CONTAIN_R` ↓ |
|
|
76
|
+
| Stronger pull between orbs | `G` ↑ |
|
|
77
|
+
| Fewer hard slingshots on near-misses | `SOFTEN` ↑ |
|
|
78
|
+
| Bouncier contacts (→ elastic) | `REST` → 1 |
|
|
79
|
+
| Sharper size→mass contrast | lower `R_REF`, or raise the `massFor` exponent |
|
|
80
|
+
|
|
81
|
+
Turn it off (freeze the orbs): comment out `initOrbits()` in `layout()` and the
|
|
82
|
+
`requestAnimationFrame(tick)` at the bottom of the IIFE — pan, zoom, hover, dive, search and the
|
|
83
|
+
table all keep working.
|
|
84
|
+
|
|
85
|
+
> **Live:** password-locked at **staging.cloudbongos.com** (the future landing page, parked
|
|
86
|
+
> for review). Setup + the password live in [`docs/recipes/cloudbongos-staging-gate.md`](../../../docs/recipes/cloudbongos-staging-gate.md).
|
|
87
|
+
> Branding (logo coin / wordmark / mascot / code-glyphs) is still being finished and is
|
|
88
|
+
> intentionally untouched here.
|
|
89
|
+
|
|
90
|
+
## Preview it locally
|
|
91
|
+
|
|
92
|
+
The page links to art one folder up (`../world/assets/…`), so it must be served from the
|
|
93
|
+
**`brand/cloud-bongos/`** folder — not from `gate/` itself, or those paths break.
|
|
94
|
+
|
|
95
|
+
**Easiest (Claude Code preview):** start the `gate-preview` server in `.claude/launch.json`,
|
|
96
|
+
then open `http://127.0.0.1:8137/gate/index.html`.
|
|
97
|
+
|
|
98
|
+
**From a terminal** (repo root): `python3 -m http.server --directory brand/cloud-bongos 8137`,
|
|
99
|
+
then open <http://127.0.0.1:8137/gate/index.html>.
|
|
100
|
+
|
|
101
|
+
Use a Chromium browser (Chrome/Edge) — the live-glass refraction relies on `backdrop-filter`
|
|
102
|
+
with an SVG displacement map. Safari ignores the displacement and falls back to a plain
|
|
103
|
+
frost, which still looks fine.
|
|
104
|
+
|
|
105
|
+
## Tuning the drag
|
|
106
|
+
|
|
107
|
+
All behaviour is in the single `<script>` at the bottom of `index.html`, which opens with a
|
|
108
|
+
commented **TUNING** block. Common tweaks:
|
|
109
|
+
|
|
110
|
+
| You want… | Edit (in the TUNING block) |
|
|
111
|
+
|------------------------------------------|-----------------------------------|
|
|
112
|
+
| Harder / easier to pull a pane free | `THRESHOLD` — ↑ harder, ↓ easier |
|
|
113
|
+
| How far a tap can move before it's a drag | `DEADZONE` — px |
|
|
114
|
+
| Freed panes wait longer before returning | `HOVER` — milliseconds |
|
|
115
|
+
| Slower, more drifty return | `DRIFT_MAXV` — ↓ slower |
|
|
116
|
+
| Wider, more wandering return path | `WMAG` ↑ and/or `TURN` ↓ |
|
|
117
|
+
| More / less idle "breathing" | `BOB_X`, `BOB_Y` (px) — `0` = off |
|
|
118
|
+
|
|
119
|
+
### Which elements are draggable
|
|
120
|
+
|
|
121
|
+
The drag targets **`.mark`** (the wordmark) + every **`.el`** sprite pane (terminal, enter,
|
|
122
|
+
search, tiles). To add one, give it the `el` class (or push it into the `els` array); to
|
|
123
|
+
exclude one, filter it out of `els`.
|
|
124
|
+
|
|
125
|
+
### Why it does not fight the other animations
|
|
126
|
+
|
|
127
|
+
The drag only ever applies `transform: translate(…)` to each pane, and it owns that property
|
|
128
|
+
exclusively — so the float/reveal animations were replaced by the drag's own idle bob, and
|
|
129
|
+
the enter key's reveal uses opacity + blur only (no transform). The **scene** transforms
|
|
130
|
+
(the dive's scale/translate) live on the parent `.gate`/`.land` containers, so they compose
|
|
131
|
+
with the per-pane drag instead of clobbering it. When a scene settles, `measure()` re-runs
|
|
132
|
+
so a freshly-revealed pane drags from the right home.
|
|
133
|
+
|
|
134
|
+
### Turn the drag off
|
|
135
|
+
|
|
136
|
+
Delete the second `<script>` IIFE (the drag) — the type→dive interaction and all the art
|
|
137
|
+
keep working untouched.
|
|
Binary file
|