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,378 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/orchestration/lifecycle/listeners/index.js
|
|
2
|
-
/**
|
|
3
|
-
* Default listener-chain builder for the standalone `lifecycle-emit`
|
|
4
|
-
* CLI surface (Story #2510, Epic #2501).
|
|
5
|
-
*
|
|
6
|
-
* This is the SOLE production wiring path for the close-tail listener
|
|
7
|
-
* roster. (The in-process `epic-runner/factory.js` that previously
|
|
8
|
-
* mirrored this roster for an in-session runner was deleted with the
|
|
9
|
-
* dead epic-runner stratum in Story #3908; the host-LLM-drives-CLIs
|
|
10
|
-
* model reaches every close-tail listener through the `lifecycle-emit.js`
|
|
11
|
-
* CLI shells in `/deliver`'s Phase 6 / 7.5 / 8 markdown invocations,
|
|
12
|
-
* which call this builder.)
|
|
13
|
-
*
|
|
14
|
-
* Canonical roster (registration order):
|
|
15
|
-
* 1. LedgerWriter (privileged hooks via `register(bus)`)
|
|
16
|
-
* 2. AcceptanceReconciler (epic.close.end → acceptance.reconcile.*)
|
|
17
|
-
* 3. Finalizer (acceptance.reconcile.{ok,waived} → pr.created)
|
|
18
|
-
* 4. AutomergeArmer (epic.merge.ready → epic.merge.armed)
|
|
19
|
-
* 5. AutomergePredicate (epic.watch.end → epic.merge.{ready,blocked})
|
|
20
|
-
* 6. BranchCleaner (epic.cleanup.start → branch reap)
|
|
21
|
-
* 7. MergeWatcher (epic.merge.armed → epic.merge.confirmed)
|
|
22
|
-
* 8. Cleaner (epic.merge.confirmed → epic.cleanup.* / epic.complete)
|
|
23
|
-
* 9. LabelTransitioner (epic.complete → Epic ticket flips to agent::done)
|
|
24
|
-
* 10. CheckpointPointerWriter (every *.end → checkpoint.json)
|
|
25
|
-
*
|
|
26
|
-
* The bus contract requires LedgerWriter first: its `onEmitted` hook
|
|
27
|
-
* lands the `emitted` ledger record on disk BEFORE any listener body
|
|
28
|
-
* executes, so a crash mid-chain leaves a recoverable trail.
|
|
29
|
-
*
|
|
30
|
-
* Listeners whose constructors require collaborators that are not
|
|
31
|
-
* available outside the runner (e.g. AutomergePredicate's `provider`,
|
|
32
|
-
* BranchCleaner's `checkpointer`) are SKIPPED with a debug log rather
|
|
33
|
-
* than constructed. This matches the factory's defensive guard pattern
|
|
34
|
-
* — registration is best-effort; the chain still wires every listener
|
|
35
|
-
* whose dependencies are satisfiable.
|
|
36
|
-
*
|
|
37
|
-
* Signature: `buildDefaultListenerChain({ bus, ledgerPath, repoRoot })`.
|
|
38
|
-
* The Tech Spec for Epic #2501 (§ Story 4) fixes the public shape at
|
|
39
|
-
* those three keys. `ledgerPath` is decomposed into `tempRoot` and
|
|
40
|
-
* `epicId` so the listener constructors receive what they need;
|
|
41
|
-
* `repoRoot` threads through as `cwd` for listeners that shell out
|
|
42
|
-
* (`Finalizer`, `AutomergeArmer`).
|
|
43
|
-
*
|
|
44
|
-
* Maintainability exemption (refs #3685): this module is listed under
|
|
45
|
-
* `delivery.quality.gates.maintainability.ignoreGlobs` in `.agentrc.json`.
|
|
46
|
-
* Its sole content is a linear, low-branching listener-registration
|
|
47
|
-
* sequence; the maintainability index mis-gauges that shape (the same
|
|
48
|
-
* reason the declarative `config-settings-schema*` files are exempt).
|
|
49
|
-
* Splitting the sequence across sibling builder modules purely to clear
|
|
50
|
-
* the floor would add indirection without making the wiring easier to
|
|
51
|
-
* read, so the debt is carried explicitly here rather than hidden behind
|
|
52
|
-
* a blanket low floor.
|
|
53
|
-
*/
|
|
54
|
-
|
|
55
|
-
import path from 'node:path';
|
|
56
|
-
|
|
57
|
-
import { createLedgerWriter } from '../ledger-writer.js';
|
|
58
|
-
import { AcceptanceReconciler } from './acceptance-reconciler.js';
|
|
59
|
-
import { AutomergeArmer } from './automerge-armer.js';
|
|
60
|
-
import { AutomergePredicate } from './automerge-predicate.js';
|
|
61
|
-
import { BranchCleaner } from './branch-cleaner.js';
|
|
62
|
-
import { CheckpointPointerWriter } from './checkpoint-pointer-writer.js';
|
|
63
|
-
import { Cleaner } from './cleaner.js';
|
|
64
|
-
import { Finalizer } from './finalizer.js';
|
|
65
|
-
import { LabelTransitioner } from './label-transitioner.js';
|
|
66
|
-
import { MergeWatcher } from './merge-watcher.js';
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Parse `temp/epic-<id>/lifecycle.ndjson` into `{ tempRoot, epicId }`.
|
|
70
|
-
*
|
|
71
|
-
* Throws when the input does not match the canonical layout. The
|
|
72
|
-
* standalone `lifecycle-emit` CLI is the sole production caller and it
|
|
73
|
-
* always feeds the canonical `epicLedgerPath(eid)` value, so a mismatch
|
|
74
|
-
* here is a programmer error — surface it loudly.
|
|
75
|
-
*
|
|
76
|
-
* @param {string} ledgerPath
|
|
77
|
-
* @returns {{ tempRoot: string, epicId: number }}
|
|
78
|
-
*/
|
|
79
|
-
export function parseLedgerPath(ledgerPath) {
|
|
80
|
-
if (typeof ledgerPath !== 'string' || ledgerPath.length === 0) {
|
|
81
|
-
throw new TypeError(
|
|
82
|
-
'buildDefaultListenerChain: ledgerPath must be a non-empty string',
|
|
83
|
-
);
|
|
84
|
-
}
|
|
85
|
-
const epicDir = path.dirname(ledgerPath);
|
|
86
|
-
const tempRoot = path.dirname(epicDir);
|
|
87
|
-
const epicDirName = path.basename(epicDir);
|
|
88
|
-
const m = /^epic-(\d+)$/.exec(epicDirName);
|
|
89
|
-
if (!m) {
|
|
90
|
-
throw new Error(
|
|
91
|
-
`buildDefaultListenerChain: ledgerPath does not match temp/epic-<id>/lifecycle.ndjson layout (got ${ledgerPath})`,
|
|
92
|
-
);
|
|
93
|
-
}
|
|
94
|
-
const epicId = Number.parseInt(m[1], 10);
|
|
95
|
-
if (!Number.isInteger(epicId) || epicId < 1) {
|
|
96
|
-
throw new Error(
|
|
97
|
-
`buildDefaultListenerChain: parsed epicId from ledgerPath is not a positive integer (got ${m[1]})`,
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
return { tempRoot, epicId };
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Build and subscribe the canonical default listener chain onto the
|
|
105
|
-
* supplied bus.
|
|
106
|
-
*
|
|
107
|
-
* @param {object} opts
|
|
108
|
-
* @param {object} opts.bus Lifecycle bus instance (must expose `on`,
|
|
109
|
-
* `emit`, and the privileged `onEmitted` / `onCompleted` / `onFailed`
|
|
110
|
-
* hook seam).
|
|
111
|
-
* @param {string} opts.ledgerPath Absolute or repo-relative path to the
|
|
112
|
-
* Epic's `lifecycle.ndjson`. Decomposed into `{ tempRoot, epicId }`
|
|
113
|
-
* for downstream constructors.
|
|
114
|
-
* @param {string} opts.repoRoot Absolute path used as `cwd` for
|
|
115
|
-
* listeners that shell out (Finalizer, AutomergeArmer, BranchCleaner).
|
|
116
|
-
* @param {object} [opts.provider] Ticketing provider. When omitted,
|
|
117
|
-
* AutomergePredicate is skipped (the listener constructor throws on
|
|
118
|
-
* a missing provider).
|
|
119
|
-
* @param {object} [opts.config] Resolved agent config. Forwarded to the
|
|
120
|
-
* AcceptanceReconciler.
|
|
121
|
-
* @param {object} [opts.checkpointer] Epic-run-state checkpoint reader.
|
|
122
|
-
* When omitted, BranchCleaner is skipped.
|
|
123
|
-
* @param {object} [opts.logger] Logger surface (`debug`/`warn`/`error`).
|
|
124
|
-
* @param {boolean} [opts.headless] Explicit must-land signal (Story
|
|
125
|
-
* #4427), threaded straight through to `MergeWatcher`. Defaults to
|
|
126
|
-
* `false` (attended-mode behavior unchanged). `lifecycle-emit.js`
|
|
127
|
-
* resolves this from its own `--headless` runtime flag — an explicit
|
|
128
|
-
* input, never an ambient global.
|
|
129
|
-
*
|
|
130
|
-
* @returns {Promise<{
|
|
131
|
-
* ledgerWriter: object,
|
|
132
|
-
* acceptanceReconciler: object,
|
|
133
|
-
* finalizer: object,
|
|
134
|
-
* automergeArmer: object,
|
|
135
|
-
* automergePredicate: object|null,
|
|
136
|
-
* branchCleaner: object|null,
|
|
137
|
-
* cleaner: object,
|
|
138
|
-
* checkpointPointerWriter: object,
|
|
139
|
-
* order: string[]
|
|
140
|
-
* }>}
|
|
141
|
-
*/
|
|
142
|
-
export async function buildDefaultListenerChain(opts = {}) {
|
|
143
|
-
const {
|
|
144
|
-
bus,
|
|
145
|
-
ledgerPath,
|
|
146
|
-
repoRoot,
|
|
147
|
-
provider = null,
|
|
148
|
-
config = null,
|
|
149
|
-
checkpointer = null,
|
|
150
|
-
logger = console,
|
|
151
|
-
headless = false,
|
|
152
|
-
} = opts;
|
|
153
|
-
if (
|
|
154
|
-
!bus ||
|
|
155
|
-
typeof bus.on !== 'function' ||
|
|
156
|
-
typeof bus.emit !== 'function' ||
|
|
157
|
-
typeof bus.onEmitted !== 'function'
|
|
158
|
-
) {
|
|
159
|
-
throw new TypeError(
|
|
160
|
-
'buildDefaultListenerChain: bus must expose on/emit and the privileged onEmitted/onCompleted/onFailed seam',
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
if (typeof repoRoot !== 'string' || repoRoot.length === 0) {
|
|
164
|
-
throw new TypeError(
|
|
165
|
-
'buildDefaultListenerChain: repoRoot must be a non-empty string',
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
const { tempRoot, epicId } = parseLedgerPath(ledgerPath);
|
|
169
|
-
|
|
170
|
-
const order = [];
|
|
171
|
-
|
|
172
|
-
// 1. LedgerWriter — MUST be first. Uses the privileged hook seam, so
|
|
173
|
-
// its registration call is `register(bus)` rather than a regular
|
|
174
|
-
// `bus.on()` subscription. The bus contract guarantees `emitted`
|
|
175
|
-
// lands on disk before any listener body executes.
|
|
176
|
-
const ledgerWriter = createLedgerWriter({ epicId, tempRoot });
|
|
177
|
-
ledgerWriter.register(bus);
|
|
178
|
-
order.push('LedgerWriter');
|
|
179
|
-
|
|
180
|
-
// 2. AcceptanceReconciler — gates Finalize on close-time AC coverage.
|
|
181
|
-
const acceptanceReconciler = new AcceptanceReconciler({
|
|
182
|
-
bus,
|
|
183
|
-
epicId,
|
|
184
|
-
cwd: repoRoot,
|
|
185
|
-
provider,
|
|
186
|
-
config,
|
|
187
|
-
logger,
|
|
188
|
-
});
|
|
189
|
-
acceptanceReconciler.register();
|
|
190
|
-
order.push('AcceptanceReconciler');
|
|
191
|
-
|
|
192
|
-
// 3. Finalizer — opens the PR on acceptance.reconcile.ok or .waived
|
|
193
|
-
// (Story #2893 split waiver out of .skipped) via the bus-owned
|
|
194
|
-
// default (`composeBusOwnedFinalize`, Story #2894), which chains
|
|
195
|
-
// openOrLocatePr → postHandoffComment and
|
|
196
|
-
// emits `epic.merge.ready` on success. The legacy
|
|
197
|
-
// `d1-default-no-op` blocker is gone; the listener constructed
|
|
198
|
-
// with no `runFinalizeFn` override always runs the real flow.
|
|
199
|
-
// The code-review / audit-results graduator steps (Stories
|
|
200
|
-
// #2555 / #2615) remain best-effort and are silently skipped
|
|
201
|
-
// when `provider` / `currentRepo` are not wired in. `currentRepo`
|
|
202
|
-
// is resolved from `config.github.{owner,repo}`; `frameworkRepo`
|
|
203
|
-
// from `config.github.frameworkRepo` when distinct, otherwise omitted.
|
|
204
|
-
const currentRepo =
|
|
205
|
-
config?.github?.owner && config?.github?.repo
|
|
206
|
-
? { owner: config.github.owner, repo: config.github.repo }
|
|
207
|
-
: null;
|
|
208
|
-
const frameworkRepo =
|
|
209
|
-
config?.github?.frameworkRepo?.owner && config?.github?.frameworkRepo?.repo
|
|
210
|
-
? {
|
|
211
|
-
owner: config.github.frameworkRepo.owner,
|
|
212
|
-
repo: config.github.frameworkRepo.repo,
|
|
213
|
-
}
|
|
214
|
-
: null;
|
|
215
|
-
const finalizer = new Finalizer({
|
|
216
|
-
bus,
|
|
217
|
-
epicId,
|
|
218
|
-
cwd: repoRoot,
|
|
219
|
-
provider,
|
|
220
|
-
config,
|
|
221
|
-
currentRepo,
|
|
222
|
-
frameworkRepo,
|
|
223
|
-
logger,
|
|
224
|
-
});
|
|
225
|
-
finalizer.register();
|
|
226
|
-
order.push('Finalizer');
|
|
227
|
-
|
|
228
|
-
// 4. AutomergeArmer — arms `gh pr merge --auto --squash --delete-branch`
|
|
229
|
-
// on epic.merge.ready. Story #4472: `headless` gates the direct-merge
|
|
230
|
-
// fallback's terminal escalation (a genuine arm failure emits
|
|
231
|
-
// `merge.unlanded` + `epic.blocked` in a `--yes` run instead of
|
|
232
|
-
// returning silently); `epicId` scopes the `merge.unlanded` ledger row.
|
|
233
|
-
const automergeArmer = new AutomergeArmer({
|
|
234
|
-
bus,
|
|
235
|
-
epicId,
|
|
236
|
-
cwd: repoRoot,
|
|
237
|
-
headless,
|
|
238
|
-
logger,
|
|
239
|
-
});
|
|
240
|
-
automergeArmer.register();
|
|
241
|
-
order.push('AutomergeArmer');
|
|
242
|
-
|
|
243
|
-
// 5. AutomergePredicate — emits epic.merge.{ready,blocked} based on
|
|
244
|
-
// the runtime predicate evaluation. Requires a truthy `provider`;
|
|
245
|
-
// skip cleanly when the caller omitted one (lifecycle-emit CLI
|
|
246
|
-
// has no provider wired in by default). `config` selects the
|
|
247
|
-
// `delivery.ci.autoMerge` posture (trust-ci default vs strict) and
|
|
248
|
-
// `cwd` (repoRoot) is where the live `gh pr checks --required`
|
|
249
|
-
// probe shells out (Story #4361).
|
|
250
|
-
let automergePredicate = null;
|
|
251
|
-
if (provider) {
|
|
252
|
-
automergePredicate = new AutomergePredicate({
|
|
253
|
-
bus,
|
|
254
|
-
epicId,
|
|
255
|
-
provider,
|
|
256
|
-
config,
|
|
257
|
-
cwd: repoRoot,
|
|
258
|
-
headless,
|
|
259
|
-
logger,
|
|
260
|
-
});
|
|
261
|
-
automergePredicate.register();
|
|
262
|
-
order.push('AutomergePredicate');
|
|
263
|
-
} else {
|
|
264
|
-
logger?.debug?.(
|
|
265
|
-
'[lifecycle] buildDefaultListenerChain: skipping AutomergePredicate (no provider)',
|
|
266
|
-
);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
// 6. BranchCleaner — reaps story/epic branches on epic.cleanup.start.
|
|
270
|
-
// Requires a checkpointer exposing `read()`; skip cleanly when
|
|
271
|
-
// the caller omitted one.
|
|
272
|
-
let branchCleaner = null;
|
|
273
|
-
if (checkpointer && typeof checkpointer.read === 'function') {
|
|
274
|
-
branchCleaner = new BranchCleaner({
|
|
275
|
-
bus,
|
|
276
|
-
epicId,
|
|
277
|
-
checkpointer,
|
|
278
|
-
cwd: repoRoot,
|
|
279
|
-
logger,
|
|
280
|
-
});
|
|
281
|
-
branchCleaner.register();
|
|
282
|
-
order.push('BranchCleaner');
|
|
283
|
-
} else {
|
|
284
|
-
logger?.debug?.(
|
|
285
|
-
'[lifecycle] buildDefaultListenerChain: skipping BranchCleaner (no checkpointer)',
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// 7. MergeWatcher (Story #2896, Epic #2880) — polls `gh pr view`
|
|
290
|
-
// after `epic.merge.armed` until the PR's mergeCommit is
|
|
291
|
-
// non-null, then emits `epic.merge.confirmed`. Cleaner now
|
|
292
|
-
// waits on the confirmed event rather than the armed event so
|
|
293
|
-
// the Epic only transitions to its terminal state after the
|
|
294
|
-
// merge is actually observed on GitHub. Reads `intervalSeconds`
|
|
295
|
-
// and `maxBudgetSeconds` from `config.delivery.mergeWatch.*`
|
|
296
|
-
// when supplied; otherwise uses the listener's framework
|
|
297
|
-
// defaults (30s / 3600s).
|
|
298
|
-
const mergeWatchConfig = config?.delivery?.mergeWatch ?? {};
|
|
299
|
-
const mergeWatcher = new MergeWatcher({
|
|
300
|
-
bus,
|
|
301
|
-
epicId,
|
|
302
|
-
tempRoot,
|
|
303
|
-
cwd: repoRoot,
|
|
304
|
-
intervalSeconds: mergeWatchConfig.intervalSeconds,
|
|
305
|
-
maxBudgetSeconds: mergeWatchConfig.maxBudgetSeconds,
|
|
306
|
-
headless,
|
|
307
|
-
logger,
|
|
308
|
-
});
|
|
309
|
-
mergeWatcher.register();
|
|
310
|
-
order.push('MergeWatcher');
|
|
311
|
-
|
|
312
|
-
// 8. Cleaner — archives temp/epic-<id>/ and emits the terminal
|
|
313
|
-
// epic.cleanup.start → .end → epic.complete sequence. Story
|
|
314
|
-
// #2896 rebound this listener from `epic.merge.armed` to
|
|
315
|
-
// `epic.merge.confirmed`.
|
|
316
|
-
const cleaner = new Cleaner({
|
|
317
|
-
bus,
|
|
318
|
-
epicId,
|
|
319
|
-
tempRoot,
|
|
320
|
-
logger,
|
|
321
|
-
});
|
|
322
|
-
cleaner.register();
|
|
323
|
-
order.push('Cleaner');
|
|
324
|
-
|
|
325
|
-
// 9. LabelTransitioner — flips the Epic ticket to `agent::done` (and
|
|
326
|
-
// closes it as completed, idempotently) on the terminal
|
|
327
|
-
// `epic.complete` event. Requires a truthy `provider`; skip
|
|
328
|
-
// cleanly when the caller omitted one — the same guard pattern as
|
|
329
|
-
// AutomergePredicate. Restores the contract the Cleaner /
|
|
330
|
-
// BranchCleaner / MergeWatcher docstrings have referenced since
|
|
331
|
-
// the original listener was deleted with the epic-runner stratum
|
|
332
|
-
// (#3936): without this registration the flip had NO owner and
|
|
333
|
-
// cleanly-merged Epics stranded at `agent::executing`.
|
|
334
|
-
let labelTransitioner = null;
|
|
335
|
-
if (provider) {
|
|
336
|
-
labelTransitioner = new LabelTransitioner({
|
|
337
|
-
bus,
|
|
338
|
-
epicId,
|
|
339
|
-
provider,
|
|
340
|
-
logger,
|
|
341
|
-
});
|
|
342
|
-
labelTransitioner.register();
|
|
343
|
-
order.push('LabelTransitioner');
|
|
344
|
-
} else {
|
|
345
|
-
logger?.debug?.(
|
|
346
|
-
'[lifecycle] buildDefaultListenerChain: skipping LabelTransitioner (no provider)',
|
|
347
|
-
);
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// 10. CheckpointPointerWriter — persists `{ lastCompletedSeqId, phase }`
|
|
351
|
-
// on every `*.end` event.
|
|
352
|
-
const checkpointPointerWriter = new CheckpointPointerWriter({
|
|
353
|
-
bus,
|
|
354
|
-
epicId,
|
|
355
|
-
tempRoot,
|
|
356
|
-
logger,
|
|
357
|
-
});
|
|
358
|
-
checkpointPointerWriter.register();
|
|
359
|
-
order.push('CheckpointPointerWriter');
|
|
360
|
-
|
|
361
|
-
logger?.debug?.(
|
|
362
|
-
`[lifecycle] buildDefaultListenerChain registered listeners: ${order.join(' → ')}`,
|
|
363
|
-
);
|
|
364
|
-
|
|
365
|
-
return {
|
|
366
|
-
ledgerWriter,
|
|
367
|
-
acceptanceReconciler,
|
|
368
|
-
finalizer,
|
|
369
|
-
automergeArmer,
|
|
370
|
-
automergePredicate,
|
|
371
|
-
branchCleaner,
|
|
372
|
-
mergeWatcher,
|
|
373
|
-
cleaner,
|
|
374
|
-
labelTransitioner,
|
|
375
|
-
checkpointPointerWriter,
|
|
376
|
-
order,
|
|
377
|
-
};
|
|
378
|
-
}
|
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js
|
|
2
|
-
/**
|
|
3
|
-
* InterventionRecorder — lifecycle listener that persists out-of-band
|
|
4
|
-
* manual interventions to the `epic-run-state` structured comment.
|
|
5
|
-
*
|
|
6
|
-
* Subscribes to (per Story #2410 / Task #2416):
|
|
7
|
-
* - `intervention.recorded` → calls `appendIntervention` on the
|
|
8
|
-
* `epic-run-state-store` so the `manualInterventions` array on the
|
|
9
|
-
* epic-run-state checkpoint comment grows by exactly one record.
|
|
10
|
-
*
|
|
11
|
-
* The lifecycle event is the canonical signal that the host LLM has
|
|
12
|
-
* performed an out-of-band recovery (`AskUserQuestion`, manual
|
|
13
|
-
* `git restore`, `--no-ff` recovery merge, `story-close --skipValidation`,
|
|
14
|
-
* etc.). The auto-merge predicate reads the persisted array and only
|
|
15
|
-
* fires when it is empty, so getting one record per emit is load-bearing.
|
|
16
|
-
*
|
|
17
|
-
* Idempotency contract (listeners/README.md): the listener keeps a
|
|
18
|
-
* per-instance `Set<seqId>` of seqIds it has handled. A repeat
|
|
19
|
-
* invocation with the same seqId short-circuits without calling the
|
|
20
|
-
* store. (`epic-run-state-store.appendIntervention` is NOT idempotent
|
|
21
|
-
* on its own — it always appends — so the seqId guard is the only
|
|
22
|
-
* thing standing between a bus replay and a duplicated record.)
|
|
23
|
-
*
|
|
24
|
-
* Side-effect firewall (listeners/README.md):
|
|
25
|
-
* - MAY read/write the epic-run-state comment via the injected
|
|
26
|
-
* `provider` (through `epic-run-state-store`).
|
|
27
|
-
* - MUST NOT `bus.emit()` from inside the handler body — the bus is a
|
|
28
|
-
* sequential mediator and cannot re-enter safely.
|
|
29
|
-
* - MUST NOT import any module from
|
|
30
|
-
* `STATE_MUTATING_MODULES` in `check-lifecycle-lint.js`. The
|
|
31
|
-
* epic-run-state-store module is NOT on that blocklist; the lint
|
|
32
|
-
* rule treats it as a safe persistence boundary for listeners.
|
|
33
|
-
*/
|
|
34
|
-
|
|
35
|
-
import { appendIntervention } from '../../epic-run-state-store.js';
|
|
36
|
-
|
|
37
|
-
export const INTERVENTION_RECORDED_EVENT = 'intervention.recorded';
|
|
38
|
-
|
|
39
|
-
export class InterventionRecorder {
|
|
40
|
-
/**
|
|
41
|
-
* @param {object} opts
|
|
42
|
-
* @param {object} opts.provider Ticketing provider threaded through to
|
|
43
|
-
* `appendIntervention`.
|
|
44
|
-
* @param {number} opts.epicId Target ticket id (the Epic). Used both
|
|
45
|
-
* to scope `appendIntervention` and to filter incoming emits — a
|
|
46
|
-
* payload whose `epicId` does not match this listener's Epic is
|
|
47
|
-
* ignored (defensive; production wiring only ever emits with the
|
|
48
|
-
* listener's own epicId).
|
|
49
|
-
* @param {{ warn?: Function, debug?: Function }} [opts.logger]
|
|
50
|
-
* @param {Function} [opts.appendIntervention] Injected store fn —
|
|
51
|
-
* defaults to the canonical import. Tests pass a spy.
|
|
52
|
-
*/
|
|
53
|
-
constructor(opts = {}) {
|
|
54
|
-
if (!opts.provider) {
|
|
55
|
-
throw new TypeError('InterventionRecorder requires a provider');
|
|
56
|
-
}
|
|
57
|
-
if (!Number.isInteger(opts.epicId) || opts.epicId < 1) {
|
|
58
|
-
throw new TypeError('InterventionRecorder requires a numeric epicId');
|
|
59
|
-
}
|
|
60
|
-
this.provider = opts.provider;
|
|
61
|
-
this.epicId = opts.epicId;
|
|
62
|
-
this.logger = opts.logger ?? console;
|
|
63
|
-
this._appendIntervention =
|
|
64
|
-
typeof opts.appendIntervention === 'function'
|
|
65
|
-
? opts.appendIntervention
|
|
66
|
-
: appendIntervention;
|
|
67
|
-
/** @type {Set<number>} seqIds we've already handled. */
|
|
68
|
-
this._seen = new Set();
|
|
69
|
-
this.events = Object.freeze([INTERVENTION_RECORDED_EVENT]);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Subscribe to `intervention.recorded`. Returns the unsubscribe
|
|
74
|
-
* handles so tests / teardown can detach without re-binding through
|
|
75
|
-
* the bus's private registry.
|
|
76
|
-
*/
|
|
77
|
-
register(bus) {
|
|
78
|
-
if (!bus || typeof bus.on !== 'function') {
|
|
79
|
-
throw new TypeError(
|
|
80
|
-
'InterventionRecorder.register requires a bus with on()',
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
return this.events.map((event) =>
|
|
84
|
-
bus.on(event, async (ctx) => this.handle(ctx)),
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Handle one `intervention.recorded` emit. Idempotent on seqId. The
|
|
90
|
-
* payload has already been schema-validated by the bus, so `reason`
|
|
91
|
-
* is guaranteed to be a non-empty string and `epicId` a positive
|
|
92
|
-
* integer.
|
|
93
|
-
*
|
|
94
|
-
* @param {{ event: string, seqId: number, payload: { epicId: number, reason: string, source?: string, ts?: string } }} ctx
|
|
95
|
-
*/
|
|
96
|
-
async handle({ event, seqId, payload }) {
|
|
97
|
-
if (event !== INTERVENTION_RECORDED_EVENT) return;
|
|
98
|
-
if (this._seen.has(seqId)) {
|
|
99
|
-
this.logger.debug?.(
|
|
100
|
-
`[InterventionRecorder] skip duplicate seqId=${seqId} (idempotent)`,
|
|
101
|
-
);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
// Defensive epicId filter — a misrouted emit (e.g., a shared bus in
|
|
105
|
-
// tests) should not pollute a different Epic's state comment.
|
|
106
|
-
if (payload?.epicId !== this.epicId) {
|
|
107
|
-
this.logger.debug?.(
|
|
108
|
-
`[InterventionRecorder] skip payload.epicId=${payload?.epicId} (listener bound to #${this.epicId})`,
|
|
109
|
-
);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
this._seen.add(seqId);
|
|
113
|
-
|
|
114
|
-
const entry = {
|
|
115
|
-
reason: payload.reason,
|
|
116
|
-
};
|
|
117
|
-
if (typeof payload.source === 'string' && payload.source.length > 0) {
|
|
118
|
-
entry.source = payload.source;
|
|
119
|
-
}
|
|
120
|
-
if (typeof payload.ts === 'string' && payload.ts.length > 0) {
|
|
121
|
-
entry.ts = payload.ts;
|
|
122
|
-
}
|
|
123
|
-
try {
|
|
124
|
-
await this._appendIntervention({
|
|
125
|
-
provider: this.provider,
|
|
126
|
-
epicId: this.epicId,
|
|
127
|
-
entry,
|
|
128
|
-
});
|
|
129
|
-
} catch (err) {
|
|
130
|
-
this.logger.warn?.(
|
|
131
|
-
`[InterventionRecorder] appendIntervention failed for seqId=${seqId}: ${err?.message ?? err}`,
|
|
132
|
-
);
|
|
133
|
-
throw err;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
resetSeen() {
|
|
138
|
-
this._seen.clear();
|
|
139
|
-
}
|
|
140
|
-
}
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// .agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js
|
|
2
|
-
/**
|
|
3
|
-
* LabelTransitioner — lifecycle listener that owns the terminal Epic
|
|
4
|
-
* ticket-state flip: on `epic.complete` it transitions the Epic to
|
|
5
|
-
* `agent::done` via the canonical `transitionTicketState` API (which
|
|
6
|
-
* also closes the issue with `state_reason: completed` and mirrors the
|
|
7
|
-
* Projects-v2 status column).
|
|
8
|
-
*
|
|
9
|
-
* Subscribes to:
|
|
10
|
-
* - `epic.complete` → and ONLY this event.
|
|
11
|
-
*
|
|
12
|
-
* Why this listener exists (regression history): the original
|
|
13
|
-
* LabelTransitioner lived in the in-process epic-runner stratum and was
|
|
14
|
-
* deleted with it (Story #3908 / #3936) — but the `lifecycle-emit`
|
|
15
|
-
* listener chain never re-registered a replacement, so the Epic
|
|
16
|
-
* `agent::done` flip silently had NO owner. Every docstring in
|
|
17
|
-
* `cleaner.js` / `branch-cleaner.js` / `merge-watcher.js` that says
|
|
18
|
-
* "LabelTransitioner flips the Epic ticket to `agent::done` on
|
|
19
|
-
* epic.complete" referenced a ghost. In practice the flip only happened
|
|
20
|
-
* when a driving session (or the operator) ran `update-ticket-state.js`
|
|
21
|
-
* by hand — observed live on 2026-07-11 when Epics #4405 / #4425 /
|
|
22
|
-
* #4429 merged cleanly (Cleaner archived, `epic.complete` on the
|
|
23
|
-
* ledger) yet stayed at `agent::executing`. This listener restores the
|
|
24
|
-
* documented contract on the SOLE production wiring path
|
|
25
|
-
* (`buildDefaultListenerChain`).
|
|
26
|
-
*
|
|
27
|
-
* Side effects executed inside `handle()`:
|
|
28
|
-
* 1. `transitionTicketState(provider, epicId, STATE_LABELS.DONE)` —
|
|
29
|
-
* adds `agent::done`, removes every other `agent::*` label, closes
|
|
30
|
-
* the issue as completed (idempotent when the GitHub Closes-#N
|
|
31
|
-
* linkage already closed it), syncs the board column, and runs the
|
|
32
|
-
* upward cascade (a no-op sweep here: story-close already flipped
|
|
33
|
-
* every child Story).
|
|
34
|
-
*
|
|
35
|
-
* Failure posture: a failed transition THROWS (per
|
|
36
|
-
* `rules/orchestration-error-handling.md` — throw, never fatal). The
|
|
37
|
-
* bus's `onFailed` hook records the failure on the ledger and
|
|
38
|
-
* `lifecycle-emit`'s `collectOutcomes` → `emitBlockedSignal` path
|
|
39
|
-
* surfaces it loudly, so a provider outage cannot silently strand the
|
|
40
|
-
* Epic at `agent::executing` again — the exact failure mode this
|
|
41
|
-
* listener exists to close.
|
|
42
|
-
*
|
|
43
|
-
* Idempotency contract: per-instance `Set<string>` of
|
|
44
|
-
* `${event}:${seqId}` keys (the standard bus-replay defence). The
|
|
45
|
-
* transition itself is also idempotent at the provider layer (label
|
|
46
|
-
* add/remove and a close on an already-closed issue are no-ops), so a
|
|
47
|
-
* cross-process replay after a crash re-runs the flip harmlessly.
|
|
48
|
-
*
|
|
49
|
-
* Side-effect firewall: exactly one provider call per handled event. No
|
|
50
|
-
* filesystem writes, no follow-up bus emits, no `gh` shell-outs.
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
import { STATE_LABELS } from '../../ticketing/reads.js';
|
|
54
|
-
import { transitionTicketState } from '../../ticketing/transition.js';
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* The single lifecycle event this listener subscribes to. `epic.complete`
|
|
58
|
-
* is the terminal event of a successful Epic run, emitted by Cleaner
|
|
59
|
-
* AFTER the MergeWatcher observed a non-null mergeCommit — so the flip
|
|
60
|
-
* can never fire for an Epic whose PR did not actually merge.
|
|
61
|
-
*/
|
|
62
|
-
export const SUBSCRIBED_EVENT = 'epic.complete';
|
|
63
|
-
|
|
64
|
-
export class LabelTransitioner {
|
|
65
|
-
/**
|
|
66
|
-
* @param {object} opts
|
|
67
|
-
* @param {object} opts.bus Lifecycle bus exposing `on()`.
|
|
68
|
-
* @param {number} opts.epicId Epic ticket id.
|
|
69
|
-
* @param {import('../../../ITicketingProvider.js').ITicketingProvider} opts.provider
|
|
70
|
-
* Ticketing provider. Required — the chain builder skips this
|
|
71
|
-
* listener entirely when no provider is wired (parity with
|
|
72
|
-
* AutomergePredicate's guard), so construction can demand one.
|
|
73
|
-
* @param {{ info?: Function, warn?: Function, debug?: Function }} [opts.logger]
|
|
74
|
-
*/
|
|
75
|
-
constructor(opts = {}) {
|
|
76
|
-
if (!opts.bus || typeof opts.bus.on !== 'function') {
|
|
77
|
-
throw new TypeError('LabelTransitioner requires a bus with on()');
|
|
78
|
-
}
|
|
79
|
-
if (!Number.isInteger(opts.epicId) || opts.epicId < 1) {
|
|
80
|
-
throw new TypeError('LabelTransitioner requires a numeric epicId');
|
|
81
|
-
}
|
|
82
|
-
if (!opts.provider) {
|
|
83
|
-
// Truthiness-only, parity with AutomergePredicate: the chain
|
|
84
|
-
// builder's best-effort registration must not explode on a
|
|
85
|
-
// shape-minimal provider — a malformed one fails loudly at
|
|
86
|
-
// handle time instead, where the ledger records the outcome.
|
|
87
|
-
throw new TypeError('LabelTransitioner requires a provider');
|
|
88
|
-
}
|
|
89
|
-
this.bus = opts.bus;
|
|
90
|
-
this.epicId = opts.epicId;
|
|
91
|
-
this.provider = opts.provider;
|
|
92
|
-
this.logger = opts.logger ?? console;
|
|
93
|
-
/** @type {Set<string>} `${event}:${seqId}` idempotency cache. */
|
|
94
|
-
this._seen = new Set();
|
|
95
|
-
// Canonical subscription-set shape: the lifecycle doc-drift gate
|
|
96
|
-
// (`check-lifecycle-doc-drift.js#extractCodeEvents`) and the
|
|
97
|
-
// event-connectivity contract test both resolve this frozen array
|
|
98
|
-
// (constant references included) to derive the subscriber table.
|
|
99
|
-
this.events = Object.freeze([SUBSCRIBED_EVENT]);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Register the listener on `epic.complete`. Returns the array of
|
|
104
|
-
* unsubscribe callbacks the bus produced (parity with the sibling
|
|
105
|
-
* listeners).
|
|
106
|
-
*/
|
|
107
|
-
register() {
|
|
108
|
-
return this.events.map((event) =>
|
|
109
|
-
this.bus.on(event, async (ctx) => this.handle(ctx)),
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Bus listener body. Idempotent on `(event, seqId)`; flips the Epic
|
|
115
|
-
* to `agent::done` exactly once per observed `epic.complete`.
|
|
116
|
-
*/
|
|
117
|
-
async handle({ event, seqId }) {
|
|
118
|
-
const key = `${event}:${seqId}`;
|
|
119
|
-
if (this._seen.has(key)) {
|
|
120
|
-
this.logger.debug?.(
|
|
121
|
-
`[LabelTransitioner] skip duplicate ${key} (idempotent)`,
|
|
122
|
-
);
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this._seen.add(key);
|
|
126
|
-
|
|
127
|
-
this.logger.info?.(
|
|
128
|
-
`[LabelTransitioner] epic.complete observed — transitioning Epic #${this.epicId} to ${STATE_LABELS.DONE}.`,
|
|
129
|
-
);
|
|
130
|
-
// Throws on failure by design: the ledger records the failed
|
|
131
|
-
// listener outcome and lifecycle-emit surfaces it (see the failure
|
|
132
|
-
// posture note in the module docstring). Swallowing here would
|
|
133
|
-
// recreate the silent agent::executing strand this listener fixes.
|
|
134
|
-
await transitionTicketState(this.provider, this.epicId, STATE_LABELS.DONE);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Test-only — clear the idempotency cache so a single instance can
|
|
139
|
-
* exercise replay scenarios without re-constructing the listener.
|
|
140
|
-
*/
|
|
141
|
-
resetSeen() {
|
|
142
|
-
this._seen.clear();
|
|
143
|
-
}
|
|
144
|
-
}
|