@bongos/core 1.18.3 → 1.18.19
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 +1576 -286
- 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/migrate.sh +99 -14
- 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 +124 -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/migrate_root_resolution.mjs +124 -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/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
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' });
|
package/scripts/gds/ship.js
CHANGED
|
@@ -31,6 +31,13 @@
|
|
|
31
31
|
// # empty-diff guard; confirms directly (no diff to
|
|
32
32
|
// # grade). REFUSED if the working tree is dirty —
|
|
33
33
|
// # that means you forgot to commit (task 1363/1365).
|
|
34
|
+
// node scripts/gds/ship.js <task-id> --base <ref> # grade a task whose commits ALREADY landed on main
|
|
35
|
+
// # (e.g. a release task: package-core needs the merge
|
|
36
|
+
// # first). Pins <ref> as the pre-task baseline so the
|
|
37
|
+
// # grader reads the REAL diff <ref>...HEAD and confirms
|
|
38
|
+
// # via the RANK-OPEN graded path — the sub-Archon-safe
|
|
39
|
+
// # alternative to --allow-empty (task 1002422 / blocker
|
|
40
|
+
// # 1000082). Implies db-only (nothing left to merge).
|
|
34
41
|
// node scripts/gds/ship.js <task-id> --regrade # re-grade a task STUCK at `completed` (a ship
|
|
35
42
|
// # whose grade failed): re-runs ONLY the grade step
|
|
36
43
|
// # against the committed diff, no claim required.
|
|
@@ -54,7 +61,20 @@ const fs = require('node:fs');
|
|
|
54
61
|
const os = require('node:os');
|
|
55
62
|
const path = require('node:path');
|
|
56
63
|
const readline = require('node:readline');
|
|
57
|
-
const { cliClient, requireSession, loadSession, printOnboardingNudge, arg } = require('./cli-lib');
|
|
64
|
+
const { cliClient, requireSession, loadSession, loadSessionSync, printOnboardingNudge, arg, rewardPolicy } = require('./cli-lib');
|
|
65
|
+
|
|
66
|
+
// The instance's reward policy, as reported by the server on GET /me (ADR 0146 /
|
|
67
|
+
// task 1002469). Cached module-level because the credits print + the completion
|
|
68
|
+
// card are reached through several call chains and the mode is a single
|
|
69
|
+
// instance-wide fact — threading it through each would be noise. Stays null
|
|
70
|
+
// until a /me lands; rewardPolicy(null) then degrades to the paying default, so
|
|
71
|
+
// a missed fetch under-explains rather than wrongly claiming "not paid".
|
|
72
|
+
let serverReward = null;
|
|
73
|
+
function noteServerReward(meRes) {
|
|
74
|
+
const r = meRes && meRes.ok && meRes.data && meRes.data.reward;
|
|
75
|
+
if (r && typeof r.mode === 'string') serverReward = r;
|
|
76
|
+
return meRes;
|
|
77
|
+
}
|
|
58
78
|
// ship-preflight + merge-lock carved to modules/lifecycle/ (BV1.R86). A script may
|
|
59
79
|
// require a module directly (it's outside the kernel boundary — the grading/economy
|
|
60
80
|
// CLI precedent).
|
|
@@ -203,8 +223,14 @@ function claudeConfigBase() {
|
|
|
203
223
|
}
|
|
204
224
|
|
|
205
225
|
function encodeProjectPath(absProjectPath) {
|
|
206
|
-
// Claude Code's encoding:
|
|
207
|
-
|
|
226
|
+
// Claude Code's encoding: every NON-ALPHANUMERIC char becomes '-'. The old
|
|
227
|
+
// `[/.]` class silently no-op'd on Windows (separator '\', drive ':', spaces),
|
|
228
|
+
// so the transcript was never found, no session_record was written, and the
|
|
229
|
+
// cost-plus reward — the only stream a cost-plus-only instance pays (ADR 0146)
|
|
230
|
+
// — could never book: every Windows ship paid 0 (task 1002471). Same rule as
|
|
231
|
+
// scripts/gds/onboarding-config.js, which already had it right. POSIX output is
|
|
232
|
+
// unchanged for paths whose only specials are '/' and '.'.
|
|
233
|
+
return String(absProjectPath).replace(/[^a-zA-Z0-9]/g, '-');
|
|
208
234
|
}
|
|
209
235
|
|
|
210
236
|
// resolveTranscriptPath — find this session's transcript .jsonl. Claude Code
|
|
@@ -327,7 +353,11 @@ const {
|
|
|
327
353
|
// snippetOf — short, single-line, scrubbed excerpt for footgun context.
|
|
328
354
|
function snippetOf(text, extraSecrets, max = 160) {
|
|
329
355
|
if (typeof text !== 'string' || text.length === 0) return undefined;
|
|
330
|
-
|
|
356
|
+
// Drop NULs first: Postgres rejects a literal NUL in jsonb, so ONE of them anywhere in
|
|
357
|
+
// ONE snippet made the whole digest unstorable — POST /sessions answered 500 and
|
|
358
|
+
// the session never paid. UTF-16LE tool output (`wsl --list` is the observed
|
|
359
|
+
// case) is full of them, and \s below does NOT match NUL, so it must go here.
|
|
360
|
+
const flat = text.replace(/\0/g, '').replace(/\s+/g, ' ').trim();
|
|
331
361
|
if (!flat) return undefined;
|
|
332
362
|
const cut = flat.length > max ? flat.slice(0, max) : flat;
|
|
333
363
|
const scrubbed = scrubSecrets(cut, extraSecrets);
|
|
@@ -859,6 +889,54 @@ function safeBranding() {
|
|
|
859
889
|
}
|
|
860
890
|
}
|
|
861
891
|
|
|
892
|
+
// readSessionApiBase — the signed-in session's persisted api_base (the instance the
|
|
893
|
+
// CLI is actually pointed at), or null when not signed in / unreadable. This is the
|
|
894
|
+
// session's OWN value only — deliberately NOT readSessionToken().apiBase, which
|
|
895
|
+
// already folds in the env override + branding default; resolveInstanceBase orders
|
|
896
|
+
// that precedence itself so it stays pure over its injected inputs.
|
|
897
|
+
function readSessionApiBase() {
|
|
898
|
+
try {
|
|
899
|
+
const s = loadSessionSync();
|
|
900
|
+
return (s && typeof s.api_base === 'string' && s.api_base.trim()) ? s.api_base.trim() : null;
|
|
901
|
+
} catch (_) {
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// resolveInstanceBase — the ORIGIN of the instance this ship is landing on, used by
|
|
907
|
+
// the ci-land deployed-version probe (/version), the /healthz re-probe, the ci
|
|
908
|
+
// PR-body deploy note, and the manual-confirm curl guidance. Precedence mirrors the
|
|
909
|
+
// CLI's own base resolution: <PREFIX>_API_BASE env → the signed-in session's
|
|
910
|
+
// api_base → branding publicOrigin → null (unknowable). There is NO hardcoded
|
|
911
|
+
// founder host — 5 sites here baked in `https://amazonprimea.com`, so a ci-land /
|
|
912
|
+
// manual-confirm on a migrated instance pointed the builder at the WRONG box; the
|
|
913
|
+
// ci-land/guidance sibling of resolveDeployTarget()'s laptop-mode fix (task 1002232
|
|
914
|
+
// ⊃ task 1002202). Pure over its injected inputs (env / sessionApiBase / branding)
|
|
915
|
+
// so the precedence is unit-tested without fs/env/network; the defaults read the
|
|
916
|
+
// real session file + brand pack.
|
|
917
|
+
function resolveInstanceBase({
|
|
918
|
+
env = process.env,
|
|
919
|
+
sessionApiBase = readSessionApiBase(),
|
|
920
|
+
branding = safeBranding(),
|
|
921
|
+
} = {}) {
|
|
922
|
+
const ic = require('../../src/instance-config');
|
|
923
|
+
const trim = (s) => (typeof s === 'string' && s.trim() ? s.trim().replace(/\/+$/, '') : null);
|
|
924
|
+
const envBase = trim(ic.resolveEnv('API_BASE', { env }));
|
|
925
|
+
if (envBase) return envBase;
|
|
926
|
+
const sess = trim(sessionApiBase);
|
|
927
|
+
if (sess) return sess;
|
|
928
|
+
const origin = branding && branding.domains && branding.domains.publicOrigin;
|
|
929
|
+
return trim(origin);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
// deployedVersionUrl — the instance's /version endpoint, or null when no instance
|
|
933
|
+
// origin is resolvable (then fetchDeployedVersion is a no-op). Derived from
|
|
934
|
+
// resolveInstanceBase — no hardcoded host (task 1002232).
|
|
935
|
+
function deployedVersionUrl() {
|
|
936
|
+
const base = resolveInstanceBase();
|
|
937
|
+
return base ? `${base}/version` : null;
|
|
938
|
+
}
|
|
939
|
+
|
|
862
940
|
// resolveDeployTarget — WHERE a laptop-mode ship SSHes `~/deploy.sh`, and which
|
|
863
941
|
// /healthz it re-probes when deploy.sh's own health check races the restart window.
|
|
864
942
|
// Pure (env + raw config + branding → target) and exported so the precedence and the
|
|
@@ -949,7 +1027,10 @@ function deployedCommitStatus(versionBody, mergeSha) {
|
|
|
949
1027
|
}
|
|
950
1028
|
|
|
951
1029
|
// Fetch + parse the prod /version JSON (#858). null on any failure — never throws.
|
|
952
|
-
|
|
1030
|
+
// Default target is the resolved instance origin (no hardcoded host — task 1002232);
|
|
1031
|
+
// null when none is resolvable, in which case the probe is skipped.
|
|
1032
|
+
function fetchDeployedVersion(url = deployedVersionUrl()) {
|
|
1033
|
+
if (!url) return null; // no resolvable instance origin → nothing to probe
|
|
953
1034
|
// stdio override DISCARDS stderr (a curl progress/error line must not pollute
|
|
954
1035
|
// the JSON parse) — the documented per-caller difference from ghJson.
|
|
955
1036
|
const out = argvOut('curl', ['-fsS', '--max-time', '10', url], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
@@ -1092,11 +1173,15 @@ async function ciLandServer(taskId, branch, valueSummary) {
|
|
|
1092
1173
|
// Pure + exported for unit tests (no git/network). Accepts the publish-status
|
|
1093
1174
|
// response shape verbatim — getPublishStatus returns snake_case (pr_state,
|
|
1094
1175
|
// deploy_status); camelCase aliases are tolerated for test convenience.
|
|
1176
|
+
// task 1002472: 'unconfigured' means the repo has no deploy-prod.yml to wait for
|
|
1177
|
+
// (ADR 0108 pin-bump publishing), so the merge IS the land. Mirrors
|
|
1178
|
+
// github-push.landConfirmed — kept in step by tests/ship_ci_deploy.mjs, since this
|
|
1179
|
+
// CLI cannot import the server module.
|
|
1095
1180
|
function classifyLandOutcome(s) {
|
|
1096
1181
|
const merged = !!(s && s.merged);
|
|
1097
1182
|
const deployStatus = s && (s.deploy_status ?? s.deployStatus);
|
|
1098
1183
|
const prState = s && (s.pr_state ?? s.prState);
|
|
1099
|
-
if (merged && deployStatus === 'success') return 'shipped';
|
|
1184
|
+
if (merged && (deployStatus === 'success' || deployStatus === 'unconfigured')) return 'shipped';
|
|
1100
1185
|
if (prState === 'closed' && !merged) return 'failed';
|
|
1101
1186
|
return 'pending';
|
|
1102
1187
|
}
|
|
@@ -1281,11 +1366,12 @@ async function ciLandLocal(taskId, branch, valueSummary) {
|
|
|
1281
1366
|
// GitHub's PR/issue number space (dead links for GDS task ids — task 916),
|
|
1282
1367
|
// so the task ref is written as "task NNN" and the summary is de-#-ed.
|
|
1283
1368
|
const title = `task ${taskId}: ${sanitizeGitHubRefs(valueSummary) || branch}`.slice(0, 240);
|
|
1369
|
+
const deployTargetNote = resolveInstanceBase() || 'the configured instance';
|
|
1284
1370
|
const body =
|
|
1285
1371
|
`Auto-opened by ship.js (deploy mode: ci) for task ${taskId}.\n\n` +
|
|
1286
1372
|
`${sanitizeGitHubRefs(valueSummary) || '(no value summary)'}\n\n` +
|
|
1287
1373
|
`Merging this PR triggers .github/workflows/deploy-prod.yml to deploy to ` +
|
|
1288
|
-
|
|
1374
|
+
`${deployTargetNote}. Auto-merge is enabled — GitHub lands it once the ` +
|
|
1289
1375
|
`required checks pass.`;
|
|
1290
1376
|
if (!existing || existing.state === 'CLOSED' || existing.state === 'MERGED') {
|
|
1291
1377
|
console.log(' ci-land: opening PR -> main...');
|
|
@@ -1367,12 +1453,14 @@ function pollCiDeploy(taskId, branch, headSha) {
|
|
|
1367
1453
|
// real deploy into a false negative on a transient miss or a concurrent
|
|
1368
1454
|
// later deploy).
|
|
1369
1455
|
const status = deployedCommitStatus(fetchDeployedVersion(), mergeSha);
|
|
1456
|
+
const instanceBase = resolveInstanceBase();
|
|
1457
|
+
const healthzUrl = instanceBase ? `${instanceBase}/healthz` : null;
|
|
1370
1458
|
if (status === 'match') {
|
|
1371
1459
|
console.log(' ci-land: /version confirms prod is live on the merge commit (commit-pinned).');
|
|
1372
1460
|
} else if (status === 'mismatch') {
|
|
1373
1461
|
console.log(' ci-land: deploy run succeeded; /version shows a different commit (a later deploy has since superseded it — fine, our change is in main).');
|
|
1374
|
-
} else if (shellOk(
|
|
1375
|
-
console.log(
|
|
1462
|
+
} else if (healthzUrl && shellOk(`curl -fsS --max-time 10 ${healthzUrl} -o /dev/null`)) {
|
|
1463
|
+
console.log(` ci-land: /version unavailable; ${healthzUrl} 200 — treating the successful deploy run as authoritative.`);
|
|
1376
1464
|
} else {
|
|
1377
1465
|
console.log(' ci-land: deploy run succeeded but /version + healthz probes both missed (likely transient). Treating the successful deploy run as authoritative.');
|
|
1378
1466
|
}
|
|
@@ -1397,6 +1485,16 @@ function pollCiDeploy(taskId, branch, headSha) {
|
|
|
1397
1485
|
// Also refuses when the branch is far behind origin/main — merging a stale
|
|
1398
1486
|
// branch is a known regression vector.
|
|
1399
1487
|
|
|
1488
|
+
// parseDeletedUpstreamPaths — pure: turn `git log --diff-filter=D --name-only
|
|
1489
|
+
// --pretty=format:` output (one deleted path per line, blank lines between
|
|
1490
|
+
// commits) into a deduped, order-preserving list. Exported so preflight's
|
|
1491
|
+
// upstream-deletion warning (step (d)) is unit-tested without a live git repo.
|
|
1492
|
+
// Null/empty input (no matches, or the git call failed closed) → [].
|
|
1493
|
+
function parseDeletedUpstreamPaths(rawLogOutput) {
|
|
1494
|
+
if (!rawLogOutput) return [];
|
|
1495
|
+
return [...new Set(rawLogOutput.split('\n').map((s) => s.trim()).filter(Boolean))];
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1400
1498
|
function preflight(claim, opts = {}) {
|
|
1401
1499
|
// (a) Working-tree check — refuse if any TRACKED file carries an uncommitted
|
|
1402
1500
|
// modification or staged change. This fires post-commit, so a clean tree
|
|
@@ -1469,6 +1567,39 @@ function preflight(claim, opts = {}) {
|
|
|
1469
1567
|
}
|
|
1470
1568
|
}
|
|
1471
1569
|
|
|
1570
|
+
// (d) Upstream-deletion warning (WARN-ONLY — never blocks, never changes the
|
|
1571
|
+
// exit code). main can delete or relocate a surface between when a task was
|
|
1572
|
+
// claimed and when it ships (a cutover, a carve, a rename — the R89 hall-ui
|
|
1573
|
+
// cutover class); the branch's own diff still touches the OLD path, and an
|
|
1574
|
+
// oblivious ship can silently revert the upstream deletion or leave this
|
|
1575
|
+
// branch's work orphaned. Scoped to the paths THIS branch's diff actually
|
|
1576
|
+
// touches — the same baseline/diff computeGraderBaseline/computeChangedFiles
|
|
1577
|
+
// compute for the grader — so an unrelated upstream deletion elsewhere in the
|
|
1578
|
+
// repo never fires it. Silent no-op if origin/main isn't resolvable (offline /
|
|
1579
|
+
// never fetched) or this branch's diff is empty — the check itself must never
|
|
1580
|
+
// be why a ship breaks. parseDeletedUpstreamPaths is the pure parsing seam,
|
|
1581
|
+
// exported + unit-tested (tests/ship_upstream_deletion.mjs) without a live git
|
|
1582
|
+
// repo; this block is just the git I/O + presentation around it.
|
|
1583
|
+
const upstreamDelBaseline = computeGraderBaseline(claim);
|
|
1584
|
+
const upstreamDelTouched = upstreamDelBaseline ? computeChangedFiles(upstreamDelBaseline) : [];
|
|
1585
|
+
if (upstreamDelTouched.length > 0) {
|
|
1586
|
+
const deletedUpstreamStr = argvOut('git', [
|
|
1587
|
+
'log', 'HEAD..origin/main', '--diff-filter=D', '--name-only', '--pretty=format:',
|
|
1588
|
+
'--', ...upstreamDelTouched,
|
|
1589
|
+
]);
|
|
1590
|
+
const deletedUpstream = parseDeletedUpstreamPaths(deletedUpstreamStr);
|
|
1591
|
+
if (deletedUpstream.length > 0) {
|
|
1592
|
+
console.error('');
|
|
1593
|
+
console.error(`⚠ Upstream deletion warning: main has removed ${deletedUpstream.length} path(s) this branch still touches, since this task was claimed:`);
|
|
1594
|
+
for (const f of deletedUpstream) console.error(` - ${f}`);
|
|
1595
|
+
console.error(' This looks like the R89 hall-ui cutover class: a surface removed or relocated upstream while the claim was in flight.');
|
|
1596
|
+
console.error(' Verify these paths still exist on main (or that the removal/rename is intentional) before shipping —');
|
|
1597
|
+
console.error(" shipping anyway may silently revert the upstream deletion or leave this branch's work orphaned.");
|
|
1598
|
+
console.error(' Warning only — not blocking; the ship proceeds.');
|
|
1599
|
+
console.error('');
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1472
1603
|
console.log(` preflight: working tree clean, branch ${behind} commits behind main, ship tooling current — OK`);
|
|
1473
1604
|
return { ok: true };
|
|
1474
1605
|
}
|
|
@@ -1645,13 +1776,80 @@ function autoLinkifyChangedDocs(ac) {
|
|
|
1645
1776
|
// full unified diff against the baseline, and (for the audit-trail signals)
|
|
1646
1777
|
// the shortstat summary.
|
|
1647
1778
|
//
|
|
1779
|
+
// task 1002422 (blocker 1000082): a builder-settable pre-task baseline. When a
|
|
1780
|
+
// task's artifact reached main BEFORE the ship (a release task: package-core
|
|
1781
|
+
// archives committed HEAD, so the version bump must merge first), merge-base(
|
|
1782
|
+
// origin/main, HEAD) == HEAD → an EMPTY committed diff → the --allow-empty /
|
|
1783
|
+
// skip-grade / Archon-only /confirm dead-end that NO sub-Archon builder can
|
|
1784
|
+
// finish. --base <ref> pins the pre-task commit so the grader reads the REAL diff
|
|
1785
|
+
// <base>...HEAD and the ship confirms via the rank-open graded path instead. Set
|
|
1786
|
+
// once in main() after resolveBaseRef() validates it; null on a normal ship.
|
|
1787
|
+
let baseOverrideSha = null;
|
|
1788
|
+
|
|
1789
|
+
// resolveBaseRef — validate + resolve a --base <ref> to a commit sha. Injection-
|
|
1790
|
+
// safe: the ref reaches git as a literal argv element (execFile, no shell), never
|
|
1791
|
+
// a string interpolation. Pure over injected git runners so the unit test drives
|
|
1792
|
+
// it with fakes (no real repo). Returns { ok:true, sha } or { ok:false, reason }.
|
|
1793
|
+
function resolveBaseRef(rawBase, { revParse, isAncestor } = {}) {
|
|
1794
|
+
const ref = String(rawBase == null ? '' : rawBase).trim();
|
|
1795
|
+
if (!ref) return { ok: false, reason: 'empty ref' };
|
|
1796
|
+
// Conservative allowlist (defence-in-depth even though transport is argv): no
|
|
1797
|
+
// whitespace, no leading dash (git would read it as a flag), no shell metachar.
|
|
1798
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._/~^-]*$/.test(ref)) {
|
|
1799
|
+
return { ok: false, reason: `invalid ref syntax "${ref}"` };
|
|
1800
|
+
}
|
|
1801
|
+
const rp = revParse || ((r) => argvOut('git', ['rev-parse', '--verify', '--quiet', `${r}^{commit}`]));
|
|
1802
|
+
const sha = rp(ref);
|
|
1803
|
+
if (!sha) return { ok: false, reason: `"${ref}" is not a commit this repo knows` };
|
|
1804
|
+
const anc = isAncestor || ((s) => gitArgvOk(['merge-base', '--is-ancestor', s, 'HEAD']));
|
|
1805
|
+
if (!anc(sha)) {
|
|
1806
|
+
return { ok: false, reason: `"${ref}" (${sha.slice(0, 9)}) is not an ancestor of HEAD — --base must be a commit your work built on top of` };
|
|
1807
|
+
}
|
|
1808
|
+
return { ok: true, sha };
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
// baseRefShipGuard — the SECURITY invariant behind --base. --base decouples the
|
|
1812
|
+
// GRADED diff (<base>...HEAD) from what the merge leg lands (the whole branch:
|
|
1813
|
+
// autoMerge runs `git merge <branch>`, bringing every commit ahead of main, not
|
|
1814
|
+
// just <base>...HEAD). So if HEAD has commits AHEAD of origin/main, honoring
|
|
1815
|
+
// --base would let the grader review only a SUBSET while the full branch merges —
|
|
1816
|
+
// an unreviewed-code path onto main. --base is therefore ONLY valid for work
|
|
1817
|
+
// ALREADY fully on main (its documented premise: "nothing left to merge"). Pure
|
|
1818
|
+
// over the ahead-count string (git rev-list --count origin/main..HEAD) so it is
|
|
1819
|
+
// unit-tested without a repo. '0' → allowed; anything else (or null) → refuse.
|
|
1820
|
+
function baseRefShipGuard(aheadCount) {
|
|
1821
|
+
if (aheadCount === '0') return { ok: true };
|
|
1822
|
+
const n = aheadCount == null ? 'an unknown number of' : aheadCount;
|
|
1823
|
+
return { ok: false, reason: `HEAD is ${n} commit(s) ahead of origin/main — --base is only for work already merged to main` };
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// applyBaseOverride — the SINGLE source of truth for --base validation, shared by
|
|
1827
|
+
// shipMain + regradeMain so the security guard can never diverge between entry
|
|
1828
|
+
// points. Resolves the ref, enforces the 0-ahead invariant, and sets the module-
|
|
1829
|
+
// level baseOverrideSha on success. Returns { ok:true, sha } | { ok:false, reason }
|
|
1830
|
+
// (the caller prints + exits). runGit is injectable for tests.
|
|
1831
|
+
function applyBaseOverride(baseRefRaw, { runGit = gitOk } = {}) {
|
|
1832
|
+
const r = resolveBaseRef(baseRefRaw);
|
|
1833
|
+
if (!r.ok) return { ok: false, reason: `--base ${baseRefRaw} is unusable (${r.reason})` };
|
|
1834
|
+
// Freshen origin/main (best-effort; a stale ref only OVER-counts → errs to refuse).
|
|
1835
|
+
runGit('git fetch origin main --quiet');
|
|
1836
|
+
const guard = baseRefShipGuard(runGit('git rev-list --count origin/main..HEAD'));
|
|
1837
|
+
if (!guard.ok) return { ok: false, reason: guard.reason };
|
|
1838
|
+
baseOverrideSha = r.sha;
|
|
1839
|
+
return { ok: true, sha: r.sha };
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1648
1842
|
// #536: the grader's baseline is computed INDEPENDENTLY of touches[] — always
|
|
1649
1843
|
// from merge-base, never gated on the declared scope. (Historically the
|
|
1650
1844
|
// touches-gated drift scanner returned a null baseline for empty touches[],
|
|
1651
1845
|
// which once blinded the grader to a fully-committed diff and hard-FAILed
|
|
1652
1846
|
// empty-touches newcomer ships; the scanner is gone as of task 879, but the
|
|
1653
1847
|
// grader keeping its own merge-base baseline remains the correct invariant.)
|
|
1654
|
-
|
|
1848
|
+
// baseSha (task 1002422): a validated --base override wins over merge-base, so a
|
|
1849
|
+
// task whose commits already landed on main still grades its real diff instead of
|
|
1850
|
+
// seeing an empty one.
|
|
1851
|
+
function computeGraderBaseline(claim, baseSha = baseOverrideSha) {
|
|
1852
|
+
if (baseSha) return baseSha;
|
|
1655
1853
|
return (
|
|
1656
1854
|
gitOk('git merge-base origin/main HEAD') ||
|
|
1657
1855
|
gitOk('git merge-base main HEAD') ||
|
|
@@ -1739,7 +1937,7 @@ async function runSubagentGrader({ task, valueSummary, baseline, changedFiles: p
|
|
|
1739
1937
|
// fail-closed layers).
|
|
1740
1938
|
let builderRank;
|
|
1741
1939
|
try {
|
|
1742
|
-
const meRes = await (await cliClient()).me.getMe();
|
|
1940
|
+
const meRes = noteServerReward(await (await cliClient()).me.getMe());
|
|
1743
1941
|
if (meRes.ok) builderRank = meRes.data?.builder?.rank;
|
|
1744
1942
|
} catch (_) { /* unknown rank → pre-pass fails open; audit + hook still gate */ }
|
|
1745
1943
|
const runOpts = {
|
|
@@ -1920,6 +2118,23 @@ function regenerateApiDocs() {
|
|
|
1920
2118
|
// (not fail-if-forgotten): before this, ship regenerated the spec but not the client,
|
|
1921
2119
|
// so a route change left the client stale until the api_client test caught it in CI.
|
|
1922
2120
|
// Non-fatal + safe on every ship path; the api_client freshness test backstops main.
|
|
2121
|
+
|
|
2122
|
+
// Where the devbox Electron app vendors the CJS client (ADR 0045). It lives inside the
|
|
2123
|
+
// dev-box MODULE (ADR 0145) — an instance that ships without that module has no copy.
|
|
2124
|
+
const VENDORED_CLIENT_REL = 'modules/dev-box/app/src/vendor/bongos-client.cjs';
|
|
2125
|
+
|
|
2126
|
+
// The git pathspec for the regenerated client. Pure + exported so the rule is unit-tested
|
|
2127
|
+
// with no git/fs (task 1002417). `git status` tolerates an unmatched pathspec (exit 0) but
|
|
2128
|
+
// `git add` aborts the ENTIRE staging step (exit 128), which leaves the regenerated client
|
|
2129
|
+
// unstaged and the worktree dirty — and a dirty worktree makes the ship's auto-merge bail,
|
|
2130
|
+
// stranding the task at `confirmed` with no PR. So the vendored copy is named ONLY when it
|
|
2131
|
+
// is actually on disk.
|
|
2132
|
+
function apiClientPathspec({ worktreeRoot, existsSync = fs.existsSync } = {}) {
|
|
2133
|
+
const paths = ['clients/bongos-client'];
|
|
2134
|
+
if (existsSync(path.join(worktreeRoot, VENDORED_CLIENT_REL))) paths.push(VENDORED_CLIENT_REL);
|
|
2135
|
+
return paths;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
1923
2138
|
function regenerateApiClient() {
|
|
1924
2139
|
console.log(' regenerating typed API client (clients/bongos-client) from the OpenAPI spec...');
|
|
1925
2140
|
if (!shellOk(`"${process.execPath}" ${JSON.stringify(path.join(__dirname, 'gen-api-client.js'))}`)) {
|
|
@@ -1928,23 +2143,28 @@ function regenerateApiClient() {
|
|
|
1928
2143
|
}
|
|
1929
2144
|
// The devbox Electron app can't require() clients/ at runtime, so it vendors the CJS
|
|
1930
2145
|
// build (ADR 0045); keep the copy byte-identical (the api_client test asserts it).
|
|
1931
|
-
// Present only
|
|
2146
|
+
// Present only where the dev-box module ships — an instance without it has no vendored
|
|
2147
|
+
// copy, so both the vendor step and the git pathspec below drop out (task 1002417: the
|
|
2148
|
+
// app moved from top-level devbox-app/ to modules/dev-box/app/ per ADR 0145, and these
|
|
2149
|
+
// paths were not updated, so the guard below silently never fired).
|
|
1932
2150
|
// Resolve it WORKTREE-LOCALLY (like the generator above, __dirname=scripts/gds), NOT
|
|
1933
2151
|
// via REPO_ROOT: on a ship from a linked worktree REPO_ROOT is the MAIN checkout, so a
|
|
1934
2152
|
// REPO_ROOT path would vendor the main tree while gen-api-client just wrote THIS tree,
|
|
1935
2153
|
// leaving this worktree's vendored copy stale → the api_client byte-identity test reds
|
|
1936
2154
|
// the PR (task 2060 fix). __dirname keeps the generator, the vendored file, and the
|
|
1937
2155
|
// git add all on the same tree.
|
|
1938
|
-
const
|
|
2156
|
+
const worktreeRoot = path.join(__dirname, '..', '..');
|
|
2157
|
+
const vendorScript = path.join(worktreeRoot, 'modules', 'dev-box', 'app', 'scripts', 'vendor-client.js');
|
|
1939
2158
|
if (fs.existsSync(vendorScript)) {
|
|
1940
2159
|
shellOk(`"${process.execPath}" ${JSON.stringify(vendorScript)}`);
|
|
1941
2160
|
}
|
|
1942
|
-
const
|
|
2161
|
+
const clientPathspec = apiClientPathspec({ worktreeRoot }).join(' ');
|
|
2162
|
+
const dirty = gitOk(`git status --porcelain ${clientPathspec}`);
|
|
1943
2163
|
if (!dirty) {
|
|
1944
2164
|
console.log(' API client already current — nothing to commit');
|
|
1945
2165
|
return 'current';
|
|
1946
2166
|
}
|
|
1947
|
-
shellOk(
|
|
2167
|
+
shellOk(`git add ${clientPathspec}`);
|
|
1948
2168
|
if (shellOk('git commit -m "chore(api-client): auto-regenerate typed client + devbox vendor (task 2051)"')) {
|
|
1949
2169
|
console.log(' committed auto-regenerated API client');
|
|
1950
2170
|
return 'committed';
|
|
@@ -2426,7 +2646,7 @@ function formatGradeOutcome(g, taskId) {
|
|
|
2426
2646
|
out.push('When the grader is reachable again, re-grade:');
|
|
2427
2647
|
out.push(` node scripts/gds/ship.js ${taskId} --regrade`);
|
|
2428
2648
|
out.push('Or land it now via manual-confirm:');
|
|
2429
|
-
out.push(` curl -X POST -H "Authorization: Bearer <token>"
|
|
2649
|
+
out.push(` curl -X POST -H "Authorization: Bearer <token>" ${resolveInstanceBase() || '<instance-origin>'}/api/gds/tasks/${taskId}/confirm`);
|
|
2430
2650
|
return out.join('\n');
|
|
2431
2651
|
}
|
|
2432
2652
|
|
|
@@ -2453,7 +2673,7 @@ function formatGradeOutcome(g, taskId) {
|
|
|
2453
2673
|
out.push('Grade failed — task stays at completed. Fix the issues above, commit, then re-grade:');
|
|
2454
2674
|
out.push(` node scripts/gds/ship.js ${taskId} --regrade`);
|
|
2455
2675
|
out.push('Last resort (only if the grade was wrong):');
|
|
2456
|
-
out.push(` curl -X POST -H "Authorization: Bearer <token>"
|
|
2676
|
+
out.push(` curl -X POST -H "Authorization: Bearer <token>" ${resolveInstanceBase() || '<instance-origin>'}/api/gds/tasks/${taskId}/confirm`);
|
|
2457
2677
|
}
|
|
2458
2678
|
return out.join('\n');
|
|
2459
2679
|
}
|
|
@@ -2473,7 +2693,14 @@ function printCreditsAndAchievements({ postGradeStatus, credits, creditsRaw, cre
|
|
|
2473
2693
|
if (postGradeStatus === 'completed') {
|
|
2474
2694
|
console.log(`Credits awarded: ${credits} (none — task at completed, grade not passed)`);
|
|
2475
2695
|
} else if (postGradeStatus === 'confirmed') {
|
|
2476
|
-
console.log(`Credits awarded: ${credits} (
|
|
2696
|
+
console.log(`Credits awarded: ${credits} (none yet — credits land when the task merges to main)`);
|
|
2697
|
+
} else if (!rewardPolicy(serverReward).perTaskCredit) {
|
|
2698
|
+
// cost-plus-only (ADR 0146): the per-task estimate is never paid, so this is
|
|
2699
|
+
// 0 BY DESIGN. Left unexplained it reads as a broken payout — it sent a real
|
|
2700
|
+
// session diagnosing the reward gate on 2026-07-26 (task 1002469).
|
|
2701
|
+
console.log(`Credits awarded: ${credits} — expected: this instance pays cost-plus only.`);
|
|
2702
|
+
console.log(" The task's estimate is a guide, never a payout. You're paid for this session's");
|
|
2703
|
+
console.log(' real cost + 20%, booked separately from your token spend.');
|
|
2477
2704
|
} else {
|
|
2478
2705
|
console.log(`Credits awarded: ${credits}`);
|
|
2479
2706
|
}
|
|
@@ -2608,7 +2835,7 @@ async function postShipSync(taskId, postGradeStatus) {
|
|
|
2608
2835
|
// read make the script exit non-zero after a successful ship.
|
|
2609
2836
|
async function onboardingNudgeBestEffort() {
|
|
2610
2837
|
try {
|
|
2611
|
-
const meRes = await (await cliClient()).me.getMe();
|
|
2838
|
+
const meRes = noteServerReward(await (await cliClient()).me.getMe());
|
|
2612
2839
|
if (meRes.ok) {
|
|
2613
2840
|
console.log('');
|
|
2614
2841
|
printOnboardingNudge(meRes.data);
|
|
@@ -2753,7 +2980,7 @@ async function printCompletionCard({ taskId, postGradeStatus, card, usage }) {
|
|
|
2753
2980
|
let total = null;
|
|
2754
2981
|
let widgetsEnabled = true;
|
|
2755
2982
|
try {
|
|
2756
|
-
const me = await (await cliClient()).me.getMe();
|
|
2983
|
+
const me = noteServerReward(await (await cliClient()).me.getMe());
|
|
2757
2984
|
if (me.ok && me.data) {
|
|
2758
2985
|
if (me.data.builder) total = me.data.builder.total_credits;
|
|
2759
2986
|
if (me.data.render) widgetsEnabled = me.data.render.widgets_enabled !== false;
|
|
@@ -2799,7 +3026,7 @@ async function printCompletionCard({ taskId, postGradeStatus, card, usage }) {
|
|
|
2799
3026
|
added,
|
|
2800
3027
|
removed,
|
|
2801
3028
|
grade: card.grade || null,
|
|
2802
|
-
credits: { awarded: card.creditsAwarded, total },
|
|
3029
|
+
credits: { awarded: card.creditsAwarded, total, perTaskCredit: rewardPolicy(serverReward).perTaskCredit },
|
|
2803
3030
|
usage: { tokens, costUsd, newTokens, cachedTokens },
|
|
2804
3031
|
deploy: { commit },
|
|
2805
3032
|
};
|
|
@@ -2824,6 +3051,20 @@ async function printCompletionCard({ taskId, postGradeStatus, card, usage }) {
|
|
|
2824
3051
|
} catch (_) {
|
|
2825
3052
|
/* a summary card must never fail a ship */
|
|
2826
3053
|
}
|
|
3054
|
+
|
|
3055
|
+
// task 1002521: the card above says SHIPPED, which means "merged" — it does
|
|
3056
|
+
// NOT mean users can see it. Publishing is manual and owner-gated, so a ship
|
|
3057
|
+
// can land on main and change nothing live, and until now nothing said so.
|
|
3058
|
+
// Blocker 1000097: 171 commits (a whole governance system) sat unreleased for
|
|
3059
|
+
// ten days while every layer reported success. Warn, never block — a shipped
|
|
3060
|
+
// task is still shipped, and an offline builder must not be punished for it.
|
|
3061
|
+
try {
|
|
3062
|
+
const { check, report } = require('./deploy-divergence');
|
|
3063
|
+
const r = await check();
|
|
3064
|
+
if (!r.undetermined && !r.ok) report(r);
|
|
3065
|
+
} catch (_) {
|
|
3066
|
+
/* a divergence warning must never fail a ship */
|
|
3067
|
+
}
|
|
2827
3068
|
}
|
|
2828
3069
|
|
|
2829
3070
|
// Decide whether a builder may re-grade a task via `--regrade` (#678). Pure so
|
|
@@ -2964,7 +3205,7 @@ async function shipMain() {
|
|
|
2964
3205
|
const args = process.argv.slice(2);
|
|
2965
3206
|
const taskId = Number(args[0]);
|
|
2966
3207
|
if (!Number.isFinite(taskId)) {
|
|
2967
|
-
console.error('usage: node scripts/gds/ship.js <task-id> --notes "..." --summary "..." [--surprise "text" | --surprise none | --no-surprise] [--no-merge] [--db-only] [--verified] [--allow-empty]');
|
|
3208
|
+
console.error('usage: node scripts/gds/ship.js <task-id> --notes "..." --summary "..." [--surprise "text" | --surprise none | --no-surprise] [--no-merge] [--db-only] [--verified] [--allow-empty] [--base <ref>]');
|
|
2968
3209
|
process.exit(2);
|
|
2969
3210
|
}
|
|
2970
3211
|
const notes = arg('--notes', args) || '';
|
|
@@ -2973,7 +3214,12 @@ async function shipMain() {
|
|
|
2973
3214
|
const force = args.includes('--force');
|
|
2974
3215
|
// task 391 (idea #37): a deliberate zero-commit / direct-SQL ship. Without it,
|
|
2975
3216
|
// a branch with no commits ahead of main is REFUSED (not silently shipped).
|
|
2976
|
-
|
|
3217
|
+
// task 1002422: --base <ref> also implies db-only — the work is already on main
|
|
3218
|
+
// (that's the whole point of --base), so the merge leg has nothing to merge and
|
|
3219
|
+
// advances straight to shipped. Unlike --allow-empty it does NOT skip the grade:
|
|
3220
|
+
// the pinned baseline gives the grader a real diff (see resolveBaseRef / main).
|
|
3221
|
+
const baseRefRaw = arg('--base', args);
|
|
3222
|
+
const dbOnly = args.includes('--db-only') || args.includes('--allow-empty') || Boolean(baseRefRaw);
|
|
2977
3223
|
// task 1053 (F): explicit attestation that a verification task's verify
|
|
2978
3224
|
// actually passed. Required to confirm a kind='verify' task (the server
|
|
2979
3225
|
// enforces it too); a no-op for every other task.
|
|
@@ -2995,7 +3241,22 @@ async function shipMain() {
|
|
|
2995
3241
|
const surpriseProvided = surpriseRaw !== null;
|
|
2996
3242
|
const noSurprise = args.includes('--no-surprise');
|
|
2997
3243
|
|
|
2998
|
-
|
|
3244
|
+
// task 1002422 (blocker 1000082): resolve + validate --base BEFORE any heavy
|
|
3245
|
+
// work so a bad ref aborts with the claim STILL ACTIVE. On success it sets the
|
|
3246
|
+
// module-level baseOverrideSha that computeGraderBaseline() reads, so the grader
|
|
3247
|
+
// reads <base>...HEAD (a real diff) and the ship takes the rank-open graded path
|
|
3248
|
+
// instead of the --allow-empty → skip-grade → Archon-only /confirm dead-end.
|
|
3249
|
+
if (baseRefRaw) {
|
|
3250
|
+
const res = applyBaseOverride(baseRefRaw);
|
|
3251
|
+
if (!res.ok) {
|
|
3252
|
+
console.error(`Ship aborted: ${res.reason}. No claim resolved, no credits awarded.`);
|
|
3253
|
+
console.error('Omit --base and ship normally — the grader reviews ALL your commits and the merge lands them. Your claim is still active.');
|
|
3254
|
+
process.exit(2);
|
|
3255
|
+
}
|
|
3256
|
+
console.log(`--base: grading ${res.sha.slice(0, 9)}...HEAD — the task's real committed diff (work already on main). Graded via the rank-open path; no --allow-empty needed.`);
|
|
3257
|
+
}
|
|
3258
|
+
|
|
3259
|
+
const me = noteServerReward(await (await cliClient()).me.getMe());
|
|
2999
3260
|
if (!me.ok) {
|
|
3000
3261
|
console.error('failed to load profile:', me.data);
|
|
3001
3262
|
process.exit(1);
|
|
@@ -3464,17 +3725,31 @@ async function regradeMain() {
|
|
|
3464
3725
|
const args = process.argv.slice(2);
|
|
3465
3726
|
const taskId = Number(args[0]);
|
|
3466
3727
|
if (!Number.isFinite(taskId)) {
|
|
3467
|
-
console.error('usage: node scripts/gds/ship.js <task-id> --regrade [--summary "..."] [--no-merge] [--force] [--verified]');
|
|
3728
|
+
console.error('usage: node scripts/gds/ship.js <task-id> --regrade [--summary "..."] [--no-merge] [--force] [--verified] [--base <ref>]');
|
|
3468
3729
|
process.exit(2);
|
|
3469
3730
|
}
|
|
3470
3731
|
const summaryArg = arg('--summary', args) || '';
|
|
3471
3732
|
const skipMerge = args.includes('--no-merge');
|
|
3472
3733
|
const force = args.includes('--force');
|
|
3473
|
-
|
|
3734
|
+
// task 1002422: honor --base on regrade/resume too, so `--regrade --base <ref>`
|
|
3735
|
+
// and shipMain's resume-grade dispatch grade the real <base>...HEAD diff and
|
|
3736
|
+
// advance already-on-main work, instead of aborting "nothing to grade". Same
|
|
3737
|
+
// 0-ahead security guard as shipMain (applyBaseOverride is the shared gate).
|
|
3738
|
+
const baseRefRaw = arg('--base', args);
|
|
3739
|
+
const dbOnly = args.includes('--db-only') || args.includes('--allow-empty') || Boolean(baseRefRaw); // task 391 — honor --db-only on a regrade too
|
|
3474
3740
|
const verified = args.includes('--verified'); // task 1053 (F) — see gate below
|
|
3475
3741
|
|
|
3742
|
+
if (baseRefRaw) {
|
|
3743
|
+
const res = applyBaseOverride(baseRefRaw);
|
|
3744
|
+
if (!res.ok) {
|
|
3745
|
+
console.error(`Regrade aborted: ${res.reason}. Omit --base and re-grade normally.`);
|
|
3746
|
+
process.exit(2);
|
|
3747
|
+
}
|
|
3748
|
+
console.log(`--base: re-grading ${res.sha.slice(0, 9)}...HEAD (work already on main).`);
|
|
3749
|
+
}
|
|
3750
|
+
|
|
3476
3751
|
// ---- Eligibility: completed + (owner | archon) ----
|
|
3477
|
-
const me = await (await cliClient()).me.getMe();
|
|
3752
|
+
const me = noteServerReward(await (await cliClient()).me.getMe());
|
|
3478
3753
|
if (!me.ok) {
|
|
3479
3754
|
console.error('failed to load profile:', me.data);
|
|
3480
3755
|
process.exit(1);
|
|
@@ -3599,6 +3874,14 @@ async function regradeMain() {
|
|
|
3599
3874
|
// exported: it does I/O + network and is covered by the live smoke at ship.)
|
|
3600
3875
|
module.exports = {
|
|
3601
3876
|
scrubSecrets, snippetOf, buildSessionDigest, resolveTranscriptPath,
|
|
3877
|
+
// task 1002471: the project-path encoder, exported pure so the win32 shapes
|
|
3878
|
+
// (drive colon, backslash separators, spaces) are pinned cross-platform —
|
|
3879
|
+
// resolveTranscriptPath itself runs path.resolve, which can't carry a Windows
|
|
3880
|
+
// path through a Linux CI run.
|
|
3881
|
+
encodeProjectPath,
|
|
3882
|
+
// task 1002417: the regenerated-client pathspec rule, exported pure so the
|
|
3883
|
+
// "never name a path git add cannot match" invariant is tested without git/fs.
|
|
3884
|
+
apiClientPathspec, VENDORED_CLIENT_REL,
|
|
3602
3885
|
// task 1003: transcript-grade scrubber internals, exported pure so the planted-
|
|
3603
3886
|
// secret corpus test (tests/scrubber_corpus.mjs) can exercise each layer + tune.
|
|
3604
3887
|
shannonEntropy, looksHighEntropySecret, redactHighEntropy, collectEnvSecrets,
|
|
@@ -3606,7 +3889,7 @@ module.exports = {
|
|
|
3606
3889
|
// #678: re-grade path + the grade-step helpers it shares with shipMain.
|
|
3607
3890
|
// Exporting these formalizes learning #43 (which had to "replicate ~10
|
|
3608
3891
|
// lines" because runSubagentGrader wasn't reachable).
|
|
3609
|
-
evaluateRegradeEligibility, computeGraderBaseline, computeChangedFiles,
|
|
3892
|
+
evaluateRegradeEligibility, computeGraderBaseline, computeChangedFiles, resolveBaseRef, baseRefShipGuard,
|
|
3610
3893
|
runSubagentGrader, buildGradePayload,
|
|
3611
3894
|
// V4.R08: diff fingerprint + grade-reuse helpers — exported pure so the
|
|
3612
3895
|
// truth table (same hash+pass→reuse, fail→no reuse, mismatch→re-grade,
|
|
@@ -3632,7 +3915,10 @@ module.exports = {
|
|
|
3632
3915
|
// test (mode precedence/safe-default, no-shell PR argv, deploy-run match).
|
|
3633
3916
|
// task 1002202: resolveDeployTarget — SSH target / healthz precedence + the safe
|
|
3634
3917
|
// "no target" default (the core has none), exported for the same reason.
|
|
3635
|
-
|
|
3918
|
+
// task 1002232: resolveInstanceBase — the ci-land/guidance origin precedence
|
|
3919
|
+
// (env → session api_base → branding publicOrigin → null), pure over injected
|
|
3920
|
+
// inputs so the precedence + the "no hardcoded host" guarantee are unit-tested.
|
|
3921
|
+
parseDeployMode, resolveDeployTarget, resolveInstanceBase, buildPrCreateArgv, buildPrMergeArgv, deployRunSucceeded,
|
|
3636
3922
|
// task 1049 (B): pure CI-land outcome classifier — a timed-out-but-armed PR is
|
|
3637
3923
|
// 'pending' (success), not failure. Exported so the semantics are unit-tested.
|
|
3638
3924
|
classifyLandOutcome,
|
|
@@ -3651,6 +3937,10 @@ module.exports = {
|
|
|
3651
3937
|
pickStaleGateReviewRun,
|
|
3652
3938
|
// task 1068: pure pre-stage-merge decision (skip on main/current, else merge).
|
|
3653
3939
|
decidePreMerge,
|
|
3940
|
+
// task 1002475: pure `git log --diff-filter=D` output parser behind
|
|
3941
|
+
// preflight's upstream-deletion warning — exported so it's unit-tested
|
|
3942
|
+
// without a live git repo.
|
|
3943
|
+
parseDeletedUpstreamPaths,
|
|
3654
3944
|
// task 1052 (E): box-aware "what to do next" for a confirmed-strand — a box
|
|
3655
3945
|
// (via 'server') is pointed at the resume, never at gh-only /merge-mode.
|
|
3656
3946
|
strandNextStep,
|