@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,425 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// /builder-sequence — walk a dependency chain end to end from one kickoff.
|
|
3
|
+
//
|
|
4
|
+
// Two-thirds of the open backlog is dependency-gated. Shipping a task
|
|
5
|
+
// auto-promotes its dependents backlog→ready (the promote_dependents_on_ship
|
|
6
|
+
// trigger, migrations 020/163) — but nothing picks the newly-unblocked task
|
|
7
|
+
// up, so an N-task chain needs N separate kickoffs. This is the plumbing that
|
|
8
|
+
// lets one session walk the whole chain: resolve scope → next → claim → build
|
|
9
|
+
// → ship → next.
|
|
10
|
+
//
|
|
11
|
+
// Calling forms:
|
|
12
|
+
// node scripts/gds/sequence.js --from 1002277 plan the chain below a task
|
|
13
|
+
// node scripts/gds/sequence.js --goal 1000045 plan a goal's open tasks
|
|
14
|
+
// node scripts/gds/sequence.js --from 1002277 --next the one task to claim now
|
|
15
|
+
// node scripts/gds/sequence.js --check 1002277 stop-reasons for one task
|
|
16
|
+
//
|
|
17
|
+
// Flags:
|
|
18
|
+
// --next emit only the next claimable task (or a named stop reason)
|
|
19
|
+
// --check <id> evaluate the halt matrix for one task
|
|
20
|
+
// --json machine-readable output
|
|
21
|
+
// --limit N cap the planned chain at N tasks (default 50)
|
|
22
|
+
//
|
|
23
|
+
// This script DECIDES NOTHING it cannot decide from facts. Every halt is a
|
|
24
|
+
// named reason a human can act on — see HALT_REASONS. The judgement half of
|
|
25
|
+
// the loop (actually building each task) lives in .claude/skills/builder-sequence.
|
|
26
|
+
|
|
27
|
+
const { cliClient, requireSession, fmtMinutes, hallBase } = require('./cli-lib');
|
|
28
|
+
const { matchProtected } = require('../../src/bongos/permission-path-check');
|
|
29
|
+
|
|
30
|
+
// Rank → tier, mirroring REQUIRES_RANK_TIER. Imported shape would drag the
|
|
31
|
+
// server's db.js (and its pool) into a CLI that only needs four integers.
|
|
32
|
+
const RANK_TIER = { xenos: 1, thetes: 2, metic: 3, archon: 4 };
|
|
33
|
+
|
|
34
|
+
// A task in one of these statuses is done as far as the chain is concerned —
|
|
35
|
+
// 'completed'/'confirmed' are mid-ship states, so the chain must not re-claim
|
|
36
|
+
// them, but they are also not yet 'shipped' so successors stay gated.
|
|
37
|
+
const TERMINAL = new Set(['shipped', 'abandoned']);
|
|
38
|
+
const IN_FLIGHT = new Set(['active', 'completed', 'confirmed']);
|
|
39
|
+
|
|
40
|
+
const DEFAULT_LIMIT = 50;
|
|
41
|
+
|
|
42
|
+
// A description shorter than this can't carry acceptance criteria worth
|
|
43
|
+
// verifying against, so the runner refuses to guess at what "done" means.
|
|
44
|
+
const MIN_SPEC_CHARS = 120;
|
|
45
|
+
|
|
46
|
+
// Kinds whose whole point is a judgement call the owner should see before it
|
|
47
|
+
// lands: a decision needs an ADR, a spike's output is a recommendation.
|
|
48
|
+
const JUDGEMENT_KINDS = new Set(['decision', 'spike']);
|
|
49
|
+
|
|
50
|
+
const HALT_REASONS = {
|
|
51
|
+
needs_migration: 'wants a schema migration — needs a reserved number and human eyes',
|
|
52
|
+
security_sensitive: 'flagged security-sensitive',
|
|
53
|
+
protected_path: 'touches the permission/deploy core',
|
|
54
|
+
rank_floor: 'requires a rank above yours',
|
|
55
|
+
thin_spec: 'has no acceptance criteria to verify against',
|
|
56
|
+
judgement_kind: 'is a judgement call (decision/spike), not mechanical work',
|
|
57
|
+
not_claimable: 'is ready but the server will not hand it over',
|
|
58
|
+
deps_unmet: 'is still waiting on a predecessor',
|
|
59
|
+
in_flight: 'is already mid-ship',
|
|
60
|
+
cycle: 'sits in a dependency cycle',
|
|
61
|
+
chain_complete: 'nothing left in this chain',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// -------------------------------------------------------------------------
|
|
65
|
+
// Argv
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
function readArgv(argv) {
|
|
69
|
+
const out = { from: null, goal: null, check: null, next: false, json: false, limit: DEFAULT_LIMIT };
|
|
70
|
+
for (let i = 0; i < argv.length; i++) {
|
|
71
|
+
const a = argv[i];
|
|
72
|
+
if (a === '--next') { out.next = true; continue; }
|
|
73
|
+
if (a === '--json') { out.json = true; continue; }
|
|
74
|
+
if (a === '--from') { out.from = Number(argv[++i]); continue; }
|
|
75
|
+
if (a.startsWith('--from=')) { out.from = Number(a.slice(7)); continue; }
|
|
76
|
+
if (a === '--goal') { out.goal = Number(argv[++i]); continue; }
|
|
77
|
+
if (a.startsWith('--goal=')) { out.goal = Number(a.slice(7)); continue; }
|
|
78
|
+
if (a === '--check') { out.check = Number(argv[++i]); continue; }
|
|
79
|
+
if (a.startsWith('--check=')) { out.check = Number(a.slice(8)); continue; }
|
|
80
|
+
if (a === '--limit') { out.limit = Number(argv[++i]); continue; }
|
|
81
|
+
if (a.startsWith('--limit=')) { out.limit = Number(a.slice(8)); continue; }
|
|
82
|
+
if (!out.from && /^\d+$/.test(a)) { out.from = Number(a); continue; }
|
|
83
|
+
}
|
|
84
|
+
return out;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// -------------------------------------------------------------------------
|
|
88
|
+
// Pure core (exported for tests)
|
|
89
|
+
// -------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
// Ids a task object points at, tolerating both the bare-number and the
|
|
92
|
+
// {id,kind} row shapes GET /tasks/:id has returned across schema versions.
|
|
93
|
+
// Ids must come out positive: Number(null) is 0, so a null edge would
|
|
94
|
+
// otherwise enter the graph as task 0 and be chased as a real dependency.
|
|
95
|
+
function edgeIds(list) {
|
|
96
|
+
return (list || [])
|
|
97
|
+
.map((d) => Number(d && typeof d === 'object' ? d.id : d))
|
|
98
|
+
.filter((n) => Number.isFinite(n) && n > 0);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Kahn's algorithm over edges BETWEEN the given tasks. An out-of-scope
|
|
102
|
+
// dependency doesn't block ordering the scope itself — it surfaces later as a
|
|
103
|
+
// deps_unmet halt, which is the honest place for it. Ties break on the same
|
|
104
|
+
// keys the claimable feed sorts by, so a plan reads in the order the board
|
|
105
|
+
// would have offered the work.
|
|
106
|
+
function topoOrder(tasks) {
|
|
107
|
+
const inScope = new Set(tasks.map((t) => Number(t.id)));
|
|
108
|
+
const byId = new Map(tasks.map((t) => [Number(t.id), t]));
|
|
109
|
+
const remaining = new Set(inScope);
|
|
110
|
+
const order = [];
|
|
111
|
+
|
|
112
|
+
const rank = (id) => {
|
|
113
|
+
const t = byId.get(id);
|
|
114
|
+
return [
|
|
115
|
+
t.priority == null ? 99 : Number(t.priority),
|
|
116
|
+
Number(t.est_minutes_calibrated ?? t.est_minutes ?? 0) || 0,
|
|
117
|
+
Number(t.id),
|
|
118
|
+
];
|
|
119
|
+
};
|
|
120
|
+
const cmp = (a, b) => {
|
|
121
|
+
const [pa, ma, ia] = rank(a);
|
|
122
|
+
const [pb, mb, ib] = rank(b);
|
|
123
|
+
return pa - pb || ma - mb || ia - ib;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
let progressed = true;
|
|
127
|
+
while (remaining.size && progressed) {
|
|
128
|
+
progressed = false;
|
|
129
|
+
const free = Array.from(remaining).filter((id) => {
|
|
130
|
+
const deps = edgeIds(byId.get(id).dependencies).filter((d) => remaining.has(d));
|
|
131
|
+
return deps.length === 0;
|
|
132
|
+
});
|
|
133
|
+
if (free.length === 0) break;
|
|
134
|
+
free.sort(cmp);
|
|
135
|
+
for (const id of free) {
|
|
136
|
+
order.push(byId.get(id));
|
|
137
|
+
remaining.delete(id);
|
|
138
|
+
progressed = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return { order, cycle: Array.from(remaining).sort((a, b) => a - b) };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The chain below a head: the head plus every task transitively reachable
|
|
146
|
+
// through dependents[]. Terminal tasks are walked THROUGH (a shipped task's
|
|
147
|
+
// successors are exactly what we want) but not included in the plan.
|
|
148
|
+
function chainFrom(headId, byId, { limit = DEFAULT_LIMIT } = {}) {
|
|
149
|
+
const seen = new Set();
|
|
150
|
+
const picked = [];
|
|
151
|
+
const queue = [Number(headId)];
|
|
152
|
+
while (queue.length) {
|
|
153
|
+
const id = queue.shift();
|
|
154
|
+
if (seen.has(id)) continue;
|
|
155
|
+
seen.add(id);
|
|
156
|
+
const t = byId.get(id);
|
|
157
|
+
if (!t) continue;
|
|
158
|
+
if (!TERMINAL.has(t.status)) picked.push(t);
|
|
159
|
+
for (const d of edgeIds(t.dependents)) queue.push(d);
|
|
160
|
+
if (picked.length >= limit) break;
|
|
161
|
+
}
|
|
162
|
+
return picked;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// The halt matrix. Facts only — every entry is something the runner can read
|
|
166
|
+
// off the task row, never a judgement about the work itself.
|
|
167
|
+
function haltReasons(task, { rank, claimableIds = null } = {}) {
|
|
168
|
+
const reasons = [];
|
|
169
|
+
if (!task) return [{ code: 'not_found', detail: 'no such task' }];
|
|
170
|
+
|
|
171
|
+
if (task.needs_migration) {
|
|
172
|
+
reasons.push({ code: 'needs_migration', detail: HALT_REASONS.needs_migration });
|
|
173
|
+
}
|
|
174
|
+
if (task.security_sensitive) {
|
|
175
|
+
reasons.push({ code: 'security_sensitive', detail: HALT_REASONS.security_sensitive });
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const hits = matchProtected(task.touches || []);
|
|
179
|
+
if (hits.length) {
|
|
180
|
+
reasons.push({
|
|
181
|
+
code: 'protected_path',
|
|
182
|
+
detail: `${HALT_REASONS.protected_path} (${hits.map((h) => h.glob).join(', ')})`,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const floor = RANK_TIER[task.requires_rank] ?? 0;
|
|
187
|
+
const mine = RANK_TIER[rank] ?? 0;
|
|
188
|
+
if (floor > mine) {
|
|
189
|
+
reasons.push({
|
|
190
|
+
code: 'rank_floor',
|
|
191
|
+
detail: `needs rank '${task.requires_rank}', you are '${rank || 'unknown'}'`,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (JUDGEMENT_KINDS.has(task.kind)) {
|
|
196
|
+
reasons.push({ code: 'judgement_kind', detail: `kind '${task.kind}' ${HALT_REASONS.judgement_kind}` });
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const spec = String(task.description || '').trim();
|
|
200
|
+
if (spec.length < MIN_SPEC_CHARS) {
|
|
201
|
+
reasons.push({
|
|
202
|
+
code: 'thin_spec',
|
|
203
|
+
detail: spec ? `description is ${spec.length} chars — too thin to verify against` : 'no description',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (IN_FLIGHT.has(task.status)) {
|
|
208
|
+
reasons.push({ code: 'in_flight', detail: `status is '${task.status}'` });
|
|
209
|
+
} else if (task.status !== 'ready') {
|
|
210
|
+
reasons.push({ code: 'deps_unmet', detail: `status is '${task.status}', not 'ready'` });
|
|
211
|
+
} else if (claimableIds && !claimableIds.has(Number(task.id))) {
|
|
212
|
+
// 'ready' but absent from the server's own claimable feed means an open
|
|
213
|
+
// blocker, a touches collision with a live claim, or a rank filter. Rather
|
|
214
|
+
// than re-deriving that query client-side (and drifting from it), defer.
|
|
215
|
+
reasons.push({ code: 'not_claimable', detail: HALT_REASONS.not_claimable });
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return reasons;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function summarize(tasks) {
|
|
222
|
+
return tasks.reduce(
|
|
223
|
+
(acc, t) => ({
|
|
224
|
+
count: acc.count + 1,
|
|
225
|
+
credits: acc.credits + (Number(t.credits_reward) || 0),
|
|
226
|
+
minutes: acc.minutes + (Number(t.est_minutes_calibrated ?? t.est_minutes) || 0),
|
|
227
|
+
}),
|
|
228
|
+
{ count: 0, credits: 0, minutes: 0 },
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// -------------------------------------------------------------------------
|
|
233
|
+
// Fetching
|
|
234
|
+
// -------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
async function fetchTask(api, id) {
|
|
237
|
+
const r = await api.tasks.getTasksId({ id });
|
|
238
|
+
if (!r.ok) return null;
|
|
239
|
+
return r.data.task || r.data;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Breadth-first over dependents[], fetching each task's detail once. The list
|
|
243
|
+
// endpoint omits the edge arrays, so the detail route is the only source.
|
|
244
|
+
async function fetchChain(api, headId, limit) {
|
|
245
|
+
const byId = new Map();
|
|
246
|
+
const queue = [Number(headId)];
|
|
247
|
+
const seen = new Set();
|
|
248
|
+
while (queue.length && byId.size < limit * 4) {
|
|
249
|
+
const wave = queue.splice(0, 12).filter((id) => !seen.has(id));
|
|
250
|
+
wave.forEach((id) => seen.add(id));
|
|
251
|
+
if (!wave.length) continue;
|
|
252
|
+
const rows = await Promise.all(wave.map((id) => fetchTask(api, id)));
|
|
253
|
+
for (const t of rows) {
|
|
254
|
+
if (!t) continue;
|
|
255
|
+
byId.set(Number(t.id), t);
|
|
256
|
+
for (const d of edgeIds(t.dependents)) if (!seen.has(d)) queue.push(d);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return byId;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function fetchGoalTasks(api, goalId, limit) {
|
|
263
|
+
const r = await api.tasks.getTasks({ query: { limit: 1000 } });
|
|
264
|
+
const rows = (r.ok ? rowsOf(r.data) : null) || [];
|
|
265
|
+
const mine = rows
|
|
266
|
+
.filter((t) => Number(t.goal_id) === Number(goalId) && !TERMINAL.has(t.status))
|
|
267
|
+
.slice(0, limit);
|
|
268
|
+
const byId = new Map();
|
|
269
|
+
for (let i = 0; i < mine.length; i += 12) {
|
|
270
|
+
const rowsD = await Promise.all(mine.slice(i, i + 12).map((t) => fetchTask(api, t.id)));
|
|
271
|
+
rowsD.forEach((t) => t && byId.set(Number(t.id), t));
|
|
272
|
+
}
|
|
273
|
+
return byId;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// GET /tasks/claimable returns its rows under `claimable`; GET /tasks under
|
|
277
|
+
// `tasks`. Returning null (not an empty Set) on an unreadable response matters:
|
|
278
|
+
// haltReasons treats null as "couldn't check" and stays quiet, where an empty
|
|
279
|
+
// Set would mark every task not_claimable and halt the runner on its first step.
|
|
280
|
+
function rowsOf(payload) {
|
|
281
|
+
if (Array.isArray(payload)) return payload;
|
|
282
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
283
|
+
for (const key of ['claimable', 'tasks', 'rows', 'items']) {
|
|
284
|
+
if (Array.isArray(payload[key])) return payload[key];
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
async function fetchClaimableIds(api) {
|
|
290
|
+
const r = await api.tasks.getTasksClaimable({ query: { limit: 500 } });
|
|
291
|
+
if (!r.ok) return null;
|
|
292
|
+
const rows = rowsOf(r.data);
|
|
293
|
+
return rows ? new Set(rows.map((t) => Number(t.id))) : null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// -------------------------------------------------------------------------
|
|
297
|
+
// Output
|
|
298
|
+
// -------------------------------------------------------------------------
|
|
299
|
+
|
|
300
|
+
function taskLine(t, i) {
|
|
301
|
+
const est = t.est_minutes_calibrated ?? t.est_minutes;
|
|
302
|
+
return ` ${String(i + 1).padStart(2)}. task ${t.id} · ${t.status} · ${t.credits_reward ?? 0}c · ${est ? fmtMinutes(est) : '—'}\n ${t.title}`;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function printPlan({ plan, cycle, scope, base }) {
|
|
306
|
+
if (!plan.length) {
|
|
307
|
+
console.log(`No open tasks in ${scope}. ${HALT_REASONS.chain_complete}.`);
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const tot = summarize(plan);
|
|
311
|
+
console.log(`Chain for ${scope} — ${tot.count} open task(s), ${tot.credits}c, ${fmtMinutes(tot.minutes)}\n`);
|
|
312
|
+
plan.forEach((t, i) => console.log(taskLine(t, i)));
|
|
313
|
+
if (cycle.length) {
|
|
314
|
+
console.log(`\n⚠ ${cycle.length} task(s) could not be ordered — dependency cycle: ${cycle.join(', ')}`);
|
|
315
|
+
}
|
|
316
|
+
console.log(`\nHall: ${base}#/task/${plan[0].id}`);
|
|
317
|
+
console.log(`Next: node scripts/gds/sequence.js --from ${plan[0].id} --next`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function printHalt(task, reasons) {
|
|
321
|
+
console.log(`HALT — task ${task.id}: ${task.title}\n`);
|
|
322
|
+
for (const r of reasons) console.log(` • [${r.code}] ${r.detail}`);
|
|
323
|
+
console.log('\nThis one needs a human. Everything already shipped stays shipped.');
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// -------------------------------------------------------------------------
|
|
327
|
+
// Main
|
|
328
|
+
// -------------------------------------------------------------------------
|
|
329
|
+
|
|
330
|
+
async function main() {
|
|
331
|
+
const opts = readArgv(process.argv.slice(2));
|
|
332
|
+
if (!opts.from && !opts.goal && !opts.check) {
|
|
333
|
+
console.error('usage: sequence.js --from <task-id> | --goal <goal-id> | --check <task-id> [--next] [--json]');
|
|
334
|
+
process.exit(2);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
await requireSession();
|
|
338
|
+
const api = await cliClient();
|
|
339
|
+
const meR = await api.me.getMe();
|
|
340
|
+
const rank = meR.ok ? meR.data.builder?.rank : null;
|
|
341
|
+
const base = hallBase();
|
|
342
|
+
|
|
343
|
+
// --check: one task, halt matrix, non-zero exit if anything fires.
|
|
344
|
+
if (opts.check) {
|
|
345
|
+
const t = await fetchTask(api, opts.check);
|
|
346
|
+
const claimableIds = await fetchClaimableIds(api);
|
|
347
|
+
const reasons = haltReasons(t, { rank, claimableIds });
|
|
348
|
+
if (opts.json) {
|
|
349
|
+
console.log(JSON.stringify({ task_id: opts.check, ok: reasons.length === 0, reasons }, null, 2));
|
|
350
|
+
} else if (!reasons.length) {
|
|
351
|
+
console.log(`task ${opts.check} is clear to run.`);
|
|
352
|
+
} else {
|
|
353
|
+
printHalt(t || { id: opts.check, title: '(not found)' }, reasons);
|
|
354
|
+
}
|
|
355
|
+
process.exit(reasons.length ? 1 : 0);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const byId = opts.goal
|
|
359
|
+
? await fetchGoalTasks(api, opts.goal, opts.limit)
|
|
360
|
+
: await fetchChain(api, opts.from, opts.limit);
|
|
361
|
+
|
|
362
|
+
const scope = opts.goal ? `goal ${opts.goal}` : `task ${opts.from}`;
|
|
363
|
+
const candidates = opts.goal
|
|
364
|
+
? Array.from(byId.values()).filter((t) => !TERMINAL.has(t.status))
|
|
365
|
+
: chainFrom(opts.from, byId, { limit: opts.limit });
|
|
366
|
+
|
|
367
|
+
const { order, cycle } = topoOrder(candidates);
|
|
368
|
+
const plan = order.slice(0, opts.limit);
|
|
369
|
+
|
|
370
|
+
// --next: the first task in the plan that clears the halt matrix.
|
|
371
|
+
if (opts.next) {
|
|
372
|
+
const claimableIds = await fetchClaimableIds(api);
|
|
373
|
+
const head = plan[0];
|
|
374
|
+
if (!head) {
|
|
375
|
+
const payload = { next: null, stop: 'chain_complete', detail: HALT_REASONS.chain_complete };
|
|
376
|
+
console.log(opts.json ? JSON.stringify(payload, null, 2) : `${HALT_REASONS.chain_complete} in ${scope}.`);
|
|
377
|
+
process.exit(0);
|
|
378
|
+
}
|
|
379
|
+
const reasons = haltReasons(head, { rank, claimableIds });
|
|
380
|
+
if (reasons.length) {
|
|
381
|
+
const payload = { next: null, stop: reasons[0].code, reasons, task: { id: head.id, title: head.title } };
|
|
382
|
+
if (opts.json) console.log(JSON.stringify(payload, null, 2));
|
|
383
|
+
else printHalt(head, reasons);
|
|
384
|
+
process.exit(1);
|
|
385
|
+
}
|
|
386
|
+
const remaining = summarize(plan);
|
|
387
|
+
if (opts.json) {
|
|
388
|
+
console.log(JSON.stringify({ next: head, remaining, scope }, null, 2));
|
|
389
|
+
} else {
|
|
390
|
+
console.log(`Next in ${scope}: task ${head.id} — ${head.title}`);
|
|
391
|
+
console.log(` ${head.credits_reward ?? 0}c · ${fmtMinutes(head.est_minutes_calibrated ?? head.est_minutes ?? 0)} · ${remaining.count - 1} more after this`);
|
|
392
|
+
console.log(` node scripts/gds/claim.js ${head.id}`);
|
|
393
|
+
}
|
|
394
|
+
process.exit(0);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (opts.json) {
|
|
398
|
+
console.log(JSON.stringify({ scope, plan, cycle, totals: summarize(plan) }, null, 2));
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
printPlan({ plan, cycle, scope, base });
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (require.main === module) {
|
|
405
|
+
main().catch((err) => {
|
|
406
|
+
console.error('fatal:', err.message || err);
|
|
407
|
+
process.exit(1);
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
module.exports = {
|
|
412
|
+
readArgv,
|
|
413
|
+
edgeIds,
|
|
414
|
+
rowsOf,
|
|
415
|
+
topoOrder,
|
|
416
|
+
chainFrom,
|
|
417
|
+
haltReasons,
|
|
418
|
+
summarize,
|
|
419
|
+
HALT_REASONS,
|
|
420
|
+
RANK_TIER,
|
|
421
|
+
MIN_SPEC_CHARS,
|
|
422
|
+
JUDGEMENT_KINDS,
|
|
423
|
+
TERMINAL,
|
|
424
|
+
IN_FLIGHT,
|
|
425
|
+
};
|
|
@@ -42,7 +42,16 @@ function encodeCwd(cwd) {
|
|
|
42
42
|
// grader transcript's first user message is the worker system prompt — match it
|
|
43
43
|
// to exclude it from the newest-file fallback. (The SessionEnd hook path is
|
|
44
44
|
// unaffected: it passes an explicit transcript_path and never calls this.)
|
|
45
|
-
|
|
45
|
+
// Match the SHAPE of the worker preamble, not a closed list of persona names: a
|
|
46
|
+
// new worker added to modules/grading/grader-workers/ would otherwise slip past
|
|
47
|
+
// this and be treated as a builder session — which now means real money, since
|
|
48
|
+
// backfill-sessions.js uses this to decide what NOT to pay for. Every worker
|
|
49
|
+
// opens `You are the **<Persona>** … adversarial subagent` (narc/hacker/efficiency
|
|
50
|
+
// say "a specialist adversarial subagent", quality says "specialist — an
|
|
51
|
+
// adversarial subagent"). Still anchored on that preamble rather than the bare
|
|
52
|
+
// phrase, so a real session that merely DISCUSSES adversarial subagents in its
|
|
53
|
+
// first message is not misread as one.
|
|
54
|
+
const GRADER_WORKER_SIGNATURE = /You are the \*\*[^*\n]{1,40}\*\*[^\n]{0,60}?adversarial subagent/;
|
|
46
55
|
|
|
47
56
|
function firstUserText(file) {
|
|
48
57
|
// Scan only until the first user message — avoids fully parsing a large
|
package/scripts/gds/setup.js
CHANGED
|
@@ -19,7 +19,18 @@
|
|
|
19
19
|
// home directory) and prints the builder profile.
|
|
20
20
|
|
|
21
21
|
const readline = require('readline');
|
|
22
|
-
const { cliClient, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE } = require('./cli-lib');
|
|
22
|
+
const { cliClient, saveSession, SESSION_PATH, loadSession, printOnboardingNudge, API_BASE, hallBase } = require('./cli-lib');
|
|
23
|
+
|
|
24
|
+
// The instance's status dashboard, from branding — never a hardcoded host, so a
|
|
25
|
+
// non-OTB instance points its builders at ITS OWN dashboard (task 1002428).
|
|
26
|
+
// Falls back to the resolved API base when branding is unreadable.
|
|
27
|
+
function statusOrigin() {
|
|
28
|
+
try {
|
|
29
|
+
const d = require('../../src/branding').branding().domains || {};
|
|
30
|
+
if (d.statusOrigin) return d.statusOrigin;
|
|
31
|
+
} catch (_) { /* fall through */ }
|
|
32
|
+
return API_BASE;
|
|
33
|
+
}
|
|
23
34
|
const onboardingConfig = require('./onboarding-config');
|
|
24
35
|
|
|
25
36
|
const DRY_RUN = process.argv.includes('--dry-run');
|
|
@@ -70,9 +81,9 @@ async function promptGeminiKey() {
|
|
|
70
81
|
console.log('Google AI Studio key for the pixel-art pipeline (per-builder, optional).');
|
|
71
82
|
console.log(' Get one at https://aistudio.google.com/app/apikey');
|
|
72
83
|
console.log(' (Or skip this and add it later with browser clicks: hall Settings →');
|
|
73
|
-
console.log(
|
|
84
|
+
console.log(` ${hallBase()}/settings#art-key — stored encrypted, ADR 0073.)`);
|
|
74
85
|
console.log(' Each builder pays their own Gemini bill so spend is attributed to them');
|
|
75
|
-
console.log(
|
|
86
|
+
console.log(` on ${statusOrigin()}.`);
|
|
76
87
|
let prompt;
|
|
77
88
|
if (existing && typeof existing === 'string' && existing.length >= 4) {
|
|
78
89
|
const last4 = existing.slice(-4);
|
|
@@ -265,6 +276,21 @@ async function promptOnboardingConsent() {
|
|
|
265
276
|
(async () => {
|
|
266
277
|
const forceFlag = process.argv.includes('--force');
|
|
267
278
|
|
|
279
|
+
// Environment preflight (task 1002401, onboarding-audit area 4). Before auth,
|
|
280
|
+
// surface the three things that used to bite a new builder one reactive crash
|
|
281
|
+
// at a time — Node, installed deps, a launchable `claude` CLI — each with a
|
|
282
|
+
// copy-paste fix, instead of `node: command not found` / `Cannot find module
|
|
283
|
+
// 'pg'` / SUBAGENT_SPAWN_FAILED turning up deep in the session. Read-only and
|
|
284
|
+
// NON-blocking: a fail is actionable, not fatal to sign-in, so it also runs in
|
|
285
|
+
// --dry-run. Shares one source of truth with `bongos doctor` (preflight.js).
|
|
286
|
+
try {
|
|
287
|
+
const { preflightChecks, formatPreflight } = require('./preflight');
|
|
288
|
+
console.log(formatPreflight(preflightChecks(), { color: process.stdout.isTTY, heading: true }));
|
|
289
|
+
console.log('');
|
|
290
|
+
} catch (err) {
|
|
291
|
+
console.error(`(environment preflight skipped: ${err.message})`);
|
|
292
|
+
}
|
|
293
|
+
|
|
268
294
|
// --dry-run: describe every side effect the real flow would perform, without
|
|
269
295
|
// doing any of them. No API calls, no file writes, no TTY prompts. Suitable
|
|
270
296
|
// for a newcomer to read before running for real. Intentionally calls
|
|
@@ -378,6 +404,47 @@ async function promptOnboardingConsent() {
|
|
|
378
404
|
}
|
|
379
405
|
}
|
|
380
406
|
|
|
407
|
+
// Federation (ADR 0141 §1, task #1002108): a HOSTED instance has no GitHub app of
|
|
408
|
+
// its own — it federates sign-in through the Cloud Bongos hub. If the instance
|
|
409
|
+
// manifest advertises auth.idp, run the hub device flow + assertion exchange via
|
|
410
|
+
// the shared login.js runDeviceFlow, then the SAME onboarding prompts. The direct
|
|
411
|
+
// GitHub device flow below (auth.idp unset) is untouched — the self-hosted default.
|
|
412
|
+
let idpCfg = null;
|
|
413
|
+
try {
|
|
414
|
+
const m = await fetch(`${API_BASE}/api/gds/instance`);
|
|
415
|
+
const mj = m.ok ? await m.json() : null;
|
|
416
|
+
idpCfg = (mj && mj.auth && mj.auth.idp) || null;
|
|
417
|
+
} catch (_) { /* manifest unreachable → fall through to the direct flow */ }
|
|
418
|
+
if (idpCfg) {
|
|
419
|
+
console.log('Signing in via the Cloud Bongos platform (hub)...');
|
|
420
|
+
const { runDeviceFlow } = require('./login');
|
|
421
|
+
const flow = await runDeviceFlow(API_BASE, { log: console.log, idp: idpCfg });
|
|
422
|
+
if (flow.state === 'not_approved') {
|
|
423
|
+
console.error('');
|
|
424
|
+
console.error('This GitHub account has not been approved yet.');
|
|
425
|
+
console.error('An Archon must approve your access request before you can enroll.');
|
|
426
|
+
console.error(' → Ask an Archon to invite you, then re-run /builder-setup.');
|
|
427
|
+
process.exit(1);
|
|
428
|
+
}
|
|
429
|
+
if (flow.state !== 'ok') {
|
|
430
|
+
console.error(`\nSign-in did not complete (${flow.state}). Re-run /builder-setup.`);
|
|
431
|
+
process.exit(1);
|
|
432
|
+
}
|
|
433
|
+
const { token, builder } = flow;
|
|
434
|
+
await saveSession({ token, builder, api_base: API_BASE });
|
|
435
|
+
console.log('');
|
|
436
|
+
console.log(`Authenticated as ${builder.github_login} (#${builder.id}). Session saved to ${SESSION_PATH}.`);
|
|
437
|
+
try {
|
|
438
|
+
const { activate } = require('./install-git-hooks');
|
|
439
|
+
activate();
|
|
440
|
+
} catch (_) { /* hook activation is best-effort */ }
|
|
441
|
+
await promptGeminiKey();
|
|
442
|
+
await promptAnthropicLink();
|
|
443
|
+
await promptDisciplines();
|
|
444
|
+
await promptOnboardingConsent();
|
|
445
|
+
process.exit(0);
|
|
446
|
+
}
|
|
447
|
+
|
|
381
448
|
console.log('Starting GitHub Device Flow...');
|
|
382
449
|
const start = await (await cliClient()).auth.postAuthDeviceStart();
|
|
383
450
|
if (!start.ok) {
|
|
@@ -411,7 +478,7 @@ async function promptOnboardingConsent() {
|
|
|
411
478
|
if (poll.data && poll.data.state === 'not_approved') {
|
|
412
479
|
console.error('');
|
|
413
480
|
console.error('This GitHub account has not been approved yet.');
|
|
414
|
-
console.error('
|
|
481
|
+
console.error(`${require('./cli-lib').productName()} admits new builders by invitation: an Archon must`);
|
|
415
482
|
console.error('approve your access request before you can enroll.');
|
|
416
483
|
console.error(' → Ask an Archon to invite you / approve your access request,');
|
|
417
484
|
console.error(' then re-run /builder-setup (or: node scripts/gds/setup.js).');
|
|
@@ -485,7 +552,7 @@ async function promptOnboardingConsent() {
|
|
|
485
552
|
printOnboardingNudge(meRes.data);
|
|
486
553
|
console.log('');
|
|
487
554
|
console.log('Newcomer path:');
|
|
488
|
-
console.log(
|
|
555
|
+
console.log(` 1. Choose thy craft: open ${hallBase()} (creative / engineering)`);
|
|
489
556
|
console.log(' 2. Survey work: /builder-start');
|
|
490
557
|
console.log(' 3. Claim + ship: /builder-claim N → /builder-ship');
|
|
491
558
|
console.log(' 4. Ship three works and you rise to Thetes — the whole queue opens.');
|
package/scripts/gds/shell.js
CHANGED
|
@@ -68,7 +68,7 @@ async function main() {
|
|
|
68
68
|
process.exit(1);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
console.log(
|
|
71
|
+
console.log(`▸ Connecting to your ${require('./cli-lib').productName()} dev box…`);
|
|
72
72
|
const { box, keyPath } = await lib.prepareConnection({ log });
|
|
73
73
|
const host = box.hostname;
|
|
74
74
|
|
|
@@ -81,8 +81,10 @@ async function main() {
|
|
|
81
81
|
sshArgs.push(...remoteCmd);
|
|
82
82
|
} else {
|
|
83
83
|
firstRun = forceLogin || lib.isFirstLogin(host);
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
// BV1.R101: re-scope /workspace to the active claim on connect, so an
|
|
85
|
+
// interactive shell always lands in a folder holding exactly that task's code.
|
|
86
|
+
sshArgs.push(lib.buildRemoteShellCommand({ firstRun, sync: true }));
|
|
87
|
+
console.log(`▸ Opening a shell on ${host} (${lib.WORKSPACE}, scoped to your active claim)…`);
|
|
86
88
|
}
|
|
87
89
|
|
|
88
90
|
const r = spawnSync('ssh', sshArgs, { stdio: 'inherit' });
|