mandrel 2.60.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 +10 -10
- package/.agents/agents/story-worker.md +1 -1
- package/.agents/docs/agentrc-reference.json +4 -82
- package/.agents/docs/configuration.md +20 -87
- package/.agents/docs/workflows.md +1 -1
- package/.agents/instructions.md +1 -3
- package/.agents/rules/gherkin-standards.md +4 -0
- package/.agents/schemas/agentrc.schema.json +11 -486
- package/.agents/schemas/story-deliver-terminal.schema.json +10 -0
- package/.agents/schemas/validation-evidence.schema.json +2 -1
- package/.agents/scripts/README.md +14 -14
- package/.agents/scripts/acceptance-eval.js +32 -164
- 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 +9 -45
- package/.agents/scripts/check-arch-cycles.js +14 -64
- package/.agents/scripts/check-baselines.js +8 -48
- package/.agents/scripts/check-context-budget.js +46 -172
- 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 +32 -99
- package/.agents/scripts/deliver-light.js +14 -101
- package/.agents/scripts/deliver-recover.js +6 -42
- package/.agents/scripts/deliver-run.js +36 -133
- 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 +10 -50
- 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 +5 -21
- 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 +31 -143
- 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 -35
- package/.agents/scripts/lib/config/explain.js +9 -80
- package/.agents/scripts/lib/config/gates/coverage.schema.js +0 -12
- package/.agents/scripts/lib/config/gates/crap-incremental-coverage.schema.js +2 -30
- 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 +10 -52
- package/.agents/scripts/lib/config-schema-shared.js +1 -4
- package/.agents/scripts/lib/config-settings-schema-delivery.js +12 -294
- 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 +12 -37
- package/.agents/scripts/lib/coverage-capture-incremental.js +12 -43
- package/.agents/scripts/lib/coverage-capture-usage.js +4 -24
- 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 -126
- 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 +93 -419
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +20 -98
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +27 -151
- 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 +31 -259
- 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 +21 -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 +5 -75
- 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 +42 -189
- 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 +42 -214
- 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 +26 -231
- 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 +52 -303
- 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 +14 -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 +49 -248
- package/.agents/scripts/lib/orchestration/plan-persist/soft-findings.js +2 -12
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +85 -368
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +8 -38
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +30 -190
- 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 -94
- 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 +40 -199
- package/.agents/scripts/lib/orchestration/run-scoped-config.js +13 -81
- 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 +18 -80
- 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 +9 -50
- 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 +110 -290
- 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 -201
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +43 -245
- package/.agents/scripts/lib/orchestration/story-init-envelope.js +4 -23
- 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 -126
- 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 +39 -181
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +51 -244
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +39 -250
- 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 +22 -83
- package/.agents/scripts/lib/story-body/footer-block.js +11 -44
- package/.agents/scripts/lib/story-body/story-body.js +83 -358
- package/.agents/scripts/lib/temp-retention.js +42 -143
- package/.agents/scripts/lib/templates/decomposer-prompts.js +11 -65
- 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 +53 -221
- 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 -104
- package/.agents/scripts/nav-registry-diff.js +22 -108
- package/.agents/scripts/notify.js +12 -69
- package/.agents/scripts/plan-context.js +27 -111
- package/.agents/scripts/plan-critics.js +12 -66
- package/.agents/scripts/plan-persist.js +31 -148
- package/.agents/scripts/plan-run-epilogue.js +11 -37
- package/.agents/scripts/pr-watch-with-update.js +76 -267
- 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 -209
- package/.agents/scripts/stories-wave-tick.js +102 -472
- 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/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/audit-lens-core.md +1 -1
- package/.agents/workflows/helpers/code-quality-guardrails.md +12 -15
- package/.agents/workflows/helpers/deliver-digest.md +6 -4
- package/.agents/workflows/helpers/deliver-reference.md +114 -188
- package/.agents/workflows/helpers/deliver-story-reference.md +189 -533
- package/.agents/workflows/helpers/plan-reference.md +86 -139
- package/.agents/workflows/helpers/qa-core.md +12 -0
- 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 +25 -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 +105 -661
- 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 +15 -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 +2 -21
- package/lib/migrations/steps/strip-removed-agentrc-keys.js +333 -0
- package/package.json +16 -14
- package/.agents/docs/SDLC.md +0 -590
- package/.agents/docs/quality-gates.md +0 -1183
- package/.agents/rules/known-tooling-behavior.md +0 -200
- 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/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 -368
- package/.agents/scripts/check-test-portability.js +0 -512
- package/.agents/scripts/check-workflow-citations.js +0 -218
- 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/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 -125
- package/.agents/scripts/test-isolate.js +0 -55
- package/.agents/scripts/update-dead-exports-baseline.js +0 -321
|
@@ -1,79 +1,64 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* full-suite-lock.js — serialize
|
|
3
|
-
*
|
|
2
|
+
* full-suite-lock.js — serialize full-suite spawns across one host's
|
|
3
|
+
* worktrees (they contend for cores and a shared coverage artifact), over
|
|
4
|
+
* the `sweep-lock.js` primitive.
|
|
4
5
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* the first instead.
|
|
12
|
-
*
|
|
13
|
-
* **It reuses the shipped advisory-lock primitive**
|
|
14
|
-
* (`single-story-sweep/sweep-lock.js`) rather than authoring a second
|
|
15
|
-
* lockfile: pid+mtime identity, stale takeover, heartbeat and owner-checked
|
|
16
|
-
* release are all already solved there, and a second implementation would be a
|
|
17
|
-
* second set of those bugs. `phases/post-land.js` is the other consumer.
|
|
18
|
-
*
|
|
19
|
-
* **Posture: best-effort, never load-bearing.** Failing to acquire — a
|
|
20
|
-
* contended wait that expires, an I/O error, an unresolvable lock home —
|
|
21
|
-
* falls through to spawning the suite anyway. The lock is a collision damper,
|
|
22
|
-
* not mutual exclusion; turning it load-bearing would let a stale lockfile
|
|
23
|
-
* fail a delivery, which is strictly worse than the contention it prevents.
|
|
24
|
-
*
|
|
25
|
-
* **It covers only the spawn.** Callers acquire immediately around the child
|
|
26
|
-
* process, never around the freshness/digest checks that precede it, so a
|
|
27
|
-
* capture that is already credited never waits.
|
|
6
|
+
* Best-effort: any failure to acquire spawns anyway — a stale lockfile must
|
|
7
|
+
* never fail a delivery. Only close opts in to defer on an expired wait
|
|
8
|
+
* ({@link LOCK_WAIT_EXPIRED_EXIT_CODE}). Waits are async so the holder's
|
|
9
|
+
* heartbeat and signal release keep working, and FIFO via
|
|
10
|
+
* `full-suite-queue.js`. The lock covers only the spawn, never the
|
|
11
|
+
* freshness checks before it.
|
|
28
12
|
*/
|
|
29
13
|
import fs from 'node:fs';
|
|
30
14
|
import path from 'node:path';
|
|
31
|
-
import { Worker } from 'node:worker_threads';
|
|
32
15
|
|
|
33
16
|
import { mainCheckoutRoot } from './config/temp-paths.js';
|
|
34
|
-
import {
|
|
35
|
-
|
|
36
|
-
acquireSweepLock,
|
|
37
|
-
readLockHolderPid,
|
|
38
|
-
refreshLockSync,
|
|
39
|
-
} from './single-story-sweep/sweep-lock.js';
|
|
17
|
+
import { isFirstInLine, waitInLine } from './full-suite-queue.js';
|
|
18
|
+
import { acquireSweepLock } from './single-story-sweep/sweep-lock.js';
|
|
40
19
|
|
|
41
20
|
/** Environment escape hatch: set to `0`/`false`/`off`/`no` to disable. */
|
|
42
21
|
export const FULL_SUITE_LOCK_ENV = 'MANDREL_FULL_SUITE_LOCK';
|
|
43
22
|
|
|
44
|
-
/**
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
23
|
+
/** Close sets it to `defer` on its gate children; nothing else does. */
|
|
24
|
+
export const FULL_SUITE_LOCK_EXPIRY_ENV = 'MANDREL_FULL_SUITE_LOCK_ON_EXPIRY';
|
|
25
|
+
|
|
26
|
+
/** `EX_TEMPFAIL`. */
|
|
27
|
+
export const LOCK_WAIT_EXPIRED_EXIT_CODE = 75;
|
|
28
|
+
|
|
29
|
+
/** Under the main checkout's `.git`, so every worktree shares one file. */
|
|
50
30
|
const FULL_SUITE_LOCK_FILENAME = 'mandrel-full-suite.lock';
|
|
51
31
|
|
|
52
|
-
/**
|
|
53
|
-
const
|
|
32
|
+
/** Well under close's ten-minute foreground ceiling. */
|
|
33
|
+
const DEFAULT_WAIT_MS = 300_000;
|
|
54
34
|
|
|
55
|
-
/**
|
|
56
|
-
const
|
|
35
|
+
/** Never above the wait budget. */
|
|
36
|
+
const DEFAULT_STALE_MS = 240_000;
|
|
57
37
|
|
|
58
|
-
/** Poll interval while waiting. */
|
|
59
38
|
const DEFAULT_POLL_MS = 2_000;
|
|
60
39
|
|
|
61
|
-
/**
|
|
62
|
-
|
|
63
|
-
* window, matching the sweep primitive's own divisor: two consecutive missed
|
|
64
|
-
* beats still leave a live holder looking live.
|
|
65
|
-
*/
|
|
66
|
-
const DEFAULT_SPAWN_HEARTBEAT_MS = DEFAULT_STALE_MS / 3;
|
|
40
|
+
/** More than one poll under 30 s, so jitter never stretches a gap past 30 s. */
|
|
41
|
+
const DEFAULT_REPORT_MS = 25_000;
|
|
67
42
|
|
|
68
43
|
const FALSEY = /^(0|false|off|no)$/i;
|
|
69
44
|
|
|
45
|
+
const LOCK_TAG = '[full-suite-lock]';
|
|
46
|
+
|
|
47
|
+
const LOCK_DEFAULTS = Object.freeze({
|
|
48
|
+
enabled: true,
|
|
49
|
+
log: () => {},
|
|
50
|
+
waitMs: DEFAULT_WAIT_MS,
|
|
51
|
+
pollMs: DEFAULT_POLL_MS,
|
|
52
|
+
staleMs: DEFAULT_STALE_MS,
|
|
53
|
+
reportMs: DEFAULT_REPORT_MS,
|
|
54
|
+
fsImpl: fs,
|
|
55
|
+
nowFn: Date.now,
|
|
56
|
+
sleepFn: (ms) => defaultSleep(ms),
|
|
57
|
+
acquireOnceFn: (opts) => acquireSweepLock(opts),
|
|
58
|
+
});
|
|
59
|
+
|
|
70
60
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* The environment wins over config so an operator can disable it for one
|
|
74
|
-
* invocation without editing `.agentrc.json`. Both hatches are one-way: they
|
|
75
|
-
* only ever turn the lock **off**, because an operator disabling a
|
|
76
|
-
* best-effort damper is always safe while forcing it on is not.
|
|
61
|
+
* Both hatches (env, then config) can only turn the lock off.
|
|
77
62
|
*
|
|
78
63
|
* @param {{ config?: object, env?: Record<string, string|undefined> }} [opts]
|
|
79
64
|
* @returns {boolean}
|
|
@@ -85,11 +70,8 @@ export function isFullSuiteLockEnabled({ config, env = process.env } = {}) {
|
|
|
85
70
|
}
|
|
86
71
|
|
|
87
72
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* repo, git unavailable). A `null` disables the lock for that call rather
|
|
91
|
-
* than inventing a cwd-local path that would never actually collide with the
|
|
92
|
-
* sibling it is meant to serialize against.
|
|
73
|
+
* `null` (no lock) when the root is unresolvable — a cwd-local path would
|
|
74
|
+
* never collide with its sibling.
|
|
93
75
|
*
|
|
94
76
|
* @param {{ cwd: string, mainCheckoutRootFn?: typeof mainCheckoutRoot }} opts
|
|
95
77
|
* @returns {string|null}
|
|
@@ -105,238 +87,60 @@ export function resolveFullSuiteLockPath({
|
|
|
105
87
|
}
|
|
106
88
|
|
|
107
89
|
/**
|
|
108
|
-
*
|
|
109
|
-
* multi-minute wait from reading as a hang — it is the difference between
|
|
110
|
-
* "nothing is happening" and "pid 4711 is running the suite; mine is next".
|
|
90
|
+
* Uncontended fast path, skipped when waiters are queued (no overtaking).
|
|
111
91
|
*
|
|
112
|
-
* @
|
|
113
|
-
*
|
|
114
|
-
* @param {object} fsImpl
|
|
92
|
+
* @returns {{ lock: object|null, lockPath: string|null }} null `lock` with a
|
|
93
|
+
* `lockPath` means wait; both null means no lock.
|
|
115
94
|
*/
|
|
116
|
-
function
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Shared preamble for both wrappers: decide whether to lock at all, take the
|
|
125
|
-
* uncontended fast path, and emit the wait line when a wait is about to
|
|
126
|
-
* happen.
|
|
127
|
-
*
|
|
128
|
-
* @returns {{ lock: object|null, lockPath: string|null }} `lock` is a held
|
|
129
|
-
* lock when the fast path won, `null` when the caller must wait (or when
|
|
130
|
-
* locking is off, in which case `lockPath` is `null` too).
|
|
131
|
-
*/
|
|
132
|
-
function beginLock({
|
|
133
|
-
cwd,
|
|
134
|
-
enabled,
|
|
135
|
-
log,
|
|
136
|
-
staleMs,
|
|
137
|
-
fsImpl,
|
|
138
|
-
acquireOnceFn,
|
|
139
|
-
lockPath: explicitLockPath,
|
|
140
|
-
}) {
|
|
141
|
-
if (!enabled) return { lock: null, lockPath: null };
|
|
142
|
-
const lockPath = explicitLockPath ?? resolveFullSuiteLockPath({ cwd });
|
|
143
|
-
if (lockPath === null) return { lock: null, lockPath: null };
|
|
95
|
+
function beginLock({ staleMs, fsImpl, acquireOnceFn, ...where }) {
|
|
96
|
+
const resolved = lockHome(where);
|
|
97
|
+
if (resolved === null) return NO_LOCK;
|
|
98
|
+
if (!isFirstInLine({ lockPath: resolved, ticket: null, staleMs, fsImpl })) {
|
|
99
|
+
return { lock: null, lockPath: resolved };
|
|
100
|
+
}
|
|
144
101
|
const first = acquireOnceFn({
|
|
145
|
-
lockPath,
|
|
102
|
+
lockPath: resolved,
|
|
146
103
|
timeoutMs: staleMs,
|
|
147
104
|
fsImpl,
|
|
148
105
|
});
|
|
149
|
-
if (first.acquired) return { lock: first, lockPath };
|
|
106
|
+
if (first.acquired) return { lock: first, lockPath: resolved };
|
|
150
107
|
// A hard I/O error will not resolve by waiting — proceed unserialized.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* The heartbeat body, run on a worker thread (Story #5278).
|
|
158
|
-
*
|
|
159
|
-
* Sleeps in `Atomics.wait` slices and refreshes the lockfile's mtime between
|
|
160
|
-
* them, stopping the moment the main thread flips the shared stop flag (and
|
|
161
|
-
* `Atomics.notify`s it), the lockfile stops being ours, or any I/O fails.
|
|
162
|
-
* Ownership is re-read from the file's first line on every beat for the same
|
|
163
|
-
* reason the in-process heartbeat does it: after a steal the file belongs to
|
|
164
|
-
* someone else, and bumping its mtime would keep *their* lock alive on our
|
|
165
|
-
* behalf.
|
|
166
|
-
*
|
|
167
|
-
* Inline source rather than a module of its own: it is nine lines of loop
|
|
168
|
-
* whose whole meaning is the lock it refreshes, and a separate worker entry
|
|
169
|
-
* point would be a second file that no reader of this one can see.
|
|
170
|
-
*/
|
|
171
|
-
const HEARTBEAT_WORKER_SOURCE = `
|
|
172
|
-
const fs = require('node:fs');
|
|
173
|
-
const { workerData } = require('node:worker_threads');
|
|
174
|
-
const { lockPath, ownerId, intervalMs, stopBuffer } = workerData;
|
|
175
|
-
const stop = new Int32Array(stopBuffer);
|
|
176
|
-
while (Atomics.load(stop, 0) === 0) {
|
|
177
|
-
Atomics.wait(stop, 0, 0, intervalMs);
|
|
178
|
-
if (Atomics.load(stop, 0) !== 0) break;
|
|
179
|
-
try {
|
|
180
|
-
if (String(fs.readFileSync(lockPath, 'utf8')).split('\\n', 1)[0] !== ownerId) break;
|
|
181
|
-
const now = new Date();
|
|
182
|
-
fs.utimesSync(lockPath, now, now);
|
|
183
|
-
} catch {
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
108
|
+
return first.reason === 'error'
|
|
109
|
+
? NO_LOCK
|
|
110
|
+
: { lock: null, lockPath: resolved };
|
|
186
111
|
}
|
|
187
|
-
`;
|
|
188
112
|
|
|
189
|
-
|
|
190
|
-
* Keep a held lock's mtime advancing while this thread is blocked inside a
|
|
191
|
-
* synchronous spawn (Story #5278).
|
|
192
|
-
*
|
|
193
|
-
* The primitive's own heartbeat is a `setInterval`, so it only fires when the
|
|
194
|
-
* holder's event loop gets a turn. `runCapture` spawns the suite with
|
|
195
|
-
* `spawnSync`: the loop stops turning for the entire run, the mtime freezes
|
|
196
|
-
* at acquisition time, and a sibling reading that mtime concludes — correctly,
|
|
197
|
-
* on the evidence available to it — that the holder died, breaks the lock, and
|
|
198
|
-
* starts a second full suite beside the first. That is the exact collision
|
|
199
|
-
* this lock exists to prevent, and it fires most reliably on the slowest
|
|
200
|
-
* suites, where it costs the most.
|
|
201
|
-
*
|
|
202
|
-
* A worker thread has its own event loop, unaffected by the main thread's
|
|
203
|
-
* blocking spawn, so it is the only place a refresh can happen at all here.
|
|
204
|
-
*
|
|
205
|
-
* **Best-effort, like everything else on this path.** A worker that cannot
|
|
206
|
-
* start (a runtime with threads disabled, a resource limit) leaves the
|
|
207
|
-
* pre-#5278 behaviour exactly as it was; it never throws and never delays the
|
|
208
|
-
* spawn it guards.
|
|
209
|
-
*
|
|
210
|
-
* @param {{ lockPath: string|null, ownerId?: string, heartbeatMs: number }} opts
|
|
211
|
-
* @returns {() => void} `stop()` — idempotent, safe when no worker started.
|
|
212
|
-
*/
|
|
213
|
-
function startSpawnHeartbeat({ lockPath, ownerId, heartbeatMs }) {
|
|
214
|
-
if (!(lockPath && typeof ownerId === 'string' && heartbeatMs > 0)) {
|
|
215
|
-
return () => {};
|
|
216
|
-
}
|
|
217
|
-
let worker = null;
|
|
218
|
-
let stop = null;
|
|
219
|
-
try {
|
|
220
|
-
const stopBuffer = new SharedArrayBuffer(4);
|
|
221
|
-
stop = new Int32Array(stopBuffer);
|
|
222
|
-
worker = new Worker(HEARTBEAT_WORKER_SOURCE, {
|
|
223
|
-
eval: true,
|
|
224
|
-
workerData: { lockPath, ownerId, intervalMs: heartbeatMs, stopBuffer },
|
|
225
|
-
});
|
|
226
|
-
// A heartbeat must never be the reason the process stays alive.
|
|
227
|
-
worker.unref();
|
|
228
|
-
worker.on('error', () => {});
|
|
229
|
-
} catch {
|
|
230
|
-
return () => {};
|
|
231
|
-
}
|
|
232
|
-
let stopped = false;
|
|
233
|
-
return () => {
|
|
234
|
-
if (stopped) return;
|
|
235
|
-
stopped = true;
|
|
236
|
-
try {
|
|
237
|
-
Atomics.store(stop, 0, 1);
|
|
238
|
-
Atomics.notify(stop, 0);
|
|
239
|
-
worker.terminate();
|
|
240
|
-
} catch {
|
|
241
|
-
// Already gone.
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* Start the spawn heartbeat without ever letting it become a reason the suite
|
|
248
|
-
* does not run.
|
|
249
|
-
*
|
|
250
|
-
* The whole module's posture is that a lock defect may slow a suite down and
|
|
251
|
-
* may never skip one, and a heartbeat is the least load-bearing thing on the
|
|
252
|
-
* path — it exists only so a *rival* reads the mtime correctly. So a starter
|
|
253
|
-
* that throws (an environment without worker threads, a resource limit, an
|
|
254
|
-
* injected seam) resolves to "no heartbeat", never to a failed close.
|
|
255
|
-
*
|
|
256
|
-
* @param {Function} startFn
|
|
257
|
-
* @param {{ lockPath: string|null, ownerId?: string, heartbeatMs: number }} opts
|
|
258
|
-
* @returns {() => void}
|
|
259
|
-
*/
|
|
260
|
-
function safeStartHeartbeat(startFn, opts) {
|
|
261
|
-
try {
|
|
262
|
-
return startFn(opts) ?? (() => {});
|
|
263
|
-
} catch {
|
|
264
|
-
return () => {};
|
|
265
|
-
}
|
|
266
|
-
}
|
|
113
|
+
const NO_LOCK = Object.freeze({ lock: null, lockPath: null });
|
|
267
114
|
|
|
268
115
|
/**
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
* A lock acquired at the end of a twenty-minute wait was created — or last
|
|
273
|
-
* heartbeat-refreshed — long before the spawn it is about to guard, and the
|
|
274
|
-
* synchronous caller's event loop is about to stop turning for the whole
|
|
275
|
-
* duration of that spawn. Refreshing here is the last chance to put a current
|
|
276
|
-
* mtime on the file.
|
|
277
|
-
*
|
|
278
|
-
* Routed through the primitive's own {@link refreshLockSync} rather than the
|
|
279
|
-
* holder's `refresh()` so an injected `acquireOnceFn` seam that returns a
|
|
280
|
-
* bare `{ acquired, release }` is refreshed identically to the real one.
|
|
281
|
-
*
|
|
282
|
-
* @param {{ ownerId?: string }|null} held
|
|
283
|
-
* @param {string|null} lockPath
|
|
284
|
-
* @param {object} fsImpl
|
|
116
|
+
* @param {{ enabled: boolean, cwd: string, lockPath?: string }} where
|
|
117
|
+
* @returns {string|null}
|
|
285
118
|
*/
|
|
286
|
-
function
|
|
287
|
-
if (!
|
|
288
|
-
|
|
119
|
+
function lockHome({ enabled, cwd, lockPath }) {
|
|
120
|
+
if (!enabled) return null;
|
|
121
|
+
return lockPath ?? resolveFullSuiteLockPath({ cwd });
|
|
289
122
|
}
|
|
290
123
|
|
|
291
124
|
/**
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
* Waiting for the lock is waiting for *someone else's* full suite against
|
|
295
|
-
* this same checkout. By the time it finishes, the thing this caller was
|
|
296
|
-
* about to spawn the suite to establish may already be true — the holder
|
|
297
|
-
* deposited the stamp, or a sibling recorded the evidence. Spawning anyway
|
|
298
|
-
* pays for a whole suite to re-derive a fact that is already on disk, which
|
|
299
|
-
* is exactly the cost the lock exists to avoid.
|
|
300
|
-
*
|
|
301
|
-
* Consulted **only after a real wait**: an uncontended caller's freshness
|
|
302
|
-
* probe ran moments ago and nothing has happened since, so re-running it
|
|
303
|
-
* would be pure overhead on the hot path.
|
|
125
|
+
* Consulted only after a real wait: the suite we waited behind may already
|
|
126
|
+
* have established what we were about to spawn for.
|
|
304
127
|
*
|
|
305
128
|
* @template T
|
|
306
|
-
* @param {(() => T|undefined)|undefined}
|
|
307
|
-
* @param {boolean}
|
|
129
|
+
* @param {(() => T|undefined)|undefined} probe
|
|
130
|
+
* @param {boolean} applies
|
|
308
131
|
* @returns {{ satisfied: boolean, value?: T }}
|
|
309
132
|
*/
|
|
310
|
-
function
|
|
311
|
-
if (!(
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
const value = skipIfSatisfied();
|
|
133
|
+
function consult(probe, applies) {
|
|
134
|
+
if (!(applies && typeof probe === 'function')) return { satisfied: false };
|
|
135
|
+
const value = probe();
|
|
315
136
|
return value === undefined
|
|
316
137
|
? { satisfied: false }
|
|
317
138
|
: { satisfied: true, value };
|
|
318
139
|
}
|
|
319
140
|
|
|
320
141
|
/**
|
|
321
|
-
*
|
|
322
|
-
*
|
|
323
|
-
* no event loop to yield to; `Atomics.wait` on a throwaway buffer is the
|
|
324
|
-
* sanctioned way to sleep on that stack.
|
|
325
|
-
*
|
|
326
|
-
* @param {number} ms
|
|
327
|
-
*/
|
|
328
|
-
function sleepSync(ms) {
|
|
329
|
-
if (!(ms > 0)) return;
|
|
330
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Run `spawn` with the full-suite lock held, from a **synchronous** caller.
|
|
335
|
-
*
|
|
336
|
-
* Never throws on the lock's account and always runs `spawn` exactly once:
|
|
337
|
-
* every lock outcome — disabled, acquired, contended past the wait budget,
|
|
338
|
-
* I/O error — ends in the same call, so a lock defect can slow a suite down
|
|
339
|
-
* but can never skip or duplicate it.
|
|
142
|
+
* Never throws on the lock's account and spawns at most once — a lock defect
|
|
143
|
+
* can slow a suite, never duplicate it.
|
|
340
144
|
*
|
|
341
145
|
* @template T
|
|
342
146
|
* @param {{
|
|
@@ -346,192 +150,119 @@ function sleepSync(ms) {
|
|
|
346
150
|
* waitMs?: number,
|
|
347
151
|
* pollMs?: number,
|
|
348
152
|
* staleMs?: number,
|
|
153
|
+
* reportMs?: number,
|
|
349
154
|
* fsImpl?: object,
|
|
350
155
|
* nowFn?: () => number,
|
|
351
|
-
* sleepFn?: (ms: number) => void
|
|
156
|
+
* sleepFn?: (ms: number) => Promise<void>,
|
|
352
157
|
* acquireOnceFn?: typeof acquireSweepLock,
|
|
353
158
|
* lockPath?: string,
|
|
354
159
|
* skipIfSatisfied?: () => T|undefined,
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
358
|
-
*
|
|
359
|
-
* already true, and a non-`undefined` return is returned in the spawn's
|
|
360
|
-
* place. Never consulted on the uncontended path.
|
|
361
|
-
* @param {() => T} spawn
|
|
362
|
-
* @returns {T}
|
|
160
|
+
* onWaitExpired?: () => T|undefined,
|
|
161
|
+
* }} opts A non-`undefined` return from either hook stands in for the spawn.
|
|
162
|
+
* @param {() => Promise<T>} spawn
|
|
163
|
+
* @returns {Promise<T>}
|
|
363
164
|
*/
|
|
364
|
-
export function
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
staleMs = DEFAULT_STALE_MS,
|
|
372
|
-
fsImpl = fs,
|
|
373
|
-
nowFn = Date.now,
|
|
374
|
-
sleepFn = sleepSync,
|
|
375
|
-
acquireOnceFn = acquireSweepLock,
|
|
376
|
-
lockPath: explicitLockPath,
|
|
377
|
-
skipIfSatisfied,
|
|
378
|
-
spawnHeartbeatMs = DEFAULT_SPAWN_HEARTBEAT_MS,
|
|
379
|
-
startSpawnHeartbeatFn = startSpawnHeartbeat,
|
|
380
|
-
},
|
|
381
|
-
spawn,
|
|
382
|
-
) {
|
|
383
|
-
const { lock, lockPath } = beginLock({
|
|
384
|
-
cwd,
|
|
385
|
-
enabled,
|
|
386
|
-
log,
|
|
387
|
-
staleMs,
|
|
388
|
-
fsImpl,
|
|
389
|
-
acquireOnceFn,
|
|
390
|
-
lockPath: explicitLockPath,
|
|
391
|
-
});
|
|
392
|
-
let held = lock;
|
|
393
|
-
let waited = false;
|
|
394
|
-
if (held === null && lockPath !== null) {
|
|
395
|
-
const deadline = nowFn() + Math.max(0, waitMs);
|
|
396
|
-
for (;;) {
|
|
397
|
-
if (nowFn() >= deadline) break;
|
|
398
|
-
waited = true;
|
|
399
|
-
sleepFn(Math.max(0, pollMs));
|
|
400
|
-
const attempt = acquireOnceFn({ lockPath, timeoutMs: staleMs, fsImpl });
|
|
401
|
-
if (attempt.acquired) {
|
|
402
|
-
held = attempt;
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
if (attempt.reason === 'error') break;
|
|
406
|
-
}
|
|
407
|
-
}
|
|
165
|
+
export async function withFullSuiteLockAsync(options, spawn) {
|
|
166
|
+
const opts = withDefaults(options);
|
|
167
|
+
const { lock, lockPath } = beginLock(opts);
|
|
168
|
+
const wait =
|
|
169
|
+
lock === null && lockPath !== null
|
|
170
|
+
? await waitInLine({ ...opts, lockPath, expiryNote: expiryNote(opts) })
|
|
171
|
+
: { held: lock, expired: false, waited: false };
|
|
408
172
|
try {
|
|
409
|
-
|
|
410
|
-
if (probe.satisfied) {
|
|
411
|
-
log(
|
|
412
|
-
'[full-suite-lock] ⏭ the run we waited for already covered this tree — skipping the spawn.',
|
|
413
|
-
);
|
|
414
|
-
return probe.value;
|
|
415
|
-
}
|
|
416
|
-
refreshBeforeSpawn(held, lockPath, fsImpl);
|
|
417
|
-
// The main thread is about to stop turning for the whole spawn, so the
|
|
418
|
-
// holder's own interval heartbeat cannot fire; this one runs off-thread.
|
|
419
|
-
const stopHeartbeat = safeStartHeartbeat(startSpawnHeartbeatFn, {
|
|
420
|
-
lockPath: held?.acquired ? lockPath : null,
|
|
421
|
-
ownerId: held?.ownerId,
|
|
422
|
-
heartbeatMs: spawnHeartbeatMs,
|
|
423
|
-
});
|
|
424
|
-
try {
|
|
425
|
-
return spawn();
|
|
426
|
-
} finally {
|
|
427
|
-
stopHeartbeat();
|
|
428
|
-
}
|
|
173
|
+
return await spawnOrStandIn(opts, wait, spawn);
|
|
429
174
|
} finally {
|
|
430
|
-
if (held?.acquired) held.release();
|
|
175
|
+
if (wait.held?.acquired) wait.held.release();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function withDefaults(options) {
|
|
180
|
+
const opts = { ...LOCK_DEFAULTS };
|
|
181
|
+
for (const [key, value] of Object.entries(options)) {
|
|
182
|
+
if (value !== undefined) opts[key] = value;
|
|
183
|
+
}
|
|
184
|
+
return opts;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function expiryNote({ onWaitExpired }) {
|
|
188
|
+
return typeof onWaitExpired === 'function'
|
|
189
|
+
? 'not spawning; the caller reports the wait instead'
|
|
190
|
+
: 'spawning anyway';
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function spawnOrStandIn(opts, wait, spawn) {
|
|
194
|
+
const probe = consult(opts.skipIfSatisfied, wait.waited);
|
|
195
|
+
if (probe.satisfied) {
|
|
196
|
+
opts.log(
|
|
197
|
+
`${LOCK_TAG} ⏭ the run we waited for already covered this tree — skipping the spawn.`,
|
|
198
|
+
);
|
|
199
|
+
return probe.value;
|
|
431
200
|
}
|
|
201
|
+
const deferred = consult(opts.onWaitExpired, wait.expired);
|
|
202
|
+
return deferred.satisfied ? deferred.value : await spawn();
|
|
432
203
|
}
|
|
433
204
|
|
|
434
205
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
206
|
+
* @param {number} ms
|
|
207
|
+
* @returns {Promise<void>}
|
|
208
|
+
*/
|
|
209
|
+
function defaultSleep(ms) {
|
|
210
|
+
return new Promise((resolve) => {
|
|
211
|
+
setTimeout(resolve, ms);
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Serialize a capture runner's spawn. Wrapped at the one call site below
|
|
217
|
+
* every skip/freshness decision, so a credited capture never waits.
|
|
445
218
|
*
|
|
446
|
-
* @param {Function} runCaptureFn
|
|
447
|
-
* @param {object} [config]
|
|
448
|
-
* @
|
|
219
|
+
* @param {Function} runCaptureFn
|
|
220
|
+
* @param {object} [config]
|
|
221
|
+
* @param {Record<string, string|undefined>} [env]
|
|
222
|
+
* @param {object} [lockOptions] Test seam only.
|
|
223
|
+
* @returns {(opts?: object) => Promise<number>}
|
|
449
224
|
*/
|
|
450
|
-
export function lockedCapture(
|
|
451
|
-
|
|
225
|
+
export function lockedCapture(
|
|
226
|
+
runCaptureFn,
|
|
227
|
+
config,
|
|
228
|
+
env = process.env,
|
|
229
|
+
lockOptions = {},
|
|
230
|
+
) {
|
|
231
|
+
const policy = {
|
|
232
|
+
enabled: isFullSuiteLockEnabled({ config, env }),
|
|
233
|
+
onWaitExpired: deferredCaptureExit(env),
|
|
234
|
+
...lockOptions,
|
|
235
|
+
};
|
|
452
236
|
return (captureOpts = {}) =>
|
|
453
|
-
|
|
237
|
+
withFullSuiteLockAsync(
|
|
454
238
|
{
|
|
239
|
+
...policy,
|
|
455
240
|
cwd: captureOpts.cwd,
|
|
456
241
|
log: captureOpts.log,
|
|
457
|
-
|
|
458
|
-
// Story #5278 — the capture path supplies its own freshness re-probe
|
|
459
|
-
// per call, because only it knows which scope ('full' /
|
|
460
|
-
// 'incremental') the stamp has to satisfy. A `true` means the suite
|
|
461
|
-
// we queued behind already stamped this tree, so this caller reports
|
|
462
|
-
// success (exit 0) without spawning a second one.
|
|
463
|
-
skipIfSatisfied:
|
|
464
|
-
typeof captureOpts.recheckFresh === 'function'
|
|
465
|
-
? () => (captureOpts.recheckFresh() ? 0 : undefined)
|
|
466
|
-
: undefined,
|
|
242
|
+
skipIfSatisfied: freshnessProbe(captureOpts),
|
|
467
243
|
},
|
|
468
244
|
() => runCaptureFn(captureOpts),
|
|
469
245
|
);
|
|
470
246
|
}
|
|
471
247
|
|
|
472
248
|
/**
|
|
473
|
-
*
|
|
474
|
-
*
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
249
|
+
* @param {Record<string, string|undefined>} env
|
|
250
|
+
* @returns {(() => number)|undefined}
|
|
251
|
+
*/
|
|
252
|
+
function deferredCaptureExit(env) {
|
|
253
|
+
return env[FULL_SUITE_LOCK_EXPIRY_ENV] === 'defer'
|
|
254
|
+
? () => LOCK_WAIT_EXPIRED_EXIT_CODE
|
|
255
|
+
: undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* A fresh recheck means exit 0 without spawning.
|
|
479
260
|
*
|
|
480
|
-
* @
|
|
481
|
-
* @
|
|
482
|
-
* acquireWithWaitFn?: typeof acquireLockWithWait,
|
|
483
|
-
* }} opts `skipIfSatisfied` behaves exactly as in the sync wrapper.
|
|
484
|
-
* @param {() => Promise<T>} spawn
|
|
485
|
-
* @returns {Promise<T>}
|
|
261
|
+
* @param {{ recheckFresh?: () => boolean }} captureOpts
|
|
262
|
+
* @returns {(() => number|undefined)|undefined}
|
|
486
263
|
*/
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
log = () => {},
|
|
492
|
-
waitMs = DEFAULT_WAIT_MS,
|
|
493
|
-
pollMs = DEFAULT_POLL_MS,
|
|
494
|
-
staleMs = DEFAULT_STALE_MS,
|
|
495
|
-
fsImpl = fs,
|
|
496
|
-
acquireOnceFn = acquireSweepLock,
|
|
497
|
-
acquireWithWaitFn = acquireLockWithWait,
|
|
498
|
-
lockPath: explicitLockPath,
|
|
499
|
-
skipIfSatisfied,
|
|
500
|
-
},
|
|
501
|
-
spawn,
|
|
502
|
-
) {
|
|
503
|
-
const { lock, lockPath } = beginLock({
|
|
504
|
-
cwd,
|
|
505
|
-
enabled,
|
|
506
|
-
log,
|
|
507
|
-
staleMs,
|
|
508
|
-
fsImpl,
|
|
509
|
-
acquireOnceFn,
|
|
510
|
-
lockPath: explicitLockPath,
|
|
511
|
-
});
|
|
512
|
-
let held = lock;
|
|
513
|
-
let waited = false;
|
|
514
|
-
if (held === null && lockPath !== null) {
|
|
515
|
-
waited = true;
|
|
516
|
-
const attempt = await acquireWithWaitFn({
|
|
517
|
-
lockPath,
|
|
518
|
-
waitMs,
|
|
519
|
-
pollMs,
|
|
520
|
-
timeoutMs: staleMs,
|
|
521
|
-
fsImpl,
|
|
522
|
-
});
|
|
523
|
-
if (attempt.acquired) held = attempt;
|
|
524
|
-
}
|
|
525
|
-
try {
|
|
526
|
-
const probe = probeAlreadySatisfied(skipIfSatisfied, waited);
|
|
527
|
-
if (probe.satisfied) {
|
|
528
|
-
log(
|
|
529
|
-
'[full-suite-lock] ⏭ the run we waited for already covered this tree — skipping the spawn.',
|
|
530
|
-
);
|
|
531
|
-
return probe.value;
|
|
532
|
-
}
|
|
533
|
-
return await spawn();
|
|
534
|
-
} finally {
|
|
535
|
-
if (held?.acquired) held.release();
|
|
536
|
-
}
|
|
264
|
+
function freshnessProbe({ recheckFresh }) {
|
|
265
|
+
return typeof recheckFresh === 'function'
|
|
266
|
+
? () => (recheckFresh() ? 0 : undefined)
|
|
267
|
+
: undefined;
|
|
537
268
|
}
|