@event4u/agent-config 14.10.0 → 14.11.0
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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +335 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/design.md +88 -0
- package/dist/agent-src/commands/project/analyze.md +47 -0
- package/dist/agent-src/commands/review/changes.md +67 -6
- package/dist/agent-src/commands/roadmap/create.md +49 -0
- package/dist/agent-src/commands/roadmap/next.md +27 -11
- package/dist/agent-src/commands/roadmap/process-full.md +14 -0
- package/dist/agent-src/commands/video/from-script.md +60 -3
- package/dist/agent-src/commands/video/stitch.md +14 -0
- package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
- package/dist/agent-src/rules/active-remediation.md +3 -3
- package/dist/agent-src/rules/code-provenance.md +2 -2
- package/dist/agent-src/rules/context-hygiene.md +13 -33
- package/dist/agent-src/rules/council-availability.md +2 -2
- package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
- package/dist/agent-src/rules/design-fidelity.md +18 -0
- package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
- package/dist/agent-src/rules/evaluator-independence.md +56 -44
- package/dist/agent-src/rules/fix-what-you-see.md +9 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
- package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
- package/dist/agent-src/rules/playbook-precedence.md +105 -0
- package/dist/agent-src/rules/recurring-criticism.md +3 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
- package/dist/agent-src/rules/scale-discipline.md +24 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
- package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
- package/dist/agent-src/rules/ui-audit-gate.md +27 -15
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
- package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
- package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
- package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
- package/dist/agent-src/skills/brand/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
- package/dist/agent-src/skills/code-review/SKILL.md +11 -0
- package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
- package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
- package/dist/agent-src/skills/design-review/SKILL.md +79 -1
- package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
- package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
- package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
- package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
- package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
- package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
- package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
- package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
- package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
- package/dist/agent-src/skills/iconography/SKILL.md +3 -0
- package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
- package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
- package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
- package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
- package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
- package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
- package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
- package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
- package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
- package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
- package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
- package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
- package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
- package/dist/agent-src/skills/terraform/SKILL.md +3 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
- package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
- package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
- package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
- package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
- package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
- package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/playbook.md +75 -0
- package/dist/agent-src/templates/roadmaps.md +37 -0
- package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/dist/cli/commands/uiAudit.js +221 -0
- package/dist/cli/commands/uiAudit.js.map +1 -0
- package/dist/cli/commands/uiRender.js +206 -0
- package/dist/cli/commands/uiRender.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/content.js +64 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/cli/mcp/dispatch.js +175 -10
- package/dist/cli/mcp/dispatch.js.map +1 -1
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
- package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
- package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
- package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
- package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
- package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +8 -6
- package/dist/cli-delegate/cmd_conformance.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +70 -9
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +359 -124
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +18 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -6
- package/dist/hooks/dispatch.js +2337 -1436
- package/dist/install/install.mjs +795 -708
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1685 -1250
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/lean_projection_mode.js +40 -0
- package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
- package/dist/scripts/_lib/scoped_projection.js +84 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -1
- package/dist/scripts/_lib/ui_surface.js +89 -0
- package/dist/scripts/_lib/ui_surface.js.map +1 -0
- package/dist/scripts/condense.js +6 -10
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +40 -2
- package/dist/scripts/project_thin_rules.js.map +1 -1
- package/dist/server/schemas/settings.js +3 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/skillRanking.js +159 -0
- package/dist/shared/skillRanking.js.map +1 -0
- package/docs/contracts/provider-lifecycle.md +47 -18
- package/docs/contracts/settings-classes.md +14 -4
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
- package/docs/guidelines/design-antipatterns.md +4 -2
- package/docs/guidelines/design-asset-discipline.md +38 -0
- package/docs/guidelines/design-fidelity-mechanics.md +45 -29
- package/docs/guidelines/design-handover-extraction.md +35 -5
- package/docs/guidelines/design-modes.md +41 -1
- package/docs/guidelines/monorepo-antipatterns.md +141 -0
- package/llms.txt +50 -39
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/src/config/agent-settings.template.yml +28 -0
- package/src/config/assurance-capability-registry.json +223 -0
- package/src/config/assurance-threshold-budget.json +65 -0
- package/src/config/ci-local-parity.yml +14 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/dispatch-economy-metrics.json +4 -2
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +207 -8
- package/src/config/gate-violation-baselines.json +76 -72
- package/src/config/hook-latency-budget.json +1 -0
- package/src/config/hook-token-budget.json +137 -125
- package/src/config/pack-size-budget.json +4 -2
- package/src/config/quorum-attendance-budget.json +12 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/config/rule-stub-ceilings.json +112 -91
- package/src/scripts/_cli/cmd_doctor.ts +15 -16
- package/src/scripts/_cli/doctor_install_mode.ts +49 -0
- package/src/scripts/_cli/doctor_overrides.ts +154 -0
- package/src/scripts/_dispatch.bash +17 -0
- package/src/scripts/_lib/as_of.ts +412 -0
- package/src/scripts/_lib/exec_evidence.ts +7 -0
- package/src/scripts/_lib/hook_settings.ts +33 -0
- package/src/scripts/_lib/host_listing_model.ts +192 -0
- package/src/scripts/_lib/lean_projection_mode.ts +44 -0
- package/src/scripts/_lib/mcp_bridge.ts +199 -0
- package/src/scripts/_lib/review_independence.ts +62 -10
- package/src/scripts/_lib/rule_injection.ts +239 -0
- package/src/scripts/_lib/run_checkpoint.ts +54 -1
- package/src/scripts/_lib/scoped_projection.ts +96 -0
- package/src/scripts/_lib/session_register.ts +44 -2
- package/src/scripts/_lib/skill_catalogue.ts +38 -2
- package/src/scripts/_lib/transcript_reads.ts +207 -0
- package/src/scripts/_lib/ui_authority.test.ts +251 -0
- package/src/scripts/_lib/ui_authority.ts +343 -0
- package/src/scripts/ai-video/adapters/fal.sh +8 -3
- package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
- package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
- package/src/scripts/ai-video/adapters/kling.sh +10 -0
- package/src/scripts/ai-video/adapters/sora.sh +10 -0
- package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
- package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
- package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
- package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
- package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
- package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
- package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
- package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
- package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
- package/src/scripts/ai-video/smoke-trace.sh +450 -2
- package/src/scripts/ai-video/stitch.sh +191 -10
- package/src/scripts/ai-video/test-pipeline.sh +84 -6
- package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
- package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
- package/src/scripts/ai_council/clients.ts +3 -2
- package/src/scripts/ai_council/consensus.ts +63 -6
- package/src/scripts/ai_council/events_log.ts +46 -1
- package/src/scripts/ai_council/orchestrator.ts +103 -108
- package/src/scripts/ai_council/orchestrator_results.ts +166 -0
- package/src/scripts/ai_council/prompts.ts +12 -5
- package/src/scripts/ai_council/quorum.ts +82 -1
- package/src/scripts/ai_council/quorum_wiring.ts +82 -5
- package/src/scripts/ai_council/stance_tally.ts +22 -0
- package/src/scripts/bench_hook_latency.ts +62 -0
- package/src/scripts/build_proof.ts +12 -1
- package/src/scripts/capture_skill_catalogue.ts +21 -0
- package/src/scripts/check_always_budget.ts +2 -1
- package/src/scripts/check_augmentignore.ts +2 -1
- package/src/scripts/check_beta_review_markers.ts +2 -1
- package/src/scripts/check_composite_arming.ts +364 -0
- package/src/scripts/check_corpus_staleness.ts +2 -1
- package/src/scripts/check_council_pin_staleness.ts +3 -1
- package/src/scripts/check_enforcement_coverage.ts +116 -3
- package/src/scripts/check_enforcement_denominator.ts +365 -0
- package/src/scripts/check_gate_coverage.ts +2 -1
- package/src/scripts/check_hook_bundle_freshness.ts +13 -1
- package/src/scripts/check_knowledge_cards.ts +2 -1
- package/src/scripts/check_knowledge_pages.ts +3 -2
- package/src/scripts/check_memory.ts +2 -1
- package/src/scripts/check_no_automerge_key.ts +287 -0
- package/src/scripts/check_pack_conformance_fixture.ts +565 -0
- package/src/scripts/check_proposal.ts +2 -1
- package/src/scripts/check_reach_staleness.ts +2 -1
- package/src/scripts/check_release_adjacent_health.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/check_review_schema.ts +34 -1
- package/src/scripts/check_roadmap_trackable.ts +48 -3
- package/src/scripts/check_source_size_budget.ts +2 -1
- package/src/scripts/check_standing_payload_delta.ts +505 -0
- package/src/scripts/check_trigger_evals.ts +2 -1
- package/src/scripts/classify_change_risk.ts +281 -0
- package/src/scripts/compute_skill_tiers.ts +186 -0
- package/src/scripts/condense.ts +8 -13
- package/src/scripts/council_attendance_metrics.ts +183 -0
- package/src/scripts/council_cli.ts +10 -8
- package/src/scripts/council_parse_rate.ts +116 -0
- package/src/scripts/derive_playbooks.ts +406 -0
- package/src/scripts/design_slop_rules.test.ts +7 -0
- package/src/scripts/design_slop_rules.ts +56 -2
- package/src/scripts/dispatch_r2_reviewer.ts +16 -0
- package/src/scripts/frontend_corpus_hash.ts +116 -0
- package/src/scripts/grade_target_readiness.ts +478 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +97 -16
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/design_pass_hook.ts +356 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
- package/src/scripts/hooks/rule_inject_hook.ts +318 -0
- package/src/scripts/hot_context_hook.ts +74 -7
- package/src/scripts/install.ts +40 -89
- package/src/scripts/lint_adapter_tier.ts +663 -0
- package/src/scripts/lint_archived_skills.ts +93 -0
- package/src/scripts/lint_budget_ownership.ts +2 -1
- package/src/scripts/lint_deterministic_time.ts +470 -0
- package/src/scripts/lint_framework_leakage.ts +5 -0
- package/src/scripts/lint_handoffs.ts +525 -1
- package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
- package/src/scripts/lint_marketplace.ts +88 -0
- package/src/scripts/lint_media_policy_linkage.ts +140 -1
- package/src/scripts/lint_one_off_age.ts +6 -1
- package/src/scripts/lint_override_kernel_guard.ts +87 -5
- package/src/scripts/lint_roadmap_complexity.ts +56 -0
- package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
- package/src/scripts/lint_rule_norm_pin.ts +321 -0
- package/src/scripts/lint_spawn_payload.ts +42 -5
- package/src/scripts/lint_symptom_intake.ts +2 -1
- package/src/scripts/mcp_parity_smoke.ts +30 -4
- package/src/scripts/mcp_render.ts +66 -1
- package/src/scripts/mcp_tool_fingerprint.ts +241 -0
- package/src/scripts/measure_risk_class_drift.ts +140 -0
- package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
- package/src/scripts/media/lib/adapter-common.sh +211 -1
- package/src/scripts/media/lib/adapter-contract.md +74 -6
- package/src/scripts/model_rule_injection.ts +907 -0
- package/src/scripts/project_thin_rules.ts +45 -2
- package/src/scripts/release.ts +66 -860
- package/src/scripts/release_drill.ts +59 -3
- package/src/scripts/release_env.ts +298 -0
- package/src/scripts/release_publication.ts +720 -0
- package/src/scripts/roadmap_context.ts +778 -0
- package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
- package/src/scripts/schemas/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +39 -0
- package/src/scripts/schemas/ui-authority.schema.json +114 -0
- package/src/scripts/session_register_hook.ts +29 -3
- package/src/scripts/sessions_cli.ts +67 -3
- package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
- package/src/scripts/token_sink_report.ts +239 -0
- package/src/scripts/trigger_coverage.ts +122 -3
- package/src/scripts/update_counts.ts +7 -3
- package/src/scripts/validate_frontmatter.ts +66 -0
- package/src/server/schemas/settings.ts +5 -0
- package/src/shared/skillRanking.ts +183 -0
package/src/scripts/release.ts
CHANGED
|
@@ -124,7 +124,6 @@ import {
|
|
|
124
124
|
derive_category_hits,
|
|
125
125
|
render_derived_head_values,
|
|
126
126
|
} from './_lib/release_highlights.js';
|
|
127
|
-
import { gh_argv_label, gh_retry } from './_lib/gh_transient.js';
|
|
128
127
|
import {
|
|
129
128
|
NEXT_SECTION_RE,
|
|
130
129
|
extract_changelog_section,
|
|
@@ -139,189 +138,77 @@ const MODULE_DOC_FIRST_LINE = 'End-to-end release automation for `event4u/agent-
|
|
|
139
138
|
|
|
140
139
|
const _HERE = fileURLToPath(import.meta.url);
|
|
141
140
|
|
|
142
|
-
// ---------------------------------------------------------------------------
|
|
143
|
-
// Python-runtime parity helpers
|
|
144
|
-
// ---------------------------------------------------------------------------
|
|
145
|
-
|
|
146
|
-
/** Mirror of Python `sys.exit(code)` raised by `die()`. Caught at the CLI entry. */
|
|
147
|
-
class SystemExitError extends Error {
|
|
148
|
-
constructor(public readonly code: number) {
|
|
149
|
-
super(`system-exit-${code}`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
141
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
}
|
|
142
|
+
import {
|
|
143
|
+
ArgparseExit,
|
|
144
|
+
AUGMENT_MARKETPLACE_JSON,
|
|
145
|
+
AUGMENT_PLUGIN_JSON,
|
|
146
|
+
CHANGELOG,
|
|
147
|
+
CalledProcessError,
|
|
148
|
+
GH_PR_BODY_LIMIT,
|
|
149
|
+
GH_RELEASE_NOTES_LIMIT,
|
|
150
|
+
MAIN_BRANCH,
|
|
151
|
+
MARKETPLACE_JSON,
|
|
152
|
+
PACKAGE_JSON,
|
|
153
|
+
PACKAGE_LOCK_JSON,
|
|
154
|
+
PROJECT_TEMPLATE,
|
|
155
|
+
REMOTE,
|
|
156
|
+
REPO_ROOT,
|
|
157
|
+
REPO_SLUG,
|
|
158
|
+
SystemExitError,
|
|
159
|
+
set_augment_manifest_version,
|
|
160
|
+
set_marketplace_version,
|
|
161
|
+
} from './release_env.js';
|
|
162
|
+
import type { RunResult } from './release_publication.js';
|
|
163
|
+
// Re-export surface for the six names tests import from `release.js` and that
|
|
164
|
+
// moved into the publication unit. `export ... from` rather than a bare import,
|
|
165
|
+
// because these are not USED here — an unused import would be dropped and the
|
|
166
|
+
// test import path would break silently.
|
|
167
|
+
export {
|
|
168
|
+
_failed_check_names,
|
|
169
|
+
_failed_checks_report,
|
|
170
|
+
_is_non_fast_forward,
|
|
171
|
+
_is_tag_already_exists,
|
|
172
|
+
_no_checks_action,
|
|
173
|
+
_required_contexts_from_rules,
|
|
174
|
+
} from './release_publication.js';
|
|
175
|
+
import { _cap_body, jsonDumpsIndent, reEscape } from './release_env.js';
|
|
176
|
+
import {
|
|
177
|
+
_MERGE_UPDATE_ROUNDS,
|
|
178
|
+
_branch_exists_local,
|
|
179
|
+
_branch_exists_remote,
|
|
180
|
+
_pr_for_branch,
|
|
181
|
+
_pr_merge_state,
|
|
182
|
+
_push_tag,
|
|
183
|
+
_refresh_pr_body_from_head,
|
|
184
|
+
_release_exists,
|
|
185
|
+
_remote_tag_commit,
|
|
186
|
+
_set_exec_override,
|
|
187
|
+
_sleep_ms,
|
|
188
|
+
_tag_exists_local,
|
|
189
|
+
_tag_exists_remote,
|
|
190
|
+
_target_from_branch,
|
|
191
|
+
die,
|
|
192
|
+
gh,
|
|
193
|
+
git,
|
|
194
|
+
have,
|
|
195
|
+
merge_release_pr,
|
|
196
|
+
push_release_branch,
|
|
197
|
+
run,
|
|
198
|
+
watch_pr_checks,
|
|
199
|
+
} from './release_publication.js';
|
|
174
200
|
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
201
|
+
// The pre-existing `export {...}` block at the end of this file already carries
|
|
202
|
+
// every moved public name, so re-exporting them here would be a duplicate
|
|
203
|
+
// identifier rather than a second surface — the import above is what makes the
|
|
204
|
+
// existing block resolve. This is the whole of step 1.1's "re-export shape that
|
|
205
|
+
// keeps callers unaffected": no caller and no test import path changes.
|
|
178
206
|
|
|
179
|
-
// REPO_ROOT: release.py is at src/scripts/release.py;
|
|
180
|
-
// Path(__file__).resolve().parent.parent.parent == src/scripts → src → repo
|
|
181
|
-
// root. release.ts lives in the same dir, so _HERE-dir is src/scripts and
|
|
182
|
-
// `..`/`..` reaches the repo root (matches changelog_eras.ts's 3-up from
|
|
183
|
-
// _lib/ and install.ts's REPO_ROOT computation).
|
|
184
|
-
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
185
|
-
const PACKAGE_JSON = path.join(REPO_ROOT, 'package.json');
|
|
186
|
-
// Bumped alongside package.json — see set_lockfile_version for why.
|
|
187
|
-
const PACKAGE_LOCK_JSON = path.join(REPO_ROOT, 'package-lock.json');
|
|
188
|
-
const MARKETPLACE_JSON = path.join(REPO_ROOT, '.claude-plugin', 'marketplace.json');
|
|
189
|
-
// Source-of-truth project-settings template. Carries an `agent_config_version`
|
|
190
|
-
// pin that check_template_pin_drift requires to equal package.json.version. We
|
|
191
|
-
// bump the src twin here; `task release-prepare` (run right after the bump)
|
|
192
|
-
// regenerates the dist/agent-src/ twin from it.
|
|
193
|
-
const PROJECT_TEMPLATE = path.join(
|
|
194
|
-
REPO_ROOT,
|
|
195
|
-
'src',
|
|
196
|
-
'agent-src',
|
|
197
|
-
'templates',
|
|
198
|
-
'agents',
|
|
199
|
-
'agent-project-settings.example.yml',
|
|
200
|
-
);
|
|
201
|
-
const CHANGELOG = path.join(REPO_ROOT, 'CHANGELOG.md');
|
|
202
|
-
const MAIN_BRANCH = 'main';
|
|
203
|
-
const REMOTE = 'origin';
|
|
204
|
-
const REPO_SLUG = 'event4u-app/agent-config';
|
|
205
|
-
|
|
206
|
-
// GitHub rejects bodies over these limits with a GraphQL "Body is too
|
|
207
|
-
// long" error. The full entry always lands in CHANGELOG.md (committed in
|
|
208
|
-
// the PR diff and attached to the tag), so an oversized body is capped
|
|
209
|
-
// with a pointer rather than failing the release — a major bump can
|
|
210
|
-
// render hundreds of commit bullets, well past the 65 536 PR-body limit.
|
|
211
|
-
const GH_PR_BODY_LIMIT = 65_536; // createPullRequest mutation hard limit
|
|
212
|
-
const GH_RELEASE_NOTES_LIMIT = 125_000; // release-notes body limit
|
|
213
207
|
|
|
214
208
|
// ---------------------------------------------------------------------------
|
|
215
209
|
// Parity helpers — code-point length, comma grouping, regex escape, JSON
|
|
216
210
|
// ---------------------------------------------------------------------------
|
|
217
211
|
|
|
218
|
-
/** Python `len(str)` — number of Unicode code points (not UTF-16 units). */
|
|
219
|
-
function pyLen(s: string): number {
|
|
220
|
-
return [...s].length;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
/** Python `text[:n]` on a string — first `n` code points. */
|
|
224
|
-
function pySlice(s: string, n: number): string {
|
|
225
|
-
return [...s].slice(0, n).join('');
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/** Python f-string `{n:,}` — thousands grouping with commas (en-US grouping). */
|
|
229
|
-
function commaGroup(n: number): string {
|
|
230
|
-
const neg = n < 0;
|
|
231
|
-
const digits = Math.abs(n).toString();
|
|
232
|
-
let out = '';
|
|
233
|
-
for (let i = 0; i < digits.length; i += 1) {
|
|
234
|
-
if (i > 0 && (digits.length - i) % 3 === 0) out += ',';
|
|
235
|
-
out += digits[i];
|
|
236
|
-
}
|
|
237
|
-
return neg ? '-' + out : out;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
/** Python `re.escape` — escape regex-special chars in a literal string. */
|
|
241
|
-
function reEscape(s: string): string {
|
|
242
|
-
return s.replace(/[.*+?^${}()|[\]\\#\-]/g, '\\$&');
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// --- JSON byte-parity (mirrors install.ts; see Parity notes for ensure_ascii). ---
|
|
246
|
-
|
|
247
|
-
function _jsonStrNoAscii(s: string): string {
|
|
248
|
-
// json.dumps(ensure_ascii=False): escape control chars + " + \, keep >=0x20.
|
|
249
|
-
let out = '"';
|
|
250
|
-
for (const ch of s) {
|
|
251
|
-
const code = ch.codePointAt(0) as number;
|
|
252
|
-
switch (ch) {
|
|
253
|
-
case '"':
|
|
254
|
-
out += '\\"';
|
|
255
|
-
break;
|
|
256
|
-
case '\\':
|
|
257
|
-
out += '\\\\';
|
|
258
|
-
break;
|
|
259
|
-
case '\n':
|
|
260
|
-
out += '\\n';
|
|
261
|
-
break;
|
|
262
|
-
case '\r':
|
|
263
|
-
out += '\\r';
|
|
264
|
-
break;
|
|
265
|
-
case '\t':
|
|
266
|
-
out += '\\t';
|
|
267
|
-
break;
|
|
268
|
-
case '\b':
|
|
269
|
-
out += '\\b';
|
|
270
|
-
break;
|
|
271
|
-
case '\f':
|
|
272
|
-
out += '\\f';
|
|
273
|
-
break;
|
|
274
|
-
default:
|
|
275
|
-
if (code < 0x20) {
|
|
276
|
-
out += '\\u' + code.toString(16).padStart(4, '0');
|
|
277
|
-
} else {
|
|
278
|
-
out += ch;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
return out + '"';
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
function _jsonScalar(value: unknown): string | null {
|
|
286
|
-
if (value === null || value === undefined) return 'null';
|
|
287
|
-
if (typeof value === 'boolean') return value ? 'true' : 'false';
|
|
288
|
-
if (typeof value === 'number') {
|
|
289
|
-
if (!Number.isFinite(value)) {
|
|
290
|
-
if (Number.isNaN(value)) return 'NaN';
|
|
291
|
-
return value > 0 ? 'Infinity' : '-Infinity';
|
|
292
|
-
}
|
|
293
|
-
return String(value);
|
|
294
|
-
}
|
|
295
|
-
if (typeof value === 'string') return _jsonStrNoAscii(value);
|
|
296
|
-
return null;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
function _dumpIndent(value: unknown, indent: number, depth: number): string {
|
|
300
|
-
const scalar = _jsonScalar(value);
|
|
301
|
-
if (scalar !== null) return scalar;
|
|
302
|
-
const pad = ' '.repeat(indent * (depth + 1));
|
|
303
|
-
const closePad = ' '.repeat(indent * depth);
|
|
304
|
-
if (Array.isArray(value)) {
|
|
305
|
-
if (value.length === 0) return '[]';
|
|
306
|
-
const items = value.map((v) => pad + _dumpIndent(v, indent, depth + 1));
|
|
307
|
-
return `[\n${items.join(',\n')}\n${closePad}]`;
|
|
308
|
-
}
|
|
309
|
-
if (typeof value === 'object' && value !== null) {
|
|
310
|
-
const obj = value as Record<string, unknown>;
|
|
311
|
-
const keys = Object.keys(obj);
|
|
312
|
-
if (keys.length === 0) return '{}';
|
|
313
|
-
const items = keys.map(
|
|
314
|
-
(k) => `${pad}${_jsonStrNoAscii(k)}: ${_dumpIndent(obj[k], indent, depth + 1)}`,
|
|
315
|
-
);
|
|
316
|
-
return `{\n${items.join(',\n')}\n${closePad}}`;
|
|
317
|
-
}
|
|
318
|
-
return _jsonStrNoAscii(String(value));
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/** `json.dumps(data, indent=N)` (sort_keys=False; ensure_ascii — see Parity notes). */
|
|
322
|
-
function jsonDumpsIndent(value: unknown, indent: number): string {
|
|
323
|
-
return _dumpIndent(value, indent, 0);
|
|
324
|
-
}
|
|
325
212
|
|
|
326
213
|
/** `_date.today().isoformat()` — local date as `YYYY-MM-DD`. */
|
|
327
214
|
function todayIso(now?: Date): string {
|
|
@@ -334,25 +221,6 @@ function todayIso(now?: Date): string {
|
|
|
334
221
|
// _cap_body (release.py:87-101)
|
|
335
222
|
// ---------------------------------------------------------------------------
|
|
336
223
|
|
|
337
|
-
/**
|
|
338
|
-
* Return `text` unchanged when within `limit` chars; otherwise truncate at
|
|
339
|
-
* the last line boundary that fits and append a pointer to `full_ref` so
|
|
340
|
-
* nothing is silently lost.
|
|
341
|
-
*/
|
|
342
|
-
function _cap_body(text: string, limit: number, full_ref: string): string {
|
|
343
|
-
if (pyLen(text) <= limit) {
|
|
344
|
-
return text;
|
|
345
|
-
}
|
|
346
|
-
const notice =
|
|
347
|
-
`\n\n> _Changelog truncated to fit GitHub's ` +
|
|
348
|
-
`${commaGroup(limit)}-character body limit — full entry in ${full_ref}._`;
|
|
349
|
-
let head = pySlice(text, limit - pyLen(notice));
|
|
350
|
-
const nl = head.lastIndexOf('\n');
|
|
351
|
-
if (nl > 0) {
|
|
352
|
-
head = head.slice(0, nl);
|
|
353
|
-
}
|
|
354
|
-
return head + notice;
|
|
355
|
-
}
|
|
356
224
|
|
|
357
225
|
// ---------------------------------------------------------------------------
|
|
358
226
|
// SECTIONS + commit regexes (release.py:103-120)
|
|
@@ -533,659 +401,6 @@ class Plan {
|
|
|
533
401
|
|
|
534
402
|
// ─── utilities ────────────────────────────────────────────────────────────────
|
|
535
403
|
|
|
536
|
-
function die(msg: string, code = 2): never {
|
|
537
|
-
process.stderr.write(`error: ${msg}\n`);
|
|
538
|
-
throw new SystemExitError(code);
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
export interface RunResult {
|
|
542
|
-
returncode: number;
|
|
543
|
-
stdout: string;
|
|
544
|
-
stderr: string;
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Thin subprocess wrapper with sane defaults.
|
|
549
|
-
*
|
|
550
|
-
* When `check` and `capture` are both True and the command fails, Python's
|
|
551
|
-
* default behaviour swallows stderr — callers only see a CalledProcessError
|
|
552
|
-
* with no hint of what went wrong. We catch that path and die with the actual
|
|
553
|
-
* stderr so release preflight failures are diagnosable without re-running with
|
|
554
|
-
* a debugger.
|
|
555
|
-
*/
|
|
556
|
-
/**
|
|
557
|
-
* Drill/test seam — when set, every external command run() would spawn is
|
|
558
|
-
* answered by this function instead. The check/die/CalledProcessError
|
|
559
|
-
* semantics of run() still apply to the simulated result, so the drill
|
|
560
|
-
* exercises the REAL error paths of the orchestration (release_drill.ts).
|
|
561
|
-
*/
|
|
562
|
-
type ExecOverride = (args: readonly string[]) => {
|
|
563
|
-
status: number;
|
|
564
|
-
stdout: string;
|
|
565
|
-
stderr: string;
|
|
566
|
-
};
|
|
567
|
-
let _exec_override: ExecOverride | null = null;
|
|
568
|
-
function _set_exec_override(fn: ExecOverride | null): void {
|
|
569
|
-
_exec_override = fn;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
function run(
|
|
573
|
-
args: readonly string[],
|
|
574
|
-
opts: { check?: boolean; capture?: boolean; cwd?: string | null } = {},
|
|
575
|
-
): RunResult {
|
|
576
|
-
const check = opts.check ?? true;
|
|
577
|
-
const capture = opts.capture ?? false;
|
|
578
|
-
const cwd = opts.cwd ?? REPO_ROOT;
|
|
579
|
-
|
|
580
|
-
const [cmd, ...rest] = args;
|
|
581
|
-
const res = _exec_override
|
|
582
|
-
? { ..._exec_override(args), error: undefined as Error | undefined }
|
|
583
|
-
: spawnSync(cmd as string, rest, {
|
|
584
|
-
cwd,
|
|
585
|
-
encoding: 'utf-8',
|
|
586
|
-
// capture_output=True → pipe; else inherit so child writes straight to
|
|
587
|
-
// this process's stdout/stderr (text mode, matching subprocess text=True).
|
|
588
|
-
stdio: capture ? ['ignore', 'pipe', 'pipe'] : ['inherit', 'inherit', 'inherit'],
|
|
589
|
-
});
|
|
590
|
-
|
|
591
|
-
if (res.error) {
|
|
592
|
-
// FileNotFoundError analogue (ENOENT) and other spawn failures — Python
|
|
593
|
-
// would raise here; only the explicit catchers (have / _count_tests_current)
|
|
594
|
-
// handle it. Surface as a thrown error.
|
|
595
|
-
throw res.error;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
const returncode = res.status ?? 0;
|
|
599
|
-
const stdout = capture ? res.stdout ?? '' : '';
|
|
600
|
-
const stderr = capture ? res.stderr ?? '' : '';
|
|
601
|
-
|
|
602
|
-
if (check && returncode !== 0) {
|
|
603
|
-
if (capture) {
|
|
604
|
-
const cmdStr = args.join(' ');
|
|
605
|
-
const out = (stderr || stdout || '').trim();
|
|
606
|
-
die(`command failed (${returncode}): ${cmdStr}\n${out}`);
|
|
607
|
-
}
|
|
608
|
-
// check && !capture → Python re-raises CalledProcessError; replicate.
|
|
609
|
-
throw new CalledProcessError(returncode, args);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
return { returncode, stdout, stderr };
|
|
613
|
-
}
|
|
614
|
-
|
|
615
|
-
function git(args: readonly string[], opts: { capture?: boolean } = {}): string {
|
|
616
|
-
const capture = opts.capture ?? false;
|
|
617
|
-
const r = run(['git', ...args], { capture });
|
|
618
|
-
return capture ? r.stdout.trim() : '';
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
function _sleep_ms(ms: number): void {
|
|
622
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/**
|
|
626
|
-
* `gh` with transient-failure retry and a readable death. Always captures, so
|
|
627
|
-
* the classifier can see stderr; `check: false` keeps the caller's fallback.
|
|
628
|
-
*/
|
|
629
|
-
function gh(args: readonly string[], opts: { check?: boolean } = {}): RunResult {
|
|
630
|
-
const check = opts.check ?? true;
|
|
631
|
-
const r = gh_retry(args, (argv) => run(argv, { check: false, capture: true }), {
|
|
632
|
-
sleep: (ms) => (_exec_override ? undefined : _sleep_ms(ms)),
|
|
633
|
-
notify: (m) => process.stdout.write(`${m}\n`),
|
|
634
|
-
});
|
|
635
|
-
if (check && r.returncode !== 0) {
|
|
636
|
-
die(`${gh_argv_label(args)} failed (${r.returncode}):\n${(r.stderr || r.stdout).trim()}`);
|
|
637
|
-
}
|
|
638
|
-
// Mutating calls used to inherit stdio — echo, or the PR / release URL
|
|
639
|
-
// vanishes. Probes (`check: false`) stay quiet; their stdout is JSON.
|
|
640
|
-
if (check && r.stdout.trim()) {
|
|
641
|
-
process.stdout.write(`${r.stdout.trim()}\n`);
|
|
642
|
-
}
|
|
643
|
-
return r;
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
/**
|
|
647
|
-
* One name per failing check, from `gh pr checks --json name,bucket` output.
|
|
648
|
-
*
|
|
649
|
-
* Pure over the JSON text so the shape is testable without gh. Returns []
|
|
650
|
-
* when the payload is unparseable or nothing sits in the `fail` bucket —
|
|
651
|
-
* the caller then falls back to the raw watch output alone.
|
|
652
|
-
*/
|
|
653
|
-
export function _failed_check_names(jsonText: string): string[] {
|
|
654
|
-
let parsed: unknown;
|
|
655
|
-
try {
|
|
656
|
-
parsed = JSON.parse(jsonText);
|
|
657
|
-
} catch {
|
|
658
|
-
return [];
|
|
659
|
-
}
|
|
660
|
-
if (!Array.isArray(parsed)) {
|
|
661
|
-
return [];
|
|
662
|
-
}
|
|
663
|
-
const names: string[] = [];
|
|
664
|
-
for (const entry of parsed) {
|
|
665
|
-
if (typeof entry !== 'object' || entry === null) {
|
|
666
|
-
continue;
|
|
667
|
-
}
|
|
668
|
-
const e = entry as Record<string, unknown>;
|
|
669
|
-
if (e['bucket'] === 'fail' && typeof e['name'] === 'string') {
|
|
670
|
-
names.push(e['name']);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
return names;
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
/**
|
|
677
|
-
* Remediation lines for a set of failing check names.
|
|
678
|
-
*
|
|
679
|
-
* The watch table scrolls failures out of view on a busy PR, so the operator
|
|
680
|
-
* repeatedly saw only `pass`/`skipping` rows plus "PR checks failed" — the
|
|
681
|
-
* 9.14.0 failure mode, twice. Name the red checks explicitly and, for the
|
|
682
|
-
* shape detector, say where the fix belongs (main, never the release branch).
|
|
683
|
-
*/
|
|
684
|
-
export function _failed_checks_report(names: readonly string[]): string {
|
|
685
|
-
if (names.length === 0) {
|
|
686
|
-
return '';
|
|
687
|
-
}
|
|
688
|
-
const lines: string[] = ['', 'Failing check(s):'];
|
|
689
|
-
for (const name of names) {
|
|
690
|
-
lines.push(` ❌ ${name}`);
|
|
691
|
-
}
|
|
692
|
-
if (names.includes('Release-PR shape detector')) {
|
|
693
|
-
lines.push(
|
|
694
|
-
'',
|
|
695
|
-
'Release-PR shape: a release PR may only contain the version-bump',
|
|
696
|
-
'allowlist. Land the out-of-shape files on main via their own PR, then',
|
|
697
|
-
'merge main into the release branch — their release-PR diff becomes',
|
|
698
|
-
'empty (docs/contracts/release-pr-gating.md § Mid-release fixes).',
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
if (names.includes('PR body equals CHANGELOG entry')) {
|
|
702
|
-
lines.push(
|
|
703
|
-
'',
|
|
704
|
-
'Surface equality: the PR body drifted from the CHANGELOG entry. A',
|
|
705
|
-
'resume run refreshes it from the branch head (release-truth Phase 1);',
|
|
706
|
-
'never hand-edit the PR body away from the changelog section.',
|
|
707
|
-
);
|
|
708
|
-
}
|
|
709
|
-
if (names.includes('Curated highlights plausible against the span')) {
|
|
710
|
-
lines.push(
|
|
711
|
-
'',
|
|
712
|
-
'Highlights: a curated `_none_` field contradicts the release span.',
|
|
713
|
-
'Fill the curated head in CHANGELOG.md on the release branch — the',
|
|
714
|
-
'gate output lists the deriving commits (release-truth Phase 2).',
|
|
715
|
-
);
|
|
716
|
-
}
|
|
717
|
-
if (names.includes('Blocking review findings dispositioned')) {
|
|
718
|
-
lines.push(
|
|
719
|
-
'',
|
|
720
|
-
'Dispositions: a blocking/high self-review finding has no committed',
|
|
721
|
-
'disposition. Ingest + adjudicate it in',
|
|
722
|
-
'agents/evidence/release-findings/<version>.json (release-truth Phase 3):',
|
|
723
|
-
' ./scripts-run src/scripts/check_finding_dispositions --ingest <findings.json>',
|
|
724
|
-
);
|
|
725
|
-
}
|
|
726
|
-
lines.push('', 'After fixing, resume with: task release -- --resume --yes', '');
|
|
727
|
-
return lines.join('\n');
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
/** Rounds of re-watching before an absent required check is called absent. */
|
|
731
|
-
const _NO_CHECKS_ROUNDS = 10;
|
|
732
|
-
/** Delay between those rounds — 10 × 60 s ≈ 10 minutes of tolerance. */
|
|
733
|
-
const _NO_CHECKS_DELAY_MS = 60_000;
|
|
734
|
-
|
|
735
|
-
/**
|
|
736
|
-
* Required status-check contexts for a branch, parsed from
|
|
737
|
-
* `gh api repos/{owner}/{repo}/rules/branches/{branch}`.
|
|
738
|
-
*
|
|
739
|
-
* That endpoint is the one that answers the question here: this repo protects
|
|
740
|
-
* `main` with a RULESET, and the classic `/branches/main/protection` API
|
|
741
|
-
* answers "Branch not protected" for it — so a check built on the classic
|
|
742
|
-
* endpoint would conclude "no required checks" and wave everything through.
|
|
743
|
-
*
|
|
744
|
-
* Any parse or shape surprise yields [] — the caller then behaves exactly as
|
|
745
|
-
* it did before this function existed.
|
|
746
|
-
*/
|
|
747
|
-
export function _required_contexts_from_rules(json: string): string[] {
|
|
748
|
-
let parsed: unknown;
|
|
749
|
-
try {
|
|
750
|
-
parsed = JSON.parse(json);
|
|
751
|
-
} catch {
|
|
752
|
-
return [];
|
|
753
|
-
}
|
|
754
|
-
if (!Array.isArray(parsed)) return [];
|
|
755
|
-
const out: string[] = [];
|
|
756
|
-
for (const rule of parsed) {
|
|
757
|
-
const r = rule as { type?: unknown; parameters?: { required_status_checks?: unknown } };
|
|
758
|
-
if (r.type !== 'required_status_checks') continue;
|
|
759
|
-
const checks = r.parameters?.required_status_checks;
|
|
760
|
-
if (!Array.isArray(checks)) continue;
|
|
761
|
-
for (const c of checks) {
|
|
762
|
-
const ctx = (c as { context?: unknown }).context;
|
|
763
|
-
if (typeof ctx === 'string' && ctx !== '') out.push(ctx);
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
return out;
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
export type NoChecksAction = 'retry' | 'accept' | 'die';
|
|
770
|
-
|
|
771
|
-
/**
|
|
772
|
-
* What to do when `gh pr checks` reports no checks at all.
|
|
773
|
-
*
|
|
774
|
-
* The old code had one answer — accept — for two situations it could not tell
|
|
775
|
-
* apart: a repo that configures no required checks (nothing will ever arrive,
|
|
776
|
-
* accepting is correct) and a repo whose checks have simply not been
|
|
777
|
-
* dispatched yet (accepting merges, tags and publishes unvalidated).
|
|
778
|
-
*
|
|
779
|
-
* The branch's own protection rules separate them. Required contexts declared
|
|
780
|
-
* ⇒ something IS expected, so absence means not-yet, and the answer is to wait
|
|
781
|
-
* and finally to refuse. None declared ⇒ the pre-existing tolerance stands.
|
|
782
|
-
*/
|
|
783
|
-
export function _no_checks_action(
|
|
784
|
-
required: readonly string[],
|
|
785
|
-
round: number,
|
|
786
|
-
maxRounds: number = _NO_CHECKS_ROUNDS,
|
|
787
|
-
): NoChecksAction {
|
|
788
|
-
if (required.length === 0) return 'accept';
|
|
789
|
-
return round < maxRounds ? 'retry' : 'die';
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
/**
|
|
793
|
-
* Watch PR checks and tolerate the 'no checks' case ONLY where it is tolerable.
|
|
794
|
-
*
|
|
795
|
-
* `gh pr checks --watch` exits 1 both on real failures and when no checks are
|
|
796
|
-
* reported at all. Real failures still die.
|
|
797
|
-
*
|
|
798
|
-
* Measured 2026-08-06/07: during a critical GitHub Actions incident
|
|
799
|
-
* (15:22–02:04 UTC, webhook triggers throttled from 20:34) `pull_request`
|
|
800
|
-
* events were delivered 15–30 minutes late. The five-second grace period below
|
|
801
|
-
* expires long before that, so the old unconditional accept would have tagged
|
|
802
|
-
* `main` and published to npm without a single check having run. It did not
|
|
803
|
-
* only because `main` happened to be independently green.
|
|
804
|
-
*
|
|
805
|
-
* So the tolerance is now conditional on the branch declaring no required
|
|
806
|
-
* checks. Where it declares some, absence is treated as latency: re-watch, and
|
|
807
|
-
* refuse rather than release blind.
|
|
808
|
-
*
|
|
809
|
-
* A short grace period gives GitHub time to register workflow runs on a
|
|
810
|
-
* freshly-pushed branch.
|
|
811
|
-
*/
|
|
812
|
-
function watch_pr_checks(branch: string): void {
|
|
813
|
-
if (_exec_override === null) {
|
|
814
|
-
// time.sleep(5) — blocking grace period for GitHub to register runs
|
|
815
|
-
// on a freshly-pushed branch. Skipped under the drill seam: there is
|
|
816
|
-
// no GitHub to wait for, and 5s per simulated wait adds up.
|
|
817
|
-
const until = Date.now() + 5000;
|
|
818
|
-
while (Date.now() < until) {
|
|
819
|
-
// busy-wait stand-in for time.sleep(5) without an event-loop yield.
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
// Rounds exist only for the no-checks-yet case below; every other outcome
|
|
823
|
-
// returns or dies on the first pass, exactly as before.
|
|
824
|
-
for (let round = 0; ; round++) {
|
|
825
|
-
// The branch is passed explicitly — `gh` inferring the PR from HEAD
|
|
826
|
-
// resolved to `main` mid-run once (2026-08-03, 9.16.0 resume) and the
|
|
827
|
-
// whole release died on "no pull requests found for branch main".
|
|
828
|
-
const proc = run(['gh', 'pr', 'checks', branch, '--watch'], {
|
|
829
|
-
check: false,
|
|
830
|
-
capture: true,
|
|
831
|
-
});
|
|
832
|
-
const output = ((proc.stdout || '') + (proc.stderr || '')).trim();
|
|
833
|
-
const returncode = proc.returncode;
|
|
834
|
-
if (returncode === 0) {
|
|
835
|
-
if (output) {
|
|
836
|
-
process.stdout.write(output + '\n');
|
|
837
|
-
}
|
|
838
|
-
return;
|
|
839
|
-
}
|
|
840
|
-
if (output.toLowerCase().includes('no checks reported')) {
|
|
841
|
-
const rules = run(
|
|
842
|
-
['gh', 'api', `repos/{owner}/{repo}/rules/branches/${MAIN_BRANCH}`],
|
|
843
|
-
{ check: false, capture: true },
|
|
844
|
-
);
|
|
845
|
-
const required = _required_contexts_from_rules(rules.stdout ?? '');
|
|
846
|
-
const action = _no_checks_action(required, round, _NO_CHECKS_ROUNDS);
|
|
847
|
-
|
|
848
|
-
if (action === 'accept') {
|
|
849
|
-
process.stdout.write(`⚠️ ${output}\n`);
|
|
850
|
-
process.stdout.write(
|
|
851
|
-
' Continuing without check validation — configure required ' +
|
|
852
|
-
'checks in branch protection to enforce this gate.\n',
|
|
853
|
-
);
|
|
854
|
-
return;
|
|
855
|
-
}
|
|
856
|
-
if (action === 'retry') {
|
|
857
|
-
process.stdout.write(
|
|
858
|
-
`⏳ no checks reported yet, but ${MAIN_BRANCH} requires ` +
|
|
859
|
-
`${String(required.length)} (${required.join(', ')}) — ` +
|
|
860
|
-
`waiting (${String(round + 1)}/${String(_NO_CHECKS_ROUNDS)})\n`,
|
|
861
|
-
);
|
|
862
|
-
if (_exec_override === null) {
|
|
863
|
-
const until = Date.now() + _NO_CHECKS_DELAY_MS;
|
|
864
|
-
while (Date.now() < until) {
|
|
865
|
-
// blocking wait, same shape as the grace period above
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
continue;
|
|
869
|
-
}
|
|
870
|
-
die(
|
|
871
|
-
`no checks reported for ${branch} after ` +
|
|
872
|
-
`${String(_NO_CHECKS_ROUNDS)} rounds, but ${MAIN_BRANCH} requires ` +
|
|
873
|
-
`${required.join(', ')}. Refusing to merge, tag and publish ` +
|
|
874
|
-
'unvalidated — GitHub may be delaying event delivery (check ' +
|
|
875
|
-
'githubstatus.com). Re-run with `task release -- --resume --yes` ' +
|
|
876
|
-
'once the checks appear.',
|
|
877
|
-
);
|
|
878
|
-
}
|
|
879
|
-
if (output) {
|
|
880
|
-
process.stderr.write(output + '\n');
|
|
881
|
-
}
|
|
882
|
-
const summary = run(['gh', 'pr', 'checks', branch, '--json', 'name,bucket'], {
|
|
883
|
-
check: false,
|
|
884
|
-
capture: true,
|
|
885
|
-
});
|
|
886
|
-
const report = _failed_checks_report(_failed_check_names(summary.stdout ?? ''));
|
|
887
|
-
if (report) {
|
|
888
|
-
process.stderr.write(report + '\n');
|
|
889
|
-
}
|
|
890
|
-
die(`PR checks failed (exit ${returncode})`);
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
/**
|
|
895
|
-
* Push the release branch, integrating a remote that moved under us.
|
|
896
|
-
*
|
|
897
|
-
* A GitHub-side "Update branch" click (or a concurrent resume run) advances
|
|
898
|
-
* `origin/<branch>` while the local ref stays behind; a bare `git push` is
|
|
899
|
-
* then rejected with "fetch first" and the whole release run dies (measured
|
|
900
|
-
* 2026-08-03, 9.15.0). Same-branch commits are never ours to drop, so the
|
|
901
|
-
* recovery is merge-and-retry, never force-push.
|
|
902
|
-
*/
|
|
903
|
-
/**
|
|
904
|
-
* True when git rejected the push because the remote ref moved ahead — the
|
|
905
|
-
* ONLY failure the merge-and-retry recovery below can repair.
|
|
906
|
-
*
|
|
907
|
-
* git's wording for this is stable across versions: the hint is `fetch first`
|
|
908
|
-
* / `Updates were rejected`, and the ref line carries `[rejected]` with
|
|
909
|
-
* `non-fast-forward` or `fetch first`. A stale lease is a SECOND wording —
|
|
910
|
-
* `[remote rejected]` + `cannot lock ref … but expected`; pinned in the tests.
|
|
911
|
-
*/
|
|
912
|
-
export function _is_non_fast_forward(stderr: string, stdout: string): boolean {
|
|
913
|
-
const text = `${stderr}\n${stdout}`;
|
|
914
|
-
if (!/\[(?:remote )?rejected\]/i.test(text)) return false;
|
|
915
|
-
if (/cannot lock ref[^\n]*but expected/i.test(text)) return true;
|
|
916
|
-
return /(non-fast-forward|fetch first|behind its remote counterpart)/i.test(text);
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
function push_release_branch(branch: string): void {
|
|
920
|
-
const first = run(['git', 'push', '-u', REMOTE, branch], { check: false, capture: true });
|
|
921
|
-
if (first.returncode === 0) {
|
|
922
|
-
process.stdout.write(first.stdout);
|
|
923
|
-
process.stderr.write(first.stderr);
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
// A push can fail for many reasons — a pre-push hook refusing the commit,
|
|
928
|
-
// no credentials, a protected branch. Only ONE of them is repaired by
|
|
929
|
-
// fetch+merge+retry, and the recovery's first step (`git fetch <branch>`)
|
|
930
|
-
// exits 128 for a branch that was never pushed. Taking that path
|
|
931
|
-
// unconditionally replaced every real error with
|
|
932
|
-
// `couldn't find remote ref <branch>` — measured 2026-08-06 on 9.26.0,
|
|
933
|
-
// where a pre-push gate rejection surfaced as a git-fetch crash and cost
|
|
934
|
-
// an hour of diagnosis pointed at the wrong layer.
|
|
935
|
-
if (!_is_non_fast_forward(first.stderr, first.stdout)) {
|
|
936
|
-
process.stdout.write(first.stdout);
|
|
937
|
-
process.stderr.write(first.stderr);
|
|
938
|
-
die(
|
|
939
|
-
`push of ${branch} failed (exit ${first.returncode}) and the remote ref did not move — ` +
|
|
940
|
-
'this is not the fetch-and-retry case. The push output above is the real error ' +
|
|
941
|
-
'(a pre-push gate, credentials, or branch protection); fix that and re-run.',
|
|
942
|
-
);
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
process.stdout.write(`↻ ${REMOTE}/${branch} moved — integrating and retrying push\n`);
|
|
946
|
-
run(['git', 'fetch', REMOTE, branch]);
|
|
947
|
-
run(['git', 'merge', '--no-edit', `${REMOTE}/${branch}`]);
|
|
948
|
-
run(['git', 'push', '-u', REMOTE, branch]);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
/** mergeStateStatus of the branch's PR ('' when the probe fails). */
|
|
952
|
-
function _pr_merge_state(branch: string): string {
|
|
953
|
-
const r = gh(['pr', 'view', branch, '--json', 'mergeStateStatus', '--jq', '.mergeStateStatus'], {
|
|
954
|
-
check: false,
|
|
955
|
-
});
|
|
956
|
-
return r.returncode === 0 ? r.stdout.trim() : '';
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
/**
|
|
960
|
-
* Bound on update-and-retry rounds in merge_release_pr. Three is deliberate:
|
|
961
|
-
* main receiving unrelated merges three times inside one checks-wait window
|
|
962
|
-
* means the queue is hot and a human should decide, not a loop.
|
|
963
|
-
*/
|
|
964
|
-
const _MERGE_UPDATE_ROUNDS = 3;
|
|
965
|
-
|
|
966
|
-
/**
|
|
967
|
-
* Merge the release PR, absorbing a moving base branch.
|
|
968
|
-
*
|
|
969
|
-
* Branch protection requires the head to be up to date with `main`, and
|
|
970
|
-
* `main` moves constantly (dependabot, unrelated merges) — so between the
|
|
971
|
-
* checks-wait and the merge the PR routinely flips to BEHIND and a bare
|
|
972
|
-
* `gh pr merge` dies with "the head branch is not up to date with the base
|
|
973
|
-
* branch" (measured 2026-08-03, 9.16.0). Recovery is mechanical and safe by
|
|
974
|
-
* construction: merging `origin/main` into `release/X.Y.Z` makes the fix
|
|
975
|
-
* files identical on both sides, so the release-PR diff stays inside the
|
|
976
|
-
* version-bump allowlist (docs/contracts/release-pr-gating.md § Mid-release
|
|
977
|
-
* fixes) — do it here instead of aborting the run.
|
|
978
|
-
*/
|
|
979
|
-
/**
|
|
980
|
-
* Re-derive the release-PR body from the CHANGELOG section at the current
|
|
981
|
-
* release-branch head and push it to the PR (release-truth Phase 1: one
|
|
982
|
-
* final source — late commits and maintainer edits on the branch must reach
|
|
983
|
-
* the PR body instead of leaving it frozen at plan time). Best-effort sync
|
|
984
|
-
* of the local branch first; a no-op when the derived body is unchanged.
|
|
985
|
-
*/
|
|
986
|
-
function _target_from_branch(branch: string): string {
|
|
987
|
-
return branch.replace(/^release\//u, '');
|
|
988
|
-
}
|
|
989
|
-
|
|
990
|
-
function _refresh_pr_body_from_head(branch: string, target: string): void {
|
|
991
|
-
run(['git', 'pull', '--ff-only', REMOTE, branch], { check: false });
|
|
992
|
-
const changelog = fs.readFileSync(CHANGELOG, 'utf-8');
|
|
993
|
-
const section = extract_changelog_section(changelog, target);
|
|
994
|
-
if (!section) {
|
|
995
|
-
die(`CHANGELOG.md at ${branch} head carries no section for ${target} — cannot derive PR body`);
|
|
996
|
-
}
|
|
997
|
-
const capped = _cap_body(section!.body, GH_PR_BODY_LIMIT - 200, '`CHANGELOG.md` in this PR');
|
|
998
|
-
const body = pr_body_from_section(capped, target);
|
|
999
|
-
const live = gh(['pr', 'view', branch, '--json', 'body', '-q', '.body'], { check: false });
|
|
1000
|
-
if (live.returncode === 0 && live.stdout.replace(/\r\n/gu, '\n').trim() === body.trim()) {
|
|
1001
|
-
return;
|
|
1002
|
-
}
|
|
1003
|
-
gh(['pr', 'edit', branch, '--body', body]);
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
function merge_release_pr(branch: string, wait_for_checks: boolean): void {
|
|
1007
|
-
if (git(['rev-parse', '--abbrev-ref', 'HEAD'], { capture: true }) !== branch) {
|
|
1008
|
-
run(['git', 'checkout', branch]);
|
|
1009
|
-
}
|
|
1010
|
-
for (let round = 0; round <= _MERGE_UPDATE_ROUNDS; round++) {
|
|
1011
|
-
// One final generation step at the final head: every merge attempt
|
|
1012
|
-
// first re-derives the PR body from the branch-head CHANGELOG, so
|
|
1013
|
-
// update rounds and maintainer edits can never desynchronize it.
|
|
1014
|
-
_refresh_pr_body_from_head(branch, _target_from_branch(branch));
|
|
1015
|
-
if (_pr_merge_state(branch) !== 'BEHIND') {
|
|
1016
|
-
const merged = run(['gh', 'pr', 'merge', branch, '--merge', '--delete-branch'], {
|
|
1017
|
-
check: false,
|
|
1018
|
-
});
|
|
1019
|
-
if (merged.returncode === 0) {
|
|
1020
|
-
return;
|
|
1021
|
-
}
|
|
1022
|
-
if (_pr_merge_state(branch) !== 'BEHIND') {
|
|
1023
|
-
// Not the moving-base failure — surface it unchanged.
|
|
1024
|
-
throw new CalledProcessError(merged.returncode, [
|
|
1025
|
-
'gh',
|
|
1026
|
-
'pr',
|
|
1027
|
-
'merge',
|
|
1028
|
-
branch,
|
|
1029
|
-
'--merge',
|
|
1030
|
-
'--delete-branch',
|
|
1031
|
-
]);
|
|
1032
|
-
}
|
|
1033
|
-
// BEHIND appeared between the probe and the merge — fall through
|
|
1034
|
-
// to the update below.
|
|
1035
|
-
}
|
|
1036
|
-
if (round === _MERGE_UPDATE_ROUNDS) {
|
|
1037
|
-
break;
|
|
1038
|
-
}
|
|
1039
|
-
process.stdout.write(
|
|
1040
|
-
`↻ head behind ${MAIN_BRANCH} (update round ${round + 1}/${_MERGE_UPDATE_ROUNDS}) — ` +
|
|
1041
|
-
`merging ${REMOTE}/${MAIN_BRANCH} into ${branch} and re-running checks\n`,
|
|
1042
|
-
);
|
|
1043
|
-
run(['git', 'fetch', REMOTE, MAIN_BRANCH]);
|
|
1044
|
-
run(['git', 'merge', '--no-edit', `${REMOTE}/${MAIN_BRANCH}`]);
|
|
1045
|
-
push_release_branch(branch);
|
|
1046
|
-
if (wait_for_checks) {
|
|
1047
|
-
watch_pr_checks(branch);
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
die(
|
|
1051
|
-
`release PR is still behind ${MAIN_BRANCH} after ${_MERGE_UPDATE_ROUNDS} update round(s) — ` +
|
|
1052
|
-
`${MAIN_BRANCH} is moving faster than the checks complete. ` +
|
|
1053
|
-
'Re-run when the queue quiets down: task release -- --resume --yes',
|
|
1054
|
-
);
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
function have(bin: string): boolean {
|
|
1058
|
-
const res = spawnSync('which', [bin], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
1059
|
-
if (res.error) {
|
|
1060
|
-
// FileNotFoundError on `which` itself → treat as not found (returncode != 0).
|
|
1061
|
-
return false;
|
|
1062
|
-
}
|
|
1063
|
-
return (res.status ?? 1) === 0;
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
// ─── resume-mode state probes ────────────────────────────────────────────────
|
|
1067
|
-
|
|
1068
|
-
function _branch_exists_local(branch: string): boolean {
|
|
1069
|
-
const r = run(['git', 'rev-parse', '--verify', '--quiet', `refs/heads/${branch}`], {
|
|
1070
|
-
check: false,
|
|
1071
|
-
capture: true,
|
|
1072
|
-
});
|
|
1073
|
-
return r.returncode === 0;
|
|
1074
|
-
}
|
|
1075
|
-
|
|
1076
|
-
function _branch_exists_remote(branch: string): boolean {
|
|
1077
|
-
const r = run(['git', 'ls-remote', '--exit-code', '--heads', REMOTE, branch], {
|
|
1078
|
-
check: false,
|
|
1079
|
-
capture: true,
|
|
1080
|
-
});
|
|
1081
|
-
return r.returncode === 0;
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
function _tag_exists_local(tag: string): boolean {
|
|
1085
|
-
return git(['tag', '-l', tag], { capture: true }).split('\n').includes(tag);
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
function _tag_exists_remote(tag: string): boolean {
|
|
1089
|
-
const r = run(['git', 'ls-remote', '--exit-code', '--tags', REMOTE, tag], {
|
|
1090
|
-
check: false,
|
|
1091
|
-
capture: true,
|
|
1092
|
-
});
|
|
1093
|
-
return r.returncode === 0;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
|
-
/**
|
|
1097
|
-
* True when git rejected a tag push because the ref already exists on the
|
|
1098
|
-
* remote. Wording pinned from the real failure (2026-08-09, 9.28.0 — two
|
|
1099
|
-
* concurrent resume runs, the loser crashed):
|
|
1100
|
-
*
|
|
1101
|
-
* ! [remote rejected] 9.28.0 -> 9.28.0 (cannot lock ref 'refs/tags/9.28.0': reference already exists)
|
|
1102
|
-
*
|
|
1103
|
-
* plus the `[rejected] … (already exists)` variant git emits when the local
|
|
1104
|
-
* and remote tag objects differ.
|
|
1105
|
-
*/
|
|
1106
|
-
export function _is_tag_already_exists(stderr: string, stdout: string): boolean {
|
|
1107
|
-
const text = `${stderr}\n${stdout}`;
|
|
1108
|
-
return /\[(?:remote )?rejected\]/i.test(text) && /already exists/i.test(text);
|
|
1109
|
-
}
|
|
1110
|
-
|
|
1111
|
-
/** Peeled commit the remote tag points at, or null when the tag is absent. */
|
|
1112
|
-
function _remote_tag_commit(tag: string): string | null {
|
|
1113
|
-
const r = run(['git', 'ls-remote', REMOTE, `refs/tags/${tag}`, `refs/tags/${tag}^{}`], {
|
|
1114
|
-
check: false,
|
|
1115
|
-
capture: true,
|
|
1116
|
-
});
|
|
1117
|
-
if (r.returncode !== 0) return null;
|
|
1118
|
-
const lines = r.stdout.trim().split('\n').filter(Boolean);
|
|
1119
|
-
if (lines.length === 0) return null;
|
|
1120
|
-
// An annotated tag lists the tag object AND the peeled `^{}` commit — the
|
|
1121
|
-
// peeled line wins so the comparison is commit-vs-commit regardless of
|
|
1122
|
-
// whether the two runs minted distinct tag objects for the same commit.
|
|
1123
|
-
const peeled = lines.find((l) => l.trimEnd().endsWith('^{}'));
|
|
1124
|
-
return (peeled ?? (lines[0] as string)).split('\t')[0] ?? null;
|
|
1125
|
-
}
|
|
1126
|
-
|
|
1127
|
-
/**
|
|
1128
|
-
* Push a tag, tolerating exactly ONE failure: the concurrent-release race.
|
|
1129
|
-
* `_tag_exists_remote` in step 8 is a live check, but between it and this
|
|
1130
|
-
* push a parallel `task release` run can land the same tag (measured
|
|
1131
|
-
* 2026-08-09, 9.28.0). When the remote tag already points at the same commit
|
|
1132
|
-
* as the local one, the repository IS in the desired state — continue. Any
|
|
1133
|
-
* other rejection, and a same-name tag on a DIFFERENT commit, stays fatal
|
|
1134
|
-
* with the push output as the error.
|
|
1135
|
-
*/
|
|
1136
|
-
function _push_tag(tag: string): void {
|
|
1137
|
-
const first = run(['git', 'push', REMOTE, tag], { check: false, capture: true });
|
|
1138
|
-
if (first.returncode === 0) {
|
|
1139
|
-
process.stdout.write(first.stdout);
|
|
1140
|
-
process.stderr.write(first.stderr);
|
|
1141
|
-
return;
|
|
1142
|
-
}
|
|
1143
|
-
if (_is_tag_already_exists(first.stderr, first.stdout)) {
|
|
1144
|
-
const remote = _remote_tag_commit(tag);
|
|
1145
|
-
const local = git(['rev-list', '-n', '1', tag], { capture: true });
|
|
1146
|
-
if (remote !== null && remote === local) {
|
|
1147
|
-
process.stdout.write(
|
|
1148
|
-
`↻ tag ${tag} already on ${REMOTE} at the same commit — a parallel run pushed it; continuing\n`,
|
|
1149
|
-
);
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
process.stdout.write(first.stdout);
|
|
1153
|
-
process.stderr.write(first.stderr);
|
|
1154
|
-
die(
|
|
1155
|
-
`tag ${tag} exists on ${REMOTE} pointing at ${remote ?? '<unreadable>'} while the local tag ` +
|
|
1156
|
-
`points at ${local} — refusing to overwrite a published tag; ` +
|
|
1157
|
-
'delete the wrong one deliberately and re-run',
|
|
1158
|
-
);
|
|
1159
|
-
}
|
|
1160
|
-
process.stdout.write(first.stdout);
|
|
1161
|
-
process.stderr.write(first.stderr);
|
|
1162
|
-
die(
|
|
1163
|
-
`push of tag ${tag} failed (exit ${first.returncode}) — ` +
|
|
1164
|
-
'the push output above is the real error (a pre-push gate, credentials, or protection)',
|
|
1165
|
-
);
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
/** Most recent PR (any state) with `release/X.Y.Z` as head, or null. */
|
|
1169
|
-
function _pr_for_branch(branch: string): Record<string, unknown> | null {
|
|
1170
|
-
const argv = ['pr', 'list', '--head', branch, '--state', 'all', '--json', 'number,state,url'];
|
|
1171
|
-
const r = gh([...argv, '--limit', '1'], { check: false });
|
|
1172
|
-
if (r.returncode !== 0) {
|
|
1173
|
-
return null;
|
|
1174
|
-
}
|
|
1175
|
-
let items: unknown;
|
|
1176
|
-
try {
|
|
1177
|
-
items = JSON.parse(r.stdout || '[]');
|
|
1178
|
-
} catch {
|
|
1179
|
-
return null;
|
|
1180
|
-
}
|
|
1181
|
-
return Array.isArray(items) && items.length > 0 ? (items[0] as Record<string, unknown>) : null;
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
function _release_exists(tag: string): boolean {
|
|
1185
|
-
const r = run(['gh', 'release', 'view', tag], { check: false, capture: true });
|
|
1186
|
-
return r.returncode === 0;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
404
|
// ─── version math ─────────────────────────────────────────────────────────────
|
|
1190
405
|
|
|
1191
406
|
const SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)$/;
|
|
@@ -1594,17 +809,6 @@ function set_lockfile_version(p: string, version: string): void {
|
|
|
1594
809
|
fs.writeFileSync(p, jsonDumpsIndent(data, 4) + '\n', 'utf-8');
|
|
1595
810
|
}
|
|
1596
811
|
|
|
1597
|
-
/** Update `metadata.version`; preserve 2-space indentation + UTF-8. */
|
|
1598
|
-
function set_marketplace_version(p: string, version: string): void {
|
|
1599
|
-
const data = JSON.parse(fs.readFileSync(p, 'utf-8')) as Record<string, unknown>;
|
|
1600
|
-
// data.setdefault("metadata", {})["version"] = version — preserve key order.
|
|
1601
|
-
if (!(typeof data['metadata'] === 'object' && data['metadata'] !== null && !Array.isArray(data['metadata']))) {
|
|
1602
|
-
data['metadata'] = {};
|
|
1603
|
-
}
|
|
1604
|
-
(data['metadata'] as Record<string, unknown>)['version'] = version;
|
|
1605
|
-
fs.writeFileSync(p, jsonDumpsIndent(data, 2) + '\n', 'utf-8');
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
812
|
function set_template_pin(p: string, version: string): void {
|
|
1609
813
|
// Rewrite the single `agent_config_version:` line in place, preserving the
|
|
1610
814
|
// rest of the YAML byte-for-byte. Quoted value to match the existing pin
|
|
@@ -2062,11 +1266,13 @@ function execute(
|
|
|
2062
1266
|
_step(
|
|
2063
1267
|
2,
|
|
2064
1268
|
total,
|
|
2065
|
-
'Bump package.json + package-lock.json + marketplace
|
|
1269
|
+
'Bump package.json + package-lock.json + marketplace manifests + template pin, prepend CHANGELOG',
|
|
2066
1270
|
);
|
|
2067
1271
|
set_package_version(PACKAGE_JSON, plan.target);
|
|
2068
1272
|
set_lockfile_version(PACKAGE_LOCK_JSON, plan.target);
|
|
2069
1273
|
set_marketplace_version(MARKETPLACE_JSON, plan.target);
|
|
1274
|
+
set_augment_manifest_version(AUGMENT_PLUGIN_JSON, plan.target);
|
|
1275
|
+
set_augment_manifest_version(AUGMENT_MARKETPLACE_JSON, plan.target);
|
|
2070
1276
|
set_template_pin(PROJECT_TEMPLATE, plan.target);
|
|
2071
1277
|
prepend_changelog(CHANGELOG, plan.changelog_entry);
|
|
2072
1278
|
}
|