mandrel 1.94.0 → 2.0.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 +59 -73
- package/.agents/agents/story-worker.md +12 -13
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +104 -138
- package/.agents/docs/execution-reference.md +17 -20
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +70 -81
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +6 -7
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +54 -214
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/risk-verdict.schema.json +0 -13
- package/.agents/scripts/analyze-execution.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/check-lifecycle-lint.js +6 -9
- package/.agents/scripts/check-prepush-recovery.js +1 -1
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +5 -5
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
- package/.agents/scripts/lib/checks/index.js +2 -1
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +16 -7
- package/.agents/scripts/lib/close-validation/gates.js +2 -2
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +45 -29
- package/.agents/scripts/lib/config/explain.js +55 -36
- package/.agents/scripts/lib/config/limits.js +17 -58
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +1 -1
- package/.agents/scripts/lib/config/runners.js +17 -50
- package/.agents/scripts/lib/config/temp-paths.js +19 -14
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +24 -148
- package/.agents/scripts/lib/config-settings-schema-quality.js +8 -14
- package/.agents/scripts/lib/config-settings-schema.js +52 -38
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +211 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +5 -5
- package/.agents/scripts/lib/framework-version.js +2 -3
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
- package/.agents/scripts/lib/git-utils.js +32 -6
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/label-constants.js +10 -23
- package/.agents/scripts/lib/label-taxonomy.js +9 -43
- package/.agents/scripts/lib/observability/active-story-env.js +1 -1
- package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
- package/.agents/scripts/lib/orchestration/code-review.js +20 -268
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +13 -13
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
- package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
- package/.agents/scripts/lib/orchestration/ticket-validator.js +2 -7
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
- package/.agents/scripts/lib/planning-corpus.js +16 -11
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/story-adjacency.js +11 -14
- package/.agents/scripts/lib/story-body/story-body.js +124 -70
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
- package/.agents/scripts/lib/validation-evidence.js +1 -1
- package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +64 -74
- package/.agents/scripts/plan-persist.js +121 -280
- package/.agents/scripts/plan-run-epilogue.js +97 -0
- package/.agents/scripts/providers/github/issues.js +17 -33
- package/.agents/scripts/providers/github/mappers.js +0 -12
- package/.agents/scripts/providers/github/tickets.js +2 -5
- package/.agents/scripts/resolve-plan-run.js +117 -0
- package/.agents/scripts/signals-view.js +24 -19
- package/.agents/scripts/single-story-close.js +11 -14
- package/.agents/scripts/single-story-confirm-merge.js +39 -23
- package/.agents/scripts/single-story-init.js +29 -20
- package/.agents/scripts/stories-wave-tick.js +6 -6
- package/.agents/scripts/story-plan.js +26 -47
- package/.agents/scripts/update-ticket-state.js +6 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +3 -43
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +3 -3
- package/.agents/workflows/audit-clean-code.md +3 -3
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +5 -5
- package/.agents/workflows/audit-lighthouse.md +3 -3
- package/.agents/workflows/audit-navigability.md +3 -2
- package/.agents/workflows/audit-performance.md +3 -3
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +3 -3
- package/.agents/workflows/audit-security.md +3 -3
- package/.agents/workflows/audit-seo.md +3 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +3 -3
- package/.agents/workflows/deliver.md +122 -174
- package/.agents/workflows/git-cleanup.md +3 -4
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +16 -29
- package/.agents/workflows/helpers/code-review.md +70 -193
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
- package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +112 -145
- package/.agents/workflows/qa-assist.md +24 -30
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -2
- package/README.md +9 -8
- package/docs/CHANGELOG.md +30 -0
- package/lib/migrations/index.js +6 -5
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
|
@@ -1,747 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* auto-refresh-runner.js — bounded baseline auto-refresh at story-close
|
|
3
|
-
* (Story #1398, Epic #1386; rerouted to `refreshBaseline()` by Story
|
|
4
|
-
* #2205; collapsed onto the single `runRefreshCommit` funnel by Story
|
|
5
|
-
* #4017).
|
|
6
|
-
*
|
|
7
|
-
* Runs *after* `runPreMergeGatesWithAttribution` returns `{ status: 'ok' }`
|
|
8
|
-
* and *before* the merge into `epic/<id>`. For each baseline kind
|
|
9
|
-
* (maintainability, crap) the runner:
|
|
10
|
-
*
|
|
11
|
-
* 1. Snapshots the prior on-disk envelope (so cap evaluation can compare
|
|
12
|
-
* regenerated rows against the pre-refresh baseline).
|
|
13
|
-
* 2. Delegates the refresh → stage → commit sequence to
|
|
14
|
-
* `runRefreshCommit()` (`baseline-attribution/phases/refresh-commit.js`)
|
|
15
|
-
* — the **single** story-close refresh funnel — injecting a `capCheck`
|
|
16
|
-
* that re-reads the refreshed envelope and evaluates the row deltas
|
|
17
|
-
* against the configured caps via {@link evaluateAutoRefresh}.
|
|
18
|
-
*
|
|
19
|
-
* - **Under-cap path** — the funnel emits one canonical commit
|
|
20
|
-
* `chore(baselines): refresh <kind> for story-<id>` per kind that
|
|
21
|
-
* actually drifted. NO `--amend`, NO `--allow-empty`.
|
|
22
|
-
*
|
|
23
|
-
* - **Over-cap path** — the funnel restores the kind's baseline file to
|
|
24
|
-
* HEAD; the runner appends a single `baseline-refresh-regression`
|
|
25
|
-
* friction signal to the per-Story NDJSON and returns
|
|
26
|
-
* `{ status: 'refused', ... }`.
|
|
27
|
-
*
|
|
28
|
-
* - **Skipped paths** — `enabled: false` in `quality.autoRefresh`, or
|
|
29
|
-
* no configured kind produced drift. The runner returns
|
|
30
|
-
* `{ status: 'skipped', reason }` without touching the branch tip.
|
|
31
|
-
*
|
|
32
|
-
* Each-kind-once contract (Story #4017): the caller threads the close
|
|
33
|
-
* cycle's shared `cycleState` (created in `runGatesAndRefresh`) into the
|
|
34
|
-
* funnel, so a kind already refreshed by the gate-failure attribution
|
|
35
|
-
* retry (`gate-failure.js` → `runRefreshCommit`) is **not re-scored or
|
|
36
|
-
* re-committed** here — the funnel short-circuits on the idempotency
|
|
37
|
-
* token. A clean close therefore computes each baseline kind exactly once
|
|
38
|
-
* and emits at most one `chore(baselines): refresh` subject per kind.
|
|
39
|
-
*
|
|
40
|
-
* Dedup contract (AC3 — idempotent re-run):
|
|
41
|
-
* On re-entry after an over-cap refusal, the runner scans the per-Story
|
|
42
|
-
* `signals.ndjson` for any prior `baseline-refresh-regression` signal
|
|
43
|
-
* tagged `source.tool === 'auto-refresh-runner'` and skips the append if
|
|
44
|
-
* one exists. The on-disk friction-signal file therefore carries one row
|
|
45
|
-
* per (story, refusal-cause) regardless of how many times story-close
|
|
46
|
-
* runs.
|
|
47
|
-
*
|
|
48
|
-
* The runner is dependency-injection-friendly: every git invocation, every
|
|
49
|
-
* fs touch, the refresh-service handle, the evaluator, and the signal
|
|
50
|
-
* writer are injectable seams. Production callers omit the seams; tests
|
|
51
|
-
* inject mocks.
|
|
52
|
-
*
|
|
53
|
-
* @see .agents/scripts/lib/baselines/refresh-service.js (the unified write path)
|
|
54
|
-
* @see ./baseline-attribution/phases/refresh-commit.js (the commit funnel)
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
import fs from 'node:fs';
|
|
58
|
-
import path from 'node:path';
|
|
59
|
-
import { loadFile as defaultReaderLoadFile } from '../../baselines/reader.js';
|
|
60
|
-
import { refreshBaseline as defaultRefreshBaseline } from '../../baselines/refresh-service.js';
|
|
61
|
-
import {
|
|
62
|
-
getBaselines as defaultGetBaselines,
|
|
63
|
-
getQuality as defaultGetQuality,
|
|
64
|
-
} from '../../config-resolver.js';
|
|
65
|
-
import { gitSpawn as defaultGitSpawn } from '../../git-utils.js';
|
|
66
|
-
import { Logger as DefaultLogger } from '../../Logger.js';
|
|
67
|
-
import {
|
|
68
|
-
appendSignal as defaultAppendSignal,
|
|
69
|
-
forEachLine as defaultForEachLine,
|
|
70
|
-
} from '../../observability/signals-writer.js';
|
|
71
|
-
import {
|
|
72
|
-
buildKindScorer,
|
|
73
|
-
computeStoryDiffPaths,
|
|
74
|
-
runRefreshCommit as defaultRunRefreshCommit,
|
|
75
|
-
} from './baseline-attribution-wiring.js';
|
|
76
|
-
|
|
77
|
-
const RUNNER_SOURCE_TOOL = 'auto-refresh-runner';
|
|
78
|
-
const FRICTION_CATEGORY = 'baseline-refresh-regression';
|
|
79
|
-
|
|
80
|
-
// ---------------------------------------------------------------------------
|
|
81
|
-
// Pure delta-cap evaluator (Story #1398; folded in from the deleted
|
|
82
|
-
// standalone evaluator module by Story #4017).
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Numeric guard — accepts finite numbers only. Strings, NaN, Infinity, null,
|
|
87
|
-
* undefined all fail. The evaluator runs against scored rows produced by the
|
|
88
|
-
* MI / CRAP scanners (which always emit numeric scores) and baseline rows
|
|
89
|
-
* loaded from the on-disk JSON (which JSON-parses numeric fields), so a
|
|
90
|
-
* non-finite value here signals upstream corruption — we exclude the row
|
|
91
|
-
* conservatively rather than coercing.
|
|
92
|
-
*/
|
|
93
|
-
function isFiniteNumber(value) {
|
|
94
|
-
return typeof value === 'number' && Number.isFinite(value);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Index `baseline.mi` rows by `path` for O(1) lookup. Bad rows (missing
|
|
99
|
-
* `path`, non-string `path`, non-finite `mi`) are skipped — their absence
|
|
100
|
-
* causes the matching scored row to be treated as "new", which never blocks
|
|
101
|
-
* auto-refresh.
|
|
102
|
-
*/
|
|
103
|
-
function indexMiBaseline(rows) {
|
|
104
|
-
const byPath = new Map();
|
|
105
|
-
if (!Array.isArray(rows)) return byPath;
|
|
106
|
-
for (const row of rows) {
|
|
107
|
-
if (!row || typeof row.path !== 'string' || row.path.length === 0) continue;
|
|
108
|
-
if (!isFiniteNumber(row.mi)) continue;
|
|
109
|
-
byPath.set(row.path, row);
|
|
110
|
-
}
|
|
111
|
-
return byPath;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Index `baseline.crap` rows by `${file}::${method}` for O(1) lookup.
|
|
116
|
-
* `startLine` is *not* part of the key — the scored row may have shifted
|
|
117
|
-
* lines vs the baseline (legitimate refactor), and we want the closest match
|
|
118
|
-
* by method name. When the same method appears multiple times in the same
|
|
119
|
-
* file (e.g. nested helpers), we pick the closest startLine at lookup time.
|
|
120
|
-
*
|
|
121
|
-
* Bad rows (missing `file`/`method`, non-finite `crap`) are skipped — their
|
|
122
|
-
* absence causes the matching scored row to be treated as "new".
|
|
123
|
-
*/
|
|
124
|
-
function indexCrapBaseline(rows) {
|
|
125
|
-
const byMethod = new Map();
|
|
126
|
-
if (!Array.isArray(rows)) return byMethod;
|
|
127
|
-
for (const row of rows) {
|
|
128
|
-
if (!row || typeof row.file !== 'string' || row.file.length === 0) {
|
|
129
|
-
continue;
|
|
130
|
-
}
|
|
131
|
-
if (typeof row.method !== 'string' || row.method.length === 0) continue;
|
|
132
|
-
if (!isFiniteNumber(row.crap)) continue;
|
|
133
|
-
const key = `${row.file}::${row.method}`;
|
|
134
|
-
if (!byMethod.has(key)) byMethod.set(key, []);
|
|
135
|
-
byMethod.get(key).push(row);
|
|
136
|
-
}
|
|
137
|
-
return byMethod;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Pick the closest baseline candidate by `startLine` distance. When the
|
|
142
|
-
* scored row's `startLine` is missing or all candidates have missing line
|
|
143
|
-
* info, returns the first candidate — matches `baseline-attribution-wiring`'s
|
|
144
|
-
* `diffCrapBaselines` resolution policy.
|
|
145
|
-
*/
|
|
146
|
-
function pickClosestBaseline(candidates, scoredStartLine) {
|
|
147
|
-
if (!Array.isArray(candidates) || candidates.length === 0) return null;
|
|
148
|
-
if (candidates.length === 1) return candidates[0];
|
|
149
|
-
const target = isFiniteNumber(scoredStartLine) ? scoredStartLine : 0;
|
|
150
|
-
let best = candidates[0];
|
|
151
|
-
let bestDist = Math.abs((best.startLine ?? 0) - target);
|
|
152
|
-
for (let i = 1; i < candidates.length; i += 1) {
|
|
153
|
-
const c = candidates[i];
|
|
154
|
-
const dist = Math.abs((c?.startLine ?? 0) - target);
|
|
155
|
-
if (dist < bestDist) {
|
|
156
|
-
bestDist = dist;
|
|
157
|
-
best = c;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
return best;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Evaluate every MI scored row against the MI cap. Returns the over-cap
|
|
165
|
-
* subset; rows under the cap (or new) are simply omitted from the result.
|
|
166
|
-
*
|
|
167
|
-
* MI is higher-is-better, so drift = baseline.mi − scored.mi. A positive
|
|
168
|
-
* drift is a regression; a drift greater than `miDropCap` breaches the cap.
|
|
169
|
-
*/
|
|
170
|
-
function evaluateMiRows({ scoredRows, baselineIndex, miDropCap }) {
|
|
171
|
-
const overCap = [];
|
|
172
|
-
if (!Array.isArray(scoredRows)) return overCap;
|
|
173
|
-
for (const row of scoredRows) {
|
|
174
|
-
if (!row || typeof row.path !== 'string' || row.path.length === 0) {
|
|
175
|
-
continue;
|
|
176
|
-
}
|
|
177
|
-
if (!isFiniteNumber(row.mi)) continue;
|
|
178
|
-
const baselineRow = baselineIndex.get(row.path);
|
|
179
|
-
if (!baselineRow) continue; // new path — never breaches
|
|
180
|
-
const drop = baselineRow.mi - row.mi;
|
|
181
|
-
if (drop > miDropCap) {
|
|
182
|
-
overCap.push({
|
|
183
|
-
path: row.path,
|
|
184
|
-
baseline: baselineRow.mi,
|
|
185
|
-
scored: row.mi,
|
|
186
|
-
delta: drop,
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return overCap;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Evaluate every CRAP scored row against the CRAP cap. Returns the over-cap
|
|
195
|
-
* subset; rows under the cap (or new) are simply omitted from the result.
|
|
196
|
-
*
|
|
197
|
-
* CRAP is lower-is-better, so jump = scored.crap − baseline.crap. A positive
|
|
198
|
-
* jump is a regression; a jump greater than `crapJumpCap` breaches the cap.
|
|
199
|
-
*/
|
|
200
|
-
function evaluateCrapRows({ scoredRows, baselineIndex, crapJumpCap }) {
|
|
201
|
-
const overCap = [];
|
|
202
|
-
if (!Array.isArray(scoredRows)) return overCap;
|
|
203
|
-
for (const row of scoredRows) {
|
|
204
|
-
if (!row || typeof row.file !== 'string' || row.file.length === 0) {
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (typeof row.method !== 'string' || row.method.length === 0) continue;
|
|
208
|
-
if (!isFiniteNumber(row.crap)) continue;
|
|
209
|
-
const candidates = baselineIndex.get(`${row.file}::${row.method}`);
|
|
210
|
-
const baselineRow = pickClosestBaseline(candidates, row.startLine);
|
|
211
|
-
if (!baselineRow) continue; // new method — never breaches
|
|
212
|
-
const jump = row.crap - baselineRow.crap;
|
|
213
|
-
if (jump > crapJumpCap) {
|
|
214
|
-
overCap.push({
|
|
215
|
-
file: row.file,
|
|
216
|
-
method: row.method,
|
|
217
|
-
startLine: row.startLine,
|
|
218
|
-
baseline: baselineRow.crap,
|
|
219
|
-
scored: row.crap,
|
|
220
|
-
delta: jump,
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return overCap;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
/**
|
|
228
|
-
* Build the human-readable refusal reasons array. Stable formatting so the
|
|
229
|
-
* friction-signal renderer (and unit tests) can pin the strings exactly.
|
|
230
|
-
*
|
|
231
|
-
* Each reason names the kind, the file/path/method, and the absolute delta
|
|
232
|
-
* vs the cap. Numbers are formatted to 3 decimal places to match the
|
|
233
|
-
* baseline JSON's float precision without trailing-zero noise.
|
|
234
|
-
*/
|
|
235
|
-
function buildRefusalReasons({ miOverCap, crapOverCap, caps }) {
|
|
236
|
-
const reasons = [];
|
|
237
|
-
for (const r of miOverCap) {
|
|
238
|
-
reasons.push(
|
|
239
|
-
`MI drop ${r.delta.toFixed(3)} > cap ${caps.miDropCap} on ${r.path} (baseline ${r.baseline.toFixed(3)} → scored ${r.scored.toFixed(3)})`,
|
|
240
|
-
);
|
|
241
|
-
}
|
|
242
|
-
for (const r of crapOverCap) {
|
|
243
|
-
reasons.push(
|
|
244
|
-
`CRAP jump ${r.delta.toFixed(3)} > cap ${caps.crapJumpCap} on ${r.file}::${r.method} (baseline ${r.baseline.toFixed(3)} → scored ${r.scored.toFixed(3)})`,
|
|
245
|
-
);
|
|
246
|
-
}
|
|
247
|
-
return reasons;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Pure delta-cap evaluator. Decides whether the regenerated rows can be
|
|
252
|
-
* silently committed (under-cap) or whether the close must refuse the
|
|
253
|
-
* refresh and surface a `baseline-refresh-regression` friction signal
|
|
254
|
-
* (over-cap).
|
|
255
|
-
*
|
|
256
|
-
* Cap semantics:
|
|
257
|
-
*
|
|
258
|
-
* - MI is "higher is better". A *drop* (baseline.mi − scored.mi) greater
|
|
259
|
-
* than `miDropCap` breaches the cap. Improvements never breach.
|
|
260
|
-
* - CRAP is "lower is better". A *jump* (scored.crap − baseline.crap)
|
|
261
|
-
* greater than `crapJumpCap` breaches the cap. Improvements never
|
|
262
|
-
* breach.
|
|
263
|
-
* - Equality at the cap (delta === cap) is *under* the cap — the cap is
|
|
264
|
-
* the maximum allowed delta, not the strict maximum.
|
|
265
|
-
* - Missing baseline rows (path/method new in the scored set) never push
|
|
266
|
-
* `canAutoRefresh` to `false` and are not surfaced in the over-cap
|
|
267
|
-
* arrays.
|
|
268
|
-
*
|
|
269
|
-
* @param {object} input
|
|
270
|
-
* @param {{
|
|
271
|
-
* mi?: Array<{ path: string, mi: number }>,
|
|
272
|
-
* crap?: Array<{ file: string, method: string, startLine?: number, crap: number }>,
|
|
273
|
-
* }} input.scoredRows Just-regenerated rows for the Story diff.
|
|
274
|
-
* @param {{
|
|
275
|
-
* mi?: Array<{ path: string, mi: number }>,
|
|
276
|
-
* crap?: Array<{ file: string, method: string, startLine?: number, crap: number }>,
|
|
277
|
-
* }} input.baseline Previously committed rows.
|
|
278
|
-
* @param {{ miDropCap: number, crapJumpCap: number }} input.caps
|
|
279
|
-
* Bounded delta caps (defaults: miDropCap=1.5, crapJumpCap=5 — see
|
|
280
|
-
* `.agents/docs/agentrc-reference.json` under `delivery.quality.autoRefresh`).
|
|
281
|
-
* @returns {{
|
|
282
|
-
* canAutoRefresh: boolean,
|
|
283
|
-
* miOverCap: Array<{ path: string, baseline: number, scored: number, delta: number }>,
|
|
284
|
-
* crapOverCap: Array<{ file: string, method: string, startLine?: number, baseline: number, scored: number, delta: number }>,
|
|
285
|
-
* refusalReasons: string[],
|
|
286
|
-
* }}
|
|
287
|
-
*/
|
|
288
|
-
export function evaluateAutoRefresh({
|
|
289
|
-
scoredRows = {},
|
|
290
|
-
baseline = {},
|
|
291
|
-
caps,
|
|
292
|
-
} = {}) {
|
|
293
|
-
if (
|
|
294
|
-
!caps ||
|
|
295
|
-
!isFiniteNumber(caps.miDropCap) ||
|
|
296
|
-
!isFiniteNumber(caps.crapJumpCap)
|
|
297
|
-
) {
|
|
298
|
-
throw new TypeError(
|
|
299
|
-
'evaluateAutoRefresh: caps.{miDropCap,crapJumpCap} must be finite numbers',
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
const miBaselineIdx = indexMiBaseline(baseline?.mi);
|
|
304
|
-
const crapBaselineIdx = indexCrapBaseline(baseline?.crap);
|
|
305
|
-
|
|
306
|
-
const miOverCap = evaluateMiRows({
|
|
307
|
-
scoredRows: scoredRows?.mi,
|
|
308
|
-
baselineIndex: miBaselineIdx,
|
|
309
|
-
miDropCap: caps.miDropCap,
|
|
310
|
-
});
|
|
311
|
-
const crapOverCap = evaluateCrapRows({
|
|
312
|
-
scoredRows: scoredRows?.crap,
|
|
313
|
-
baselineIndex: crapBaselineIdx,
|
|
314
|
-
crapJumpCap: caps.crapJumpCap,
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
const canAutoRefresh = miOverCap.length === 0 && crapOverCap.length === 0;
|
|
318
|
-
const refusalReasons = canAutoRefresh
|
|
319
|
-
? []
|
|
320
|
-
: buildRefusalReasons({ miOverCap, crapOverCap, caps });
|
|
321
|
-
|
|
322
|
-
return { canAutoRefresh, miOverCap, crapOverCap, refusalReasons };
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ---------------------------------------------------------------------------
|
|
326
|
-
// Runner plumbing
|
|
327
|
-
// ---------------------------------------------------------------------------
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Load + parse the baseline envelope at `absPath` via the injected
|
|
331
|
-
* reader. Returns `null` when the file is missing, unreadable, or fails
|
|
332
|
-
* schema validation — the caller treats null as "no prior, every row is
|
|
333
|
-
* new" (the cap evaluator handles missing-prior gracefully).
|
|
334
|
-
*/
|
|
335
|
-
function readEnvelope({ absPath, kind, readerLoadFile }) {
|
|
336
|
-
if (typeof absPath !== 'string' || absPath.length === 0) return null;
|
|
337
|
-
try {
|
|
338
|
-
const parsed = readerLoadFile(absPath, { kind });
|
|
339
|
-
if (!parsed || !Array.isArray(parsed.rows)) return null;
|
|
340
|
-
return { rows: parsed.rows };
|
|
341
|
-
} catch {
|
|
342
|
-
return null;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Adapt the writer's CRAP row shape (`{path, method, startLine, crap}`) to
|
|
348
|
-
* the evaluator's expectation (`{file, method, startLine, crap}`). The MI
|
|
349
|
-
* evaluator already keys on `path`, so no adapter is required for MI rows.
|
|
350
|
-
*
|
|
351
|
-
* Pure.
|
|
352
|
-
*/
|
|
353
|
-
function adaptCrapRowsForEvaluator(rows) {
|
|
354
|
-
return (rows ?? []).map((row) => {
|
|
355
|
-
if (!row || typeof row !== 'object') return row;
|
|
356
|
-
const { path: p, ...rest } = row;
|
|
357
|
-
return typeof p === 'string' ? { ...rest, file: p } : { ...rest };
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Filter rows to those whose path/file is in the Story's diff footprint.
|
|
363
|
-
* Empty `storyDiffPaths` returns the input unchanged so interactive
|
|
364
|
-
* re-runs (`story-close --skip-validation`) on already-merged branches
|
|
365
|
-
* still evaluate every row.
|
|
366
|
-
*/
|
|
367
|
-
function filterToStoryDiff({ miRows, crapRows, storyDiffPaths }) {
|
|
368
|
-
if (!Array.isArray(storyDiffPaths) || storyDiffPaths.length === 0) {
|
|
369
|
-
return { mi: miRows ?? [], crap: crapRows ?? [] };
|
|
370
|
-
}
|
|
371
|
-
const scope = new Set(storyDiffPaths);
|
|
372
|
-
const mi = (miRows ?? []).filter((r) => scope.has(r.path));
|
|
373
|
-
const crap = (crapRows ?? []).filter((r) => scope.has(r.file));
|
|
374
|
-
return { mi, crap };
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
/**
|
|
378
|
-
* Check whether a `baseline-refresh-regression` signal tagged with the
|
|
379
|
-
* runner's `emitter.tool === 'auto-refresh-runner'` already exists in the
|
|
380
|
-
* Story's signals stream. Backs the AC3 idempotent-re-run contract.
|
|
381
|
-
*/
|
|
382
|
-
async function priorRefusalSignalExists({
|
|
383
|
-
epicId,
|
|
384
|
-
storyId,
|
|
385
|
-
forEachLine = defaultForEachLine,
|
|
386
|
-
}) {
|
|
387
|
-
let found = false;
|
|
388
|
-
await forEachLine(epicId, storyId, (record) => {
|
|
389
|
-
if (
|
|
390
|
-
record &&
|
|
391
|
-
typeof record === 'object' &&
|
|
392
|
-
record.kind === 'friction' &&
|
|
393
|
-
record.category === FRICTION_CATEGORY &&
|
|
394
|
-
record?.emitter?.tool === RUNNER_SOURCE_TOOL
|
|
395
|
-
) {
|
|
396
|
-
found = true;
|
|
397
|
-
}
|
|
398
|
-
});
|
|
399
|
-
return found;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Compose the friction-signal record. Stable shape so the analyzer +
|
|
404
|
-
* `diagnose-friction` pattern-matchers can pin against fixed inputs.
|
|
405
|
-
*/
|
|
406
|
-
function buildRefusalSignal({
|
|
407
|
-
epicId,
|
|
408
|
-
storyId,
|
|
409
|
-
miOverCap,
|
|
410
|
-
crapOverCap,
|
|
411
|
-
refusalReasons,
|
|
412
|
-
caps,
|
|
413
|
-
}) {
|
|
414
|
-
return {
|
|
415
|
-
kind: 'friction',
|
|
416
|
-
ts: new Date().toISOString(),
|
|
417
|
-
epicId,
|
|
418
|
-
storyId,
|
|
419
|
-
category: FRICTION_CATEGORY,
|
|
420
|
-
emitter: { tool: RUNNER_SOURCE_TOOL },
|
|
421
|
-
details: {
|
|
422
|
-
message: `Auto-refresh refused: ${refusalReasons.length} row(s) breach configured caps (miDropCap=${caps.miDropCap}, crapJumpCap=${caps.crapJumpCap}).`,
|
|
423
|
-
},
|
|
424
|
-
refusalReasons,
|
|
425
|
-
miOverCap,
|
|
426
|
-
crapOverCap,
|
|
427
|
-
caps: { miDropCap: caps.miDropCap, crapJumpCap: caps.crapJumpCap },
|
|
428
|
-
};
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function resolveBaselineAbs(cwd, p) {
|
|
432
|
-
if (typeof p !== 'string' || p.length === 0) return null;
|
|
433
|
-
return path.isAbsolute(p) ? p : path.resolve(cwd, p);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
async function probeDedup({ epicId, storyId, forEachLine, logger }) {
|
|
437
|
-
try {
|
|
438
|
-
return await priorRefusalSignalExists({ epicId, storyId, forEachLine });
|
|
439
|
-
} catch (err) {
|
|
440
|
-
logger.warn?.(
|
|
441
|
-
`[auto-refresh-runner] dedup probe failed: ${err?.message ?? err}`,
|
|
442
|
-
);
|
|
443
|
-
return false;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
async function maybeAppendRefusalSignal({
|
|
448
|
-
dedup,
|
|
449
|
-
epicId,
|
|
450
|
-
storyId,
|
|
451
|
-
verdict,
|
|
452
|
-
caps,
|
|
453
|
-
appendSignal,
|
|
454
|
-
config,
|
|
455
|
-
logger,
|
|
456
|
-
}) {
|
|
457
|
-
if (dedup) return false;
|
|
458
|
-
const signal = buildRefusalSignal({
|
|
459
|
-
epicId,
|
|
460
|
-
storyId,
|
|
461
|
-
miOverCap: verdict.miOverCap,
|
|
462
|
-
crapOverCap: verdict.crapOverCap,
|
|
463
|
-
refusalReasons: verdict.refusalReasons,
|
|
464
|
-
caps,
|
|
465
|
-
});
|
|
466
|
-
try {
|
|
467
|
-
return await appendSignal({ epicId, storyId, signal, config });
|
|
468
|
-
} catch (err) {
|
|
469
|
-
logger.warn?.(
|
|
470
|
-
`[auto-refresh-runner] friction signal append failed: ${err?.message ?? err}`,
|
|
471
|
-
);
|
|
472
|
-
return false;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function resolveAutoRefreshDeps(deps) {
|
|
477
|
-
return {
|
|
478
|
-
logger: deps.logger ?? DefaultLogger,
|
|
479
|
-
getQuality: deps.getQuality ?? defaultGetQuality,
|
|
480
|
-
getBaselines: deps.getBaselines ?? defaultGetBaselines,
|
|
481
|
-
evaluateAutoRefresh: deps.evaluateAutoRefresh ?? evaluateAutoRefresh,
|
|
482
|
-
refreshBaseline: deps.refreshBaseline ?? defaultRefreshBaseline,
|
|
483
|
-
scorerBuilder: deps.scorerBuilder ?? buildKindScorer,
|
|
484
|
-
runRefreshCommit: deps.runRefreshCommit ?? defaultRunRefreshCommit,
|
|
485
|
-
gitRunner: deps.gitRunner ?? { gitSpawn: defaultGitSpawn },
|
|
486
|
-
fsImpl: deps.fsImpl ?? fs,
|
|
487
|
-
appendSignal: deps.appendSignal ?? defaultAppendSignal,
|
|
488
|
-
forEachLine: deps.forEachLine ?? defaultForEachLine,
|
|
489
|
-
computeDiffPaths: deps.computeStoryDiffPaths ?? computeStoryDiffPaths,
|
|
490
|
-
readerLoadFile: deps.readerLoadFile ?? defaultReaderLoadFile,
|
|
491
|
-
};
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/**
|
|
495
|
-
* Build the per-kind `capCheck` closure the funnel invokes after drift is
|
|
496
|
-
* staged and before the commit lands. Re-reads the refreshed envelope,
|
|
497
|
-
* narrows to the Story diff (unless `quality.autoRefresh.scope === 'full'`),
|
|
498
|
-
* and evaluates the single kind's rows against the configured caps with the
|
|
499
|
-
* prior (pre-refresh) snapshot as the baseline.
|
|
500
|
-
*/
|
|
501
|
-
function buildCapCheck({
|
|
502
|
-
kind,
|
|
503
|
-
priorEnv,
|
|
504
|
-
autoRefresh,
|
|
505
|
-
caps,
|
|
506
|
-
cwd,
|
|
507
|
-
epicBranch,
|
|
508
|
-
storyBranch,
|
|
509
|
-
evaluate,
|
|
510
|
-
gitRunner,
|
|
511
|
-
computeDiffPaths,
|
|
512
|
-
readerLoadFile,
|
|
513
|
-
}) {
|
|
514
|
-
return ({ writePath }) => {
|
|
515
|
-
const finalEnv = readEnvelope({ absPath: writePath, kind, readerLoadFile });
|
|
516
|
-
const isMi = kind === 'maintainability';
|
|
517
|
-
const finalRows = isMi
|
|
518
|
-
? (finalEnv?.rows ?? [])
|
|
519
|
-
: adaptCrapRowsForEvaluator(finalEnv?.rows ?? []);
|
|
520
|
-
const priorRows = isMi
|
|
521
|
-
? (priorEnv?.rows ?? [])
|
|
522
|
-
: adaptCrapRowsForEvaluator(priorEnv?.rows ?? []);
|
|
523
|
-
|
|
524
|
-
let scoped;
|
|
525
|
-
if ((autoRefresh.scope ?? 'diff') === 'full') {
|
|
526
|
-
scoped = isMi ? { mi: finalRows } : { crap: finalRows };
|
|
527
|
-
} else {
|
|
528
|
-
const storyDiffPaths = computeDiffPaths({
|
|
529
|
-
cwd,
|
|
530
|
-
epicBranch,
|
|
531
|
-
storyBranch,
|
|
532
|
-
gitRunner,
|
|
533
|
-
});
|
|
534
|
-
const filtered = filterToStoryDiff({
|
|
535
|
-
miRows: isMi ? finalRows : [],
|
|
536
|
-
crapRows: isMi ? [] : finalRows,
|
|
537
|
-
storyDiffPaths,
|
|
538
|
-
});
|
|
539
|
-
scoped = isMi ? { mi: filtered.mi } : { crap: filtered.crap };
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
return evaluate({
|
|
543
|
-
scoredRows: scoped,
|
|
544
|
-
baseline: isMi ? { mi: priorRows } : { crap: priorRows },
|
|
545
|
-
caps,
|
|
546
|
-
});
|
|
547
|
-
};
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Map a funnel failure string back onto the runner's historical failure
|
|
552
|
-
* vocabulary so `phases/refresh.js` keeps logging the same reason labels.
|
|
553
|
-
*/
|
|
554
|
-
function classifyFunnelError(error) {
|
|
555
|
-
return typeof error === 'string' && error.startsWith('refreshBaseline(')
|
|
556
|
-
? 'refresh-service-threw'
|
|
557
|
-
: 'commit-failed';
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/**
|
|
561
|
-
* Aggregate per-kind refused verdicts into the single refusal envelope the
|
|
562
|
-
* close pipeline consumes, appending the friction signal (dedup-aware,
|
|
563
|
-
* AC3) as the publish step. The funnel already rolled the refused kinds'
|
|
564
|
-
* files back to HEAD.
|
|
565
|
-
*/
|
|
566
|
-
async function publishRefusal({
|
|
567
|
-
refusedVerdicts,
|
|
568
|
-
caps,
|
|
569
|
-
epicId,
|
|
570
|
-
storyId,
|
|
571
|
-
appendSignal,
|
|
572
|
-
forEachLine,
|
|
573
|
-
config,
|
|
574
|
-
logger,
|
|
575
|
-
}) {
|
|
576
|
-
const verdict = {
|
|
577
|
-
miOverCap: refusedVerdicts.flatMap((v) => v.miOverCap ?? []),
|
|
578
|
-
crapOverCap: refusedVerdicts.flatMap((v) => v.crapOverCap ?? []),
|
|
579
|
-
refusalReasons: refusedVerdicts.flatMap((v) => v.refusalReasons ?? []),
|
|
580
|
-
};
|
|
581
|
-
const dedup = await probeDedup({ epicId, storyId, forEachLine, logger });
|
|
582
|
-
const signalAppended = await maybeAppendRefusalSignal({
|
|
583
|
-
dedup,
|
|
584
|
-
epicId,
|
|
585
|
-
storyId,
|
|
586
|
-
verdict,
|
|
587
|
-
caps,
|
|
588
|
-
appendSignal,
|
|
589
|
-
config,
|
|
590
|
-
logger,
|
|
591
|
-
});
|
|
592
|
-
logger.info?.(
|
|
593
|
-
`[auto-refresh-runner] refused — ${verdict.refusalReasons.length} cap breach(es); friction signal ${dedup ? 'already present (dedup)' : signalAppended ? 'appended' : 'append failed'}.`,
|
|
594
|
-
);
|
|
595
|
-
return {
|
|
596
|
-
status: 'refused',
|
|
597
|
-
refusalReasons: verdict.refusalReasons,
|
|
598
|
-
signalAppended,
|
|
599
|
-
dedup,
|
|
600
|
-
miOverCap: verdict.miOverCap,
|
|
601
|
-
crapOverCap: verdict.crapOverCap,
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
/**
|
|
606
|
-
* Bounded baseline auto-refresh. Delegates the per-kind refresh → stage →
|
|
607
|
-
* commit mechanics to the single `runRefreshCommit` funnel; this function
|
|
608
|
-
* owns only the config gating, the prior-envelope snapshot, the cap-check
|
|
609
|
-
* closure, and the refusal publication.
|
|
610
|
-
*
|
|
611
|
-
* @param {object} args
|
|
612
|
-
* @param {{ refreshedKinds?: Set<string>, lastRefreshSha?: string|null } | null} [args.cycleState]
|
|
613
|
-
* The close cycle's shared idempotency token (Story #4017). When the
|
|
614
|
-
* gate-failure attribution retry already refreshed a kind this cycle,
|
|
615
|
-
* the funnel short-circuits and the kind is not re-scored here.
|
|
616
|
-
* @returns {Promise<
|
|
617
|
-
* | { status: 'committed', sha: string, files: string[], committed: Array<{ kind: string, sha: string }> }
|
|
618
|
-
* | { status: 'refused', refusalReasons: string[], signalAppended: boolean, dedup: boolean, miOverCap: Array, crapOverCap: Array }
|
|
619
|
-
* | { status: 'skipped', reason: string }
|
|
620
|
-
* | { status: 'failed', reason: string, detail?: string }
|
|
621
|
-
* >}
|
|
622
|
-
*/
|
|
623
|
-
export async function runAutoRefresh({
|
|
624
|
-
storyId,
|
|
625
|
-
epicId,
|
|
626
|
-
cwd,
|
|
627
|
-
epicBranch,
|
|
628
|
-
storyBranch,
|
|
629
|
-
config,
|
|
630
|
-
cycleState = null,
|
|
631
|
-
deps = {},
|
|
632
|
-
} = {}) {
|
|
633
|
-
const resolved = resolveAutoRefreshDeps(deps);
|
|
634
|
-
const {
|
|
635
|
-
logger,
|
|
636
|
-
getQuality,
|
|
637
|
-
getBaselines,
|
|
638
|
-
evaluateAutoRefresh: evaluate,
|
|
639
|
-
refreshBaseline,
|
|
640
|
-
scorerBuilder,
|
|
641
|
-
runRefreshCommit,
|
|
642
|
-
gitRunner,
|
|
643
|
-
fsImpl,
|
|
644
|
-
appendSignal,
|
|
645
|
-
forEachLine,
|
|
646
|
-
computeDiffPaths,
|
|
647
|
-
readerLoadFile,
|
|
648
|
-
} = resolved;
|
|
649
|
-
|
|
650
|
-
const autoRefresh = getQuality(config)?.autoRefresh;
|
|
651
|
-
if (!autoRefresh || autoRefresh.enabled === false) {
|
|
652
|
-
return { status: 'skipped', reason: 'disabled' };
|
|
653
|
-
}
|
|
654
|
-
const caps = {
|
|
655
|
-
miDropCap: autoRefresh.miDropCap,
|
|
656
|
-
crapJumpCap: autoRefresh.crapJumpCap,
|
|
657
|
-
};
|
|
658
|
-
|
|
659
|
-
const baselines = getBaselines(config);
|
|
660
|
-
const kinds = [
|
|
661
|
-
{
|
|
662
|
-
kind: 'maintainability',
|
|
663
|
-
abs: resolveBaselineAbs(cwd, baselines?.maintainability?.path),
|
|
664
|
-
},
|
|
665
|
-
{ kind: 'crap', abs: resolveBaselineAbs(cwd, baselines?.crap?.path) },
|
|
666
|
-
].filter((k) => k.abs);
|
|
667
|
-
|
|
668
|
-
const committed = [];
|
|
669
|
-
const refusedVerdicts = [];
|
|
670
|
-
let lastSha = '';
|
|
671
|
-
|
|
672
|
-
for (const { kind, abs } of kinds) {
|
|
673
|
-
// Snapshot the prior envelope BEFORE the funnel's refreshBaseline()
|
|
674
|
-
// overwrites it — the cap evaluator compares against the pre-refresh
|
|
675
|
-
// rows. Reader-routed: schema-validated via `reader.loadFile`.
|
|
676
|
-
const priorEnv = readEnvelope({ absPath: abs, kind, readerLoadFile });
|
|
677
|
-
const capCheck = buildCapCheck({
|
|
678
|
-
kind,
|
|
679
|
-
priorEnv,
|
|
680
|
-
autoRefresh,
|
|
681
|
-
caps,
|
|
682
|
-
cwd,
|
|
683
|
-
epicBranch,
|
|
684
|
-
storyBranch,
|
|
685
|
-
evaluate,
|
|
686
|
-
gitRunner,
|
|
687
|
-
computeDiffPaths,
|
|
688
|
-
readerLoadFile,
|
|
689
|
-
});
|
|
690
|
-
|
|
691
|
-
const res = await runRefreshCommit({
|
|
692
|
-
cwd,
|
|
693
|
-
kind,
|
|
694
|
-
storyId,
|
|
695
|
-
epicBranch,
|
|
696
|
-
storyBranch,
|
|
697
|
-
config,
|
|
698
|
-
cycleState,
|
|
699
|
-
capCheck,
|
|
700
|
-
refreshBaseline,
|
|
701
|
-
scorerBuilder,
|
|
702
|
-
getBaselines,
|
|
703
|
-
getQuality,
|
|
704
|
-
fsImpl,
|
|
705
|
-
gitRunner,
|
|
706
|
-
logger,
|
|
707
|
-
});
|
|
708
|
-
|
|
709
|
-
if (res.ok !== true) {
|
|
710
|
-
return {
|
|
711
|
-
status: 'failed',
|
|
712
|
-
reason: classifyFunnelError(res.error),
|
|
713
|
-
detail: res.error,
|
|
714
|
-
};
|
|
715
|
-
}
|
|
716
|
-
if (res.refused) {
|
|
717
|
-
refusedVerdicts.push(res.verdict);
|
|
718
|
-
continue;
|
|
719
|
-
}
|
|
720
|
-
if (!res.skipped && res.sha) {
|
|
721
|
-
committed.push({ kind, sha: res.sha });
|
|
722
|
-
lastSha = res.sha;
|
|
723
|
-
}
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
if (refusedVerdicts.length > 0) {
|
|
727
|
-
return publishRefusal({
|
|
728
|
-
refusedVerdicts,
|
|
729
|
-
caps,
|
|
730
|
-
epicId,
|
|
731
|
-
storyId,
|
|
732
|
-
appendSignal,
|
|
733
|
-
forEachLine,
|
|
734
|
-
config,
|
|
735
|
-
logger,
|
|
736
|
-
});
|
|
737
|
-
}
|
|
738
|
-
if (committed.length === 0) {
|
|
739
|
-
return { status: 'skipped', reason: 'no-baseline-drift' };
|
|
740
|
-
}
|
|
741
|
-
return {
|
|
742
|
-
status: 'committed',
|
|
743
|
-
sha: lastSha,
|
|
744
|
-
files: kinds.map((k) => k.abs),
|
|
745
|
-
committed,
|
|
746
|
-
};
|
|
747
|
-
}
|