@bongos/core 1.21.175 → 1.21.177
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 +3371 -1636
- 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 +15 -15
- 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/config/scheduled-routines.json +3 -8
- package/docs/adr/0029-status-page-outage-resilience.md +6 -6
- package/docs/adr/0091-bounding-the-kernel-and-db-carve.md +1 -1
- package/docs/adr/0093-tranche-2-core-carve-sequence.md +1 -1
- 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 +4 -4
- 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 +92 -96
- package/docs/handoff-template.md +5 -5
- package/docs/module-api-changelog.md +7 -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 +5 -9
- package/docs/onboarding/diagrams/assertions.json +3 -3
- 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 +13 -10
- 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/db.js +9 -0
- package/modules/autonomy/gauge.js +1 -1
- package/modules/autonomy/governor-docket.js +2 -2
- package/modules/autonomy/module.json +2 -2
- package/modules/autonomy/pollers/daily-checks.js +262 -0
- package/modules/autonomy/pollers/routine-timers.js +9 -7
- package/modules/autonomy/rank-roles.js +64 -0
- package/modules/autonomy/routes/autonomy.js +3 -3
- package/modules/autonomy/server-schedule.js +37 -0
- 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 +186 -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/ask.js +231 -0
- package/scripts/bongos/audit-authorship.sh +324 -0
- package/scripts/bongos/audit-deps.js +189 -0
- package/scripts/bongos/audit-gate.js +121 -0
- package/scripts/bongos/audit-rank-roles.js +131 -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/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 +380 -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/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 +332 -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.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 +338 -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-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 +318 -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-bongos-v1-idea-routing-tasks.js +152 -0
- package/scripts/bongos/seed-bv1-profiles-tasks.js +135 -0
- package/scripts/bongos/seed-bv2-ideator-surface-tasks.js +390 -0
- package/scripts/bongos/seed-staging.sh +117 -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 +291 -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/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/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/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/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.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-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-bongos-v1-idea-routing-tasks.js +6 -152
- package/scripts/gds/seed-bv1-profiles-tasks.js +6 -135
- package/scripts/gds/seed-bv2-ideator-surface-tasks.js +6 -390
- package/scripts/gds/seed-staging.sh +7 -116
- 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/security/dep-audit.js +50 -124
- 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/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_daily_checks.mjs +201 -0
- 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 +68 -68
- 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 +1 -19
- 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_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/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 +24 -13
- package/tests/routine_schedule.mjs +51 -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/artifact-staleness.js +0 -267
- package/scripts/gds/audit-exports.js +0 -128
- package/scripts/gds/classify-discipline.js +0 -153
- package/scripts/gds/codemod-rename-src-gds.js +0 -145
- package/scripts/gds/coreB-carve-proof.sh +0 -725
- package/scripts/gds/goal-names.js +0 -102
- package/scripts/gds/rename-goals.js +0 -109
- package/scripts/gds/resolve-skill-model.js +0 -57
- package/scripts/gds/security-baseline-check.sh +0 -180
- package/scripts/gds/seed-adopt-tasks.js +0 -75
- package/scripts/gds/seed-api-contract-goal-tasks.js +0 -141
- package/scripts/gds/seed-board-room-tasks.js +0 -196
- package/scripts/gds/seed-bongos-app-tasks.js +0 -294
- package/scripts/gds/seed-bongos-coreB-tranche1-tasks.js +0 -81
- package/scripts/gds/seed-bongos-coreB-tranche2-tasks.js +0 -100
- package/scripts/gds/seed-bongos-goal-hierarchy-tasks.js +0 -307
- package/scripts/gds/seed-bongos-module-system-tasks.js +0 -346
- package/scripts/gds/seed-bongos-tasks.js +0 -214
- package/scripts/gds/seed-bongos-v1-seo-tasks.js +0 -104
- package/scripts/gds/seed-bv1-community-tasks.js +0 -131
- package/scripts/gds/seed-bv1-privacy-invites-tasks.js +0 -133
- package/scripts/gds/seed-bv1-profile-page-tasks.js +0 -161
- package/scripts/gds/seed-bv1-r01-delta-tasks.js +0 -154
- package/scripts/gds/seed-bv2-tweak-mode-tasks.js +0 -277
- package/scripts/gds/seed-gds-v4-tasks.js +0 -624
- package/scripts/gds/seed-governance-tasks.js +0 -340
- package/scripts/gds/seed-medusa-tasks.js +0 -440
- package/scripts/gds/seed-module-upstreaming.js +0 -219
- package/scripts/gds/seed-productization-tasks.js +0 -235
- package/scripts/gds/seed-project-admin-console-tasks.js +0 -88
- package/scripts/gds/seed-provisioning-tasks.js +0 -83
- package/scripts/gds/seed-strict-versioning-tasks.js +0 -275
- package/scripts/gds/seed-v2-world-tasks.js +0 -283
- package/scripts/gds/spike-task-map.js +0 -899
- package/scripts/migrate_cost_ledger.sh +0 -178
- package/scripts/status-probe.mjs +0 -213
- package/src/bongos/uptime-poller.js +0 -93
- package/tests/artifact_staleness.mjs +0 -207
- package/tests/goal_names.mjs +0 -120
- package/tests/government_fold_bench.mjs +0 -50
- package/tests/spike_task_map.mjs +0 -422
- package/tests/status-probe.mjs +0 -89
- /package/{scripts/gds → modules/raise}/CLAUDE.md +0 -0
- /package/scripts/{gds → bongos}/cloudbongos-token-isolation.conf +0 -0
- /package/scripts/{gds → bongos}/discipline-modes.json +0 -0
- /package/scripts/{gds → bongos}/retired-core-skills.json +0 -0
|
@@ -0,0 +1,1361 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
// scripts/bongos/autobongos-run.js — the Autobongos supervisor's I/O half
|
|
5
|
+
// (task 1003902; ADR 0307 §1). One iteration is: gauge → pick → claim → spawn
|
|
6
|
+
// one worker → record. No model anywhere in this file; every branch it takes is
|
|
7
|
+
// a branch a person can read.
|
|
8
|
+
//
|
|
9
|
+
// node scripts/bongos/autobongos-run.js --forever # the daemon shape
|
|
10
|
+
// node scripts/bongos/autobongos-run.js # one task, then exit
|
|
11
|
+
// node scripts/bongos/autobongos-run.js --goal 1000119 # narrow to one goal
|
|
12
|
+
// node scripts/bongos/autobongos-run.js --max-tasks 5
|
|
13
|
+
// node scripts/bongos/autobongos-run.js --dry-run # pick only, claim nothing
|
|
14
|
+
//
|
|
15
|
+
// SCOPE, and what deliberately is NOT here:
|
|
16
|
+
// · the ALLOWLIST and the KILL SWITCH live in the DATABASE (task 1003908,
|
|
17
|
+
// ADR 0307 §6) and are read at step 0 of every iteration. `--goal` and
|
|
18
|
+
// AUTOBONGOS_GOALS survive only as a NARROWING filter — they can take goals
|
|
19
|
+
// away from the allowlist and can never add one, because a setting on this
|
|
20
|
+
// machine is editable by the very bypassPermissions worker the allowlist
|
|
21
|
+
// exists to bound.
|
|
22
|
+
// · CONFIRMING a ship against the ledger is autobongos-verify.js (task
|
|
23
|
+
// 1003903), called at step 6. A worker's `shipped` is a CLAIM — recorded here
|
|
24
|
+
// as `claims_shipped` so nothing can mistake it for a fact — and the ledger
|
|
25
|
+
// read is what turns it into one, or into a released claim and a blocker.
|
|
26
|
+
// · IT NEVER TERMINALLY STOPS (task 1003904, ADR 0307 §5). `--forever` runs the
|
|
27
|
+
// cadence state machine in modules/autonomy/cadence.js: three straight
|
|
28
|
+
// failures drop to `probing` — no new work, a growing wait, then exactly one
|
|
29
|
+
// task — and one success returns it to full speed. Nothing exits the loop,
|
|
30
|
+
// not even the kill switch, because a machine nobody is watching must not be
|
|
31
|
+
// able to look idle while it is dead. On boot it reconciles every claim a
|
|
32
|
+
// previous run left behind.
|
|
33
|
+
|
|
34
|
+
const { execFile, spawn } = require('node:child_process');
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const path = require('node:path');
|
|
37
|
+
const crypto = require('node:crypto');
|
|
38
|
+
const os = require('node:os');
|
|
39
|
+
|
|
40
|
+
const seq = require('./sequence.js');
|
|
41
|
+
const gauge = require('./autonomy-gauge.js');
|
|
42
|
+
const {
|
|
43
|
+
buildWorkerArgs, buildWorkerPrompt, classifyRun, usageLimitHit, DEFAULT_MODEL, DEFAULT_WORKER_TIMEOUT_MS,
|
|
44
|
+
} = require('./autobongos-loop.js');
|
|
45
|
+
const { verifyShip, failureReason, recheckLands, pendingLandIds } = require('./autobongos-verify.js');
|
|
46
|
+
const gradeCap = require('./autobongos-grade-cap.js');
|
|
47
|
+
const fence = require('../../modules/autonomy/fence.js');
|
|
48
|
+
const tweakLane = require('./autobongos-tweak-lane.js');
|
|
49
|
+
// The ADR 0043 protected-path registry, reused rather than re-derived. scripts/
|
|
50
|
+
// sits outside the kernel boundary, so this direct core require is allowed here
|
|
51
|
+
// where it would not be inside modules/ — which is exactly why fence.js takes the
|
|
52
|
+
// hits as an argument instead of matching them itself.
|
|
53
|
+
const { matchProtected } = require('../../src/bongos/permission-path-check');
|
|
54
|
+
const { resolveEnv, envPrefix, LEGACY_ENV_PREFIXES } = require('../../src/instance-config');
|
|
55
|
+
const cadence = require('../../modules/autonomy/cadence.js');
|
|
56
|
+
const configIdle = require('../../modules/autonomy/config-idle.js');
|
|
57
|
+
|
|
58
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..');
|
|
59
|
+
|
|
60
|
+
function readArgv(argv) {
|
|
61
|
+
const out = { goals: [], maxTasks: 1, dryRun: false, json: false, forever: false, maxLoops: Infinity, model: DEFAULT_MODEL, timeoutMs: DEFAULT_WORKER_TIMEOUT_MS };
|
|
62
|
+
for (let i = 0; i < argv.length; i++) {
|
|
63
|
+
const a = argv[i];
|
|
64
|
+
if (a === '--goal') { out.goals.push(Number(argv[++i])); continue; }
|
|
65
|
+
if (a.startsWith('--goal=')) { out.goals.push(Number(a.slice(7))); continue; }
|
|
66
|
+
if (a === '--max-tasks') { out.maxTasks = Number(argv[++i]); continue; }
|
|
67
|
+
if (a === '--model') { out.model = argv[++i]; continue; }
|
|
68
|
+
if (a === '--timeout-min') { out.timeoutMs = Number(argv[++i]) * 60_000; continue; }
|
|
69
|
+
if (a === '--dry-run') { out.dryRun = true; continue; }
|
|
70
|
+
if (a === '--json') { out.json = true; continue; }
|
|
71
|
+
// --forever is the DAEMON shape: never returns, decides its own speed.
|
|
72
|
+
if (a === '--forever') { out.forever = true; continue; }
|
|
73
|
+
// --max-loops bounds the forever loop. It exists so a test (and an operator
|
|
74
|
+
// trying the thing out) can run the machine to completion; production passes
|
|
75
|
+
// it never, and Infinity is the honest default.
|
|
76
|
+
if (a === '--max-loops') { out.maxLoops = Number(argv[++i]); continue; }
|
|
77
|
+
}
|
|
78
|
+
if (!out.goals.length && process.env.AUTOBONGOS_GOALS) {
|
|
79
|
+
out.goals = String(process.env.AUTOBONGOS_GOALS).split(',').map((g) => Number(g.trim())).filter(Boolean);
|
|
80
|
+
}
|
|
81
|
+
// NOTE the semantics changed in task 1003908: `out.goals` is a REQUESTED filter,
|
|
82
|
+
// intersected with the database allowlist. An empty list no longer means "refuse
|
|
83
|
+
// to run" — it means "every allowlisted goal" — because the thing that refuses
|
|
84
|
+
// when nothing is permitted is now the fence, which this machine cannot edit.
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const run = (file, args, opts = {}) => new Promise((resolve) => {
|
|
89
|
+
execFile(file, args, { cwd: REPO_ROOT, maxBuffer: 32 * 1024 * 1024, ...opts },
|
|
90
|
+
(err, stdout, stderr) => resolve({ ok: !err, code: err ? (err.code ?? 1) : 0, stdout: stdout || '', stderr: stderr || '' }));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// The run log is the supervisor's own record. It is NOT the ledger — Bongos is —
|
|
94
|
+
// but a machine nobody is watching must be able to say afterwards what it did,
|
|
95
|
+
// and an append-only JSONL survives a kill -9 mid-iteration.
|
|
96
|
+
function logPath() {
|
|
97
|
+
if (process.env.AUTOBONGOS_LOG_FILE) return process.env.AUTOBONGOS_LOG_FILE;
|
|
98
|
+
try { return require('../../src/instance-config.js').configPath('autobongos-runs.jsonl'); }
|
|
99
|
+
catch (_) { return path.join(REPO_ROOT, 'autobongos-runs.jsonl'); }
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function record(entry) {
|
|
103
|
+
const row = { at: new Date().toISOString(), ...entry };
|
|
104
|
+
try {
|
|
105
|
+
const p = logPath();
|
|
106
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
107
|
+
fs.appendFileSync(p, `${JSON.stringify(row)}\n`);
|
|
108
|
+
} catch (_) { /* a runner that cannot log must still run */ }
|
|
109
|
+
return row;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// readRunLogRows — the run log back as rows, newest last. Only the TAIL is read
|
|
113
|
+
// (a log that runs for weeks must not be slurped whole every loop), and a line
|
|
114
|
+
// that will not parse — the half-written last line of a kill -9 — is skipped.
|
|
115
|
+
function readRunLogRows(p = logPath(), maxBytes = 4 * 1024 * 1024) {
|
|
116
|
+
let text = '';
|
|
117
|
+
try {
|
|
118
|
+
const size = fs.statSync(p).size;
|
|
119
|
+
const fd = fs.openSync(p, 'r');
|
|
120
|
+
try {
|
|
121
|
+
const len = Math.min(size, maxBytes);
|
|
122
|
+
const buf = Buffer.alloc(len);
|
|
123
|
+
fs.readSync(fd, buf, 0, len, size - len);
|
|
124
|
+
text = buf.toString('utf8');
|
|
125
|
+
} finally { fs.closeSync(fd); }
|
|
126
|
+
} catch (_) { return []; }
|
|
127
|
+
const rows = [];
|
|
128
|
+
for (const line of text.split('\n')) {
|
|
129
|
+
if (!line.trim()) continue;
|
|
130
|
+
try { rows.push(JSON.parse(line)); } catch (_) { /* partial line */ }
|
|
131
|
+
}
|
|
132
|
+
return rows;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// recheckPendingLands — once per loop (task 1004538). Which tasks are waiting on
|
|
136
|
+
// a land comes from the run log on disk and when they shipped comes from the
|
|
137
|
+
// ledger, so a restart loses neither. Open `shipped_no_artifact` blockers are read
|
|
138
|
+
// from the database and resolved once main carries the commit. Every outcome is a
|
|
139
|
+
// row, so the next pass reads it back.
|
|
140
|
+
//
|
|
141
|
+
// The blocker sweep runs at most once per LAND_SWEEP_MS per process (and at once
|
|
142
|
+
// after a restart): a false alarm closing within the hour is plenty, and the
|
|
143
|
+
// open-blocker list need not be read on every loop. Pending lands are re-probed
|
|
144
|
+
// every loop — and cost nothing when there are none.
|
|
145
|
+
const LAND_SWEEP_MS = 60 * 60 * 1000;
|
|
146
|
+
async function recheckPendingLands(deps = {}) {
|
|
147
|
+
const log = deps.record || record;
|
|
148
|
+
const st = deps.state || RUNNER_STATE;
|
|
149
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
150
|
+
const sweepBlockers = !st.landSweepAt || now - st.landSweepAt >= LAND_SWEEP_MS;
|
|
151
|
+
const ids = pendingLandIds(readRunLogRows());
|
|
152
|
+
if (!ids.length && !sweepBlockers) return { rechecked: [], resolved: [], errors: [] };
|
|
153
|
+
const out = await recheckLands({ pendingIds: ids, sweepBlockers }, { ...(deps.api ? { api: deps.api } : {}), ...(deps.landDeps || {}) });
|
|
154
|
+
if (sweepBlockers) st.landSweepAt = now;
|
|
155
|
+
for (const r of out.rechecked) log({ event: 'land_recheck', ...r });
|
|
156
|
+
for (const r of out.resolved) {
|
|
157
|
+
log({ event: 'land_blocker_resolved', task_id: r.task_id, blocker_id: r.blocker_id, sha: r.sha,
|
|
158
|
+
reason: `main now carries ${r.sha} naming task ${r.task_id} — the no-artifact blocker was a slow land, resolved` });
|
|
159
|
+
}
|
|
160
|
+
if (out.errors.length) log({ event: 'land_recheck_failed', errors: out.errors.slice(0, 10) });
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// pickedFrom — the row fields that say where a pick came from (task 1004453).
|
|
165
|
+
// `priority` is true only when the fence named a priority goal AND this pick came
|
|
166
|
+
// from it, so "the priority goal ran dry and the runner moved on" reads as
|
|
167
|
+
// priority:false beside the goal it moved on to.
|
|
168
|
+
function pickedFrom(goalId, gate) {
|
|
169
|
+
const p = gate && gate.priorityGoal != null ? gate.priorityGoal : null;
|
|
170
|
+
return { priority: p !== null && Number(goalId) === p };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// pickTask — the allowlisted goals, in order, through sequence.js's halt matrix.
|
|
174
|
+
// The matrix is reused rather than re-derived: it already refuses thin specs,
|
|
175
|
+
// protected paths, judgement kinds and unmet dependencies, from facts on the row.
|
|
176
|
+
// The assembly below mirrors sequence.js's own --goal --next path step for step,
|
|
177
|
+
// so the runner and a person running /builder-sequence cannot disagree about
|
|
178
|
+
// which task is next.
|
|
179
|
+
//
|
|
180
|
+
// `claimableIds` is fetched because the matrix's `not_claimable` reason defers to
|
|
181
|
+
// the server's own feed rather than re-deriving its filter here. A null (feed
|
|
182
|
+
// unreadable) makes that one reason stay QUIET rather than halt everything —
|
|
183
|
+
// ADR 0153 records the bug where the opposite turned an empty read into a
|
|
184
|
+
// universal halt.
|
|
185
|
+
async function pickTask(goals, deps = {}) {
|
|
186
|
+
const api = deps.api || await (require('./cli-lib').cliClient)();
|
|
187
|
+
let rank = deps.rank;
|
|
188
|
+
if (rank === undefined) {
|
|
189
|
+
const me = await api.me.getMe();
|
|
190
|
+
rank = me.ok ? me.data.builder?.rank : null;
|
|
191
|
+
}
|
|
192
|
+
const claimableIds = await seq.fetchClaimableIds(api);
|
|
193
|
+
|
|
194
|
+
// The skipped lists from EVERY goal are kept, not just the last one. An empty
|
|
195
|
+
// queue and a queue where every task halts both yield no task, and telling them
|
|
196
|
+
// apart is the whole of `unworkable_queue` (task 1003904): calling the second
|
|
197
|
+
// one "idle" is how a goal full of blocked work sits for a week with the runner
|
|
198
|
+
// reporting itself perfectly healthy.
|
|
199
|
+
const allSkipped = [];
|
|
200
|
+
for (const goalId of goals) {
|
|
201
|
+
let byId;
|
|
202
|
+
try { byId = await seq.fetchGoalTasks(api, goalId, seq.GOAL_PAGE); }
|
|
203
|
+
catch (e) { return { error: `goal ${goalId}: ${e.message}` }; }
|
|
204
|
+
|
|
205
|
+
const candidates = Array.from(byId.values()).filter((t) => !seq.TERMINAL.has(t.status));
|
|
206
|
+
const { order } = seq.topoOrder(candidates);
|
|
207
|
+
// Tasks this process set aside after a refused claim (task 1004396) drop out
|
|
208
|
+
// AFTER ordering, so a dependent of one is still halted by its own unmet
|
|
209
|
+
// dependency rather than promoted by the task's absence.
|
|
210
|
+
const exclude = deps.exclude || null;
|
|
211
|
+
const unexcluded = exclude && exclude.size ? order.filter((t) => !exclude.has(String(t.id))) : order;
|
|
212
|
+
// Work this machine structurally cannot do — SSH, root, systemd on a live
|
|
213
|
+
// host (task 1004537) — is skipped BEFORE the claim, and listed with its
|
|
214
|
+
// reason. It is decided here and not in sequence.js's matrix because the
|
|
215
|
+
// matrix also serves /builder-sequence, where the builder may well hold the
|
|
216
|
+
// host access an unattended runner never has.
|
|
217
|
+
const hostSkipped = [];
|
|
218
|
+
const open = unexcluded.filter((t) => {
|
|
219
|
+
const host = fence.needsHostAccess(t);
|
|
220
|
+
if (!host.needs) return true;
|
|
221
|
+
hostSkipped.push({ id: t.id, title: t.title, reasons: [{ code: 'needs_host_access', detail: `${fence.REFUSALS.needs_host_access} (${host.signals.join('; ')})` }] });
|
|
222
|
+
return false;
|
|
223
|
+
});
|
|
224
|
+
const { next, skipped } = seq.pickNext(open, { rank, claimableIds });
|
|
225
|
+
if (next) return { task: next, goalId, skipped: [...hostSkipped, ...skipped] };
|
|
226
|
+
for (const sk of [...hostSkipped, ...skipped]) allSkipped.push({ ...sk, goal_id: goalId });
|
|
227
|
+
}
|
|
228
|
+
return { none: true, skipped: allSkipped };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// WORKER_ENV_ALLOW — what a worker is allowed to inherit (grader finding, task
|
|
232
|
+
// 1003902). Spreading `process.env` handed a bypassPermissions session every
|
|
233
|
+
// credential the supervisor happened to hold — DB URLs, npm and git tokens — to a
|
|
234
|
+
// process whose only injection defence is a prompt fence. The fence is good and
|
|
235
|
+
// it is not a technical barrier, so the blast radius of a jailbreak has to be
|
|
236
|
+
// bounded by something that is.
|
|
237
|
+
//
|
|
238
|
+
// The worker needs: to run node and claude (PATH/HOME/SHELL and the platform's
|
|
239
|
+
// own required vars), to reach the instance (BONGOS_API_BASE), and to be told it
|
|
240
|
+
// is a subagent. It does NOT need a git push credential — the road to main is the
|
|
241
|
+
// server-mediated pipeline (ADR 0307 §6) — so none is passed.
|
|
242
|
+
function claudeBinEnvNames() {
|
|
243
|
+
return [...new Set([envPrefix(), ...LEGACY_ENV_PREFIXES].map((p) => `${p}_CLAUDE_BIN`)), 'CLAUDE_BIN'];
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const WORKER_ENV_ALLOW = [
|
|
247
|
+
'PATH', 'HOME', 'SHELL', 'LANG', 'LC_ALL', 'TZ', 'TMPDIR', 'USER', 'LOGNAME',
|
|
248
|
+
'SystemRoot', 'windir', 'COMSPEC', 'PATHEXT', 'USERPROFILE', 'APPDATA', 'LOCALAPPDATA', 'TEMP', 'TMP',
|
|
249
|
+
'BONGOS_API_BASE', 'CLOUDBONGOS_API_BASE',
|
|
250
|
+
// Every spelling resolveWorkerBin below can read (task 1001441): the branded
|
|
251
|
+
// <PREFIX>_CLAUDE_BIN, the legacy ones, and the unprefixed CLAUDE_BIN. A name
|
|
252
|
+
// missing here would be set on the host and silently absent in the worker.
|
|
253
|
+
...claudeBinEnvNames(),
|
|
254
|
+
'XDG_CONFIG_HOME',
|
|
255
|
+
// THE WORKER'S OWN CREDENTIAL, and it has to be here (task 1003907, found by
|
|
256
|
+
// running it). On a desktop, the claude CLI authenticates from the credential
|
|
257
|
+
// store under USERPROFILE/HOME, which are already allowed - so this list looked
|
|
258
|
+
// complete. On a HEADLESS host there is no interactive login to have written
|
|
259
|
+
// one, and setup.js's own preflight says to set CLAUDE_CODE_OAUTH_TOKEN there
|
|
260
|
+
// instead. Stripping it meant every worker on the machine this was built for
|
|
261
|
+
// started unauthenticated, exited in ~2s, and the run recorded only "worker
|
|
262
|
+
// output was not JSON".
|
|
263
|
+
//
|
|
264
|
+
// This is not the leak the list exists to prevent. The worker IS a Claude
|
|
265
|
+
// session; its own auth is the one credential it cannot do its job without.
|
|
266
|
+
// Everything the comment above refuses - DB URLs, git and npm tokens - stays
|
|
267
|
+
// refused.
|
|
268
|
+
'CLAUDE_CODE_OAUTH_TOKEN', 'ANTHROPIC_API_KEY',
|
|
269
|
+
];
|
|
270
|
+
|
|
271
|
+
function workerEnv(source = process.env, extra = {}) {
|
|
272
|
+
const out = {};
|
|
273
|
+
for (const k of WORKER_ENV_ALLOW) if (source[k] !== undefined) out[k] = source[k];
|
|
274
|
+
// An operator can widen this deliberately and visibly, never by accident.
|
|
275
|
+
const widen = String(source.AUTOBONGOS_WORKER_ENV_EXTRA || '').split(',').map((x) => x.trim()).filter(Boolean);
|
|
276
|
+
for (const k of widen) if (source[k] !== undefined) out[k] = source[k];
|
|
277
|
+
return { ...out, OTB_SUBAGENT: '1', ...extra };
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const MAX_WORKER_OUTPUT = 8 * 1024 * 1024; // a misbehaving worker must not grow memory until the timeout
|
|
281
|
+
|
|
282
|
+
// resolveWorkerBin — WHICH `claude`, and whether it needs a shell.
|
|
283
|
+
//
|
|
284
|
+
// THE BUG THIS EXISTS FOR (task 1003907, found by running it on the real Windows
|
|
285
|
+
// host). On Windows `claude` on PATH is a .cmd shim, and Node has refused to
|
|
286
|
+
// spawn .cmd/.bat without a shell since CVE-2024-27980 — so this used to pass
|
|
287
|
+
// `shell: process.platform === 'win32'`. But `shell: true` does not pass argv
|
|
288
|
+
// through: it CONCATENATES it into one command line that cmd.exe re-parses,
|
|
289
|
+
// which destroys every embedded quote. `--json-schema` is a JSON blob made
|
|
290
|
+
// almost entirely of quotes, so claude received a mangled argument and exited in
|
|
291
|
+
// ~2s with `--json-schema is not valid JSON`. Every worker. Every run. Forever —
|
|
292
|
+
// and the cadence classes that as `quiet`, so it never trips the failure ladder.
|
|
293
|
+
//
|
|
294
|
+
// The npm package ships a REAL claude.exe beside the shim, so prefer it: a true
|
|
295
|
+
// executable spawns with shell:false and exact argv, and nothing re-parses.
|
|
296
|
+
// Falling back to the old behaviour is deliberate — a host with a differently
|
|
297
|
+
// laid-out install should still run, and a corrupted schema surfaces as a failed
|
|
298
|
+
// worker rather than as a refusal to start at all.
|
|
299
|
+
function resolveWorkerBin(env = process.env, platform = process.platform) {
|
|
300
|
+
const override = resolveEnv('CLAUDE_BIN', { env }) || env.CLAUDE_BIN;
|
|
301
|
+
if (override) return { bin: override, shell: platform === 'win32' && /\.(cmd|bat)$/i.test(override) };
|
|
302
|
+
if (platform !== 'win32') return { bin: 'claude', shell: false };
|
|
303
|
+
for (const dir of String(env.PATH || env.Path || '').split(path.delimiter)) {
|
|
304
|
+
if (!dir) continue;
|
|
305
|
+
const candidates = [
|
|
306
|
+
path.join(dir, 'node_modules', '@anthropic-ai', 'claude-code', 'bin', 'claude.exe'),
|
|
307
|
+
path.join(dir, 'claude.exe'),
|
|
308
|
+
];
|
|
309
|
+
for (const c of candidates) {
|
|
310
|
+
try { if (fs.existsSync(c)) return { bin: c, shell: false }; } catch (_) {}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return { bin: 'claude', shell: true };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// gradeLedgerPath — the run's LOCAL ledger (task 1004536): the file ship.js reads
|
|
317
|
+
// to refuse a third grade early. A fresh file per run, under the instance config
|
|
318
|
+
// dir, never inside the task's tree. It is the courtesy half of the cap — see the
|
|
319
|
+
// header of autobongos-grade-cap.js; the boundary is the server count below.
|
|
320
|
+
const GRADE_CAP_WATCH_MS = 30_000;
|
|
321
|
+
function gradeLedgerPath(taskId) {
|
|
322
|
+
const id = Number(taskId);
|
|
323
|
+
if (!Number.isSafeInteger(id) || id <= 0) throw new Error(`gradeLedgerPath: task id must be a positive integer, got ${taskId}`);
|
|
324
|
+
const name = path.join('autobongos-grades', `${id}-${Date.now()}-${crypto.randomBytes(3).toString('hex')}.jsonl`);
|
|
325
|
+
try { return require('../../src/instance-config.js').configPath(name); }
|
|
326
|
+
catch (_) { return path.join(os.tmpdir(), name); }
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// gradeCapReader — how the supervisor counts this run's graded rounds.
|
|
330
|
+
//
|
|
331
|
+
// The SERVER's grade_attempts is the count that matters: the worker has no write
|
|
332
|
+
// path to it, so clearing an env var or deleting a file cannot reset it. The
|
|
333
|
+
// baseline is the newest attempt recorded BEFORE the worker starts; only rounds
|
|
334
|
+
// after it belong to this run. When the server cannot be read, the local ledger
|
|
335
|
+
// is the fallback — and either source showing the cap exhausted is enough.
|
|
336
|
+
async function gradeCapReader({ api, taskId, ledger }) {
|
|
337
|
+
const before = await gradeCap.readServerAttempts(api, taskId);
|
|
338
|
+
const afterMs = before ? gradeCap.newestAttemptAt(before) : null;
|
|
339
|
+
return async function read() {
|
|
340
|
+
const local = gradeCap.gradeCapState(gradeCap.readLedger(ledger), taskId);
|
|
341
|
+
const attempts = before ? await gradeCap.readServerAttempts(api, taskId) : null;
|
|
342
|
+
if (!attempts) return { ...local, source: 'local' };
|
|
343
|
+
const server = gradeCap.gradeCapState(gradeCap.roundsFromAttempts(attempts, taskId, afterMs), taskId);
|
|
344
|
+
return server.exhausted || !local.exhausted ? { ...server, source: 'server' } : { ...local, source: 'local' };
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// spawnWorker — one headless session, bounded by WALL CLOCK, in the task's own
|
|
349
|
+
// working tree.
|
|
350
|
+
async function spawnWorker({ task, model, timeoutMs, cwd, gradeLedger = null, capCheck = null, prompt: givenPrompt = null, deps = {} }) {
|
|
351
|
+
const spawnFn = deps.spawn || spawn;
|
|
352
|
+
const sessionId = deps.sessionId || crypto.randomUUID(); // pinned, so the session can be resumed
|
|
353
|
+
const { bin, shell: workerShell } = deps.resolveBin ? deps.resolveBin() : resolveWorkerBin();
|
|
354
|
+
const isRoot = typeof process.getuid === 'function' && process.getuid() === 0;
|
|
355
|
+
const args = buildWorkerArgs({ sessionId, model, isRoot });
|
|
356
|
+
const prompt = givenPrompt || buildWorkerPrompt({ task }); // the tweak lane names its own (task 1004679)
|
|
357
|
+
|
|
358
|
+
return new Promise((resolve) => {
|
|
359
|
+
let child;
|
|
360
|
+
try {
|
|
361
|
+
child = spawnFn(bin, args, {
|
|
362
|
+
cwd,
|
|
363
|
+
// stdin is a PIPE we write the prompt into and close. Never inherit: a
|
|
364
|
+
// `claude -p` with an open inherited stdin waits seconds for input that
|
|
365
|
+
// never comes, on every single spawn.
|
|
366
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
367
|
+
shell: workerShell,
|
|
368
|
+
windowsHide: true,
|
|
369
|
+
// The run's local grade ledger (task 1004536): ship.js appends each graded
|
|
370
|
+
// round to it and refuses a third early. The worker can see this variable,
|
|
371
|
+
// so it is the courtesy half only — capCheck below is the boundary.
|
|
372
|
+
env: workerEnv(process.env, gradeLedger ? { [gradeCap.LEDGER_ENV]: gradeLedger } : {}),
|
|
373
|
+
});
|
|
374
|
+
} catch (e) { return resolve({ spawnError: e.message }); }
|
|
375
|
+
|
|
376
|
+
let stdout = ''; let stderr = ''; let timedOut = false; let truncated = false; let gradeCapped = false;
|
|
377
|
+
const cap = (cur, d) => {
|
|
378
|
+
if (cur.length >= MAX_WORKER_OUTPUT) { truncated = true; return cur; }
|
|
379
|
+
return cur + String(d).slice(0, MAX_WORKER_OUTPUT - cur.length);
|
|
380
|
+
};
|
|
381
|
+
const timer = setTimeout(() => { timedOut = true; try { child.kill('SIGKILL'); } catch (_) {} }, timeoutMs);
|
|
382
|
+
// task 1004536: once the second graded FAIL is recorded the run is over — the
|
|
383
|
+
// task goes to the owner. capCheck reads the SERVER's grade_attempts (see
|
|
384
|
+
// gradeCapReader), so a worker that cleared the ledger env or deleted the file
|
|
385
|
+
// is still stopped. One read at a time: a slow API must not stack polls.
|
|
386
|
+
let capBusy = false;
|
|
387
|
+
const capWatch = capCheck ? setInterval(async () => {
|
|
388
|
+
if (capBusy || gradeCapped) return;
|
|
389
|
+
capBusy = true;
|
|
390
|
+
try {
|
|
391
|
+
if ((await capCheck()).exhausted) {
|
|
392
|
+
gradeCapped = true; clearInterval(capWatch);
|
|
393
|
+
try { child.kill('SIGKILL'); } catch (_) {}
|
|
394
|
+
}
|
|
395
|
+
} catch (_) { /* an unreadable count is retried next tick; the post-run read still decides */ }
|
|
396
|
+
capBusy = false;
|
|
397
|
+
}, deps.capWatchMs || GRADE_CAP_WATCH_MS) : null;
|
|
398
|
+
const stopWatch = () => { if (capWatch) clearInterval(capWatch); };
|
|
399
|
+
child.stdout.on('data', (d) => { stdout = cap(stdout, d); });
|
|
400
|
+
child.stderr.on('data', (d) => { stderr = cap(stderr, d); });
|
|
401
|
+
child.on('error', (e) => { clearTimeout(timer); stopWatch(); resolve({ spawnError: e.message, sessionId }); });
|
|
402
|
+
child.on('close', (code) => {
|
|
403
|
+
clearTimeout(timer); stopWatch();
|
|
404
|
+
resolve({ envelope: stdout, exitCode: code, timedOut, truncated, gradeCapped, sessionId, stderr: stderr.slice(-2000) });
|
|
405
|
+
});
|
|
406
|
+
child.stdin.end(prompt);
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// worktreeFor — the task gets its OWN linked working tree, and this is not a
|
|
411
|
+
// nicety (grader finding, task 1003902).
|
|
412
|
+
//
|
|
413
|
+
// worktree-per-claim is an ENFORCED project default: worktree-claim-guard.js
|
|
414
|
+
// refuses any claim made from the main checkout, with no escape but `--here`.
|
|
415
|
+
// The natural shape for a long-lived unattended supervisor is one persistent
|
|
416
|
+
// checkout — which is the main checkout — so the first cut of this loop would
|
|
417
|
+
// have taken `claim_failed` on every single iteration and never done any work.
|
|
418
|
+
// Reaching for `--here` would "fix" that by defeating the guard: the worker's
|
|
419
|
+
// branch would land in the supervisor's own tree, which is precisely the strand
|
|
420
|
+
// the guard exists to prevent.
|
|
421
|
+
//
|
|
422
|
+
// The name carries the task id plus a random suffix. Unique by construction, so
|
|
423
|
+
// it cannot collide with another active claim's tree — a collision makes the
|
|
424
|
+
// server store a disambiguated name the ship lock can never match (task 1004031).
|
|
425
|
+
function worktreeName(taskId) {
|
|
426
|
+
return `autobongos-${taskId}-${crypto.randomBytes(3).toString('hex')}`;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// readFence — the two facts the fence is made of, in one place.
|
|
430
|
+
//
|
|
431
|
+
// BOTH come from the server. `GET /autonomy/fence` is the kill switch and the
|
|
432
|
+
// allowlist; `GET /me`'s `grader_bypass` is ADR 0041's global grader killswitch,
|
|
433
|
+
// resolved server-side from the server's own env. Neither is readable from a file
|
|
434
|
+
// on this machine, and that is the entire point — see modules/autonomy/fence.js.
|
|
435
|
+
//
|
|
436
|
+
// A failure of EITHER read returns the shape that refuses. An unreachable
|
|
437
|
+
// instance and a thrown kill switch are different facts, but they license the
|
|
438
|
+
// same action: do nothing.
|
|
439
|
+
async function readFence(deps = {}) {
|
|
440
|
+
const api = deps.api || await (require('./cli-lib').cliClient)();
|
|
441
|
+
let raw = null;
|
|
442
|
+
let graderBypassed = null;
|
|
443
|
+
try {
|
|
444
|
+
const r = await api.autonomy.getAutonomyFence();
|
|
445
|
+
if (r && r.ok) raw = r.data;
|
|
446
|
+
} catch (_) { /* unreadable → refuses below */ }
|
|
447
|
+
try {
|
|
448
|
+
const me = await api.me.getMe();
|
|
449
|
+
// Strictly a boolean or nothing. `undefined` from an older server is NOT
|
|
450
|
+
// `false`: it means this instance cannot tell us, which refuses.
|
|
451
|
+
if (me && me.ok && typeof me.data.grader_bypass === 'boolean') graderBypassed = me.data.grader_bypass;
|
|
452
|
+
} catch (_) { /* unreadable → refuses below */ }
|
|
453
|
+
return { raw, graderBypassed };
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// ── what one process remembers between iterations (task 1004396) ────────────
|
|
457
|
+
//
|
|
458
|
+
// Owner ruling, 2026-09-30: a refused claim must not put the runner into waiting
|
|
459
|
+
// by default. It sets THAT task aside and tries the next one. It waits only when
|
|
460
|
+
// there is a block and nothing else it can claim.
|
|
461
|
+
//
|
|
462
|
+
// The two refusals are told apart by claim.js's exit code, because the message is
|
|
463
|
+
// prose and the first cut of this read prose. The run that found this burned
|
|
464
|
+
// ~30 attempts on task 1004254, whose refusal quoted "Your work changes the
|
|
465
|
+
// tools…" — which reads like a per-task protected-path verdict and was in fact
|
|
466
|
+
// the plain-words line of a QUEUE-WIDE gate about a different, stranded task.
|
|
467
|
+
// Setting 1004254 aside would have moved the runner on to the next task, which
|
|
468
|
+
// the same gate refuses too.
|
|
469
|
+
//
|
|
470
|
+
// QUEUE_GATED_EXIT is pinned against claim.js's own value in a test rather than
|
|
471
|
+
// required from it: requiring the CLI would load its whole dependency graph into
|
|
472
|
+
// a process that runs for weeks, to read one integer.
|
|
473
|
+
const QUEUE_GATED_EXIT = 4;
|
|
474
|
+
// How long a task refused ON ITS OWN is left alone. Not forever: most per-task
|
|
475
|
+
// refusals clear (a dependency ships, a claim is released, a blocker resolves),
|
|
476
|
+
// and a process that remembered them for its whole life would shelve work it can
|
|
477
|
+
// do. Half an hour is several wakes at full speed, so a refused head of the queue
|
|
478
|
+
// costs one attempt per half hour instead of one per wake.
|
|
479
|
+
const CLAIM_SET_ASIDE_MS = 30 * 60 * 1000;
|
|
480
|
+
// A bound on claim attempts in ONE pass, so a queue of refusals cannot turn one
|
|
481
|
+
// iteration into dozens of worktree creations.
|
|
482
|
+
const MAX_CLAIM_ATTEMPTS = 5;
|
|
483
|
+
// A limit message with no machine-readable reset (the CLI's human forms name a
|
|
484
|
+
// clock time in a zone we would have to guess). Half an hour is short against a
|
|
485
|
+
// five-hour window and long against a spawn that is cut off in two seconds, so a
|
|
486
|
+
// guess that is wrong costs one wasted spawn per half hour, not one per wake.
|
|
487
|
+
const LIMIT_UNKNOWN_RESET_MS = 30 * 60 * 1000;
|
|
488
|
+
|
|
489
|
+
function newRunnerState() {
|
|
490
|
+
return { setAside: new Map(), queueGate: null, limitUntil: null, diskHead: null, mainHead: null, landSweepAt: null };
|
|
491
|
+
}
|
|
492
|
+
const RUNNER_STATE = newRunnerState();
|
|
493
|
+
|
|
494
|
+
// The stranded task ids a queue-gated refusal names (" - task 777 Title"). An
|
|
495
|
+
// empty list is a legal answer: the gate is still a gate, the runner simply has
|
|
496
|
+
// nothing to poll and tries one claim per wake instead.
|
|
497
|
+
function owedTaskIds(text) {
|
|
498
|
+
const ids = [];
|
|
499
|
+
const re = /^\s*-\s*task\s+(\d+)/gm;
|
|
500
|
+
let m;
|
|
501
|
+
while ((m = re.exec(String(text || ''))) !== null) ids.push(m[1]);
|
|
502
|
+
return ids;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Does the gate still hold? Asked of the LEDGER, one read per stranded task, and
|
|
506
|
+
// never by attempting a claim: a claim attempt per wake is the loop this replaces.
|
|
507
|
+
// An unreadable task keeps the wait — guessing "cleared" would restart the
|
|
508
|
+
// attempts on a read failure, the opposite of backing off.
|
|
509
|
+
async function queueGateHolds(queueGate, deps = {}) {
|
|
510
|
+
if (!queueGate.owed.length) return false;
|
|
511
|
+
const api = deps.api && deps.api.tasks ? deps.api : await (require('./cli-lib').cliClient)();
|
|
512
|
+
for (const id of queueGate.owed) {
|
|
513
|
+
let r = null;
|
|
514
|
+
try { r = await api.tasks.getTasksId({ id: Number(id) }); } catch (_) { return true; }
|
|
515
|
+
if (!r || !r.ok || !r.data || !r.data.task) return true;
|
|
516
|
+
if (r.data.task.status === 'confirmed') return true;
|
|
517
|
+
}
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
function queueGatedRow(queueGate, now) {
|
|
522
|
+
const on = queueGate.owed.length ? queueGate.owed.map((id) => `task ${id}`).join(', ') : 'a stranded confirmed task';
|
|
523
|
+
return {
|
|
524
|
+
event: 'queue_gated', waiting_on: queueGate.owed,
|
|
525
|
+
waited_s: Math.max(0, Math.round((now - queueGate.since) / 1000)),
|
|
526
|
+
reason: `every claim is refused until ${on} lands — waiting, not claiming (${queueGate.reason})`,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function iteration(opts, deps = {}) {
|
|
531
|
+
const runCmd = deps.run || run;
|
|
532
|
+
const spawnWork = deps.spawnWorker || spawnWorker;
|
|
533
|
+
const readGauge = deps.gauge || (() => gauge.evaluate());
|
|
534
|
+
const pick = deps.pickTask || pickTask;
|
|
535
|
+
const log = deps.record || record;
|
|
536
|
+
|
|
537
|
+
// 0. THE FENCE (task 1003908, ADR 0307 §6). Before the gauge, because three of
|
|
538
|
+
// its four gates make every task refusable and there is no sense paying for a
|
|
539
|
+
// capacity read to discover that. Before anything is picked, claimed or spawned,
|
|
540
|
+
// because a fence checked after the fact is not a fence.
|
|
541
|
+
const { raw, graderBypassed } = deps.readFence ? await deps.readFence(deps) : await readFence(deps);
|
|
542
|
+
// NOT named `run`. The module-level exec helper above is called `run`, and a
|
|
543
|
+
// `const run` anywhere in this function hoists into a Temporal Dead Zone that
|
|
544
|
+
// covers the WHOLE body — including `deps.run || run` at the top, which is the
|
|
545
|
+
// PRODUCTION call shape (main() passes no deps). That shadowing threw
|
|
546
|
+
// `ReferenceError: Cannot access 'run' before initialization` on the first line
|
|
547
|
+
// of every real iteration while every test stayed green, because the harness
|
|
548
|
+
// always injected deps.run. Caught by the grader on task 1003908; the test that
|
|
549
|
+
// now drives iteration() with no deps is the actual fix — this rename is half.
|
|
550
|
+
const gate = fence.decideRun({ fence: raw, requestedGoals: opts.goals, graderBypassed });
|
|
551
|
+
// ADR 0371 D3 (task 1004679): the tweak lane's own setting replaces the GOAL
|
|
552
|
+
// ALLOWLIST for that lane only, so a fence that refused on goals alone still lets
|
|
553
|
+
// the lane run below; the kill switch and the grader stop refuse it here.
|
|
554
|
+
const fencedRow = () => log({ event: 'fenced', code: gate.code, reason: gate.reason, refused_goals: gate.refusedGoals || [] });
|
|
555
|
+
const goalsOnly = !gate.go && tweakLane.GOAL_ONLY_CODES.has(gate.code)
|
|
556
|
+
&& await (deps.laneSwitchOn || tweakLane.laneSwitchOn)(deps.api);
|
|
557
|
+
if (!gate.go && !goalsOnly) return fencedRow();
|
|
558
|
+
|
|
559
|
+
// 1. The gauge. Never start a task we cannot pay for out of the window.
|
|
560
|
+
const g = readGauge();
|
|
561
|
+
if (!g.decision.go) {
|
|
562
|
+
return log({ event: 'hold', go: false, unknown: !!g.decision.unknown, reason: g.decision.reason, sleep_until: g.decision.sleepUntil ?? null });
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// 1b. The usage limit, once HIT (task 1004454). Owner ruling 2026-09-30: no
|
|
566
|
+
// fixed cap on how much work a reading buys — the gauge above is read before
|
|
567
|
+
// every task and a derived reading proceeds — and hitting the limit is
|
|
568
|
+
// acceptable, so long as the runner then waits for the reset and resumes by
|
|
569
|
+
// itself. A worker cut off by the limit records when it resets; until then no
|
|
570
|
+
// task is taken, because every worker spawned would be cut off the same way.
|
|
571
|
+
const state = deps.state || RUNNER_STATE;
|
|
572
|
+
const now = deps.now ? deps.now() : Date.now();
|
|
573
|
+
if (state.limitUntil) {
|
|
574
|
+
if (now < state.limitUntil) {
|
|
575
|
+
return log({
|
|
576
|
+
event: 'hold', go: false, unknown: false, usage_limit: true,
|
|
577
|
+
reason: 'the subscription usage limit was hit — sleeping to its reset, then resuming by itself',
|
|
578
|
+
sleep_until: Math.round(state.limitUntil / 1000),
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
log({ event: 'usage_limit_reset', was_until: new Date(state.limitUntil).toISOString() });
|
|
582
|
+
state.limitUntil = null;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
// 1c. A QUEUE-WIDE gate from an earlier pass (task 1004396). While the
|
|
586
|
+
// stranded task it named is still `confirmed`, every claim would be refused, so
|
|
587
|
+
// none is attempted. The wait wakes early when main moves (waitOrJump), which is
|
|
588
|
+
// exactly what the strand landing does.
|
|
589
|
+
if (state.queueGate) {
|
|
590
|
+
if (await queueGateHolds(state.queueGate, deps)) return log(queueGatedRow(state.queueGate, now));
|
|
591
|
+
log({ event: 'queue_gate_cleared', waited_on: state.queueGate.owed, waited_s: Math.max(0, Math.round((now - state.queueGate.since) / 1000)) });
|
|
592
|
+
state.queueGate = null;
|
|
593
|
+
}
|
|
594
|
+
for (const [id, until] of state.setAside) if (until <= now) state.setAside.delete(id);
|
|
595
|
+
|
|
596
|
+
// 1d. THE TWEAK LANE (task 1004679, ADR 0371 D1): before ordinary work, the
|
|
597
|
+
// oldest submitted page round the lane may take. Null means it took none.
|
|
598
|
+
const laneRow = await (deps.tweakLaneStep || tweakLane.tweakLaneStep)({ opts, state }, {
|
|
599
|
+
api: deps.api || await (require('./cli-lib').cliClient)(), log, matchProtected, runCmd, spawnWork,
|
|
600
|
+
worktreeName: deps.worktreeName || worktreeName, spawnDeps: deps, gradeCapReader,
|
|
601
|
+
gradeLedgerPath: deps.gradeLedgerPath || gradeLedgerPath, ...(deps.laneDeps || {}),
|
|
602
|
+
});
|
|
603
|
+
if (laneRow) return laneRow;
|
|
604
|
+
if (goalsOnly) return fencedRow();
|
|
605
|
+
|
|
606
|
+
// 2–4, up to MAX_CLAIM_ATTEMPTS times: pick, fence, tree, claim. A task refused
|
|
607
|
+
// on its own is set aside and the NEXT one is tried in the same pass.
|
|
608
|
+
let task = null; let picked = null; let wtName = null; let wtPath = null;
|
|
609
|
+
let lastRefusal = null;
|
|
610
|
+
for (let attempt = 0; attempt < MAX_CLAIM_ATTEMPTS && !task; attempt++) {
|
|
611
|
+
const exclude = new Set(state.setAside.keys());
|
|
612
|
+
// 2. Pick — from the ALLOWLISTED goals, never from what the command line asked
|
|
613
|
+
// for. gate.goals is already the intersection.
|
|
614
|
+
const got = await pick(gate.goals, { ...(deps.pickDeps || {}), exclude });
|
|
615
|
+
if (got.error) return log({ event: 'pick_failed', reason: got.error });
|
|
616
|
+
// A picker that hands back a set-aside task is treated as having none left:
|
|
617
|
+
// the exclusion is this function's contract, not the picker's courtesy.
|
|
618
|
+
if (got.none || exclude.has(String(got.task.id))) {
|
|
619
|
+
// Everything left was refused in THIS pass: return the last refusal, not
|
|
620
|
+
// "nothing claimable". Every claim failing at once is also what an outage
|
|
621
|
+
// looks like, and it must still reach the failure ladder (task 1004179).
|
|
622
|
+
if (lastRefusal) return lastRefusal;
|
|
623
|
+
return log({
|
|
624
|
+
event: 'nothing_claimable', goals: gate.goals, priority_goal: gate.priorityGoal ?? null,
|
|
625
|
+
// Carried so the cadence can tell an EMPTY queue from one where every task
|
|
626
|
+
// halts — the reasons are the matrix's own codes, safe to print.
|
|
627
|
+
skipped: (got.skipped || []).map((sk) => ({ id: sk.id, goal_id: sk.goal_id, reasons: (sk.reasons || []).map((r) => r.code) })),
|
|
628
|
+
set_aside: Array.from(state.setAside, ([id, until]) => ({ id, until: new Date(until).toISOString() })),
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
const candidate = got.task;
|
|
632
|
+
|
|
633
|
+
// 2b. The per-TASK half of the fence. sequence.js's halt matrix already refuses
|
|
634
|
+
// a protected-path task, and this re-asks anyway — not from distrust of the
|
|
635
|
+
// matrix, but because the matrix is a shared component with its own callers and
|
|
636
|
+
// the fence must not be a property of whoever happens to be picking. A gate that
|
|
637
|
+
// only holds while a collaborator keeps calling it is not a gate.
|
|
638
|
+
//
|
|
639
|
+
// Both halves read the task's DECLARED touches[], which is a claim, not a fact —
|
|
640
|
+
// see the header of modules/autonomy/fence.js for why that is acceptable as the
|
|
641
|
+
// OUTER of three layers and would not be as the only one.
|
|
642
|
+
const taskGate = fence.decideTask({
|
|
643
|
+
task: candidate, goals: gate.goals, protectedHits: matchProtected(candidate.touches || []),
|
|
644
|
+
});
|
|
645
|
+
if (!taskGate.go) {
|
|
646
|
+
return log({ event: 'fenced', code: taskGate.code, reason: taskGate.reason, task_id: candidate.id, goal_id: got.goalId, ...pickedFrom(got.goalId, gate), globs: taskGate.globs || [] });
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
if (opts.dryRun) return log({ event: 'dry_run', task_id: candidate.id, title: candidate.title, goal_id: got.goalId, ...pickedFrom(got.goalId, gate) });
|
|
650
|
+
|
|
651
|
+
// 3. The task's own working tree.
|
|
652
|
+
const name = deps.worktreeName ? deps.worktreeName(candidate.id) : worktreeName(candidate.id);
|
|
653
|
+
const made = await runCmd(process.execPath, [path.join('scripts', 'bongos', 'worktree.js'), 'add', name, '--base', 'origin/main']);
|
|
654
|
+
if (!made.ok) {
|
|
655
|
+
return log({ event: 'worktree_failed', task_id: candidate.id, worktree: name, reason: failureReason(made, 'worktree.js') });
|
|
656
|
+
}
|
|
657
|
+
const treePath = path.join(REPO_ROOT, '.claude', 'worktrees', name);
|
|
658
|
+
|
|
659
|
+
// 4. Claim FROM that tree, so claim.js records it as the claim's tree and the
|
|
660
|
+
// ship lock later agrees with where the work actually is.
|
|
661
|
+
const claimed = await runCmd(process.execPath, [path.join(REPO_ROOT, 'scripts', 'bongos', 'claim.js'), String(candidate.id)], { cwd: treePath });
|
|
662
|
+
if (!claimed.ok) {
|
|
663
|
+
await runCmd(process.execPath, [path.join('scripts', 'bongos', 'worktree.js'), 'remove', name]);
|
|
664
|
+
const reason = failureReason(claimed, 'claim.js');
|
|
665
|
+
if (claimed.code === QUEUE_GATED_EXIT) {
|
|
666
|
+
state.queueGate = { owed: owedTaskIds(`${claimed.stderr || ''}\n${claimed.stdout || ''}`), since: now, reason };
|
|
667
|
+
return log({ ...queueGatedRow(state.queueGate, now), task_id: candidate.id, worktree: name });
|
|
668
|
+
}
|
|
669
|
+
state.setAside.set(String(candidate.id), now + CLAIM_SET_ASIDE_MS);
|
|
670
|
+
lastRefusal = log({ event: 'claim_failed', task_id: candidate.id, worktree: name, reason, set_aside_s: CLAIM_SET_ASIDE_MS / 1000 });
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
task = candidate; picked = got; wtName = name; wtPath = treePath;
|
|
674
|
+
}
|
|
675
|
+
if (!task) return lastRefusal;
|
|
676
|
+
|
|
677
|
+
// 5. Work, in that tree.
|
|
678
|
+
const started = Date.now();
|
|
679
|
+
const gradeLedger = deps.gradeLedgerPath ? deps.gradeLedgerPath(task.id) : gradeLedgerPath(task.id);
|
|
680
|
+
// Same client rule as verifyShip below: an injected api is used as given.
|
|
681
|
+
let capApi = deps.api || null;
|
|
682
|
+
if (!capApi) { try { capApi = await (require('./cli-lib').cliClient)(); } catch (_) { capApi = null; } }
|
|
683
|
+
const capCheck = await gradeCapReader({ api: capApi, taskId: task.id, ledger: gradeLedger });
|
|
684
|
+
const res = await spawnWork({ task, model: opts.model, timeoutMs: opts.timeoutMs, cwd: wtPath, gradeLedger, capCheck, deps });
|
|
685
|
+
let verdict = classifyRun(res);
|
|
686
|
+
// 5b. THE GRADE CAP (task 1004536). The owner's rule — a task that fails the
|
|
687
|
+
// grade twice goes to the owner — read from the run's ledger, not from the
|
|
688
|
+
// worker's verdict: the worker was told this in its prompt and kept re-grading.
|
|
689
|
+
const grades = await capCheck();
|
|
690
|
+
try { fs.rmSync(gradeLedger, { force: true }); } catch (_) {} // its counts travel in the run-log row
|
|
691
|
+
if (grades && grades.exhausted) {
|
|
692
|
+
verdict = {
|
|
693
|
+
...verdict, outcome: 'needs_human', releaseClaim: true,
|
|
694
|
+
reason: `the grade failed ${grades.fails} times in this run — handed to the owner instead of re-graded again (task 1004536)`,
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
// Cut off by the usage limit? Recorded here so the NEXT iteration sleeps to the
|
|
698
|
+
// reset instead of spawning a worker that will be cut off the same way.
|
|
699
|
+
const limit = verdict.outcome === 'claims_shipped' ? null : usageLimitHit({ envelope: res.envelope, stderr: res.stderr });
|
|
700
|
+
if (limit) state.limitUntil = limit.resetAt ? limit.resetAt * 1000 : now + LIMIT_UNKNOWN_RESET_MS;
|
|
701
|
+
|
|
702
|
+
// 6. VERIFY FROM THE LEDGER (task 1003903). Read the task back from Bongos and
|
|
703
|
+
// let IT say what happened — for every outcome, not only a claimed ship. The
|
|
704
|
+
// worker's own verdict becomes context in the blocker body, never the branch: a
|
|
705
|
+
// worker that stopped dead needing a decision and one that finished the job
|
|
706
|
+
// return structurally identical envelopes, and the ledger is the only thing
|
|
707
|
+
// that can tell them apart.
|
|
708
|
+
//
|
|
709
|
+
// The release decision moved HERE from the worker's verdict, and that move is
|
|
710
|
+
// the point of the step rather than a tidy-up. `still_held` is a fact about the
|
|
711
|
+
// ledger; `worker_failed` is a report from the thing being checked. The TREE is
|
|
712
|
+
// left alone either way — it may hold committed work, and the land/ship
|
|
713
|
+
// pipeline owns worktree cleanup.
|
|
714
|
+
const ledger = await verifyShip(
|
|
715
|
+
{ taskId: task.id, taskTitle: task.title, worker: { outcome: verdict.outcome, reason: verdict.reason } },
|
|
716
|
+
{
|
|
717
|
+
api: deps.api,
|
|
718
|
+
cwd: wtPath,
|
|
719
|
+
release: deps.release || ((id, reason) => runCmd(process.execPath,
|
|
720
|
+
[path.join(REPO_ROOT, 'scripts', 'bongos', 'release.js'), String(id), '--reason', reason], { cwd: wtPath })),
|
|
721
|
+
...(deps.verifyDeps || {}),
|
|
722
|
+
},
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
const row = {
|
|
726
|
+
event: 'worked', task_id: task.id, title: task.title, goal_id: picked.goalId,
|
|
727
|
+
...pickedFrom(picked.goalId, gate),
|
|
728
|
+
worktree: wtName,
|
|
729
|
+
outcome: verdict.outcome, reason: verdict.reason,
|
|
730
|
+
session_id: res.sessionId || null, duration_s: Math.round((Date.now() - started) / 1000),
|
|
731
|
+
cost_usd: verdict.costUsd ?? null,
|
|
732
|
+
...(grades && (grades.graded || grades.outages) ? { grade_rounds: grades.graded, grade_outages: grades.outages, grade_cap_reached: grades.exhausted, grade_count_source: grades.source } : {}),
|
|
733
|
+
output_truncated: !!res.truncated,
|
|
734
|
+
...(limit ? { usage_limit: { reset_at: limit.resetAt } } : {}),
|
|
735
|
+
undetermined_decisions: verdict.verdict ? verdict.verdict.undetermined_decisions : null,
|
|
736
|
+
// What the LEDGER says, kept separate from what the worker said, so the run
|
|
737
|
+
// log can be read afterwards without having to trust either one alone.
|
|
738
|
+
ledger_status: ledger.status, ledger_shape: ledger.shape, verified: !!ledger.verified,
|
|
739
|
+
// The LEDGER's own explanation, kept beside the worker's. `reason` above is
|
|
740
|
+
// verdict.reason — what the WORKER said happened — and it was the only reason
|
|
741
|
+
// in the row, so anything reading the log afterwards was quoting the thing
|
|
742
|
+
// being checked rather than the check. The morning digest did exactly that
|
|
743
|
+
// until the grader caught it (task 1003906).
|
|
744
|
+
ledger_reason: ledger.reason || null,
|
|
745
|
+
ledger_remedy: ledger.remedy || null,
|
|
746
|
+
artifact_on_main: ledger.artifact && ledger.artifact.checked ? !!ledger.artifact.onMain : null,
|
|
747
|
+
claim_released: !!ledger.released,
|
|
748
|
+
blocker_id: ledger.blocker && ledger.blocker.blockerId ? ledger.blocker.blockerId : null,
|
|
749
|
+
task_parked: !!(ledger.blocker && ledger.blocker.parked),
|
|
750
|
+
};
|
|
751
|
+
// An unreadable ledger stops the run. Picking another task while unable to see
|
|
752
|
+
// what happened to the last one is how a machine nobody is watching digs.
|
|
753
|
+
if (ledger.halt) row.event = 'verify_halt';
|
|
754
|
+
return log(row);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// ---------------------------------------------------------------------------
|
|
758
|
+
// Boot recovery, and the forever loop (task 1003904, ADR 0307 §5)
|
|
759
|
+
// ---------------------------------------------------------------------------
|
|
760
|
+
|
|
761
|
+
// reconcileLeftoverClaims — the FIRST thing a run does.
|
|
762
|
+
//
|
|
763
|
+
// "A silently locked task is the worst outcome this loop can produce" is the
|
|
764
|
+
// task's own wording, and cutting power mid-task is exactly how it happens: the
|
|
765
|
+
// claim is held by a session that no longer exists anywhere, and nothing times it
|
|
766
|
+
// out. So before taking any new work, every claim a previous run left behind is
|
|
767
|
+
// read back from the ledger and resolved.
|
|
768
|
+
//
|
|
769
|
+
// The reconciliation is autobongos-verify.js, unchanged (task 1003903). That file
|
|
770
|
+
// already answers "did this actually ship, and if not what do I do about it" for
|
|
771
|
+
// a worker that just exited; a worker that exited three days ago in a power cut
|
|
772
|
+
// is the same question with a longer gap. Writing a second, subtly different
|
|
773
|
+
// answer here is how two code paths end up disagreeing about what `confirmed`
|
|
774
|
+
// means.
|
|
775
|
+
//
|
|
776
|
+
// ONLY TREES THE RUNNER ITSELF MADE, matched against the EXACT shape
|
|
777
|
+
// worktreeName() produces — not a loose prefix.
|
|
778
|
+
//
|
|
779
|
+
// FOUND THE HARD WAY, twice. `autobongos-` as a prefix reads as "ours", and it is
|
|
780
|
+
// not: a person naming a worktree `autobongos-resume-1003904-d29f25` while
|
|
781
|
+
// working on this very feature matched it, and reconciliation released their live
|
|
782
|
+
// claim out from under them. A human at a keyboard picks readable names; the
|
|
783
|
+
// runner picks `autobongos-<task id>-<6 hex>` and nothing else. Matching the
|
|
784
|
+
// generator's own shape is the difference between "probably ours" and "ours".
|
|
785
|
+
//
|
|
786
|
+
// Derived FROM worktreeName so the two cannot drift: change the name format and
|
|
787
|
+
// this regex is what stops the next reconciliation from quietly matching nothing.
|
|
788
|
+
const CLAIM_PREFIX = 'autobongos-';
|
|
789
|
+
const RUNNER_TREE_RE = /^autobongos-\d+-[0-9a-f]{6}$/;
|
|
790
|
+
const isRunnerTree = (name) => RUNNER_TREE_RE.test(String(name || ''));
|
|
791
|
+
|
|
792
|
+
// ── the heartbeat, and why reconciliation needs one ──────────────────────────
|
|
793
|
+
//
|
|
794
|
+
// FOUND BY RUNNING IT. The first live run of reconcileLeftoverClaims released a
|
|
795
|
+
// claim that was very much alive — the one held by the session writing this code.
|
|
796
|
+
// It was right by its own rules (an `autobongos-` tree, a task sitting at
|
|
797
|
+
// `active`), and that is the problem: THE LEDGER CANNOT TELL A LIVE RUN FROM A
|
|
798
|
+
// DEAD ONE. Both look like a held claim. A reconciler that guesses wrong in one
|
|
799
|
+
// direction leaves a task locked forever; in the other it yanks a claim out from
|
|
800
|
+
// under a worker mid-build and strands whatever it had committed.
|
|
801
|
+
//
|
|
802
|
+
// So the runner says, on disk, that it is alive. A heartbeat file carries the pid
|
|
803
|
+
// and a timestamp; reconciliation refuses to touch anything while one is FRESH
|
|
804
|
+
// and its pid still exists. Both halves are needed — a stale file from a power
|
|
805
|
+
// cut must not veto recovery forever, and a fresh file whose process is gone is
|
|
806
|
+
// just litter. This is also what stops two runners on one machine from fighting
|
|
807
|
+
// over each other's claims.
|
|
808
|
+
const HEARTBEAT_STALE_MS = 10 * 60 * 1000;
|
|
809
|
+
|
|
810
|
+
function heartbeatPath() {
|
|
811
|
+
if (process.env.AUTOBONGOS_HEARTBEAT_FILE) return process.env.AUTOBONGOS_HEARTBEAT_FILE;
|
|
812
|
+
try { return require('../../src/instance-config.js').configPath('autobongos-heartbeat.json'); }
|
|
813
|
+
catch (_) { return path.join(REPO_ROOT, '.autobongos-heartbeat.json'); }
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
// Best-effort in both directions: a runner that cannot write its heartbeat must
|
|
817
|
+
// still run (it only loses the guard), and one that cannot read it must assume
|
|
818
|
+
// nothing is alive — the file's absence is the common case, on every first boot.
|
|
819
|
+
//
|
|
820
|
+
// DO NOT point AUTOBONGOS_HEARTBEAT_FILE UNDER /proc TO TEST THE FAILURE PATH (task
|
|
821
|
+
// 1004052). The mkdirSync below is recursive, and on Linux a recursive mkdir under
|
|
822
|
+
// /proc never returns — measured 10s+ with no result, where the same call throws
|
|
823
|
+
// EACCES instantly under /sys or /, and the NON-recursive form throws ENOENT instantly
|
|
824
|
+
// under /proc itself. macOS has no /proc, so it fails fast there. A test that used it
|
|
825
|
+
// was green on every developer machine and hung the Ubuntu CI runner for 90s with no
|
|
826
|
+
// output, reddening `unit` on main and blocking every PR. Use a regular FILE as the
|
|
827
|
+
// parent directory instead: mkdir under it is ENOTDIR immediately, everywhere, and it
|
|
828
|
+
// does not depend on the caller's permissions.
|
|
829
|
+
function writeHeartbeat(now = Date.now(), runnerState = RUNNER_STATE) {
|
|
830
|
+
try {
|
|
831
|
+
const p = heartbeatPath();
|
|
832
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
833
|
+
// `head` is what this process is RUNNING, not what is on disk (task 1004374).
|
|
834
|
+
// Alive and alive-on-stale-code look identical from a check-in age alone.
|
|
835
|
+
// `disk_head` appears only when the checkout was refreshed in place past what
|
|
836
|
+
// is running (task 1004406): "up to date on disk, old supervisor" is visible.
|
|
837
|
+
const disk = runnerState && runnerState.diskHead && runnerState.diskHead !== LOADED_HEAD ? runnerState.diskHead : undefined;
|
|
838
|
+
fs.writeFileSync(p, JSON.stringify({ pid: process.pid, at: now, head: LOADED_HEAD, ...(disk ? { disk_head: disk } : {}) }));
|
|
839
|
+
return true;
|
|
840
|
+
} catch (_) { return false; }
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// postHeartbeat — tell the SERVER we are alive (task 1003905).
|
|
844
|
+
//
|
|
845
|
+
// The file above guards against two runners on this machine; this is the half an
|
|
846
|
+
// owner can see from a web page in another country. Both matter and neither
|
|
847
|
+
// replaces the other: a file is unreadable on exactly the day the machine is off.
|
|
848
|
+
//
|
|
849
|
+
// FAILS SILENTLY, ON PURPOSE. A runner that cannot check in must keep working —
|
|
850
|
+
// letting the watching mechanism take down the thing it watches is a strictly
|
|
851
|
+
// worse outcome than a gap in the log. The gap reports itself anyway: the hall
|
|
852
|
+
// reads the AGE of the last check-in, so a failed post shows up as silence, which
|
|
853
|
+
// is the signal.
|
|
854
|
+
const RUNNER_STARTED_AT = new Date().toISOString();
|
|
855
|
+
|
|
856
|
+
// claudeAccountLabel — WHICH Claude account this machine's workers spend
|
|
857
|
+
// (task 1004407), as a label and never a secret. Workers inherit
|
|
858
|
+
// ANTHROPIC_API_KEY and CLAUDE_CODE_OAUTH_TOKEN from this process
|
|
859
|
+
// (WORKER_ENV_ALLOW), and either one outranks the stored login, so those are
|
|
860
|
+
// named first, by the variable that is set and never by its value. Otherwise it
|
|
861
|
+
// is the login Claude Code itself records in ~/.claude.json (oauthAccount): the
|
|
862
|
+
// email, plus the organisation when it has one. Read on every beat, so signing
|
|
863
|
+
// a different account in shows up without a restart; null when nothing can be
|
|
864
|
+
// read, which the card leaves blank rather than guessing.
|
|
865
|
+
function claudeAccountLabel({ env = process.env, readFile = (p) => fs.readFileSync(p, 'utf8') } = {}) {
|
|
866
|
+
if (env.ANTHROPIC_API_KEY) return 'an API key (ANTHROPIC_API_KEY)';
|
|
867
|
+
if (env.CLAUDE_CODE_OAUTH_TOKEN) return 'a Claude token (CLAUDE_CODE_OAUTH_TOKEN)';
|
|
868
|
+
try {
|
|
869
|
+
const dir = env.CLAUDE_CONFIG_DIR || os.homedir();
|
|
870
|
+
const acct = JSON.parse(readFile(path.join(dir, '.claude.json'))).oauthAccount || {};
|
|
871
|
+
const email = typeof acct.emailAddress === 'string' ? acct.emailAddress.trim() : '';
|
|
872
|
+
if (!email) return null;
|
|
873
|
+
const org = typeof acct.organizationName === 'string' ? acct.organizationName.trim() : '';
|
|
874
|
+
return (org ? `${email} · ${org}` : email).slice(0, 200);
|
|
875
|
+
} catch (_) { return null; }
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
// heartbeatIdentity — the fields that say which code and which account (task
|
|
879
|
+
// 1004407). Undefined fields are dropped, so a runner that cannot read its own
|
|
880
|
+
// commit sends nothing for it rather than an empty string the route would refuse.
|
|
881
|
+
function heartbeatIdentity(deps = {}) {
|
|
882
|
+
const st = deps.state || RUNNER_STATE;
|
|
883
|
+
const out = {
|
|
884
|
+
head: LOADED_HEAD || undefined,
|
|
885
|
+
disk_head: st.diskHead && st.diskHead !== LOADED_HEAD ? st.diskHead : undefined,
|
|
886
|
+
main_head: st.mainHead || undefined,
|
|
887
|
+
claude_account: (deps.claudeAccountLabel || claudeAccountLabel)() || undefined,
|
|
888
|
+
};
|
|
889
|
+
for (const k of Object.keys(out)) if (out[k] === undefined) delete out[k];
|
|
890
|
+
return out;
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
async function postHeartbeat(fields = {}, deps = {}) {
|
|
894
|
+
try {
|
|
895
|
+
const api = deps.api || await (require('./cli-lib').cliClient)();
|
|
896
|
+
await api.autonomy.postAutonomyHeartbeat({
|
|
897
|
+
body: {
|
|
898
|
+
host: os.hostname(),
|
|
899
|
+
pid: process.pid,
|
|
900
|
+
started_at: RUNNER_STARTED_AT,
|
|
901
|
+
...heartbeatIdentity(deps),
|
|
902
|
+
...fields,
|
|
903
|
+
},
|
|
904
|
+
});
|
|
905
|
+
return true;
|
|
906
|
+
} catch (_) { return false; }
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
function readHeartbeat() {
|
|
910
|
+
try { return JSON.parse(fs.readFileSync(heartbeatPath(), 'utf8')); }
|
|
911
|
+
catch (_) { return null; }
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// pidAlive — `kill(pid, 0)` signals nothing and throws ESRCH when the process is
|
|
915
|
+
// gone. EPERM means it exists and belongs to someone else, which still counts as
|
|
916
|
+
// alive: the question is "is something running", not "is it mine".
|
|
917
|
+
function pidAlive(pid) {
|
|
918
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
919
|
+
try { process.kill(pid, 0); return true; }
|
|
920
|
+
catch (e) { return e && e.code === 'EPERM'; }
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
// anotherRunnerIsAlive — the veto. Fresh file AND a live pid AND not us.
|
|
924
|
+
function anotherRunnerIsAlive({ now = Date.now(), hb = readHeartbeat() } = {}) {
|
|
925
|
+
if (!hb || !Number.isFinite(Number(hb.at))) return false;
|
|
926
|
+
if (Number(hb.pid) === process.pid) return false; // our own beat
|
|
927
|
+
if (now - Number(hb.at) > HEARTBEAT_STALE_MS) return false; // a power cut leaves a stale file
|
|
928
|
+
return pidAlive(Number(hb.pid));
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
async function reconcileLeftoverClaims(deps = {}) {
|
|
932
|
+
const api = deps.api || await (require('./cli-lib').cliClient)();
|
|
933
|
+
const log = deps.record || record;
|
|
934
|
+
const verify = deps.verifyShip || verifyShip;
|
|
935
|
+
const runCmd = deps.run || run;
|
|
936
|
+
const isAlive = deps.anotherRunnerIsAlive || anotherRunnerIsAlive;
|
|
937
|
+
|
|
938
|
+
// Refuse outright while another runner is beating. Releasing a claim a live
|
|
939
|
+
// worker is building against strands whatever it has committed — the same harm
|
|
940
|
+
// as leaving one locked, pointed the other way.
|
|
941
|
+
if (isAlive()) return log({ event: 'boot_reconcile', ok: true, skipped: 'another runner is alive', leftover: null });
|
|
942
|
+
|
|
943
|
+
let claims = [];
|
|
944
|
+
try {
|
|
945
|
+
const me = await api.me.getMe();
|
|
946
|
+
if (!me || !me.ok) return log({ event: 'boot_reconcile', ok: false, reason: `GET /me → ${me ? me.status : 'no response'}` });
|
|
947
|
+
claims = (me.data.active_claims || []).filter((c) => isRunnerTree(c.worktree_name));
|
|
948
|
+
} catch (e) {
|
|
949
|
+
// Unreadable is NOT "nothing to reconcile". Say so and let the cadence treat
|
|
950
|
+
// it as a failure, rather than starting work on top of an unknown lock.
|
|
951
|
+
return log({ event: 'boot_reconcile', ok: false, reason: e && e.message ? e.message : String(e) });
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
if (!claims.length) return log({ event: 'boot_reconcile', ok: true, leftover: 0 });
|
|
955
|
+
|
|
956
|
+
const resolved = [];
|
|
957
|
+
for (const c of claims) {
|
|
958
|
+
const wtPath = path.join(REPO_ROOT, '.claude', 'worktrees', c.worktree_name);
|
|
959
|
+
const out = await verify(
|
|
960
|
+
{ taskId: c.task_id, taskTitle: c.title, worker: { outcome: 'interrupted', reason: 'a previous run did not finish this claim' } },
|
|
961
|
+
{
|
|
962
|
+
api,
|
|
963
|
+
// The tree may be gone — a power cut leaves the claim, not necessarily the
|
|
964
|
+
// checkout. verifyShip only uses cwd for the artifact probe, which degrades
|
|
965
|
+
// to `checked: false` rather than failing, so a missing path is survivable.
|
|
966
|
+
cwd: fs.existsSync(wtPath) ? wtPath : REPO_ROOT,
|
|
967
|
+
release: (id, reason) => runCmd(process.execPath,
|
|
968
|
+
[path.join(REPO_ROOT, 'scripts', 'bongos', 'release.js'), String(id), '--reason', reason],
|
|
969
|
+
{ cwd: fs.existsSync(wtPath) ? wtPath : REPO_ROOT }),
|
|
970
|
+
},
|
|
971
|
+
);
|
|
972
|
+
resolved.push({ task_id: String(c.task_id), worktree: c.worktree_name, shape: out.shape, released: !!out.released, blocker_id: out.blocker?.blockerId ?? null });
|
|
973
|
+
}
|
|
974
|
+
return log({ event: 'boot_reconcile', ok: true, leftover: claims.length, resolved });
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
// pollSignals — the cheap facts whose change makes waiting pointless.
|
|
978
|
+
//
|
|
979
|
+
// Three reads, all small, all failing SILENTLY to null. A failed probe is not a
|
|
980
|
+
// signal: treating it as one would turn an unreachable instance into a busy loop
|
|
981
|
+
// hammering it, which is the exact opposite of backing off.
|
|
982
|
+
async function pollSignals(deps = {}) {
|
|
983
|
+
const api = deps.api || await (require('./cli-lib').cliClient)();
|
|
984
|
+
let head = null; let fenceUpdatedAt = null; let claimableCount = null;
|
|
985
|
+
try {
|
|
986
|
+
const r = await runFetchHead(deps);
|
|
987
|
+
if (r) head = r;
|
|
988
|
+
} catch (_) { /* null */ }
|
|
989
|
+
try {
|
|
990
|
+
const f = await api.autonomy.getAutonomyFence();
|
|
991
|
+
if (f && f.ok) fenceUpdatedAt = f.data.updated_at || null;
|
|
992
|
+
} catch (_) { /* null */ }
|
|
993
|
+
try {
|
|
994
|
+
const ids = await seq.fetchClaimableIds(api);
|
|
995
|
+
if (ids) claimableCount = ids.size;
|
|
996
|
+
} catch (_) { /* null */ }
|
|
997
|
+
return cadence.signalFingerprint({ head, fenceUpdatedAt, claimableCount });
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
// UPGRADE_EXIT_CODE — the one exit `--forever` is allowed to make (task 1004374).
|
|
1001
|
+
//
|
|
1002
|
+
// THE PROBLEM. autobongos-service.cmd refreshes the checkout with git fetch +
|
|
1003
|
+
// reset --hard origin/main BEFORE launching node, and only then. `--forever`
|
|
1004
|
+
// never returns, by design (task 1003904): a stopped daemon on an always-on
|
|
1005
|
+
// machine is indistinguishable from a working one. Together those mean a
|
|
1006
|
+
// process never re-reads its code, so a shipped fix reaches the disk and never
|
|
1007
|
+
// reaches the runner, and nothing restarts it — not stopping IS the design. On
|
|
1008
|
+
// the host this ran week-old code for four days across three landed fixes,
|
|
1009
|
+
// while the heartbeat said alive and the hall showed a healthy runner.
|
|
1010
|
+
//
|
|
1011
|
+
// WHY AN EXIT IS SAFE HERE, when task 1003904 forbids exiting. That rule exists
|
|
1012
|
+
// so a machine nobody watches cannot look idle while it is dead. This exit is
|
|
1013
|
+
// the opposite: it is deliberate, it is logged as `upgrade_exit`, and the
|
|
1014
|
+
// WRAPPER loops on this specific code — re-running the fetch + reset and
|
|
1015
|
+
// relaunching — so the runner is not depending on Task Scheduler noticing.
|
|
1016
|
+
// Every other exit still falls through to the scheduler exactly as before.
|
|
1017
|
+
const UPGRADE_EXIT_CODE = 75;
|
|
1018
|
+
|
|
1019
|
+
// MIN_UPTIME_BEFORE_UPGRADE_MS — the guard against a restart storm.
|
|
1020
|
+
//
|
|
1021
|
+
// The wrapper's `git reset --hard origin/main` is BEST EFFORT: a fetch that
|
|
1022
|
+
// fails leaves the checkout behind on purpose, because being a commit behind
|
|
1023
|
+
// beats not running at all. Without this guard that becomes a spin — the runner
|
|
1024
|
+
// starts behind, sees drift on its first loop, exits, and the wrapper relaunches
|
|
1025
|
+
// it into the same state as fast as the machine allows. One minute of required
|
|
1026
|
+
// uptime turns a failing fetch into a slow retry instead.
|
|
1027
|
+
const MIN_UPTIME_BEFORE_UPGRADE_MS = 60_000;
|
|
1028
|
+
|
|
1029
|
+
// loadedHead — the commit this PROCESS is running, read once. Not re-read: the
|
|
1030
|
+
// point is to compare what is executing against what has landed, and re-reading
|
|
1031
|
+
// would just track the disk, which the wrapper already moves.
|
|
1032
|
+
let LOADED_HEAD = null;
|
|
1033
|
+
async function loadedHead(deps = {}) {
|
|
1034
|
+
if (LOADED_HEAD !== null) return LOADED_HEAD;
|
|
1035
|
+
const runCmd = deps.run || run;
|
|
1036
|
+
try {
|
|
1037
|
+
const r = await runCmd('git', ['rev-parse', 'HEAD'], { cwd: REPO_ROOT });
|
|
1038
|
+
LOADED_HEAD = r && r.ok ? String(r.stdout || '').trim() || null : null;
|
|
1039
|
+
} catch (_) { LOADED_HEAD = null; }
|
|
1040
|
+
return LOADED_HEAD;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// codeDrifted — has main moved past the code this process loaded?
|
|
1044
|
+
//
|
|
1045
|
+
// Fails CLOSED in the "keep running" direction: an unreadable local head or an
|
|
1046
|
+
// unreachable origin returns false, so a network blip never cycles the runner.
|
|
1047
|
+
// The cost of a missed upgrade is one more loop; the cost of a false positive is
|
|
1048
|
+
// a restart storm.
|
|
1049
|
+
async function codeDrifted(deps = {}) {
|
|
1050
|
+
// Injectable like every other collaborator here: the module-level cache is
|
|
1051
|
+
// right for production (read the running commit ONCE) and would otherwise make
|
|
1052
|
+
// the comparison untestable in isolation.
|
|
1053
|
+
const mine = deps.loadedHead ? await deps.loadedHead(deps) : await loadedHead(deps);
|
|
1054
|
+
if (!mine) return null;
|
|
1055
|
+
// Measured from what is on DISK once the checkout has been refreshed in place
|
|
1056
|
+
// (task 1004406), or every loop after a refresh would see the same drift again.
|
|
1057
|
+
const base = ((deps.state || RUNNER_STATE).diskHead) || mine;
|
|
1058
|
+
let tip = null;
|
|
1059
|
+
try { tip = await runFetchHead(deps); } catch (_) { return null; }
|
|
1060
|
+
if (!tip) return null;
|
|
1061
|
+
// Kept for the heartbeat (task 1004407), so the hall can say "behind main"
|
|
1062
|
+
// without the server ever reaching GitHub itself.
|
|
1063
|
+
(deps.state || RUNNER_STATE).mainHead = tip;
|
|
1064
|
+
return tip !== base ? { from: base.slice(0, 8), to: String(tip).slice(0, 8) } : false;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// ── pull always, restart rarely (task 1004406) ──────────────────────────────
|
|
1068
|
+
//
|
|
1069
|
+
// Owner-approved 2026-09-30. Main moves every time ANY session lands, and a
|
|
1070
|
+
// relaunch per merge meant a busy day of restarts (four in fourteen minutes on
|
|
1071
|
+
// 2026-09-29). But a restart only refreshes code THIS process holds in memory:
|
|
1072
|
+
// every worker, and every claim.js / worktree.js / release.js the loop shells
|
|
1073
|
+
// out to, is a fresh process that runs whatever is on disk. So on drift the
|
|
1074
|
+
// checkout is updated in place, and the process exits for the wrapper only when
|
|
1075
|
+
// a change touches code it has actually loaded.
|
|
1076
|
+
//
|
|
1077
|
+
// SUPERVISOR_PATH_RES catch the supervisor's own files even before they are
|
|
1078
|
+
// required (autobongos-verify.js is loaded lazily on the first ship), so a change
|
|
1079
|
+
// to a file the loop is ABOUT to load cannot mix old callers with a new callee.
|
|
1080
|
+
const SUPERVISOR_PATH_RES = [/^scripts\/(?:gds|bongos)\/autobongos-[^/]+$/, /^modules\/autonomy\//];
|
|
1081
|
+
|
|
1082
|
+
// The repo files this process has loaded, repo-relative with forward slashes.
|
|
1083
|
+
function loadedFiles() {
|
|
1084
|
+
const root = REPO_ROOT + path.sep;
|
|
1085
|
+
const nm = path.sep + 'node_modules' + path.sep;
|
|
1086
|
+
return Object.keys(require.cache)
|
|
1087
|
+
.filter((p) => p.startsWith(root) && !p.includes(nm))
|
|
1088
|
+
.map((p) => path.relative(REPO_ROOT, p).split(path.sep).join('/'));
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
// The changed files that force a restart: ones this process holds, or ones under
|
|
1092
|
+
// the supervisor's own paths. Everything else is picked up by the next child.
|
|
1093
|
+
function supervisorTouched(changed = [], loaded = loadedFiles()) {
|
|
1094
|
+
const held = new Set(loaded);
|
|
1095
|
+
return changed.filter((f) => held.has(f) || SUPERVISOR_PATH_RES.some((re) => re.test(f)));
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
// refreshCheckout — the wrapper's fetch + reset, done from inside the process.
|
|
1099
|
+
//
|
|
1100
|
+
// THE MAIN CHECKOUT ONLY. It runs `git reset --hard`, and a linked worktree is a
|
|
1101
|
+
// claim somebody is building in; resetting one would destroy their work. The
|
|
1102
|
+
// check compares git-dir to git-common-dir, which are equal only in the main
|
|
1103
|
+
// checkout. Armed only by main() (`opts.refreshInPlace`), so a test driving
|
|
1104
|
+
// forever() can never reach it — in CI that reset would wipe the code under test.
|
|
1105
|
+
//
|
|
1106
|
+
// The diff is from the LOADED commit, not the last refresh, so a supervisor
|
|
1107
|
+
// change that landed several refreshes ago still forces the restart it needs.
|
|
1108
|
+
async function refreshCheckout(deps = {}) {
|
|
1109
|
+
const runCmd = deps.run || run;
|
|
1110
|
+
const git = (args) => runCmd('git', args, { cwd: REPO_ROOT });
|
|
1111
|
+
try {
|
|
1112
|
+
const [gd, cd] = await Promise.all([git(['rev-parse', '--git-dir']), git(['rev-parse', '--git-common-dir'])]);
|
|
1113
|
+
if (!gd.ok || !cd.ok) return { ok: false, reason: 'could not tell which checkout this is' };
|
|
1114
|
+
if (path.resolve(REPO_ROOT, String(gd.stdout).trim()) !== path.resolve(REPO_ROOT, String(cd.stdout).trim())) {
|
|
1115
|
+
return { ok: false, reason: 'not the main checkout — refusing to reset a linked worktree' };
|
|
1116
|
+
}
|
|
1117
|
+
const loaded = deps.loadedHead ? await deps.loadedHead(deps) : await loadedHead(deps);
|
|
1118
|
+
if (!loaded) return { ok: false, reason: 'the running commit is unknown' };
|
|
1119
|
+
if (!(await git(['fetch', '--quiet', 'origin', 'main'])).ok) return { ok: false, reason: 'fetch failed' };
|
|
1120
|
+
const fh = await git(['rev-parse', 'FETCH_HEAD']);
|
|
1121
|
+
const to = fh.ok ? String(fh.stdout).trim() : '';
|
|
1122
|
+
if (!/^[0-9a-f]{40}$/.test(to)) return { ok: false, reason: 'could not read the fetched commit' };
|
|
1123
|
+
const diff = await git(['diff', '--name-only', loaded, to]);
|
|
1124
|
+
if (!diff.ok) return { ok: false, reason: 'could not diff the running commit against main' };
|
|
1125
|
+
if (!(await git(['reset', '--quiet', '--hard', to])).ok) return { ok: false, reason: 'reset failed' };
|
|
1126
|
+
const changed = String(diff.stdout).split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
1127
|
+
return { ok: true, to, changed };
|
|
1128
|
+
} catch (e) {
|
|
1129
|
+
return { ok: false, reason: e && e.message ? e.message : String(e) };
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
// The head of origin/main, or null. `git ls-remote` rather than `fetch`: it moves
|
|
1134
|
+
// no refs and writes nothing, so polling it every few minutes cannot interfere
|
|
1135
|
+
// with a worker building in a tree beside it.
|
|
1136
|
+
async function runFetchHead(deps = {}) {
|
|
1137
|
+
const runCmd = deps.run || run;
|
|
1138
|
+
const r = await runCmd('git', ['ls-remote', 'origin', 'refs/heads/main']);
|
|
1139
|
+
if (!r.ok) return null;
|
|
1140
|
+
const m = /^([0-9a-f]{40})/.exec(String(r.stdout).trim());
|
|
1141
|
+
return m ? m[1] : null;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
// waitOrJump — sleep, but wake early if the world moved.
|
|
1145
|
+
//
|
|
1146
|
+
// Polls at most every `pollEveryS`, so a thirty-minute backoff costs six cheap
|
|
1147
|
+
// probes rather than a spin. `sleep` is injected so a test can drive an hour of
|
|
1148
|
+
// wall clock in a millisecond.
|
|
1149
|
+
async function waitOrJump(waitS, deps = {}, cfg = {}) {
|
|
1150
|
+
if (!(waitS > 0)) return { waited: 0, jumped: false };
|
|
1151
|
+
const pollEveryS = cfg.pollEveryS ?? 60;
|
|
1152
|
+
const sleep = deps.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
1153
|
+
const poll = deps.pollSignals || pollSignals;
|
|
1154
|
+
|
|
1155
|
+
const before = await poll(deps);
|
|
1156
|
+
let waited = 0;
|
|
1157
|
+
while (waited < waitS) {
|
|
1158
|
+
const chunk = Math.min(pollEveryS, waitS - waited);
|
|
1159
|
+
await sleep(chunk * 1000);
|
|
1160
|
+
waited += chunk;
|
|
1161
|
+
// Beat DURING the wait. A thirty-minute backoff is the runner working exactly
|
|
1162
|
+
// as designed, and it must not be indistinguishable from a machine that fell
|
|
1163
|
+
// over at the start of it.
|
|
1164
|
+
if (deps.beat) { try { await deps.beat({ last_event: 'waiting' }); } catch (_) { /* never break a wait */ } }
|
|
1165
|
+
if (waited >= waitS) break;
|
|
1166
|
+
const now = await poll(deps);
|
|
1167
|
+
if (cadence.shouldJumpWait(before, now)) return { waited, jumped: true, signal: now };
|
|
1168
|
+
}
|
|
1169
|
+
return { waited, jumped: false };
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// forever — the loop that has no terminal stop (task 1003904, ADR 0307 §5).
|
|
1173
|
+
//
|
|
1174
|
+
// It is a `while (true)` on purpose and the exceptions are the interesting part:
|
|
1175
|
+
// NOTHING in here returns. Not an unreachable instance, not an expired token, not
|
|
1176
|
+
// the kill switch. The only exits are a signal from the operating system and
|
|
1177
|
+
// `--max-loops`, which exists so a test can run the machine to completion.
|
|
1178
|
+
//
|
|
1179
|
+
// What varies is the SPEED, and the speed is decided entirely by
|
|
1180
|
+
// modules/autonomy/cadence.js from the row the last iteration recorded. This
|
|
1181
|
+
// function holds no policy — it turns a `waitS` into a wait and a `takeWork` into
|
|
1182
|
+
// an iteration, so the rules stay somewhere a test can reach without a clock.
|
|
1183
|
+
async function forever(opts, deps = {}) {
|
|
1184
|
+
const log = deps.record || record;
|
|
1185
|
+
const iterate = deps.iteration || iteration;
|
|
1186
|
+
const reconcile = deps.reconcileLeftoverClaims || reconcileLeftoverClaims;
|
|
1187
|
+
const wait = deps.waitOrJump || waitOrJump;
|
|
1188
|
+
const emit = deps.emit || ((row) => {
|
|
1189
|
+
if (opts.json) console.log(JSON.stringify(row));
|
|
1190
|
+
else console.log(`${row.event}${row.task_id ? ` task ${row.task_id}` : ''}${row.outcome ? ` → ${row.outcome}` : ''}${row.reason ? `: ${row.reason}` : ''}`);
|
|
1191
|
+
});
|
|
1192
|
+
|
|
1193
|
+
// Claim the machine BEFORE reconciling: a second runner starting a moment later
|
|
1194
|
+
// must see this one alive rather than deciding its claims are litter.
|
|
1195
|
+
// Both halves of "I am alive": the local file (which stops a second runner
|
|
1196
|
+
// treating this one's claims as litter) and the server row (which is the only
|
|
1197
|
+
// one an owner can see from anywhere else).
|
|
1198
|
+
const beatFile = deps.writeHeartbeat || writeHeartbeat;
|
|
1199
|
+
const beatServer = deps.postHeartbeat || postHeartbeat;
|
|
1200
|
+
// WRAPPED AT THE CALL SITE, not just inside postHeartbeat. The guarantee is
|
|
1201
|
+
// "reporting that I am alive can never be what kills me", and that has to hold
|
|
1202
|
+
// for whatever beat implementation is in play — a future one, an injected one —
|
|
1203
|
+
// not only for the well-behaved default. Letting the watching mechanism take
|
|
1204
|
+
// down the thing it watches is strictly worse than a gap in the log, and the gap
|
|
1205
|
+
// reports itself anyway: the hall reads the AGE of the last check-in, so a
|
|
1206
|
+
// failed beat shows up as silence, which is the signal.
|
|
1207
|
+
// How long this runner has been idle on a setting the owner can fix (task
|
|
1208
|
+
// 1004539). Past that setting's grace, every beat that is not about a task says
|
|
1209
|
+
// `config_idle:<code>` instead of "waiting", so the hall shows the fix rather
|
|
1210
|
+
// than "alive" for ten hours. Any row that is not that refusal ends the stretch.
|
|
1211
|
+
let idle = null;
|
|
1212
|
+
const clock = () => (deps.now ? deps.now() : Date.now());
|
|
1213
|
+
const beat = async (rawFields) => {
|
|
1214
|
+
const fields = configIdle.markBeat(rawFields, idle, clock());
|
|
1215
|
+
// The runner state is passed, not defaulted: the file names `disk_head` from
|
|
1216
|
+
// it (task 1004406), and an injected state must reach the beat that reports it.
|
|
1217
|
+
try { beatFile(Date.now(), deps.state || RUNNER_STATE); } catch (_) { /* a runner that cannot write its file still runs */ }
|
|
1218
|
+
try { await beatServer(fields, deps); } catch (_) { /* and one that cannot reach the instance still runs */ }
|
|
1219
|
+
};
|
|
1220
|
+
// The running commit, read ONCE, BEFORE the first beat (task 1004397). It used
|
|
1221
|
+
// to be read lazily by codeDrifted(), which sits behind the one-minute uptime
|
|
1222
|
+
// floor, so every beat in a process's first minute said head:null — the window
|
|
1223
|
+
// in which a crash-looping or repeatedly-restarted runner is being looked at.
|
|
1224
|
+
// The floor exists to stop a restart STORM, not to avoid this read: one local
|
|
1225
|
+
// rev-parse. Fails soft to null inside loadedHead(), and a null is retried by
|
|
1226
|
+
// the drift check later, so an unreadable head never stops the runner.
|
|
1227
|
+
try { await (deps.loadedHead ? deps.loadedHead(deps) : loadedHead(deps)); } catch (_) { /* head stays null */ }
|
|
1228
|
+
await beat({ last_event: 'boot' });
|
|
1229
|
+
|
|
1230
|
+
// BEFORE ANY WORK. A claim left locked by a previous run is the worst thing
|
|
1231
|
+
// this loop produces, and taking new work on top of one compounds it.
|
|
1232
|
+
emit(await reconcile(deps));
|
|
1233
|
+
|
|
1234
|
+
let state = { ...cadence.START };
|
|
1235
|
+
const maxLoops = Number.isFinite(Number(opts.maxLoops)) ? Number(opts.maxLoops) : Infinity;
|
|
1236
|
+
|
|
1237
|
+
for (let loop = 0; loop < maxLoops; loop++) {
|
|
1238
|
+
// Every loop, so a crash leaves a heartbeat that goes stale on its own — the
|
|
1239
|
+
// runner never has to notice it is dying for its death to become visible.
|
|
1240
|
+
await beat({ mode: state.mode, consecutive_failures: state.consecutiveFailures, last_event: 'loop' });
|
|
1241
|
+
// Slow lands (task 1004538): re-probe shipped tasks still waiting on main, and
|
|
1242
|
+
// close no-artifact blockers whose commit has since landed. Armed only by
|
|
1243
|
+
// main() (or an injected stub), like refreshInPlace, so no test harness that
|
|
1244
|
+
// predates it can fall through to the network. It can never stop the loop.
|
|
1245
|
+
if (opts.recheckLands || deps.recheckLands) {
|
|
1246
|
+
try { await (deps.recheckLands || recheckPendingLands)(deps); } catch (e) {
|
|
1247
|
+
log({ event: 'land_recheck_failed', errors: [e && e.message ? e.message : String(e)] });
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
// In `probing` the runner takes exactly ONE task and only after its wait, so
|
|
1251
|
+
// a broken window costs a handful of cheap requests rather than a night of
|
|
1252
|
+
// failing spawns. `maxTasks` still bounds a full-speed burst.
|
|
1253
|
+
const budget = state.mode === 'probing' ? 1 : opts.maxTasks;
|
|
1254
|
+
let row = null;
|
|
1255
|
+
|
|
1256
|
+
for (let i = 0; i < budget; i++) {
|
|
1257
|
+
row = await iterate(opts, deps);
|
|
1258
|
+
emit(row);
|
|
1259
|
+
idle = configIdle.trackIdle(idle, row, clock());
|
|
1260
|
+
// Carry the task id so the hall can tell "quiet because it is building
|
|
1261
|
+
// task 1234" from "quiet because it is dead". A worker may hold the loop for
|
|
1262
|
+
// ninety minutes, and without this every honest hour of work looks like a
|
|
1263
|
+
// corpse.
|
|
1264
|
+
await beat({ mode: state.mode, last_event: row.event, working_task_id: Number(row.task_id) || undefined, working_goal_id: Number(row.goal_id) || undefined });
|
|
1265
|
+
const seen = cadence.classifyEvent(row);
|
|
1266
|
+
// A hold that names its reset SECOND waits until that second (task 1004454).
|
|
1267
|
+
// classifyEvent carries it as an absolute epoch; the cadence has no clock of
|
|
1268
|
+
// its own, so the conversion to a wait lives here beside the one clock read.
|
|
1269
|
+
// Without it every hold waited the fixed idle and the "wake at the reset"
|
|
1270
|
+
// the goal asks for was only ever as accurate as the poll.
|
|
1271
|
+
if (Number.isFinite(Number(seen.sleepUntil)) && seen.sleepUntil !== null) {
|
|
1272
|
+
const nowS = Math.floor((deps.now ? deps.now() : Date.now()) / 1000);
|
|
1273
|
+
seen.sleepUntilS = Math.max(0, Number(seen.sleepUntil) - nowS);
|
|
1274
|
+
}
|
|
1275
|
+
state = cadence.nextCadence(state, seen, opts.cadence);
|
|
1276
|
+
log({ event: 'cadence', mode: state.mode, consecutive_failures: state.consecutiveFailures, wait_s: state.waitS, class: seen.class, why: state.why });
|
|
1277
|
+
// A burst continues only while work is actually landing. Anything else ends
|
|
1278
|
+
// the burst and hands the decision to the cadence, which is the only thing
|
|
1279
|
+
// allowed to say how long to wait.
|
|
1280
|
+
if (seen.class !== 'progress') break;
|
|
1281
|
+
}
|
|
1282
|
+
|
|
1283
|
+
// AFTER the burst, never during one: a worker mid-task must not be abandoned
|
|
1284
|
+
// to pick up a fix. Before the wait, because waiting is where the runner
|
|
1285
|
+
// spends nearly all of its time and an upgrade should not sit behind it.
|
|
1286
|
+
const upMs = (deps.now ? deps.now() : Date.now()) - Date.parse(RUNNER_STARTED_AT);
|
|
1287
|
+
const mayUpgrade = upMs >= MIN_UPTIME_BEFORE_UPGRADE_MS;
|
|
1288
|
+
let drift = mayUpgrade
|
|
1289
|
+
? (deps.codeDrifted ? await deps.codeDrifted(deps) : await codeDrifted(deps))
|
|
1290
|
+
: false;
|
|
1291
|
+
if (drift) {
|
|
1292
|
+
// Pull always, restart rarely (task 1004406). Only main() arms the refresh.
|
|
1293
|
+
let why = 'exiting so the wrapper can relaunch on the new code';
|
|
1294
|
+
if (opts.refreshInPlace) {
|
|
1295
|
+
const refreshed = await (deps.refreshCheckout || refreshCheckout)(deps);
|
|
1296
|
+
if (refreshed && refreshed.ok) {
|
|
1297
|
+
const touched = supervisorTouched(refreshed.changed, deps.loadedFiles ? deps.loadedFiles() : loadedFiles());
|
|
1298
|
+
if (!touched.length) {
|
|
1299
|
+
(deps.state || RUNNER_STATE).diskHead = refreshed.to;
|
|
1300
|
+
log({ event: 'code_refreshed', from: drift.from, to: refreshed.to.slice(0, 8), changed: refreshed.changed.length,
|
|
1301
|
+
reason: `main moved from ${drift.from} to ${refreshed.to.slice(0, 8)} — checkout updated in place; none of the ${refreshed.changed.length} changed file(s) is code this process runs, so no restart` });
|
|
1302
|
+
await beat({ mode: state.mode, last_event: 'code_refreshed' });
|
|
1303
|
+
drift = null;
|
|
1304
|
+
} else {
|
|
1305
|
+
const more = touched.length > 5 ? `, +${touched.length - 5} more` : '';
|
|
1306
|
+
why = `it changed code this process runs (${touched.slice(0, 5).join(', ')}${more}) — exiting so the wrapper can relaunch on it`;
|
|
1307
|
+
}
|
|
1308
|
+
} else {
|
|
1309
|
+
why = `the in-place refresh did not run (${(refreshed && refreshed.reason) || 'no result'}) — exiting so the wrapper's own refresh can`;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
if (drift) {
|
|
1313
|
+
log({ event: 'upgrade_exit', from: drift.from, to: drift.to, reason: `main moved from ${drift.from} to ${drift.to} — ${why}` });
|
|
1314
|
+
await beat({ mode: state.mode, last_event: 'upgrade_exit' });
|
|
1315
|
+
return UPGRADE_EXIT_CODE;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
const { waited, jumped } = await wait(state.waitS, { ...deps, beat }, opts.cadence);
|
|
1320
|
+
if (jumped) log({ event: 'cadence', mode: state.mode, jumped: true, waited_s: waited, why: 'a new signal arrived — main moved, the fence changed, or the queue did' });
|
|
1321
|
+
}
|
|
1322
|
+
return 0;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
async function main(argv) {
|
|
1326
|
+
const opts = readArgv(argv);
|
|
1327
|
+
// No "name a goal" refusal here any more. It used to be the only allowlist, and
|
|
1328
|
+
// it lived on the machine being fenced; the database owns that now, and an empty
|
|
1329
|
+
// `--goal` means "every allowlisted goal" rather than "anything claimable". The
|
|
1330
|
+
// refusal did not disappear — it moved somewhere the runner host cannot edit.
|
|
1331
|
+
if (opts.forever) return forever({ ...opts, refreshInPlace: true, recheckLands: true });
|
|
1332
|
+
|
|
1333
|
+
// The one-shot path, kept for an operator running a single task by hand. It
|
|
1334
|
+
// still reconciles first: a leftover claim is a leftover claim whoever is
|
|
1335
|
+
// watching.
|
|
1336
|
+
const boot = await reconcileLeftoverClaims();
|
|
1337
|
+
if (opts.json) console.log(JSON.stringify(boot));
|
|
1338
|
+
for (let i = 0; i < opts.maxTasks; i++) {
|
|
1339
|
+
const row = await iteration(opts);
|
|
1340
|
+
if (opts.json) console.log(JSON.stringify(row));
|
|
1341
|
+
else console.log(`${row.event}${row.task_id ? ` task ${row.task_id}` : ''}${row.outcome ? ` → ${row.outcome}` : ''}${row.reason ? `: ${row.reason}` : ''}`);
|
|
1342
|
+
if (row.event !== 'worked') break;
|
|
1343
|
+
}
|
|
1344
|
+
return 0;
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (require.main === module) {
|
|
1348
|
+
main(process.argv.slice(2)).then((c) => process.exit(c)).catch((e) => {
|
|
1349
|
+
console.error(`autobongos-run: ${e && e.message ? e.message : e}`);
|
|
1350
|
+
process.exit(1);
|
|
1351
|
+
});
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
module.exports = {
|
|
1355
|
+
readArgv, pickTask, iteration, record, logPath, spawnWorker, workerEnv, WORKER_ENV_ALLOW, resolveWorkerBin,
|
|
1356
|
+
worktreeName, readFence, gradeLedgerPath, gradeCapReader, reconcileLeftoverClaims, pollSignals, waitOrJump, forever, CLAIM_PREFIX, codeDrifted, loadedHead, UPGRADE_EXIT_CODE, MIN_UPTIME_BEFORE_UPGRADE_MS,
|
|
1357
|
+
heartbeatPath, writeHeartbeat, readHeartbeat, pidAlive, anotherRunnerIsAlive, HEARTBEAT_STALE_MS,
|
|
1358
|
+
isRunnerTree, RUNNER_TREE_RE, postHeartbeat, claudeAccountLabel, RUNNER_STARTED_AT, failureReason,
|
|
1359
|
+
newRunnerState, loadedFiles, supervisorTouched, refreshCheckout, LIMIT_UNKNOWN_RESET_MS, QUEUE_GATED_EXIT, CLAIM_SET_ASIDE_MS, MAX_CLAIM_ATTEMPTS, owedTaskIds,
|
|
1360
|
+
readRunLogRows, recheckPendingLands, LAND_SWEEP_MS,
|
|
1361
|
+
};
|