mandrel 1.94.0 → 2.1.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 +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- 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 +328 -28
- 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 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- 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 +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- 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 +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- 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/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- 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 +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- 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/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- 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 +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +3 -53
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -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 +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -8
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- 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-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- 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/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- 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/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
- 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/risk-verdict.js +0 -105
- 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/story-run-progress-writer.js +0 -400
- 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/error-journal.js +0 -139
- 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/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- 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/merge-watcher.js +0 -665
- 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/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- 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-perf-heuristics.js +0 -275
- 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/analyze-execution/SKILL.md +0 -98
- 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/workflows/helpers/single-story-deliver.md +0 -557
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* plan-critic-conditions.js —
|
|
2
|
+
* plan-critic-conditions.js — size/heuristic-conditional dispatch decisions for
|
|
3
3
|
* the /plan author-step critics (Epic #4474 PR6, design §4).
|
|
4
4
|
*
|
|
5
5
|
* The collapsed plan flow keeps the consolidation (8.3) and pre-mortem
|
|
@@ -18,10 +18,12 @@
|
|
|
18
18
|
* small draft is NOT a confirmed divergence — it skips, because a
|
|
19
19
|
* ≤-threshold draft is small enough for gate #2's single-view review
|
|
20
20
|
* to catch a distorted shape without a dedicated sub-agent.
|
|
21
|
-
* - **Pre-mortem (8.5)**: dispatch when the
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* - **Pre-mortem (8.5)**: dispatch when the ticket count is at least half
|
|
22
|
+
* of `maxTickets`, OR any configured `planning.riskHeuristics` phrase
|
|
23
|
+
* matches the plan text (case-insensitive substring). Story #4542 removed
|
|
24
|
+
* its third condition — the authored risk verdict's overall level — along
|
|
25
|
+
* with the verdict itself; both surviving conditions read the plan's own
|
|
26
|
+
* observable text and shape rather than a self-assessment.
|
|
25
27
|
*
|
|
26
28
|
* Under-firing risk (design PR6 note): the persist validators are
|
|
27
29
|
* unchanged hard gates and G2's cohort re-measures plan quality; every
|
|
@@ -34,7 +36,6 @@
|
|
|
34
36
|
*/
|
|
35
37
|
|
|
36
38
|
import { evaluateConsolidationPrecondition } from './consolidation-precondition.js';
|
|
37
|
-
import { deriveRiskEnvelope } from './planning-risk.js';
|
|
38
39
|
|
|
39
40
|
/**
|
|
40
41
|
* Draft-story count above which the consolidation critic fires even
|
|
@@ -105,14 +106,10 @@ export function evaluateConsolidationDispatch({ draftStories, specText }) {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
/**
|
|
108
|
-
* Decide the 8.5 pre-mortem dispatch:
|
|
109
|
-
*
|
|
109
|
+
* Decide the 8.5 pre-mortem dispatch: size ≥ ½ budget, or a risk-heuristic
|
|
110
|
+
* phrase match.
|
|
110
111
|
*
|
|
111
112
|
* @param {object} input
|
|
112
|
-
* @param {import('./planning-risk.js').RiskVerdict} input.riskVerdict -
|
|
113
|
-
* The authored `risk-verdict.json` payload; the overall level is derived
|
|
114
|
-
* deterministically from its axes (`deriveRiskEnvelope`), never trusted
|
|
115
|
-
* as a free-standing field.
|
|
116
113
|
* @param {number} input.ticketCount - Draft ticket count (0 in the
|
|
117
114
|
* single-delivery shape — no tickets exist).
|
|
118
115
|
* @param {number} input.maxTickets - The reviewability budget
|
|
@@ -120,11 +117,10 @@ export function evaluateConsolidationDispatch({ draftStories, specText }) {
|
|
|
120
117
|
* @param {string[]} [input.riskHeuristics] - `planning.riskHeuristics`
|
|
121
118
|
* phrases from the resolved config.
|
|
122
119
|
* @param {string} [input.planText] - Concatenated plan text the heuristics
|
|
123
|
-
* match against (tech spec + serialized tickets
|
|
120
|
+
* match against (tech spec + serialized tickets).
|
|
124
121
|
* @returns {CriticDispatchDecision}
|
|
125
122
|
*/
|
|
126
123
|
export function evaluatePremortemDispatch({
|
|
127
|
-
riskVerdict,
|
|
128
124
|
ticketCount,
|
|
129
125
|
maxTickets,
|
|
130
126
|
riskHeuristics = [],
|
|
@@ -137,13 +133,6 @@ export function evaluatePremortemDispatch({
|
|
|
137
133
|
}
|
|
138
134
|
const reasons = [];
|
|
139
135
|
|
|
140
|
-
const { overallLevel } = deriveRiskEnvelope(riskVerdict);
|
|
141
|
-
if (overallLevel === 'high') {
|
|
142
|
-
reasons.push(
|
|
143
|
-
'Risk verdict overall level is high — predicted-rework findings are worth a fresh-context pass.',
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
136
|
const count = Number.isInteger(ticketCount) ? ticketCount : 0;
|
|
148
137
|
if (count * 2 >= maxTickets) {
|
|
149
138
|
reasons.push(
|
|
@@ -172,7 +161,7 @@ export function evaluatePremortemDispatch({
|
|
|
172
161
|
critic: 'pre-mortem',
|
|
173
162
|
dispatch: false,
|
|
174
163
|
reasons: [
|
|
175
|
-
`
|
|
164
|
+
`Ticket count ${count} is under half the budget (maxTickets ${maxTickets}) and no planning.riskHeuristics phrase matches the plan text.`,
|
|
176
165
|
],
|
|
177
166
|
};
|
|
178
167
|
}
|
|
@@ -36,7 +36,7 @@ function resolveRiskHeuristics(config = {}) {
|
|
|
36
36
|
if (Array.isArray(config.planning?.riskHeuristics)) {
|
|
37
37
|
return config.planning.riskHeuristics;
|
|
38
38
|
}
|
|
39
|
-
return
|
|
39
|
+
return [];
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
/**
|
|
@@ -47,13 +47,12 @@ function resolveRiskHeuristics(config = {}) {
|
|
|
47
47
|
* - Consolidation: skipped outright when `tickets` is null/absent (the
|
|
48
48
|
* single-delivery shape authors no draft tickets); otherwise the
|
|
49
49
|
* deterministic precondition + size/divergence conditions.
|
|
50
|
-
* - Pre-mortem:
|
|
51
|
-
*
|
|
52
|
-
* the
|
|
50
|
+
* - Pre-mortem: ticket count at least half `maxTickets`, OR any
|
|
51
|
+
* `planning.riskHeuristics` phrase matching the plan text. Story #4542
|
|
52
|
+
* retired its authored-risk-level condition with the verdict itself.
|
|
53
53
|
*
|
|
54
54
|
* @param {{
|
|
55
55
|
* techSpecContent: string,
|
|
56
|
-
* riskVerdict: { summary?: string },
|
|
57
56
|
* tickets?: Array<object>|null,
|
|
58
57
|
* config?: object,
|
|
59
58
|
* }} args
|
|
@@ -64,7 +63,6 @@ function resolveRiskHeuristics(config = {}) {
|
|
|
64
63
|
*/
|
|
65
64
|
export function evaluatePlanCritics({
|
|
66
65
|
techSpecContent,
|
|
67
|
-
riskVerdict,
|
|
68
66
|
tickets = null,
|
|
69
67
|
config = {},
|
|
70
68
|
}) {
|
|
@@ -84,14 +82,12 @@ export function evaluatePlanCritics({
|
|
|
84
82
|
});
|
|
85
83
|
|
|
86
84
|
const premortem = evaluatePremortemDispatch({
|
|
87
|
-
riskVerdict,
|
|
88
85
|
ticketCount: ticketList?.length ?? 0,
|
|
89
86
|
maxTickets: getLimits(config).maxTickets,
|
|
90
87
|
riskHeuristics: resolveRiskHeuristics(config),
|
|
91
88
|
planText: [
|
|
92
89
|
techSpecContent ?? '',
|
|
93
90
|
ticketList ? JSON.stringify(ticketList) : '',
|
|
94
|
-
riskVerdict?.summary ?? '',
|
|
95
91
|
].join('\n'),
|
|
96
92
|
});
|
|
97
93
|
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* phase is removed, every plan CLI invocation stamps an entry/exit record so
|
|
6
6
|
* the current 12-phase baseline is captured on disk. Each record is one
|
|
7
7
|
* newline-terminated JSON line appended to
|
|
8
|
-
* `temp/
|
|
8
|
+
* `temp/run-<id>/plan-metrics.json` (per-Epic plan CLIs) or
|
|
9
9
|
* `temp/standalone/plan-metrics.json` (the standalone `story-plan.js` path
|
|
10
10
|
* and Epic-less healthcheck runs — same standalone routing the friction
|
|
11
11
|
* ledger uses).
|
|
@@ -61,7 +61,7 @@ import path from 'node:path';
|
|
|
61
61
|
|
|
62
62
|
import {
|
|
63
63
|
anchorTempRoot,
|
|
64
|
-
|
|
64
|
+
runArtifactPath,
|
|
65
65
|
tempRootFrom,
|
|
66
66
|
} from '../config/temp-paths.js';
|
|
67
67
|
import { Logger } from '../Logger.js';
|
|
@@ -95,7 +95,7 @@ export function planMetricsPath(epicId, config) {
|
|
|
95
95
|
PLAN_METRICS_BASENAME,
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
|
-
return
|
|
98
|
+
return runArtifactPath(epicId, PLAN_METRICS_BASENAME, config);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
@@ -314,7 +314,8 @@ export async function readPlanMetrics(epicId, config) {
|
|
|
314
314
|
|
|
315
315
|
/**
|
|
316
316
|
* Roll a read ledger up into the compact summary surfaced by the persist
|
|
317
|
-
* summary
|
|
317
|
+
* summary. (Story #4545 deleted its second consumer, `analyze-execution.js`,
|
|
318
|
+
* with the execution-analysis surface.) Returns `null` when there is nothing
|
|
318
319
|
* to summarize (missing ledger or zero parseable entries).
|
|
319
320
|
*
|
|
320
321
|
* Critic-skip records (kind: 'critic-skip') are counted separately from
|
|
@@ -322,7 +323,15 @@ export async function readPlanMetrics(epicId, config) {
|
|
|
322
323
|
* them down, so the skip-audit trail is visible in the persist summary
|
|
323
324
|
* without inflating the turns-per-plan proxy.
|
|
324
325
|
*
|
|
326
|
+
* Pass `opts.since` (an ISO-8601 instant) to scope the roll-up to one plan
|
|
327
|
+
* run (Story #4541). The Epic-less ledger at `temp/standalone/` is shared by
|
|
328
|
+
* every plan the repo has ever run, so an unfiltered summary reported
|
|
329
|
+
* lifetime totals under a line the reader takes to describe the invocation
|
|
330
|
+
* in front of them. Records are timestamped `startedAt` (invocations) or
|
|
331
|
+
* `at` (critic skips); either at-or-after `since` is in scope.
|
|
332
|
+
*
|
|
325
333
|
* @param {{ entries: object[], malformedLines?: number }} ledger
|
|
334
|
+
* @param {{ since?: string|null }} [opts]
|
|
326
335
|
* @returns {{
|
|
327
336
|
* invocations: number,
|
|
328
337
|
* failures: number,
|
|
@@ -337,8 +346,31 @@ export async function readPlanMetrics(epicId, config) {
|
|
|
337
346
|
* malformedLines: number,
|
|
338
347
|
* }|null}
|
|
339
348
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
349
|
+
/**
|
|
350
|
+
* Timestamp a ledger record is ordered by: `startedAt` for invocation
|
|
351
|
+
* records, `at` for critic-skip records.
|
|
352
|
+
*
|
|
353
|
+
* @param {object} entry
|
|
354
|
+
* @returns {string|null}
|
|
355
|
+
*/
|
|
356
|
+
function recordTimestamp(entry) {
|
|
357
|
+
const stamp =
|
|
358
|
+
entry?.kind === PLAN_METRICS_KIND_CRITIC_SKIP ? entry.at : entry.startedAt;
|
|
359
|
+
return typeof stamp === 'string' ? stamp : null;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function summarizePlanMetrics(ledger, opts = {}) {
|
|
363
|
+
const all = ledger?.entries ?? [];
|
|
364
|
+
const since = typeof opts.since === 'string' ? opts.since : null;
|
|
365
|
+
// ISO-8601 UTC strings sort lexicographically in time order, so a string
|
|
366
|
+
// compare is a correct (and allocation-free) instant compare here.
|
|
367
|
+
const entries =
|
|
368
|
+
since === null
|
|
369
|
+
? all
|
|
370
|
+
: all.filter((e) => {
|
|
371
|
+
const stamp = recordTimestamp(e);
|
|
372
|
+
return stamp !== null && stamp >= since;
|
|
373
|
+
});
|
|
342
374
|
if (entries.length === 0) return null;
|
|
343
375
|
const byCli = {};
|
|
344
376
|
const byMode = {};
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plan-navigation.js — shared navigability helpers for the persist-side
|
|
3
|
+
* draft reachability check (`plan-reachability.js`).
|
|
4
|
+
*
|
|
5
|
+
* Extracted from the retired `epic-plan-healthcheck.js` so the live
|
|
6
|
+
* draft-ticket scan owns its own mechanics without pulling in the
|
|
7
|
+
* deleted post-plan healthcheck CLI.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolve the navigation config that drives the reachability check.
|
|
12
|
+
*
|
|
13
|
+
* The check is opt-in: a consumer that has not configured
|
|
14
|
+
* `planning.navigation.routeGlobs` gets a silent no-op. The nav-registry
|
|
15
|
+
* token list is what a route-adding Story is expected to reference
|
|
16
|
+
* somewhere in its body or `## Acceptance` section.
|
|
17
|
+
*
|
|
18
|
+
* @param {object} config Resolved `.agentrc.json`.
|
|
19
|
+
* @returns {{ routeGlobs: string[], navRegistry: string[] }}
|
|
20
|
+
*/
|
|
21
|
+
export function resolveNavConfig(config) {
|
|
22
|
+
const nav = config?.planning?.navigation ?? {};
|
|
23
|
+
const toList = (v) =>
|
|
24
|
+
(Array.isArray(v) ? v : v == null ? [] : [v])
|
|
25
|
+
.filter((s) => typeof s === 'string' && s.trim().length > 0)
|
|
26
|
+
.map((s) => s.trim());
|
|
27
|
+
return {
|
|
28
|
+
routeGlobs: toList(nav.routeGlobs),
|
|
29
|
+
navRegistry: toList(nav.navRegistry),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Translate a route glob (`pages/**`, `app/**\/route.ts`) into a RegExp that
|
|
35
|
+
* matches a path string. Supports `**` (any depth, including `/`), `*` (any
|
|
36
|
+
* run of non-separator chars), and `?` (single non-separator char). All other
|
|
37
|
+
* characters are matched literally.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} glob
|
|
40
|
+
* @returns {RegExp}
|
|
41
|
+
*/
|
|
42
|
+
export function globToRegExp(glob) {
|
|
43
|
+
// Collapse adjacent `**` segments before compiling. `**/**` and `***` both
|
|
44
|
+
// mean "any depth", but compiling them literally emits adjacent `.*` runs
|
|
45
|
+
// (`.*/.*` / `.*.*`) that backtrack catastrophically on a long non-matching
|
|
46
|
+
// path. Collapsing to a single `**` preserves semantics and keeps the
|
|
47
|
+
// matcher linear (ReDoS hardening).
|
|
48
|
+
const normalized = glob
|
|
49
|
+
.replace(/\*\*(?:\/\*\*)+/g, '**')
|
|
50
|
+
.replace(/\*{3,}/g, '**');
|
|
51
|
+
let re = '';
|
|
52
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
53
|
+
const ch = normalized[i];
|
|
54
|
+
if (ch === '*') {
|
|
55
|
+
if (normalized[i + 1] === '*') {
|
|
56
|
+
re += '.*';
|
|
57
|
+
i++;
|
|
58
|
+
} else {
|
|
59
|
+
re += '[^/]*';
|
|
60
|
+
}
|
|
61
|
+
} else if (ch === '?') {
|
|
62
|
+
re += '[^/]';
|
|
63
|
+
} else {
|
|
64
|
+
re += ch.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return new RegExp(`^${re}$`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Extract the candidate route-touching paths a Story declares. Reads the
|
|
72
|
+
* `## Changes` block (the decompose-author emits one `{"path":...}` JSON
|
|
73
|
+
* object per bullet) and falls back to any bare `` `path/like/this` ``
|
|
74
|
+
* inline-code spans in the body.
|
|
75
|
+
*
|
|
76
|
+
* @param {string} body
|
|
77
|
+
* @returns {string[]}
|
|
78
|
+
*/
|
|
79
|
+
export function extractStoryPaths(body) {
|
|
80
|
+
if (typeof body !== 'string' || body.length === 0) return [];
|
|
81
|
+
const paths = new Set();
|
|
82
|
+
// `{"path":"pages/foo.tsx", ...}` change descriptors.
|
|
83
|
+
for (const m of body.matchAll(/"path"\s*:\s*"([^"]+)"/g)) {
|
|
84
|
+
paths.add(m[1]);
|
|
85
|
+
}
|
|
86
|
+
// Inline-code spans that look like a path (contain a slash or a dotted ext).
|
|
87
|
+
for (const m of body.matchAll(/`([^`]+)`/g)) {
|
|
88
|
+
const token = m[1].trim();
|
|
89
|
+
if (/[/.]/.test(token) && !token.includes(' ')) paths.add(token);
|
|
90
|
+
}
|
|
91
|
+
return [...paths];
|
|
92
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fan-out / soft-conflict gates used by plan-persist (extracted from the
|
|
3
|
+
* retired epic-plan-decompose persist phase in Stage 5).
|
|
4
|
+
*
|
|
5
|
+
* @module lib/orchestration/plan-persist/fan-out-gate
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { Logger } from '../../Logger.js';
|
|
9
|
+
import {
|
|
10
|
+
renderFanOutEvidence,
|
|
11
|
+
renderFanOutRemedy,
|
|
12
|
+
renderHardConflictError,
|
|
13
|
+
} from '../ticket-validator-conflicts.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param {object[]} findings
|
|
17
|
+
* @param {boolean} allowLargeFanOut
|
|
18
|
+
* @param {string} [tag]
|
|
19
|
+
*/
|
|
20
|
+
export function enforceFanOutGate(
|
|
21
|
+
findings,
|
|
22
|
+
allowLargeFanOut,
|
|
23
|
+
tag = 'plan-persist',
|
|
24
|
+
) {
|
|
25
|
+
const fanOut = (findings ?? []).filter((f) => f.kind === 'fan-out-warning');
|
|
26
|
+
if (fanOut.length === 0) return;
|
|
27
|
+
if (allowLargeFanOut) {
|
|
28
|
+
for (const f of fanOut) {
|
|
29
|
+
Logger.warn(
|
|
30
|
+
`[${tag}] Persisting a large-fan-out deletion: ` +
|
|
31
|
+
`Story "${f.storySlug}" deletes "${f.path}" with ${f.callSiteCount} ` +
|
|
32
|
+
`importer(s) (threshold ${f.threshold}). Operator override --allow-large-fan-out.` +
|
|
33
|
+
renderFanOutEvidence(f),
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const lines = fanOut
|
|
39
|
+
.map(
|
|
40
|
+
(f) =>
|
|
41
|
+
` - Story "${f.storySlug}" deletes "${f.path}" — ` +
|
|
42
|
+
`${f.callSiteCount} importer(s) (threshold ${f.threshold})` +
|
|
43
|
+
renderFanOutEvidence(f),
|
|
44
|
+
)
|
|
45
|
+
.join('\n');
|
|
46
|
+
// Each finding carries its own remedy — a rename-shaped deletion has no
|
|
47
|
+
// subsystems to split across, so a blanket "split it up" footer would be
|
|
48
|
+
// wrong advice for it (Story #4547).
|
|
49
|
+
const remedies = [...new Set(fanOut.map((f) => renderFanOutRemedy(f)))];
|
|
50
|
+
throw new Error(
|
|
51
|
+
`[${tag}] ${fanOut.length} Task(s) declare large-fan-out deletions:\n${lines}\n\n` +
|
|
52
|
+
remedies.join('\n\n'),
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* @param {object[]} findings
|
|
58
|
+
* @param {string} [tag]
|
|
59
|
+
*/
|
|
60
|
+
export function surfaceSoftConflictFindings(findings, tag = 'plan-persist') {
|
|
61
|
+
const soft = (findings ?? []).filter(
|
|
62
|
+
(f) => f?.severity === 'soft' && f?.kind !== 'fan-out-warning',
|
|
63
|
+
);
|
|
64
|
+
if (soft.length === 0) return;
|
|
65
|
+
Logger.warn(
|
|
66
|
+
`[${tag}] ${soft.length} soft cross-Story conflict finding(s) — review before approving the plan:`,
|
|
67
|
+
);
|
|
68
|
+
for (const finding of soft) {
|
|
69
|
+
Logger.warn(`[${tag}] soft conflict: ${renderHardConflictError(finding)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* persist-helpers.js — pure helper surface for the flat Story `/plan` persist.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* - `resolveBaseBranchRef(config)` — the one place the persist gates learn
|
|
6
|
+
* which ref to probe.
|
|
7
|
+
* - `validateTickets(tickets, config)` — runs the cross-link, model-capacity,
|
|
8
|
+
* freshness, and task-body validators in one pass. Capacity settings are
|
|
9
|
+
* explicit inputs so the validator and decomposer share one live delivery
|
|
10
|
+
* envelope instead of silently falling back to framework defaults.
|
|
11
|
+
* - `makeDefaultFanOutCounter({ baseBranchRef, cwd, git })` — production
|
|
12
|
+
* fan-out probe used by the conflict policy.
|
|
13
|
+
*
|
|
14
|
+
* @module lib/orchestration/plan-persist/persist-helpers
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import posix from 'node:path/posix';
|
|
18
|
+
import { resolveListValue } from '../../config/shared.js';
|
|
19
|
+
import { gitSpawn } from '../../git-utils.js';
|
|
20
|
+
import { validateTaskBodies } from '../task-body-validator.js';
|
|
21
|
+
import { validateAndNormalizeTickets } from '../ticket-validator.js';
|
|
22
|
+
import { DEFAULT_REGISTRY_PATTERNS } from '../ticket-validator-conflicts.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Extensions an import specifier may elide. Probed longest-path-first when
|
|
26
|
+
* resolving an extensionless specifier back onto a concrete repo path.
|
|
27
|
+
*/
|
|
28
|
+
const RESOLVABLE_EXTENSIONS = ['', '.js', '.mjs', '.cjs', '.jsx', '.json'];
|
|
29
|
+
|
|
30
|
+
/** Every quoted string on a candidate line — the specifier lives in one. */
|
|
31
|
+
const QUOTED_RE = /['"]([^'"\n]+)['"]/g;
|
|
32
|
+
|
|
33
|
+
function escapeRegExp(value) {
|
|
34
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The specifier tails an importer of `path` could plausibly write as the
|
|
39
|
+
* final segment of its quoted specifier: the basename, the basename minus
|
|
40
|
+
* its extension, and — for a directory-index module — the directory name
|
|
41
|
+
* (`./foo` resolving to `foo/index.js`).
|
|
42
|
+
*
|
|
43
|
+
* This is a *candidate* net only. It is deliberately generous because the
|
|
44
|
+
* resolution pass below re-checks every hit against the real path; a tail
|
|
45
|
+
* that over-matches costs one extra resolve, a tail that under-matches
|
|
46
|
+
* loses a genuine importer.
|
|
47
|
+
*/
|
|
48
|
+
function specifierTails(path) {
|
|
49
|
+
const base = posix.basename(path);
|
|
50
|
+
const ext = posix.extname(base);
|
|
51
|
+
const stem = ext ? base.slice(0, -ext.length) : base;
|
|
52
|
+
const tails = new Set([base, stem]);
|
|
53
|
+
if (stem === 'index') {
|
|
54
|
+
const dir = posix.basename(posix.dirname(path));
|
|
55
|
+
if (dir && dir !== '.') tails.add(dir);
|
|
56
|
+
}
|
|
57
|
+
return [...tails].filter((t) => t.length > 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* ERE matching a line that carries an `import` / `export … from` /
|
|
62
|
+
* `require(` / dynamic `import(` whose quoted specifier *ends* at one of
|
|
63
|
+
* `tails`. The `(^|/)`-equivalent guard (`([^'"]*\/)?`) is what keeps
|
|
64
|
+
* `notification.js` from matching `push-notification.js`.
|
|
65
|
+
*/
|
|
66
|
+
function buildProbePattern(tails) {
|
|
67
|
+
const alt = tails.map(escapeRegExp).join('|');
|
|
68
|
+
return `(from|require|import)[[:space:]]*\\(?[[:space:]]*['"]([^'"]*/)?(${alt})['"]`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a relative specifier written in `importerPath` back onto a repo
|
|
73
|
+
* path and report whether it names `deletedPath`.
|
|
74
|
+
*
|
|
75
|
+
* **Known boundary:** only relative (`./`, `../`) specifiers resolve. A
|
|
76
|
+
* consumer repo that imports its own modules through bare specifiers or a
|
|
77
|
+
* path alias (`#lib/x`, `@app/x`, a `tsconfig` `paths` entry) would
|
|
78
|
+
* under-count, because resolving those needs the resolver config this probe
|
|
79
|
+
* deliberately does not read. Mandrel's own internal imports are all
|
|
80
|
+
* relative. Under-counting is the *quiet* failure direction — it argues for
|
|
81
|
+
* a deletion rather than against one — so if alias-importing consumers
|
|
82
|
+
* appear, this is the place to teach the probe their resolver.
|
|
83
|
+
*/
|
|
84
|
+
function specifierResolvesTo(importerPath, specifier, deletedPath) {
|
|
85
|
+
if (!specifier.startsWith('./') && !specifier.startsWith('../')) return false;
|
|
86
|
+
const resolved = posix.normalize(
|
|
87
|
+
posix.join(posix.dirname(importerPath), specifier),
|
|
88
|
+
);
|
|
89
|
+
for (const ext of RESOLVABLE_EXTENSIONS) {
|
|
90
|
+
if (`${resolved}${ext}` === deletedPath) return true;
|
|
91
|
+
if (ext && `${resolved}/index${ext}` === deletedPath) return true;
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Quote one argv entry so the reported probe is **runnable as emitted**.
|
|
98
|
+
*
|
|
99
|
+
* The probe is the operator's route to checking the number, so it has to
|
|
100
|
+
* survive a paste into a shell. Unquoted, the ERE's `(`, `|`, `[[:space:]]`
|
|
101
|
+
* and `?` are glob/grouping metacharacters: zsh fails the paste with
|
|
102
|
+
* `no matches found` *and exits 0*, which reads as "zero importers" — the
|
|
103
|
+
* gate's own audit trail would then argue for the deletion it is meant to
|
|
104
|
+
* question (Story #4547).
|
|
105
|
+
*/
|
|
106
|
+
function shellQuote(value) {
|
|
107
|
+
if (/^[A-Za-z0-9_./-]+$/.test(value)) return value;
|
|
108
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Parse one `git grep -n` output line of the form `<ref>:<path>:<lineno>:<text>`.
|
|
113
|
+
*/
|
|
114
|
+
function parseGrepLine(line, baseBranchRef) {
|
|
115
|
+
const prefix = `${baseBranchRef}:`;
|
|
116
|
+
if (!line.startsWith(prefix)) return null;
|
|
117
|
+
const rest = line.slice(prefix.length);
|
|
118
|
+
const pathEnd = rest.indexOf(':');
|
|
119
|
+
if (pathEnd === -1) return null;
|
|
120
|
+
const path = rest.slice(0, pathEnd);
|
|
121
|
+
const afterPath = rest.slice(pathEnd + 1);
|
|
122
|
+
const lineEnd = afterPath.indexOf(':');
|
|
123
|
+
if (lineEnd === -1) return null;
|
|
124
|
+
return { path, text: afterPath.slice(lineEnd + 1) };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Default fan-out probe — resolves the *importers* of the deleted module at
|
|
129
|
+
* `baseBranchRef`, and reports the referencing files alongside the exact
|
|
130
|
+
* probe that produced them.
|
|
131
|
+
*
|
|
132
|
+
* Two-stage, because accuracy and cost pull in opposite directions:
|
|
133
|
+
*
|
|
134
|
+
* 1. `git grep -n -E` narrows the tree to lines whose quoted import /
|
|
135
|
+
* require specifier could name the module (final-segment match).
|
|
136
|
+
* 2. Each candidate specifier is resolved against its own importer's
|
|
137
|
+
* directory and compared to the deleted path. Only a real resolution
|
|
138
|
+
* counts.
|
|
139
|
+
*
|
|
140
|
+
* The predecessor (Story #2962) grepped the basename stem as a bare word
|
|
141
|
+
* across the whole tree, so a module named `notification` or `options`
|
|
142
|
+
* reported dozens of call sites drawn from prose, schemas, and unrelated
|
|
143
|
+
* modules — and the gate that fired on that number told the operator to
|
|
144
|
+
* split a migration that did not exist. It also returned 0 without probing
|
|
145
|
+
* for any stem under three characters, under-reporting in silence. Both are
|
|
146
|
+
* gone: coupling is measured by resolution, not vocabulary (Story #4547).
|
|
147
|
+
*
|
|
148
|
+
* @returns {(arg: { path: string }) => { count: number, files: string[], probe: string }}
|
|
149
|
+
*/
|
|
150
|
+
export function makeDefaultFanOutCounter({ baseBranchRef, cwd, git } = {}) {
|
|
151
|
+
const spawn = git?.gitSpawn ?? gitSpawn;
|
|
152
|
+
return ({ path }) => {
|
|
153
|
+
const tails = specifierTails(path);
|
|
154
|
+
const pattern = buildProbePattern(tails);
|
|
155
|
+
const args = ['grep', '-n', '-E', '--full-name', pattern, baseBranchRef];
|
|
156
|
+
const probe = `git ${args.map(shellQuote).join(' ')}`;
|
|
157
|
+
const result = spawn(cwd ?? process.cwd(), ...args);
|
|
158
|
+
// git grep exits 1 on "no matches" — an empty result, not a failure.
|
|
159
|
+
if (result.status !== 0) return { count: 0, files: [], probe };
|
|
160
|
+
const files = new Set();
|
|
161
|
+
for (const line of result.stdout.split('\n')) {
|
|
162
|
+
if (line.trim().length === 0) continue;
|
|
163
|
+
const hit = parseGrepLine(line, baseBranchRef);
|
|
164
|
+
// The deleted module's own self-references are not call sites.
|
|
165
|
+
if (!hit || hit.path === path) continue;
|
|
166
|
+
for (const match of hit.text.matchAll(QUOTED_RE)) {
|
|
167
|
+
if (specifierResolvesTo(hit.path, match[1], path)) {
|
|
168
|
+
files.add(hit.path);
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const sorted = [...files].sort();
|
|
174
|
+
return { count: sorted.length, files: sorted, probe };
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Resolve the cross-Story conflict-finding policy from `_config.planning`.
|
|
180
|
+
*/
|
|
181
|
+
function resolveConflictPolicy(cfg) {
|
|
182
|
+
const planning = cfg?.planning;
|
|
183
|
+
const policy = {
|
|
184
|
+
failOnSharedEditors: planning?.failOnSharedEditors === true,
|
|
185
|
+
requireExplicitCrossStoryDeps:
|
|
186
|
+
planning?.requireExplicitCrossStoryDeps === true,
|
|
187
|
+
failOnRegistryConflicts: planning?.failOnRegistryConflicts === true,
|
|
188
|
+
failOnLargeFanOut: planning?.failOnLargeFanOut === true,
|
|
189
|
+
};
|
|
190
|
+
if (Number.isFinite(planning?.largeFanOutThreshold)) {
|
|
191
|
+
policy.largeFanOutThreshold = planning.largeFanOutThreshold;
|
|
192
|
+
}
|
|
193
|
+
if (planning?.crossCuttingRegistries !== undefined) {
|
|
194
|
+
policy.registries = resolveListValue(
|
|
195
|
+
DEFAULT_REGISTRY_PATTERNS,
|
|
196
|
+
planning.crossCuttingRegistries,
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
return policy;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Resolve the ref the persist gates probe against.
|
|
204
|
+
*
|
|
205
|
+
* The canonical resolved config carries the base branch at
|
|
206
|
+
* `project.baseBranch` (`lib/config-resolver.js` defaults it to `main`).
|
|
207
|
+
* This helper used to read `config.baseBranch` — a key the resolver never
|
|
208
|
+
* produces — so every freshness / file-assumption / fan-out probe silently
|
|
209
|
+
* targeted the literal `main` regardless of configuration. Benign in a repo
|
|
210
|
+
* whose base branch *is* `main`; wrong for any consumer that configured
|
|
211
|
+
* something else (Story #4541).
|
|
212
|
+
*
|
|
213
|
+
* The flat `config.baseBranch` fallback is retained for the legacy
|
|
214
|
+
* `settings`-bag callers that pass `{ baseBranch, paths, planning }`.
|
|
215
|
+
*
|
|
216
|
+
* @param {object} [config] Resolved config, or a legacy settings bag.
|
|
217
|
+
* @returns {string}
|
|
218
|
+
*/
|
|
219
|
+
export function resolveBaseBranchRef(config) {
|
|
220
|
+
return config?.project?.baseBranch ?? config?.baseBranch ?? 'main';
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function validateTickets(tickets, config, opts = {}) {
|
|
224
|
+
const baseBranchRef = resolveBaseBranchRef(config);
|
|
225
|
+
const conflictPolicy = resolveConflictPolicy(config);
|
|
226
|
+
if (typeof opts.fanOutCounter === 'function') {
|
|
227
|
+
conflictPolicy.fanOutCounter = opts.fanOutCounter;
|
|
228
|
+
} else {
|
|
229
|
+
conflictPolicy.fanOutCounter = makeDefaultFanOutCounter({
|
|
230
|
+
baseBranchRef,
|
|
231
|
+
cwd: opts.cwd,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
const validated = validateAndNormalizeTickets(tickets, {
|
|
235
|
+
baseBranchRef,
|
|
236
|
+
conflictPolicy,
|
|
237
|
+
modelCapacity: opts.modelCapacity,
|
|
238
|
+
// Thread the repo cwd into the AC-freshness / file-assumption git
|
|
239
|
+
// probes (#4474 PR7) — without it they silently ran against
|
|
240
|
+
// process.cwd(), which is only the repo root by coincidence.
|
|
241
|
+
cwd: opts.cwd,
|
|
242
|
+
});
|
|
243
|
+
validateTaskBodies(validated);
|
|
244
|
+
return validated;
|
|
245
|
+
}
|