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,359 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* amend.js — the `--amend` change-request delta path of the collapsed
|
|
3
|
-
* persist surface (Epic #4474 PR4, design §2 mode matrix, amend row).
|
|
4
|
-
*
|
|
5
|
-
* An amend run receives the full ticket set with every ticket carrying an
|
|
6
|
-
* `op` field:
|
|
7
|
-
*
|
|
8
|
-
* - `add` — a new Story; created fresh.
|
|
9
|
-
* - `modify` — an existing Story whose contract changed; closed and
|
|
10
|
-
* recreated (close-and-recreate is scoped to modify/close
|
|
11
|
-
* slugs ONLY — never the whole tree, which is `--force`'s
|
|
12
|
-
* semantics).
|
|
13
|
-
* - `keep` — an existing Story left byte-untouched; it participates in
|
|
14
|
-
* the merged-set DAG validation but no provider mutation
|
|
15
|
-
* ever targets it.
|
|
16
|
-
* - `close` — an existing Story dropped from the plan; closed.
|
|
17
|
-
*
|
|
18
|
-
* The DAG (and the full ticket validator) runs over the MERGED set —
|
|
19
|
-
* keeps + adds + modifies, closes excluded — so a cycle introduced by an
|
|
20
|
-
* add that depends on a kept Story that depends on a modify is caught
|
|
21
|
-
* before any GitHub call, and a dependency pointing at a closed slug fails
|
|
22
|
-
* the unknown-slug check.
|
|
23
|
-
*
|
|
24
|
-
* Close ops are destructive, so they are gated behind the same explicit
|
|
25
|
-
* confirmation contract as `epic-reconcile.js`: without `--explicit-delete`
|
|
26
|
-
* the run stops BEFORE any mutation, prints the dry-run op diff, and exits
|
|
27
|
-
* 2 ({@link AmendExplicitCloseError} carries the rendered diff; the CLI
|
|
28
|
-
* maps it onto the exit code). Non-interactive callers pass the flag.
|
|
29
|
-
*
|
|
30
|
-
* Slug → issue resolution: an explicit `id` on the ticket (the authoring
|
|
31
|
-
* envelope's open-children listing carries issue numbers) wins; otherwise
|
|
32
|
-
* the reconciler state ledger (`.agents/epics/<id>.state.json`) resolves
|
|
33
|
-
* the slug. A modify/keep/close slug that resolves nowhere is a hard error
|
|
34
|
-
* — the amend never guesses which Story it is about to close.
|
|
35
|
-
*
|
|
36
|
-
* @module lib/orchestration/plan-persist/amend
|
|
37
|
-
*/
|
|
38
|
-
|
|
39
|
-
import { Logger } from '../../Logger.js';
|
|
40
|
-
|
|
41
|
-
/** Valid `op` values on an amend ticket. */
|
|
42
|
-
const AMEND_OPS = Object.freeze(['add', 'modify', 'keep', 'close']);
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Error carrying the rendered dry-run diff for the exit-2 confirmation
|
|
46
|
-
* path (mirrors `epic-reconcile.js` EXIT_CODES.EXPLICIT_DELETE_REQUIRED).
|
|
47
|
-
*/
|
|
48
|
-
export class AmendExplicitCloseError extends Error {
|
|
49
|
-
/**
|
|
50
|
-
* @param {string} message
|
|
51
|
-
* @param {{ diff: string }} detail
|
|
52
|
-
*/
|
|
53
|
-
constructor(message, { diff }) {
|
|
54
|
-
super(message);
|
|
55
|
-
this.name = 'AmendExplicitCloseError';
|
|
56
|
-
this.code = 'PLAN_AMEND_EXPLICIT_DELETE_REQUIRED';
|
|
57
|
-
this.diff = diff;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Partition an amend ticket set by `op`, validating the op vocabulary and
|
|
63
|
-
* slug uniqueness. Every ticket MUST carry an op — a missing op on one
|
|
64
|
-
* ticket of an amend payload is an authoring defect, not a default.
|
|
65
|
-
*
|
|
66
|
-
* @param {Array<{ slug?: string, op?: string }>} tickets
|
|
67
|
-
* @returns {{ add: object[], modify: object[], keep: object[], close: object[] }}
|
|
68
|
-
*/
|
|
69
|
-
export function partitionAmendTickets(tickets) {
|
|
70
|
-
const partition = { add: [], modify: [], keep: [], close: [] };
|
|
71
|
-
const seen = new Set();
|
|
72
|
-
for (const ticket of tickets) {
|
|
73
|
-
const slug = ticket?.slug;
|
|
74
|
-
if (typeof slug !== 'string' || slug.length === 0) {
|
|
75
|
-
throw new Error(
|
|
76
|
-
'[plan-persist] amend ticket without a slug — every amend ticket ' +
|
|
77
|
-
'must carry both slug and op.',
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
if (seen.has(slug)) {
|
|
81
|
-
throw new Error(
|
|
82
|
-
`[plan-persist] duplicate slug "${slug}" in the amend payload.`,
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
seen.add(slug);
|
|
86
|
-
const op = ticket?.op;
|
|
87
|
-
if (!AMEND_OPS.includes(op)) {
|
|
88
|
-
throw new Error(
|
|
89
|
-
`[plan-persist] amend ticket "${slug}" carries invalid op ` +
|
|
90
|
-
`"${op}" — expected one of ${AMEND_OPS.join(', ')}.`,
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
partition[op].push(ticket);
|
|
94
|
-
}
|
|
95
|
-
return partition;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Project the MERGED ticket set for validation and spec rendering:
|
|
100
|
-
* keeps + adds + modifies in original authoring order, closes excluded,
|
|
101
|
-
* with the amend-only `op`/`id` carrier fields stripped so the ticket
|
|
102
|
-
* validator and `renderSpec` see the exact fan-out ticket shape.
|
|
103
|
-
*
|
|
104
|
-
* @param {Array<{ op?: string }>} tickets the full amend payload
|
|
105
|
-
* @returns {object[]} merged set, op/id stripped
|
|
106
|
-
*/
|
|
107
|
-
export function buildMergedTicketSet(tickets) {
|
|
108
|
-
return tickets
|
|
109
|
-
.filter((t) => t?.op !== 'close')
|
|
110
|
-
.map(({ op: _op, id: _id, ...rest }) => rest);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Resolve every modify/keep/close slug to its live issue number and
|
|
115
|
-
* hard-error on any ambiguity (the "closing the wrong stories" risk):
|
|
116
|
-
*
|
|
117
|
-
* - modify/keep/close must resolve via explicit `id` or the state
|
|
118
|
-
* ledger mapping — no resolution is a hard error;
|
|
119
|
-
* - an add slug must NOT already resolve to an issue (a collision means
|
|
120
|
-
* the author meant modify, or picked a stale slug);
|
|
121
|
-
* - resolved modify/close targets are fetched and must exist; a target
|
|
122
|
-
* already closed on GitHub is surfaced (skipped close, not an error —
|
|
123
|
-
* re-running an interrupted amend must stay safe).
|
|
124
|
-
*
|
|
125
|
-
* @param {{
|
|
126
|
-
* partition: ReturnType<typeof partitionAmendTickets>,
|
|
127
|
-
* stateMapping: Record<string, { issueNumber?: number }>,
|
|
128
|
-
* provider: { getTicket: (id: number) => Promise<object|null> },
|
|
129
|
-
* }} args
|
|
130
|
-
* @returns {Promise<{
|
|
131
|
-
* modify: Array<{ ticket: object, issueNumber: number, alreadyClosed: boolean }>,
|
|
132
|
-
* keep: Array<{ ticket: object, issueNumber: number }>,
|
|
133
|
-
* close: Array<{ ticket: object, issueNumber: number, alreadyClosed: boolean }>,
|
|
134
|
-
* }>}
|
|
135
|
-
*/
|
|
136
|
-
export async function resolveAmendTargets({
|
|
137
|
-
partition,
|
|
138
|
-
stateMapping,
|
|
139
|
-
provider,
|
|
140
|
-
}) {
|
|
141
|
-
const mapping = stateMapping ?? {};
|
|
142
|
-
const resolveSlug = (ticket) => {
|
|
143
|
-
if (Number.isInteger(ticket.id)) return ticket.id;
|
|
144
|
-
const entry = mapping[ticket.slug];
|
|
145
|
-
if (Number.isInteger(entry?.issueNumber)) return entry.issueNumber;
|
|
146
|
-
return null;
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
for (const ticket of partition.add) {
|
|
150
|
-
const collision = resolveSlug(ticket);
|
|
151
|
-
if (collision !== null) {
|
|
152
|
-
throw new Error(
|
|
153
|
-
`[plan-persist] amend op "add" for slug "${ticket.slug}" collides ` +
|
|
154
|
-
`with existing issue #${collision} — use op "modify" to replace ` +
|
|
155
|
-
'it, or pick a fresh slug.',
|
|
156
|
-
);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
const resolveExisting = async (ticket, op) => {
|
|
161
|
-
const issueNumber = resolveSlug(ticket);
|
|
162
|
-
if (issueNumber === null) {
|
|
163
|
-
throw new Error(
|
|
164
|
-
`[plan-persist] amend op "${op}" for slug "${ticket.slug}" resolves ` +
|
|
165
|
-
'to no existing issue (no `id` on the ticket, no state-ledger ' +
|
|
166
|
-
'mapping). Refusing to guess — carry the issue number as `id`.',
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
const issue = await provider.getTicket(issueNumber);
|
|
170
|
-
if (!issue) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`[plan-persist] amend op "${op}" for slug "${ticket.slug}" points ` +
|
|
173
|
-
`at issue #${issueNumber}, which does not exist.`,
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
return { ticket, issueNumber, alreadyClosed: issue.state === 'closed' };
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const modify = [];
|
|
180
|
-
for (const t of partition.modify)
|
|
181
|
-
modify.push(await resolveExisting(t, 'modify'));
|
|
182
|
-
const close = [];
|
|
183
|
-
for (const t of partition.close)
|
|
184
|
-
close.push(await resolveExisting(t, 'close'));
|
|
185
|
-
const keep = [];
|
|
186
|
-
for (const t of partition.keep) {
|
|
187
|
-
const { ticket, issueNumber } = await resolveExisting(t, 'keep');
|
|
188
|
-
keep.push({ ticket, issueNumber });
|
|
189
|
-
}
|
|
190
|
-
return { modify, keep, close };
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Render the operator-facing dry-run diff of the amend plan — printed on
|
|
195
|
-
* the exit-2 confirmation path and logged before an `--explicit-delete`
|
|
196
|
-
* apply so the destructive set is always visible.
|
|
197
|
-
*
|
|
198
|
-
* @param {{
|
|
199
|
-
* epicId: number,
|
|
200
|
-
* targets: Awaited<ReturnType<typeof resolveAmendTargets>>,
|
|
201
|
-
* adds: Array<{ slug: string, title?: string }>,
|
|
202
|
-
* }} args
|
|
203
|
-
* @returns {string}
|
|
204
|
-
*/
|
|
205
|
-
export function renderAmendPlanDiff({ epicId, targets, adds }) {
|
|
206
|
-
const rows = [
|
|
207
|
-
...targets.close.map(
|
|
208
|
-
(t) =>
|
|
209
|
-
`| close | \`${t.ticket.slug}\` | #${t.issueNumber} | closed${t.alreadyClosed ? ' (already closed)' : ''} |`,
|
|
210
|
-
),
|
|
211
|
-
...targets.modify.map(
|
|
212
|
-
(t) =>
|
|
213
|
-
`| modify | \`${t.ticket.slug}\` | #${t.issueNumber} | closed + recreated |`,
|
|
214
|
-
),
|
|
215
|
-
...adds.map((t) => `| add | \`${t.slug}\` | — | created |`),
|
|
216
|
-
...targets.keep.map(
|
|
217
|
-
(t) => `| keep | \`${t.ticket.slug}\` | #${t.issueNumber} | untouched |`,
|
|
218
|
-
),
|
|
219
|
-
];
|
|
220
|
-
return [
|
|
221
|
-
`Amend plan for Epic #${epicId} (dry-run):`,
|
|
222
|
-
'',
|
|
223
|
-
'| Op | Slug | Issue | Effect |',
|
|
224
|
-
'| --- | --- | --- | --- |',
|
|
225
|
-
...rows,
|
|
226
|
-
].join('\n');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
/**
|
|
230
|
-
* Enforce the close-op confirmation gate: when the amend plan carries
|
|
231
|
-
* close ops (including the close half of modify) affecting live issues and
|
|
232
|
-
* `--explicit-delete` was not passed, throw {@link AmendExplicitCloseError}
|
|
233
|
-
* with the rendered diff — BEFORE any mutation. Mirrors the
|
|
234
|
-
* `epic-reconcile.js` exit-2 contract.
|
|
235
|
-
*
|
|
236
|
-
* @param {{
|
|
237
|
-
* epicId: number,
|
|
238
|
-
* targets: Awaited<ReturnType<typeof resolveAmendTargets>>,
|
|
239
|
-
* adds: object[],
|
|
240
|
-
* explicitDelete: boolean,
|
|
241
|
-
* }} args
|
|
242
|
-
*/
|
|
243
|
-
export function enforceAmendCloseGate({
|
|
244
|
-
epicId,
|
|
245
|
-
targets,
|
|
246
|
-
adds,
|
|
247
|
-
explicitDelete,
|
|
248
|
-
}) {
|
|
249
|
-
const liveCloses = [
|
|
250
|
-
...targets.close.filter((t) => !t.alreadyClosed),
|
|
251
|
-
...targets.modify.filter((t) => !t.alreadyClosed),
|
|
252
|
-
];
|
|
253
|
-
if (liveCloses.length === 0 || explicitDelete) return;
|
|
254
|
-
const diff = renderAmendPlanDiff({ epicId, targets, adds });
|
|
255
|
-
const named = liveCloses
|
|
256
|
-
.map((t) => `#${t.issueNumber} (\`${t.ticket.slug}\`)`)
|
|
257
|
-
.join(', ');
|
|
258
|
-
throw new AmendExplicitCloseError(
|
|
259
|
-
`[plan-persist] amend plan would close ${liveCloses.length} live ` +
|
|
260
|
-
`issue(s): ${named}. Review the dry-run diff and re-run with ` +
|
|
261
|
-
'--explicit-delete to apply.',
|
|
262
|
-
{ diff },
|
|
263
|
-
);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Apply the amend ops against the provider, in destructive-last order per
|
|
268
|
-
* slug class: closes first (their disappearance is what the operator
|
|
269
|
-
* confirmed), then modify (close old + create new), then adds. Keeps are
|
|
270
|
-
* never touched by construction — no code path here receives them.
|
|
271
|
-
*
|
|
272
|
-
* @param {{
|
|
273
|
-
* epicId: number,
|
|
274
|
-
* provider: {
|
|
275
|
-
* updateTicket: (id: number, patch: object) => Promise<object>,
|
|
276
|
-
* createTicket: (parentId: number, payload: object) => Promise<{ id: number }>,
|
|
277
|
-
* },
|
|
278
|
-
* targets: Awaited<ReturnType<typeof resolveAmendTargets>>,
|
|
279
|
-
* validatedBySlug: Map<string, { slug: string, title: string, body?: string, labels?: string[] }>,
|
|
280
|
-
* }} args
|
|
281
|
-
* @returns {Promise<{
|
|
282
|
-
* closed: Array<{ slug: string, issueNumber: number }>,
|
|
283
|
-
* recreated: Array<{ slug: string, oldIssueNumber: number, issueNumber: number }>,
|
|
284
|
-
* created: Array<{ slug: string, issueNumber: number }>,
|
|
285
|
-
* mapping: Record<string, { entity: 'story', issueNumber: number }>,
|
|
286
|
-
* closedSlugs: string[],
|
|
287
|
-
* }>}
|
|
288
|
-
*/
|
|
289
|
-
export async function applyAmendOps({
|
|
290
|
-
epicId,
|
|
291
|
-
provider,
|
|
292
|
-
targets,
|
|
293
|
-
validatedBySlug,
|
|
294
|
-
}) {
|
|
295
|
-
const closed = [];
|
|
296
|
-
const recreated = [];
|
|
297
|
-
const created = [];
|
|
298
|
-
const mapping = {};
|
|
299
|
-
const closedSlugs = [];
|
|
300
|
-
|
|
301
|
-
const createFromValidated = async (slug) => {
|
|
302
|
-
const validated = validatedBySlug.get(slug);
|
|
303
|
-
if (!validated) {
|
|
304
|
-
throw new Error(
|
|
305
|
-
`[plan-persist] amend apply: no validated ticket for slug "${slug}".`,
|
|
306
|
-
);
|
|
307
|
-
}
|
|
308
|
-
const result = await provider.createTicket(epicId, {
|
|
309
|
-
title: validated.title,
|
|
310
|
-
body: validated.body ?? '',
|
|
311
|
-
labels: validated.labels ?? [],
|
|
312
|
-
});
|
|
313
|
-
return result.id;
|
|
314
|
-
};
|
|
315
|
-
|
|
316
|
-
for (const target of targets.close) {
|
|
317
|
-
if (!target.alreadyClosed) {
|
|
318
|
-
await provider.updateTicket(target.issueNumber, { state: 'closed' });
|
|
319
|
-
} else {
|
|
320
|
-
Logger.info(
|
|
321
|
-
`[plan-persist] amend close: #${target.issueNumber} ` +
|
|
322
|
-
`(${target.ticket.slug}) already closed — skipping.`,
|
|
323
|
-
);
|
|
324
|
-
}
|
|
325
|
-
closed.push({ slug: target.ticket.slug, issueNumber: target.issueNumber });
|
|
326
|
-
closedSlugs.push(target.ticket.slug);
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
for (const target of targets.modify) {
|
|
330
|
-
if (!target.alreadyClosed) {
|
|
331
|
-
await provider.updateTicket(target.issueNumber, { state: 'closed' });
|
|
332
|
-
}
|
|
333
|
-
const newId = await createFromValidated(target.ticket.slug);
|
|
334
|
-
recreated.push({
|
|
335
|
-
slug: target.ticket.slug,
|
|
336
|
-
oldIssueNumber: target.issueNumber,
|
|
337
|
-
issueNumber: newId,
|
|
338
|
-
});
|
|
339
|
-
mapping[target.ticket.slug] = { entity: 'story', issueNumber: newId };
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
for (const [slug] of validatedBySlug) {
|
|
343
|
-
const isModify = targets.modify.some((t) => t.ticket.slug === slug);
|
|
344
|
-
const isKeep = targets.keep.some((t) => t.ticket.slug === slug);
|
|
345
|
-
if (isModify || isKeep) continue;
|
|
346
|
-
const newId = await createFromValidated(slug);
|
|
347
|
-
created.push({ slug, issueNumber: newId });
|
|
348
|
-
mapping[slug] = { entity: 'story', issueNumber: newId };
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
for (const target of targets.keep) {
|
|
352
|
-
mapping[target.ticket.slug] = {
|
|
353
|
-
entity: 'story',
|
|
354
|
-
issueNumber: target.issueNumber,
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
return { closed, recreated, created, mapping, closedSlugs };
|
|
359
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* delivery-mode.js — delivery-shape mode resolution for the collapsed
|
|
3
|
-
* persist surface (Epic #4474 PR4, design §2 mode matrix).
|
|
4
|
-
*
|
|
5
|
-
* The risk verdict's optional `deliveryShape` field ("fan-out" | "single",
|
|
6
|
-
* absent → fan-out so every existing verdict stays valid) selects between
|
|
7
|
-
* the three persist modes:
|
|
8
|
-
*
|
|
9
|
-
* - `fan-out` — the full mode: ticket validator + DAG + budget gates,
|
|
10
|
-
* Story-tree creation via the structural reconciler.
|
|
11
|
-
* - `single` — the spec-only single-delivery mode: NO tickets are
|
|
12
|
-
* authored, the ticket validator + DAG are skipped (fenced by
|
|
13
|
-
* construction — {@link resolveDeliveryMode} hard-refuses the
|
|
14
|
-
* combination of `deliveryShape: "single"` with a tickets payload, so
|
|
15
|
-
* the skip branch is unreachable when tickets are present), and the
|
|
16
|
-
* persist applies the `delivery::single` routing marker instead of a
|
|
17
|
-
* Story tree. Inert until #4475 lands the deliver-side reader.
|
|
18
|
-
* - `amend` — the change-request delta path (`--amend`): tickets carry
|
|
19
|
-
* `op: add|modify|keep|close` and the persist maps the ops onto the
|
|
20
|
-
* existing Story tree (see `plan-persist/amend.js`).
|
|
21
|
-
*
|
|
22
|
-
* @module lib/orchestration/plan-persist/delivery-mode
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
import { parseDeliverySlicingTable } from '../consolidation-precondition.js';
|
|
26
|
-
|
|
27
|
-
/** Canonical delivery-shape values the risk-verdict schema admits. */
|
|
28
|
-
const DELIVERY_SHAPES = Object.freeze(['fan-out', 'single']);
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Mode-coherence hard error (design §1 step 3 item 3, extended for PR4).
|
|
32
|
-
*
|
|
33
|
-
* Resolves the persist mode from the risk verdict's `deliveryShape` (absent
|
|
34
|
-
* → `fan-out`), the tickets payload, and the `--amend` flag — and refuses
|
|
35
|
-
* every incoherent combination loudly instead of silently coercing:
|
|
36
|
-
*
|
|
37
|
-
* - unknown `deliveryShape` values;
|
|
38
|
-
* - `deliveryShape: "single"` WITH a tickets payload (the DAG-skip
|
|
39
|
-
* leakage risk — the single mode's validator/DAG skip is only sound
|
|
40
|
-
* when there are no tickets to validate);
|
|
41
|
-
* - fan-out without a non-empty tickets array;
|
|
42
|
-
* - `--amend` combined with `deliveryShape: "single"` (the delta path is
|
|
43
|
-
* fan-out-shaped by definition — a single-delivery re-plan is a
|
|
44
|
-
* `--force` re-persist);
|
|
45
|
-
* - `--amend` without tickets (the delta IS the tickets-with-ops
|
|
46
|
-
* payload);
|
|
47
|
-
* - `op` fields present without `--amend` (a full persist must never
|
|
48
|
-
* silently reinterpret an amend delta as a fresh tree).
|
|
49
|
-
*
|
|
50
|
-
* @param {{ deliveryShape?: string }} riskVerdict schema-validated verdict
|
|
51
|
-
* @param {unknown} tickets parsed tickets payload (null when no file)
|
|
52
|
-
* @param {{ amend?: boolean }} [opts]
|
|
53
|
-
* @returns {'fan-out'|'single'|'amend'} the resolved persist mode
|
|
54
|
-
*/
|
|
55
|
-
export function resolveDeliveryMode(riskVerdict, tickets, opts = {}) {
|
|
56
|
-
const { amend = false } = opts;
|
|
57
|
-
const shape = riskVerdict?.deliveryShape ?? 'fan-out';
|
|
58
|
-
if (!DELIVERY_SHAPES.includes(shape)) {
|
|
59
|
-
throw new Error(
|
|
60
|
-
`[plan-persist] unknown deliveryShape "${shape}" — expected one of ` +
|
|
61
|
-
`${DELIVERY_SHAPES.join(', ')} (or omit the field for fan-out).`,
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
const hasTickets = Array.isArray(tickets) && tickets.length > 0;
|
|
65
|
-
|
|
66
|
-
if (amend) {
|
|
67
|
-
if (shape === 'single') {
|
|
68
|
-
throw new Error(
|
|
69
|
-
'[plan-persist] --amend is incoherent with deliveryShape "single" — ' +
|
|
70
|
-
'the amend delta maps ops onto an existing Story tree, and a ' +
|
|
71
|
-
'single-delivery plan has none. Re-author the verdict as fan-out, ' +
|
|
72
|
-
'or re-persist the single plan with --force.',
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
if (!hasTickets) {
|
|
76
|
-
throw new Error(
|
|
77
|
-
'[plan-persist] --amend requires a tickets payload carrying ' +
|
|
78
|
-
'`op: add|modify|keep|close` on every ticket — the delta IS the ' +
|
|
79
|
-
'tickets file (--tickets <file>).',
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
return 'amend';
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (shape === 'single') {
|
|
86
|
-
if (hasTickets) {
|
|
87
|
-
throw new Error(
|
|
88
|
-
'[plan-persist] mode-coherence: deliveryShape "single" with a ' +
|
|
89
|
-
'tickets payload is contradictory — the single-delivery mode ' +
|
|
90
|
-
'authors NO tickets (the Delivery Slicing table is the audit ' +
|
|
91
|
-
'trail). Remove the tickets file, or re-author the risk verdict ' +
|
|
92
|
-
'as fan-out.',
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
return 'single';
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (!hasTickets) {
|
|
99
|
-
throw new Error(
|
|
100
|
-
'[plan-persist] fan-out persist requires a non-empty tickets array ' +
|
|
101
|
-
'(--tickets <file>). A ticket-less spec-only plan must declare ' +
|
|
102
|
-
'deliveryShape: "single" in the risk verdict.',
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
if (tickets.some((t) => t && typeof t === 'object' && 'op' in t)) {
|
|
106
|
-
throw new Error(
|
|
107
|
-
'[plan-persist] tickets carry `op` fields but --amend was not passed ' +
|
|
108
|
-
'— refusing to reinterpret an amend delta as a full persist. Pass ' +
|
|
109
|
-
'--amend, or strip the op fields for a fresh fan-out.',
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
return 'fan-out';
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Count the slices of the authored Tech Spec's `## Delivery Slicing` table
|
|
117
|
-
* — the single-mode plan summary's `sliceCount` (design §2: the slicing
|
|
118
|
-
* table IS the single mode's audit trail). Returns `null` when the table
|
|
119
|
-
* is absent or unparseable (fail-open, mirroring the parser's contract).
|
|
120
|
-
*
|
|
121
|
-
* @param {string} techSpecContent
|
|
122
|
-
* @returns {number|null}
|
|
123
|
-
*/
|
|
124
|
-
export function countDeliverySlices(techSpecContent) {
|
|
125
|
-
const rows = parseDeliverySlicingTable(techSpecContent ?? '');
|
|
126
|
-
return Array.isArray(rows) ? rows.length : null;
|
|
127
|
-
}
|
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* post-merge-pipeline.js — sequencer for the post-merge phases of
|
|
3
|
-
* `story-close`.
|
|
4
|
-
*
|
|
5
|
-
* After the Story branch is merged into the Epic branch, several best-effort
|
|
6
|
-
* cleanup + reporting phases must run:
|
|
7
|
-
*
|
|
8
|
-
* 1. worktree-reap — remove the per-Story worktree.
|
|
9
|
-
* 2. branch-cleanup — delete the Story branch (local + remote).
|
|
10
|
-
* 3. ticket-closure — transition the Story to agent::done
|
|
11
|
-
* and run cascade completion.
|
|
12
|
-
* 4. notification — fire the story-complete webhook.
|
|
13
|
-
* 5. dashboard-refresh — regenerate the dispatch manifest.
|
|
14
|
-
* 6. temp-cleanup — delete the per-Story manifest pair under
|
|
15
|
-
* `temp/epic-<eid>/stories/story-<sid>/manifest.{md,json}`
|
|
16
|
-
* (Epic #1030 Story #1040). Falls back to the
|
|
17
|
-
* legacy flat `temp/story-manifest-<id>.{md,json}`
|
|
18
|
-
* layout when `epicId` is unknown — both paths
|
|
19
|
-
* are tried so partial migrations don't leak
|
|
20
|
-
* files in either layout.
|
|
21
|
-
* 7. detectors — fire rework + retry signal detectors against
|
|
22
|
-
* this Story's `traces.ndjson` and persist each
|
|
23
|
-
* emission to `signals.ndjson` BEFORE the
|
|
24
|
-
* analyzer renders the perf-summary comment.
|
|
25
|
-
* 8. perf-summary — shell out to `analyze-execution.js` so the
|
|
26
|
-
* analyzer is the single writer of the
|
|
27
|
-
* `<!-- structured:story-perf-summary -->`
|
|
28
|
-
* comment on the Story ticket.
|
|
29
|
-
*
|
|
30
|
-
* Each phase is wrapped by `runPhase` so a single failure does not abort
|
|
31
|
-
* the rest of the close-out — the same best-effort contract that the
|
|
32
|
-
* pre-extraction inline code provided. Phase return values are merged into
|
|
33
|
-
* a `state` object whose shape `runStoryClose` consumes to build the final
|
|
34
|
-
* structured result.
|
|
35
|
-
*
|
|
36
|
-
* The phase implementations live under `./post-merge/phases/` (one file
|
|
37
|
-
* per phase) — this module is a pure sequencer that imports each phase,
|
|
38
|
-
* re-exports them for backwards compatibility, and runs them in order
|
|
39
|
-
* via `runPhase`.
|
|
40
|
-
*/
|
|
41
|
-
|
|
42
|
-
import { execFileSync } from 'node:child_process';
|
|
43
|
-
import path from 'node:path';
|
|
44
|
-
import { Logger } from '../Logger.js';
|
|
45
|
-
import { detectorsPhase } from './detectors-phase.js';
|
|
46
|
-
import { runPhase } from './phase-runner.js';
|
|
47
|
-
import { branchCleanupPhase } from './post-merge/phases/branch-cleanup.js';
|
|
48
|
-
import { dashboardRefreshPhase } from './post-merge/phases/dashboard-refresh.js';
|
|
49
|
-
import { notificationPhase } from './post-merge/phases/notification.js';
|
|
50
|
-
import { tempCleanupPhase } from './post-merge/phases/temp-cleanup.js';
|
|
51
|
-
import { ticketClosurePhase } from './post-merge/phases/ticket-closure.js';
|
|
52
|
-
import {
|
|
53
|
-
createWorktreeReapState,
|
|
54
|
-
worktreeReapPhase,
|
|
55
|
-
} from './post-merge/phases/worktree-reap.js';
|
|
56
|
-
|
|
57
|
-
export {
|
|
58
|
-
branchCleanupPhase,
|
|
59
|
-
dashboardRefreshPhase,
|
|
60
|
-
detectorsPhase,
|
|
61
|
-
notificationPhase,
|
|
62
|
-
tempCleanupPhase,
|
|
63
|
-
ticketClosurePhase,
|
|
64
|
-
worktreeReapPhase,
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
function reapPhaseLogger(progress) {
|
|
68
|
-
return progress ?? (() => {});
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* perfSummaryPhase — shells out to `analyze-execution.js --story <sid>
|
|
73
|
-
* --epic <eid> --phase-timings <path>` so the analyzer is the single
|
|
74
|
-
* writer of the `<!-- structured:story-perf-summary -->` comment on the
|
|
75
|
-
* Story ticket (Epic #1030 Story #1046). Replaces the legacy
|
|
76
|
-
* `<!-- structured:phase-timings -->` post that lived inline in
|
|
77
|
-
* `post-merge-close.js`.
|
|
78
|
-
*
|
|
79
|
-
* Best-effort: any failure (missing analyzer, non-zero exit, no path
|
|
80
|
-
* supplied) logs a warning and resolves — the merge has already
|
|
81
|
-
* succeeded and we would rather lose the perf summary than roll back
|
|
82
|
-
* closure.
|
|
83
|
-
*
|
|
84
|
-
* @param {{
|
|
85
|
-
* storyId: number|string,
|
|
86
|
-
* epicId: number|string,
|
|
87
|
-
* phaseTimingsPath: string|null|undefined,
|
|
88
|
-
* projectRoot?: string,
|
|
89
|
-
* progress?: Function,
|
|
90
|
-
* logger?: object,
|
|
91
|
-
* spawnFn?: typeof execFileSync,
|
|
92
|
-
* }} ctx
|
|
93
|
-
* @returns {Promise<{ status: 'ok'|'skipped'|'failed', reason?: string }>}
|
|
94
|
-
*/
|
|
95
|
-
export async function perfSummaryPhase(ctx) {
|
|
96
|
-
const {
|
|
97
|
-
storyId,
|
|
98
|
-
epicId,
|
|
99
|
-
phaseTimingsPath,
|
|
100
|
-
projectRoot,
|
|
101
|
-
progress,
|
|
102
|
-
logger,
|
|
103
|
-
spawnFn = execFileSync,
|
|
104
|
-
} = ctx;
|
|
105
|
-
const log = reapPhaseLogger(progress);
|
|
106
|
-
if (!phaseTimingsPath) {
|
|
107
|
-
log('PERF', '⏭️ Skipping perf-summary (no phase-timings path provided)');
|
|
108
|
-
return { status: 'skipped', reason: 'no-phase-timings-path' };
|
|
109
|
-
}
|
|
110
|
-
const root = projectRoot ?? process.cwd();
|
|
111
|
-
const analyzerPath = path.join(
|
|
112
|
-
root,
|
|
113
|
-
'.agents',
|
|
114
|
-
'scripts',
|
|
115
|
-
'analyze-execution.js',
|
|
116
|
-
);
|
|
117
|
-
const args = [
|
|
118
|
-
analyzerPath,
|
|
119
|
-
'--story',
|
|
120
|
-
String(storyId),
|
|
121
|
-
'--epic',
|
|
122
|
-
String(epicId),
|
|
123
|
-
'--phase-timings',
|
|
124
|
-
phaseTimingsPath,
|
|
125
|
-
];
|
|
126
|
-
log(
|
|
127
|
-
'PERF',
|
|
128
|
-
`Running analyzer: analyze-execution.js --story ${storyId} --epic ${epicId}`,
|
|
129
|
-
);
|
|
130
|
-
try {
|
|
131
|
-
spawnFn(process.execPath, args, {
|
|
132
|
-
cwd: root,
|
|
133
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
134
|
-
});
|
|
135
|
-
log('PERF', '✅ story-perf-summary posted');
|
|
136
|
-
return { status: 'ok' };
|
|
137
|
-
} catch (err) {
|
|
138
|
-
const reason = err?.message ?? String(err);
|
|
139
|
-
logger?.warn?.(
|
|
140
|
-
`[post-merge-pipeline] ⚠️ analyze-execution failed (non-fatal): ${reason}`,
|
|
141
|
-
);
|
|
142
|
-
return { status: 'failed', reason };
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export const DEFAULT_POST_MERGE_PHASES = Object.freeze([
|
|
147
|
-
{
|
|
148
|
-
name: 'worktree-reap',
|
|
149
|
-
fn: worktreeReapPhase,
|
|
150
|
-
stateKey: 'worktreeReap',
|
|
151
|
-
fallback: createWorktreeReapState({
|
|
152
|
-
status: 'failed',
|
|
153
|
-
reason: 'phase-error',
|
|
154
|
-
}),
|
|
155
|
-
},
|
|
156
|
-
{ name: 'branch-cleanup', fn: branchCleanupPhase, stateKey: 'branchCleanup' },
|
|
157
|
-
{ name: 'ticket-closure', fn: ticketClosurePhase, stateKey: 'ticketClosure' },
|
|
158
|
-
{ name: 'notification', fn: notificationPhase },
|
|
159
|
-
{
|
|
160
|
-
name: 'dashboard-refresh',
|
|
161
|
-
fn: dashboardRefreshPhase,
|
|
162
|
-
stateKey: 'manifestUpdated',
|
|
163
|
-
},
|
|
164
|
-
{ name: 'temp-cleanup', fn: tempCleanupPhase },
|
|
165
|
-
// Detectors MUST run before `perf-summary`. The perf phase shells out
|
|
166
|
-
// to `analyze-execution.js`, which reads the per-Story signals stream
|
|
167
|
-
// to author the `<!-- structured:story-perf-summary -->` comment;
|
|
168
|
-
// emitting rework/retry events first ensures the rendered surface
|
|
169
|
-
// reflects this Story's signals.
|
|
170
|
-
{ name: 'detectors', fn: detectorsPhase, stateKey: 'detectors' },
|
|
171
|
-
{ name: 'perf-summary', fn: perfSummaryPhase, stateKey: 'perfSummary' },
|
|
172
|
-
]);
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Sequence the post-merge phases of `story-close`. Every phase runs
|
|
176
|
-
* under `runPhase` so a single failure logs `[phase=<name>] <err>` and the
|
|
177
|
-
* pipeline keeps going. Each phase's return value is recorded under its
|
|
178
|
-
* `stateKey` (when defined) on the returned state object.
|
|
179
|
-
*
|
|
180
|
-
* @param {object} ctx Phase collaborators (provider, notify,
|
|
181
|
-
* logger, progress, etc.).
|
|
182
|
-
* @param {Array<{name: string, fn: Function, stateKey?: string, fallback?: any}>} [phases]
|
|
183
|
-
* Phase descriptors. Defaults to `DEFAULT_POST_MERGE_PHASES`.
|
|
184
|
-
* @returns {Promise<object>} Aggregated state from each phase.
|
|
185
|
-
*/
|
|
186
|
-
export async function runPostMergePipeline(
|
|
187
|
-
ctx,
|
|
188
|
-
phases = DEFAULT_POST_MERGE_PHASES,
|
|
189
|
-
) {
|
|
190
|
-
const logger = ctx.logger ?? Logger;
|
|
191
|
-
const state = {
|
|
192
|
-
worktreeReap: createWorktreeReapState(),
|
|
193
|
-
branchCleanup: { localDeleted: false, remoteDeleted: false },
|
|
194
|
-
ticketClosure: { closedTickets: [], cascadedTo: [], cascadeFailed: [] },
|
|
195
|
-
manifestUpdated: false,
|
|
196
|
-
};
|
|
197
|
-
for (const phase of phases) {
|
|
198
|
-
const value = await runPhase(phase.name, () => phase.fn(ctx, state), {
|
|
199
|
-
logger,
|
|
200
|
-
fallback: phase.fallback,
|
|
201
|
-
});
|
|
202
|
-
if (phase.stateKey && value !== undefined) state[phase.stateKey] = value;
|
|
203
|
-
}
|
|
204
|
-
return state;
|
|
205
|
-
}
|