@bongos/core 1.18.0 → 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 +1740 -640
- 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 +5 -3
- 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 +11 -2
- 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/0142-project-context-is-prose-only.md +41 -0
- package/docs/adr/0143-onboarding-repo-picker-oauth-scope.md +35 -0
- 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 +15 -2
- package/docs/api/openapi.json +2648 -483
- package/docs/api-reference.md +73 -13
- 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 +15 -7
- 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/packaging-the-core.md +13 -0
- 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/upgrading-the-core.md +5 -2
- 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/039_newcomer_friendly_column.sql +5 -0
- package/migrations/102_remove_box_first_provision_approval.sql +4 -0
- package/migrations/core_192_github_oauth_tokens.sql +33 -0
- 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/my-projects.html +78 -0
- package/modules/hall-ui/public/my-projects.js +142 -0
- 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/scouting.html +45 -0
- package/modules/hall-ui/public/scouting.js +73 -0
- 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/modules/status-ui/public/explore.html +54 -0
- package/modules/status-ui/public/explore.js +67 -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 +252 -62
- 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 -21
- 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/lint-migrations.js +102 -0
- package/scripts/gds/login.js +58 -11
- package/scripts/gds/mirror-redact.js +5 -0
- package/scripts/gds/package-core.js +263 -4
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +268 -19
- package/scripts/gds/publish-manifest.js +22 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +42 -16
- 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 +72 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +433 -59
- 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 +271 -7
- package/scripts/migrate.sh +10 -0
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +809 -107
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +105 -0
- package/src/bongos/github-repos.js +93 -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 +312 -13
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/github.js +90 -0
- package/src/bongos/routes/instance.js +15 -3
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/routes.js +2 -0
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +49 -2
- package/src/instance-config.js +46 -0
- package/src/module-api.js +140 -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 +41 -23
- 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/github_repos_helpers.mjs +91 -0
- package/tests/github_token_store.mjs +121 -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 +198 -0
- package/tests/idp_federation.mjs +100 -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/migrations_forward_safe.mjs +86 -0
- package/tests/module_api.mjs +1 -1
- 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/package_core.mjs +246 -1
- 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 +289 -7
- package/tests/pruned_lockfile_integrity.mjs +79 -0
- 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 +140 -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 +393 -5
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
- package/.gitignore +0 -91
- package/docs/adr/0139-platform-history-migration-to-cloudbongos.md +0 -94
- package/docs/adr/0140-cloudbongos-standalone-reprovision.md +0 -43
- package/scripts/hall-preview/fixtures/access-requests.json +0 -20
- package/scripts/hall-preview/fixtures/achievements.json +0 -76
- package/scripts/hall-preview/fixtures/analytics__builder.json +0 -251
- package/scripts/hall-preview/fixtures/audit-log.json +0 -68
- package/scripts/hall-preview/fixtures/blockers__detail.json +0 -12
- package/scripts/hall-preview/fixtures/box__me.json +0 -5
- package/scripts/hall-preview/fixtures/boxes__cost-ledger.json +0 -69
- package/scripts/hall-preview/fixtures/builders__active-claims.json +0 -1
- package/scripts/hall-preview/fixtures/builders__directory.json +0 -81
- package/scripts/hall-preview/fixtures/builders__roster.json +0 -48
- package/scripts/hall-preview/fixtures/goals.json +0 -502
- package/scripts/hall-preview/fixtures/goals__25.json +0 -96
- package/scripts/hall-preview/fixtures/goals__26.json +0 -67
- package/scripts/hall-preview/fixtures/goals__3.json +0 -103
- package/scripts/hall-preview/fixtures/goals__35.json +0 -49
- package/scripts/hall-preview/fixtures/goals__conflicts.json +0 -154
- package/scripts/hall-preview/fixtures/goals__detail.json +0 -96
- package/scripts/hall-preview/fixtures/goals__inbox.json +0 -5
- package/scripts/hall-preview/fixtures/goals__requests.json +0 -3
- package/scripts/hall-preview/fixtures/grades__by-builder.json +0 -32
- package/scripts/hall-preview/fixtures/inbox.json +0 -330
- package/scripts/hall-preview/fixtures/inbox__detail.json +0 -15
- package/scripts/hall-preview/fixtures/instance.json +0 -124
- package/scripts/hall-preview/fixtures/leaderboard.json +0 -184
- package/scripts/hall-preview/fixtures/me.json +0 -476
- package/scripts/hall-preview/fixtures/me__art-key__own.json +0 -6
- package/scripts/hall-preview/fixtures/me__event-sound-prefs.json +0 -209
- package/scripts/hall-preview/fixtures/me__recent-ships.json +0 -304
- package/scripts/hall-preview/fixtures/me__render.json +0 -5
- package/scripts/hall-preview/fixtures/me__skill-prefs.json +0 -143
- package/scripts/hall-preview/fixtures/me__sound-prefs.json +0 -67
- package/scripts/hall-preview/fixtures/me__wandering.json +0 -43
- package/scripts/hall-preview/fixtures/override-requests.json +0 -15
- package/scripts/hall-preview/fixtures/provisioning__fleet.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__fleet__cost-ledger.json +0 -1
- package/scripts/hall-preview/fixtures/provisioning__instances.json +0 -1
- package/scripts/hall-preview/fixtures/public__analytics__ecosystem.json +0 -516
- package/scripts/hall-preview/fixtures/public__bounty-table.json +0 -37
- package/scripts/hall-preview/fixtures/public__cost-summary.json +0 -742
- package/scripts/hall-preview/fixtures/public__estimation-drift.json +0 -41
- package/scripts/hall-preview/fixtures/public__grades.json +0 -50
- package/scripts/hall-preview/fixtures/public__progress.json +0 -124
- package/scripts/hall-preview/fixtures/public__recent-shipped.json +0 -426
- package/scripts/hall-preview/fixtures/public__repo-health.json +0 -160
- package/scripts/hall-preview/fixtures/public__repo-info.json +0 -5
- package/scripts/hall-preview/fixtures/security__docs.json +0 -10
- package/scripts/hall-preview/fixtures/security__reports.json +0 -31
- package/scripts/hall-preview/fixtures/sessions__search.json +0 -9
- package/scripts/hall-preview/fixtures/tasks.json +0 -2420
- package/scripts/hall-preview/fixtures/tasks__claimable.json +0 -2555
- package/scripts/hall-preview/fixtures/tasks__detail.json +0 -111
- package/scripts/hall-preview/fixtures/tasks__peer-votes__feed.json +0 -330
- package/scripts/hall-preview/fixtures/versions.json +0 -89
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
-- core_195_idea_triage_provenance.sql — give a triage VERDICT somewhere to
|
|
2
|
+
-- record what it decided (task 1002447).
|
|
3
|
+
--
|
|
4
|
+
-- Triage has three verbs; only two of them had storage. `status` records THAT an
|
|
5
|
+
-- idea was discarded or merged, and `promoted_to_task_id` records the task a
|
|
6
|
+
-- promotion produced — but nothing recorded WHICH idea a merge folded into, or
|
|
7
|
+
-- WHY a discard happened. `promoted_to_task_id` cannot stand in for the merge
|
|
8
|
+
-- target: it is `REFERENCES tasks(id)`, so writing an idea id there either trips
|
|
9
|
+
-- the FK or silently points at an unrelated task. BOTH callers that documented
|
|
10
|
+
-- or performed that write are corrected alongside this migration — the
|
|
11
|
+
-- /idea-triage skill doc and the interactive walker `scripts/gds/triage.js`.
|
|
12
|
+
-- Without a target, "merge" is just "discard" wearing a different label.
|
|
13
|
+
--
|
|
14
|
+
-- CORE, not modules/ideas/migrations/, even though the ideas module owns every
|
|
15
|
+
-- query against this table: `idea_inbox` is a CORE table, and a module migration
|
|
16
|
+
-- may only touch its own <key>_-prefixed objects — `fitness.js`
|
|
17
|
+
-- checkModuleMigrationsAdditive hard-fails an `ALTER TABLE idea_inbox` from a
|
|
18
|
+
-- module (ADR 0083 §Decision #5). The module's "new ideas migration goes in
|
|
19
|
+
-- modules/ideas/migrations/" note applies to objects the module OWNS, and it
|
|
20
|
+
-- owns none.
|
|
21
|
+
--
|
|
22
|
+
-- Two additive, nullable columns. Forward-safe by construction: every existing
|
|
23
|
+
-- reader SELECTs an explicit column list, so old code on the new schema neither
|
|
24
|
+
-- sees nor needs these (tests/migrations_forward_safe.mjs).
|
|
25
|
+
|
|
26
|
+
BEGIN;
|
|
27
|
+
|
|
28
|
+
ALTER TABLE idea_inbox
|
|
29
|
+
ADD COLUMN IF NOT EXISTS merged_into_idea_id bigint REFERENCES idea_inbox(id);
|
|
30
|
+
|
|
31
|
+
ALTER TABLE idea_inbox
|
|
32
|
+
ADD COLUMN IF NOT EXISTS triage_note text;
|
|
33
|
+
|
|
34
|
+
-- A target exists IFF the idea is merged. Without this, two ordinary PATCHes
|
|
35
|
+
-- build a merge CHAIN: set A's target while leaving A open, then merge C into A
|
|
36
|
+
-- — whose status still reads 'open', so a status-based no-chains check waves it
|
|
37
|
+
-- through. One direction only (`target IS NULL OR status='merged'`), because
|
|
38
|
+
-- every pre-existing merged row has a NULL target and must stay valid.
|
|
39
|
+
-- CHECK constraints have no ADD ... IF NOT EXISTS, so guard the add (the
|
|
40
|
+
-- migration 042 idiom) and keep the file replay-safe.
|
|
41
|
+
DO $$
|
|
42
|
+
BEGIN
|
|
43
|
+
IF NOT EXISTS (
|
|
44
|
+
SELECT 1 FROM pg_constraint
|
|
45
|
+
WHERE conrelid = 'idea_inbox'::regclass
|
|
46
|
+
AND conname = 'idea_inbox_merge_target_needs_merged_status'
|
|
47
|
+
) THEN
|
|
48
|
+
ALTER TABLE idea_inbox
|
|
49
|
+
ADD CONSTRAINT idea_inbox_merge_target_needs_merged_status
|
|
50
|
+
CHECK (merged_into_idea_id IS NULL OR status = 'merged');
|
|
51
|
+
END IF;
|
|
52
|
+
END $$;
|
|
53
|
+
|
|
54
|
+
INSERT INTO schema_migrations (version) VALUES ('core_195_idea_triage_provenance')
|
|
55
|
+
ON CONFLICT DO NOTHING;
|
|
56
|
+
|
|
57
|
+
COMMIT;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
-- core_196_session_reward_topup.sql
|
|
2
|
+
-- Let a multi-ship session be paid for ALL of its cost, not just its first ship.
|
|
3
|
+
--
|
|
4
|
+
-- WHY THIS IS A CORE MIGRATION, NOT modules/economy/. credit_log is a CORE table — the
|
|
5
|
+
-- economy module owns the queries against it, not the table itself (ADR 0083 §Decision #5;
|
|
6
|
+
-- modules/economy/CLAUDE.md "Tables: None owned"). A module migration may only touch its own
|
|
7
|
+
-- economy_-namespaced objects, so this shipped as economy_002_* and reddened the fitness gate
|
|
8
|
+
-- three ways (destructive DDL + index + alter on a non-namespaced table). The real defect was
|
|
9
|
+
-- placement, not DDL: as a module migration these columns only existed when the economy module
|
|
10
|
+
-- happened to be ENABLED, which makes core schema depend on module enablement — the exact
|
|
11
|
+
-- coupling the rule forbids. Relocated per the core_195 precedent (task 1002447).
|
|
12
|
+
--
|
|
13
|
+
-- schema_migrations is stem-keyed, so this re-applies once under its new stem. That is safe
|
|
14
|
+
-- BECAUSE every statement below is IF [NOT] EXISTS — the re-run is a no-op on any database
|
|
15
|
+
-- that already had it as economy_002.
|
|
16
|
+
--
|
|
17
|
+
-- THE DEFECT. Migration 091 made the cost-plus session reward idempotent with a
|
|
18
|
+
-- PARTIAL UNIQUE INDEX on credit_log (session_id) WHERE reason='session.token_reward'
|
|
19
|
+
-- — "the hard DB-level backstop against a double payout". That is correct and stays.
|
|
20
|
+
-- But it was the ONLY control, so once a session booked its first reward row, every
|
|
21
|
+
-- later ship in the same session was a hard no-op. A session's cost keeps growing
|
|
22
|
+
-- after its first ship, and that increment had nowhere to land.
|
|
23
|
+
--
|
|
24
|
+
-- Measured: three tasks shipped from one session, priced $14.30 / $27.76 / $40.32,
|
|
25
|
+
-- paid 17 drachmae where the whole session was worth round(40.32 × 1.20) = 48.
|
|
26
|
+
-- Roughly 65% of real, shipped, graded work went unrewarded. Multi-ship sessions are
|
|
27
|
+
-- normal — one claim at a time, but ship -> new worktree -> claim again.
|
|
28
|
+
--
|
|
29
|
+
-- THE FIX is watermark accounting: each landed ship books the DIFFERENCE between what
|
|
30
|
+
-- the whole session is now worth and what it has already been paid. That needs more
|
|
31
|
+
-- than one row per session, so uniqueness moves from (session_id) to
|
|
32
|
+
-- (session_id, reward_basis) — still scoped to the same reason.
|
|
33
|
+
--
|
|
34
|
+
-- WHY A CUMULATIVE BASIS AND NOT THE DELTA. Two concurrent uploads at the SAME cost
|
|
35
|
+
-- level must still collide and DO NOTHING. reward_basis is the CUMULATIVE owed total
|
|
36
|
+
-- at the moment of the row, so it is monotonic and identical for a racing pair. A bare
|
|
37
|
+
-- delta would be wrong: two genuine increments that happened to be equal would
|
|
38
|
+
-- collide and one payment would be silently lost.
|
|
39
|
+
--
|
|
40
|
+
-- Additive and non-destructive. No historical row changes value.
|
|
41
|
+
|
|
42
|
+
-- (a) The cumulative cost-plus total this session was owed when the row was written.
|
|
43
|
+
-- NOT NULL DEFAULT 0 (rather than nullable) so the unique index below has clean
|
|
44
|
+
-- semantics: Postgres treats NULLs as DISTINCT in a unique index, so a nullable
|
|
45
|
+
-- basis would let two legacy rows share a session_id and would WEAKEN the very
|
|
46
|
+
-- backstop 091 added.
|
|
47
|
+
--
|
|
48
|
+
-- Existing rows therefore land on reward_basis = 0, which is the marker for
|
|
49
|
+
-- "booked before top-ups existed". The application refuses to top those up: doing
|
|
50
|
+
-- so would be an uncontrolled retroactive payout across every historical session.
|
|
51
|
+
-- Reconstructing what past sessions are owed is a deliberate, owner-signed-off
|
|
52
|
+
-- backfill, tracked separately.
|
|
53
|
+
ALTER TABLE credit_log
|
|
54
|
+
ADD COLUMN IF NOT EXISTS reward_basis integer NOT NULL DEFAULT 0;
|
|
55
|
+
|
|
56
|
+
-- (b) Swap the uniqueness key. CREATE the replacement BEFORE dropping the original so
|
|
57
|
+
-- the double-pay backstop is never absent, not even inside this transaction.
|
|
58
|
+
--
|
|
59
|
+
-- For legacy rows (all at basis 0) the new index is exactly as strict as the old
|
|
60
|
+
-- one — (session_id, 0) is unique per session — so nothing already paid can be
|
|
61
|
+
-- paid twice.
|
|
62
|
+
CREATE UNIQUE INDEX IF NOT EXISTS uniq_credit_session_token_reward_basis
|
|
63
|
+
ON credit_log (session_id, reward_basis)
|
|
64
|
+
WHERE reason = 'session.token_reward';
|
|
65
|
+
|
|
66
|
+
DROP INDEX IF EXISTS uniq_credit_session_token_reward;
|
|
67
|
+
|
|
68
|
+
-- Counter hygiene: as a core migration this consumes core number 196 (next free after
|
|
69
|
+
-- core_195), so the core allocator floor advances with it.
|
|
@@ -8,10 +8,12 @@
|
|
|
8
8
|
// contributes.pollers:["routine-timers"]; the loader (src/module-loader/loader.js
|
|
9
9
|
// startModulePollers) requires THIS file and calls start(deps) at boot, stop() at
|
|
10
10
|
// shutdown — the module-agnostic successor to the hardcoded `routineTimers.start()`
|
|
11
|
-
// block server.js used to carry. server.js
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
11
|
+
// block server.js used to carry. src/platform-server.js is now the ONLY caller of
|
|
12
|
+
// startModulePollers (task 1002419) — server.js, the retired game/droplet
|
|
13
|
+
// entrypoint, was the previous one, and while it was gone NO module poller ran at
|
|
14
|
+
// all. It runs as a single process (docker-entrypoint.sh `exec`s it, no cluster),
|
|
15
|
+
// so a second always-on instance on the same DB still can't double-run — and the
|
|
16
|
+
// FOR UPDATE row lock in tallyPeerVotes is the hard guard if one ever does.
|
|
15
17
|
//
|
|
16
18
|
// WHY THIS EXISTS
|
|
17
19
|
// ADR 0078 demoted peer-vote-tally to `mode: deterministic` and built an EXTERNAL
|
|
@@ -100,10 +102,9 @@ async function runPeerVoteTally(deps = {}) {
|
|
|
100
102
|
|
|
101
103
|
// Start the timer(s). Idempotent. Kill-switched by ROUTINE_TIMERS_DISABLED=1.
|
|
102
104
|
// Unref'd so it never holds the process open on a systemd restart. Started by the
|
|
103
|
-
// loader's startModulePollers
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
// hard guard if one ever does).
|
|
105
|
+
// loader's startModulePollers from src/platform-server.js — the single always-on
|
|
106
|
+
// entrypoint (task 1002419) — so a second instance on the same DB can't double-run
|
|
107
|
+
// (and the FOR UPDATE row lock in tallyPeerVotes is the hard guard if one ever does).
|
|
107
108
|
function start(deps = {}) {
|
|
108
109
|
if (process.env.ROUTINE_TIMERS_DISABLED === '1') {
|
|
109
110
|
console.log('[routine-timers] disabled (ROUTINE_TIMERS_DISABLED=1)');
|
|
@@ -18,13 +18,14 @@
|
|
|
18
18
|
// blob; future behavior knobs add their own reserved keys. A missing key falls
|
|
19
19
|
// back to the rank-derived default below, so the column stays sparse.
|
|
20
20
|
//
|
|
21
|
-
// Resolution = default-by-
|
|
21
|
+
// Resolution = default-by-role, capped-by-role, server-enforced (see the ROLE
|
|
22
|
+
// METADATA block below for why a ceiling is metadata and not a permission).
|
|
22
23
|
// - DEFAULT level rides the trust ladder (Xenos→Locked … Archon→Exploratory).
|
|
23
|
-
// -
|
|
24
|
-
//
|
|
25
|
-
//
|
|
24
|
+
// - CEILING per role: Xenos and Thetes are capped at their default (they may
|
|
25
|
+
// dial DOWN for more focus, never UP); Metic and Archon may set any level
|
|
26
|
+
// (their rank is a starting default, not a ceiling). Owner decision
|
|
26
27
|
// 2026-06-19: "Thetes is gated; Metic is just a starting default."
|
|
27
|
-
// - effectiveLevel = clamp(override ?? default, 0,
|
|
28
|
+
// - effectiveLevel = clamp(override ?? default, 0, ceiling). So a Xenos who
|
|
28
29
|
// stores Exploratory is still resolved to Locked — opening the setting up
|
|
29
30
|
// for self-service later stays safe because the clamp is the enforcement,
|
|
30
31
|
// not the UI.
|
|
@@ -98,15 +99,54 @@ const LEVEL_CONTRACT = Object.freeze({
|
|
|
98
99
|
+ 'investigations worth the user\'s attention as you go, while still landing the claimed task.',
|
|
99
100
|
});
|
|
100
101
|
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
102
|
+
// ---- the wandering ceiling as ROLE METADATA (BV1.R107, ADR 0151) ----
|
|
103
|
+
//
|
|
104
|
+
// The governance model makes a permission a BOOLEAN atom and a rank a seeded
|
|
105
|
+
// ROLE. A wandering ceiling is neither: it is a NUMBER in 0..3. Spelling it as
|
|
106
|
+
// permissions would need one catalog key per level (`me.wandering.level.0..3`) —
|
|
107
|
+
// four keys no route consumes individually, against the catalog's first rule
|
|
108
|
+
// ("a permission no route consumes does not exist") and multiplying the key count
|
|
109
|
+
// to encode a quantity that was never a yes/no capability.
|
|
110
|
+
//
|
|
111
|
+
// So the ceiling is ROLE METADATA: a property carried BY the role, alongside —
|
|
112
|
+
// not inside — the permission that authorizes the write. The two compose:
|
|
113
|
+
// • the PERMISSION `me.wandering.set` (catalog: floor xenos, scope 'own',
|
|
114
|
+
// resource 'builder_prefs') answers MAY this builder set their OWN level. It
|
|
115
|
+
// is ownership-scoped, so it is answered by resolver.authorizeOwned(), never
|
|
116
|
+
// by an unconditional "do they hold it" — that is the route gate's business.
|
|
117
|
+
// • the ROLE METADATA below answers HOW HIGH they may set it. Holding the
|
|
118
|
+
// permission is never sufficient on its own: effectiveLevel() clamps to the
|
|
119
|
+
// role's ceiling server-side, so the ceiling is enforcement, not UI copy.
|
|
120
|
+
//
|
|
121
|
+
// Keyed by ROLE KEY. The four seeded rank-roles are named for the ranks they seed
|
|
122
|
+
// (governance_002), so a role key and a rank string coincide — which is why the
|
|
123
|
+
// rank-keyed helpers below still resolve correctly, and why BV1.R108 can fold this
|
|
124
|
+
// table onto the one live ladder without changing a value. A role this table does
|
|
125
|
+
// not name carries NO wandering metadata and resolves to the safest floor
|
|
126
|
+
// (Locked), the same fail-closed posture the resolver takes for a rank with no
|
|
127
|
+
// seeded role.
|
|
128
|
+
const ROLE_WANDERING = Object.freeze({
|
|
129
|
+
xenos: Object.freeze({ default: 0, ceiling: 0 }),
|
|
130
|
+
thetes: Object.freeze({ default: 1, ceiling: 1 }),
|
|
131
|
+
metic: Object.freeze({ default: 2, ceiling: 3 }),
|
|
132
|
+
archon: Object.freeze({ default: 3, ceiling: 3 }),
|
|
133
|
+
});
|
|
134
|
+
const NO_ROLE_WANDERING = Object.freeze({ default: 0, ceiling: 0 });
|
|
135
|
+
|
|
136
|
+
function roleWandering(roleKey) {
|
|
137
|
+
return Object.prototype.hasOwnProperty.call(ROLE_WANDERING, roleKey)
|
|
138
|
+
? ROLE_WANDERING[roleKey]
|
|
139
|
+
: NO_ROLE_WANDERING;
|
|
140
|
+
}
|
|
106
141
|
|
|
107
|
-
//
|
|
108
|
-
//
|
|
109
|
-
const
|
|
142
|
+
// DERIVED projections of the metadata above, kept because scripts/gds/session-eval.js
|
|
143
|
+
// and the /me payload read them by rank. Deriving them means the two views cannot drift.
|
|
144
|
+
const RANK_DEFAULT = Object.freeze(
|
|
145
|
+
Object.fromEntries(Object.entries(ROLE_WANDERING).map(([role, meta]) => [role, meta.default])),
|
|
146
|
+
);
|
|
147
|
+
const RANK_MAX = Object.freeze(
|
|
148
|
+
Object.fromEntries(Object.entries(ROLE_WANDERING).map(([role, meta]) => [role, meta.ceiling])),
|
|
149
|
+
);
|
|
110
150
|
|
|
111
151
|
const TOP_LEVEL = LEVELS.length - 1; // 3
|
|
112
152
|
const RESERVED_KEY = 'wandering';
|
|
@@ -120,9 +160,10 @@ for (const key of LEVELS) {
|
|
|
120
160
|
for (let i = 0; i < LEVELS.length; i++) {
|
|
121
161
|
if (LEVEL_META[LEVELS[i]].n !== i) throw new Error(`wandering-prefs: LEVEL_META.${LEVELS[i]}.n must equal its index ${i}`);
|
|
122
162
|
}
|
|
123
|
-
for (const
|
|
124
|
-
if (!(
|
|
125
|
-
if (
|
|
163
|
+
for (const [role, meta] of Object.entries(ROLE_WANDERING)) {
|
|
164
|
+
if (!isLevelInt(meta.default)) throw new Error(`wandering-prefs: role '${role}' has a non-level default`);
|
|
165
|
+
if (!isLevelInt(meta.ceiling)) throw new Error(`wandering-prefs: role '${role}' has a non-level ceiling`);
|
|
166
|
+
if (meta.default > meta.ceiling) throw new Error(`wandering-prefs: default > ceiling for role '${role}'`);
|
|
126
167
|
}
|
|
127
168
|
|
|
128
169
|
// ---- pure helpers ----
|
|
@@ -141,11 +182,11 @@ function levelIndex(key) {
|
|
|
141
182
|
}
|
|
142
183
|
|
|
143
184
|
function defaultLevelForRank(rank) {
|
|
144
|
-
return
|
|
185
|
+
return roleWandering(rank).default;
|
|
145
186
|
}
|
|
146
187
|
|
|
147
188
|
function maxLevelForRank(rank) {
|
|
148
|
-
return
|
|
189
|
+
return roleWandering(rank).ceiling;
|
|
149
190
|
}
|
|
150
191
|
|
|
151
192
|
// The override a builder has stored (the raw 'wandering' key off behavior_prefs),
|
|
@@ -262,6 +303,8 @@ module.exports = {
|
|
|
262
303
|
LEVELS,
|
|
263
304
|
LEVEL_META,
|
|
264
305
|
LEVEL_CONTRACT,
|
|
306
|
+
ROLE_WANDERING,
|
|
307
|
+
roleWandering,
|
|
265
308
|
RANK_DEFAULT,
|
|
266
309
|
RANK_MAX,
|
|
267
310
|
TOP_LEVEL,
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# OTB Dev Box — the desktop switch for a builder's hosted box
|
|
2
|
+
|
|
3
|
+
A menu-bar (macOS) / system-tray (Windows) app that turns a builder's hosted
|
|
4
|
+
dev box on and off with one switch. Built for the newcomer who should never
|
|
5
|
+
see a terminal: sign in with browser clicks (app pairing, ADR 0045), flip the
|
|
6
|
+
switch, and connect from Claude Desktop or the browser terminal.
|
|
7
|
+
|
|
8
|
+
What it does beyond the switch — each fixing a failure observed in the
|
|
9
|
+
2026-06-10 onboarding sessions that killed the old `curl | bash` flow:
|
|
10
|
+
|
|
11
|
+
- **Pairing sign-in** — opens the builders' hall, one Approve click, token
|
|
12
|
+
delivered over the app's own secret channel (`/api/gds/auth/app-pair/*`).
|
|
13
|
+
The session is written to `~/.config/otb/gds-session.json`, the same file
|
|
14
|
+
the `/builder-*` CLI uses — pairing the app signs in the CLI too.
|
|
15
|
+
- **SSH key done right** — generates `~/.ssh/otb_builder` (ed25519) with real
|
|
16
|
+
600 perms (icacls ACL on Windows) and registers it via `POST /box/ssh-key`.
|
|
17
|
+
- **known_hosts kept fresh** — Claude Desktop's embedded ssh2 reads ONLY
|
|
18
|
+
`~/.ssh/known_hosts`, and host keys change on every re-provision; the app
|
|
19
|
+
re-scans and replaces entries each time the box turns active.
|
|
20
|
+
- **Claude Desktop connection file** — installs the managed-settings file to
|
|
21
|
+
the system path with a native elevation prompt (osascript / UAC), the step
|
|
22
|
+
the agent-driven flow could never finish.
|
|
23
|
+
- **Native notifications** — box ready, rested while idle.
|
|
24
|
+
- **No heartbeats** — the app never sends `claude_active`; the droplet's idle
|
|
25
|
+
sweep stays authoritative so an unused box still reclaims itself (~10 min).
|
|
26
|
+
|
|
27
|
+
## Layout
|
|
28
|
+
|
|
29
|
+
- `src/main.js` — Electron main: tray, window, IPC, wiring.
|
|
30
|
+
- `src/brand.js` — every brand string (ADR 0004: the civ name lives here only).
|
|
31
|
+
- `src/{pairing,box-controller,gds-client,session-store,ssh-setup,updater}.js` — the
|
|
32
|
+
logic, Electron-free and unit-tested in `test/unit.mjs`.
|
|
33
|
+
- `src/updater.js` — in-app update check (#924): semver compare + platform→target
|
|
34
|
+
resolution. `main.js` polls `GET /downloads/devbox/version` on open + every 12h
|
|
35
|
+
and, when the published release is strictly newer, shows the Update banner; the
|
|
36
|
+
Update button downloads the installer and hands it to the OS (browser-download
|
|
37
|
+
fallback on any error). **Bumping `config/devbox-app.json` version is what makes
|
|
38
|
+
the banner appear in the field** — see [`docs/recipes/devbox-app-release.md`](../docs/recipes/devbox-app-release.md).
|
|
39
|
+
- `renderer/` — the panel UI (sandboxed, contextIsolated; preload exposes
|
|
40
|
+
named verbs only).
|
|
41
|
+
- `scripts/gen-icons.js` — regenerates all icons from pixel grids (`npm run
|
|
42
|
+
icons`; borrows `sharp` from the repo root's node_modules).
|
|
43
|
+
|
|
44
|
+
## Develop
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
cd devbox-app
|
|
48
|
+
npm install
|
|
49
|
+
npm test # hermetic unit tests, no Electron needed
|
|
50
|
+
npm start # run against prod (or OTB_DEVBOX_API_BASE=http://… npm start)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Dev/test env hooks: `OTB_DEVBOX_API_BASE` (point at a stub/staging GDS),
|
|
54
|
+
`OTB_DEVBOX_NO_ELEVATE=1` (skip the managed-settings elevation prompt),
|
|
55
|
+
`OTB_DEVBOX_SCREENSHOT_DIR` (write a PNG of the panel on every state change —
|
|
56
|
+
how CI/verification captures proof).
|
|
57
|
+
|
|
58
|
+
## Build & release
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
npm run dist:mac # dmg, arm64 + x64 (unsigned → ad-hoc)
|
|
62
|
+
npm run dist:win # NSIS one-click installer, x64 + arm64
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
CI builds both on every push that touches `devbox-app/**`
|
|
66
|
+
(`.github/workflows/build-devbox-app.yml`) and uploads them as artifacts.
|
|
67
|
+
Publishing = attach the artifacts to a release on the PUBLIC releases repo
|
|
68
|
+
named in `config/devbox-app.json`, then bump that config and ship — the
|
|
69
|
+
branded `/downloads/devbox/*` redirects follow the config. Full recipe:
|
|
70
|
+
`docs/recipes/devbox-app-release.md`.
|
|
71
|
+
|
|
72
|
+
This subproject is self-contained on purpose: its `package.json` never feeds
|
|
73
|
+
the droplet's `npm ci`, and nothing under `devbox-app/` runs server-side.
|
|
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
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# TEMPLATE — rendered to electron-builder.yml by scripts/gen-brand.js from
|
|
2
|
+
# config/branding.json (@@TOKENS@@ are substituted; ${version}/${arch}/${ext}
|
|
3
|
+
# are electron-builder's own and stay literal). Edit THIS file, never the
|
|
4
|
+
# generated electron-builder.yml.
|
|
5
|
+
#
|
|
6
|
+
# Brand strings (appId, productName, copyright, artifact slug) come from the
|
|
7
|
+
# instance branding so one source tree builds any instance's app (ADR 0004,
|
|
8
|
+
# generalised). Signing rides the instance's own CI (main-ref-gated Apple
|
|
9
|
+
# Developer ID; ADR 0072) — an unarmed build ad-hoc-signs the mac app, which
|
|
10
|
+
# arm64 requires to launch at all.
|
|
11
|
+
#
|
|
12
|
+
# Artifact names are pinned (no spaces, no world name inside — ADR 0004) and
|
|
13
|
+
# MUST match config/devbox-app.json's assets map; tests/devbox_downloads.mjs
|
|
14
|
+
# cross-checks the config side.
|
|
15
|
+
|
|
16
|
+
appId: @@APP_ID@@
|
|
17
|
+
productName: @@PRODUCT_NAME@@
|
|
18
|
+
copyright: @@COPYRIGHT@@
|
|
19
|
+
|
|
20
|
+
# buildResources is named build-resources (not electron-builder's default
|
|
21
|
+
# `build`) because the repo root .gitignore ignores build/ everywhere and the
|
|
22
|
+
# app icon in here is COMMITTED source, not output.
|
|
23
|
+
directories:
|
|
24
|
+
buildResources: build-resources
|
|
25
|
+
output: dist
|
|
26
|
+
|
|
27
|
+
files:
|
|
28
|
+
- src/**/*
|
|
29
|
+
- renderer/**/*
|
|
30
|
+
- assets/**/*
|
|
31
|
+
- package.json
|
|
32
|
+
|
|
33
|
+
asar: true
|
|
34
|
+
|
|
35
|
+
mac:
|
|
36
|
+
category: public.app-category.developer-tools
|
|
37
|
+
icon: build-resources/icon.png
|
|
38
|
+
target:
|
|
39
|
+
- target: dmg
|
|
40
|
+
arch: [arm64, x64]
|
|
41
|
+
artifactName: @@PRODUCT_SLUG@@-${version}-${arch}.${ext}
|
|
42
|
+
|
|
43
|
+
dmg:
|
|
44
|
+
artifactName: @@PRODUCT_SLUG@@-${version}-${arch}.${ext}
|
|
45
|
+
# On-brand install window (task 912). The backdrop
|
|
46
|
+
# (build-resources/dmg-background.png + @2x, drawn by
|
|
47
|
+
# scripts/gen-dmg-backdrop.py) guides the drag from the container (the app)
|
|
48
|
+
# into Applications. iconSize + window + the contents coords MUST match
|
|
49
|
+
# the icon-well centers the backdrop draws (LEFT 180,198 / RIGHT 480,198) — if
|
|
50
|
+
# you move one, re-run the generator. The volume icon is the app icon (the
|
|
51
|
+
# container). electron-builder auto-picks dmg-background@2x.png on Retina.
|
|
52
|
+
#
|
|
53
|
+
# task 1235: this electron-builder DMG is only an INTERMEDIATE. Its window
|
|
54
|
+
# backdrop does NOT render on macOS >= Sonoma — electron-builder writes a
|
|
55
|
+
# `pBBk` bookmark Finder can't resolve, so the install window shows up white.
|
|
56
|
+
# The CI step "Apply install-window backdrop (dmgbuild)" re-packages the
|
|
57
|
+
# signed+notarized .app out of this DMG into the FINAL DMG via dmgbuild
|
|
58
|
+
# (scripts/apply-dmg-backdrop.sh + scripts/dmgbuild-settings.py), which writes
|
|
59
|
+
# an alias-only .DS_Store that DOES render. Keep the coords below in sync with
|
|
60
|
+
# dmgbuild-settings.py (currently identical: 180,198 / 480,198, 660x400, size 100).
|
|
61
|
+
icon: build-resources/icon.png
|
|
62
|
+
background: build-resources/dmg-background.png
|
|
63
|
+
iconSize: 100
|
|
64
|
+
window:
|
|
65
|
+
width: 660
|
|
66
|
+
height: 400
|
|
67
|
+
contents:
|
|
68
|
+
- x: 180
|
|
69
|
+
y: 198
|
|
70
|
+
type: file
|
|
71
|
+
- x: 480
|
|
72
|
+
y: 198
|
|
73
|
+
type: link
|
|
74
|
+
path: /Applications
|
|
75
|
+
|
|
76
|
+
win:
|
|
77
|
+
icon: build-resources/icon.png
|
|
78
|
+
target:
|
|
79
|
+
- target: nsis
|
|
80
|
+
# x64 covers Intel/AMD PCs; arm64 is native for ARM Windows (Surface,
|
|
81
|
+
# Snapdragon laptops, Parallels VMs on Apple-silicon Macs). x64 also
|
|
82
|
+
# RUNS on arm64 via Windows emulation, so x64 stays the safe default
|
|
83
|
+
# link; arm64 is the first-class build for those machines (#908).
|
|
84
|
+
arch: [x64, arm64]
|
|
85
|
+
|
|
86
|
+
nsis:
|
|
87
|
+
oneClick: true
|
|
88
|
+
perMachine: false
|
|
89
|
+
runAfterFinish: true
|
|
90
|
+
deleteAppDataOnUninstall: false
|
|
91
|
+
# ${arch} suffix added in #908 when arm64 joined — two arches can't share
|
|
92
|
+
# one filename. Must stay in lockstep with config/devbox-app.json assets.
|
|
93
|
+
artifactName: @@PRODUCT_SLUG@@-Setup-${version}-${arch}.${ext}
|
|
94
|
+
|
|
95
|
+
publish: null
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# TEMPLATE — rendered to electron-builder.yml by scripts/gen-brand.js from
|
|
2
|
+
# config/branding.json (@@TOKENS@@ are substituted; ${version}/${arch}/${ext}
|
|
3
|
+
# are electron-builder's own and stay literal). Edit THIS file, never the
|
|
4
|
+
# generated electron-builder.yml.
|
|
5
|
+
#
|
|
6
|
+
# Brand strings (appId, productName, copyright, artifact slug) come from the
|
|
7
|
+
# instance branding so one source tree builds any instance's app (ADR 0004,
|
|
8
|
+
# generalised). Signing rides the instance's own CI (main-ref-gated Apple
|
|
9
|
+
# Developer ID; ADR 0072) — an unarmed build ad-hoc-signs the mac app, which
|
|
10
|
+
# arm64 requires to launch at all.
|
|
11
|
+
#
|
|
12
|
+
# Artifact names are pinned (no spaces, no world name inside — ADR 0004) and
|
|
13
|
+
# MUST match config/devbox-app.json's assets map; tests/devbox_downloads.mjs
|
|
14
|
+
# cross-checks the config side.
|
|
15
|
+
|
|
16
|
+
appId: com.cloudbongos.devbox
|
|
17
|
+
productName: Cloud Bongos Dev Box
|
|
18
|
+
copyright: © Cloud Bongos
|
|
19
|
+
|
|
20
|
+
# buildResources is named build-resources (not electron-builder's default
|
|
21
|
+
# `build`) because the repo root .gitignore ignores build/ everywhere and the
|
|
22
|
+
# app icon in here is COMMITTED source, not output.
|
|
23
|
+
directories:
|
|
24
|
+
buildResources: build-resources
|
|
25
|
+
output: dist
|
|
26
|
+
|
|
27
|
+
files:
|
|
28
|
+
- src/**/*
|
|
29
|
+
- renderer/**/*
|
|
30
|
+
- assets/**/*
|
|
31
|
+
- package.json
|
|
32
|
+
|
|
33
|
+
asar: true
|
|
34
|
+
|
|
35
|
+
mac:
|
|
36
|
+
category: public.app-category.developer-tools
|
|
37
|
+
icon: build-resources/icon.png
|
|
38
|
+
target:
|
|
39
|
+
- target: dmg
|
|
40
|
+
arch: [arm64, x64]
|
|
41
|
+
artifactName: Cloud-Bongos-Dev-Box-${version}-${arch}.${ext}
|
|
42
|
+
|
|
43
|
+
dmg:
|
|
44
|
+
artifactName: Cloud-Bongos-Dev-Box-${version}-${arch}.${ext}
|
|
45
|
+
# On-brand install window (task 912). The backdrop
|
|
46
|
+
# (build-resources/dmg-background.png + @2x, drawn by
|
|
47
|
+
# scripts/gen-dmg-backdrop.py) guides the drag from the container (the app)
|
|
48
|
+
# into Applications. iconSize + window + the contents coords MUST match
|
|
49
|
+
# the icon-well centers the backdrop draws (LEFT 180,198 / RIGHT 480,198) — if
|
|
50
|
+
# you move one, re-run the generator. The volume icon is the app icon (the
|
|
51
|
+
# container). electron-builder auto-picks dmg-background@2x.png on Retina.
|
|
52
|
+
#
|
|
53
|
+
# task 1235: this electron-builder DMG is only an INTERMEDIATE. Its window
|
|
54
|
+
# backdrop does NOT render on macOS >= Sonoma — electron-builder writes a
|
|
55
|
+
# `pBBk` bookmark Finder can't resolve, so the install window shows up white.
|
|
56
|
+
# The CI step "Apply install-window backdrop (dmgbuild)" re-packages the
|
|
57
|
+
# signed+notarized .app out of this DMG into the FINAL DMG via dmgbuild
|
|
58
|
+
# (scripts/apply-dmg-backdrop.sh + scripts/dmgbuild-settings.py), which writes
|
|
59
|
+
# an alias-only .DS_Store that DOES render. Keep the coords below in sync with
|
|
60
|
+
# dmgbuild-settings.py (currently identical: 180,198 / 480,198, 660x400, size 100).
|
|
61
|
+
icon: build-resources/icon.png
|
|
62
|
+
background: build-resources/dmg-background.png
|
|
63
|
+
iconSize: 100
|
|
64
|
+
window:
|
|
65
|
+
width: 660
|
|
66
|
+
height: 400
|
|
67
|
+
contents:
|
|
68
|
+
- x: 180
|
|
69
|
+
y: 198
|
|
70
|
+
type: file
|
|
71
|
+
- x: 480
|
|
72
|
+
y: 198
|
|
73
|
+
type: link
|
|
74
|
+
path: /Applications
|
|
75
|
+
|
|
76
|
+
win:
|
|
77
|
+
icon: build-resources/icon.png
|
|
78
|
+
target:
|
|
79
|
+
- target: nsis
|
|
80
|
+
# x64 covers Intel/AMD PCs; arm64 is native for ARM Windows (Surface,
|
|
81
|
+
# Snapdragon laptops, Parallels VMs on Apple-silicon Macs). x64 also
|
|
82
|
+
# RUNS on arm64 via Windows emulation, so x64 stays the safe default
|
|
83
|
+
# link; arm64 is the first-class build for those machines (#908).
|
|
84
|
+
arch: [x64, arm64]
|
|
85
|
+
|
|
86
|
+
nsis:
|
|
87
|
+
oneClick: true
|
|
88
|
+
perMachine: false
|
|
89
|
+
runAfterFinish: true
|
|
90
|
+
deleteAppDataOnUninstall: false
|
|
91
|
+
# ${arch} suffix added in #908 when arm64 joined — two arches can't share
|
|
92
|
+
# one filename. Must stay in lockstep with config/devbox-app.json assets.
|
|
93
|
+
artifactName: Cloud-Bongos-Dev-Box-Setup-${version}-${arch}.${ext}
|
|
94
|
+
|
|
95
|
+
publish: null
|