mandrel 1.94.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +22 -66
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +328 -28
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +26 -24
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +17 -19
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +86 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +77 -32
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +44 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +3 -53
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +12 -8
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -219
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -270
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -33
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- package/.agents/workflows/helpers/single-story-deliver.md +0 -557
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 ordered by `depends_on` edges;
|
|
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,143 @@ 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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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`. There is no risk artifact
|
|
53
|
+
to author (Story #4542).
|
|
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** (raised only by an explicit `--force-review`)
|
|
58
|
+
then `plan-persist.js` runs every deterministic gate and
|
|
59
|
+
creates Story issue(s) with `type::story` + `agent::ready`, writing
|
|
60
|
+
each authored `depends_on` edge into the sibling body as a
|
|
61
|
+
`blocked by #<id>` footer when N>1.
|
|
62
|
+
|
|
63
|
+
2. **Deliver the Story.** Run [`/deliver <storyId>`](../workflows/deliver.md)
|
|
64
|
+
(or `/deliver <a> <b> …` for several) in your IDE. `/deliver` takes
|
|
65
|
+
only Story ids and resolves their dependency graph from live state —
|
|
66
|
+
body edges union native GitHub `blocked_by` edges, with every blocker
|
|
67
|
+
checked against its real issue state, so a Story whose blocker landed in
|
|
68
|
+
an earlier plan run is simply ready. `/deliver` owns input resolution and
|
|
69
|
+
`depends_on` sequencing only — every Story runs through
|
|
70
|
+
[`helpers/deliver-story`](../workflows/helpers/deliver-story.md), the
|
|
71
|
+
single v2 delivery engine. Per-Story it:
|
|
72
|
+
|
|
73
|
+
1. **Init** (`single-story-init.js`) — acquires the Story lease, cuts
|
|
74
|
+
`story-<id>` from `main`, materializes a worktree, flips to
|
|
75
|
+
`agent::executing`.
|
|
76
|
+
2. **Implement** — the agent delivers the Story in one guarded session
|
|
77
|
+
against its inline `acceptance[]` / `verify[]` contract (optional
|
|
78
|
+
`## Slicing` intra-session checkpoints).
|
|
79
|
+
3. **Acceptance self-eval** — a bounded critic loop scores the working
|
|
80
|
+
diff against each acceptance item before close (see
|
|
81
|
+
[`helpers/acceptance-self-eval`](../workflows/helpers/acceptance-self-eval.md)).
|
|
82
|
+
4. **Ceremony** — acceptance critic mode and review depth, both routed off
|
|
83
|
+
the change level derived from the Story's own diff
|
|
84
|
+
(`review-depth.js#deriveChangeLevel` → `ceremony-routing.js`).
|
|
85
|
+
5. **Close** (`single-story-close.js`) — runs close-validation gates,
|
|
86
|
+
the maker-blind Story-scope code review, pushes `story-<id>`, opens
|
|
87
|
+
a PR to `main`, and (under the default `delivery.ci.autoMerge:
|
|
88
|
+
"trust-ci"`) arms GitHub native auto-merge. The Story flips to
|
|
89
|
+
`agent::closing` (issue stays OPEN).
|
|
90
|
+
6. **CI watch + fix** — watches required checks to green, fixing and
|
|
91
|
+
re-pushing on red.
|
|
92
|
+
7. **Confirm merge** (`single-story-confirm-merge.js`) — on a confirmed
|
|
93
|
+
`MERGED` PR the Story flips to `agent::done`; local branch cleanup
|
|
94
|
+
and Projects-v2 Status re-assert run out-of-band.
|
|
95
|
+
|
|
96
|
+
For a multi-Story run, `/deliver` sequences ready Stories by
|
|
97
|
+
`depends_on` and runs the per-run epilogue (audit roster · follow-up
|
|
98
|
+
roll-up · sibling coherence) once after the last Story lands.
|
|
131
99
|
|
|
132
100
|
That is the whole happy path. Everything below is **detail** — branching
|
|
133
|
-
conventions, HITL escalation, audit
|
|
134
|
-
default flow requires adjustment.
|
|
101
|
+
conventions, HITL escalation, audit lenses — that you only need when the
|
|
102
|
+
default flow requires adjustment. It intentionally **links** to
|
|
103
|
+
[`plan.md`](../workflows/plan.md) and [`deliver.md`](../workflows/deliver.md)
|
|
104
|
+
rather than re-documenting the ceremony they own.
|
|
135
105
|
|
|
136
106
|
---
|
|
137
107
|
|
|
138
108
|
## Core Principles
|
|
139
109
|
|
|
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.
|
|
110
|
+
- **Layered state stores with explicit precedence.** Ticket status lives
|
|
111
|
+
in GitHub Issues and Labels; the lifecycle bus
|
|
112
|
+
(`temp/run-<id>/lifecycle.ndjson`) is the canonical resume target for
|
|
113
|
+
in-flight runs; structured comments (`verification-results`, retro) are
|
|
114
|
+
the operator-visible rollup. The
|
|
115
|
+
stores, their owners, and their conflict-resolution rules are listed in
|
|
116
|
+
[§ State stores](#state-stores) — that matrix is the single source of
|
|
117
|
+
truth for "who owns which write."
|
|
118
|
+
- **Provider Abstraction.** Orchestration flows through
|
|
119
|
+
`ITicketingProvider`, an abstract interface with a shipped GitHub
|
|
120
|
+
implementation.
|
|
121
|
+
- **Story-level branching.** All work for a Story lands on the shared
|
|
122
|
+
`story-<id>` branch. Each Story reaches `main` through its own PR
|
|
123
|
+
(squash + required checks); there is **no** `epic/<id>` integration
|
|
124
|
+
branch and **no** `--no-ff` wave merge.
|
|
125
|
+
- **One delivery engine.** `/deliver` resolves and sequences a Story set;
|
|
126
|
+
`helpers/deliver-story` executes each Story identically (trivial or
|
|
127
|
+
large). Story sub-agents run inside the operator's Claude session via
|
|
128
|
+
the Agent tool — worktree filesystem isolation is preserved; only the
|
|
129
|
+
subprocess boundary is gone.
|
|
130
|
+
- **PR is the sole promotion gate.** Delivery ends with a PR open against
|
|
131
|
+
`main` and (by default) GitHub native auto-merge armed; the workflow
|
|
132
|
+
itself never executes `git merge` against `main`. Branch protection on
|
|
133
|
+
`main` enforces required checks before the merge button (auto or
|
|
134
|
+
manual) fires.
|
|
135
|
+
- **HITL-minimal by default.** Exactly one mandatory operator touchpoint
|
|
136
|
+
on the happy path — blocker resolution mid-run. PR merge is autonomous
|
|
137
|
+
via the armed auto-merge; the operator becomes a second touchpoint only
|
|
138
|
+
when they disarm auto-merge (`--no-auto-merge` / `delivery.ci.autoMerge:
|
|
139
|
+
"strict"`) or when required checks fail and need remediation.
|
|
178
140
|
|
|
179
141
|
---
|
|
180
142
|
|
|
181
143
|
## State stores
|
|
182
144
|
|
|
183
|
-
Mandrel writes orchestration state across
|
|
184
|
-
store has
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
|
193
|
-
|
|
|
194
|
-
| `
|
|
195
|
-
| `
|
|
196
|
-
| Lifecycle ledger NDJSON
|
|
197
|
-
| Validation evidence cache
|
|
198
|
-
| PR / auto-merge state
|
|
199
|
-
| Worktree cleanup state
|
|
145
|
+
Mandrel writes orchestration state across several distinct stores. Each
|
|
146
|
+
store has one canonical writer and one well-defined idempotency key;
|
|
147
|
+
conflicts are resolved in the **Conflict resolution** column. Run-scoped
|
|
148
|
+
artifacts live under `temp/run-<id>/` (standalone Stories under
|
|
149
|
+
`temp/standalone/stories/story-<id>/`); the `run-<id>` directory naming is
|
|
150
|
+
historical (it predates the Story-centric cutover) but remains the live
|
|
151
|
+
on-disk layout resolved by
|
|
152
|
+
[`lib/config/temp-paths.js`](../scripts/lib/config/temp-paths.js).
|
|
153
|
+
|
|
154
|
+
| State Store | Owner (canonical writer) | Mutation API | Idempotency key | Conflict resolution |
|
|
155
|
+
| --- | --- | --- | --- | --- |
|
|
156
|
+
| 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. |
|
|
157
|
+
| `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. |
|
|
158
|
+
| Lifecycle ledger NDJSON | `LedgerWriter` (`lib/orchestration/lifecycle/ledger-writer.js`, registered as the first listener on every bus event — 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. |
|
|
159
|
+
| 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. |
|
|
160
|
+
| 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. |
|
|
161
|
+
| 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. |
|
|
162
|
+
|
|
163
|
+
> The `gh pr merge` merge-lockout lint rule keeps the merge command
|
|
164
|
+
> confined to the sanctioned close path; no other production caller may
|
|
165
|
+
> shell it.
|
|
200
166
|
|
|
201
167
|
---
|
|
202
168
|
|
|
@@ -210,48 +176,42 @@ graph LR
|
|
|
210
176
|
|
|
211
177
|
subgraph Phase0 ["Phase 0: Bootstrap"]
|
|
212
178
|
direction TB
|
|
213
|
-
Z["👤 npx mandrel init<br/>(install → sync →
|
|
179
|
+
Z["👤 npx mandrel init<br/>(install → sync → bootstrap.js → onboarding tail → /plan handoff)"]:::manual
|
|
214
180
|
end
|
|
215
181
|
|
|
216
|
-
subgraph Phase1 ["Phase 1:
|
|
182
|
+
subgraph Phase1 ["Phase 1: Plan"]
|
|
217
183
|
direction TB
|
|
218
|
-
A["👤 /plan
|
|
184
|
+
A["👤 /plan --seed | --seed-file | --tickets"]:::manual
|
|
185
|
+
B["🤖 interrogate → author → persist"]:::agentic
|
|
186
|
+
A --> B
|
|
187
|
+
B -.-> B_Art["📄 type::story issue(s)<br/>(+ depends_on edges)"]:::artifact
|
|
219
188
|
end
|
|
220
189
|
|
|
221
|
-
subgraph Phase2 ["Phase 2:
|
|
190
|
+
subgraph Phase2 ["Phase 2: Deliver"]
|
|
222
191
|
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
|
|
192
|
+
E["👤 /deliver <storyId> [<storyId>…]"]:::manual
|
|
193
|
+
F["🤖 deliver-story: story-<id> from main<br/>implement → self-eval → ceremony → close"]:::agentic
|
|
194
|
+
G["🤖 close-validation → code-review → open PR"]:::agentic
|
|
234
195
|
E --> F --> G
|
|
235
196
|
G -.-> G_Art["📄 PR open against main"]:::artifact
|
|
236
197
|
end
|
|
237
198
|
|
|
238
|
-
subgraph
|
|
199
|
+
subgraph Phase3 ["Phase 3: PR merge (auto by default)"]
|
|
239
200
|
direction TB
|
|
240
201
|
H["🤖 Auto-merge armed → PR lands when checks pass<br/>(👤 operator may disarm to merge manually)"]:::agentic
|
|
241
202
|
end
|
|
242
203
|
|
|
243
204
|
Z --> A
|
|
244
|
-
|
|
245
|
-
D --> E
|
|
205
|
+
B --> E
|
|
246
206
|
G --> H
|
|
247
207
|
```
|
|
248
208
|
|
|
249
209
|
---
|
|
250
210
|
|
|
251
|
-
## Phase 0: Bootstrap (
|
|
211
|
+
## Phase 0: Bootstrap (one-time setup)
|
|
252
212
|
|
|
253
|
-
Before any
|
|
254
|
-
|
|
213
|
+
Before any workflow, bootstrap your project to seed `.agentrc.json`, wire
|
|
214
|
+
the framework system prompt, and create the GitHub labels, Projects V2
|
|
255
215
|
fields, and (when enabled) main-branch protection the orchestration engine
|
|
256
216
|
depends on.
|
|
257
217
|
|
|
@@ -261,916 +221,256 @@ The canonical cold-start path is a single command:
|
|
|
261
221
|
npx mandrel init
|
|
262
222
|
```
|
|
263
223
|
|
|
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
|
-
|
|
224
|
+
`mandrel init` installs `mandrel` (when `./.agents/` is absent),
|
|
225
|
+
materializes `./.agents/` via `mandrel sync`, then presents a two-option
|
|
226
|
+
prompt: **configure now** (option 1 → runs `node
|
|
227
|
+
.agents/scripts/bootstrap.js`, forwarding any flags you pass) or **just
|
|
228
|
+
the files** (option 2 → re-run `mandrel init` any time to configure
|
|
229
|
+
later). `--assume-yes` skips the prompt and proceeds straight to configure;
|
|
230
|
+
a non-TTY run without it defaults to files-only so GitHub provisioning
|
|
231
|
+
never runs unattended. `bootstrap.js`:
|
|
232
|
+
|
|
233
|
+
1. **Provisions a cold start.** Initializes the local git repo (with a
|
|
234
|
+
first commit) when absent, creates the GitHub repo (`gh repo create
|
|
235
|
+
--source=. --push`; choose visibility with `--visibility
|
|
236
|
+
private|public|internal`, default `private`), and creates the Projects
|
|
237
|
+
V2 board (`gh project create`) when it doesn't exist. No pre-created
|
|
238
|
+
repo or remote is required.
|
|
239
|
+
2. **Seeds `.agentrc.json`** from `.agents/starter-agentrc.json` (the
|
|
240
|
+
`github` section carries owner, repo, base branch, operator handle, and
|
|
241
|
+
project number — inferred from your local `git` config where possible).
|
|
242
|
+
See `.agents/docs/agentrc-reference.json` for the exhaustive key
|
|
243
|
+
reference.
|
|
282
244
|
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
|
|
245
|
+
`github.branchProtection.enforce` is `true` (default) — creates or
|
|
246
|
+
merges branch protection on `main` with the project's
|
|
247
|
+
`github.branchProtection.requiredChecks` as required status checks.
|
|
248
|
+
This step is load-bearing because PR merges to `main` are the sole
|
|
287
249
|
promotion gate.
|
|
288
250
|
|
|
289
251
|
When `.agents/` is already materialized you can run the bootstrap directly
|
|
290
252
|
(`node .agents/scripts/bootstrap.js`). The guided first-run steps (stack
|
|
291
253
|
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.
|
|
254
|
+
handoff) are part of `mandrel init`'s configure path.
|
|
295
255
|
|
|
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.
|
|
256
|
+
> [!NOTE] Bootstrap runs once per repository. It is safe to re-run —
|
|
257
|
+
> existing labels, fields, and branch-protection entries are preserved;
|
|
258
|
+
> missing ones are added.
|
|
299
259
|
|
|
300
260
|
---
|
|
301
261
|
|
|
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
|
-
The `/plan` Epic path's interrogate step runs the
|
|
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.
|
|
262
|
+
## Phase 1: Planning
|
|
263
|
+
|
|
264
|
+
Planning is owned end-to-end by [`/plan`](../workflows/plan.md). Rather than
|
|
265
|
+
re-document the ceremony here, this section states the contract the rest of
|
|
266
|
+
the SDLC depends on:
|
|
267
|
+
|
|
268
|
+
- **Entry is text or tickets, never Epic.** The only accepted invocations
|
|
269
|
+
are `--seed`, `--seed-file`, and `--tickets`. There is no `--idea`, no
|
|
270
|
+
`--one-pager`, no `--from-notes`, and no positional `/plan <epicId>`.
|
|
271
|
+
- **One Story by default.** `/plan` authors a single `type::story` issue
|
|
272
|
+
whose body carries a folded `## Spec` (inline only — never spilled to
|
|
273
|
+
`docs/`) plus top-level `acceptance[]` / `verify[]`. It splits into N>1
|
|
274
|
+
siblings (ordered by `depends_on` edges) **only**
|
|
275
|
+
under the default-single split policy: near-zero overlap or a genuine
|
|
276
|
+
architectural seam. Coupled work stays one Story and is decomposed inside
|
|
277
|
+
`## Slicing` as intra-session checkpoints, not sibling tickets.
|
|
278
|
+
- **No Epic-scale ceremony on the default path.** N=1 skips the
|
|
279
|
+
Epic-era Tech Spec / Acceptance Table / clarity-gate / decompose /
|
|
280
|
+
reconciler machinery. `plan-persist.js` runs the deterministic gates
|
|
281
|
+
(ticket validator, split policy, reachability, budget) and — for N>1 —
|
|
282
|
+
`assertAcceptancePartition` so every acceptance criterion belongs to
|
|
283
|
+
exactly one Story.
|
|
284
|
+
- **Handoff.** Persist creates the Story issue(s) at `agent::ready` and
|
|
285
|
+
names the delivery command: `/deliver <storyId> [<storyId> ...]`.
|
|
286
|
+
|
|
287
|
+
Optional split advisory notes come from
|
|
288
|
+
[`core/scope-triage`](../skills/core/scope-triage/SKILL.md); there is no
|
|
289
|
+
`epic|story` routing verdict, scorer, schema field, or label transition
|
|
290
|
+
behind them.
|
|
291
|
+
|
|
292
|
+
Audit findings enter planning through
|
|
293
|
+
[`/audit-to-stories`](../workflows/audit-to-stories.md), which groups and
|
|
294
|
+
deduplicates findings and hands off via `--emit-plan-seed` →
|
|
295
|
+
`/plan --seed-file <path>`.
|
|
399
296
|
|
|
400
297
|
---
|
|
401
298
|
|
|
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
|
-
---
|
|
299
|
+
## Phase 2: Delivery
|
|
611
300
|
|
|
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.
|
|
301
|
+
Delivery is owned end-to-end by [`/deliver`](../workflows/deliver.md), which
|
|
302
|
+
delegates every Story to
|
|
303
|
+
[`helpers/deliver-story`](../workflows/helpers/deliver-story.md). This
|
|
304
|
+
section states the contract; the per-Story step detail (init, implement,
|
|
305
|
+
self-eval, ceremony, close, CI watch, confirm-merge, cleanup) lives in the
|
|
306
|
+
`deliver-story` workflow and its
|
|
307
|
+
[reference](../workflows/helpers/deliver-story-reference.md).
|
|
647
308
|
|
|
648
309
|
### Invocation modes
|
|
649
310
|
|
|
650
|
-
| Mode
|
|
651
|
-
|
|
|
652
|
-
| **
|
|
653
|
-
| **
|
|
654
|
-
| **
|
|
655
|
-
| **Standalone Story — deliver** | `/deliver <storyId> [<storyId>...]` | Deliver one or more standalone Stories authored by `/plan`. |
|
|
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.
|
|
744
|
-
|
|
745
|
-
### State sync
|
|
746
|
-
|
|
747
|
-
Agents update their state in real-time on GitHub:
|
|
748
|
-
|
|
749
|
-
- **Labels**: `agent::ready` → `agent::executing` → `agent::done`. The
|
|
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.
|
|
761
|
-
|
|
762
|
-
### Dependency unblocking
|
|
311
|
+
| Mode | Entry point | When to use |
|
|
312
|
+
| --- | --- | --- |
|
|
313
|
+
| **Single Story** | `/deliver <storyId>` | Deliver one Story end-to-end; ends with a PR open to `main`. |
|
|
314
|
+
| **Story set** | `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (default concurrency **3**), resolved from live state so edges may point at Stories from earlier plan runs; each lands through its own PR, and the per-run epilogue runs after the set lands. |
|
|
315
|
+
| **Story worker (internal)** | *helper* `helpers/deliver-story <storyId>` | Per-Story engine invoked internally by `/deliver`; not an operator slash command. |
|
|
763
316
|
|
|
764
|
-
|
|
765
|
-
|
|
317
|
+
The single operator-facing entry point is `/deliver`. It performs no
|
|
318
|
+
git/label mutations itself — `deliver-story` owns every script invocation
|
|
319
|
+
per Story. Any ticket that is not `type::story`, or that still carries an
|
|
320
|
+
`Epic: #N` reference, is a hard error naming the ID and the fix (close or
|
|
321
|
+
re-plan as a v2 Story).
|
|
766
322
|
|
|
767
|
-
###
|
|
323
|
+
### Branch model (authoritative)
|
|
768
324
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
`helpers/epic-deliver-story`) per id per wave; sibling sub-agents never
|
|
773
|
-
race on the same Story.
|
|
774
|
-
|
|
775
|
-
`runtime.sessionId` survives as a stable per-process identity surfaced in
|
|
776
|
-
the startup `[ENV]` log line for operator correlation. It is a 12-char
|
|
777
|
-
short-id derived from hostname+pid+random.
|
|
778
|
-
|
|
779
|
-
### Launch-time dependency guard
|
|
780
|
-
|
|
781
|
-
Before any branch operation, `story-init.js` reads the Epic's
|
|
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.
|
|
325
|
+
```text
|
|
326
|
+
story-<id> → PR → main (squash + required checks)
|
|
327
|
+
```
|
|
787
328
|
|
|
788
|
-
|
|
329
|
+
There is no `epic/<id>` integration branch and no `--no-ff` wave merge.
|
|
330
|
+
Dependent Stories land sequentially so each builds on the previous merge to
|
|
331
|
+
`main`.
|
|
789
332
|
|
|
790
|
-
###
|
|
333
|
+
### Ceremony
|
|
791
334
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
335
|
+
Ceremony depth is selected by `delivery.routing.ceremonyProfile`
|
|
336
|
+
(`minimal` | `standard` | `strict`, default `standard`) and the Story's
|
|
337
|
+
own planning risk. Hard gates (lint / test / format / coverage / CRAP /
|
|
338
|
+
maintainability) always run at close — risk never disables them; it only
|
|
339
|
+
tunes acceptance-critic mode, review depth, and audit-lens selection. The
|
|
340
|
+
full profile × scope matrix lives in
|
|
341
|
+
[`deliver.md` § Ceremony](../workflows/deliver.md).
|
|
798
342
|
|
|
799
|
-
|
|
800
|
-
`.agents/scripts/lib/config/runners.js`) — 3.
|
|
801
|
-
- `DEFAULT_STORY_MERGE_RETRY.backoffMs` (same module) — `[250, 500, 1000]`.
|
|
343
|
+
### State sync
|
|
802
344
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
345
|
+
Agents update their state in real time on GitHub, always through
|
|
346
|
+
`update-ticket-state.js`:
|
|
347
|
+
|
|
348
|
+
- **Labels**: `agent::ready` → `agent::executing` → `agent::closing` →
|
|
349
|
+
`agent::done`. The `agent::done` flip happens only after
|
|
350
|
+
`single-story-confirm-merge.js` confirms the PR merged. When a
|
|
351
|
+
`projectNumber` is configured, the Projects v2 Status column is synced on
|
|
352
|
+
each transition (and re-asserted after merge to beat the board's late
|
|
353
|
+
built-in write).
|
|
354
|
+
- **Acceptance/verify**: the agent works the Story's inline `acceptance[]`
|
|
355
|
+
/ `verify[]` arrays; `verify[]` commands are consumed as required
|
|
356
|
+
evidence by the acceptance self-eval loop.
|
|
357
|
+
- **Friction**: friction is posted as a structured comment on the **Story**
|
|
358
|
+
(`diagnose-friction.js`), and rolls up into the retro and, for N>1, the
|
|
359
|
+
per-run follow-up roll-up.
|
|
806
360
|
|
|
807
361
|
### Cross-clone coordination
|
|
808
362
|
|
|
809
|
-
Concurrent runs are serialised by **two distinct layers
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
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.
|
|
363
|
+
Concurrent runs are serialised by **two distinct layers**:
|
|
364
|
+
|
|
365
|
+
- **Filesystem locks are same-machine-only.** The single-story sweep lock
|
|
366
|
+
(`sweep-lock.js`) is a single-file rendezvous keyed on a local process
|
|
367
|
+
PID + mtime TTL. Because a PID is only meaningful on its own machine and
|
|
368
|
+
`.git/` is never committed, these locks coordinate only the worktrees and
|
|
369
|
+
sessions on **one** clone.
|
|
370
|
+
- **The assignee-as-lease is the cross-clone layer.** To stop two clones
|
|
371
|
+
from both *starting* the same Story, `deliver-story` takes an exclusive,
|
|
372
|
+
time-bounded claim on the ticket via
|
|
373
|
+
[`ticket-lease.js`](../scripts/lib/orchestration/ticket-lease.js), riding
|
|
374
|
+
the ticket's GitHub `assignees` field so a live foreign claim is visible
|
|
375
|
+
to every clone. The standalone lease **fails closed** on a foreign
|
|
376
|
+
assignee; `--steal` is the only override. See
|
|
377
|
+
[`README.md` § Multi-developer coordination](../README.md#multi-developer-coordination).
|
|
378
|
+
|
|
379
|
+
### Concurrent close
|
|
380
|
+
|
|
381
|
+
`single-story-close.js` syncs the Story branch from `origin/main` before
|
|
382
|
+
pushing and opening/locating the PR, so concurrent closes serialize through
|
|
383
|
+
their own worktrees rather than racing one shared branch. The push does not
|
|
384
|
+
retry: a rejected push, or a real content conflict at base-sync, aborts with
|
|
385
|
+
a clear error, leaves the tree clean, and exits non-zero for manual
|
|
386
|
+
resolution.
|
|
961
387
|
|
|
962
388
|
---
|
|
963
389
|
|
|
964
390
|
## HITL (Human-in-the-Loop) model
|
|
965
391
|
|
|
966
392
|
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
|
|
393
|
+
after `/deliver` fires (blocker resolution). PR merge is autonomous via
|
|
394
|
+
armed auto-merge; the operator becomes a second touchpoint only by
|
|
395
|
+
exception.
|
|
396
|
+
|
|
397
|
+
1. **Blocker resolution (mandatory when triggered).** If a Story hits an
|
|
398
|
+
unresolvable condition, it flips to `agent::blocked`, posts a structured
|
|
399
|
+
friction comment, and fires the notification webhook (fire-and-forget).
|
|
400
|
+
The operator resolves the underlying issue (a hand-fix commit on the
|
|
401
|
+
Story branch, or a scope edit on the ticket) and flips the Story back to
|
|
979
402
|
`agent::executing` to resume.
|
|
980
403
|
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.
|
|
404
|
+
close, `deliver-story` opens a PR to `main` and arms GitHub native
|
|
405
|
+
auto-merge. When required checks pass, the PR lands without a second
|
|
406
|
+
operator visit and the standard label transition flips the Story to
|
|
407
|
+
`agent::done`. The operator becomes a touchpoint only when they (a)
|
|
408
|
+
disarm auto-merge (`--no-auto-merge` per run, or
|
|
409
|
+
`delivery.ci.autoMerge: "strict"`) to inspect checks / the
|
|
410
|
+
`verification-results` comment / the retro before merging by hand, or
|
|
411
|
+
(b) checks fail and need remediation.
|
|
993
412
|
|
|
994
413
|
### What triggers `agent::blocked`
|
|
995
414
|
|
|
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
|
|
415
|
+
- Unresolvable merge conflict automated strategies cannot reconcile.
|
|
416
|
+
- Test failures that persist after automated remediation.
|
|
417
|
+
- Ambiguity requiring a product/scope decision the agent cannot make from
|
|
418
|
+
ticket context alone.
|
|
419
|
+
- A destructive action not pre-authorized by the ticket body.
|
|
420
|
+
- External-service failure preventing progress (GitHub API 5xx loop, npm
|
|
1003
421
|
registry down).
|
|
1004
|
-
-
|
|
422
|
+
- Acceptance self-eval exhausting its bounded round cap with criteria still
|
|
423
|
+
unmet.
|
|
1005
424
|
|
|
1006
425
|
### What is *not* gated at runtime
|
|
1007
426
|
|
|
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.
|
|
427
|
+
- `risk::high` Stories **run without pause.** The label is planning/audit
|
|
428
|
+
metadata and retro telemetry only; the sole runtime pause point is
|
|
429
|
+
`agent::blocked`. Branch protection on `main` and blocker escalation are
|
|
430
|
+
the runtime defenses for destructive actions.
|
|
431
|
+
- Individual Story completion — no per-Story approval prompt beyond the PR
|
|
432
|
+
merge gate.
|
|
1097
433
|
|
|
1098
434
|
---
|
|
1099
435
|
|
|
1100
436
|
## Testing strategy
|
|
1101
437
|
|
|
1102
438
|
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
|
|
439
|
+
belongs to exactly one tier — **unit**, **contract**, or **e2e /
|
|
440
|
+
acceptance**. The canonical tier definitions, assertion-placement rules,
|
|
441
|
+
and coverage thresholds live in
|
|
442
|
+
[`rules/testing-standards.md`](../rules/testing-standards.md); Gherkin
|
|
443
|
+
authoring for the acceptance tier is governed by
|
|
1108
444
|
[`rules/gherkin-standards.md`](../rules/gherkin-standards.md).
|
|
1109
445
|
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
[`workflows/helpers/epic-testing.md`](../workflows/helpers/epic-testing.md).
|
|
446
|
+
Write a Story's acceptance criteria in Gherkin-compatible `Given / When /
|
|
447
|
+
Then` form so the acceptance suite can lift them into executable `.feature`
|
|
448
|
+
files.
|
|
1114
449
|
|
|
1115
450
|
### QA workflows: explore, assist, and run-harness
|
|
1116
451
|
|
|
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`.
|
|
452
|
+
Three complementary QA workflows sit alongside the automated pyramid, all
|
|
453
|
+
reading the consumer's `qa.*` contract from `.agentrc.json` through
|
|
454
|
+
[`scripts/lib/qa/resolve-qa-contract.js`](../scripts/lib/qa/resolve-qa-contract.js)
|
|
455
|
+
(which fails loudly when no `qa` block is bound):
|
|
456
|
+
|
|
457
|
+
- **[`/qa-explore`](../workflows/qa-explore.md)** — an **agent-led**,
|
|
458
|
+
open-ended **Plan → Capture → Triage** exploratory sweep. The operator
|
|
459
|
+
names a surface; the agent drives it (browser MCP by default), recording
|
|
460
|
+
each observation as a `QaLedgerItem`
|
|
461
|
+
([`schemas/qa-ledger.schema.json`](../schemas/qa-ledger.schema.json)) in a
|
|
462
|
+
session ledger under `temp/qa/`. Capture is strictly **read-only**; every
|
|
463
|
+
state-changing action lands in Triage after explicit operator
|
|
464
|
+
confirmation.
|
|
465
|
+
- **[`/qa-assist`](../workflows/qa-assist.md)** — the **human-led** sibling:
|
|
466
|
+
a single-observation **Intake → Enrich → Record** loop. The operator
|
|
467
|
+
reports one observation; the agent enriches it into a triage-ready
|
|
468
|
+
`QaLedgerItem`. Same ledger contract and decision seams as `/qa-explore`.
|
|
469
|
+
- **[`/qa-run`](../workflows/qa-run.md)** — the **automated complement**:
|
|
470
|
+
steps a *known* set of Gherkin `.feature` scenarios through a real
|
|
471
|
+
browser, asserting `Then` outcomes against the accessibility snapshot and
|
|
472
|
+
bundling console/network problems into structured `F#` findings.
|
|
473
|
+
|
|
1174
474
|
Consumer adoption steps are in
|
|
1175
475
|
[`README.md` § Adopting the QA harness](../README.md#adopting-the-qa-harness).
|
|
1176
476
|
|
|
@@ -1178,170 +478,120 @@ Consumer adoption steps are in
|
|
|
1178
478
|
|
|
1179
479
|
## Static analysis & audit orchestration
|
|
1180
480
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
|
481
|
+
Audit lenses are woven into delivery as a **shift-left, three-tier**
|
|
482
|
+
verification model in which each lens concern is verified at exactly one
|
|
483
|
+
tier, chosen by the lens's `scope` field in `audit-rules.json` (resolved by
|
|
484
|
+
`resolveLensTier`). There is **no** separate Epic-lifecycle-gate delivery
|
|
485
|
+
pass — the tiers below *are* the audit machinery.
|
|
486
|
+
|
|
487
|
+
| Tier | When | What runs | Blocking? |
|
|
488
|
+
| --- | --- | --- | --- |
|
|
489
|
+
| Tier 1 — write-time | During Story implementation | Footprint-matched **local**-lens authoring checklists threaded into the Story prompt (`checklistPath`) | advisory |
|
|
490
|
+
| 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 🔴 |
|
|
491
|
+
| Tier 3 — run closeout | `/deliver` per-run epilogue (`plan-run-epilogue.js`, N>1 only) | Cumulative + global lenses (`selectAudits`) over the combined landed tip | blocking |
|
|
492
|
+
|
|
493
|
+
- **`local`** lenses (decidable from a single Story's diff) are verified at
|
|
494
|
+
Tiers 1–2 and are **not** re-run at run closeout.
|
|
495
|
+
- **`cumulative`** lenses (only decidable across a run's combined diff)
|
|
496
|
+
and **`global`** lenses (whole-product properties) are verified at Tier 3.
|
|
497
|
+
|
|
498
|
+
There is no risk-routed lens tier. Story #4542 deleted the risk→lens router:
|
|
499
|
+
it had zero callers while this document claimed it ran inside close. Lens
|
|
500
|
+
selection is change-set-matched (`selectAudits` / `selectLocalLenses`); the
|
|
501
|
+
`sensitivePaths` classes in `audit-rules.json` route review **depth**, not
|
|
502
|
+
lenses.
|
|
503
|
+
|
|
504
|
+
The run-closeout roster is deliberately **slim**: it excludes every
|
|
505
|
+
local-tier change-set lens so the outermost tier — where a fix is most
|
|
506
|
+
expensive — does not re-verify a concern already covered shift-left.
|
|
507
|
+
|
|
508
|
+
### Code review
|
|
509
|
+
|
|
510
|
+
The Story-scope code review runs **outside the maker's context**, inside
|
|
511
|
+
the `single-story-close.js` close subprocess, over `main...story-<id>`
|
|
512
|
+
(see [`helpers/code-review.md`](../workflows/helpers/code-review.md)). It
|
|
513
|
+
walks the Story diff once, executing the change-set-matched local lens roster
|
|
514
|
+
as review dimensions alongside the review pillars, and posts the unified
|
|
515
|
+
`verification-results` comment. Remediation is tier-aware and split by
|
|
516
|
+
finding class off `delivery.codeReview.autoFixSeverity` (default `medium`);
|
|
517
|
+
surviving 🔴 Critical findings halt the run. The legacy `scope: epic`
|
|
518
|
+
Epic-branch review path was removed with the v2 cutover.
|
|
519
|
+
|
|
520
|
+
### Quality ratchets
|
|
521
|
+
|
|
522
|
+
- **Maintainability ratchet** (`check-baselines.js` via
|
|
523
|
+
`lib/baselines/kinds/maintainability.js`) — fails if the
|
|
1247
524
|
composite score drops below the established baseline.
|
|
1248
|
-
- **CRAP gate
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
`
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
525
|
+
- **CRAP gate** (`check-baselines.js` via `lib/baselines/kinds/crap.js`) —
|
|
526
|
+
per-method complexity × coverage risk
|
|
527
|
+
against `baselines/crap.json`, wired into close-validation, `ci.yml`, and
|
|
528
|
+
`.husky/pre-push`. The `baseline-refresh: true` commit-trailer convention
|
|
529
|
+
is the project standard for baseline edits (see
|
|
530
|
+
[`core/gates-and-baselines`](../skills/core/gates-and-baselines/SKILL.md)).
|
|
531
|
+
|
|
532
|
+
### Audits → Stories
|
|
533
|
+
|
|
534
|
+
The standalone `/audit-<dimension>` workflows are read-only emitters that
|
|
535
|
+
write `audit-<dimension>-results.md` under `temp/audits/`.
|
|
536
|
+
[`/audit-to-stories`](../workflows/audit-to-stories.md) parses those
|
|
537
|
+
reports, groups and deduplicates findings, and hands off to
|
|
538
|
+
`/plan --seed-file` (or opens standalone Stories) — closing the loop back
|
|
539
|
+
into planning.
|
|
1263
540
|
|
|
1264
541
|
---
|
|
1265
542
|
|
|
1266
543
|
## Notification system
|
|
1267
544
|
|
|
1268
|
-
Two independent notification surfaces, both living in `.agents/` so they
|
|
1269
|
-
consuming projects
|
|
545
|
+
Two independent notification surfaces, both living in `.agents/` so they
|
|
546
|
+
ship to consuming projects.
|
|
1270
547
|
|
|
1271
548
|
### 1. Unified `notify()` dispatcher
|
|
1272
549
|
|
|
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:
|
|
550
|
+
Every notification — whether a manual orchestration milestone (Story
|
|
551
|
+
merged, HITL gate triggered) or an auto-fired ticket-state transition —
|
|
552
|
+
routes through [`notify.js`](../scripts/notify.js). Two delivery channels:
|
|
1276
553
|
|
|
1277
|
-
| Channel
|
|
1278
|
-
|
|
|
1279
|
-
| GitHub comment
|
|
1280
|
-
| Webhook
|
|
554
|
+
| Channel | What it does |
|
|
555
|
+
| --- | --- |
|
|
556
|
+
| GitHub comment | Posts to the targeted ticket; @mentions the operator for `medium`/`high`. |
|
|
557
|
+
| Webhook | Fire-and-forget POST to the configured URL (Make.com / Slack / Discord). |
|
|
1281
558
|
|
|
1282
|
-
Severity vocabulary (
|
|
1283
|
-
`lib/notifications/notifier.js` derives it for state transitions):
|
|
559
|
+
Severity vocabulary (`eventSeverity()` derives it for state transitions):
|
|
1284
560
|
|
|
1285
|
-
| Severity | Used for
|
|
1286
|
-
|
|
|
1287
|
-
| `low`
|
|
1288
|
-
| `medium` | Operator-visible milestones: Story state transitions,
|
|
1289
|
-
| `high`
|
|
561
|
+
| Severity | Used for | Webhook prefix |
|
|
562
|
+
| --- | --- | --- |
|
|
563
|
+
| `low` | Intermediate transitions, audit reports. | `[low]` |
|
|
564
|
+
| `medium` | Operator-visible milestones: Story state transitions, story merged, run complete. | `[medium]` |
|
|
565
|
+
| `high` | Operator must act (HITL gates, Story blockers, autonomous-chain failures); body leads with `🚨 Action Required:`. | `[Action Required]` |
|
|
1290
566
|
|
|
1291
|
-
Two independent event-allowlist knobs in `github.notifications`
|
|
1292
|
-
|
|
567
|
+
Two independent event-allowlist knobs in `github.notifications` (both
|
|
568
|
+
mandatory) filter each channel independently — there is no fallback chain:
|
|
1293
569
|
|
|
1294
|
-
- `commentEvents` —
|
|
1295
|
-
posting. Default:
|
|
570
|
+
- `commentEvents` — allowlist for GitHub-ticket comment posting. Default:
|
|
1296
571
|
`["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 |
|
|
572
|
+
- `webhookEvents` — allowlist for `NOTIFICATION_WEBHOOK_URL` deliveries.
|
|
573
|
+
|
|
574
|
+
`transitionTicketState` suppresses the `notify()` dispatch for low-severity
|
|
575
|
+
transitions so the comment channel sees only the medium-severity
|
|
576
|
+
Story-level events operators expect. To suppress a channel entirely, set
|
|
577
|
+
its array to `[]`.
|
|
578
|
+
|
|
579
|
+
**Webhook URL resolution.** `NOTIFICATION_WEBHOOK_URL` process env var only
|
|
580
|
+
— loaded from `.env` at the project root. It is **not** sourced from
|
|
581
|
+
`.agentrc.json` or `.mcp.json`.
|
|
582
|
+
|
|
583
|
+
Because `notify()` is called in-band from the orchestration SDK, it
|
|
584
|
+
captures state changes from `deliver-story`, the per-Story scripts
|
|
585
|
+
(`single-story-init.js`, `single-story-close.js`,
|
|
586
|
+
`single-story-confirm-merge.js`), and any script that routes through
|
|
587
|
+
`transitionTicketState`. It does **not** capture manual label clicks in the
|
|
588
|
+
GitHub UI.
|
|
589
|
+
|
|
590
|
+
### 2. Blocker / HITL notifications
|
|
591
|
+
|
|
592
|
+
Fire-and-forget webhooks fire on blocker-escalation events
|
|
593
|
+
(`agent::blocked`) and operator-attention events (PR-open hand-off, run
|
|
594
|
+
cancellation). Webhook failures never block execution.
|
|
1345
595
|
|
|
1346
596
|
---
|
|
1347
597
|
|
|
@@ -1349,106 +599,67 @@ failures never block execution.
|
|
|
1349
599
|
|
|
1350
600
|
### Sub-agent CI workflow editing
|
|
1351
601
|
|
|
1352
|
-
Sub-agents
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
with an error of the shape:
|
|
602
|
+
Sub-agents operating under the framework's default `GITHUB_TOKEN` **cannot
|
|
603
|
+
edit files under `.github/workflows/**`** — the token does not carry the
|
|
604
|
+
`workflows` permission scope, so a push touching a workflow file is
|
|
605
|
+
rejected with:
|
|
1357
606
|
|
|
1358
607
|
> refusing to allow a GitHub App to create or update workflow
|
|
1359
608
|
> `.github/workflows/<file>.yml` without `workflows` permission
|
|
1360
609
|
|
|
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.
|
|
610
|
+
This is a hard constraint, not a transient failure. **When a Story plans a
|
|
611
|
+
new CI gate**, route the check through a `package.json` script (add it to
|
|
612
|
+
`npm run lint` / `npm run docs:check` / `npm test`, or wire a new
|
|
613
|
+
`npm run check:<name>` script) so an existing CI job picks it up by
|
|
614
|
+
transitivity. **When a workflow file genuinely must change** (a new job, a
|
|
615
|
+
trigger change, a runner bump), the edit must be made by an operator with
|
|
616
|
+
`Workflows: Read and write` PAT permissions — see
|
|
617
|
+
[`docs/release-operations.md` § One-time PAT setup](../../docs/release-operations.md#one-time-pat-setup).
|
|
1391
618
|
|
|
1392
619
|
### Worktree config shadow
|
|
1393
620
|
|
|
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.
|
|
621
|
+
`helpers/deliver-story` runs inside per-Story worktrees under
|
|
622
|
+
`.worktrees/story-<id>/`. A worktree checks out the **Story branch's own
|
|
623
|
+
copy** of every repo-tracked file — including `.agentrc.json`. **Operator
|
|
624
|
+
edits made in the main checkout do NOT propagate to an already-active
|
|
625
|
+
worktree.** Symptom: you bump a runtime knob in `<main-repo>/.agentrc.json`,
|
|
626
|
+
re-run `single-story-close.js --cwd <worktree>`, and the script still uses
|
|
627
|
+
the old value. When tuning knobs mid-Story:
|
|
628
|
+
|
|
629
|
+
1. **Prefer an env-var override** when the knob exposes one (timeouts,
|
|
630
|
+
`AGENT_LOG_LEVEL`, concurrency caps) — env vars are read from your shell,
|
|
631
|
+
bypassing worktree shadow entirely.
|
|
632
|
+
2. **Edit the file inside the worktree** (`.worktrees/story-<id>/.agentrc.json`)
|
|
633
|
+
so the script sees the bump on its next read.
|
|
634
|
+
3. **Use `.agentrc.local.json`** for per-machine tuning you never commit
|
|
635
|
+
(see
|
|
636
|
+
[`configuration.md`](configuration.md#per-machine-local-overrides)) —
|
|
637
|
+
place it inside the worktree, or invoke the script with
|
|
638
|
+
`--cwd <main-repo>` so the resolver reads the main checkout's override.
|
|
1431
639
|
|
|
1432
640
|
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
|
-
|
|
641
|
+
`single-story-init.js` invocation, because new Story branches fork from
|
|
642
|
+
`main`'s current tip.
|
|
643
|
+
|
|
644
|
+
### `Epic: #N` refusal
|
|
645
|
+
|
|
646
|
+
`/deliver` refuses any ticket that still carries an `Epic: #N` footer or is
|
|
647
|
+
not `type::story`. This is expected — v2 has no Epic delivery path. Close
|
|
648
|
+
the ticket or re-plan the work as a v2 Story via `/plan --tickets <id>`.
|
|
1436
649
|
|
|
1437
650
|
---
|
|
1438
651
|
|
|
1439
652
|
## Quick reference
|
|
1440
653
|
|
|
1441
|
-
| Command
|
|
1442
|
-
|
|
|
1443
|
-
| `npx mandrel init`
|
|
1444
|
-
| `/plan`
|
|
1445
|
-
| `/plan --
|
|
1446
|
-
| `/plan <
|
|
1447
|
-
| `/deliver <
|
|
1448
|
-
| `/deliver <storyId> [<storyId
|
|
1449
|
-
|
|
|
1450
|
-
|
|
|
1451
|
-
|
|
|
1452
|
-
|
|
|
1453
|
-
| `/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). |
|
|
1454
|
-
| `epic-reconcile.js --explicit-delete` | Hard reset — close orphaned Epic-scoped issues per `.agents/epics/<id>.yaml` |
|
|
654
|
+
| Command | Purpose |
|
|
655
|
+
| --- | --- |
|
|
656
|
+
| `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). |
|
|
657
|
+
| `/plan --seed "<text>"` | Plan from chat text — interrogate → author **one Story by default** → persist `type::story`. |
|
|
658
|
+
| `/plan --seed-file <path>` | Plan from on-disk notes / a plan seed (the `/audit-to-stories` handoff). |
|
|
659
|
+
| `/plan --tickets <ids>` | Analyze existing issue(s) into proper Stories (prefer an N=1 rewrite). |
|
|
660
|
+
| `/deliver <storyId>` | Deliver one Story via `helpers/deliver-story` — `story-<id>` → PR → `main`. |
|
|
661
|
+
| `/deliver <storyId> [<storyId>…]` | Deliver multiple Stories in `depends_on` order (resolved from live state), then run the per-run epilogue. |
|
|
662
|
+
| *helper* `helpers/deliver-story` | Per-Story engine invoked by `/deliver`; not an operator slash command. See [`deliver-story.md`](../workflows/helpers/deliver-story.md). |
|
|
663
|
+
| `/audit-to-stories` | Convert audit findings into a plan seed / Stories → `/plan --seed-file`. |
|
|
664
|
+
| `/qa-explore` · `/qa-assist` · `/qa-run` | Agent-led / human-led exploratory QA and the automated Gherkin harness. |
|
|
665
|
+
| `/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). |
|