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,998 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: >-
|
|
3
|
-
Drive an Epic from `agent::ready` to a merged pull request against `main`.
|
|
4
|
-
The ten-phase flow runs the wave loop, close-validation, the Epic-close
|
|
5
|
-
lens-roster resolve, the code-review pass (which walks the cumulative diff
|
|
6
|
-
once with the lens roster folded in), retro, finalize, watch-and-iterate,
|
|
7
|
-
conditional auto-merge,
|
|
8
|
-
and local branch cleanup. When the run is end-to-end clean (zero manual
|
|
9
|
-
interventions, zero 🔴/🟠 review findings, compact retro) the PR
|
|
10
|
-
auto-merges via `gh pr merge --squash --delete-branch`; otherwise the
|
|
11
|
-
workflow falls back to the operator-merges-button path so a human
|
|
12
|
-
inspects the surface area.
|
|
13
|
-
---
|
|
14
|
-
|
|
15
|
-
# helpers/deliver-epic — Epic delivery path (invoked by /deliver)
|
|
16
|
-
|
|
17
|
-
> **Runtime core.** This file is the always-ingested Epic-delivery path:
|
|
18
|
-
> phase flow, commands, gate contracts, and return shapes. The recovery
|
|
19
|
-
> procedures, historical rationale, and troubleshooting detail live in the
|
|
20
|
-
> sibling [`deliver-epic-reference.md`](deliver-epic-reference.md); each
|
|
21
|
-
> moved procedure keeps a one-line pointer at its trigger point below. The
|
|
22
|
-
> reference is not projected to `.claude/commands/` — it is consulted on
|
|
23
|
-
> demand.
|
|
24
|
-
|
|
25
|
-
## Overview
|
|
26
|
-
|
|
27
|
-
This helper is the **Epic delivery path** behind `/deliver` — the router
|
|
28
|
-
delegates to it once per Epic ID, either as the sole route (single-Epic
|
|
29
|
-
input) or as one **Epic segment** of the sequential segment plan `/deliver`
|
|
30
|
-
composes over mixed Epic / standalone-Story input (Epic segments run in
|
|
31
|
-
input order, after the standalone segment; see
|
|
32
|
-
[`deliver.md`](../deliver.md)). Each invocation opens a PR against `main`
|
|
33
|
-
and auto-merges when every signal certifies a clean run; otherwise it falls
|
|
34
|
-
back to the operator-merges-button path.
|
|
35
|
-
|
|
36
|
-
```text
|
|
37
|
-
/deliver <epicId>
|
|
38
|
-
→ Phase 1 — prepare (epic-deliver-prepare.js)
|
|
39
|
-
→ Phase 2 — ready-set loop (wave-tick.js → dispatch ready set → observe → re-tick)
|
|
40
|
-
→ Phase 3 — close-validation (lint + test + ratchets on epic/<id>)
|
|
41
|
-
→ Phase 4 — epic-close lens roster (helpers/epic-audit.md — resolve the slim cumulative+global+risk-routed roster)
|
|
42
|
-
→ Phase 5 — code-review (helpers/code-review.md scope: epic — walks the cumulative diff once, lens roster folded in)
|
|
43
|
-
→ Phase 6 — retro (.agents/scripts/lib/orchestration/retro-runner.js)
|
|
44
|
-
→ Phase 6.5 — integration gate (whole-product navigability + journey suite; @pending ≠ green for surface-adding Epics — blocks finalize)
|
|
45
|
-
→ Phase 7 — finalize (lifecycle-emit → epic.close.end → open PR to main)
|
|
46
|
-
→ Phase 8 — watch-and-iterate (poll `gh pr checks`; fix locally until green)
|
|
47
|
-
→ Phase 8.5 — auto-merge gate (lifecycle-emit → epic.automerge.start)
|
|
48
|
-
→ Phase 9 — cleanup (BranchCleaner + Cleaner lifecycle listeners on epic.cleanup.start / epic.merge.armed; fire via lifecycle-emit → epic.merge.armed)
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
The argument is always a single Epic ID (`type::epic`) — multi-Epic or
|
|
52
|
-
mixed input is segmented by the `/deliver` router before this helper runs.
|
|
53
|
-
Story IDs go to
|
|
54
|
-
[`helpers/deliver-stories`](deliver-stories.md) (standalone) or the
|
|
55
|
-
[`helpers/epic-deliver-story`](epic-deliver-story.md) helper
|
|
56
|
-
(Epic-attached, invoked by this workflow's fan-out); Tasks are not directly
|
|
57
|
-
executable.
|
|
58
|
-
Story dispatch is in-session via the Agent tool — no subprocess is
|
|
59
|
-
spawned.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Arguments
|
|
64
|
-
|
|
65
|
-
```text
|
|
66
|
-
/deliver <epicId> [--skip-epic-audit] [--skip-code-review] [--skip-retro] [--full-retro] [--skip-integration-gate]
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
- `epicId` — must carry `type::epic`. Otherwise STOP and tell the operator
|
|
70
|
-
to use `/deliver <id>` (standalone Story) or open the parent Epic.
|
|
71
|
-
- `--skip-epic-audit` — skip Phase 4 (log the override). Use only when the
|
|
72
|
-
change-set audits are known to be irrelevant (e.g., docs-only Epic).
|
|
73
|
-
- `--skip-code-review` — skip Phase 5 (log the override).
|
|
74
|
-
- `--skip-retro` — skip Phase 6 (use sparingly).
|
|
75
|
-
- `--full-retro` — force the six-section retro regardless of manifest
|
|
76
|
-
cleanliness. `--skip-retro` wins over `--full-retro`.
|
|
77
|
-
- `--skip-integration-gate` — skip Phase 6.5 (log the override). The
|
|
78
|
-
explicit operator override for the post-wave integration gate,
|
|
79
|
-
consistent with `--skip-epic-audit`. Skipping the gate is recorded as a
|
|
80
|
-
manual intervention and disqualifies auto-merge, exactly like the other
|
|
81
|
-
`--skip-*` overrides.
|
|
82
|
-
|
|
83
|
-
Every other runtime modifier is sourced from the Epic's labels or from
|
|
84
|
-
`delivery.deliverRunner` in `.agentrc.json`.
|
|
85
|
-
|
|
86
|
-
- `--yes` — the top-level `/deliver` unattended flag (suppresses the
|
|
87
|
-
operator confirmation gate; see [`deliver.md`](../deliver.md)). This
|
|
88
|
-
helper reads it as the **headless signal** for Phase 8.5's must-land
|
|
89
|
-
terminal step (Story #4427): when `/deliver` was invoked with `--yes`,
|
|
90
|
-
thread `--headless true` into the Phase 8.5
|
|
91
|
-
`epic.automerge.start` `lifecycle-emit.js` call (omit the flag, or
|
|
92
|
-
pass `--headless false`, for an attended run). The runtime reads this
|
|
93
|
-
as an explicit `headless` input threaded through
|
|
94
|
-
`runLifecycleEmit` → `buildDefaultListenerChain` →
|
|
95
|
-
`MergeWatcher({ headless })` — never an ambient/undefined global.
|
|
96
|
-
Attended-mode behavior (no `--headless` flag) is byte-for-byte
|
|
97
|
-
unchanged: `MergeWatcher` defaults `headless` to `false`.
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## Contract
|
|
102
|
-
|
|
103
|
-
- **Idempotent by checkpoint.** Re-runs resume from `epic-run-state`.
|
|
104
|
-
- **Single pause point.** Only `agent::blocked` halts execution. No
|
|
105
|
-
clarifying questions — if stuck, flip to `agent::blocked`, post a
|
|
106
|
-
friction comment, park.
|
|
107
|
-
- **Flat Story dispatch by design.** Host LLM fans out per-Story Agent
|
|
108
|
-
calls directly — with `subagent_type: story-worker` when
|
|
109
|
-
`delivery.routing.roleScopedAgents` is on (the default; § 2b), else
|
|
110
|
-
`general-purpose`. Keeping Story
|
|
111
|
-
dispatch flat — the host owns the single fan-out level — is a
|
|
112
|
-
**design choice**, not a harness constraint: the wave aggregator, idle
|
|
113
|
-
watchdog, and merge-lock all assume one host-owned dispatch level. As of
|
|
114
|
-
Claude Code 2.1.202 a level-1 sub-agent **does** carry the `Agent` tool
|
|
115
|
-
and can nest further (verified depth 2, announced max depth 5; see
|
|
116
|
-
[#2870](https://github.com/dsj1984/mandrel/issues/2870)), so a Story
|
|
117
|
-
worker may itself fan out for its own sub-work within that depth budget —
|
|
118
|
-
the Epic wave loop nonetheless stays flat by choice, not because nesting
|
|
119
|
-
is unavailable.
|
|
120
|
-
- **Operator-merges-PR exit.** Phase 7 opens the PR; the workflow
|
|
121
|
-
never merges to `main` itself. Phase 8.5 may fire auto-merge when
|
|
122
|
-
every signal is clean.
|
|
123
|
-
- **Lifecycle bus is the runner model.** Phase transitions, ticket
|
|
124
|
-
state flips, structured comments, and notifications are emitted as
|
|
125
|
-
typed events on the in-session lifecycle bus; a fixed roster of
|
|
126
|
-
listeners performs the side effects. Phase 7, 8.5, and 9 each fire
|
|
127
|
-
exactly one lifecycle event via the generic
|
|
128
|
-
[`lifecycle-emit.js`](../../scripts/lifecycle-emit.js) CLI
|
|
129
|
-
(`--event epic.close.end` / `--event epic.automerge.start` /
|
|
130
|
-
`--event epic.merge.armed`); the matching listener chain runs the
|
|
131
|
-
bus-driven side effects (acceptance reconcile, automerge-armer,
|
|
132
|
-
branch cleanup). The append-only NDJSON ledger at
|
|
133
|
-
`temp/epic-<id>/lifecycle.ndjson` is the resume target. See
|
|
134
|
-
[`docs/LIFECYCLE.md`](../../../docs/LIFECYCLE.md) for the bus
|
|
135
|
-
contract, event taxonomy, ledger format, and listener model.
|
|
136
|
-
|
|
137
|
-
> **Hierarchy.** `/deliver` operates over the 2-tier hierarchy
|
|
138
|
-
> (Epic → Story). The fan-out is one `Agent` tool call per
|
|
139
|
-
> Story per wave (§ 2b); Story branches merge into `epic/<id>` with
|
|
140
|
-
> `--no-ff` via `story-close.js`; the close-validation chain
|
|
141
|
-
> (Phase 3), epic-audit, code-review, retro, finalize, and auto-merge
|
|
142
|
-
> gates all operate on Story-level units.
|
|
143
|
-
> [`helpers/epic-deliver-story`](epic-deliver-story.md) runs a
|
|
144
|
-
> single Story-implementation phase per Story against the Story's
|
|
145
|
-
> inline `acceptance[]` / `verify[]` fields. See
|
|
146
|
-
> [`.agents/instructions.md` § 5.D](../../instructions.md) and
|
|
147
|
-
> [`.agents/docs/SDLC.md` § Ticket hierarchy](../../docs/SDLC.md) for the full
|
|
148
|
-
> contract.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Phase 1 — Prepare the Epic run
|
|
153
|
-
|
|
154
|
-
### Phase 1 prelude — Delivery preflight (Story #2899 / F13)
|
|
155
|
-
|
|
156
|
-
Before `epic-deliver-prepare.js` seeds the checkpoint, run
|
|
157
|
-
`epic-deliver-preflight.js` so the operator (and any reviewer reading the
|
|
158
|
-
Epic ticket) sees the estimated Story count, install cost, dependency depth,
|
|
159
|
-
GitHub API request volume, and Claude Max quota burn for the run that is
|
|
160
|
-
about to fan out. **Preflight always runs before Story fan-out.**
|
|
161
|
-
|
|
162
|
-
```bash
|
|
163
|
-
node .agents/scripts/epic-deliver-preflight.js --epic <epicId> --post
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
The CLI upserts a `delivery-preflight` structured comment on the Epic
|
|
167
|
-
(idempotent across re-runs) and prints a JSON envelope on stdout with
|
|
168
|
-
the canonical metric keys `storyCount`, `installCostSeconds`,
|
|
169
|
-
`dependencyDepth` (the longest dependency chain — the ready-set wall-clock
|
|
170
|
-
floor, replacing the retired wave count), `githubApiRequests`,
|
|
171
|
-
`claudeQuotaTokens`, plus a `breaches` array describing any
|
|
172
|
-
`delivery.preflight.max*` thresholds the estimate exceeds.
|
|
173
|
-
|
|
174
|
-
**Breach handling.** When `breaches` is non-empty, the workflow MUST
|
|
175
|
-
flip the Epic to `agent::blocked`, surface the envelope in chat for the
|
|
176
|
-
operator, and halt before Phase 1's `epic-deliver-prepare.js` call.
|
|
177
|
-
Resume after the operator unblocks (raising the threshold in
|
|
178
|
-
`.agentrc.json`, splitting the Epic, or accepting the cost) by re-running
|
|
179
|
-
`/deliver <epicId>` — the preflight is idempotent and the second
|
|
180
|
-
run upserts the same comment in place.
|
|
181
|
-
|
|
182
|
-
Threshold defaults live in `delivery.preflight.*` in `.agentrc.json`
|
|
183
|
-
(all keys default to "no cap" — the gate is opt-in until an operator
|
|
184
|
-
configures `maxStories` etc.).
|
|
185
|
-
|
|
186
|
-
**Remote evidence — land or block (issue #4483).** The envelope also
|
|
187
|
-
carries `remoteVerified` + `remoteProbe` (deterministic probes:
|
|
188
|
-
`git remote get-url origin`, bounded `git ls-remote origin HEAD`). When
|
|
189
|
-
`remoteVerified` is `false`, flip the Epic to `agent::blocked`, post a
|
|
190
|
-
friction comment quoting `remoteProbe.detail`, and halt — the same
|
|
191
|
-
explicit-block shape as #4425/#4480. NEVER fall back to executing Stories
|
|
192
|
-
inline in this session or committing the delivery to local `main`; the
|
|
193
|
-
worktree/branch/PR path below is the only sanctioned mechanism. Phase 7's
|
|
194
|
-
finalize additionally refuses with a `delivery-branch-missing-on-origin`
|
|
195
|
-
blocker when `epic/<epicId>` never reached origin.
|
|
196
|
-
|
|
197
|
-
### Phase 1 main — Seed the wave plan
|
|
198
|
-
|
|
199
|
-
```bash
|
|
200
|
-
node .agents/scripts/epic-deliver-prepare.js --epic <epicId> [--steal] [--as <handle>]
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Validates `type::epic`, enumerates `type::story` descendants, parses
|
|
204
|
-
`blocked by #N` plus explicit `dependencies`, computes the dependency DAG
|
|
205
|
-
(to enumerate the open Story set), and upserts the `epic-run-state`
|
|
206
|
-
checkpoint in the per-Story-status shape (a flat `stories` map seeded at
|
|
207
|
-
`pending`, plus the global `concurrencyCap`). Treat the printed JSON as
|
|
208
|
-
`state`: `{ epicId, storyCount, concurrencyCap, stories, checkpointInitializedAt, docsDigestPath }`.
|
|
209
|
-
`stories` is the flat dispatch hint (`{ storyId, worktree, title,
|
|
210
|
-
checklistPath }` per open Story); the ready-set `tick` (Phase 2) decides
|
|
211
|
-
which to dispatch on each beat. `checklistPath` is the repo-relative path to
|
|
212
|
-
that Story's footprint-matched local-lens authoring checklist
|
|
213
|
-
(`temp/epic-<epicId>/checklists/story-<storyId>.md`, Story #4410) — thread it
|
|
214
|
-
into that child's prompt (§ 2b, item 7); it is `null` when the Story's
|
|
215
|
-
predicted footprint matched no local lens. `docsDigestPath` is the
|
|
216
|
-
repo-relative path to the per-Epic docs digest
|
|
217
|
-
(`temp/epic-<epicId>/docs-digest.md`) that prepare writes from
|
|
218
|
-
`project.docsContextFiles` — thread it into every child prompt (§ 2b, item 6).
|
|
219
|
-
It is `null` when the project configured no `docsContextFiles` (no digest is
|
|
220
|
-
written). Flip the Epic to `agent::executing` (idempotent) after the CLI
|
|
221
|
-
returns.
|
|
222
|
-
|
|
223
|
-
**No spec-ticket linkage to resolve (Story #4324).** The Tech Spec lives
|
|
224
|
-
as managed sections of the Epic body itself — there is no separate
|
|
225
|
-
Tech-Spec issue id in the envelope and no `--tech-spec` flag to thread
|
|
226
|
-
into the per-Story `story-init.js` invocations. Story agents receive the
|
|
227
|
-
Tech Spec via context hydration, which embeds the Epic body (with the
|
|
228
|
-
`## Acceptance Table` section stripped) directly into each Story prompt.
|
|
229
|
-
|
|
230
|
-
> **Preflight guards + acceptance-table start gate.** Before the snapshot
|
|
231
|
-
> phase runs — and before any worktree is created — prepare runs two
|
|
232
|
-
> **fail-closed** guards (checkout safety + Epic lease), then the snapshot
|
|
233
|
-
> phase asserts the Epic carries an `acceptance::n-a` waiver or a
|
|
234
|
-
> `## Acceptance Table` section. Both throw on failure. See
|
|
235
|
-
> [`deliver-epic-reference.md` § Phase 1 — Preflight guards](deliver-epic-reference.md#phase-1--preflight-guards-story-3482--f-workflow-guards)
|
|
236
|
-
> for the remediation detail (dirty-tree recovery, `--steal`, waiver
|
|
237
|
-
> options).
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## Phase 2 — Ready-set loop
|
|
242
|
-
|
|
243
|
-
The scheduler lives in
|
|
244
|
-
[`lib/wave-runner/tick.js`](../../scripts/lib/wave-runner/tick.js) — a thin
|
|
245
|
-
**Epic adapter over the ready-set core**
|
|
246
|
-
([`lib/wave-runner/ready-set.js`](../../scripts/lib/wave-runner/ready-set.js)).
|
|
247
|
-
One stateless `tick({ epic })` call re-derives readiness from the **live**
|
|
248
|
-
Story bodies + labels on every beat and returns one `WaveTickResult`
|
|
249
|
-
describing the next action. There is **no wave barrier** (Story #4155): a
|
|
250
|
-
Story whose own dependencies are all done is dispatched the instant a slot is
|
|
251
|
-
free under the GLOBAL in-flight cap, even while an unrelated sibling Story is
|
|
252
|
-
still `agent::executing`. The loop is simply:
|
|
253
|
-
|
|
254
|
-
```text
|
|
255
|
-
tick → dispatch the ready set → observe → re-tick → … → epic-complete
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
The slash command's job each beat is to call `tick()` via its CLI shim,
|
|
259
|
-
dispatch the Stories in `nextAction.stories` via the Agent tool, record each
|
|
260
|
-
returned Story's terminal status, and re-tick until terminal. There is no
|
|
261
|
-
`record-wave` / `currentWave` step — the checkpoint carries only a flat
|
|
262
|
-
per-Story status map (for resume + the operator rollup) and the global cap.
|
|
263
|
-
|
|
264
|
-
### 2.0. Open the Epic PR as a draft at wave 1 (Story #4359)
|
|
265
|
-
|
|
266
|
-
When `delivery.ci.earlyPr` is on (the default), open the Epic PR as a
|
|
267
|
-
**draft** once, before the first `tick`, so every subsequent per-wave push
|
|
268
|
-
to `epic/<epicId>` runs CI attributed to its own wave. (CI is keyed on the PR
|
|
269
|
-
ref with `cancel-in-progress`, so each new wave push **supersedes** the prior
|
|
270
|
-
wave's in-flight run rather than queuing behind it — the latest wave always
|
|
271
|
-
gets the verdict, and CI-minute use stays bounded; intermediate wave runs are
|
|
272
|
-
cancelled, not completed.) Resolve the flag through the
|
|
273
|
-
[`getCiDelivery`](../../scripts/lib/config/ci.js) accessor (default `true`);
|
|
274
|
-
do not read `delivery.ci.earlyPr` directly.
|
|
275
|
-
|
|
276
|
-
> **This step is host-LLM-driven, with no runtime enforcement seam** — unlike
|
|
277
|
-
> the Phase 7 ready-flip, which the `Finalizer` listener wires in
|
|
278
|
-
> deterministically (`finalizer.js` resolves `earlyPr` and calls
|
|
279
|
-
> `markPrReady`). The asymmetry is intentional and safe: if this wave-1
|
|
280
|
-
> draft-open is skipped, the `earlyPr`-on Phase 7 `markPrReady` call degrades
|
|
281
|
-
> to a no-op on the PR that finalize opens at close time (`gh pr ready` is a
|
|
282
|
-
> no-op on an already-ready PR), so the merge gate is never stranded — the run
|
|
283
|
-
> only loses the per-wave CI attribution this step buys.
|
|
284
|
-
|
|
285
|
-
- **`earlyPr` on** — call
|
|
286
|
-
[`openOrLocatePr`](../../scripts/lib/orchestration/finalize/open-or-locate-pr.js)
|
|
287
|
-
with `{ epicId, headBranch: 'epic/<epicId>', baseBranch: 'main', draft: true }`.
|
|
288
|
-
The helper probes for an existing open PR first, so this is idempotent — a
|
|
289
|
-
resumed `/deliver` run re-locates the same draft and opens no duplicate.
|
|
290
|
-
Phase 7 later flips this draft to ready-for-review (it does **not**
|
|
291
|
-
re-create the PR).
|
|
292
|
-
- **`earlyPr` off** — skip this step entirely. No draft is opened at wave 1;
|
|
293
|
-
Phase 7 opens the PR at close time on the pre-Story timing.
|
|
294
|
-
|
|
295
|
-
The draft carries the same title/body contract Phase 7 uses
|
|
296
|
-
(`feat: Epic #<epicId>` / `Closes #<epicId>`), so no title/body reconciliation
|
|
297
|
-
is needed when it is marked ready.
|
|
298
|
-
|
|
299
|
-
### 2a. Tick — plan the next action
|
|
300
|
-
|
|
301
|
-
```bash
|
|
302
|
-
node .agents/scripts/wave-tick.js --epic <epicId>
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
Stdout is one `WaveTickResult` envelope:
|
|
306
|
-
|
|
307
|
-
```json
|
|
308
|
-
{
|
|
309
|
-
"nextAction":
|
|
310
|
-
{ "kind": "dispatch", "stories": [{ "id": <n>, "title": "…" }, ...], "in-flight": [<storyId>, ...] }
|
|
311
|
-
| { "kind": "observe", "waitingOn": [<storyId>, ...], "in-flight": [<storyId>, ...] }
|
|
312
|
-
| { "kind": "halt", "reason": "dependency-cycle" | "unsatisfiable-dependency", "stuckStories": [<storyId>, ...], "cycle"?: [<storyId>, ...], "in-flight": [<storyId>, ...] }
|
|
313
|
-
| { "kind": "epic-complete", "in-flight": [<storyId>, ...] },
|
|
314
|
-
"blockedStories": [{ "storyId": <n>, "reason": "…", "detail"?: "…" }, ...],
|
|
315
|
-
"gateFailures": [{ "storyId": <n>, "gate": "…", "detail"?: "…" }, ...],
|
|
316
|
-
"readyCount": <n>,
|
|
317
|
-
"inFlight": [<storyId>, ...]
|
|
318
|
-
}
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
`nextAction.stories` is the **ready set** for this beat — the
|
|
322
|
-
dependency-satisfied, overlap-free subset of open Stories, capped at
|
|
323
|
-
`globalCap − inFlight`. The CLI is a planner: it dispatches nothing and
|
|
324
|
-
persists nothing. It emits only the two wave-window forensics signals that
|
|
325
|
-
have a live consumer — `wave-start` (on the run's first dispatch) and
|
|
326
|
-
`wave-complete` (when the run finishes), which the perf-aggregator brackets
|
|
327
|
-
into the `waveParallelism` report (and `wave-start` anchors span-tree Story
|
|
328
|
-
spans). The [`signals` helper](signals.md)
|
|
329
|
-
(`node .agents/scripts/signals-view.js`) renders the forensics signals in the
|
|
330
|
-
span-tree view.
|
|
331
|
-
|
|
332
|
-
> **Old-shape checkpoint → fail-closed.** A pre-ready-set
|
|
333
|
-
> (`plan` / `currentWave` / `totalWaves`) checkpoint makes the tick refuse
|
|
334
|
-
> to run and throw; re-seed via `epic-deliver-prepare.js`. See
|
|
335
|
-
> [`deliver-epic-reference.md` § Fail-closed on an old-shape checkpoint](deliver-epic-reference.md#fail-closed-on-an-old-shape-checkpoint).
|
|
336
|
-
|
|
337
|
-
### 2b. Dispatch — fan out per-Story Agent calls
|
|
338
|
-
|
|
339
|
-
*You* (the LLM running this skill) are the dispatcher; you never invoke
|
|
340
|
-
`helpers/epic-deliver-story` yourself. Emit **one `Agent` tool call per
|
|
341
|
-
Story** in `nextAction.stories` (even when `length === 1` — the
|
|
342
|
-
parent-child boundary keeps the return-parser uniform). The *children*
|
|
343
|
-
run [`helpers/epic-deliver-story`](epic-deliver-story.md).
|
|
344
|
-
|
|
345
|
-
**Sub-agent type (Epic #4478, M7-B).** When
|
|
346
|
-
`delivery.routing.roleScopedAgents` is enabled (the **default**), each Story
|
|
347
|
-
child is dispatched with `subagent_type: story-worker` — it boots on the
|
|
348
|
-
role-scoped [`story-worker`](../../agents/story-worker.md) context (its own
|
|
349
|
-
system prompt, no `CLAUDE.md` @-closure) that carries every load-bearing
|
|
350
|
-
delivery MUST standalone, so the spawn stops re-paying the always-loaded
|
|
351
|
-
context. When the kill-switch is **off**
|
|
352
|
-
(`delivery.routing.roleScopedAgents: false`), fall back to
|
|
353
|
-
`subagent_type: general-purpose` — the instant, code-rollback-free revert and
|
|
354
|
-
the escape for hosts that ignore `.claude/agents/`.
|
|
355
|
-
|
|
356
|
-
Emit **one assistant turn** with **N parallel `Agent` calls** where
|
|
357
|
-
`N === nextAction.stories.length` (the ready set is already capped at
|
|
358
|
-
`globalCap − inFlight` by the tick, so it never exceeds available slots).
|
|
359
|
-
Dispatch the ready set as background calls (`run_in_background: true`) and,
|
|
360
|
-
as each child returns, record it (§ 2c) and **re-tick** (§ 2a) to pull the
|
|
361
|
-
next ready set — never wait for the whole set before refilling.
|
|
362
|
-
|
|
363
|
-
> **Throughput + capability tuning.** The default `concurrencyCap` of 3 is
|
|
364
|
-
> a deliberate operator-tuning knob (raise
|
|
365
|
-
> `delivery.deliverRunner.concurrencyCap`), and the optional per-call
|
|
366
|
-
> `model:` escape hatch lets mechanical Stories run on a cheaper capability.
|
|
367
|
-
> See [`deliver-epic-reference.md` § Throughput tradeoff](deliver-epic-reference.md#throughput-tradeoff)
|
|
368
|
-
> and [§ Sub-agent dispatch capability](deliver-epic-reference.md#sub-agent-dispatch-capability).
|
|
369
|
-
|
|
370
|
-
**Ledger the dispatch BEFORE the Agent call.** Immediately before each
|
|
371
|
-
per-Story `Agent` tool call (one shell-out per Story, every attempt —
|
|
372
|
-
including retries from a refill), invoke
|
|
373
|
-
[`lifecycle-emit-story-dispatch.js`](../../scripts/lifecycle-emit-story-dispatch.js)
|
|
374
|
-
so the lifecycle ledger durably records the dispatch attempt. The
|
|
375
|
-
emit must happen **before** the Agent call fires — never after — so
|
|
376
|
-
that a host-process crash mid-Agent leaves a `story.dispatch.start`
|
|
377
|
-
record that `wave-tick.js` (see § 2a) excludes from the next beat's ready
|
|
378
|
-
set and surfaces under `nextAction['in-flight']`:
|
|
379
|
-
|
|
380
|
-
```bash
|
|
381
|
-
node .agents/scripts/lifecycle-emit-story-dispatch.js \
|
|
382
|
-
--epic <epicId> --story <storyId> \
|
|
383
|
-
--wave 0 --attempt <attempt>
|
|
384
|
-
```
|
|
385
|
-
|
|
386
|
-
Pass `--wave 0` — the ready-set runtime has a single continuous front, so
|
|
387
|
-
the ledger's `waveIndex` is a fixed `0` (it is metadata for the start/end
|
|
388
|
-
pairing math, not a scheduling input). `<attempt>` starts at 1 for the
|
|
389
|
-
Story's first dispatch and increments on each retry/refill. The CLI appends
|
|
390
|
-
exactly one NDJSON line to `temp/epic-<epicId>/lifecycle.ndjson`; the
|
|
391
|
-
matching `story.dispatch.end` record is appended later by
|
|
392
|
-
`epic-execute-record-wave.js` (via `emit-story-dispatch-end.js`, Story #3900)
|
|
393
|
-
after the Agent return is recorded in § 2c.
|
|
394
|
-
|
|
395
|
-
Each Agent call's prompt must (1) name the Story + Epic ids, (2)
|
|
396
|
-
instruct the child to invoke `helpers/epic-deliver-story <storyId>`
|
|
397
|
-
(whose Step 4 defines the child's return shape), (3) remind the child
|
|
398
|
-
of the **non-interactive contract** (no clarifying questions;
|
|
399
|
-
transition to `agent::blocked` and exit if stuck), (4) tell the child to
|
|
400
|
-
suppress per-Story chat relay and instead relay **one line per phase
|
|
401
|
-
transition** (e.g. `Story #<id>: implementing → closing`) — the child's
|
|
402
|
-
authoritative progress lands in the `story-run-progress` snapshot the
|
|
403
|
-
`story-phase.js` CLI upserts, not in a verbatim body dump, (5) remind the
|
|
404
|
-
child that its `story.heartbeat` liveness signal is emitted **off the token
|
|
405
|
-
stream** by the PostToolUse hook (Epic #4476) — a throttled heartbeat lands as
|
|
406
|
-
a free byproduct of every tool call, keyed off the active-Story env
|
|
407
|
-
`story-init.js` exported — so it does **not** run `story-phase.js` per step
|
|
408
|
-
just to heartbeat (a `story-phase.js` call at a genuine Story-level phase
|
|
409
|
-
transition still renders the snapshot and stamps an operator-bearing
|
|
410
|
-
heartbeat), and if it cannot make progress it must transition to
|
|
411
|
-
`agent::blocked` rather than fall silent, and (6) pass the
|
|
412
|
-
**docs digest path** — the `docsDigestPath` field from the
|
|
413
|
-
`epic-deliver-prepare.js` envelope (§ Phase 1 main), which points at
|
|
414
|
-
`temp/epic-<epicId>/docs-digest.md`. Instruct the child to read that
|
|
415
|
-
digest instead of re-reading the full `project.docsContextFiles` set,
|
|
416
|
-
and to pull individual docs files on demand (per
|
|
417
|
-
[`.agents/instructions.md` § 3](../../instructions.md)). When
|
|
418
|
-
`docsDigestPath` is null (the project configured no `docsContextFiles`),
|
|
419
|
-
say so — the child then has no per-Story docs mandate. (7) pass the
|
|
420
|
-
**checklist path** — the `checklistPath` field from that Story's entry in
|
|
421
|
-
the `stories` dispatch hint (Story #4410), which points at
|
|
422
|
-
`temp/epic-<epicId>/checklists/story-<storyId>.md`. Instruct the child to
|
|
423
|
-
read that file (footprint-matched local-lens authoring checklists) and
|
|
424
|
-
self-check its change against those concerns while writing. When
|
|
425
|
-
`checklistPath` is null (the Story's predicted footprint matched no local
|
|
426
|
-
lens), there is nothing to read. The pairing of
|
|
427
|
-
`story.heartbeat` and `agent::blocked` is what lets the § 2d Idle
|
|
428
|
-
Watchdog distinguish a working child from a dead one; a silent child
|
|
429
|
-
with no recent heartbeat and no blocker label is the failure mode the
|
|
430
|
-
watchdog is built to catch.
|
|
431
|
-
|
|
432
|
-
There is **no per-child JSON return-parsing ceremony** for the parent
|
|
433
|
-
to enforce. GitHub state is the contract: `epic-execute-record-wave.js`
|
|
434
|
-
(§ 2c, mode B) treats each child's raw return text as a best-effort
|
|
435
|
-
hint and reconciles any unparseable, empty, or missing return directly
|
|
436
|
-
from the Story's live labels and comments.
|
|
437
|
-
|
|
438
|
-
### 2c. Record the Story outcomes
|
|
439
|
-
|
|
440
|
-
As dispatched Stories return (record them as they land — you need not wait
|
|
441
|
-
for the whole ready set), persist each Story's terminal status via
|
|
442
|
-
`epic-execute-record-wave.js`. There is **no `--wave` flag and no
|
|
443
|
-
`currentWave`** — the recorder splices each Story's status into the
|
|
444
|
-
checkpoint's flat per-Story map and re-renders the rollup:
|
|
445
|
-
|
|
446
|
-
```bash
|
|
447
|
-
# Mode A — host LLM already parsed each child return.
|
|
448
|
-
node .agents/scripts/epic-execute-record-wave.js \
|
|
449
|
-
--epic <epicId> --results @<file>|<inline-json>
|
|
450
|
-
|
|
451
|
-
# Mode B — pipe the raw per-Story sub-agent return texts directly.
|
|
452
|
-
node .agents/scripts/epic-execute-record-wave.js \
|
|
453
|
-
--epic <epicId> --returns @<file>|<inline-json>
|
|
454
|
-
# `<inline-json>` shape: [{ "storyId": <n>, "returnText": "<raw text>" }]
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
**Mode B is the default path** — pipe the raw return texts through
|
|
458
|
-
without inspecting them. The CLI reconciles parse failures from GitHub,
|
|
459
|
-
records each Story's terminal status, emits one `story.dispatch.end` per
|
|
460
|
-
recorded Story (closing the ledger pairing), re-renders
|
|
461
|
-
`epic-run-progress`, and prints `{ status, nextAction, renderedBody, ... }`.
|
|
462
|
-
Print `renderedBody` verbatim, then optionally append a short **Notable**
|
|
463
|
-
section (0–5 bullets on newly blocked / failed / slow Stories, friction,
|
|
464
|
-
elapsed-time surprises).
|
|
465
|
-
|
|
466
|
-
> **Crash recovery.** A child that finished but was never recorded is
|
|
467
|
-
> re-derived from its live label on the next `tick` and never
|
|
468
|
-
> re-dispatched. See
|
|
469
|
-
> [`deliver-epic-reference.md` § Crash recovery (record step)](deliver-epic-reference.md#crash-recovery-record-step)
|
|
470
|
-
> for the manual re-record command.
|
|
471
|
-
|
|
472
|
-
### 2d. Loop on `nextAction`
|
|
473
|
-
|
|
474
|
-
After `2c`, re-run `wave-tick.js`. Branch on the new envelope:
|
|
475
|
-
|
|
476
|
-
- `dispatch` → repeat 2b/2c for the new ready set (the next beat's
|
|
477
|
-
dependency-satisfied Stories), then re-tick.
|
|
478
|
-
- `observe` → poll the Epic (children may still be in flight, or some
|
|
479
|
-
are `agent::blocked`). If `blockedStories` is non-empty, post a
|
|
480
|
-
friction comment, flip Epic to `agent::blocked`, park.
|
|
481
|
-
- `halt` → the run is stuck: no Story is dispatchable, nothing is in
|
|
482
|
-
flight, yet not every Story is done. `reason` distinguishes the two
|
|
483
|
-
causes — `dependency-cycle` (the in-scope Stories form a `blocked by`
|
|
484
|
-
cycle; `cycle` lists the offending Story ids) or
|
|
485
|
-
`unsatisfiable-dependency` (a Story is gated on a dependency that can
|
|
486
|
-
never satisfy). `stuckStories` names the Story id(s) that stranded the
|
|
487
|
-
run. Post a friction comment quoting `reason` + `stuckStories`, flip the
|
|
488
|
-
Epic to `agent::blocked`, and park for the operator. **Never** treat a
|
|
489
|
-
`halt` as completion — proceeding to Phase 3 would silently drop the
|
|
490
|
-
stuck Story.
|
|
491
|
-
- `epic-complete` → **every** in-scope Story is done and nothing is in
|
|
492
|
-
flight; proceed to Phase 3. (The tick returns `epic-complete` only when
|
|
493
|
-
the done count equals the in-scope Story count — a stuck Story surfaces
|
|
494
|
-
as `halt`, not a false `epic-complete`.)
|
|
495
|
-
|
|
496
|
-
> **Idle Watchdog.** While any Story is in flight, re-tick every 30 minutes
|
|
497
|
-
> with `wave-tick.js --epic <epicId> --check-idle 30` so a silent child
|
|
498
|
-
> (crashed host, lost return) is surfaced as a `wave-stall` and
|
|
499
|
-
> re-dispatched or blocked. The full cadence, staleness test (heartbeat +
|
|
500
|
-
> deterministic branch-commit signal), stall envelope, and the "why 30 not
|
|
501
|
-
> 10" rationale are in
|
|
502
|
-
> [`deliver-epic-reference.md` § 2e. Idle Watchdog](deliver-epic-reference.md#2e-idle-watchdog).
|
|
503
|
-
> Stop the cadence once the tick returns `epic-complete`.
|
|
504
|
-
|
|
505
|
-
---
|
|
506
|
-
|
|
507
|
-
## Phase 3 — Close-validation
|
|
508
|
-
|
|
509
|
-
Run lint + test + ratchets against `epic/<epicId>` before opening the PR:
|
|
510
|
-
|
|
511
|
-
```bash
|
|
512
|
-
node .agents/scripts/evidence-gate.js \
|
|
513
|
-
--epic-id <epicId> --scope-id <epicId> --gate lint -- npm run lint
|
|
514
|
-
node .agents/scripts/evidence-gate.js \
|
|
515
|
-
--epic-id <epicId> --scope-id <epicId> --gate test -- npm test
|
|
516
|
-
```
|
|
517
|
-
|
|
518
|
-
If either gate fails: STOP, fix on a hotfix branch, merge back to the
|
|
519
|
-
Epic branch, restart this phase.
|
|
520
|
-
|
|
521
|
-
### 3.1 Refresh ratcheted baselines
|
|
522
|
-
|
|
523
|
-
Inspect the scripts in `.husky/pre-push` (typecheck, lint, maintainability,
|
|
524
|
-
design tokens, dependency audits, bundle-size budgets). Run each against
|
|
525
|
-
the Epic branch; if any drifts, refresh and commit
|
|
526
|
-
`chore(baselines): refresh <name> for Epic #<epicId>`.
|
|
527
|
-
|
|
528
|
-
---
|
|
529
|
-
|
|
530
|
-
## Phase 4 — Epic-close lens roster
|
|
531
|
-
|
|
532
|
-
Skip when `--skip-epic-audit`. Otherwise auto-invoke
|
|
533
|
-
[`helpers/epic-audit.md`](epic-audit.md) inline to **resolve** (not walk) the
|
|
534
|
-
slim Epic-close lens roster. The helper runs
|
|
535
|
-
[`epic-audit-prepare.js`](../../scripts/epic-audit-prepare.js) to ask the
|
|
536
|
-
[`selectAudits`](../../scripts/lib/audit-suite/index.js) SDK which lenses fire
|
|
537
|
-
at the `gate3` close gate, **unions in the model-judged risk-routed lenses**
|
|
538
|
-
(Story #3889 — `epic-audit-prepare.js` reads the Epic's `planningRisk`
|
|
539
|
-
envelope off the `epic-plan-state` checkpoint and maps each high-risk axis to
|
|
540
|
-
its lens via `resolveAuditLenses`), then restricts that union to the tiers the
|
|
541
|
-
Epic-close tier owns via
|
|
542
|
-
[`selectEpicCloseLenses`](../../scripts/lib/orchestration/code-review.js): the
|
|
543
|
-
envelope's **`epicCloseLenses`** field is the slim roster of **cumulative +
|
|
544
|
-
global + risk-routed** lenses, with every **local-tier** change-set lens
|
|
545
|
-
excluded (its concern is already verified shift-left at the write-time and
|
|
546
|
-
Story-scope tiers). A high-risk Epic still auto-runs its risk-routed lenses
|
|
547
|
-
even when the change set alone did not select them; a docs-only or
|
|
548
|
-
already-shift-left-covered change set yields an empty roster.
|
|
549
|
-
|
|
550
|
-
**The lens roster is not walked here.** Story #4412 folded the standalone
|
|
551
|
-
Phase 4 lens walk into the Phase 5 code-review pass so the cumulative Epic diff
|
|
552
|
-
is walked **once**. Phase 4 resolves `epicCloseLenses` (plus `depth`,
|
|
553
|
-
`globalLenses`, `substitutionsPayload`) and hands it to Phase 5; there is no
|
|
554
|
-
separate `audit-results` comment. Remediation of the lens findings happens in
|
|
555
|
-
Phase 5 and is **tier-aware** (Story #4412): the Epic-close tier reads
|
|
556
|
-
`delivery.epicAudit.autoFixSeverity` (default **`high`**) and routes only
|
|
557
|
-
🔴 Critical + 🟠 High lens findings into on-branch remediation while 🟡 Medium
|
|
558
|
-
and 🟢 Suggestion findings graduate (🟡 Medium concerns are already remediated
|
|
559
|
-
shift-left); setting `medium` opts back into routing 🔴/🟠/🟡.
|
|
560
|
-
|
|
561
|
-
- **Selector reports `degraded: true`** — STOP. Propagate the
|
|
562
|
-
`reason`/`detail`, post a friction comment, do not fall back to a
|
|
563
|
-
full-roster audit.
|
|
564
|
-
- **`epicCloseLenses` is empty** (docs-only change set, or every selected lens
|
|
565
|
-
already covered shift-left, and no risk-routed lens) — there is no lens
|
|
566
|
-
dimension for Phase 5 to walk; continue to Phase 5, which still runs its
|
|
567
|
-
review pillars.
|
|
568
|
-
|
|
569
|
-
---
|
|
570
|
-
|
|
571
|
-
## Phase 5 — Code review (cumulative diff walked once)
|
|
572
|
-
|
|
573
|
-
Skip when `--skip-code-review`. Otherwise resolve the **risk-derived review
|
|
574
|
-
depth** for this Epic, then auto-invoke
|
|
575
|
-
[`helpers/code-review.md`](code-review.md) inline (read-only audit)
|
|
576
|
-
with the argument envelope `{ scope: 'epic', ticketId: <epicId>, baseRef:
|
|
577
|
-
'main', headRef: 'epic/<epicId>', depth: <reviewDepth> }`, threading the
|
|
578
|
-
Phase 4 `epicCloseLenses` roster through. The pass walks the cumulative
|
|
579
|
-
`main..epic/<epicId>` diff **once**: it executes the Epic-close lens roster as
|
|
580
|
-
review dimensions (helper Step 1b) **and** the review pillars, folding both
|
|
581
|
-
into a single aggregate. Findings persist as the unified `verification-results`
|
|
582
|
-
structured comment on the Epic (the single findings surface — Story #4411
|
|
583
|
-
unified the former `code-review` and `audit-results` contracts, and Story #4412
|
|
584
|
-
folded the lens walk into this pass).
|
|
585
|
-
|
|
586
|
-
The `depth` is the live epic-scope producer for Story #3876's review-depth
|
|
587
|
-
lever (Story #3937). Resolve it from the Epic's judged risk envelope the same
|
|
588
|
-
best-effort way Phase 4 routes audit lenses — via
|
|
589
|
-
[`resolveReviewDepthForEpic`](../../scripts/lib/orchestration/code-review.js),
|
|
590
|
-
which reads `planningRisk.overallLevel` off the Epic's `epic-plan-state`
|
|
591
|
-
checkpoint and maps it: `high` → `deep`, `low` → `light`, everything else
|
|
592
|
-
(including a missing/unparseable checkpoint, or an Epic that skipped
|
|
593
|
-
`/plan`) → `standard`. The helper threads `depth` into `runCodeReview`,
|
|
594
|
-
which forwards it to every provider's `runReview` input; the LLM-backed
|
|
595
|
-
providers (codex, security-review, ultrareview) render it into the prompt they
|
|
596
|
-
emit so a high-risk Epic gets a deeper adversarial pass and a low-risk one a
|
|
597
|
-
lighter one. Depth is **input-only** — it never changes the findings envelope
|
|
598
|
-
or the posted comment shape.
|
|
599
|
-
|
|
600
|
-
Remediation in this pass is **tier-aware and split by finding class**
|
|
601
|
-
(Story #4412). The **review-pillar** findings (Pillars 1–4) route off
|
|
602
|
-
`delivery.codeReview.autoFixSeverity` (default **`medium`** — 🔴/🟠/🟡
|
|
603
|
-
on-branch, Mediums batched per lens, 🟢 stays on the comment). The **Epic-close
|
|
604
|
-
lens** findings (Step 1b) route off `delivery.epicAudit.autoFixSeverity`
|
|
605
|
-
(default **`high`** — only 🔴/🟠 on-branch; 🟡 Medium + 🟢 graduate, because
|
|
606
|
-
🟡 Medium concerns were already remediated shift-left). Setting either key to
|
|
607
|
-
its other value re-widens or narrows that class. Remediated findings are
|
|
608
|
-
rendered under the single `verification-results` comment's `## Fixed on-branch`
|
|
609
|
-
section so they never graduate to follow-up issues. The severity gate below is
|
|
610
|
-
**unchanged** — it keys off the surviving (unfixed) findings.
|
|
611
|
-
|
|
612
|
-
- **Any surviving 🔴 Critical Blocker** — STOP. Relay to the operator.
|
|
613
|
-
- **Only 🟠/🟡/🟢 surviving** — log as non-blocking and continue.
|
|
614
|
-
|
|
615
|
-
---
|
|
616
|
-
|
|
617
|
-
## Phase 6 — Retro
|
|
618
|
-
|
|
619
|
-
Skip when `--skip-retro`. Otherwise post the `epic-perf-report` via
|
|
620
|
-
`node .agents/scripts/analyze-execution.js --epic <epicId>` (failure →
|
|
621
|
-
warn and continue; the retro runner falls back). Then invoke the retro
|
|
622
|
-
runner via its CLI wrapper:
|
|
623
|
-
|
|
624
|
-
```bash
|
|
625
|
-
node .agents/scripts/retro-run.js --epic <epicId>
|
|
626
|
-
```
|
|
627
|
-
|
|
628
|
-
[`retro-run.js`](../../scripts/retro-run.js) resolves the config/provider,
|
|
629
|
-
constructs a lifecycle bus with a `LedgerWriter` (so the run's
|
|
630
|
-
`retro.start` / `retro.end` boundaries land in
|
|
631
|
-
`temp/epic-<epicId>/lifecycle.ndjson`), and calls `runRetro` — the
|
|
632
|
-
canonical compose-and-post surface at
|
|
633
|
-
[`.agents/scripts/lib/orchestration/retro-runner.js`](../../scripts/lib/orchestration/retro-runner.js).
|
|
634
|
-
Propagate `--full-retro` to bypass the compact-path heuristic.
|
|
635
|
-
|
|
636
|
-
Retro fires here (before the PR opens) so it stays in the operator's
|
|
637
|
-
local session with full env access (env vars, credentials, MCP). After
|
|
638
|
-
the GitHub upsert succeeds, the retro body is also **mirrored locally** to
|
|
639
|
-
`temp/epic-<epicId>/retro.md` (path resolved via
|
|
640
|
-
[`lib/config/temp-paths.js`](../../scripts/lib/config/temp-paths.js)'s
|
|
641
|
-
`epicRetroMirrorPath`). GitHub remains the source of truth — a
|
|
642
|
-
mirror-write failure only logs a warn and never fails the phase.
|
|
643
|
-
|
|
644
|
-
**Auto-file routed proposals (Story #4418).** Between gathering the retro
|
|
645
|
-
signals and composing the body, the runner files the retro's **actionable
|
|
646
|
-
routed proposals** (the `framework` / `consumer` friction categories that
|
|
647
|
-
recurred ≥ 2× or were force-flagged by an unresolved `agent::blocked`) as
|
|
648
|
-
GitHub follow-up issues via the graduator pre-parsed-findings seam
|
|
649
|
-
([`retro-proposals-graduator.js`](../../scripts/lib/feedback-loop/retro-proposals-graduator.js)).
|
|
650
|
-
Each filed issue carries `meta::<framework-gap|consumer-improvement>` +
|
|
651
|
-
`friction::<category>` labels — the join key `/plan` Phase 0's
|
|
652
|
-
prior-feedback fetcher reads back — and the rendered retro sections then
|
|
653
|
-
list the **real filed issue numbers** instead of paste-ready `gh issue
|
|
654
|
-
create` command stanzas. Filing is idempotent (a content-hash marker probe
|
|
655
|
-
skips already-filed categories) and respects the graduator per-run filing
|
|
656
|
-
cap. The beat runs behind `delivery.feedbackLoop.retroProposals` (default
|
|
657
|
-
**ON**); set it to `false` to suppress auto-filing and fall back to the
|
|
658
|
-
command stanzas. Filing never fails the phase — a filing error degrades to
|
|
659
|
-
the command stanzas.
|
|
660
|
-
|
|
661
|
-
---
|
|
662
|
-
|
|
663
|
-
## Phase 6.5 — Post-wave integration gate (Epic #4131, F1/F4)
|
|
664
|
-
|
|
665
|
-
This phase runs **after** the Phase 2 wave loop reports `epic-complete` and
|
|
666
|
-
**before** the Phase 7 finalize emit opens the PR to `main`. It is the one
|
|
667
|
-
**deliberately-global** gate — its evidence spans the whole product, not just
|
|
668
|
-
the Epic's change set — so it catches the surface each Story shipped correctly
|
|
669
|
-
in isolation yet the assembled product cannot reach (an unnavigable route, a
|
|
670
|
-
broken persona journey).
|
|
671
|
-
|
|
672
|
-
Skip when `--skip-integration-gate` (log the override; record a manual
|
|
673
|
-
intervention per
|
|
674
|
-
[`deliver-epic-reference.md` § Recording manual interventions](deliver-epic-reference.md#recording-manual-interventions)).
|
|
675
|
-
The gate is otherwise **always evaluated** but a **silent no-op when
|
|
676
|
-
unconfigured** (no `routeGlobs` / `navRegistry` / `journeySuite` in
|
|
677
|
-
`.agentrc.json`).
|
|
678
|
-
|
|
679
|
-
Sub-steps and hard-failure semantics:
|
|
680
|
-
|
|
681
|
-
- **6.5a — Whole-product navigability**: run the `navigability` lens in
|
|
682
|
-
whole-route mode over the `epic/<epicId>` tip. An **orphaned** route (no
|
|
683
|
-
nav door for any entitled persona) or a dead nav href is a hard failure
|
|
684
|
-
that **blocks finalize** and names the surface.
|
|
685
|
-
- **6.5b — Consumer journey suite**: run
|
|
686
|
-
`delivery.quality.navigability.journeySuite` over the tip. A failing
|
|
687
|
-
persona journey is a hard failure that **blocks finalize** and names the
|
|
688
|
-
broken journey.
|
|
689
|
-
- **6.5c — `@pending` ≠ green for surface-adding Epics (F4)**: for a
|
|
690
|
-
**surface-adding** Epic, an AC covered **only** by `@pending` scenarios is
|
|
691
|
-
treated as unsatisfied and **fails the close gate** instead of passing
|
|
692
|
-
green. This is **purely additive** and scoped to surface-adding Epics —
|
|
693
|
-
refactor-only and docs-only Epics are **unaffected** and the existing
|
|
694
|
-
`satisfied` / `missing` reconciliation is **not de-scoped** for any Epic.
|
|
695
|
-
|
|
696
|
-
On any hard failure, post a friction structured comment naming the surface
|
|
697
|
-
(route / nav-door identifier only — never route bodies or persona PII per
|
|
698
|
-
`security-baseline.md`), flip the Epic to `agent::blocked`, and **do not**
|
|
699
|
-
open the PR — the gate fails safe and loud. See
|
|
700
|
-
[`deliver-epic-reference.md` § Phase 6.5](deliver-epic-reference.md#phase-65--post-wave-integration-gate-epic-4131-f1f4)
|
|
701
|
-
for the full lens config, the surface-adding-signal derivation, the no-op
|
|
702
|
-
degradation contract, and the fail-safe-and-loud security note.
|
|
703
|
-
|
|
704
|
-
---
|
|
705
|
-
|
|
706
|
-
## Phase 7 — Finalize (ready the PR / open PR to main)
|
|
707
|
-
|
|
708
|
-
Before the close-tail emit, sync the Epic branch with `origin/main` so the
|
|
709
|
-
PR opens with the latest base commits already integrated (a stale base
|
|
710
|
-
stalls at branch-protection's `up-to-date branch` rule):
|
|
711
|
-
|
|
712
|
-
```bash
|
|
713
|
-
git checkout epic/<epicId>
|
|
714
|
-
node .agents/scripts/sync-branch-from-base.js \
|
|
715
|
-
--branch epic/<epicId> --base main
|
|
716
|
-
git push origin epic/<epicId>
|
|
717
|
-
```
|
|
718
|
-
|
|
719
|
-
Then fire the close-tail emit:
|
|
720
|
-
|
|
721
|
-
```bash
|
|
722
|
-
node .agents/scripts/lifecycle-emit.js --epic <epicId> --event epic.close.end
|
|
723
|
-
```
|
|
724
|
-
|
|
725
|
-
`epic.close.end` drives the bus-owned `Finalizer` chain: acceptance-table
|
|
726
|
-
reconciliation (throws and aborts finalize on a coverage gap, `waived` under
|
|
727
|
-
`acceptance::n-a`), the PR-open/ready step (below), and the
|
|
728
|
-
`epic-handoff` comment naming the PR URL. The chain emits `pr.created` →
|
|
729
|
-
`epic.finalize.end` and **stops** — it never emits `epic.merge.ready` (the
|
|
730
|
-
auto-merge arm is driven later from the Phase 8.5 gated watch path). The
|
|
731
|
-
operator shells nothing beyond the sync and the single emit.
|
|
732
|
-
|
|
733
|
-
**PR-open/ready is gated by `delivery.ci.earlyPr` (Story #4359).** Resolve
|
|
734
|
-
the flag through the [`getCiDelivery`](../../scripts/lib/config/ci.js)
|
|
735
|
-
accessor (default `true`); do not read `delivery.ci.earlyPr` directly.
|
|
736
|
-
|
|
737
|
-
- **`earlyPr` on (default)** — the Epic PR already exists as a draft (Phase
|
|
738
|
-
2 opened it at wave 1). Finalize **locates** the existing PR and flips it
|
|
739
|
-
ready-for-review via
|
|
740
|
-
[`markPrReady`](../../scripts/lib/orchestration/finalize/open-or-locate-pr.js)
|
|
741
|
-
rather than creating a PR. `gh pr ready` on an already-ready PR is a
|
|
742
|
-
no-op, so a re-run is idempotent.
|
|
743
|
-
- **`earlyPr` off** — no draft was opened at wave 1; finalize opens the PR
|
|
744
|
-
now via `openOrLocatePr` (no `draft`), exactly as the pre-Story timing.
|
|
745
|
-
|
|
746
|
-
In both modes the PR title/body contract (`feat: Epic #<epicId>` /
|
|
747
|
-
`Closes #<epicId>`) is identical.
|
|
748
|
-
|
|
749
|
-
**Drain the bookkeeping outbox (Epic #4476 M5).** When this run was invoked
|
|
750
|
-
with `--yes` (headless), the mechanical bookkeeping — non-urgent
|
|
751
|
-
structured-comment upserts and intermediate `agent::*` label flips — was
|
|
752
|
-
**buffered to a local per-Epic outbox** instead of posting live per
|
|
753
|
-
transition (see the box below). Reconcile it to GitHub once, now, after the PR
|
|
754
|
-
is open:
|
|
755
|
-
|
|
756
|
-
```bash
|
|
757
|
-
node .agents/scripts/bookkeeping-reconcile.js --epic <epicId>
|
|
758
|
-
```
|
|
759
|
-
|
|
760
|
-
This drains `temp/epic-<epicId>/bookkeeping-outbox.ndjson` (FIFO,
|
|
761
|
-
idempotent) so GitHub is the source of truth at rest. It exits non-zero and
|
|
762
|
-
**retains** the outbox if any op fails (crash-recovery: a later reconcile
|
|
763
|
-
re-drains the remainder). Attended runs buffered nothing, so this is a no-op.
|
|
764
|
-
|
|
765
|
-
> **Headless buffering, not silence (§1.H / §1.J).** In `--yes` mode the
|
|
766
|
-
> comment/label CLIs accept `--buffer --epic <epicId>` to enqueue a mutation
|
|
767
|
-
> instead of a live round-trip
|
|
768
|
-
> ([`post-structured-comment.js`](../../scripts/post-structured-comment.js),
|
|
769
|
-
> [`update-ticket-state.js`](../../scripts/update-ticket-state.js)). The
|
|
770
|
-
> `agent::blocked` HITL gate is **never** buffered — a genuine blocker flips
|
|
771
|
-
> the label live and immediately, exactly as before, so the operator can see
|
|
772
|
-
> and resume it. `agent::done` is also never buffered (its cascade runs live).
|
|
773
|
-
> Attended runs pass no `--buffer` and behave byte-for-byte as they always
|
|
774
|
-
> have.
|
|
775
|
-
|
|
776
|
-
See
|
|
777
|
-
[`deliver-epic-reference.md` § Phase 7 — Finalize](deliver-epic-reference.md#phase-7--finalize-close-tail-listener-chain)
|
|
778
|
-
for the branch-sync outcome table (conflict / fetch-failed recovery) and the
|
|
779
|
-
full three-step listener contract (why finalize must not emit
|
|
780
|
-
`epic.merge.ready`, the merge-lockout lint rule, the no planning-ticket close
|
|
781
|
-
sweep).
|
|
782
|
-
|
|
783
|
-
---
|
|
784
|
-
|
|
785
|
-
## Phase 8 — Watch-and-iterate until CI is green
|
|
786
|
-
|
|
787
|
-
The host LLM owns the green-bar loop until the operator merges. Use
|
|
788
|
-
`pr-watch-with-update.js` — the **single CI-watch mechanism** shared with
|
|
789
|
-
the standalone single-Story Step 4 path (Story #4358). It polls the PR's
|
|
790
|
-
required checks to a terminal state and additionally auto-recovers from
|
|
791
|
-
`mergeStateStatus: BEHIND` by calling `gh pr update-branch` once every
|
|
792
|
-
required check is green (branch-protection rules requiring "up to date
|
|
793
|
-
before merging" otherwise park the PR until the operator clicks **Update
|
|
794
|
-
branch** manually):
|
|
795
|
-
|
|
796
|
-
```bash
|
|
797
|
-
node <agentRoot>/scripts/pr-watch-with-update.js --pr <prNumber> --epic <epicId>
|
|
798
|
-
```
|
|
799
|
-
|
|
800
|
-
`<agentRoot>` resolves from `project.paths.agentRoot` (default `.agents`).
|
|
801
|
-
Poll cadence and caps come from `delivery.ci.watch.*`
|
|
802
|
-
(`pollIntervalMs`, `maxPolls`, `maxResumes`); pass `--poll-interval-ms`,
|
|
803
|
-
`--max-polls`, `--max-resumes`, or `--max-updates` to override for one
|
|
804
|
-
run. Passing `--epic <epicId>` scopes the red-path failure digest to
|
|
805
|
-
`temp/epic-<epicId>-ci-digest.{json,md}`.
|
|
806
|
-
|
|
807
|
-
**Three-way exit (slow-vs-failed semantics):**
|
|
808
|
-
|
|
809
|
-
- **Exit 0** — every required check is green → proceed to Phase 8.5.
|
|
810
|
-
- **Exit 1** — a required check genuinely failed (red). The CLI writes
|
|
811
|
-
`temp/epic-<epicId>-ci-digest.{json,md}` (failing check, run id,
|
|
812
|
-
`gh run view --log-failed` tail, coarse classification) and surfaces
|
|
813
|
-
the fix-loop handoff. Remediate on `epic/<epicId>` and re-run the
|
|
814
|
-
helper (auto-merge stays armed across retries). If the same failure
|
|
815
|
-
class recurs, hand the convergence off to a self-paced host loop
|
|
816
|
-
(`/loop`) that re-runs the failing check and applies the smallest fix
|
|
817
|
-
until it exits green.
|
|
818
|
-
- **Exit 2** — **still-running** (slow CI, not red): the poll cap fired
|
|
819
|
-
with checks still pending and the watcher exhausted its
|
|
820
|
-
`delivery.ci.watch.maxResumes` re-arm budget with nothing red. This is
|
|
821
|
-
**never** a failure and **never** `timed_out`. Hand the wait off to the
|
|
822
|
-
host's interval loop rather than blocking the delivery turn: `/loop 5m`
|
|
823
|
-
polling `gh pr checks` until the checks settle.
|
|
824
|
-
|
|
825
|
-
> **Triage authority.** How to classify and remediate a red (or repeatedly
|
|
826
|
-
> slow) check — the root-cause-only decision tree for infra/transient and
|
|
827
|
-
> flaky failures (reproduce → check `main` → bisect env vs code → fix in-scope
|
|
828
|
-
> or file a `meta::framework-gap` issue), the never-rerun / never-quarantine
|
|
829
|
-
> prohibitions, and the escalation criteria (three-strikes, the 30-minute
|
|
830
|
-
> wall-clock timebox, and the clearly-environmental fast path) — is defined
|
|
831
|
-
> once in [`.agents/rules/ci-remediation.md`](../../rules/ci-remediation.md).
|
|
832
|
-
> Read it before remediating.
|
|
833
|
-
>
|
|
834
|
-
> **Remediation + hard prohibitions.** For the per-check fix table (lint,
|
|
835
|
-
> baseline drift, test, coverage), the three-strikes halt rule, and the
|
|
836
|
-
> never-merge / never-force-push / never-dodge prohibitions, see
|
|
837
|
-
> [`deliver-epic-reference.md` § Phase 8 — Watch-and-iterate remediation](deliver-epic-reference.md#phase-8--watch-and-iterate-remediation).
|
|
838
|
-
|
|
839
|
-
---
|
|
840
|
-
|
|
841
|
-
## Phase 8.5 — Auto-merge gate
|
|
842
|
-
|
|
843
|
-
After Phase 8 exits 0, evaluate the auto-merge predicate by emitting
|
|
844
|
-
`epic.automerge.start`. When this `/deliver` run was invoked with `--yes`,
|
|
845
|
-
add `--headless true` so the downstream `MergeWatcher` engages the
|
|
846
|
-
must-land terminal step (Story #4427, § Arguments above); omit the flag
|
|
847
|
-
(or pass `--headless false`) for an attended run:
|
|
848
|
-
|
|
849
|
-
```bash
|
|
850
|
-
node .agents/scripts/lifecycle-emit.js --epic <epicId> \
|
|
851
|
-
--event epic.automerge.start --pr-url <prUrl> [--headless true]
|
|
852
|
-
```
|
|
853
|
-
|
|
854
|
-
**Must-land terminal step (headless only).** `MergeWatcher` polls
|
|
855
|
-
`epic.merge.armed` to confirmation as usual. If its poll budget is
|
|
856
|
-
exhausted, a headless (`--headless true`) run does not exit silently —
|
|
857
|
-
it classifies the block (`classifyMergeBlock`, the shared classifier
|
|
858
|
-
from Story #4426) and applies one bounded retry before giving up:
|
|
859
|
-
|
|
860
|
-
- `checks-pending-timeout` (required checks still progressing) — extend
|
|
861
|
-
the watch budget once and keep polling.
|
|
862
|
-
- `api-race-other` (no definitive block signal) — re-arm once by
|
|
863
|
-
re-emitting `epic.merge.ready` on the bus (never a direct `gh pr merge`
|
|
864
|
-
call — `AutomergeArmer` remains the sole authorized call site).
|
|
865
|
-
- `branch-protection-human-required`, or both bounded retries already
|
|
866
|
-
spent — terminal: emit `merge.unlanded` (`scope: "epic"`, carrying the
|
|
867
|
-
block class) and fall through to the existing single `epic.blocked`
|
|
868
|
-
emit — one blocked path, never a duplicate `agent::blocked` transition.
|
|
869
|
-
|
|
870
|
-
Attended runs (no `--headless` flag) keep today's exact behavior: budget
|
|
871
|
-
exhaustion emits `epic.blocked` immediately, with no classification, no
|
|
872
|
-
retry, and no `merge.unlanded`.
|
|
873
|
-
|
|
874
|
-
`AutomergePredicate` first runs a **live `gh pr checks --required` probe**
|
|
875
|
-
(Story #4361): green required CI is the arming signal, so if any required
|
|
876
|
-
check is red, pending, or the probe is unreadable it emits
|
|
877
|
-
`epic.merge.blocked` immediately — even if the Phase 8 watch was interrupted
|
|
878
|
-
before it observed green (closing the Story #3901 interrupted-watch hole).
|
|
879
|
-
When the probe is green it evaluates the structured-signal verdict under the
|
|
880
|
-
`delivery.ci.autoMerge` policy (default `"trust-ci"`; see
|
|
881
|
-
[`configuration.md`](../../docs/configuration.md)):
|
|
882
|
-
|
|
883
|
-
- **`trust-ci`** (default) — the ONLY structured conditions that block
|
|
884
|
-
arming are an unresolved 🔴 critical (red) code-review finding or an
|
|
885
|
-
`agent::blocked` state (a story-level blocker recorded in run-state, a
|
|
886
|
-
non-done story, or a missing run-state checkpoint). Manual interventions,
|
|
887
|
-
🟠 warning-level findings, and a non-clean retro are **recorded for audit**
|
|
888
|
-
(surfaced on the classification log and the arm-reason) but no longer block.
|
|
889
|
-
- **`strict`** — restores the prior clean-sprint predicate exactly: empty
|
|
890
|
-
manual-interventions, every story done, no story blocked, `0` 🔴 + `0` 🟠
|
|
891
|
-
review findings, and the retro's `automerge-verdict` trailer reporting
|
|
892
|
-
`cleanSprint: true`. Any dirty signal blocks.
|
|
893
|
-
|
|
894
|
-
On an arming decision the predicate emits `epic.merge.ready`; the downstream
|
|
895
|
-
`AutomergeArmer` (the sole authorized `gh pr merge` call site) fires
|
|
896
|
-
`gh pr merge --auto --squash --delete-branch`. Otherwise the predicate emits
|
|
897
|
-
`epic.merge.blocked` with the disqualifying reasons and exits without merging
|
|
898
|
-
— the operator merges manually.
|
|
899
|
-
|
|
900
|
-
**Blocked-path output (operator merges the button).** When arming is
|
|
901
|
-
declined, `epic.merge.armed` never fires inside this run, so Phase 9 does not
|
|
902
|
-
reap automatically. Surface the exact one-liner the operator runs **after**
|
|
903
|
-
they merge the PR by hand so local refs are reaped and `main` is
|
|
904
|
-
fast-forwarded (the idempotent-resume path below runs this automatically on
|
|
905
|
-
the next `/deliver <epicId>`):
|
|
906
|
-
|
|
907
|
-
```bash
|
|
908
|
-
node .agents/scripts/lifecycle-emit.js --epic <epicId> \
|
|
909
|
-
--event epic.merge.armed --pr-url <prUrl>
|
|
910
|
-
```
|
|
911
|
-
|
|
912
|
-
Close the phase wrapper by emitting `epic.automerge.end` (records the arm
|
|
913
|
-
outcome on the ledger; `merged: true` once GitHub completes the squash,
|
|
914
|
-
`merged: false` with a reason otherwise):
|
|
915
|
-
|
|
916
|
-
```bash
|
|
917
|
-
node .agents/scripts/lifecycle-emit.js --epic <epicId> \
|
|
918
|
-
--event epic.automerge.end --pr-url <prUrl> --merged <true|false>
|
|
919
|
-
```
|
|
920
|
-
|
|
921
|
-
> **Predicate wiring + manual-intervention recording.** For the full
|
|
922
|
-
> predicate contract (the trailer read, the `delivery.ci.autoMerge` policy
|
|
923
|
-
> split, and the Story #4361 live `gh pr checks --required` probe that
|
|
924
|
-
> replaced the former CI-freshness skip) and the
|
|
925
|
-
> `epic-deliver-note-intervention.js` command + its trigger list, see
|
|
926
|
-
> [`deliver-epic-reference.md` § Phase 8.5 — Auto-merge predicate detail](deliver-epic-reference.md#phase-85--auto-merge-predicate-detail).
|
|
927
|
-
|
|
928
|
-
---
|
|
929
|
-
|
|
930
|
-
## Phase 9 — Local branch cleanup
|
|
931
|
-
|
|
932
|
-
Phase 9 runs **automatically** inside the lifecycle bus once auto-merge
|
|
933
|
-
arms: the `BranchCleaner` listener subscribes to `epic.cleanup.start`
|
|
934
|
-
and reaps local refs (the `epic/<id>` branch, every `story-<id>` in the
|
|
935
|
-
checkpoint, attached worktrees, and stale tracking refs) before `Cleaner`
|
|
936
|
-
archives the `temp/epic-<id>/` tree. No operator step is required on the
|
|
937
|
-
auto-merge path.
|
|
938
|
-
|
|
939
|
-
For out-of-band cleanup re-entry (resume after a crash, or operator
|
|
940
|
-
override), fire `epic.merge.armed`:
|
|
941
|
-
|
|
942
|
-
```bash
|
|
943
|
-
node .agents/scripts/lifecycle-emit.js --epic <epicId> \
|
|
944
|
-
--event epic.merge.armed --pr-url <prUrl>
|
|
945
|
-
```
|
|
946
|
-
|
|
947
|
-
> **Reap order + operator-merges fallback.** For the full in-process reap
|
|
948
|
-
> order, the per-branch classification log, and the manual reap sequence
|
|
949
|
-
> when Phase 8.5 fell back to the operator-merges-button path (auto-merge
|
|
950
|
-
> declined, `epic.merge.armed` never fired), see
|
|
951
|
-
> [`deliver-epic-reference.md` § Phase 9 — Local branch cleanup detail](deliver-epic-reference.md#phase-9--local-branch-cleanup-detail).
|
|
952
|
-
|
|
953
|
-
---
|
|
954
|
-
|
|
955
|
-
## Idempotence and resume
|
|
956
|
-
|
|
957
|
-
Re-runs pick up at the next undispatched wave (in-flight Stories finish
|
|
958
|
-
via `helpers/epic-deliver-story`'s own checkpointing). The PR from Phase 7 is
|
|
959
|
-
updated in place on subsequent runs. The authoritative live view is
|
|
960
|
-
the `epic-run-progress` structured comment.
|
|
961
|
-
|
|
962
|
-
**Resume auto-arm for a merged-but-uncleaned Epic.** When `/deliver` resumes
|
|
963
|
-
against an Epic whose PR already merged (operator merged the button in a prior
|
|
964
|
-
session) but whose local `epic/<id>` / `story-<id>` refs still linger, the
|
|
965
|
-
resume path detects the merged-but-uncleaned state
|
|
966
|
-
(`detectMergedUncleanedEpic` in
|
|
967
|
-
[`epic-cleanup.js`](../../scripts/lib/orchestration/epic-cleanup.js)) and fires
|
|
968
|
-
`epic.merge.armed` automatically so Phase 9 reaps — no manual command. The
|
|
969
|
-
detection is idempotent: an already-reaped Epic (no local refs) is a clean
|
|
970
|
-
no-op, and an unmerged Epic never arms. It resolves the merged PR's URL for the
|
|
971
|
-
required `epic.merge.armed` payload and fails closed (does **not** arm) on any
|
|
972
|
-
indeterminate `gh` probe. The one-liner under Phase 8.5 / Phase 9 is the manual
|
|
973
|
-
equivalent for the case where the operator does not re-run `/deliver`.
|
|
974
|
-
|
|
975
|
-
---
|
|
976
|
-
|
|
977
|
-
## Constraints
|
|
978
|
-
|
|
979
|
-
- **Never** merge `epic/<epicId>` to `main` outside Phase 8.5.
|
|
980
|
-
- **Never** dispatch more than the global `concurrencyCap` allows;
|
|
981
|
-
concurrency lives inside the ready-set fan-out.
|
|
982
|
-
- **Never** flip Story-level labels from this skill; **never** invoke
|
|
983
|
-
`helpers/epic-deliver-story` yourself (children run it via Agent fan-out,
|
|
984
|
-
even for single-Story waves); **never** spawn a subprocess for dispatch.
|
|
985
|
-
- **Always** checkpoint via `epic-deliver-prepare.js` /
|
|
986
|
-
`epic-execute-record-wave.js`; never write run state elsewhere.
|
|
987
|
-
- **Always** post a friction structured comment before a non-`complete`
|
|
988
|
-
outcome.
|
|
989
|
-
- **Always** auto-invoke the epic-audit, code-review, and retro helpers
|
|
990
|
-
(Phases 4–6) when their artefacts aren't already present.
|
|
991
|
-
- **Always** run the Phase 6.5 integration gate after the wave loop
|
|
992
|
-
reports `epic-complete` and before Phase 7 finalize (unless
|
|
993
|
-
`--skip-integration-gate`); **never** open the PR while the gate
|
|
994
|
-
reports a hard failure (orphaned surface, dead nav href, broken
|
|
995
|
-
journey, or a surface-adding Epic with only `@pending` AC coverage).
|
|
996
|
-
- **Always** drive Phase 8 to green CI before returning control — the
|
|
997
|
-
host LLM owns the loop until the PR is mergeable or the Epic is
|
|
998
|
-
parked at `agent::blocked`.
|