mandrel 1.93.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/acceptance-critic.md +129 -0
- package/.agents/agents/story-worker.md +161 -0
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +108 -136
- package/.agents/docs/execution-reference.md +44 -22
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +107 -108
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +224 -0
- package/.agents/rules/git-conventions.md +42 -223
- package/.agents/rules/security-baseline.md +5 -0
- package/.agents/rules/testing-standards.md +106 -13
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +71 -201
- 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/acceptance-eval.js +62 -18
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- 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-context-budget.js +62 -5
- 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/diagnose-friction.js +0 -6
- 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/bootstrap/project-bootstrap.js +56 -18
- 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 +160 -22
- package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +103 -0
- package/.agents/scripts/lib/config/explain.js +57 -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 +3 -8
- package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -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 +39 -4
- 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 +112 -3
- package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
- 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-critic-conditions.js +3 -2
- 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 +14 -14
- 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/close-validation.js +1 -0
- 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 +3 -8
- 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/provider-factory.js +1 -1
- 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/post-structured-comment.js +38 -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/sync-claude-agents.js +165 -0
- package/.agents/scripts/update-ticket-state.js +37 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
- package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
- package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +8 -418
- 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 -131
- 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 +52 -40
- 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/mandrel-sync-config.md +1 -1
- 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 +3 -2
- 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 +46 -0
- package/lib/cli/registry.js +95 -0
- package/lib/migrations/index.js +6 -5
- package/package.json +5 -3
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer-mobile.md +0 -120
- package/.agents/personas/engineer-web.md +0 -111
- package/.agents/personas/engineer.md +0 -119
- package/.agents/personas/product.md +0 -94
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/refactorer.md +0 -113
- package/.agents/personas/security-engineer.md +0 -112
- package/.agents/personas/sre.md +0 -86
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/personas/ux-designer.md +0 -95
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- 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 -590
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-decompose.js +0 -54
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-plan-spec.js +0 -64
- 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 -660
- 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 -388
- 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-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
- 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 -278
- 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/skill-capsule-loader.js +0 -109
- 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 -301
- 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/plan-critics.js +0 -199
- package/.agents/scripts/retro-run.js +0 -218
- 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 -335
- package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
- package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
- package/.agents/skills/core/code-simplification/SKILL.md +0 -389
- package/.agents/skills/core/context-engineering/SKILL.md +0 -309
- package/.agents/skills/core/context-engineering/examples.md +0 -58
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
- 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 -413
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
- package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
- package/.agents/skills/core/idea-refinement/examples.md +0 -437
- package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
- package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
- package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
- package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
- package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
- package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
- package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
- package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
- package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
- package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
- package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
- package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
- package/.agents/workflows/helpers/deliver-epic.md +0 -955
- package/.agents/workflows/helpers/deliver-stories.md +0 -440
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
- 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 -351
- 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
package/.agents/docs/SDLC.md
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
# Software Development Life Cycle (SDLC) Workflow
|
|
2
2
|
|
|
3
|
-
Mandrel uses **
|
|
4
|
-
Labels, and Projects V2 are the Single Source of Truth
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
Mandrel uses **Story-centric GitHub orchestration** — GitHub Issues,
|
|
4
|
+
Labels, and Projects V2 are the Single Source of Truth. Plans persist as
|
|
5
|
+
`type::story` tickets (optionally grouped by a `plan-run::<id>` label);
|
|
6
|
+
each Story is delivered on its own `story-<id>` branch and reaches `main`
|
|
7
|
+
through its own PR.
|
|
8
|
+
|
|
9
|
+
An Epic may still exist as an **optional untyped human umbrella issue**
|
|
10
|
+
(no `type::epic` label and no shipped Epic issue form — only
|
|
11
|
+
`.github/ISSUE_TEMPLATE/story.yml`), but **delivery and planning
|
|
12
|
+
orchestration are Story-only**: there is no Epic wave loop, no
|
|
13
|
+
`epic/<id>` integration branch, no `epic.yaml` reconciler, and any ticket
|
|
14
|
+
that still carries an `Epic: #N` footer is **refused** by `/deliver`
|
|
15
|
+
(close it or re-plan it as a v2 Story).
|
|
8
16
|
|
|
9
17
|
The framework is **Claude Code-first**: `.claude/`, hooks, skills, and
|
|
10
18
|
the slash-command surface lean in on Claude Code as the reference
|
|
@@ -18,185 +26,139 @@ ADR 20260512-coupling-stance in [`../docs/decisions.md`](../../docs/decisions.md
|
|
|
18
26
|
|
|
19
27
|
From zero to shipped:
|
|
20
28
|
|
|
21
|
-
1. **Plan the work.** Run `/plan` in your agentic
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
1. **Interrogate** —
|
|
40
|
-
envelope
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
risk-routed lenses, restricted to the **cumulative + global +
|
|
87
|
-
risk-routed** tiers via `selectEpicCloseLenses` (every local-tier
|
|
88
|
-
change-set lens is excluded — already verified shift-left). The roster is
|
|
89
|
-
handed to Phase 5.
|
|
90
|
-
5. **Phase 5 — code-review** — auto-invokes the in-process
|
|
91
|
-
`lib/orchestration/code-review.js`; walks the cumulative Epic diff
|
|
92
|
-
**once**, executing the Phase 4 lens roster as review dimensions
|
|
93
|
-
**alongside** the review pillars (Story #4412 folded the standalone lens
|
|
94
|
-
walk into this pass). Findings persist as the single unified
|
|
95
|
-
`verification-results` structured comment on the Epic. Critical findings
|
|
96
|
-
halt the run.
|
|
97
|
-
6. **Phase 6 — retro** — auto-invokes the in-process
|
|
98
|
-
`lib/orchestration/retro-runner.js` (extracted from the old
|
|
99
|
-
retro helper) and posts the structured retro comment on the
|
|
100
|
-
Epic. The retro fires **before** the PR is opened so it has
|
|
101
|
-
full env access in the operator's local session.
|
|
102
|
-
7. **Phase 7 — finalize** — pushes `epic/<epicId>` to `origin`,
|
|
103
|
-
opens a pull request to `main`, sets the required-checks
|
|
104
|
-
expectation from `github.branchProtection.requiredChecks`, and
|
|
105
|
-
posts the hand-off comment naming the PR URL. The Epic stays
|
|
106
|
-
at `agent::executing` until the PR merges; the standard
|
|
107
|
-
label-transition pathway flips it to `agent::done` on merge.
|
|
108
|
-
Finalize hands off to the watch / auto-merge / cleanup tail
|
|
109
|
-
below — it does **not** stop the run.
|
|
110
|
-
8. **Phase 8 — watch-and-iterate** — watches CI on the open PR
|
|
111
|
-
until checks turn green (or a failure surfaces for human
|
|
112
|
-
remediation).
|
|
113
|
-
9. **Phase 8.5 — auto-merge** — arms GitHub native auto-merge
|
|
114
|
-
(`gh pr merge --auto --squash --delete-branch`) once the
|
|
115
|
-
required checks have passed so the PR lands without a second
|
|
116
|
-
operator visit. The operator can disarm auto-merge in the
|
|
117
|
-
GitHub UI if they want to gate the merge manually.
|
|
118
|
-
10. **Phase 9 — cleanup** — reaps local Story/Epic branch refs and
|
|
119
|
-
worktrees after the PR merges so the workspace returns to a
|
|
120
|
-
clean state for the next Epic.
|
|
121
|
-
|
|
122
|
-
For a single Epic-attached Story (re-driving a hotfix, resuming after
|
|
123
|
-
a halt), re-run `/deliver <epicId>` — the wave loop picks up
|
|
124
|
-
incomplete Stories from the dispatch manifest automatically. Standalone
|
|
125
|
-
Stories (no `Epic: #N` reference) use `/deliver <storyId>` instead.
|
|
126
|
-
Mixed input — several Epics, or Epics plus standalone Stories — is
|
|
127
|
-
accepted in one invocation: `/deliver` composes a **sequential segment
|
|
128
|
-
plan** (the standalone-Story set as one segment, delivered first, then
|
|
129
|
-
each Epic as its own segment in input order) and executes the segments
|
|
130
|
-
one at a time through the same two path helpers, never interleaved.
|
|
29
|
+
1. **Plan the work.** Run [`/plan`](../workflows/plan.md) in your agentic
|
|
30
|
+
IDE. The framework authors **one Story by default** (folded Tech Spec
|
|
31
|
+
in `## Spec`), with N>1 only under the default-single split policy.
|
|
32
|
+
|
|
33
|
+
Three operator modes (the **only** accepted entries):
|
|
34
|
+
- `/plan --seed "<text>"` — ideate from chat text.
|
|
35
|
+
- `/plan --seed-file <path>` — author from on-disk notes / a plan seed
|
|
36
|
+
(this is the [`/audit-to-stories`](../workflows/audit-to-stories.md)
|
|
37
|
+
handoff seam via `--emit-plan-seed`).
|
|
38
|
+
- `/plan --tickets 123[,456…]` — analyze existing issue(s) into proper
|
|
39
|
+
Stories (prefer an N=1 rewrite).
|
|
40
|
+
|
|
41
|
+
`/plan` is a **single path** — there is no Epic/Story router, no
|
|
42
|
+
scope-triage `epic|story` verdict, and no `deliveryShape`. All GitHub
|
|
43
|
+
reads happen in `plan-context.js`, all writes in `plan-persist.js`, and
|
|
44
|
+
two HITL gates bracket the authoring middle. Duplicate search targets
|
|
45
|
+
open **Stories** (`type::story`), never Epics.
|
|
46
|
+
|
|
47
|
+
1. **Interrogate** — `plan-context.js` emits the single authoring
|
|
48
|
+
envelope (open-Story duplicate candidates, codebase snapshot, BDD
|
|
49
|
+
probe, risk heuristics, `systemPrompts.story`). Duplicate review
|
|
50
|
+
folds into **gate #1**.
|
|
51
|
+
2. **Author** — write `stories.json` (**one Story by default**) with a
|
|
52
|
+
folded Tech Spec in `## Spec` / `## Slicing`, plus
|
|
53
|
+
`risk-verdict.json` (axes + summary only — no `deliveryShape`).
|
|
54
|
+
Binding criteria live in top-level `acceptance[]` / `verify[]`;
|
|
55
|
+
changes/references are `{ path, assumption }` objects. Split into
|
|
56
|
+
N>1 only under the default-single split policy.
|
|
57
|
+
3. **Persist** — **gate #2** (risk-routed; typically skipped for N=1
|
|
58
|
+
low-risk) then `plan-persist.js` runs every deterministic gate and
|
|
59
|
+
creates Story issue(s) with `type::story` + `agent::ready` (plus a
|
|
60
|
+
shared `plan-run::<id>` label when N>1).
|
|
61
|
+
|
|
62
|
+
2. **Deliver the Story.** Run [`/deliver <storyId>`](../workflows/deliver.md)
|
|
63
|
+
(or `/deliver <a> <b> …`, or `/deliver --run <planRunId>` for a
|
|
64
|
+
multi-Story plan-run) in your IDE. `/deliver` owns input resolution and
|
|
65
|
+
`depends_on` sequencing only — every Story runs through
|
|
66
|
+
[`helpers/deliver-story`](../workflows/helpers/deliver-story.md), the
|
|
67
|
+
single v2 delivery engine. Per-Story it:
|
|
68
|
+
|
|
69
|
+
1. **Init** (`single-story-init.js`) — acquires the Story lease, cuts
|
|
70
|
+
`story-<id>` from `main`, materializes a worktree, flips to
|
|
71
|
+
`agent::executing`.
|
|
72
|
+
2. **Implement** — the agent delivers the Story in one guarded session
|
|
73
|
+
against its inline `acceptance[]` / `verify[]` contract (optional
|
|
74
|
+
`## Slicing` intra-session checkpoints).
|
|
75
|
+
3. **Acceptance self-eval** — a bounded, risk-routed critic loop scores
|
|
76
|
+
the working diff against each acceptance item before close (see
|
|
77
|
+
[`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)).
|
|
78
|
+
4. **Ceremony** — risk-routed acceptance critics, review depth, and
|
|
79
|
+
audit lenses (`ceremony-routing.js`).
|
|
80
|
+
5. **Close** (`single-story-close.js`) — runs close-validation gates,
|
|
81
|
+
the maker-blind Story-scope code review, pushes `story-<id>`, opens
|
|
82
|
+
a PR to `main`, and (under the default `delivery.ci.autoMerge:
|
|
83
|
+
"trust-ci"`) arms GitHub native auto-merge. The Story flips to
|
|
84
|
+
`agent::closing` (issue stays OPEN).
|
|
85
|
+
6. **CI watch + fix** — watches required checks to green, fixing and
|
|
86
|
+
re-pushing on red.
|
|
87
|
+
7. **Confirm merge** (`single-story-confirm-merge.js`) — on a confirmed
|
|
88
|
+
`MERGED` PR the Story flips to `agent::done`; local branch cleanup
|
|
89
|
+
and Projects-v2 Status re-assert run out-of-band.
|
|
90
|
+
|
|
91
|
+
For a multi-Story plan-run, `/deliver` sequences ready Stories by
|
|
92
|
+
`depends_on` and runs the per-run epilogue (audit roster · follow-up
|
|
93
|
+
roll-up · sibling coherence) once after the last Story lands.
|
|
131
94
|
|
|
132
95
|
That is the whole happy path. Everything below is **detail** — branching
|
|
133
|
-
conventions, HITL escalation, audit
|
|
134
|
-
default flow requires adjustment.
|
|
96
|
+
conventions, HITL escalation, audit lenses — that you only need when the
|
|
97
|
+
default flow requires adjustment. It intentionally **links** to
|
|
98
|
+
[`plan.md`](../workflows/plan.md) and [`deliver.md`](../workflows/deliver.md)
|
|
99
|
+
rather than re-documenting the ceremony they own.
|
|
135
100
|
|
|
136
101
|
---
|
|
137
102
|
|
|
138
103
|
## Core Principles
|
|
139
104
|
|
|
140
|
-
- **Layered state stores with explicit precedence.**
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
- **Story-
|
|
152
|
-
`story-<id>` branch.
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
- **
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
`
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
protection on `main` enforces required-checks before the merge button
|
|
171
|
-
(auto or manual) fires. The operator can disarm auto-merge in the
|
|
172
|
-
GitHub UI to make the merge an explicit human action.
|
|
173
|
-
- **HITL-minimal by default.** Exactly one mandatory operator touchpoint on
|
|
174
|
-
the happy path — blocker resolution mid-run. PR merge is autonomous via
|
|
175
|
-
the armed auto-merge; the operator becomes a second touchpoint only when
|
|
176
|
-
they disarm auto-merge or when required checks fail and need
|
|
177
|
-
remediation.
|
|
105
|
+
- **Layered state stores with explicit precedence.** Ticket status lives
|
|
106
|
+
in GitHub Issues and Labels; the lifecycle bus
|
|
107
|
+
(`temp/run-<id>/lifecycle.ndjson`) is the canonical resume target for
|
|
108
|
+
in-flight runs; structured comments (`story-run-progress`,
|
|
109
|
+
`verification-results`, retro) are the operator-visible rollup. The
|
|
110
|
+
stores, their owners, and their conflict-resolution rules are listed in
|
|
111
|
+
[§ State stores](#state-stores) — that matrix is the single source of
|
|
112
|
+
truth for "who owns which write."
|
|
113
|
+
- **Provider Abstraction.** Orchestration flows through
|
|
114
|
+
`ITicketingProvider`, an abstract interface with a shipped GitHub
|
|
115
|
+
implementation.
|
|
116
|
+
- **Story-level branching.** All work for a Story lands on the shared
|
|
117
|
+
`story-<id>` branch. Each Story reaches `main` through its own PR
|
|
118
|
+
(squash + required checks); there is **no** `epic/<id>` integration
|
|
119
|
+
branch and **no** `--no-ff` wave merge.
|
|
120
|
+
- **One delivery engine.** `/deliver` resolves and sequences a Story set;
|
|
121
|
+
`helpers/deliver-story` executes each Story identically (trivial or
|
|
122
|
+
large). Story sub-agents run inside the operator's Claude session via
|
|
123
|
+
the Agent tool — worktree filesystem isolation is preserved; only the
|
|
124
|
+
subprocess boundary is gone.
|
|
125
|
+
- **PR is the sole promotion gate.** Delivery ends with a PR open against
|
|
126
|
+
`main` and (by default) GitHub native auto-merge armed; the workflow
|
|
127
|
+
itself never executes `git merge` against `main`. Branch protection on
|
|
128
|
+
`main` enforces required checks before the merge button (auto or
|
|
129
|
+
manual) fires.
|
|
130
|
+
- **HITL-minimal by default.** Exactly one mandatory operator touchpoint
|
|
131
|
+
on the happy path — blocker resolution mid-run. PR merge is autonomous
|
|
132
|
+
via the armed auto-merge; the operator becomes a second touchpoint only
|
|
133
|
+
when they disarm auto-merge (`--no-auto-merge` / `delivery.ci.autoMerge:
|
|
134
|
+
"strict"`) or when required checks fail and need remediation.
|
|
178
135
|
|
|
179
136
|
---
|
|
180
137
|
|
|
181
138
|
## State stores
|
|
182
139
|
|
|
183
|
-
Mandrel writes orchestration state across
|
|
184
|
-
store has
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
|
193
|
-
|
|
|
194
|
-
| `
|
|
195
|
-
| `story-run-progress` comment
|
|
196
|
-
|
|
|
197
|
-
|
|
|
198
|
-
|
|
|
199
|
-
|
|
|
140
|
+
Mandrel writes orchestration state across several distinct stores. Each
|
|
141
|
+
store has one canonical writer and one well-defined idempotency key;
|
|
142
|
+
conflicts are resolved in the **Conflict resolution** column. Run-scoped
|
|
143
|
+
artifacts live under `temp/run-<id>/` (standalone Stories under
|
|
144
|
+
`temp/standalone/stories/story-<id>/`); the `run-<id>` directory naming is
|
|
145
|
+
historical (it predates the Story-centric cutover) but remains the live
|
|
146
|
+
on-disk layout resolved by
|
|
147
|
+
[`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js).
|
|
148
|
+
|
|
149
|
+
| State Store | Owner (canonical writer) | Mutation API | Idempotency key | Conflict resolution |
|
|
150
|
+
| --- | --- | --- | --- | --- |
|
|
151
|
+
| GitHub labels | `transitionTicketState` via `ticketing.js` | `gh issue edit --add-label / --remove-label`, wrapped in `update-ticket-state.js` | `(ticketId, label-set)` — set-equality before write | Authoritative for current ticket lifecycle state; if a label disagrees with the lifecycle ledger, the **ledger wins on resume** and the label is re-derived. |
|
|
152
|
+
| `story-run-progress` comment | `story-phase.js` (per Story, per phase transition) | `post-structured-comment.js` (upsert by `kind`) | `(storyId, kind='story-run-progress')` | Authoritative for Story-level phase progress. |
|
|
153
|
+
| `verification-results` comment | `lib/orchestration/code-review.js` | `post-structured-comment.js` (upsert by `kind`) | `(storyId, kind='verification-results')` | Authoritative for the Story-scope review + lens findings; critical findings block close. |
|
|
154
|
+
| Lifecycle ledger NDJSON | `lifecycle-emit.js` (single append-only writer per run) | Append-only line write to `temp/run-<id>/lifecycle.ndjson` | `(runId, eventId)` — `eventId` is a content hash of `{type, ts, payload}` | **Canonical resume target.** When labels / comments disagree with the ledger, the ledger wins and the others are re-derived. |
|
|
155
|
+
| Validation evidence cache | `evidence-gate.js` | JSON cache file under the run temp tree, keyed by HEAD SHA | `(gate, git rev-parse HEAD)` | Pure cache: a missing entry triggers a re-run; presence is a fast-path skip. Cache eviction is safe. |
|
|
156
|
+
| PR / auto-merge state | `single-story-close.js` (sole authorized caller of `gh pr merge`) | `gh pr merge --auto --squash --delete-branch`; PR open via the close pipeline's `gh pr create` | `(prNumber, head-branch SHA)` — `gh pr list --head` probes before create | GitHub is authoritative for PR + auto-merge arming state; the ledger records the *intent* to arm, GitHub records the outcome. |
|
|
157
|
+
| Worktree cleanup state | `WorktreeManager.reap` (via `single-story-close.js` / `git-cleanup.js`) | `git worktree remove` + on-disk pending-cleanup JSON under the run temp tree | `(storyId, worktree-path)` | Filesystem is authoritative for "is the worktree gone?"; the pending-cleanup JSON only tracks stale-registry entries needing a follow-up sweep. |
|
|
158
|
+
|
|
159
|
+
> The `gh pr merge` merge-lockout lint rule keeps the merge command
|
|
160
|
+
> confined to the sanctioned close path; no other production caller may
|
|
161
|
+
> shell it.
|
|
200
162
|
|
|
201
163
|
---
|
|
202
164
|
|
|
@@ -210,48 +172,42 @@ graph LR
|
|
|
210
172
|
|
|
211
173
|
subgraph Phase0 ["Phase 0: Bootstrap"]
|
|
212
174
|
direction TB
|
|
213
|
-
Z["👤 npx mandrel init<br/>(install → sync →
|
|
175
|
+
Z["👤 npx mandrel init<br/>(install → sync → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
|
|
214
176
|
end
|
|
215
177
|
|
|
216
|
-
subgraph Phase1 ["Phase 1:
|
|
178
|
+
subgraph Phase1 ["Phase 1: Plan"]
|
|
217
179
|
direction TB
|
|
218
|
-
A["👤 /plan
|
|
180
|
+
A["👤 /plan --seed | --seed-file | --tickets"]:::manual
|
|
181
|
+
B["🤖 interrogate → author → persist"]:::agentic
|
|
182
|
+
A --> B
|
|
183
|
+
B -.-> B_Art["📄 type::story issue(s)<br/>(+ optional plan-run::<id>)"]:::artifact
|
|
219
184
|
end
|
|
220
185
|
|
|
221
|
-
subgraph Phase2 ["Phase 2:
|
|
186
|
+
subgraph Phase2 ["Phase 2: Deliver"]
|
|
222
187
|
direction TB
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
D -.-> D_Art["📄 GitHub Issue Hierarchy"]:::artifact
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
subgraph Phase3 ["Phase 3: Delivery"]
|
|
230
|
-
direction TB
|
|
231
|
-
E["👤 /deliver <epicId>"]:::manual
|
|
232
|
-
F["🤖 wave loop (one /deliver sub-agent per Story per wave)"]:::agentic
|
|
233
|
-
G["🤖 close-validation → code-review → retro → open PR"]:::agentic
|
|
188
|
+
E["👤 /deliver <storyId><br/>(or --run <planRunId>)"]:::manual
|
|
189
|
+
F["🤖 deliver-story: story-<id> from main<br/>implement → self-eval → ceremony → close"]:::agentic
|
|
190
|
+
G["🤖 close-validation → code-review → open PR"]:::agentic
|
|
234
191
|
E --> F --> G
|
|
235
192
|
G -.-> G_Art["📄 PR open against main"]:::artifact
|
|
236
193
|
end
|
|
237
194
|
|
|
238
|
-
subgraph
|
|
195
|
+
subgraph Phase3 ["Phase 3: PR merge (auto by default)"]
|
|
239
196
|
direction TB
|
|
240
197
|
H["🤖 Auto-merge armed → PR lands when checks pass<br/>(👤 operator may disarm to merge manually)"]:::agentic
|
|
241
198
|
end
|
|
242
199
|
|
|
243
200
|
Z --> A
|
|
244
|
-
|
|
245
|
-
D --> E
|
|
201
|
+
B --> E
|
|
246
202
|
G --> H
|
|
247
203
|
```
|
|
248
204
|
|
|
249
205
|
---
|
|
250
206
|
|
|
251
|
-
## Phase 0: Bootstrap (
|
|
207
|
+
## Phase 0: Bootstrap (one-time setup)
|
|
252
208
|
|
|
253
|
-
Before any
|
|
254
|
-
|
|
209
|
+
Before any workflow, bootstrap your project to seed `.agentrc.json`, wire
|
|
210
|
+
the framework system prompt, and create the GitHub labels, Projects V2
|
|
255
211
|
fields, and (when enabled) main-branch protection the orchestration engine
|
|
256
212
|
depends on.
|
|
257
213
|
|
|
@@ -261,916 +217,258 @@ The canonical cold-start path is a single command:
|
|
|
261
217
|
npx mandrel init
|
|
262
218
|
```
|
|
263
219
|
|
|
264
|
-
`mandrel init` installs `mandrel` (when `./.agents/` is absent),
|
|
265
|
-
`./.agents/` via `mandrel sync`, then presents a two-option
|
|
266
|
-
now** (option 1 → runs `node
|
|
267
|
-
you pass) or **just
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
files-only so
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
220
|
+
`mandrel init` installs `mandrel` (when `./.agents/` is absent),
|
|
221
|
+
materializes `./.agents/` via `mandrel sync`, then presents a two-option
|
|
222
|
+
prompt: **configure now** (option 1 → runs `node
|
|
223
|
+
.agents/scripts/bootstrap.js`, forwarding any flags you pass) or **just
|
|
224
|
+
the files** (option 2 → re-run `mandrel init` any time to configure
|
|
225
|
+
later). `--assume-yes` skips the prompt and proceeds straight to configure;
|
|
226
|
+
a non-TTY run without it defaults to files-only so GitHub provisioning
|
|
227
|
+
never runs unattended. `bootstrap.js`:
|
|
228
|
+
|
|
229
|
+
1. **Provisions a cold start.** Initializes the local git repo (with a
|
|
230
|
+
first commit) when absent, creates the GitHub repo (`gh repo create
|
|
231
|
+
--source=. --push`; choose visibility with `--visibility
|
|
232
|
+
private|public|internal`, default `private`), and creates the Projects
|
|
233
|
+
V2 board (`gh project create`) when it doesn't exist. No pre-created
|
|
234
|
+
repo or remote is required.
|
|
235
|
+
2. **Seeds `.agentrc.json`** from `.agents/starter-agentrc.json` (the
|
|
236
|
+
`github` section carries owner, repo, base branch, operator handle, and
|
|
237
|
+
project number — inferred from your local `git` config where possible).
|
|
238
|
+
See `.agents/docs/agentrc-reference.json` for the exhaustive key
|
|
239
|
+
reference.
|
|
282
240
|
3. **Creates the label taxonomy and Projects V2 fields**, and — when
|
|
283
|
-
`github.branchProtection.enforce` is `true` (default) — creates or
|
|
284
|
-
branch protection on `main` with the project's
|
|
285
|
-
`github.branchProtection.requiredChecks` as required status checks.
|
|
286
|
-
step is load-bearing
|
|
241
|
+
`github.branchProtection.enforce` is `true` (default) — creates or
|
|
242
|
+
merges branch protection on `main` with the project's
|
|
243
|
+
`github.branchProtection.requiredChecks` as required status checks.
|
|
244
|
+
This step is load-bearing because PR merges to `main` are the sole
|
|
287
245
|
promotion gate.
|
|
288
246
|
|
|
289
247
|
When `.agents/` is already materialized you can run the bootstrap directly
|
|
290
248
|
(`node .agents/scripts/bootstrap.js`). The guided first-run steps (stack
|
|
291
249
|
detection, docs scaffolding, `mandrel doctor` readiness gate, and `/plan`
|
|
292
|
-
handoff) are
|
|
293
|
-
`mandrel init` again to pick them up if you bootstrapped via the script
|
|
294
|
-
directly.
|
|
250
|
+
handoff) are part of `mandrel init`'s configure path.
|
|
295
251
|
|
|
296
|
-
> [!NOTE] Bootstrap runs once per repository. It is safe to re-run —
|
|
297
|
-
> labels, fields, and branch-protection entries are preserved;
|
|
298
|
-
> are added.
|
|
252
|
+
> [!NOTE] Bootstrap runs once per repository. It is safe to re-run —
|
|
253
|
+
> existing labels, fields, and branch-protection entries are preserved;
|
|
254
|
+
> missing ones are added.
|
|
299
255
|
|
|
300
256
|
---
|
|
301
257
|
|
|
302
|
-
## Phase 1:
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
[`core/scope-triage`](../skills/core/scope-triage/SKILL.md) rubric over the
|
|
338
|
-
sharpened one-pager so a story-sized scope is not pushed through the full Epic
|
|
339
|
-
ceremony (Tech Spec + Acceptance Spec + Story backlog +
|
|
340
|
-
`epic/<id>` integration branch) only to land as a degenerate one-Story
|
|
341
|
-
output. The rubric anchors its sizing judgment **by reference** to
|
|
342
|
-
the existing sizing SSOT (`DELIVERABLE_GRANULARITY_GUIDANCE` /
|
|
343
|
-
`DEFAULT_TASK_SIZING` in `ticket-validator-sizing.js`) and emits one of three
|
|
344
|
-
verdicts — `epic` | `story` | `borderline`.
|
|
345
|
-
|
|
346
|
-
The verdict is **host-LLM judgment** (no scorer, no schema, no label
|
|
347
|
-
transition) and **advisory** — the operator always decides. It folds into the
|
|
348
|
-
existing gate #1 confirmation rather than adding a second stop: an `epic`
|
|
349
|
-
verdict proceeds with a plain confirm, while a `story` / `borderline` verdict
|
|
350
|
-
offers a three-way choice (single Story / plan as Epic anyway / abort). On an
|
|
351
|
-
accepted `story`, `/plan` hands the one-pager off to
|
|
352
|
-
`/plan --from-notes` as a scope-triage handoff and exits. The ideation triage is
|
|
353
|
-
skipped when `/plan` is itself entered via a scope-triage handoff, so the
|
|
354
|
-
two workflows never ping-pong a settled decision.
|
|
355
|
-
|
|
356
|
-
The same rubric also guards the **existing-Epic entry** (1b) as the
|
|
357
|
-
**story-sized advisory**, which catches a story-sized scope that was
|
|
358
|
-
hand-opened directly as a `type::epic` issue (the Epic Clarity Gate rubric
|
|
359
|
-
scores section *presence*, not scope *size*, so a clear-but-thin Epic would
|
|
360
|
-
otherwise sail through). The advisory fires **only** when the envelope's
|
|
361
|
-
re-plan signal found no folded Tech Spec sections **and** the Epic has no
|
|
362
|
-
open Story children, so
|
|
363
|
-
it never re-triages an Epic that is being re-planned. An `epic` verdict
|
|
364
|
-
proceeds silently; a `story` / `borderline` verdict STOPs with the same
|
|
365
|
-
three-way choice (convert to a standalone Story / proceed as Epic anyway /
|
|
366
|
-
abort). Converting is **close-and-recreate** — a `type::epic` body cannot
|
|
367
|
-
satisfy `validateStoryBody`, and editing the issue in place would violate the
|
|
368
|
-
"do not modify existing issues without explicit permission" rule — so, only
|
|
369
|
-
after the operator confirms, the Epic body seeds a notes file,
|
|
370
|
-
`/plan --from-notes` opens a replacement Story (identified as a
|
|
371
|
-
scope-triage handoff so it skips its own gate, with a `## Notes` back-link to
|
|
372
|
-
the Epic), and the Epic is closed with `gh issue close --comment` cross-linking
|
|
373
|
-
the replacement. No deterministic scorer, no schema, and no label transition
|
|
374
|
-
sit behind either gate.
|
|
375
|
-
|
|
376
|
-
The rubric also runs in the **escalation direction** — the symmetric
|
|
377
|
-
counterpart in [`/plan`](../workflows/helpers/plan-story.md). After `/plan`
|
|
378
|
-
Phase 2 drafts a standalone Story body (the draft, not the seed, is the honest
|
|
379
|
-
basis for the judgment), the same `core/scope-triage` rubric judges whether the
|
|
380
|
-
scope is actually Epic-sized. The verdict folds into the existing Phase 2
|
|
381
|
-
draft-confirmation HITL stop with no extra stop on a `story` verdict; an `epic`
|
|
382
|
-
verdict offers a three-way choice (escalate to `/plan --idea` as a
|
|
383
|
-
scope-triage handoff / persist as a standalone Story anyway / abort). On an
|
|
384
|
-
accepted escalation, `/plan` abandons the draft and hands the notes off to
|
|
385
|
-
`/plan --idea`, marked as a handoff so `/plan` skips its own ideation
|
|
386
|
-
triage gate. This gate is itself skipped when `/plan` was entered via a
|
|
387
|
-
scope-triage handoff (from the Epic path's ideation triage or its
|
|
388
|
-
story-sized conversion path), so the two workflows never ping-pong a settled decision. As with the
|
|
389
|
-
inbound gates, the verdict is advisory and host-LLM judgment — no auto-routing,
|
|
390
|
-
no scorer, no schema, and no label transition.
|
|
391
|
-
|
|
392
|
-
### 1b. Existing-Epic entry
|
|
393
|
-
|
|
394
|
-
Run `/plan <epicId>` directly when the Epic Issue already exists. The
|
|
395
|
-
ideation phases (1a) are skipped.
|
|
396
|
-
|
|
397
|
-
In both modes the planning flow continues into Phase 2 with the captured
|
|
398
|
-
Epic id.
|
|
258
|
+
## Phase 1: Planning
|
|
259
|
+
|
|
260
|
+
Planning is owned end-to-end by [`/plan`](../workflows/plan.md). Rather than
|
|
261
|
+
re-document the ceremony here, this section states the contract the rest of
|
|
262
|
+
the SDLC depends on:
|
|
263
|
+
|
|
264
|
+
- **Entry is text or tickets, never Epic.** The only accepted invocations
|
|
265
|
+
are `--seed`, `--seed-file`, and `--tickets`. There is no `--idea`, no
|
|
266
|
+
`--one-pager`, no `--from-notes`, and no positional `/plan <epicId>`.
|
|
267
|
+
- **One Story by default.** `/plan` authors a single `type::story` issue
|
|
268
|
+
whose body carries a folded `## Spec` (inline only — never spilled to
|
|
269
|
+
`docs/`) plus top-level `acceptance[]` / `verify[]`. It splits into N>1
|
|
270
|
+
siblings (sharing a `plan-run::<id>` label + `depends_on` edges) **only**
|
|
271
|
+
under the default-single split policy: near-zero overlap or a genuine
|
|
272
|
+
architectural seam. Coupled work stays one Story and is decomposed inside
|
|
273
|
+
`## Slicing` as intra-session checkpoints, not sibling tickets.
|
|
274
|
+
- **No Epic-scale ceremony on the default path.** N=1 skips the
|
|
275
|
+
Epic-era Tech Spec / Acceptance Table / clarity-gate / decompose /
|
|
276
|
+
reconciler machinery. `plan-persist.js` runs the deterministic gates
|
|
277
|
+
(ticket validator, split policy, reachability, budget) and — for N>1 —
|
|
278
|
+
`assertAcceptancePartition` so every acceptance criterion belongs to
|
|
279
|
+
exactly one Story.
|
|
280
|
+
- **Handoff.** Persist creates the Story issue(s) at `agent::ready` and
|
|
281
|
+
names the delivery command: `/deliver <storyId>` (or `/deliver --run
|
|
282
|
+
<planRunId>`).
|
|
283
|
+
|
|
284
|
+
Optional split advisory notes come from
|
|
285
|
+
[`core/scope-triage`](../skills/core/scope-triage/SKILL.md); there is no
|
|
286
|
+
`epic|story` routing verdict, scorer, schema field, or label transition
|
|
287
|
+
behind them.
|
|
288
|
+
|
|
289
|
+
Audit findings enter planning through
|
|
290
|
+
[`/audit-to-stories`](../workflows/audit-to-stories.md), which groups and
|
|
291
|
+
deduplicates findings and hands off via `--emit-plan-seed` →
|
|
292
|
+
`/plan --seed-file <path>`.
|
|
399
293
|
|
|
400
294
|
---
|
|
401
295
|
|
|
402
|
-
## Phase 2:
|
|
403
|
-
|
|
404
|
-
The framework reads the Epic and autonomously builds the entire work breakdown.
|
|
405
|
-
|
|
406
|
-
> **Epic Clarity Gate (`/plan` `planning.clarity-gate` state).** Before Tech Spec /
|
|
407
|
-
> Acceptance Spec authoring kicks off, `/plan` scores the Epic body
|
|
408
|
-
> against the five canonical sections from
|
|
409
|
-
> [`templates/epic-from-idea.md`](../templates/epic-from-idea.md) (Context,
|
|
410
|
-
> Goal, Non-Goals, Scope, Acceptance Criteria). Common legacy heading
|
|
411
|
-
> variants (`Problem`, `Direction`, `MVP Scope`, `Not Doing`,
|
|
412
|
-
> `Out of Scope`) are accepted by the scorer's regex for back-compat.
|
|
413
|
-
> The rubric is deterministic (section-presence): `clear` requires ≥ 4 of 5
|
|
414
|
-
> sections present **and** the Acceptance Criteria section present (AC is
|
|
415
|
-
> required). A
|
|
416
|
-
> `clear` verdict
|
|
417
|
-
> skips fast with no prompt; a `needs-refinement` verdict drops into the
|
|
418
|
-
> `idea-refinement` skill seeded from the current Epic body, surfaces a
|
|
419
|
-
> HITL diff, and on approval persists the sharpened body via
|
|
420
|
-
> `gh issue edit` before the `planning.spec-authoring` state begins.
|
|
421
|
-
> The gate honours the
|
|
422
|
-
> "do not modify existing issues without permission" Constraint — every
|
|
423
|
-
> body rewrite is operator-confirmed.
|
|
424
|
-
|
|
425
|
-
1. **Epic Planner** (the spec half of `plan-persist.js`):
|
|
426
|
-
- Synthesizes the Epic body with project documentation.
|
|
427
|
-
- Folds the authored **Tech Spec** (opening with `## Delivery
|
|
428
|
-
Slicing`) and the **Acceptance Table** (the AC-ID table) into
|
|
429
|
-
marker-delimited managed sections of the Epic body. The Epic body
|
|
430
|
-
carries its `## User Stories` section inline — the PRD artifact
|
|
431
|
-
class was retired (Story #4314), and the `context::tech-spec` /
|
|
432
|
-
`context::acceptance-spec` ticket classes were retired the same way
|
|
433
|
-
(Story #4324): a `/plan` Epic run creates exactly **one** GitHub
|
|
434
|
-
issue.
|
|
435
|
-
|
|
436
|
-
> [!TIP] **Acceptance criteria phrasing.** Write the Epic's acceptance
|
|
437
|
-
> criteria in Gherkin-compatible `Given / When / Then` form so the QA
|
|
438
|
-
> acceptance suite can lift them directly into executable `.feature` files. See
|
|
439
|
-
> [`rules/gherkin-standards.md`](../rules/gherkin-standards.md) for the canonical
|
|
440
|
-
> clause grammar, tag taxonomy, and forbidden patterns.
|
|
441
|
-
|
|
442
|
-
### Acceptance Table — the second folded planning section
|
|
443
|
-
|
|
444
|
-
Every planned Epic body carries **two** managed planning sections
|
|
445
|
-
(Story #4324 — no separate context tickets):
|
|
446
|
-
|
|
447
|
-
| Section | Artifact | Authored by | Drives |
|
|
448
|
-
| ---------------------- | ---------------- | --------------------------------------------------- | ----------------------------------------------------- |
|
|
449
|
-
| `## Delivery Slicing`… | Tech Spec | `epic-plan-spec-author` skill (Architect persona) | How we're shipping it. |
|
|
450
|
-
| `## Acceptance Table` | Acceptance Table | `epic-plan-spec-author` skill (Acceptance Engineer) | The AC ID table that gates close-time reconciliation. |
|
|
451
|
-
|
|
452
|
-
The Acceptance Table section is a single Markdown table —
|
|
453
|
-
`| AC ID | Outcome | Feature File | Scenario | Disposition |` — with
|
|
454
|
-
stable `AC-<n>` IDs assigned in document order. IDs are reused across
|
|
455
|
-
re-plans when an Outcome is materially unchanged so scenario tags
|
|
456
|
-
(`@ac-N`) stay aligned with the spec. Each row's `Disposition` is one
|
|
457
|
-
of `new | updated | unchanged`. The skill also renders a **Runner
|
|
458
|
-
Verification** line directly under the table that records the verified
|
|
459
|
-
BDD runner + pending-tag (e.g. `playwright-bdd supports @skip`) for the
|
|
460
|
-
features-first Story to consume.
|
|
461
|
-
|
|
462
|
-
The spec is persisted by
|
|
463
|
-
`plan-persist.js --epic [Epic_ID] --tech-spec ... --acceptance-table ...`
|
|
464
|
-
— the persist step folds both artifacts into the Epic body's managed
|
|
465
|
-
sections in one atomic, section-scoped write (everything outside the
|
|
466
|
-
managed regions is byte-preserved) and fails loudly if any input is
|
|
467
|
-
missing or empty. At delivery time, hydration strips the
|
|
468
|
-
`## Acceptance Table` section from story prompts — it is
|
|
469
|
-
authoring/close-time machinery, not delivery context.
|
|
470
|
-
|
|
471
|
-
#### Adaptive planning risk routing
|
|
472
|
-
|
|
473
|
-
`/plan`'s `planning.spec-authoring` state derives a deterministic
|
|
474
|
-
**`planningRisk`** envelope from a **planner-authored risk verdict**
|
|
475
|
-
(`risk-verdict.json`, the third planning artifact the
|
|
476
|
-
`epic-plan-spec-author` Skill writes from the Epic body / Tech Spec it just
|
|
477
|
-
authored). `plan-persist.js` validates the verdict
|
|
478
|
-
against `risk-verdict.schema.json` — a malformed verdict fails closed —
|
|
479
|
-
then derives the envelope via `deriveRiskEnvelope`
|
|
480
|
-
(`lib/orchestration/planning-risk.js`). The verdict is recorded as a
|
|
481
|
-
`risk-verdict` structured comment on the Epic, and both the verdict and
|
|
482
|
-
the envelope land in the `epic-plan-state` checkpoint, consumed by two
|
|
483
|
-
downstream decisions:
|
|
484
|
-
|
|
485
|
-
- **Acceptance disposition** — `acceptanceDisposition` is one of
|
|
486
|
-
`required`, `recommended`, or `not-applicable`. The `not-applicable`
|
|
487
|
-
case is the planner-selected route to the `acceptance::n-a` waiver
|
|
488
|
-
(see the section below); the other two cause the Acceptance Spec to
|
|
489
|
-
be authored normally.
|
|
490
|
-
- **Gate routing** — `gateDecision` is either `review-required`
|
|
491
|
-
(paired with `requiresReview: true`) or `auto-proceed`. High-risk
|
|
492
|
-
Epics (visible behavior, public API, security, billing, data
|
|
493
|
-
migration, destructive mutation, critical workflow) trigger a HITL
|
|
494
|
-
stop after `planning.spec-authoring` so the operator can read the
|
|
495
|
-
Epic body's Tech Spec / Acceptance Table sections on GitHub before
|
|
496
|
-
decomposition starts. Low-risk Epics (docs-only, internal refactor, pure test
|
|
497
|
-
harness, cleanup) print the auto-proceed message from
|
|
498
|
-
`reviewRouting.operatorMessage` and chain directly into the
|
|
499
|
-
`planning.decompose` state. The operator can force the review
|
|
500
|
-
stop on low-risk work by passing `--force-review` to `/plan`.
|
|
501
|
-
|
|
502
|
-
The risk envelope is also threaded into the `planning.decompose` state's decomposer context
|
|
503
|
-
so the ticket array can cite the relevant axes when assigning
|
|
504
|
-
`risk::high` labels to Stories. The split is "judgment proposes, harness
|
|
505
|
-
gates": the planner supplies the axes (with per-axis rationale), and the
|
|
506
|
-
envelope derivation — overall level, review requirement, acceptance
|
|
507
|
-
disposition, gate decision — is local and deterministic.
|
|
508
|
-
|
|
509
|
-
#### Opting out — the `acceptance::n-a` waiver
|
|
510
|
-
|
|
511
|
-
Not every Epic warrants a formal Acceptance Spec (pure refactors,
|
|
512
|
-
framework maintenance, docs-only churn). The **`acceptance::n-a`** label
|
|
513
|
-
on the Epic ticket records the waiver. There are two routes to the label:
|
|
514
|
-
|
|
515
|
-
- **Operator-applied** — the operator labels the Epic before or during
|
|
516
|
-
`/plan`'s `planning.spec-authoring` state when they already
|
|
517
|
-
know the work does not need a spec.
|
|
518
|
-
- **Planner-selected** — `/plan`'s `planning.spec-authoring` state derives a
|
|
519
|
-
`planningRisk` envelope from the planner-authored risk verdict
|
|
520
|
-
(see § Adaptive planning risk routing) and,
|
|
521
|
-
when `acceptanceDisposition === 'not-applicable'`, the persist step
|
|
522
|
-
of `plan-persist.js` applies `acceptance::n-a` on the Epic and skips
|
|
523
|
-
the Acceptance Table section for that run (stripping a stale one on a
|
|
524
|
-
re-plan). The disposition is also
|
|
525
|
-
recorded in the `epic-plan-state` checkpoint so the decision is
|
|
526
|
-
auditable.
|
|
527
|
-
|
|
528
|
-
Either route produces the same runtime behavior. The waiver is respected
|
|
529
|
-
by both runtime gates:
|
|
530
|
-
|
|
531
|
-
- The `/deliver` **start gate** (`delivery.snapshot` state) skips
|
|
532
|
-
the acceptance-spec presence check when the label is set.
|
|
533
|
-
- The finalize-time **acceptance reconciler** returns
|
|
534
|
-
`status: 'waived'` without scanning `tests/features/**` and the
|
|
535
|
-
finalize step proceeds. (The waiver now waives the Epic body's
|
|
536
|
-
`## Acceptance Table` section — Story #4324 — with unchanged
|
|
537
|
-
meaning.)
|
|
538
|
-
|
|
539
|
-
The waiver is binary — there is no partial opt-out. If an Epic later
|
|
540
|
-
warrants spec coverage, remove the label and run `/plan`'s
|
|
541
|
-
`planning.spec-authoring` state to author the spec.
|
|
542
|
-
|
|
543
|
-
1. **Ticket Decomposer** (the fan-out half of `plan-persist.js`):
|
|
544
|
-
- Decomposes specs into the **2-tier hierarchy**
|
|
545
|
-
(Epic → Story):
|
|
546
|
-
|
|
547
|
-
```text
|
|
548
|
-
Epic (type::epic) ← body carries the folded Tech Spec
|
|
549
|
-
│ sections + ## Acceptance Table
|
|
550
|
-
├── Story (type::story)
|
|
551
|
-
│ ├── acceptance[] ← inline on Story body
|
|
552
|
-
│ └── verify[] ← inline on Story body
|
|
553
|
-
└── Story (type::story)
|
|
554
|
-
```
|
|
555
|
-
|
|
556
|
-
- **Wiring.** Each ticket is linked using `blocked by #NNN` syntax and
|
|
557
|
-
GitHub's native sub-issues API.
|
|
558
|
-
- **Metadata.** Each Story is stamped with persona, estimated files,
|
|
559
|
-
and agent prompts, plus the inline `acceptance[]` / `verify[]`
|
|
560
|
-
arrays the executing sub-agent reads.
|
|
561
|
-
|
|
562
|
-
`/deliver` runs a **single** Story-implementation phase per
|
|
563
|
-
Story. The wave-loop fan-out in `/deliver` and the
|
|
564
|
-
Story-branch → Epic-branch merge model are unchanged; the Feature and
|
|
565
|
-
Task layers are gone, and thematic grouping lives as prose in the Epic
|
|
566
|
-
body (which also carries the folded Tech Spec sections).
|
|
567
|
-
|
|
568
|
-
When the persist step completes the Epic flips to `agent::ready` and the
|
|
569
|
-
`plan-summary` structured comment (with the dry-run wave table) lands on
|
|
570
|
-
the Epic. The live wave manifest is written at deliver time by the
|
|
571
|
-
prepare phase — planning posts no separate dispatch-manifest comment
|
|
572
|
-
(Epic #4474).
|
|
573
|
-
|
|
574
|
-
### `agent::ready` exit conditions
|
|
575
|
-
|
|
576
|
-
The planning → delivery handoff is governed by an explicit checklist.
|
|
577
|
-
`plan-persist.js` refuses to flip the Epic to `agent::ready`
|
|
578
|
-
unless **every** condition below is true. The
|
|
579
|
-
contract is enforced at the planner boundary so `/deliver` can
|
|
580
|
-
treat `agent::ready` as a load-bearing precondition rather than a
|
|
581
|
-
hopeful signal.
|
|
582
|
-
|
|
583
|
-
- **Planning sections present or waived.** The Epic body carries the
|
|
584
|
-
folded Tech Spec sections (`## Delivery Slicing` onward), and either
|
|
585
|
-
the `## Acceptance Table` managed section **or** the
|
|
586
|
-
`acceptance::n-a` waiver label. Missing-without-waiver fails the
|
|
587
|
-
handoff.
|
|
588
|
-
- **Decomposition persisted.** The structural reconciler has applied
|
|
589
|
-
the Epic's child-Story backlog and written the spec to
|
|
590
|
-
`.agents/epics/<epicId>.yaml`. The `epic-plan-state` checkpoint
|
|
591
|
-
comment records `phase: ready`.
|
|
592
|
-
- **Healthcheck green.** The inline `epic-plan-healthcheck` pass run
|
|
593
|
-
by the persist CLI returned `ok: true`. A failing healthcheck
|
|
594
|
-
blocks the handoff — there is no advisory degrade-mode for
|
|
595
|
-
`agent::ready`.
|
|
596
|
-
- **Summary posted.** The persist CLI has posted the `plan-summary`
|
|
597
|
-
structured comment (backlog stats + the dry-run wave table) so the
|
|
598
|
-
operator knows the Epic is ready to fan out.
|
|
599
|
-
|
|
600
|
-
**Operator override.** The `planning::healthcheck-waived` label, applied
|
|
601
|
-
to the Epic by the operator, is the documented escape hatch for cases
|
|
602
|
-
where the healthcheck reports `ok: false` for an environmental reason
|
|
603
|
-
the operator has triaged and accepted (for example: a transient
|
|
604
|
-
`origin` outage during a known maintenance window). When the label is
|
|
605
|
-
present, the persist half allows the `agent::ready` flip even though
|
|
606
|
-
the healthcheck failed. Every other exit condition above still
|
|
607
|
-
applies — the waiver scopes to the healthcheck check alone. Remove
|
|
608
|
-
the label to re-arm the gate.
|
|
609
|
-
|
|
610
|
-
---
|
|
296
|
+
## Phase 2: Delivery
|
|
611
297
|
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
finalize, automerge, and cleanup. The `delivery.finalize`,
|
|
620
|
-
`delivery.automerge`, and `delivery.complete` states each fire one
|
|
621
|
-
typed event via `lifecycle-emit.js` (`epic.close.end`,
|
|
622
|
-
`epic.automerge.start`, `epic.merge.armed`); the matching listeners run
|
|
623
|
-
the side effects. See
|
|
624
|
-
[`docs/LIFECYCLE.md`](../../docs/LIFECYCLE.md) for the bus contract,
|
|
625
|
-
event taxonomy, ledger format, and listener model — every phase
|
|
626
|
-
transition, ticket-state flip, and webhook fan-out now flows through
|
|
627
|
-
that bus, and the on-disk ledger at `temp/epic-<id>/lifecycle.ndjson`
|
|
628
|
-
is the canonical resume target. Safety gates (auto-merge arming,
|
|
629
|
-
acceptance-spec reconciliation, blocker handling) are listener
|
|
630
|
-
side-effects rather than inline calls at phase boundaries; the
|
|
631
|
-
"merge-lockout" lint rule keeps `gh pr merge` confined to the
|
|
632
|
-
`AutomergeArmer` listener.
|
|
633
|
-
|
|
634
|
-
> **Acceptance start gate.** Before a single wave fans out,
|
|
635
|
-
> `/deliver`'s `delivery.snapshot` state
|
|
636
|
-
> ([`lib/orchestration/epic-runner/phases/snapshot.js`](../scripts/lib/orchestration/epic-runner/phases/snapshot.js))
|
|
637
|
-
> asserts that the Epic either (a) carries the `acceptance::n-a`
|
|
638
|
-
> waiver label, or (b) carries the `## Acceptance Table` managed
|
|
639
|
-
> section in its body (Story #4324). Presence is sufficient — the
|
|
640
|
-
> reviewer's OK during `/plan`'s `planning.spec-authoring` state is
|
|
641
|
-
> the approval signal. Neither
|
|
642
|
-
> condition met → the snapshot throws a clear error naming the
|
|
643
|
-
> missing precondition and `runAsCli` maps it to `process.exit(1)`.
|
|
644
|
-
> This refuses to launch Epics that skipped acceptance-spec
|
|
645
|
-
> authoring, surfacing the gap at delivery time rather than letting
|
|
646
|
-
> Story dispatch race ahead.
|
|
298
|
+
Delivery is owned end-to-end by [`/deliver`](../workflows/deliver.md), which
|
|
299
|
+
delegates every Story to
|
|
300
|
+
[`helpers/deliver-story`](../workflows/helpers/deliver-story.md). This
|
|
301
|
+
section states the contract; the per-Story step detail (init, implement,
|
|
302
|
+
self-eval, ceremony, close, CI watch, confirm-merge, cleanup) lives in the
|
|
303
|
+
`deliver-story` workflow and its
|
|
304
|
+
[reference](../workflows/helpers/deliver-story-reference.md).
|
|
647
305
|
|
|
648
306
|
### Invocation modes
|
|
649
307
|
|
|
650
|
-
| Mode
|
|
651
|
-
|
|
|
652
|
-
| **
|
|
653
|
-
| **
|
|
654
|
-
| **
|
|
655
|
-
| **
|
|
656
|
-
| **Standalone Story (worker)** | *helper* `helpers/single-story-deliver <storyId>` | Per-Story sub-agent called internally by `/deliver`; not an operator slash command. |
|
|
657
|
-
| **Mixed set** | `/deliver <ids...>` | Any mix of ≥1 Epics and standalone Stories. The router composes a sequential segment plan — standalone segment first, then Epic segments in input order — delegating each segment to the path helpers above. |
|
|
658
|
-
|
|
659
|
-
The single operator-facing entry point is `/deliver` — it routes a lone
|
|
660
|
-
Epic, a standalone-Story set, or a mixed set (via the sequential segment
|
|
661
|
-
plan) to the right path helper(s). The `helpers/` layer sits below it and
|
|
662
|
-
is never invoked directly by the operator.
|
|
663
|
-
|
|
664
|
-
### Story-centric branching
|
|
665
|
-
|
|
666
|
-
- **Format**: `story-<storyId>` (merges into `epic/<epicId>`).
|
|
667
|
-
- **Goal**: minimize merge conflicts and consolidation waves by grouping
|
|
668
|
-
related work on one context slice.
|
|
669
|
-
|
|
670
|
-
### Story execution lifecycle
|
|
671
|
-
|
|
672
|
-
Whether the Story is launched directly by the operator or fanned out by
|
|
673
|
-
`/deliver`'s wave loop, the same three phases run:
|
|
674
|
-
|
|
675
|
-
1. **Initialization** (`story-init.js`):
|
|
676
|
-
- Verifies all upstream dependencies are satisfied.
|
|
677
|
-
- Syncs the Epic base branch with `main`.
|
|
678
|
-
- Creates or seeds the Story branch (in a worktree when
|
|
679
|
-
`delivery.worktreeIsolation.enabled: true`).
|
|
680
|
-
- Transitions the Story to `agent::executing`. `story-phase.js`
|
|
681
|
-
upserts the initial `story-run-progress` snapshot at the `init`
|
|
682
|
-
phase.
|
|
683
|
-
2. **Story implementation.** The agent executes the Story's inline
|
|
684
|
-
`acceptance[]` / `verify[]` contract on the shared Story branch,
|
|
685
|
-
authoring one or more commits referencing the parent Story via
|
|
686
|
-
`(refs #<storyId>)`. Each phase transition (`implementing`,
|
|
687
|
-
`closing`, `done`, `blocked`) is recorded via `story-phase.js`.
|
|
688
|
-
|
|
689
|
-
After the implementation commits land and **before** the phase flips
|
|
690
|
-
to `closing`, a **bounded acceptance self-eval loop** runs (Story
|
|
691
|
-
#3819). An independent, fresh-context critic pass scores the working
|
|
692
|
-
diff against **each** `acceptance[]` item — `met | partial | unmet`
|
|
693
|
-
plus a short evidence string, consuming the Story's `verify[]`
|
|
694
|
-
commands as **required evidence** (the `verify[]` commands are no
|
|
695
|
-
longer optional advisory pre-flight). The critic writes its verdict to
|
|
696
|
-
a verdict file
|
|
697
|
-
(`.agents/schemas/acceptance-eval-verdict.schema.json`); the
|
|
698
|
-
`acceptance-eval.js` gate validates it, enforces the bounded round
|
|
699
|
-
cap, and decides the next action:
|
|
700
|
-
- **all `met`** → the phase flips to `closing`.
|
|
701
|
-
- **any `partial`/`unmet`, rounds remaining** → the agent redrafts
|
|
702
|
-
the flagged criteria and re-runs the critic pass for the next round.
|
|
703
|
-
- **round cap reached, criteria still unmet** → the Story transitions
|
|
704
|
-
to `agent::blocked` (not `closing`), posts a `friction` comment
|
|
705
|
-
naming the unmet criteria and their evidence, and exits non-zero. It
|
|
706
|
-
never silently proceeds to close.
|
|
707
|
-
|
|
708
|
-
The loop is **always on** (a hard cutover — there is no flag toggling
|
|
709
|
-
it off) and **bounded**: the redraft ceiling is
|
|
710
|
-
`delivery.acceptanceEval.maxRounds` (default 2), clamped by the
|
|
711
|
-
resolver into `[1, hard ceiling]` so no configuration can disable the
|
|
712
|
-
cap or let the loop spin unbounded. Each terminus emits a
|
|
713
|
-
per-criterion `acceptance-eval` signal into the retro / feedback
|
|
714
|
-
substrate so the retro and `/plan` Phase 0 feedback fetch see
|
|
715
|
-
which acceptance items needed rework and the round count. The loop is
|
|
716
|
-
**additive** and sits below the Epic-level acceptance-spec
|
|
717
|
-
reconciliation (`/deliver` Phase 7.1) — it evaluates the actual
|
|
718
|
-
work product per Story mid-delivery, not test-tag presence at
|
|
719
|
-
finalize.
|
|
720
|
-
3. **Closure** (`story-close.js`):
|
|
721
|
-
- Runs shift-left validation (lint, format, test).
|
|
722
|
-
- Merges the Story branch into `epic/<epicId>`.
|
|
723
|
-
- Transitions the Story → `agent::done`. There is no upward
|
|
724
|
-
auto-cascade — the Epic flips only when the operator merges the
|
|
725
|
-
`epic/<id>` PR to `main`.
|
|
726
|
-
- Reaps the Story worktree and cleans up the merged Story branch.
|
|
727
|
-
|
|
728
|
-
### Context hydration
|
|
729
|
-
|
|
730
|
-
When a sub-agent runs `helpers/epic-deliver-story <storyId>` (for
|
|
731
|
-
Epic-attached Stories) or `helpers/single-story-deliver <storyId>` (for
|
|
732
|
-
standalone Stories), the Context Hydrator assembles a self-contained prompt:
|
|
733
|
-
|
|
734
|
-
1. `agent-protocol.md` (universal rules).
|
|
735
|
-
2. Persona and skill directives (from Task labels).
|
|
736
|
-
3. Hierarchy context (Story → Epic — the Epic body carries the folded
|
|
737
|
-
Tech Spec sections; the `## Acceptance Table` section is stripped
|
|
738
|
-
from delivery prompts).
|
|
739
|
-
4. **Story branch context.** Automatic checkouts to the Story branch. Under
|
|
740
|
-
worktree isolation, each Story runs in its own `.worktrees/story-<id>/` so
|
|
741
|
-
branch swaps, staging, and reflog activity are isolated per-story. See
|
|
742
|
-
[`workflows/helpers/worktree-lifecycle.md`](../workflows/helpers/worktree-lifecycle.md).
|
|
743
|
-
5. Task-specific instructions and subtask checklist.
|
|
308
|
+
| Mode | Entry point | When to use |
|
|
309
|
+
| --- | --- | --- |
|
|
310
|
+
| **Single Story** | `/deliver <storyId>` | Deliver one Story end-to-end; ends with a PR open to `main`. |
|
|
311
|
+
| **Story set** | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (default concurrency **3**); each lands through its own PR. |
|
|
312
|
+
| **Plan-run** | `/deliver --run <planRunId>` | Resolve Stories labeled `plan-run::<id>`, sequence them, and run the per-run epilogue after the set lands. |
|
|
313
|
+
| **Story worker (internal)** | *helper* `helpers/deliver-story <storyId>` | Per-Story engine invoked internally by `/deliver`; not an operator slash command. |
|
|
744
314
|
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
315
|
+
The single operator-facing entry point is `/deliver`. It performs no
|
|
316
|
+
git/label mutations itself — `deliver-story` owns every script invocation
|
|
317
|
+
per Story. Any ticket that is not `type::story`, or that still carries an
|
|
318
|
+
`Epic: #N` reference, is a hard error naming the ID and the fix (close or
|
|
319
|
+
re-plan as a v2 Story).
|
|
748
320
|
|
|
749
|
-
|
|
750
|
-
intermediate review label is not part of the label taxonomy; the
|
|
751
|
-
PR opened by `/deliver`'s `delivery.finalize` state is the equivalent "ready to merge"
|
|
752
|
-
signal at the Epic level. The `WaveObserver` submodule additionally
|
|
753
|
-
syncs a GitHub Projects v2 Status column on each transition when a
|
|
754
|
-
`projectNumber` is configured.
|
|
755
|
-
- **Tasklists**: subtasks are checked off in the ticket body (`- [ ]` →
|
|
756
|
-
`- [x]`).
|
|
757
|
-
- **Friction**: friction logs are posted as structured comments on the Task.
|
|
758
|
-
- **Wave transitions**: the Epic Deliver Runner emits `wave-N-start` and
|
|
759
|
-
`wave-N-end` structured comments on the Epic, each carrying the wave
|
|
760
|
-
manifest, story outcomes, and timing.
|
|
321
|
+
### Branch model (authoritative)
|
|
761
322
|
|
|
762
|
-
|
|
323
|
+
```text
|
|
324
|
+
story-<id> → PR → main (squash + required checks)
|
|
325
|
+
```
|
|
763
326
|
|
|
764
|
-
|
|
765
|
-
|
|
327
|
+
There is no `epic/<id>` integration branch and no `--no-ff` wave merge.
|
|
328
|
+
Dependent Stories land sequentially so each builds on the previous merge to
|
|
329
|
+
`main`.
|
|
766
330
|
|
|
767
|
-
###
|
|
331
|
+
### Ceremony
|
|
768
332
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
333
|
+
Ceremony depth is selected by `delivery.routing.ceremonyProfile`
|
|
334
|
+
(`minimal` | `standard` | `strict`, default `standard`) and the Story's
|
|
335
|
+
own planning risk. Hard gates (lint / test / format / coverage / CRAP /
|
|
336
|
+
maintainability) always run at close — risk never disables them; it only
|
|
337
|
+
tunes acceptance-critic mode, review depth, and audit-lens selection. The
|
|
338
|
+
full profile × scope matrix lives in
|
|
339
|
+
[`deliver.md` § Ceremony](../workflows/deliver.md).
|
|
774
340
|
|
|
775
|
-
|
|
776
|
-
the startup `[ENV]` log line for operator correlation. It is a 12-char
|
|
777
|
-
short-id derived from hostname+pid+random.
|
|
341
|
+
### State sync
|
|
778
342
|
|
|
779
|
-
|
|
343
|
+
Agents update their state in real time on GitHub, always through
|
|
344
|
+
`update-ticket-state.js`:
|
|
345
|
+
|
|
346
|
+
- **Labels**: `agent::ready` → `agent::executing` → `agent::closing` →
|
|
347
|
+
`agent::done`. The `agent::done` flip happens only after
|
|
348
|
+
`single-story-confirm-merge.js` confirms the PR merged. When a
|
|
349
|
+
`projectNumber` is configured, the Projects v2 Status column is synced on
|
|
350
|
+
each transition (and re-asserted after merge to beat the board's late
|
|
351
|
+
built-in write).
|
|
352
|
+
- **Acceptance/verify**: the agent works the Story's inline `acceptance[]`
|
|
353
|
+
/ `verify[]` arrays; `verify[]` commands are consumed as required
|
|
354
|
+
evidence by the acceptance self-eval loop.
|
|
355
|
+
- **Friction**: friction is posted as a structured comment on the **Story**
|
|
356
|
+
(`diagnose-friction.js`), and rolls up into the retro and, for N>1, the
|
|
357
|
+
per-run follow-up roll-up.
|
|
780
358
|
|
|
781
|
-
|
|
782
|
-
dispatch manifest and verifies the target story's blockers are all merged.
|
|
783
|
-
Unmerged blockers print each blocker's id, state, and URL; the session exits
|
|
784
|
-
0 (operator-error, not a system error) without touching any branches. A
|
|
785
|
-
missing or stale-format manifest emits a warning and proceeds — the guard is
|
|
786
|
-
a footgun-prevention layer, not a strict gate.
|
|
359
|
+
### Cross-clone coordination
|
|
787
360
|
|
|
788
|
-
|
|
361
|
+
Concurrent runs are serialised by **two distinct layers**:
|
|
362
|
+
|
|
363
|
+
- **Filesystem locks are same-machine-only.** The single-story sweep lock
|
|
364
|
+
(`sweep-lock.js`) is a single-file rendezvous keyed on a local process
|
|
365
|
+
PID + mtime TTL. Because a PID is only meaningful on its own machine and
|
|
366
|
+
`.git/` is never committed, these locks coordinate only the worktrees and
|
|
367
|
+
sessions on **one** clone.
|
|
368
|
+
- **The assignee-as-lease is the cross-clone layer.** To stop two clones
|
|
369
|
+
from both *starting* the same Story, `deliver-story` takes an exclusive,
|
|
370
|
+
time-bounded claim on the ticket via
|
|
371
|
+
[`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js), riding
|
|
372
|
+
the ticket's GitHub `assignees` field so a live foreign claim is visible
|
|
373
|
+
to every clone. The standalone lease **fails closed** on a foreign
|
|
374
|
+
assignee; `--steal` is the only override. See
|
|
375
|
+
[`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination).
|
|
789
376
|
|
|
790
377
|
### Concurrent close — push retry
|
|
791
378
|
|
|
792
|
-
`story-close.js`
|
|
793
|
-
and
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
- `DEFAULT_STORY_MERGE_RETRY.maxAttempts` (framework-internal constant in
|
|
800
|
-
`.agents/scripts/lib/config/runners.js`) — 3.
|
|
801
|
-
- `DEFAULT_STORY_MERGE_RETRY.backoffMs` (same module) — `[250, 500, 1000]`.
|
|
802
|
-
|
|
803
|
-
A real content conflict (both stories touched the same lines) aborts the
|
|
804
|
-
loop with a clear error, leaves the local tree clean, and exits non-zero for
|
|
805
|
-
manual resolution. The retry path is a wrapper around the existing happy path.
|
|
806
|
-
|
|
807
|
-
### Cross-clone coordination
|
|
808
|
-
|
|
809
|
-
Concurrent runs are serialised by **two distinct layers**, and the
|
|
810
|
-
distinction matters: getting it wrong leaves two clones racing on the same
|
|
811
|
-
Epic with no guard between them.
|
|
812
|
-
|
|
813
|
-
**Filesystem locks are same-machine-only.** The Epic merge lock
|
|
814
|
-
(`.agents/scripts/lib/epic-merge-lock.js`) lives at
|
|
815
|
-
`<gitCommonDir>/epic-<epicId>.merge.lock` inside `.git/`, and the
|
|
816
|
-
single-story sweep lock (`.agents/scripts/lib/single-story-sweep/sweep-lock.js`)
|
|
817
|
-
is a single-file rendezvous on the local filesystem. Both decide staleness
|
|
818
|
-
by probing a recorded **process PID** with `process.kill(pid, 0)` and by
|
|
819
|
-
comparing a local-filesystem mtime against a TTL. Because a PID is only
|
|
820
|
-
meaningful on the machine that owns it and `.git/` is never committed,
|
|
821
|
-
**these locks coordinate only the worktrees and sessions on a single
|
|
822
|
-
machine/clone. They do NOT coordinate across clones.** Two operators on
|
|
823
|
-
two separate clones (or two CI runners) will each acquire their *own*
|
|
824
|
-
merge lock and never see the other's — the locks are invisible to each
|
|
825
|
-
other. The only cross-clone safety the merge step itself has is the
|
|
826
|
-
bounded push-retry described above, which recovers from the
|
|
827
|
-
non-fast-forward rejection *after* the race has already happened.
|
|
828
|
-
|
|
829
|
-
**The assignee-as-lease is the cross-clone layer.** To stop two clones
|
|
830
|
-
from both *starting* to drive the same Epic or Story, the framework takes
|
|
831
|
-
an exclusive, time-bounded claim on the ticket via
|
|
832
|
-
[`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js). The lease
|
|
833
|
-
rides the ticket's GitHub `assignees` field — a substrate every clone can
|
|
834
|
-
read — so a live foreign claim is visible to, and refuses, a second
|
|
835
|
-
operator regardless of which machine they are on. All three delivery and
|
|
836
|
-
planning entry points take the claim: `/plan`'s persist CLI acquires the
|
|
837
|
-
Epic lease before its first mutation and releases it on every exit path,
|
|
838
|
-
`/deliver` acquires the Epic lease in its prepare guard, and
|
|
839
|
-
`/single-story-deliver` acquires the Story lease at init. For
|
|
840
|
-
`/deliver` and `/single-story-deliver`, liveness is decided by the
|
|
841
|
-
owner's most-recent `story.heartbeat` against `delivery.lease.ttlMs`;
|
|
842
|
-
because planning emits no `story.heartbeat`, `/plan` has no
|
|
843
|
-
live-heartbeat source and treats **any** foreign assignee as a live claim.
|
|
844
|
-
A live foreign claim fails the preflight closed (refuse-and-exit, naming
|
|
845
|
-
the owner); `--steal` is the only override. See
|
|
846
|
-
[`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination)
|
|
847
|
-
for the full lease behaviour table.
|
|
848
|
-
|
|
849
|
-
The two layers are complementary, not redundant: the lease prevents two
|
|
850
|
-
clones from racing in the first place, while the same-machine merge lock
|
|
851
|
-
serialises the parallel-wave story closures *within* the one clone that
|
|
852
|
-
holds the lease.
|
|
853
|
-
|
|
854
|
-
### Close-tail (`delivery.close-validation` through `delivery.complete` of `/deliver`)
|
|
855
|
-
|
|
856
|
-
After the wave loop returns `complete`, `/deliver` runs the
|
|
857
|
-
remaining phases against the Epic branch — close-validation, audit,
|
|
858
|
-
code-review, retro, and finalize — before handing off to the
|
|
859
|
-
watch / auto-merge / cleanup tail that drives the PR to merge:
|
|
860
|
-
|
|
861
|
-
1. **Close-validation (Phase 3).** Lint + test + project-extended ratchets
|
|
862
|
-
(maintainability, CRAP, lint baseline) run via `evidence-gate.js` keyed
|
|
863
|
-
on `git rev-parse HEAD`. A clean tree on a re-run short-circuits in
|
|
864
|
-
milliseconds. A failing gate halts the workflow until the regression is
|
|
865
|
-
fixed on a hotfix branch and re-merged into the Epic.
|
|
866
|
-
2. **Epic-close lens roster (Phase 4).** Resolves — does not walk — the slim
|
|
867
|
-
Epic-close lens roster (`epicCloseLenses`): the `gate3` change-set selection
|
|
868
|
-
plus the risk-routed lenses, restricted to the **cumulative + global +
|
|
869
|
-
risk-routed** tiers via `selectEpicCloseLenses`. Every **local-tier**
|
|
870
|
-
change-set lens is excluded because its concern is already verified
|
|
871
|
-
shift-left — the write-time distilled checklist threaded into the Story
|
|
872
|
-
prompt (Story #4410) and the maker-blind Story-scope local-lens pass in
|
|
873
|
-
`story-close` (Story #4409). The roster is handed to Phase 5; there is no
|
|
874
|
-
standalone Phase 4 walk and no separate `audit-results` comment (Story
|
|
875
|
-
#4412).
|
|
876
|
-
3. **Code-review (Phase 5) — cumulative diff walked once.**
|
|
877
|
-
`lib/orchestration/code-review.js` walks `main..epic/<id>` a single time,
|
|
878
|
-
executing the Phase 4 lens roster as review **dimensions** alongside the
|
|
879
|
-
review pillars, and posts the unified `verification-results` structured
|
|
880
|
-
comment on the Epic (Story #4411 unified the former `code-review` and
|
|
881
|
-
`audit-results` contracts; Story #4412 folded the lens walk into this pass).
|
|
882
|
-
Remediation is **tier-aware and split by finding class** (Story #4412): the
|
|
883
|
-
review-pillar findings route off `delivery.codeReview.autoFixSeverity`
|
|
884
|
-
(default `medium` — 🔴/🟠/🟡 on-branch, 🟢 stays on the comment), while the
|
|
885
|
-
Epic-close lens findings route off `delivery.epicAudit.autoFixSeverity`
|
|
886
|
-
(default **`high`** — only 🔴/🟠 on-branch; 🟡/🟢 graduate, because 🟡
|
|
887
|
-
Medium concerns were already remediated shift-left). Fixed findings land
|
|
888
|
-
under the comment's `## Fixed on-branch` section so the graduator skips
|
|
889
|
-
them. The severity gate is unchanged: surviving 🔴 Critical findings halt
|
|
890
|
-
the run; surviving 🟠/🟡/🟢 flow through as non-blocking.
|
|
891
|
-
4. **Retro (Phase 6).** `lib/orchestration/retro-runner.js` (extracted from the old
|
|
892
|
-
retro helper) aggregates perf signals, friction counts, hotfix counts,
|
|
893
|
-
recut counts, parked counts, and HITL count using
|
|
894
|
-
`retro-heuristics.js`. The structured retro comment is posted on the
|
|
895
|
-
Epic. The retro fires **before** the PR opens — this keeps it inside
|
|
896
|
-
the operator's local session with full env access (env vars,
|
|
897
|
-
credentials, MCP servers); pushing it after PR-open would deny it
|
|
898
|
-
that access. After the GitHub upsert succeeds, the retro body is
|
|
899
|
-
also **mirrored locally** to the per-Epic temp tree at
|
|
900
|
-
`temp/epic-<id>/retro.md` (path resolved via
|
|
901
|
-
[`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js)'s
|
|
902
|
-
`epicRetroMirrorPath`) so operators can read the retro without
|
|
903
|
-
re-fetching from GitHub. GitHub remains the source of truth; the
|
|
904
|
-
mirror write is best-effort and a failure only logs a warn.
|
|
905
|
-
5. **Finalize (Phase 7).** `/deliver` fires `epic.close.end` via
|
|
906
|
-
`lifecycle-emit.js`; the `AcceptanceReconciler` → `Finalizer`
|
|
907
|
-
listener chain owns every close-time side effect end to end
|
|
908
|
-
(Story #2894 — bus-owned finalize). The chain runs three
|
|
909
|
-
responsibilities in order:
|
|
910
|
-
1. **Acceptance reconciliation.** Invokes
|
|
911
|
-
`acceptance-spec-reconciler.js` to diff the AC IDs declared in
|
|
912
|
-
the Epic body's `## Acceptance Table` managed section
|
|
913
|
-
(Story #4324) against `@epic-<id>-ac-*` / `@pending` tags in
|
|
914
|
-
`tests/features/**`. A non-OK reconciliation throws (per
|
|
915
|
-
`.agents/rules/orchestration-error-handling.md`), aborting
|
|
916
|
-
finalize **before** the PR opens — the Epic blocks until the AC
|
|
917
|
-
coverage gap is fixed. On a clean run the reconciler records
|
|
918
|
-
each row's verification outcome (`satisfied | pending |
|
|
919
|
-
missing`) into the table's Disposition column — a
|
|
920
|
-
section-scoped write that touches only the managed region.
|
|
921
|
-
Skipped (`status: 'waived'`) when the Epic carries
|
|
922
|
-
`acceptance::n-a`.
|
|
923
|
-
2. **PR open (bus-owned, Story #2894).** On
|
|
924
|
-
`acceptance.reconcile.ok`, the `Finalizer` listener invokes
|
|
925
|
-
`openOrLocatePr({ epicId, headBranch: 'epic/<id>', baseBranch:
|
|
926
|
-
'main' })`. The helper probes for an existing open PR on the
|
|
927
|
-
head branch first (idempotent locate path) and only runs
|
|
928
|
-
`gh pr create` when the head branch has no open PR. The
|
|
929
|
-
Finalizer does **not** arm auto-merge — it emits
|
|
930
|
-
`epic.merge.ready` carrying `{ prNumber, epicId, prUrl }` and
|
|
931
|
-
hands off to the auto-merge gate. The sole production caller
|
|
932
|
-
authorised to shell `gh pr merge` in the entire codebase is
|
|
933
|
-
the `AutomergeArmer` listener at the `delivery.automerge` state
|
|
934
|
-
(enforced by the merge-lockout rule in
|
|
935
|
-
`.agents/scripts/check-lifecycle-lint.js`); the
|
|
936
|
-
`delivery.finalize` state never shells the merge command.
|
|
937
|
-
3. **Hand-off (bus-owned, Story #2894).** The `Finalizer` runs
|
|
938
|
-
`postHandoffComment({ epicId, prNumber, prUrl, provider })` to
|
|
939
|
-
upsert the canonical `epic-handoff` structured comment naming
|
|
940
|
-
the PR. (Story #4324 retired the `closePlanningTickets` sweep
|
|
941
|
-
with the context-ticket classes — there are no planning tickets
|
|
942
|
-
to close.) The helper is idempotent — re-running finalize after
|
|
943
|
-
a crash edits the existing handoff comment in place rather than
|
|
944
|
-
appending a duplicate. The Epic stays at `agent::executing`
|
|
945
|
-
until the PR merges.
|
|
946
|
-
6. **Watch-and-iterate (Phase 8).** `/deliver` watches the open PR's
|
|
947
|
-
required checks until they turn green. Transient failures trigger an
|
|
948
|
-
automated re-run loop; durable failures surface for human remediation
|
|
949
|
-
on the Epic branch.
|
|
950
|
-
7. **Auto-merge (Phase 8.5).** Once the watch loop reports all required
|
|
951
|
-
checks passing, the auto-merge gate arms GitHub native auto-merge via
|
|
952
|
-
`gh pr merge --auto --squash --delete-branch` so the PR lands without
|
|
953
|
-
a second operator visit.
|
|
954
|
-
8. **Cleanup (Phase 9).** After the PR merges, the cleanup phase reaps
|
|
955
|
-
local Story/Epic branch refs and any lingering worktrees so the
|
|
956
|
-
workspace returns to a clean state for the next Epic.
|
|
957
|
-
|
|
958
|
-
`/deliver` exits cleanly once auto-merge is armed (or sooner if the
|
|
959
|
-
operator declines auto-merge). The operator can merge through the GitHub UI
|
|
960
|
-
at any time; the `delivery.complete` state handles the post-merge branch reap.
|
|
379
|
+
`single-story-close.js` syncs the Story branch from `origin/main` before
|
|
380
|
+
pushing and opening/locating the PR. Bounded retry constants live in
|
|
381
|
+
`.agents/scripts/lib/config/runners.js` (`DEFAULT_STORY_MERGE_RETRY`:
|
|
382
|
+
3 attempts, `[250, 500, 1000]` ms backoff). A real content conflict aborts
|
|
383
|
+
the loop with a clear error, leaves the tree clean, and exits non-zero for
|
|
384
|
+
manual resolution.
|
|
961
385
|
|
|
962
386
|
---
|
|
963
387
|
|
|
964
388
|
## HITL (Human-in-the-Loop) model
|
|
965
389
|
|
|
966
390
|
On the happy path there is exactly **one** mandatory operator touchpoint
|
|
967
|
-
after `/deliver` fires (blocker resolution). PR merge is autonomous
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
wave N's in-flight stories finish naturally). The operator resolves
|
|
977
|
-
the underlying issue (e.g. a hand-fix commit on the Story branch or a
|
|
978
|
-
scope edit on the blocking ticket), then flips the Epic back to
|
|
391
|
+
after `/deliver` fires (blocker resolution). PR merge is autonomous via
|
|
392
|
+
armed auto-merge; the operator becomes a second touchpoint only by
|
|
393
|
+
exception.
|
|
394
|
+
|
|
395
|
+
1. **Blocker resolution (mandatory when triggered).** If a Story hits an
|
|
396
|
+
unresolvable condition, it flips to `agent::blocked`, posts a structured
|
|
397
|
+
friction comment, and fires the notification webhook (fire-and-forget).
|
|
398
|
+
The operator resolves the underlying issue (a hand-fix commit on the
|
|
399
|
+
Story branch, or a scope edit on the ticket) and flips the Story back to
|
|
979
400
|
`agent::executing` to resume.
|
|
980
401
|
2. **PR merge (autonomous by default; operator-gated by exception).** At
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
`verification-results` comment
|
|
988
|
-
(b) checks fail and need remediation
|
|
989
|
-
no separate close command — the close-out side effects (PR open,
|
|
990
|
-
handoff comment) are owned by `/deliver`'s
|
|
991
|
-
`delivery.finalize` state (the lifecycle Finalizer listener), whose
|
|
992
|
-
replay is idempotent.
|
|
402
|
+
close, `deliver-story` opens a PR to `main` and arms GitHub native
|
|
403
|
+
auto-merge. When required checks pass, the PR lands without a second
|
|
404
|
+
operator visit and the standard label transition flips the Story to
|
|
405
|
+
`agent::done`. The operator becomes a touchpoint only when they (a)
|
|
406
|
+
disarm auto-merge (`--no-auto-merge` per run, or
|
|
407
|
+
`delivery.ci.autoMerge: "strict"`) to inspect checks / the
|
|
408
|
+
`verification-results` comment / the retro before merging by hand, or
|
|
409
|
+
(b) checks fail and need remediation.
|
|
993
410
|
|
|
994
411
|
### What triggers `agent::blocked`
|
|
995
412
|
|
|
996
|
-
- Unresolvable merge conflict
|
|
997
|
-
- Test failures that persist after
|
|
998
|
-
- Ambiguity
|
|
999
|
-
|
|
1000
|
-
- A destructive action not pre-authorized by the ticket body
|
|
1001
|
-
|
|
1002
|
-
- External service failure preventing progress (GitHub API 5xx loop, npm
|
|
413
|
+
- Unresolvable merge conflict automated strategies cannot reconcile.
|
|
414
|
+
- Test failures that persist after automated remediation.
|
|
415
|
+
- Ambiguity requiring a product/scope decision the agent cannot make from
|
|
416
|
+
ticket context alone.
|
|
417
|
+
- A destructive action not pre-authorized by the ticket body.
|
|
418
|
+
- External-service failure preventing progress (GitHub API 5xx loop, npm
|
|
1003
419
|
registry down).
|
|
1004
|
-
-
|
|
420
|
+
- Acceptance self-eval exhausting its bounded round cap with criteria still
|
|
421
|
+
unmet.
|
|
1005
422
|
|
|
1006
423
|
### What is *not* gated at runtime
|
|
1007
424
|
|
|
1008
|
-
- `risk::high`
|
|
1009
|
-
metadata and retro telemetry
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
- Individual story completion — no per-story approval prompt.
|
|
1015
|
-
|
|
1016
|
-
> [!NOTE] Legacy `risk::high` runtime gating has been retired. `risk::high`
|
|
1017
|
-
> remains planning/audit metadata only; the sole runtime pause point is
|
|
1018
|
-
> `agent::blocked`.
|
|
1019
|
-
|
|
1020
|
-
---
|
|
1021
|
-
|
|
1022
|
-
## Epic Deliver Runner internals
|
|
1023
|
-
|
|
1024
|
-
`/deliver` drives the long-running coordinator inside the operator's
|
|
1025
|
-
Claude session. The slash command composes the submodules listed below;
|
|
1026
|
-
`helpers/epic-deliver-story` is launched as an Agent-tool sub-agent of
|
|
1027
|
-
`/deliver`'s wave loop — no subprocess worker sessions for Story
|
|
1028
|
-
execution, no GitHub Actions runner. Deterministic Node CLIs remain the
|
|
1029
|
-
state-mutation contract.
|
|
1030
|
-
|
|
1031
|
-
| Submodule | Role |
|
|
1032
|
-
| ------------------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
1033
|
-
| `wave-scheduler` | Iterates waves from `Graph.computeWaves()`. |
|
|
1034
|
-
| `story-launcher` | Fans out up to `concurrencyCap` Agent-tool Story sub-agents per wave. |
|
|
1035
|
-
| `checkpointer` | Upserts the `epic-run-state` structured comment; handles phase-granular resume across all six phases. |
|
|
1036
|
-
| `blocker-handler` | The sole runtime pause point — halts on `agent::blocked`. |
|
|
1037
|
-
| `notification-hook` | Fire-and-forget webhook for blocker / wave-transition events. |
|
|
1038
|
-
| `wave-observer` | Emits `wave-N-start` / `wave-N-end` comments and reads each Story's `story-run-progress` snapshot. |
|
|
1039
|
-
| `column-sync` | Syncs the Projects v2 Status column from `agent::` labels. |
|
|
1040
|
-
| `code-review` | `lib/orchestration/code-review.js` — `delivery.code-review` inline audit; halts on critical findings. |
|
|
1041
|
-
| `retro-runner` | `lib/orchestration/retro-runner.js` — `delivery.retro` authoring; posts structured retro comment. |
|
|
1042
|
-
|
|
1043
|
-
### Claude Max quota
|
|
1044
|
-
|
|
1045
|
-
`/deliver` consumes Max subscription quota (5-hour rolling window with
|
|
1046
|
-
overage disabled at the org level by default). If a long Epic exceeds the
|
|
1047
|
-
5-hour window, `BlockerHandler` surfaces the rate-limit error as
|
|
1048
|
-
`agent::blocked` so you can resume after the quota rolls.
|
|
1049
|
-
|
|
1050
|
-
### Skipping CI/CD on orchestrator commits
|
|
1051
|
-
|
|
1052
|
-
The orchestrator pushes many commits during a run, each potentially triggering
|
|
1053
|
-
the project's `CI / CD` workflow. Two mitigations:
|
|
1054
|
-
|
|
1055
|
-
- Add `[skip ci]` to orchestrator commit messages (requires a small tweak in
|
|
1056
|
-
`story-close.js`), OR
|
|
1057
|
-
- Add a `paths-ignore` or branch filter to `ci.yml` that excludes `epic/*` and
|
|
1058
|
-
`story-*` branches. Only `main` pushes trigger CI.
|
|
1059
|
-
|
|
1060
|
-
---
|
|
1061
|
-
|
|
1062
|
-
## Phase 4: PR merge (auto by default)
|
|
1063
|
-
|
|
1064
|
-
Once the wave loop, close-validation, code-review, and retro have all
|
|
1065
|
-
completed, `/deliver` opens a pull request from `epic/<epicId>` to
|
|
1066
|
-
`main` and arms GitHub native auto-merge. When the required checks pass
|
|
1067
|
-
the PR lands without further intervention; the operator can disarm
|
|
1068
|
-
auto-merge in the GitHub UI to make the final merge an explicit human
|
|
1069
|
-
action.
|
|
1070
|
-
|
|
1071
|
-
1. **Story merging.** Stories merge into `epic/<epicId>` automatically
|
|
1072
|
-
during Story closure (`story-close.js`). The Epic branch is the rolling
|
|
1073
|
-
integration target.
|
|
1074
|
-
2. **Completion.** Each Story flips to `agent::done` at its own closure
|
|
1075
|
-
(`story-close.js`); the wave loop tracks Epic-level progress as
|
|
1076
|
-
Stories complete. There is no upward auto-cascade — Epics and
|
|
1077
|
-
Tech Specs are never flipped by Story closure; the Epic only flips to
|
|
1078
|
-
`agent::done` when the operator merges the PR to `main`.
|
|
1079
|
-
|
|
1080
|
-
3. **PR merge — the sole promotion gate.** When the PR merges (auto or
|
|
1081
|
-
manual):
|
|
1082
|
-
- the Epic-to-`main` merge lands as a real PR merge with a real
|
|
1083
|
-
reviewer-trail and required-checks history;
|
|
1084
|
-
- the standard label-transition pathway flips the Epic to
|
|
1085
|
-
`agent::done`;
|
|
1086
|
-
- branch cleanup runs out-of-band: the `delivery.complete` state of
|
|
1087
|
-
`/deliver` reaps local refs after the merge; the rare "scrap
|
|
1088
|
-
and reset" case for an unmerged Epic is handled manually.
|
|
1089
|
-
|
|
1090
|
-
If the operator chooses not to merge (rolling back, deferring, re-scoping),
|
|
1091
|
-
`/deliver` has not poisoned `main`. The Epic branch can be amended
|
|
1092
|
-
in place; re-running `/deliver <epicId>` re-runs the
|
|
1093
|
-
`delivery.close-validation` / `delivery.audit` / `delivery.code-review`
|
|
1094
|
-
states against the new HEAD (the evidence wrapper picks up the new SHA) and
|
|
1095
|
-
updates the same PR — no duplicate PRs are opened against the same Epic
|
|
1096
|
-
branch.
|
|
425
|
+
- `risk::high` Stories **run without pause.** The label is planning/audit
|
|
426
|
+
metadata and retro telemetry only; the sole runtime pause point is
|
|
427
|
+
`agent::blocked`. Branch protection on `main` and blocker escalation are
|
|
428
|
+
the runtime defenses for destructive actions.
|
|
429
|
+
- Individual Story completion — no per-Story approval prompt beyond the PR
|
|
430
|
+
merge gate.
|
|
1097
431
|
|
|
1098
432
|
---
|
|
1099
433
|
|
|
1100
434
|
## Testing strategy
|
|
1101
435
|
|
|
1102
436
|
Tests are **pyramid-aware**. Every test written during Story delivery
|
|
1103
|
-
belongs to exactly one tier — **unit**, **contract**, or **e2e /
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
[`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin
|
|
1107
|
-
for the acceptance tier is governed by
|
|
437
|
+
belongs to exactly one tier — **unit**, **contract**, or **e2e /
|
|
438
|
+
acceptance**. The canonical tier definitions, assertion-placement rules,
|
|
439
|
+
and coverage thresholds live in
|
|
440
|
+
[`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin
|
|
441
|
+
authoring for the acceptance tier is governed by
|
|
1108
442
|
[`rules/gherkin-standards.md`](../rules/gherkin-standards.md).
|
|
1109
443
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
[`workflows/helpers/epic-testing.md`](../workflows/helpers/epic-testing.md).
|
|
444
|
+
Write a Story's acceptance criteria in Gherkin-compatible `Given / When /
|
|
445
|
+
Then` form so the acceptance suite can lift them into executable `.feature`
|
|
446
|
+
files.
|
|
1114
447
|
|
|
1115
448
|
### QA workflows: explore, assist, and run-harness
|
|
1116
449
|
|
|
1117
|
-
Three complementary QA workflows sit alongside the automated
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
- **
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
`audit-to-stories`) make the decisions; the agent never re-derives them in
|
|
1140
|
-
prose. A resumed session appends and carries its un-triaged backlog forward
|
|
1141
|
-
as a rolling backlog.
|
|
1142
|
-
- **[`workflows/qa-assist.md`](../workflows/qa-assist.md)** (`/qa-assist`) — the
|
|
1143
|
-
**human-led** sibling of `/qa-explore`: a single-observation
|
|
1144
|
-
**Intake → Enrich → Record** loop. Here the **human drives** — the operator
|
|
1145
|
-
reports one observation they hit (a bug, a flaky behavior, a "this feels
|
|
1146
|
-
off") and the agent enriches it into a triage-ready `QaLedgerItem` (a clean
|
|
1147
|
-
repro, a `file:line` root-cause locus, a coverage verdict), asking clarifying
|
|
1148
|
-
questions when the observation is ambiguous, then appends it — after explicit
|
|
1149
|
-
operator confirmation — to a persistent, resumable rolling session under
|
|
1150
|
-
`temp/qa/`. It produces the **same** ledger contract `/qa-explore` writes
|
|
1151
|
-
(`qa-ledger.schema.json`) and reuses the same `scripts/lib/qa/` and
|
|
1152
|
-
`scripts/lib/findings/` decision seams, so a `/qa-assist` item flows through
|
|
1153
|
-
the identical dedup, classification, and promotion machinery later.
|
|
1154
|
-
- **[`workflows/qa-run.md`](../workflows/qa-run.md)**
|
|
1155
|
-
(`/qa-run`) — the **automated complement**: it steps a *known* set of
|
|
1156
|
-
Gherkin `.feature` scenarios through a real browser, asserting `Then`
|
|
1157
|
-
outcomes semantically against the accessibility snapshot and bundling
|
|
1158
|
-
console/network problems into structured `F#` findings for operator sign-off.
|
|
1159
|
-
|
|
1160
|
-
All three workflows resolve the `qa.*` contract through the single seam
|
|
1161
|
-
[`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js).
|
|
1162
|
-
The block is **optional in the schema** (so config validation never breaks a
|
|
1163
|
-
non-QA consumer) but enforced at run time: the resolver fails **loudly** with
|
|
1164
|
-
"this project has not bound the QA harness" when no `qa` block is present —
|
|
1165
|
-
there is no silent fallback. The contract's four required keys are
|
|
1166
|
-
`qa.featureRoot` (the `.feature` discovery root), `qa.fixturesManifest`
|
|
1167
|
-
(persona → seed-data binding), `qa.environments` (the environment-keyed map —
|
|
1168
|
-
each entry `{ baseUrl, signInSeam, allowWrites? }`, selected per invocation by
|
|
1169
|
-
`resolveQaEnvironment`; the per-environment `signInSeam` is either
|
|
1170
|
-
`{ urlTemplate }` or `{ skill }`), and `qa.personas` (the persona set,
|
|
1171
|
-
authored as a name-only array under a url-template seam or as a per-persona
|
|
1172
|
-
credential/skill map under a skill seam); the two optional keys
|
|
1173
|
-
`qa.consoleAllowlist` and `qa.designTokens` default to `[]` and `null`.
|
|
450
|
+
Three complementary QA workflows sit alongside the automated pyramid, all
|
|
451
|
+
reading the consumer's `qa.*` contract from `.agentrc.json` through
|
|
452
|
+
[`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js)
|
|
453
|
+
(which fails loudly when no `qa` block is bound):
|
|
454
|
+
|
|
455
|
+
- **[`/qa-explore`](../workflows/qa-explore.md)** — an **agent-led**,
|
|
456
|
+
open-ended **Plan → Capture → Triage** exploratory sweep. The operator
|
|
457
|
+
names a surface; the agent drives it (browser MCP by default), recording
|
|
458
|
+
each observation as a `QaLedgerItem`
|
|
459
|
+
([`schemas/qa-ledger.schema.json`](../schemas/qa-ledger.schema.json)) in a
|
|
460
|
+
session ledger under `temp/qa/`. Capture is strictly **read-only**; every
|
|
461
|
+
state-changing action lands in Triage after explicit operator
|
|
462
|
+
confirmation.
|
|
463
|
+
- **[`/qa-assist`](../workflows/qa-assist.md)** — the **human-led** sibling:
|
|
464
|
+
a single-observation **Intake → Enrich → Record** loop. The operator
|
|
465
|
+
reports one observation; the agent enriches it into a triage-ready
|
|
466
|
+
`QaLedgerItem`. Same ledger contract and decision seams as `/qa-explore`.
|
|
467
|
+
- **[`/qa-run`](../workflows/qa-run.md)** — the **automated complement**:
|
|
468
|
+
steps a *known* set of Gherkin `.feature` scenarios through a real
|
|
469
|
+
browser, asserting `Then` outcomes against the accessibility snapshot and
|
|
470
|
+
bundling console/network problems into structured `F#` findings.
|
|
471
|
+
|
|
1174
472
|
Consumer adoption steps are in
|
|
1175
473
|
[`README.md` § Adopting the QA harness](../README.md#adopting-the-qa-harness).
|
|
1176
474
|
|
|
@@ -1178,170 +476,114 @@ Consumer adoption steps are in
|
|
|
1178
476
|
|
|
1179
477
|
## Static analysis & audit orchestration
|
|
1180
478
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
The
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
The three audit-lens tiers above (write-time → Story-scope → Epic-close) form a
|
|
1220
|
-
**shift-left** verification model in which **each lens concern is verified at
|
|
1221
|
-
exactly one tier**, chosen by the lens's `scope` field in `audit-rules.json`
|
|
1222
|
-
(resolved by `resolveLensTier`):
|
|
1223
|
-
|
|
1224
|
-
- **`local`** lenses (decidable from a single Story's diff) are verified at the
|
|
1225
|
-
two innermost tiers — the write-time distilled checklist (Tier 1) and the
|
|
1226
|
-
maker-blind Story-scope pass (Tier 2). They are **not** re-run at Epic close.
|
|
1227
|
-
- **`cumulative`** lenses (only decidable across the Epic's combined diff) and
|
|
1228
|
-
**`global`** lenses (whole-product properties) are verified at Epic close
|
|
1229
|
-
(Tier 3) only.
|
|
1230
|
-
- **Risk-routed** lenses run at Epic close regardless of tier, because a
|
|
1231
|
-
high-risk axis (or a route-adding change set) explicitly demands them.
|
|
1232
|
-
|
|
1233
|
-
The Epic-close roster (`selectEpicCloseLenses`) is deliberately **slim**: it
|
|
1234
|
-
excludes every local-tier change-set lens so the outermost tier — where a fix
|
|
1235
|
-
is most expensive — does not re-verify a concern already covered shift-left.
|
|
1236
|
-
The Epic-close remediation threshold reflects this (`delivery.epicAudit.autoFixSeverity`
|
|
1237
|
-
defaults to `high`): 🟡 Medium code-quality findings are remediated at the
|
|
1238
|
-
innermost tiers, not re-remediated at close.
|
|
1239
|
-
|
|
1240
|
-
### Review & feedback loop
|
|
1241
|
-
|
|
1242
|
-
When audits produce findings, the orchestrator compiles a structured Markdown
|
|
1243
|
-
report and posts it as a ticket comment via the `ITicketingProvider`.
|
|
1244
|
-
|
|
1245
|
-
- **Maintainability ratchet.** The orchestrator enforces code quality by relying
|
|
1246
|
-
on maintainability checks (`check-maintainability.js`), which fail if the
|
|
479
|
+
Audit lenses are woven into delivery as a **shift-left, three-tier**
|
|
480
|
+
verification model in which each lens concern is verified at exactly one
|
|
481
|
+
tier, chosen by the lens's `scope` field in `audit-rules.json` (resolved by
|
|
482
|
+
`resolveLensTier`). There is **no** separate Epic-lifecycle-gate delivery
|
|
483
|
+
pass — the tiers below *are* the audit machinery.
|
|
484
|
+
|
|
485
|
+
| Tier | When | What runs | Blocking? |
|
|
486
|
+
| --- | --- | --- | --- |
|
|
487
|
+
| Tier 1 — write-time | During Story implementation | Footprint-matched **local**-lens authoring checklists threaded into the Story prompt (`checklistPath`) | advisory |
|
|
488
|
+
| Tier 2 — Story-scope | `single-story-close.js` (maker-blind subprocess) | Local-tier lens roster over the Story diff (`selectLocalLenses`) + review pillars, posted as `verification-results` | blocking on 🔴 |
|
|
489
|
+
| Tier 3 — run closeout | `/deliver` per-run epilogue (`plan-run-epilogue.js`, N>1 only) | Cumulative + global + risk-routed lenses (`selectAudits` / `resolveAuditLenses`) over the combined landed tip | blocking |
|
|
490
|
+
|
|
491
|
+
- **`local`** lenses (decidable from a single Story's diff) are verified at
|
|
492
|
+
Tiers 1–2 and are **not** re-run at run closeout.
|
|
493
|
+
- **`cumulative`** lenses (only decidable across a plan-run's combined diff)
|
|
494
|
+
and **`global`** lenses (whole-product properties) are verified at Tier 3.
|
|
495
|
+
- **Risk-routed** lenses run regardless of tier when a high-risk axis (or a
|
|
496
|
+
route-adding change set) demands them.
|
|
497
|
+
|
|
498
|
+
The run-closeout roster is deliberately **slim**: it excludes every
|
|
499
|
+
local-tier change-set lens so the outermost tier — where a fix is most
|
|
500
|
+
expensive — does not re-verify a concern already covered shift-left.
|
|
501
|
+
|
|
502
|
+
### Code review
|
|
503
|
+
|
|
504
|
+
The Story-scope code review runs **outside the maker's context**, inside
|
|
505
|
+
the `single-story-close.js` close subprocess, over `main...story-<id>`
|
|
506
|
+
(see [`helpers/code-review.md`](../workflows/helpers/code-review.md)). It
|
|
507
|
+
walks the Story diff once, executing the risk-routed lens roster as review
|
|
508
|
+
dimensions alongside the review pillars, and posts the unified
|
|
509
|
+
`verification-results` comment. Remediation is tier-aware and split by
|
|
510
|
+
finding class off `delivery.codeReview.autoFixSeverity` (default `medium`);
|
|
511
|
+
surviving 🔴 Critical findings halt the run. The legacy `scope: epic`
|
|
512
|
+
Epic-branch review path was removed with the v2 cutover.
|
|
513
|
+
|
|
514
|
+
### Quality ratchets
|
|
515
|
+
|
|
516
|
+
- **Maintainability ratchet** (`check-maintainability.js`) — fails if the
|
|
1247
517
|
composite score drops below the established baseline.
|
|
1248
|
-
- **CRAP gate
|
|
1249
|
-
into
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
`
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
-
|
|
1261
|
-
|
|
1262
|
-
implement and verify them.
|
|
518
|
+
- **CRAP gate** (`check-crap.js`) — per-method complexity × coverage risk
|
|
519
|
+
against `baselines/crap.json`, wired into close-validation, `ci.yml`, and
|
|
520
|
+
`.husky/pre-push`. The `baseline-refresh:`-tagged commit convention is the
|
|
521
|
+
project standard for baseline edits (see
|
|
522
|
+
[`core/gates-and-baselines`](../skills/core/gates-and-baselines/SKILL.md)).
|
|
523
|
+
|
|
524
|
+
### Audits → Stories
|
|
525
|
+
|
|
526
|
+
The standalone `/audit-<dimension>` workflows are read-only emitters that
|
|
527
|
+
write `audit-<dimension>-results.md` under `temp/audits/`.
|
|
528
|
+
[`/audit-to-stories`](../workflows/audit-to-stories.md) parses those
|
|
529
|
+
reports, groups and deduplicates findings, and hands off to
|
|
530
|
+
`/plan --seed-file` (or opens standalone Stories) — closing the loop back
|
|
531
|
+
into planning.
|
|
1263
532
|
|
|
1264
533
|
---
|
|
1265
534
|
|
|
1266
535
|
## Notification system
|
|
1267
536
|
|
|
1268
|
-
Two independent notification surfaces, both living in `.agents/` so they
|
|
1269
|
-
consuming projects
|
|
537
|
+
Two independent notification surfaces, both living in `.agents/` so they
|
|
538
|
+
ship to consuming projects.
|
|
1270
539
|
|
|
1271
540
|
### 1. Unified `notify()` dispatcher
|
|
1272
541
|
|
|
1273
|
-
Every notification — whether a manual orchestration milestone (
|
|
1274
|
-
HITL gate triggered) or an auto-fired ticket-state transition —
|
|
1275
|
-
[`notify.js`](../scripts/notify.js). Two delivery channels:
|
|
542
|
+
Every notification — whether a manual orchestration milestone (Story
|
|
543
|
+
merged, HITL gate triggered) or an auto-fired ticket-state transition —
|
|
544
|
+
routes through [`notify.js`](../scripts/notify.js). Two delivery channels:
|
|
1276
545
|
|
|
1277
|
-
| Channel
|
|
1278
|
-
|
|
|
1279
|
-
| GitHub comment
|
|
1280
|
-
| Webhook
|
|
546
|
+
| Channel | What it does |
|
|
547
|
+
| --- | --- |
|
|
548
|
+
| GitHub comment | Posts to the targeted ticket; @mentions the operator for `medium`/`high`. |
|
|
549
|
+
| Webhook | Fire-and-forget POST to the configured URL (Make.com / Slack / Discord). |
|
|
1281
550
|
|
|
1282
|
-
Severity vocabulary (
|
|
1283
|
-
`lib/notifications/notifier.js` derives it for state transitions):
|
|
551
|
+
Severity vocabulary (`eventSeverity()` derives it for state transitions):
|
|
1284
552
|
|
|
1285
|
-
| Severity | Used for
|
|
1286
|
-
|
|
|
1287
|
-
| `low`
|
|
1288
|
-
| `medium` | Operator-visible milestones: Story state transitions,
|
|
1289
|
-
| `high`
|
|
553
|
+
| Severity | Used for | Webhook prefix |
|
|
554
|
+
| --- | --- | --- |
|
|
555
|
+
| `low` | `story-run-progress` upserts, intermediate transitions, audit reports. | `[low]` |
|
|
556
|
+
| `medium` | Operator-visible milestones: Story state transitions, story merged, run complete. | `[medium]` |
|
|
557
|
+
| `high` | Operator must act (HITL gates, Story blockers, autonomous-chain failures); body leads with `🚨 Action Required:`. | `[Action Required]` |
|
|
1290
558
|
|
|
1291
|
-
Two independent event-allowlist knobs in `github.notifications`
|
|
1292
|
-
|
|
559
|
+
Two independent event-allowlist knobs in `github.notifications` (both
|
|
560
|
+
mandatory) filter each channel independently — there is no fallback chain:
|
|
1293
561
|
|
|
1294
|
-
- `commentEvents` —
|
|
1295
|
-
posting. Default:
|
|
562
|
+
- `commentEvents` — allowlist for GitHub-ticket comment posting. Default:
|
|
1296
563
|
`["state-transition", "story-merged", "operator-message"]`.
|
|
1297
|
-
- `webhookEvents` —
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
`
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
changes from:
|
|
1321
|
-
|
|
1322
|
-
- The Epic Deliver Runner (coordinator-driven state flips).
|
|
1323
|
-
- Per-story scripts (`story-init.js`, `story-close.js`).
|
|
1324
|
-
- Any script that routes state changes through `transitionTicketState`.
|
|
1325
|
-
|
|
1326
|
-
It does **not** capture manual label clicks in the GitHub UI (no webhook
|
|
1327
|
-
receiver). For programmatic orchestration workflows this covers >95% of
|
|
1328
|
-
lifecycle transitions.
|
|
1329
|
-
|
|
1330
|
-
### 2. Deliver-runner blocker / HITL notifications
|
|
1331
|
-
|
|
1332
|
-
The `NotificationHook` inside the Epic Deliver Runner fires on
|
|
1333
|
-
blocker-escalation events (`agent::blocked`) and operator-attention events
|
|
1334
|
-
(PR-open hand-off, run cancellation). Fire-and-forget by design; webhook
|
|
1335
|
-
failures never block execution.
|
|
1336
|
-
|
|
1337
|
-
| Event | Type | Channel | Operator Action |
|
|
1338
|
-
| ------------------ | ---------- | ------------------ | ---------------------- |
|
|
1339
|
-
| `task-complete` | **INFO** | @mention | Review when convenient |
|
|
1340
|
-
| `feature-complete` | **INFO** | @mention | Informational only |
|
|
1341
|
-
| `epic-complete` | **INFO** | @mention + webhook | Final review |
|
|
1342
|
-
| `pr-opened` | **ACTION** | @mention + webhook | Inspect checks + merge |
|
|
1343
|
-
| `epic-blocked` | **ACTION** | webhook | Resolve and re-flip |
|
|
1344
|
-
| `wave-transition` | **INFO** | webhook | Informational only |
|
|
564
|
+
- `webhookEvents` — allowlist for `NOTIFICATION_WEBHOOK_URL` deliveries.
|
|
565
|
+
|
|
566
|
+
`transitionTicketState` suppresses the `notify()` dispatch for low-severity
|
|
567
|
+
transitions so the comment channel sees only the medium-severity
|
|
568
|
+
Story-level events operators expect. To suppress a channel entirely, set
|
|
569
|
+
its array to `[]`.
|
|
570
|
+
|
|
571
|
+
**Webhook URL resolution.** `NOTIFICATION_WEBHOOK_URL` process env var only
|
|
572
|
+
— loaded from `.env` at the project root. It is **not** sourced from
|
|
573
|
+
`.agentrc.json` or `.mcp.json`.
|
|
574
|
+
|
|
575
|
+
Because `notify()` is called in-band from the orchestration SDK, it
|
|
576
|
+
captures state changes from `deliver-story`, the per-Story scripts
|
|
577
|
+
(`single-story-init.js`, `single-story-close.js`,
|
|
578
|
+
`single-story-confirm-merge.js`), and any script that routes through
|
|
579
|
+
`transitionTicketState`. It does **not** capture manual label clicks in the
|
|
580
|
+
GitHub UI.
|
|
581
|
+
|
|
582
|
+
### 2. Blocker / HITL notifications
|
|
583
|
+
|
|
584
|
+
Fire-and-forget webhooks fire on blocker-escalation events
|
|
585
|
+
(`agent::blocked`) and operator-attention events (PR-open hand-off, run
|
|
586
|
+
cancellation). Webhook failures never block execution.
|
|
1345
587
|
|
|
1346
588
|
---
|
|
1347
589
|
|
|
@@ -1349,106 +591,68 @@ failures never block execution.
|
|
|
1349
591
|
|
|
1350
592
|
### Sub-agent CI workflow editing
|
|
1351
593
|
|
|
1352
|
-
Sub-agents
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
with an error of the shape:
|
|
594
|
+
Sub-agents operating under the framework's default `GITHUB_TOKEN` **cannot
|
|
595
|
+
edit files under `.github/workflows/**`** — the token does not carry the
|
|
596
|
+
`workflows` permission scope, so a push touching a workflow file is
|
|
597
|
+
rejected with:
|
|
1357
598
|
|
|
1358
599
|
> refusing to allow a GitHub App to create or update workflow
|
|
1359
600
|
> `.github/workflows/<file>.yml` without `workflows` permission
|
|
1360
601
|
|
|
1361
|
-
This is a hard constraint, not a transient failure.
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
-
|
|
1369
|
-
`npm test` so an existing CI job picks it up by transitivity.
|
|
1370
|
-
- Wire a new `package.json` script and chain it from one of the
|
|
1371
|
-
existing scripts the workflow already invokes.
|
|
1372
|
-
- For a check that genuinely cannot be expressed as an npm script,
|
|
1373
|
-
surface it as a script anyway (e.g. `npm run check:<name>` →
|
|
1374
|
-
`node .agents/scripts/<name>.js`) and call the script from the
|
|
1375
|
-
existing `Validate and Test` job's `run:` block — but the YAML edit
|
|
1376
|
-
itself must be made by an operator.
|
|
1377
|
-
|
|
1378
|
-
Precedent: Epic #2880 Story #2895 Task #2916 intended to add
|
|
1379
|
-
`check-lifecycle-doc-drift.js` directly to `.github/workflows/ci.yml`,
|
|
1380
|
-
hit this constraint, and worked around it by chaining the check into
|
|
1381
|
-
`npm run docs:check`. The functional outcome is identical; the
|
|
1382
|
-
workaround is the canonical pattern.
|
|
1383
|
-
|
|
1384
|
-
**When a workflow file genuinely must change** (a new top-level job, a
|
|
1385
|
-
trigger change, a runner-image bump, etc.), the edit must be made by an
|
|
1386
|
-
operator with `Workflows: Read and write` PAT permissions. See
|
|
1387
|
-
[§ One-time PAT setup](../../AGENTS.md#one-time-pat-setup) in the root
|
|
1388
|
-
`AGENTS.md` for how to provision a PAT with the required scope. The same
|
|
1389
|
-
operator surface that release-please relies on is the one that authorizes
|
|
1390
|
-
workflow edits.
|
|
602
|
+
This is a hard constraint, not a transient failure. **When a Story plans a
|
|
603
|
+
new CI gate**, route the check through a `package.json` script (add it to
|
|
604
|
+
`npm run lint` / `npm run docs:check` / `npm test`, or wire a new
|
|
605
|
+
`npm run check:<name>` script) so an existing CI job picks it up by
|
|
606
|
+
transitivity. **When a workflow file genuinely must change** (a new job, a
|
|
607
|
+
trigger change, a runner bump), the edit must be made by an operator with
|
|
608
|
+
`Workflows: Read and write` PAT permissions — see
|
|
609
|
+
[`docs/release-operations.md` § One-time PAT setup](../../docs/release-operations.md#one-time-pat-setup).
|
|
1391
610
|
|
|
1392
611
|
### Worktree config shadow
|
|
1393
612
|
|
|
1394
|
-
`helpers/
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
`.agentrc.json
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
1. **Prefer an env-var override** when the knob exposes one (e.g.
|
|
1413
|
-
timeouts, log level via `AGENT_LOG_LEVEL`, concurrency caps). Env
|
|
1414
|
-
vars are read from the operator's actual shell, not from the
|
|
1415
|
-
checked-out config, so they bypass worktree shadow entirely.
|
|
1416
|
-
2. **Edit the file inside the worktree** —
|
|
1417
|
-
`.worktrees/<story-id>/.agentrc.json` — so the script sees the bump
|
|
1418
|
-
on its next read. Either commit the change on the Story branch (if
|
|
1419
|
-
the bump is project-wide and should land with the Story) or leave
|
|
1420
|
-
it uncommitted as a scratch tweak that gets discarded when the
|
|
1421
|
-
worktree is reaped.
|
|
1422
|
-
3. **Use `.agentrc.local.json`** for per-machine tuning you never want
|
|
1423
|
-
to commit. The file is gitignored and layered on top of
|
|
1424
|
-
`.agentrc.json` by the config resolver
|
|
1425
|
-
(see [`.agents/docs/configuration.md`](configuration.md#per-machine-local-overrides)).
|
|
1426
|
-
Note: the local override is still read relative to the script's
|
|
1427
|
-
cwd, so for worktree-bound scripts you must place
|
|
1428
|
-
`.agentrc.local.json` inside the worktree directory — or invoke the
|
|
1429
|
-
script with `--cwd <main-repo>` so the resolver reads from the main
|
|
1430
|
-
checkout's local override.
|
|
613
|
+
`helpers/deliver-story` runs inside per-Story worktrees under
|
|
614
|
+
`.worktrees/story-<id>/`. A worktree checks out the **Story branch's own
|
|
615
|
+
copy** of every repo-tracked file — including `.agentrc.json`. **Operator
|
|
616
|
+
edits made in the main checkout do NOT propagate to an already-active
|
|
617
|
+
worktree.** Symptom: you bump a runtime knob in `<main-repo>/.agentrc.json`,
|
|
618
|
+
re-run `single-story-close.js --cwd <worktree>`, and the script still uses
|
|
619
|
+
the old value. When tuning knobs mid-Story:
|
|
620
|
+
|
|
621
|
+
1. **Prefer an env-var override** when the knob exposes one (timeouts,
|
|
622
|
+
`AGENT_LOG_LEVEL`, concurrency caps) — env vars are read from your shell,
|
|
623
|
+
bypassing worktree shadow entirely.
|
|
624
|
+
2. **Edit the file inside the worktree** (`.worktrees/story-<id>/.agentrc.json`)
|
|
625
|
+
so the script sees the bump on its next read.
|
|
626
|
+
3. **Use `.agentrc.local.json`** for per-machine tuning you never commit
|
|
627
|
+
(see
|
|
628
|
+
[`configuration.md`](configuration.md#per-machine-local-overrides)) —
|
|
629
|
+
place it inside the worktree, or invoke the script with
|
|
630
|
+
`--cwd <main-repo>` so the resolver reads the main checkout's override.
|
|
1431
631
|
|
|
1432
632
|
Editing the main checkout's `.agentrc.json` only affects **the next**
|
|
1433
|
-
`story-init.js` invocation, because new Story branches fork from
|
|
1434
|
-
`main`'s current tip
|
|
1435
|
-
|
|
633
|
+
`single-story-init.js` invocation, because new Story branches fork from
|
|
634
|
+
`main`'s current tip.
|
|
635
|
+
|
|
636
|
+
### `Epic: #N` refusal
|
|
637
|
+
|
|
638
|
+
`/deliver` refuses any ticket that still carries an `Epic: #N` footer or is
|
|
639
|
+
not `type::story`. This is expected — v2 has no Epic delivery path. Close
|
|
640
|
+
the ticket or re-plan the work as a v2 Story via `/plan --tickets <id>`.
|
|
1436
641
|
|
|
1437
642
|
---
|
|
1438
643
|
|
|
1439
644
|
## Quick reference
|
|
1440
645
|
|
|
1441
|
-
| Command
|
|
1442
|
-
|
|
|
1443
|
-
| `npx mandrel init`
|
|
1444
|
-
| `/plan`
|
|
1445
|
-
| `/plan --
|
|
1446
|
-
| `/plan <
|
|
1447
|
-
| `/deliver <
|
|
1448
|
-
| `/deliver <storyId> [<storyId
|
|
1449
|
-
| `/
|
|
1450
|
-
| *helper* `
|
|
1451
|
-
|
|
|
1452
|
-
|
|
|
1453
|
-
| `/git-deliver`
|
|
1454
|
-
| `epic-reconcile.js --explicit-delete` | Hard reset — close orphaned Epic-scoped issues per `.agents/epics/<id>.yaml` |
|
|
646
|
+
| Command | Purpose |
|
|
647
|
+
| --- | --- |
|
|
648
|
+
| `npx mandrel init` | Cold-start — install `mandrel` (if absent), `mandrel sync`, `bootstrap.js` (provisions repo + Projects V2 board, labels, branch protection), then the onboarding tail (stack detection, docs scaffolding, doctor gate, `/plan` handoff). |
|
|
649
|
+
| `/plan --seed "<text>"` | Plan from chat text — interrogate → author **one Story by default** → persist `type::story`. |
|
|
650
|
+
| `/plan --seed-file <path>` | Plan from on-disk notes / a plan seed (the `/audit-to-stories` handoff). |
|
|
651
|
+
| `/plan --tickets <ids>` | Analyze existing issue(s) into proper Stories (prefer an N=1 rewrite). |
|
|
652
|
+
| `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story` — `story-<id>` → PR → `main`. |
|
|
653
|
+
| `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order; each lands through its own PR. |
|
|
654
|
+
| `/deliver --run <planRunId>` | Resolve Stories labeled `plan-run::<id>`, sequence them, and run the per-run epilogue. |
|
|
655
|
+
| *helper* `helpers/deliver-story` | Per-Story engine invoked by `/deliver`; not an operator slash command. See [`deliver-story.md`](../workflows/helpers/deliver-story.md). |
|
|
656
|
+
| `/audit-to-stories` | Convert audit findings into a plan seed / Stories → `/plan --seed-file`. |
|
|
657
|
+
| `/qa-explore` · `/qa-assist` · `/qa-run` | Agent-led / human-led exploratory QA and the automated Gherkin harness. |
|
|
658
|
+
| `/git-deliver` | Ad-hoc delivery of working-tree changes — detects the git setup and escalates to commit, commit + push, or commit + push + PR (auto-merge armed). |
|