@bongos/core 1.21.175 → 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 +3551 -1516
- 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 +1370 -1411
- package/docs/copy-registry.json +2136 -2536
- 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 +5 -3
- 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 +20 -1
- package/docs/page-readings.json +4387 -4603
- 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 +4 -4
- package/modules/hall-ui/public/blockers.js +29 -30
- package/modules/hall-ui/public/board-room.css +5 -17
- package/modules/hall-ui/public/board-room.html +5 -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-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-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 +12 -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 +9 -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 +172 -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 +14 -4
- 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_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/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
package/scripts/gds/upgrade.js
CHANGED
|
@@ -1,1499 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
// scripts/gds/upgrade.js — @bongos-forwarder (task 1004848). Generated by
|
|
3
|
+
// scripts/bongos/codemod-rename-scripts-gds.js; do not edit.
|
|
4
|
+
// The real script is scripts/bongos/upgrade.js. This forwarder keeps callers that still name
|
|
5
|
+
// the old path working, and warns once so they get repointed. Task 1003706 removes it.
|
|
2
6
|
'use strict';
|
|
3
|
-
|
|
4
|
-
//
|
|
5
|
-
// Turns the former stub (which only ran a module-vs-core compatibility PRE-CHECK) into the real
|
|
6
|
-
// channel a two-repo consumer uses to pull the Cloud Bongos core from version N to N+1:
|
|
7
|
-
//
|
|
8
|
-
// pre-flight clean git tree + module-compat vs the TARGET core version + the artist gate (ADR 0241)
|
|
9
|
-
// → bump pin vendor the new core .tgz, rewrite the package.json `file:` dependency
|
|
10
|
-
// → npm install node_modules/@bongos/core becomes the new version
|
|
11
|
-
// → migrate `npm run migrate` (no such script: the core's migrate.sh itself, upgrade-migrate.js) applies additive `core_*` migrations
|
|
12
|
-
// → materialize refresh .claude/ skills+settings+hooks from the new core (claude-materialize / W3)
|
|
13
|
-
// → restart restart the instance's service so the new core is live
|
|
14
|
-
// → verify installed package version == target (+ optional /healthz + ledger row)
|
|
15
|
-
//
|
|
16
|
-
// SCOPE (ADR 0100 §2, reshaped by ADR 0103 §4): first built for emersonian-circles (task 1917), an
|
|
17
|
-
// npm-dependency instance pinning "@bongos/core": "file:vendor/bongos-core-<ver>.tgz" with
|
|
18
|
-
// `start`/`migrate` npm scripts and a materialized .claude/; the registry source (ADR 0134) and
|
|
19
|
-
// auto-rollback (task 2149) came later, with the consumers that needed them.
|
|
20
|
-
//
|
|
21
|
-
// Runs from the consumer instance root (default) or `--instance <dir>`. This script ships IN the core
|
|
22
|
-
// package, so once a consumer is on core vN+1 its own `bongos upgrade` is this code. The FIRST bump
|
|
23
|
-
// (before vN+1 is installed) is driven by running THIS file out of the new version's staged tree
|
|
24
|
-
// against `--instance <consumer>` — see docs/recipes for the bootstrap note.
|
|
25
|
-
|
|
26
|
-
const fs = require('node:fs');
|
|
27
|
-
const os = require('node:os');
|
|
28
|
-
const path = require('node:path');
|
|
29
|
-
const crypto = require('node:crypto');
|
|
30
|
-
const { spawnSync } = require('node:child_process');
|
|
31
|
-
const { arg, hasFlag } = require('./cli-lib');
|
|
32
|
-
const { parseSemver, compareSemver, isStable, listAvailableVersions, npmSpawnOpts } = require('./update-channel');
|
|
33
|
-
const { checkArtistGate } = require('./upgrade-artist-gate');
|
|
34
|
-
// The database side of a move: which command migrates, as which account, and whether that
|
|
35
|
-
// account can reach the database (tasks 1004274, 1002712, 1004512).
|
|
36
|
-
const { preflightDbIdentity, asAccount, ACCOUNT_RE, teeSpawn, migrateResult, migrateError } = require('./upgrade-migrate');
|
|
37
|
-
|
|
38
|
-
const CORE_PKG = '@bongos/core';
|
|
39
|
-
const ARTIFACT = 'bongos-core';
|
|
40
|
-
// WHERE THE VENDORED TARBALL SITS, as a package.json `file:` specifier — and the reason
|
|
41
|
-
// this is not a path.join(). The same string is spent two ways: it is joined onto an
|
|
42
|
-
// absolute dir for an fs check (path.join normalises a forward slash on every platform,
|
|
43
|
-
// so that direction is free), and it is WRITTEN INTO package.json as `file:<vendorRel>`.
|
|
44
|
-
// npm specifiers are POSIX. A path.join() here emits `file:vendor\\bongos-core-x.y.z.tgz`
|
|
45
|
-
// on a Windows builder's machine, and that pin is then COMMITTED to the instance repo —
|
|
46
|
-
// where the provisioner runs `npm ci` on Linux and reads a literal backslash in the
|
|
47
|
-
// filename. One copy, POSIX by construction, so a scaffold cannot be platform-stamped
|
|
48
|
-
// by the box that happened to run the scaffolder (task 1003839).
|
|
49
|
-
function vendorRelFor(version) { return path.posix.join('vendor', `${ARTIFACT}-${version}.tgz`); }
|
|
50
|
-
|
|
51
|
-
// ---- pure helpers (no I/O side effects beyond the injected fs) --------------
|
|
52
|
-
|
|
53
|
-
// The pin a consumer carries is the vendored-tarball version in its `file:` dependency — there is no
|
|
54
|
-
// separate pin file for this consumer shape. Extract the version from either the dep string or the
|
|
55
|
-
// installed package.
|
|
56
|
-
function versionFromTgzPath(p) {
|
|
57
|
-
const m = String(p || '').match(new RegExp(`${ARTIFACT}-(\\d+\\.\\d+\\.\\d+[^/]*)\\.tgz$`));
|
|
58
|
-
return m ? m[1] : null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// The version from a REGISTRY dep spec — a bare semver, optionally range-prefixed
|
|
62
|
-
// (`1.17.0`, `^1.17.0`, `~1.17.0`). Returns null for a `file:` tarball spec (that's
|
|
63
|
-
// versionFromTgzPath's job) so the two never both match. ADR 0134 (task 2090).
|
|
64
|
-
function versionFromDep(dep) {
|
|
65
|
-
const s = String(dep || '').trim();
|
|
66
|
-
if (!s || /^file:/.test(s)) return null;
|
|
67
|
-
const m = s.match(/^[\^~>=<\s]*v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)$/);
|
|
68
|
-
return m ? m[1] : null;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function readJson(file, fsImpl = fs) {
|
|
72
|
-
try { return JSON.parse(fsImpl.readFileSync(file, 'utf8')); } catch { return null; }
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// The version currently installed under node_modules — the authoritative "from".
|
|
76
|
-
function readInstalledCoreVersion(instanceDir, fsImpl = fs) {
|
|
77
|
-
const pkg = readJson(path.join(instanceDir, 'node_modules', CORE_PKG, 'package.json'), fsImpl);
|
|
78
|
-
return pkg && pkg.version ? pkg.version : null;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// The version the consumer's package.json currently pins — from either the `file:`
|
|
82
|
-
// tarball path (vendored) or a bare registry semver (ADR 0134). A best-effort "from"
|
|
83
|
-
// hint; readInstalledCoreVersion (node_modules) is the authoritative source.
|
|
84
|
-
function readPinnedCoreVersion(instanceDir, fsImpl = fs) {
|
|
85
|
-
const pkg = readJson(path.join(instanceDir, 'package.json'), fsImpl);
|
|
86
|
-
const dep = pkg && ((pkg.dependencies || {})[CORE_PKG] || (pkg.devDependencies || {})[CORE_PKG]);
|
|
87
|
-
return versionFromTgzPath(dep) || versionFromDep(dep);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Rewrite the consumer package.json to pin `file:<vendorRel>`. Returns the previous dep string.
|
|
91
|
-
function writePin(instanceDir, vendorRel, fsImpl = fs) {
|
|
92
|
-
const pkgPath = path.join(instanceDir, 'package.json');
|
|
93
|
-
const pkg = readJson(pkgPath, fsImpl);
|
|
94
|
-
if (!pkg) throw new Error(`no readable package.json at ${pkgPath}`);
|
|
95
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
96
|
-
const prev = pkg.dependencies[CORE_PKG];
|
|
97
|
-
pkg.dependencies[CORE_PKG] = `file:${vendorRel}`;
|
|
98
|
-
fsImpl.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
99
|
-
return prev;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Rewrite the consumer package.json to pin an EXACT registry version: the registry sibling
|
|
103
|
-
// of writePin. `npm install` then resolves @bongos/core@version from the public registry —
|
|
104
|
-
// no credential, since the package is public (task 1003872). Returns the previous dep string.
|
|
105
|
-
function writeRegistryPin(instanceDir, version, fsImpl = fs) {
|
|
106
|
-
const pkgPath = path.join(instanceDir, 'package.json');
|
|
107
|
-
const pkg = readJson(pkgPath, fsImpl);
|
|
108
|
-
if (!pkg) throw new Error(`no readable package.json at ${pkgPath}`);
|
|
109
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
110
|
-
const prev = pkg.dependencies[CORE_PKG];
|
|
111
|
-
pkg.dependencies[CORE_PKG] = version;
|
|
112
|
-
fsImpl.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
113
|
-
return prev;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Restore the consumer package.json's @bongos/core pin to an EXACT previous dep string,
|
|
117
|
-
// VERBATIM — whether that was a `file:` tarball path (vendored) or a bare registry semver.
|
|
118
|
-
// The inverse of writePin/writeRegistryPin used by the auto-rollback path (task 2149), which
|
|
119
|
-
// snapshots the returned prev-dep before the bump. Returns the dep string it overwrote.
|
|
120
|
-
function restorePin(instanceDir, depString, fsImpl = fs) {
|
|
121
|
-
const pkgPath = path.join(instanceDir, 'package.json');
|
|
122
|
-
const pkg = readJson(pkgPath, fsImpl);
|
|
123
|
-
if (!pkg) throw new Error(`no readable package.json at ${pkgPath}`);
|
|
124
|
-
pkg.dependencies = pkg.dependencies || {};
|
|
125
|
-
const prev = pkg.dependencies[CORE_PKG];
|
|
126
|
-
pkg.dependencies[CORE_PKG] = depString;
|
|
127
|
-
fsImpl.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
128
|
-
return prev;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// ---- steps (each injectable for tests) --------------------------------------
|
|
132
|
-
|
|
133
|
-
// `ignore` is waived (it comes from — and is argued for in — claude-materialize.js materializeWillRewrite, task 1004122); every OTHER dirty path still halts the bump. -uall (task 1004188): a bare status collapses a NEW untracked dir to one '?? dir/' entry the file-path waiver never matches.
|
|
134
|
-
function gitTreeClean(instanceDir, run = spawnSync, ignore = []) {
|
|
135
|
-
const r = run('git', ['status', '--porcelain', '-uall'], { cwd: instanceDir, encoding: 'utf8' });
|
|
136
|
-
// git's OWN words, not a guess at them (task 1004512). `not a git repo?` was the only
|
|
137
|
-
// explanation ever offered, and the failure that actually happens in the field is
|
|
138
|
-
// `detected dubious ownership` — a repo git can see perfectly well and refuses to read
|
|
139
|
-
// for the account asking. Swallowing the reason is what let that read as dirt.
|
|
140
|
-
if (!r || r.error || r.status !== 0) {
|
|
141
|
-
const said = String((r && (r.stderr || r.stdout)) || (r && r.error && r.error.message) || '').trim().split('\n').slice(0, 4).join('\n');
|
|
142
|
-
return { ok: false, reason: said || 'git status failed (not a git repo?)' };
|
|
143
|
-
}
|
|
144
|
-
const skip = new Set(ignore), all = (r.stdout || '').split('\n').filter((l) => l.trim());
|
|
145
|
-
const kept = all.filter((l) => !skip.has(l.slice(3).trim()));
|
|
146
|
-
return { ok: !kept.length, dirty: kept.join('\n'), ignored: all.length - kept.length };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// ---- persisting the pin (task 1002712) --------------------------------------
|
|
150
|
-
//
|
|
151
|
-
// THE BUG THIS EXISTS FOR. `bongos upgrade` rewrites package.json +
|
|
152
|
-
// package-lock.json in the instance WORKING TREE and stops there. On a
|
|
153
|
-
// pull-deploy instance that is not durable, and the way it fails is silent:
|
|
154
|
-
//
|
|
155
|
-
// deploy.sh short-circuits while HEAD == origin/main, so a freshly-upgraded
|
|
156
|
-
// (but uncommitted) tree survives — health-green, ledger-recorded, apparently
|
|
157
|
-
// done. Then ANY later push to the instance repo moves origin/main, deploy.sh
|
|
158
|
-
// runs `git reset --hard origin/main`, and the pin reverts to whatever is
|
|
159
|
-
// COMMITTED. The lockfile hash changes with it, which trips deploy.sh's
|
|
160
|
-
// `npm ci`, which reinstalls the OLD core. Nothing errors, at any point.
|
|
161
|
-
//
|
|
162
|
-
// cloudbongos.com sat on 1.19.56 for thirteen releases exactly this way: the
|
|
163
|
-
// core_upgrades ledger holds two successful `auto-subscription (patch)` rows
|
|
164
|
-
// (1.19.56→1.19.60, 1.19.60→1.19.62) whose result no longer existed.
|
|
165
|
-
//
|
|
166
|
-
// The pin is REPO state, so the repo of record is the right place for it —
|
|
167
|
-
// deploy.sh treating origin/main as authoritative is correct, not the bug.
|
|
168
|
-
// Hence: --commit-pin lands it, and its absence WARNS rather than staying quiet.
|
|
169
|
-
const PIN_FILES = ['package.json', 'package-lock.json'];
|
|
170
|
-
|
|
171
|
-
// The tracked files regenerateApiArtifacts' generators rewrite WHOLE, by generator (task
|
|
172
|
-
// 1003926). Committing only the pin left these dirty, so the NEXT `bongos upgrade` refused
|
|
173
|
-
// its clean-tree pre-flight. They are generated end to end, so once a generator has run
|
|
174
|
-
// their content is its output whatever they held before: committing them can never carry
|
|
175
|
-
// an owner's edit. tests/upgrade_persist_pin.mjs pins this list against both generators.
|
|
176
|
-
const API_ARTIFACT_FILES = {
|
|
177
|
-
'gen-api-docs.js': ['docs/api/openapi.json', 'docs/api-reference.md', 'docs/routes-permissions.md'],
|
|
178
|
-
'gen-api-client.js': ['index.mjs', 'index.cjs', 'bongos-client.global.js', 'index.d.ts', 'package.json', 'README.md']
|
|
179
|
-
.map((f) => `clients/bongos-client/${f}`),
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
function gitCurrentBranch(instanceDir, run = spawnSync) {
|
|
183
|
-
const r = run('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: instanceDir, encoding: 'utf8' });
|
|
184
|
-
if (!r || r.error || r.status !== 0) return null;
|
|
185
|
-
const b = (r.stdout || '').trim();
|
|
186
|
-
return b && b !== 'HEAD' ? b : null;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Which of `paths` git currently reports as changed — never a path it was not asked about,
|
|
190
|
-
// and no git call for an empty list (a pathspec of nothing is the whole tree).
|
|
191
|
-
function dirtyPaths(instanceDir, paths, run = spawnSync) {
|
|
192
|
-
if (!paths.length) return [];
|
|
193
|
-
const r = run('git', ['status', '--porcelain', '--', ...paths], { cwd: instanceDir, encoding: 'utf8' });
|
|
194
|
-
if (!r || r.error || r.status !== 0) return null; // not a git repo — nothing to persist
|
|
195
|
-
return (r.stdout || '').split('\n').map((l) => l.slice(3).trim()).filter((p) => paths.includes(p));
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
function dirtyPinFiles(instanceDir, run = spawnSync) { return dirtyPaths(instanceDir, PIN_FILES, run); }
|
|
199
|
-
|
|
200
|
-
// ---- which credential the pin push rides (task 1003081, ADR 0176 follow-up) ----
|
|
201
|
-
//
|
|
202
|
-
// THE BUG THIS EXISTS FOR. A PRIVATE standalone instance's `origin` authenticates with
|
|
203
|
-
// a per-repo deploy key that GitHub registers READ-ONLY (ADR 0176): it can pull, never
|
|
204
|
-
// push. The push leg below went `git push origin HEAD:main` regardless, so on such an
|
|
205
|
-
// instance GitHub refused the pin push, the bump was not durable, and the next
|
|
206
|
-
// pull-deploy `git reset --hard origin/main` + `npm ci` reinstalled the OLD core
|
|
207
|
-
// against an already-migrated DB — the exact silent rollback task 1002712 closed for
|
|
208
|
-
// every other path, back through a warning line nobody read. The scaffold push and the
|
|
209
|
-
// re-provision pin refresh already prefer the owner's stored `repo`-scoped OAuth token
|
|
210
|
-
// over the SSH origin (provision.js resolveOwnerGithubToken + buildCorePinRefreshCommit);
|
|
211
|
-
// the pin push now has the same preference — the token when one is stored, the git
|
|
212
|
-
// remote otherwise — and a refused push is LOUD, naming the read-only key and the fix.
|
|
213
|
-
|
|
214
|
-
// The GitHub owner/name behind `remote`, or null when the remote is not a GitHub
|
|
215
|
-
// repository (there is nothing to push a GitHub token at) or cannot be read. Accepts
|
|
216
|
-
// the plain github.com forms and every SSH alias instanceRepoRemotes wires
|
|
217
|
-
// (`git@github-deploy:owner/repo.git`) — never another host, so a stored token is
|
|
218
|
-
// never pointed at github.com/<owner>/<repo> for a repo that lives elsewhere.
|
|
219
|
-
function remoteGithubRepo(instanceDir, remote = 'origin', run = spawnSync) {
|
|
220
|
-
const r = run('git', ['remote', 'get-url', remote], { cwd: instanceDir, encoding: 'utf8' });
|
|
221
|
-
if (!r || r.error || r.status !== 0) return null;
|
|
222
|
-
const url = String(r.stdout || '').trim();
|
|
223
|
-
let auth;
|
|
224
|
-
try { auth = require('./provision-repo-auth'); } catch { return null; } // a package without the provisioner has no token path
|
|
225
|
-
const repo = auth.parseTargetRef(url);
|
|
226
|
-
if (!repo) return null;
|
|
227
|
-
const host = (url.match(/^(?:[a-z+]+:\/\/)?(?:[^@/]+@)?([^/:]+)[/:]/i) || [])[1] || '';
|
|
228
|
-
if (host.toLowerCase() !== 'github.com' && !auth.instanceRepoRemotes(repo).includes(url)) return null;
|
|
229
|
-
return repo;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// The deploy key `remote` authenticates with when the checkout is wired the ADR 0176
|
|
233
|
-
// way (`core.sshCommand … -i <key>` in the repo's own config); null when it is not.
|
|
234
|
-
// What a refused origin push is diagnosed against — GitHub's SSH refusal is a terse
|
|
235
|
-
// one-liner that names no key.
|
|
236
|
-
function pinnedDeployKey(instanceDir, run = spawnSync) {
|
|
237
|
-
const r = run('git', ['config', '--get', 'core.sshCommand'], { cwd: instanceDir, encoding: 'utf8' });
|
|
238
|
-
if (!r || r.error || r.status !== 0) return null;
|
|
239
|
-
const m = String(r.stdout || '').match(/(?:^|\s)-i\s+(\S+)/);
|
|
240
|
-
return m ? m[1] : null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// Resolve { token, repo } for the pin push, or null → push via the git remote.
|
|
244
|
-
// repo — the remote's GitHub owner/name (remoteGithubRepo);
|
|
245
|
-
// row — provision-repo-auth lookupInstanceRow, used ONLY when its target_ref IS that
|
|
246
|
-
// repo, so another instance's credential is never spent on this checkout;
|
|
247
|
-
// token — provision-repo-auth resolveOwnerGithubToken (deps.getGithubToken / deps.db
|
|
248
|
-
// are its test seams), the same resolver the scaffold push uses.
|
|
249
|
-
// Every miss is the origin path, not an error, and the token is never logged. The row
|
|
250
|
-
// lookup and the token read BOTH live in the leaf rather than here because their default
|
|
251
|
-
// requires are computed, and this file is inside the merge resolver's executed closure
|
|
252
|
-
// with a pinned computed-require count of its own (task 1003081).
|
|
253
|
-
async function resolvePinPushAuth({ instanceDir, remote = 'origin' }, deps = {}, run = spawnSync) {
|
|
254
|
-
const log = deps.log || console.log;
|
|
255
|
-
const repo = remoteGithubRepo(instanceDir, remote, run);
|
|
256
|
-
if (!repo) return null;
|
|
257
|
-
const auth = require('./provision-repo-auth');
|
|
258
|
-
const inst = await auth.lookupInstanceRow(instanceDir, deps);
|
|
259
|
-
if (!inst) { const key = pinnedDeployKey(instanceDir, run); if (key) log(auth.missingRowNote(key, remote)); return null; } // task 1004065
|
|
260
|
-
const rowRepo = auth.parseTargetRef(inst.target_ref);
|
|
261
|
-
const same = (a, b) => a && b && a.owner.toLowerCase() === b.owner.toLowerCase() && a.name.toLowerCase() === b.name.toLowerCase();
|
|
262
|
-
if (!same(rowRepo, repo)) {
|
|
263
|
-
log(` • pin push: the provisioning row for ${path.basename(instanceDir)} names ${inst.target_ref}, not ${remote}'s ${repo.owner}/${repo.name} — its owner's token is not for this checkout; pushing via ${remote}`);
|
|
264
|
-
return null;
|
|
265
|
-
}
|
|
266
|
-
const token = await auth.resolveOwnerGithubToken(inst, { ...deps, log }, { tag: '[pin-push]' });
|
|
267
|
-
if (!token) {
|
|
268
|
-
log(` • pin push: no owner GitHub token stored for ${repo.owner}/${repo.name} — pushing via ${remote}`);
|
|
269
|
-
return null;
|
|
270
|
-
}
|
|
271
|
-
return { token, repo };
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// The pin push over the owner's token: a chmod-600 credential-store file in the temp
|
|
275
|
-
// dir carries the secret, git reads it through `credential.helper=store`, and the file
|
|
276
|
-
// is removed in `finally` whatever the push did — the token is never in argv (ps,
|
|
277
|
-
// journald) and never in a log line. provision.js buildCorePinRefreshCommit's shape,
|
|
278
|
-
// as argv instead of a shell string.
|
|
279
|
-
function pushPinViaToken({ instanceDir, token, repo, target }, run = spawnSync) {
|
|
280
|
-
const credFile = path.join(os.tmpdir(), `bongos-gitcred-pin-${process.pid}-${crypto.randomBytes(6).toString('hex')}`);
|
|
281
|
-
try {
|
|
282
|
-
fs.writeFileSync(credFile, `https://x-access-token:${token}@github.com\n`, { mode: 0o600 });
|
|
283
|
-
return run('git', ['-c', `credential.helper=store --file=${credFile}`, 'push', `https://x-access-token@github.com/${repo.owner}/${repo.name}.git`, target], {
|
|
284
|
-
cwd: instanceDir, encoding: 'utf8',
|
|
285
|
-
env: { ...process.env, GIT_TERMINAL_PROMPT: '0' }, // a rejected credential must fail, not sit at a password prompt
|
|
286
|
-
});
|
|
287
|
-
} finally {
|
|
288
|
-
fs.rmSync(credFile, { force: true });
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// What git said on stderr, for a refusal line — GitHub's own reason ("ERROR: The key you
|
|
293
|
-
// are authenticating with has been marked as read only", "remote: Write access to
|
|
294
|
-
// repository not granted") is the fact an operator wants beside the diagnosis, and it
|
|
295
|
-
// is never the LAST line: git closes with its generic `fatal:`. Prefer the server's
|
|
296
|
-
// line, fall back to the last one.
|
|
297
|
-
function gitSaid(r) {
|
|
298
|
-
const lines = String((r && r.stderr) || '').split('\n').map((l) => l.trim()).filter(Boolean);
|
|
299
|
-
const server = lines.filter((l) => /^(ERROR:|remote:)/i.test(l));
|
|
300
|
-
const said = server.length ? server[server.length - 1] : lines[lines.length - 1];
|
|
301
|
-
return said ? ` (git: ${said})` : '';
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// `push: false` is CO-TENANT MODE (task 1003521). The reasoning above assumes the repo
|
|
305
|
-
// of record is OURS; on a provisioned instance it is the customer's own GitHub repo, so
|
|
306
|
-
// pushing there is both wrong and uncredentialed. Such an instance also has no
|
|
307
|
-
// pull-deploy timer, so nothing will `reset --hard` the pin away — the commit alone
|
|
308
|
-
// makes it durable, and leaves the tree clean for the customer's own pulls.
|
|
309
|
-
//
|
|
310
|
-
// `pushAuth` ({ token, repo }, from resolvePinPushAuth) makes the push ride the owner's
|
|
311
|
-
// GitHub token over HTTPS (task 1003081); null pushes via `remote`. The result carries
|
|
312
|
-
// `via: 'owner-token'` on the token path and `refused` ('read-only-deploy-key' |
|
|
313
|
-
// 'owner-token') with a plain-sentence `error` when GitHub turned the push away.
|
|
314
|
-
// `artifactPaths` (task 1003926) are the generated files this upgrade rewrote; the ones
|
|
315
|
-
// that changed are staged and committed with the pin.
|
|
316
|
-
function persistPin({ instanceDir, fromVersion, toVersion, commitPin, push = true, remote = 'origin', dryRun = false, pushAuth = null, artifactPaths = [] },
|
|
317
|
-
run = spawnSync, log = console.log, err = console.error) {
|
|
318
|
-
const dirtyPins = dirtyPinFiles(instanceDir, run);
|
|
319
|
-
if (dirtyPins === null) return { applicable: false, reason: 'not a git repo' };
|
|
320
|
-
const dirtyArtifacts = dirtyPaths(instanceDir, artifactPaths, run) || [];
|
|
321
|
-
const dirty = [...dirtyPins, ...dirtyArtifacts];
|
|
322
|
-
if (!dirty.length) return { applicable: false, reason: 'pin files already match the repo' };
|
|
323
|
-
const toStage = [...PIN_FILES, ...dirtyArtifacts];
|
|
324
|
-
|
|
325
|
-
const branch = gitCurrentBranch(instanceDir, run);
|
|
326
|
-
const target = `HEAD:${branch || 'main'}`;
|
|
327
|
-
// Single-quote the interpolated values. Nothing here executes these strings — they
|
|
328
|
-
// are printed for a human to paste — but an unquoted branch or remote carrying shell
|
|
329
|
-
// metacharacters would misbehave once pasted, so emit them paste-safe.
|
|
330
|
-
const q = (s) => `'${String(s).replace(/'/g, `'\\''`)}'`;
|
|
331
|
-
const at = `git -C ${q(instanceDir)}`;
|
|
332
|
-
|
|
333
|
-
if (!commitPin) {
|
|
334
|
-
if (!push) {
|
|
335
|
-
// No pull-deploy of ours reaches this instance, so an uncommitted pin is untidy
|
|
336
|
-
// rather than doomed. Crying wolf here would teach an operator to ignore the
|
|
337
|
-
// warning that DOES matter on the platform instance.
|
|
338
|
-
err(` ! pin not committed — ${dirty.join(', ')} changed in the working tree only.`);
|
|
339
|
-
err(' This target pulls from a repo that is not ours, so nothing here will revert it —');
|
|
340
|
-
err(' but the tree stays dirty until it is committed. Re-run with --commit-pin to commit it.');
|
|
341
|
-
return { applicable: true, committed: false, pushed: false, dirty, warned: true };
|
|
342
|
-
}
|
|
343
|
-
// LOUD — this is the whole point. A silent uncommitted pin is the bug.
|
|
344
|
-
err(` ! PIN NOT COMMITTED — ${dirty.join(', ')} changed in the working tree only.`);
|
|
345
|
-
err(' On a pull-deploy instance the next push to the repo of record makes deploy.sh');
|
|
346
|
-
err(` 'git reset --hard ${remote}/${branch || 'main'}', which REVERTS this pin and lets npm ci`);
|
|
347
|
-
err(` reinstall ${fromVersion || 'the previous core'}. Re-run with --commit-pin, or commit + push:`);
|
|
348
|
-
err(` ${at} add ${toStage.map(q).join(' ')} && ${at} commit -m ${q(`pin @bongos/core ${toVersion}`)} && ${at} push ${q(remote)} ${q(target)}`);
|
|
349
|
-
return { applicable: true, committed: false, pushed: false, dirty, warned: true };
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if (dryRun) {
|
|
353
|
-
log(push
|
|
354
|
-
? ` [dry-run] would commit + push ${dirty.join(', ')} to ${remote}/${branch || 'main'}`
|
|
355
|
-
: ` [dry-run] would commit ${dirty.join(', ')} locally (no push — this remote is not ours)`);
|
|
356
|
-
return { applicable: true, committed: false, pushed: false, dirty, dryRun: true };
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const message = `pin @bongos/core ${toVersion}${fromVersion ? ` (was ${fromVersion})` : ''}`;
|
|
360
|
-
const add = run('git', ['add', ...toStage], { cwd: instanceDir, encoding: 'utf8' });
|
|
361
|
-
if (!add || add.error || add.status !== 0) {
|
|
362
|
-
err(' ! could not stage the pin files — commit + push them by hand or the next deploy reverts this bump.');
|
|
363
|
-
return { applicable: true, committed: false, pushed: false, dirty, error: 'git add failed' };
|
|
364
|
-
}
|
|
365
|
-
const idEnv = require('./upgrade-commit-identity').commitIdentityEnv(instanceDir, run), commit = run('git', ['commit', '-m', message], { cwd: instanceDir, encoding: 'utf8', ...(idEnv ? { env: idEnv } : {}) }); // a repo naming nobody still commits (task 1004514)
|
|
366
|
-
if (!commit || commit.error || commit.status !== 0) {
|
|
367
|
-
err(' ! could not commit the pin — commit + push it by hand or the next deploy reverts this bump.');
|
|
368
|
-
return { applicable: true, committed: false, pushed: false, dirty, error: 'git commit failed' };
|
|
369
|
-
}
|
|
370
|
-
log(` ✓ committed the pin (${dirty.join(', ')})`);
|
|
371
|
-
|
|
372
|
-
if (!push) {
|
|
373
|
-
log(' ✓ not pushed, by request — this instance\'s repo of record is not ours, and no pull-deploy of ours can revert the commit');
|
|
374
|
-
return { applicable: true, committed: true, pushed: false, dirty, pushSkipped: true };
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Committed but not pushed is still doomed: deploy.sh resets to the REMOTE. So every
|
|
378
|
-
// refusal below is LOUD (task 1003081) — the pull-deploy consequence, the diagnosis,
|
|
379
|
-
// and a by-hand push that can actually write.
|
|
380
|
-
const revert = () => err(` The next pull-deploy 'git reset --hard ${remote}/${branch || 'main'}' + npm ci then reinstalls ${fromVersion || 'the previous core'}.`);
|
|
381
|
-
|
|
382
|
-
if (pushAuth && pushAuth.token && pushAuth.repo) {
|
|
383
|
-
const { repo } = pushAuth;
|
|
384
|
-
let pushed = null;
|
|
385
|
-
try {
|
|
386
|
-
pushed = pushPinViaToken({ instanceDir, token: pushAuth.token, repo, target }, run);
|
|
387
|
-
} catch (e) {
|
|
388
|
-
// The credential file could not even be written (an unwritable temp dir): say so
|
|
389
|
-
// and fall through to the remote — a public origin may still take it.
|
|
390
|
-
err(` ! could not stage the owner's GitHub credential (${e && e.message}) — pushing via ${remote} instead`);
|
|
391
|
-
}
|
|
392
|
-
if (pushed && !pushed.error && pushed.status === 0) {
|
|
393
|
-
log(` ✓ pushed the pin to ${repo.owner}/${repo.name}@${branch || 'main'} via the owner's GitHub token — a pull-deploy reset can no longer revert it`);
|
|
394
|
-
return { applicable: true, committed: true, pushed: true, dirty, via: 'owner-token' };
|
|
395
|
-
}
|
|
396
|
-
if (pushed) {
|
|
397
|
-
err(` ! PIN NOT PUSHED — GitHub refused the push over the owner's GitHub token, so this bump is NOT durable.`);
|
|
398
|
-
err(` The stored token could not write ${repo.owner}/${repo.name}: it may have expired (they live an hour), or`);
|
|
399
|
-
err(` lack the \`repo\` scope (\`public_repo\` cannot write a private repository — ADR 0155).${gitSaid(pushed)}`);
|
|
400
|
-
revert();
|
|
401
|
-
err(' Fix: have the instance owner reconnect GitHub with repository access, then re-run `bongos upgrade --commit-pin`;');
|
|
402
|
-
err(' or push the commit yourself with a credential that can write:');
|
|
403
|
-
err(` ${at} push ${q(`https://github.com/${repo.owner}/${repo.name}.git`)} ${q(target)}`);
|
|
404
|
-
return {
|
|
405
|
-
applicable: true, committed: true, pushed: false, dirty, refused: 'owner-token',
|
|
406
|
-
error: `GitHub refused the pin push over the owner's GitHub token (expired, or missing the repo scope) — the pin is committed locally but not on ${repo.owner}/${repo.name}`,
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const pushed = run('git', ['push', remote, target], { cwd: instanceDir, encoding: 'utf8' });
|
|
412
|
-
if (!pushed || pushed.error || pushed.status !== 0) {
|
|
413
|
-
const key = pinnedDeployKey(instanceDir, run);
|
|
414
|
-
if (key) {
|
|
415
|
-
// A wired private instance (ADR 0176): the key can pull, never push, and no owner
|
|
416
|
-
// token was stored to push with instead — the bug this leg exists for.
|
|
417
|
-
const repo = remoteGithubRepo(instanceDir, remote, run);
|
|
418
|
-
err(` ! PIN NOT PUSHED — GitHub refused the push to ${remote}, so this bump is NOT durable.`);
|
|
419
|
-
err(` ${remote} authenticates with the deploy key at ${key}, which is registered READ-ONLY on the`);
|
|
420
|
-
err(` repository (ADR 0176): it can pull, never push. No owner GitHub token was available to push with instead (none is stored, or — on a hosted project's own box — the hub's record of it is not readable from here).${gitSaid(pushed)}`);
|
|
421
|
-
revert();
|
|
422
|
-
err(' Fix: have the instance owner reconnect GitHub with repository access (that stores a short-lived repo token),');
|
|
423
|
-
err(` then re-run \`bongos upgrade --commit-pin\` where that token is readable — the control plane, with --instance ${q(instanceDir)};`);
|
|
424
|
-
err(' or push the commit yourself with a credential that can write:');
|
|
425
|
-
err(` ${at} push ${q(`https://github.com/${repo ? `${repo.owner}/${repo.name}` : '<owner>/<repo>'}.git`)} ${q(target)}`);
|
|
426
|
-
return {
|
|
427
|
-
applicable: true, committed: true, pushed: false, dirty, refused: 'read-only-deploy-key',
|
|
428
|
-
error: `GitHub refused the pin push: ${remote} authenticates with the read-only deploy key at ${key}, and no owner GitHub token was available to push with — the pin is committed locally but not on the remote`,
|
|
429
|
-
};
|
|
430
|
-
}
|
|
431
|
-
err(` ! commit landed locally but the push to ${remote} FAILED — this bump is still not durable.`);
|
|
432
|
-
err(` Push it: git -C ${instanceDir} push ${remote} ${target}`);
|
|
433
|
-
return { applicable: true, committed: true, pushed: false, dirty, error: 'git push failed' };
|
|
434
|
-
}
|
|
435
|
-
log(` ✓ pushed the pin to ${remote}/${branch || 'main'} — a pull-deploy reset can no longer revert it`);
|
|
436
|
-
return { applicable: true, committed: true, pushed: true, dirty };
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Copy the source .tgz (+ its side-car manifest, if present) into the consumer's vendor/ dir under the
|
|
440
|
-
// canonical name. Returns the vendor-relative tarball path to pin.
|
|
441
|
-
function vendorTarball({ instanceDir, fromTgz, targetVersion }, fsImpl = fs) {
|
|
442
|
-
const vendorDir = path.join(instanceDir, 'vendor');
|
|
443
|
-
fsImpl.mkdirSync(vendorDir, { recursive: true });
|
|
444
|
-
const destName = `${ARTIFACT}-${targetVersion}.tgz`;
|
|
445
|
-
const destAbs = path.join(vendorDir, destName);
|
|
446
|
-
fsImpl.copyFileSync(fromTgz, destAbs);
|
|
447
|
-
const fromManifest = fromTgz.replace(/\.tgz$/, '.manifest.json');
|
|
448
|
-
if (fsImpl.existsSync(fromManifest)) {
|
|
449
|
-
fsImpl.copyFileSync(fromManifest, path.join(vendorDir, `${ARTIFACT}-${targetVersion}.manifest.json`));
|
|
450
|
-
}
|
|
451
|
-
return path.posix.join('vendor', destName);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// Lightweight module-compat pre-check reusing module.js against the TARGET core version. Best-effort:
|
|
455
|
-
// a vanilla consumer has no modules/ (trivially OK); any failure downgrades to a warning, never blocks
|
|
456
|
-
// the MVP bump. Returns { ok, blocking:[], warnings:[], deprecations:[], expired:[], note }.
|
|
457
|
-
//
|
|
458
|
-
// WARNINGS ARE NOT OPTIONAL OUTPUT (ADR 0166 / task 1001773). This function used to return only
|
|
459
|
-
// `blocking`, so a DISABLED module whose coreVersion the target no longer satisfies vanished from the
|
|
460
|
-
// bump entirely. That is precisely the catalog-rot case ADR 0107 §7 names: an accepted catalog module
|
|
461
|
-
// ships `default: false` (§6), so EVERY rotted catalog entry is disabled by construction and every one
|
|
462
|
-
// of them was invisible here. The bump still doesn't BLOCK on them — a disabled module breaks nothing
|
|
463
|
-
// at boot — but it must SAY so, with the maintainer named, or the incompatibility is discovered by
|
|
464
|
-
// whoever next tries to enable it.
|
|
465
|
-
function preflightModules({ instanceDir, targetVersion }, deps = {}) {
|
|
466
|
-
const empty = { blocking: [], warnings: [], deprecations: [], expired: [] };
|
|
467
|
-
const modulesDir = path.join(instanceDir, 'modules');
|
|
468
|
-
if (!fs.existsSync(modulesDir)) return { ok: true, ...empty, note: 'no modules/ — nothing to pre-check' };
|
|
469
|
-
try {
|
|
470
|
-
const mod = deps.moduleLib || require('./module');
|
|
471
|
-
const data = mod.gatherUpgrade({ modulesDir, core: targetVersion });
|
|
472
|
-
const verdict = mod.evaluateUpgrade(data);
|
|
473
|
-
const rotted = (verdict.warnings || []).length;
|
|
474
|
-
const note = !verdict.ok
|
|
475
|
-
? 'incompatible enabled module(s)'
|
|
476
|
-
: rotted
|
|
477
|
-
? `all enabled modules satisfy the target, but ${rotted} disabled module(s) do NOT`
|
|
478
|
-
: 'all enabled modules satisfy the target';
|
|
479
|
-
return {
|
|
480
|
-
ok: verdict.ok,
|
|
481
|
-
blocking: verdict.blocking || [],
|
|
482
|
-
warnings: verdict.warnings || [],
|
|
483
|
-
deprecations: verdict.deprecations || [],
|
|
484
|
-
expired: verdict.expired || [],
|
|
485
|
-
note,
|
|
486
|
-
};
|
|
487
|
-
} catch (e) {
|
|
488
|
-
return { ok: true, ...empty, note: `module pre-check skipped (${e.message})` };
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// Print the non-blocking half of the module pre-check: rotted-but-disabled modules and
|
|
493
|
-
// self-declared deprecations, each with its maintenance hint so the reader knows who to chase.
|
|
494
|
-
// Split out of runUpgrade so it is testable without driving a whole bump.
|
|
495
|
-
function reportModulePreflight(mods, targetVersion, { log = console.log, err = console.error, hint } = {}) {
|
|
496
|
-
const fmt = hint || ((row) => { try { return require('./module').formatMaintenanceHint(row); } catch { return ''; } });
|
|
497
|
-
const lines = [];
|
|
498
|
-
for (const w of mods.warnings || []) {
|
|
499
|
-
lines.push(` ! module "${w.key}" is DISABLED here, so it does not block this bump — but its coreVersion ${w.coreVersion || '(none)'} is not satisfied by core ${targetVersion}: enabling it would silently fail to mount.`);
|
|
500
|
-
lines.push(` ↳ ${fmt(w)}`);
|
|
501
|
-
}
|
|
502
|
-
for (const d of mods.deprecations || []) {
|
|
503
|
-
const overdue = (mods.expired || []).includes(d);
|
|
504
|
-
lines.push(` ! module "${d.key}" is ${d.maintenance.status}${overdue ? ` and PAST its removeAfter ${d.maintenance.removeAfter} (core ${targetVersion}) — it is due for removal` : ''}.`);
|
|
505
|
-
lines.push(` ↳ ${fmt(d)}`);
|
|
506
|
-
}
|
|
507
|
-
for (const l of lines) err(l);
|
|
508
|
-
return lines;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function npmInstall(instanceDir, run = spawnSync) {
|
|
512
|
-
// `npm install` (NOT `npm ci`): the pin just changed, so the lockfile must be regenerated from the
|
|
513
|
-
// new tarball; `npm ci` would refuse on the package.json↔lock divergence.
|
|
514
|
-
// This used to take an extraEnv carrying a resolved npm credential, because the instance
|
|
515
|
-
// `.npmrc`'s env expansion could shadow a valid user-level one (task 1002754). The core is
|
|
516
|
-
// public and writes no `.npmrc`, so there is no credential to resolve (task 1003878).
|
|
517
|
-
const opts = { cwd: instanceDir, stdio: 'inherit', ...npmSpawnOpts() }; // Windows: npm.cmd needs a shell (task 1004631)
|
|
518
|
-
const r = run('npm', ['install', '--no-audit', '--no-fund'], opts);
|
|
519
|
-
return !r || r.error ? { ok: false, reason: r && r.error ? r.error.message : 'spawn failed' } : { ok: r.status === 0, code: r.status };
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
async function runMigrate(instanceDir, run = spawnSync, account = null) {
|
|
523
|
-
const { cmd, args, env } = require('./upgrade-migrate').migratePlan(instanceDir); // its own migrate script, else the core's migrate.sh (task 1004274)
|
|
524
|
-
// `account`: the project's own unix+Postgres identity when the move itself is running as
|
|
525
|
-
// the account that owns the CHECKOUT instead (--migrate-as; upgrade-migrate.js, task 1004512).
|
|
526
|
-
const q = asAccount(account, cmd, args, env ? { INIT_CWD: instanceDir } : {});
|
|
527
|
-
// TEED, not inherited: still streams live, and a failure names its migration + psql's ERROR line (upgrade-migrate.js, task 1004734)
|
|
528
|
-
return migrateResult(await (run === spawnSync ? teeSpawn : run)(q.cmd, q.args, { cwd: instanceDir, ...(env ? { env } : {}) }));
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
// WHICH database will migrate target? (task 1002755)
|
|
532
|
-
//
|
|
533
|
-
// The pre-flight probe below is only meaningful if it tests the SAME database the migration
|
|
534
|
-
// will later use. It used to be handed `process.env.PGDATABASE` — undefined on a normal box —
|
|
535
|
-
// so psql fell through to a database named after the OS user, failed for an unrelated reason,
|
|
536
|
-
// and soft-skipped with "psql failed for another reason". migrate then picked its own name and
|
|
537
|
-
// hit a database that did not exist. Two soft failures composing into a hard one, discovered
|
|
538
|
-
// only AFTER the pin had moved.
|
|
539
|
-
//
|
|
540
|
-
// So resolve it here, up front, by asking the core's OWN resolver in a child pointed at this
|
|
541
|
-
// instance — the same precedence migrate.sh uses (PGDATABASE wins, else the instance's branding
|
|
542
|
-
// pack) and the same function, so the probe and the migration cannot disagree. Spawned rather
|
|
543
|
-
// than required because requiring pool.js constructs a pg Pool in this process.
|
|
544
|
-
// Returns { db, ran }:
|
|
545
|
-
// { db: 'name', ran: true } — resolved
|
|
546
|
-
// { db: null, ran: true } — the resolver RAN and this instance names no database. Fatal:
|
|
547
|
-
// migrate cannot proceed and we know it before the pin moves.
|
|
548
|
-
// { db: null, ran: false } — the resolver could not run at all (no node_modules yet, a
|
|
549
|
-
// stubbed spawn in tests). NOT fatal: that is a different problem
|
|
550
|
-
// from "no database configured", and guessing which one it is would
|
|
551
|
-
// block bumps for the wrong reason. migrate.sh makes the same
|
|
552
|
-
// distinction and still fails loudly later, so the net holds.
|
|
553
|
-
function resolveInstanceDb({ instanceDir }, run = spawnSync) {
|
|
554
|
-
if (process.env.PGDATABASE) return { db: process.env.PGDATABASE, ran: true };
|
|
555
|
-
const poolPath = path.join(instanceDir, 'node_modules', CORE_PKG, 'src', 'bongos', 'pool');
|
|
556
|
-
// the module path rides as argv, never interpolated into JS source — a Windows-shaped path's
|
|
557
|
-
// backslashes are escape sequences inside a string literal
|
|
558
|
-
const r = run(process.execPath, ['-e', "process.stdout.write(require(process.argv[1]).instanceDbName() || '')", poolPath], {
|
|
559
|
-
cwd: instanceDir,
|
|
560
|
-
encoding: 'utf8',
|
|
561
|
-
env: { ...process.env, BONGOS_INSTANCE_ROOT: instanceDir },
|
|
562
|
-
});
|
|
563
|
-
if (!r || r.error || r.status !== 0 || typeof r.stdout !== 'string') return { db: null, ran: false };
|
|
564
|
-
return { db: String(r.stdout).trim() || null, ran: true };
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
// Regenerate the per-instance API artifacts (OpenAPI spec + api-reference + typed
|
|
568
|
-
// client) from the NEW core's routes — the claude-materialize sibling (task 2051).
|
|
569
|
-
// WHY: the routes + generators ship WITH core, and the generated spec/docs/client also
|
|
570
|
-
// ship as CORE STARTERS (task 2078 — a fresh hall/docs must render before first regen),
|
|
571
|
-
// but they are per-instance GENERATED, so a bump changes the served API while the
|
|
572
|
-
// instance's committed spec/client lag until regenerated (the classification settled in
|
|
573
|
-
// task 2141; init does the same at standup). We SPAWN the new core's generators (a fresh child
|
|
574
|
-
// captures its output root from the env below) with <FALLBACK_PREFIX>_INSTANCE_ROOT set
|
|
575
|
-
// so resolveDocsRoot() writes INTO the instance repo, not node_modules. docs first — the
|
|
576
|
-
// client reads the spec it produces. Best-effort: a hiccup warns, never fails the bump
|
|
577
|
-
// (fitness `gen-api-docs --check` + the api_client test backstop it on the next ship).
|
|
578
|
-
// Runs AFTER npm install so the generators come from the NEW core.
|
|
579
|
-
function regenerateApiArtifacts({ instanceDir, coreRoot }, run = spawnSync, fsImpl = fs) {
|
|
580
|
-
const env = { ...process.env, BONGOS_INSTANCE_ROOT: instanceDir };
|
|
581
|
-
const ran = [];
|
|
582
|
-
for (const gen of Object.keys(API_ARTIFACT_FILES)) {
|
|
583
|
-
const script = path.join(coreRoot, 'scripts', 'gds', gen);
|
|
584
|
-
if (!fsImpl.existsSync(script)) return { ok: false, ran, reason: `${gen} not found in core (${coreRoot})` };
|
|
585
|
-
const r = run(process.execPath, [script], { cwd: instanceDir, env, stdio: 'inherit' });
|
|
586
|
-
if (!r || r.error || r.status !== 0) {
|
|
587
|
-
return { ok: false, ran, reason: r && r.error ? r.error.message : `${gen} exited ${r && r.status}` };
|
|
588
|
-
}
|
|
589
|
-
ran.push(gen);
|
|
590
|
-
}
|
|
591
|
-
return { ok: true, ran };
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
// The instance's GITIGNORED WHOLE-FILE navigation docs (ADR 0110). On the monorepo these
|
|
595
|
-
// regenerate at deploy (deploy-prod.sh) + box-fetch and are gitignored so no branch ever diffs
|
|
596
|
-
// them. STANDALONE instances (ADR 0108) never got that step: their bespoke deploy regenerated
|
|
597
|
-
// nothing and init/upgrade only did the API docs, so a fresh instance shipped with an empty docs/
|
|
598
|
-
// and nothing kept the index live (task 1002314, the owner's "docs didn't transfer from primea").
|
|
599
|
-
// This is the standalone sibling of deploy-prod.sh's regen: run the generator(s) against the
|
|
600
|
-
// INSTANCE root, best-effort + LOUD — a broken generator must surface (::ERROR::) but must NEVER
|
|
601
|
-
// fail the deploy/bump (a stale nav doc is tolerable; a failed deploy is not — ADR 0110 §2
|
|
602
|
-
// "accepted staleness").
|
|
603
|
-
//
|
|
604
|
-
// SCOPE — deliberately just session-index (the one gitignored whole-file doc that is INSTANCE
|
|
605
|
-
// content). Excluded on purpose:
|
|
606
|
-
// • repo-map.md — CORE-scoped: gen-repo-map writes to resolveCoreRoot() (the platform code map),
|
|
607
|
-
// not the instance. A pure standalone instance runs core from node_modules and has ~no code of
|
|
608
|
-
// its own to map, so an instance-level repo-map is empty/meaningless; the core ships its own.
|
|
609
|
-
// (An instance-scoped map for BROWNFIELD/adopted repos is a separate enhancement — task 1001655.)
|
|
610
|
-
// • file-map.md + API docs — TRACKED/committed (seeded at init, refreshed at ship/upgrade);
|
|
611
|
-
// regenerating them on the box would perpetually dirty the tree that deploy.sh `git reset
|
|
612
|
-
// --hard`es. file-map is also HYBRID (hand-written prose + generated sections, ADR 0110) so it
|
|
613
|
-
// cannot leave git; seeding it at standup is a follow-up (needs a scaffolded file-map.md).
|
|
614
|
-
// • diagrams + atlas.json — NOT instance content: they are CORE-scoped generated assets that
|
|
615
|
-
// live under resolveCoreRoot(). They are handled separately by healDocAssets() below, because
|
|
616
|
-
// an upgrade is precisely what destroys them (task 1002441).
|
|
617
|
-
const NAV_WHOLE_FILE_GENERATORS = ['gen-session-index.js'];
|
|
618
|
-
function regenerateNavDocs({ instanceDir, coreRoot, generators = NAV_WHOLE_FILE_GENERATORS }, run = spawnSync, fsImpl = fs) {
|
|
619
|
-
const env = { ...process.env, BONGOS_INSTANCE_ROOT: instanceDir };
|
|
620
|
-
const ran = [];
|
|
621
|
-
const failed = [];
|
|
622
|
-
for (const gen of generators) {
|
|
623
|
-
const script = path.join(coreRoot, 'scripts', 'gds', gen);
|
|
624
|
-
if (!fsImpl.existsSync(script)) { failed.push({ gen, reason: `not found in core (${coreRoot})` }); continue; }
|
|
625
|
-
const r = run(process.execPath, [script], { cwd: instanceDir, env, stdio: 'inherit' });
|
|
626
|
-
if (!r || r.error || r.status !== 0) {
|
|
627
|
-
// LOUD per ADR 0110 §2 — journald greps ::ERROR:: so a broken generator alarms.
|
|
628
|
-
failed.push({ gen, reason: r && r.error ? r.error.message : `exited ${r && r.status}` });
|
|
629
|
-
continue;
|
|
630
|
-
}
|
|
631
|
-
ran.push(gen);
|
|
632
|
-
}
|
|
633
|
-
return { ok: failed.length === 0, ran, failed };
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
// The CORE-scoped generated doc assets that back two builders'-hall surfaces:
|
|
637
|
-
//
|
|
638
|
-
// modules/hall-ui/public/atlas.json → the Repo Atlas (/builders/atlas)
|
|
639
|
-
// docs/onboarding/diagrams/<n>.svg+.png → the system maps (/builders/diagrams)
|
|
640
|
-
//
|
|
641
|
-
// Both are GITIGNORED (.gitignore: `modules/hall-ui/public/atlas.json`,
|
|
642
|
-
// `docs/onboarding/diagrams/*.svg|*.png` — ADR 0122 / task 1002027) and both are generated INTO
|
|
643
|
-
// the vendored core root. So an upgrade DESTROYS them: npmInstall replaces node_modules wholesale
|
|
644
|
-
// and the new tarball carries only the .mmd sources. Nothing else puts them back —
|
|
645
|
-
// • packaging does not render (package-core.js has no render step, and the renders are
|
|
646
|
-
// gitignored so a git checkout has none to copy), and
|
|
647
|
-
// • a standalone instance's pull-deploy typically short-circuits on an unchanged git SHA, which
|
|
648
|
-
// an upgrade does not move — so its regen hooks never fire.
|
|
649
|
-
// Result before this healer: /builders/atlas 404'd and the diagrams page showed broken images
|
|
650
|
-
// after every `bongos upgrade`, until some unrelated commit happened to land (task 1002441).
|
|
651
|
-
//
|
|
652
|
-
// Both generators resolve their own output dir from __dirname, so running the CORE's copy always
|
|
653
|
-
// writes into coreRoot regardless of cwd. Idempotent by design — each asset is regenerated only
|
|
654
|
-
// when actually absent, so this is also safe to call from a deploy loop.
|
|
655
|
-
//
|
|
656
|
-
// Best-effort + LOUD, per ADR 0110 §2 "accepted staleness": a stale diagram is tolerable, a failed
|
|
657
|
-
// upgrade is not. render-diagrams.sh additionally needs a system Chromium (ADR 0122); where none is
|
|
658
|
-
// installed it fails and we say so via ::ERROR:: rather than aborting the bump.
|
|
659
|
-
const DOC_ASSET_GENERATORS = {
|
|
660
|
-
atlas: { rel: path.join('scripts', 'gds', 'gen-atlas.js'), kind: 'node' },
|
|
661
|
-
diagrams: { rel: path.join('scripts', 'render-diagrams.sh'), kind: 'bash' },
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
// Which of the two asset groups are missing under coreRoot. Exported for testing.
|
|
665
|
-
function missingDocAssets(coreRoot, fsImpl = fs) {
|
|
666
|
-
const missing = [];
|
|
667
|
-
if (!fsImpl.existsSync(path.join(coreRoot, 'modules', 'hall-ui', 'public', 'atlas.json'))) {
|
|
668
|
-
missing.push('atlas');
|
|
669
|
-
}
|
|
670
|
-
const diagramDir = path.join(coreRoot, 'docs', 'onboarding', 'diagrams');
|
|
671
|
-
let sources = [];
|
|
672
|
-
try {
|
|
673
|
-
sources = fsImpl.readdirSync(diagramDir).filter((f) => f.endsWith('.mmd'));
|
|
674
|
-
} catch { sources = []; } // no diagram dir in this core → nothing to render
|
|
675
|
-
const unrendered = sources.some((src) => {
|
|
676
|
-
const base = path.join(diagramDir, src.replace(/\.mmd$/, ''));
|
|
677
|
-
return !fsImpl.existsSync(`${base}.svg`) || !fsImpl.existsSync(`${base}.png`);
|
|
678
|
-
});
|
|
679
|
-
if (unrendered) missing.push('diagrams');
|
|
680
|
-
return missing;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
function healDocAssets({ coreRoot }, run = spawnSync, fsImpl = fs) {
|
|
684
|
-
const missing = missingDocAssets(coreRoot, fsImpl);
|
|
685
|
-
if (missing.length === 0) return { ok: true, ran: [], failed: [], missing: [] };
|
|
686
|
-
const ran = [];
|
|
687
|
-
const failed = [];
|
|
688
|
-
for (const group of missing) {
|
|
689
|
-
const gen = DOC_ASSET_GENERATORS[group];
|
|
690
|
-
const script = path.join(coreRoot, gen.rel);
|
|
691
|
-
if (!fsImpl.existsSync(script)) { failed.push({ group, reason: `not found in core (${gen.rel})` }); continue; }
|
|
692
|
-
const argv = gen.kind === 'bash' ? ['bash', [script]] : [process.execPath, [script]];
|
|
693
|
-
const r = run(argv[0], argv[1], { cwd: coreRoot, stdio: 'inherit' });
|
|
694
|
-
if (!r || r.error || r.status !== 0) {
|
|
695
|
-
failed.push({ group, reason: r && r.error ? r.error.message : `exited ${r && r.status}` });
|
|
696
|
-
continue;
|
|
697
|
-
}
|
|
698
|
-
ran.push(group);
|
|
699
|
-
}
|
|
700
|
-
return { ok: failed.length === 0, ran, failed, missing };
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
// Restart the instance service so the newly-installed core becomes the one being SERVED.
|
|
704
|
-
// Escalation is the whole difficulty here (task 1002884). A provisioned instance runs under a
|
|
705
|
-
// `User=` systemd unit, and the unattended sweep that restarts it has no TTY — so a bare
|
|
706
|
-
// `systemctl restart` from the owning (non-root) user returns "Interactive authentication
|
|
707
|
-
// required" and exits 1. We retry once through `sudo -n`, the same idiom dev.js and dev-lib.js
|
|
708
|
-
// already use for privileged systemctl; `-n` fails fast rather than blocking on a password
|
|
709
|
-
// prompt, so the retry costs nothing when it isn't permitted. An operator who prefers polkit
|
|
710
|
-
// over sudo can install the rule in docs/recipes/instance-service-restart.md instead — either
|
|
711
|
-
// path makes this return ok.
|
|
712
|
-
// `--restart-cmd` is NOT escalated: an explicit command is the operator's own contract.
|
|
713
|
-
// Trust boundary: `service` comes from `--service` or the subscription roster — operator-supplied
|
|
714
|
-
// config, never request input — and the escalation reaches only as far as the host's own sudoers
|
|
715
|
-
// or polkit scoping allows. Do not wire a caller that derives `service` from anything untrusted.
|
|
716
|
-
function restartService({ service, restartCmd }, run = spawnSync, deps = {}) {
|
|
717
|
-
if (restartCmd) {
|
|
718
|
-
const parts = restartCmd.split(/\s+/);
|
|
719
|
-
const r = run(parts[0], parts.slice(1), { stdio: 'inherit' });
|
|
720
|
-
return { ok: !!r && !r.error && r.status === 0, code: r && r.status };
|
|
721
|
-
}
|
|
722
|
-
if (service) {
|
|
723
|
-
const r = run('systemctl', ['restart', service], { stdio: 'inherit' });
|
|
724
|
-
if (r && !r.error && r.status === 0) return { ok: true, code: 0 };
|
|
725
|
-
const isRoot = deps.isRoot !== undefined
|
|
726
|
-
? !!deps.isRoot
|
|
727
|
-
: (typeof process.getuid === 'function' && process.getuid() === 0);
|
|
728
|
-
if (isRoot) return { ok: false, code: r && r.status };
|
|
729
|
-
const s = run('sudo', ['-n', 'systemctl', 'restart', service], { stdio: 'inherit' });
|
|
730
|
-
const ok = !!s && !s.error && s.status === 0;
|
|
731
|
-
return { ok, code: ok ? 0 : (s && s.status), triedSudo: true, directCode: r && r.status };
|
|
732
|
-
}
|
|
733
|
-
return { ok: null, skipped: true };
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
// The two attribution columns migration core_241 adds (task 1003159, ADR 0293 D5).
|
|
737
|
-
//
|
|
738
|
-
// APPENDED ONLY WHEN THE CALLER SUPPLIES THEM, and that conditionality is the whole
|
|
739
|
-
// design rather than laziness. An upgrade run the old way — a human at a shell — has
|
|
740
|
-
// no builder to attribute, and this file also runs ON instances whose DB may not have
|
|
741
|
-
// core_241 yet (the core upgrades itself; the migration lands during the very run that
|
|
742
|
-
// would be writing this row). Naming a column that does not exist turns a best-effort
|
|
743
|
-
// ledger append into a hard `column "source" does not exist`, so the bump would report
|
|
744
|
-
// a failure over bookkeeping. Absent → the original four-column INSERT, byte for byte.
|
|
745
|
-
//
|
|
746
|
-
// The DB is still the backstop: core_upgrades_door_needs_actor_chk refuses a row that
|
|
747
|
-
// claims source='owner-control' without an actor, so a caller that passes one and not
|
|
748
|
-
// the other is rejected there rather than landing an unattributable privileged row.
|
|
749
|
-
const LEDGER_ACTOR_COLS = Object.freeze(['requested_by_builder_id', 'source']);
|
|
750
|
-
|
|
751
|
-
// Build the INSERT for the columns this call actually has. Returns the statement plus
|
|
752
|
-
// the ordered values, so the two connection paths below cannot drift apart.
|
|
753
|
-
function ledgerInsert({ fromVersion, toVersion, sourceCommit, note, requestedByBuilderId, source }) {
|
|
754
|
-
const cols = ['from_version', 'to_version', 'source_commit', 'note'];
|
|
755
|
-
const vals = [fromVersion || null, toVersion, sourceCommit || null, note || null];
|
|
756
|
-
if (requestedByBuilderId != null || source) {
|
|
757
|
-
cols.push(...LEDGER_ACTOR_COLS);
|
|
758
|
-
vals.push(requestedByBuilderId != null ? String(requestedByBuilderId) : null, source || null);
|
|
759
|
-
}
|
|
760
|
-
const holders = cols.map((_, i) => `$${i + 1}`).join(',');
|
|
761
|
-
return { sql: `INSERT INTO core_upgrades (${cols.join(', ')}) VALUES (${holders})`, cols, vals };
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
// Best-effort append to the core_upgrades ledger (migration core_189). Resolves the DB the same two
|
|
765
|
-
// ways a consumer might connect:
|
|
766
|
-
// • DATABASE_URL — a connection string (managed / hosted shape): via the pg driver.
|
|
767
|
-
// • PGDATABASE — a bare db name over the unix socket + peer auth (the self-host shape): by
|
|
768
|
-
// delegating to `psql -d "$PGDATABASE"`, EXACTLY as migrate.sh does. We shell out rather than
|
|
769
|
-
// reuse pg here because pg defaults host to localhost/TCP, so `new Pool({database})` would miss
|
|
770
|
-
// the socket + peer auth a provisioned instance relies on (systemd sets only PGDATABASE — task
|
|
771
|
-
// 1976 found the URL-only path silently skipped on emersonian, the very shape it must serve).
|
|
772
|
-
// Skips silently only when NEITHER is present (a local rehearsal with no DB pointed at) — the bump's
|
|
773
|
-
// verification never depends on the ledger. An attempted-but-failed insert returns skipped:false so
|
|
774
|
-
// the caller can surface it (the manual-INSERT footgun from 1976), never lose it.
|
|
775
|
-
async function recordLedger({ fromVersion, toVersion, sourceCommit, note, requestedByBuilderId, source, account = null }, deps = {}) {
|
|
776
|
-
const url = deps.databaseUrl !== undefined ? deps.databaseUrl : process.env.DATABASE_URL;
|
|
777
|
-
const database = deps.pgDatabase !== undefined ? deps.pgDatabase : process.env.PGDATABASE;
|
|
778
|
-
if (!url && !database) return { ok: false, skipped: true, reason: 'no DATABASE_URL or PGDATABASE' };
|
|
779
|
-
const stmt = ledgerInsert({ fromVersion, toVersion, sourceCommit, note, requestedByBuilderId, source });
|
|
780
|
-
|
|
781
|
-
// DATABASE_URL path: the pg driver with a bound-parameter query (no psql client needed).
|
|
782
|
-
if (url) {
|
|
783
|
-
let Pool;
|
|
784
|
-
try { ({ Pool } = deps.pg || require('pg')); } catch { return { ok: false, skipped: true, reason: 'pg not resolvable' }; }
|
|
785
|
-
const pool = new Pool({ connectionString: url });
|
|
786
|
-
try {
|
|
787
|
-
await pool.query(stmt.sql, stmt.vals);
|
|
788
|
-
return { ok: true };
|
|
789
|
-
} catch (e) {
|
|
790
|
-
return { ok: false, skipped: false, reason: e.message };
|
|
791
|
-
} finally {
|
|
792
|
-
try { await pool.end(); } catch { /* ignore */ }
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
// PGDATABASE path: `psql -d <db>` lets libpq resolve the socket + peer auth (same as migrate.sh).
|
|
797
|
-
// Values pass as separate argv `--set`s (no shell → no shell injection) and reach the SQL as psql
|
|
798
|
-
// :'var' literals, which psql quotes/escapes safely (no SQL injection). NULLIF maps an absent value
|
|
799
|
-
// to NULL, matching the `x || null` of the pg path. The SQL is fed on STDIN, not `-c`: `-c` requires
|
|
800
|
-
// pure server-parseable SQL and does NOT run :'var' interpolation, so a `-c` INSERT would send the
|
|
801
|
-
// literal `:'from_version'` and syntax-error — psql only interpolates variables in script/stdin
|
|
802
|
-
// mode. ON_ERROR_STOP=1 guarantees a non-zero exit on any SQL error.
|
|
803
|
-
const run = deps.run || spawnSync;
|
|
804
|
-
// Built from the SAME column list the pg path uses, so the two cannot drift.
|
|
805
|
-
// to_version is the one NOT NULL column, hence the one without a NULLIF; the
|
|
806
|
-
// builder id is text on the wire (a psql :'var' is always a quoted literal) and
|
|
807
|
-
// casts at the DB, which also rejects anything non-numeric rather than coercing.
|
|
808
|
-
const psqlValue = (col) => {
|
|
809
|
-
if (col === 'to_version') return `:'${col}'`;
|
|
810
|
-
if (col === 'requested_by_builder_id') return `NULLIF(:'${col}','')::bigint`;
|
|
811
|
-
return `NULLIF(:'${col}','')`;
|
|
812
|
-
};
|
|
813
|
-
const sql = `${stmt.sql.slice(0, stmt.sql.indexOf(' VALUES '))}\n VALUES (${stmt.cols.map(psqlValue).join(', ')});\n`;
|
|
814
|
-
const sets = [];
|
|
815
|
-
for (let i = 0; i < stmt.cols.length; i++) sets.push('--set', `${stmt.cols[i]}=${stmt.vals[i] == null ? '' : stmt.vals[i]}`);
|
|
816
|
-
// Through the project's own account when the move is not running as it (task 1004512):
|
|
817
|
-
// this psql peer-auths exactly as migrate does, so it must be the same identity or the
|
|
818
|
-
// ledger row silently stops being written on every own-account project.
|
|
819
|
-
const q = asAccount(account, 'psql', ['-d', database, '-v', 'ON_ERROR_STOP=1', '-q', '--no-psqlrc', ...sets]);
|
|
820
|
-
const r = run(q.cmd, q.args, { encoding: 'utf8', input: sql });
|
|
821
|
-
if (!r || r.error) {
|
|
822
|
-
const enoent = !!(r && r.error && r.error.code === 'ENOENT');
|
|
823
|
-
return { ok: false, skipped: enoent, reason: enoent ? 'psql not found' : (r && r.error ? r.error.message : 'psql spawn failed') };
|
|
824
|
-
}
|
|
825
|
-
if (r.status !== 0) return { ok: false, skipped: false, reason: (r.stderr || '').trim() || `psql exit ${r.status}` };
|
|
826
|
-
return { ok: true };
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
// Poll a health URL until it returns 2xx or the time budget (~30s) elapses. After `systemctl restart`
|
|
830
|
-
// the service needs a moment to bind its port, so a single immediate GET races the restart and
|
|
831
|
-
// spuriously reports `fetch failed` (task 1976 — it bound ~1s later on the emersonian 1.15→1.16 bump).
|
|
832
|
-
// Connection-refused fails fast, so the early retries are cheap; the backoff caps at 2s.
|
|
833
|
-
async function pollHealth(healthUrl, deps = {}) {
|
|
834
|
-
const fetchImpl = deps.fetch || fetch;
|
|
835
|
-
const sleep = deps.sleep || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
836
|
-
const now = deps.now || Date.now;
|
|
837
|
-
const budgetMs = deps.healthBudgetMs != null ? deps.healthBudgetMs : 30_000;
|
|
838
|
-
const deadline = now() + budgetMs;
|
|
839
|
-
let attempt = 0;
|
|
840
|
-
let last = { ok: false, error: 'not attempted' };
|
|
841
|
-
for (;;) {
|
|
842
|
-
attempt++;
|
|
843
|
-
try {
|
|
844
|
-
const res = await fetchImpl(healthUrl, { signal: AbortSignal.timeout(5_000) });
|
|
845
|
-
if (res.ok) return { ok: true, status: res.status, attempts: attempt };
|
|
846
|
-
last = { ok: false, status: res.status };
|
|
847
|
-
} catch (e) {
|
|
848
|
-
last = { ok: false, error: e.message };
|
|
849
|
-
}
|
|
850
|
-
if (now() >= deadline) return { ...last, attempts: attempt, timedOut: true };
|
|
851
|
-
await sleep(Math.min(2_000, 500 * attempt));
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
// ---- served-version verification (task 1002884) -----------------------------
|
|
856
|
-
// The gap this closes: after a restart, `readInstalledCoreVersion` reads node_modules on DISK
|
|
857
|
-
// and `pollHealth` proves only that *something* answers the port. Neither can tell "the new core
|
|
858
|
-
// is up" from "the old core never went down" — so when a restart silently failed on
|
|
859
|
-
// cloudbongos.com (2026-08-11), the health poll hit the STILL-RUNNING old process, got 200, and
|
|
860
|
-
// the bump reported "✓ upgrade complete" while live kept serving the previous core.
|
|
861
|
-
// `/version` reports the LIVE process's coreVersion (serve-internal.js, since 1.17.2), which is
|
|
862
|
-
// the one signal that distinguishes the two.
|
|
863
|
-
|
|
864
|
-
// Default the version endpoint to /version on the same origin as the health URL. Deriving rather
|
|
865
|
-
// than demanding a second flag is deliberate: every existing call site passes only --health-url,
|
|
866
|
-
// and a check that has to be opted into is off exactly where it is needed. Returns null when the
|
|
867
|
-
// health URL isn't parseable as one.
|
|
868
|
-
function deriveVersionUrl(healthUrl) {
|
|
869
|
-
try {
|
|
870
|
-
const u = new URL(healthUrl);
|
|
871
|
-
u.pathname = '/version';
|
|
872
|
-
u.search = '';
|
|
873
|
-
u.hash = '';
|
|
874
|
-
return u.toString();
|
|
875
|
-
} catch { return null; }
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
// Poll the version endpoint until it yields a parseable coreVersion or the budget elapses. Runs
|
|
879
|
-
// AFTER the health poll, so the service is already answering and the budget is short — this is
|
|
880
|
-
// re-reading a live endpoint, not waiting for a boot. A body without a usable coreVersion is
|
|
881
|
-
// "unreadable", NOT a mismatch: the caller must be able to tell "it says the wrong version"
|
|
882
|
-
// (a real failure) from "it wouldn't tell me" (unconfirmed), because only the first is proof.
|
|
883
|
-
async function pollServedVersion(versionUrl, deps = {}) {
|
|
884
|
-
const fetchImpl = deps.fetch || fetch;
|
|
885
|
-
const sleep = deps.sleep || ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
886
|
-
const now = deps.now || Date.now;
|
|
887
|
-
// Default to the health budget when a caller (or a test) has already narrowed it, else 15s.
|
|
888
|
-
const budgetMs = deps.versionBudgetMs != null
|
|
889
|
-
? deps.versionBudgetMs
|
|
890
|
-
: (deps.healthBudgetMs != null ? deps.healthBudgetMs : 15_000);
|
|
891
|
-
// A hard attempt cap alongside the clock: `now` is injectable, and a frozen or non-advancing
|
|
892
|
-
// clock would otherwise spin this loop forever instead of giving up.
|
|
893
|
-
const maxAttempts = deps.versionMaxAttempts != null ? deps.versionMaxAttempts : 12;
|
|
894
|
-
const deadline = now() + budgetMs;
|
|
895
|
-
let attempt = 0;
|
|
896
|
-
let last = { ok: false, error: 'not attempted' };
|
|
897
|
-
for (;;) {
|
|
898
|
-
attempt++;
|
|
899
|
-
try {
|
|
900
|
-
const res = await fetchImpl(versionUrl, { signal: AbortSignal.timeout(5_000) });
|
|
901
|
-
if (!res || !res.ok) last = { ok: false, error: `status ${res && res.status}` };
|
|
902
|
-
else if (typeof res.json !== 'function') last = { ok: false, error: 'response carried no JSON body' };
|
|
903
|
-
else {
|
|
904
|
-
const body = await res.json();
|
|
905
|
-
const version = body && typeof body.coreVersion === 'string' ? body.coreVersion : null;
|
|
906
|
-
if (version) return { ok: true, version, attempts: attempt };
|
|
907
|
-
last = { ok: false, error: 'no coreVersion in the response body' };
|
|
908
|
-
}
|
|
909
|
-
} catch (e) {
|
|
910
|
-
last = { ok: false, error: e.message };
|
|
911
|
-
}
|
|
912
|
-
if (now() >= deadline || attempt >= maxAttempts) return { ...last, attempts: attempt, timedOut: true };
|
|
913
|
-
await sleep(Math.min(2_000, 500 * attempt));
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
// ---- integrity-pin verification (task 1002216 / audit H8, finding F1) --------
|
|
918
|
-
// The packager (package-core.js) computes tree_sha256 = canonicalTreeHash over the
|
|
919
|
-
// redacted core files (+ the two synthesized files) and records it, the per-file
|
|
920
|
-
// hashes, AND the exact tarball bytes' sha256 in a side-car manifest — stating a
|
|
921
|
-
// consumer "verifies an install by recomputing it from the unpacked tree." Until now
|
|
922
|
-
// nothing on the CONSUME side did: `bongos upgrade` compared only the version STRING,
|
|
923
|
-
// so a corrupted / truncated / swapped tarball whose package.json still read the target
|
|
924
|
-
// version installed silently — the R88-class failure this goal exists to prevent.
|
|
925
|
-
// The three functions below close that gap: recompute the pin over the INSTALLED tree
|
|
926
|
-
// and assert it equals the TRUSTED reference manifest.
|
|
927
|
-
|
|
928
|
-
// Read + parse a manifest JSON; null on any failure (missing / unparseable).
|
|
929
|
-
function readManifest(file, fsImpl = fs) {
|
|
930
|
-
try { return JSON.parse(fsImpl.readFileSync(file, 'utf8')); } catch { return null; }
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
// Resolve the trusted reference pin for this bump:
|
|
934
|
-
// • VENDORED — the committed side-car `vendor/<artifact>-<v>.manifest.json` (carries
|
|
935
|
-
// tree_sha256 + files[] + the exact tarball.sha256). It lives OUTSIDE the tarball
|
|
936
|
-
// (version-controlled in the consumer repo), so it detects a swapped/corrupted
|
|
937
|
-
// tarball. This is the strong reference.
|
|
938
|
-
// • REGISTRY / no side-car — the inner manifest `<core>/.bongos-core.json` that ships
|
|
939
|
-
// INSIDE the package (tree_sha256 + files[], no tarball.*). Self-referential (a full
|
|
940
|
-
// swap regenerates it too), so it is a self-consistency check backing npm's own
|
|
941
|
-
// registry integrity rather than an independent one — flagged `strong:false`.
|
|
942
|
-
function resolveReferenceManifest({ instanceDir, coreDir, targetVersion }, fsImpl = fs) {
|
|
943
|
-
const sidecarPath = path.join(instanceDir, 'vendor', `${ARTIFACT}-${targetVersion}.manifest.json`);
|
|
944
|
-
const sidecar = readManifest(sidecarPath, fsImpl);
|
|
945
|
-
if (sidecar && sidecar.tree_sha256) {
|
|
946
|
-
const tgz = path.join(instanceDir, 'vendor', `${ARTIFACT}-${targetVersion}.tgz`);
|
|
947
|
-
return { manifest: sidecar, tgzPath: fsImpl.existsSync(tgz) ? tgz : null, source: 'vendored side-car manifest', strong: true };
|
|
948
|
-
}
|
|
949
|
-
const inner = readManifest(path.join(coreDir, '.bongos-core.json'), fsImpl);
|
|
950
|
-
if (inner && inner.tree_sha256) return { manifest: inner, tgzPath: null, source: 'installed inner manifest', strong: false };
|
|
951
|
-
return { manifest: null, tgzPath: null, source: null, strong: false };
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Recompute the integrity pin over the installed core tree and compare to `referenceManifest`.
|
|
955
|
-
// Reuses the PACKAGER's exact hash functions (canonicalTreeHash / sha256hex) so the consume-side
|
|
956
|
-
// recompute can never drift from the produce-side pin. Returns { ran, ok, checks, problems };
|
|
957
|
-
// ran=false ⇒ the manifest carried no pin to check against.
|
|
958
|
-
function verifyInstalledPin({ coreDir, referenceManifest, tgzPath }, deps = {}) {
|
|
959
|
-
const fsImpl = deps.fs || fs;
|
|
960
|
-
const pc = deps.packageCore || require('./package-core');
|
|
961
|
-
const checks = [];
|
|
962
|
-
const problems = [];
|
|
963
|
-
if (!referenceManifest || !referenceManifest.tree_sha256) return { ran: false, ok: false, checks, problems };
|
|
964
|
-
|
|
965
|
-
// (a) exact tarball bytes (vendored path only) — the strongest single check: the bytes
|
|
966
|
-
// npm actually installs, hashed and compared to the externally-held pin.
|
|
967
|
-
const tb = referenceManifest.tarball;
|
|
968
|
-
if (tgzPath && tb && tb.sha256 && fsImpl.existsSync(tgzPath)) {
|
|
969
|
-
const got = pc.sha256hex(fsImpl.readFileSync(tgzPath));
|
|
970
|
-
const ok = got === tb.sha256;
|
|
971
|
-
checks.push({ name: 'tarball.sha256', ok });
|
|
972
|
-
if (!ok) problems.push(`vendored tarball bytes sha256 ${got.slice(0, 12)}… ≠ pinned ${String(tb.sha256).slice(0, 12)}…`);
|
|
973
|
-
}
|
|
974
|
-
|
|
975
|
-
// (b) installed tree — recompute each pinned file's sha256 from the unpacked tree, rebuild
|
|
976
|
-
// the canonical tree hash (using the manifest's already-canonical modes), assert equality.
|
|
977
|
-
// Catches partial extraction, post-install corruption, or a registry package that diverged.
|
|
978
|
-
const files = Array.isArray(referenceManifest.files) ? referenceManifest.files : [];
|
|
979
|
-
if (files.length) {
|
|
980
|
-
const recomputed = [];
|
|
981
|
-
let missing = 0, changed = 0;
|
|
982
|
-
for (const f of files) {
|
|
983
|
-
const abs = path.join(coreDir, ...String(f.path).split('/')); // POSIX manifest path → native
|
|
984
|
-
let buf;
|
|
985
|
-
try { buf = fsImpl.readFileSync(abs); } catch { missing++; problems.push(`missing installed file: ${f.path}`); continue; }
|
|
986
|
-
const sha = pc.sha256hex(buf);
|
|
987
|
-
if (sha !== f.sha256) { changed++; problems.push(`content mismatch: ${f.path}`); }
|
|
988
|
-
recomputed.push({ path: f.path, mode: f.mode, sha256: sha });
|
|
989
|
-
}
|
|
990
|
-
const treeHash = pc.canonicalTreeHash(recomputed);
|
|
991
|
-
const ok = missing === 0 && changed === 0 && treeHash === referenceManifest.tree_sha256;
|
|
992
|
-
checks.push({ name: 'tree_sha256', ok, missing, changed, file_count: files.length });
|
|
993
|
-
if (!ok && missing === 0 && changed === 0) {
|
|
994
|
-
problems.push(`recomputed tree_sha256 ${treeHash.slice(0, 12)}… ≠ pinned ${String(referenceManifest.tree_sha256).slice(0, 12)}…`);
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
const ran = checks.length > 0;
|
|
999
|
-
return { ran, ok: ran && problems.length === 0, checks, problems };
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// Auto-rollback (task 2149): a bump failed AFTER the pin was moved — an install or migrate
|
|
1003
|
-
// error, or a failed post-restart health check. Revert the code pin to the pre-upgrade
|
|
1004
|
-
// snapshot, reinstall the previous core, restart, re-verify health, and record the reversal
|
|
1005
|
-
// in the ledger. The DB is deliberately NOT rolled back: core migrations are additive +
|
|
1006
|
-
// forward-safe (the core migration rule), so any already-applied migration is left in place —
|
|
1007
|
-
// only the code PIN moves back. Best-effort + fail-soft: every sub-step logs but never throws,
|
|
1008
|
-
// so a rollback attempt can't itself crash the upgrader on top of the original failure.
|
|
1009
|
-
async function rollback({ instanceDir, snapshot, fromVersion, toVersion, opts }, reason, deps = {}) {
|
|
1010
|
-
const log = deps.log || console.log;
|
|
1011
|
-
const err = deps.err || console.error;
|
|
1012
|
-
const run = deps.run || spawnSync;
|
|
1013
|
-
const fsImpl = deps.fs || fs;
|
|
1014
|
-
const restore = deps.restorePin || restorePin;
|
|
1015
|
-
|
|
1016
|
-
err(` ↩ rolling back: ${reason} — reverting ${CORE_PKG} ${toVersion} → ${snapshot.prevVersion || snapshot.prevPin}`);
|
|
1017
|
-
const result = { rolledBack: true, reason, restoredPin: snapshot.prevPin, restoredVersion: snapshot.prevVersion };
|
|
1018
|
-
|
|
1019
|
-
// 1. revert the pin to the exact previous dep string.
|
|
1020
|
-
try { restore(instanceDir, snapshot.prevPin, fsImpl); result.restoreOk = true; log(` ✓ restored pin → ${snapshot.prevPin}`); }
|
|
1021
|
-
catch (e) { result.restoreOk = false; err(` ! could not restore pin (${e.message}) — package.json may need manual repair`); }
|
|
1022
|
-
|
|
1023
|
-
// 2. reinstall the previous core.
|
|
1024
|
-
const inst = npmInstall(instanceDir, run);
|
|
1025
|
-
result.reinstallOk = !!inst.ok;
|
|
1026
|
-
if (inst.ok) log(' ✓ reinstalled previous core');
|
|
1027
|
-
else err(` ! reinstall of previous core failed (${inst.reason || 'exit ' + inst.code}) — the instance may need manual repair`);
|
|
1028
|
-
|
|
1029
|
-
// 3. restart back onto the previous core.
|
|
1030
|
-
// `deps` is passed here for the same reason the forward path passes it: this restart runs
|
|
1031
|
-
// AFTER a failure, so its sudo-escalation is the one that matters most and must be testable.
|
|
1032
|
-
const rs = restartService({ service: opts.service, restartCmd: opts.restartCmd }, run, deps);
|
|
1033
|
-
if (rs.skipped) log(' • restart: no --service/--restart-cmd — restart the instance service manually to load the reverted core.');
|
|
1034
|
-
else if (!rs.ok) err(` ! restart after rollback failed (exit ${rs.code}) — restart the service manually.`);
|
|
1035
|
-
else log(` ✓ restarted ${opts.service || 'service'} on the previous core`);
|
|
1036
|
-
result.restartOk = rs.ok;
|
|
1037
|
-
|
|
1038
|
-
// 4. re-verify: installed == previous, optional health recovery.
|
|
1039
|
-
const installedNow = readInstalledCoreVersion(instanceDir, fsImpl);
|
|
1040
|
-
result.installedAfter = installedNow;
|
|
1041
|
-
log(` ${installedNow === snapshot.prevVersion ? '✓' : '!'} installed core after rollback: ${installedNow || '(unknown)'}`);
|
|
1042
|
-
if (opts.healthUrl) {
|
|
1043
|
-
const h = await pollHealth(opts.healthUrl, deps);
|
|
1044
|
-
result.healthOk = h.ok;
|
|
1045
|
-
if (h.ok) log(` ✓ health recovered ${opts.healthUrl} → ${h.status}${h.attempts > 1 ? ` (after ${h.attempts} tries)` : ''}`);
|
|
1046
|
-
else err(` ! health STILL failing after rollback (${h.error || 'status ' + h.status}) — manual intervention needed`);
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
// 5. record the reversal in the ledger via the always-present `note` column (no schema
|
|
1050
|
-
// dependency — so it records even when a FAILED migrate means a newer column wouldn't exist).
|
|
1051
|
-
const note = `rolled_back: ${reason} (attempted ${fromVersion || '(none)'} → ${toVersion}, restored ${snapshot.prevVersion || 'previous'})`;
|
|
1052
|
-
const ledger = await recordLedger({ fromVersion, toVersion, sourceCommit: opts.sourceCommit, note, requestedByBuilderId: opts.requestedBy, source: opts.upgradeSource, account: opts.migrateAs || null }, deps);
|
|
1053
|
-
if (ledger.ok) log(' ✓ recorded rollback in core_upgrades ledger');
|
|
1054
|
-
else if (ledger.skipped) log(` • rollback ledger skipped (${ledger.reason})`);
|
|
1055
|
-
else err(` ! rollback ledger insert failed (${ledger.reason}) — record it manually`);
|
|
1056
|
-
result.ledger = ledger.ok;
|
|
1057
|
-
|
|
1058
|
-
err(` ↩ rollback complete — instance restored to core ${snapshot.prevVersion || snapshot.prevPin}.`);
|
|
1059
|
-
return result;
|
|
1060
|
-
}
|
|
1061
|
-
|
|
1062
|
-
// ---- orchestration ----------------------------------------------------------
|
|
1063
|
-
|
|
1064
|
-
async function runUpgrade(opts, deps = {}) {
|
|
1065
|
-
const log = deps.log || console.log;
|
|
1066
|
-
const err = deps.err || console.error;
|
|
1067
|
-
const run = deps.run || spawnSync;
|
|
1068
|
-
const fsImpl = deps.fs || fs;
|
|
1069
|
-
const materialize = deps.materialize || require('./claude-materialize').materializeClaude;
|
|
1070
|
-
|
|
1071
|
-
const instanceDir = path.resolve(opts.instance || process.cwd());
|
|
1072
|
-
const targetVersion = opts.to;
|
|
1073
|
-
const dryRun = !!opts.dryRun;
|
|
1074
|
-
// Auto-rollback is ON by default (task 2149); `--no-rollback-on-failure` opts out (e.g. an
|
|
1075
|
-
// interactive run that wants to inspect a broken state). Programmatic callers pass false.
|
|
1076
|
-
const rollbackOnFailure = opts.rollbackOnFailure !== false;
|
|
1077
|
-
// Pre-upgrade snapshot { prevPin, prevVersion } — captured the instant the pin is moved, so a
|
|
1078
|
-
// failure AFTER that point (install / migrate / health) can revert. null until then = nothing to undo.
|
|
1079
|
-
let snapshot = null;
|
|
1080
|
-
if (!targetVersion) return { ok: false, error: 'missing --to <version> (the target core version)' };
|
|
1081
|
-
// Checked once, here, rather than at each of the three places it is spent: an account
|
|
1082
|
-
// name that reaches `sudo` is refused outright, never quoted into shape (task 1004512).
|
|
1083
|
-
if (opts.migrateAs && !ACCOUNT_RE.test(opts.migrateAs)) return { ok: false, error: `--migrate-as ${JSON.stringify(opts.migrateAs)} is not a unix account name` };
|
|
1084
|
-
|
|
1085
|
-
const pinnedVersion = readPinnedCoreVersion(instanceDir, fsImpl);
|
|
1086
|
-
const fromVersion = readInstalledCoreVersion(instanceDir, fsImpl) || pinnedVersion;
|
|
1087
|
-
const doRollback = (reason) => rollback({ instanceDir, snapshot, fromVersion, toVersion: targetVersion, opts }, reason, deps);
|
|
1088
|
-
// A --to OLDER than this instance's core is refused before anything moves, --force included
|
|
1089
|
-
// (task 1004009). Measured against the NEWER of installed and pinned — a half-applied bump
|
|
1090
|
-
// leaves them apart. rollback() restores its own pin and never comes through here.
|
|
1091
|
-
const baseline = compareSemver(pinnedVersion, fromVersion) > 0 ? pinnedVersion : fromVersion;
|
|
1092
|
-
const isDowngrade = !!(parseSemver(baseline) && parseSemver(targetVersion) && compareSemver(targetVersion, baseline) < 0);
|
|
1093
|
-
if (isDowngrade && !opts.allowDowngrade) {
|
|
1094
|
-
const newest = listAvailableVersions({ cwd: instanceDir, run }).versions.filter(isStable).sort(compareSemver).pop();
|
|
1095
|
-
return { ok: false, downgrade: true, fromVersion, toVersion: targetVersion, error: `refusing to DOWNGRADE: --to ${targetVersion} is older than this instance's core ${baseline}${pinnedVersion && pinnedVersion !== fromVersion ? ` (installed ${fromVersion}, pinned ${pinnedVersion})` : ''}; newest published: ${newest || 'unknown — the registry could not be read'}. Re-read the live version and target that; pass --allow-downgrade only if going backwards is the point (migrations are not reverted).` };
|
|
1096
|
-
}
|
|
1097
|
-
log(`bongos ${isDowngrade ? 'DOWNGRADE (--allow-downgrade; migrations are NOT reverted)' : 'upgrade'} — ${CORE_PKG}: ${fromVersion || '(none)'} → ${targetVersion}`);
|
|
1098
|
-
log(` instance: ${instanceDir}${dryRun ? ' [DRY RUN — no changes]' : ''}`);
|
|
1099
|
-
if (fromVersion === targetVersion && !opts.force) {
|
|
1100
|
-
return { ok: false, error: `already on ${targetVersion} (pass --force to re-run the steps anyway)`, fromVersion };
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
|
-
// 1. pre-flight: clean tree (so the pin change is reviewable + revertable) + module compatibility.
|
|
1104
|
-
if (!opts.force) {
|
|
1105
|
-
// Step 5's own output is not dirt (task 1004122) — but only when step 5 is going to run.
|
|
1106
|
-
const preview = opts.skipMaterialize ? () => [] : (deps.willRewrite || require('./claude-materialize').materializeWillRewrite);
|
|
1107
|
-
const tree = gitTreeClean(instanceDir, run, preview({ instanceDir, materialize }, err));
|
|
1108
|
-
// TWO DIFFERENT FAILURES, TWO DIFFERENT SENTENCES (task 1004512). git ANSWERING with a
|
|
1109
|
-
// list of dirty paths is a dirty tree, which the self-heal chain knows how to fix. git
|
|
1110
|
-
// REFUSING to answer is not: committing anything would be committing a guess. They used
|
|
1111
|
-
// to share one message, so a `dubious ownership` refusal was classified dirty_tree, the
|
|
1112
|
-
// runner committed a tree that was never dirty, and the retry failed identically.
|
|
1113
|
-
if (!tree.ok && tree.reason) return { ok: false, error: `git could not read the working tree in ${instanceDir} — the clean-tree pre-flight never ran, so this is NOT a report of uncommitted work.\n${tree.reason}` };
|
|
1114
|
-
if (!tree.ok) return { ok: false, error: `working tree not clean — commit/stash first, or pass --force.\n${tree.dirty || ''}` };
|
|
1115
|
-
if (tree.ignored) log(` • pre-flight: ignored ${tree.ignored} dirty path(s) this upgrade rewrites from the core (.claude/ materialization)${opts.commitPin ? ' — they are committed with the pin' : ' — pass --commit-pin to commit them'}.`);
|
|
1116
|
-
}
|
|
1117
|
-
const mods = preflightModules({ instanceDir, targetVersion }, deps);
|
|
1118
|
-
log(` ✓ pre-flight: ${mods.note}`);
|
|
1119
|
-
// Surface the non-blocking findings (rotted-but-disabled + deprecated modules) BEFORE the
|
|
1120
|
-
// blocking check returns, so they are reported on a refused bump too — a refusal is exactly
|
|
1121
|
-
// when someone is reading this output (ADR 0166).
|
|
1122
|
-
(deps.reportModulePreflight || reportModulePreflight)(mods, targetVersion, { log, err });
|
|
1123
|
-
if (!mods.ok) return { ok: false, error: `module pre-check failed: ${mods.blocking.map((b) => b.key).join(', ')} incompatible with core ${targetVersion}` };
|
|
1124
|
-
// Fail on the DB identity BEFORE anything moves — a wrong OS user is an operator
|
|
1125
|
-
// mistake, and finding it at migrate means unwinding a pin that never should have
|
|
1126
|
-
// been written (task 1002712).
|
|
1127
|
-
if (!opts.skipMigrate) {
|
|
1128
|
-
const { db, ran } = (deps.resolveInstanceDb || resolveInstanceDb)({ instanceDir }, run);
|
|
1129
|
-
if (ran && !db) {
|
|
1130
|
-
return { ok: false, error: 'cannot determine which database to migrate.\n'
|
|
1131
|
-
+ ' Set PGDATABASE, or give this instance a db.database in config/branding.json.\n'
|
|
1132
|
-
+ ' Checked BEFORE the pin moves: migrate runs after the install, so a name that\n'
|
|
1133
|
-
+ ' cannot be resolved there costs a full rollback to discover (task 1002755).' };
|
|
1134
|
-
}
|
|
1135
|
-
const dbId = (deps.preflightDbIdentity || preflightDbIdentity)({ instanceDir, db, account: opts.migrateAs || null }, run);
|
|
1136
|
-
if (!dbId.ok) return { ok: false, error: dbId.reason };
|
|
1137
|
-
if (dbId.skipped) log(` • db identity pre-check${db ? ` (${db})` : ''}: ${dbId.skipped}`);
|
|
1138
|
-
else log(` ✓ pre-flight: database${db ? ` "${db}"` : ''} reachable as ${opts.migrateAs || 'the current user'}`);
|
|
1139
|
-
const { note: migrateNote } = require('./upgrade-migrate').migratePlan(instanceDir); if (migrateNote) log(` ✓ pre-flight: ${migrateNote}`);
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
// 1a. artist gate (ADR 0241, task 1004205): an open review holds the pin, --force too; a downgrade never waits.
|
|
1143
|
-
const gate = isDowngrade ? { halts: [], notes: [] } : await (deps.checkArtistGate || checkArtistGate)(opts, deps);
|
|
1144
|
-
for (const n of gate.notes) err(` ! ${n.message}`);
|
|
1145
|
-
if (gate.halts.length) return { ok: false, artistGate: true, error: gate.halts.map((h) => [h.message, h.detail].filter(Boolean).join('\n ')).join('\n') };
|
|
1146
|
-
// 1b. mandatory post-restart health check (audit H9 / task 1002222, F5): a bump that restarts the
|
|
1147
|
-
// service must prove it serves, so --health-url is required up front, before anything moves.
|
|
1148
|
-
// (No managed restart ⇒ nothing is brought live here ⇒ the operator restarts + verifies.)
|
|
1149
|
-
const willRestart = !!(opts.service || opts.restartCmd);
|
|
1150
|
-
if (!dryRun && opts.healthCheck !== false && willRestart && !opts.healthUrl) {
|
|
1151
|
-
return { ok: false, error: 'post-restart health check is mandatory for a bump that restarts the service, but no --health-url was given.\n Pass --health-url <url> to confirm the new core serves after restart, or --no-health-check to explicitly skip the confirmation.' };
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
// 2. bump the pin. Two mutually exclusive core sources:
|
|
1155
|
-
// REGISTRY (--registry): pin @bongos/core@<version> and let `npm install` pull it from
|
|
1156
|
-
// the public registry. No credential — the package is public (task 1003872), which
|
|
1157
|
-
// also retired the whole resolve-a-token-and-inject-it dance (task 1002754/1003878).
|
|
1158
|
-
// VENDORED (default, --from <tgz>): copy the .tgz into vendor/ + rewrite the file: dep.
|
|
1159
|
-
if (opts.registry) {
|
|
1160
|
-
if (opts.from) return { ok: false, error: '--registry and --from are mutually exclusive (registry pull vs vendored tarball) — pick one core source' };
|
|
1161
|
-
const prevPin = dryRun ? null : writeRegistryPin(instanceDir, targetVersion, fsImpl);
|
|
1162
|
-
if (!dryRun) { snapshot = { prevPin, prevVersion: fromVersion }; }
|
|
1163
|
-
log(` ${dryRun ? '[dry-run] would pin' : '✓ pinned'} ${CORE_PKG} → ${targetVersion} (registry)${prevPin ? ` (was ${prevPin})` : ''}`);
|
|
1164
|
-
} else {
|
|
1165
|
-
let vendorRel = opts.pinPath || (opts.from ? null : vendorRelFor(targetVersion));
|
|
1166
|
-
if (opts.from) {
|
|
1167
|
-
if (!fsImpl.existsSync(opts.from)) return { ok: false, error: `--from tarball not found: ${opts.from}` };
|
|
1168
|
-
if (dryRun) { log(` [dry-run] would vendor ${opts.from} → vendor/${ARTIFACT}-${targetVersion}.tgz`); vendorRel = vendorRelFor(targetVersion); }
|
|
1169
|
-
else vendorRel = vendorTarball({ instanceDir, fromTgz: path.resolve(opts.from), targetVersion }, fsImpl);
|
|
1170
|
-
}
|
|
1171
|
-
if (!fsImpl.existsSync(path.join(instanceDir, vendorRel)) && !dryRun) {
|
|
1172
|
-
return { ok: false, error: `no core tarball at ${vendorRel} — pass --from <path-to-.tgz> to vendor it (or --registry to pull from the npm registry)` };
|
|
1173
|
-
}
|
|
1174
|
-
const prevPin = dryRun ? null : writePin(instanceDir, vendorRel, fsImpl);
|
|
1175
|
-
if (!dryRun) snapshot = { prevPin, prevVersion: fromVersion };
|
|
1176
|
-
log(` ${dryRun ? '[dry-run] would pin' : '✓ pinned'} ${CORE_PKG} → file:${vendorRel}${prevPin ? ` (was ${prevPin})` : ''}`);
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
if (dryRun) { log(` [dry-run] would: npm install → ${opts.skipMigrate ? '(no migrate)' : require('./upgrade-migrate').migratePlan(instanceDir).label} → materialize .claude → regen API docs+client → restart → verify`); return { ok: true, dryRun: true, fromVersion, toVersion: targetVersion }; }
|
|
1180
|
-
|
|
1181
|
-
// 3. npm install.
|
|
1182
|
-
const inst = npmInstall(instanceDir, run);
|
|
1183
|
-
if (!inst.ok) {
|
|
1184
|
-
const error = `npm install failed (${inst.reason || 'exit ' + inst.code})`;
|
|
1185
|
-
if (rollbackOnFailure && snapshot) {
|
|
1186
|
-
const rb = await doRollback('npm install failed');
|
|
1187
|
-
return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb };
|
|
1188
|
-
}
|
|
1189
|
-
return { ok: false, error, fromVersion };
|
|
1190
|
-
}
|
|
1191
|
-
log(' ✓ npm install');
|
|
1192
|
-
|
|
1193
|
-
// 3b. verify the integrity pin (audit H8 / task 1002216, finding F1). Recompute tree_sha256 over
|
|
1194
|
-
// the freshly-installed core (+ the exact vendored tarball bytes) and assert it matches the
|
|
1195
|
-
// TRUSTED reference manifest — the pin the packager produced but the consume side never
|
|
1196
|
-
// re-checked. Runs BEFORE migrate so a corrupted core can never touch the DB. A MISMATCH is
|
|
1197
|
-
// fatal → rollback (like an install/migrate failure). A MISSING reference (no side-car AND no
|
|
1198
|
-
// inner manifest) can't be verified: a warning by default, fatal only with --require-pin.
|
|
1199
|
-
if (!opts.skipPinVerify) {
|
|
1200
|
-
const coreDir = path.join(instanceDir, 'node_modules', CORE_PKG);
|
|
1201
|
-
const ref = (deps.resolveReferenceManifest || resolveReferenceManifest)({ instanceDir, coreDir, targetVersion }, fsImpl);
|
|
1202
|
-
if (!ref.manifest) {
|
|
1203
|
-
const msg = `integrity pin: no reference manifest (looked for vendor/${ARTIFACT}-${targetVersion}.manifest.json and ${CORE_PKG}/.bongos-core.json) — cannot verify this install`;
|
|
1204
|
-
if (opts.requirePin) {
|
|
1205
|
-
const error = `${msg} (--require-pin)`;
|
|
1206
|
-
if (rollbackOnFailure && snapshot) { const rb = await doRollback('integrity pin unverifiable (--require-pin)'); return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb }; }
|
|
1207
|
-
return { ok: false, error, fromVersion };
|
|
1208
|
-
}
|
|
1209
|
-
err(` ! ${msg} — pass --require-pin to treat this as fatal.`);
|
|
1210
|
-
} else {
|
|
1211
|
-
const pin = (deps.verifyInstalledPin || verifyInstalledPin)({ coreDir, referenceManifest: ref.manifest, tgzPath: ref.tgzPath }, deps);
|
|
1212
|
-
if (!pin.ok) {
|
|
1213
|
-
const detail = pin.problems.slice(0, 6).join('; ') + (pin.problems.length > 6 ? `; +${pin.problems.length - 6} more` : '');
|
|
1214
|
-
const error = `integrity pin MISMATCH (${ref.source}) — installed core does not match its pin: ${detail}`;
|
|
1215
|
-
if (rollbackOnFailure && snapshot) {
|
|
1216
|
-
const rb = await doRollback('integrity pin mismatch');
|
|
1217
|
-
return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb };
|
|
1218
|
-
}
|
|
1219
|
-
return { ok: false, error, fromVersion };
|
|
1220
|
-
}
|
|
1221
|
-
log(` ✓ integrity pin verified (${pin.checks.map((c) => c.name).join(' + ')}) against the ${ref.source}${ref.strong ? '' : ' — self-consistency check; npm registry integrity is the primary guard'}`);
|
|
1222
|
-
}
|
|
1223
|
-
} else log(' • integrity-pin verify skipped (--skip-pin-verify)');
|
|
1224
|
-
|
|
1225
|
-
// 4. migrate (additive core_* migrations).
|
|
1226
|
-
if (!opts.skipMigrate) {
|
|
1227
|
-
const mig = await runMigrate(instanceDir, run, opts.migrateAs || null);
|
|
1228
|
-
if (!mig.ok) {
|
|
1229
|
-
const error = migrateError(mig); // names the failing migration + its ERROR line (task 1004734)
|
|
1230
|
-
if (rollbackOnFailure && snapshot) {
|
|
1231
|
-
const rb = await doRollback('migrate failed');
|
|
1232
|
-
return { ok: false, error, fromVersion, toVersion: targetVersion, rolledBack: true, rollback: rb };
|
|
1233
|
-
}
|
|
1234
|
-
return { ok: false, error, fromVersion };
|
|
1235
|
-
}
|
|
1236
|
-
log(' ✓ migrate');
|
|
1237
|
-
} else log(' • migrate skipped (--skip-migrate)');
|
|
1238
|
-
|
|
1239
|
-
// 5. materialize .claude/ from the freshly-installed core.
|
|
1240
|
-
//
|
|
1241
|
-
// Its output rides the pin commit for the same reason the regenerated API artifacts below
|
|
1242
|
-
// do (task 1003957 extending task 1003926): left dirty, the NEXT sweep's clean-tree guard
|
|
1243
|
-
// reads `.claude/skills/*` as a human's uncommitted work and skips the instance forever —
|
|
1244
|
-
// an enrolled co-tenant that upgrades exactly once. materializeClaude reports the paths it
|
|
1245
|
-
// actually wrote or withdrew, so this is what THIS run touched, not a static list.
|
|
1246
|
-
let materializedPaths = [];
|
|
1247
|
-
if (!opts.skipMaterialize) {
|
|
1248
|
-
const coreRoot = path.join(instanceDir, 'node_modules', CORE_PKG);
|
|
1249
|
-
const sum = materialize({ coreRoot, instanceDir, log: (m) => log(m) });
|
|
1250
|
-
materializedPaths = sum.writtenPaths || [];
|
|
1251
|
-
log(` ✓ materialized .claude/ (${sum.skills} skill file(s)${sum.settings ? ', settings.json' : ''}${sum.hooks ? `, ${sum.hooks} hook(s)` : ''})`);
|
|
1252
|
-
} else log(' • materialize skipped (--skip-materialize)');
|
|
1253
|
-
|
|
1254
|
-
// 5b. regenerate the per-instance API artifacts (OpenAPI spec + typed client) from the
|
|
1255
|
-
// new core's routes — the claude-materialize sibling (task 2051). Best-effort: a regen
|
|
1256
|
-
// hiccup is a warning, not a failed bump (the next ship's fitness gate refreshes them).
|
|
1257
|
-
let regenArtifacts = [];
|
|
1258
|
-
if (!opts.skipRegenDocs) {
|
|
1259
|
-
const coreRoot = path.join(instanceDir, 'node_modules', CORE_PKG);
|
|
1260
|
-
const regen = (deps.regenerateApiArtifacts || regenerateApiArtifacts)({ instanceDir, coreRoot }, run);
|
|
1261
|
-
regenArtifacts = (regen.ran || []).flatMap((gen) => API_ARTIFACT_FILES[gen] || []);
|
|
1262
|
-
if (regen.ok) log(` ✓ regenerated API docs + typed client from core ${targetVersion}`);
|
|
1263
|
-
else err(` ! API-artifact regen incomplete (${regen.reason}) — ran ${regen.ran.join(', ') || 'nothing'}; the next ship's fitness gate refreshes them.`);
|
|
1264
|
-
// 5c. regenerate the whole-file navigation docs (repo-map + session-index) from the new
|
|
1265
|
-
// core against the instance root (ADR 0110 / task 1002314). Best-effort + LOUD: a broken
|
|
1266
|
-
// generator alarms via ::ERROR:: but never fails the bump.
|
|
1267
|
-
const nav = (deps.regenerateNavDocs || regenerateNavDocs)({ instanceDir, coreRoot }, run);
|
|
1268
|
-
if (nav.ok) log(` ✓ regenerated navigation docs (${nav.ran.join(', ')}) from core ${targetVersion}`);
|
|
1269
|
-
else err(` ! ::ERROR:: navigation-doc regen incomplete — ran ${nav.ran.join(', ') || 'nothing'}; failed ${nav.failed.map((f) => `${f.gen} (${f.reason})`).join(', ')}. The next deploy retries.`);
|
|
1270
|
-
// 5d. re-generate the CORE-scoped doc assets this very upgrade just wiped — the Repo Atlas
|
|
1271
|
-
// data file + the onboarding diagram renders (task 1002441). See healDocAssets() for why
|
|
1272
|
-
// nothing else puts them back. Best-effort + LOUD, same contract as 5c.
|
|
1273
|
-
const heal = (deps.healDocAssets || healDocAssets)({ coreRoot }, run);
|
|
1274
|
-
if (heal.missing.length === 0) log(' • doc assets intact (atlas + diagram renders present)');
|
|
1275
|
-
else if (heal.ok) log(` ✓ regenerated doc assets wiped by the core swap (${heal.ran.join(', ')})`);
|
|
1276
|
-
else err(` ! ::ERROR:: doc-asset regen incomplete — ran ${heal.ran.join(', ') || 'nothing'}; failed ${heal.failed.map((f) => `${f.group} (${f.reason})`).join(', ')}. /builders/atlas or /builders/diagrams may be broken until the next deploy retries (diagram renders need a system Chromium — ADR 0122).`);
|
|
1277
|
-
} else log(' • doc regen skipped (--skip-regen-docs)');
|
|
1278
|
-
|
|
1279
|
-
// 6. restart.
|
|
1280
|
-
const rs = restartService({ service: opts.service, restartCmd: opts.restartCmd }, run, deps);
|
|
1281
|
-
if (rs.skipped) log(` • restart: no --service/--restart-cmd — restart the instance service to load core ${targetVersion}.`);
|
|
1282
|
-
else if (!rs.ok) {
|
|
1283
|
-
// task 1002884: a failed restart is an upgrade FAILURE, not a warning. It used to fall
|
|
1284
|
-
// through to the verification below — but the old process is still up, so the health poll
|
|
1285
|
-
// passed against IT and the bump wrote a success row for a core that was never served.
|
|
1286
|
-
// Failing here also keeps disk and process consistent: rollback puts the previous core back
|
|
1287
|
-
// on disk, matching the one that is (still) running.
|
|
1288
|
-
err(` ✖ restart failed (exit ${rs.code})${rs.triedSudo ? ` — \`systemctl restart\` exited ${rs.directCode} and \`sudo -n systemctl restart\` could not escalate` : ''}`);
|
|
1289
|
-
err(' The previous core is still the one being served. Grant the restart a non-interactive path');
|
|
1290
|
-
err(' (sudoers NOPASSWD or a polkit rule — docs/recipes/instance-service-restart.md), then re-run.');
|
|
1291
|
-
const error = `restart failed (exit ${rs.code}) — the instance is still running the previous core`;
|
|
1292
|
-
if (rollbackOnFailure && snapshot) {
|
|
1293
|
-
const rb = await doRollback('restart failed');
|
|
1294
|
-
return { ok: false, error, fromVersion, toVersion: targetVersion, restartOk: false, rolledBack: true, rollback: rb };
|
|
1295
|
-
}
|
|
1296
|
-
return { ok: false, error, fromVersion, toVersion: targetVersion, restartOk: false };
|
|
1297
|
-
} else log(` ✓ restarted ${opts.service || 'service'}${rs.triedSudo ? ' (via sudo -n)' : ''}`);
|
|
1298
|
-
|
|
1299
|
-
// 7. verify: installed version == target, optional /healthz, ledger row.
|
|
1300
|
-
const installed = readInstalledCoreVersion(instanceDir, fsImpl);
|
|
1301
|
-
const versionOk = installed === targetVersion;
|
|
1302
|
-
log(` ${versionOk ? '✓' : '!'} installed core version: ${installed || '(unknown)'}${versionOk ? '' : ` (expected ${targetVersion})`}`);
|
|
1303
|
-
|
|
1304
|
-
let healthOk = null;
|
|
1305
|
-
if (opts.healthCheck === false) {
|
|
1306
|
-
log(' • post-restart health check skipped (--no-health-check) — success is NOT confirmed against a live endpoint.');
|
|
1307
|
-
} else if (opts.healthUrl) {
|
|
1308
|
-
const h = await pollHealth(opts.healthUrl, deps);
|
|
1309
|
-
healthOk = h.ok;
|
|
1310
|
-
if (h.ok) log(` ✓ health ${opts.healthUrl} → ${h.status}${h.attempts > 1 ? ` (ready after ${h.attempts} tries)` : ''}`);
|
|
1311
|
-
else err(` ! health ${opts.healthUrl} not ready after ${h.attempts} tr${h.attempts === 1 ? 'y' : 'ies'}: ${h.error || 'status ' + h.status}`);
|
|
1312
|
-
} else {
|
|
1313
|
-
// Reachable only when NO managed restart happened (pre-flight requires a URL when one will):
|
|
1314
|
-
// the new core is installed on disk, but nothing was brought live here to health-check.
|
|
1315
|
-
log(' • no managed restart + no --health-url — new core installed but not confirmed serving; restart the instance + health-check it to complete the bump.');
|
|
1316
|
-
}
|
|
1317
|
-
|
|
1318
|
-
// 7b. Confirm the SERVED process actually swapped (task 1002884). Everything above this point
|
|
1319
|
-
// can be true of an instance still running the OLD core: the pin moved, node_modules holds
|
|
1320
|
-
// the new version, and /healthz answers 200 — from the process that never went down. Only
|
|
1321
|
-
// the live coreVersion settles it.
|
|
1322
|
-
// A definite MISMATCH is a failure (same treatment as a failed health check). Being unable
|
|
1323
|
-
// to READ the endpoint is not: an instance may not expose it, and refusing every such bump
|
|
1324
|
-
// would be a worse regression than the false-pass. Passing --version-url explicitly asks
|
|
1325
|
-
// for proof, so there an unreadable endpoint IS a failure.
|
|
1326
|
-
let servedOk = null;
|
|
1327
|
-
let servedVersion = null;
|
|
1328
|
-
const versionUrl = opts.versionUrl || (opts.healthUrl ? deriveVersionUrl(opts.healthUrl) : null);
|
|
1329
|
-
if (opts.healthCheck === false) {
|
|
1330
|
-
// --no-health-check is the documented opt-out from confirming the bump serves; it covers this.
|
|
1331
|
-
} else if (versionUrl && healthOk !== false) {
|
|
1332
|
-
const sv = await pollServedVersion(versionUrl, deps);
|
|
1333
|
-
servedVersion = sv.version || null;
|
|
1334
|
-
if (sv.ok && servedVersion === targetVersion) {
|
|
1335
|
-
servedOk = true;
|
|
1336
|
-
log(` ✓ served core version: ${servedVersion} (${versionUrl})`);
|
|
1337
|
-
} else if (sv.ok) {
|
|
1338
|
-
servedOk = false;
|
|
1339
|
-
err(` ! served core version is ${servedVersion} but the target is ${targetVersion} (${versionUrl})`);
|
|
1340
|
-
err(' The restart did not swap the running process — the old core is still being served.');
|
|
1341
|
-
} else if (opts.versionUrl) {
|
|
1342
|
-
servedOk = false;
|
|
1343
|
-
err(` ! could not read ${versionUrl} (${sv.error}) — --version-url was given, so this bump cannot be confirmed`);
|
|
1344
|
-
} else {
|
|
1345
|
-
err(` ! could not read ${versionUrl} (${sv.error}) — this bump is NOT confirmed against the running process.`);
|
|
1346
|
-
err(' Pass --version-url <url> if this instance serves its version elsewhere.');
|
|
1347
|
-
}
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
// A failed post-restart health check means the new core is serving broken — auto-revert (task
|
|
1351
|
-
// 2149) instead of recording an "upgraded" row + leaving the instance down. A served-version
|
|
1352
|
-
// mismatch means the new core is not serving AT ALL (task 1002884) and reverts the same way.
|
|
1353
|
-
// The rollback writes its OWN rolled_back ledger row, so skip the normal insert below.
|
|
1354
|
-
if ((healthOk === false || servedOk === false) && rollbackOnFailure && snapshot) {
|
|
1355
|
-
const why = healthOk === false
|
|
1356
|
-
? 'health check failed after restart'
|
|
1357
|
-
: `served core version did not change (still ${servedVersion || 'unconfirmed'}, expected ${targetVersion})`;
|
|
1358
|
-
const rb = await doRollback(why);
|
|
1359
|
-
return { ok: false, error: `${why} — rolled back to ${snapshot.prevVersion || 'the previous core'}`, fromVersion, toVersion: targetVersion, versionOk, healthOk, servedOk, servedVersion, rolledBack: true, rollback: rb };
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
const ledger = await recordLedger({ fromVersion, toVersion: targetVersion, sourceCommit: opts.sourceCommit, note: isDowngrade ? `downgrade (--allow-downgrade)${opts.note ? `: ${opts.note}` : ''}` : opts.note, requestedByBuilderId: opts.requestedBy, source: opts.upgradeSource, account: opts.migrateAs || null }, deps);
|
|
1363
|
-
if (ledger.ok) log(' ✓ recorded in core_upgrades ledger');
|
|
1364
|
-
else if (ledger.skipped) log(` • ledger skipped (${ledger.reason})`);
|
|
1365
|
-
else err(` ! ledger insert failed (${ledger.reason}) — record it manually`);
|
|
1366
|
-
|
|
1367
|
-
// Make the pin durable (task 1002712). Runs LAST — only a verified-healthy
|
|
1368
|
-
// upgrade is worth persisting, and a rolled-back one returned long before here.
|
|
1369
|
-
//
|
|
1370
|
-
// Which credential the push rides is resolved HERE (async — it may read the token
|
|
1371
|
-
// store) and handed to persistPin, which stays synchronous — and only when a push is
|
|
1372
|
-
// actually coming: --commit-pin without --pin-no-push, not a dry run, and the pin
|
|
1373
|
-
// files really are dirty. Nothing reads a credential it will not spend (task 1003081).
|
|
1374
|
-
const remote = opts.gitRemote || 'origin';
|
|
1375
|
-
// Everything this upgrade generated into the instance's tree: the API artifacts (task
|
|
1376
|
-
// 1003926) and the materialized .claude/ (task 1003957). One list, so the push-credential
|
|
1377
|
-
// probe and persistPin agree on what "the upgrade rewrote" means.
|
|
1378
|
-
const generatedPaths = [...regenArtifacts, ...materializedPaths];
|
|
1379
|
-
const pushComing = !!opts.commitPin && !opts.pinNoPush && !dryRun
|
|
1380
|
-
&& ((dirtyPinFiles(instanceDir, run) || []).length > 0 || (dirtyPaths(instanceDir, generatedPaths, run) || []).length > 0);
|
|
1381
|
-
const pushAuth = pushComing ? await (deps.resolvePinPushAuth || resolvePinPushAuth)({ instanceDir, remote }, deps, run) : null;
|
|
1382
|
-
const pin = persistPin({
|
|
1383
|
-
instanceDir,
|
|
1384
|
-
fromVersion,
|
|
1385
|
-
toVersion: targetVersion,
|
|
1386
|
-
commitPin: opts.commitPin,
|
|
1387
|
-
push: !opts.pinNoPush,
|
|
1388
|
-
remote,
|
|
1389
|
-
dryRun: opts.dryRun,
|
|
1390
|
-
pushAuth,
|
|
1391
|
-
artifactPaths: generatedPaths,
|
|
1392
|
-
}, run, log, err);
|
|
1393
|
-
|
|
1394
|
-
const ok = versionOk && healthOk !== false && servedOk !== false;
|
|
1395
|
-
log(ok ? `\n✓ ${isDowngrade ? 'downgrade' : 'upgrade'} complete — core ${fromVersion || '(none)'} → ${targetVersion}` : `\n! ${isDowngrade ? 'downgrade' : 'upgrade'} finished with warnings — review above.`);
|
|
1396
|
-
return { ok, fromVersion, toVersion: targetVersion, versionOk, healthOk, servedOk, servedVersion, ledger: ledger.ok, pin };
|
|
1397
|
-
}
|
|
1398
|
-
|
|
1399
|
-
function parseArgs(argv) {
|
|
1400
|
-
return {
|
|
1401
|
-
to: arg('--to', argv),
|
|
1402
|
-
from: arg('--from', argv),
|
|
1403
|
-
instance: arg('--instance', argv),
|
|
1404
|
-
service: arg('--service', argv),
|
|
1405
|
-
restartCmd: arg('--restart-cmd', argv),
|
|
1406
|
-
healthUrl: arg('--health-url', argv),
|
|
1407
|
-
versionUrl: arg('--version-url', argv), // task 1002884: read the LIVE process's coreVersion back; defaults to /version on the health URL's origin
|
|
1408
|
-
sourceCommit: arg('--source-commit', argv),
|
|
1409
|
-
note: arg('--note', argv),
|
|
1410
|
-
// Who asked, and through which door (task 1003159, ADR 0293 D5). Both reach the
|
|
1411
|
-
// core_upgrades row so a privileged act that an HTTP request could start is never
|
|
1412
|
-
// unattributable. Omitted on a hand-run bump, where there is no builder to name —
|
|
1413
|
-
// and omitting them keeps the INSERT on its pre-core_241 four columns, which is
|
|
1414
|
-
// what lets this script keep working against an instance DB that has not migrated.
|
|
1415
|
-
requestedBy: arg('--requested-by', argv),
|
|
1416
|
-
upgradeSource: arg('--upgrade-source', argv),
|
|
1417
|
-
pinPath: arg('--pin-path', argv),
|
|
1418
|
-
migrateAs: arg('--migrate-as', argv), // task 1004512: the project's own unix+Postgres identity, when the move itself runs as the account that owns the checkout
|
|
1419
|
-
registry: hasFlag('--registry', argv), // ADR 0134: pull @bongos/core@<--to> from the npm registry instead of vendoring
|
|
1420
|
-
commitPin: hasFlag('--commit-pin', argv), // task 1002712: commit + push package.json/-lock so a pull-deploy reset cannot revert the bump
|
|
1421
|
-
pinNoPush: hasFlag('--pin-no-push', argv), // task 1003521 co-tenant mode: commit the pin but leave the customer's own remote alone
|
|
1422
|
-
gitRemote: arg('--git-remote', argv),
|
|
1423
|
-
dryRun: hasFlag('--dry-run', argv),
|
|
1424
|
-
force: hasFlag('--force', argv),
|
|
1425
|
-
allowDowngrade: hasFlag('--allow-downgrade', argv), // task 1004009: a --to older than the installed core is refused without it
|
|
1426
|
-
skipMigrate: hasFlag('--skip-migrate', argv),
|
|
1427
|
-
skipMaterialize: hasFlag('--skip-materialize', argv),
|
|
1428
|
-
skipRegenDocs: hasFlag('--skip-regen-docs', argv),
|
|
1429
|
-
skipPinVerify: hasFlag('--skip-pin-verify', argv), // task 1002216: opt out of the post-install integrity-pin check
|
|
1430
|
-
requirePin: hasFlag('--require-pin', argv), // task 1002216: a MISSING reference manifest becomes fatal (not just a warning)
|
|
1431
|
-
healthCheck: !hasFlag('--no-health-check', argv), // task 1002222: post-restart health check is ON by default (finding F5/H9)
|
|
1432
|
-
rollbackOnFailure: !hasFlag('--no-rollback-on-failure', argv), // ADR 0100: auto-revert ON by default (task 2149)
|
|
1433
|
-
};
|
|
1434
|
-
}
|
|
1435
|
-
|
|
1436
|
-
async function main(argv = process.argv.slice(2), deps = {}) {
|
|
1437
|
-
if (hasFlag('--help', argv) || argv[0] === 'help') {
|
|
1438
|
-
process.stdout.write([
|
|
1439
|
-
'bongos upgrade — pull the Cloud Bongos core to a new version and apply it.',
|
|
1440
|
-
'',
|
|
1441
|
-
'Usage:',
|
|
1442
|
-
' bongos upgrade module-compat pre-check against the installed core (exit 1 on mismatch)',
|
|
1443
|
-
' bongos upgrade --to <version> [opts] pull + apply a new core version (the real channel)',
|
|
1444
|
-
'',
|
|
1445
|
-
' --to <version> target core version — presence switches from pre-check to a real bump',
|
|
1446
|
-
' --from <tgz> source tarball to vendor into vendor/ (else it must already be there)',
|
|
1447
|
-
' --registry pull @bongos/core@<--to> from the public npm registry (no token needed) instead of vendoring a tarball; mutually exclusive with --from',
|
|
1448
|
-
' --instance <dir> consumer instance root (default: cwd)',
|
|
1449
|
-
' --service <unit> systemd unit to restart after applying',
|
|
1450
|
-
' --restart-cmd <cmd> explicit restart command (overrides --service)',
|
|
1451
|
-
' --health-url <url> poll after restart (~30s) until 2xx; a still-failing check fails the bump (auto-rollback). REQUIRED for a bump that restarts the service, unless --no-health-check.',
|
|
1452
|
-
' --version-url <url> read the RUNNING process\'s coreVersion back after restart and require it to equal --to (default: /version on the --health-url origin). A health check alone cannot tell a new core from an old one that never went down.',
|
|
1453
|
-
' --no-health-check explicitly skip the post-restart health confirmation (default: the check is ON — a bump that restarts the service must confirm it serves); also skips the served-version read-back',
|
|
1454
|
-
' --skip-migrate do not migrate (the default runs the instance\'s `migrate` script, else the core\'s scripts/migrate.sh)',
|
|
1455
|
-
' --migrate-as <user> run the database steps (identity pre-check, migrate, ledger) as this unix account via `sudo -n -u`. For a project whose checkout is owned by the operator account but whose database is owned by its OWN account: the move needs the first to write files, the migration needs the second to connect. Omit it and everything runs as the current user, as before',
|
|
1456
|
-
' --skip-materialize do not refresh .claude/',
|
|
1457
|
-
' --skip-regen-docs do not regenerate the OpenAPI spec + typed client from the new core',
|
|
1458
|
-
' --skip-pin-verify do not verify the integrity pin (tree_sha256 + tarball.sha256) after install',
|
|
1459
|
-
' --require-pin treat a MISSING reference manifest as fatal (default: warn — a MISMATCH is always fatal)',
|
|
1460
|
-
' --no-rollback-on-failure do NOT auto-revert on a failed install/pin-verify/migrate/health check (default: auto-rollback ON)',
|
|
1461
|
-
' --commit-pin commit + push package.json/-lock so a pull-deploy reset cannot revert the bump. The push rides the instance owner\'s stored GitHub token when one is stored (a PRIVATE instance\'s origin is a read-only deploy key — ADR 0176), else the git remote (--git-remote <name>, default origin); a refused push is reported loudly with the fix',
|
|
1462
|
-
' --pin-no-push with --commit-pin: commit the pin but do NOT push it — for an instance whose repo of record is not ours (a provisioned co-tenant), where the push is wrong and no pull-deploy can revert the commit',
|
|
1463
|
-
' --dry-run show the plan; change nothing',
|
|
1464
|
-
' --force skip the clean-tree + same-version guards (NOT the downgrade guard)',
|
|
1465
|
-
' --allow-downgrade let --to be OLDER than the installed core. Refused otherwise: a stale --to silently takes merged work off a live instance, and migrations are never reverted, so the DB stays ahead of the code',
|
|
1466
|
-
'',
|
|
1467
|
-
'Full bump: pre-flight → bump pin → npm install → verify integrity pin → migrate → materialize .claude → regen API docs+client → restart → verify (health + served coreVersion).',
|
|
1468
|
-
'On a failure after the pin moves (install/pin-verify/migrate/restart/health/served-version), auto-rollback reverts the pin, reinstalls the previous core, restarts, and records a rolled_back ledger row (the DB is not rolled back — core migrations are additive).',
|
|
1469
|
-
].join('\n') + '\n');
|
|
1470
|
-
return 0;
|
|
1471
|
-
}
|
|
1472
|
-
// Backward-compatible: bare `bongos upgrade` (no --to) is the documented module-compatibility
|
|
1473
|
-
// PRE-CHECK — unchanged behavior, delegated to the module CLI. A --to switches to the real channel.
|
|
1474
|
-
// task 1004596: from a builder's own checkout of a project its OPERATOR runs, say who upgrades it and how to ask — never a server-only refusal (upgrade-operator.js has the condition).
|
|
1475
|
-
const operatorMsg = await (deps.operatorNotice || require('./upgrade-operator').operatorNotice)({ instanceDir: path.resolve(arg('--instance', argv) || process.cwd()) });
|
|
1476
|
-
if (!arg('--to', argv)) {
|
|
1477
|
-
const code = await (deps.cmdUpgrade || require('./module').cmdUpgrade)(argv);
|
|
1478
|
-
if (operatorMsg) process.stdout.write(`\n${operatorMsg}`);
|
|
1479
|
-
return code;
|
|
1480
|
-
}
|
|
1481
|
-
if (operatorMsg) { process.stderr.write(`\n✖ ${operatorMsg}`); return 1; }
|
|
1482
|
-
const res = await runUpgrade(parseArgs(argv));
|
|
1483
|
-
if (!res.ok) { process.stderr.write(`\n✖ ${res.error || 'upgrade failed'}\n${require('./upgrade-outcome').outcomeLine(res)}`); return 1; } // the outcome line: how far it got, for the deploy runner (task 1004446)
|
|
1484
|
-
return 0;
|
|
1485
|
-
}
|
|
1486
|
-
|
|
1487
|
-
module.exports = {
|
|
1488
|
-
runUpgrade, parseArgs, main, rollback,
|
|
1489
|
-
versionFromTgzPath, versionFromDep, readInstalledCoreVersion, readPinnedCoreVersion, writePin, writeRegistryPin, restorePin,
|
|
1490
|
-
vendorTarball, gitTreeClean, persistPin, dirtyPinFiles, gitCurrentBranch, PIN_FILES, API_ARTIFACT_FILES, preflightDbIdentity,
|
|
1491
|
-
resolveInstanceDb, preflightModules, reportModulePreflight, npmInstall, runMigrate, regenerateApiArtifacts, regenerateNavDocs, NAV_WHOLE_FILE_GENERATORS, missingDocAssets, healDocAssets, DOC_ASSET_GENERATORS, restartService, recordLedger, ledgerInsert, LEDGER_ACTOR_COLS, pollHealth, deriveVersionUrl, pollServedVersion,
|
|
1492
|
-
resolvePinPushAuth, remoteGithubRepo, pinnedDeployKey, // task 1003081 — the pin push prefers the owner's GitHub token (ADR 0176 follow-up)
|
|
1493
|
-
readManifest, resolveReferenceManifest, verifyInstalledPin, // task 1002216 (audit H8) — integrity-pin verification
|
|
1494
|
-
CORE_PKG, ARTIFACT, vendorRelFor,
|
|
1495
|
-
};
|
|
1496
|
-
|
|
1497
|
-
if (require.main === module) {
|
|
1498
|
-
main().then((code) => { if (arg('--to', process.argv.slice(2))) process.exit(code); else process.exitCode = code; }).catch((e) => { process.stderr.write(`✖ ${e.message}\n`); process.exit(1); }); // bare pre-check drains: it ends after a store read (task 1003787), and a hard exit there aborts on Windows (task 1002734)
|
|
1499
|
-
}
|
|
7
|
+
require('../bongos/forwarder.js').forward(module, 'upgrade.js');
|