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,658 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* merge-runner.js — collapse the duplicated epic-merge-lock try/finally and
|
|
3
|
-
* the duplicated `pushEpicWithRetry` + `PushRetryConflictError` envelope
|
|
4
|
-
* into two reusable helpers used by both finalizeMerge and
|
|
5
|
-
* completeInProgressMerge.
|
|
6
|
-
*
|
|
7
|
-
* Extracted from story-close.js (Story #955, Theme A part 1) so the close
|
|
8
|
-
* orchestrator becomes a thin CLI shell.
|
|
9
|
-
*
|
|
10
|
-
* `withEpicMergeLock` wraps acquire → user fn → release in a single
|
|
11
|
-
* try/finally with consistent `🔒 Acquired` / `🔓 Released` log lines.
|
|
12
|
-
* Acquisition failure throws a single, operator-actionable Error mentioning
|
|
13
|
-
* the lock-file path so a stale lock can be cleared by hand.
|
|
14
|
-
*
|
|
15
|
-
* `pushEpicAndHandleConflicts` wraps `pushEpicWithRetry` + the
|
|
16
|
-
* `PushRetryConflictError` → throw envelope, plus the retry-exhausted / * generic-failure → throw envelope used by `runFinalizeMerge`. The resume
|
|
17
|
-
* path (`runResumeMerge`) shares the same envelope but routes generic
|
|
18
|
-
* failures through the `describeResumePushFailure` helper for consistent
|
|
19
|
-
* operator-facing copy (see `comment-bodies.js`). Errors thrown from these
|
|
20
|
-
* helpers reach the `runAsCli` boundary in `story-close.js` and are mapped
|
|
21
|
-
* to `process.exit(1)` (Story #959 — close-tail scripts must throw rather
|
|
22
|
-
* than route through the logger's fatal sink, see
|
|
23
|
-
* `.agents/instructions.md`).
|
|
24
|
-
*
|
|
25
|
-
* Both helpers are dependency-injected: the lock acquire/release pair and
|
|
26
|
-
* the push retry runner are parameters so unit tests can pin behaviour
|
|
27
|
-
* without spawning the close script. Default arguments point at the
|
|
28
|
-
* production wiring from `lib/epic-merge-lock.js` + `lib/push-epic-retry.js`.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
import fs from 'node:fs';
|
|
32
|
-
import path from 'node:path';
|
|
33
|
-
import { getRunners as defaultGetRunners } from '../../config/runners.js';
|
|
34
|
-
import { resolveWorkingPath } from '../../config-resolver.js';
|
|
35
|
-
import {
|
|
36
|
-
acquireEpicMergeLock as defaultAcquire,
|
|
37
|
-
releaseEpicMergeLock as defaultRelease,
|
|
38
|
-
resolveGitCommonDir,
|
|
39
|
-
} from '../../epic-merge-lock.js';
|
|
40
|
-
import { mergeFeatureBranch } from '../../git-merge-orchestrator.js';
|
|
41
|
-
import {
|
|
42
|
-
gitSpawn as defaultGitSpawn,
|
|
43
|
-
gitSync as defaultGitSync,
|
|
44
|
-
} from '../../git-utils.js';
|
|
45
|
-
import { Logger as DefaultLogger } from '../../Logger.js';
|
|
46
|
-
import {
|
|
47
|
-
pushEpicWithRetry as defaultPushEpicWithRetry,
|
|
48
|
-
PushRetryConflictError,
|
|
49
|
-
} from '../../push-epic-retry.js';
|
|
50
|
-
import {
|
|
51
|
-
buildResumeMergeCommitMsg,
|
|
52
|
-
describeResumePushFailure,
|
|
53
|
-
} from './comment-bodies.js';
|
|
54
|
-
import {
|
|
55
|
-
buildMergeMessageWithCap,
|
|
56
|
-
loadHeaderMaxLength,
|
|
57
|
-
} from './merge-subject.js';
|
|
58
|
-
import {
|
|
59
|
-
assertNoForeignEpicLock as defaultAssertNoForeignEpicLock,
|
|
60
|
-
assertSharedCheckoutAvailable as defaultAssertSharedCheckoutAvailable,
|
|
61
|
-
} from './shared-checkout-guard.js';
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Render the lock-file path for a given main-repo `cwd` + `epicId`. Pure;
|
|
65
|
-
* exported so the operator-facing error message stays a single source of
|
|
66
|
-
* truth.
|
|
67
|
-
*/
|
|
68
|
-
export function lockPathDisplay(cwd, epicId) {
|
|
69
|
-
return path.join(resolveGitCommonDir(cwd), `epic-${epicId}.merge.lock`);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Best-effort `story.blocked` lifecycle emit (Story #2241 / Task #2247).
|
|
74
|
-
* Used by the merge runner's failure paths to surface a typed `reason`
|
|
75
|
-
* on the bus before re-throwing. The bus is optional — every legacy
|
|
76
|
-
* caller and the existing unit fixtures pass `null` — and emit failures
|
|
77
|
-
* are swallowed so the original throw (which the operator must see)
|
|
78
|
-
* always reaches `runAsCli`.
|
|
79
|
-
*
|
|
80
|
-
* Exported so the wider story-close path (and the spawn-timeout helper
|
|
81
|
-
* in `story-close.js`) can use the same low-friction wrapper.
|
|
82
|
-
*
|
|
83
|
-
* @param {{
|
|
84
|
-
* bus?: object|null,
|
|
85
|
-
* storyId: number|string,
|
|
86
|
-
* reason: string,
|
|
87
|
-
* logger?: { warn?: Function },
|
|
88
|
-
* }} args
|
|
89
|
-
* @returns {Promise<void>}
|
|
90
|
-
*/
|
|
91
|
-
export async function emitStoryBlockedSafe({ bus, storyId, reason, logger }) {
|
|
92
|
-
if (!bus) return;
|
|
93
|
-
try {
|
|
94
|
-
await bus.emit('story.blocked', {
|
|
95
|
-
storyId: Number(storyId),
|
|
96
|
-
reason: String(reason),
|
|
97
|
-
});
|
|
98
|
-
} catch (err) {
|
|
99
|
-
logger?.warn?.(
|
|
100
|
-
`[story-close] ⚠️ story.blocked emit failed for #${storyId} (swallowed): ${err?.message ?? err}`,
|
|
101
|
-
);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Canonical "blocked close-result" envelope builder used by all four
|
|
107
|
-
* pre-merge and merge-phase blocked exits (Story #3638 — duplication
|
|
108
|
-
* noted by audit fe90dd9c36fc).
|
|
109
|
-
*
|
|
110
|
-
* The four previously-parallel patterns each:
|
|
111
|
-
* 1. Build a `{ success: false, status: 'blocked', phase, reason, …extra }` result.
|
|
112
|
-
* 2. Call `emitStoryBlockedSafe` (best-effort bus emit, optional).
|
|
113
|
-
* 3. Print the `--- STORY CLOSE RESULT ---` banner via `Logger.info`.
|
|
114
|
-
* 4. Call `progress('BLOCKED', blockedMessage)` for the operator-facing line.
|
|
115
|
-
*
|
|
116
|
-
* This helper centralises all four steps so callers supply only their
|
|
117
|
-
* domain-specific fields via `extra` and the human-facing `blockedMessage`.
|
|
118
|
-
* Bus emission is skipped when `bus` is `null`/`undefined` (the same
|
|
119
|
-
* semantics as `emitStoryBlockedSafe`).
|
|
120
|
-
*
|
|
121
|
-
* @param {{
|
|
122
|
-
* storyId: number|string,
|
|
123
|
-
* phase: string,
|
|
124
|
-
* reason: string,
|
|
125
|
-
* extra?: object,
|
|
126
|
-
* bus?: object|null,
|
|
127
|
-
* progress: (tag: string, msg: string) => void,
|
|
128
|
-
* blockedMessage: string,
|
|
129
|
-
* logger?: { info?: Function, warn?: Function },
|
|
130
|
-
* }} args
|
|
131
|
-
* @returns {Promise<{ success: false, status: 'blocked', phase: string, reason: string, [key: string]: unknown }>}
|
|
132
|
-
*/
|
|
133
|
-
export async function emitBlockedCloseResult({
|
|
134
|
-
storyId,
|
|
135
|
-
phase,
|
|
136
|
-
reason,
|
|
137
|
-
extra = {},
|
|
138
|
-
bus = null,
|
|
139
|
-
progress,
|
|
140
|
-
blockedMessage,
|
|
141
|
-
logger = DefaultLogger,
|
|
142
|
-
}) {
|
|
143
|
-
const result = { success: false, status: 'blocked', phase, reason, ...extra };
|
|
144
|
-
await emitStoryBlockedSafe({ bus, storyId, reason, logger });
|
|
145
|
-
logger.info?.(
|
|
146
|
-
`\n--- STORY CLOSE RESULT ---\n${JSON.stringify(result, null, 2)}\n--- END RESULT ---\n`,
|
|
147
|
-
);
|
|
148
|
-
progress('BLOCKED', blockedMessage);
|
|
149
|
-
return result;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Acquire the per-Epic filesystem merge lock, run `fn(handle)` inside a
|
|
154
|
-
* try/finally, and always release. Logs `🔒 Acquired ...` at acquire and
|
|
155
|
-
* `🔓 Released epic-merge lock` on release via the supplied `log` sink.
|
|
156
|
-
*
|
|
157
|
-
* @template T
|
|
158
|
-
* @param {number|string} epicId
|
|
159
|
-
* @param {{
|
|
160
|
-
* repoRoot: string,
|
|
161
|
-
* timeoutMs?: number,
|
|
162
|
-
* log?: (tag: string, msg: string) => void,
|
|
163
|
-
* acquire?: typeof defaultAcquire,
|
|
164
|
-
* release?: typeof defaultRelease,
|
|
165
|
-
* }} opts
|
|
166
|
-
* @param {(handle: object) => Promise<T> | T} fn
|
|
167
|
-
* @returns {Promise<T>}
|
|
168
|
-
*/
|
|
169
|
-
export async function withEpicMergeLock(
|
|
170
|
-
epicId,
|
|
171
|
-
{
|
|
172
|
-
repoRoot,
|
|
173
|
-
timeoutMs = 60_000,
|
|
174
|
-
log = () => {},
|
|
175
|
-
acquire = defaultAcquire,
|
|
176
|
-
release = defaultRelease,
|
|
177
|
-
},
|
|
178
|
-
fn,
|
|
179
|
-
) {
|
|
180
|
-
log('LOCK', `Acquiring epic-merge lock for epic #${epicId}...`);
|
|
181
|
-
let lockHandle;
|
|
182
|
-
try {
|
|
183
|
-
lockHandle = await acquire(epicId, { repoRoot, timeoutMs });
|
|
184
|
-
} catch (err) {
|
|
185
|
-
throw new Error(
|
|
186
|
-
`Could not acquire epic-merge lock for epic #${epicId}: ${err.message}. ` +
|
|
187
|
-
`Another story closure may be in progress, or a stale lock is present at ` +
|
|
188
|
-
`${lockPathDisplay(repoRoot, epicId)} — inspect and remove it manually if no ` +
|
|
189
|
-
`other process is running.`,
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
log('LOCK', `🔒 Acquired ${path.basename(lockHandle.filePath)}`);
|
|
193
|
-
try {
|
|
194
|
-
return await fn(lockHandle);
|
|
195
|
-
} finally {
|
|
196
|
-
release(lockHandle);
|
|
197
|
-
log('LOCK', '🔓 Released epic-merge lock');
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Push the Epic branch with retry, surfacing `PushRetryConflictError` and
|
|
203
|
-
* generic failure modes by throwing an `Error` (Story #959 — see file
|
|
204
|
-
* header). Used by both `runFinalizeMerge` (post-merge push) and
|
|
205
|
-
* `runResumeMerge` (resume-after-conflict push).
|
|
206
|
-
*
|
|
207
|
-
* The two callers diverge only on how they format generic-failure copy:
|
|
208
|
-
* - finalize path inlines the `retries-exhausted vs other-reason` switch
|
|
209
|
-
* directly,
|
|
210
|
-
* - resume path routes through `describeResumePushFailure`.
|
|
211
|
-
*
|
|
212
|
-
* Pass `mode: 'resume'` to use the resume-style copy.
|
|
213
|
-
*
|
|
214
|
-
* @param {{
|
|
215
|
-
* cwd: string,
|
|
216
|
-
* epicBranch: string,
|
|
217
|
-
* storyBranch: string,
|
|
218
|
-
* config: object,
|
|
219
|
-
* log?: (msg: string) => void,
|
|
220
|
-
* mode?: 'finalize' | 'resume',
|
|
221
|
-
* pushEpicWithRetry?: typeof defaultPushEpicWithRetry,
|
|
222
|
-
* git?: { gitSpawn: typeof defaultGitSpawn },
|
|
223
|
-
* getRunners?: (config: object) => any,
|
|
224
|
-
* }} opts
|
|
225
|
-
* @returns {Promise<{ ok: boolean, attempts: number, reason?: string, result?: object }>}
|
|
226
|
-
*/
|
|
227
|
-
export async function pushEpicAndHandleConflicts({
|
|
228
|
-
cwd,
|
|
229
|
-
epicBranch,
|
|
230
|
-
storyBranch,
|
|
231
|
-
config,
|
|
232
|
-
log = () => {},
|
|
233
|
-
mode = 'finalize',
|
|
234
|
-
mergeMessage = null,
|
|
235
|
-
pushEpicWithRetry = defaultPushEpicWithRetry,
|
|
236
|
-
git = { gitSpawn: defaultGitSpawn },
|
|
237
|
-
getRunners = defaultGetRunners,
|
|
238
|
-
}) {
|
|
239
|
-
let pushOutcome;
|
|
240
|
-
try {
|
|
241
|
-
pushOutcome = await pushEpicWithRetry({
|
|
242
|
-
cwd,
|
|
243
|
-
epicBranch,
|
|
244
|
-
storyBranch,
|
|
245
|
-
storyMergeRetry: getRunners(config).storyMergeRetry,
|
|
246
|
-
git,
|
|
247
|
-
log,
|
|
248
|
-
mergeMessage,
|
|
249
|
-
});
|
|
250
|
-
} catch (err) {
|
|
251
|
-
if (err instanceof PushRetryConflictError) {
|
|
252
|
-
throw new Error(err.message);
|
|
253
|
-
}
|
|
254
|
-
throw err;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!pushOutcome.ok) {
|
|
258
|
-
if (mode === 'resume') {
|
|
259
|
-
const fatal = describeResumePushFailure(pushOutcome);
|
|
260
|
-
if (fatal) throw new Error(fatal);
|
|
261
|
-
} else {
|
|
262
|
-
const reasonLabel =
|
|
263
|
-
pushOutcome.reason === 'retry-exhausted'
|
|
264
|
-
? `retries exhausted after ${pushOutcome.attempts} attempt(s)`
|
|
265
|
-
: pushOutcome.reason;
|
|
266
|
-
throw new Error(
|
|
267
|
-
`Push failed (${reasonLabel}): ${pushOutcome.result?.stderr || pushOutcome.result?.stdout || 'unknown'}`,
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return pushOutcome;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// ---------------------------------------------------------------------------
|
|
275
|
-
// Story-close merge sequencing
|
|
276
|
-
// ---------------------------------------------------------------------------
|
|
277
|
-
//
|
|
278
|
-
// `runFinalizeMerge` and `runResumeMerge` previously lived inline in
|
|
279
|
-
// `story-close.js`. They both take the per-Epic merge lock, then either
|
|
280
|
-
// rebase + merge + push (finalize path) or commit-the-pending-merge + push
|
|
281
|
-
// (resume path). Extracted from story-close.js (Story #956, Theme A finishing
|
|
282
|
-
// touch) so the close orchestrator becomes a thin CLI shell. Both helpers
|
|
283
|
-
// take the same dependency-injection seams as withEpicMergeLock /
|
|
284
|
-
// pushEpicAndHandleConflicts so tests can pin behaviour without spawning
|
|
285
|
-
// the script.
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* Pre-merge rebase of the Story branch onto `origin/<epicBranch>`.
|
|
289
|
-
*
|
|
290
|
-
* Parallel wave execution lets two Stories land on the Epic between the time
|
|
291
|
-
* a later Story branched off and the time it closes. Rebasing the Story on
|
|
292
|
-
* the latest Epic before the close-merge shrinks the conflict surface to the
|
|
293
|
-
* Story's real delta and lets `mergeFeatureBranch`'s minor-conflict auto-
|
|
294
|
-
* resolve apply surgically instead of against stale base content.
|
|
295
|
-
*
|
|
296
|
-
* Runs inside the per-story worktree so it does not disturb the main
|
|
297
|
-
* checkout. On any failure (fetch error, rebase conflict) the rebase is
|
|
298
|
-
* aborted and the caller falls through to the plain merge path, which will
|
|
299
|
-
* surface the same conflict via triage.
|
|
300
|
-
*
|
|
301
|
-
* @returns {{ rebased: boolean, reason?: string }}
|
|
302
|
-
*/
|
|
303
|
-
function resolveStoryWorktreePath({ config, storyId, repoRoot }) {
|
|
304
|
-
const wtConfig = config?.delivery?.worktreeIsolation;
|
|
305
|
-
if (!wtConfig?.enabled) return { reason: 'isolation-disabled' };
|
|
306
|
-
const wtPath = resolveWorkingPath({
|
|
307
|
-
worktreeEnabled: true,
|
|
308
|
-
repoRoot,
|
|
309
|
-
storyId,
|
|
310
|
-
worktreeRoot: wtConfig.root,
|
|
311
|
-
});
|
|
312
|
-
if (!fs.existsSync(wtPath)) return { reason: 'worktree-missing' };
|
|
313
|
-
return { wtPath };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
function runFetchOrigin({ wtPath, epicBranch, log, gitSpawn }) {
|
|
317
|
-
const fetch = gitSpawn(wtPath, 'fetch', 'origin', epicBranch);
|
|
318
|
-
if (fetch.status === 0) return true;
|
|
319
|
-
log('GIT', `⚠️ fetch origin ${epicBranch} failed; skipping pre-merge rebase`);
|
|
320
|
-
return false;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
function runRebaseAndAbortOnConflict({ wtPath, epicBranch, log, gitSpawn }) {
|
|
324
|
-
const rebase = gitSpawn(wtPath, 'rebase', `origin/${epicBranch}`);
|
|
325
|
-
if (rebase.status === 0) return true;
|
|
326
|
-
gitSpawn(wtPath, 'rebase', '--abort');
|
|
327
|
-
log('GIT', '⚠️ rebase conflicted; aborted — merge triage will handle overlap');
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
export function rebaseStoryOnEpic({
|
|
332
|
-
config,
|
|
333
|
-
storyId,
|
|
334
|
-
epicBranch,
|
|
335
|
-
storyBranch,
|
|
336
|
-
repoRoot,
|
|
337
|
-
log = () => {},
|
|
338
|
-
gitSpawn = defaultGitSpawn,
|
|
339
|
-
}) {
|
|
340
|
-
const wt = resolveStoryWorktreePath({ config, storyId, repoRoot });
|
|
341
|
-
if (!wt.wtPath) return { rebased: false, reason: wt.reason };
|
|
342
|
-
const { wtPath } = wt;
|
|
343
|
-
|
|
344
|
-
log('GIT', `Rebasing ${storyBranch} onto origin/${epicBranch}...`);
|
|
345
|
-
if (!runFetchOrigin({ wtPath, epicBranch, log, gitSpawn })) {
|
|
346
|
-
return { rebased: false, reason: 'fetch-failed' };
|
|
347
|
-
}
|
|
348
|
-
if (!runRebaseAndAbortOnConflict({ wtPath, epicBranch, log, gitSpawn })) {
|
|
349
|
-
return { rebased: false, reason: 'rebase-conflict' };
|
|
350
|
-
}
|
|
351
|
-
log('GIT', `✅ Rebased ${storyBranch} onto origin/${epicBranch}`);
|
|
352
|
-
return { rebased: true };
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
/**
|
|
356
|
-
* Run the finalize-path merge: optional rebase, checkout + pull --rebase,
|
|
357
|
-
* merge --no-ff, push (with retry/conflict handling). Branch cleanup is
|
|
358
|
-
* deferred to after worktree reap (git refuses to delete a branch still
|
|
359
|
-
* checked out by a worktree).
|
|
360
|
-
*
|
|
361
|
-
* The caller (story-close.js) holds the per-Epic merge lock across the
|
|
362
|
-
* entire close flow, so this helper no longer acquires it itself.
|
|
363
|
-
*
|
|
364
|
-
* @param {{
|
|
365
|
-
* epicBranch: string,
|
|
366
|
-
* storyBranch: string,
|
|
367
|
-
* storyTitle: string,
|
|
368
|
-
* storyId: number|string,
|
|
369
|
-
* epicId: number|string,
|
|
370
|
-
* cwd: string,
|
|
371
|
-
* config: object,
|
|
372
|
-
* log?: (tag: string, msg: string) => void,
|
|
373
|
-
* logger?: { error: (msg: string) => void },
|
|
374
|
-
* gitSync?: typeof defaultGitSync,
|
|
375
|
-
* gitSpawn?: typeof defaultGitSpawn,
|
|
376
|
-
* }} opts
|
|
377
|
-
*/
|
|
378
|
-
async function buildMergeMessage(storyTitle, storyId, { cwd, logger }) {
|
|
379
|
-
const headerMaxLength = await loadHeaderMaxLength(cwd, { logger });
|
|
380
|
-
const { message } = buildMergeMessageWithCap({
|
|
381
|
-
type: 'feat',
|
|
382
|
-
title: storyTitle,
|
|
383
|
-
storyId,
|
|
384
|
-
headerMaxLength,
|
|
385
|
-
logger,
|
|
386
|
-
});
|
|
387
|
-
return message;
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
function buildVerboseMergeLogger(logger) {
|
|
391
|
-
return (_level, _ctx, msg, meta) => {
|
|
392
|
-
const tail = meta ? ` ${JSON.stringify(meta)}` : '';
|
|
393
|
-
logger.error(`[merge] ${msg}${tail}`);
|
|
394
|
-
};
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
function throwOnMajorConflict({ result, epicBranch }) {
|
|
398
|
-
if (result.merged || !result.major) return;
|
|
399
|
-
throw new Error(
|
|
400
|
-
`Major merge conflict on story close: ` +
|
|
401
|
-
`${result.conflicts.files} file(s), ${result.conflicts.lines} marker(s). ` +
|
|
402
|
-
`Conflicting files: ${result.conflicts.fileList.join(', ')}. ` +
|
|
403
|
-
`Merge has been aborted. Resolve manually on ${epicBranch}, then ` +
|
|
404
|
-
`re-run this script.`,
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
function logMergeOutcome({ result, log }) {
|
|
409
|
-
if (!result.autoResolved) {
|
|
410
|
-
log('GIT', '✅ Merge successful');
|
|
411
|
-
return;
|
|
412
|
-
}
|
|
413
|
-
log(
|
|
414
|
-
'GIT',
|
|
415
|
-
`✅ Merge completed with auto-resolved minor conflicts ` +
|
|
416
|
-
`(${result.conflicts.files} file(s) resolved to theirs)`,
|
|
417
|
-
);
|
|
418
|
-
for (const f of result.autoResolvedFiles ?? []) {
|
|
419
|
-
log(
|
|
420
|
-
'GIT',
|
|
421
|
-
` ↳ auto-resolved ${f.file} (${f.discardedLines} base line(s) discarded; trailer in merge commit)`,
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export async function runFinalizeMerge({
|
|
427
|
-
epicBranch,
|
|
428
|
-
storyBranch,
|
|
429
|
-
storyTitle,
|
|
430
|
-
storyId,
|
|
431
|
-
epicId,
|
|
432
|
-
cwd,
|
|
433
|
-
config,
|
|
434
|
-
bus = null,
|
|
435
|
-
log = () => {},
|
|
436
|
-
logger = DefaultLogger,
|
|
437
|
-
gitSync = defaultGitSync,
|
|
438
|
-
gitSpawn = defaultGitSpawn,
|
|
439
|
-
assertSharedCheckoutAvailable = defaultAssertSharedCheckoutAvailable,
|
|
440
|
-
}) {
|
|
441
|
-
rebaseStoryOnEpic({
|
|
442
|
-
config,
|
|
443
|
-
storyId,
|
|
444
|
-
epicBranch,
|
|
445
|
-
storyBranch,
|
|
446
|
-
repoRoot: cwd,
|
|
447
|
-
log,
|
|
448
|
-
gitSpawn,
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
// Story #4460 — cross-epic shared-checkout guard. Runs AFTER the
|
|
452
|
-
// per-Epic merge lock is already held (acquired around the whole close
|
|
453
|
-
// flow in story-close.js) so it composes with, rather than replaces,
|
|
454
|
-
// that same-epic serialization. Fails fast with an actionable
|
|
455
|
-
// diagnostic instead of letting a raw `git checkout` error surface
|
|
456
|
-
// when another epic's merge phase (or unrelated dirt) holds the
|
|
457
|
-
// shared checkout.
|
|
458
|
-
assertSharedCheckoutAvailable({ cwd, epicId, gitSpawn });
|
|
459
|
-
|
|
460
|
-
log('GIT', `Checking out ${epicBranch}...`);
|
|
461
|
-
gitSync(cwd, 'checkout', epicBranch);
|
|
462
|
-
gitSpawn(cwd, 'pull', '--rebase', 'origin', epicBranch);
|
|
463
|
-
|
|
464
|
-
log('GIT', `Merging ${storyBranch} into ${epicBranch} (--no-ff)...`);
|
|
465
|
-
const mergeMessage = await buildMergeMessage(storyTitle, storyId, {
|
|
466
|
-
cwd,
|
|
467
|
-
logger,
|
|
468
|
-
});
|
|
469
|
-
const result = mergeFeatureBranch(
|
|
470
|
-
cwd,
|
|
471
|
-
storyBranch,
|
|
472
|
-
buildVerboseMergeLogger(logger),
|
|
473
|
-
{ message: mergeMessage },
|
|
474
|
-
);
|
|
475
|
-
|
|
476
|
-
// Story #2241 / Task #2247 — surface a `story.blocked` lifecycle emit
|
|
477
|
-
// on the typed merge-failure paths before re-throwing so the bus
|
|
478
|
-
// ledger (and any subscribed BlockerHandler listener) sees the
|
|
479
|
-
// typed reason. The emit is best-effort; the throw is the canonical
|
|
480
|
-
// signal that the close was halted.
|
|
481
|
-
try {
|
|
482
|
-
throwOnMajorConflict({ result, epicBranch });
|
|
483
|
-
} catch (err) {
|
|
484
|
-
await emitStoryBlockedSafe({
|
|
485
|
-
bus,
|
|
486
|
-
storyId,
|
|
487
|
-
reason: 'merge-conflict:major',
|
|
488
|
-
logger,
|
|
489
|
-
});
|
|
490
|
-
throw err;
|
|
491
|
-
}
|
|
492
|
-
logMergeOutcome({ result, log });
|
|
493
|
-
|
|
494
|
-
log('GIT', `Pushing ${epicBranch}...`);
|
|
495
|
-
let pushOutcome;
|
|
496
|
-
try {
|
|
497
|
-
pushOutcome = await pushEpicAndHandleConflicts({
|
|
498
|
-
cwd,
|
|
499
|
-
epicBranch,
|
|
500
|
-
storyBranch,
|
|
501
|
-
config,
|
|
502
|
-
log: (msg) => log('GIT', msg),
|
|
503
|
-
mode: 'finalize',
|
|
504
|
-
mergeMessage,
|
|
505
|
-
});
|
|
506
|
-
} catch (err) {
|
|
507
|
-
await emitStoryBlockedSafe({
|
|
508
|
-
bus,
|
|
509
|
-
storyId,
|
|
510
|
-
reason: 'push-failed:finalize',
|
|
511
|
-
logger,
|
|
512
|
-
});
|
|
513
|
-
throw err;
|
|
514
|
-
}
|
|
515
|
-
if (pushOutcome.attempts > 1) {
|
|
516
|
-
log(
|
|
517
|
-
'GIT',
|
|
518
|
-
`✅ Push succeeded on attempt ${pushOutcome.attempts} after sibling session landed on ${epicBranch}`,
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
/**
|
|
524
|
-
* Commit a pending in-progress merge (resume path) using the
|
|
525
|
-
* conventional-commit subject from `buildResumeMergeCommitMsg`. No-op when
|
|
526
|
-
* `.git/MERGE_HEAD` is absent (merge already committed by the operator).
|
|
527
|
-
*/
|
|
528
|
-
function isMergePending(cwd) {
|
|
529
|
-
return fs.existsSync(path.join(cwd, '.git', 'MERGE_HEAD'));
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
function commitPendingMerge({
|
|
533
|
-
cwd,
|
|
534
|
-
storyTitle,
|
|
535
|
-
storyId,
|
|
536
|
-
gitSpawn,
|
|
537
|
-
headerMaxLength,
|
|
538
|
-
logger,
|
|
539
|
-
}) {
|
|
540
|
-
return gitSpawn(
|
|
541
|
-
cwd,
|
|
542
|
-
'commit',
|
|
543
|
-
'--no-verify',
|
|
544
|
-
'-m',
|
|
545
|
-
buildResumeMergeCommitMsg(storyTitle, storyId, {
|
|
546
|
-
headerMaxLength,
|
|
547
|
-
logger,
|
|
548
|
-
}),
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
export async function finalizeMergeIfPending({
|
|
553
|
-
cwd,
|
|
554
|
-
epicBranch,
|
|
555
|
-
storyBranch,
|
|
556
|
-
storyTitle,
|
|
557
|
-
storyId,
|
|
558
|
-
log = () => {},
|
|
559
|
-
gitSpawn = defaultGitSpawn,
|
|
560
|
-
logger = DefaultLogger,
|
|
561
|
-
}) {
|
|
562
|
-
if (!isMergePending(cwd)) {
|
|
563
|
-
log(
|
|
564
|
-
'GIT',
|
|
565
|
-
'⚠️ No MERGE_HEAD found — merge already committed; proceeding to push',
|
|
566
|
-
);
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
log('GIT', 'Finalizing in-progress merge (git commit --no-verify)');
|
|
570
|
-
const headerMaxLength = await loadHeaderMaxLength(cwd, { logger });
|
|
571
|
-
const commit = commitPendingMerge({
|
|
572
|
-
cwd,
|
|
573
|
-
storyTitle,
|
|
574
|
-
storyId,
|
|
575
|
-
gitSpawn,
|
|
576
|
-
headerMaxLength,
|
|
577
|
-
logger,
|
|
578
|
-
});
|
|
579
|
-
if (commit.status !== 0) {
|
|
580
|
-
throw new Error(
|
|
581
|
-
`Failed to finalize merge commit: ${commit.stderr || commit.stdout || 'unknown'}. ` +
|
|
582
|
-
`Check that all conflicts are resolved and staged on ${epicBranch}.`,
|
|
583
|
-
);
|
|
584
|
-
}
|
|
585
|
-
log('GIT', `✅ Merge of ${storyBranch} finalized on ${epicBranch}`);
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
/**
|
|
589
|
-
* Run the resume-path merge: finalize the in-progress merge (if any), then
|
|
590
|
-
* push. Used by `runStoryClose` when prior state is `partial-merge`. The
|
|
591
|
-
* caller holds the per-Epic merge lock across the entire close flow.
|
|
592
|
-
*/
|
|
593
|
-
export async function runResumeMerge({
|
|
594
|
-
cwd,
|
|
595
|
-
epicBranch,
|
|
596
|
-
storyBranch,
|
|
597
|
-
storyTitle,
|
|
598
|
-
storyId,
|
|
599
|
-
epicId,
|
|
600
|
-
config,
|
|
601
|
-
bus = null,
|
|
602
|
-
logger = DefaultLogger,
|
|
603
|
-
log = () => {},
|
|
604
|
-
gitSpawn = defaultGitSpawn,
|
|
605
|
-
assertNoForeignEpicLockFn = defaultAssertNoForeignEpicLock,
|
|
606
|
-
}) {
|
|
607
|
-
// Story #4460 follow-up: the resume path re-enters the shared checkout
|
|
608
|
-
// just like the finalize path, so another epic's live merge phase is
|
|
609
|
-
// the same hazard here. Only the foreign-lock half of the guard runs —
|
|
610
|
-
// a resume's own partial merge legitimately leaves the tree dirty, so
|
|
611
|
-
// the dirty-tree probe would false-positive against our own state.
|
|
612
|
-
assertNoForeignEpicLockFn({ cwd, epicId });
|
|
613
|
-
|
|
614
|
-
const resumeMergeMessage = await buildMergeMessage(storyTitle, storyId, {
|
|
615
|
-
cwd,
|
|
616
|
-
logger,
|
|
617
|
-
});
|
|
618
|
-
try {
|
|
619
|
-
await finalizeMergeIfPending({
|
|
620
|
-
cwd,
|
|
621
|
-
epicBranch,
|
|
622
|
-
storyBranch,
|
|
623
|
-
storyTitle,
|
|
624
|
-
storyId,
|
|
625
|
-
log,
|
|
626
|
-
gitSpawn,
|
|
627
|
-
logger,
|
|
628
|
-
});
|
|
629
|
-
} catch (err) {
|
|
630
|
-
await emitStoryBlockedSafe({
|
|
631
|
-
bus,
|
|
632
|
-
storyId,
|
|
633
|
-
reason: 'merge-finalize-failed:resume',
|
|
634
|
-
logger,
|
|
635
|
-
});
|
|
636
|
-
throw err;
|
|
637
|
-
}
|
|
638
|
-
log('GIT', `Pushing ${epicBranch}...`);
|
|
639
|
-
try {
|
|
640
|
-
await pushEpicAndHandleConflicts({
|
|
641
|
-
cwd,
|
|
642
|
-
epicBranch,
|
|
643
|
-
storyBranch,
|
|
644
|
-
config,
|
|
645
|
-
log: (msg) => log('GIT', msg),
|
|
646
|
-
mode: 'resume',
|
|
647
|
-
mergeMessage: resumeMergeMessage,
|
|
648
|
-
});
|
|
649
|
-
} catch (err) {
|
|
650
|
-
await emitStoryBlockedSafe({
|
|
651
|
-
bus,
|
|
652
|
-
storyId,
|
|
653
|
-
reason: 'push-failed:resume',
|
|
654
|
-
logger,
|
|
655
|
-
});
|
|
656
|
-
throw err;
|
|
657
|
-
}
|
|
658
|
-
}
|