mandrel 1.58.0 → 1.60.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 +100 -98
- package/.agents/docs/SDLC.md +140 -141
- package/.agents/docs/configuration.md +16 -16
- package/.agents/docs/workflows.md +7 -8
- package/.agents/instructions.md +12 -11
- package/.agents/personas/architect.md +1 -1
- package/.agents/personas/product.md +1 -1
- package/.agents/personas/project-manager.md +14 -14
- package/.agents/personas/technical-writer.md +1 -1
- package/.agents/rules/changelog-style.md +5 -5
- package/.agents/rules/git-conventions.md +3 -3
- package/.agents/schemas/agentrc.schema.json +3 -3
- package/.agents/schemas/audit-rules.json +20 -0
- package/.agents/schemas/dispatch-manifest.json +4 -4
- package/.agents/schemas/epic-spec.schema.json +15 -45
- package/.agents/schemas/lifecycle/README.md +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.dispatch.start.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +1 -1
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/README.md +1 -1
- package/.agents/scripts/acceptance-eval.js +21 -4
- package/.agents/scripts/acceptance-spec-reconciler.js +2 -2
- package/.agents/scripts/analyze-execution.js +2 -2
- package/.agents/scripts/assert-branch.js +1 -3
- package/.agents/scripts/audit-to-stories.js +1 -1
- package/.agents/scripts/bootstrap.js +1 -1
- package/.agents/scripts/check-arch-cycles.js +360 -0
- package/.agents/scripts/check-doc-links.js +2 -3
- package/.agents/scripts/coverage-capture.js +24 -3
- package/.agents/scripts/diagnose-friction.js +1 -1
- package/.agents/scripts/dispatcher.js +2 -2
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/epic-audit-prepare.js +3 -3
- package/.agents/scripts/epic-deliver-note-intervention.js +2 -2
- package/.agents/scripts/epic-deliver-preflight.js +11 -9
- package/.agents/scripts/epic-deliver-prepare.js +13 -5
- package/.agents/scripts/epic-execute-record-wave.js +5 -5
- package/.agents/scripts/epic-plan-healthcheck.js +6 -10
- package/.agents/scripts/epic-plan-spec-validate.js +1 -1
- package/.agents/scripts/epic-reconcile.js +11 -29
- package/.agents/scripts/evidence-gate.js +2 -2
- package/.agents/scripts/generate-workflows-doc.js +1 -1
- package/.agents/scripts/git-rebase-and-resolve.js +1 -1
- package/.agents/scripts/hierarchy-gate.js +40 -24
- package/.agents/scripts/lib/ITicketingProvider.js +1 -1
- package/.agents/scripts/lib/audit-suite/selector.js +1 -1
- package/.agents/scripts/lib/audit-to-stories/seed-epic-from-findings.js +2 -2
- package/.agents/scripts/lib/baseline-snapshot.js +7 -7
- package/.agents/scripts/lib/baselines/kinds/coverage.js +33 -149
- package/.agents/scripts/lib/baselines/kinds/duplication.js +27 -116
- package/.agents/scripts/lib/baselines/kinds/kind-factory.js +192 -0
- package/.agents/scripts/lib/baselines/kinds/lighthouse.js +34 -133
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +31 -124
- package/.agents/scripts/lib/baselines/kinds/mutation.js +25 -111
- package/.agents/scripts/lib/baselines/maintainability-baseline-io.js +59 -0
- package/.agents/scripts/lib/baselines/maintainability-baseline-save.js +37 -0
- package/.agents/scripts/lib/baselines/writer.js +1 -1
- package/.agents/scripts/lib/bdd-runner-detect.js +1 -1
- package/.agents/scripts/lib/bdd-scenario-scanner.js +3 -3
- package/.agents/scripts/lib/bootstrap/baselines-layout-migration.js +1 -1
- package/.agents/scripts/lib/bootstrap/branch-protection.js +1 -1
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +1 -1
- package/.agents/scripts/lib/bootstrap/commit-push.js +2 -2
- package/.agents/scripts/lib/close-validation/commands.js +188 -0
- package/.agents/scripts/lib/close-validation/gates.js +235 -0
- package/.agents/scripts/lib/close-validation/process.js +101 -0
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +1 -1
- package/.agents/scripts/lib/close-validation/runner.js +325 -0
- package/.agents/scripts/lib/close-validation/telemetry.js +70 -0
- package/.agents/scripts/lib/codebase-snapshot.js +1 -1
- package/.agents/scripts/lib/config/explain.js +1 -1
- package/.agents/scripts/lib/config/quality.js +6 -6
- package/.agents/scripts/lib/config/runners.js +2 -2
- package/.agents/scripts/lib/config/runtime.js +1 -1
- package/.agents/scripts/lib/config/temp-paths.js +2 -2
- package/.agents/scripts/lib/config-resolver.js +2 -5
- package/.agents/scripts/lib/config-settings-schema-delivery.js +2 -2
- package/.agents/scripts/lib/config-settings-schema-quality.js +1 -1
- package/.agents/scripts/lib/config-settings-schema.js +3 -3
- package/.agents/scripts/lib/coverage-capture.js +147 -4
- package/.agents/scripts/lib/cpu-pool.js +14 -0
- package/.agents/scripts/lib/crap-utils.js +6 -11
- package/.agents/scripts/lib/duplicate-search.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/capability.js +1 -1
- package/.agents/scripts/lib/dynamic-workflow/documentation-report-contract.js +87 -0
- package/.agents/scripts/lib/epic-plan-clarity.js +1 -1
- package/.agents/scripts/lib/epic-plan-ideation.js +1 -1
- package/.agents/scripts/lib/feedback-loop/memory-freshness.js +1 -1
- package/.agents/scripts/lib/feedback-loop/prior-feedback-fetcher.js +1 -1
- package/.agents/scripts/lib/findings/classify-finding.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +10 -10
- package/.agents/scripts/lib/git-utils.js +24 -22
- package/.agents/scripts/lib/label-constants.js +3 -4
- package/.agents/scripts/lib/label-taxonomy.js +3 -8
- package/.agents/scripts/lib/maintainability-engine.js +1 -1
- package/.agents/scripts/lib/maintainability-utils.js +4 -187
- package/.agents/scripts/lib/observability/perf-report-readers.js +32 -23
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +81 -7
- package/.agents/scripts/lib/orchestration/code-review.js +95 -82
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +8 -9
- package/.agents/scripts/lib/orchestration/dependency-analyzer.js +3 -3
- package/.agents/scripts/lib/orchestration/detectors-phase.js +2 -2
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +30 -38
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +14 -37
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +22 -22
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +7 -21
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +2 -2
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +206 -58
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +27 -3
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/prompts.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +28 -8
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +1 -1
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +3 -3
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +13 -41
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +7 -7
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +2 -3
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +2 -8
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +103 -0
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +22 -64
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +38 -76
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +4 -4
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +10 -10
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +8 -20
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +7 -15
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +72 -41
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +2 -4
- package/.agents/scripts/lib/orchestration/file-assumptions.js +6 -5
- package/.agents/scripts/lib/orchestration/finalize/close-planning-tickets.js +1 -1
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +2 -2
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +1 -1
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +144 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +3 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +8 -8
- package/.agents/scripts/lib/orchestration/manifest-builder.js +5 -5
- package/.agents/scripts/lib/orchestration/parked-follow-ons.js +2 -2
- package/.agents/scripts/lib/orchestration/plan-runner/plan-router.js +5 -5
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +3 -3
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +7 -7
- package/.agents/scripts/lib/orchestration/preflight-cache.js +36 -13
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +1 -1
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +2 -2
- package/.agents/scripts/lib/orchestration/retro-runner.js +3 -3
- package/.agents/scripts/lib/orchestration/review-depth.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +5 -60
- package/.agents/scripts/lib/orchestration/review-providers/native.js +7 -6
- package/.agents/scripts/lib/orchestration/review-providers/parse-findings.js +105 -0
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +7 -59
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +2 -4
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +32 -35
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +1 -2
- package/.agents/scripts/lib/orchestration/spec-freshness.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-renderer.js +36 -73
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +451 -503
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +8 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +47 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +2 -2
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +358 -54
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +3 -2
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +32 -5
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +5 -18
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +3 -3
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +33 -16
- package/.agents/scripts/lib/orchestration/story-reachability.js +47 -0
- package/.agents/scripts/lib/orchestration/task-body-validator.js +6 -6
- package/.agents/scripts/lib/orchestration/ticket-lease.js +1 -1
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +4 -35
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +1 -10
- package/.agents/scripts/lib/orchestration/ticket-validator.js +25 -70
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +44 -73
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +16 -7
- package/.agents/scripts/lib/orchestration/ticketing/state.js +53 -439
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +471 -0
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +3 -3
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +2 -8
- package/.agents/scripts/lib/plan-phase-cleanup.js +1 -1
- package/.agents/scripts/lib/preflight-runner.js +1 -1
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +4 -5
- package/.agents/scripts/lib/presentation/manifest-builder.js +28 -34
- package/.agents/scripts/lib/presentation/manifest-formatter.js +3 -4
- package/.agents/scripts/lib/presentation/manifest-helpers.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-procedures.js +4 -4
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +4 -23
- package/.agents/scripts/lib/presentation/manifest-renderer.js +1 -1
- package/.agents/scripts/lib/presentation/manifest-story-views.js +2 -11
- package/.agents/scripts/lib/project-root.js +17 -0
- package/.agents/scripts/lib/signals/schema.js +1 -1
- package/.agents/scripts/lib/spec/index.js +1 -1
- package/.agents/scripts/lib/spec/loader.js +2 -2
- package/.agents/scripts/lib/spec/state.js +7 -16
- package/.agents/scripts/lib/story-adjacency.js +76 -0
- package/.agents/scripts/lib/story-init/context-resolver.js +3 -3
- package/.agents/scripts/lib/story-init/state-transitioner.js +2 -2
- package/.agents/scripts/lib/story-init/task-graph-builder.js +7 -7
- package/.agents/scripts/lib/story-lifecycle.js +9 -9
- package/.agents/scripts/lib/story-plan.js +1 -1
- package/.agents/scripts/lib/templates/decomposer-prompts.js +59 -52
- package/.agents/scripts/lib/transpile.js +93 -0
- package/.agents/scripts/lib/wave-runner/tick.js +4 -153
- package/.agents/scripts/lib/workers/crap-worker.js +1 -1
- package/.agents/scripts/lib/workers/maintainability-report-worker.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/creation.js +20 -2
- package/.agents/scripts/lib/worktree/lifecycle/force-drain.js +90 -0
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +26 -8
- package/.agents/scripts/lib/worktree/node-modules-strategy.js +74 -0
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +1 -1
- package/.agents/scripts/lifecycle-emit.js +1 -1
- package/.agents/scripts/providers/github/board-add.js +1 -1
- package/.agents/scripts/providers/github/errors.js +1 -1
- package/.agents/scripts/providers/github/mappers.js +2 -2
- package/.agents/scripts/providers/github/tickets.js +114 -10
- package/.agents/scripts/resync-status-column.js +1 -1
- package/.agents/scripts/retro-run.js +2 -2
- package/.agents/scripts/run-lint.js +10 -1
- package/.agents/scripts/run-tests.js +24 -4
- package/.agents/scripts/single-story-init.js +1 -1
- package/.agents/scripts/stories-wave-tick.js +13 -10
- package/.agents/scripts/story-close.js +1 -1
- package/.agents/scripts/story-init.js +162 -26
- package/.agents/scripts/story-phase.js +5 -5
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/sync-branch-from-base.js +2 -2
- package/.agents/scripts/validate-docs-freshness.js +1 -1
- package/.agents/scripts/wave-tick.js +1 -1
- package/.agents/skills/core/analyze-execution/SKILL.md +2 -2
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +21 -26
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +23 -56
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +4 -4
- package/.agents/skills/core/hydrate-context/SKILL.md +2 -2
- package/.agents/skills/core/idea-refinement/SKILL.md +4 -4
- package/.agents/skills/core/knowledge-transfer/SKILL.md +2 -2
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +1 -1
- package/.agents/skills/core/scope-triage/SKILL.md +9 -10
- package/.agents/skills/core/using-agent-skills/SKILL.md +1 -1
- package/.agents/skills/skills.index.json +7 -7
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +1 -1
- package/.agents/templates/agent-protocol.md +2 -2
- package/.agents/workflows/agents-update.md +2 -2
- package/.agents/workflows/audit-architecture.md +2 -2
- package/.agents/workflows/audit-clean-code.md +2 -2
- package/.agents/workflows/audit-dependencies.md +1 -1
- package/.agents/workflows/audit-devops.md +1 -1
- package/.agents/workflows/audit-documentation.md +226 -0
- package/.agents/workflows/audit-lighthouse.md +1 -1
- package/.agents/workflows/audit-performance.md +2 -2
- package/.agents/workflows/audit-privacy.md +1 -1
- package/.agents/workflows/audit-quality.md +2 -2
- package/.agents/workflows/audit-security.md +2 -2
- package/.agents/workflows/audit-seo.md +1 -1
- package/.agents/workflows/audit-sre.md +1 -1
- package/.agents/workflows/audit-to-stories.md +10 -10
- package/.agents/workflows/audit-ux-ui.md +1 -1
- package/.agents/workflows/deliver.md +85 -0
- package/.agents/workflows/explain.md +3 -3
- package/.agents/workflows/git-merge-pr.md +1 -1
- package/.agents/workflows/git-pr-all.md +13 -10
- package/.agents/workflows/git-push.md +6 -3
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +1 -1
- package/.agents/workflows/helpers/code-review.md +5 -5
- package/.agents/workflows/{epic-deliver.md → helpers/deliver-epic.md} +59 -66
- package/.agents/workflows/{story-deliver.md → helpers/deliver-stories.md} +25 -25
- package/.agents/workflows/helpers/diagnose.md +1 -1
- package/.agents/workflows/helpers/epic-audit.md +6 -6
- package/.agents/workflows/helpers/epic-deliver-story.md +28 -39
- package/.agents/workflows/helpers/epic-plan-decompose.md +23 -23
- package/.agents/workflows/helpers/epic-plan-spec.md +6 -6
- package/.agents/workflows/helpers/epic-testing.md +3 -3
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/{epic-plan.md → helpers/plan-epic.md} +84 -84
- package/.agents/workflows/{story-plan.md → helpers/plan-story.md} +43 -43
- package/.agents/workflows/helpers/signals.md +1 -1
- package/.agents/workflows/helpers/single-story-deliver.md +12 -11
- package/.agents/workflows/helpers/worktree-lifecycle.md +18 -18
- package/.agents/workflows/onboard.md +21 -20
- package/.agents/workflows/plan.md +89 -0
- package/.agents/workflows/qa-explore.md +1 -1
- package/.agents/workflows/qa-run-harness.md +1 -1
- package/README.md +17 -20
- package/docs/CHANGELOG.md +1149 -0
- package/lib/cli/__tests__/update-changelog-surface.test.js +357 -0
- package/lib/cli/__tests__/update-reexec.test.js +513 -0
- package/lib/cli/init.js +338 -0
- package/lib/cli/update.js +413 -52
- package/package.json +3 -1
- package/.agents/scripts/lib/auto-refresh-baselines.js +0 -308
- package/.agents/scripts/lib/close-validation.js +0 -897
- package/.agents/scripts/lib/orchestration/cascade-grouping.js +0 -275
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter.js +0 -69
- package/.agents/scripts/lib/orchestration/reconciler.js +0 -137
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-scoped.js +0 -221
- package/.agents/scripts/lib/orchestration/story-close/format-autofix-shared.js +0 -123
- package/.agents/scripts/lib/task-utils.js +0 -26
- package/.agents/scripts/story-deliver-prepare.js +0 -267
|
@@ -1,308 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auto-refresh-baselines.js — pure delta-cap evaluator for the bounded
|
|
3
|
-
* baseline auto-refresh at story-close (Story #1398, Epic #1386).
|
|
4
|
-
*
|
|
5
|
-
* Story #1891 (Epic #1786) note: this evaluator is purely functional and
|
|
6
|
-
* has never written a baseline JSON file directly — every regenerated row
|
|
7
|
-
* funnels through the per-kind refresh entry points
|
|
8
|
-
* (`update-crap-baseline.js`, `update-maintainability-baseline.js`,
|
|
9
|
-
* `lib/coverage-baseline.js`'s `writeBaseline`), which now write through
|
|
10
|
-
* the shared `lib/baselines/writer.js`. The migration leaves this
|
|
11
|
-
* evaluator unchanged structurally; it remains the single seam for the
|
|
12
|
-
* delta-cap decision.
|
|
13
|
-
*
|
|
14
|
-
* After the close-validation chain has passed (`runPreMergeGatesWithAttribution`
|
|
15
|
-
* already auto-refreshed any attributable drift), `story-close` regenerates
|
|
16
|
-
* the baseline rows scoped to the Story diff and asks this evaluator whether
|
|
17
|
-
* the regenerated rows are within bounded delta caps. If yes, `story-close`
|
|
18
|
-
* amends the regenerated rows into the close commit (no separate
|
|
19
|
-
* `baseline-refresh:` commit). If no, `story-close` refuses to amend, leaves
|
|
20
|
-
* the close commit untouched, and appends a `baseline-refresh-regression`
|
|
21
|
-
* friction signal naming the offending file(s)/method(s).
|
|
22
|
-
*
|
|
23
|
-
* The evaluator is a pure function — no I/O, no spawn, no provider calls.
|
|
24
|
-
* Inputs are fixtures the caller has already loaded; outputs are plain
|
|
25
|
-
* objects ready for the friction-signal renderer.
|
|
26
|
-
*
|
|
27
|
-
* Contract:
|
|
28
|
-
*
|
|
29
|
-
* evaluateAutoRefresh({ scoredRows, baseline, caps }) →
|
|
30
|
-
* {
|
|
31
|
-
* canAutoRefresh: boolean,
|
|
32
|
-
* miOverCap: Array<{ path, baseline, scored, delta }>,
|
|
33
|
-
* crapOverCap: Array<{ file, method, startLine, baseline, scored, delta }>,
|
|
34
|
-
* refusalReasons: string[],
|
|
35
|
-
* }
|
|
36
|
-
*
|
|
37
|
-
* - `scoredRows` carries the *just-regenerated* rows, partitioned by kind:
|
|
38
|
-
* { mi: Array<{ path, mi }>, crap: Array<{ file, method, startLine, crap }> }
|
|
39
|
-
* Either kind may be absent or empty — the evaluator treats a missing
|
|
40
|
-
* kind as "no rows of that kind to evaluate".
|
|
41
|
-
*
|
|
42
|
-
* - `baseline` carries the *previously committed* rows, in the same shape:
|
|
43
|
-
* { mi: Array<{ path, mi }>, crap: Array<{ file, method, startLine, crap }> }
|
|
44
|
-
* A scored row whose path/method has no matching baseline row is treated
|
|
45
|
-
* as "new" and evaluated against the cap with `baseline = null` — its
|
|
46
|
-
* delta is the scored value vs an absent prior, which by convention
|
|
47
|
-
* never breaches a cap (the row didn't exist before, there is no drop /
|
|
48
|
-
* jump to bound). New rows therefore never block auto-refresh.
|
|
49
|
-
*
|
|
50
|
-
* - `caps = { miDropCap: number, crapJumpCap: number }` carries the
|
|
51
|
-
* bounded delta thresholds. The defaults (`miDropCap: 1.5`,
|
|
52
|
-
* `crapJumpCap: 5`) live in `.agents/docs/agentrc-reference.json` under
|
|
53
|
-
* `delivery.quality.autoRefresh` (Story #1413). Callers always
|
|
54
|
-
* pass an explicit caps object; the evaluator does not default-fill.
|
|
55
|
-
*
|
|
56
|
-
* Cap semantics:
|
|
57
|
-
*
|
|
58
|
-
* - MI is "higher is better". A *drop* (baseline.mi − scored.mi) greater
|
|
59
|
-
* than `miDropCap` breaches the cap. Improvements (scored.mi ≥ baseline.mi
|
|
60
|
-
* or any negative drop) never breach.
|
|
61
|
-
*
|
|
62
|
-
* - CRAP is "lower is better". A *jump* (scored.crap − baseline.crap)
|
|
63
|
-
* greater than `crapJumpCap` breaches the cap. Improvements (scored.crap
|
|
64
|
-
* ≤ baseline.crap or any negative jump) never breach.
|
|
65
|
-
*
|
|
66
|
-
* - Equality at the cap (delta === cap) is *under* the cap — the cap is
|
|
67
|
-
* the maximum allowed delta, not the strict maximum. This matches the
|
|
68
|
-
* Tech Spec's "at or below" wording and the AC1 phrasing ("every scored
|
|
69
|
-
* row delta is at or below the configured caps").
|
|
70
|
-
*
|
|
71
|
-
* - Missing baseline rows (path/method new in the scored set) are recorded
|
|
72
|
-
* with `baseline: null` and a `delta` of 0 — they never push
|
|
73
|
-
* `canAutoRefresh` to `false`. The evaluator does not surface them in
|
|
74
|
-
* `miOverCap` / `crapOverCap`. The friction renderer therefore never
|
|
75
|
-
* names a file that was newly introduced by the Story.
|
|
76
|
-
*
|
|
77
|
-
* The renderer is pure so callers can unit-test the cap math against fixed
|
|
78
|
-
* inputs without spawning git or reading the filesystem. Story-close wires
|
|
79
|
-
* it into the post-validation amend path; tests for the wiring live in
|
|
80
|
-
* `tests/story-close-auto-refresh.test.js` (Story #1415).
|
|
81
|
-
*/
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Numeric guard — accepts finite numbers only. Strings, NaN, Infinity, null,
|
|
85
|
-
* undefined all fail. The evaluator runs against scored rows produced by the
|
|
86
|
-
* MI / CRAP scanners (which always emit numeric scores) and baseline rows
|
|
87
|
-
* loaded from the on-disk JSON (which JSON-parses numeric fields), so a
|
|
88
|
-
* non-finite value here signals upstream corruption — we exclude the row
|
|
89
|
-
* conservatively rather than coercing.
|
|
90
|
-
*/
|
|
91
|
-
function isFiniteNumber(value) {
|
|
92
|
-
return typeof value === 'number' && Number.isFinite(value);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Index `baseline.mi` rows by `path` for O(1) lookup. Bad rows (missing
|
|
97
|
-
* `path`, non-string `path`, non-finite `mi`) are skipped — their absence
|
|
98
|
-
* causes the matching scored row to be treated as "new", which never blocks
|
|
99
|
-
* auto-refresh.
|
|
100
|
-
*/
|
|
101
|
-
function indexMiBaseline(rows) {
|
|
102
|
-
const byPath = new Map();
|
|
103
|
-
if (!Array.isArray(rows)) return byPath;
|
|
104
|
-
for (const row of rows) {
|
|
105
|
-
if (!row || typeof row.path !== 'string' || row.path.length === 0) continue;
|
|
106
|
-
if (!isFiniteNumber(row.mi)) continue;
|
|
107
|
-
byPath.set(row.path, row);
|
|
108
|
-
}
|
|
109
|
-
return byPath;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Index `baseline.crap` rows by `${file}::${method}` for O(1) lookup.
|
|
114
|
-
* `startLine` is *not* part of the key — the scored row may have shifted
|
|
115
|
-
* lines vs the baseline (legitimate refactor), and we want the closest match
|
|
116
|
-
* by method name. When the same method appears multiple times in the same
|
|
117
|
-
* file (e.g. nested helpers), we pick the closest startLine at lookup time.
|
|
118
|
-
*
|
|
119
|
-
* Bad rows (missing `file`/`method`, non-finite `crap`) are skipped — their
|
|
120
|
-
* absence causes the matching scored row to be treated as "new".
|
|
121
|
-
*/
|
|
122
|
-
function indexCrapBaseline(rows) {
|
|
123
|
-
const byMethod = new Map();
|
|
124
|
-
if (!Array.isArray(rows)) return byMethod;
|
|
125
|
-
for (const row of rows) {
|
|
126
|
-
if (!row || typeof row.file !== 'string' || row.file.length === 0) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
|
-
if (typeof row.method !== 'string' || row.method.length === 0) continue;
|
|
130
|
-
if (!isFiniteNumber(row.crap)) continue;
|
|
131
|
-
const key = `${row.file}::${row.method}`;
|
|
132
|
-
if (!byMethod.has(key)) byMethod.set(key, []);
|
|
133
|
-
byMethod.get(key).push(row);
|
|
134
|
-
}
|
|
135
|
-
return byMethod;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Pick the closest baseline candidate by `startLine` distance. When the
|
|
140
|
-
* scored row's `startLine` is missing or all candidates have missing line
|
|
141
|
-
* info, returns the first candidate — matches `baseline-attribution-wiring`'s
|
|
142
|
-
* `diffCrapBaselines` resolution policy.
|
|
143
|
-
*/
|
|
144
|
-
function pickClosestBaseline(candidates, scoredStartLine) {
|
|
145
|
-
if (!Array.isArray(candidates) || candidates.length === 0) return null;
|
|
146
|
-
if (candidates.length === 1) return candidates[0];
|
|
147
|
-
const target = isFiniteNumber(scoredStartLine) ? scoredStartLine : 0;
|
|
148
|
-
let best = candidates[0];
|
|
149
|
-
let bestDist = Math.abs((best.startLine ?? 0) - target);
|
|
150
|
-
for (let i = 1; i < candidates.length; i += 1) {
|
|
151
|
-
const c = candidates[i];
|
|
152
|
-
const dist = Math.abs((c?.startLine ?? 0) - target);
|
|
153
|
-
if (dist < bestDist) {
|
|
154
|
-
bestDist = dist;
|
|
155
|
-
best = c;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return best;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Evaluate every MI scored row against the MI cap. Returns the over-cap
|
|
163
|
-
* subset; rows under the cap (or new) are simply omitted from the result.
|
|
164
|
-
*
|
|
165
|
-
* MI is higher-is-better, so drift = baseline.mi − scored.mi. A positive
|
|
166
|
-
* drift is a regression; a drift greater than `miDropCap` breaches the cap.
|
|
167
|
-
*/
|
|
168
|
-
function evaluateMiRows({ scoredRows, baselineIndex, miDropCap }) {
|
|
169
|
-
const overCap = [];
|
|
170
|
-
if (!Array.isArray(scoredRows)) return overCap;
|
|
171
|
-
for (const row of scoredRows) {
|
|
172
|
-
if (!row || typeof row.path !== 'string' || row.path.length === 0) {
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
if (!isFiniteNumber(row.mi)) continue;
|
|
176
|
-
const baselineRow = baselineIndex.get(row.path);
|
|
177
|
-
if (!baselineRow) continue; // new path — never breaches
|
|
178
|
-
const drop = baselineRow.mi - row.mi;
|
|
179
|
-
if (drop > miDropCap) {
|
|
180
|
-
overCap.push({
|
|
181
|
-
path: row.path,
|
|
182
|
-
baseline: baselineRow.mi,
|
|
183
|
-
scored: row.mi,
|
|
184
|
-
delta: drop,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
return overCap;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Evaluate every CRAP scored row against the CRAP cap. Returns the over-cap
|
|
193
|
-
* subset; rows under the cap (or new) are simply omitted from the result.
|
|
194
|
-
*
|
|
195
|
-
* CRAP is lower-is-better, so jump = scored.crap − baseline.crap. A positive
|
|
196
|
-
* jump is a regression; a jump greater than `crapJumpCap` breaches the cap.
|
|
197
|
-
*/
|
|
198
|
-
function evaluateCrapRows({ scoredRows, baselineIndex, crapJumpCap }) {
|
|
199
|
-
const overCap = [];
|
|
200
|
-
if (!Array.isArray(scoredRows)) return overCap;
|
|
201
|
-
for (const row of scoredRows) {
|
|
202
|
-
if (!row || typeof row.file !== 'string' || row.file.length === 0) {
|
|
203
|
-
continue;
|
|
204
|
-
}
|
|
205
|
-
if (typeof row.method !== 'string' || row.method.length === 0) continue;
|
|
206
|
-
if (!isFiniteNumber(row.crap)) continue;
|
|
207
|
-
const candidates = baselineIndex.get(`${row.file}::${row.method}`);
|
|
208
|
-
const baselineRow = pickClosestBaseline(candidates, row.startLine);
|
|
209
|
-
if (!baselineRow) continue; // new method — never breaches
|
|
210
|
-
const jump = row.crap - baselineRow.crap;
|
|
211
|
-
if (jump > crapJumpCap) {
|
|
212
|
-
overCap.push({
|
|
213
|
-
file: row.file,
|
|
214
|
-
method: row.method,
|
|
215
|
-
startLine: row.startLine,
|
|
216
|
-
baseline: baselineRow.crap,
|
|
217
|
-
scored: row.crap,
|
|
218
|
-
delta: jump,
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
return overCap;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Build the human-readable refusal reasons array. Stable formatting so the
|
|
227
|
-
* friction-signal renderer (and unit tests) can pin the strings exactly.
|
|
228
|
-
*
|
|
229
|
-
* Each reason names the kind, the file/path/method, and the absolute delta
|
|
230
|
-
* vs the cap. Numbers are formatted to 3 decimal places to match the
|
|
231
|
-
* baseline JSON's float precision without trailing-zero noise.
|
|
232
|
-
*/
|
|
233
|
-
function buildRefusalReasons({ miOverCap, crapOverCap, caps }) {
|
|
234
|
-
const reasons = [];
|
|
235
|
-
for (const r of miOverCap) {
|
|
236
|
-
reasons.push(
|
|
237
|
-
`MI drop ${r.delta.toFixed(3)} > cap ${caps.miDropCap} on ${r.path} (baseline ${r.baseline.toFixed(3)} → scored ${r.scored.toFixed(3)})`,
|
|
238
|
-
);
|
|
239
|
-
}
|
|
240
|
-
for (const r of crapOverCap) {
|
|
241
|
-
reasons.push(
|
|
242
|
-
`CRAP jump ${r.delta.toFixed(3)} > cap ${caps.crapJumpCap} on ${r.file}::${r.method} (baseline ${r.baseline.toFixed(3)} → scored ${r.scored.toFixed(3)})`,
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
return reasons;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* Pure delta-cap evaluator. Decides whether the regenerated rows can be
|
|
250
|
-
* silently amended into the close commit (under-cap) or whether the close
|
|
251
|
-
* must refuse the amend and surface a `baseline-refresh-regression` friction
|
|
252
|
-
* signal (over-cap).
|
|
253
|
-
*
|
|
254
|
-
* @param {object} input
|
|
255
|
-
* @param {{
|
|
256
|
-
* mi?: Array<{ path: string, mi: number }>,
|
|
257
|
-
* crap?: Array<{ file: string, method: string, startLine?: number, crap: number }>,
|
|
258
|
-
* }} input.scoredRows Just-regenerated rows for the Story diff.
|
|
259
|
-
* @param {{
|
|
260
|
-
* mi?: Array<{ path: string, mi: number }>,
|
|
261
|
-
* crap?: Array<{ file: string, method: string, startLine?: number, crap: number }>,
|
|
262
|
-
* }} input.baseline Previously committed rows.
|
|
263
|
-
* @param {{ miDropCap: number, crapJumpCap: number }} input.caps
|
|
264
|
-
* Bounded delta caps (defaults: miDropCap=1.5, crapJumpCap=5 — see
|
|
265
|
-
* `.agents/docs/agentrc-reference.json` under `delivery.quality.autoRefresh`).
|
|
266
|
-
* @returns {{
|
|
267
|
-
* canAutoRefresh: boolean,
|
|
268
|
-
* miOverCap: Array<{ path: string, baseline: number, scored: number, delta: number }>,
|
|
269
|
-
* crapOverCap: Array<{ file: string, method: string, startLine?: number, baseline: number, scored: number, delta: number }>,
|
|
270
|
-
* refusalReasons: string[],
|
|
271
|
-
* }}
|
|
272
|
-
*/
|
|
273
|
-
export function evaluateAutoRefresh({
|
|
274
|
-
scoredRows = {},
|
|
275
|
-
baseline = {},
|
|
276
|
-
caps,
|
|
277
|
-
} = {}) {
|
|
278
|
-
if (
|
|
279
|
-
!caps ||
|
|
280
|
-
!isFiniteNumber(caps.miDropCap) ||
|
|
281
|
-
!isFiniteNumber(caps.crapJumpCap)
|
|
282
|
-
) {
|
|
283
|
-
throw new TypeError(
|
|
284
|
-
'evaluateAutoRefresh: caps.{miDropCap,crapJumpCap} must be finite numbers',
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
const miBaselineIdx = indexMiBaseline(baseline?.mi);
|
|
289
|
-
const crapBaselineIdx = indexCrapBaseline(baseline?.crap);
|
|
290
|
-
|
|
291
|
-
const miOverCap = evaluateMiRows({
|
|
292
|
-
scoredRows: scoredRows?.mi,
|
|
293
|
-
baselineIndex: miBaselineIdx,
|
|
294
|
-
miDropCap: caps.miDropCap,
|
|
295
|
-
});
|
|
296
|
-
const crapOverCap = evaluateCrapRows({
|
|
297
|
-
scoredRows: scoredRows?.crap,
|
|
298
|
-
baselineIndex: crapBaselineIdx,
|
|
299
|
-
crapJumpCap: caps.crapJumpCap,
|
|
300
|
-
});
|
|
301
|
-
|
|
302
|
-
const canAutoRefresh = miOverCap.length === 0 && crapOverCap.length === 0;
|
|
303
|
-
const refusalReasons = canAutoRefresh
|
|
304
|
-
? []
|
|
305
|
-
: buildRefusalReasons({ miOverCap, crapOverCap, caps });
|
|
306
|
-
|
|
307
|
-
return { canAutoRefresh, miOverCap, crapOverCap, refusalReasons };
|
|
308
|
-
}
|