mandrel 2.59.0 → 2.61.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/.agents/README.md +21 -19
- package/.agents/agents/acceptance-critic.md +24 -43
- package/.agents/agents/story-worker.md +20 -21
- package/.agents/docs/agentrc-reference.json +5 -84
- package/.agents/docs/configuration.md +48 -132
- package/.agents/docs/workflows.md +2 -2
- package/.agents/instructions.md +5 -8
- package/.agents/rules/ci-remediation.md +41 -8
- package/.agents/rules/gherkin-standards.md +4 -0
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +16 -496
- package/.agents/schemas/story-deliver-terminal.schema.json +13 -3
- package/.agents/schemas/validation-evidence.schema.json +2 -1
- package/.agents/scripts/README.md +23 -13
- package/.agents/scripts/acceptance-eval.js +35 -169
- package/.agents/scripts/agents-bootstrap-github.js +27 -129
- package/.agents/scripts/apply-quality-bootstrap.js +4 -40
- package/.agents/scripts/audit-baselines.js +6 -27
- package/.agents/scripts/audit-labels-bootstrap.js +5 -29
- package/.agents/scripts/audit-to-stories.js +91 -361
- package/.agents/scripts/boot-sweep.js +19 -72
- package/.agents/scripts/bootstrap.js +74 -395
- package/.agents/scripts/ceremony-derive.js +10 -41
- package/.agents/scripts/check-arch-cycles.js +14 -64
- package/.agents/scripts/check-baselines.js +8 -48
- package/.agents/scripts/check-context-budget.js +105 -311
- package/.agents/scripts/check-cyclomatic.js +12 -44
- package/.agents/scripts/check-dead-exports.js +13 -64
- package/.agents/scripts/check-doc-links.js +38 -186
- package/.agents/scripts/check-gherkin-corpus.js +24 -127
- package/.agents/scripts/check-test-temp-hygiene.js +28 -169
- package/.agents/scripts/coverage-capture.js +34 -98
- package/.agents/scripts/deliver-light.js +25 -171
- package/.agents/scripts/deliver-recover.js +6 -42
- package/.agents/scripts/deliver-run.js +534 -0
- package/.agents/scripts/diagnose-friction.js +24 -116
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +23 -85
- package/.agents/scripts/file-ci-gap.js +57 -49
- package/.agents/scripts/generate-config-docs.js +30 -170
- package/.agents/scripts/generate-lens-checklists.js +8 -52
- package/.agents/scripts/generate-skills-index.js +14 -112
- package/.agents/scripts/generate-workflows-doc.js +10 -70
- package/.agents/scripts/git-cleanup.js +2 -34
- package/.agents/scripts/lib/Graph.js +24 -81
- package/.agents/scripts/lib/ITicketingProvider.js +47 -143
- package/.agents/scripts/lib/Logger.js +14 -76
- package/.agents/scripts/lib/audit-baselines/engine.js +11 -31
- package/.agents/scripts/lib/audit-baselines/gate-surface.js +3 -17
- package/.agents/scripts/lib/audit-baselines/headroom.js +4 -20
- package/.agents/scripts/lib/audit-baselines/hotspots.js +3 -15
- package/.agents/scripts/lib/audit-baselines/kinds.js +23 -89
- package/.agents/scripts/lib/audit-baselines/outliers.js +6 -23
- package/.agents/scripts/lib/audit-baselines/read.js +3 -16
- package/.agents/scripts/lib/audit-baselines/staleness.js +7 -26
- package/.agents/scripts/lib/audit-baselines/surface-entry.js +7 -28
- package/.agents/scripts/lib/audit-baselines/trend.js +7 -20
- package/.agents/scripts/lib/audit-baselines/weights.js +9 -37
- package/.agents/scripts/lib/audit-suite/audit-rules-reader.js +3 -18
- package/.agents/scripts/lib/audit-suite/checklist-threading.js +26 -124
- package/.agents/scripts/lib/audit-suite/dispatch-checklist.js +14 -45
- package/.agents/scripts/lib/audit-suite/findings.js +12 -52
- package/.agents/scripts/lib/audit-suite/frontmatter.js +5 -29
- package/.agents/scripts/lib/audit-suite/index.js +1 -15
- package/.agents/scripts/lib/audit-suite/lens-checklist.js +10 -45
- package/.agents/scripts/lib/audit-suite/lens-diff-floor.js +11 -76
- package/.agents/scripts/lib/audit-suite/runner.js +13 -43
- package/.agents/scripts/lib/audit-suite/selector.js +49 -349
- package/.agents/scripts/lib/audit-suite/substitutions.js +12 -40
- package/.agents/scripts/lib/audit-suite/workflow-loader.js +3 -15
- package/.agents/scripts/lib/audit-to-stories/audit-label-taxonomy.js +11 -73
- package/.agents/scripts/lib/audit-to-stories/audit-lenses.js +8 -37
- package/.agents/scripts/lib/audit-to-stories/build-story-body.js +25 -143
- package/.agents/scripts/lib/audit-to-stories/dedupe-against-github.js +14 -80
- package/.agents/scripts/lib/audit-to-stories/epic-grouping-directive.js +4 -18
- package/.agents/scripts/lib/audit-to-stories/finding-adapter.js +7 -40
- package/.agents/scripts/lib/audit-to-stories/group-findings.js +9 -39
- package/.agents/scripts/lib/audit-to-stories/issue-corpus.js +10 -66
- package/.agents/scripts/lib/audit-to-stories/issue-index.js +6 -30
- package/.agents/scripts/lib/audit-to-stories/issues-file.js +6 -36
- package/.agents/scripts/lib/audit-to-stories/ledger-commit.js +17 -69
- package/.agents/scripts/lib/audit-to-stories/ledger-pr.js +17 -85
- package/.agents/scripts/lib/audit-to-stories/ledger-record.js +10 -48
- package/.agents/scripts/lib/audit-to-stories/parse-audit-md.js +38 -146
- package/.agents/scripts/lib/audit-to-stories/seed-from-findings.js +7 -42
- package/.agents/scripts/lib/audit-to-stories/wire-dependencies.js +10 -57
- package/.agents/scripts/lib/baseline-loader.js +12 -51
- package/.agents/scripts/lib/baseline-schema-registry.js +5 -22
- package/.agents/scripts/lib/baselines/component-matcher.js +3 -12
- package/.agents/scripts/lib/baselines/components.js +10 -61
- package/.agents/scripts/lib/baselines/coverage-updater-cli.js +7 -36
- package/.agents/scripts/lib/baselines/crap-preview-incremental.js +7 -19
- package/.agents/scripts/lib/baselines/crap-preview-scan.js +19 -45
- package/.agents/scripts/lib/baselines/crap-updater-cli.js +16 -65
- package/.agents/scripts/lib/baselines/diff-scope-cli.js +5 -33
- package/.agents/scripts/lib/baselines/duplication-scanner.js +11 -60
- package/.agents/scripts/lib/baselines/env-overrides.js +12 -67
- package/.agents/scripts/lib/baselines/envelope.js +13 -129
- package/.agents/scripts/lib/baselines/exit-codes.js +6 -49
- package/.agents/scripts/lib/baselines/git-base.js +23 -142
- package/.agents/scripts/lib/baselines/kernel.js +11 -111
- package/.agents/scripts/lib/baselines/kinds/_crap-new-method-gate.js +9 -47
- package/.agents/scripts/lib/baselines/kinds/_crap-read.js +8 -51
- package/.agents/scripts/lib/baselines/kinds/_shared-metric.js +7 -62
- package/.agents/scripts/lib/baselines/kinds/bundle-size.js +8 -29
- package/.agents/scripts/lib/baselines/kinds/coverage.js +3 -15
- package/.agents/scripts/lib/baselines/kinds/crap.js +94 -415
- package/.agents/scripts/lib/baselines/kinds/duplication.js +4 -30
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +7 -53
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +15 -80
- package/.agents/scripts/lib/baselines/kinds/mutation.js +12 -72
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +4 -11
- package/.agents/scripts/lib/baselines/merge-envelopes.js +30 -146
- package/.agents/scripts/lib/baselines/path-canon.js +19 -128
- package/.agents/scripts/lib/baselines/preview-gates.js +8 -37
- package/.agents/scripts/lib/baselines/reader.js +14 -107
- package/.agents/scripts/lib/baselines/refresh-service.js +35 -252
- package/.agents/scripts/lib/baselines/scope.js +11 -135
- package/.agents/scripts/lib/baselines/writer.js +16 -149
- package/.agents/scripts/lib/bdd-runner-detect.js +18 -113
- package/.agents/scripts/lib/bdd-scenario-budget.js +9 -36
- package/.agents/scripts/lib/bdd-scenario-scanner.js +11 -73
- package/.agents/scripts/lib/bdd-step-index.js +26 -100
- package/.agents/scripts/lib/bootstrap/baseline-merge-driver.js +23 -103
- package/.agents/scripts/lib/bootstrap/branch-protection.js +2 -5
- package/.agents/scripts/lib/bootstrap/commit-push.js +12 -50
- package/.agents/scripts/lib/bootstrap/gh-preflight.js +14 -101
- package/.agents/scripts/lib/bootstrap/hitl-confirm.js +5 -30
- package/.agents/scripts/lib/bootstrap/install-ledger.js +17 -73
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +17 -121
- package/.agents/scripts/lib/bootstrap/manifest.js +15 -86
- package/.agents/scripts/lib/bootstrap/merge-methods.js +4 -33
- package/.agents/scripts/lib/bootstrap/preflight.js +12 -61
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +34 -235
- package/.agents/scripts/lib/bootstrap/prompt.js +21 -129
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +27 -141
- package/.agents/scripts/lib/bootstrap/summary.js +1 -8
- package/.agents/scripts/lib/bootstrap/workflow-audit.js +11 -84
- package/.agents/scripts/lib/branch-name-guard.js +4 -23
- package/.agents/scripts/lib/changed-files.js +39 -121
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -24
- package/.agents/scripts/lib/checks/index.js +18 -101
- package/.agents/scripts/lib/checks/loop-health.js +11 -53
- package/.agents/scripts/lib/checks/state.js +16 -91
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +10 -61
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +14 -83
- package/.agents/scripts/lib/child-exec.js +39 -108
- package/.agents/scripts/lib/cli/standard-args.js +9 -116
- package/.agents/scripts/lib/cli-args.js +31 -146
- package/.agents/scripts/lib/cli-usage.js +5 -27
- package/.agents/scripts/lib/cli-utils.js +2 -12
- package/.agents/scripts/lib/close-validation/commands.js +13 -76
- package/.agents/scripts/lib/close-validation/gates.js +92 -331
- package/.agents/scripts/lib/close-validation/process.js +121 -151
- package/.agents/scripts/lib/close-validation/projections/advisories.js +5 -31
- package/.agents/scripts/lib/close-validation/projections/crap.js +19 -67
- package/.agents/scripts/lib/close-validation/projections/head-sha.js +1 -17
- package/.agents/scripts/lib/close-validation/projections/inputs.js +4 -34
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +11 -58
- package/.agents/scripts/lib/close-validation/runner.js +53 -110
- package/.agents/scripts/lib/command-header.js +6 -28
- package/.agents/scripts/lib/config/acceptance-eval.js +4 -30
- package/.agents/scripts/lib/config/baselines.js +3 -14
- package/.agents/scripts/lib/config/ci.js +8 -38
- package/.agents/scripts/lib/config/commands.js +1 -15
- package/.agents/scripts/lib/config/defaults.js +4 -33
- package/.agents/scripts/lib/config/delivery-routing.js +6 -34
- package/.agents/scripts/lib/config/explain.js +10 -83
- package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +3 -31
- package/.agents/scripts/lib/config/gates/crap.schema.js +1 -34
- package/.agents/scripts/lib/config/gates/duplication.schema.js +1 -8
- package/.agents/scripts/lib/config/gates/index.js +2 -17
- package/.agents/scripts/lib/config/gates/maintainability.schema.js +1 -20
- package/.agents/scripts/lib/config/gates/mutation.schema.js +1 -7
- package/.agents/scripts/lib/config/gates/shared.js +4 -64
- package/.agents/scripts/lib/config/github.js +4 -22
- package/.agents/scripts/lib/config/limits.js +4 -57
- package/.agents/scripts/lib/config/paths.js +2 -21
- package/.agents/scripts/lib/config/qa.js +4 -38
- package/.agents/scripts/lib/config/quality.js +76 -432
- package/.agents/scripts/lib/config/runners.js +12 -56
- package/.agents/scripts/lib/config/runtime.js +13 -57
- package/.agents/scripts/lib/config/shared.js +2 -16
- package/.agents/scripts/lib/config/sync-agentrc.js +9 -50
- package/.agents/scripts/lib/config/temp-paths.js +40 -280
- package/.agents/scripts/lib/config/validate-orchestration.js +5 -17
- package/.agents/scripts/lib/config/worktree-isolation.js +9 -28
- package/.agents/scripts/lib/config-resolver.js +11 -52
- package/.agents/scripts/lib/config-schema-shared.js +1 -4
- package/.agents/scripts/lib/config-settings-schema-delivery.js +16 -291
- package/.agents/scripts/lib/config-settings-schema-quality.js +7 -219
- package/.agents/scripts/lib/config-settings-schema.js +37 -274
- package/.agents/scripts/lib/coverage-baseline.js +20 -110
- package/.agents/scripts/lib/coverage-capture-fullscope.js +18 -35
- package/.agents/scripts/lib/coverage-capture-incremental.js +15 -45
- package/.agents/scripts/lib/coverage-capture-usage.js +8 -25
- package/.agents/scripts/lib/coverage-capture.js +99 -224
- package/.agents/scripts/lib/coverage-utils.js +14 -79
- package/.agents/scripts/lib/cpu-pool.js +17 -123
- package/.agents/scripts/lib/crap-baseline-join.js +16 -88
- package/.agents/scripts/lib/crap-coordinates.js +6 -25
- package/.agents/scripts/lib/crap-engine.js +40 -153
- package/.agents/scripts/lib/crap-method-identity.js +16 -78
- package/.agents/scripts/lib/crap-utils.js +31 -155
- package/.agents/scripts/lib/cyclomatic-ceiling.js +14 -87
- package/.agents/scripts/lib/cyclomatic-scope.js +10 -50
- package/.agents/scripts/lib/dead-exports-knip.js +18 -56
- package/.agents/scripts/lib/dead-exports-mode.js +5 -24
- package/.agents/scripts/lib/degraded-mode.js +3 -26
- package/.agents/scripts/lib/dependency-parser.js +8 -40
- package/.agents/scripts/lib/dependency-version.js +13 -35
- package/.agents/scripts/lib/detect-package-manager.js +6 -36
- package/.agents/scripts/lib/doc-tiers.js +22 -124
- package/.agents/scripts/lib/duplicate-search.js +8 -72
- package/.agents/scripts/lib/env-loader.js +5 -27
- package/.agents/scripts/lib/error-redactor.js +6 -31
- package/.agents/scripts/lib/errors/index.js +2 -22
- package/.agents/scripts/lib/escomplex-ast-compat.js +22 -163
- package/.agents/scripts/lib/escomplex-kernel.js +21 -123
- package/.agents/scripts/lib/feedback-loop/graduator-core.js +94 -419
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -149
- package/.agents/scripts/lib/findings/audit-ledger.js +23 -97
- package/.agents/scripts/lib/findings/classify-finding.js +14 -69
- package/.agents/scripts/lib/findings/promote-finding.js +14 -112
- package/.agents/scripts/lib/findings/provenance-field.js +11 -52
- package/.agents/scripts/lib/findings/route-finding.js +37 -255
- package/.agents/scripts/lib/findings/semantic-issue-search.js +14 -59
- package/.agents/scripts/lib/findings/severity.js +18 -104
- package/.agents/scripts/lib/format-generated-json.js +12 -44
- package/.agents/scripts/lib/full-suite-lock.js +160 -429
- package/.agents/scripts/lib/full-suite-queue.js +213 -0
- package/.agents/scripts/lib/gates/baseline-store.js +5 -9
- package/.agents/scripts/lib/gates/friction.js +1 -3
- package/.agents/scripts/lib/generated/agentrc-validator.js +2 -2
- package/.agents/scripts/lib/gh-exec.js +123 -191
- package/.agents/scripts/lib/git/cached-fetch.js +9 -52
- package/.agents/scripts/lib/git/sync-from-base.js +15 -107
- package/.agents/scripts/lib/git-branch-cleanup.js +14 -61
- package/.agents/scripts/lib/git-branch-lifecycle.js +16 -87
- package/.agents/scripts/lib/git-utils.js +37 -161
- package/.agents/scripts/lib/github/framework-repo.js +8 -71
- package/.agents/scripts/lib/github-url.js +2 -17
- package/.agents/scripts/lib/import-graph.js +11 -38
- package/.agents/scripts/lib/install-cmd-parser.js +4 -13
- package/.agents/scripts/lib/json-utils.js +3 -18
- package/.agents/scripts/lib/label-constants.js +16 -111
- package/.agents/scripts/lib/label-taxonomy.js +4 -30
- package/.agents/scripts/lib/maintainability-engine.js +12 -80
- package/.agents/scripts/lib/maintainability-unscorable.js +5 -25
- package/.agents/scripts/lib/maintainability-utils.js +16 -92
- package/.agents/scripts/lib/mandrel-catalog.js +12 -70
- package/.agents/scripts/lib/notifications/notifier.js +13 -38
- package/.agents/scripts/lib/npm-scripts.js +4 -23
- package/.agents/scripts/lib/observability/metrics-ledger.js +16 -76
- package/.agents/scripts/lib/observability/runtime-friction.js +44 -286
- package/.agents/scripts/lib/observability/signal-validator.js +5 -37
- package/.agents/scripts/lib/observability/signals-writer.js +11 -121
- package/.agents/scripts/lib/observability/source-classifier.js +22 -203
- package/.agents/scripts/lib/observability/terse-result.js +14 -47
- package/.agents/scripts/lib/onboard/init-tail.js +13 -74
- package/.agents/scripts/lib/onboard/scaffold-docs.js +11 -34
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +25 -139
- package/.agents/scripts/lib/orchestration/auto-merge-cwd.js +11 -66
- package/.agents/scripts/lib/orchestration/behind-recovery.js +10 -55
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +28 -156
- package/.agents/scripts/lib/orchestration/change-set.js +7 -36
- package/.agents/scripts/lib/orchestration/check-baselines/phases/compare.js +9 -46
- package/.agents/scripts/lib/orchestration/check-baselines/phases/evaluate.js +11 -44
- package/.agents/scripts/lib/orchestration/check-baselines/phases/floors.js +5 -24
- package/.agents/scripts/lib/orchestration/check-baselines/phases/friction.js +3 -12
- package/.agents/scripts/lib/orchestration/check-baselines/phases/parse-args.js +5 -38
- package/.agents/scripts/lib/orchestration/check-baselines/phases/pipeline.js +3 -8
- package/.agents/scripts/lib/orchestration/check-baselines/phases/refresh-ack.js +28 -136
- package/.agents/scripts/lib/orchestration/check-baselines/phases/report.js +3 -13
- package/.agents/scripts/lib/orchestration/check-state.js +92 -0
- package/.agents/scripts/lib/orchestration/ci-gap-intake.js +37 -144
- package/.agents/scripts/lib/orchestration/ci-rerun-guard.js +121 -157
- package/.agents/scripts/lib/orchestration/code-review.js +24 -128
- package/.agents/scripts/lib/orchestration/column-sync.js +23 -111
- package/.agents/scripts/lib/orchestration/complexity-gate.js +118 -462
- package/.agents/scripts/lib/orchestration/deliver-recover.js +36 -168
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +4 -35
- package/.agents/scripts/lib/orchestration/dependency-candidates.js +9 -41
- package/.agents/scripts/lib/orchestration/diff-magnitude.js +25 -115
- package/.agents/scripts/lib/orchestration/doc-reader.js +2 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +13 -51
- package/.agents/scripts/lib/orchestration/epic-candidates.js +13 -53
- package/.agents/scripts/lib/orchestration/epic-checklist.js +9 -33
- package/.agents/scripts/lib/orchestration/epic-container.js +41 -167
- package/.agents/scripts/lib/orchestration/epic-expansion.js +10 -40
- package/.agents/scripts/lib/orchestration/epic-rollup.js +55 -207
- package/.agents/scripts/lib/orchestration/file-assumption-enum.js +2 -22
- package/.agents/scripts/lib/orchestration/file-assumptions.js +45 -253
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-detect.js +9 -46
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches-reap.js +7 -57
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/branches.js +21 -126
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +2 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +1 -4
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/filters.js +1 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes-ff.js +9 -34
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/git-probes.js +41 -190
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/merged-tip.js +9 -45
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/parse-args.js +1 -4
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/phase-drivers.js +19 -85
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prompts.js +3 -17
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/prune.js +1 -5
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/render.js +16 -100
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/stashes.js +1 -4
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +16 -67
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +9 -33
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +8 -29
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +15 -68
- package/.agents/scripts/lib/orchestration/light-backstop.js +11 -49
- package/.agents/scripts/lib/orchestration/light-escalation.js +21 -89
- package/.agents/scripts/lib/orchestration/light-suitability.js +37 -307
- package/.agents/scripts/lib/orchestration/merge-block-class.js +39 -216
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -377
- package/.agents/scripts/lib/orchestration/pinned-identifier-lint.js +17 -57
- package/.agents/scripts/lib/orchestration/plan-context.js +55 -318
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +22 -79
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +5 -31
- package/.agents/scripts/lib/orchestration/plan-metrics.js +29 -108
- package/.agents/scripts/lib/orchestration/plan-navigation.js +8 -27
- package/.agents/scripts/lib/orchestration/plan-persist/acceptance-handle-repair.js +12 -49
- package/.agents/scripts/lib/orchestration/plan-persist/audit-provenance.js +18 -73
- package/.agents/scripts/lib/orchestration/plan-persist/changes-repair.js +16 -78
- package/.agents/scripts/lib/orchestration/plan-persist/cross-plan-links.js +6 -30
- package/.agents/scripts/lib/orchestration/plan-persist/epic-adoption.js +14 -65
- package/.agents/scripts/lib/orchestration/plan-persist/epic-ops.js +21 -76
- package/.agents/scripts/lib/orchestration/plan-persist/external-deps.js +6 -43
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +14 -68
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +9 -41
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +71 -289
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +96 -362
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +9 -39
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +60 -186
- package/.agents/scripts/lib/orchestration/plan-persist/wave-collision-gate.js +9 -49
- package/.agents/scripts/lib/orchestration/plan-persist/wave-serialisation.js +7 -28
- package/.agents/scripts/lib/orchestration/plan-reachability.js +13 -42
- package/.agents/scripts/lib/orchestration/plan-run-labels/reap.js +16 -91
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +16 -54
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +10 -52
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +14 -109
- package/.agents/scripts/lib/orchestration/planning/memory-pool-advisory.js +11 -83
- package/.agents/scripts/lib/orchestration/pr-watch.js +48 -306
- package/.agents/scripts/lib/orchestration/project-meta-cache.js +15 -80
- package/.agents/scripts/lib/orchestration/project-meta-resolver.js +10 -51
- package/.agents/scripts/lib/orchestration/reassert-status-column.js +12 -76
- package/.agents/scripts/lib/orchestration/remote-verifier.js +12 -36
- package/.agents/scripts/lib/orchestration/resolve-stories.js +33 -164
- package/.agents/scripts/lib/orchestration/retro-proposals.js +66 -361
- package/.agents/scripts/lib/orchestration/review-base-ref.js +9 -43
- package/.agents/scripts/lib/orchestration/review-depth.js +12 -91
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +10 -111
- package/.agents/scripts/lib/orchestration/review-providers/degraded-gates.js +13 -71
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +7 -51
- package/.agents/scripts/lib/orchestration/review-providers/mi-exemptions.js +8 -53
- package/.agents/scripts/lib/orchestration/review-providers/native.js +30 -214
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +7 -52
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +4 -33
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +11 -71
- package/.agents/scripts/lib/orchestration/review-providers/scoped-lint.js +19 -131
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +6 -89
- package/.agents/scripts/lib/orchestration/review-providers/types.js +26 -56
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +3 -46
- package/.agents/scripts/lib/orchestration/run-epilogue.js +245 -326
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +55 -159
- package/.agents/scripts/lib/orchestration/single-story-close/close-note.js +7 -34
- package/.agents/scripts/lib/orchestration/single-story-close/failed-terminal.js +16 -89
- package/.agents/scripts/lib/orchestration/single-story-close/gate-log.js +18 -109
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +83 -239
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +20 -82
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +103 -97
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +8 -64
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +136 -486
- package/.agents/scripts/lib/orchestration/single-story-close/phases/conventional-subject.js +30 -134
- package/.agents/scripts/lib/orchestration/single-story-close/phases/graphql-preflight.js +96 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/lock-wait-pending.js +49 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/normalize-pr-title.js +13 -86
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +20 -95
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +46 -203
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pre-gate-steps.js +8 -50
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +12 -83
- package/.agents/scripts/lib/orchestration/single-story-close/phases/push.js +6 -39
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +1 -6
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-outcome.js +4 -28
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-override.js +6 -51
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +6 -34
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +23 -121
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +180 -273
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +15 -69
- package/.agents/scripts/lib/orchestration/story-body-gate.js +4 -23
- package/.agents/scripts/lib/orchestration/story-close/baseline-upward-writeback.js +50 -179
- package/.agents/scripts/lib/orchestration/story-close/context-budget-writeback.js +11 -41
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +23 -311
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +22 -147
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +16 -67
- package/.agents/scripts/lib/orchestration/story-deliver-terminal-schema.js +12 -68
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +42 -200
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +167 -252
- package/.agents/scripts/lib/orchestration/story-init-envelope.js +52 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +2 -6
- package/.agents/scripts/lib/orchestration/story-reachability.js +3 -23
- package/.agents/scripts/lib/orchestration/task-body-validator.js +12 -135
- package/.agents/scripts/lib/orchestration/ticket-lease.js +28 -131
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +15 -149
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +5 -33
- package/.agents/scripts/lib/orchestration/ticket-validator.js +63 -344
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -261
- package/.agents/scripts/lib/orchestration/ticketing/state.js +14 -68
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +46 -263
- package/.agents/scripts/lib/orchestration/ticketing.js +2 -26
- package/.agents/scripts/lib/orchestration/verify-credit.js +18 -79
- package/.agents/scripts/lib/orchestration/worktree-dirty.js +5 -30
- package/.agents/scripts/lib/path-security.js +3 -6
- package/.agents/scripts/lib/plan-phase-cleanup.js +9 -49
- package/.agents/scripts/lib/preflight-runner.js +13 -69
- package/.agents/scripts/lib/process-group.js +143 -0
- package/.agents/scripts/lib/project-root.js +2 -8
- package/.agents/scripts/lib/provider-factory.js +2 -25
- package/.agents/scripts/lib/qa/console-allowlist.js +10 -59
- package/.agents/scripts/lib/qa/qa-session.js +15 -69
- package/.agents/scripts/lib/qa/redact-evidence.js +18 -129
- package/.agents/scripts/lib/qa/resolve-qa-contract.js +26 -135
- package/.agents/scripts/lib/qa/resolve-selection.js +16 -84
- package/.agents/scripts/lib/reserved-test-ids.js +9 -47
- package/.agents/scripts/lib/runtime-deps/dep-resolution.js +13 -53
- package/.agents/scripts/lib/runtime-deps/ensure-installed.js +10 -44
- package/.agents/scripts/lib/runtime-deps/manifest.js +10 -30
- package/.agents/scripts/lib/runtime-deps/parser-major.js +15 -51
- package/.agents/scripts/lib/runtime-deps/preflight.js +4 -22
- package/.agents/scripts/lib/runtime-deps/scan-imports.js +11 -55
- package/.agents/scripts/lib/signals/detectors/common.js +12 -38
- package/.agents/scripts/lib/signals/index.js +2 -19
- package/.agents/scripts/lib/signals/schema.js +14 -109
- package/.agents/scripts/lib/signals/write.js +2 -11
- package/.agents/scripts/lib/single-story/confirm-merge.js +31 -72
- package/.agents/scripts/lib/single-story/story-merged-notify.js +8 -49
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +4 -26
- package/.agents/scripts/lib/single-story-sweep/protection.js +13 -100
- package/.agents/scripts/lib/single-story-sweep/sweep-lock.js +60 -258
- package/.agents/scripts/lib/single-story-sweep.js +14 -97
- package/.agents/scripts/lib/skills/parse-skill.js +13 -59
- package/.agents/scripts/lib/skills/skills-index.js +8 -29
- package/.agents/scripts/lib/skills/walk-skill-files.js +17 -78
- package/.agents/scripts/lib/source-extensions.js +11 -42
- package/.agents/scripts/lib/source-text/strip-js-comments.js +8 -41
- package/.agents/scripts/lib/stdio-flush.js +9 -36
- package/.agents/scripts/lib/story-adjacency.js +8 -40
- package/.agents/scripts/lib/story-body/body-format-lints.js +51 -66
- package/.agents/scripts/lib/story-body/footer-block.js +11 -44
- package/.agents/scripts/lib/story-body/story-body.js +123 -344
- package/.agents/scripts/lib/temp-retention.js +42 -143
- package/.agents/scripts/lib/templates/decomposer-prompts.js +18 -80
- package/.agents/scripts/lib/test-env.js +12 -65
- package/.agents/scripts/lib/test-run-credit.js +10 -65
- package/.agents/scripts/lib/test-runner-contract.js +19 -74
- package/.agents/scripts/lib/test-temp.js +38 -184
- package/.agents/scripts/lib/test-tiers.js +12 -96
- package/.agents/scripts/lib/ticket-body-sections.js +21 -96
- package/.agents/scripts/lib/transpile.js +15 -74
- package/.agents/scripts/lib/util/concurrent-map.js +6 -25
- package/.agents/scripts/lib/util/parse-id-list.js +8 -34
- package/.agents/scripts/lib/util/poll-loop.js +8 -28
- package/.agents/scripts/lib/util/with-timeout.js +2 -11
- package/.agents/scripts/lib/validation-evidence.js +21 -96
- package/.agents/scripts/lib/wave-runner/footprint.js +17 -70
- package/.agents/scripts/lib/wave-runner/live-probe.js +67 -209
- package/.agents/scripts/lib/wave-runner/ready-set.js +72 -301
- package/.agents/scripts/lib/workers/crap-worker.js +11 -64
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +8 -45
- package/.agents/scripts/lib/workers/maintainability-worker.js +5 -21
- package/.agents/scripts/lib/workers/serve-worker-messages.js +2 -18
- package/.agents/scripts/lib/workflow-closure.js +22 -105
- package/.agents/scripts/lib/workspace-provisioner.js +16 -54
- package/.agents/scripts/lib/worktree/git-hooks.js +15 -60
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +20 -60
- package/.agents/scripts/lib/worktree/lifecycle/merge-reachability.js +10 -62
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +22 -86
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +24 -126
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +2 -20
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +46 -180
- package/.agents/scripts/lib/worktree-manager.js +15 -40
- package/.agents/scripts/lint-issue-body.js +12 -68
- package/.agents/scripts/mandrel-update-preflight.js +10 -70
- package/.agents/scripts/merge-baseline.js +30 -105
- package/.agents/scripts/nav-registry-diff.js +22 -108
- package/.agents/scripts/notify.js +12 -69
- package/.agents/scripts/plan-context.js +97 -92
- package/.agents/scripts/plan-critics.js +12 -66
- package/.agents/scripts/plan-persist.js +71 -137
- package/.agents/scripts/plan-run-epilogue.js +20 -43
- package/.agents/scripts/pr-watch-with-update.js +79 -263
- package/.agents/scripts/providers/github/auth.js +3 -8
- package/.agents/scripts/providers/github/blocked-by-add.js +14 -61
- package/.agents/scripts/providers/github/board-add.js +5 -21
- package/.agents/scripts/providers/github/branch-protection.js +11 -43
- package/.agents/scripts/providers/github/cache.js +2 -14
- package/.agents/scripts/providers/github/comments.js +7 -40
- package/.agents/scripts/providers/github/compose.js +3 -19
- package/.agents/scripts/providers/github/errors.js +22 -125
- package/.agents/scripts/providers/github/issues.js +31 -123
- package/.agents/scripts/providers/github/labels.js +24 -124
- package/.agents/scripts/providers/github/mappers.js +5 -27
- package/.agents/scripts/providers/github/merge-methods.js +3 -22
- package/.agents/scripts/providers/github/project-board.js +2 -17
- package/.agents/scripts/providers/github/projects-v2-graphql.js +3 -6
- package/.agents/scripts/providers/github/request-helpers.js +5 -32
- package/.agents/scripts/providers/github/search-budget.js +9 -39
- package/.agents/scripts/providers/github/search-query.js +5 -26
- package/.agents/scripts/providers/github/sub-issue-add.js +15 -65
- package/.agents/scripts/providers/github/sub-issues.js +7 -36
- package/.agents/scripts/providers/github/tickets.js +32 -134
- package/.agents/scripts/providers/github.js +18 -59
- package/.agents/scripts/prune-plan-run-labels.js +12 -46
- package/.agents/scripts/quality-preview.js +35 -220
- package/.agents/scripts/resolve-doc-tiers.js +3 -26
- package/.agents/scripts/resolve-stories.js +16 -67
- package/.agents/scripts/resync-status-column.js +6 -25
- package/.agents/scripts/run-tests.js +29 -104
- package/.agents/scripts/single-story-close.js +23 -135
- package/.agents/scripts/single-story-confirm-merge.js +32 -139
- package/.agents/scripts/single-story-init.js +47 -259
- package/.agents/scripts/stories-wave-tick.js +179 -415
- package/.agents/scripts/sync-agentrc.js +3 -21
- package/.agents/scripts/sync-claude-agents.js +9 -55
- package/.agents/scripts/sync-claude-commands.js +20 -110
- package/.agents/scripts/test-wrapper.js +6 -43
- package/.agents/scripts/update-coverage-baseline.js +5 -22
- package/.agents/scripts/update-crap-baseline.js +7 -30
- package/.agents/scripts/update-duplication-baseline.js +16 -92
- package/.agents/scripts/update-maintainability-baseline.js +8 -62
- package/.agents/scripts/update-ticket-state.js +1 -7
- package/.agents/scripts/validate-skills.js +6 -26
- package/.agents/skills/core/gates-and-baselines/reference.md +0 -1
- package/.agents/skills/skills.index.json +2 -2
- package/.agents/skills/stack/qa/playwright/SKILL.md +26 -0
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/templates/docs/audit-sweep-runbook.md +3 -4
- package/.agents/workflows/audit-accessibility.md +4 -7
- package/.agents/workflows/audit-adrs.md +3 -6
- package/.agents/workflows/audit-architecture.md +3 -11
- package/.agents/workflows/audit-baselines.md +6 -9
- package/.agents/workflows/audit-clean-code.md +8 -12
- package/.agents/workflows/audit-data-model.md +3 -7
- package/.agents/workflows/audit-dependencies.md +4 -7
- package/.agents/workflows/audit-devops.md +4 -8
- package/.agents/workflows/audit-documentation.md +1 -4
- package/.agents/workflows/audit-mobile.md +4 -7
- package/.agents/workflows/audit-navigability.md +3 -6
- package/.agents/workflows/audit-performance.md +2 -4
- package/.agents/workflows/audit-privacy.md +4 -7
- package/.agents/workflows/audit-quality.md +4 -7
- package/.agents/workflows/audit-security.md +4 -7
- package/.agents/workflows/audit-seo.md +4 -7
- package/.agents/workflows/audit-sre.md +4 -7
- package/.agents/workflows/audit-to-stories.md +17 -30
- package/.agents/workflows/audit-ux-ui.md +4 -7
- package/.agents/workflows/helpers/acceptance-self-eval.md +84 -157
- package/.agents/workflows/helpers/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
- package/.agents/workflows/helpers/code-review.md +4 -2
- package/.agents/workflows/helpers/deliver-digest.md +37 -28
- package/.agents/workflows/helpers/deliver-light.md +92 -101
- package/.agents/workflows/helpers/deliver-reference.md +162 -220
- package/.agents/workflows/helpers/deliver-story-reference.md +197 -607
- package/.agents/workflows/helpers/deliver-story.md +17 -18
- package/.agents/workflows/helpers/plan-reference.md +125 -167
- package/.agents/workflows/helpers/qa-core.md +12 -0
- package/.agents/workflows/mandrel-deliver.md +47 -31
- package/.agents/workflows/mandrel-plan.md +22 -21
- package/.agents/workflows/mandrel-update.md +36 -21
- package/.agents/workflows/memory-consolidate.md +1 -1
- package/.agents/workflows/qa-assist.md +27 -71
- package/.agents/workflows/qa-explore.md +19 -59
- package/.agents/workflows/qa-run.md +11 -33
- package/README.md +1 -1
- package/bin/mandrel.js +5 -46
- package/bin/postinstall.js +18 -107
- package/docs/CHANGELOG.md +60 -0
- package/lib/cli/doctor.js +11 -71
- package/lib/cli/init.js +26 -165
- package/lib/cli/migrate.js +5 -64
- package/lib/cli/registry.js +84 -467
- package/lib/cli/sync-agents.js +4 -64
- package/lib/cli/sync-commands.js +11 -64
- package/lib/cli/sync.js +34 -213
- package/lib/cli/uninstall.js +40 -219
- package/lib/cli/update.js +326 -523
- package/lib/cli/version-check.js +8 -72
- package/lib/cli/version-helpers.js +14 -87
- package/lib/migrations/helpers/retire-agentrc-key.js +11 -52
- package/lib/migrations/index.js +17 -92
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +2 -15
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +1 -13
- package/lib/migrations/steps/2.11.0-retire-max-seed-words.js +2 -15
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +9 -38
- package/lib/migrations/steps/2.20.0-retire-codebase-snapshot.js +1 -13
- package/lib/migrations/steps/2.32.0-retire-lint-baseline-command.js +2 -27
- package/lib/migrations/steps/2.57.0-retire-delivery-limit-knobs.js +2 -23
- package/lib/migrations/steps/2.57.0-retire-planning-limit-knobs.js +2 -27
- package/lib/migrations/steps/2.60.0-retire-audit-results-autofile.js +21 -0
- package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
- package/package.json +17 -14
- package/.agents/docs/SDLC.md +0 -590
- package/.agents/docs/quality-gates.md +0 -1179
- package/.agents/rules/known-tooling-behavior.md +0 -150
- package/.agents/rules/orchestration-error-handling.md +0 -61
- package/.agents/rules/test-seams.md +0 -59
- package/.agents/schemas/baselines/lighthouse.schema.json +0 -59
- package/.agents/schemas/baselines/lint.schema.json +0 -47
- package/.agents/schemas/model-attribution.schema.json +0 -53
- package/.agents/scripts/check-action-pinning.js +0 -260
- package/.agents/scripts/check-audit-attribution.js +0 -302
- package/.agents/scripts/check-baseline-drift.js +0 -211
- package/.agents/scripts/check-baseline-scope.js +0 -362
- package/.agents/scripts/check-generated-validator.js +0 -202
- package/.agents/scripts/check-knip-entries.js +0 -159
- package/.agents/scripts/check-lifecycle-lint.js +0 -294
- package/.agents/scripts/check-pinned-override-notes.js +0 -102
- package/.agents/scripts/check-schema-references.js +0 -366
- package/.agents/scripts/check-test-portability.js +0 -512
- package/.agents/scripts/check-workflow-citations.js +0 -332
- package/.agents/scripts/check-workflow-cli-lint.js +0 -299
- package/.agents/scripts/check-workflow-timeouts.js +0 -291
- package/.agents/scripts/install-matrix-assert.js +0 -326
- package/.agents/scripts/lib/audit-advisories.js +0 -195
- package/.agents/scripts/lib/audit-attribution.js +0 -134
- package/.agents/scripts/lib/baselines/drift-detector.js +0 -351
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +0 -87
- package/.agents/scripts/lib/baselines/kinds/lint.js +0 -184
- package/.agents/scripts/lib/baselines/orphan-pruner.js +0 -233
- package/.agents/scripts/lib/baselines/scope-assert.js +0 -223
- package/.agents/scripts/lib/baselines/scope-inventory.js +0 -314
- package/.agents/scripts/lib/c8-cli-path.js +0 -21
- package/.agents/scripts/lib/config/gates/lighthouse.schema.js +0 -51
- package/.agents/scripts/lib/config/gates/lint.schema.js +0 -18
- package/.agents/scripts/lib/dynamic-workflow/architecture-report-contract.js +0 -70
- package/.agents/scripts/lib/dynamic-workflow/audit-orchestrator.js +0 -284
- package/.agents/scripts/lib/dynamic-workflow/clean-code-report-contract.js +0 -80
- package/.agents/scripts/lib/dynamic-workflow/degraded-coverage.js +0 -81
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +0 -87
- package/.agents/scripts/lib/dynamic-workflow/performance-report-contract.js +0 -74
- package/.agents/scripts/lib/dynamic-workflow/quality-report-contract.js +0 -90
- package/.agents/scripts/lib/dynamic-workflow/report-contract-core.js +0 -43
- package/.agents/scripts/lib/dynamic-workflow/security-report-contract.js +0 -83
- package/.agents/scripts/lib/fs-walk.js +0 -52
- package/.agents/scripts/lib/knip-config-resolver.js +0 -181
- package/.agents/scripts/lib/knip-entry-sync.js +0 -452
- package/.agents/scripts/lib/orchestration/model-attribution.js +0 -418
- package/.agents/scripts/lib/orchestration/story-plan-state.js +0 -33
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +0 -67
- package/.agents/scripts/lib/pinned-override-notes.js +0 -88
- package/.agents/scripts/lib/pinned-override-resolve.js +0 -212
- package/.agents/scripts/lib/test-isolate/cli-options.js +0 -93
- package/.agents/scripts/lib/test-isolate/env-snapshot-loader.js +0 -52
- package/.agents/scripts/lib/test-isolate/list-files.js +0 -90
- package/.agents/scripts/lib/test-isolate/parse-tap.js +0 -75
- package/.agents/scripts/lib/test-isolate/progress-log.js +0 -45
- package/.agents/scripts/lib/test-isolate/render-report.js +0 -97
- package/.agents/scripts/lib/test-isolate/run-isolate.js +0 -87
- package/.agents/scripts/lib/test-isolate/runner.js +0 -483
- package/.agents/scripts/lib/test-profile/parse-tap.js +0 -136
- package/.agents/scripts/lib/test-profile/render-report.js +0 -45
- package/.agents/scripts/lint-label-vocabulary.js +0 -214
- package/.agents/scripts/post-structured-comment.js +0 -127
- package/.agents/scripts/provision-git-hooks.js +0 -85
- package/.agents/scripts/prune-baseline-orphans.js +0 -181
- package/.agents/scripts/run-coverage.js +0 -197
- package/.agents/scripts/run-lint.js +0 -133
- package/.agents/scripts/run-test-profile.js +0 -129
- package/.agents/scripts/run-verify.js +0 -118
- package/.agents/scripts/test-isolate.js +0 -55
- package/.agents/scripts/update-dead-exports-baseline.js +0 -321
|
@@ -1,93 +1,21 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* phases/confirm-merge.js — the close-and-land merge wait
|
|
3
|
-
*
|
|
2
|
+
* phases/confirm-merge.js — the close-and-land merge wait, the default
|
|
3
|
+
* terminal step of every close (`--no-wait-merge` opts out).
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
5
|
+
* Two timing domains: `maxWaitSeconds` bounds THIS invocation (it must fit
|
|
6
|
+
* the host's ~10-min tool ceiling after the close gates) and expires to a
|
|
7
|
+
* resumable `pending` with no mutation; `maxBudgetSeconds` bounds the
|
|
8
|
+
* cumulative wait, anchored at the PR's `createdAt` so resumes don't restart
|
|
9
|
+
* it, and exhausting it is the real give-up (classify, emit, block). The
|
|
10
|
+
* poll is stateless and re-entrant. Async mode only shortens the
|
|
11
|
+
* per-invocation window. Checks are probed every iteration so a red required
|
|
12
|
+
* check fails fast instead of burning the budget.
|
|
11
13
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* whose CI took longer than roughly eight minutes was **killed mid-poll**
|
|
18
|
-
* with no terminal path taken — no `merge.unlanded` event, no `agent::blocked`
|
|
19
|
-
* flip, the Story parked at `agent::closing`: precisely the strand the
|
|
20
|
-
* must-land contract exists to eliminate.
|
|
21
|
-
*
|
|
22
|
-
* The fix splits the two timing domains that were conflated:
|
|
23
|
-
*
|
|
24
|
-
* - **`maxWaitSeconds`** bounds THIS invocation (default 300s, comfortably
|
|
25
|
-
* inside the host ceiling). On expiry the wait returns
|
|
26
|
-
* `terminal: 'pending'` — **no label mutation, no `merge.unlanded`
|
|
27
|
-
* event** — and the caller surfaces a resumable terminal with its own
|
|
28
|
-
* exit code. Merely shrinking `maxBudgetSeconds` instead would have been
|
|
29
|
-
* wrong: that path conflates slow CI with a hard block, so most runs
|
|
30
|
-
* would have been misfiled as blocked.
|
|
31
|
-
* - **`maxBudgetSeconds`** bounds the CUMULATIVE wait, anchored at the
|
|
32
|
-
* PR's `createdAt` rather than this invocation's start, so resumes do not
|
|
33
|
-
* restart the clock. Exhausting it is the genuine give-up: classify,
|
|
34
|
-
* emit, block. `agent::blocked` stays reserved for hard blocks.
|
|
35
|
-
*
|
|
36
|
-
* Backgrounding is not a workaround here and does not need to be: an
|
|
37
|
-
* interrupted poll is stateless and re-entrant by construction.
|
|
38
|
-
*
|
|
39
|
-
* ## Async mode (Story #4698 — a designed short probe window, not an accident)
|
|
40
|
-
*
|
|
41
|
-
* `maxWaitSeconds` (default 300s) still routinely EXPIRES on a slow-CI
|
|
42
|
-
* consumer: the median PR-create→merge time can be minutes, so nearly every
|
|
43
|
-
* close burns its whole foreground slot polling and then returns `pending`
|
|
44
|
-
* anyway. `delivery.mergeWatch.mode: "async"` makes that async confirm a
|
|
45
|
-
* designed mode rather than an expiry accident. It caps the per-invocation
|
|
46
|
-
* wait to a short probe window (`ASYNC_PROBE_WINDOW_SECONDS`, ~60s) — long
|
|
47
|
-
* enough for the loop's existing checks to catch an instant merge and, via the
|
|
48
|
-
* imported {@link decideMergeWaitFailFast} decision (Story #4695/#4710), an
|
|
49
|
-
* instantly-red required check — then returns the SAME resumable `pending`
|
|
50
|
-
* terminal, whose `nextCommand` the worker launches in the background. Nothing
|
|
51
|
-
* else changes: the cumulative `maxBudgetSeconds` anchor is untouched, and
|
|
52
|
-
* `sync` mode (the default) is byte-compatible. An explicit `--max-wait-seconds`
|
|
53
|
-
* override wins over the async cap so a headless caller can still land in one
|
|
54
|
-
* block. The clamp lives entirely in `resolveMergeWaitConfig`; the poll loop is
|
|
55
|
-
* mode-agnostic.
|
|
56
|
-
*
|
|
57
|
-
* ## The wait is not weaker than the watch it displaced
|
|
58
|
-
*
|
|
59
|
-
* The pre-#4543 poll read only `state` / `mergedAt`. A check that went red
|
|
60
|
-
* at minute one therefore burned the full hour and then classified as
|
|
61
|
-
* `branch-protection-human-required` (the exhaustion probe sees
|
|
62
|
-
* `mergeStateStatus: BLOCKED` with checks settled) — sending the operator to
|
|
63
|
-
* diagnose branch protection instead of their red check. This wait probes the
|
|
64
|
-
* checks every iteration: it fails fast on `checks-failed`, and runs a
|
|
65
|
-
* bounded `gh pr update-branch` on a BEHIND PR instead of waiting out the
|
|
66
|
-
* budget behind a base it could have caught up to.
|
|
67
|
-
*
|
|
68
|
-
* The per-iteration `provider.getTicket` is also gone. It was re-fetched
|
|
69
|
-
* every poll for an idempotence check whose answer cannot change mid-poll —
|
|
70
|
-
* ~240 reads per Story per hour. The loop now probes the PR only, and calls
|
|
71
|
-
* the shared `confirmStoryMerged` exactly once, after a merge is observed.
|
|
72
|
-
*
|
|
73
|
-
* Terminal outcomes:
|
|
74
|
-
* - `{ confirmed: true, action, tail }` — the PR merged; `confirmStoryMerged`
|
|
75
|
-
* flipped `agent::closing → agent::done` and closed the issue, and the
|
|
76
|
-
* shared post-land tail ran.
|
|
77
|
-
* - `{ confirmed: false, terminal: 'pending', waitBudget }` — this
|
|
78
|
-
* invocation's bound expired with the PR still in flight. Resumable;
|
|
79
|
-
* nothing was mutated.
|
|
80
|
-
* - `{ confirmed: false, terminal: 'blocked', blockClass, reason }` — the
|
|
81
|
-
* arm failed outright, the PR closed without merging, a required check
|
|
82
|
-
* went red, or the cumulative budget was exhausted. Classified via the
|
|
83
|
-
* shared `classifyMergeBlock`, emitted as `merge.unlanded`, friction
|
|
84
|
-
* posted, Story transitioned to `agent::blocked`.
|
|
85
|
-
* - `{ confirmed: false, terminal: 'blocked', blockClass: 'merged-flip-failed' }`
|
|
86
|
-
* — the PR merged but the `agent::done` label write failed. Its own
|
|
87
|
-
* `merge.flip-failed` event and friction wording (Story #4539): the merge
|
|
88
|
-
* landed, so attributing it to an unlanded merge would send the operator
|
|
89
|
-
* to diagnose branch protection instead of re-running the idempotent
|
|
90
|
-
* confirm.
|
|
14
|
+
* Terminals: `landed` (confirmed, post-land tail ran); `pending` (nothing
|
|
15
|
+
* mutated); `blocked` (classified, `merge.unlanded` emitted, friction posted,
|
|
16
|
+
* Story → `agent::blocked`); `blocked`/`merged-flip-failed` (merged but the
|
|
17
|
+
* `agent::done` write failed — its own event and wording, since the merge
|
|
18
|
+
* is not in question).
|
|
91
19
|
*/
|
|
92
20
|
|
|
93
21
|
import { getCiDelivery } from '../../../config/ci.js';
|
|
@@ -98,6 +26,7 @@ import {
|
|
|
98
26
|
} from '../../../single-story/confirm-merge.js';
|
|
99
27
|
import { pollUntil } from '../../../util/poll-loop.js';
|
|
100
28
|
import { applyBehindUpdate } from '../../behind-recovery.js';
|
|
29
|
+
import { isRerunPermitted } from '../../check-state.js';
|
|
101
30
|
import {
|
|
102
31
|
emitMergeFlipFailed as defaultEmitMergeFlipFailed,
|
|
103
32
|
MERGED_FLIP_FAILED_BLOCK_CLASS,
|
|
@@ -114,6 +43,7 @@ import {
|
|
|
114
43
|
deriveChecksStatus,
|
|
115
44
|
deriveRedHeadRuns,
|
|
116
45
|
deriveRequiredRunEvidence,
|
|
46
|
+
isPrMerged,
|
|
117
47
|
MERGE_WAIT_GH_TIMEOUT_MS,
|
|
118
48
|
parseWorkflowRunId,
|
|
119
49
|
readRunSummary,
|
|
@@ -128,77 +58,38 @@ import {
|
|
|
128
58
|
import { disarmAutoMerge } from './auto-merge.js';
|
|
129
59
|
import { runPostLandTail as defaultRunPostLandTail } from './post-land.js';
|
|
130
60
|
|
|
131
|
-
/**
|
|
132
|
-
* Per-invocation merge-wait bound. 300s fits inside a single host tool
|
|
133
|
-
* invocation (~10 min ceiling) with room for the close gates that precede
|
|
134
|
-
* the wait. A headless caller with no such ceiling raises
|
|
135
|
-
* `delivery.mergeWatch.maxWaitSeconds` to keep single-block semantics.
|
|
136
|
-
*/
|
|
61
|
+
/** Per-invocation bound; fits the host's ~10-min tool ceiling after the gates. */
|
|
137
62
|
export const DEFAULT_MAX_WAIT_SECONDS = 300;
|
|
138
63
|
|
|
139
|
-
/**
|
|
140
|
-
* Async-mode per-invocation probe window (Story #4698). When
|
|
141
|
-
* `delivery.mergeWatch.mode` is `"async"`, `resolveMergeWaitConfig` caps the
|
|
142
|
-
* per-invocation wait to this many seconds so close returns the resumable
|
|
143
|
-
* `pending` terminal fast instead of burning the foreground host slot. Sized
|
|
144
|
-
* to catch an instant merge and — via the head-anchored required-check
|
|
145
|
-
* predicate — an instantly-red required check, while staying far inside the
|
|
146
|
-
* cumulative `maxBudgetSeconds` give-up bound.
|
|
147
|
-
*/
|
|
64
|
+
/** Async-mode per-invocation cap: long enough to catch an instant merge or red check. */
|
|
148
65
|
export const ASYNC_PROBE_WINDOW_SECONDS = 60;
|
|
149
66
|
|
|
150
|
-
/** Bounded `gh pr update-branch` attempts for a BEHIND PR. */
|
|
151
67
|
export const DEFAULT_UPDATE_ATTEMPTS = 3;
|
|
152
68
|
|
|
153
69
|
/**
|
|
154
|
-
* Minimum polls before the
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
* restart it — but that alone means a PR older than `maxBudgetSeconds` (1h by
|
|
158
|
-
* default) is already over budget on its very first probe. Resuming a Story
|
|
159
|
-
* the next morning, or landing a long-open PR, would then flip
|
|
160
|
-
* `agent::blocked` and emit `merge.unlanded` against a perfectly healthy PR
|
|
161
|
-
* that was seconds from merging, without ever having waited.
|
|
162
|
-
*
|
|
163
|
-
* The floor gives every invocation at least one real poll cycle before the
|
|
164
|
-
* cumulative bound can fire. A genuinely stuck PR still blocks within one
|
|
165
|
-
* interval (~30s), so the give-up bound keeps its meaning; a PR about to go
|
|
166
|
-
* green gets the chance it earned.
|
|
70
|
+
* Minimum polls before the cumulative budget may block: the clock is anchored
|
|
71
|
+
* at `createdAt`, so a long-open PR is over budget on its first probe and
|
|
72
|
+
* would otherwise block without ever having waited.
|
|
167
73
|
*/
|
|
168
74
|
export const MIN_POLLS_BEFORE_BUDGET_BLOCK = 2;
|
|
169
75
|
|
|
76
|
+
/** Verdict for a PR closed without merging, decided by the wait itself. */
|
|
77
|
+
const CLOSED_UNMERGED_BLOCK_CLASS = 'api-race-other';
|
|
78
|
+
const CLOSED_UNMERGED_REASON =
|
|
79
|
+
'PR probe error: PR closed without merging (state=CLOSED)';
|
|
80
|
+
|
|
170
81
|
function defaultSleep(ms) {
|
|
171
82
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
172
83
|
}
|
|
173
84
|
|
|
174
|
-
/**
|
|
175
|
-
* The wait's default `gh` facade, bound to a spawn-level timeout (Story
|
|
176
|
-
* #4710): every subprocess the wait launches through it carries
|
|
177
|
-
* `MERGE_WAIT_GH_TIMEOUT_MS`, so a wedged `gh` child is killed rather than
|
|
178
|
-
* stranding an unattended async-mode wait forever. Callers that inject their
|
|
179
|
-
* own `gh` (tests, the resume CLI) are bounded by {@link withGhTimeout} at
|
|
180
|
-
* the call sites instead.
|
|
181
|
-
*/
|
|
85
|
+
/** Spawn-level timeout kills a wedged `gh` child; injected `gh`s are bounded by withGhTimeout. */
|
|
182
86
|
const defaultGh = createGh(undefined, { timeoutMs: MERGE_WAIT_GH_TIMEOUT_MS });
|
|
183
87
|
|
|
184
88
|
/**
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* and the merge wait must never hang on any of them. Rejection maps to the
|
|
190
|
-
* caller's existing error handling: the probe degrades to its conservative
|
|
191
|
-
* pending shape, the update-branch attempt logs and continues.
|
|
192
|
-
*
|
|
193
|
-
* A late settlement of the losing promise is explicitly absorbed so a
|
|
194
|
-
* post-timeout rejection cannot surface as an unhandled rejection.
|
|
195
|
-
*
|
|
196
|
-
* The timeout timer is deliberately NOT `unref`'d: when the awaited call is a
|
|
197
|
-
* promise that never settles (a hung stub, or a real gh child whose I/O has
|
|
198
|
-
* gone quiet), the timer is the ONLY handle keeping the event loop alive, so
|
|
199
|
-
* unref'ing it would let the process/test exit before the timeout ever fires —
|
|
200
|
-
* exactly the hang this guard exists to prevent. It is short-lived and always
|
|
201
|
-
* cleared in `finally`, so keeping it referenced costs nothing.
|
|
89
|
+
* Wall-clock bound for any `gh` call, including injected ones that may never
|
|
90
|
+
* settle. The losing promise's late rejection is absorbed. The timer is NOT
|
|
91
|
+
* unref'd: for a never-settling call it is the only handle keeping the event
|
|
92
|
+
* loop alive until the timeout fires.
|
|
202
93
|
*/
|
|
203
94
|
function withGhTimeout(promise, timeoutMs, label) {
|
|
204
95
|
let timer;
|
|
@@ -215,19 +106,11 @@ function withGhTimeout(promise, timeoutMs, label) {
|
|
|
215
106
|
}
|
|
216
107
|
|
|
217
108
|
/**
|
|
218
|
-
*
|
|
219
|
-
*
|
|
220
|
-
* identical `typeof x === 'string'` ternaries put that one function over the
|
|
221
|
-
* CRAP ratchet the moment a seventh field was needed.
|
|
222
|
-
*
|
|
223
|
-
* An empty string counts as absent — `gh` returns `""` for a field it cannot
|
|
224
|
-
* read, and every caller treats that exactly as "not there".
|
|
109
|
+
* A string field off a `gh pr view` payload; `""` (gh's unreadable field)
|
|
110
|
+
* counts as absent.
|
|
225
111
|
*
|
|
226
112
|
* @param {unknown} value
|
|
227
|
-
* @param {null|undefined} [absent]
|
|
228
|
-
* `null` for the three fields the poll loop compares against null; the
|
|
229
|
-
* `undefined` default for the ones whose absence must not shadow a
|
|
230
|
-
* downstream default.
|
|
113
|
+
* @param {null|undefined} [absent] `undefined` where absence must not shadow a downstream default.
|
|
231
114
|
* @returns {string|null|undefined}
|
|
232
115
|
*/
|
|
233
116
|
function readString(value, absent = undefined) {
|
|
@@ -235,17 +118,8 @@ function readString(value, absent = undefined) {
|
|
|
235
118
|
}
|
|
236
119
|
|
|
237
120
|
/**
|
|
238
|
-
* One probe per poll iteration
|
|
239
|
-
*
|
|
240
|
-
* status (for BEHIND recovery and human-required classification), and
|
|
241
|
-
* `createdAt` (the cumulative-budget anchor).
|
|
242
|
-
*
|
|
243
|
-
* Returns a degraded `{ checksStatus: 'pending', error }` probe when the read
|
|
244
|
-
* itself fails, preserving the conservative classification on probe errors —
|
|
245
|
-
* a flaky API read must not be mistaken for a definitive verdict. A probe
|
|
246
|
-
* that exceeds `ghTimeoutMs` (Story #4710) takes the SAME degraded path: a
|
|
247
|
-
* hung subprocess must surface as a probe error within the bound, never
|
|
248
|
-
* strand the wait.
|
|
121
|
+
* One probe per poll iteration. A failed or timed-out read degrades to
|
|
122
|
+
* `{ checksStatus: 'pending', error }` so a flaky read is never a verdict.
|
|
249
123
|
*
|
|
250
124
|
* @returns {Promise<object>}
|
|
251
125
|
*/
|
|
@@ -263,8 +137,6 @@ export async function readPrWaitProbe({
|
|
|
263
137
|
'mergeStateStatus',
|
|
264
138
|
'reviewDecision',
|
|
265
139
|
'statusCheckRollup',
|
|
266
|
-
// Story #5266 — the head the red advisory runs belong to, so their
|
|
267
|
-
// check-run output can be read back and classified.
|
|
268
140
|
'headRefOid',
|
|
269
141
|
]),
|
|
270
142
|
ghTimeoutMs,
|
|
@@ -277,14 +149,9 @@ export async function readPrWaitProbe({
|
|
|
277
149
|
mergeStateStatus: readString(view?.mergeStateStatus),
|
|
278
150
|
reviewDecision: readString(view?.reviewDecision),
|
|
279
151
|
checksStatus: deriveChecksStatus(view?.statusCheckRollup),
|
|
280
|
-
// Head-anchored
|
|
281
|
-
//
|
|
282
|
-
// the aggregate `checksStatus` folds together. `null` when the rollup is
|
|
283
|
-
// absent/empty — the loop's consecutive-probe fallback owns that path.
|
|
152
|
+
// Head-anchored, so superseded/pending runs don't read as red; `null`
|
|
153
|
+
// when the rollup is empty (the consecutive-probe fallback owns that).
|
|
284
154
|
requiredRunEvidence: deriveRequiredRunEvidence(view?.statusCheckRollup),
|
|
285
|
-
// The named red head runs (Story #5096), so an `advisory-gate-red`
|
|
286
|
-
// verdict can name the offending job and match the allowlist. Same
|
|
287
|
-
// red-ness test as `requiredRunEvidence`, so the two cannot disagree.
|
|
288
155
|
redHeadRuns: deriveRedHeadRuns(view?.statusCheckRollup),
|
|
289
156
|
headSha: readString(view?.headRefOid),
|
|
290
157
|
};
|
|
@@ -301,31 +168,11 @@ export async function readPrWaitProbe({
|
|
|
301
168
|
}
|
|
302
169
|
|
|
303
170
|
/**
|
|
304
|
-
* Resolve
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
*
|
|
309
|
-
* raises the per-invocation bound to keep single-block semantics without
|
|
310
|
-
* editing the consumer's config.
|
|
311
|
-
*
|
|
312
|
-
* `mode` (Story #4698) selects the close-time merge posture. `async` caps the
|
|
313
|
-
* per-invocation wait to `ASYNC_PROBE_WINDOW_SECONDS` so close returns the
|
|
314
|
-
* resumable `pending` terminal fast; `sync` (the default) is unchanged. An
|
|
315
|
-
* explicit `maxWaitSecondsOverride` still wins over the async cap — a headless
|
|
316
|
-
* caller with no host ceiling opts back into single-block waiting.
|
|
317
|
-
*
|
|
318
|
-
* `modeOverride` is the per-invocation `--merge-watch-mode` flag (Story #4949)
|
|
319
|
-
* and wins over `delivery.mergeWatch.mode` on exactly the precedence
|
|
320
|
-
* `maxWaitSecondsOverride` already uses. It exists because run topology is
|
|
321
|
-
* knowable only to the orchestrator: close sees one Story and cannot tell a
|
|
322
|
-
* solo delivery (where a foreground wait is the cheapest ending) from the Nth
|
|
323
|
-
* close of a wave (where each foreground wait is serialized dead time). The
|
|
324
|
-
* config default therefore stays `sync`, and the caller that knows better says
|
|
325
|
-
* so per invocation. The two flags remain composable — `--merge-watch-mode
|
|
326
|
-
* async --max-wait-seconds 900` selects the async posture and then overrides
|
|
327
|
-
* its probe cap, because the cap check below keys on the override's presence,
|
|
328
|
-
* not on where the mode came from.
|
|
171
|
+
* Resolve posture and budgets from `delivery.mergeWatch.*`. Both overrides
|
|
172
|
+
* (`--max-wait-seconds`, `--merge-watch-mode`) win over config; the mode flag
|
|
173
|
+
* exists because only the orchestrator knows whether a foreground wait is
|
|
174
|
+
* cheap (solo) or serialized dead time (a wave). An explicit max-wait
|
|
175
|
+
* override also beats the async cap.
|
|
329
176
|
*
|
|
330
177
|
* @param {object} [config]
|
|
331
178
|
* @param {number} [maxWaitSecondsOverride]
|
|
@@ -338,37 +185,21 @@ export function resolveMergeWaitConfig(
|
|
|
338
185
|
modeOverride,
|
|
339
186
|
) {
|
|
340
187
|
const mergeWatch = config?.delivery?.mergeWatch ?? {};
|
|
341
|
-
const int = (value, fallback
|
|
342
|
-
Number.isInteger(value) && value >=
|
|
188
|
+
const int = (value, fallback) =>
|
|
189
|
+
Number.isInteger(value) && value >= 1 ? value : fallback;
|
|
343
190
|
const requestedMode = modeOverride ?? mergeWatch.mode;
|
|
344
191
|
const mode = requestedMode === 'async' ? 'async' : 'sync';
|
|
345
192
|
const configuredMaxWait = int(
|
|
346
193
|
maxWaitSecondsOverride,
|
|
347
194
|
int(mergeWatch.maxWaitSeconds, DEFAULT_MAX_WAIT_SECONDS),
|
|
348
195
|
);
|
|
349
|
-
// Async mode caps the per-invocation wait to a short probe window so close
|
|
350
|
-
// returns `pending` fast instead of burning the foreground host slot on a
|
|
351
|
-
// merge that lands after the wait would have expired anyway. The window is
|
|
352
|
-
// long enough for the loop's existing checks to catch an instant merge and —
|
|
353
|
-
// via the imported `decideMergeWaitFailFast` decision — an instantly
|
|
354
|
-
// red required check. An explicit `--max-wait-seconds` override still wins so
|
|
355
|
-
// a headless caller with no host ceiling opts back into single-block waiting.
|
|
356
196
|
const maxWaitSeconds =
|
|
357
197
|
mode === 'async' && maxWaitSecondsOverride == null
|
|
358
198
|
? Math.min(configuredMaxWait, ASYNC_PROBE_WINDOW_SECONDS)
|
|
359
199
|
: configuredMaxWait;
|
|
360
|
-
//
|
|
361
|
-
//
|
|
362
|
-
|
|
363
|
-
// MIN_POLLS_BEFORE_BUDGET_BLOCK, and the cumulative budget would become
|
|
364
|
-
// unreachable across ANY number of resumes — a Story stuck in permanent
|
|
365
|
-
// `pending` that never escalates. Clamping the interval to the bound keeps
|
|
366
|
-
// at least one real poll cycle possible, which is what both the floor and
|
|
367
|
-
// the give-up bound depend on.
|
|
368
|
-
const intervalSeconds = Math.min(
|
|
369
|
-
int(mergeWatch.intervalSeconds, DEFAULT_INTERVAL_SECONDS),
|
|
370
|
-
maxWaitSeconds,
|
|
371
|
-
);
|
|
200
|
+
// An interval longer than the bound would expire every invocation on poll
|
|
201
|
+
// 1, so MIN_POLLS_BEFORE_BUDGET_BLOCK and the budget could never be reached.
|
|
202
|
+
const intervalSeconds = Math.min(DEFAULT_INTERVAL_SECONDS, maxWaitSeconds);
|
|
372
203
|
return {
|
|
373
204
|
mode,
|
|
374
205
|
intervalSeconds,
|
|
@@ -377,16 +208,13 @@ export function resolveMergeWaitConfig(
|
|
|
377
208
|
mergeWatch.maxBudgetSeconds,
|
|
378
209
|
DEFAULT_MAX_BUDGET_SECONDS,
|
|
379
210
|
),
|
|
380
|
-
updateAttempts:
|
|
211
|
+
updateAttempts: DEFAULT_UPDATE_ATTEMPTS,
|
|
381
212
|
};
|
|
382
213
|
}
|
|
383
214
|
|
|
384
215
|
/**
|
|
385
|
-
*
|
|
386
|
-
*
|
|
387
|
-
* probe carried no timestamp — a conservative degrade: the worst case is a
|
|
388
|
-
* resume getting a fresh cumulative budget, which is exactly the pre-#4543
|
|
389
|
-
* behaviour, never a premature block.
|
|
216
|
+
* Budget anchor; without `createdAt` it falls back to this invocation's start
|
|
217
|
+
* (worst case a fresh budget, never a premature block).
|
|
390
218
|
*
|
|
391
219
|
* @returns {number} epoch ms
|
|
392
220
|
*/
|
|
@@ -397,22 +225,9 @@ export function resolveBudgetAnchorMs({ createdAt, fallbackMs }) {
|
|
|
397
225
|
}
|
|
398
226
|
|
|
399
227
|
/**
|
|
400
|
-
*
|
|
401
|
-
*
|
|
402
|
-
*
|
|
403
|
-
* opens by telling the operator to resolve "branch protection, required
|
|
404
|
-
* checks, or a manual merge" — a diagnosis of a fault that provably does not
|
|
405
|
-
* exist here. An advisory gate blocks precisely BECAUSE GitHub reports the PR
|
|
406
|
-
* mergeable (`mergeStateStatus=UNSTABLE`) over a NON-required red check, so
|
|
407
|
-
* close refused to let native auto-merge land it. Saying so is the paragraph
|
|
408
|
-
* that stops the operator hunting a protection rule that is working fine.
|
|
409
|
-
*
|
|
410
|
-
* The two classes then part on what the evidence authorises. `inconclusive`
|
|
411
|
-
* is a job that failed WITHOUT FINISHING and reported no violation — nothing
|
|
412
|
-
* says the change is bad — so the proportionate act is a re-run, named here
|
|
413
|
-
* as `--rerun-advisory`; the permanent global exemption is deliberately
|
|
414
|
-
* mentioned last. `advisory-gate-red` reported a real violation, so the
|
|
415
|
-
* change is implicated and landing over it is a deliberate override.
|
|
228
|
+
* Advisory-gate remedy: the PR is mergeable (UNSTABLE over a non-required
|
|
229
|
+
* red), so branch protection is not the fault. `inconclusive` never finished
|
|
230
|
+
* and implicates nothing (re-run first); `red` reported a real violation.
|
|
416
231
|
*
|
|
417
232
|
* @param {{ storyId: number, blockClass: string }} args
|
|
418
233
|
* @returns {string}
|
|
@@ -444,10 +259,6 @@ function advisoryGateRemedy({ storyId, blockClass }) {
|
|
|
444
259
|
}
|
|
445
260
|
|
|
446
261
|
/**
|
|
447
|
-
* The class-specific remediation paragraph of the unlanded friction comment.
|
|
448
|
-
* Split out of {@link formatUnlandedFriction} so each class's wording is one
|
|
449
|
-
* named branch rather than a nested ternary.
|
|
450
|
-
*
|
|
451
262
|
* @param {{ storyId: number, prNumber: number|null, blockClass: string }} args
|
|
452
263
|
* @returns {string}
|
|
453
264
|
*/
|
|
@@ -473,10 +284,6 @@ function unlandedRemedy({ storyId, prNumber, blockClass }) {
|
|
|
473
284
|
);
|
|
474
285
|
}
|
|
475
286
|
|
|
476
|
-
/**
|
|
477
|
-
* Format the `friction` comment body posted alongside the `agent::blocked`
|
|
478
|
-
* transition when a landing attempt gives up without a confirmed merge.
|
|
479
|
-
*/
|
|
480
287
|
function formatUnlandedFriction({
|
|
481
288
|
storyId,
|
|
482
289
|
prNumber,
|
|
@@ -500,13 +307,7 @@ function formatUnlandedFriction({
|
|
|
500
307
|
);
|
|
501
308
|
}
|
|
502
309
|
|
|
503
|
-
/**
|
|
504
|
-
* Format the `friction` comment for a merge that **landed** while the
|
|
505
|
-
* `agent::done` label write failed. Deliberately not the unlanded wording:
|
|
506
|
-
* the merge is not in question, so pointing the operator at branch
|
|
507
|
-
* protection and required checks would send them to diagnose a fault that
|
|
508
|
-
* does not exist. Name the actual remedy instead.
|
|
509
|
-
*/
|
|
310
|
+
/** Friction for a merge that landed but whose `agent::done` write failed. */
|
|
510
311
|
function formatFlipFailedFriction({
|
|
511
312
|
storyId,
|
|
512
313
|
prNumber,
|
|
@@ -535,10 +336,7 @@ function formatFlipFailedFriction({
|
|
|
535
336
|
}
|
|
536
337
|
|
|
537
338
|
/**
|
|
538
|
-
*
|
|
539
|
-
* surfaces one. The id is the terminal envelope's `frictionCommentId`
|
|
540
|
-
* pointer, so a caller can link the operator straight at the remediation
|
|
541
|
-
* instead of telling them to go find it.
|
|
339
|
+
* Best-effort; the id becomes the envelope's `frictionCommentId`.
|
|
542
340
|
*
|
|
543
341
|
* @returns {Promise<string|null>}
|
|
544
342
|
*/
|
|
@@ -561,12 +359,7 @@ async function postFriction({ provider, storyId, body, progress }) {
|
|
|
561
359
|
}
|
|
562
360
|
}
|
|
563
361
|
|
|
564
|
-
/**
|
|
565
|
-
* Terminal for a confirmed merge whose `agent::done` flip failed. Emits
|
|
566
|
-
* `merge.flip-failed` (NOT `merge.unlanded` — the merge landed), posts the
|
|
567
|
-
* flip-failed friction, and blocks explicitly. Best-effort throughout: the
|
|
568
|
-
* caller owns the non-zero exit.
|
|
569
|
-
*/
|
|
362
|
+
/** Emits `merge.flip-failed` (not `merge.unlanded`); best-effort throughout. */
|
|
570
363
|
async function blockOnFlipFailed({
|
|
571
364
|
storyId,
|
|
572
365
|
prNumber,
|
|
@@ -628,25 +421,17 @@ async function blockOnFlipFailed({
|
|
|
628
421
|
reason,
|
|
629
422
|
frictionCommentId,
|
|
630
423
|
elapsedSeconds,
|
|
631
|
-
// The merge is
|
|
632
|
-
// envelope must say MERGED even when the probe that got us here was read
|
|
633
|
-
// before the merge landed. Reporting the stale OPEN (or null) would tell
|
|
634
|
-
// the operator to chase a merge that already happened.
|
|
424
|
+
// The merge is confirmed even if the probe was read before it landed.
|
|
635
425
|
prProbe: { ...(prProbe ?? {}), state: 'MERGED' },
|
|
636
426
|
};
|
|
637
427
|
}
|
|
638
428
|
|
|
639
429
|
/**
|
|
640
|
-
*
|
|
430
|
+
* `--rerun-advisory` beats `delivery.ci.rerunAdvisory`; both default 0
|
|
431
|
+
* because a rerun spends CI minutes. An invalid override falls back to config.
|
|
641
432
|
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
644
|
-
* rerun spends CI minutes and mutates GitHub state, so close does neither
|
|
645
|
-
* unasked. A non-integer or negative override is not an instruction to guess
|
|
646
|
-
* — it degrades to the config value.
|
|
647
|
-
*
|
|
648
|
-
* @param {object|null} config Resolved config (or any `delivery.ci` bag).
|
|
649
|
-
* @param {number} [override] The `--rerun-advisory` value, when supplied.
|
|
433
|
+
* @param {object|null} config
|
|
434
|
+
* @param {number} [override]
|
|
650
435
|
* @returns {number} allowance ≥ 0
|
|
651
436
|
*/
|
|
652
437
|
export function resolveAdvisoryRerunAllowance(config, override) {
|
|
@@ -655,10 +440,8 @@ export function resolveAdvisoryRerunAllowance(config, override) {
|
|
|
655
440
|
}
|
|
656
441
|
|
|
657
442
|
/**
|
|
658
|
-
*
|
|
659
|
-
*
|
|
660
|
-
* the conclusion), so this is what lets the wait tell a re-run's verdict apart
|
|
661
|
-
* from the stale pre-rerun snapshot it will keep seeing for a poll or two.
|
|
443
|
+
* One observation of a red run; a rerun changes `completedAt`, which tells a
|
|
444
|
+
* fresh verdict from the stale pre-rerun snapshot.
|
|
662
445
|
*
|
|
663
446
|
* @param {{name?: string|null, runId?: number, completedAt?: string}} run
|
|
664
447
|
* @returns {string}
|
|
@@ -668,17 +451,9 @@ function advisoryRunSignature(run) {
|
|
|
668
451
|
}
|
|
669
452
|
|
|
670
453
|
/**
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
674
|
-
* output text for a CheckRun, so on the rollup alone every red advisory run
|
|
675
|
-
* looks identical — which is the defect. The check-runs API for the head SHA
|
|
676
|
-
* carries `output.title` / `output.summary`, and ONE call for the whole head
|
|
677
|
-
* is enough to classify every red run on it.
|
|
678
|
-
*
|
|
679
|
-
* Called only on the block path (never per poll), and **fails open**: any
|
|
680
|
-
* error, timeout, or missing head SHA returns the runs unchanged, which
|
|
681
|
-
* classifies them as `advisory-gate-red` — the pre-#5266 verdict.
|
|
454
|
+
* Attach each red run's check-run output (the rollup carries none), in one
|
|
455
|
+
* call per head. Block path only; fails open to the unenriched runs, which
|
|
456
|
+
* classify as `advisory-gate-red`.
|
|
682
457
|
*
|
|
683
458
|
* @returns {Promise<Array<object>>} the runs, enriched where output was found
|
|
684
459
|
*/
|
|
@@ -720,17 +495,10 @@ async function enrichRedRunsWithOutput({
|
|
|
720
495
|
}
|
|
721
496
|
|
|
722
497
|
/**
|
|
723
|
-
*
|
|
724
|
-
* caller's remaining allowance.
|
|
725
|
-
*
|
|
726
|
-
* Requests `rerun-failed-jobs` per distinct workflow run rather than per job:
|
|
727
|
-
* one advisory workflow commonly fans out, and re-running the whole failed set
|
|
728
|
-
* is both cheaper in API calls and what an operator means by "re-run it".
|
|
498
|
+
* `rerun-failed-jobs` once per distinct workflow run (not per job).
|
|
729
499
|
*
|
|
730
|
-
* @returns {Promise<boolean>}
|
|
731
|
-
*
|
|
732
|
-
* because an allowance that cannot be spent must not silently suppress the
|
|
733
|
-
* gate.
|
|
500
|
+
* @returns {Promise<boolean>} `false` (incl. no run id) keeps the caller on
|
|
501
|
+
* the block path — an unspendable allowance must not suppress the gate.
|
|
734
502
|
*/
|
|
735
503
|
async function rerunAdvisoryRuns({ blockingRuns, gh, ghTimeoutMs, progress }) {
|
|
736
504
|
const runIds = [
|
|
@@ -775,16 +543,20 @@ async function rerunAdvisoryRuns({ blockingRuns, gh, ghTimeoutMs, progress }) {
|
|
|
775
543
|
}
|
|
776
544
|
|
|
777
545
|
/**
|
|
778
|
-
*
|
|
779
|
-
*
|
|
780
|
-
* pre-rerun snapshot the next poll reads does not re-block on the same job.
|
|
546
|
+
* Advisory runs are non-required by construction; a red required check
|
|
547
|
+
* fails fast as `checks-failed` and never reaches the rerun path.
|
|
781
548
|
*
|
|
782
|
-
*
|
|
783
|
-
*
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
549
|
+
* @param {{ remaining: number }} rerunState
|
|
550
|
+
* @returns {number}
|
|
551
|
+
*/
|
|
552
|
+
function advisoryRerunsLeft(rerunState) {
|
|
553
|
+
return isRerunPermitted({ required: false }) ? rerunState.remaining : 0;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Spend one rerun unit, recording each re-run's signature so the stale
|
|
558
|
+
* snapshot doesn't re-block. Does NOT disarm: a green re-run should land on
|
|
559
|
+
* its own; the armed window is what opting in to `--rerun-advisory` accepts.
|
|
788
560
|
*
|
|
789
561
|
* @returns {Promise<boolean>} `true` when the caller should keep polling.
|
|
790
562
|
*/
|
|
@@ -795,7 +567,7 @@ async function maybeRerunAdvisory({
|
|
|
795
567
|
ghTimeoutMs,
|
|
796
568
|
progress,
|
|
797
569
|
}) {
|
|
798
|
-
if (rerunState
|
|
570
|
+
if (advisoryRerunsLeft(rerunState) <= 0) return false;
|
|
799
571
|
const rerun = await rerunAdvisoryRuns({
|
|
800
572
|
blockingRuns,
|
|
801
573
|
gh,
|
|
@@ -811,22 +583,10 @@ async function maybeRerunAdvisory({
|
|
|
811
583
|
}
|
|
812
584
|
|
|
813
585
|
/**
|
|
814
|
-
*
|
|
815
|
-
*
|
|
816
|
-
*
|
|
817
|
-
*
|
|
818
|
-
* branch. `runMergePoll` is already above `check-cyclomatic`'s ceiling; the
|
|
819
|
-
* three decision points this would otherwise cost inline are a real gate
|
|
820
|
-
* regression, and they belong with the policy either way.
|
|
821
|
-
*
|
|
822
|
-
* Disarms BEFORE returning the terminal: an armed PR can merge out from under
|
|
823
|
-
* the block the caller is about to record.
|
|
824
|
-
*
|
|
825
|
-
* Story #5266 threads three more steps through the same single assignment:
|
|
826
|
-
* runs this invocation already re-ran (and has no fresh verdict for) are
|
|
827
|
-
* skipped rather than re-blocked; the survivors are enriched with their own
|
|
828
|
-
* check-run output so the class can be `advisory-gate-inconclusive`; and a
|
|
829
|
-
* remaining rerun allowance is spent before any block is recorded.
|
|
586
|
+
* Advisory-gate terminal for one poll; passes a decided `unlanded` through so
|
|
587
|
+
* the caller stays branch-free (`runMergePoll` is at the cyclomatic ceiling).
|
|
588
|
+
* Skips runs already re-ran, enriches the rest, spends any rerun allowance,
|
|
589
|
+
* and disarms BEFORE returning a block so the PR cannot merge out from under it.
|
|
830
590
|
*/
|
|
831
591
|
async function resolveAdvisoryUnlanded({
|
|
832
592
|
unlanded,
|
|
@@ -848,9 +608,6 @@ async function resolveAdvisoryUnlanded({
|
|
|
848
608
|
advisoryAllowlist,
|
|
849
609
|
});
|
|
850
610
|
if (!advisory) return null;
|
|
851
|
-
// Every blocking run is one this invocation already re-ran and has not seen
|
|
852
|
-
// a fresh verdict for yet (Story #5266) — keep polling rather than blocking
|
|
853
|
-
// on the snapshot the rerun was meant to replace.
|
|
854
611
|
const pending = advisory.blockingRuns.filter(
|
|
855
612
|
(run) => !rerunState.issued.has(advisoryRunSignature(run)),
|
|
856
613
|
);
|
|
@@ -887,12 +644,7 @@ async function resolveAdvisoryUnlanded({
|
|
|
887
644
|
};
|
|
888
645
|
}
|
|
889
646
|
|
|
890
|
-
/**
|
|
891
|
-
* Classify the unlanded merge, emit `merge.unlanded`, post a `friction`
|
|
892
|
-
* comment, and transition the Story to `agent::blocked`. Every side effect
|
|
893
|
-
* is best-effort logged rather than thrown — the caller owns surfacing the
|
|
894
|
-
* non-zero exit once this returns.
|
|
895
|
-
*/
|
|
647
|
+
/** Classify, emit `merge.unlanded`, post friction, block — all best-effort. */
|
|
896
648
|
async function blockOnUnlanded({
|
|
897
649
|
storyId,
|
|
898
650
|
prNumber,
|
|
@@ -907,11 +659,9 @@ async function blockOnUnlanded({
|
|
|
907
659
|
blockClassOverride,
|
|
908
660
|
reasonOverride,
|
|
909
661
|
}) {
|
|
910
|
-
//
|
|
911
|
-
//
|
|
912
|
-
//
|
|
913
|
-
// condition the class names, so every classifier heuristic reads the PR as
|
|
914
|
-
// healthy.
|
|
662
|
+
// A verdict decided at detection is emitted as-is, never re-derived (the
|
|
663
|
+
// classifier reads an advisory-gate PR as healthy); the classifier runs
|
|
664
|
+
// only for arm-failure and budget exhaustion.
|
|
915
665
|
const { blockClass, reason } = blockClassOverride
|
|
916
666
|
? {
|
|
917
667
|
blockClass: blockClassOverride,
|
|
@@ -923,11 +673,7 @@ async function blockOnUnlanded({
|
|
|
923
673
|
budget,
|
|
924
674
|
});
|
|
925
675
|
const elapsedSeconds = budget?.elapsedSeconds ?? 0;
|
|
926
|
-
//
|
|
927
|
-
// `per-run` (head-anchored required-run evidence) or `consecutive-probe`
|
|
928
|
-
// (the evidence-unavailable fallback). Named on the emitted record so the
|
|
929
|
-
// `merge.unlanded` telemetry attributes the fail-fast to the path that
|
|
930
|
-
// fired it. Absent for every other block class.
|
|
676
|
+
// `per-run` | `consecutive-probe` for `checks-failed`; absent otherwise.
|
|
931
677
|
const evidencePath = prProbe?.evidencePath;
|
|
932
678
|
|
|
933
679
|
if (Number.isInteger(prNumber) && prNumber > 0) {
|
|
@@ -988,27 +734,14 @@ async function blockOnUnlanded({
|
|
|
988
734
|
reason,
|
|
989
735
|
frictionCommentId,
|
|
990
736
|
elapsedSeconds,
|
|
991
|
-
// The
|
|
992
|
-
// `pr.state` / `pr.checksStatus` from here; dropping it made every
|
|
993
|
-
// blocked envelope claim `null` for facts we had just observed — a
|
|
994
|
-
// `checks-failed` envelope reporting `checksStatus: null` contradicts
|
|
995
|
-
// itself. Schema wants "live PR facts as observed at terminal time".
|
|
737
|
+
// The envelope's `pr.state` / `pr.checksStatus` come from here.
|
|
996
738
|
prProbe,
|
|
997
739
|
};
|
|
998
740
|
}
|
|
999
741
|
|
|
1000
742
|
/**
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
* real state and lets the normal classification decide, which is why a
|
|
1004
|
-
* failed attempt still counts against the wait's tick.
|
|
1005
|
-
*
|
|
1006
|
-
* The BEHIND / budget / did-it-land decision itself lives in the shared
|
|
1007
|
-
* {@link applyBehindUpdate} (Story #5006) — the CI-watch loop in
|
|
1008
|
-
* `lib/orchestration/pr-watch.js` runs the same one. This wrapper supplies
|
|
1009
|
-
* the merge wait's probe source, its `gh` facade (bounded by
|
|
1010
|
-
* {@link withGhTimeout}, so a wedged child cannot strand an unattended
|
|
1011
|
-
* async-mode wait), and its operator wording.
|
|
743
|
+
* Update a BEHIND PR, bounded by `updateAttempts`; a failed update is not a
|
|
744
|
+
* terminal (the next poll re-reads) but still counts as an attempt.
|
|
1012
745
|
*
|
|
1013
746
|
* @returns {Promise<boolean>} whether an update was actually attempted.
|
|
1014
747
|
*/
|
|
@@ -1050,11 +783,7 @@ async function maybeUpdateBehindPr({
|
|
|
1050
783
|
return recovery.attempted;
|
|
1051
784
|
}
|
|
1052
785
|
|
|
1053
|
-
/**
|
|
1054
|
-
* Handle an observed merge: run the shared `confirmStoryMerged` flip, then
|
|
1055
|
-
* the shared post-land tail. Called at most once per wait — the loop probes
|
|
1056
|
-
* the PR, not the ticket.
|
|
1057
|
-
*/
|
|
786
|
+
/** Called at most once per wait: the flip, then the post-land tail. */
|
|
1058
787
|
async function onMergeObserved({
|
|
1059
788
|
storyId,
|
|
1060
789
|
storyBranch,
|
|
@@ -1085,15 +814,13 @@ async function onMergeObserved({
|
|
|
1085
814
|
injectedGh,
|
|
1086
815
|
injectedNotify,
|
|
1087
816
|
readPrMergeStateFn,
|
|
817
|
+
// Saves confirmation a second `gh pr view`.
|
|
818
|
+
prState: prProbe,
|
|
1088
819
|
});
|
|
1089
820
|
|
|
1090
821
|
if (confirmation.merged && confirmation.action === 'flip-failed') {
|
|
1091
|
-
//
|
|
1092
|
-
//
|
|
1093
|
-
// strand the Story at agent::closing with no notification. Reporting it
|
|
1094
|
-
// as UNLANDED was not (Story #4539): the merge landed, so the
|
|
1095
|
-
// merge.unlanded event would be false and its friction would send the
|
|
1096
|
-
// operator to branch protection instead of the one-line remedy.
|
|
822
|
+
// Block explicitly (confirmed:true would strand it at agent::closing),
|
|
823
|
+
// but not as unlanded — the merge landed.
|
|
1097
824
|
progress?.(
|
|
1098
825
|
'CONFIRM',
|
|
1099
826
|
`⚠️ Story #${storyId} merge confirmed but the agent::done flip failed — blocking explicitly.`,
|
|
@@ -1128,11 +855,7 @@ async function onMergeObserved({
|
|
|
1128
855
|
terminal: 'landed',
|
|
1129
856
|
action: confirmation.action,
|
|
1130
857
|
tail,
|
|
1131
|
-
//
|
|
1132
|
-
// by admin override, or with non-required checks red, must not be reported
|
|
1133
|
-
// as a green run nobody actually saw — that is the same
|
|
1134
|
-
// report-an-outcome-you-never-checked shape the land tail's per-step
|
|
1135
|
-
// booleans exist to prevent.
|
|
858
|
+
// The observed rollup, not a stamped 'success' (admin merges exist).
|
|
1136
859
|
prProbe,
|
|
1137
860
|
};
|
|
1138
861
|
}
|
|
@@ -1152,29 +875,20 @@ async function onMergeObserved({
|
|
|
1152
875
|
* @param {string|null} args.autoMergeReason
|
|
1153
876
|
* @param {object} args.provider
|
|
1154
877
|
* @param {object} [args.config]
|
|
1155
|
-
* @param {'sync'|'async'} [args.mergeWatchMode]
|
|
1156
|
-
* `--merge-watch-mode` override (Story #4949); wins over
|
|
1157
|
-
* `delivery.mergeWatch.mode`.
|
|
878
|
+
* @param {'sync'|'async'} [args.mergeWatchMode]
|
|
1158
879
|
* @param {(tag: string, msg: string) => void} [args.progress]
|
|
1159
|
-
* @param {number} [args.rerunAdvisory]
|
|
1160
|
-
* per-invocation override of `delivery.ci.rerunAdvisory` (both default 0).
|
|
880
|
+
* @param {number} [args.rerunAdvisory]
|
|
1161
881
|
* @param {object} [args.injectedGh]
|
|
1162
882
|
* @param {Function} [args.injectedNotify]
|
|
1163
|
-
* @param {Function} [args.confirmStoryMergedFn]
|
|
1164
|
-
*
|
|
1165
|
-
*
|
|
1166
|
-
* @param {Function} [args.
|
|
1167
|
-
* @param {Function} [args.
|
|
1168
|
-
* @param {Function} [args.
|
|
1169
|
-
* @param {
|
|
1170
|
-
* @param {
|
|
1171
|
-
* @param {
|
|
1172
|
-
* suite does not actually wait.
|
|
1173
|
-
* @param {() => number} [args.nowMsFn] Test seam; returns epoch ms.
|
|
1174
|
-
* @param {number} [args.ghTimeoutMs] Wall-clock bound for each `gh` call the
|
|
1175
|
-
* wait makes (Story #4710). A framework constant
|
|
1176
|
-
* (`MERGE_WAIT_GH_TIMEOUT_MS`), overridable only as a test seam — not
|
|
1177
|
-
* config.
|
|
883
|
+
* @param {Function} [args.confirmStoryMergedFn] The one shared merged/`agent::done` implementation.
|
|
884
|
+
* @param {Function} [args.readPrWaitProbeFn]
|
|
885
|
+
* @param {Function} [args.readPrMergeStateFn]
|
|
886
|
+
* @param {Function} [args.classifyMergeBlockFn]
|
|
887
|
+
* @param {Function} [args.emitMergeUnlandedFn]
|
|
888
|
+
* @param {Function} [args.runPostLandTailFn]
|
|
889
|
+
* @param {(ms: number) => Promise<void>} [args.sleepFn]
|
|
890
|
+
* @param {() => number} [args.nowMsFn]
|
|
891
|
+
* @param {number} [args.ghTimeoutMs] Test seam only, not config.
|
|
1178
892
|
* @returns {Promise<object>}
|
|
1179
893
|
*/
|
|
1180
894
|
export async function runConfirmMergePhase({
|
|
@@ -1207,10 +921,7 @@ export async function runConfirmMergePhase({
|
|
|
1207
921
|
nowMsFn = Date.now,
|
|
1208
922
|
ghTimeoutMs = MERGE_WAIT_GH_TIMEOUT_MS,
|
|
1209
923
|
}) {
|
|
1210
|
-
//
|
|
1211
|
-
// deliberate disablement) — there is no "armed but unconfirmed" PR to
|
|
1212
|
-
// poll. An explicit terminal state is still required, so classify and
|
|
1213
|
-
// block immediately rather than resting silently.
|
|
924
|
+
// Never armed: nothing to poll, but a terminal is still required.
|
|
1214
925
|
if (!autoMergeEnabled) {
|
|
1215
926
|
progress?.(
|
|
1216
927
|
'CONFIRM',
|
|
@@ -1226,12 +937,8 @@ export async function runConfirmMergePhase({
|
|
|
1226
937
|
progress,
|
|
1227
938
|
classifyMergeBlockFn,
|
|
1228
939
|
emitMergeUnlandedFn,
|
|
1229
|
-
//
|
|
1230
|
-
//
|
|
1231
|
-
// as a generic `arm-failure`.
|
|
1232
|
-
// Story #5266 — carry the arm phase's CLASS too: a pre-arm refusal over
|
|
1233
|
-
// a scan that never finished is `advisory-gate-inconclusive`, and
|
|
1234
|
-
// hard-coding the red class here would relabel it at the terminal.
|
|
940
|
+
// Carry the arm phase's advisory verdict and class (which may be
|
|
941
|
+
// `inconclusive`) rather than a generic `arm-failure`.
|
|
1235
942
|
...(autoMergeReason === 'advisory-gate-red'
|
|
1236
943
|
? {
|
|
1237
944
|
blockClassOverride: advisoryGate?.blockClass ?? 'advisory-gate-red',
|
|
@@ -1252,11 +959,8 @@ export async function runConfirmMergePhase({
|
|
|
1252
959
|
maxWaitSecondsOverride,
|
|
1253
960
|
mergeWatchModeOverride,
|
|
1254
961
|
);
|
|
1255
|
-
// Story #5096 — the advisory-gate knobs, read once for the whole wait.
|
|
1256
962
|
const { blockOnAdvisoryFailure, advisoryAllowlist } = getCiDelivery(config);
|
|
1257
|
-
//
|
|
1258
|
-
// wait rather than per poll, so `n` bounds the CI minutes this invocation
|
|
1259
|
-
// can cost no matter how many times the gate is observed red.
|
|
963
|
+
// Spent across the whole wait, not per poll, so `n` bounds CI minutes.
|
|
1260
964
|
const rerunAllowance = resolveAdvisoryRerunAllowance(
|
|
1261
965
|
config,
|
|
1262
966
|
rerunAdvisoryOverride,
|
|
@@ -1271,11 +975,8 @@ export async function runConfirmMergePhase({
|
|
|
1271
975
|
let anchorMs = startedAtMs;
|
|
1272
976
|
let updatesUsed = 0;
|
|
1273
977
|
let polls = 0;
|
|
1274
|
-
//
|
|
1275
|
-
//
|
|
1276
|
-
// snapshot never fail-fasts — two consecutive failing probes at least one
|
|
1277
|
-
// poll interval apart are required. Reset on any non-failing (or genuinely
|
|
1278
|
-
// evidenced) probe.
|
|
978
|
+
// Without per-run evidence, fail fast only after two consecutive failing
|
|
979
|
+
// probes an interval apart; reset on any other probe.
|
|
1279
980
|
let consecutiveRequiredFailSnapshots = 0;
|
|
1280
981
|
|
|
1281
982
|
progress?.(
|
|
@@ -1285,19 +986,7 @@ export async function runConfirmMergePhase({
|
|
|
1285
986
|
`cumulative budget=${maxBudgetSeconds}s)...`,
|
|
1286
987
|
);
|
|
1287
988
|
|
|
1288
|
-
/**
|
|
1289
|
-
* One poll iteration. Returns `{ done: false }` to keep polling, or
|
|
1290
|
-
* `{ done: true, outcome }` with the phase's terminal. Story #4873 lifted
|
|
1291
|
-
* this body out of a bespoke unbounded loop so the cadence is owned by the
|
|
1292
|
-
* shared {@link pollUntil} primitive — the loop below sleeps, aborts, and
|
|
1293
|
-
* counts ticks in exactly one place for every wait in the codebase. Every
|
|
1294
|
-
* budget, floor, and classification decision is unchanged; only who owns the
|
|
1295
|
-
* `await sleep(...)` moved.
|
|
1296
|
-
*
|
|
1297
|
-
* A throw from any of the terminal handlers is captured rather than allowed
|
|
1298
|
-
* to escape into `pollUntil` (which treats a throwing `fn` as a non-match
|
|
1299
|
-
* and would spin forever on it); the caller re-throws it after the loop.
|
|
1300
|
-
*/
|
|
989
|
+
/** One iteration: `{ done: false }` or `{ done: true, outcome }`. */
|
|
1301
990
|
async function runMergePoll() {
|
|
1302
991
|
const probe = await readPrWaitProbeFn({
|
|
1303
992
|
prNumber,
|
|
@@ -1306,8 +995,6 @@ export async function runConfirmMergePhase({
|
|
|
1306
995
|
});
|
|
1307
996
|
polls += 1;
|
|
1308
997
|
|
|
1309
|
-
// Anchor the cumulative budget at the PR's creation the first time we
|
|
1310
|
-
// learn it, so a resumed wait continues the clock instead of restarting.
|
|
1311
998
|
anchorMs = resolveBudgetAnchorMs({
|
|
1312
999
|
createdAt: probe.createdAt,
|
|
1313
1000
|
fallbackMs: startedAtMs,
|
|
@@ -1322,12 +1009,7 @@ export async function runConfirmMergePhase({
|
|
|
1322
1009
|
maxBudgetSeconds,
|
|
1323
1010
|
};
|
|
1324
1011
|
|
|
1325
|
-
// Heartbeat
|
|
1326
|
-
// progress to its own output file, and between the opening banner and the
|
|
1327
|
-
// terminal there used to be NOTHING for minutes at a time — so an
|
|
1328
|
-
// orchestrator watching that file could not tell a healthy in-flight wait
|
|
1329
|
-
// from a wedged process without going back to GitHub itself. One line per
|
|
1330
|
-
// poll makes the file's own growth the liveness signal.
|
|
1012
|
+
// Heartbeat: a backgrounded close's output-file growth is its liveness signal.
|
|
1331
1013
|
progress?.(
|
|
1332
1014
|
'CONFIRM',
|
|
1333
1015
|
`⏱ poll ${polls}: PR #${prNumber} state=${probe.state ?? 'unknown'} ` +
|
|
@@ -1338,7 +1020,7 @@ export async function runConfirmMergePhase({
|
|
|
1338
1020
|
(probe.error ? ` — probe error: ${probe.error}` : ''),
|
|
1339
1021
|
);
|
|
1340
1022
|
|
|
1341
|
-
if (probe
|
|
1023
|
+
if (isPrMerged(probe)) {
|
|
1342
1024
|
return doneWith(
|
|
1343
1025
|
await onMergeObserved({
|
|
1344
1026
|
storyId,
|
|
@@ -1362,38 +1044,21 @@ export async function runConfirmMergePhase({
|
|
|
1362
1044
|
);
|
|
1363
1045
|
}
|
|
1364
1046
|
|
|
1365
|
-
//
|
|
1366
|
-
// definitive condition fills `unlanded` and the single call site at the
|
|
1367
|
-
// bottom classifies, emits, and blocks — the fail-fast tree used to
|
|
1368
|
-
// duplicate that block twice inline.
|
|
1047
|
+
// Each definitive condition fills `unlanded`; one call site below blocks.
|
|
1369
1048
|
let unlanded = null;
|
|
1370
1049
|
|
|
1371
1050
|
if (probe.state === 'CLOSED') {
|
|
1372
|
-
// Closed without merging — a definitive terminal, not a "still
|
|
1373
|
-
// pending" condition the budget should keep waiting on. checksStatus
|
|
1374
|
-
// MUST be a non-pending, non-undefined value here: the classifier's
|
|
1375
|
-
// budget-exhausted branch treats an undefined checksStatus as "still
|
|
1376
|
-
// pending", which would misclassify this definitive case as
|
|
1377
|
-
// checks-pending-timeout instead of reaching the api-race-other
|
|
1378
|
-
// reason built from prProbe.error.
|
|
1379
1051
|
unlanded = {
|
|
1380
|
-
prProbe:
|
|
1381
|
-
checksStatus: 'closed',
|
|
1382
|
-
error: 'PR closed without merging (state=CLOSED)',
|
|
1383
|
-
},
|
|
1052
|
+
prProbe: probe,
|
|
1384
1053
|
budget: {
|
|
1385
1054
|
exhausted: true,
|
|
1386
1055
|
elapsedSeconds: Math.round(waitedMs / 1000),
|
|
1387
1056
|
},
|
|
1057
|
+
blockClassOverride: CLOSED_UNMERGED_BLOCK_CLASS,
|
|
1058
|
+
reasonOverride: CLOSED_UNMERGED_REASON,
|
|
1388
1059
|
};
|
|
1389
1060
|
} else {
|
|
1390
|
-
//
|
|
1391
|
-
// #4695), decided by the extracted `decideMergeWaitFailFast` (Story
|
|
1392
|
-
// #4710): per-run evidence decides on a single probe; without evidence
|
|
1393
|
-
// two consecutive failing probes are required. No remaining budget
|
|
1394
|
-
// turns a failed check green, and waiting it out is what made the
|
|
1395
|
-
// pre-#4543 wait report the operator's red test run as a
|
|
1396
|
-
// branch-protection block.
|
|
1061
|
+
// No remaining budget turns a red required check green; fail fast.
|
|
1397
1062
|
const decision = decideMergeWaitFailFast({
|
|
1398
1063
|
probe,
|
|
1399
1064
|
consecutiveRequiredFailSnapshots,
|
|
@@ -1413,14 +1078,13 @@ export async function runConfirmMergePhase({
|
|
|
1413
1078
|
exhausted: false,
|
|
1414
1079
|
elapsedSeconds: Math.round(waitedMs / 1000),
|
|
1415
1080
|
},
|
|
1081
|
+
blockClassOverride: decision.blockClass,
|
|
1082
|
+
reasonOverride: decision.reason,
|
|
1416
1083
|
};
|
|
1417
1084
|
}
|
|
1418
1085
|
|
|
1419
|
-
//
|
|
1420
|
-
//
|
|
1421
|
-
// gate is still QUEUED, so the pre-arm refusal in `auto-merge.js` sees
|
|
1422
|
-
// nothing; the gate reddens here, mid-wait, and native auto-merge would
|
|
1423
|
-
// land the PR the moment the REQUIRED contexts go green.
|
|
1086
|
+
// Advisory gates are usually still QUEUED at arm time, so they redden
|
|
1087
|
+
// here, mid-wait, before auto-merge lands over them.
|
|
1424
1088
|
unlanded = await resolveAdvisoryUnlanded({
|
|
1425
1089
|
unlanded,
|
|
1426
1090
|
probe,
|
|
@@ -1451,11 +1115,7 @@ export async function runConfirmMergePhase({
|
|
|
1451
1115
|
updatesUsed += 1;
|
|
1452
1116
|
}
|
|
1453
1117
|
|
|
1454
|
-
// Cumulative budget exhausted
|
|
1455
|
-
// probe we already hold. Gated behind the poll floor so an
|
|
1456
|
-
// already-over-budget PR (anchored at a createdAt older than the budget
|
|
1457
|
-
// — a resume the next day, or a long-open PR) still gets a real poll
|
|
1458
|
-
// cycle instead of being blocked before this invocation waited at all.
|
|
1118
|
+
// Cumulative budget exhausted, behind the poll floor.
|
|
1459
1119
|
if (
|
|
1460
1120
|
polls >= MIN_POLLS_BEFORE_BUDGET_BLOCK &&
|
|
1461
1121
|
cumulativeMs + intervalMs > maxBudgetSeconds * 1000
|
|
@@ -1485,11 +1145,7 @@ export async function runConfirmMergePhase({
|
|
|
1485
1145
|
);
|
|
1486
1146
|
}
|
|
1487
1147
|
|
|
1488
|
-
//
|
|
1489
|
-
// nothing is wrong, the run simply reached the edge of its host slot.
|
|
1490
|
-
// No label mutation, no merge.unlanded event — the caller surfaces a
|
|
1491
|
-
// resumable terminal and the next invocation continues the cumulative
|
|
1492
|
-
// clock from the PR's createdAt.
|
|
1148
|
+
// Invocation bound reached: resumable `pending`, no mutation.
|
|
1493
1149
|
if (waitedMs + intervalMs > maxWaitSeconds * 1000) {
|
|
1494
1150
|
progress?.(
|
|
1495
1151
|
'CONFIRM',
|
|
@@ -1515,25 +1171,19 @@ export async function runConfirmMergePhase({
|
|
|
1515
1171
|
try {
|
|
1516
1172
|
return await runMergePoll();
|
|
1517
1173
|
} catch (err) {
|
|
1518
|
-
//
|
|
1519
|
-
// non-match and would poll forever on it, so the throw is carried out
|
|
1520
|
-
// as a match and re-raised below.
|
|
1174
|
+
// pollUntil treats a throw as a non-match and would spin; carry it out.
|
|
1521
1175
|
return { done: true, thrown: err };
|
|
1522
1176
|
}
|
|
1523
1177
|
},
|
|
1524
1178
|
predicate: (result) => result?.done === true,
|
|
1525
1179
|
intervalMs,
|
|
1526
|
-
//
|
|
1527
|
-
// `maxBudgetSeconds` → blocked), and both are decided from the probe
|
|
1528
|
-
// inside the tick. A second, cruder wall-clock timeout here would throw
|
|
1529
|
-
// past those classifications.
|
|
1180
|
+
// No pollUntil timeout: the tick owns both bounds and their classification.
|
|
1530
1181
|
sleepFn: (ms) => sleepFn(ms),
|
|
1531
1182
|
});
|
|
1532
1183
|
if (tick.thrown) throw tick.thrown;
|
|
1533
1184
|
return tick.outcome;
|
|
1534
1185
|
}
|
|
1535
1186
|
|
|
1536
|
-
/** Wrap a phase terminal as the poll loop's match. */
|
|
1537
1187
|
function doneWith(outcome) {
|
|
1538
1188
|
return { done: true, outcome };
|
|
1539
1189
|
}
|