@bongos/core 1.21.174 → 1.21.176
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 +3565 -1515
- package/.claude/skills/ask-for-help/SKILL.md +4 -4
- package/.claude/skills/backlog-review/SKILL.md +1 -1
- package/.claude/skills/blocker-review/SKILL.md +2 -2
- package/.claude/skills/blocker-solve/SKILL.md +4 -4
- package/.claude/skills/bongos-feedback/SKILL.md +1 -1
- package/.claude/skills/bug-triage/SKILL.md +8 -8
- package/.claude/skills/builder-backup/SKILL.md +3 -3
- package/.claude/skills/builder-claim/SKILL.md +5 -5
- package/.claude/skills/builder-cost/SKILL.md +5 -5
- package/.claude/skills/builder-end/SKILL.md +6 -6
- package/.claude/skills/builder-exit/SKILL.md +2 -2
- package/.claude/skills/builder-key/SKILL.md +6 -6
- package/.claude/skills/builder-reauth/SKILL.md +4 -4
- package/.claude/skills/builder-redteam/SKILL.md +3 -3
- package/.claude/skills/builder-release/SKILL.md +3 -3
- package/.claude/skills/builder-sequence/SKILL.md +8 -8
- package/.claude/skills/builder-setup/SKILL.md +4 -4
- package/.claude/skills/builder-ship/SKILL.md +7 -7
- package/.claude/skills/builder-stage/SKILL.md +8 -8
- package/.claude/skills/builder-start/SKILL.md +4 -4
- package/.claude/skills/collab-review/SKILL.md +5 -5
- package/.claude/skills/design/SKILL.md +6 -4
- package/.claude/skills/design-sync/SKILL.md +3 -3
- package/.claude/skills/figma-design-sync/SKILL.md +4 -4
- package/.claude/skills/fix-task/SKILL.md +8 -8
- package/.claude/skills/goal-close/SKILL.md +9 -9
- package/.claude/skills/goal-create/SKILL.md +18 -18
- package/.claude/skills/grade-recover/SKILL.md +8 -8
- package/.claude/skills/grade-sweep/SKILL.md +15 -15
- package/.claude/skills/idea-triage/SKILL.md +15 -15
- package/.claude/skills/merge-mode/SKILL.md +14 -14
- package/.claude/skills/new-project/SKILL.md +5 -5
- package/.claude/skills/owner-review/SKILL.md +1 -1
- package/.claude/skills/planning-session/SKILL.md +14 -14
- package/.claude/skills/priority-session/SKILL.md +6 -6
- package/.claude/skills/read-session-export/SKILL.md +3 -3
- package/.claude/skills/recall/SKILL.md +7 -7
- package/.claude/skills/scan-before-install/SKILL.md +3 -3
- package/.claude/skills/session-handoff/SKILL.md +1 -1
- package/.claude/skills/ship-check/SKILL.md +3 -3
- package/.claude/skills/status/SKILL.md +3 -3
- package/.claude/skills/strand-fix/SKILL.md +6 -6
- package/.claude/skills/worktree-clean/SKILL.md +4 -4
- package/.gitattributes +12 -32
- package/GOVERNMENT.md +1 -1
- package/README.md +8 -8
- package/bin/bongos.js +23 -23
- package/clients/bongos-client/README.md +2 -2
- package/clients/bongos-client/bongos-client.global.js +22 -4
- package/clients/bongos-client/index.cjs +22 -4
- package/clients/bongos-client/index.d.ts +33 -4
- package/clients/bongos-client/index.mjs +21 -3
- package/config/branding.neutral.json +1 -1
- package/config/design-tokens.schema.json +1 -1
- package/config/modules.neutral.json +1 -3
- package/docs/adr/0029-status-page-outage-resilience.md +6 -6
- package/docs/adr/0227-a-task-network-map-is-refused-the-goal-graph-is-the-map.md +1 -1
- package/docs/adr/0294-cli-sessions-do-not-expire.md +1 -1
- package/docs/adr/0383-sessions-end-after-ten-idle-days.md +35 -0
- package/docs/adr/0384-the-raise-page-is-a-hub-module-builders-edit-in-place.md +31 -0
- package/docs/adr/README.md +2 -0
- package/docs/api/openapi.json +606 -44
- package/docs/api-reference.md +25 -12
- package/docs/architecture.md +8 -7
- package/docs/canonical-permissions.md +3 -3
- package/docs/copy-inventory.md +1389 -1437
- package/docs/copy-registry.json +2160 -2634
- package/docs/design/demonolith-plan.md +6 -6
- package/docs/design/modular-architecture/00-research-report.md +1 -1
- package/docs/design/project-startup-direction.md +2 -2
- package/docs/design/projects-hub-direction-v2.md +1 -1
- package/docs/design/reviews/hall-v2/README.md +2 -2
- package/docs/design/vanilla-hall-ui-redesign-scope.md +1 -1
- package/docs/file-map.md +89 -88
- package/docs/handoff-template.md +4 -4
- package/docs/module-api-changelog.md +6 -2
- package/docs/modules-contract.md +8 -7
- package/docs/onboarding/diagrams/01-task-lifecycle.mmd +1 -1
- package/docs/onboarding/diagrams/02-rank-ladder.mmd +1 -1
- package/docs/onboarding/diagrams/03-drachmae-karma.mmd +1 -1
- package/docs/onboarding/diagrams/04-architecture.mmd +1 -1
- package/docs/onboarding/diagrams/05-versioning.mmd +1 -1
- package/docs/onboarding/diagrams/README.md +2 -2
- package/docs/onboarding/diagrams/assertions.json +2 -2
- package/docs/onboarding/slash-commands.md +2 -2
- package/docs/packs/artist.md +4 -4
- package/docs/packs/engineer.md +11 -10
- package/docs/packs/ideator.md +7 -7
- package/docs/page-inventory.json +25 -1
- package/docs/page-readings.json +4388 -4602
- package/docs/recipes/README.md +1 -1
- package/docs/recipes/audit-replay.md +4 -4
- package/docs/recipes/autobongos-windows-host.md +6 -6
- package/docs/recipes/bongos-cli-release.md +2 -2
- package/docs/recipes/claude-design-loop.md +7 -7
- package/docs/recipes/core-release-pipeline.md +19 -19
- package/docs/recipes/failed-ship-recovery-proof.md +2 -2
- package/docs/recipes/figma-design-loop.md +6 -6
- package/docs/recipes/gds-db-backup.md +9 -9
- package/docs/recipes/instance-deploy.md +3 -3
- package/docs/recipes/instance-first-run.md +1 -1
- package/docs/recipes/multi-builder-merge.md +2 -2
- package/docs/recipes/ops-gotchas.md +10 -9
- package/docs/recipes/packaging-the-core.md +6 -6
- package/docs/recipes/parallel-goal-waves.md +4 -4
- package/docs/recipes/private-npm-distribution.md +9 -9
- package/docs/recipes/redteam-patrol.md +5 -5
- package/docs/recipes/render-pilot.md +2 -2
- package/docs/recipes/reranking-a-goals-board.md +2 -2
- package/docs/recipes/search-index-ingestion.md +5 -5
- package/docs/recipes/self-host.md +1 -1
- package/docs/recipes/ship-and-regrade.md +4 -4
- package/docs/recipes/staging-environment.md +1 -1
- package/docs/recipes/standalone-live-docs.md +1 -1
- package/docs/recipes/ui-look-before-you-ship.md +2 -2
- package/docs/recipes/upgrading-the-core.md +11 -11
- package/docs/recipes/windows-builders.md +18 -18
- package/docs/research/llm-model-agnostic-and-cheaper-grader.md +6 -6
- package/docs/research/task-network-map-readability.md +3 -3
- package/migrations/core_278_builder_tester_tag.sql +18 -0
- package/migrations/core_279_bongos_shipper_new_scripts_path.sql +13 -0
- package/modules/agents/lib/answer-hold.js +1 -1
- package/modules/agents/lib/gate.js +1 -1
- package/modules/agents/lib/validate.js +1 -1
- package/modules/autonomy/gauge.js +1 -1
- package/modules/autonomy/governor-docket.js +2 -2
- package/modules/autonomy/pollers/routine-timers.js +3 -3
- package/modules/autonomy/routes/autonomy.js +3 -3
- package/modules/builder-settings/interaction-prefs.js +1 -1
- package/modules/builder-settings/render-prefs.js +1 -1
- package/modules/builder-settings/routes/settings.js +1 -1
- package/modules/builder-settings/skill-prefs.js +2 -2
- package/modules/builder-settings/wandering-prefs.js +2 -2
- package/modules/copy-desk/module.json +2 -2
- package/modules/copy-desk/page-data.js +5 -5
- package/modules/copy-desk/page-decisions.js +13 -1
- package/modules/copy-desk/proposals.js +4 -4
- package/modules/copy-desk/registry.js +2 -2
- package/modules/copy-desk/routes/copy-desk-release.js +119 -0
- package/modules/copy-desk/routes/copy-desk.js +3 -12
- package/modules/copy-desk/skills/tweak/SKILL.md +12 -12
- package/modules/copy-desk/tests/copy_flags.mjs +1 -1
- package/modules/copy-desk/tests/copy_no_cms.mjs +7 -3
- package/modules/copy-desk/tests/copy_proposals.mjs +2 -2
- package/modules/copy-desk/tests/copy_queue.mjs +1 -1
- package/modules/design-styles/skills/README.md +4 -4
- package/modules/design-styles/skills/imagegen-frontend-mobile/SKILL.md +1 -1
- package/modules/design-styles/skills/imagegen-frontend-web/SKILL.md +1 -1
- package/modules/discord/bug-attachments.js +1 -1
- package/modules/discord/ship-broadcast.js +1 -1
- package/modules/economy/achievements.js +49 -7
- package/modules/economy/cost-classify.js +6 -6
- package/modules/economy/cost-report.js +1 -1
- package/modules/economy/cost.js +1 -1
- package/modules/economy/credits.js +2 -1
- package/modules/economy/idea-size.js +2 -2
- package/modules/economy/reward.js +2 -0
- package/modules/economy/routes/cost.js +1 -1
- package/modules/economy/streak.js +43 -4
- package/modules/government/asks.js +1 -1
- package/modules/government/board.js +19 -2
- package/modules/government/catalog.js +3 -0
- package/modules/government/migrations/government_033_idea_vote.sql +46 -0
- package/modules/government/protected-surfaces.json +48 -47
- package/modules/government/recommendations.js +6 -2
- package/modules/government/routes/government.js +1 -1
- package/modules/grading/generated-provenance.js +13 -5
- package/modules/grading/grade-engine.js +1 -1
- package/modules/grading/grader-rubric.json +1 -1
- package/modules/grading/grader-score.js +1 -1
- package/modules/grading/grader-workers/index.js +1 -1
- package/modules/grading/grader.js +1 -1
- package/modules/grading/routes/grade.js +1 -1
- package/modules/hall-ui/public/atlas.html +5 -7
- package/modules/hall-ui/public/atlas.js +1 -1
- package/modules/hall-ui/public/atlas.states.json +1 -1
- package/modules/hall-ui/public/blockers.css +9 -0
- package/modules/hall-ui/public/blockers.html +6 -4
- package/modules/hall-ui/public/blockers.js +39 -27
- package/modules/hall-ui/public/board-room.css +5 -12
- package/modules/hall-ui/public/board-room.html +6 -6
- package/modules/hall-ui/public/board-room.js +41 -23
- package/modules/hall-ui/public/board-room.states.json +1 -1
- package/modules/hall-ui/public/brand-apply.js +1 -1
- package/modules/hall-ui/public/brand-holes.js +1 -1
- package/modules/hall-ui/public/builders.js +40 -86
- package/modules/hall-ui/public/city-decide.js +18 -4
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +6 -9
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/copy-desk.js +2 -2
- package/modules/hall-ui/public/craft-mode.js +4 -0
- package/modules/hall-ui/public/deploy.html +6 -2
- package/modules/hall-ui/public/deploy.js +21 -13
- package/modules/hall-ui/public/diagrams.css +1 -1
- package/modules/hall-ui/public/diagrams.html +5 -8
- package/modules/hall-ui/public/diagrams.states.json +1 -1
- package/modules/hall-ui/public/drachmae.html +3 -7
- package/modules/hall-ui/public/fleet.html +3 -2
- package/modules/hall-ui/public/fleet.js +0 -4
- package/modules/hall-ui/public/gate.html +7 -7
- package/modules/hall-ui/public/gate.js +58 -77
- package/modules/hall-ui/public/goal-inbox.js +69 -86
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals-page.js +5 -5
- package/modules/hall-ui/public/goals-render.js +10 -11
- package/modules/hall-ui/public/goals.css +10 -0
- package/modules/hall-ui/public/goals.html +14 -7
- package/modules/hall-ui/public/government.html +3 -5
- package/modules/hall-ui/public/government.js +1 -5
- package/modules/hall-ui/public/hall-ask.css +57 -0
- package/modules/hall-ui/public/hall-ask.js +50 -0
- package/modules/hall-ui/public/hall-render.js +117 -57
- package/modules/hall-ui/public/hall-widgets.js +11 -11
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +3 -1
- package/modules/hall-ui/public/ideas.js +1 -0
- package/modules/hall-ui/public/index.html +15 -6
- package/modules/hall-ui/public/index.states.json +1 -1
- package/modules/hall-ui/public/mine-lens.js +1 -1
- package/modules/hall-ui/public/modules.html +7 -10
- package/modules/hall-ui/public/modules.js +0 -2
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/palette.js +5 -5
- package/modules/hall-ui/public/primer.html +4 -9
- package/modules/hall-ui/public/profile-connections.js +10 -2
- package/modules/hall-ui/public/profile-roles.js +3 -10
- package/modules/hall-ui/public/profile.html +12 -10
- package/modules/hall-ui/public/profile.js +13 -17
- package/modules/hall-ui/public/project-settings.html +3 -4
- package/modules/hall-ui/public/project-settings.js +0 -3
- package/modules/hall-ui/public/ranks.html +5 -11
- package/modules/hall-ui/public/ranks.js +6 -8
- package/modules/hall-ui/public/roadmap.css +0 -2
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roadmap.js +16 -51
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +6 -8
- package/modules/hall-ui/public/sessions.js +7 -7
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell-trail.js +1 -1
- package/modules/hall-ui/public/shell.js +17 -17
- package/modules/hall-ui/public/street-deck.css +2 -1
- package/modules/hall-ui/public/street-deck.js +10 -2
- package/modules/hall-ui/public/street-desk.js +16 -5
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio-lib.js +4 -0
- package/modules/hall-ui/public/studio.css +2 -0
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/studio.js +27 -1
- package/modules/hall-ui/public/style.css +43 -26
- package/modules/hall-ui/public/task-detail.js +10 -7
- package/modules/hall-ui/public/task.css +2 -0
- package/modules/hall-ui/public/task.html +3 -1
- package/modules/hall-ui/public/task.js +23 -7
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/vote-control.js +208 -0
- package/modules/hall-ui/public/watch.html +3 -5
- package/modules/hall-ui/public/watch.js +5 -4
- package/modules/hall-ui/public/welcome-boot.css +130 -0
- package/modules/hall-ui/public/welcome-boot.js +334 -0
- package/modules/hall-ui/public/welcome-day.webp +0 -0
- package/modules/hall-ui/public/welcome-night.webp +0 -0
- package/modules/hall-ui/public/work-summary.js +1 -1
- package/modules/hall-ui/public/work.css +1 -0
- package/modules/hall-ui/public/work.html +3 -1
- package/modules/hall-ui/public/work.js +2 -1
- package/modules/hall-ui/records/accomplishments.md +16 -0
- package/modules/hall-ui/records/hall-ask-card.md +33 -0
- package/modules/hall-ui/records/karma-vote-control.md +13 -0
- package/modules/hall-ui/records/oversight-family.md +1 -1
- package/modules/hall-ui/records/profile-connections.md +2 -0
- package/modules/hall-ui/records/profile-roles.md +1 -1
- package/modules/hall-ui/records/reading-rooms.md +1 -1
- package/modules/hall-ui/records/welcome-tower.md +50 -0
- package/modules/ideas/migrations/ideas_011_idea_votes.sql +31 -0
- package/modules/ideas/projection.js +1 -1
- package/modules/ideas/routes/blockers.js +3 -3
- package/modules/ideas/routes/inbox.js +29 -1
- package/modules/ideas/routes/ratify-goal.js +1 -1
- package/modules/ideas/routing.js +1 -1
- package/modules/ideas/templates.js +1 -1
- package/modules/ideas/trail-descent.js +1 -1
- package/modules/ideas/trail-size.js +1 -1
- package/modules/ideas/votes.js +93 -0
- package/modules/lifecycle/abandon-leftovers.js +1 -1
- package/modules/lifecycle/cascade.js +1 -1
- package/modules/lifecycle/conflict-resolve.js +13 -21
- package/modules/lifecycle/criterion-ref.js +1 -1
- package/modules/lifecycle/db-analytics.js +57 -4
- package/modules/lifecycle/db-deps-criteria.js +1 -1
- package/modules/lifecycle/db-ship.js +2 -2
- package/modules/lifecycle/db-tasks.js +2 -2
- package/modules/lifecycle/db-versions.js +1 -1
- package/modules/lifecycle/db.js +2 -0
- package/modules/lifecycle/dead-deps.js +3 -3
- package/modules/lifecycle/dependency-advisory.js +3 -3
- package/modules/lifecycle/goal-category.js +1 -1
- package/modules/lifecycle/goal-forest.js +1 -1
- package/modules/lifecycle/goal-task-relevance-advisory.js +1 -1
- package/modules/lifecycle/kickoff-checklist.js +1 -1
- package/modules/lifecycle/kickoff-seed.js +1 -1
- package/modules/lifecycle/lifecycle.js +2 -0
- package/modules/lifecycle/merge-reach.js +29 -15
- package/modules/lifecycle/missing-dep-classify.js +3 -3
- package/modules/lifecycle/module.json +2 -1
- package/modules/lifecycle/page-tweak-approve.js +85 -1
- package/modules/lifecycle/page-tweak-hold.js +1 -1
- package/modules/lifecycle/publish-reconciler.js +1 -2
- package/modules/lifecycle/rot.js +1 -1
- package/modules/lifecycle/routes/artist-gate.js +1 -1
- package/modules/lifecycle/routes/gate-approvals.js +22 -1
- package/modules/lifecycle/routes/goals.js +1 -1
- package/modules/lifecycle/routes/task-route-helpers.js +1 -1
- package/modules/lifecycle/routes/task-votes.js +54 -0
- package/modules/lifecycle/routes/task-write-routes.js +1 -1
- package/modules/lifecycle/routes/tasks.js +2 -2
- package/modules/lifecycle/routes/versions.js +2 -2
- package/modules/lifecycle/ship-card.js +4 -4
- package/modules/lifecycle/ship-preflight.js +1 -1
- package/modules/lifecycle/task-classifier.js +1 -1
- package/modules/lifecycle/task-status.js +1 -1
- package/modules/lifecycle/task-visual-db.js +1 -1
- package/modules/memory/learnings.js +1 -1
- package/modules/memory/pollers/search-index.js +2 -2
- package/modules/memory/routes/learnings.js +1 -1
- package/modules/memory/routes/memory.js +1 -1
- package/modules/memory/search.js +2 -2
- package/modules/npm-release/release.js +1 -1
- package/modules/onboarding/howto-moments.js +1 -1
- package/modules/onboarding/newcomer-restock.js +3 -3
- package/modules/onboarding/onboarding.js +1 -1
- package/modules/pixel-art/skills/otb-tile-generate/SKILL.md +1 -1
- package/modules/platform-identity/origin-containment.js +1 -1
- package/modules/platform-identity/publish-gate.js +1 -1
- package/modules/platform-identity/tests/platform-identity.mjs +1 -1
- package/modules/provisioning/capacity.js +1 -1
- package/modules/provisioning/core-upgrade.js +15 -9
- package/modules/provisioning/credential-preflight.js +3 -3
- package/modules/provisioning/disconnect.js +1 -1
- package/modules/provisioning/dns-resolves.js +1 -1
- package/modules/provisioning/module.json +1 -1
- package/modules/provisioning/onboard-plan.js +1 -1
- package/modules/provisioning/paid-shape-gate.js +2 -2
- package/modules/provisioning/planet-physics.js +11 -2
- package/modules/provisioning/pollers/liveness-sweep.js +1 -1
- package/modules/provisioning/pollers/runner-drift-notify.js +5 -5
- package/modules/provisioning/provisioning.js +2 -2
- package/modules/provisioning/render-lookup.js +1 -1
- package/modules/provisioning/render-standup.js +3 -3
- package/modules/provisioning/repo-private.js +1 -1
- package/modules/provisioning/routes/callback-page.js +1 -1
- package/modules/provisioning/routes/core-upgrade.js +23 -3
- package/modules/provisioning/routes/provisioning.js +2 -2
- package/modules/provisioning/routes/render-standup.js +1 -1
- package/modules/provisioning/routes/repo-private.js +1 -1
- package/modules/provisioning/soft-limits.js +1 -1
- package/modules/provisioning/starter-bundles.js +4 -2
- package/modules/provisioning/tests/provisioning.mjs +1 -1
- package/modules/public-landing/public/account.html +4 -6
- package/modules/public-landing/public/assets/cosmos.css +13 -6
- package/modules/public-landing/public/connections.html +4 -6
- package/modules/public-landing/public/contact.html +4 -6
- package/modules/public-landing/public/guild.html +4 -6
- package/modules/public-landing/public/index.html +11 -9
- package/modules/public-landing/public/invitations.html +4 -6
- package/modules/public-landing/public/privacy.html +4 -6
- package/modules/public-landing/public/projects.html +168 -46
- package/modules/public-landing/public/terms.html +4 -6
- package/modules/raise/content/copy.json +40 -0
- package/modules/raise/content/nda.html +39 -0
- package/modules/raise/content/settings.json +49 -0
- package/modules/raise/db.js +95 -0
- package/modules/raise/migrations/raise_001_tables.sql +71 -0
- package/modules/raise/module.json +28 -0
- package/modules/raise/page.js +194 -0
- package/modules/raise/public/edit.js +152 -0
- package/modules/raise/public/favicon.svg +1 -0
- package/modules/raise/public/logo-ink.svg +1 -0
- package/modules/raise/public/logo-night.svg +1 -0
- package/modules/raise/public/mode.js +8 -0
- package/modules/raise/public/raise.css +234 -0
- package/modules/raise/public/raise.js +91 -0
- package/modules/raise/raise.js +174 -0
- package/modules/raise/routes/raise.js +152 -0
- package/modules/raise/views/gate.html +39 -0
- package/modules/raise/views/nda.html +21 -0
- package/modules/raise/views/page.html +133 -0
- package/modules/raise/web.js +72 -0
- package/modules/render-deploy/deploys.js +1 -1
- package/modules/render-deploy/render.js +2 -2
- package/modules/security/secret-scrub.js +3 -3
- package/modules/security/security.js +1 -1
- package/modules/security/tests/artifact_scan.mjs +1 -1
- package/modules/security/tests/mirror.mjs +1 -1
- package/modules/security/tests/scan_store.mjs +1 -1
- package/modules/sessions/db.js +2 -2
- package/modules/sessions/pulse.js +2 -2
- package/modules/sessions/routes/sessions.js +1 -1
- package/modules/specialities/specialities.js +1 -1
- package/modules/status-ui/public/README.md +3 -4
- package/modules/status-ui/public/index.html +5 -31
- package/modules/status-ui/public/status.js +6 -174
- package/modules/status-ui/public/style.css +4 -133
- package/modules/ui-design/docs/design-contract.md +3 -3
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/serve.js +13 -4
- package/modules/ui-design/scripts/design-sync.js +1 -1
- package/modules/ui-design/scripts/figma-design-sync.js +1 -1
- package/modules/ui-design/scripts/validate-design.js +1 -1
- package/package-lock.json +27 -11
- package/package.json +2 -3
- package/release-notes.json +178 -0
- package/scripts/bongos/CLAUDE.md +15 -0
- package/scripts/bongos/abandon.js +137 -0
- package/scripts/bongos/accessibility-replay.js +195 -0
- package/scripts/bongos/address-park-sweep.sh +91 -0
- package/scripts/bongos/adopt-detect.js +123 -0
- package/scripts/bongos/adopt-import.js +111 -0
- package/scripts/bongos/adopt-preflight.js +187 -0
- package/scripts/bongos/adr-namespace.js +273 -0
- package/scripts/bongos/agent-invoke.js +689 -0
- package/scripts/bongos/agents-sync.js +502 -0
- package/scripts/bongos/api-alias-caller-scan.js +256 -0
- package/scripts/bongos/api-artifacts.js +91 -0
- package/scripts/bongos/api.js +86 -0
- package/scripts/bongos/api.ps1 +26 -0
- package/scripts/bongos/architect-audit.js +294 -0
- package/scripts/bongos/artifact-audit.js +326 -0
- package/scripts/bongos/artifact-format.js +203 -0
- package/scripts/bongos/artifact-staleness.js +267 -0
- package/scripts/bongos/ask.js +231 -0
- package/scripts/bongos/audit-authorship.sh +329 -0
- package/scripts/bongos/audit-deps.js +189 -0
- package/scripts/bongos/audit-gate.js +121 -0
- package/scripts/bongos/audit-rank-roles.js +172 -0
- package/scripts/bongos/audit-replay.sh +205 -0
- package/scripts/bongos/autobongos-digest.js +123 -0
- package/scripts/bongos/autobongos-grade-cap.js +187 -0
- package/scripts/bongos/autobongos-loop.js +210 -0
- package/scripts/bongos/autobongos-run.js +1361 -0
- package/scripts/bongos/autobongos-service.cmd +50 -0
- package/scripts/bongos/autobongos-service.ps1 +270 -0
- package/scripts/bongos/autobongos-tweak-lane.js +262 -0
- package/scripts/bongos/autobongos-verify.js +618 -0
- package/scripts/bongos/autonomy-gate.js +135 -0
- package/scripts/bongos/autonomy-gauge-statusline.js +124 -0
- package/scripts/bongos/autonomy-gauge.js +84 -0
- package/scripts/bongos/backfill-cost.js +176 -0
- package/scripts/bongos/backfill-disciplines.js +144 -0
- package/scripts/bongos/backfill-session-earnings.js +123 -0
- package/scripts/bongos/backfill-session-types.js +87 -0
- package/scripts/bongos/backfill-sessions.js +411 -0
- package/scripts/bongos/backfill-subagent-rewards.js +485 -0
- package/scripts/bongos/backfill-task-rewards.js +413 -0
- package/scripts/bongos/backlog-review.js +399 -0
- package/scripts/bongos/backup-dropin-sweep.sh +95 -0
- package/scripts/bongos/backup.js +116 -0
- package/scripts/bongos/baseline-staleness.js +217 -0
- package/scripts/bongos/bfg-principal-token.js +65 -0
- package/scripts/bongos/bug-attachment-audit.js +149 -0
- package/scripts/bongos/bug-attachment-recover.js +198 -0
- package/scripts/bongos/bug-triage.js +292 -0
- package/scripts/bongos/build-bongos.js +94 -0
- package/scripts/bongos/bump-version.js +221 -0
- package/scripts/bongos/capture.js +481 -0
- package/scripts/bongos/category-advisory-guard.js +206 -0
- package/scripts/bongos/ci-grade.js +82 -0
- package/scripts/bongos/claim-recall.js +112 -0
- package/scripts/bongos/claim.js +789 -0
- package/scripts/bongos/classify-discipline.js +153 -0
- package/scripts/bongos/claude-materialize.js +1200 -0
- package/scripts/bongos/claude-md-template.js +142 -0
- package/scripts/bongos/cleanup-orphans.js +74 -0
- package/scripts/bongos/cli-lib.js +1226 -0
- package/scripts/bongos/client-baseurl-guard.js +116 -0
- package/scripts/bongos/codemod-rename-scripts-gds.js +381 -0
- package/scripts/bongos/codemod-rename-src-gds.js +145 -0
- package/scripts/bongos/conductor-dispatch.js +535 -0
- package/scripts/bongos/context-pack.js +326 -0
- package/scripts/bongos/control-manifest.js +199 -0
- package/scripts/bongos/copy-apply.js +758 -0
- package/scripts/bongos/copy-inventory.js +571 -0
- package/scripts/bongos/core-origin-follow.js +185 -0
- package/scripts/bongos/core-pin-sync.js +260 -0
- package/scripts/bongos/coreB-carve-proof.sh +725 -0
- package/scripts/bongos/cost-report.js +63 -0
- package/scripts/bongos/cost.js +47 -0
- package/scripts/bongos/credential-presence.js +296 -0
- package/scripts/bongos/cutover-codemod.js +240 -0
- package/scripts/bongos/db-backup-nightly.sh +232 -0
- package/scripts/bongos/db-backup-offsite.sh +267 -0
- package/scripts/bongos/demote.js +45 -0
- package/scripts/bongos/deploy-divergence.js +454 -0
- package/scripts/bongos/deploy-main-audit-gate.sh +97 -0
- package/scripts/bongos/design-sync.js +12 -0
- package/scripts/bongos/dev-box-guard.js +166 -0
- package/scripts/bongos/dev-lib.js +722 -0
- package/scripts/bongos/dev.js +645 -0
- package/scripts/bongos/device-flow-help.js +72 -0
- package/scripts/bongos/diagnose-achievements.js +118 -0
- package/scripts/bongos/diagram-facts.js +355 -0
- package/scripts/bongos/discord-channels.js +77 -0
- package/scripts/bongos/do-api.js +109 -0
- package/scripts/bongos/doc-cli-guard.js +274 -0
- package/scripts/bongos/doc-comment-coverage.js +139 -0
- package/scripts/bongos/doc-control-claims.js +170 -0
- package/scripts/bongos/docs-entropy.js +453 -0
- package/scripts/bongos/doctor.js +493 -0
- package/scripts/bongos/enroll-control-plane.js +101 -0
- package/scripts/bongos/exec-path-guard.js +564 -0
- package/scripts/bongos/feedback-send.js +249 -0
- package/scripts/bongos/fetch-art-key.js +106 -0
- package/scripts/bongos/fetch-sound-prefs.js +126 -0
- package/scripts/bongos/figma-design-sync.js +11 -0
- package/scripts/bongos/find-untested.js +371 -0
- package/scripts/bongos/fitness-checks-error-envelope.js +134 -0
- package/scripts/bongos/fitness-checks-identity.js +343 -0
- package/scripts/bongos/fitness-checks-packaging.js +132 -0
- package/scripts/bongos/fitness-checks-route-pins.js +76 -0
- package/scripts/bongos/fitness-checks-vocabulary.js +392 -0
- package/scripts/bongos/fitness-checks-write-validation.js +157 -0
- package/scripts/bongos/fitness-lib.js +127 -0
- package/scripts/bongos/fitness-migration-contraction.js +50 -0
- package/scripts/bongos/fitness-ratchets.js +594 -0
- package/scripts/bongos/fitness.js +1498 -0
- package/scripts/bongos/forwarder.js +80 -0
- package/scripts/bongos/gate-review.js +330 -0
- package/scripts/bongos/gating.js +348 -0
- package/scripts/{gds → bongos}/gds-db-backup.service +6 -6
- package/scripts/{gds → bongos}/gds-db-backup.timer +4 -4
- package/scripts/{gds → bongos}/gds-db-offsite.service +4 -4
- package/scripts/{gds → bongos}/gds-db-offsite.timer +4 -4
- package/scripts/bongos/gds-literal-scan.js +196 -0
- package/scripts/bongos/gen-api-client.js +576 -0
- package/scripts/bongos/gen-api-docs.js +1111 -0
- package/scripts/bongos/gen-atlas.js +366 -0
- package/scripts/bongos/gen-diagrams.js +608 -0
- package/scripts/bongos/gen-file-map.js +337 -0
- package/scripts/bongos/gen-freshness.js +32 -0
- package/scripts/bongos/gen-interaction-posture.js +122 -0
- package/scripts/bongos/gen-repo-map.js +540 -0
- package/scripts/bongos/gen-role-responsibilities.js +134 -0
- package/scripts/bongos/gen-session-index.js +243 -0
- package/scripts/bongos/gen-sounds.js +176 -0
- package/scripts/bongos/git-merge-regen.js +254 -0
- package/scripts/bongos/go-live.js +1071 -0
- package/scripts/bongos/goal-names.js +102 -0
- package/scripts/bongos/goal.js +272 -0
- package/scripts/bongos/government-walk.js +1477 -0
- package/scripts/bongos/grade-correlation-audit.js +310 -0
- package/scripts/bongos/grade-replay.js +237 -0
- package/scripts/bongos/grade-smoke.js +205 -0
- package/scripts/bongos/home-isolation-guard.js +126 -0
- package/scripts/bongos/hooks/pre-push.js +211 -0
- package/scripts/bongos/howto.js +145 -0
- package/scripts/bongos/http-api-client.js +81 -0
- package/scripts/bongos/identity-strip.js +75 -0
- package/scripts/bongos/init.js +1159 -0
- package/scripts/bongos/install-git-hooks.js +200 -0
- package/scripts/bongos/instance-apex.js +38 -0
- package/scripts/bongos/intent-retry.js +52 -0
- package/scripts/bongos/key-card.js +222 -0
- package/scripts/bongos/key-status.js +126 -0
- package/scripts/bongos/land-watch.js +201 -0
- package/scripts/bongos/leak-scan-allowlist.js +293 -0
- package/scripts/bongos/learning-capture.js +145 -0
- package/scripts/bongos/lib.sh +74 -0
- package/scripts/bongos/linkify-refs.js +655 -0
- package/scripts/bongos/lint-infra-exec.js +70 -0
- package/scripts/bongos/lint-migrations.js +102 -0
- package/scripts/bongos/lint-workflow-exec.js +63 -0
- package/scripts/bongos/local-preview-lib.js +128 -0
- package/scripts/bongos/local-preview.js +82 -0
- package/scripts/bongos/login.js +360 -0
- package/scripts/bongos/main-audit.js +625 -0
- package/scripts/bongos/me.js +97 -0
- package/scripts/bongos/memory-map.js +176 -0
- package/scripts/bongos/merge-lock.js +71 -0
- package/scripts/bongos/migration-namespace.js +313 -0
- package/scripts/bongos/mirror-lineage-check.js +343 -0
- package/scripts/bongos/mirror-redact.js +350 -0
- package/scripts/bongos/module-artifact.js +289 -0
- package/scripts/bongos/module-assess-docs.js +238 -0
- package/scripts/bongos/module-assess-installs.js +228 -0
- package/scripts/bongos/module-assess-score.js +111 -0
- package/scripts/bongos/module-assess-security.js +252 -0
- package/scripts/bongos/module-assess-tests.js +278 -0
- package/scripts/bongos/module-assess-version.js +92 -0
- package/scripts/bongos/module-error-report.js +148 -0
- package/scripts/{gds → bongos}/module-test-runner/README.md +3 -3
- package/scripts/{gds → bongos}/module-test-runner/module-tests.yml +2 -2
- package/scripts/bongos/module-test-runner.js +414 -0
- package/scripts/bongos/module.js +1376 -0
- package/scripts/bongos/move-escalation.js +141 -0
- package/scripts/{gds → bongos}/newcomer-floor.config.json +1 -1
- package/scripts/bongos/newcomer-floor.js +421 -0
- package/scripts/{gds → bongos}/newcomer-floor.templates.json +13 -13
- package/scripts/bongos/newcomer-floor.test.js +235 -0
- package/scripts/bongos/next-number.js +163 -0
- package/scripts/bongos/oauth-secret.js +202 -0
- package/scripts/bongos/onboard.js +267 -0
- package/scripts/bongos/onboarding-analyze.js +226 -0
- package/scripts/bongos/onboarding-config.js +179 -0
- package/scripts/bongos/onboarding-ship.js +92 -0
- package/scripts/bongos/pack-env-prefix.js +73 -0
- package/scripts/bongos/package-core.js +1200 -0
- package/scripts/bongos/page-inventory.js +348 -0
- package/scripts/bongos/page-reader.js +722 -0
- package/scripts/bongos/paste-token.js +212 -0
- package/scripts/bongos/plain-cards.js +351 -0
- package/scripts/bongos/portability-smoke-art.sh +85 -0
- package/scripts/bongos/portability-smoke-memory.sh +176 -0
- package/scripts/bongos/preflight.js +214 -0
- package/scripts/bongos/probe-cache.js +246 -0
- package/scripts/bongos/prove-strict-versioning.js +284 -0
- package/scripts/bongos/provision-auto-upgrade.js +162 -0
- package/scripts/bongos/provision-byo-host.js +68 -0
- package/scripts/bongos/provision-capacity.js +79 -0
- package/scripts/bongos/provision-caps.js +63 -0
- package/scripts/bongos/provision-config.js +233 -0
- package/scripts/bongos/provision-core-upgrade.js +1166 -0
- package/scripts/bongos/provision-disconnect.js +88 -0
- package/scripts/bongos/provision-github-setup.js +168 -0
- package/scripts/bongos/provision-net.js +357 -0
- package/scripts/bongos/provision-pin-key.js +176 -0
- package/scripts/bongos/provision-pin-replay.js +126 -0
- package/scripts/bongos/provision-process.js +170 -0
- package/scripts/bongos/provision-render.js +201 -0
- package/scripts/bongos/provision-repo-auth.js +164 -0
- package/scripts/bongos/provision-repo-private.js +98 -0
- package/scripts/bongos/provision-repo.js +1337 -0
- package/scripts/bongos/provision-teardown.js +106 -0
- package/scripts/bongos/provision-units.js +469 -0
- package/scripts/bongos/provision.js +1479 -0
- package/scripts/bongos/publish-credential-check.js +66 -0
- package/scripts/bongos/publish-integration-gate.js +142 -0
- package/scripts/bongos/publish-manifest.js +440 -0
- package/scripts/bongos/publish-watch.js +268 -0
- package/scripts/bongos/pulse.js +336 -0
- package/scripts/bongos/push-main.js +56 -0
- package/scripts/bongos/push-path-brief.js +100 -0
- package/scripts/bongos/read-session-export.js +359 -0
- package/scripts/bongos/reauth.js +155 -0
- package/scripts/bongos/recall.js +183 -0
- package/scripts/bongos/redteam-patrol.js +437 -0
- package/scripts/bongos/regen-instance-docs.js +47 -0
- package/scripts/bongos/release-core.js +80 -0
- package/scripts/bongos/release-notes.js +271 -0
- package/scripts/bongos/release.js +139 -0
- package/scripts/bongos/rename-goals.js +109 -0
- package/scripts/bongos/rename-history-check.js +369 -0
- package/scripts/bongos/rename-ledger-prose.js +482 -0
- package/scripts/bongos/render-api.js +197 -0
- package/scripts/bongos/render-check.js +206 -0
- package/scripts/bongos/render-ideas.js +226 -0
- package/scripts/bongos/render-payload.js +85 -0
- package/scripts/bongos/repo-metrics.js +363 -0
- package/scripts/bongos/review-sheet.js +108 -0
- package/scripts/bongos/role-pack-guard.js +142 -0
- package/scripts/bongos/route-shadow-guard.js +175 -0
- package/scripts/bongos/routine-schedule.js +283 -0
- package/scripts/bongos/run-routine.js +257 -0
- package/scripts/bongos/run-unit-tests.js +654 -0
- package/scripts/bongos/runner-drift.js +151 -0
- package/scripts/bongos/sandbox-stage.js +459 -0
- package/scripts/bongos/scope-map-guard.js +300 -0
- package/scripts/bongos/scope-roster-guard.js +118 -0
- package/scripts/bongos/seam-wiring-guard.js +275 -0
- package/scripts/bongos/search-ingest-db.js +288 -0
- package/scripts/bongos/search-ingest-md.js +269 -0
- package/scripts/bongos/seed-adopt-tasks.js +75 -0
- package/scripts/bongos/seed-api-contract-goal-tasks.js +141 -0
- package/scripts/bongos/seed-board-room-tasks.js +196 -0
- package/scripts/bongos/seed-bongos-app-tasks.js +294 -0
- package/scripts/bongos/seed-bongos-coreB-tranche1-tasks.js +81 -0
- package/scripts/bongos/seed-bongos-coreB-tranche2-tasks.js +100 -0
- package/scripts/bongos/seed-bongos-goal-hierarchy-tasks.js +307 -0
- package/scripts/bongos/seed-bongos-module-system-tasks.js +346 -0
- package/scripts/bongos/seed-bongos-tasks.js +214 -0
- package/scripts/bongos/seed-bongos-v1-idea-routing-tasks.js +152 -0
- package/scripts/bongos/seed-bongos-v1-seo-tasks.js +104 -0
- package/scripts/bongos/seed-bv1-community-tasks.js +131 -0
- package/scripts/bongos/seed-bv1-privacy-invites-tasks.js +133 -0
- package/scripts/bongos/seed-bv1-profile-page-tasks.js +161 -0
- package/scripts/bongos/seed-bv1-profiles-tasks.js +135 -0
- package/scripts/bongos/seed-bv1-r01-delta-tasks.js +154 -0
- package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +390 -0
- package/scripts/bongos/seed-bv2-tweak-mode-tasks.js +277 -0
- package/scripts/bongos/seed-gds-v4-tasks.js +624 -0
- package/scripts/bongos/seed-governance-tasks.js +340 -0
- package/scripts/bongos/seed-medusa-tasks.js +440 -0
- package/scripts/bongos/seed-module-upstreaming.js +219 -0
- package/scripts/bongos/seed-productization-tasks.js +235 -0
- package/scripts/bongos/seed-project-admin-console-tasks.js +88 -0
- package/scripts/bongos/seed-provisioning-tasks.js +83 -0
- package/scripts/bongos/seed-staging.sh +117 -0
- package/scripts/bongos/seed-strict-versioning-tasks.js +275 -0
- package/scripts/bongos/seed-v2-world-tasks.js +283 -0
- package/scripts/bongos/seed-verify-criteria.js +124 -0
- package/scripts/bongos/sequence.js +597 -0
- package/scripts/bongos/session-cost.js +430 -0
- package/scripts/bongos/session-digest-build.js +293 -0
- package/scripts/bongos/session-digest.js +200 -0
- package/scripts/bongos/session-eval.js +631 -0
- package/scripts/bongos/session-transcript-build.js +155 -0
- package/scripts/bongos/session-transcript-upload.js +29 -0
- package/scripts/bongos/session.js +404 -0
- package/scripts/bongos/setup.js +646 -0
- package/scripts/bongos/ship-check.js +89 -0
- package/scripts/bongos/ship-deploy-target.js +393 -0
- package/scripts/bongos/ship-escalation.js +167 -0
- package/scripts/bongos/ship-finish.js +420 -0
- package/scripts/bongos/ship-flow.js +1217 -0
- package/scripts/bongos/ship-git.js +100 -0
- package/scripts/bongos/ship-grade-steps.js +547 -0
- package/scripts/bongos/ship-grade.js +426 -0
- package/scripts/bongos/ship-honesty.js +314 -0
- package/scripts/bongos/ship-io.js +139 -0
- package/scripts/bongos/ship-land.js +1130 -0
- package/scripts/bongos/ship-memory-sync.js +249 -0
- package/scripts/bongos/ship-merge-gate.js +225 -0
- package/scripts/bongos/ship-merge.js +466 -0
- package/scripts/bongos/ship-mode-ask.js +138 -0
- package/scripts/bongos/ship-mode-manual.js +54 -0
- package/scripts/bongos/ship-preflight-steps.js +399 -0
- package/scripts/bongos/ship-regen.js +292 -0
- package/scripts/bongos/ship-resume.js +201 -0
- package/scripts/bongos/ship-session-upload.js +313 -0
- package/scripts/bongos/ship-state.js +110 -0
- package/scripts/bongos/ship-surprise.js +84 -0
- package/scripts/bongos/ship-visual.js +75 -0
- package/scripts/bongos/ship.js +382 -0
- package/scripts/bongos/shipping-mode.js +71 -0
- package/scripts/bongos/skill-lint.js +250 -0
- package/scripts/bongos/skill-preflight.js +361 -0
- package/scripts/bongos/smoke-batch-claim.sh +238 -0
- package/scripts/bongos/smoke-batch-release.sh +316 -0
- package/scripts/bongos/smoke-claim-consistency.sh +222 -0
- package/scripts/bongos/smoke-dependencies.sh +143 -0
- package/scripts/bongos/smoke-gds.js +256 -0
- package/scripts/bongos/smoke-write.sh +275 -0
- package/scripts/bongos/source-text-test-scan.js +106 -0
- package/scripts/bongos/spark.js +294 -0
- package/scripts/bongos/stale-worktrees.js +212 -0
- package/scripts/bongos/start-goals.js +102 -0
- package/scripts/bongos/start.js +1090 -0
- package/scripts/bongos/status.js +545 -0
- package/scripts/bongos/strand-watch.js +315 -0
- package/scripts/bongos/stranded-rows.js +200 -0
- package/scripts/bongos/stray-unit-sweep.sh +71 -0
- package/scripts/bongos/surface.js +101 -0
- package/scripts/bongos/task.js +273 -0
- package/scripts/bongos/test-path-guard.js +99 -0
- package/scripts/bongos/triage.js +742 -0
- package/scripts/bongos/turn-end-sound.js +74 -0
- package/scripts/bongos/tweak-halt.js +70 -0
- package/scripts/bongos/tweak-renders.js +200 -0
- package/scripts/bongos/uat.js +120 -0
- package/scripts/bongos/ui-kit-transform.js +43 -0
- package/scripts/bongos/update-channel.js +512 -0
- package/scripts/bongos/update-sweep.js +829 -0
- package/scripts/bongos/upgrade-artist-gate.js +46 -0
- package/scripts/bongos/upgrade-commit-identity.js +32 -0
- package/scripts/bongos/upgrade-migrate.js +224 -0
- package/scripts/bongos/upgrade-operator.js +73 -0
- package/scripts/bongos/upgrade-outcome.js +174 -0
- package/scripts/bongos/upgrade.js +1499 -0
- package/scripts/bongos/validate-design.js +12 -0
- package/scripts/bongos/version-close.js +621 -0
- package/scripts/bongos/version-literal-guard.js +140 -0
- package/scripts/bongos/version-select.js +79 -0
- package/scripts/bongos/visual-review-nudge.js +39 -0
- package/scripts/bongos/wedge-remedy.js +174 -0
- package/scripts/bongos/worktree-claim-guard.js +130 -0
- package/scripts/bongos/worktree.js +449 -0
- package/scripts/discord/post.js +1 -1
- package/scripts/gds/README.md +9 -0
- package/scripts/gds/abandon.js +6 -136
- package/scripts/gds/accessibility-replay.js +5 -194
- package/scripts/gds/address-park-sweep.sh +7 -90
- package/scripts/gds/adopt-detect.js +5 -121
- package/scripts/gds/adopt-import.js +5 -109
- package/scripts/gds/adopt-preflight.js +5 -185
- package/scripts/gds/adr-namespace.js +5 -271
- package/scripts/gds/agent-invoke.js +5 -687
- package/scripts/gds/agents-sync.js +5 -500
- package/scripts/gds/api-alias-caller-scan.js +5 -255
- package/scripts/gds/api-artifacts.js +6 -0
- package/scripts/gds/api.js +6 -85
- package/scripts/gds/api.ps1 +8 -26
- package/scripts/gds/architect-audit.js +6 -293
- package/scripts/gds/artifact-audit.js +5 -324
- package/scripts/gds/artifact-format.js +6 -203
- package/scripts/gds/artifact-staleness.js +5 -265
- package/scripts/gds/ask.js +5 -229
- package/scripts/gds/audit-authorship.sh +7 -328
- package/scripts/gds/audit-deps.js +6 -188
- package/scripts/gds/audit-gate.js +5 -119
- package/scripts/gds/audit-rank-roles.js +6 -171
- package/scripts/gds/audit-replay.sh +7 -204
- package/scripts/gds/autobongos-digest.js +5 -121
- package/scripts/gds/autobongos-grade-cap.js +5 -186
- package/scripts/gds/autobongos-loop.js +5 -208
- package/scripts/gds/autobongos-run.js +5 -1359
- package/scripts/gds/autobongos-service.cmd +8 -49
- package/scripts/gds/autobongos-service.ps1 +8 -270
- package/scripts/gds/autobongos-tweak-lane.js +5 -261
- package/scripts/gds/autobongos-verify.js +5 -616
- package/scripts/gds/autonomy-gate.js +5 -133
- package/scripts/gds/autonomy-gauge-statusline.js +5 -122
- package/scripts/gds/autonomy-gauge.js +5 -82
- package/scripts/gds/backfill-cost.js +6 -175
- package/scripts/gds/backfill-disciplines.js +5 -142
- package/scripts/gds/backfill-session-earnings.js +5 -121
- package/scripts/gds/backfill-session-types.js +5 -85
- package/scripts/gds/backfill-sessions.js +5 -409
- package/scripts/gds/backfill-subagent-rewards.js +5 -483
- package/scripts/gds/backfill-task-rewards.js +5 -411
- package/scripts/gds/backlog-review.js +6 -398
- package/scripts/gds/backup-dropin-sweep.sh +7 -94
- package/scripts/gds/backup.js +6 -115
- package/scripts/gds/baseline-staleness.js +5 -215
- package/scripts/gds/bfg-principal-token.js +6 -64
- package/scripts/gds/bug-attachment-audit.js +5 -147
- package/scripts/gds/bug-attachment-recover.js +5 -196
- package/scripts/gds/bug-triage.js +6 -291
- package/scripts/gds/build-bongos.js +5 -92
- package/scripts/gds/bump-version.js +5 -219
- package/scripts/gds/capture.js +6 -480
- package/scripts/gds/category-advisory-guard.js +5 -205
- package/scripts/gds/ci-grade.js +6 -81
- package/scripts/gds/claim-recall.js +6 -111
- package/scripts/gds/claim.js +6 -788
- package/scripts/gds/classify-discipline.js +6 -152
- package/scripts/gds/claude-materialize.js +5 -1198
- package/scripts/gds/claude-md-template.js +5 -140
- package/scripts/gds/cleanup-orphans.js +6 -73
- package/scripts/gds/cli-lib.js +6 -1204
- package/scripts/gds/client-baseurl-guard.js +5 -114
- package/scripts/gds/codemod-rename-src-gds.js +6 -144
- package/scripts/gds/conductor-dispatch.js +6 -534
- package/scripts/gds/context-pack.js +6 -325
- package/scripts/gds/control-manifest.js +5 -197
- package/scripts/gds/copy-apply.js +5 -756
- package/scripts/gds/copy-inventory.js +5 -569
- package/scripts/gds/core-origin-follow.js +5 -184
- package/scripts/gds/core-pin-sync.js +5 -258
- package/scripts/gds/coreB-carve-proof.sh +7 -724
- package/scripts/gds/cost-report.js +6 -62
- package/scripts/gds/cost.js +6 -46
- package/scripts/gds/credential-presence.js +5 -294
- package/scripts/gds/cutover-codemod.js +5 -239
- package/scripts/gds/db-backup-nightly.sh +7 -231
- package/scripts/gds/db-backup-offsite.sh +7 -266
- package/scripts/gds/demote.js +6 -44
- package/scripts/gds/deploy-divergence.js +6 -445
- package/scripts/gds/deploy-main-audit-gate.sh +7 -96
- package/scripts/gds/design-sync.js +5 -10
- package/scripts/gds/dev-box-guard.js +5 -164
- package/scripts/gds/dev-lib.js +5 -721
- package/scripts/gds/dev.js +5 -643
- package/scripts/gds/device-flow-help.js +5 -70
- package/scripts/gds/diagnose-achievements.js +5 -116
- package/scripts/gds/diagram-facts.js +6 -355
- package/scripts/gds/discord-channels.js +5 -75
- package/scripts/gds/do-api.js +6 -109
- package/scripts/gds/doc-cli-guard.js +5 -266
- package/scripts/gds/doc-comment-coverage.js +5 -137
- package/scripts/gds/doc-control-claims.js +5 -166
- package/scripts/gds/docs-entropy.js +5 -443
- package/scripts/gds/doctor.js +6 -492
- package/scripts/gds/enroll-control-plane.js +5 -99
- package/scripts/gds/exec-path-guard.js +5 -562
- package/scripts/gds/feedback-send.js +5 -247
- package/scripts/gds/fetch-art-key.js +5 -104
- package/scripts/gds/fetch-sound-prefs.js +5 -124
- package/scripts/gds/figma-design-sync.js +5 -9
- package/scripts/gds/find-untested.js +5 -369
- package/scripts/gds/fitness-checks-error-envelope.js +5 -133
- package/scripts/gds/fitness-checks-identity.js +5 -342
- package/scripts/gds/fitness-checks-packaging.js +5 -131
- package/scripts/gds/fitness-checks-route-pins.js +5 -75
- package/scripts/gds/fitness-checks-vocabulary.js +5 -391
- package/scripts/gds/fitness-checks-write-validation.js +5 -156
- package/scripts/gds/fitness-lib.js +5 -126
- package/scripts/gds/fitness-migration-contraction.js +5 -49
- package/scripts/gds/fitness-ratchets.js +5 -563
- package/scripts/gds/fitness.js +5 -1493
- package/scripts/gds/gate-review.js +5 -328
- package/scripts/gds/gating.js +5 -346
- package/scripts/gds/gds-literal-scan.js +5 -194
- package/scripts/gds/gen-api-client.js +5 -555
- package/scripts/gds/gen-api-docs.js +5 -1100
- package/scripts/gds/gen-atlas.js +5 -364
- package/scripts/gds/gen-diagrams.js +6 -607
- package/scripts/gds/gen-file-map.js +5 -335
- package/scripts/gds/gen-freshness.js +5 -31
- package/scripts/gds/gen-interaction-posture.js +5 -120
- package/scripts/gds/gen-repo-map.js +5 -538
- package/scripts/gds/gen-role-responsibilities.js +5 -133
- package/scripts/gds/gen-session-index.js +5 -241
- package/scripts/gds/gen-sounds.js +5 -174
- package/scripts/gds/git-merge-regen.js +6 -291
- package/scripts/gds/go-live.js +5 -1069
- package/scripts/gds/goal-names.js +5 -100
- package/scripts/gds/goal.js +6 -271
- package/scripts/gds/government-walk.js +5 -1475
- package/scripts/gds/grade-correlation-audit.js +5 -308
- package/scripts/gds/grade-replay.js +5 -235
- package/scripts/gds/grade-smoke.js +6 -204
- package/scripts/gds/home-isolation-guard.js +5 -125
- package/scripts/gds/hooks/pre-push.js +5 -209
- package/scripts/gds/howto.js +5 -143
- package/scripts/gds/http-api-client.js +6 -81
- package/scripts/gds/identity-strip.js +5 -74
- package/scripts/gds/init.js +5 -1157
- package/scripts/gds/install-git-hooks.js +5 -198
- package/scripts/gds/instance-apex.js +5 -37
- package/scripts/gds/intent-retry.js +5 -51
- package/scripts/gds/key-card.js +6 -221
- package/scripts/gds/key-status.js +5 -124
- package/scripts/gds/land-watch.js +5 -199
- package/scripts/gds/leak-scan-allowlist.js +5 -291
- package/scripts/gds/learning-capture.js +6 -144
- package/scripts/gds/lib.sh +7 -73
- package/scripts/gds/linkify-refs.js +6 -654
- package/scripts/gds/lint-infra-exec.js +5 -68
- package/scripts/gds/lint-migrations.js +5 -100
- package/scripts/gds/lint-workflow-exec.js +5 -61
- package/scripts/gds/local-preview-lib.js +6 -128
- package/scripts/gds/local-preview.js +6 -81
- package/scripts/gds/login.js +5 -358
- package/scripts/gds/main-audit.js +5 -623
- package/scripts/gds/me.js +6 -96
- package/scripts/gds/memory-map.js +5 -174
- package/scripts/gds/merge-lock.js +6 -70
- package/scripts/gds/migration-namespace.js +5 -311
- package/scripts/gds/mirror-lineage-check.js +5 -341
- package/scripts/gds/mirror-redact.js +5 -348
- package/scripts/gds/module-artifact.js +6 -289
- package/scripts/gds/module-assess-docs.js +6 -237
- package/scripts/gds/module-assess-installs.js +6 -227
- package/scripts/gds/module-assess-score.js +6 -110
- package/scripts/gds/module-assess-security.js +6 -251
- package/scripts/gds/module-assess-tests.js +6 -277
- package/scripts/gds/module-assess-version.js +6 -91
- package/scripts/gds/module-error-report.js +5 -146
- package/scripts/gds/module-test-runner.js +6 -413
- package/scripts/gds/module.js +5 -1374
- package/scripts/gds/move-escalation.js +5 -140
- package/scripts/gds/newcomer-floor.js +6 -420
- package/scripts/gds/newcomer-floor.test.js +6 -234
- package/scripts/gds/next-number.js +6 -162
- package/scripts/gds/oauth-secret.js +5 -200
- package/scripts/gds/onboard.js +5 -265
- package/scripts/gds/onboarding-analyze.js +6 -225
- package/scripts/gds/onboarding-config.js +6 -179
- package/scripts/gds/onboarding-ship.js +6 -91
- package/scripts/gds/pack-env-prefix.js +5 -72
- package/scripts/gds/package-core.js +5 -1102
- package/scripts/gds/page-inventory.js +5 -346
- package/scripts/gds/page-reader.js +5 -720
- package/scripts/gds/paste-token.js +6 -211
- package/scripts/gds/plain-cards.js +5 -350
- package/scripts/gds/portability-smoke-art.sh +7 -84
- package/scripts/gds/portability-smoke-memory.sh +7 -175
- package/scripts/gds/preflight.js +5 -212
- package/scripts/gds/probe-cache.js +5 -244
- package/scripts/gds/prove-strict-versioning.js +6 -283
- package/scripts/gds/provision-auto-upgrade.js +5 -161
- package/scripts/gds/provision-byo-host.js +5 -67
- package/scripts/gds/provision-capacity.js +5 -78
- package/scripts/gds/provision-caps.js +5 -62
- package/scripts/gds/provision-config.js +5 -232
- package/scripts/gds/provision-core-upgrade.js +5 -1165
- package/scripts/gds/provision-disconnect.js +5 -87
- package/scripts/gds/provision-github-setup.js +5 -167
- package/scripts/gds/provision-net.js +5 -356
- package/scripts/gds/provision-pin-key.js +5 -175
- package/scripts/gds/provision-pin-replay.js +5 -125
- package/scripts/gds/provision-process.js +5 -169
- package/scripts/gds/provision-render.js +5 -200
- package/scripts/gds/provision-repo-auth.js +5 -163
- package/scripts/gds/provision-repo-private.js +5 -97
- package/scripts/gds/provision-repo.js +5 -1336
- package/scripts/gds/provision-teardown.js +5 -105
- package/scripts/gds/provision-units.js +5 -468
- package/scripts/gds/provision.js +5 -1477
- package/scripts/gds/publish-credential-check.js +6 -65
- package/scripts/gds/publish-integration-gate.js +5 -140
- package/scripts/gds/publish-manifest.js +5 -437
- package/scripts/gds/publish-watch.js +5 -266
- package/scripts/gds/pulse.js +6 -335
- package/scripts/gds/push-main.js +5 -54
- package/scripts/gds/push-path-brief.js +5 -98
- package/scripts/gds/read-session-export.js +5 -357
- package/scripts/gds/reauth.js +6 -154
- package/scripts/gds/recall.js +6 -182
- package/scripts/gds/redteam-patrol.js +5 -435
- package/scripts/gds/regen-instance-docs.js +5 -45
- package/scripts/gds/release-core.js +5 -78
- package/scripts/gds/release-notes.js +5 -270
- package/scripts/gds/release.js +6 -138
- package/scripts/gds/rename-goals.js +5 -107
- package/scripts/gds/rename-history-check.js +5 -367
- package/scripts/gds/rename-ledger-prose.js +5 -480
- package/scripts/gds/render-api.js +5 -196
- package/scripts/gds/render-check.js +5 -204
- package/scripts/gds/render-ideas.js +6 -225
- package/scripts/gds/render-payload.js +5 -84
- package/scripts/gds/repo-metrics.js +6 -362
- package/scripts/gds/review-sheet.js +5 -106
- package/scripts/gds/role-pack-guard.js +5 -141
- package/scripts/gds/route-shadow-guard.js +5 -173
- package/scripts/gds/routine-schedule.js +5 -281
- package/scripts/gds/run-routine.js +5 -255
- package/scripts/gds/run-unit-tests.js +6 -659
- package/scripts/gds/runner-drift.js +5 -149
- package/scripts/gds/sandbox-stage.js +6 -458
- package/scripts/gds/scope-map-guard.js +5 -299
- package/scripts/gds/scope-roster-guard.js +5 -117
- package/scripts/gds/seam-wiring-guard.js +5 -274
- package/scripts/gds/search-ingest-db.js +6 -287
- package/scripts/gds/search-ingest-md.js +6 -268
- package/scripts/gds/seed-adopt-tasks.js +6 -74
- package/scripts/gds/seed-api-contract-goal-tasks.js +6 -140
- package/scripts/gds/seed-board-room-tasks.js +6 -196
- package/scripts/gds/seed-bongos-app-tasks.js +6 -293
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +6 -80
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +6 -99
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +6 -306
- package/scripts/gds/seed-bongos-module-system-tasks.js +6 -345
- package/scripts/gds/seed-bongos-tasks.js +6 -213
- package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +6 -152
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +6 -104
- package/scripts/gds/seed-bv1-community-tasks.js +6 -131
- package/scripts/gds/seed-bv1-privacy-invites-tasks.js +6 -133
- package/scripts/gds/seed-bv1-profile-page-tasks.js +6 -161
- package/scripts/gds/seed-bv1-profiles-tasks.js +6 -135
- package/scripts/gds/seed-bv1-r01-delta-tasks.js +6 -154
- package/scripts/gds/seed-bv2-ideator-surface-tasks.js +6 -390
- package/scripts/gds/seed-bv2-tweak-mode-tasks.js +6 -277
- package/scripts/gds/seed-gds-v4-tasks.js +6 -623
- package/scripts/gds/seed-governance-tasks.js +6 -339
- package/scripts/gds/seed-medusa-tasks.js +6 -439
- package/scripts/gds/seed-module-upstreaming.js +5 -217
- package/scripts/gds/seed-productization-tasks.js +6 -234
- package/scripts/gds/seed-project-admin-console-tasks.js +6 -88
- package/scripts/gds/seed-provisioning-tasks.js +6 -82
- package/scripts/gds/seed-staging.sh +7 -116
- package/scripts/gds/seed-strict-versioning-tasks.js +6 -274
- package/scripts/gds/seed-v2-world-tasks.js +6 -282
- package/scripts/gds/seed-verify-criteria.js +6 -124
- package/scripts/gds/sequence.js +6 -596
- package/scripts/gds/session-cost.js +6 -429
- package/scripts/gds/session-digest-build.js +6 -293
- package/scripts/gds/session-digest.js +6 -199
- package/scripts/gds/session-eval.js +6 -630
- package/scripts/gds/session-transcript-build.js +6 -155
- package/scripts/gds/session-transcript-upload.js +6 -29
- package/scripts/gds/session.js +6 -403
- package/scripts/gds/setup.js +6 -645
- package/scripts/gds/ship-check.js +5 -85
- package/scripts/gds/ship-deploy-target.js +5 -392
- package/scripts/gds/ship-escalation.js +5 -166
- package/scripts/gds/ship-finish.js +5 -419
- package/scripts/gds/ship-flow.js +5 -1216
- package/scripts/gds/ship-git.js +5 -99
- package/scripts/gds/ship-grade-steps.js +5 -546
- package/scripts/gds/ship-grade.js +5 -425
- package/scripts/gds/ship-honesty.js +5 -312
- package/scripts/gds/ship-io.js +5 -138
- package/scripts/gds/ship-land.js +5 -1129
- package/scripts/gds/ship-memory-sync.js +5 -248
- package/scripts/gds/ship-merge-gate.js +5 -224
- package/scripts/gds/ship-merge.js +5 -465
- package/scripts/gds/ship-mode-ask.js +5 -137
- package/scripts/gds/ship-mode-manual.js +5 -53
- package/scripts/gds/ship-preflight-steps.js +5 -398
- package/scripts/gds/ship-regen.js +5 -313
- package/scripts/gds/ship-resume.js +5 -200
- package/scripts/gds/ship-session-upload.js +5 -312
- package/scripts/gds/ship-state.js +5 -109
- package/scripts/gds/ship-surprise.js +5 -83
- package/scripts/gds/ship-visual.js +5 -74
- package/scripts/gds/ship.js +5 -380
- package/scripts/gds/shipping-mode.js +5 -69
- package/scripts/gds/skill-lint.js +5 -248
- package/scripts/gds/skill-preflight.js +5 -359
- package/scripts/gds/smoke-batch-claim.sh +7 -237
- package/scripts/gds/smoke-batch-release.sh +7 -315
- package/scripts/gds/smoke-claim-consistency.sh +7 -221
- package/scripts/gds/smoke-dependencies.sh +7 -142
- package/scripts/gds/smoke-gds.js +6 -255
- package/scripts/gds/smoke-write.sh +7 -274
- package/scripts/gds/source-text-test-scan.js +7 -0
- package/scripts/gds/spark.js +6 -293
- package/scripts/gds/stale-worktrees.js +5 -210
- package/scripts/gds/start-goals.js +5 -101
- package/scripts/gds/start.js +6 -1089
- package/scripts/gds/status.js +6 -544
- package/scripts/gds/strand-watch.js +5 -313
- package/scripts/gds/stranded-rows.js +5 -198
- package/scripts/gds/stray-unit-sweep.sh +7 -70
- package/scripts/gds/surface.js +5 -100
- package/scripts/gds/task.js +6 -272
- package/scripts/gds/test-path-guard.js +5 -97
- package/scripts/gds/triage.js +6 -741
- package/scripts/gds/turn-end-sound.js +5 -72
- package/scripts/gds/tweak-halt.js +5 -68
- package/scripts/gds/tweak-renders.js +5 -198
- package/scripts/gds/uat.js +6 -119
- package/scripts/gds/ui-kit-transform.js +5 -41
- package/scripts/gds/update-channel.js +5 -510
- package/scripts/gds/update-sweep.js +5 -827
- package/scripts/gds/upgrade-artist-gate.js +5 -45
- package/scripts/gds/upgrade-commit-identity.js +5 -31
- package/scripts/gds/upgrade-migrate.js +5 -223
- package/scripts/gds/upgrade-operator.js +6 -73
- package/scripts/gds/upgrade-outcome.js +5 -173
- package/scripts/gds/upgrade.js +5 -1497
- package/scripts/gds/validate-design.js +5 -10
- package/scripts/gds/version-close.js +6 -620
- package/scripts/gds/version-literal-guard.js +5 -138
- package/scripts/gds/version-select.js +5 -77
- package/scripts/gds/visual-review-nudge.js +5 -38
- package/scripts/gds/wedge-remedy.js +5 -173
- package/scripts/gds/worktree-claim-guard.js +5 -129
- package/scripts/gds/worktree.js +6 -448
- package/scripts/hall-preview/refresh-fixtures.js +1 -1
- package/scripts/hall-preview/server.js +2 -2
- package/scripts/migrate.sh +3 -3
- package/scripts/public-mirror-export.js +3 -3
- package/scripts/setup-builder.js +7 -7
- package/src/bongos/api-errors.js +1 -0
- package/src/bongos/api-prefix.js +1 -1
- package/src/bongos/auth-github.js +19 -3
- package/src/bongos/auth-idp.js +7 -0
- package/src/bongos/claim-attribution.js +2 -2
- package/src/bongos/core-moves.js +1 -1
- package/src/bongos/core-update.js +3 -3
- package/src/bongos/craft-mode.js +2 -2
- package/src/bongos/db-kernel.js +15 -3
- package/src/bongos/db.js +30 -4
- package/src/bongos/github-repos.js +1 -1
- package/src/bongos/llm-cache.js +1 -1
- package/src/bongos/llm-pricing.js +1 -1
- package/src/bongos/metrics.js +1 -1
- package/src/bongos/middleware/rate-limit.js +2 -2
- package/src/bongos/module-listing.js +93 -1
- package/src/bongos/module-scope-map.js +90 -85
- package/src/bongos/module-store.js +1 -1
- package/src/bongos/module-submissions.js +1 -1
- package/src/bongos/optimizer.js +1 -1
- package/src/bongos/package-registry.js +1 -1
- package/src/bongos/path-match.js +1 -1
- package/src/bongos/permission-path-check.js +3 -3
- package/src/bongos/platform-visibility-gate.js +3 -5
- package/src/bongos/prelaunch-gate.js +7 -6
- package/src/bongos/route-rank-check.js +2 -2
- package/src/bongos/routes/auth.js +15 -2
- package/src/bongos/routes/backup.js +1 -1
- package/src/bongos/routes/builders.js +29 -1
- package/src/bongos/routes/me.js +49 -18
- package/src/bongos/routes/modules.js +29 -9
- package/src/bongos/routes/public.js +3 -72
- package/src/bongos/schema-lag.js +1 -1
- package/src/bongos/sensitive-surfaces.js +1 -1
- package/src/bongos/serve-internal.js +65 -16
- package/src/bongos/software-update.js +2 -2
- package/src/bongos/upgrade-hold.js +3 -3
- package/src/hall-look.js +6 -5
- package/src/module-api.js +3 -3
- package/src/module-loader/catalog.js +1 -1
- package/src/module-loader/error-tally.js +3 -3
- package/src/module-loader/manifest-schema.js +3 -3
- package/src/platform-server.js +10 -0
- package/src/role-responsibilities.js +1 -1
- package/src/stale-timer.js +2 -2
- package/tests/accessibility_replay.mjs +1 -1
- package/tests/activity_rollup_order_db.mjs +1 -1
- package/tests/activity_snapshots_db.mjs +1 -1
- package/tests/adopt_detect.mjs +1 -1
- package/tests/adopt_import.mjs +1 -1
- package/tests/adopt_preflight.mjs +1 -1
- package/tests/adr_0089_scope.mjs +10 -7
- package/tests/adr_0162_citation.mjs +1 -1
- package/tests/adr_0162_gate_citation.mjs +1 -1
- package/tests/agent_invoke.mjs +1 -1
- package/tests/agent_invoke_live.mjs +3 -3
- package/tests/agents_cron_trigger.mjs +2 -2
- package/tests/agents_roster.mjs +1 -1
- package/tests/agents_sync.mjs +4 -4
- package/tests/allow_rule_guard.mjs +20 -20
- package/tests/api_alias_caller_ratchet.mjs +9 -10
- package/tests/api_alias_exceptions.mjs +7 -6
- package/tests/api_artifacts.mjs +214 -0
- package/tests/api_cli_flags.mjs +3 -3
- package/tests/api_client.mjs +56 -54
- package/tests/api_docs.mjs +30 -9
- package/tests/api_error_envelope_buildcheck.mjs +1 -1
- package/tests/api_msys_path_repair.mjs +2 -2
- package/tests/architect.mjs +1 -1
- package/tests/art_stage.mjs +2 -2
- package/tests/artifact_audit.mjs +2 -2
- package/tests/artifact_staleness.mjs +9 -9
- package/tests/atlas.mjs +1 -1
- package/tests/audit_deps.mjs +1 -1
- package/tests/audit_gate.mjs +1 -1
- package/tests/auth_flow_slice1.mjs +3 -3
- package/tests/auth_inactive_builder.mjs +43 -5
- package/tests/auth_session_expiry_db.mjs +16 -4
- package/tests/auth_session_ttl.mjs +23 -0
- package/tests/authorship_audit.mjs +2 -2
- package/tests/autobongos_cadence.mjs +1 -1
- package/tests/autobongos_fence.mjs +1 -1
- package/tests/autobongos_grade_cap.mjs +2 -2
- package/tests/autobongos_heartbeat_head.mjs +1 -1
- package/tests/autobongos_host_access.mjs +3 -3
- package/tests/autobongos_loop.mjs +17 -17
- package/tests/autobongos_service_ps1.mjs +3 -3
- package/tests/autobongos_tweak_lane.mjs +2 -2
- package/tests/autobongos_verify.mjs +2 -2
- package/tests/autonomous_fit_protected_paths.mjs +3 -3
- package/tests/autonomy_config_idle.mjs +1 -1
- package/tests/autonomy_digest.mjs +1 -1
- package/tests/autonomy_gate.mjs +1 -1
- package/tests/autonomy_gauge.mjs +2 -2
- package/tests/autonomy_runner_identity.mjs +1 -1
- package/tests/backfill_sessions.mjs +8 -8
- package/tests/backfill_subagent_rewards.mjs +1 -1
- package/tests/backfill_task_rewards.mjs +2 -2
- package/tests/backlog_review.mjs +3 -3
- package/tests/baseline_staleness.mjs +2 -2
- package/tests/bfg.mjs +11 -5
- package/tests/bfg_write.mjs +20 -11
- package/tests/blockers_hall.mjs +17 -5
- package/tests/blockers_page_ui.mjs +56 -12
- package/tests/bongos_dispatch.mjs +5 -5
- package/tests/bongos_login.mjs +2 -2
- package/tests/bug_attachment_audit.mjs +1 -1
- package/tests/bug_attachment_recover.mjs +2 -2
- package/tests/bug_triage.mjs +3 -3
- package/tests/build_info.mjs +1 -1
- package/tests/builder_tester_tag.mjs +184 -0
- package/tests/bump_version.mjs +3 -3
- package/tests/canonical_profile_url.mjs +1 -1
- package/tests/capture_error_format.mjs +1 -1
- package/tests/capture_goal_routing.mjs +2 -2
- package/tests/category_advisory_guard.mjs +3 -3
- package/tests/claim_error_surface.mjs +2 -2
- package/tests/claim_from_task_and_home.mjs +1 -1
- package/tests/claim_gate_open_blocker.mjs +7 -4
- package/tests/claim_gate_rebase.mjs +12 -8
- package/tests/claim_recall.mjs +4 -4
- package/tests/claim_resolve_worktree_lock.mjs +1 -1
- package/tests/claude_materialize.mjs +58 -58
- package/tests/clawback_confirmed_backlog.mjs +8 -5
- package/tests/cli_allowlist_permissions.mjs +11 -11
- package/tests/cli_dispatcher.mjs +2 -2
- package/tests/cli_err_code.mjs +6 -6
- package/tests/cli_exit_no_abort.mjs +7 -7
- package/tests/cli_lib_formatters.mjs +2 -2
- package/tests/cli_sessions.mjs +5 -5
- package/tests/cli_surface.mjs +15 -15
- package/tests/cli_token_reissue.mjs +2 -2
- package/tests/client_baseurl_guard.mjs +1 -1
- package/tests/client_bundle_route.mjs +35 -4
- package/tests/cloud_bongos_pack.mjs +1 -1
- package/tests/community_leaderboard_db.mjs +1 -1
- package/tests/conductor.mjs +18 -18
- package/tests/conflict_resolve.mjs +14 -16
- package/tests/consumer_layout_boot.mjs +12 -12
- package/tests/context_pack.mjs +6 -6
- package/tests/control_manifest.mjs +4 -4
- package/tests/copy_desk_page_reads.mjs +1 -1
- package/tests/copy_desk_page_release.mjs +192 -0
- package/tests/copy_inventory.mjs +2 -2
- package/tests/core_auto_upgrade.mjs +1 -1
- package/tests/core_move_notice.mjs +2 -2
- package/tests/core_origin_follow.mjs +7 -7
- package/tests/core_pin_sync.mjs +3 -3
- package/tests/core_upgrade_door.mjs +2 -2
- package/tests/core_upgrade_owner_door.mjs +34 -1
- package/tests/core_upgrade_runner.mjs +19 -19
- package/tests/credential_presence.mjs +2 -2
- package/tests/credit_grant.mjs +3 -3
- package/tests/credit_log_single_writer.mjs +5 -5
- package/tests/criterion_ref_status.mjs +2 -2
- package/tests/cross_discipline_priority_direction.mjs +3 -3
- package/tests/cutover_codemod.mjs +2 -4
- package/tests/db_backup_nightly.mjs +1 -1
- package/tests/db_backup_offsite.mjs +1 -1
- package/tests/db_ownership_handover.mjs +2 -2
- package/tests/dead_deps.mjs +3 -3
- package/tests/dependency_advisory.mjs +1 -1
- package/tests/deploy_audit_gate.mjs +4 -4
- package/tests/deploy_divergence_line.mjs +36 -16
- package/tests/deploy_page_projects.mjs +35 -4
- package/tests/dev_lib.mjs +2 -2
- package/tests/dev_windows.mjs +2 -2
- package/tests/diagram_facts_offset.mjs +1 -1
- package/tests/diagram_repo_slug.mjs +1 -1
- package/tests/diff_derived_signals.mjs +2 -2
- package/tests/dns_preflight.mjs +2 -2
- package/tests/do_api.mjs +3 -3
- package/tests/doc_cli_guard.mjs +28 -28
- package/tests/doc_comment_coverage.mjs +1 -1
- package/tests/doc_control_claims.mjs +11 -11
- package/tests/docs_entropy.mjs +2 -2
- package/tests/docs_openapi_route.mjs +8 -6
- package/tests/doctor_hooks.mjs +1 -1
- package/tests/edit_hints.mjs +3 -2
- package/tests/effective_visibility_predicate.mjs +3 -3
- package/tests/env_knobs_neutral_prefix.mjs +13 -13
- package/tests/exec_path_guard.mjs +4 -4
- package/tests/executed_closure.mjs +18 -12
- package/tests/file_map.mjs +1 -1
- package/tests/find_untested.mjs +13 -13
- package/tests/first_admin_bootstrap.mjs +1 -1
- package/tests/fitness.mjs +16 -16
- package/tests/fitness_gds_ratchet.mjs +2 -2
- package/tests/fitness_installed_core.mjs +3 -3
- package/tests/fitness_no_raw_nul.mjs +1 -1
- package/tests/fitness_ratchets.mjs +3 -3
- package/tests/fitness_scope_roster.mjs +2 -2
- package/tests/fitness_two_root.mjs +8 -8
- package/tests/fixtures/accessibility-replay.json +5 -5
- package/tests/fixtures/grade-replay/recorded-panel-grades.json +1 -1
- package/tests/fixtures/ideator-prefold/README.md +1 -1
- package/tests/fixtures/page-ledger.mjs +22 -0
- package/tests/fixtures/session-506-digest.json +5 -5
- package/tests/gate_approvals.mjs +50 -0
- package/tests/gate_review.mjs +8 -8
- package/tests/gating_review_retired.mjs +2 -2
- package/tests/gen_freshness.mjs +3 -3
- package/tests/git_guard.mjs +3 -3
- package/tests/git_merge_regen.mjs +41 -65
- package/tests/github_one_trip.mjs +4 -4
- package/tests/github_pick_up.mjs +2 -2
- package/tests/github_push_merge_driver.mjs +1 -1
- package/tests/go_live.mjs +3 -2
- package/tests/goal_category.mjs +1 -1
- package/tests/goal_cli.mjs +2 -2
- package/tests/goal_create_spec_entry.mjs +1 -1
- package/tests/goal_invite_consent.mjs +3 -1
- package/tests/goal_move.mjs +1 -1
- package/tests/goal_names.mjs +2 -2
- package/tests/goal_working_area.mjs +1 -1
- package/tests/government_agent_asks.mjs +7 -7
- package/tests/government_board_room_copy.mjs +6 -1
- package/tests/government_board_view.mjs +39 -0
- package/tests/government_board_vote_ui.mjs +39 -1
- package/tests/government_config.mjs +2 -2
- package/tests/government_live_proof_shape.mjs +1 -1
- package/tests/government_protected_surfaces.mjs +54 -53
- package/tests/government_require_permission.mjs +9 -1
- package/tests/government_vanilla_boot.mjs +1 -1
- package/tests/government_vocabulary.mjs +2 -2
- package/tests/government_walk.mjs +2 -2
- package/tests/grade_body_limit.mjs +6 -4
- package/tests/grade_correlation_audit.mjs +3 -3
- package/tests/grade_diff_hash.mjs +1 -1
- package/tests/grade_engine_replay.mjs +2 -2
- package/tests/grade_server_authoritative.mjs +1 -1
- package/tests/grade_unavailable_display.mjs +1 -1
- package/tests/grader.mjs +8 -24
- package/tests/grader_generated_provenance.mjs +14 -14
- package/tests/grader_no_artifact.mjs +1 -1
- package/tests/grader_prompt_fencing.mjs +1 -1
- package/tests/grader_quality_window_overflow.mjs +1 -1
- package/tests/grader_reader_lens.mjs +3 -3
- package/tests/grader_regrade_delta.mjs +1 -1
- package/tests/grader_unbacked_score.mjs +2 -2
- package/tests/hall_acclaim_vote_control.mjs +113 -0
- package/tests/hall_asset_cache.mjs +127 -0
- package/tests/hall_audit.mjs +63 -0
- package/tests/hall_board_world.mjs +20 -0
- package/tests/hall_city_card_words.mjs +61 -0
- package/tests/hall_client_request_shape.mjs +5 -1
- package/tests/hall_home_ships_bounded.mjs +160 -0
- package/tests/hall_landing_boot.mjs +15 -2
- package/tests/hall_nav.mjs +33 -0
- package/tests/hall_onboarding_bongos_setup.mjs +1 -1
- package/tests/hall_oversight_world.mjs +44 -4
- package/tests/hall_palette.mjs +12 -9
- package/tests/hall_preview_security.mjs +1 -1
- package/tests/hall_profile_world.mjs +17 -0
- package/tests/hall_reading_world.mjs +16 -1
- package/tests/hall_roster_world.mjs +2 -1
- package/tests/hall_studio_world.mjs +4 -1
- package/tests/hall_work_history_id.mjs +39 -0
- package/tests/held_tweak_reporting.mjs +4 -4
- package/tests/helpers.mjs +3 -3
- package/tests/home_attention_cards.mjs +2 -0
- package/tests/home_bounded_reads.mjs +1 -1
- package/tests/home_isolation_guard.mjs +1 -1
- package/tests/hook_core_resolve.mjs +7 -7
- package/tests/hook_user_agents.mjs +1 -1
- package/tests/hooks_drain.mjs +2 -2
- package/tests/host_topology_skips.mjs +1 -1
- package/tests/howto_guide.mjs +1 -1
- package/tests/howto_moments.mjs +3 -3
- package/tests/hub_account_page.mjs +1 -1
- package/tests/idea_capture_grades.mjs +2 -2
- package/tests/idea_develop_spark.mjs +1 -1
- package/tests/idea_filed_via.mjs +2 -2
- package/tests/idea_ideate_full.mjs +1 -1
- package/tests/idea_spark_mark.mjs +1 -1
- package/tests/idea_spark_queue.mjs +1 -1
- package/tests/idea_triage.mjs +1 -1
- package/tests/idea_triage_grade.mjs +3 -3
- package/tests/idea_vote_permission.mjs +113 -0
- package/tests/idea_votes_fold_db.mjs +123 -0
- package/tests/infra_exec_paths.mjs +12 -12
- package/tests/init.mjs +2 -2
- package/tests/instance_db_migrated_on_code_move.mjs +5 -5
- package/tests/instance_feedback.mjs +9 -9
- package/tests/instance_mismatch_guard.mjs +2 -2
- package/tests/integration_parked_ratchet.mjs +24 -2
- package/tests/intent_retry.mjs +1 -1
- package/tests/interaction_posture.mjs +3 -3
- package/tests/invite_expiry_db.mjs +1 -1
- package/tests/joinability_invite_only.mjs +1 -1
- package/tests/karma_vote_lists.mjs +270 -0
- package/tests/key-card.mjs +2 -2
- package/tests/key-status.mjs +2 -2
- package/tests/knip_ratchet_ambient.mjs +2 -2
- package/tests/known_failing_ratchet.mjs +1 -1
- package/tests/land_watch.mjs +2 -2
- package/tests/landing_legal_pages.mjs +1 -1
- package/tests/landing_page.mjs +2 -2
- package/tests/leaderboard_roles.mjs +4 -1
- package/tests/leak_scan_allowlist.mjs +8 -8
- package/tests/leak_scan_fail_closed.mjs +1 -1
- package/tests/lib_sh_resolution.mjs +9 -9
- package/tests/lifecycle-card.mjs +7 -7
- package/tests/linkify_check.mjs +2 -2
- package/tests/linkify_refs.mjs +1 -1
- package/tests/llm-cache-route.mjs +1 -1
- package/tests/local_preview.mjs +2 -2
- package/tests/main_audit.mjs +3 -3
- package/tests/main_audit_attestation.mjs +1 -1
- package/tests/main_audit_db_roster.mjs +2 -2
- package/tests/main_worktree_busy_reason.mjs +3 -3
- package/tests/markdown_line_endings.mjs +2 -2
- package/tests/me_streak_leaderboard_queries.mjs +193 -0
- package/tests/membership_kind_lockstep.mjs +1 -1
- package/tests/memory_isolation.mjs +16 -8
- package/tests/memory_map.mjs +2 -2
- package/tests/memory_pull.mjs +33 -25
- package/tests/merge_driver_register.mjs +3 -3
- package/tests/migration_allocation.mjs +2 -2
- package/tests/migration_namespace.mjs +1 -1
- package/tests/migrations_forward_safe.mjs +1 -1
- package/tests/mingle.mjs +1 -1
- package/tests/mirror_lineage_fulltree.mjs +3 -3
- package/tests/mirror_lineage_proof.mjs +2 -2
- package/tests/mirror_redact.mjs +4 -4
- package/tests/missing_dep_classify.mjs +3 -3
- package/tests/module-scope-map.mjs +22 -22
- package/tests/module_api_lazy.mjs +1 -1
- package/tests/module_assess_docs.mjs +2 -2
- package/tests/module_assess_installs.mjs +3 -3
- package/tests/module_assess_price_parity.mjs +5 -5
- package/tests/module_assess_score.mjs +5 -5
- package/tests/module_assess_security.mjs +4 -4
- package/tests/module_assess_tests.mjs +3 -3
- package/tests/module_assessments_migration.mjs +1 -1
- package/tests/module_catalog.mjs +2 -2
- package/tests/module_cli.mjs +3 -3
- package/tests/module_entitlements.mjs +1 -1
- package/tests/module_error_counts.mjs +1 -1
- package/tests/module_install.mjs +4 -4
- package/tests/module_loader.mjs +3 -1
- package/tests/module_owned_tests.mjs +1 -1
- package/tests/module_release_channels.mjs +1 -1
- package/tests/module_store_howto.mjs +3 -3
- package/tests/module_store_listing.mjs +139 -2
- package/tests/module_store_publish.mjs +4 -4
- package/tests/module_store_publish_route.mjs +1 -1
- package/tests/module_store_reassess.mjs +1 -1
- package/tests/module_store_registry_migration.mjs +1 -1
- package/tests/module_test_runner.mjs +3 -3
- package/tests/module_update.mjs +4 -4
- package/tests/module_upgrade_updates.mjs +1 -1
- package/tests/move_escalation.mjs +2 -2
- package/tests/nav_permission_atoms.mjs +3 -2
- package/tests/needs_rebase.mjs +7 -3
- package/tests/newcomer_floor.mjs +1 -1
- package/tests/newcomer_generator.mjs +48 -0
- package/tests/next_number.mjs +1 -1
- package/tests/no_artifact_escalation.mjs +3 -3
- package/tests/npm_release_where.mjs +1 -1
- package/tests/oauth_secret.mjs +2 -2
- package/tests/onboard.mjs +6 -6
- package/tests/onboarding_gate.mjs +2 -2
- package/tests/opt_in_skill_modules.mjs +3 -3
- package/tests/own_scope_guard.mjs +1 -1
- package/tests/own_session_revoke.mjs +4 -1
- package/tests/package_core.mjs +10 -11
- package/tests/page_inventory.mjs +5 -5
- package/tests/page_reader.mjs +7 -7
- package/tests/page_tweak_hold.mjs +6 -6
- package/tests/parallel-load.mjs +1 -1
- package/tests/paste_token_api_base.mjs +1 -1
- package/tests/paste_token_session_store.mjs +2 -2
- package/tests/pending_invite_purge_db.mjs +1 -1
- package/tests/permission_path.mjs +1 -1
- package/tests/pin_replay.mjs +1 -1
- package/tests/pin_write_key.mjs +7 -7
- package/tests/plain_cards.mjs +15 -15
- package/tests/pr_assign.mjs +1 -1
- package/tests/pre_push_hook.mjs +3 -3
- package/tests/preflight_checks.mjs +2 -2
- package/tests/prelaunch_gate.mjs +1 -1
- package/tests/privacy_matrix.mjs +1 -1
- package/tests/probe_cache.mjs +6 -6
- package/tests/profile_connections_ui.mjs +4 -3
- package/tests/profile_fields.mjs +11 -1
- package/tests/profile_privacy_owner_view.mjs +1 -1
- package/tests/profile_role_headline.mjs +10 -9
- package/tests/project_admin_console_model.mjs +1 -1
- package/tests/projects_hub.mjs +1 -1
- package/tests/projects_hub_cloud_hop.mjs +1 -1
- package/tests/promote_goal_id.mjs +6 -6
- package/tests/promotion_sweep.mjs +1 -1
- package/tests/provision.mjs +24 -24
- package/tests/provision_account_identity.mjs +2 -2
- package/tests/provision_byo_host.mjs +1 -1
- package/tests/provision_caddy_wiring.mjs +2 -2
- package/tests/provision_disconnect.mjs +2 -2
- package/tests/provision_dns_first.mjs +1 -1
- package/tests/provision_render.mjs +9 -9
- package/tests/provision_repo_private.mjs +3 -3
- package/tests/provision_resource_caps.mjs +5 -5
- package/tests/provision_restart.mjs +3 -3
- package/tests/provision_settings_apply.mjs +7 -7
- package/tests/provision_teardown_address.mjs +2 -2
- package/tests/provisioning_capacity.mjs +5 -5
- package/tests/provisioning_credential_preflight.mjs +1 -1
- package/tests/provisioning_disconnect.mjs +1 -1
- package/tests/provisioning_error_note_scope.mjs +4 -4
- package/tests/provisioning_paid_shape_gate.mjs +2 -2
- package/tests/provisioning_planet_physics.mjs +16 -0
- package/tests/provisioning_runner_drift_notice.mjs +2 -2
- package/tests/provisioning_settings_env.mjs +1 -1
- package/tests/pruned_lockfile_integrity.mjs +1 -1
- package/tests/public_mirror_export.mjs +1 -1
- package/tests/publish_integration_gate.mjs +2 -2
- package/tests/publish_manifest.mjs +6 -6
- package/tests/publish_watch.mjs +1 -1
- package/tests/push_path_brief.mjs +2 -2
- package/tests/raise.mjs +248 -0
- package/tests/rank_roles_audit.mjs +1 -1
- package/tests/rank_tier_single_source.mjs +2 -2
- package/tests/rate_limit_public.mjs +1 -1
- package/tests/read_default_meter.mjs +2 -2
- package/tests/read_session_export.mjs +1 -1
- package/tests/rebase_gate_set_aside.mjs +1 -1
- package/tests/recall_cli.mjs +1 -1
- package/tests/recall_corpus_health.mjs +3 -2
- package/tests/recall_hints.mjs +1 -1
- package/tests/recruiter_sliver_boundary.mjs +1 -1
- package/tests/redteam_patrol.mjs +2 -2
- package/tests/regrade_actual_minutes.mjs +2 -2
- package/tests/regrade_eligibility.mjs +2 -2
- package/tests/release_core.mjs +2 -2
- package/tests/release_notes.mjs +4 -4
- package/tests/rename_history_restraint.mjs +3 -3
- package/tests/rename_ledger_prose.mjs +6 -6
- package/tests/render_api.mjs +1 -1
- package/tests/render_check.mjs +4 -4
- package/tests/render_deploy.mjs +1 -1
- package/tests/repo_info_binding.mjs +1 -1
- package/tests/repo_map.mjs +5 -5
- package/tests/reward_mode_cost_plus_only.mjs +7 -3
- package/tests/reward_mode_surfacing.mjs +1 -1
- package/tests/role_responsibilities.mjs +1 -1
- package/tests/rot_inbox.mjs +10 -7
- package/tests/route_permission_check.mjs +1 -1
- package/tests/route_shadow_guard.mjs +1 -1
- package/tests/route_validation.mjs +1 -1
- package/tests/routine_capture_kind.mjs +6 -6
- package/tests/routine_schedule.mjs +9 -9
- package/tests/run_routine.mjs +5 -5
- package/tests/run_routine_two_root.mjs +5 -5
- package/tests/runner_drift.mjs +17 -17
- package/tests/sandbox_stage.mjs +5 -5
- package/tests/scope_map_guard.mjs +7 -7
- package/tests/scouting_owner_access_and_reach.mjs +1 -1
- package/tests/scripts_gds_forwarders.mjs +224 -0
- package/tests/scrubber_corpus.mjs +2 -2
- package/tests/seam_wiring_guard.mjs +1 -1
- package/tests/search_ingest.mjs +2 -2
- package/tests/search_ingest_db.mjs +1 -1
- package/tests/search_ingest_nul.mjs +6 -6
- package/tests/seed_verify_criteria.mjs +3 -3
- package/tests/sequence.mjs +3 -3
- package/tests/served_paths_shipped.mjs +3 -3
- package/tests/session_506_regression.mjs +1 -1
- package/tests/session_accessibility.mjs +1 -1
- package/tests/session_caps.mjs +1 -1
- package/tests/session_cost.mjs +1 -1
- package/tests/session_digest.mjs +2 -2
- package/tests/session_eval.mjs +6 -6
- package/tests/session_index.mjs +1 -1
- package/tests/session_records.mjs +4 -4
- package/tests/session_rename_fallback.mjs +2 -2
- package/tests/session_reward_on_land.mjs +7 -149
- package/tests/session_reward_on_land_db.mjs +191 -0
- package/tests/session_reward_reconcile.mjs +6 -88
- package/tests/session_reward_reconcile_db.mjs +132 -0
- package/tests/session_transcript.mjs +1 -1
- package/tests/sessions_kit_pager.mjs +2 -1
- package/tests/setup_device_flow_fallback.mjs +2 -2
- package/tests/setup_smoke.mjs +12 -5
- package/tests/setup_status_facts.mjs +54 -12
- package/tests/ship_autolinkify.mjs +2 -2
- package/tests/ship_automerge_injection.mjs +1 -1
- package/tests/ship_base_flag.mjs +1 -1
- package/tests/ship_broadcast_routing.mjs +1 -1
- package/tests/ship_broadcast_server.mjs +2 -2
- package/tests/ship_builder_model.mjs +1 -1
- package/tests/ship_cannot_lie.mjs +9 -9
- package/tests/ship_card.mjs +2 -2
- package/tests/ship_ci_deploy.mjs +7 -7
- package/tests/ship_error_shape.mjs +1 -1
- package/tests/ship_land_ci_not_started.mjs +3 -3
- package/tests/ship_land_conflict.mjs +3 -3
- package/tests/ship_land_proof.mjs +3 -3
- package/tests/ship_land_red_pr.mjs +5 -5
- package/tests/ship_merge_gate.mjs +14 -14
- package/tests/ship_mode.mjs +1 -1
- package/tests/ship_mode_first_ship.mjs +4 -4
- package/tests/ship_mode_hands_off.mjs +8 -8
- package/tests/ship_post_land_verify.mjs +1 -1
- package/tests/ship_preflight.mjs +19 -19
- package/tests/ship_premerge.mjs +1 -1
- package/tests/ship_publish_error.mjs +2 -2
- package/tests/ship_push_diagnosis.mjs +1 -1
- package/tests/ship_regen_copy_inventory.mjs +3 -3
- package/tests/ship_resume.mjs +4 -4
- package/tests/ship_smoke_gate.mjs +1 -1
- package/tests/ship_upload_skip.mjs +1 -1
- package/tests/ship_upstream_deletion.mjs +1 -1
- package/tests/ship_vendored_instance.mjs +8 -8
- package/tests/ship_visual_regrade.mjs +3 -3
- package/tests/ship_zero_commit.mjs +1 -1
- package/tests/skill_docs_unavailable_regrade.mjs +3 -3
- package/tests/skill_friction_kind.mjs +1 -1
- package/tests/skill_grade_recover.mjs +1 -1
- package/tests/skill_grader_health.mjs +1 -1
- package/tests/skill_lint.mjs +1 -1
- package/tests/skill_menu_ruling.mjs +1 -1
- package/tests/skill_preflight.mjs +7 -7
- package/tests/skip_is_not_pass.mjs +22 -1
- package/tests/smoke_dependencies_witness.mjs +2 -2
- package/tests/smoke_preflight.mjs +1 -1
- package/tests/smoke_target.mjs +2 -2
- package/tests/smoke_write_contract.mjs +2 -2
- package/tests/sound_player.mjs +8 -8
- package/tests/source_text_test_ratchet.mjs +97 -0
- package/tests/speciality_corpus_scope.mjs +1 -1
- package/tests/speciality_session_skills.mjs +1 -1
- package/tests/sso_checkin_legacy_field_gone.mjs +4 -2
- package/tests/stale_timer.mjs +3 -3
- package/tests/stale_worktrees.mjs +1 -1
- package/tests/start_env_guard.mjs +1 -1
- package/tests/start_goal_first.mjs +3 -3
- package/tests/start_inbox_count.mjs +1 -1
- package/tests/start_rebase_warning.mjs +1 -1
- package/tests/status_default_version.mjs +3 -3
- package/tests/status_mirror_sync.mjs +1 -1
- package/tests/status_page_no_dead_uptime.mjs +80 -0
- package/tests/stop_refcheck.mjs +1 -1
- package/tests/strand_watch.mjs +1 -1
- package/tests/stranded_rows.mjs +1 -1
- package/tests/stuck_detector.mjs +14 -14
- package/tests/sweep_app_account.mjs +3 -3
- package/tests/task_cli.mjs +2 -2
- package/tests/task_detail_ui.mjs +11 -1
- package/tests/task_discipline_required.mjs +2 -2
- package/tests/task_list_paging.mjs +1 -1
- package/tests/task_visual_slots.mjs +2 -2
- package/tests/terms_acceptance.mjs +1 -1
- package/tests/terms_grandfather_db.mjs +1 -1
- package/tests/test_path_guard.mjs +1 -1
- package/tests/triage_development.mjs +1 -1
- package/tests/tweak_batch_apply.mjs +2 -2
- package/tests/tweak_land_heal.mjs +3 -3
- package/tests/tweak_unattended.mjs +3 -3
- package/tests/ui_design_skills.mjs +2 -2
- package/tests/unit_report_status.mjs +2 -2
- package/tests/unit_runner_serial.mjs +2 -2
- package/tests/unit_runner_teardown_abort.mjs +1 -1
- package/tests/update_channel.mjs +2 -2
- package/tests/update_channel_db.mjs +4 -4
- package/tests/update_subscription_engine.mjs +26 -26
- package/tests/update_sweep_home.mjs +14 -14
- package/tests/upgrade.mjs +11 -11
- package/tests/upgrade_artist_gate.mjs +4 -4
- package/tests/upgrade_commit_identity.mjs +2 -2
- package/tests/upgrade_migrate.mjs +7 -7
- package/tests/upgrade_operator.mjs +2 -2
- package/tests/upgrade_outcome.mjs +2 -2
- package/tests/upgrade_persist_pin.mjs +5 -5
- package/tests/upkeep_home.mjs +1 -1
- package/tests/version_close.mjs +2 -2
- package/tests/version_close_cli.mjs +2 -2
- package/tests/version_close_route.mjs +1 -1
- package/tests/version_literals.mjs +23 -23
- package/tests/visual_review.mjs +5 -5
- package/tests/wedge_remedy.mjs +5 -5
- package/tests/welcome_boot.mjs +116 -0
- package/tests/welcome_boot_browser.mjs +271 -0
- package/tests/wizard_born_first_board.mjs +3 -3
- package/tests/wizard_demo.mjs +1 -1
- package/tests/wizard_one_page.mjs +4 -3
- package/tests/wizard_physics.mjs +3 -1
- package/tests/wizard_physics_more.mjs +42 -3
- package/tests/work_title_wrap.mjs +1 -1
- package/tests/workflow_exec_paths.mjs +6 -6
- package/tests/worktree_claim_guard.mjs +1 -1
- package/tests/worktree_helper.mjs +2 -2
- package/tests/write_validation_gaps.mjs +3 -3
- package/scripts/gds/api.sh +0 -6
- package/scripts/gds/audit-exports.js +0 -128
- package/scripts/gds/resolve-skill-model.js +0 -57
- package/scripts/gds/security-baseline-check.sh +0 -180
- package/scripts/gds/spike-task-map.js +0 -899
- package/scripts/status-probe.mjs +0 -213
- package/src/bongos/uptime-poller.js +0 -93
- package/tests/government_fold_bench.mjs +0 -50
- package/tests/spike_task_map.mjs +0 -422
- package/tests/status-probe.mjs +0 -89
- /package/{scripts/gds → modules/raise}/CLAUDE.md +0 -0
- /package/scripts/{gds → bongos}/cloudbongos-token-isolation.conf +0 -0
- /package/scripts/{gds → bongos}/discipline-modes.json +0 -0
- /package/scripts/{gds → bongos}/retired-core-skills.json +0 -0
|
@@ -0,0 +1,1166 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// scripts/bongos/provision-core-upgrade.js — the owner's deploy door, CONTROL-PLANE half
|
|
4
|
+
// (task 1003159, ADR 0293).
|
|
5
|
+
//
|
|
6
|
+
// The web tier queues a 'core-upgrade' intent naming a target version and gains no new
|
|
7
|
+
// capability doing so (modules/provisioning/core-upgrade.js). THIS is the half that acts:
|
|
8
|
+
// the runner drains that intent and drives the proven `bongos upgrade` path, with its
|
|
9
|
+
// health gate, its served-version check and its auto-rollback (task 2149) intact.
|
|
10
|
+
//
|
|
11
|
+
// It is a DOOR ONTO THE PROVEN PATH, NOT A SECOND UPGRADE MECHANISM. Every step that
|
|
12
|
+
// could get an upgrade wrong — install, migrate, pin, restart, verify, revert — already
|
|
13
|
+
// lives in upgrade.js and is exercised by every hand-run bump. Re-implementing any of it
|
|
14
|
+
// here would mean two paths that must stay in step, and the one used less often would be
|
|
15
|
+
// the one that rots. So this file does exactly three things upgrade.js cannot do for
|
|
16
|
+
// itself: decide whether the request is ALLOWED, decide whether the target is REAL, and
|
|
17
|
+
// report what the box actually serves afterwards.
|
|
18
|
+
//
|
|
19
|
+
// WHY THE REGISTRY CHECK LIVES HERE AND NOT ON THE ROUTE (ADR 0293 D3). Answering "is
|
|
20
|
+
// this version published" means `npm view`, which update-channel.js runs via spawnSync —
|
|
21
|
+
// on the single-process web server that blocks every other request for the seconds npm
|
|
22
|
+
// takes. Here it is a CLI process with nothing else to serve, and blocking is free. The
|
|
23
|
+
// check is not optional tidiness: the release lane mints a git tag BEFORE the npm publish
|
|
24
|
+
// completes (8–13 minutes, measured 2026-09-16 — learnings 1000263/1000266), so a door
|
|
25
|
+
// that trusted the request would routinely queue a version that does not exist yet and
|
|
26
|
+
// fail deep inside npm with `notarget`.
|
|
27
|
+
//
|
|
28
|
+
// Its own file because scripts/bongos/provision.js sits at the 1500-line oversized-file
|
|
29
|
+
// ratchet, the same reason provision-net.js and provision-units.js were carved out.
|
|
30
|
+
|
|
31
|
+
const crypto = require('crypto');
|
|
32
|
+
const fs = require('fs');
|
|
33
|
+
const os = require('os');
|
|
34
|
+
const path = require('path');
|
|
35
|
+
const { spawnSync } = require('child_process');
|
|
36
|
+
const { CONFIG, MANIFEST_VERIFY_INTERVAL_MS, MANIFEST_VERIFY_TRIES, REPO_ROOT } = require('./provision-config.js');
|
|
37
|
+
const { appUser, dbName, instanceUser, parseTargetRef, resolveOwnerGithubToken, selfInstanceRoot, standaloneRoot } = require('./provision-repo.js');
|
|
38
|
+
const { ensurePinWriteKey, hasOwnAccount, pinKeyPath, pinSshCommand } = require('./provision-pin-key.js');
|
|
39
|
+
const { replayPinCommit } = require('./provision-pin-replay.js'); // GitHub ahead → pin on top (task 1004733)
|
|
40
|
+
const { isValidSlug } = require('../../modules/provisioning/provisioning.js');
|
|
41
|
+
const { RELEASE_NOTES_FILE, RELEASE_NOTES_MAX_BYTES, parseChangeRecords, notesBetween } = require('./release-notes.js');
|
|
42
|
+
const outcome = require('./upgrade-outcome.js'); // how far a failed move got, as a class (task 1004446)
|
|
43
|
+
|
|
44
|
+
// The shapes whose box this runner can actually reach. Mirrors UPGRADE_SHAPES in the
|
|
45
|
+
// web-tier half, and deliberately EXCLUDES 'dedicated': a dedicated droplet is stood up
|
|
46
|
+
// by cloud-init and the runner has no post-hoc exec path to it, so queueing an upgrade
|
|
47
|
+
// for one would be a request that is accepted and never lands. The route refuses the
|
|
48
|
+
// same shape; this is the second gate, and it is the one that holds if a row's shape
|
|
49
|
+
// changes between the request and the drain.
|
|
50
|
+
const UPGRADE_SHAPES = Object.freeze(new Set(['co-tenant', 'cloud-host', 'control-plane']));
|
|
51
|
+
|
|
52
|
+
// The strict three-number form a core version takes. Re-stated here rather than imported
|
|
53
|
+
// from the web tier's core-upgrade.js: a module may not be required from scripts/ (ADR
|
|
54
|
+
// 0083) and this side needs it to guard a SHELL interpolation, which is a stricter duty
|
|
55
|
+
// than validating a request body.
|
|
56
|
+
const CORE_VERSION_RE = /^[0-9]+\.[0-9]+\.[0-9]+$/;
|
|
57
|
+
|
|
58
|
+
// The box's own answer to "what core am I running?" — loopback, on the instance's port.
|
|
59
|
+
//
|
|
60
|
+
// THE APEX CANNOT BE ASKED (ADR 0293 D6). It sits behind pre-launch basic_auth and its
|
|
61
|
+
// HTML rides a CDN, so a public read can report a stale or gated answer with complete
|
|
62
|
+
// confidence. 127.0.0.1 is the only address that is answering FROM the process whose
|
|
63
|
+
// version is in question. This is the same reasoning healthzCmd already encodes, and the
|
|
64
|
+
// DONE-WHEN task 1003156 established in so many words.
|
|
65
|
+
function versionCmd(inst) {
|
|
66
|
+
return `curl -sS --max-time 5 http://127.0.0.1:${inst.port}/version`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Pull coreVersion out of whatever /version answers with.
|
|
70
|
+
//
|
|
71
|
+
// TOLERANT BY DESIGN, and the tolerance is load-bearing rather than lazy. This reads a
|
|
72
|
+
// process that may be running a core OLDER than the one that wrote this parser — that is
|
|
73
|
+
// the entire point of an upgrade — so the shape of its response is not something this
|
|
74
|
+
// side gets to assume. A body it cannot read returns null, which the caller reports as
|
|
75
|
+
// "could not confirm", never as a mismatch: claiming the upgrade failed because the
|
|
76
|
+
// reply was unfamiliar would roll back a perfectly good deploy.
|
|
77
|
+
function parseServedVersion(stdout) {
|
|
78
|
+
if (!stdout || typeof stdout !== 'string') return null;
|
|
79
|
+
try {
|
|
80
|
+
const j = JSON.parse(stdout);
|
|
81
|
+
for (const k of ['coreVersion', 'core_version', 'version']) {
|
|
82
|
+
if (typeof j[k] === 'string' && j[k].trim()) return j[k].trim();
|
|
83
|
+
}
|
|
84
|
+
} catch { /* not JSON — fall through to the text probe */ }
|
|
85
|
+
const m = stdout.match(/\b\d+\.\d+\.\d+\b/);
|
|
86
|
+
return m ? m[0] : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* The `schemaPending` count from a /version body — migrations the box's code ships that its
|
|
91
|
+
* own database has not recorded (task 1004153). PURE.
|
|
92
|
+
*
|
|
93
|
+
* null means UNKNOWN (no answer, not JSON, or a core older than the field), never 0: a
|
|
94
|
+
* co-tenant that cannot say must not read as one whose database is current. Bounded the
|
|
95
|
+
* same way scripts/bongos/deploy-divergence.js bounds it, since the body is a network answer.
|
|
96
|
+
*/
|
|
97
|
+
function parseSchemaPending(stdout) {
|
|
98
|
+
if (!stdout || typeof stdout !== 'string') return null;
|
|
99
|
+
let n;
|
|
100
|
+
try { n = JSON.parse(stdout).schemaPending; } catch { return null; }
|
|
101
|
+
return Number.isInteger(n) && n >= 0 && n <= 100000 ? n : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Is this target a version the instance may actually be moved to? PURE.
|
|
106
|
+
*
|
|
107
|
+
* Two separate questions, and conflating them is how a door starts lying:
|
|
108
|
+
* • does it EXIST — is it published to the registry at all;
|
|
109
|
+
* • is it ALLOWED — does this instance's channel policy admit it (ADR 0136).
|
|
110
|
+
*
|
|
111
|
+
* A version that exists but the channel forbids is a refusal the owner can act on
|
|
112
|
+
* ("your channel is stable and that is a prerelease"); one that does not exist yet is a
|
|
113
|
+
* refusal they can only wait out. Saying which is which is the difference between a
|
|
114
|
+
* useful message and "upgrade failed".
|
|
115
|
+
*
|
|
116
|
+
* @param {string} to the requested version
|
|
117
|
+
* @param {string[]} available versions the registry reports
|
|
118
|
+
* @param {string} installed the version running now
|
|
119
|
+
* @param {string} channel this instance's update channel
|
|
120
|
+
*/
|
|
121
|
+
function targetFault(to, available, installed, channel, channelAllows, compare) {
|
|
122
|
+
if (!Array.isArray(available) || available.length === 0) {
|
|
123
|
+
return `could not read the published core versions from the registry — nothing was changed; the release may still be publishing (a tag is minted before the publish completes)`;
|
|
124
|
+
}
|
|
125
|
+
if (!available.includes(to)) {
|
|
126
|
+
return `core ${to} is not published yet — nothing was changed. A release tag appears several minutes before the package itself does; try again once it lands.`;
|
|
127
|
+
}
|
|
128
|
+
// AN UNREADABLE `installed` IS A FAULT, NOT A FREE PASS (task 1004123).
|
|
129
|
+
//
|
|
130
|
+
// This guard used to be written `if (installed && …)`, so a null installed version
|
|
131
|
+
// skipped the channel check entirely rather than failing it. That is backwards: not
|
|
132
|
+
// knowing where an instance is today is the one state in which "is this jump
|
|
133
|
+
// permitted" cannot be answered, and the channel exists precisely to refuse jumps
|
|
134
|
+
// nobody has looked at. It also hid the defect that motivated this: the control
|
|
135
|
+
// plane read its installed version from the wrong directory for weeks and got null
|
|
136
|
+
// every time, and every press sailed past this line.
|
|
137
|
+
if (!installed) {
|
|
138
|
+
return `could not read the core version installed at this instance — nothing was changed. An upgrade that cannot say where it is starting from cannot be bounded by the '${channel}' channel.`;
|
|
139
|
+
}
|
|
140
|
+
// A TARGET THE BOX HAS ALREADY REACHED IS NOT A CHANNEL QUESTION (task 1004534).
|
|
141
|
+
// channelAllows answers "may an unattended jump go here" and says no to anything not
|
|
142
|
+
// strictly newer, so on 2026-10-01 a press for 1.20.73, landed a minute earlier by the
|
|
143
|
+
// sweep, was reported as "outside this project's 'minor' update channel … across a major
|
|
144
|
+
// version or onto a prerelease" — false on every count, and it put a "your call" card on
|
|
145
|
+
// /deploy for a project that was simply up to date. Already there: no fault, so the
|
|
146
|
+
// caller's already-serving no-op answers it. Already past it: say that, in words that
|
|
147
|
+
// classify as a downgrade, never as the update rule.
|
|
148
|
+
if (installed === to) return null;
|
|
149
|
+
if (compare && compare(to, installed) < 0) {
|
|
150
|
+
return `core ${to} is older than the core this project already runs (it already runs core ${installed}) — nothing was changed. A move never goes back to an older core on its own.`;
|
|
151
|
+
}
|
|
152
|
+
if (channelAllows && !channelAllows(installed, to, channel)) {
|
|
153
|
+
return `core ${to} is outside this project's '${channel}' update channel — nothing was changed. ${channelNextStep(channel)}`;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// The step that gets a channel-refused move through, named by the control a person can
|
|
159
|
+
// actually reach (task 1004405). The old line said "change the channel first" while no
|
|
160
|
+
// channel could be changed; the update rule on /deploy (task 1004445) is that control now,
|
|
161
|
+
// and its labels are the words used here. `minor` is the widest rule, so a move it refuses
|
|
162
|
+
// (a major, a prerelease) has no rule to widen to and is named as the by-hand step it is.
|
|
163
|
+
function channelNextStep(channel) {
|
|
164
|
+
if (channel === 'pinned') {
|
|
165
|
+
return 'Its update rule on /deploy is "Stay put"; change it to "Fixes only" or "Fixes and new releases", then move again.';
|
|
166
|
+
}
|
|
167
|
+
if (channel === 'minor') {
|
|
168
|
+
return 'No update rule moves a project across a major version or onto a prerelease; that move is made by hand with upgrade.js (docs/recipes/upgrading-the-core.md).';
|
|
169
|
+
}
|
|
170
|
+
return 'To take a new release line, set this project\'s update rule on /deploy to "Fixes and new releases" (or press "Allow new releases and move"), then move again.';
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// The update channel that bounds a move for THIS instance (task 1004445): the row's own
|
|
174
|
+
// `update_channel`, normalised by update-channel.js so anything unknown or absent (a row
|
|
175
|
+
// read before migration provisioning_029) falls back to DEFAULT_CHANNEL. PURE. One helper
|
|
176
|
+
// for the move and the snapshot, so the button's offer and the runner's refusal cannot
|
|
177
|
+
// disagree about which rule applies.
|
|
178
|
+
function instanceChannel(inst, channelMod) {
|
|
179
|
+
return channelMod.normalizeChannel(inst && inst.update_channel != null ? inst.update_channel : channelMod.DEFAULT_CHANNEL);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// The versions this project may be moved to: the published ones its FOLLOW admits (task 1004298,
|
|
183
|
+
// ADR 0361). The platform hall (control-plane) takes every candidate; every other project only what
|
|
184
|
+
// has been released, and NOTHING when the registry cannot say which version that is — a project that
|
|
185
|
+
// follows releases is never moved on a guess. A channel module without the follow (an old core
|
|
186
|
+
// resolved at drain time) offers the whole list, which is what every version meant before candidates.
|
|
187
|
+
//
|
|
188
|
+
// The released label is one fact about the registry, the same for every project in a pass, and
|
|
189
|
+
// reading it is a blocking `npm view`. So it is read once per channel module per RELEASE_TAGS_TTL_MS
|
|
190
|
+
// and shared: a pass over N projects costs one extra subprocess, not N. A failed read is not kept.
|
|
191
|
+
const RELEASE_TAGS_TTL_MS = 60 * 1000;
|
|
192
|
+
const releaseTagsMemo = new WeakMap();
|
|
193
|
+
function releaseTagsFor(channelMod, instanceDir, now = Date.now()) {
|
|
194
|
+
const hit = releaseTagsMemo.get(channelMod);
|
|
195
|
+
if (hit && now - hit.at < RELEASE_TAGS_TTL_MS) return hit.tags;
|
|
196
|
+
const tags = channelMod.readReleaseTags({ cwd: instanceDir });
|
|
197
|
+
if (tags && tags.ok) releaseTagsMemo.set(channelMod, { at: now, tags });
|
|
198
|
+
return tags;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function offeredVersions(channelMod, inst, instanceDir) {
|
|
202
|
+
const listed = channelMod.listAvailableVersions({ cwd: instanceDir });
|
|
203
|
+
if (!listed || !listed.ok || typeof channelMod.followForShape !== 'function') return listed;
|
|
204
|
+
if (channelMod.followForShape(inst && inst.hosting_shape) === 'candidates') return listed;
|
|
205
|
+
const tags = releaseTagsFor(channelMod, instanceDir);
|
|
206
|
+
if (!tags.ok) return { ok: false, error: `could not read which version is released (${tags.error})`, versions: [] };
|
|
207
|
+
return { ok: true, versions: channelMod.versionsForFollow({ follow: 'released', available: listed.versions, released: tags.released }) };
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// A classified refusal (task 1004446): the drain loop stores `failure` beside last_error,
|
|
211
|
+
// so the rest of the self-heal chain acts on a class, not on a sentence. PURE.
|
|
212
|
+
function failed(reason, error, extra = {}) {
|
|
213
|
+
return { ok: false, error, failure: outcome.classifyFailure(reason), ...extra };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// The builder id a queued move is attributed to, from the actor the web tier recorded.
|
|
217
|
+
// PURE. Two doors reach the move (ADR 0339): an Archon (`api:archon:<id>`) and, since
|
|
218
|
+
// task 1004174, the project's own owner (`api:owner:<id>`). Both carry the id, because
|
|
219
|
+
// it becomes `--requested-by` on the core_upgrades ledger (ADR 0293 D5). Anything else
|
|
220
|
+
// yields '' — the id is interpolated into a shell command, so only digits ever pass.
|
|
221
|
+
function requestedByOf(intent) {
|
|
222
|
+
const m = intent && typeof intent.requested_by === 'string' && /^api:(?:archon|owner):(\d+)$/.exec(intent.requested_by);
|
|
223
|
+
return m ? m[1] : '';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Perform a queued core-pin move. Returns { ok } — NEVER throws for an expected refusal.
|
|
228
|
+
*
|
|
229
|
+
* The drain loop's catch flips the instance to 'error', which is the wrong outcome for
|
|
230
|
+
* every refusal here: a request that was never valid, or a version that is not published
|
|
231
|
+
* yet, says nothing at all about the health of a running instance. So each of those
|
|
232
|
+
* returns { ok:false, error } and the loop retries or retires the INTENT, leaving the
|
|
233
|
+
* instance exactly as it found it (ADR 0190 §2, the shape restartInstance established).
|
|
234
|
+
*
|
|
235
|
+
* @param {object} inst the provisioning_instances row
|
|
236
|
+
* @param {object} deps the runner's dependency bag
|
|
237
|
+
* @param {object} intent the claimed provisioning_intents row (carries target_version)
|
|
238
|
+
*/
|
|
239
|
+
async function coreUpgradeInstance(inst, deps, intent) {
|
|
240
|
+
const { db, provisioning, exec, log, apply, privileged } = deps;
|
|
241
|
+
// A standalone's box IS the control plane's box, the same routing restartInstance and
|
|
242
|
+
// settingsApplyInstance use; a co-tenant shares this checkout.
|
|
243
|
+
const boxExec = inst.hosting_shape === 'cloud-host' ? (deps.controlExec || exec) : exec;
|
|
244
|
+
const to = intent && intent.target_version;
|
|
245
|
+
log(`core-upgrade ${inst.slug} → ${to || '(no target)'} (shape=${inst.hosting_shape}, status=${inst.status})`);
|
|
246
|
+
|
|
247
|
+
// ---- 1. is this request allowed at all ------------------------------------------
|
|
248
|
+
// UPGRADE_SHAPES IS THE WHOLE SHAPE CHECK. The self-row guard is deliberately not
|
|
249
|
+
// consulted: `intentAllowedForShape('core-upgrade', …)` is true for every shape
|
|
250
|
+
// there is, because 'core-upgrade' is permanently on CONTROL_PLANE_ACTIONS — so
|
|
251
|
+
// calling it here would read as a second gate while being unable to refuse
|
|
252
|
+
// anything. The guard's real job is the OTHER actions on the self row, enforced at
|
|
253
|
+
// enqueueIntent where every intent passes.
|
|
254
|
+
if (!UPGRADE_SHAPES.has(inst.hosting_shape)) {
|
|
255
|
+
return failed('shape_not_automated', `core-upgrade is not automated for a '${inst.hosting_shape}' instance — whoever runs that machine has to upgrade it there`);
|
|
256
|
+
}
|
|
257
|
+
if (inst.status !== 'active') {
|
|
258
|
+
// Not an error: a teardown or a stand-up that raced the request must not burn the
|
|
259
|
+
// intent's retry budget, and there is no settled pin to move either way.
|
|
260
|
+
log(` instance is '${inst.status}' — no running core whose pin can move`);
|
|
261
|
+
return { ok: true, noop: true };
|
|
262
|
+
}
|
|
263
|
+
if (!to) {
|
|
264
|
+
return failed('no_target', 'core-upgrade intent carries no target_version — refusing to guess which version was meant');
|
|
265
|
+
}
|
|
266
|
+
// THE SLUG IS INTERPOLATED INTO A SHELL COMMAND BELOW, so it is re-checked HERE
|
|
267
|
+
// rather than trusted. `isValidSlug` is enforced on the create route, but the
|
|
268
|
+
// control-plane self row is inserted by an operator script (enroll-control-plane.js)
|
|
269
|
+
// and any later write path is one more place that would have to remember. The value
|
|
270
|
+
// reaching a shell is this function's problem, and a format check at the point of use
|
|
271
|
+
// is the only one that cannot be bypassed by a path nobody has written yet. The other
|
|
272
|
+
// two interpolated values are already constrained — the version by the column's own
|
|
273
|
+
// CHECK and by VERSION_RE, the actor by the digits-only extraction below.
|
|
274
|
+
if (!isValidSlug(inst.slug)) {
|
|
275
|
+
return failed('bad_slug', `instance slug '${inst.slug}' is not a valid slug — refusing to build a command from it`);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ---- 2. is the target real, and permitted -----------------------------------------
|
|
279
|
+
const channelMod = deps.updateChannel || require('./update-channel.js');
|
|
280
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
281
|
+
const instanceDir = inst.hosting_shape === 'cloud-host' ? standaloneRoot(inst) : selfRoot();
|
|
282
|
+
const readInstalled = deps.readInstalledCoreVersion || require('./upgrade.js').readInstalledCoreVersion;
|
|
283
|
+
const installed = readInstalled(instanceDir);
|
|
284
|
+
// THIS PROJECT'S OWN UPDATE RULE (task 1004445, migration provisioning_029). Before it
|
|
285
|
+
// every instance was bounded by DEFAULT_CHANNEL (patch-only), so a step-up release
|
|
286
|
+
// could reach no project through the door and the refusal's "change the channel first"
|
|
287
|
+
// pointed at nothing. A row from before the column (undefined) still normalises to that
|
|
288
|
+
// default, so an old runner schema reads exactly as it always did.
|
|
289
|
+
const channel = instanceChannel(inst, channelMod);
|
|
290
|
+
const listed = offeredVersions(channelMod, inst, instanceDir);
|
|
291
|
+
const available = (listed && listed.versions) || [];
|
|
292
|
+
const fault = targetFault(to, available, installed, channel, channelMod.channelAllows, channelMod.compareSemver);
|
|
293
|
+
if (fault) return failed(outcome.runnerReason(fault), fault);
|
|
294
|
+
if (installed && installed === to) {
|
|
295
|
+
// `bongos upgrade` would no-op on this too, but saying so here keeps a re-drained
|
|
296
|
+
// intent from restarting a healthy service to land exactly where it already is.
|
|
297
|
+
log(` already serving core ${to} — nothing to move`);
|
|
298
|
+
return { ok: true, noop: true };
|
|
299
|
+
}
|
|
300
|
+
// ---- 2b. is the database current for the code it runs NOW (task 1004448) -------------
|
|
301
|
+
// A database behind its own code is already failing every page that touches the missing
|
|
302
|
+
// tables (the 2026-07-04 shape), and moving the code on top of it tangles a catch-up
|
|
303
|
+
// migration into a pin move. So it is refused as a known snag, and wedge-remedy.js
|
|
304
|
+
// migrates that database and tries the move once more. An answer it cannot read proceeds.
|
|
305
|
+
if (apply) {
|
|
306
|
+
const probe = boxExec(versionCmd(inst), { allowFail: true });
|
|
307
|
+
const pending = parseSchemaPending(probe && !probe.softFailed ? (probe.stdout || '') : null);
|
|
308
|
+
if (pending > 0) return failed('schema_pending', `the project's database is ${pending} migration(s) behind the code it is running now — it has to catch up before the core moves`);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// ---- 3. drive the proven path -----------------------------------------------------
|
|
312
|
+
// Exactly the invocation docs/recipes/core-release-pipeline.md gate 3 documents, plus
|
|
313
|
+
// the two attribution flags (ADR 0293 D5). --commit-pin is NOT optional here: without
|
|
314
|
+
// it the pin lives only in the working tree, and the next pull-deploy resets it away —
|
|
315
|
+
// the silent thirteen-release revert upgrade.js's own header records.
|
|
316
|
+
const versionUrl = `http://127.0.0.1:${inst.port}/version`;
|
|
317
|
+
const requestedBy = requestedByOf(intent);
|
|
318
|
+
// Built by the SAME function the preview uses, so "what you were shown" and "what runs"
|
|
319
|
+
// cannot drift apart into two hand-maintained flag lists (task 1004121).
|
|
320
|
+
// The preview probes; the move must probe identically or the two commands diverge on
|
|
321
|
+
// the one field task 1004121 exists to keep identical. Skipped when !apply, where the
|
|
322
|
+
// command is printed and never run — a plan should not spend a subprocess per tick.
|
|
323
|
+
const { runAs, migrateAs } = resolveRunAs(inst, deps, { probe: apply });
|
|
324
|
+
// A move the hub queued by itself (provision-auto-upgrade.js) says so in the project's
|
|
325
|
+
// own ledger, so its hall can tell its owner "upgraded automatically" (task 1004731).
|
|
326
|
+
const automatic = !!intent && intent.requested_by === 'runner:auto-upgrade';
|
|
327
|
+
const { cmd, runDir } = upgradeInvocation(inst, to, { privileged, requestedBy, automatic, selfRoot, runAs, migrateAs });
|
|
328
|
+
|
|
329
|
+
if (!apply) {
|
|
330
|
+
// A --dry-run runner tick used to only PRINT this command. It now runs the CLI's own
|
|
331
|
+
// dry run, so `provision.js --dry-run` reports what the upgrade would actually refuse
|
|
332
|
+
// rather than a command string the reader has to evaluate in their head.
|
|
333
|
+
log(` ✓ would run (in ${runDir}): ${cmd}`);
|
|
334
|
+
const verdict = (deps.preflightUpgrade || preflightUpgrade)(inst, to, deps);
|
|
335
|
+
for (const line of verdict.lines || []) log(` ✓ pre-flight: ${line}`);
|
|
336
|
+
if (!verdict.ok) log(` ✖ ${verdict.error}`);
|
|
337
|
+
log(` ✓ would then read ${versionUrl} on the box and require it to say ${to}`);
|
|
338
|
+
return { ok: true, served: null, preflight: verdict };
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
log(` [upgrade] (in ${runDir}) ${cmd}`);
|
|
342
|
+
boxExec(cmd, { cwd: runDir });
|
|
343
|
+
} catch (e) {
|
|
344
|
+
// HOW FAR IT GOT, from upgrade.js's own outcome line (task 1004446) — never assumed.
|
|
345
|
+
// It used to say "failed and was rolled back" for every exit, including refusals that
|
|
346
|
+
// fire before the pin moves (nothing changed) and rollbacks that themselves failed
|
|
347
|
+
// (the project may be down). ok:false, never a throw: the drain loop's catch marks the
|
|
348
|
+
// INSTANCE broken, which is only ever right for the rollback_failed case, and that one
|
|
349
|
+
// is reported (and retired, never retried unattended) rather than guessed at here.
|
|
350
|
+
const text = `${(e && e.stderr) || ''}\n${(e && e.message) || e}`;
|
|
351
|
+
const o = outcome.parseOutcomeLine(text);
|
|
352
|
+
const said = (/✖ ([^\n]+)/.exec(text) || [])[1] || String((e && e.message) || e).split('\n')[0];
|
|
353
|
+
const reason = o ? o.reason : 'unknown';
|
|
354
|
+
return failed(reason, outcome.describeUpgradeFailure(to, o, said), { stage: o ? o.stage : null, terminal: reason === 'rollback_failed' });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ---- 4. read back what the BOX serves (ADR 0293 D6) --------------------------------
|
|
358
|
+
// With the same patience the manifest probe uses: the service has just restarted and a
|
|
359
|
+
// single immediate read races it binding its port. A false "did not take" here is
|
|
360
|
+
// never cosmetic — it is what the owner is shown after the one act they were told they
|
|
361
|
+
// could finally perform.
|
|
362
|
+
const sleep = deps.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
363
|
+
let served = null;
|
|
364
|
+
for (let i = 0; i < MANIFEST_VERIFY_TRIES; i++) {
|
|
365
|
+
const res = boxExec(versionCmd(inst), { allowFail: true });
|
|
366
|
+
served = parseServedVersion(res && !res.softFailed ? (res.stdout || '') : null);
|
|
367
|
+
if (served) break;
|
|
368
|
+
if (i < MANIFEST_VERIFY_TRIES - 1) await sleep(MANIFEST_VERIFY_INTERVAL_MS);
|
|
369
|
+
}
|
|
370
|
+
if (served && served !== to) {
|
|
371
|
+
// upgrade.js's own --version-url gate should have caught and reverted this, so
|
|
372
|
+
// reaching here means the two disagree. Report what was SEEN rather than a verdict.
|
|
373
|
+
return failed('served_mismatch', `the box is serving core ${served}, not the requested ${to} — the move did not take`);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ---- 5. the pin reaches the project's own repo, pushed from HERE (task 1004065) ----
|
|
377
|
+
// Only now: a pin worth saving is one the box is serving. upgrade.js was told
|
|
378
|
+
// --pin-no-push for this shape, so it committed and stopped; see pushUpgradePin.
|
|
379
|
+
const repo = hubPushesPin(inst) ? await pushUpgradePin(inst, deps, { instanceDir }) : null;
|
|
380
|
+
const unsaved = repo && !repo.pushed ? `; GitHub was not updated (${repo.code})` : '';
|
|
381
|
+
await provisioning.recordEvent(db, {
|
|
382
|
+
instanceId: inst.id, ownerBuilderId: inst.owner_builder_id, event: 'core-upgraded',
|
|
383
|
+
detail: (served ? `core pin moved to ${served}` : `core pin moved to ${to}; the box did not answer /version, so this is unconfirmed`) + unsaved,
|
|
384
|
+
actor: intent.requested_by || 'runner',
|
|
385
|
+
}).catch(() => {});
|
|
386
|
+
if (provisioning.clearErrorNote) await provisioning.clearErrorNote(db, inst.id, 'core-upgrade').catch(() => {});
|
|
387
|
+
log(served ? ` ✓ ${inst.slug} serves core ${served}` : ` ⚠ ${inst.slug} upgraded to ${to}, but did not answer /version — unconfirmed`);
|
|
388
|
+
// `doneNote`: the drain loop resolves the intent DONE and stores this beside it, which
|
|
389
|
+
// is how the deploy page learns the upgrade landed but GitHub did not get it.
|
|
390
|
+
return { ok: true, served, confirmed: served === to, repo, doneNote: repo && !repo.pushed ? repo.note : null };
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// ── the pin reaches the project's GitHub repo, pushed from the HUB (task 1004065) ──
|
|
394
|
+
//
|
|
395
|
+
// THE BUG THIS EXISTS FOR. upgrade.js commits the new pin on the box and then pushes it.
|
|
396
|
+
// On a hosted project it runs connected to the PROJECT's database — it has to, for
|
|
397
|
+
// migrations — while the owner's GitHub token lives in the HUB's. Its token lookup found
|
|
398
|
+
// no provisioning table there (ADR 0281), fell back to `git push origin`, and origin is a
|
|
399
|
+
// read-only deploy key (ADR 0176). GitHub refused, the refusal reached only the journal
|
|
400
|
+
// and named the wrong cause, and the intent resolved `done`. The box ran the new core,
|
|
401
|
+
// GitHub still pinned the old one, and the box's local commit stopped its fast-forward
|
|
402
|
+
// pulls from taking the owner's new work: hermeslines-marketing served 1.19.994 against
|
|
403
|
+
// a repo pinning 1.19.881, and pages pushed to that repo 404'd on the live site.
|
|
404
|
+
//
|
|
405
|
+
// WHY THE RUNNER PUSHES rather than handing upgrade.js the token. This process already
|
|
406
|
+
// holds the hub's database, where the token is, and it runs as the account that owns the
|
|
407
|
+
// checkout (the project's own account only READS it — grantInstanceRepoReadCmd). Passing
|
|
408
|
+
// the token into the upgrade would put a credential that can write the repo inside the
|
|
409
|
+
// project's own process. So the move passes --pin-no-push, upgrade.js commits, and the
|
|
410
|
+
// push happens here, in the shape refreshStandaloneCorePin already uses for this repo.
|
|
411
|
+
//
|
|
412
|
+
// A FAILED PUSH IS NOT A FAILED UPGRADE. The site runs the new core, and rolling a good
|
|
413
|
+
// deploy back would be worse than the split. The move still resolves `done`, carrying a
|
|
414
|
+
// note the deploy page reads: `repo_not_updated:<code>: <sentence>`, parsed by
|
|
415
|
+
// parseRepoNote in modules/provisioning/core-upgrade.js (re-stated there because a
|
|
416
|
+
// module may not require a script; tests/core_upgrade_runner.mjs round-trips the two).
|
|
417
|
+
//
|
|
418
|
+
// NOTHING IS FORCED, AND THE LIVE BOX IS NEVER REBASED. When GitHub has commits the box
|
|
419
|
+
// does not, moving the box onto them would change what it runs with no restart and no
|
|
420
|
+
// health check. Since task 1004733 the pin commit ALONE is replayed onto GitHub's history in
|
|
421
|
+
// a scratch worktree and pushed as an ordinary fast-forward (provision-pin-replay.js), so
|
|
422
|
+
// GitHub names the new core and the box stays exactly what the move proved. A conflict in
|
|
423
|
+
// the pin files is reported ("conflict"), never resolved here. The box still does not pick
|
|
424
|
+
// up the owner's other commits — that would be a deploy, not a pin push (ADR 0350 amendment).
|
|
425
|
+
//
|
|
426
|
+
// WHAT MAKES IT DURABLE (task 1004291, ADR 0350). The owner's token is kept for an hour
|
|
427
|
+
// after they sign in with repository access (GITHUB_TOKEN_TTL_MS in src/bongos/db.js — one
|
|
428
|
+
// wizard sitting), so on its own it cannot carry an upgrade days later. The hub therefore
|
|
429
|
+
// holds a per-project WRITE deploy key (provision-pin-key.js) and tries it FIRST: write key
|
|
430
|
+
// → owner token (when fresh) → the box's own remote, which can only read a private repo.
|
|
431
|
+
// A fresh token also mints the key when the project has none, so reconnecting GitHub once
|
|
432
|
+
// is enough for every later upgrade. The note says which of these is missing, in words.
|
|
433
|
+
|
|
434
|
+
const REPO_NOTE_PREFIX = 'repo_not_updated';
|
|
435
|
+
const REPO_NOTES = Object.freeze({
|
|
436
|
+
no_repo: 'This project has no GitHub repository on record, so the new version was saved on the server only.',
|
|
437
|
+
no_branch: 'The server\'s copy of the project is not on a branch, so there was nothing to save to GitHub.',
|
|
438
|
+
no_access: 'The hub had no permission to write to the project\'s GitHub repository: it holds no write key for it yet, the owner\'s GitHub access is only kept for an hour after they connect GitHub, and the server\'s own key can only read the repository. Reconnecting GitHub with repository access sets up lasting access.',
|
|
439
|
+
needs_account: 'The hub could not set up lasting write access to the project\'s GitHub repository: this project needs its own account on the server first. Once it has one, reconnecting GitHub with repository access sets up lasting access.',
|
|
440
|
+
refused: 'GitHub turned down the owner\'s GitHub access — it may have expired, or lack permission to write to the repository.',
|
|
441
|
+
behind: 'GitHub has changes the server does not have, so the new version could not be added on top of them. An operator needs to bring the two back in line; the site itself is running normally.',
|
|
442
|
+
conflict: 'GitHub has its own changes to the same version files (package.json or package-lock.json), so the new version was not added on top of them and nothing there was overwritten. An operator needs to choose between the two; the site itself is running normally.',
|
|
443
|
+
failed: 'The hub could not save the new version to GitHub.',
|
|
444
|
+
});
|
|
445
|
+
// A branch name that is safe to interpolate. Narrower than git's own ref grammar on
|
|
446
|
+
// purpose: a name outside it is refused, never quoted.
|
|
447
|
+
const BRANCH_RE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,99}$/;
|
|
448
|
+
|
|
449
|
+
/** The note a done-but-unsaved move carries, bounded for the DOM it reaches. PURE. */
|
|
450
|
+
function repoNote(code, extra = '') {
|
|
451
|
+
const known = Object.prototype.hasOwnProperty.call(REPO_NOTES, code) ? code : 'failed';
|
|
452
|
+
return `${REPO_NOTE_PREFIX}:${known}: ${REPO_NOTES[known]}${extra ? ` ${extra}` : ''}`.slice(0, 600);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// Which shapes get their pin pushed from here: a hosted project, whose repo is the
|
|
456
|
+
// owner's own. The platform's own row pushes to a repo its key CAN write, and a
|
|
457
|
+
// co-tenant shares the platform's checkout, so neither changes.
|
|
458
|
+
function hubPushesPin(inst) { return !!inst && inst.hosting_shape === 'cloud-host'; }
|
|
459
|
+
|
|
460
|
+
// Why a push did not happen, from what git said. `behind` is checked first: a rejected
|
|
461
|
+
// update means the credential WORKED and the history is what stopped it.
|
|
462
|
+
function pushFault(r) {
|
|
463
|
+
const said = `${(r && r.stdout) || ''}\n${(r && r.stderr) || ''}`;
|
|
464
|
+
if (/\[rejected\]|non-fast-forward|fetch first|Updates were rejected/i.test(said)) return 'behind';
|
|
465
|
+
if (/\b403\b|Authentication failed|Invalid username or password|Write access to repository not granted|Permission to \S+ denied|marked as read only|Permission denied \(publickey\)|could not read Username/i.test(said)) return 'refused';
|
|
466
|
+
return 'failed';
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// GitHub's own line beats git's closing `fatal:`, which names no reason (upgrade.js
|
|
470
|
+
// gitSaid has the same preference).
|
|
471
|
+
function gitLine(r) {
|
|
472
|
+
const lines = String((r && r.stderr) || '').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
473
|
+
const server = lines.filter((l) => /^(ERROR:|remote:|!)/i.test(l));
|
|
474
|
+
const said = server.length ? server[server.length - 1] : lines[lines.length - 1];
|
|
475
|
+
return said ? `(git: ${said.slice(0, 200)})` : '';
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Push a confirmed upgrade's committed pin to the project's repo. NEVER THROWS: every
|
|
480
|
+
* miss returns `{ pushed:false, code, note }`, because the drain loop turns a throw into
|
|
481
|
+
* a broken instance and this instance is running fine.
|
|
482
|
+
*
|
|
483
|
+
* The token rides a chmod-600 credential file that is removed whatever the push did —
|
|
484
|
+
* never argv (ps, the journal), never a log line. The helper list is reset first
|
|
485
|
+
* (helpers are cumulative, so a global one would answer with some other account) and
|
|
486
|
+
* hooks are off, so nothing in the project's checkout runs as this account.
|
|
487
|
+
*/
|
|
488
|
+
async function pushUpgradePin(inst, deps, { instanceDir }) {
|
|
489
|
+
const log = deps.log || (() => {});
|
|
490
|
+
// The CONTROL PLANE's executor and writer: the checkout, the hub db and the token are
|
|
491
|
+
// all here, never on a remote co-hosting box (refreshStandaloneCorePin's routing).
|
|
492
|
+
const exec = deps.controlExec || deps.exec;
|
|
493
|
+
const writeFile = deps.controlWriteFile || deps.writeFile || ((p, body, o) => fs.writeFileSync(p, body, o));
|
|
494
|
+
const failedRun = (r) => !r || r.softFailed === true || r.ok === false;
|
|
495
|
+
const miss = (code, extra = '') => {
|
|
496
|
+
log(` ⚠ ${inst.slug}: GitHub was not updated — ${REPO_NOTES[code] || REPO_NOTES.failed}${extra ? ` ${extra}` : ''}`);
|
|
497
|
+
return { pushed: false, code, note: repoNote(code, extra) };
|
|
498
|
+
};
|
|
499
|
+
const repo = parseTargetRef(inst.target_ref);
|
|
500
|
+
if (!repo) return miss('no_repo');
|
|
501
|
+
// Exported and callable on its own, and the slug reaches shell strings below (the write
|
|
502
|
+
// key's path), so it is checked here rather than trusting coreUpgradeInstance's own guard.
|
|
503
|
+
if (!isValidSlug(inst.slug)) return miss('failed', '(the project name is not a valid slug)');
|
|
504
|
+
const head = exec(`git -C ${instanceDir} symbolic-ref --short HEAD`, { allowFail: true });
|
|
505
|
+
const branch = failedRun(head) ? '' : String(head.stdout || '').trim();
|
|
506
|
+
if (!BRANCH_RE.test(branch)) return miss('no_branch');
|
|
507
|
+
const at = `${repo.owner}/${repo.name}@${branch}`;
|
|
508
|
+
const hardened = `git -C ${instanceDir} -c core.hooksPath=/dev/null`;
|
|
509
|
+
const env = { GIT_TERMINAL_PROMPT: '0' }; // a refused credential fails; it never waits at a prompt
|
|
510
|
+
// GitHub is ahead: replay the pin commit alone on top, over the credential that was just
|
|
511
|
+
// refused as behind, and push again — never forced (task 1004733, provision-pin-replay.js).
|
|
512
|
+
const replayed = (via, git, remote) => {
|
|
513
|
+
const rr = replayPinCommit({ exec, instanceDir, branch, git, remote, env, slug: inst.slug, tmpDir: deps.tmpDir, pushFault, gitLine, log });
|
|
514
|
+
if (!rr.ok) return miss(rr.code, rr.extra);
|
|
515
|
+
if (rr.already) log(` ✓ ${at} already names this version — nothing to add`);
|
|
516
|
+
return { pushed: true, via, branch, ...(rr.replayed ? { replayed: true } : { already: true }) };
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const token = await resolveOwnerGithubToken(inst, deps, { tag: '[core-upgrade]' });
|
|
520
|
+
|
|
521
|
+
// 1. The hub's own WRITE key (ADR 0350). A fresh token mints it when the project has
|
|
522
|
+
// none yet — the reconnect the deploy page asks for — and the mint is best effort: a
|
|
523
|
+
// miss falls through to the token, then to the box's remote, exactly as before.
|
|
524
|
+
const fsImpl = deps.fsImpl || fs;
|
|
525
|
+
const keyPath = pinKeyPath(inst, deps);
|
|
526
|
+
if (token && !fsImpl.existsSync(keyPath)) await ensurePinWriteKey(inst, deps, { token });
|
|
527
|
+
if (fsImpl.existsSync(keyPath)) {
|
|
528
|
+
const r = exec(`${hardened} -c ${pinSshCommand(keyPath)} push git@github.com:${repo.owner}/${repo.name}.git HEAD:${branch}`,
|
|
529
|
+
{ allowFail: true, env });
|
|
530
|
+
if (!failedRun(r)) {
|
|
531
|
+
log(` ✓ pushed the pin to ${at} from the hub, over its write deploy key`);
|
|
532
|
+
return { pushed: true, via: 'write-key', branch };
|
|
533
|
+
}
|
|
534
|
+
const fault = pushFault(r);
|
|
535
|
+
// The key WORKED and the history stopped it: no other credential changes that, so the
|
|
536
|
+
// pin is replayed on top of GitHub's history over this same key (task 1004733).
|
|
537
|
+
if (fault === 'behind') return replayed('write-key', `${hardened} -c ${pinSshCommand(keyPath)}`, `git@github.com:${repo.owner}/${repo.name}.git`);
|
|
538
|
+
log(` • ${inst.slug}: the write key did not push (${fault}) — trying the next credential`);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (token) {
|
|
542
|
+
const credFile = path.join(os.tmpdir(), `bongos-gitcred-upgrade-${inst.slug}-${process.pid}-${crypto.randomBytes(6).toString('hex')}`);
|
|
543
|
+
let r = null;
|
|
544
|
+
try {
|
|
545
|
+
writeFile(credFile, `https://x-access-token:${token}@github.com\n`, { mode: 0o600 });
|
|
546
|
+
const viaToken = `${hardened} -c credential.helper= -c credential.helper='store --file=${credFile}'`;
|
|
547
|
+
const tokenRemote = `https://x-access-token@github.com/${repo.owner}/${repo.name}.git`;
|
|
548
|
+
r = exec(`${viaToken} push ${tokenRemote} HEAD:${branch}`, { allowFail: true, env });
|
|
549
|
+
// Behind: replayed while the credential file still exists (the finally removes it).
|
|
550
|
+
if (failedRun(r) && pushFault(r) === 'behind') return replayed('owner-token', viaToken, tokenRemote);
|
|
551
|
+
} catch (e) {
|
|
552
|
+
return miss('failed', `(could not stage the owner's credential: ${String((e && e.message) || e).slice(0, 160)})`);
|
|
553
|
+
} finally {
|
|
554
|
+
exec(`rm -f ${credFile}`, { allowFail: true });
|
|
555
|
+
}
|
|
556
|
+
if (!failedRun(r)) {
|
|
557
|
+
log(` ✓ pushed the pin to ${at} from the hub, over the owner's GitHub token`);
|
|
558
|
+
return { pushed: true, via: 'owner-token', branch };
|
|
559
|
+
}
|
|
560
|
+
return miss(pushFault(r), gitLine(r));
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
// No token stored — which is the ordinary case (see THE LIMIT above). The box's own
|
|
564
|
+
// remote is still tried: a checkout wired with a key that can write is not wrong to use.
|
|
565
|
+
const r = exec(`${hardened} push origin HEAD:${branch}`, { allowFail: true, env });
|
|
566
|
+
if (!failedRun(r)) {
|
|
567
|
+
log(` ✓ pushed the pin to ${at} from the hub, over the checkout's own remote`);
|
|
568
|
+
return { pushed: true, via: 'origin', branch };
|
|
569
|
+
}
|
|
570
|
+
const fault = pushFault(r);
|
|
571
|
+
if (fault === 'behind') return replayed('origin', hardened, 'origin');
|
|
572
|
+
// A refusal here is not the owner's access being turned down — there was none to turn
|
|
573
|
+
// down. It is the read-only key, which is the no-access case.
|
|
574
|
+
// ...unless the project has no account of its own, in which case no key CAN be minted
|
|
575
|
+
// for it (ADR 0350) and reconnecting GitHub alone will not help: say that instead.
|
|
576
|
+
if (fault === 'refused') return miss(hasOwnAccount(inst, exec) ? 'no_access' : 'needs_account', gitLine(r));
|
|
577
|
+
return miss(fault, gitLine(r));
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// The pre-flight lines a dry run emits, and the refusal when it has one. upgrade.js writes
|
|
581
|
+
// its pre-flight to stdout as ` ✓ pre-flight: …` and returns a `✖`-prefixed multi-line
|
|
582
|
+
// error on a refusal, so BOTH streams matter: a pass is only legible from stdout, and a
|
|
583
|
+
// refusal only from the failure text. PURE given its input.
|
|
584
|
+
function parsePreflight(out) {
|
|
585
|
+
const text = String(out || '');
|
|
586
|
+
const lines = text.split('\n').map((l) => l.trim());
|
|
587
|
+
const checks = lines.filter((l) => /^[✓✖]\s*pre-flight:/.test(l)).map((l) => l.replace(/^[✓✖]\s*/, ''));
|
|
588
|
+
// A refusal is whatever upgrade.js said after the ✖ — reported verbatim rather than
|
|
589
|
+
// re-worded, because its wording already names the fix ("Set PGDATABASE, or …").
|
|
590
|
+
const failAt = text.indexOf('✖');
|
|
591
|
+
const error = failAt >= 0 ? text.slice(failAt + 1).trim() : null;
|
|
592
|
+
return { checks, error };
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Which unix accounts does this instance's upgrade need? Asks the box (task 1004128).
|
|
597
|
+
*
|
|
598
|
+
* TWO ANSWERS, NOT ONE (task 1004512). Task 1004128 asked a single question — "which
|
|
599
|
+
* account does this instance run as?" — and ran the WHOLE move as it. That is right for
|
|
600
|
+
* the database and wrong for the files, and the two have different owners on every
|
|
601
|
+
* project provisioned since task 1003369:
|
|
602
|
+
*
|
|
603
|
+
* • `runAs` — the account that owns the CHECKOUT, which is the app user on every
|
|
604
|
+
* hosting shape (provision-repo.js scaffolds /srv/<base>/<slug> as the runner, and
|
|
605
|
+
* grantInstanceRepoReadCmd gives the project's own account group READ, nothing more).
|
|
606
|
+
* Everything the move writes — the pin, node_modules, .claude/, the pin commit — and
|
|
607
|
+
* the `git status` it starts with, belong to this account. Running them as the
|
|
608
|
+
* project's account is what made every such move die on `fatal: detected dubious
|
|
609
|
+
* ownership in repository` and report a dirty tree that did not exist.
|
|
610
|
+
* • `migrateAs` — the project's OWN account, which is also its Postgres login role
|
|
611
|
+
* (provision-repo.js instanceDbRole); its database is owned by that role with CONNECT
|
|
612
|
+
* revoked from PUBLIC, so this is the only identity that can migrate it. That is where
|
|
613
|
+
* the isolation of task 1003369 actually lives, and it is kept.
|
|
614
|
+
*
|
|
615
|
+
* The fleet is heterogeneous: instances provisioned before task 1003369 run as the
|
|
616
|
+
* shared app user, instances provisioned after run as their own `bongos-<slug>`. Neither
|
|
617
|
+
* is guessable from the row, and getting it wrong is not a soft failure — `sudo` refuses
|
|
618
|
+
* an unknown user and the upgrade dies before pre-flight, which is exactly how the
|
|
619
|
+
* tenant leg has been broken.
|
|
620
|
+
*
|
|
621
|
+
* Falling back is NOT a privilege escalation: an instance with no account of its own is
|
|
622
|
+
* already running as the app user (its unit says so), so this migrates as precisely the
|
|
623
|
+
* account that owns the database it is about to touch. It IS reported, because a missing
|
|
624
|
+
* per-instance account is a provisioning gap someone should close, and a silent fallback
|
|
625
|
+
* would hide it forever. On that shape the two answers coincide and the move is byte-for-
|
|
626
|
+
* byte what it was before this task.
|
|
627
|
+
*/
|
|
628
|
+
function resolveRunAs(inst, deps, { probe: allowProbe = true } = {}) {
|
|
629
|
+
const owner = appUser(); // owns the checkout on every shape — see above
|
|
630
|
+
if (inst.hosting_shape === 'control-plane') return { runAs: owner, migrateAs: owner, fellBack: false };
|
|
631
|
+
// Re-checked HERE rather than left to instanceUser()'s own throw, for the reason
|
|
632
|
+
// upgradeInvocation states about itself: a call site whose safety depends on a
|
|
633
|
+
// helper's internals is one refactor away from a shell-interpolation path.
|
|
634
|
+
if (!isValidSlug(inst && inst.slug)) {
|
|
635
|
+
throw new Error(`resolveRunAs: refusing to probe an account for invalid slug ${JSON.stringify(inst && inst.slug)}`);
|
|
636
|
+
}
|
|
637
|
+
const wanted = instanceUser(inst);
|
|
638
|
+
if (!allowProbe) return { runAs: owner, migrateAs: wanted, fellBack: false };
|
|
639
|
+
// THE SAME EXECUTOR THE UPGRADE ITSELF WILL USE. Every sibling here picks
|
|
640
|
+
// `standalone ? (controlExec || exec) : exec`, because under PROVISION_REMOTE_HOST
|
|
641
|
+
// (ADR 0130) `exec` is the REMOTE ssh executor while `controlExec` stays local. A
|
|
642
|
+
// probe on `exec` would then ask the co-hosting box whether an account exists while
|
|
643
|
+
// the command it is deciding for runs on the control plane — a wrong answer that
|
|
644
|
+
// looks like a right one.
|
|
645
|
+
const boxExec = inst.hosting_shape === 'cloud-host' ? (deps.controlExec || deps.exec) : deps.exec;
|
|
646
|
+
const r = boxExec(`id -u ${wanted}`, { allowFail: true });
|
|
647
|
+
const failed = !r || r.softFailed === true || r.ok === false;
|
|
648
|
+
if (!failed) return { runAs: owner, migrateAs: wanted, fellBack: false };
|
|
649
|
+
// ONLY A DEFINITIVE "no such user" JUSTIFIES THE FALLBACK. A probe that could not
|
|
650
|
+
// answer — an exec timeout, an NSS hiccup — must not silently narrow the per-instance
|
|
651
|
+
// isolation of task 1003369 for this operation. So an inconclusive probe keeps the
|
|
652
|
+
// intended account: if it really is missing the upgrade fails loudly, and since this
|
|
653
|
+
// task a failure now carries the reason instead of "the dry run did not complete".
|
|
654
|
+
const said = `${(r && r.stdout) || ''}\n${(r && r.stderr) || ''}`;
|
|
655
|
+
if (!/no such user|unknown user/i.test(said)) {
|
|
656
|
+
return { runAs: owner, migrateAs: wanted, fellBack: false, probeInconclusive: true };
|
|
657
|
+
}
|
|
658
|
+
return { runAs: owner, migrateAs: owner, fellBack: true, wanted };
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// ONE BUILDER FOR BOTH LEGS (task 1004121).
|
|
662
|
+
//
|
|
663
|
+
// The preview and the move must be the same command or the preview is worthless — and
|
|
664
|
+
// two hand-copied builders are exactly how they stop being the same. The mode decides
|
|
665
|
+
// only the TAIL: a dry run stops after pre-flight, so the flags that describe what to do
|
|
666
|
+
// AFTER the pin moves (restart, health, version, pin-commit, attribution) would be
|
|
667
|
+
// inert on it. Everything that decides whether the upgrade is ALLOWED — the account, the
|
|
668
|
+
// database, the target, the instance — is shared, which is the half a preview is about.
|
|
669
|
+
//
|
|
670
|
+
// The slug rides into a shell command through instanceUser()/dbName(), so it is checked
|
|
671
|
+
// HERE rather than left to instanceUser()'s own throw: that throw is correct today, but
|
|
672
|
+
// a call site whose safety depends on a helper's internals is one refactor away from a
|
|
673
|
+
// shell-injection path (and the apply leg's own guard sits at the route, not here).
|
|
674
|
+
function upgradeInvocation(inst, to, { privileged = false, dryRun = false, requestedBy = '', automatic = false, selfRoot = selfInstanceRoot, runAs = null, migrateAs = null } = {}) {
|
|
675
|
+
if (!isValidSlug(inst && inst.slug)) {
|
|
676
|
+
throw new Error(`upgradeInvocation: refusing to build a shell command from an invalid slug ${JSON.stringify(inst && inst.slug)}`);
|
|
677
|
+
}
|
|
678
|
+
// WHERE THE CLI LIVES AND WHERE THE TARGET LIVES ARE DIFFERENT PLACES, TWICE OVER.
|
|
679
|
+
//
|
|
680
|
+
// `upgrade.js` ships in the CORE, and a provisioned instance root holds no scripts/
|
|
681
|
+
// directory at all — so the CLI runs from REPO_ROOT and NAMES its target (task
|
|
682
|
+
// 1004053, which fixed every standalone tenant).
|
|
683
|
+
//
|
|
684
|
+
// The second half is that REPO_ROOT is not the instance either (task 1004123). It
|
|
685
|
+
// names the copy of the core that is EXECUTING, which coincides with the instance
|
|
686
|
+
// only while the core is a checkout run in place. Once the control plane's runner
|
|
687
|
+
// moved into its vendored core the two diverged silently — REPO_ROOT still resolved,
|
|
688
|
+
// just to node_modules/@bongos/core — and the door aimed at the package instead of
|
|
689
|
+
// the project. So ask, rather than assume; in the legacy shape the answer is
|
|
690
|
+
// REPO_ROOT and the command below is byte-identical to what it always was.
|
|
691
|
+
const instanceDir = inst.hosting_shape === 'cloud-host' ? standaloneRoot(inst) : selfRoot();
|
|
692
|
+
// WHICH ACCOUNT, AND WHY IT IS RESOLVED RATHER THAN DERIVED (task 1004128).
|
|
693
|
+
//
|
|
694
|
+
// Task 1004119 wrote this as `control-plane ? APP_USER : instanceUser(inst)` and was
|
|
695
|
+
// half right. The control-plane leg is correct and verified live. The tenant leg
|
|
696
|
+
// assumed the per-instance account model of task 1003369 is provisioned everywhere —
|
|
697
|
+
// and on the live box NO `bongos-*` account exists, no such Postgres role exists, and
|
|
698
|
+
// every instance unit still names the shared app user. So the fix for "Postgres has no role
|
|
699
|
+
// root" swapped one absent account for another, and every tenant upgrade died with
|
|
700
|
+
// `sudo: unknown user bongos-test4`.
|
|
701
|
+
//
|
|
702
|
+
// The account model is a real security control, not an accident — a distinct uid per
|
|
703
|
+
// instance is what stops a co-tenant reading a sibling's /proc/<pid>/environ. So the
|
|
704
|
+
// answer is NOT to route everyone back to the shared account. It is to run as the
|
|
705
|
+
// account the instance ACTUALLY runs as, which a caller establishes by probing
|
|
706
|
+
// (resolveRunAs below) instead of either side assuming. Passing it in keeps this
|
|
707
|
+
// builder pure; the default preserves the old derivation for callers that have not
|
|
708
|
+
// probed.
|
|
709
|
+
//
|
|
710
|
+
// AND WHICH ACCOUNT DOES WHAT (task 1004512). `account` runs the move; `dbAccount` runs
|
|
711
|
+
// its three database steps. They differ on exactly the projects that have an account of
|
|
712
|
+
// their own, because the checkout is the app user's and the database is theirs —
|
|
713
|
+
// resolveRunAs above argues the split. `--migrate-as` is emitted ONLY when they differ,
|
|
714
|
+
// so a control plane and a pre-1003369 project produce the command they always did.
|
|
715
|
+
const account = runAs || appUser();
|
|
716
|
+
const dbAccount = migrateAs || (inst.hosting_shape === 'control-plane' ? appUser() : instanceUser(inst));
|
|
717
|
+
const sudoP = privileged ? `sudo -u ${account} env PGDATABASE=${dbName(inst)} ` : '';
|
|
718
|
+
const cli = path.posix.join('scripts', 'bongos', 'upgrade.js');
|
|
719
|
+
const shared = [`${sudoP}node ${cli}`, '--registry', `--to ${to}`,
|
|
720
|
+
instanceDir === REPO_ROOT ? '' : `--instance ${instanceDir}`, // REPO_ROOT is where the CLI runs (runDir below)
|
|
721
|
+
// SHARED, not in the tail: the dry run's own database pre-check connects as this
|
|
722
|
+
// account too, so a preview that omitted it would test a different identity than the
|
|
723
|
+
// move — the exact drift one builder for both legs exists to prevent.
|
|
724
|
+
dbAccount === account ? '' : `--migrate-as ${dbAccount}`];
|
|
725
|
+
const tail = dryRun
|
|
726
|
+
? ['--dry-run']
|
|
727
|
+
: [`--service ${inst.slug}`,
|
|
728
|
+
`--health-url http://127.0.0.1:${inst.port}/healthz`,
|
|
729
|
+
`--version-url http://127.0.0.1:${inst.port}/version`,
|
|
730
|
+
'--commit-pin',
|
|
731
|
+
// A hosted project's pin is pushed by the RUNNER, which can reach the owner's
|
|
732
|
+
// token; the upgrade process cannot (task 1004065, pushUpgradePin).
|
|
733
|
+
hubPushesPin(inst) ? '--pin-no-push' : '',
|
|
734
|
+
requestedBy ? `--requested-by ${requestedBy}` : '',
|
|
735
|
+
requestedBy ? '--upgrade-source owner-control' : '',
|
|
736
|
+
// The ledger's existing mark for an unattended move (the self-host sweep writes
|
|
737
|
+
// `auto-subscription (<rule>)`); one shell-safe word here (task 1004731).
|
|
738
|
+
automatic && !requestedBy ? '--note auto-subscription' : ''];
|
|
739
|
+
return { cmd: [...shared, ...tail].filter(Boolean).join(' '), runDir: REPO_ROOT, instanceDir, runAs: account, migrateAs: dbAccount };
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Ask the upgrade CLI what it WOULD do, changing nothing. `allowFail` because a refusal
|
|
743
|
+
// is the interesting answer, not an exception: pre-flight refusing is what this is for.
|
|
744
|
+
function preflightUpgrade(inst, to, deps) {
|
|
745
|
+
const { exec, privileged } = deps;
|
|
746
|
+
const boxExec = inst.hosting_shape === 'cloud-host' ? (deps.controlExec || exec) : exec;
|
|
747
|
+
// A distinct local name: `deps.selfInstanceRoot || selfInstanceRoot` with a const of
|
|
748
|
+
// the same name would TDZ-shadow the import and crash every uninjected call.
|
|
749
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
750
|
+
const { runAs, migrateAs, fellBack, wanted } = resolveRunAs(inst, deps);
|
|
751
|
+
const { cmd, runDir } = upgradeInvocation(inst, to, { privileged, dryRun: true, selfRoot, runAs, migrateAs });
|
|
752
|
+
const r = boxExec(cmd, { cwd: runDir, allowFail: true });
|
|
753
|
+
const out = `${(r && r.stdout) || ''}\n${(r && r.stderr) || ''}`;
|
|
754
|
+
const { checks, error } = parsePreflight(out);
|
|
755
|
+
// ok is the RUN's verdict, not the parser's: a command that exited non-zero without a
|
|
756
|
+
// recognisable ✖ is still a refusal, and reporting it as a pass is the failure mode
|
|
757
|
+
// this whole task exists to remove.
|
|
758
|
+
const ok = !!(r && !r.softFailed && r.ok !== false) && !error;
|
|
759
|
+
// WHEN THERE IS NO ✖, SAY WHAT THE COMMAND SAID (task 1004128). The generic sentence
|
|
760
|
+
// is the LAST resort, not the first: the first live pre-flight reported "the dry run
|
|
761
|
+
// did not complete" while `sudo: unknown user bongos-test4` sat in the journal, and
|
|
762
|
+
// the second did the same over `✖ already on 1.19.927`. Both were in hand. The trim
|
|
763
|
+
// is because this reaches a DOM.
|
|
764
|
+
const raw = out.trim().split('\n').filter(Boolean).slice(-4).join('\n').slice(0, 600);
|
|
765
|
+
const why = error || (raw || null);
|
|
766
|
+
const note = fellBack
|
|
767
|
+
? `migrated as ${migrateAs} — this instance has no ${wanted} account yet (it predates the per-instance account model)`
|
|
768
|
+
: null;
|
|
769
|
+
return {
|
|
770
|
+
// Both accounts, because since task 1004512 there are two and "which user did this run
|
|
771
|
+
// as" has no single answer: `ran_as` writes the files, `migrated_as` touches the database.
|
|
772
|
+
target: to, ok, lines: checks, ran_as: runAs, migrated_as: migrateAs, account_note: note,
|
|
773
|
+
error: ok ? null : (why || 'the dry run did not complete, and said nothing this could report'),
|
|
774
|
+
};
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
// ── WHAT THE MOVE WOULD CARRY (task 1004173) ────────────────────────────────
|
|
778
|
+
//
|
|
779
|
+
// The owner, 2026-09-22: "I don't actually see what would change when I hit the button."
|
|
780
|
+
// The pre-flight above answers a different question — whether the machinery will let the
|
|
781
|
+
// move happen — and answering it was being presented as a preview. ADR 0293 D4 promised
|
|
782
|
+
// both halves in so many words: "the control shows what a bump would move — live version
|
|
783
|
+
// → target, AND WHAT SITS BETWEEN THEM." Only the first half was built, so the owner was
|
|
784
|
+
// asked to restart a live site to take ninety-one releases of work the page named none of.
|
|
785
|
+
//
|
|
786
|
+
// WHERE THE ANSWER ALREADY LIVES. Every ship writes a merge commit whose summary is
|
|
787
|
+
// already prose for exactly this reader — the owner, not a developer — because ship.js
|
|
788
|
+
// requires one and posts it to Discord. So this reads the merge summaries between the two
|
|
789
|
+
// releases rather than inventing a changelog nobody would maintain.
|
|
790
|
+
//
|
|
791
|
+
// IT READS THE NOTES THE CORE PACKAGE CARRIES, and a core checkout only as a fallback
|
|
792
|
+
// (task 1004218). "What changed in the core between 1.19.881 and 1.19.972" is a question
|
|
793
|
+
// about the CORE repository, and no tenant box will ever hold its history — every project
|
|
794
|
+
// is a checkout of its OWN repo with the core vendored in from npm. So the packer writes
|
|
795
|
+
// the summaries down once, from the full history CI packs with (release-notes.json, built
|
|
796
|
+
// by scripts/bongos/release-notes.js), and any machine that can download a core version can
|
|
797
|
+
// read what it carries. The checkout path below is kept for a machine that has one.
|
|
798
|
+
const CHANGE_CAP = 20;
|
|
799
|
+
// One registry download of a core tarball. The preview's owner is waiting on the dry run
|
|
800
|
+
// beside it anyway, but a hung registry must not hold the runner forever.
|
|
801
|
+
const NOTES_FETCH_TIMEOUT_MS = 60000;
|
|
802
|
+
|
|
803
|
+
function runFault(r) {
|
|
804
|
+
if (!r) return 'no answer';
|
|
805
|
+
if (r.error) return String(r.error.message || r.error).slice(0, 160);
|
|
806
|
+
const tail = String(r.stderr || '').trim().split('\n').filter(Boolean).slice(-1)[0] || '';
|
|
807
|
+
return (tail || `exit ${r.status}`).slice(0, 160);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* The release notes the core THIS runner executes from carries, if any. Free to read, and
|
|
812
|
+
* enough on its own whenever the target is at or below this core's own version — a tenant
|
|
813
|
+
* catching up to where the platform already is.
|
|
814
|
+
*/
|
|
815
|
+
function readInstalledNotes(deps = {}) {
|
|
816
|
+
const fsImpl = deps.fs || fs;
|
|
817
|
+
let raw;
|
|
818
|
+
try { raw = fsImpl.readFileSync(path.join(REPO_ROOT, RELEASE_NOTES_FILE), 'utf8'); }
|
|
819
|
+
catch { return { missing: 'this core carries no release notes' }; }
|
|
820
|
+
try { return { notes: JSON.parse(raw) }; }
|
|
821
|
+
catch { return { error: "this core's release notes are not valid JSON" }; }
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* The release notes inside ONE published core package — the TARGET's own (task 1004218).
|
|
826
|
+
*
|
|
827
|
+
* WHY THE TARGET'S. The installed core's notes stop at its own version, which is zero
|
|
828
|
+
* coverage for the move looked at most — the platform's own, onto a release newer than
|
|
829
|
+
* itself. The target's package is the one guaranteed to reach the target, because that is
|
|
830
|
+
* where the target was packed. `npm pack` downloads it (the core is public, so no
|
|
831
|
+
* credential — the same registry listAvailableVersions reads) and `tar -xzOf` reads the one
|
|
832
|
+
* file out of it rather than unpacking the rest.
|
|
833
|
+
*
|
|
834
|
+
* Returns `{ notes }`, `{ missing }` when the package carries none (every release published
|
|
835
|
+
* before the notes were packaged), or `{ error }`. The download never outlives the call.
|
|
836
|
+
*/
|
|
837
|
+
function readPackagedNotes(version, deps = {}) {
|
|
838
|
+
if (!CORE_VERSION_RE.test(String(version || ''))) return { error: 'could not work out which package to read' };
|
|
839
|
+
const run = deps.spawn || spawnSync;
|
|
840
|
+
const fsImpl = deps.fs || fs;
|
|
841
|
+
let dir = null;
|
|
842
|
+
try {
|
|
843
|
+
dir = fsImpl.mkdtempSync(path.join(deps.tmpDir || os.tmpdir(), 'bongos-release-notes-'));
|
|
844
|
+
const packed = run('npm', ['pack', `@bongos/core@${version}`, '--pack-destination', dir, '--silent'],
|
|
845
|
+
{ cwd: dir, env: deps.env || process.env, encoding: 'utf8', timeout: NOTES_FETCH_TIMEOUT_MS });
|
|
846
|
+
if (!packed || packed.error || packed.status !== 0) {
|
|
847
|
+
return { error: `could not download the core ${version} package to read its release notes (${runFault(packed)})` };
|
|
848
|
+
}
|
|
849
|
+
// Whatever npm named it: a scoped package's tarball is <scope>-<name>-<version>.tgz today.
|
|
850
|
+
const tgz = fsImpl.readdirSync(dir).find((f) => f.endsWith('.tgz'));
|
|
851
|
+
if (!tgz) return { error: `npm reported the core ${version} package downloaded, but left no tarball` };
|
|
852
|
+
const x = run('tar', ['-xzOf', path.join(dir, tgz), `package/${RELEASE_NOTES_FILE}`],
|
|
853
|
+
{ encoding: 'utf8', maxBuffer: 4 * RELEASE_NOTES_MAX_BYTES, timeout: NOTES_FETCH_TIMEOUT_MS });
|
|
854
|
+
if (!x || x.error || x.status !== 0) {
|
|
855
|
+
if (/not found in archive/i.test(String((x && x.stderr) || ''))) {
|
|
856
|
+
return { missing: `the core ${version} package carries no release notes` };
|
|
857
|
+
}
|
|
858
|
+
return { error: `could not read the release notes out of the core ${version} package (${runFault(x)})` };
|
|
859
|
+
}
|
|
860
|
+
try { return { notes: JSON.parse(x.stdout) }; }
|
|
861
|
+
catch { return { error: `the release notes in the core ${version} package are not valid JSON` }; }
|
|
862
|
+
} catch (e) {
|
|
863
|
+
return { error: `could not fetch the core ${version} package (${String((e && e.message) || e).slice(0, 160)})` };
|
|
864
|
+
} finally {
|
|
865
|
+
if (dir) { try { fsImpl.rmSync(dir, { recursive: true, force: true }); } catch { /* best-effort */ } }
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* The range, answered from release notes alone: `{ entries }`, or `{ why }` they cannot.
|
|
871
|
+
* The installed core first, when its notes already reach the target; the target's own
|
|
872
|
+
* package otherwise. No git, no SHAs, no checkout.
|
|
873
|
+
*/
|
|
874
|
+
function changesFromNotes(fromVersion, toVersion, deps) {
|
|
875
|
+
const installed = (deps.readInstalledNotes || readInstalledNotes)(deps);
|
|
876
|
+
if (installed.notes) {
|
|
877
|
+
const r = notesBetween(installed.notes, fromVersion, toVersion);
|
|
878
|
+
if (r.entries) return r;
|
|
879
|
+
}
|
|
880
|
+
const target = (deps.readPackagedNotes || readPackagedNotes)(toVersion, deps);
|
|
881
|
+
if (!target.notes) return { why: target.missing || target.error || `the core ${toVersion} package's release notes could not be read` };
|
|
882
|
+
const r = notesBetween(target.notes, fromVersion, toVersion);
|
|
883
|
+
return r.entries ? r : { why: `the release notes in the core ${toVersion} package ${r.uncovered}` };
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Classify a git command that did not succeed. PURE.
|
|
888
|
+
*
|
|
889
|
+
* "COULD NOT LOOK" AND "LOOKED AND DID NOT FIND" ARE DIFFERENT ANSWERS (task 1004212).
|
|
890
|
+
* The first cut collapsed both into "could not find those releases in the core history on
|
|
891
|
+
* this machine", and that sentence sent the investigation at the history for an hour when
|
|
892
|
+
* the history was never the problem — the directory was. A message that names the wrong
|
|
893
|
+
* cause is worse than a vague one, because it is followed.
|
|
894
|
+
*/
|
|
895
|
+
function gitFault(r, dir) {
|
|
896
|
+
const said = `${(r && r.stdout) || ''}\n${(r && r.stderr) || ''}`;
|
|
897
|
+
if (/dubious ownership|safe\.directory/i.test(said)) {
|
|
898
|
+
return `the core checkout at ${dir} belongs to another account, so this could not read it`;
|
|
899
|
+
}
|
|
900
|
+
if (/not a git repository/i.test(said)) return `${dir} is not a git checkout`;
|
|
901
|
+
const tail = said.trim().split('\n').filter(Boolean).slice(-1)[0] || '';
|
|
902
|
+
return `could not read ${dir}${tail ? ` — ${tail.slice(0, 160)}` : ''}`;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
/**
|
|
906
|
+
* Where the CORE's own git history lives on this machine, if anywhere (task 1004212).
|
|
907
|
+
*
|
|
908
|
+
* WHAT THE FIRST CUT GOT WRONG, and it was not a small thing. It read `selfInstanceRoot()`
|
|
909
|
+
* on the assumption that the control plane's instance root is a checkout of the core. On
|
|
910
|
+
* the live box it is not: `/version` there reports a commit that does not exist in the core
|
|
911
|
+
* repository at all, because the instance root is a checkout of the INSTANCE's own repo
|
|
912
|
+
* with the core vendored in at node_modules (the task 1004123 shape). So every lookup
|
|
913
|
+
* searched the wrong repository's history.
|
|
914
|
+
*
|
|
915
|
+
* THAT IS A CORRECTNESS BUG, NOT ONLY A MISSING FEATURE. On a box whose instance repo
|
|
916
|
+
* happened to carry `Merge <branch>: <summary>` commits — which any repo built this way
|
|
917
|
+
* does — the page would have shown ANOTHER project's changelog as the core's, with no
|
|
918
|
+
* error at all. Which is why a candidate is not trusted for being a git repo: it has to
|
|
919
|
+
* prove it carries core RELEASE history, and `looksLikeCore` is that proof.
|
|
920
|
+
*
|
|
921
|
+
* NO PATH IS HARDCODED. `audit-authorship.sh` forbids naming the founder's directories,
|
|
922
|
+
* and a guessed path is how this got here. The candidates are: an explicit operator
|
|
923
|
+
* setting, the directory the CLI is executing from, and the instance root — each checked,
|
|
924
|
+
* none assumed.
|
|
925
|
+
*/
|
|
926
|
+
function coreCheckoutCandidates(deps) {
|
|
927
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
928
|
+
const env = deps.env || process.env;
|
|
929
|
+
const out = [];
|
|
930
|
+
const add = (dir, why) => { if (dir && !out.some((c) => c.dir === dir)) out.push({ dir, why }); };
|
|
931
|
+
add(String(env.PROVISION_CORE_CHECKOUT || '').trim() || null, 'PROVISION_CORE_CHECKOUT');
|
|
932
|
+
add(REPO_ROOT, 'the directory this core is running from');
|
|
933
|
+
add(selfRoot(), "this project's own root");
|
|
934
|
+
return out;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
/**
|
|
938
|
+
* Read the core's release history, or say precisely why it cannot be. Returns
|
|
939
|
+
* `{ dir }` on success, or `{ error }` — never a directory it has not verified.
|
|
940
|
+
*/
|
|
941
|
+
function findCoreCheckout(deps, git) {
|
|
942
|
+
const tried = [];
|
|
943
|
+
for (const { dir, why } of coreCheckoutCandidates(deps)) {
|
|
944
|
+
const probe = git(dir, 'rev-parse --is-shallow-repository');
|
|
945
|
+
if (!probe.ok) { tried.push(`${why}: ${gitFault(probe, dir)}`); continue; }
|
|
946
|
+
if (probe.stdout.trim() === 'true') {
|
|
947
|
+
// A depth-limited clone resolves HEAD perfectly well — which is why /version reports
|
|
948
|
+
// `resolvedVia: git` on a box that cannot answer this — and carries no old releases.
|
|
949
|
+
tried.push(`${why}: ${dir} is a shallow clone, so it carries no release history`);
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
// THE PROOF THAT THIS IS THE CORE. Any repository answers `rev-parse`; only the core's
|
|
953
|
+
// carries release commits, and accepting a repo without asking is how the wrong
|
|
954
|
+
// project's changelog would have been shown as this one's.
|
|
955
|
+
const marker = git(dir, "log --format=%H --max-count=1 -E --grep='^release: core [0-9]+\\.[0-9]+\\.[0-9]+ '");
|
|
956
|
+
if (!marker.ok) { tried.push(`${why}: ${gitFault(marker, dir)}`); continue; }
|
|
957
|
+
if (!marker.stdout.trim()) { tried.push(`${why}: ${dir} is a git checkout, but not of the core`); continue; }
|
|
958
|
+
return { dir };
|
|
959
|
+
}
|
|
960
|
+
return { error: `no core checkout is reachable on this machine (${tried.join('; ')}). Set PROVISION_CORE_CHECKOUT to one.` };
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* The range, answered from a core CHECKOUT's git history: `{ entries }` or `{ error }`.
|
|
965
|
+
* The fallback for a machine that has one, since the package's notes only exist for
|
|
966
|
+
* releases published after they were introduced (task 1004218).
|
|
967
|
+
*/
|
|
968
|
+
function changesFromCheckout(fromVersion, toVersion, deps) {
|
|
969
|
+
// The CONTROL PLANE's executor, never the previewed instance's: under
|
|
970
|
+
// PROVISION_REMOTE_HOST `exec` is the remote box, while the core history — and the whole
|
|
971
|
+
// question of what changed in the CORE — belongs to the machine that builds it.
|
|
972
|
+
const exec = deps.controlExec || deps.exec;
|
|
973
|
+
// AS THE ACCOUNT THAT OWNS THE CHECKOUT, for the same reason the dry run beside this one
|
|
974
|
+
// does (task 1004128). `appUser()` is defined as "the unprivileged account that owns this
|
|
975
|
+
// checkout" and throws rather than return root, so a runner executing as root reads the
|
|
976
|
+
// files through the account that may actually read them instead of tripping git's
|
|
977
|
+
// ownership check. Unprivileged runners are unchanged — there is nothing to escalate to.
|
|
978
|
+
let runAs = null;
|
|
979
|
+
if (deps.privileged) {
|
|
980
|
+
try { runAs = (deps.appUser || appUser)(); } catch { runAs = null; }
|
|
981
|
+
}
|
|
982
|
+
const git = (dir, args) => {
|
|
983
|
+
const r = exec(`${runAs ? `sudo -u ${runAs} ` : ''}git -C ${dir} ${args}`, { allowFail: true });
|
|
984
|
+
return {
|
|
985
|
+
ok: !!(r && r.softFailed !== true && r.ok !== false),
|
|
986
|
+
stdout: String((r && r.stdout) || ''),
|
|
987
|
+
stderr: String((r && r.stderr) || ''),
|
|
988
|
+
};
|
|
989
|
+
};
|
|
990
|
+
const found = findCoreCheckout(deps, git);
|
|
991
|
+
if (found.error) return { error: found.error };
|
|
992
|
+
const dir = found.dir;
|
|
993
|
+
const shaOf = (v) => {
|
|
994
|
+
const r = git(dir, `log --format=%H --max-count=1 -F --grep='release: core ${v} '`);
|
|
995
|
+
if (!r.ok) return { fault: gitFault(r, dir) };
|
|
996
|
+
const sha = r.stdout.trim().split('\n')[0] || '';
|
|
997
|
+
return /^[0-9a-f]{7,40}$/.test(sha) ? { sha } : {};
|
|
998
|
+
};
|
|
999
|
+
const from = shaOf(fromVersion);
|
|
1000
|
+
const to = shaOf(toVersion);
|
|
1001
|
+
if (from.fault || to.fault) return { error: from.fault || to.fault };
|
|
1002
|
+
// LOOKED, AND DID NOT FIND — a different sentence from "could not look", and it names
|
|
1003
|
+
// WHICH version is missing so the reader can tell a not-yet-published target from a
|
|
1004
|
+
// checkout that has not been fetched.
|
|
1005
|
+
const missing = [!from.sha && fromVersion, !to.sha && toVersion].filter(Boolean);
|
|
1006
|
+
if (missing.length) {
|
|
1007
|
+
return { error: `core ${missing.join(' and ')} ${missing.length > 1 ? 'are' : 'is'} not in the core history on this machine yet` };
|
|
1008
|
+
}
|
|
1009
|
+
const r = git(dir, `log --merges --format=%s%x1f%b%x1e ${from.sha}..${to.sha}`);
|
|
1010
|
+
if (!r.ok) return { error: gitFault(r, dir) };
|
|
1011
|
+
return { entries: parseChangeRecords(r.stdout) };
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
/**
|
|
1015
|
+
* What landed between two core releases, as the summaries their ships wrote.
|
|
1016
|
+
*
|
|
1017
|
+
* Returns `{ from, to, entries, more, source }`, or `{ from, to, error }` — NEVER an empty
|
|
1018
|
+
* `entries` for a range it could not resolve. An empty list reads as "nothing changed",
|
|
1019
|
+
* which on a page whose job is to say what a restart buys is the one wrong impression it
|
|
1020
|
+
* must not give, and it is exactly what a missing checkout produces.
|
|
1021
|
+
*
|
|
1022
|
+
* THE PACKAGE'S NOTES FIRST, THE CHECKOUT SECOND (task 1004218). The notes answer on every
|
|
1023
|
+
* box; the checkout only where one exists, which is never a tenant. When neither can, the
|
|
1024
|
+
* error carries BOTH reasons — the notes' and the checkout's own precise sentence (task
|
|
1025
|
+
* 1004212) — so the reader is told everything that was tried and why each fell short.
|
|
1026
|
+
*
|
|
1027
|
+
* Every version reaching a shell is re-checked against the strict three-number form
|
|
1028
|
+
* FIRST. They arrive from an intent row whose column has the same CHECK, but a value that
|
|
1029
|
+
* is interpolated into a command is checked where it is interpolated — the rule
|
|
1030
|
+
* upgradeInvocation states about the slug.
|
|
1031
|
+
*/
|
|
1032
|
+
function changesBetween(fromVersion, toVersion, deps) {
|
|
1033
|
+
const bad = [fromVersion, toVersion].find((v) => !CORE_VERSION_RE.test(String(v || '')));
|
|
1034
|
+
if (bad !== undefined) return { from: fromVersion || null, to: toVersion || null, error: 'could not work out which releases this covers' };
|
|
1035
|
+
const listed = (all, source) => ({
|
|
1036
|
+
from: fromVersion, to: toVersion, entries: all.slice(0, CHANGE_CAP), more: Math.max(0, all.length - CHANGE_CAP), source,
|
|
1037
|
+
});
|
|
1038
|
+
const notes = changesFromNotes(fromVersion, toVersion, deps);
|
|
1039
|
+
if (notes.entries) return listed(notes.entries, 'release-notes');
|
|
1040
|
+
const checkout = changesFromCheckout(fromVersion, toVersion, deps);
|
|
1041
|
+
if (checkout.entries) return listed(checkout.entries, 'checkout');
|
|
1042
|
+
return { from: fromVersion, to: toVersion, error: `${notes.why}; ${checkout.error}` };
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
// Run a dry run for one instance and cache its verdict — the ON-DEMAND leg, drained from
|
|
1046
|
+
// a `core-upgrade-preflight` intent the owner's arming queues (task 1004121). The tick
|
|
1047
|
+
// does not do this: a verdict that is only ever produced on a background schedule is
|
|
1048
|
+
// missing at exactly the moment it is wanted (a fresh release, a new instance), and
|
|
1049
|
+
// re-running it every tick for a box that has been behind for hours spends a subprocess
|
|
1050
|
+
// to re-learn the same answer. Asked for, then cached, is both cheaper and timelier.
|
|
1051
|
+
async function preflightCoreUpgrade(inst, deps, intent) {
|
|
1052
|
+
const { db, log } = deps;
|
|
1053
|
+
const to = intent && intent.target_version;
|
|
1054
|
+
if (!UPGRADE_SHAPES.has(inst.hosting_shape) || inst.status !== 'active') {
|
|
1055
|
+
return { ok: false, error: `cannot preview a ${inst.hosting_shape} instance` };
|
|
1056
|
+
}
|
|
1057
|
+
if (!to) return { ok: false, error: 'no target version to preview' };
|
|
1058
|
+
const verdict = (deps.preflightUpgrade || preflightUpgrade)(inst, to, deps);
|
|
1059
|
+
// AND WHAT THE MOVE WOULD CARRY, taken in the same breath (task 1004173). It rides the
|
|
1060
|
+
// verdict rather than a column of its own for the reason the verdict's own `target`
|
|
1061
|
+
// exists: both answers are about ONE move, and storing them together is what makes
|
|
1062
|
+
// "is this still the move on offer" a single question with a single answer. It also
|
|
1063
|
+
// costs the owner no second wait — the dry run is already the thing they are waiting on.
|
|
1064
|
+
//
|
|
1065
|
+
// `from` is the version the box is on RIGHT NOW, read back from the snapshot the tick
|
|
1066
|
+
// writes, and it is stored because the answer is only true of that starting point. The
|
|
1067
|
+
// web tier withholds the list when the box has moved since, the same way it withholds a
|
|
1068
|
+
// verdict taken against a superseded target.
|
|
1069
|
+
let servedNow = null;
|
|
1070
|
+
try {
|
|
1071
|
+
const { rows } = await db.query('SELECT served_version FROM provisioning_core_version_snapshot WHERE instance_id = $1', [inst.id]);
|
|
1072
|
+
servedNow = (rows[0] && rows[0].served_version) || null;
|
|
1073
|
+
} catch { /* no snapshot yet — changesBetween refuses on the missing version, and says so */ }
|
|
1074
|
+
verdict.changes = (deps.changesBetween || changesBetween)(servedNow, to, deps);
|
|
1075
|
+
await db.query(
|
|
1076
|
+
`INSERT INTO provisioning_core_version_snapshot (instance_id, preflight, preflight_at, observed_at)
|
|
1077
|
+
VALUES ($1, $2::jsonb, now(), now())
|
|
1078
|
+
ON CONFLICT (instance_id) DO UPDATE
|
|
1079
|
+
SET preflight = EXCLUDED.preflight, preflight_at = now()`,
|
|
1080
|
+
[inst.id, JSON.stringify(verdict)]);
|
|
1081
|
+
log(` ${verdict.ok ? '✓' : '✖'} ${inst.slug}: pre-flight for ${to} — ${verdict.ok ? 'clear' : verdict.error}`);
|
|
1082
|
+
// ok:true means THE PREVIEW RAN. A refusal is a successful preview of a refusal — the
|
|
1083
|
+
// intent must not go to 'error', or the owner's next arming is blocked by a stale open
|
|
1084
|
+
// intent describing a check that worked perfectly.
|
|
1085
|
+
return { ok: true, preflight: verdict };
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Observe one instance and cache what the deploy control needs, without acting.
|
|
1090
|
+
*
|
|
1091
|
+
* Runs on every drain tick for each upgradable instance. Both facts it records are ones
|
|
1092
|
+
* the web tier cannot reach for itself — the served version lives on loopback, the
|
|
1093
|
+
* available versions need a blocking `npm view` (migration provisioning_019 says why at length)
|
|
1094
|
+
* — so this is the runner reporting on behalf of a process that may only ask.
|
|
1095
|
+
*
|
|
1096
|
+
* BEST-EFFORT, ALWAYS. A registry hiccup or a box that will not answer must never fail a
|
|
1097
|
+
* tick that has real work in it: the snapshot simply keeps its previous value and its
|
|
1098
|
+
* previous, now-visibly-older, observed_at. That staleness is the honest signal, and it
|
|
1099
|
+
* is why observed_at is stored rather than assumed.
|
|
1100
|
+
*/
|
|
1101
|
+
async function snapshotCoreVersions(inst, deps) {
|
|
1102
|
+
const { db, exec, log } = deps;
|
|
1103
|
+
if (!UPGRADE_SHAPES.has(inst.hosting_shape) || inst.status !== 'active') return { ok: true, skipped: true };
|
|
1104
|
+
const boxExec = inst.hosting_shape === 'cloud-host' ? (deps.controlExec || exec) : exec;
|
|
1105
|
+
const channelMod = deps.updateChannel || require('./update-channel.js');
|
|
1106
|
+
const selfRoot = deps.selfInstanceRoot || selfInstanceRoot;
|
|
1107
|
+
const instanceDir = inst.hosting_shape === 'cloud-host' ? standaloneRoot(inst) : selfRoot();
|
|
1108
|
+
try {
|
|
1109
|
+
const probe = boxExec(versionCmd(inst), { allowFail: true });
|
|
1110
|
+
const body = probe && !probe.softFailed ? (probe.stdout || '') : null;
|
|
1111
|
+
const served = parseServedVersion(body);
|
|
1112
|
+
const schemaPending = parseSchemaPending(body);
|
|
1113
|
+
const listed = offeredVersions(channelMod, inst, instanceDir);
|
|
1114
|
+
const available = (listed && listed.versions) || [];
|
|
1115
|
+
// What the channel would take FROM the version the box is actually on. Resolving it
|
|
1116
|
+
// here rather than in the browser is what keeps the button's promise and the
|
|
1117
|
+
// runner's behaviour the same sentence.
|
|
1118
|
+
// resolveChannelTarget returns the bare version string, or null when the channel
|
|
1119
|
+
// admits nothing newer — "already current" and "nothing to take" are the same
|
|
1120
|
+
// answer here, and both correctly leave `recommended` NULL.
|
|
1121
|
+
const recommended = served
|
|
1122
|
+
? channelMod.resolveChannelTarget({ installed: served, channel: instanceChannel(inst, channelMod), available })
|
|
1123
|
+
: null;
|
|
1124
|
+
// THE TICK DOES NOT DRY-RUN. The verdict is produced on demand (preflightCoreUpgrade),
|
|
1125
|
+
// because a check re-run every tick for a box that has been behind for hours spends a
|
|
1126
|
+
// subprocess to re-learn the same answer, and is still missing in the one window that
|
|
1127
|
+
// matters — the minutes after a release, which is when someone presses the button.
|
|
1128
|
+
// The version probe below is cheap and genuinely per-tick; a dry run is not.
|
|
1129
|
+
const preflight = null;
|
|
1130
|
+
await db.query(
|
|
1131
|
+
`INSERT INTO provisioning_core_version_snapshot
|
|
1132
|
+
(instance_id, served_version, available, recommended, observed_at, preflight, preflight_at, schema_pending)
|
|
1133
|
+
VALUES ($1, $2, $3::jsonb, $4, now(), $5::jsonb, $6, $7)
|
|
1134
|
+
ON CONFLICT (instance_id) DO UPDATE
|
|
1135
|
+
SET served_version = EXCLUDED.served_version, available = EXCLUDED.available,
|
|
1136
|
+
recommended = EXCLUDED.recommended, observed_at = now(),
|
|
1137
|
+
schema_pending = EXCLUDED.schema_pending,
|
|
1138
|
+
-- A tick with nothing to preview must not ERASE the last verdict; it simply
|
|
1139
|
+
-- has nothing newer to say. Only a real dry run overwrites one.
|
|
1140
|
+
preflight = COALESCE(EXCLUDED.preflight, provisioning_core_version_snapshot.preflight),
|
|
1141
|
+
preflight_at = COALESCE(EXCLUDED.preflight_at, provisioning_core_version_snapshot.preflight_at)`,
|
|
1142
|
+
[inst.id, served, JSON.stringify(available), recommended || null,
|
|
1143
|
+
preflight ? JSON.stringify(preflight) : null, preflight ? new Date() : null, schemaPending]);
|
|
1144
|
+
return { ok: true, served, available: available.length, preflight, schemaPending };
|
|
1145
|
+
} catch (e) {
|
|
1146
|
+
log(` ~ core-version snapshot for ${inst.slug} skipped (${(e && e.message) || e})`);
|
|
1147
|
+
return { ok: true, skipped: true };
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/** Snapshot every instance whose box the runner can reach. Never throws. */
|
|
1152
|
+
async function snapshotAllCoreVersions(deps) {
|
|
1153
|
+
const { db, log } = deps;
|
|
1154
|
+
const shapes = [...UPGRADE_SHAPES];
|
|
1155
|
+
const { rows } = await db.query(
|
|
1156
|
+
`SELECT * FROM provisioning_instances WHERE status = 'active' AND hosting_shape = ANY($1)`, [shapes]);
|
|
1157
|
+
let seen = 0;
|
|
1158
|
+
for (const inst of rows) {
|
|
1159
|
+
const r = await snapshotCoreVersions(inst, deps);
|
|
1160
|
+
if (r && !r.skipped) seen++;
|
|
1161
|
+
}
|
|
1162
|
+
if (seen) log(`core-version snapshot: refreshed ${seen} instance(s)`);
|
|
1163
|
+
return { seen };
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
module.exports = { pushUpgradePin, UPGRADE_SHAPES, instanceChannel, CHANGE_CAP, REPO_NOTES, repoNote, requestedByOf, parseChangeRecords, readInstalledNotes, readPackagedNotes, changesBetween, coreCheckoutCandidates, findCoreCheckout, gitFault, resolveRunAs, versionCmd, parseServedVersion, parseSchemaPending, targetFault, parsePreflight, upgradeInvocation, preflightUpgrade, preflightCoreUpgrade, coreUpgradeInstance, snapshotCoreVersions, snapshotAllCoreVersions };
|