mandrel 1.94.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/story-worker.md +12 -13
- package/.agents/docs/SDLC.md +489 -1285
- package/.agents/docs/agentrc-reference.json +177 -67
- package/.agents/docs/configuration.md +104 -138
- package/.agents/docs/execution-reference.md +17 -20
- package/.agents/docs/quality-gates.md +13 -19
- package/.agents/docs/workflows.md +3 -3
- package/.agents/instructions.md +70 -81
- package/.agents/rules/ci-remediation.md +8 -12
- package/.agents/rules/git-conventions-reference.md +6 -7
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +54 -214
- 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/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-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/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/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 +2 -2
- package/.agents/scripts/lib/config/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +45 -29
- package/.agents/scripts/lib/config/explain.js +55 -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 +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +24 -148
- package/.agents/scripts/lib/config-settings-schema-quality.js +8 -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 +2 -2
- 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 +1 -1
- package/.agents/scripts/lib/observability/hook-heartbeat.js +20 -52
- package/.agents/scripts/lib/observability/source-classifier.js +3 -3
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +128 -0
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +8 -5
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +83 -20
- 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-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 +13 -13
- 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/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 +2 -7
- 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/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/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/update-ticket-state.js +6 -15
- package/.agents/skills/core/analyze-execution/SKILL.md +21 -18
- package/.agents/skills/core/idea-refinement/SKILL.md +6 -6
- package/.agents/skills/core/scope-triage/SKILL.md +28 -172
- package/.agents/skills/skills.index.json +3 -43
- 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 -174
- 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 +16 -29
- 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/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 +2 -1
- 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 +30 -0
- package/lib/migrations/index.js +6 -5
- package/package.json +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-spec.schema.json +0 -153
- package/.agents/schemas/lifecycle/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/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-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/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/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- /package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +0 -0
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* progress-reporter/composition.js — body builders for the
|
|
3
|
-
* `epic-run-progress` structured comment and the periodic ProgressReporter
|
|
4
|
-
* snapshot.
|
|
5
|
-
*
|
|
6
|
-
* Extracted from the parent `progress-reporter.js` so the
|
|
7
|
-
* structured-comment rendering surface is testable independently of the
|
|
8
|
-
* orchestration shell (`ProgressReporter`) and the I/O webhook posters in
|
|
9
|
-
* `transport.js`. Pure functions only — no provider calls, no clock reads
|
|
10
|
-
* beyond the caller-supplied `now()`.
|
|
11
|
-
*
|
|
12
|
-
* `upsertEpicRunProgress` is the canonical body builder + persistence
|
|
13
|
-
* surface for the rolled-up Epic-level table that lands on the Epic
|
|
14
|
-
* ticket after every wave; the ProgressReporter class delegates its
|
|
15
|
-
* per-poll `#render` / `#renderNotable` to the pure renderers below so
|
|
16
|
-
* the same shape is generated either side of the boundary.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
import { upsertStructuredComment } from '../../ticketing.js';
|
|
20
|
-
import { EPIC_RUN_PROGRESS_TYPE, STATE_EMOJI } from './signals.js';
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Truncate `s` to at most `n` characters, suffixing with a single ellipsis
|
|
24
|
-
* (`…`) when the string was longer. Returns the empty string for any
|
|
25
|
-
* falsy input so table cells never render `undefined`/`null`.
|
|
26
|
-
*/
|
|
27
|
-
function truncate(s, n) {
|
|
28
|
-
if (!s) return '';
|
|
29
|
-
return s.length > n ? `${s.slice(0, n - 1)}…` : s;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Escape pipe characters so a value can be inlined into a markdown table
|
|
34
|
-
* cell without breaking the column separators.
|
|
35
|
-
*/
|
|
36
|
-
export function escapePipes(s) {
|
|
37
|
-
return String(s).replace(/\|/g, '\\|');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Derive the high-level state classification for a single ticket. Reads
|
|
42
|
-
* the canonical `agent::*` label set first, then falls back to the GitHub
|
|
43
|
-
* `state` string for the closed-without-done case. Returns `'unknown'`
|
|
44
|
-
* for any unrecognized shape so the renderer can flag unreadable rows in
|
|
45
|
-
* the Notable section.
|
|
46
|
-
*/
|
|
47
|
-
export function deriveState(ticket, AGENT_LABELS) {
|
|
48
|
-
if (!ticket) return 'unknown';
|
|
49
|
-
const labels = ticket.labels ?? [];
|
|
50
|
-
const state = (ticket.state ?? '').toString().toUpperCase();
|
|
51
|
-
if (state === 'CLOSED' || labels.includes(AGENT_LABELS.DONE)) return 'done';
|
|
52
|
-
if (labels.includes(AGENT_LABELS.BLOCKED)) return 'blocked';
|
|
53
|
-
if (labels.includes(AGENT_LABELS.EXECUTING)) return 'in-flight';
|
|
54
|
-
if (labels.includes(AGENT_LABELS.READY)) return 'queued';
|
|
55
|
-
return 'unknown';
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Declarative descriptor table that drives the Notable bullet block. Each
|
|
60
|
-
* descriptor names a row state, the emoji prefix to render, and the
|
|
61
|
-
* `label(count)` function that produces the count-aware human phrase (e.g.
|
|
62
|
-
* "1 story blocked" vs "2 stories blocked"). Iteration order is the
|
|
63
|
-
* canonical render order: blocked → in-flight → unknown, matching the
|
|
64
|
-
* pre-refactor sequential filter walks so output stays byte-identical.
|
|
65
|
-
*/
|
|
66
|
-
const STATE_NOTABLE_DESCRIPTORS = [
|
|
67
|
-
{
|
|
68
|
-
state: 'blocked',
|
|
69
|
-
emoji: STATE_EMOJI.blocked,
|
|
70
|
-
label: (n) => `${n} stor${n === 1 ? 'y' : 'ies'} blocked`,
|
|
71
|
-
},
|
|
72
|
-
{
|
|
73
|
-
state: 'in-flight',
|
|
74
|
-
emoji: STATE_EMOJI['in-flight'],
|
|
75
|
-
label: (n) => `${n} in flight`,
|
|
76
|
-
},
|
|
77
|
-
{
|
|
78
|
-
state: 'unknown',
|
|
79
|
-
emoji: STATE_EMOJI.unknown,
|
|
80
|
-
label: (n) => `${n} unreadable (token scope / network?)`,
|
|
81
|
-
},
|
|
82
|
-
];
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Single-pass grouping of `rows` keyed by the descriptor states. Returns a
|
|
86
|
-
* Map<state, row[]> so callers can iterate descriptors and look up the
|
|
87
|
-
* matching slice in O(1); states absent from `rows` get an empty array so
|
|
88
|
-
* the renderer can skip them with a single `length` check.
|
|
89
|
-
*/
|
|
90
|
-
function groupRowsByNotableState(rows) {
|
|
91
|
-
const groups = new Map(STATE_NOTABLE_DESCRIPTORS.map((d) => [d.state, []]));
|
|
92
|
-
for (const r of rows) {
|
|
93
|
-
const bucket = groups.get(r.state);
|
|
94
|
-
if (bucket) bucket.push(r);
|
|
95
|
-
}
|
|
96
|
-
return groups;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Run a single detector against `rows`/`ctx`, swallowing any thrown or
|
|
101
|
-
* rejected error so one misbehaving detector cannot kill the whole render
|
|
102
|
-
* path. Failures are surfaced via `logger.warn` so operators still see the
|
|
103
|
-
* signal; the bullet array is treated as empty on failure.
|
|
104
|
-
*/
|
|
105
|
-
async function runDetector(detector, rows, ctx, logger) {
|
|
106
|
-
try {
|
|
107
|
-
const fn = typeof detector === 'function' ? detector : detector?.detect;
|
|
108
|
-
if (typeof fn !== 'function') return [];
|
|
109
|
-
const out = await fn.call(detector, rows, ctx);
|
|
110
|
-
return Array.isArray(out) ? out : [];
|
|
111
|
-
} catch (err) {
|
|
112
|
-
logger?.warn?.(`[ProgressReporter] detector failed: ${err.message}`);
|
|
113
|
-
return [];
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Render the **Notable** bullet block: blocked-story summary, in-flight
|
|
119
|
-
* counts, unreadable rows, plus any detector bullets that were collected
|
|
120
|
-
* from the caller. Detectors receive `(rows, ctx)` and may return either
|
|
121
|
-
* an array of strings or a thenable resolving to one — the caller is
|
|
122
|
-
* responsible for awaiting and trapping detector failures (we trap them
|
|
123
|
-
* here to keep the render path non-fatal).
|
|
124
|
-
*
|
|
125
|
-
* The state-driven bullets are emitted from `STATE_NOTABLE_DESCRIPTORS`
|
|
126
|
-
* via a single grouping pass, which keeps the cyclomatic surface flat as
|
|
127
|
-
* new notable states are added.
|
|
128
|
-
*
|
|
129
|
-
* Returns the rendered block (without the leading `**Notable**` header so
|
|
130
|
-
* the caller can place it inside a larger composition).
|
|
131
|
-
*/
|
|
132
|
-
export async function renderNotable({ rows, detectors = [], wave, logger }) {
|
|
133
|
-
const items = [];
|
|
134
|
-
const groups = groupRowsByNotableState(rows);
|
|
135
|
-
for (const { state, emoji, label } of STATE_NOTABLE_DESCRIPTORS) {
|
|
136
|
-
const matched = groups.get(state);
|
|
137
|
-
if (!matched.length) continue;
|
|
138
|
-
const ids = matched.map((r) => `#${r.id}`).join(', ');
|
|
139
|
-
items.push(`- ${emoji} ${label(matched.length)}: ${ids}`);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const ctx = { wave };
|
|
143
|
-
const detectorResults = await Promise.all(
|
|
144
|
-
(detectors ?? []).map((detector) =>
|
|
145
|
-
runDetector(detector, rows, ctx, logger),
|
|
146
|
-
),
|
|
147
|
-
);
|
|
148
|
-
for (const bullets of detectorResults) {
|
|
149
|
-
for (const b of bullets) items.push(b.startsWith('- ') ? b : `- ${b}`);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!items.length) items.push('- (none)');
|
|
153
|
-
return items.join('\n');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Render and upsert the rolled-up `epic-run-progress` comment on the Epic.
|
|
158
|
-
*
|
|
159
|
-
* Called by `/deliver`'s per-Story status recorder
|
|
160
|
-
* (`epic-execute-record-wave.js`) after each recorder beat. Story #4155
|
|
161
|
-
* (Epic #4151) — the Epic `/deliver` runtime cut over from the wave-batch
|
|
162
|
-
* scheduler to the continuous ready-set core, so the rollup is a **flat
|
|
163
|
-
* per-Story table** keyed by the checkpoint's `stories` status map, not a
|
|
164
|
-
* wave-grouped table. There is no `currentWave` / `totalWaves` / `waves[]`
|
|
165
|
-
* in the payload any more.
|
|
166
|
-
*
|
|
167
|
-
* The payload schema:
|
|
168
|
-
*
|
|
169
|
-
* {
|
|
170
|
-
* "kind": "epic-run-progress",
|
|
171
|
-
* "epicId": <number>,
|
|
172
|
-
* "stories": [ { id, title?, state, blockerCommentId? } ],
|
|
173
|
-
* "startedAt"?: "<iso8601>",
|
|
174
|
-
* "updatedAt": "<iso8601>"
|
|
175
|
-
* }
|
|
176
|
-
*
|
|
177
|
-
* The function does not re-derive Story state from labels — it trusts the
|
|
178
|
-
* `stories` map supplied by the caller (the checkpoint's recorded per-Story
|
|
179
|
-
* statuses).
|
|
180
|
-
*
|
|
181
|
-
* @param {{
|
|
182
|
-
* provider: import('../../../ITicketingProvider.js').ITicketingProvider,
|
|
183
|
-
* epicId: number,
|
|
184
|
-
* stories: Record<string, { status?: string, title?: string,
|
|
185
|
-
* blockerCommentId?: string }>,
|
|
186
|
-
* startedAt?: string,
|
|
187
|
-
* now?: () => Date,
|
|
188
|
-
* }} args
|
|
189
|
-
* @returns {Promise<{ body: string, payload: object }>} the rendered body
|
|
190
|
-
* and payload that were upserted onto the Epic.
|
|
191
|
-
*/
|
|
192
|
-
export async function upsertEpicRunProgress({
|
|
193
|
-
provider,
|
|
194
|
-
epicId,
|
|
195
|
-
stories,
|
|
196
|
-
startedAt,
|
|
197
|
-
now = () => new Date(),
|
|
198
|
-
} = {}) {
|
|
199
|
-
if (!provider || typeof provider.postComment !== 'function') {
|
|
200
|
-
throw new TypeError(
|
|
201
|
-
'upsertEpicRunProgress requires a provider with postComment',
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
const epicIdNum = Number(epicId);
|
|
205
|
-
if (!Number.isInteger(epicIdNum) || epicIdNum <= 0) {
|
|
206
|
-
throw new TypeError('upsertEpicRunProgress requires a numeric epicId');
|
|
207
|
-
}
|
|
208
|
-
const statusMap = stories && typeof stories === 'object' ? stories : {};
|
|
209
|
-
|
|
210
|
-
const updatedAt = now().toISOString();
|
|
211
|
-
const rows = Object.entries(statusMap)
|
|
212
|
-
.map(([key, rec]) => {
|
|
213
|
-
const id = Number(key);
|
|
214
|
-
const state = String(rec?.status ?? 'pending');
|
|
215
|
-
const row = { id, title: String(rec?.title ?? ''), state };
|
|
216
|
-
if (rec?.blockerCommentId != null) {
|
|
217
|
-
row.blockerCommentId = String(rec.blockerCommentId);
|
|
218
|
-
}
|
|
219
|
-
return row;
|
|
220
|
-
})
|
|
221
|
-
.filter((r) => Number.isInteger(r.id) && r.id > 0)
|
|
222
|
-
.sort((a, b) => a.id - b.id);
|
|
223
|
-
|
|
224
|
-
const payload = {
|
|
225
|
-
kind: EPIC_RUN_PROGRESS_TYPE,
|
|
226
|
-
epicId: epicIdNum,
|
|
227
|
-
stories: rows,
|
|
228
|
-
updatedAt,
|
|
229
|
-
};
|
|
230
|
-
if (typeof startedAt === 'string' && startedAt) {
|
|
231
|
-
payload.startedAt = startedAt;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const totalStories = rows.length;
|
|
235
|
-
const doneStories = rows.filter((s) => s.state === 'done').length;
|
|
236
|
-
const header = `### 📊 Epic Progress — ${doneStories}/${totalStories} stories done`;
|
|
237
|
-
|
|
238
|
-
const tableLines = ['| ID | State | Title |', '|---|---|---|'];
|
|
239
|
-
if (rows.length === 0) {
|
|
240
|
-
tableLines.push('| — | _(no stories yet)_ | — |');
|
|
241
|
-
} else {
|
|
242
|
-
for (const s of rows) {
|
|
243
|
-
const emoji = STATE_EMOJI[s.state] ?? '';
|
|
244
|
-
const title = escapePipes(truncate(s.title, 60));
|
|
245
|
-
tableLines.push(`| #${s.id} | ${emoji} ${s.state} | ${title} |`);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
const body = [
|
|
250
|
-
header,
|
|
251
|
-
'',
|
|
252
|
-
tableLines.join('\n'),
|
|
253
|
-
'',
|
|
254
|
-
'```json',
|
|
255
|
-
JSON.stringify(payload, null, 2),
|
|
256
|
-
'```',
|
|
257
|
-
].join('\n');
|
|
258
|
-
|
|
259
|
-
await upsertStructuredComment(
|
|
260
|
-
provider,
|
|
261
|
-
epicIdNum,
|
|
262
|
-
EPIC_RUN_PROGRESS_TYPE,
|
|
263
|
-
body,
|
|
264
|
-
);
|
|
265
|
-
|
|
266
|
-
return { body, payload };
|
|
267
|
-
}
|
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* progress-reporter/signals.js — parsing and aggregation helpers for the
|
|
3
|
-
* `story-run-progress` and `phase-timings` structured comments.
|
|
4
|
-
*
|
|
5
|
-
* Extracted from the parent `progress-reporter.js` so the aggregator
|
|
6
|
-
* surface (phase percentile rollups, story-run-progress parsing) is
|
|
7
|
-
* testable independently of the comment composition (`composition.js`)
|
|
8
|
-
* and webhook transport (`transport.js`). Pure functions only — no
|
|
9
|
-
* provider calls, no I/O — which makes this the natural home for the
|
|
10
|
-
* shared state lookup tables (`PHASE_TO_STATE`, `PHASE_ORDER`,
|
|
11
|
-
* `STATE_EMOJI`) and the structured-comment kind constants.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
import { parseFencedJsonComment } from '../../structured-comment-parser.js';
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Structured-comment kind for the rolled-up Epic-level progress
|
|
18
|
-
* comment. Persisted via `composition.upsertEpicRunProgress` and read
|
|
19
|
-
* back by tests + reconcilers.
|
|
20
|
-
*/
|
|
21
|
-
export const EPIC_RUN_PROGRESS_TYPE = 'epic-run-progress';
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Structured-comment kind for the per-Story phase-timings summary
|
|
25
|
-
* posted by `story-close.js`. Read back by the ProgressReporter so the
|
|
26
|
-
* Epic-level snapshot can aggregate median/p95 across waves.
|
|
27
|
-
*/
|
|
28
|
-
export const PHASE_TIMINGS_TYPE = 'phase-timings';
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Structured-comment kind for the per-Story run-progress snapshot that
|
|
32
|
-
* `/deliver` upserts on every Task transition. Read by
|
|
33
|
-
* ProgressReporter so the Epic-level table reflects sub-agent state in
|
|
34
|
-
* near-real time instead of label-derived classifications.
|
|
35
|
-
*/
|
|
36
|
-
export const STORY_RUN_PROGRESS_TYPE = 'story-run-progress';
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Phase → high-level state classification. Lookup table flattens the
|
|
40
|
-
* previous switch so cyclomatic complexity stays at 1 (one branch in the
|
|
41
|
-
* `??` fallback) rather than 6 — keeps the CRAP score floor-bound under
|
|
42
|
-
* coverage variance.
|
|
43
|
-
*/
|
|
44
|
-
export const PHASE_TO_STATE = {
|
|
45
|
-
done: 'done',
|
|
46
|
-
blocked: 'blocked',
|
|
47
|
-
implementing: 'in-flight',
|
|
48
|
-
closing: 'in-flight',
|
|
49
|
-
init: 'queued',
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Fixed ordering for the rendered phase-timings table. Matches the enum
|
|
54
|
-
* in lib/util/phase-timer.js so rows line up with how operators think
|
|
55
|
-
* about the story lifecycle rather than re-sorting by alphabet or
|
|
56
|
-
* frequency.
|
|
57
|
-
*/
|
|
58
|
-
export const PHASE_ORDER = [
|
|
59
|
-
'worktree-create',
|
|
60
|
-
'bootstrap',
|
|
61
|
-
'install',
|
|
62
|
-
'implement',
|
|
63
|
-
'lint',
|
|
64
|
-
'test',
|
|
65
|
-
'close',
|
|
66
|
-
'api-sync',
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Emoji prefix per high-level state. Consumed by the rolled-up Epic table
|
|
71
|
-
* (`composition.upsertEpicRunProgress`) so operators see a consistent icon
|
|
72
|
-
* per state across the progress surface.
|
|
73
|
-
*/
|
|
74
|
-
export const STATE_EMOJI = {
|
|
75
|
-
done: '✅',
|
|
76
|
-
blocked: '🚧',
|
|
77
|
-
'in-flight': '🔧',
|
|
78
|
-
queued: '⏳',
|
|
79
|
-
unknown: '❓',
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Map a `story-run-progress` phase to the high-level state label used
|
|
84
|
-
* by the rendered tables. Returns `'unknown'` for any phase value that
|
|
85
|
-
* isn't in the lookup table so an unexpected phase surfaces as an
|
|
86
|
-
* unreadable row rather than a crash.
|
|
87
|
-
*/
|
|
88
|
-
export function phaseToState(phase) {
|
|
89
|
-
return PHASE_TO_STATE[phase] ?? 'unknown';
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Parse a `story-run-progress` structured comment posted by `/deliver`.
|
|
94
|
-
* Returns `null` for any malformed body — the caller falls back to the
|
|
95
|
-
* ticket-label state derivation in that case.
|
|
96
|
-
*
|
|
97
|
-
* Expected payload shape (JSON inside a fenced json codeblock):
|
|
98
|
-
* {
|
|
99
|
-
* storyId: number,
|
|
100
|
-
* branch?: string,
|
|
101
|
-
* phase: 'init'|'implementing'|'closing'|'blocked'|'done',
|
|
102
|
-
* title?: string,
|
|
103
|
-
* updatedAt?: string,
|
|
104
|
-
* }
|
|
105
|
-
*/
|
|
106
|
-
export function parseStoryRunProgressComment(comment) {
|
|
107
|
-
const payload = parseFencedJsonComment(comment);
|
|
108
|
-
if (!payload || typeof payload !== 'object') return null;
|
|
109
|
-
const phase = typeof payload.phase === 'string' ? payload.phase : undefined;
|
|
110
|
-
return {
|
|
111
|
-
storyId: Number(payload.storyId),
|
|
112
|
-
title: typeof payload.title === 'string' ? payload.title : '',
|
|
113
|
-
phase,
|
|
114
|
-
state: phaseToState(phase),
|
|
115
|
-
};
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Extract the `{ phases, ... }` payload from a `phase-timings` structured
|
|
120
|
-
* comment. Comment body is the fenced-JSON format produced by
|
|
121
|
-
* `renderPhaseTimingsCommentBody` in story-close. Returns `null`
|
|
122
|
-
* for any parse failure — the caller treats that as "no summary
|
|
123
|
-
* available" without erroring out progress rendering.
|
|
124
|
-
*/
|
|
125
|
-
export function parsePhaseTimingsComment(comment) {
|
|
126
|
-
const payload = parseFencedJsonComment(comment);
|
|
127
|
-
if (!payload || typeof payload !== 'object') return null;
|
|
128
|
-
if (!Array.isArray(payload.phases)) return null;
|
|
129
|
-
return {
|
|
130
|
-
storyId: Number(payload.storyId),
|
|
131
|
-
totalMs: Number(payload.totalMs) || 0,
|
|
132
|
-
phases: payload.phases
|
|
133
|
-
.filter(
|
|
134
|
-
(p) =>
|
|
135
|
-
p &&
|
|
136
|
-
typeof p.name === 'string' &&
|
|
137
|
-
Number.isFinite(Number(p.elapsedMs)),
|
|
138
|
-
)
|
|
139
|
-
.map((p) => ({ name: p.name, elapsedMs: Number(p.elapsedMs) })),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Aggregate a list of `phase-timings` summaries into per-phase median,
|
|
145
|
-
* p95, and sample count. Returns phases ordered by the canonical
|
|
146
|
-
* `PHASE_ORDER` so the rendered table always has the same row sequence —
|
|
147
|
-
* operators should never have to hunt for the `install` row.
|
|
148
|
-
*/
|
|
149
|
-
export function aggregatePhaseTimings(summaries) {
|
|
150
|
-
const buckets = new Map();
|
|
151
|
-
for (const s of summaries) {
|
|
152
|
-
if (!s || !Array.isArray(s.phases)) continue;
|
|
153
|
-
for (const p of s.phases) {
|
|
154
|
-
if (!buckets.has(p.name)) buckets.set(p.name, []);
|
|
155
|
-
buckets.get(p.name).push(p.elapsedMs);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
const rows = [];
|
|
159
|
-
for (const name of PHASE_ORDER) {
|
|
160
|
-
const samples = buckets.get(name);
|
|
161
|
-
if (!samples || samples.length === 0) continue;
|
|
162
|
-
rows.push({
|
|
163
|
-
name,
|
|
164
|
-
median: percentile(samples, 0.5),
|
|
165
|
-
p95: percentile(samples, 0.95),
|
|
166
|
-
n: samples.length,
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
// Include any unexpected phase names at the tail so a future enum
|
|
170
|
-
// addition surfaces in the table instead of being silently dropped.
|
|
171
|
-
for (const [name, samples] of buckets.entries()) {
|
|
172
|
-
if (PHASE_ORDER.includes(name)) continue;
|
|
173
|
-
rows.push({
|
|
174
|
-
name,
|
|
175
|
-
median: percentile(samples, 0.5),
|
|
176
|
-
p95: percentile(samples, 0.95),
|
|
177
|
-
n: samples.length,
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
return rows;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
function percentile(samples, q) {
|
|
184
|
-
const sorted = [...samples].sort((a, b) => a - b);
|
|
185
|
-
if (sorted.length === 0) return 0;
|
|
186
|
-
// Nearest-rank method — clamped so q=1 picks the last element.
|
|
187
|
-
const idx = Math.min(
|
|
188
|
-
sorted.length - 1,
|
|
189
|
-
Math.max(0, Math.ceil(q * sorted.length) - 1),
|
|
190
|
-
);
|
|
191
|
-
return sorted[idx];
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
/**
|
|
195
|
-
* Render the aggregated phase-timings table. Returns `null` when there
|
|
196
|
-
* are no summaries to render so the caller can elide the section
|
|
197
|
-
* entirely rather than emitting an empty stub.
|
|
198
|
-
*/
|
|
199
|
-
export function renderPhaseTimingsSection(summaries) {
|
|
200
|
-
if (!Array.isArray(summaries) || summaries.length === 0) return null;
|
|
201
|
-
const rows = aggregatePhaseTimings(summaries);
|
|
202
|
-
if (rows.length === 0) return null;
|
|
203
|
-
const header = `### Phase timings (last ${summaries.length} completed stor${summaries.length === 1 ? 'y' : 'ies'})`;
|
|
204
|
-
const table = [
|
|
205
|
-
'| Phase | median ms | p95 ms | n |',
|
|
206
|
-
'| --- | --- | --- | --- |',
|
|
207
|
-
...rows.map((r) => `| ${r.name} | ${r.median} | ${r.p95} | ${r.n} |`),
|
|
208
|
-
].join('\n');
|
|
209
|
-
return `${header}\n\n${table}`;
|
|
210
|
-
}
|