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,450 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* phases/gather-signals.js — retro Phase 1: aggregate retro signals.
|
|
3
|
-
*
|
|
4
|
-
* Reads child Stories' `story-perf-summary` comments, descendant labels
|
|
5
|
-
* (HITL events), the Epic's `parked-follow-ons` structured comment, and
|
|
6
|
-
* per-Story `signals.ndjson` streams. Composes the routed-proposal
|
|
7
|
-
* sections via `composeRoutedProposals`.
|
|
8
|
-
*
|
|
9
|
-
* Exported as `gatherRetroSignals` for the parent sequencer (and tests).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { TYPE_LABELS } from '../../../label-constants.js';
|
|
13
|
-
import {
|
|
14
|
-
forEachEpicLine,
|
|
15
|
-
forEachLine,
|
|
16
|
-
} from '../../../observability/signals-writer.js';
|
|
17
|
-
import { concurrentMap } from '../../../util/concurrent-map.js';
|
|
18
|
-
import { read as readEpicRunState } from '../../epic-run-state-store.js';
|
|
19
|
-
import { composeRoutedProposals } from '../../retro-proposals.js';
|
|
20
|
-
import { parseFencedJsonComment } from '../../structured-comment-parser.js';
|
|
21
|
-
import { findStructuredComment } from '../../ticketing.js';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Default framework repo (the Mandrel mirror) used when the caller does
|
|
25
|
-
* not supply an override. Consumer projects re-routing framework friction
|
|
26
|
-
* back to mandrel rely on this constant.
|
|
27
|
-
*/
|
|
28
|
-
export const DEFAULT_FRAMEWORK_REPO = 'dsj1984/mandrel';
|
|
29
|
-
|
|
30
|
-
const RECUT_BODY_MARKER = /<!--\s*recut-of:\s*#?\d+\s*-->/;
|
|
31
|
-
|
|
32
|
-
// Story #3347 — bounded fan-out cap for the per-Story `signals.ndjson`
|
|
33
|
-
// reads below. Mirrors the read-concurrency convention used elsewhere in
|
|
34
|
-
// the orchestrator (e.g. SUBTICKET_HYDRATION_CONCURRENCY,
|
|
35
|
-
// CASCADE_SIBLING_READ_CONCURRENCY); keeps a large Epic from opening an
|
|
36
|
-
// unbounded number of concurrent file handles while still collapsing N
|
|
37
|
-
// sequential disk reads into one wall-clock fan-out.
|
|
38
|
-
const SIGNALS_READ_CONCURRENCY = 8;
|
|
39
|
-
|
|
40
|
-
// Detects #NNN issue references in an Epic body — any match means the Epic
|
|
41
|
-
// likely has child Stories enumerated in the planning artifact, so a
|
|
42
|
-
// zero-descendant walk is suspicious.
|
|
43
|
-
const EPIC_BODY_REFERENCE = /#\d+/;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Fallback category stamped on a snapshot-derived blocked event that
|
|
47
|
-
* carries no `category` of its own. A non-empty category is what promotes
|
|
48
|
-
* the event to an actionable routed proposal in `composeRoutedProposals`,
|
|
49
|
-
* so a category-less `agent::blocked` record must still resolve to a
|
|
50
|
-
* concrete bucket rather than being silently dropped.
|
|
51
|
-
*/
|
|
52
|
-
const BLOCKED_EVENT_FALLBACK_CATEGORY = 'agent-blocked';
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Pure: aggregate `frictionByCategory` payloads into a single integer.
|
|
56
|
-
*/
|
|
57
|
-
function sumFriction(byCategory) {
|
|
58
|
-
if (!byCategory || typeof byCategory !== 'object') return 0;
|
|
59
|
-
let total = 0;
|
|
60
|
-
for (const v of Object.values(byCategory)) {
|
|
61
|
-
if (typeof v === 'number' && Number.isFinite(v) && v >= 0) total += v;
|
|
62
|
-
}
|
|
63
|
-
return total;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Fold one parsed `signals.ndjson` record into the routed-signal
|
|
68
|
-
* accumulator. Shared by the per-Story and per-Epic scans so both streams
|
|
69
|
-
* contribute to the SAME unified counts that feed the routed proposals AND
|
|
70
|
-
* the compact/full retro decision. Reads the canonical envelope: top-level
|
|
71
|
-
* `category` and the string `source` classifier tag (Epic #4406). Non-object
|
|
72
|
-
* records and records without a `category` contribute nothing.
|
|
73
|
-
*
|
|
74
|
-
* @param {unknown} parsed
|
|
75
|
-
* @param {Array<{ category: string, source: 'framework'|'consumer' }>} routedSignals
|
|
76
|
-
*/
|
|
77
|
-
function accumulateSignalRecord(parsed, routedSignals) {
|
|
78
|
-
if (parsed === null || typeof parsed !== 'object') return;
|
|
79
|
-
const record = /** @type {Record<string, unknown>} */ (parsed);
|
|
80
|
-
const category = typeof record.category === 'string' ? record.category : null;
|
|
81
|
-
if (!category) return;
|
|
82
|
-
const source = record.source === 'framework' ? 'framework' : 'consumer';
|
|
83
|
-
routedSignals.push({ category, source });
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Read the `epic-run-state` snapshot and project every Story still recorded
|
|
88
|
-
* as `blocked` into an `unresolvedBlockedEvents` entry for
|
|
89
|
-
* `composeRoutedProposals`. Each event carries a **non-empty** category —
|
|
90
|
-
* the snapshot record's own `category` when present, otherwise the
|
|
91
|
-
* `agent-blocked` fallback — so it force-promotes an actionable proposal
|
|
92
|
-
* even when no friction signal shares its category. Best-effort: a read
|
|
93
|
-
* failure or shapeless snapshot degrades to an empty array (observability
|
|
94
|
-
* MUST NOT take down the retro path).
|
|
95
|
-
*
|
|
96
|
-
* @param {{
|
|
97
|
-
* epicId: number,
|
|
98
|
-
* provider: object,
|
|
99
|
-
* logger?: { warn?: Function },
|
|
100
|
-
* readFn: typeof readEpicRunState,
|
|
101
|
-
* }} args
|
|
102
|
-
* @returns {Promise<Array<{ ticketId?: number, category: string, source: 'framework'|'consumer' }>>}
|
|
103
|
-
*/
|
|
104
|
-
async function readUnresolvedBlockedEvents({
|
|
105
|
-
epicId,
|
|
106
|
-
provider,
|
|
107
|
-
logger,
|
|
108
|
-
readFn,
|
|
109
|
-
}) {
|
|
110
|
-
let snapshot;
|
|
111
|
-
try {
|
|
112
|
-
snapshot = await readFn({ provider, epicId });
|
|
113
|
-
} catch (err) {
|
|
114
|
-
logger?.warn?.(
|
|
115
|
-
`[retro-runner] Failed to read epic-run-state for blocked events (continuing): ${
|
|
116
|
-
err?.message ?? err
|
|
117
|
-
}`,
|
|
118
|
-
);
|
|
119
|
-
return [];
|
|
120
|
-
}
|
|
121
|
-
const storiesMap =
|
|
122
|
-
snapshot && typeof snapshot.stories === 'object' && snapshot.stories
|
|
123
|
-
? snapshot.stories
|
|
124
|
-
: {};
|
|
125
|
-
const events = [];
|
|
126
|
-
for (const [key, record] of Object.entries(storiesMap)) {
|
|
127
|
-
if (!record || typeof record !== 'object' || record.status !== 'blocked') {
|
|
128
|
-
continue;
|
|
129
|
-
}
|
|
130
|
-
const rawCategory =
|
|
131
|
-
typeof record.category === 'string' ? record.category.trim() : '';
|
|
132
|
-
const category =
|
|
133
|
-
rawCategory.length > 0 ? rawCategory : BLOCKED_EVENT_FALLBACK_CATEGORY;
|
|
134
|
-
const source = record.source === 'framework' ? 'framework' : 'consumer';
|
|
135
|
-
const ticketId = Number(key);
|
|
136
|
-
const event = { category, source };
|
|
137
|
-
if (Number.isInteger(ticketId) && ticketId > 0) event.ticketId = ticketId;
|
|
138
|
-
events.push(event);
|
|
139
|
-
}
|
|
140
|
-
return events;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Walk every descendant ticket of `epicId` once. Returns the flat list with
|
|
145
|
-
* each ticket's labels + body + state — the consumer derives its own
|
|
146
|
-
* counts from this snapshot. Pure with respect to the provider injection.
|
|
147
|
-
*
|
|
148
|
-
* `provider.getSubTickets` is part of the `ITicketingProvider` contract;
|
|
149
|
-
* the call is **not** optional-chained so a missing method (or a typo'd
|
|
150
|
-
* test stub) surfaces as a thrown error rather than a silent empty graph.
|
|
151
|
-
*/
|
|
152
|
-
async function collectDescendants(provider, epicId) {
|
|
153
|
-
const visited = new Set([epicId]);
|
|
154
|
-
const out = [];
|
|
155
|
-
// Story #2853 — level-order BFS so every parent at the current depth
|
|
156
|
-
// fires its getSubTickets call concurrently. Previously this loop
|
|
157
|
-
// awaited one parent at a time, serializing one network round-trip per
|
|
158
|
-
// descendant tier (a 2-level Epic with 10 Stories was 11 sequential
|
|
159
|
-
// calls). Each `getSubTickets` already fans out child hydration
|
|
160
|
-
// internally with concurrency=8 (issues.js:SUBTICKET_HYDRATION_CONCURRENCY),
|
|
161
|
-
// so outer parallelism is consistent with the existing design.
|
|
162
|
-
let frontier = [epicId];
|
|
163
|
-
while (frontier.length > 0) {
|
|
164
|
-
const results = await Promise.all(
|
|
165
|
-
frontier.map((id) => provider.getSubTickets(id)),
|
|
166
|
-
);
|
|
167
|
-
const nextFrontier = [];
|
|
168
|
-
for (const subs of results) {
|
|
169
|
-
for (const sub of subs ?? []) {
|
|
170
|
-
const subId = Number(sub?.id ?? sub?.number);
|
|
171
|
-
if (!Number.isInteger(subId) || visited.has(subId)) continue;
|
|
172
|
-
visited.add(subId);
|
|
173
|
-
out.push(sub);
|
|
174
|
-
nextFrontier.push(subId);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
frontier = nextFrontier;
|
|
178
|
-
}
|
|
179
|
-
return out;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Defensive guard: when the descendant walker returned zero, probe the
|
|
184
|
-
* Epic ticket itself. If the Epic exists and its body references child
|
|
185
|
-
* issues (e.g., `#123` planning refs), the empty walk is almost certainly
|
|
186
|
-
* a contract drift in the underlying provider — emit a loud warn so the
|
|
187
|
-
* silent failure surfaces. Probe errors degrade silently (the guard is
|
|
188
|
-
* best-effort observability, not a correctness gate).
|
|
189
|
-
*/
|
|
190
|
-
async function warnIfEpicLooksPopulated({ epicId, provider, logger }) {
|
|
191
|
-
let epic;
|
|
192
|
-
try {
|
|
193
|
-
epic = await provider.getTicket?.(epicId);
|
|
194
|
-
} catch (err) {
|
|
195
|
-
logger?.warn?.(
|
|
196
|
-
`[retro-runner] guard: getTicket(#${epicId}) failed (continuing): ${err?.message ?? err}`,
|
|
197
|
-
);
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
if (!epic) return;
|
|
201
|
-
const body = typeof epic.body === 'string' ? epic.body : '';
|
|
202
|
-
if (!EPIC_BODY_REFERENCE.test(body)) return;
|
|
203
|
-
logger?.warn?.(
|
|
204
|
-
`[retro-runner] WARNING: Epic #${epicId} body references child issues, ` +
|
|
205
|
-
'but the descendant walker returned zero — retro will under-report ' +
|
|
206
|
-
'friction. Possible provider contract drift in getSubTickets.',
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* Read raw retro signals from the GitHub graph. Pure with respect to the
|
|
212
|
-
* provider injection — exported so tests can drive the predicate end-to-end
|
|
213
|
-
* with a stub provider.
|
|
214
|
-
*
|
|
215
|
-
* When `descendants.length === 0`, the function probes `provider.getTicket`
|
|
216
|
-
* to distinguish "Epic legitimately empty" from "the descendant walker
|
|
217
|
-
* silently returned nothing under a populated Epic" (the failure mode the
|
|
218
|
-
* pre-Story #2289 `getSubIssues` contract drift produced). A populated
|
|
219
|
-
* Epic with zero walked descendants emits a loud warn via the supplied
|
|
220
|
-
* `logger` so the silent failure becomes visible. Probe failure degrades
|
|
221
|
-
* gracefully — the function never throws on the guard alone.
|
|
222
|
-
*
|
|
223
|
-
* **2-tier ledgers (Story #3151, Story #3200).** Under the 2-tier
|
|
224
|
-
* hierarchy (Epic → Story; no Task-tier children), `friction`
|
|
225
|
-
* / `parked` / `recuts` / `storyPerfSummaries` are all Story-scoped, so
|
|
226
|
-
* the function continues to produce a non-empty signals report. The
|
|
227
|
-
* empty-walk guard above is **not** triggered for this shape because
|
|
228
|
-
* `descendants` is populated (it contains the Stories themselves); the
|
|
229
|
-
* guard fires only when the walker returned literally nothing under a
|
|
230
|
-
* body that references children.
|
|
231
|
-
*
|
|
232
|
-
* @returns {Promise<{
|
|
233
|
-
* stories: Array<{ id: number, body?: string, labels?: string[] }>,
|
|
234
|
-
* counts: { friction: number, parked: number, recuts: number, hitl: number },
|
|
235
|
-
* storyPerfSummaries: object[],
|
|
236
|
-
* epicPerfReport: object|null,
|
|
237
|
-
* parkedFollowOns: { recuts: object[], parked: object[], present: boolean },
|
|
238
|
-
* }>}
|
|
239
|
-
*/
|
|
240
|
-
export async function gatherRetroSignals({
|
|
241
|
-
epicId,
|
|
242
|
-
provider,
|
|
243
|
-
logger,
|
|
244
|
-
frameworkRepo,
|
|
245
|
-
consumerRepo,
|
|
246
|
-
forEachLineFn = forEachLine,
|
|
247
|
-
forEachEpicLineFn = forEachEpicLine,
|
|
248
|
-
epicRunStateReadFn = readEpicRunState,
|
|
249
|
-
composeRoutedProposalsFn = composeRoutedProposals,
|
|
250
|
-
}) {
|
|
251
|
-
const descendants = await collectDescendants(provider, epicId);
|
|
252
|
-
if (descendants.length === 0) {
|
|
253
|
-
await warnIfEpicLooksPopulated({ epicId, provider, logger });
|
|
254
|
-
}
|
|
255
|
-
const stories = descendants.filter((t) =>
|
|
256
|
-
(t.labels ?? []).includes(TYPE_LABELS.STORY),
|
|
257
|
-
);
|
|
258
|
-
// HITL count: distinct descendants that currently or historically carry
|
|
259
|
-
// `agent::blocked`. We can only see "currently" here without an event
|
|
260
|
-
// stream — counts undercount but never overcount.
|
|
261
|
-
const hitl = descendants.filter((t) =>
|
|
262
|
-
(t.labels ?? []).includes('agent::blocked'),
|
|
263
|
-
).length;
|
|
264
|
-
|
|
265
|
-
// Aggregate per-Story `story-perf-summary` payloads for friction totals.
|
|
266
|
-
// Story #2853 — fan out the per-Story `findStructuredComment` lookups
|
|
267
|
-
// concurrently. Each Story's lookup resolves to a single paginated
|
|
268
|
-
// `getTicketComments` round-trip (the raw-comments cache at
|
|
269
|
-
// ticketing/reads.js:381-412 dedupes the per-type lookups within a
|
|
270
|
-
// ticket), so parallelization adds no extra wire calls — it just
|
|
271
|
-
// collapses N sequential round-trips into one wall-clock fan-out.
|
|
272
|
-
// `Promise.all` preserves index order, so the resulting
|
|
273
|
-
// `storyPerfSummaries` array stays deterministic relative to `stories`.
|
|
274
|
-
const perStoryComments = await Promise.all(
|
|
275
|
-
stories.map((story) =>
|
|
276
|
-
findStructuredComment(
|
|
277
|
-
provider,
|
|
278
|
-
story.id ?? story.number,
|
|
279
|
-
'story-perf-summary',
|
|
280
|
-
),
|
|
281
|
-
),
|
|
282
|
-
);
|
|
283
|
-
const storyPerfSummaries = [];
|
|
284
|
-
let frictionFromSummaries = 0;
|
|
285
|
-
for (const comment of perStoryComments) {
|
|
286
|
-
const parsed = parseFencedJsonComment(comment);
|
|
287
|
-
if (parsed) {
|
|
288
|
-
storyPerfSummaries.push(parsed);
|
|
289
|
-
frictionFromSummaries += sumFriction(parsed.frictionByCategory);
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
// Epic-level perf report (used by the full retro's "Top hotspots").
|
|
294
|
-
const epicPerfComment = await findStructuredComment(
|
|
295
|
-
provider,
|
|
296
|
-
epicId,
|
|
297
|
-
'epic-perf-report',
|
|
298
|
-
);
|
|
299
|
-
const epicPerfReport = parseFencedJsonComment(epicPerfComment);
|
|
300
|
-
|
|
301
|
-
// Parked + recut counts: prefer the structured comment; fall back to body
|
|
302
|
-
// grep for the recut marker when the comment is absent.
|
|
303
|
-
const parkedComment = await findStructuredComment(
|
|
304
|
-
provider,
|
|
305
|
-
epicId,
|
|
306
|
-
'parked-follow-ons',
|
|
307
|
-
);
|
|
308
|
-
const parkedParsed = parseFencedJsonComment(parkedComment);
|
|
309
|
-
let parkedFollowOns;
|
|
310
|
-
if (parkedParsed) {
|
|
311
|
-
parkedFollowOns = {
|
|
312
|
-
present: true,
|
|
313
|
-
recuts: Array.isArray(parkedParsed.recuts) ? parkedParsed.recuts : [],
|
|
314
|
-
parked: Array.isArray(parkedParsed.parked) ? parkedParsed.parked : [],
|
|
315
|
-
};
|
|
316
|
-
} else {
|
|
317
|
-
const recutsByBody = stories.filter(
|
|
318
|
-
(s) => typeof s.body === 'string' && RECUT_BODY_MARKER.test(s.body),
|
|
319
|
-
);
|
|
320
|
-
parkedFollowOns = {
|
|
321
|
-
present: false,
|
|
322
|
-
recuts: recutsByBody.map((s) => ({ storyId: s.id ?? s.number })),
|
|
323
|
-
parked: [],
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Story #4417 — the compact/full retro decision, the automerge-verdict
|
|
328
|
-
// cleanSprint flag, and the routed proposals must all read from ONE
|
|
329
|
-
// signals scan. We fold the per-Story `signals.ndjson` streams AND the
|
|
330
|
-
// per-Epic `signals.ndjson` stream (the `appendEpicSignal` wave-lifecycle
|
|
331
|
-
// writers, e.g. lifecycle-emit) into a single `routedSignals`
|
|
332
|
-
// accumulation; `counts.friction` then derives from that same scan so a
|
|
333
|
-
// retro can never read "clean" from the comment substrate while
|
|
334
|
-
// actionable friction sits unrendered in the ndjson substrate.
|
|
335
|
-
//
|
|
336
|
-
// Story #2558 — the streams are source-tagged by the writer's
|
|
337
|
-
// `tagSignalSource`; `accumulateSignalRecord` reads the canonical
|
|
338
|
-
// envelope (top-level `category` + string `source`, Epic #4406). Read
|
|
339
|
-
// failures degrade silently — observability MUST NOT take down the retro.
|
|
340
|
-
//
|
|
341
|
-
// Story #3347 — the per-Story reads fan out via `concurrentMap` with a
|
|
342
|
-
// bounded cap (`SIGNALS_READ_CONCURRENCY`); `concurrentMap` preserves
|
|
343
|
-
// input order so we concatenate the per-Story `routedSignals` in
|
|
344
|
-
// `stories` order, keeping the composed `routedProposals` deterministic.
|
|
345
|
-
const perStorySignals = await concurrentMap(
|
|
346
|
-
stories,
|
|
347
|
-
async (story) => {
|
|
348
|
-
const sid = Number(story.id ?? story.number);
|
|
349
|
-
if (!Number.isInteger(sid) || sid <= 0) {
|
|
350
|
-
return { routedSignals: [] };
|
|
351
|
-
}
|
|
352
|
-
const localRoutedSignals = [];
|
|
353
|
-
try {
|
|
354
|
-
await forEachLineFn(epicId, sid, (parsed) => {
|
|
355
|
-
accumulateSignalRecord(parsed, localRoutedSignals);
|
|
356
|
-
});
|
|
357
|
-
} catch (err) {
|
|
358
|
-
logger?.warn?.(
|
|
359
|
-
`[retro-runner] forEachLine failed for story #${sid} (continuing): ${
|
|
360
|
-
err?.message ?? err
|
|
361
|
-
}`,
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
return { routedSignals: localRoutedSignals };
|
|
365
|
-
},
|
|
366
|
-
{ concurrency: SIGNALS_READ_CONCURRENCY },
|
|
367
|
-
);
|
|
368
|
-
const routedSignals = [];
|
|
369
|
-
for (const perStory of perStorySignals) {
|
|
370
|
-
routedSignals.push(...perStory.routedSignals);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
// Story #4417 — fold in the per-Epic `signals.ndjson` stream
|
|
374
|
-
// (`appendEpicSignal` wave-lifecycle writers, e.g. lifecycle-emit) so the
|
|
375
|
-
// unified scan is not blind to Epic-scoped friction that never lands on
|
|
376
|
-
// an individual Story stream.
|
|
377
|
-
try {
|
|
378
|
-
await forEachEpicLineFn(epicId, (parsed) => {
|
|
379
|
-
accumulateSignalRecord(parsed, routedSignals);
|
|
380
|
-
});
|
|
381
|
-
} catch (err) {
|
|
382
|
-
logger?.warn?.(
|
|
383
|
-
`[retro-runner] forEachEpicLine failed for epic #${epicId} (continuing): ${
|
|
384
|
-
err?.message ?? err
|
|
385
|
-
}`,
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
const counts = {
|
|
390
|
-
// `friction` derives from the unified ndjson scan (every categorized
|
|
391
|
-
// signal across the per-Story and per-Epic streams) PLUS the legacy
|
|
392
|
-
// story-perf-summary totals, so a non-empty ndjson substrate forces the
|
|
393
|
-
// full retro even when no `story-perf-summary` comment was posted.
|
|
394
|
-
friction: frictionFromSummaries + routedSignals.length,
|
|
395
|
-
parked: parkedFollowOns.parked.length,
|
|
396
|
-
recuts: parkedFollowOns.recuts.length,
|
|
397
|
-
hitl,
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
// Story #4417 — project unresolved `agent::blocked` Stories from the
|
|
401
|
-
// epic-run-state snapshot into force-flag events for the routed-proposal
|
|
402
|
-
// composer. This replaces the formerly hardwired empty array: a blocked
|
|
403
|
-
// Story now force-promotes an actionable proposal even absent a matching
|
|
404
|
-
// friction category (its category falls back to `agent-blocked`).
|
|
405
|
-
const unresolvedBlockedEvents = await readUnresolvedBlockedEvents({
|
|
406
|
-
epicId,
|
|
407
|
-
provider,
|
|
408
|
-
logger,
|
|
409
|
-
readFn: epicRunStateReadFn,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
// Resolve repos. The framework repo falls back to the Mandrel mirror
|
|
413
|
-
// constant (a known, stable default). The consumer repo does NOT fall
|
|
414
|
-
// back to the framework repo: when the caller resolves no consumer repo
|
|
415
|
-
// from `config.github`, the consumer proposal pane is DISABLED loudly
|
|
416
|
-
// (Story #4417) rather than silently routing consumer-tagged friction at
|
|
417
|
-
// the framework mirror.
|
|
418
|
-
const resolvedFrameworkRepo =
|
|
419
|
-
typeof frameworkRepo === 'string' && frameworkRepo.length > 0
|
|
420
|
-
? frameworkRepo
|
|
421
|
-
: DEFAULT_FRAMEWORK_REPO;
|
|
422
|
-
const resolvedConsumerRepo =
|
|
423
|
-
typeof consumerRepo === 'string' && consumerRepo.length > 0
|
|
424
|
-
? consumerRepo
|
|
425
|
-
: '';
|
|
426
|
-
if (resolvedConsumerRepo.length === 0) {
|
|
427
|
-
logger?.warn?.(
|
|
428
|
-
'[retro-runner] No consumer repo resolved from config.github — the ' +
|
|
429
|
-
'consumer proposal pane is DISABLED for this retro. Set ' +
|
|
430
|
-
'github.owner / github.repo to route consumer-tagged friction.',
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
const routedProposals = composeRoutedProposalsFn({
|
|
435
|
-
epicId,
|
|
436
|
-
frameworkRepo: resolvedFrameworkRepo,
|
|
437
|
-
consumerRepo: resolvedConsumerRepo,
|
|
438
|
-
signals: routedSignals,
|
|
439
|
-
unresolvedBlockedEvents,
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
return {
|
|
443
|
-
stories,
|
|
444
|
-
counts,
|
|
445
|
-
storyPerfSummaries,
|
|
446
|
-
epicPerfReport,
|
|
447
|
-
parkedFollowOns,
|
|
448
|
-
routedProposals,
|
|
449
|
-
};
|
|
450
|
-
}
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* phases/post-and-mirror.js — retro Phase 4: compose + upsert + mirror.
|
|
3
|
-
*
|
|
4
|
-
* Runs gather → compose → checks → upsert structured comment → mirror to
|
|
5
|
-
* the per-Epic temp dir, and emits the `retro.end` boundary on the bus
|
|
6
|
-
* once the write side-effects settle.
|
|
7
|
-
*
|
|
8
|
-
* Extracted so `runRetro` can wrap the full body in a try/catch for the
|
|
9
|
-
* `retro.end` boundary emit on throw without cluttering the happy-path
|
|
10
|
-
* read.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import nodeFs from 'node:fs';
|
|
14
|
-
import path from 'node:path';
|
|
15
|
-
|
|
16
|
-
import { epicRetroMirrorPath } from '../../../config/temp-paths.js';
|
|
17
|
-
import { fileRetroProposals as defaultFileRetroProposals } from '../../../feedback-loop/retro-proposals-graduator.js';
|
|
18
|
-
import { upsertStructuredComment } from '../../ticketing.js';
|
|
19
|
-
import { appendChecksSection, collectRetroFindings } from './checks.js';
|
|
20
|
-
import {
|
|
21
|
-
composeRetroBody as defaultComposeRetroBody,
|
|
22
|
-
deriveDefectClasses,
|
|
23
|
-
normalizeInterventionCount,
|
|
24
|
-
} from './compose-body.js';
|
|
25
|
-
import { gatherRetroSignals as defaultGatherRetroSignals } from './gather-signals.js';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Inner compose-and-post helper. Extracted so `runRetro` can wrap the
|
|
29
|
-
* full body in a try/catch for the `retro.end` boundary emit without
|
|
30
|
-
* cluttering the happy-path read.
|
|
31
|
-
*/
|
|
32
|
-
export async function composeAndPostRetro({
|
|
33
|
-
epicId,
|
|
34
|
-
provider,
|
|
35
|
-
logger,
|
|
36
|
-
forceFull,
|
|
37
|
-
timestamp,
|
|
38
|
-
bus,
|
|
39
|
-
now,
|
|
40
|
-
manualInterventions,
|
|
41
|
-
frameworkRepo,
|
|
42
|
-
consumerRepo,
|
|
43
|
-
config = null,
|
|
44
|
-
gatherFn = defaultGatherRetroSignals,
|
|
45
|
-
composeFn = defaultComposeRetroBody,
|
|
46
|
-
upsertFn = upsertStructuredComment,
|
|
47
|
-
fileRetroProposalsFn = defaultFileRetroProposals,
|
|
48
|
-
ghPath,
|
|
49
|
-
spawnImpl,
|
|
50
|
-
runChecksFn,
|
|
51
|
-
assembleStateFn,
|
|
52
|
-
cwd,
|
|
53
|
-
fsImpl = nodeFs,
|
|
54
|
-
startedAt,
|
|
55
|
-
onMirrorWritten,
|
|
56
|
-
perfThresholds = null,
|
|
57
|
-
}) {
|
|
58
|
-
const signals = await gatherFn({
|
|
59
|
-
epicId,
|
|
60
|
-
provider,
|
|
61
|
-
logger,
|
|
62
|
-
frameworkRepo,
|
|
63
|
-
consumerRepo,
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Story #4418 — auto-file the retro's actionable routed proposals BEFORE
|
|
67
|
-
// the body composes, so the rendered retro sections list the real filed
|
|
68
|
-
// issue numbers instead of paste-ready `gh` command stanzas. Runs behind
|
|
69
|
-
// the `delivery.feedbackLoop.retroProposals` toggle (default ON); when
|
|
70
|
-
// OFF (or when filing is skipped / fails) the proposals pass through
|
|
71
|
-
// unenriched and the composer falls back to the command stanzas. Never
|
|
72
|
-
// throws — a filing failure degrades gracefully.
|
|
73
|
-
const { routedProposals: filedRoutedProposals, summary: filingSummary } =
|
|
74
|
-
await fileRetroProposalsFn({
|
|
75
|
-
epicId,
|
|
76
|
-
provider,
|
|
77
|
-
config,
|
|
78
|
-
frameworkRepo,
|
|
79
|
-
consumerRepo,
|
|
80
|
-
routedProposals: signals.routedProposals,
|
|
81
|
-
ghPath,
|
|
82
|
-
spawnImpl,
|
|
83
|
-
cwd,
|
|
84
|
-
logger,
|
|
85
|
-
});
|
|
86
|
-
// Surface (never throw on) per-proposal filing failures. Dropping
|
|
87
|
-
// `summary.errors` silently made a failed `gh issue create` — e.g. a
|
|
88
|
-
// `friction::<category>` label that doesn't exist in the target repo —
|
|
89
|
-
// invisible except as an unenriched command stanza in the retro body.
|
|
90
|
-
if (Array.isArray(filingSummary?.errors) && filingSummary.errors.length) {
|
|
91
|
-
for (const err of filingSummary.errors) {
|
|
92
|
-
logger?.warn?.(
|
|
93
|
-
`[retro] proposal auto-file error (degrading to command stanza): ${
|
|
94
|
-
typeof err === 'string' ? err : JSON.stringify(err)
|
|
95
|
-
}`,
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// Best-effort fetch of the Epic title for the heading.
|
|
101
|
-
let epicTitle;
|
|
102
|
-
try {
|
|
103
|
-
const epic = await provider.getTicket?.(epicId);
|
|
104
|
-
epicTitle = epic?.title;
|
|
105
|
-
} catch (err) {
|
|
106
|
-
logger?.warn?.(
|
|
107
|
-
`[retro-runner] Failed to fetch Epic #${epicId} title (using fallback): ${err?.message ?? err}`,
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const interventions = normalizeInterventionCount(manualInterventions);
|
|
112
|
-
const { body, compact, scorecard } = composeFn({
|
|
113
|
-
epicId,
|
|
114
|
-
epicTitle,
|
|
115
|
-
counts: { ...signals.counts, interventions },
|
|
116
|
-
storyPerfSummaries: signals.storyPerfSummaries,
|
|
117
|
-
epicPerfReport: signals.epicPerfReport,
|
|
118
|
-
parkedFollowOns: signals.parkedFollowOns,
|
|
119
|
-
routedProposals: filedRoutedProposals,
|
|
120
|
-
timestamp,
|
|
121
|
-
forceFull,
|
|
122
|
-
perfThresholds,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Story #4135 (Epic #4131, F11) — derive the recurring-defect-class signal
|
|
126
|
-
// from the same routed proposals the body composer consumed. The signal is
|
|
127
|
-
// surfaced on the runRetro envelope (`defectClasses`) so callers/tests can
|
|
128
|
-
// observe the recurring classes the routed-proposal `gh issue create`
|
|
129
|
-
// commands stamp with `friction::<class>` labels — the join key the
|
|
130
|
-
// `/plan` Phase 0 fetcher reads back. No-op-safe: a clean sprint (no
|
|
131
|
-
// routed proposals) yields `[]`.
|
|
132
|
-
const defectClasses = deriveDefectClasses(signals.routedProposals);
|
|
133
|
-
|
|
134
|
-
const findings = await collectRetroFindings({
|
|
135
|
-
runChecksFn,
|
|
136
|
-
assembleStateFn,
|
|
137
|
-
cwd,
|
|
138
|
-
logger,
|
|
139
|
-
});
|
|
140
|
-
const bodyWithChecks = appendChecksSection(body, findings);
|
|
141
|
-
|
|
142
|
-
logger?.info?.(
|
|
143
|
-
`[retro-runner] Posting ${compact ? 'compact' : 'full'} retro on Epic #${epicId}${findings.length > 0 ? ` (${findings.length} finding(s))` : ''}...`,
|
|
144
|
-
);
|
|
145
|
-
const result = await upsertFn(provider, epicId, 'retro', bodyWithChecks);
|
|
146
|
-
|
|
147
|
-
// Story #2089: also mirror the retro body to the per-Epic temp dir so
|
|
148
|
-
// operators can read it locally without re-fetching from GitHub. GitHub
|
|
149
|
-
// remains SSOT — a write failure logs a warn and does not fail the
|
|
150
|
-
// phase. The path is resolved relative to `cwd` when supplied so that
|
|
151
|
-
// worktree-scoped invocations land under the worktree's temp tree.
|
|
152
|
-
let mirrorAbsPath = null;
|
|
153
|
-
try {
|
|
154
|
-
const rel = epicRetroMirrorPath(epicId);
|
|
155
|
-
const absPath = path.isAbsolute(rel)
|
|
156
|
-
? rel
|
|
157
|
-
: path.join(cwd ?? process.cwd(), rel);
|
|
158
|
-
fsImpl.mkdirSync(path.dirname(absPath), { recursive: true });
|
|
159
|
-
fsImpl.writeFileSync(absPath, bodyWithChecks, 'utf8');
|
|
160
|
-
mirrorAbsPath = absPath;
|
|
161
|
-
onMirrorWritten?.(absPath);
|
|
162
|
-
logger?.info?.(`[retro-runner] Mirrored retro to ${absPath}`);
|
|
163
|
-
} catch (err) {
|
|
164
|
-
logger?.warn?.(
|
|
165
|
-
`[retro-runner] Failed to write retro mirror (retro.md) for Epic #${epicId} (continuing — GitHub upsert succeeded): ${err?.message ?? err}`,
|
|
166
|
-
);
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Story #2252 — emit `retro.end` after the upsert + mirror settle so
|
|
170
|
-
// the lifecycle ledger captures the closing boundary with the
|
|
171
|
-
// posted/compact flags AND the resolved mirror path (when present).
|
|
172
|
-
const endedAt = typeof now === 'function' ? now() : Date.now();
|
|
173
|
-
const retroEndPayload = {
|
|
174
|
-
epicId,
|
|
175
|
-
posted: true,
|
|
176
|
-
compact: Boolean(compact),
|
|
177
|
-
durationMs: Math.max(0, Math.floor(endedAt - startedAt)),
|
|
178
|
-
};
|
|
179
|
-
if (mirrorAbsPath) retroEndPayload.retroPath = mirrorAbsPath;
|
|
180
|
-
await bus.emit('retro.end', retroEndPayload);
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
posted: true,
|
|
184
|
-
compact,
|
|
185
|
-
scorecard,
|
|
186
|
-
body: bodyWithChecks,
|
|
187
|
-
findings,
|
|
188
|
-
defectClasses,
|
|
189
|
-
commentId: result?.commentId,
|
|
190
|
-
};
|
|
191
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Heuristics that drive which retro path the `epic-retro` helper composes.
|
|
3
|
-
*
|
|
4
|
-
* The compact three-section retro fires when the Epic's dispatch manifest
|
|
5
|
-
* carries zero friction signals. Non-zero on any of the five dimensions
|
|
6
|
-
* routes back to the full six-section retro. Keeping the predicate here
|
|
7
|
-
* (pure, no I/O) guards the clean-sprint definition from drifting as the
|
|
8
|
-
* retro helper markdown evolves.
|
|
9
|
-
*
|
|
10
|
-
* Epic #1030 Story #1046 — the `friction` count is now sourced from the
|
|
11
|
-
* aggregated `story-perf-summary.frictionByCategory` totals across the
|
|
12
|
-
* Epic's descendants (the unified summary comment posted by
|
|
13
|
-
* `analyze-execution.js`), not the legacy per-Task `friction` structured
|
|
14
|
-
* comment fan-out. The predicate signature stays unchanged — the input
|
|
15
|
-
* is still a single integer count — so callers only need to swap their
|
|
16
|
-
* data source.
|
|
17
|
-
*
|
|
18
|
-
* Story #2289 — added `interventions`, sourced from the
|
|
19
|
-
* `manualInterventions` array on the `epic-run-state` snapshot read
|
|
20
|
-
* via `epic-run-state-store.read`. The same list disqualifies the
|
|
21
|
-
* Epic from auto-merge, so the retro shape and the auto-merge gate
|
|
22
|
-
* now agree on what "clean" means.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Evaluate whether an Epic's dispatch manifest counts qualify for the
|
|
27
|
-
* compact retro path.
|
|
28
|
-
*
|
|
29
|
-
* All five signals must be zero. Any non-zero value (including negatives,
|
|
30
|
-
* which are invalid but treated defensively as "non-clean") returns false.
|
|
31
|
-
* Non-number inputs are treated as missing and default to zero so callers
|
|
32
|
-
* can omit dimensions they have not yet gathered — a call with no arguments
|
|
33
|
-
* returns true.
|
|
34
|
-
*
|
|
35
|
-
* @param {Object} [counts]
|
|
36
|
-
* @param {number} [counts.friction=0] Aggregate friction-event count, summed from `story-perf-summary.frictionByCategory` across the Epic's descendants (Story #1046).
|
|
37
|
-
* @param {number} [counts.parked=0] Count of parked follow-on Stories (no manifest lineage).
|
|
38
|
-
* @param {number} [counts.recuts=0] Count of Stories carrying a `<!-- recut-of: #N -->` marker.
|
|
39
|
-
* @param {number} [counts.hitl=0] Count of tickets that raised an `agent::blocked` event mid-sprint (the runtime HITL pause point).
|
|
40
|
-
* @param {number} [counts.interventions=0] Count of recorded manual interventions (the same `manualInterventions` list the auto-merge predicate consults — Story #2289).
|
|
41
|
-
* @returns {boolean}
|
|
42
|
-
*/
|
|
43
|
-
export function isCleanManifest(counts = {}) {
|
|
44
|
-
const { friction, parked, recuts, hitl, interventions } = counts;
|
|
45
|
-
return (
|
|
46
|
-
normalize(friction) === 0 &&
|
|
47
|
-
normalize(parked) === 0 &&
|
|
48
|
-
normalize(recuts) === 0 &&
|
|
49
|
-
normalize(hitl) === 0 &&
|
|
50
|
-
normalize(interventions) === 0
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function normalize(value) {
|
|
55
|
-
if (typeof value !== 'number' || !Number.isFinite(value)) return 0;
|
|
56
|
-
return value;
|
|
57
|
-
}
|