mandrel 1.93.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +59 -73
- package/.agents/agents/acceptance-critic.md +129 -0
- package/.agents/agents/story-worker.md +161 -0
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +108 -136
- package/.agents/docs/execution-reference.md +44 -22
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +107 -108
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +224 -0
- package/.agents/rules/git-conventions.md +42 -223
- package/.agents/rules/security-baseline.md +5 -0
- package/.agents/rules/testing-standards.md +106 -13
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +71 -201
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/retro.end.schema.json +1 -1
- package/.agents/schemas/risk-verdict.schema.json +0 -13
- package/.agents/scripts/acceptance-eval.js +62 -18
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/analyze-execution.js +1 -1
- package/.agents/scripts/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.js +1 -1
- package/.agents/scripts/check-context-budget.js +62 -5
- package/.agents/scripts/check-lifecycle-lint.js +6 -9
- package/.agents/scripts/check-prepush-recovery.js +1 -1
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- package/.agents/scripts/diagnose-friction.js +0 -6
- package/.agents/scripts/lib/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +5 -5
- package/.agents/scripts/lib/audit-to-stories/{seed-epic-from-findings.js → seed-from-findings.js} +9 -9
- package/.agents/scripts/lib/baselines/kernel.js +206 -18
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -4
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -33
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +56 -18
- package/.agents/scripts/lib/checks/core-bare-clean.js +2 -2
- package/.agents/scripts/lib/checks/index.js +2 -1
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +23 -21
- package/.agents/scripts/lib/cli/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +16 -7
- package/.agents/scripts/lib/close-validation/gates.js +160 -22
- package/.agents/scripts/lib/config/acceptance-eval.js +52 -5
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +103 -0
- package/.agents/scripts/lib/config/explain.js +57 -36
- package/.agents/scripts/lib/config/limits.js +17 -58
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +1 -1
- package/.agents/scripts/lib/config/runners.js +17 -50
- package/.agents/scripts/lib/config/temp-paths.js +19 -14
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +3 -8
- package/.agents/scripts/lib/config-settings-schema-delivery.js +46 -136
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -14
- package/.agents/scripts/lib/config-settings-schema.js +52 -38
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +39 -4
- package/.agents/scripts/lib/duplicate-search.js +211 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- package/.agents/scripts/lib/findings/promote-finding.js +5 -5
- package/.agents/scripts/lib/framework-version.js +2 -3
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -22
- package/.agents/scripts/lib/git-utils.js +32 -6
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/label-constants.js +10 -23
- package/.agents/scripts/lib/label-taxonomy.js +9 -43
- package/.agents/scripts/lib/observability/active-story-env.js +112 -3
- package/.agents/scripts/lib/observability/hook-heartbeat.js +187 -0
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/observability/tool-trace-hook.js +15 -4
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +111 -0
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +32 -4
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +273 -0
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +204 -0
- package/.agents/scripts/lib/orchestration/code-review.js +20 -268
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +1 -1
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- package/.agents/scripts/lib/orchestration/docs-digest.js +8 -8
- package/.agents/scripts/lib/orchestration/file-assumptions.js +7 -13
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/cli.js +1 -1
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +8 -8
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +2 -2
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +17 -43
- package/.agents/scripts/lib/orchestration/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +1 -1
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +248 -266
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +1 -1
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +61 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +97 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +223 -854
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +361 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +35 -108
- package/.agents/scripts/lib/orchestration/plan-reachability.js +9 -14
- package/.agents/scripts/lib/orchestration/plan-runner/worktree-sweep.js +1 -1
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/authoring-context.js +14 -14
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/risk-verdict.js +3 -4
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +2 -2
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +8 -20
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +3 -2
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/preflight-cache.js +5 -5
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +155 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +8 -8
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +26 -12
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- package/.agents/scripts/lib/orchestration/review-providers/review-provider-factory.js +21 -56
- package/.agents/scripts/lib/orchestration/run-epilogue.js +426 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +1 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +95 -41
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +16 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +40 -0
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +11 -3
- package/.agents/scripts/lib/orchestration/spec-freshness.js +14 -205
- package/.agents/scripts/lib/orchestration/spec-section-validator.js +4 -5
- package/.agents/scripts/lib/orchestration/spec-spill.js +60 -0
- package/.agents/scripts/lib/orchestration/split-policy-validator.js +188 -0
- package/.agents/scripts/lib/orchestration/story-close/emit-blocked.js +49 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +15 -12
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +237 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +47 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +48 -0
- package/.agents/scripts/lib/orchestration/{epic-runner → story-progress}/story-run-progress-writer.js +3 -3
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +8 -18
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +11 -61
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +189 -373
- package/.agents/scripts/lib/orchestration/ticket-validator.js +3 -8
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +0 -25
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +29 -26
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +5 -5
- package/.agents/scripts/lib/planning-corpus.js +16 -11
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/provider-factory.js +1 -1
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +36 -0
- package/.agents/scripts/lib/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/story-adjacency.js +11 -14
- package/.agents/scripts/lib/story-body/story-body.js +124 -70
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +46 -45
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +26 -26
- package/.agents/scripts/lib/validation-evidence.js +1 -1
- package/.agents/scripts/lib/wave-runner/ready-set.js +6 -6
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +5 -7
- package/.agents/scripts/lint-issue-body.js +71 -40
- package/.agents/scripts/mandrel-update-preflight.js +1 -1
- package/.agents/scripts/notify.js +4 -3
- package/.agents/scripts/plan-context.js +64 -74
- package/.agents/scripts/plan-persist.js +121 -280
- package/.agents/scripts/plan-run-epilogue.js +97 -0
- package/.agents/scripts/post-structured-comment.js +38 -0
- package/.agents/scripts/providers/github/issues.js +17 -33
- package/.agents/scripts/providers/github/mappers.js +0 -12
- package/.agents/scripts/providers/github/tickets.js +2 -5
- package/.agents/scripts/resolve-plan-run.js +117 -0
- package/.agents/scripts/signals-view.js +24 -19
- package/.agents/scripts/single-story-close.js +11 -14
- package/.agents/scripts/single-story-confirm-merge.js +39 -23
- package/.agents/scripts/single-story-init.js +29 -20
- package/.agents/scripts/stories-wave-tick.js +6 -6
- package/.agents/scripts/story-plan.js +26 -47
- package/.agents/scripts/sync-claude-agents.js +165 -0
- package/.agents/scripts/update-ticket-state.js +37 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/api-and-interface-design/SKILL.md +5 -3
- package/.agents/skills/core/code-review-and-quality/SKILL.md +63 -7
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +1 -1
- package/.agents/skills/core/gates-and-baselines/SKILL.md +149 -0
- package/.agents/skills/core/idea-refinement/SKILL.md +8 -14
- package/.agents/skills/core/qa-coverage-mapping/SKILL.md +7 -7
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +8 -418
- package/.agents/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +3 -3
- package/.agents/workflows/audit-clean-code.md +3 -3
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +5 -5
- package/.agents/workflows/audit-lighthouse.md +3 -3
- package/.agents/workflows/audit-navigability.md +3 -2
- package/.agents/workflows/audit-performance.md +3 -3
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +3 -3
- package/.agents/workflows/audit-security.md +3 -3
- package/.agents/workflows/audit-seo.md +3 -3
- package/.agents/workflows/audit-sre.md +3 -3
- package/.agents/workflows/audit-to-stories.md +20 -20
- package/.agents/workflows/audit-ux-ui.md +3 -3
- package/.agents/workflows/deliver.md +122 -131
- package/.agents/workflows/git-cleanup.md +3 -4
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +52 -40
- package/.agents/workflows/helpers/code-review.md +70 -193
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +12 -14
- package/.agents/workflows/helpers/{single-story-deliver.md → deliver-story.md} +113 -139
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/mandrel-sync-config.md +1 -1
- package/.agents/workflows/helpers/parallel-tooling.md +1 -1
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +48 -64
- package/.agents/workflows/mandrel-update.md +3 -2
- package/.agents/workflows/plan.md +112 -145
- package/.agents/workflows/qa-assist.md +24 -30
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -2
- package/README.md +9 -8
- package/docs/CHANGELOG.md +46 -0
- package/lib/cli/registry.js +95 -0
- package/lib/migrations/index.js +6 -5
- package/package.json +5 -3
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer-mobile.md +0 -120
- package/.agents/personas/engineer-web.md +0 -111
- package/.agents/personas/engineer.md +0 -119
- package/.agents/personas/product.md +0 -94
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/refactorer.md +0 -113
- package/.agents/personas/security-engineer.md +0 -112
- package/.agents/personas/sre.md +0 -86
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/personas/ux-designer.md +0 -95
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -590
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-decompose.js +0 -54
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-plan-spec.js +0 -64
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -660
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -388
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/sub-agent-return.js +0 -276
- package/.agents/scripts/lib/orchestration/epic-runner/wave-scheduler.js +0 -66
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-apply.js +0 -789
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-diff.js +0 -676
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-discriminator.js +0 -389
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-format.js +0 -230
- package/.agents/scripts/lib/orchestration/epic-spec-reconciler-ops.js +0 -361
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -384
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -278
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/notify-dispatcher.js +0 -174
- package/.agents/scripts/lib/orchestration/manifest-builder.js +0 -222
- package/.agents/scripts/lib/orchestration/plan-persist/amend.js +0 -359
- package/.agents/scripts/lib/orchestration/plan-persist/delivery-mode.js +0 -127
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/recurring-failure-detector.js +0 -152
- package/.agents/scripts/lib/orchestration/retro/phases/checks.js +0 -94
- package/.agents/scripts/lib/orchestration/retro/phases/compose-body.js +0 -571
- package/.agents/scripts/lib/orchestration/retro/phases/gather-signals.js +0 -450
- package/.agents/scripts/lib/orchestration/retro/phases/post-and-mirror.js +0 -191
- package/.agents/scripts/lib/orchestration/retro-heuristics.js +0 -57
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/skill-capsule-loader.js +0 -109
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -301
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/plan-critics.js +0 -199
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -335
- package/.agents/skills/core/baseline-refresh/SKILL.md +0 -181
- package/.agents/skills/core/ci-cd-and-automation/SKILL.md +0 -274
- package/.agents/skills/core/ci-cd-and-automation/examples.md +0 -211
- package/.agents/skills/core/code-simplification/SKILL.md +0 -389
- package/.agents/skills/core/context-engineering/SKILL.md +0 -309
- package/.agents/skills/core/context-engineering/examples.md +0 -58
- package/.agents/skills/core/deprecation-and-migration/SKILL.md +0 -250
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -413
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/skills/core/frontend-ui-engineering/SKILL.md +0 -357
- package/.agents/skills/core/hydrate-context/SKILL.md +0 -123
- package/.agents/skills/core/idea-refinement/examples.md +0 -437
- package/.agents/skills/core/idea-refinement/frameworks.md +0 -135
- package/.agents/skills/core/incremental-implementation/SKILL.md +0 -271
- package/.agents/skills/core/introducing-a-baseline-gate/SKILL.md +0 -213
- package/.agents/skills/core/knowledge-transfer/SKILL.md +0 -180
- package/.agents/skills/core/mutation-survivor-remediation/SKILL.md +0 -117
- package/.agents/skills/core/performance-optimization/SKILL.md +0 -314
- package/.agents/skills/core/planning-and-task-breakdown/SKILL.md +0 -277
- package/.agents/skills/core/property-based-testing/SKILL.md +0 -148
- package/.agents/skills/core/refactoring-discipline/SKILL.md +0 -111
- package/.agents/skills/core/shipping-and-launch/SKILL.md +0 -328
- package/.agents/skills/core/spec-driven-development/SKILL.md +0 -252
- package/.agents/skills/core/test-driven-development/SKILL.md +0 -475
- package/.agents/skills/core/using-agent-skills/SKILL.md +0 -232
- package/.agents/skills/stack/architecture/monorepo-path-strategist/SKILL.md +0 -31
- package/.agents/skills/stack/architecture/structured-output-zod/SKILL.md +0 -51
- package/.agents/skills/stack/architecture/subagent-orchestration/SKILL.md +0 -76
- package/.agents/skills/stack/backend/cloudflare-hono-architect/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-hono-architect/examples/route-template.ts +0 -33
- package/.agents/skills/stack/backend/cloudflare-queue-manager/SKILL.md +0 -31
- package/.agents/skills/stack/backend/cloudflare-workers/SKILL.md +0 -51
- package/.agents/skills/stack/backend/highlevel-crm/SKILL.md +0 -54
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/SKILL.md +0 -29
- package/.agents/skills/stack/backend/sqlite-drizzle-expert/examples/schema-template.ts +0 -30
- package/.agents/skills/stack/backend/stripe-integration/SKILL.md +0 -57
- package/.agents/skills/stack/backend/stripe-integration/scripts/listen-stripe.sh +0 -9
- package/.agents/skills/stack/backend/turso-sqlite/SKILL.md +0 -48
- package/.agents/skills/stack/frontend/astro/SKILL.md +0 -62
- package/.agents/skills/stack/frontend/astro-react-island-strategist/SKILL.md +0 -30
- package/.agents/skills/stack/frontend/expo-react-native-developer/SKILL.md +0 -29
- package/.agents/skills/stack/frontend/google-analytics-v4/SKILL.md +0 -50
- package/.agents/skills/stack/frontend/tailwind-v4/SKILL.md +0 -58
- package/.agents/skills/stack/frontend/ui-accessibility-engineer/SKILL.md +0 -34
- package/.agents/skills/stack/qa/audit-accessibility/SKILL.md +0 -51
- package/.agents/skills/stack/qa/lighthouse-baseline/SKILL.md +0 -199
- package/.agents/skills/stack/security/backend-security-patterns/SKILL.md +0 -68
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -534
- package/.agents/workflows/helpers/deliver-epic.md +0 -955
- package/.agents/workflows/helpers/deliver-stories.md +0 -440
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -427
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -351
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* epic-deliver-lease-guard.js — preflight guards for `/deliver`
|
|
3
|
-
* (Story #3482, Epic #3457).
|
|
4
|
-
*
|
|
5
|
-
* `/deliver`'s prepare phase used to checkout `epic/<id>` over whatever
|
|
6
|
-
* the operator had checked out, yanking HEAD and resetting baselines under a
|
|
7
|
-
* live working session (the documented "epic-deliver shares the main checkout"
|
|
8
|
-
* footgun). This module supplies the two preflight guards the Tech Spec
|
|
9
|
-
* (#3476) wires into `epic-deliver-prepare.js` *before* any mutating git work:
|
|
10
|
-
*
|
|
11
|
-
* 1. **Epic-lease preflight.** Acquire the assignee-as-lease on the Epic
|
|
12
|
-
* ticket via `ticket-lease.acquireLease`. On a live foreign claim the
|
|
13
|
-
* guard **fails closed** — it throws a clear, operator-facing error that
|
|
14
|
-
* names the current owner (the `critical-workflow` risk axis demands
|
|
15
|
-
* refuse-and-exit, never warn-and-continue). `--steal` is the only
|
|
16
|
-
* override.
|
|
17
|
-
* 2. **Checkout-safety preflight.** Refuse to start when the working tree is
|
|
18
|
-
* dirty or HEAD is on a branch other than the expected one, instead of
|
|
19
|
-
* checking `epic/<id>` out over the operator's work.
|
|
20
|
-
*
|
|
21
|
-
* Both guards are pure over injected seams (a `git` shim with
|
|
22
|
-
* `statusPorcelain` / `currentBranch`, and the lease provider) so the unit
|
|
23
|
-
* suite exercises them without standing up a real repo or hitting GitHub.
|
|
24
|
-
* Liveness is threaded in via `heartbeatAt` exactly as `ticket-lease.js`
|
|
25
|
-
* documents — this module does not read the lifecycle ledger itself.
|
|
26
|
-
*
|
|
27
|
-
* Per `.agents/rules/orchestration-error-handling.md`, the composed guard
|
|
28
|
-
* surfaces unrecoverable failures with `throw new Error(...)` (mapped to
|
|
29
|
-
* `process.exit(1)` by the `runAsCli` boundary), never `Logger.fatal`.
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
import {
|
|
33
|
-
acquireLeaseFailClosed,
|
|
34
|
-
resolveOperatorFromCandidates,
|
|
35
|
-
} from './lease-guard-shared.js';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Resolve the acquiring operator's identity. Precedence (Tech Spec #3476):
|
|
39
|
-
* 1. An explicit `--as <handle>` flag.
|
|
40
|
-
* 2. `github.operatorHandle` in `.agentrc.json`.
|
|
41
|
-
* 3. The local `git config user.email`.
|
|
42
|
-
*
|
|
43
|
-
* The `@`-prefix some operators carry on `operatorHandle` is stripped (via
|
|
44
|
-
* the shared lease-guard kernel) so the value matches the bare login written
|
|
45
|
-
* to a ticket's `assignees`. This surface's missing-handle policy is `'null'`
|
|
46
|
-
* — `runPrepareGuards` fails closed on a null operator with deliver-specific
|
|
47
|
-
* wording after the (cheap, local) checkout-safety guard has run.
|
|
48
|
-
*
|
|
49
|
-
* @param {object} args
|
|
50
|
-
* @param {string} [args.asFlag] Explicit `--as` value.
|
|
51
|
-
* @param {object} [args.config] Resolved config (reads github.operatorHandle).
|
|
52
|
-
* @param {string} [args.gitUserEmail] Fallback `git config user.email`.
|
|
53
|
-
* @returns {string|null} The resolved operator handle, or null when none could
|
|
54
|
-
* be determined.
|
|
55
|
-
*/
|
|
56
|
-
export function resolveOperator({ asFlag, config, gitUserEmail } = {}) {
|
|
57
|
-
return resolveOperatorFromCandidates({
|
|
58
|
-
candidates: [asFlag, config?.github?.operatorHandle, gitUserEmail],
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Normalise the expected-branch argument into a non-empty list. `/deliver`
|
|
64
|
-
* may legitimately start from either the Epic integration branch (`epic/<id>`,
|
|
65
|
-
* on a resume) or the project base branch (`main`, on a fresh run), so the
|
|
66
|
-
* guard accepts a set rather than a single name.
|
|
67
|
-
*
|
|
68
|
-
* @param {string|string[]} expectedBranch
|
|
69
|
-
* @returns {string[]}
|
|
70
|
-
*/
|
|
71
|
-
function normaliseExpectedBranches(expectedBranch) {
|
|
72
|
-
const list = (
|
|
73
|
-
Array.isArray(expectedBranch) ? expectedBranch : [expectedBranch]
|
|
74
|
-
).filter((b) => typeof b === 'string' && b.length > 0);
|
|
75
|
-
if (list.length === 0) {
|
|
76
|
-
throw new Error('checkCheckoutSafety: expectedBranch is required');
|
|
77
|
-
}
|
|
78
|
-
return list;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Pure checkout-safety classifier. The working tree must be clean and HEAD
|
|
83
|
-
* must sit on one of `expectedBranch` before prepare moves anything.
|
|
84
|
-
*
|
|
85
|
-
* @param {object} args
|
|
86
|
-
* @param {object} args.git Injected git shim.
|
|
87
|
-
* @param {() => { dirty: boolean, entries?: string }} args.git.statusPorcelain
|
|
88
|
-
* Returns whether the tree has uncommitted/untracked changes.
|
|
89
|
-
* @param {() => string|null} args.git.currentBranch Returns HEAD's branch (or
|
|
90
|
-
* null in detached-HEAD state).
|
|
91
|
-
* @param {string|string[]} args.expectedBranch The branch (or branches)
|
|
92
|
-
* prepare expects HEAD on (typically `epic/<id>` or the project base branch).
|
|
93
|
-
* @returns {{
|
|
94
|
-
* safe: boolean,
|
|
95
|
-
* reason: 'clean'|'dirty'|'wrong-branch'|'detached-head',
|
|
96
|
-
* currentBranch: string|null,
|
|
97
|
-
* expectedBranches: string[],
|
|
98
|
-
* dirtyEntries?: string,
|
|
99
|
-
* }}
|
|
100
|
-
*/
|
|
101
|
-
export function checkCheckoutSafety({ git, expectedBranch }) {
|
|
102
|
-
if (!git || typeof git.statusPorcelain !== 'function') {
|
|
103
|
-
throw new Error(
|
|
104
|
-
'checkCheckoutSafety: git shim with statusPorcelain/currentBranch is required',
|
|
105
|
-
);
|
|
106
|
-
}
|
|
107
|
-
const expectedBranches = normaliseExpectedBranches(expectedBranch);
|
|
108
|
-
|
|
109
|
-
const status = git.statusPorcelain();
|
|
110
|
-
if (status?.dirty) {
|
|
111
|
-
return {
|
|
112
|
-
safe: false,
|
|
113
|
-
reason: 'dirty',
|
|
114
|
-
currentBranch: git.currentBranch(),
|
|
115
|
-
expectedBranches,
|
|
116
|
-
dirtyEntries: status.entries,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const branch = git.currentBranch();
|
|
121
|
-
if (branch === null || branch === undefined || branch === '') {
|
|
122
|
-
return {
|
|
123
|
-
safe: false,
|
|
124
|
-
reason: 'detached-head',
|
|
125
|
-
currentBranch: null,
|
|
126
|
-
expectedBranches,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
if (!expectedBranches.includes(branch)) {
|
|
130
|
-
return {
|
|
131
|
-
safe: false,
|
|
132
|
-
reason: 'wrong-branch',
|
|
133
|
-
currentBranch: branch,
|
|
134
|
-
expectedBranches,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
return {
|
|
138
|
-
safe: true,
|
|
139
|
-
reason: 'clean',
|
|
140
|
-
currentBranch: branch,
|
|
141
|
-
expectedBranches,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Render the operator-facing message for a failed checkout-safety guard.
|
|
147
|
-
*
|
|
148
|
-
* @param {ReturnType<typeof checkCheckoutSafety>} result
|
|
149
|
-
* @returns {string}
|
|
150
|
-
*/
|
|
151
|
-
export function renderCheckoutRefusal(result) {
|
|
152
|
-
const expected = result.expectedBranches.map((b) => `'${b}'`).join(' or ');
|
|
153
|
-
if (result.reason === 'dirty') {
|
|
154
|
-
return (
|
|
155
|
-
`[epic-deliver] Refusing to start: the working tree is dirty. ` +
|
|
156
|
-
`/deliver will not check out ${expected} over uncommitted ` +
|
|
157
|
-
`or untracked changes — commit, stash, or clean them, then re-run.\n` +
|
|
158
|
-
`--- dirty entries ---\n${result.dirtyEntries ?? '(unavailable)'}`
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
if (result.reason === 'detached-head') {
|
|
162
|
-
return (
|
|
163
|
-
`[epic-deliver] Refusing to start: HEAD is detached. /deliver ` +
|
|
164
|
-
`expects HEAD on ${expected}. Check one out before re-running.`
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return (
|
|
168
|
-
`[epic-deliver] Refusing to start: HEAD is on '${result.currentBranch}', ` +
|
|
169
|
-
`not the expected ${expected}. /deliver will not yank HEAD ` +
|
|
170
|
-
`away from your branch — switch to ${expected} yourself before re-running.`
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/**
|
|
175
|
-
* Render the operator-facing message for a refused (live foreign) lease.
|
|
176
|
-
*
|
|
177
|
-
* @param {{ owner: string|null }} lease
|
|
178
|
-
* @param {number} epicId
|
|
179
|
-
* @returns {string}
|
|
180
|
-
*/
|
|
181
|
-
export function renderLeaseRefusal(lease, epicId) {
|
|
182
|
-
return (
|
|
183
|
-
`[epic-deliver] Refusing to start: Epic #${epicId} is already claimed by ` +
|
|
184
|
-
`'${lease.owner}' with a live lease (recent heartbeat within the TTL). ` +
|
|
185
|
-
`Another /deliver run is driving this Epic. Wait for it to finish, ` +
|
|
186
|
-
`or pass --steal to forcibly transfer the claim.`
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Acquire the Epic lease for `operator`, failing closed on a live foreign
|
|
192
|
-
* claim. Returns the `acquireLease` result on success.
|
|
193
|
-
*
|
|
194
|
-
* @param {object} args
|
|
195
|
-
* @param {object} args.provider Ticketing provider.
|
|
196
|
-
* @param {number} args.epicId Epic ticket id.
|
|
197
|
-
* @param {string} args.operator Resolved operator handle.
|
|
198
|
-
* @param {number|null} [args.heartbeatAt] Current owner's last heartbeat (epoch ms).
|
|
199
|
-
* @param {boolean} [args.steal=false] Transfer a live foreign claim.
|
|
200
|
-
* @param {object} [args.config] Resolved config (TTL default).
|
|
201
|
-
* @param {number} [args.now] Injectable clock.
|
|
202
|
-
* @returns {Promise<{ acquired: boolean, owner: string, previousOwner: string|null, reason: string }>}
|
|
203
|
-
* @throws {Error} when the Epic carries a live foreign claim and `steal` is false.
|
|
204
|
-
*/
|
|
205
|
-
export async function acquireEpicLease({
|
|
206
|
-
provider,
|
|
207
|
-
epicId,
|
|
208
|
-
operator,
|
|
209
|
-
heartbeatAt = null,
|
|
210
|
-
steal = false,
|
|
211
|
-
config,
|
|
212
|
-
now,
|
|
213
|
-
}) {
|
|
214
|
-
return acquireLeaseFailClosed({
|
|
215
|
-
provider,
|
|
216
|
-
ticketId: epicId,
|
|
217
|
-
operator,
|
|
218
|
-
heartbeatAt,
|
|
219
|
-
steal,
|
|
220
|
-
config,
|
|
221
|
-
now,
|
|
222
|
-
renderRefusal: renderLeaseRefusal,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Compose the two preflight guards in the order prepare needs them: the
|
|
228
|
-
* checkout-safety check first (cheap, local, no network), then the Epic-lease
|
|
229
|
-
* acquisition (a GitHub round-trip). Both fail closed by throwing.
|
|
230
|
-
*
|
|
231
|
-
* The checkout-safety guard runs first (cheap, local) and always executes. The
|
|
232
|
-
* Epic-lease step then **fails closed**: when `operator` cannot be resolved
|
|
233
|
-
* (null — `--as`, `github.operatorHandle`, and `git user.email` all empty, with
|
|
234
|
-
* the shipped `@[USERNAME]` placeholder normalised to null), this throws rather
|
|
235
|
-
* than running an ownerless, unguarded delivery. The lease is the cross-clone
|
|
236
|
-
* coordination layer; without an identity it cannot serialise concurrent runs,
|
|
237
|
-
* so silently skipping it would defeat the guard.
|
|
238
|
-
*
|
|
239
|
-
* @param {object} args
|
|
240
|
-
* @param {number} args.epicId
|
|
241
|
-
* @param {string|string[]} args.expectedBranch Branch(es) prepare expects HEAD on.
|
|
242
|
-
* @param {object} args.git Injected git shim (statusPorcelain/currentBranch).
|
|
243
|
-
* @param {object} args.provider Ticketing provider for the lease.
|
|
244
|
-
* @param {string|null} args.operator Resolved operator handle (null fails closed → throw).
|
|
245
|
-
* @param {number|null} [args.heartbeatAt] Current owner's last heartbeat (epoch ms).
|
|
246
|
-
* @param {boolean} [args.steal=false] Transfer a live foreign claim.
|
|
247
|
-
* @param {object} [args.config] Resolved config.
|
|
248
|
-
* @param {number} [args.now] Injectable clock.
|
|
249
|
-
* @param {object} [args.logger] Logger with info/warn.
|
|
250
|
-
* @returns {Promise<{
|
|
251
|
-
* checkout: ReturnType<typeof checkCheckoutSafety>,
|
|
252
|
-
* lease: { acquired: boolean, owner: string, reason: string },
|
|
253
|
-
* }>}
|
|
254
|
-
* @throws {Error} on a dirty/wrong-branch tree, an unresolvable operator
|
|
255
|
-
* identity, or a refused live foreign lease.
|
|
256
|
-
*/
|
|
257
|
-
export async function runPrepareGuards({
|
|
258
|
-
epicId,
|
|
259
|
-
expectedBranch,
|
|
260
|
-
git,
|
|
261
|
-
provider,
|
|
262
|
-
operator,
|
|
263
|
-
heartbeatAt = null,
|
|
264
|
-
steal = false,
|
|
265
|
-
config,
|
|
266
|
-
now,
|
|
267
|
-
logger,
|
|
268
|
-
}) {
|
|
269
|
-
const log = logger ?? { info: () => {}, warn: () => {} };
|
|
270
|
-
|
|
271
|
-
const checkout = checkCheckoutSafety({ git, expectedBranch });
|
|
272
|
-
if (!checkout.safe) {
|
|
273
|
-
throw new Error(renderCheckoutRefusal(checkout));
|
|
274
|
-
}
|
|
275
|
-
log.info?.(
|
|
276
|
-
`[epic-deliver] ✅ Checkout-safety guard passed (clean tree on '${checkout.currentBranch}').`,
|
|
277
|
-
);
|
|
278
|
-
|
|
279
|
-
if (!operator) {
|
|
280
|
-
throw new Error(
|
|
281
|
-
'[epic-deliver] Refusing to start: no operator identity could be ' +
|
|
282
|
-
'resolved. --as, github.operatorHandle (unset or still the shipped ' +
|
|
283
|
-
'`@[USERNAME]` placeholder), and git user.email are all empty, so the ' +
|
|
284
|
-
'Epic-lease has no owner and concurrent /deliver runs cannot be ' +
|
|
285
|
-
'serialised. Set your own handle in .agentrc.local.json (e.g. ' +
|
|
286
|
-
'{ "github": { "operatorHandle": "@your-login" } }), pass --as <handle>, ' +
|
|
287
|
-
'or configure git user.email, then re-run.',
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const lease = await acquireEpicLease({
|
|
292
|
-
provider,
|
|
293
|
-
epicId,
|
|
294
|
-
operator,
|
|
295
|
-
heartbeatAt,
|
|
296
|
-
steal,
|
|
297
|
-
config,
|
|
298
|
-
now,
|
|
299
|
-
});
|
|
300
|
-
if (lease.reason === 'stolen') {
|
|
301
|
-
log.warn?.(
|
|
302
|
-
`[epic-deliver] ⚠️ Stole the Epic #${epicId} lease from '${lease.previousOwner}' via --steal (live claim forcibly transferred).`,
|
|
303
|
-
);
|
|
304
|
-
} else {
|
|
305
|
-
log.info?.(
|
|
306
|
-
`[epic-deliver] ✅ Acquired Epic #${epicId} lease for '${operator}' (reason=${lease.reason}).`,
|
|
307
|
-
);
|
|
308
|
-
}
|
|
309
|
-
return { checkout, lease };
|
|
310
|
-
}
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* context.js — Phase 3 of the epic-plan-decompose pipeline (Story #2466).
|
|
3
|
-
*
|
|
4
|
-
* Builds the authoring context (the Epic body — which carries the folded
|
|
5
|
-
* Tech Spec sections and Acceptance Table per Story #4324 — plus
|
|
6
|
-
* heuristics, system prompt, ticket cap) the host LLM /
|
|
7
|
-
* `epic-plan-decompose-author` Skill consumes when producing the ticket
|
|
8
|
-
* JSON array.
|
|
9
|
-
*
|
|
10
|
-
* Extracted verbatim from `epic-plan-decompose.js`; both
|
|
11
|
-
* `buildDecomposerSystemPrompt` and `buildDecompositionContext` retain
|
|
12
|
-
* their public-export contract for the existing unit tests.
|
|
13
|
-
*
|
|
14
|
-
* @module lib/orchestration/epic-plan-decompose/phases/context
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import {
|
|
18
|
-
getLimits,
|
|
19
|
-
resolvePreflightCeilings,
|
|
20
|
-
} from '../../../config-resolver.js';
|
|
21
|
-
import { hasTechSpecContent } from '../../../epic-body-sections.js';
|
|
22
|
-
import { renderDecomposerSystemPrompt } from '../../../templates/decomposer-prompts.js';
|
|
23
|
-
import { read as readPlanState } from '../../epic-plan-state-store.js';
|
|
24
|
-
import { applyBudget } from '../../planning-context-budget.js';
|
|
25
|
-
|
|
26
|
-
export function buildDecomposerSystemPrompt(
|
|
27
|
-
heuristics = [],
|
|
28
|
-
{ maxTickets, maxTokenBudget, epicId } = {},
|
|
29
|
-
) {
|
|
30
|
-
const base = renderDecomposerSystemPrompt({
|
|
31
|
-
maxTickets,
|
|
32
|
-
maxTokenBudget,
|
|
33
|
-
epicId,
|
|
34
|
-
});
|
|
35
|
-
const heuristicsStr =
|
|
36
|
-
heuristics.length > 0
|
|
37
|
-
? `### RISK HEURISTICS (planning metadata if any apply):\n- ${heuristics.join('\n- ')}`
|
|
38
|
-
: '';
|
|
39
|
-
return `${base}${heuristicsStr ? `\n\n${heuristicsStr}` : ''}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function resolveHeuristics(config) {
|
|
43
|
-
if (Array.isArray(config.planning?.riskHeuristics)) {
|
|
44
|
-
return config.planning.riskHeuristics;
|
|
45
|
-
}
|
|
46
|
-
return config.agentSettings?.planning?.riskHeuristics || [];
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function projectBudgetedEntry(item, ticket, mode) {
|
|
50
|
-
if (mode === 'full') return { id: ticket.id, body: ticket.body };
|
|
51
|
-
return { id: ticket.id, body: null, bodySummary: item };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Read the persisted planning decision for the Epic so the decomposition
|
|
56
|
-
* authoring step (Phase 8) can cite the same risk classification the
|
|
57
|
-
* gate routing used in Phase 7. The decision lives in the `epic-plan-state`
|
|
58
|
-
* structured comment written by `epic-plan-spec.js`.
|
|
59
|
-
*
|
|
60
|
-
* Returns `{ planningRisk: null, reviewRouting: null }` when the comment
|
|
61
|
-
* is missing (older plans planned before Story #2801 landed) or when the
|
|
62
|
-
* payload predates the fields. The null sentinels are part of the
|
|
63
|
-
* decomposition context contract — callers MUST be able to JSON.stringify
|
|
64
|
-
* the result without losing the keys.
|
|
65
|
-
*
|
|
66
|
-
* @param {import('../../../ITicketingProvider.js').ITicketingProvider} provider
|
|
67
|
-
* @param {number} epicId
|
|
68
|
-
* @returns {Promise<{ planningRisk: object | null, reviewRouting: object | null }>}
|
|
69
|
-
*/
|
|
70
|
-
async function readPlanningDecision(provider, epicId) {
|
|
71
|
-
let state = null;
|
|
72
|
-
try {
|
|
73
|
-
state = await readPlanState({ provider, epicId });
|
|
74
|
-
} catch (_err) {
|
|
75
|
-
// `read` already swallows JSON parse errors; a thrown error here
|
|
76
|
-
// means the provider couldn't fetch comments. Treat as null state —
|
|
77
|
-
// the decomposer can still author tickets without the risk envelope.
|
|
78
|
-
state = null;
|
|
79
|
-
}
|
|
80
|
-
if (!state || typeof state !== 'object') {
|
|
81
|
-
return { planningRisk: null, reviewRouting: null };
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
planningRisk: state.planningRisk ?? null,
|
|
85
|
-
reviewRouting: state.reviewRouting ?? null,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async function fetchPlanningEpic(provider, epicId) {
|
|
90
|
-
const epic = await provider.getEpic(epicId);
|
|
91
|
-
if (!epic || !hasTechSpecContent(epic.body ?? '')) {
|
|
92
|
-
throw new Error(
|
|
93
|
-
`[Decomposer] Epic #${epicId} body carries no Tech Spec sections (no ## Delivery Slicing). Run the Epic Planner (Phase 7) first.`,
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
return { epic };
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Build the authoring context the host LLM (or the
|
|
101
|
-
* `epic-plan-decompose-author` Skill) needs to produce the ticket JSON.
|
|
102
|
-
*
|
|
103
|
-
* The Epic body (ideation sections + folded Tech Spec sections +
|
|
104
|
-
* Acceptance Table — the AC-ID source for wave-0 BDD scaffold tags) is
|
|
105
|
-
* bounded by the planning-context budget (Epic #817 Story 9). Pass
|
|
106
|
-
* `{ fullContext: true }` (CLI: `--full-context`) to restore the
|
|
107
|
-
* unbounded full body.
|
|
108
|
-
*/
|
|
109
|
-
export async function buildDecompositionContext(
|
|
110
|
-
epicId,
|
|
111
|
-
provider,
|
|
112
|
-
config = {},
|
|
113
|
-
opts = {},
|
|
114
|
-
) {
|
|
115
|
-
const { epic } = await fetchPlanningEpic(provider, epicId);
|
|
116
|
-
const { planningRisk, reviewRouting } = await readPlanningDecision(
|
|
117
|
-
provider,
|
|
118
|
-
epicId,
|
|
119
|
-
);
|
|
120
|
-
const heuristics = resolveHeuristics(config);
|
|
121
|
-
const limits = getLimits(config);
|
|
122
|
-
const maxTickets = limits.maxTickets;
|
|
123
|
-
const maxTokenBudget = limits.maxTokenBudget;
|
|
124
|
-
const planningLimits = limits.planningContext;
|
|
125
|
-
const { fullContext = false } = opts;
|
|
126
|
-
const systemPrompt = buildDecomposerSystemPrompt(heuristics, {
|
|
127
|
-
maxTickets,
|
|
128
|
-
maxTokenBudget,
|
|
129
|
-
epicId,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
const budgeted = applyBudget(
|
|
133
|
-
[{ path: `epic-${epic.id}.md`, content: epic.body ?? '' }],
|
|
134
|
-
planningLimits,
|
|
135
|
-
{ fullContext },
|
|
136
|
-
);
|
|
137
|
-
const [epicItem] = budgeted.items;
|
|
138
|
-
return {
|
|
139
|
-
epic: { id: epic.id, title: epic.title },
|
|
140
|
-
// Story #4324 — the Epic body is the single planning document: it
|
|
141
|
-
// carries the ideation sections, the folded Tech Spec sections
|
|
142
|
-
// (## Delivery Slicing first), and the ## Acceptance Table the wave-0
|
|
143
|
-
// BDD scaffold reads its AC IDs from.
|
|
144
|
-
epicBody: projectBudgetedEntry(epicItem, epic, budgeted.mode),
|
|
145
|
-
heuristics,
|
|
146
|
-
systemPrompt,
|
|
147
|
-
maxTickets,
|
|
148
|
-
// Story #3875 — surface the real delivery envelope to the decomposer
|
|
149
|
-
// so Stories are sized against the hydration budget and the
|
|
150
|
-
// configured preflight ceilings rather than guessed. Story #4162 also
|
|
151
|
-
// threads this value into the rendered systemPrompt above as a sizing
|
|
152
|
-
// input so the prompt itself names the budget.
|
|
153
|
-
maxTokenBudget,
|
|
154
|
-
preflightCeilings: resolvePreflightCeilings(config),
|
|
155
|
-
contextMode: budgeted.mode,
|
|
156
|
-
// Story #2801 — surface the Phase 7 planning decision so the
|
|
157
|
-
// decomposition authoring step can cite the same risk
|
|
158
|
-
// classification used by gate routing. Both fields are `null`
|
|
159
|
-
// when the Epic was planned before the decision contract existed.
|
|
160
|
-
planningRisk,
|
|
161
|
-
reviewRouting,
|
|
162
|
-
};
|
|
163
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* creation.js — sub-issue link reconciliation, Epic label transitions,
|
|
3
|
-
* the advisory ticket-cap warning, and the blocked-by dependency wiring
|
|
4
|
-
* used by the reconciler-based persist flow (`persist.js`).
|
|
5
|
-
*
|
|
6
|
-
* Exports: `reconcileSubIssueLinks`, `setBlockedByDependencies`,
|
|
7
|
-
* `setEpicLabel`, `warnTicketCapNearLimit`.
|
|
8
|
-
*
|
|
9
|
-
* @module lib/orchestration/epic-plan-decompose/phases/creation
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { applyBlockedByDependencies } from '../../../../providers/github/blocked-by-add.js';
|
|
13
|
-
import { Logger } from '../../../Logger.js';
|
|
14
|
-
import { AGENT_LABELS } from '../../../label-constants.js';
|
|
15
|
-
|
|
16
|
-
export async function reconcileSubIssueLinks(epicId, provider) {
|
|
17
|
-
if (typeof provider.reconcileSubIssueLinks !== 'function') return;
|
|
18
|
-
Logger.info(
|
|
19
|
-
`[Decomposer] Reconciling sub-issue API links for Epic #${epicId}...`,
|
|
20
|
-
);
|
|
21
|
-
const result = await provider.reconcileSubIssueLinks(epicId);
|
|
22
|
-
const { totalExpected, alreadyLinked, reconciled, failed, failures } = result;
|
|
23
|
-
if (failed === 0) {
|
|
24
|
-
const reconciledNote = reconciled > 0 ? ` (${reconciled} reconciled)` : '';
|
|
25
|
-
Logger.info(
|
|
26
|
-
`[Decomposer] linked ${alreadyLinked + reconciled}/${totalExpected} sub-issues${reconciledNote}`,
|
|
27
|
-
);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
for (const failure of failures) {
|
|
31
|
-
Logger.error(
|
|
32
|
-
`[Decomposer] sub-issue link gap: parent #${failure.parentId} ← child #${failure.childId}: ${failure.reason}`,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
throw new Error(
|
|
36
|
-
`[Decomposer] Sub-issue reconciliation incomplete: ${failed}/${totalExpected} links could not be established (linked=${alreadyLinked}, reconciled=${reconciled}). See log for per-child reasons.`,
|
|
37
|
-
);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Translate each Story's `dependsOn` slug list into native GitHub "blocked
|
|
42
|
-
* by" dependency edges. Best-effort and non-fatal: individual edge failures
|
|
43
|
-
* are logged as warnings and do not abort the decompose phase.
|
|
44
|
-
*
|
|
45
|
-
* Requires the provider to expose `owner`, `repo`, `_gh`, and `getTicket`.
|
|
46
|
-
* No-ops silently when any required surface is absent so callers remain
|
|
47
|
-
* safe across provider stubs.
|
|
48
|
-
*
|
|
49
|
-
* @param {number} epicId
|
|
50
|
-
* @param {import('../../../ITicketingProvider.js').ITicketingProvider} provider
|
|
51
|
-
* @param {object} spec — the parsed YAML spec (has `.stories[*].dependsOn`).
|
|
52
|
-
* @param {object} stateMapping — slug → `{ issueNumber }` from the reconciler state.
|
|
53
|
-
*/
|
|
54
|
-
export async function setBlockedByDependencies(
|
|
55
|
-
epicId,
|
|
56
|
-
provider,
|
|
57
|
-
spec,
|
|
58
|
-
stateMapping,
|
|
59
|
-
) {
|
|
60
|
-
const stories = Array.isArray(spec?.stories) ? spec.stories : [];
|
|
61
|
-
const hasDeps = stories.some(
|
|
62
|
-
(s) => Array.isArray(s.dependsOn) && s.dependsOn.length > 0,
|
|
63
|
-
);
|
|
64
|
-
if (!hasDeps) return;
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
!provider?.owner ||
|
|
68
|
-
!provider?.repo ||
|
|
69
|
-
!provider?._gh ||
|
|
70
|
-
typeof provider.getTicket !== 'function'
|
|
71
|
-
) {
|
|
72
|
-
Logger.warn(
|
|
73
|
-
`[Decomposer] setBlockedByDependencies: provider missing required surface (owner/repo/_gh/getTicket); skipping.`,
|
|
74
|
-
);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
Logger.info(
|
|
79
|
-
`[Decomposer] Setting native blocked-by dependency edges for Epic #${epicId}...`,
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
// Build a slug → issueNumber map from the reconciler state mapping.
|
|
83
|
-
const slugToIssueNumber = {};
|
|
84
|
-
for (const [slug, entry] of Object.entries(stateMapping ?? {})) {
|
|
85
|
-
if (typeof entry?.issueNumber === 'number') {
|
|
86
|
-
slugToIssueNumber[slug] = entry.issueNumber;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const result = await applyBlockedByDependencies({
|
|
91
|
-
stories,
|
|
92
|
-
slugToIssueNumber,
|
|
93
|
-
getTicket: (n) => provider.getTicket(n),
|
|
94
|
-
owner: provider.owner,
|
|
95
|
-
repo: provider.repo,
|
|
96
|
-
gh: provider._gh,
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
const { edgesAdded, edgesSkipped, edgesFailed, storiesProcessed } = result;
|
|
100
|
-
Logger.info(
|
|
101
|
-
`[Decomposer] blocked-by edges: ${edgesAdded} added, ${edgesSkipped} already present, ${edgesFailed} failed (${storiesProcessed} stories with deps processed).`,
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
export async function setEpicLabel(provider, epicId, targetLabel) {
|
|
106
|
-
const planningLabels = [AGENT_LABELS.REVIEW_SPEC, AGENT_LABELS.READY];
|
|
107
|
-
await provider.updateTicket(epicId, {
|
|
108
|
-
labels: {
|
|
109
|
-
add: [targetLabel],
|
|
110
|
-
remove: planningLabels.filter((l) => l !== targetLabel),
|
|
111
|
-
},
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Advisory-only ticket-count check (Story #2798).
|
|
117
|
-
*
|
|
118
|
-
* `maxTickets` is a **reviewability budget**, not a hard authoring cap.
|
|
119
|
-
* This helper emits a non-destructive warning when a decomposition meets
|
|
120
|
-
* or exceeds the budget so the operator can spot over-budget plans early
|
|
121
|
-
* in the persist flow. It never blocks — the hard gate lives in the
|
|
122
|
-
* `runDecomposePhase` over-budget check, which requires an explicit
|
|
123
|
-
* `allowOverBudget` (CLI: `--allow-over-budget`) override.
|
|
124
|
-
*
|
|
125
|
-
* @param {Array} tickets
|
|
126
|
-
* @param {number} maxTickets — the reviewability budget
|
|
127
|
-
* @param {string} [tag] — log prefix
|
|
128
|
-
* @param {{ logger?: Pick<typeof Logger, 'warn'> }} [opts]
|
|
129
|
-
*/
|
|
130
|
-
export function warnTicketCapNearLimit(
|
|
131
|
-
tickets,
|
|
132
|
-
maxTickets,
|
|
133
|
-
tag = 'epic-plan-decompose',
|
|
134
|
-
{ logger = Logger } = {},
|
|
135
|
-
) {
|
|
136
|
-
if (tickets.length < maxTickets) return;
|
|
137
|
-
logger.warn(
|
|
138
|
-
`[${tag}] ⚠️ Received ${tickets.length} tickets against a reviewability budget of ${maxTickets}. Review the Story decomposition before persisting; over-budget persistence requires --allow-over-budget.`,
|
|
139
|
-
);
|
|
140
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* dag.js — Phase 2 of the epic-plan-decompose pipeline (Story #2466).
|
|
3
|
-
*
|
|
4
|
-
* Owns the deterministic DAG helpers used by the reconciler pipeline:
|
|
5
|
-
* - `resolveDependencies(ticket, slugMap)`
|
|
6
|
-
* - `orderTicketsForCreation(validated)` (topological sort of the
|
|
7
|
-
* Story set so dependency producers are created before consumers)
|
|
8
|
-
*
|
|
9
|
-
* Extracted verbatim from `epic-plan-decompose.js` so the named exports
|
|
10
|
-
* (`resolveDependencies`, `orderTicketsForCreation`) that the
|
|
11
|
-
* `tests/ticket-decomposer.test.js` suite imports keep their contract.
|
|
12
|
-
*
|
|
13
|
-
* @module lib/orchestration/epic-plan-decompose/phases/dag
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
export function resolveDependencies(ticket, slugMap) {
|
|
17
|
-
const resolved = [];
|
|
18
|
-
for (const dep of ticket.depends_on || []) {
|
|
19
|
-
const depId = slugMap.get(dep);
|
|
20
|
-
if (depId === undefined) {
|
|
21
|
-
// Unreachable through normal flow: validateAndNormalizeTickets
|
|
22
|
-
// already rejects unknown slugs and the topological sort guarantees
|
|
23
|
-
// creation order. A throw here turns a future regression (e.g.
|
|
24
|
-
// someone bypassing the validator) into a loud failure instead of a
|
|
25
|
-
// silently-dropped DAG edge.
|
|
26
|
-
throw new Error(
|
|
27
|
-
`[Decomposer] ${ticket.type.toUpperCase()} "${ticket.title}" (${ticket.slug}) depends on unresolved slug "${dep}". This indicates a planner bug or out-of-order ticket creation.`,
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
resolved.push(depId);
|
|
31
|
-
}
|
|
32
|
-
return resolved;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function topoSortGroup(group) {
|
|
36
|
-
const slugToTicket = new Map(group.map((t) => [t.slug, t]));
|
|
37
|
-
const visited = new Set();
|
|
38
|
-
const sorted = [];
|
|
39
|
-
|
|
40
|
-
function visit(t) {
|
|
41
|
-
if (visited.has(t.slug)) return;
|
|
42
|
-
visited.add(t.slug);
|
|
43
|
-
for (const dep of t.depends_on ?? []) {
|
|
44
|
-
const depTicket = slugToTicket.get(dep);
|
|
45
|
-
if (depTicket) visit(depTicket);
|
|
46
|
-
}
|
|
47
|
-
sorted.push(t);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
for (const t of group) visit(t);
|
|
51
|
-
return sorted;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Topologically sort the Story set so intra-set dep chains resolve
|
|
56
|
-
* before their dependents are created. The 2-tier hierarchy has a
|
|
57
|
-
* single ticket type (story) attached directly to the Epic, so there
|
|
58
|
-
* is no parent-type ordering to interleave.
|
|
59
|
-
*/
|
|
60
|
-
export function orderTicketsForCreation(validated) {
|
|
61
|
-
const result = [];
|
|
62
|
-
for (const t of topoSortGroup([...validated])) result.push(t);
|
|
63
|
-
return result;
|
|
64
|
-
}
|