@bongos/core 1.18.3 → 1.18.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +1575 -285
- package/.claude/skills/builder-box/SKILL.md +6 -0
- package/.claude/skills/builder-sequence/SKILL.md +97 -0
- package/.claude/skills/builder-ship/SKILL.md +8 -2
- package/.claude/skills/builder-start/SKILL.md +11 -0
- package/.claude/skills/builder-sync/SKILL.md +41 -0
- package/.claude/skills/fix-task/SKILL.md +64 -0
- package/.claude/skills/goal-create/SKILL.md +158 -0
- package/.claude/skills/goal-review/SKILL.md +3 -1
- package/.claude/skills/idea-triage/SKILL.md +14 -8
- package/.claude/skills/merge-mode/SKILL.md +2 -2
- package/.claude/skills/new-project/SKILL.md +1 -0
- package/.claude/skills/planning-session/SKILL.md +45 -18
- package/.env.local.example +4 -3
- package/README.md +4 -2
- package/bin/bongos.js +1 -0
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +82 -18
- package/clients/bongos-client/index.cjs +82 -18
- package/clients/bongos-client/index.d.ts +131 -29
- package/clients/bongos-client/index.mjs +82 -18
- package/config/branding.neutral.json +6 -1
- package/config/governance.neutral.json +21 -0
- package/docs/adr/0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md +24 -0
- package/docs/adr/0090-metic-task-authoring.md +1 -1
- package/docs/adr/0144-devbox-rehome-onto-cloudbongos-plane.md +41 -0
- package/docs/adr/0144-federated-single-logout-backchannel.md +42 -0
- package/docs/adr/0145-devbox-app-branding-driven-module.md +44 -0
- package/docs/adr/0145-free-hosted-project-tier-isolation-and-domain-separation.md +62 -0
- package/docs/adr/0146-configurable-reward-mode-cost-plus-only.md +53 -0
- package/docs/adr/0147-standalone-live-docs-regen.md +39 -0
- package/docs/adr/0148-task-scoped-box-source-access.md +48 -0
- package/docs/adr/0149-bongos-dev-local-launcher.md +57 -0
- package/docs/adr/0150-box-first-boot-bringup-vendored-instances.md +68 -0
- package/docs/adr/0151-governance-permissions-as-atom-ranks-as-roles.md +229 -0
- package/docs/adr/0152-landing-gate-served-by-the-core-app.md +92 -0
- package/docs/adr/0152-metic-task-abandonment.md +67 -0
- package/docs/adr/0153-sequential-chain-runner.md +51 -0
- package/docs/adr/0154-goal-owner-criterion-authoring.md +86 -0
- package/docs/adr/README.md +10 -0
- package/docs/api/openapi.json +843 -17
- package/docs/api-reference.md +23 -10
- package/docs/architecture.md +4 -2
- package/docs/branding-contract.md +3 -1
- package/docs/canonical-permissions.md +21 -8
- package/docs/file-map.md +8 -0
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +2 -2
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +3 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -3
- package/docs/onboarding/diagrams/assertions.json +4 -7
- package/docs/onboarding/drachmae-and-karma.md +3 -3
- package/docs/onboarding/primer.md +17 -16
- package/docs/onboarding/slash-commands.md +124 -0
- package/docs/recipes/builder-box-lifecycle.md +23 -0
- package/docs/recipes/devbox-app-release.md +14 -4
- package/docs/recipes/instance-first-run.md +16 -0
- package/docs/recipes/local-dev.md +81 -0
- package/docs/recipes/ops-gotchas.md +19 -1
- package/docs/recipes/overnight-builder.md +1 -1
- package/docs/recipes/parallel-goal-waves.md +174 -0
- package/docs/recipes/ship-and-regrade.md +2 -2
- package/docs/recipes/standalone-live-docs.md +48 -0
- package/docs/recipes/work-on-a-task-from-your-dev-box.md +108 -0
- package/migrations/003_pms.sql +6 -7
- package/migrations/006_pms_v2.sql +3 -31
- package/migrations/core_193_task_needs_rebase.sql +28 -0
- package/migrations/core_194_confirmed_backlog_clawback.sql +63 -0
- package/migrations/core_195_idea_triage_provenance.sql +57 -0
- package/migrations/core_196_session_reward_topup.sql +69 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -8
- package/modules/builder-settings/wandering-prefs.js +61 -18
- package/modules/dev-box/app/README.md +73 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-alertTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-busyTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-offTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate.png +0 -0
- package/modules/dev-box/app/assets/tray/mac-onTemplate@2x.png +0 -0
- package/modules/dev-box/app/assets/tray/win-alert.png +0 -0
- package/modules/dev-box/app/assets/tray/win-busy.png +0 -0
- package/modules/dev-box/app/assets/tray/win-off.png +0 -0
- package/modules/dev-box/app/assets/tray/win-on.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background.png +0 -0
- package/modules/dev-box/app/build-resources/dmg-background@2x.png +0 -0
- package/modules/dev-box/app/build-resources/icon.png +0 -0
- package/modules/dev-box/app/electron-builder.tmpl.yml +95 -0
- package/modules/dev-box/app/electron-builder.yml +95 -0
- package/modules/dev-box/app/package-lock.json +5291 -0
- package/modules/dev-box/app/package.json +30 -0
- package/modules/dev-box/app/renderer/app.js +319 -0
- package/modules/dev-box/app/renderer/index.html +45 -0
- package/modules/dev-box/app/renderer/styles.css +246 -0
- package/modules/dev-box/app/renderer/theme.generated.css +16 -0
- package/modules/dev-box/app/scripts/apply-dmg-backdrop.sh +82 -0
- package/modules/dev-box/app/scripts/dmgbuild-settings.py +39 -0
- package/modules/dev-box/app/scripts/gen-brand.js +138 -0
- package/modules/dev-box/app/scripts/gen-dmg-backdrop.py +323 -0
- package/modules/dev-box/app/scripts/gen-icons.js +286 -0
- package/modules/dev-box/app/scripts/vendor-client.js +23 -0
- package/modules/dev-box/app/src/box-controller.js +287 -0
- package/modules/dev-box/app/src/brand.generated.json +13 -0
- package/modules/dev-box/app/src/brand.js +16 -0
- package/modules/dev-box/app/src/config.js +19 -0
- package/modules/dev-box/app/src/gds-client.js +90 -0
- package/modules/dev-box/app/src/main.js +610 -0
- package/modules/dev-box/app/src/pairing.js +57 -0
- package/modules/dev-box/app/src/preload.js +29 -0
- package/modules/dev-box/app/src/session-store.js +58 -0
- package/modules/dev-box/app/src/ssh-setup.js +569 -0
- package/modules/dev-box/app/src/task-picker.js +70 -0
- package/modules/dev-box/app/src/tray.js +99 -0
- package/modules/dev-box/app/src/updater.js +55 -0
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +698 -0
- package/modules/dev-box/app/test/unit.mjs +526 -0
- package/modules/dev-box/box-access.js +121 -4
- package/modules/dev-box/boxes.js +20 -0
- package/modules/dev-box/module.json +1 -1
- package/modules/dev-box/routes/box.js +87 -3
- package/modules/economy/credits.js +116 -25
- package/modules/economy/reward.js +8 -0
- package/modules/economy/streak.js +2 -1
- package/modules/governance/CLAUDE.md +15 -0
- package/modules/governance/catalog.js +419 -0
- package/modules/governance/config.js +174 -0
- package/modules/governance/db.js +125 -0
- package/modules/governance/migrations/governance_001_tables.sql +81 -0
- package/modules/governance/migrations/governance_002_seed_and_backfill.sql +103 -0
- package/modules/governance/migrations/governance_003_task_abandon_metic.sql +28 -0
- package/modules/governance/migrations/governance_004_criterion_create.sql +39 -0
- package/modules/governance/migrations/governance_005_resync_builder_rank_roles.sql +56 -0
- package/modules/governance/module.json +10 -0
- package/modules/governance/protected-surfaces.js +88 -0
- package/modules/governance/protected-surfaces.json +80 -0
- package/modules/governance/rank-roles.js +83 -0
- package/modules/governance/resolver.js +221 -0
- package/modules/governance/routes/governance.js +89 -0
- package/modules/governance/routes/role-sync.js +49 -0
- package/modules/governance/session-scopes.js +127 -0
- package/modules/governance/session-scopes.json +38 -0
- package/modules/hall-ui/public/atlas.css +117 -0
- package/modules/hall-ui/public/atlas.html +3 -114
- package/modules/hall-ui/public/brand-apply.js +34 -6
- package/modules/hall-ui/public/builders.js +187 -219
- package/modules/hall-ui/public/diagrams.html +2 -0
- package/modules/hall-ui/public/dom-utils.js +27 -0
- package/modules/hall-ui/public/drachmae.html +2 -0
- package/modules/hall-ui/public/drachmae.js +54 -1
- package/modules/hall-ui/public/gate.css +98 -0
- package/modules/hall-ui/public/gate.html +3 -95
- package/modules/hall-ui/public/goal-inbox.js +4 -1
- package/modules/hall-ui/public/goals-page.js +626 -114
- package/modules/hall-ui/public/goals.css +79 -1
- package/modules/hall-ui/public/goals.html +38 -11
- package/modules/hall-ui/public/hall-widgets.js +62 -52
- package/modules/hall-ui/public/harbor.css +123 -0
- package/modules/hall-ui/public/harbor.html +3 -120
- package/modules/hall-ui/public/index.html +11 -4
- package/modules/hall-ui/public/instances-panel.js +4 -1
- package/modules/hall-ui/public/modules.css +63 -0
- package/modules/hall-ui/public/modules.html +3 -60
- package/modules/hall-ui/public/new-project-wizard.js +7 -3
- package/modules/hall-ui/public/not-ready.html +2 -0
- package/modules/hall-ui/public/pair.css +73 -0
- package/modules/hall-ui/public/pair.html +3 -70
- package/modules/hall-ui/public/palette.js +302 -0
- package/modules/hall-ui/public/primer.css +13 -0
- package/modules/hall-ui/public/primer.html +2 -0
- package/modules/hall-ui/public/profile.html +2 -0
- package/modules/hall-ui/public/ranks.html +2 -0
- package/modules/hall-ui/public/roadmap.css +179 -0
- package/modules/hall-ui/public/roadmap.html +9 -172
- package/modules/hall-ui/public/roadmap.js +329 -316
- package/modules/hall-ui/public/sessions.css +16 -0
- package/modules/hall-ui/public/sessions.html +3 -13
- package/modules/hall-ui/public/settings.html +2 -0
- package/modules/hall-ui/public/settings.js +4 -4
- package/modules/hall-ui/public/shell.js +143 -18
- package/modules/hall-ui/public/style.css +231 -39
- package/modules/hall-ui/public/task-detail.js +347 -26
- package/modules/hall-ui/public/task.html +6 -1
- package/modules/hall-ui/public/watch.html +2 -0
- package/modules/hall-ui/public/work.html +8 -6
- package/modules/hall-ui/public/work.js +1 -1
- package/modules/ideas/inbox.js +61 -11
- package/modules/ideas/routes/inbox.js +23 -0
- package/modules/lifecycle/claim-eligibility.js +72 -0
- package/modules/lifecycle/db.js +519 -139
- package/modules/lifecycle/done-when.js +18 -3
- package/modules/lifecycle/github-push.js +19 -2
- package/modules/lifecycle/goal-advisory.js +114 -0
- package/modules/lifecycle/goal-authz.js +62 -0
- package/modules/lifecycle/lifecycle.js +9 -0
- package/modules/lifecycle/publish-reconciler.js +109 -6
- package/modules/lifecycle/routes/claims.js +68 -23
- package/modules/lifecycle/routes/done-when.js +110 -15
- package/modules/lifecycle/routes/goals.js +8 -38
- package/modules/lifecycle/routes/tasks.js +38 -6
- package/modules/lifecycle/ship-card.js +6 -0
- package/modules/onboarding/onboarding-state.js +1 -1
- package/modules/platform-identity/CLAUDE.md +15 -0
- package/modules/platform-identity/hub-device.js +147 -0
- package/modules/platform-identity/hub-keys.js +151 -0
- package/modules/platform-identity/migrations/platform_identity_001_tables.sql +114 -0
- package/modules/platform-identity/migrations/platform_identity_002_client_origin_index.sql +18 -0
- package/modules/platform-identity/migrations/platform_identity_003_project_memberships.sql +42 -0
- package/modules/platform-identity/migrations/platform_identity_004_projects.sql +44 -0
- package/modules/platform-identity/migrations/platform_identity_005_builder_activity.sql +42 -0
- package/modules/platform-identity/module.json +18 -0
- package/modules/platform-identity/platform-identity.js +531 -0
- package/modules/platform-identity/pollers/code-sweep.js +43 -0
- package/modules/platform-identity/routes/my-projects.js +140 -0
- package/modules/platform-identity/routes/projects.js +107 -0
- package/modules/platform-identity/routes/scouting.js +35 -0
- package/modules/platform-identity/routes/sso.js +402 -0
- package/modules/platform-identity/tests/platform-identity.mjs +388 -0
- package/modules/platform-identity/ui/.gitkeep +0 -0
- package/modules/security/routes/reports.js +10 -0
- package/modules/sessions/db.js +166 -21
- package/modules/sessions/module.json +1 -1
- package/modules/sessions/pollers/session-reward-reconcile.js +78 -0
- package/modules/sessions/routes/sessions.js +33 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/anim/idle/frame_00.webp +0 -0
- package/public-landing/assets/anim/idle/frame_01.webp +0 -0
- package/public-landing/assets/anim/idle/frame_02.webp +0 -0
- package/public-landing/assets/anim/idle/frame_03.webp +0 -0
- package/public-landing/assets/anim/idle/frame_04.webp +0 -0
- package/public-landing/assets/anim/idle/frame_05.webp +0 -0
- package/public-landing/assets/anim/idle/frame_06.webp +0 -0
- package/public-landing/assets/anim/idle/frame_07.webp +0 -0
- package/public-landing/assets/anim/idle/frame_08.webp +0 -0
- package/public-landing/assets/anim/idle/frame_09.webp +0 -0
- package/public-landing/assets/anim/idle/frame_10.webp +0 -0
- package/public-landing/assets/anim/idle/frame_11.webp +0 -0
- package/public-landing/assets/anim/idle/frame_12.webp +0 -0
- package/public-landing/assets/anim/idle/frame_13.webp +0 -0
- package/public-landing/assets/anim/idle/frame_14.webp +0 -0
- package/public-landing/assets/anim/idle/frame_15.webp +0 -0
- package/public-landing/assets/anim/idle/frame_16.webp +0 -0
- package/public-landing/assets/anim/idle/frame_17.webp +0 -0
- package/public-landing/assets/anim/idle/frame_18.webp +0 -0
- package/public-landing/assets/anim/idle/frame_19.webp +0 -0
- package/public-landing/assets/anim/idle/frame_20.webp +0 -0
- package/public-landing/assets/anim/idle/frame_21.webp +0 -0
- package/public-landing/assets/anim/idle/frame_22.webp +0 -0
- package/public-landing/assets/anim/idle/frame_23.webp +0 -0
- package/public-landing/assets/anim/idle/frame_24.webp +0 -0
- package/public-landing/assets/anim/idle/frame_25.webp +0 -0
- package/public-landing/assets/anim/idle/frame_26.webp +0 -0
- package/public-landing/assets/anim/idle/frame_27.webp +0 -0
- package/public-landing/assets/anim/idle/frame_28.webp +0 -0
- package/public-landing/assets/anim/idle/frame_29.webp +0 -0
- package/public-landing/assets/anim/idle/frame_30.webp +0 -0
- package/public-landing/assets/anim/idle/frame_31.webp +0 -0
- package/public-landing/assets/anim/idle/frame_32.webp +0 -0
- package/public-landing/assets/anim/idle/frame_33.webp +0 -0
- package/public-landing/assets/anim/idle/frame_34.webp +0 -0
- package/public-landing/assets/anim/idle/frame_35.webp +0 -0
- package/public-landing/assets/anim/idle/frame_36.webp +0 -0
- package/public-landing/assets/anim/idle/frame_37.webp +0 -0
- package/public-landing/assets/anim/idle/frame_38.webp +0 -0
- package/public-landing/assets/anim/idle/frame_39.webp +0 -0
- package/public-landing/assets/anim/idle/frame_40.webp +0 -0
- package/public-landing/assets/anim/idle/frame_41.webp +0 -0
- package/public-landing/assets/anim/idle/frame_42.webp +0 -0
- package/public-landing/assets/anim/idle/frame_43.webp +0 -0
- package/public-landing/create.html +51 -58
- package/public-landing/gate/README.md +137 -0
- package/public-landing/gate/dispmaps/bar.png +0 -0
- package/public-landing/gate/dispmaps/flat.png +0 -0
- package/public-landing/gate/dispmaps/key.png +0 -0
- package/public-landing/gate/index.html +587 -0
- package/public-landing/gate/previews/cloud-bongos.jpg +0 -0
- package/public-landing/gate/previews/off-the-boats.jpg +0 -0
- package/public-landing/gate/space.css +799 -0
- package/public-landing/gate/space.js +1826 -0
- package/public-landing/gate/sprites/enter.png +0 -0
- package/public-landing/gate/sprites/search.png +0 -0
- package/public-landing/gate/sprites/terminal-blank.png +0 -0
- package/public-landing/gate/sprites/tile.png +0 -0
- package/public-landing/world/assets/clouds/cloud-island-large.png +0 -0
- package/public-landing/world/assets/clouds/cloud-puff.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-cargo.png +0 -0
- package/public-landing/world/assets/creatures/creature-whale-glide.png +0 -0
- package/public-landing/world/assets/plates/plate-clouds-near.png +0 -0
- package/public-landing/world/assets/plates/scene-establishing.png +0 -0
- package/scripts/gds/abandon.js +6 -4
- package/scripts/gds/api.js +10 -5
- package/scripts/gds/api.ps1 +26 -0
- package/scripts/gds/api.sh +6 -0
- package/scripts/gds/audit-ships.js +7 -1
- package/scripts/gds/backfill-sessions.js +78 -18
- package/scripts/gds/backup.js +23 -1
- package/scripts/gds/box-connect-lib.js +124 -31
- package/scripts/gds/box-sync.js +66 -0
- package/scripts/gds/box.js +225 -53
- package/scripts/gds/cf-tunnel.js +145 -6
- package/scripts/gds/claim.js +34 -3
- package/scripts/gds/cli-lib.js +161 -27
- package/scripts/gds/code.js +6 -2
- package/scripts/gds/deploy-divergence.js +209 -0
- package/scripts/gds/dev-lib.js +309 -0
- package/scripts/gds/dev.js +322 -0
- package/scripts/gds/doctor.js +90 -61
- package/scripts/gds/fetch-art-key.js +1 -1
- package/scripts/gds/fetch-sound-prefs.js +2 -2
- package/scripts/gds/fitness.js +228 -12
- package/scripts/gds/gds-db-backup.service +16 -1
- package/scripts/gds/gds-db-offsite.service +9 -1
- package/scripts/gds/gen-api-client.js +1 -1
- package/scripts/gds/gen-api-docs.js +36 -14
- package/scripts/gds/gen-diagrams.js +45 -28
- package/scripts/gds/gen-file-map.js +2 -1
- package/scripts/gds/gen-freshness.js +32 -0
- package/scripts/gds/gen-repo-map.js +3 -2
- package/scripts/gds/gen-session-index.js +14 -5
- package/scripts/gds/hooks/pre-push.js +2 -2
- package/scripts/gds/init.js +60 -3
- package/scripts/gds/install-git-hooks.js +1 -1
- package/scripts/gds/key-card.js +15 -6
- package/scripts/gds/key-status.js +1 -1
- package/scripts/gds/linkify-refs.js +35 -9
- package/scripts/gds/login.js +16 -1
- package/scripts/gds/paste-token.js +7 -1
- package/scripts/gds/preflight.js +126 -0
- package/scripts/gds/provision.js +123 -12
- package/scripts/gds/publish-manifest.js +7 -1
- package/scripts/gds/reauth.js +7 -3
- package/scripts/gds/regen-instance-docs.js +47 -0
- package/scripts/gds/run-unit-tests.js +4 -2
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/gds/seed-bv1-profiles-tasks.js +126 -0
- package/scripts/gds/sequence.js +425 -0
- package/scripts/gds/session-cost.js +10 -1
- package/scripts/gds/setup.js +31 -5
- package/scripts/gds/shell.js +5 -3
- package/scripts/gds/ship.js +319 -29
- package/scripts/gds/smoke-gds.js +139 -8
- package/scripts/gds/start.js +171 -22
- package/scripts/gds/status.js +1 -1
- package/scripts/gds/triage.js +10 -9
- package/scripts/gds/upgrade.js +124 -2
- package/scripts/status-mirror-sync.js +114 -84
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/auth.js +447 -102
- package/src/bongos/db-kernel.js +27 -0
- package/src/bongos/db.js +39 -0
- package/src/bongos/module-scope-map.js +27 -1
- package/src/bongos/permission-path-check.js +136 -102
- package/src/bongos/repo-info.js +47 -11
- package/src/bongos/route-rank-check.js +429 -46
- package/src/bongos/routes/_helpers.js +12 -1
- package/src/bongos/routes/auth.js +219 -2
- package/src/bongos/routes/backup.js +58 -2
- package/src/bongos/routes/me.js +35 -1
- package/src/bongos/serve-internal.js +84 -15
- package/src/branding.js +32 -0
- package/src/instance-config.js +30 -0
- package/src/module-api.js +112 -1
- package/src/platform-server.js +86 -9
- package/tests/abandon_task.mjs +59 -3
- package/tests/api_cli_flags.mjs +53 -0
- package/tests/auth_page_gate.mjs +13 -0
- package/tests/backfill_sessions.mjs +127 -1
- package/tests/backup_cli.mjs +54 -0
- package/tests/bongos_box_connect.mjs +77 -0
- package/tests/box_access.mjs +130 -1
- package/tests/box_credential.mjs +125 -0
- package/tests/box_host_keys.mjs +24 -0
- package/tests/box_infra_resolve.mjs +149 -0
- package/tests/box_scope_session.mjs +6 -2
- package/tests/box_sync.mjs +55 -0
- package/tests/box_task_scope.mjs +99 -0
- package/tests/branding.mjs +12 -0
- package/tests/cf_ruleset_preflight.mjs +142 -0
- package/tests/cf_tunnel.mjs +37 -0
- package/tests/claim_gate_rebase.mjs +98 -0
- package/tests/clawback_confirmed_backlog.mjs +103 -0
- package/tests/cli_lib_formatters.mjs +20 -0
- package/tests/confirm_attribution.mjs +30 -46
- package/tests/credit_grant.mjs +198 -10
- package/tests/criterion_authoring_gate.mjs +272 -0
- package/tests/criterion_progress.mjs +33 -1
- package/tests/dev_lib.mjs +253 -0
- package/tests/devbox_artifact_gate.mjs +75 -0
- package/tests/devbox_dmg_backdrop.mjs +137 -0
- package/tests/devbox_icons.mjs +304 -0
- package/tests/devbox_installer_brand.mjs +232 -0
- package/tests/devbox_key_register.mjs +234 -0
- package/tests/docs_openapi_route.mjs +110 -0
- package/tests/doctor_hooks.mjs +141 -0
- package/tests/extract_json.mjs +140 -0
- package/tests/fitness.mjs +89 -0
- package/tests/gen_freshness.mjs +96 -0
- package/tests/goal_advisory.mjs +251 -0
- package/tests/goal_criterion_confirm_ui.mjs +269 -0
- package/tests/goal_routes.mjs +59 -4
- package/tests/goal_tier.mjs +12 -3
- package/tests/governance_catalog.mjs +129 -0
- package/tests/governance_claim_eligibility.mjs +262 -0
- package/tests/governance_config.mjs +187 -0
- package/tests/governance_ownership_scope.mjs +410 -0
- package/tests/governance_page_gates.mjs +324 -0
- package/tests/governance_parity.mjs +336 -0
- package/tests/governance_principals.mjs +232 -0
- package/tests/governance_protected_surfaces.mjs +208 -0
- package/tests/governance_require_permission.mjs +223 -0
- package/tests/governance_resolver.mjs +84 -0
- package/tests/governance_role_sync.mjs +231 -0
- package/tests/governance_seed.mjs +127 -0
- package/tests/governance_session_scope.mjs +434 -0
- package/tests/governance_vanilla_boot.mjs +203 -0
- package/tests/grade_attribution.mjs +7 -7
- package/tests/hall_audit.mjs +223 -0
- package/tests/hall_nav.mjs +191 -0
- package/tests/hall_palette.mjs +206 -0
- package/tests/hall_signout.mjs +124 -0
- package/tests/hook_user_agents.mjs +10 -0
- package/tests/idea_triage.mjs +162 -0
- package/tests/idp_device_exchange.mjs +93 -0
- package/tests/init.mjs +51 -0
- package/tests/instance_config.mjs +65 -0
- package/tests/instance_mismatch_guard.mjs +23 -6
- package/tests/instance_release_config.mjs +104 -0
- package/tests/linkify_refs.mjs +15 -2
- package/tests/module_contributions.mjs +55 -0
- package/tests/module_loader.mjs +10 -5
- package/tests/module_scope_active_claims.mjs +94 -0
- package/tests/needs_rebase.mjs +92 -0
- package/tests/one_active_claim_per_session.mjs +76 -0
- package/tests/override_attribution.mjs +10 -8
- package/tests/override_request_ownership.mjs +8 -4
- package/tests/parse_cookie_decode.mjs +42 -0
- package/tests/pay_on_land.mjs +129 -0
- package/tests/platform_boot.mjs +100 -1
- package/tests/post_login_landing.mjs +42 -0
- package/tests/preflight_checks.mjs +72 -0
- package/tests/provision.mjs +132 -7
- package/tests/publish_branch_route.mjs +53 -0
- package/tests/publish_reconciler.mjs +187 -2
- package/tests/repo_info_binding.mjs +248 -0
- package/tests/reward_gate.mjs +11 -2
- package/tests/reward_mode_cost_plus_only.mjs +116 -0
- package/tests/reward_mode_surfacing.mjs +168 -0
- package/tests/route_permission_check.mjs +301 -0
- package/tests/route_rank_check.mjs +254 -12
- package/tests/route_validation.mjs +30 -0
- package/tests/sequence.mjs +267 -0
- package/tests/session_digest.mjs +44 -0
- package/tests/session_index.mjs +18 -6
- package/tests/session_records.mjs +39 -1
- package/tests/session_reward_on_land.mjs +381 -0
- package/tests/session_reward_reconcile.mjs +229 -0
- package/tests/ship_api_client_pathspec.mjs +100 -0
- package/tests/ship_base_flag.mjs +93 -0
- package/tests/ship_ci_deploy.mjs +80 -3
- package/tests/ship_upstream_deletion.mjs +57 -0
- package/tests/smoke_target.mjs +233 -0
- package/tests/start_env_guard.mjs +87 -0
- package/tests/start_rebase_warning.mjs +103 -0
- package/tests/status_mirror_sync.mjs +122 -0
- package/tests/stop_refcheck.mjs +19 -1
- package/tests/task_goal_link.mjs +15 -6
- package/tests/upgrade.mjs +139 -0
- package/tests/web_admission_status.mjs +162 -0
- package/tests/write_validation_gaps.mjs +110 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Seed the tasks for goal 1000045 "Public builder profiles" (BONGOS-V1, internal).
|
|
2
|
+
// Source of truth: docs/specs/bongos-v1-public-builder-profiles.md (status: final).
|
|
3
|
+
// Idempotent on (source, source_ref). Re-running resolves to the existing rows.
|
|
4
|
+
//
|
|
5
|
+
// Run: node scripts/gds/seed-bv1-profiles-tasks.js
|
|
6
|
+
const os = require('os');
|
|
7
|
+
const sess = require(os.homedir() + '/.config/cloudbongos/gds-session.json');
|
|
8
|
+
const BASE = sess.api_base.replace(/\/$/, '');
|
|
9
|
+
const TOKEN = sess.token;
|
|
10
|
+
const VER = 'BONGOS-V1';
|
|
11
|
+
const GOAL = 1000045;
|
|
12
|
+
const SRC = 'planning-session';
|
|
13
|
+
const H = { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' };
|
|
14
|
+
|
|
15
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
16
|
+
async function api(method, path, body, _tries = 0) {
|
|
17
|
+
await sleep(400); // base throttle to stay under the per-IP rate limit
|
|
18
|
+
const res = await fetch(`${BASE}/api/gds${path}`, {
|
|
19
|
+
method, headers: H, body: body ? JSON.stringify(body) : undefined,
|
|
20
|
+
});
|
|
21
|
+
const text = await res.text();
|
|
22
|
+
let json; try { json = JSON.parse(text); } catch { json = { _raw: text.slice(0, 300) }; }
|
|
23
|
+
if (res.status === 429 && _tries < 8) {
|
|
24
|
+
const wait = ((json.error && json.error.details && json.error.details.retry_after_seconds) || 30) + 1;
|
|
25
|
+
console.log(` …rate-limited, waiting ${wait}s`);
|
|
26
|
+
await sleep(wait * 1000);
|
|
27
|
+
return api(method, path, body, _tries + 1);
|
|
28
|
+
}
|
|
29
|
+
return { status: res.status, ok: res.ok, json };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// disc: engineer (backend/API/spike) | ui (frontend). kind: spike | feature | verify (proofs).
|
|
33
|
+
const TASKS = [
|
|
34
|
+
// Phase 1 — profile shows cross-project record + hide (C1,C2) — P5
|
|
35
|
+
{ ref:'R01', kind:'spike', disc:'engineer', prio:5, est:180, crit:['C1','C2'], deps:[], title:'R01 · Design profile↔cross-project rollup join + consent model', desc:'Needs-exploration: how the public profile reads the platform-identity rollup (listScoutingRollup / platform_identity_builder_project_activity), public-by-default, and where the hide flag (C2) lives. Output an ADR + sub-tasks. See docs/specs/bongos-v1-public-builder-profiles.md.' },
|
|
36
|
+
{ ref:'R02', kind:'feature', disc:'engineer', prio:5, est:240, crit:['C1'], deps:['R01'], title:'R02 · Profile API returns cross-project record + project names', desc:'Extend GET /builders/:id/profile to include aggregated cross-project credits/ships/karma/disciplines + the list of projects by name, from the platform-identity rollup; public by default; respects the whitelist + leak test (tests/profile_route.mjs). Done-when: another account sees the aggregate + project names, no private columns. Deps R01.' },
|
|
37
|
+
{ ref:'R03', kind:'feature', disc:'engineer', prio:5, est:180, crit:['C2'], deps:['R01'], title:'R03 · Hide flag: builder hides cross-project stats (opt-out, live)', desc:'Self-serve opt-out that hides the cross-project record from the profile + applications, honored live everywhere. Default is shown. Deps R01.' },
|
|
38
|
+
{ ref:'R04', kind:'feature', disc:'ui', prio:5, est:180, crit:['C1','C2'], deps:['R02','R03'], title:'R04 · Profile UI: cross-project section + self hide-toggle', desc:'Render the cross-project section (totals + project names) on the profile; show the hide toggle to the profile owner. Deps R02,R03.' },
|
|
39
|
+
{ ref:'R05', kind:'feature', disc:'ui', prio:4, est:120, crit:['C1'], deps:['R02'], title:'R05 · Shareable-by-login profile URL (?builder=<login>)', desc:'Add a login-addressable profile URL alongside ?id=. Promotes idea #1000522. Deps R02.' },
|
|
40
|
+
{ ref:'R06', kind:'verify', disc:'engineer', prio:4, est:90, crit:['C1','C2'], deps:['R04','R03'], title:'R06 · PROOF: cross-project record shows to a viewer; hide removes it live', desc:'Verify a 2-project builder\'s combined record shows to another account, and hiding removes it within one load. Extends tests/profile_route.mjs. Deps R04,R03.' },
|
|
41
|
+
|
|
42
|
+
// Phase 2 — apply from orbs + application carries profile (C3,C4) — P5
|
|
43
|
+
{ ref:'R07', kind:'spike', disc:'engineer', prio:5, est:180, crit:['C3','C4'], deps:[], title:'R07 · Design application model + "project owner (registrant+delegates)" authz', desc:'Needs-exploration: extend access_requests vs a new cross-project applications record; how the profile snapshot travels hub→target; the owner (registrant + delegates) authz; pending queue + approve/decline. Output an ADR + sub-tasks. Deps none.' },
|
|
44
|
+
{ ref:'R08', kind:'feature', disc:'engineer', prio:5, est:240, crit:['C3','C4'], deps:['R07'], title:'R08 · Application API: apply / list-pending / approve / decline', desc:'Cross-project application flow reusing the join relay (/my-projects/join → target /access-requests). Deps R07.' },
|
|
45
|
+
{ ref:'R09', kind:'feature', disc:'engineer', prio:5, est:150, crit:['C4'], deps:['R08','R02'], title:'R09 · Application carries the applicant\'s cross-project record (+note)', desc:'Attach the applicant\'s cross-project record (subject to the C2 hide, live) + optional note to the application the owner sees. Deps R08,R02.' },
|
|
46
|
+
{ ref:'R10', kind:'feature', disc:'ui', prio:5, est:180, crit:['C3'], deps:['R08'], title:'R10 · "Apply" affordance on the in-core browse surface', desc:'Apply action + state (applied/pending/member) on the project cards / My Projects. Deps R08.' },
|
|
47
|
+
{ ref:'R11', kind:'feature', disc:'ui', prio:5, est:180, crit:['C4'], deps:['R08'], title:'R11 · Owner pending-applications view (approve/decline)', desc:'A project owner sees pending applications for their project and approves/declines. Deps R08.' },
|
|
48
|
+
{ ref:'R12', kind:'feature', disc:'ui', prio:4, est:150, crit:['C3'], deps:['R10'], title:'R12 · Surface "Apply" on the orbs front door', desc:'Wire the Apply affordance onto the orbs page. NOTE: the orbs front door is a separate static site — this likely lands instance-side. Deps R10.' },
|
|
49
|
+
{ ref:'R13', kind:'verify', disc:'engineer', prio:5, est:90, crit:['C3','C4'], deps:['R10','R11','R09'], title:'R13 · PROOF: apply → owner sees record → approves → applicant joins', desc:'End-to-end verify of the apply→approve flow carrying the profile. Deps R10,R11,R09.' },
|
|
50
|
+
|
|
51
|
+
// Phase 3 — owners browse builder profiles (C5) — P4
|
|
52
|
+
{ ref:'R14', kind:'feature', disc:'engineer', prio:4, est:210, crit:['C5'], deps:['R02'], title:'R14 · Graduate /scouting to project-owner-facing + opt-out-of-recruiting flag', desc:'Widen the Archon-only /scouting to a project owner (registrant+delegates); add a builder opt-out-of-recruiting flag. Touches authz → Metic+ floor. Deps R02.' },
|
|
53
|
+
{ ref:'R15', kind:'feature', disc:'ui', prio:4, est:210, crit:['C5'], deps:['R14'], title:'R15 · Owner recruiting UI (search/filter/sort; honors hide + opt-out)', desc:'Owner-facing recruiting surface: search/filter by discipline, sort by track record; honors C2 hide + recruiting opt-out. Deps R14.' },
|
|
54
|
+
{ ref:'R16', kind:'feature', disc:'ui', prio:4, est:150, crit:['C5'], deps:['R15','R08'], title:'R16 · Browse → invite/recruit a builder to your project', desc:'Invite/recruit a builder from the browse surface, tying into the application/invite flow. Deps R15,R08.' },
|
|
55
|
+
{ ref:'R17', kind:'verify', disc:'engineer', prio:4, est:90, crit:['C5'], deps:['R15','R16'], title:'R17 · PROOF: non-Archon owner browses, filters, invites', desc:'Verify a non-Archon project owner browses builder profiles, filters by discipline, invites one. Deps R15,R16.' },
|
|
56
|
+
|
|
57
|
+
// Phase 4 — builder-to-builder connections + degrees (C6) — P4
|
|
58
|
+
{ ref:'R18', kind:'spike', disc:'engineer', prio:4, est:180, crit:['C6'], deps:[], title:'R18 · Design connections model incl. 1st/2nd/3rd-degree traversal', desc:'Needs-exploration: platform-level connections (account↔account) with request/accept/decline/remove; degree-of-separation traversal (1st/2nd/3rd); "people you may know" from native signals. Output an ADR + sub-tasks. Deps none.' },
|
|
59
|
+
{ ref:'R19', kind:'feature', disc:'engineer', prio:4, est:240, crit:['C6'], deps:['R18'], title:'R19 · Connections API: request/accept/decline/remove/list + degree queries', desc:'The connection graph API incl. degree-of-separation queries. Deps R18.' },
|
|
60
|
+
{ ref:'R20', kind:'feature', disc:'ui', prio:4, est:180, crit:['C6'], deps:['R19','R04'], title:'R20 · Connections on the profile (with degree labels) + connect button', desc:'Show a builder\'s connections (public) with 1st/2nd/3rd-degree labels; connect/pending/accept from the profile. Deps R19,R04.' },
|
|
61
|
+
{ ref:'R21', kind:'feature', disc:'engineer', prio:4, est:150, crit:['C6'], deps:['R19'], title:'R21 · "Builders you may know" from mutuals + shared projects/guilds', desc:'Suggest connections from mutual connections + shared projects/guilds (2nd/3rd degree). Deps R19.' },
|
|
62
|
+
{ ref:'R22', kind:'verify', disc:'engineer', prio:4, est:90, crit:['C6'], deps:['R20','R21'], title:'R22 · PROOF: two builders connect; each sees the other + a 2nd-degree suggestion', desc:'Verify request→accept, mutual visibility, and a 2nd-degree suggestion appears. Deps R20,R21.' },
|
|
63
|
+
|
|
64
|
+
// Phase 5 — guilds + apply/invite + guild map (C7,C8,C9) — P3
|
|
65
|
+
{ ref:'R23', kind:'spike', disc:'engineer', prio:3, est:210, crit:['C7','C8','C9'], deps:['R18','R07'], title:'R23 · Design guild model (tables, roles, public/unlisted, rollup, map ranking)', desc:'Needs-exploration: platform-level guilds + guild_members (multi-guild, distinct from goal_members); roles; public/unlisted visibility; collective rollup (excludes hidden members); guild↔project application reuse; map ranking. Output an ADR + sub-tasks. Deps R18,R07.' },
|
|
66
|
+
{ ref:'R24', kind:'feature', disc:'engineer', prio:3, est:240, crit:['C7'], deps:['R23'], title:'R24 · Guild CRUD + membership API + public/unlisted visibility', desc:'Create guild, invite/accept/join-request, roster, leave; owner sets public/unlisted. Deps R23.' },
|
|
67
|
+
{ ref:'R25', kind:'feature', disc:'ui', prio:3, est:210, crit:['C7'], deps:['R24'], title:'R25 · Guild page UI (identity, roster, collective record)', desc:'Guild page: identity, roster, collective track record (N of M counted). Deps R24.' },
|
|
68
|
+
{ ref:'R26', kind:'feature', disc:'engineer', prio:3, est:180, crit:['C7'], deps:['R24','R02'], title:'R26 · Guild collective track-record rollup (excludes hidden members)', desc:'Aggregate members\' cross-project stats into the guild total; exclude C2-hidden members ("N of M counted"). Deps R24,R02.' },
|
|
69
|
+
{ ref:'R27', kind:'feature', disc:'engineer', prio:3, est:210, crit:['C8'], deps:['R24','R08'], title:'R27 · Guild → project applications + owner invites a guild (each-member-opt-in)', desc:'Guild applies to / is invited to a project (guild-level mirror of R08); each member opts in before being attached. Deps R24,R08.' },
|
|
70
|
+
{ ref:'R28', kind:'feature', disc:'ui', prio:3, est:150, crit:['C8'], deps:['R27','R11'], title:'R28 · Owner view: pending guild applications', desc:'Extends the owner application view (R11) to guild applications. Deps R27,R11.' },
|
|
71
|
+
{ ref:'R29', kind:'spike', disc:'engineer', prio:3, est:150, crit:['C9'], deps:['R26'], title:'R29 · Design guild map ranking + orb-sky viz approach', desc:'Needs-exploration: rank guilds by size/success; render on the orb-sky visual language (goal 1000044). Output design + sub-tasks. Deps R26.' },
|
|
72
|
+
{ ref:'R30', kind:'feature', disc:'engineer', prio:3, est:150, crit:['C9'], deps:['R26'], title:'R30 · Guild map API (ranked; public guilds only; honors hide)', desc:'Ranked guilds by size/success; only public (not unlisted) guilds; honors member hide. Deps R26.' },
|
|
73
|
+
{ ref:'R31', kind:'feature', disc:'ui', prio:3, est:210, crit:['C9'], deps:['R30','R29','R15'], title:'R31 · Guild map UI on the recruiting surface (orb-sky visual)', desc:'Visual guild map on the owner recruiting surface. Deps R30,R29,R15.' },
|
|
74
|
+
{ ref:'R32', kind:'verify', disc:'engineer', prio:3, est:120, crit:['C7','C8','C9'], deps:['R25','R27','R31'], title:'R32 · PROOF: form guild → applies (members opt in) → owner approves → ranked on map', desc:'End-to-end verify of the guild lifecycle incl. the map. Deps R25,R27,R31.' },
|
|
75
|
+
|
|
76
|
+
// Phase 6 — connection proximity via orb-sky physics (C10) — P3
|
|
77
|
+
{ ref:'R33', kind:'spike', disc:'engineer', prio:3, est:210, crit:['C10'], deps:['R19','R23'], title:'R33 · Design connection→orb-physics proximity + degree model', desc:'Needs-exploration: connection strength (direct + mutuals + shared projects/guilds + co-shipped; no time-decay) feeding orb-world physics (goals 1000044/1000049); cluster detection. NOTE cross-goal dependency on orb-physics infra. Output an ADR + sub-tasks. Deps R19,R23.' },
|
|
78
|
+
{ ref:'R34', kind:'feature', disc:'engineer', prio:3, est:210, crit:['C10'], deps:['R33'], title:'R34 · Connection-strength + degree + cluster compute API', desc:'Compute connection strength + degrees + close-knit clusters; feeds orb physics + recruiting. Deps R33.' },
|
|
79
|
+
{ ref:'R35', kind:'feature', disc:'ui', prio:3, est:180, crit:['C10'], deps:['R34','R25'], title:'R35 · Builder-side: clusters in the orb sky → invite a close cluster to a guild', desc:'Close connections cluster in the orb sky; invite a cluster to form a guild from it. Deps R34,R25.' },
|
|
80
|
+
{ ref:'R36', kind:'feature', disc:'ui', prio:3, est:180, crit:['C10'], deps:['R34','R16'], title:'R36 · Project-side: recruit a close-knit cluster together', desc:'Surface close-knit clusters on the recruiting surface; recruit them together. Deps R34,R16.' },
|
|
81
|
+
{ ref:'R37', kind:'verify', disc:'engineer', prio:3, est:120, crit:['C10'], deps:['R35','R36'], title:'R37 · PROOF: cluster forms a guild from the sky; owner recruits a cluster', desc:'End-to-end verify of the proximity-driven guild-forming + cluster-recruiting. Deps R35,R36.' },
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const ROOTS = ['R01', 'R07', 'R18']; // zero-dep entry spikes to open for claiming
|
|
85
|
+
|
|
86
|
+
(async () => {
|
|
87
|
+
const idByRef = {};
|
|
88
|
+
let created = 0, existing = 0;
|
|
89
|
+
console.log(`Seeding ${TASKS.length} tasks under goal ${GOAL} (${VER})…`);
|
|
90
|
+
for (const t of TASKS) {
|
|
91
|
+
const r = await api('POST', '/tasks', {
|
|
92
|
+
version_id: VER, goal_id: GOAL, title: t.title, description: t.desc,
|
|
93
|
+
kind: t.kind, discipline: t.disc, priority: t.prio, est_minutes: t.est,
|
|
94
|
+
criterion_ids: t.crit, source: SRC, source_ref: `bv1-profiles-${t.ref}`,
|
|
95
|
+
});
|
|
96
|
+
if (!r.ok) { console.log(` ${t.ref}: FAILED ${r.status} ${JSON.stringify(r.json)}`); continue; }
|
|
97
|
+
const task = r.json.task || r.json;
|
|
98
|
+
idByRef[t.ref] = task.id;
|
|
99
|
+
if (r.json.idempotent || r.json.existing) existing++; else created++;
|
|
100
|
+
console.log(` ${t.ref} → task ${task.id} (${t.kind}/${t.disc}, P${t.prio}, [${t.crit.join(',')}])`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
console.log('Wiring dependencies…');
|
|
104
|
+
let edges = 0;
|
|
105
|
+
for (const t of TASKS) {
|
|
106
|
+
if (!t.deps.length) continue;
|
|
107
|
+
const id = idByRef[t.ref];
|
|
108
|
+
const depIds = t.deps.map((d) => idByRef[d]).filter(Boolean);
|
|
109
|
+
if (!id || depIds.length !== t.deps.length) { console.log(` ${t.ref}: dep resolve gap, skipping`); continue; }
|
|
110
|
+
const r = await api('POST', `/tasks/${id}/dependencies`, { depends_on_task_ids: depIds });
|
|
111
|
+
if (!r.ok) { console.log(` ${t.ref}: dep FAILED ${r.status} ${JSON.stringify(r.json)}`); continue; }
|
|
112
|
+
edges += (r.json.added || depIds.length);
|
|
113
|
+
console.log(` ${t.ref} depends on ${t.deps.join(',')}`);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
console.log('Promoting root spikes to ready…');
|
|
117
|
+
for (const ref of ROOTS) {
|
|
118
|
+
const id = idByRef[ref];
|
|
119
|
+
if (!id) continue;
|
|
120
|
+
const r = await api('POST', `/tasks/${id}/promote`, {});
|
|
121
|
+
console.log(` ${ref} (task ${id}): ${r.ok ? 'ready' : 'promote note ' + JSON.stringify(r.json).slice(0,120)}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
console.log(`\nDONE. created=${created} existing=${existing} depEdges=${edges}. Roots opened: ${ROOTS.join(', ')}.`);
|
|
125
|
+
console.log('ref→id:', JSON.stringify(idByRef));
|
|
126
|
+
})();
|
|
@@ -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
|
|
@@ -452,7 +478,7 @@ async function promptOnboardingConsent() {
|
|
|
452
478
|
if (poll.data && poll.data.state === 'not_approved') {
|
|
453
479
|
console.error('');
|
|
454
480
|
console.error('This GitHub account has not been approved yet.');
|
|
455
|
-
console.error('
|
|
481
|
+
console.error(`${require('./cli-lib').productName()} admits new builders by invitation: an Archon must`);
|
|
456
482
|
console.error('approve your access request before you can enroll.');
|
|
457
483
|
console.error(' → Ask an Archon to invite you / approve your access request,');
|
|
458
484
|
console.error(' then re-run /builder-setup (or: node scripts/gds/setup.js).');
|
|
@@ -526,7 +552,7 @@ async function promptOnboardingConsent() {
|
|
|
526
552
|
printOnboardingNudge(meRes.data);
|
|
527
553
|
console.log('');
|
|
528
554
|
console.log('Newcomer path:');
|
|
529
|
-
console.log(
|
|
555
|
+
console.log(` 1. Choose thy craft: open ${hallBase()} (creative / engineering)`);
|
|
530
556
|
console.log(' 2. Survey work: /builder-start');
|
|
531
557
|
console.log(' 3. Claim + ship: /builder-claim N → /builder-ship');
|
|
532
558
|
console.log(' 4. Ship three works and you rise to Thetes — the whole queue opens.');
|