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,117 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* epic-plan-state-store — stateless functions for reading and writing the
|
|
3
|
-
* `epic-plan-state` structured comment on the Epic issue.
|
|
4
|
-
*
|
|
5
|
-
* This module is the function-based replacement for the legacy
|
|
6
|
-
* `PlanCheckpointer` class that previously lived at
|
|
7
|
-
* `./plan-runner/plan-checkpointer.js`. Bodies were lifted verbatim from
|
|
8
|
-
* the corresponding `PlanCheckpointer` methods so the structured-comment
|
|
9
|
-
* shape is preserved byte-for-byte. Story #2423 (Epic #2307) deleted
|
|
10
|
-
* the class file; the class API survives as a tests-only fixture at
|
|
11
|
-
* `tests/fixtures/epic-plan-state-store.js`.
|
|
12
|
-
*
|
|
13
|
-
* Schema (see Tech Spec #351):
|
|
14
|
-
*
|
|
15
|
-
* ```json
|
|
16
|
-
* {
|
|
17
|
-
* "version": 1,
|
|
18
|
-
* "epicId": 349,
|
|
19
|
-
* "startedAt": "...",
|
|
20
|
-
* "lastUpdatedAt": "...",
|
|
21
|
-
* "spec": { "techSpecPersisted": false, "acceptanceTable": "none", "completedAt": null },
|
|
22
|
-
* "decompose": { "ticketCount": null, "completedAt": null },
|
|
23
|
-
* "planningRisk": { "overallLevel": "...", "requiresReview": true, "gateDecision": "..." },
|
|
24
|
-
* "reviewRouting": { "decision": "review-required|auto-proceed|operator-override-review", "requiresStop": true, "forceReviewApplied": false },
|
|
25
|
-
* "manifestCommentId": null
|
|
26
|
-
* }
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* The checkpoint no longer carries a write-only `phase` field (Story #3909):
|
|
30
|
-
* the lifecycle phase is already authoritative on the Epic's `agent::*` labels,
|
|
31
|
-
* so the duplicate `phase` telemetry (and its `setPhase` round-trips) was
|
|
32
|
-
* deleted. The fields that survive — `spec`, `decompose`, `planningRisk`,
|
|
33
|
-
* `reviewRouting`, `manifestCommentId` — are the ones `/plan --resume`
|
|
34
|
-
* reads to skip already-completed work.
|
|
35
|
-
*/
|
|
36
|
-
|
|
37
|
-
import { parseFencedJsonComment } from './structured-comment-parser.js';
|
|
38
|
-
import { findStructuredComment, upsertStructuredComment } from './ticketing.js';
|
|
39
|
-
|
|
40
|
-
export const EPIC_PLAN_STATE_TYPE = 'epic-plan-state';
|
|
41
|
-
export const PLAN_CHECKPOINT_SCHEMA_VERSION = 1;
|
|
42
|
-
|
|
43
|
-
function assertProvider(provider) {
|
|
44
|
-
if (!provider)
|
|
45
|
-
throw new TypeError('epic-plan-state-store requires a provider');
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
function assertEpicId(epicId) {
|
|
49
|
-
if (!Number.isInteger(epicId)) {
|
|
50
|
-
throw new TypeError('epic-plan-state-store requires a numeric epicId');
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Read and parse the checkpoint. Returns null if the comment is missing or
|
|
56
|
-
* unparseable (callers treat null as "start fresh").
|
|
57
|
-
*
|
|
58
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number }} opts
|
|
59
|
-
* @returns {Promise<object | null>}
|
|
60
|
-
*/
|
|
61
|
-
export async function read({ provider, epicId } = {}) {
|
|
62
|
-
assertProvider(provider);
|
|
63
|
-
assertEpicId(epicId);
|
|
64
|
-
const comment = await findStructuredComment(
|
|
65
|
-
provider,
|
|
66
|
-
epicId,
|
|
67
|
-
EPIC_PLAN_STATE_TYPE,
|
|
68
|
-
);
|
|
69
|
-
return parseFencedJsonComment(comment);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Overwrite the checkpoint with the supplied merged state.
|
|
74
|
-
*
|
|
75
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, state: object }} opts
|
|
76
|
-
*/
|
|
77
|
-
export async function write({ provider, epicId, state } = {}) {
|
|
78
|
-
assertProvider(provider);
|
|
79
|
-
assertEpicId(epicId);
|
|
80
|
-
const payload = {
|
|
81
|
-
version: PLAN_CHECKPOINT_SCHEMA_VERSION,
|
|
82
|
-
...state,
|
|
83
|
-
lastUpdatedAt: new Date().toISOString(),
|
|
84
|
-
};
|
|
85
|
-
const body = `\`\`\`json\n${JSON.stringify(payload, null, 2)}\n\`\`\``;
|
|
86
|
-
await upsertStructuredComment(provider, epicId, EPIC_PLAN_STATE_TYPE, body);
|
|
87
|
-
return payload;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Initialize the checkpoint. Idempotent — returns the existing state if one
|
|
92
|
-
* is present, otherwise writes a fresh skeleton. Overrides from `seed` are
|
|
93
|
-
* merged into a freshly-written skeleton; an already-present checkpoint is
|
|
94
|
-
* returned unchanged.
|
|
95
|
-
*
|
|
96
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, seed?: object }} opts
|
|
97
|
-
* @returns {Promise<object>}
|
|
98
|
-
*/
|
|
99
|
-
export async function initialize({ provider, epicId, seed = {} } = {}) {
|
|
100
|
-
assertProvider(provider);
|
|
101
|
-
assertEpicId(epicId);
|
|
102
|
-
const existing = await read({ provider, epicId });
|
|
103
|
-
if (existing) return existing;
|
|
104
|
-
const now = new Date().toISOString();
|
|
105
|
-
const skeleton = {
|
|
106
|
-
epicId,
|
|
107
|
-
startedAt: now,
|
|
108
|
-
spec: {
|
|
109
|
-
techSpecPersisted: false,
|
|
110
|
-
acceptanceTable: 'none',
|
|
111
|
-
completedAt: null,
|
|
112
|
-
},
|
|
113
|
-
decompose: { ticketCount: null, completedAt: null },
|
|
114
|
-
manifestCommentId: null,
|
|
115
|
-
};
|
|
116
|
-
return write({ provider, epicId, state: { ...skeleton, ...seed } });
|
|
117
|
-
}
|
|
@@ -1,388 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* epic-run-state-store — stateless functions for reading and writing the
|
|
3
|
-
* `epic-run-state` structured comment used by `/deliver`.
|
|
4
|
-
*
|
|
5
|
-
* Story #4155 (Epic #4151) — the Epic `/deliver` runtime cut over from
|
|
6
|
-
* the wave-batch scheduler to the continuous ready-set core
|
|
7
|
-
* (`lib/wave-runner/ready-set.js`). The checkpoint shrank with it: it no
|
|
8
|
-
* longer carries `currentWave`, `plan[][]`, `totalWaves`, or the
|
|
9
|
-
* per-wave `waves[]` aggregation. The durable run state is now a flat
|
|
10
|
-
* **per-Story status map** (`stories: { [storyId]: { status, title?,
|
|
11
|
-
* blockerCommentId? } }`) plus the run-level `concurrencyCap` (the
|
|
12
|
-
* GLOBAL in-flight cap the ready-set selector honours), `phase`,
|
|
13
|
-
* `startedAt`, and `manualInterventions[]`. There is no resume-pointer
|
|
14
|
-
* to reconcile — the ready-set core re-derives adjacency and readiness
|
|
15
|
-
* from live Story bodies/labels on every tick, so the checkpoint only
|
|
16
|
-
* records terminal Story outcomes (for the auto-merge predicate, branch
|
|
17
|
-
* cleanup, and the operator rollup) and the run-level knobs.
|
|
18
|
-
*
|
|
19
|
-
* This module is the function-based replacement for the legacy
|
|
20
|
-
* `Checkpointer` class that previously lived at
|
|
21
|
-
* `./epic-runner/checkpointer.js`. Story #2423 (Epic #2307) deleted the
|
|
22
|
-
* class file; the class API survives as a tests-only fixture at
|
|
23
|
-
* `tests/fixtures/epic-run-state-store.js`.
|
|
24
|
-
*
|
|
25
|
-
* The comment is identified by a stable HTML marker so it can be overwritten
|
|
26
|
-
* idempotently across orchestrator restarts. The body is a fenced JSON block.
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
import { assertValidDeliverPhase } from './epic-runner/deliver-phases.js';
|
|
30
|
-
import { parseFencedJsonComment } from './structured-comment-parser.js';
|
|
31
|
-
import { findStructuredComment, upsertStructuredComment } from './ticketing.js';
|
|
32
|
-
|
|
33
|
-
export const EPIC_RUN_STATE_TYPE = 'epic-run-state';
|
|
34
|
-
export const CHECKPOINT_SCHEMA_VERSION = 2;
|
|
35
|
-
|
|
36
|
-
/** Terminal / in-progress per-Story statuses persisted on the checkpoint. */
|
|
37
|
-
export const STORY_STATUSES = Object.freeze([
|
|
38
|
-
'pending',
|
|
39
|
-
'done',
|
|
40
|
-
'blocked',
|
|
41
|
-
'failed',
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
// Re-export the phase enum + index helper so downstream importers continue
|
|
45
|
-
// to use this module as a single import target.
|
|
46
|
-
export {
|
|
47
|
-
DELIVER_PHASES,
|
|
48
|
-
phaseIndex,
|
|
49
|
-
} from './epic-runner/deliver-phases.js';
|
|
50
|
-
|
|
51
|
-
function assertProvider(provider) {
|
|
52
|
-
if (!provider)
|
|
53
|
-
throw new TypeError('epic-run-state-store requires a provider');
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function assertEpicId(epicId) {
|
|
57
|
-
if (!Number.isInteger(epicId)) {
|
|
58
|
-
throw new TypeError('epic-run-state-store requires a numeric epicId');
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Normalize an inbound Story id (accepts the ticket `id` shape, the raw
|
|
64
|
-
* GitHub `number` shape, and a bare integer) to a positive integer, or
|
|
65
|
-
* `null` when it is absent / non-positive / non-integer.
|
|
66
|
-
*
|
|
67
|
-
* @param {object|number|string} entry
|
|
68
|
-
* @returns {number|null}
|
|
69
|
-
*/
|
|
70
|
-
function storyIdOf(entry) {
|
|
71
|
-
if (typeof entry === 'number') {
|
|
72
|
-
return Number.isInteger(entry) && entry > 0 ? entry : null;
|
|
73
|
-
}
|
|
74
|
-
if (!entry || typeof entry !== 'object') {
|
|
75
|
-
const n = Number(entry);
|
|
76
|
-
return Number.isInteger(n) && n > 0 ? n : null;
|
|
77
|
-
}
|
|
78
|
-
const raw = entry.id ?? entry.storyId ?? entry.number;
|
|
79
|
-
const id = Number(raw);
|
|
80
|
-
return Number.isInteger(id) && id > 0 ? id : null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Build the initial per-Story status map from a list of Story records (or
|
|
85
|
-
* ids). Every Story seeds at `status: 'pending'`; an optional `title` is
|
|
86
|
-
* carried through when the record supplies one so the operator rollup and
|
|
87
|
-
* branch-cleanup surfaces have a label without a second fetch. Keys are the
|
|
88
|
-
* positive-integer Story ids as strings (JSON object keys are strings);
|
|
89
|
-
* shapeless / non-positive entries are dropped.
|
|
90
|
-
*
|
|
91
|
-
* Pure helper — exported for unit tests.
|
|
92
|
-
*
|
|
93
|
-
* @param {Array<object|number>} stories
|
|
94
|
-
* @returns {Record<string, { status: string, title?: string }>}
|
|
95
|
-
*/
|
|
96
|
-
export function buildStoryStatusMap(stories) {
|
|
97
|
-
const out = {};
|
|
98
|
-
for (const entry of Array.isArray(stories) ? stories : []) {
|
|
99
|
-
const id = storyIdOf(entry);
|
|
100
|
-
if (id === null) continue;
|
|
101
|
-
const record = { status: 'pending' };
|
|
102
|
-
const title =
|
|
103
|
-
entry && typeof entry === 'object' && typeof entry.title === 'string'
|
|
104
|
-
? entry.title
|
|
105
|
-
: undefined;
|
|
106
|
-
if (title) record.title = title;
|
|
107
|
-
out[String(id)] = record;
|
|
108
|
-
}
|
|
109
|
-
return out;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Read and parse the checkpoint. Returns null if the comment is missing or
|
|
114
|
-
* unparseable (callers treat null as "start fresh").
|
|
115
|
-
*
|
|
116
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number }} opts
|
|
117
|
-
* @returns {Promise<object | null>}
|
|
118
|
-
*/
|
|
119
|
-
export async function read({ provider, epicId } = {}) {
|
|
120
|
-
assertProvider(provider);
|
|
121
|
-
assertEpicId(epicId);
|
|
122
|
-
const comment = await findStructuredComment(
|
|
123
|
-
provider,
|
|
124
|
-
epicId,
|
|
125
|
-
EPIC_RUN_STATE_TYPE,
|
|
126
|
-
);
|
|
127
|
-
return parseFencedJsonComment(comment);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* Overwrite the checkpoint with `state`. Idempotent — callers may invoke
|
|
132
|
-
* freely per tick; the marker-scoped upsert deletes the prior comment.
|
|
133
|
-
*
|
|
134
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, state: object }} opts
|
|
135
|
-
*/
|
|
136
|
-
export async function write({ provider, epicId, state } = {}) {
|
|
137
|
-
assertProvider(provider);
|
|
138
|
-
assertEpicId(epicId);
|
|
139
|
-
const payload = {
|
|
140
|
-
version: CHECKPOINT_SCHEMA_VERSION,
|
|
141
|
-
...state,
|
|
142
|
-
lastUpdatedAt: new Date().toISOString(),
|
|
143
|
-
};
|
|
144
|
-
const body = `\`\`\`json\n${JSON.stringify(payload, null, 2)}\n\`\`\``;
|
|
145
|
-
await upsertStructuredComment(provider, epicId, EPIC_RUN_STATE_TYPE, body);
|
|
146
|
-
return payload;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Initial checkpoint for a brand-new run. Idempotent against re-dispatch:
|
|
151
|
-
* when an existing checkpoint is found and the persisted `concurrencyCap`
|
|
152
|
-
* matches the incoming value, the existing state is returned verbatim (no
|
|
153
|
-
* rewrite) so a re-prepare preserves `startedAt`, prior Story statuses, and
|
|
154
|
-
* `manualInterventions`. When the cap differs (an operator re-tuned the
|
|
155
|
-
* global in-flight cap) it is refreshed in place; the Story status map is
|
|
156
|
-
* **merged** so any Story that already reached a terminal status keeps it
|
|
157
|
-
* while newly-discovered Stories are seeded at `pending`. Prepare owns the
|
|
158
|
-
* Story set (it overwrites it on every run) but never clobbers recorded
|
|
159
|
-
* progress.
|
|
160
|
-
*
|
|
161
|
-
* @param {{
|
|
162
|
-
* provider: import('../ITicketingProvider.js').ITicketingProvider,
|
|
163
|
-
* epicId: number,
|
|
164
|
-
* storyIds: Array<object|number>,
|
|
165
|
-
* concurrencyCap: number,
|
|
166
|
-
* }} opts
|
|
167
|
-
* `concurrencyCap` is the GLOBAL in-flight cap the ready-set selector
|
|
168
|
-
* honours (`selectReadySet({ globalCap })`).
|
|
169
|
-
*/
|
|
170
|
-
export async function initialize({
|
|
171
|
-
provider,
|
|
172
|
-
epicId,
|
|
173
|
-
storyIds,
|
|
174
|
-
concurrencyCap,
|
|
175
|
-
} = {}) {
|
|
176
|
-
assertProvider(provider);
|
|
177
|
-
assertEpicId(epicId);
|
|
178
|
-
const seededStories = buildStoryStatusMap(storyIds);
|
|
179
|
-
const existing = await read({ provider, epicId });
|
|
180
|
-
if (existing) {
|
|
181
|
-
const mergedStories = mergeStoryStatuses(existing.stories, seededStories);
|
|
182
|
-
if (
|
|
183
|
-
existing.concurrencyCap === concurrencyCap &&
|
|
184
|
-
storyMapsEqual(existing.stories, mergedStories)
|
|
185
|
-
) {
|
|
186
|
-
return existing;
|
|
187
|
-
}
|
|
188
|
-
return write({
|
|
189
|
-
provider,
|
|
190
|
-
epicId,
|
|
191
|
-
state: { ...existing, concurrencyCap, stories: mergedStories },
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
return write({
|
|
195
|
-
provider,
|
|
196
|
-
epicId,
|
|
197
|
-
state: {
|
|
198
|
-
epicId,
|
|
199
|
-
startedAt: new Date().toISOString(),
|
|
200
|
-
concurrencyCap,
|
|
201
|
-
phase: 'prepare',
|
|
202
|
-
stories: seededStories,
|
|
203
|
-
manualInterventions: [],
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Merge a freshly-seeded Story status map onto a persisted one. Every Story
|
|
210
|
-
* present in either map appears in the result; when a Story exists in the
|
|
211
|
-
* prior map its recorded status / blockerCommentId win (recorded progress is
|
|
212
|
-
* never lost), while its `title` is refreshed from the incoming seed when the
|
|
213
|
-
* seed supplies one. Stories present only in the incoming seed are added at
|
|
214
|
-
* their seeded (`pending`) status. Pure — exported for unit tests.
|
|
215
|
-
*
|
|
216
|
-
* @param {Record<string, object>|undefined} prior
|
|
217
|
-
* @param {Record<string, object>} incoming
|
|
218
|
-
* @returns {Record<string, object>}
|
|
219
|
-
*/
|
|
220
|
-
export function mergeStoryStatuses(prior, incoming) {
|
|
221
|
-
const priorMap = prior && typeof prior === 'object' ? prior : {};
|
|
222
|
-
const seedMap = incoming && typeof incoming === 'object' ? incoming : {};
|
|
223
|
-
const out = {};
|
|
224
|
-
for (const key of new Set([
|
|
225
|
-
...Object.keys(priorMap),
|
|
226
|
-
...Object.keys(seedMap),
|
|
227
|
-
])) {
|
|
228
|
-
const priorEntry = priorMap[key];
|
|
229
|
-
const seedEntry = seedMap[key];
|
|
230
|
-
if (priorEntry && typeof priorEntry === 'object') {
|
|
231
|
-
const merged = { ...priorEntry };
|
|
232
|
-
if (seedEntry && typeof seedEntry.title === 'string') {
|
|
233
|
-
merged.title = seedEntry.title;
|
|
234
|
-
}
|
|
235
|
-
out[key] = merged;
|
|
236
|
-
} else {
|
|
237
|
-
out[key] = seedEntry;
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
return out;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Structural equality on two Story status maps — same key set and, per key,
|
|
245
|
-
* the same `status`, `title`, and `blockerCommentId`. Used by `initialize`
|
|
246
|
-
* to decide whether an idempotent re-prepare needs a rewrite. Pure.
|
|
247
|
-
*
|
|
248
|
-
* @param {Record<string, object>|undefined} a
|
|
249
|
-
* @param {Record<string, object>|undefined} b
|
|
250
|
-
* @returns {boolean}
|
|
251
|
-
*/
|
|
252
|
-
function storyMapsEqual(a, b) {
|
|
253
|
-
const left = a && typeof a === 'object' ? a : {};
|
|
254
|
-
const right = b && typeof b === 'object' ? b : {};
|
|
255
|
-
const keys = Object.keys(left);
|
|
256
|
-
if (keys.length !== Object.keys(right).length) return false;
|
|
257
|
-
for (const key of keys) {
|
|
258
|
-
const l = left[key] ?? {};
|
|
259
|
-
const r = right[key];
|
|
260
|
-
if (!r) return false;
|
|
261
|
-
if (
|
|
262
|
-
l.status !== r.status ||
|
|
263
|
-
l.title !== r.title ||
|
|
264
|
-
l.blockerCommentId !== r.blockerCommentId
|
|
265
|
-
) {
|
|
266
|
-
return false;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
return true;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Record a per-Story terminal (or in-progress) status on the checkpoint.
|
|
274
|
-
* Reads the current state first, splices the single Story's record into the
|
|
275
|
-
* `stories` map, and re-writes. Other Stories and all run-level fields are
|
|
276
|
-
* preserved verbatim. Tolerant of a legacy/absent `stories` map (treated as
|
|
277
|
-
* empty) so a checkpoint that predates a field is upgraded in place.
|
|
278
|
-
*
|
|
279
|
-
* @param {{
|
|
280
|
-
* provider: import('../ITicketingProvider.js').ITicketingProvider,
|
|
281
|
-
* epicId: number,
|
|
282
|
-
* storyId: number,
|
|
283
|
-
* status: string,
|
|
284
|
-
* title?: string,
|
|
285
|
-
* blockerCommentId?: string|number|null,
|
|
286
|
-
* }} opts
|
|
287
|
-
* @returns {Promise<object>} the persisted state
|
|
288
|
-
*/
|
|
289
|
-
export async function recordStoryStatus({
|
|
290
|
-
provider,
|
|
291
|
-
epicId,
|
|
292
|
-
storyId,
|
|
293
|
-
status,
|
|
294
|
-
title,
|
|
295
|
-
blockerCommentId,
|
|
296
|
-
} = {}) {
|
|
297
|
-
assertProvider(provider);
|
|
298
|
-
assertEpicId(epicId);
|
|
299
|
-
const id = storyIdOf(storyId);
|
|
300
|
-
if (id === null) {
|
|
301
|
-
throw new TypeError(
|
|
302
|
-
'recordStoryStatus: storyId must be a positive integer',
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
if (!STORY_STATUSES.includes(status)) {
|
|
306
|
-
throw new RangeError(
|
|
307
|
-
`recordStoryStatus: status "${status}" must be one of: ${STORY_STATUSES.join(', ')}`,
|
|
308
|
-
);
|
|
309
|
-
}
|
|
310
|
-
const existing = (await read({ provider, epicId })) ?? {};
|
|
311
|
-
const stories =
|
|
312
|
-
existing.stories && typeof existing.stories === 'object'
|
|
313
|
-
? { ...existing.stories }
|
|
314
|
-
: {};
|
|
315
|
-
const prior = stories[String(id)] ?? {};
|
|
316
|
-
const record = { ...prior, status };
|
|
317
|
-
if (typeof title === 'string' && title) record.title = title;
|
|
318
|
-
if (status === 'blocked' && blockerCommentId != null) {
|
|
319
|
-
record.blockerCommentId = String(blockerCommentId);
|
|
320
|
-
}
|
|
321
|
-
stories[String(id)] = record;
|
|
322
|
-
return write({
|
|
323
|
-
provider,
|
|
324
|
-
epicId,
|
|
325
|
-
state: { ...existing, stories },
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Append a manual-intervention record to the checkpoint. Out-of-band
|
|
331
|
-
* recovery steps the host LLM performs during a delivery — `AskUserQuestion`
|
|
332
|
-
* calls, `git restore`/`git reset` against the working tree, manual `--no-ff`
|
|
333
|
-
* recovery merges, story-close `--skipValidation` overrides — disqualify the
|
|
334
|
-
* Epic from auto-merge. The auto-merge predicate reads this array and only
|
|
335
|
-
* fires when it is empty.
|
|
336
|
-
*
|
|
337
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, entry: { reason: string, source?: string, ts?: string } }} opts
|
|
338
|
-
* @returns {Promise<object>} the persisted state
|
|
339
|
-
*/
|
|
340
|
-
export async function appendIntervention({ provider, epicId, entry } = {}) {
|
|
341
|
-
assertProvider(provider);
|
|
342
|
-
assertEpicId(epicId);
|
|
343
|
-
if (!entry || typeof entry.reason !== 'string' || entry.reason.length === 0) {
|
|
344
|
-
throw new TypeError('appendIntervention: { reason: string } is required.');
|
|
345
|
-
}
|
|
346
|
-
const existing = (await read({ provider, epicId })) ?? {};
|
|
347
|
-
const list = Array.isArray(existing.manualInterventions)
|
|
348
|
-
? existing.manualInterventions
|
|
349
|
-
: [];
|
|
350
|
-
const record = {
|
|
351
|
-
reason: entry.reason,
|
|
352
|
-
source: typeof entry.source === 'string' ? entry.source : 'host-llm',
|
|
353
|
-
ts: typeof entry.ts === 'string' ? entry.ts : new Date().toISOString(),
|
|
354
|
-
};
|
|
355
|
-
return write({
|
|
356
|
-
provider,
|
|
357
|
-
epicId,
|
|
358
|
-
state: {
|
|
359
|
-
...existing,
|
|
360
|
-
manualInterventions: [...list, record],
|
|
361
|
-
},
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Advance the checkpoint's `phase` field to the next `/deliver`
|
|
367
|
-
* phase. Reads the current state first so the caller does not need to
|
|
368
|
-
* keep an in-memory copy. Other state fields are preserved verbatim.
|
|
369
|
-
*
|
|
370
|
-
* Story #1155 / Epic #1142 — phase-granular resume. The runner writes
|
|
371
|
-
* the **next phase to run** here, not the phase that just finished, so
|
|
372
|
-
* a resume can match `phase === 'code-review'` to mean "Phase D is the
|
|
373
|
-
* next thing to do."
|
|
374
|
-
*
|
|
375
|
-
* @param {{ provider: import('../ITicketingProvider.js').ITicketingProvider, epicId: number, nextPhase: string }} opts
|
|
376
|
-
* nextPhase - One of `DELIVER_PHASES` or `'done'`.
|
|
377
|
-
*/
|
|
378
|
-
export async function setPhase({ provider, epicId, nextPhase } = {}) {
|
|
379
|
-
assertProvider(provider);
|
|
380
|
-
assertEpicId(epicId);
|
|
381
|
-
assertValidDeliverPhase(nextPhase);
|
|
382
|
-
const existing = (await read({ provider, epicId })) ?? {};
|
|
383
|
-
return write({
|
|
384
|
-
provider,
|
|
385
|
-
epicId,
|
|
386
|
-
state: { ...existing, phase: nextPhase },
|
|
387
|
-
});
|
|
388
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cross-Story conflict-finding gate for `epic-deliver-prepare.js`.
|
|
3
|
-
*
|
|
4
|
-
* Story #2297 — when the bounded `/plan` flow emitted concurrency
|
|
5
|
-
* findings (Story #2296's validator pass), the operator may have shipped
|
|
6
|
-
* them through to `/deliver` without resolving the underlying
|
|
7
|
-
* `depends_on` gaps. This gate runs at Phase 1 of `/deliver` and
|
|
8
|
-
* refuses to flip the Epic to `agent::executing` when the upcoming
|
|
9
|
-
* waves still contain unresolved conflicts — surfacing the exact
|
|
10
|
-
* remediation commands the operator should run before retrying.
|
|
11
|
-
*
|
|
12
|
-
* The gate is opt-in by default:
|
|
13
|
-
* - Advisory findings (severity 'soft') only log a summary.
|
|
14
|
-
* - Hard findings (severity 'hard') OR `delivery.failOnConcurrencyHazards: true`
|
|
15
|
-
* promote a hard-stop.
|
|
16
|
-
*
|
|
17
|
-
* `--ignore-concurrency-hazards` bypasses the gate; the flag use is
|
|
18
|
-
* recorded on the Epic checkpoint so retro tooling can flag a run that
|
|
19
|
-
* shipped despite an outstanding hazard.
|
|
20
|
-
*
|
|
21
|
-
* The module is pure: it consumes a findings array and a wave plan and
|
|
22
|
-
* returns either a `{ ok: true }` envelope or throws. No I/O.
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Build the set of "pending" story identifiers from a wave plan. A Story
|
|
27
|
-
* is pending unless its labels carry `agent::done`. Identifiers are
|
|
28
|
-
* collected in three shapes — `id` (number), `number`, and `slug` — so
|
|
29
|
-
* the caller can match findings keyed by any of them.
|
|
30
|
-
*
|
|
31
|
-
* @param {Array<Array<{ id?: number|string, number?: number|string, slug?: string, labels?: string[] }>>} wavePlan
|
|
32
|
-
* @returns {Set<string>}
|
|
33
|
-
*/
|
|
34
|
-
export function collectPendingStoryKeys(wavePlan) {
|
|
35
|
-
const pending = new Set();
|
|
36
|
-
if (!Array.isArray(wavePlan)) return pending;
|
|
37
|
-
for (const wave of wavePlan) {
|
|
38
|
-
if (!Array.isArray(wave)) continue;
|
|
39
|
-
for (const story of wave) {
|
|
40
|
-
if (!story || typeof story !== 'object') continue;
|
|
41
|
-
const labels = Array.isArray(story.labels) ? story.labels : [];
|
|
42
|
-
if (labels.includes('agent::done')) continue;
|
|
43
|
-
if (story.id != null) pending.add(String(story.id));
|
|
44
|
-
if (story.number != null) pending.add(String(story.number));
|
|
45
|
-
if (story.slug != null) pending.add(String(story.slug));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return pending;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Extract every story identifier a finding references. Both finding
|
|
53
|
-
* kinds in #2296's shape carry slugs; future persisted shapes may carry
|
|
54
|
-
* numeric IDs — accept both transparently.
|
|
55
|
-
*/
|
|
56
|
-
function findingStoryKeys(finding) {
|
|
57
|
-
const keys = [];
|
|
58
|
-
if (Array.isArray(finding?.storySlugs)) {
|
|
59
|
-
for (const s of finding.storySlugs) keys.push(String(s));
|
|
60
|
-
}
|
|
61
|
-
if (finding?.producer?.storySlug != null) {
|
|
62
|
-
keys.push(String(finding.producer.storySlug));
|
|
63
|
-
}
|
|
64
|
-
if (finding?.consumer?.storySlug != null) {
|
|
65
|
-
keys.push(String(finding.consumer.storySlug));
|
|
66
|
-
}
|
|
67
|
-
return keys;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Filter findings to those that touch at least one pending Story. A
|
|
72
|
-
* finding all of whose referenced Stories are already `agent::done` is
|
|
73
|
-
* harmless — the conflict has already played out — and is dropped.
|
|
74
|
-
*
|
|
75
|
-
* When `pendingKeys` is empty (every wave is done) every finding is
|
|
76
|
-
* filtered out. When a finding carries no identifiers (shape drift),
|
|
77
|
-
* conservatively keep it so the operator sees the signal.
|
|
78
|
-
*
|
|
79
|
-
* @param {object[]} findings
|
|
80
|
-
* @param {Set<string>} pendingKeys
|
|
81
|
-
* @returns {object[]}
|
|
82
|
-
*/
|
|
83
|
-
export function filterFindingsToPending(findings, pendingKeys) {
|
|
84
|
-
if (!Array.isArray(findings)) return [];
|
|
85
|
-
if (!(pendingKeys instanceof Set)) return findings;
|
|
86
|
-
return findings.filter((f) => {
|
|
87
|
-
const keys = findingStoryKeys(f);
|
|
88
|
-
if (keys.length === 0) return true;
|
|
89
|
-
return keys.some((k) => pendingKeys.has(k));
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function isHardFinding(f) {
|
|
94
|
-
return f && f.severity === 'hard';
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Render the operator-facing remediation block for a hard-gate trip.
|
|
99
|
-
* Produces a single multi-line string suitable for `throw new Error(...)`
|
|
100
|
-
* — names every offending path, the affected Stories, and the exact
|
|
101
|
-
* `gh issue edit` commands needed to add the missing `depends_on` links.
|
|
102
|
-
*/
|
|
103
|
-
export function renderGateErrorMessage(findings, ownerRepo) {
|
|
104
|
-
const lines = [
|
|
105
|
-
'Refusing to flip Epic to agent::executing — cross-Story concurrency hazards remain unresolved.',
|
|
106
|
-
'',
|
|
107
|
-
];
|
|
108
|
-
const sharedEditors = findings.filter((f) => f.kind === 'shared-editor');
|
|
109
|
-
const implicit = findings.filter(
|
|
110
|
-
(f) => f.kind === 'implicit-cross-story-dep',
|
|
111
|
-
);
|
|
112
|
-
if (sharedEditors.length > 0) {
|
|
113
|
-
lines.push('Shared-editor conflicts:');
|
|
114
|
-
for (const f of sharedEditors) {
|
|
115
|
-
const stories = Array.isArray(f.storySlugs) ? f.storySlugs : [];
|
|
116
|
-
lines.push(
|
|
117
|
-
` - "${f.path}" written by ${stories.length} concurrent Stories: ${stories.map((s) => `#${s}`).join(', ')}`,
|
|
118
|
-
);
|
|
119
|
-
lines.push(
|
|
120
|
-
' Remediation: add a `depends_on` chain or split the edits into a dedicated late-wave wiring Story.',
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
lines.push('');
|
|
124
|
-
}
|
|
125
|
-
if (implicit.length > 0) {
|
|
126
|
-
lines.push('Implicit cross-Story dependencies:');
|
|
127
|
-
for (const f of implicit) {
|
|
128
|
-
const p = f.producer ?? {};
|
|
129
|
-
const c = f.consumer ?? {};
|
|
130
|
-
lines.push(
|
|
131
|
-
` - "${f.path}" produced by Story #${p.storySlug ?? '?'} consumed by Story #${c.storySlug ?? '?'} (body.${c.sourceField ?? '?'})`,
|
|
132
|
-
);
|
|
133
|
-
const repoFlag = ownerRepo ? ` --repo ${ownerRepo}` : '';
|
|
134
|
-
lines.push(
|
|
135
|
-
` Remediation: gh issue edit ${c.storySlug ?? '?'}${repoFlag} --add-body $'\\n\\nblocked by #${p.storySlug ?? '?'}'`,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
lines.push('');
|
|
139
|
-
}
|
|
140
|
-
lines.push(
|
|
141
|
-
'Resolve the listed conflicts and re-run `/deliver`, or pass `--ignore-concurrency-hazards` to bypass this gate.',
|
|
142
|
-
);
|
|
143
|
-
return lines.join('\n');
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Apply the gate to a findings array.
|
|
148
|
-
*
|
|
149
|
-
* @param {object} input
|
|
150
|
-
* @param {object[]} input.findings - Findings filtered to pending waves.
|
|
151
|
-
* @param {object} [input.policy] - Resolved `delivery.failOnConcurrencyHazards` (boolean).
|
|
152
|
-
* @param {boolean} [input.ignore] - `--ignore-concurrency-hazards` flag value.
|
|
153
|
-
* @param {string} [input.ownerRepo] - "owner/repo" string for `gh issue edit` hints.
|
|
154
|
-
* @returns {{ tripped: boolean, reason: 'hard-severity'|'config-fail-on'|null, findings: object[], bypassed: boolean }}
|
|
155
|
-
* When the gate trips and `ignore` is false the caller should
|
|
156
|
-
* throw `renderGateErrorMessage(findings, ownerRepo)`. The
|
|
157
|
-
* returned `bypassed` is true when `ignore` short-circuits a
|
|
158
|
-
* trip; record it on the checkpoint.
|
|
159
|
-
*/
|
|
160
|
-
export function evaluateConcurrencyGate({
|
|
161
|
-
findings = [],
|
|
162
|
-
policy = {},
|
|
163
|
-
ignore = false,
|
|
164
|
-
ownerRepo,
|
|
165
|
-
} = {}) {
|
|
166
|
-
void ownerRepo;
|
|
167
|
-
const hardFindings = findings.filter(isHardFinding);
|
|
168
|
-
const failOnConcurrencyHazards = policy.failOnConcurrencyHazards === true;
|
|
169
|
-
if (hardFindings.length > 0) {
|
|
170
|
-
return {
|
|
171
|
-
tripped: true,
|
|
172
|
-
reason: 'hard-severity',
|
|
173
|
-
findings: hardFindings,
|
|
174
|
-
bypassed: ignore === true,
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
if (failOnConcurrencyHazards && findings.length > 0) {
|
|
178
|
-
return {
|
|
179
|
-
tripped: true,
|
|
180
|
-
reason: 'config-fail-on',
|
|
181
|
-
findings,
|
|
182
|
-
bypassed: ignore === true,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
return { tripped: false, reason: null, findings: [], bypassed: false };
|
|
186
|
-
}
|