mandrel 2.0.0 → 2.2.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 -28
- package/.agents/agents/acceptance-critic.md +20 -9
- package/.agents/agents/story-worker.md +45 -48
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +60 -46
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +33 -57
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +17 -19
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +10 -0
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
- package/.agents/schemas/agentrc.schema.json +35 -46
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +24 -68
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/audit-suite/selector.js +323 -23
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
- package/.agents/scripts/lib/checks/index.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli-args.js +23 -2
- package/.agents/scripts/lib/close-validation/gates.js +13 -13
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
- package/.agents/scripts/lib/config/delivery-routing.js +7 -6
- package/.agents/scripts/lib/config/explain.js +10 -16
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +15 -25
- package/.agents/scripts/lib/config/quality.js +11 -14
- package/.agents/scripts/lib/config/runners.js +8 -21
- package/.agents/scripts/lib/config/temp-paths.js +18 -56
- package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
- package/.agents/scripts/lib/config-settings-schema.js +48 -22
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/duplicate-search.js +38 -7
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -189
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
- package/.agents/scripts/lib/git-utils.js +0 -14
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +0 -15
- package/.agents/scripts/lib/label-taxonomy.js +1 -12
- package/.agents/scripts/lib/observability/active-story-env.js +42 -163
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +2 -2
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
- package/.agents/scripts/lib/orchestration/change-set.js +103 -0
- package/.agents/scripts/lib/orchestration/code-review.js +70 -191
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
- package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
- package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
- package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
- package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
- package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
- package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -286
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +8 -7
- package/.agents/scripts/lib/story-body/story-body.js +81 -13
- package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/ticket-body-sections.js +0 -14
- package/.agents/scripts/lib/validation-evidence.js +31 -59
- package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/plan-context.js +38 -7
- package/.agents/scripts/plan-critics.js +203 -0
- package/.agents/scripts/plan-persist.js +145 -35
- package/.agents/scripts/plan-run-epilogue.js +83 -38
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +1 -11
- package/.agents/scripts/providers/github/mappers.js +5 -0
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -153
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/quality-preview.js +13 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +9 -10
- package/.agents/scripts/single-story-close.js +173 -18
- package/.agents/scripts/single-story-confirm-merge.js +288 -15
- package/.agents/scripts/single-story-init.js +6 -10
- package/.agents/scripts/stories-wave-tick.js +380 -53
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/update-ticket-state.js +8 -50
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
- package/.agents/skills/core/scope-triage/SKILL.md +3 -0
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/workflows/audit-architecture.md +3 -4
- package/.agents/workflows/audit-clean-code.md +4 -4
- package/.agents/workflows/audit-documentation.md +4 -5
- package/.agents/workflows/audit-lighthouse.md +8 -0
- package/.agents/workflows/audit-navigability.md +10 -0
- package/.agents/workflows/audit-performance.md +2 -3
- package/.agents/workflows/audit-quality.md +8 -9
- package/.agents/workflows/audit-security.md +1 -2
- package/.agents/workflows/audit-seo.md +10 -0
- package/.agents/workflows/audit-ux-ui.md +7 -0
- package/.agents/workflows/deliver.md +133 -45
- package/.agents/workflows/git-cleanup.md +2 -2
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
- package/.agents/workflows/helpers/code-review.md +14 -12
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +209 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +239 -19
- package/.agents/workflows/qa-assist.md +6 -6
- package/.agents/workflows/qa-explore.md +3 -3
- package/.agents/workflows/qa-run.md +1 -5
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +62 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +17 -9
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
- package/package.json +2 -2
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -53
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
- package/.agents/scripts/resolve-plan-run.js +0 -117
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
|
@@ -6,17 +6,24 @@ import { getCiDelivery } from '../../config/ci.js';
|
|
|
6
6
|
import { resolveConfig } from '../../config-resolver.js';
|
|
7
7
|
import { getStoryBranch, gitSync } from '../../git-utils.js';
|
|
8
8
|
import { Logger } from '../../Logger.js';
|
|
9
|
+
import { emitTerminalFriction } from '../../observability/runtime-friction.js';
|
|
9
10
|
import { createProvider } from '../../provider-factory.js';
|
|
10
11
|
import { flipLabelAndNotify } from '../../single-story/story-merged-notify.js';
|
|
11
12
|
import { WorktreeManager } from '../../worktree-manager.js';
|
|
12
13
|
import { runCodeReview as runCodeReviewDefault } from '../code-review.js';
|
|
13
14
|
import { releaseStoryLease } from '../single-story-lease-guard.js';
|
|
15
|
+
import {
|
|
16
|
+
buildTerminalEnvelope,
|
|
17
|
+
emitTerminalEnvelope,
|
|
18
|
+
NEXT_COMMANDS,
|
|
19
|
+
terminalFromWaitOutcome,
|
|
20
|
+
} from '../story-deliver-terminal.js';
|
|
14
21
|
import { runAutoMergePhase } from './phases/auto-merge.js';
|
|
15
22
|
import { runBaseSyncPhase } from './phases/base-sync.js';
|
|
16
23
|
import { runCloseValidationPhase } from './phases/close-validation.js';
|
|
17
24
|
import { parsePrNumber, runStoryScopeReview } from './phases/code-review.js';
|
|
18
25
|
import { runConfirmMergePhase } from './phases/confirm-merge.js';
|
|
19
|
-
import { parseCloseOptions } from './phases/options.js';
|
|
26
|
+
import { parseCloseOptions, resolveWaitForMerge } from './phases/options.js';
|
|
20
27
|
import { ensurePullRequestWith } from './phases/pull-request.js';
|
|
21
28
|
import { pushStoryBranch } from './phases/push.js';
|
|
22
29
|
import { handleCriticalReviewBlock } from './phases/review-block.js';
|
|
@@ -25,16 +32,94 @@ import { runWrongTreeGuardPhase } from './phases/wrong-tree-guard.js';
|
|
|
25
32
|
|
|
26
33
|
const progress = Logger.createProgress('single-story-close', { stderr: true });
|
|
27
34
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Emit the terminal envelope on stdout alongside the legacy close result.
|
|
37
|
+
*
|
|
38
|
+
* Both are printed: the envelope is the contract callers parse (Story
|
|
39
|
+
* #4543), while `STORY CLOSE RESULT` stays byte-compatible for the existing
|
|
40
|
+
* surfaces that grep it. One writer, one place — so the envelope can never
|
|
41
|
+
* be emitted from a path that forgot it.
|
|
42
|
+
*
|
|
43
|
+
* Story #4578 — the same one-place property is why the runtime-derived
|
|
44
|
+
* friction emit hangs here: a close that ends `failed`, or parks `pending`
|
|
45
|
+
* on an exhausted merge-wait budget, is friction the retro must see, and
|
|
46
|
+
* routing it through the single emitter means no terminal path can forget
|
|
47
|
+
* it. `emitTerminalFriction` decides what (if anything) is worth a record —
|
|
48
|
+
* see `frictionForTerminal` for why `blocked` and a `--no-wait-merge`
|
|
49
|
+
* `pending` are deliberately NOT flagged here.
|
|
50
|
+
*
|
|
51
|
+
* Async, and awaited by every caller: the CLI exits via `process.exit` the
|
|
52
|
+
* moment `main` resolves, so a fire-and-forget append would be discarded.
|
|
53
|
+
* The emit is best-effort internally and cannot throw.
|
|
54
|
+
*/
|
|
55
|
+
async function emitTerminal({ terminal, result, config }) {
|
|
56
|
+
// The human-facing result dump stays level-gated; the terminal envelope is
|
|
57
|
+
// the machine contract and must survive AGENT_LOG_LEVEL=silent.
|
|
58
|
+
if (result) {
|
|
59
|
+
Logger.info(
|
|
60
|
+
`\n--- STORY CLOSE RESULT ---\n${JSON.stringify(result, null, 2)}\n--- END RESULT ---\n`,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
emitTerminalEnvelope(terminal);
|
|
64
|
+
await emitTerminalFriction({ envelope: terminal, config });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Terminal for a Story that was already closed before this run started.
|
|
69
|
+
*
|
|
70
|
+
* `state: 'closed'` alone does NOT mean the work landed. GitHub closes an
|
|
71
|
+
* issue as `completed` (the `Closes #<id>` footer firing on merge — the work
|
|
72
|
+
* IS on the base branch) or as `not_planned` (superseded by a re-plan,
|
|
73
|
+
* abandoned — nothing ever merged). Reporting the second as `landed` told
|
|
74
|
+
* `/deliver` that work had reached `main` when no PR ever merged, which would
|
|
75
|
+
* also satisfy any dependent Story waiting on it. Fail loudly instead: being
|
|
76
|
+
* handed an abandoned Story is an input error only the operator can resolve.
|
|
77
|
+
*
|
|
78
|
+
* A null `stateReason` keeps the `completed` reading — GitHub defaults to it,
|
|
79
|
+
* and issues closed before the field existed carry null.
|
|
80
|
+
*/
|
|
81
|
+
async function alreadyClosedResult(storyId, stateReason = null, config) {
|
|
82
|
+
if (stateReason === 'not_planned') {
|
|
83
|
+
const result = {
|
|
32
84
|
storyId,
|
|
33
85
|
standalone: true,
|
|
34
86
|
action: 'noop',
|
|
35
|
-
reason: '
|
|
36
|
-
}
|
|
87
|
+
reason: 'closed-not-planned',
|
|
88
|
+
};
|
|
89
|
+
const terminal = buildTerminalEnvelope({
|
|
90
|
+
storyId,
|
|
91
|
+
status: 'failed',
|
|
92
|
+
phase: 'init',
|
|
93
|
+
failure: {
|
|
94
|
+
reason:
|
|
95
|
+
`Story #${storyId} is closed as not planned (superseded or abandoned) — ` +
|
|
96
|
+
'there is nothing to land. Re-plan it as a new Story, or pass a Story that is still open.',
|
|
97
|
+
},
|
|
98
|
+
nextCommand: null,
|
|
99
|
+
elapsedSeconds: 0,
|
|
100
|
+
});
|
|
101
|
+
await emitTerminal({ terminal, result, config });
|
|
102
|
+
return { success: false, result, terminal };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const result = {
|
|
106
|
+
storyId,
|
|
107
|
+
standalone: true,
|
|
108
|
+
action: 'noop',
|
|
109
|
+
reason: 'already-closed',
|
|
37
110
|
};
|
|
111
|
+
// Idempotent re-run against a Story closed as completed. `landed` is the
|
|
112
|
+
// honest status — the issue closed on merge, so the work is on the base
|
|
113
|
+
// branch — and there is nothing left to command.
|
|
114
|
+
const terminal = buildTerminalEnvelope({
|
|
115
|
+
storyId,
|
|
116
|
+
status: 'landed',
|
|
117
|
+
phase: 'done',
|
|
118
|
+
nextCommand: null,
|
|
119
|
+
elapsedSeconds: 0,
|
|
120
|
+
});
|
|
121
|
+
await emitTerminal({ terminal, result, config });
|
|
122
|
+
return { success: true, result, terminal };
|
|
38
123
|
}
|
|
39
124
|
|
|
40
125
|
function resolveWorktreePath({ cwd, config, storyId }) {
|
|
@@ -55,7 +140,9 @@ async function runPrePushPhases({
|
|
|
55
140
|
skipSync,
|
|
56
141
|
injectedSync,
|
|
57
142
|
injectedGitSpawn,
|
|
143
|
+
setPhase = () => {},
|
|
58
144
|
}) {
|
|
145
|
+
setPhase('wrong-tree-guard');
|
|
59
146
|
await runWrongTreeGuardPhase({
|
|
60
147
|
cwd,
|
|
61
148
|
worktreePath,
|
|
@@ -66,6 +153,7 @@ async function runPrePushPhases({
|
|
|
66
153
|
gitSpawn: injectedGitSpawn,
|
|
67
154
|
});
|
|
68
155
|
if (!skipValidation) {
|
|
156
|
+
setPhase('close-validation');
|
|
69
157
|
await runCloseValidationPhase({
|
|
70
158
|
cwd,
|
|
71
159
|
worktreePath,
|
|
@@ -81,6 +169,7 @@ async function runPrePushPhases({
|
|
|
81
169
|
progress('VALIDATE', '⏭ Skipped (--skip-validation).');
|
|
82
170
|
}
|
|
83
171
|
if (!skipSync) {
|
|
172
|
+
setPhase('base-sync');
|
|
84
173
|
await runBaseSyncPhase({
|
|
85
174
|
cwd,
|
|
86
175
|
worktreePath,
|
|
@@ -105,8 +194,11 @@ async function openAndReviewPr({
|
|
|
105
194
|
provider,
|
|
106
195
|
injectedGh,
|
|
107
196
|
injectedRunCodeReview,
|
|
197
|
+
setPhase = () => {},
|
|
108
198
|
}) {
|
|
199
|
+
setPhase('push');
|
|
109
200
|
pushStoryBranch({ cwd, storyBranch, gitSync, progress });
|
|
201
|
+
setPhase('pull-request');
|
|
110
202
|
const prUrl = await ensurePullRequestWith({
|
|
111
203
|
cwd,
|
|
112
204
|
storyId,
|
|
@@ -117,6 +209,7 @@ async function openAndReviewPr({
|
|
|
117
209
|
progress,
|
|
118
210
|
});
|
|
119
211
|
const prNumber = parsePrNumber(prUrl);
|
|
212
|
+
setPhase('code-review');
|
|
120
213
|
const reviewOutcome = await runStoryScopeReview({
|
|
121
214
|
cwd,
|
|
122
215
|
storyId,
|
|
@@ -176,8 +269,12 @@ async function releaseLease({
|
|
|
176
269
|
* The two recoverable-blocked close exits (base-sync conflict in
|
|
177
270
|
* `runBaseSyncPhase`, and a critical-blocker review halt in
|
|
178
271
|
* `openAndReviewPr`) throw before the clean-close lease release at the
|
|
179
|
-
* tail of `runSingleStoryClose`, stranding the operator's lease
|
|
180
|
-
*
|
|
272
|
+
* tail of `runSingleStoryClose`, stranding the operator's lease
|
|
273
|
+
* indefinitely. The standalone lease does **not** expire by TTL: it is
|
|
274
|
+
* fail-closed by design (`lease-guard-shared.js` anchors `heartbeatAt` to
|
|
275
|
+
* now, so `isClaimLive` is true for any foreign assignee regardless of the
|
|
276
|
+
* configured TTL), so a stranded claim is cleared only by `--steal` or
|
|
277
|
+
* de-assignment. That fail-closed-refuses a different operator who picks up
|
|
181
278
|
* the blocked Story — exactly the hand-off case. Releasing here closes
|
|
182
279
|
* that gap.
|
|
183
280
|
*
|
|
@@ -232,7 +329,7 @@ function closeResult({
|
|
|
232
329
|
waitedForMerge,
|
|
233
330
|
merged,
|
|
234
331
|
note: waitedForMerge
|
|
235
|
-
? '
|
|
332
|
+
? 'Close-and-land: PR merge confirmed. Story flipped agent::closing → agent::done, the issue closed (confirmStoryMerged), and the post-land tail ran.'
|
|
236
333
|
: autoMergeEnabled
|
|
237
334
|
? 'PR open against baseBranch with auto-merge enabled. Story rests at agent::closing (issue stays OPEN). GitHub will squash-merge when required checks pass; run single-story-confirm-merge.js after the merge confirms to flip agent::done and close the issue (the Closes #<id> footer also auto-closes it).'
|
|
238
335
|
: 'PR open against baseBranch. Story rests at agent::closing (issue stays OPEN). Operator merges via GitHub UI; run single-story-confirm-merge.js after the merge confirms to flip agent::done (the Closes #<id> footer also auto-closes the issue).',
|
|
@@ -245,9 +342,9 @@ export async function runSingleStoryClose({
|
|
|
245
342
|
skipValidation: skipValidationParam,
|
|
246
343
|
skipSync: skipSyncParam,
|
|
247
344
|
noAutoMerge: noAutoMergeParam,
|
|
248
|
-
noFullScopeCrap: noFullScopeCrapParam,
|
|
249
345
|
waitForMerge: waitForMergeParam,
|
|
250
346
|
noWaitForMerge: noWaitForMergeParam,
|
|
347
|
+
maxWaitSeconds: maxWaitSecondsParam,
|
|
251
348
|
injectedProvider,
|
|
252
349
|
injectedConfig,
|
|
253
350
|
injectedNotify,
|
|
@@ -263,16 +360,65 @@ export async function runSingleStoryClose({
|
|
|
263
360
|
skipValidationParam,
|
|
264
361
|
skipSyncParam,
|
|
265
362
|
noAutoMergeParam,
|
|
266
|
-
noFullScopeCrapParam,
|
|
267
363
|
waitForMergeParam,
|
|
268
364
|
noWaitForMergeParam,
|
|
365
|
+
maxWaitSecondsParam,
|
|
269
366
|
});
|
|
270
367
|
if (!options.storyId) {
|
|
271
368
|
throw new Error(
|
|
272
|
-
'Usage: node single-story-close.js --story <STORY_ID> [--cwd <main-repo>] [--skip-validation] [--skip-sync] [--no-auto-merge] [--no-
|
|
369
|
+
'Usage: node single-story-close.js --story <STORY_ID> [--cwd <main-repo>] [--skip-validation] [--skip-sync] [--no-auto-merge] [--wait-merge|--no-wait-merge] [--max-wait-seconds <n>]',
|
|
273
370
|
);
|
|
274
371
|
}
|
|
275
372
|
|
|
373
|
+
// Story #4543 — track the phase so a crash is attributable. The runner
|
|
374
|
+
// deliberately keeps THROWING (the library contract every caller and test
|
|
375
|
+
// relies on: a failed gate must not look like a return value); it only tags
|
|
376
|
+
// the error with the phase it died in, and the CLI boundary turns that into
|
|
377
|
+
// the `failed` terminal envelope. Tagging rather than swallowing is what
|
|
378
|
+
// lets `failed` name its phase without inventing a second success path.
|
|
379
|
+
let phase = 'init';
|
|
380
|
+
const setPhase = (next) => {
|
|
381
|
+
phase = next;
|
|
382
|
+
};
|
|
383
|
+
try {
|
|
384
|
+
return await runClosePipeline({
|
|
385
|
+
options,
|
|
386
|
+
setPhase,
|
|
387
|
+
injectedProvider,
|
|
388
|
+
injectedConfig,
|
|
389
|
+
injectedNotify,
|
|
390
|
+
injectedSync,
|
|
391
|
+
injectedRunCodeReview,
|
|
392
|
+
injectedGh,
|
|
393
|
+
injectedGitSpawn,
|
|
394
|
+
injectedReleaseLease,
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
if (err && typeof err === 'object' && !err.closePhase) {
|
|
398
|
+
err.closePhase = phase;
|
|
399
|
+
}
|
|
400
|
+
throw err;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* The close pipeline proper. Split out of `runSingleStoryClose` so the
|
|
406
|
+
* phase-tagging wrapper above stays a thin, obviously-correct boundary rather
|
|
407
|
+
* than a try block wrapped around a hundred lines of pipeline.
|
|
408
|
+
*/
|
|
409
|
+
async function runClosePipeline({
|
|
410
|
+
options,
|
|
411
|
+
setPhase,
|
|
412
|
+
injectedProvider,
|
|
413
|
+
injectedConfig,
|
|
414
|
+
injectedNotify,
|
|
415
|
+
injectedSync,
|
|
416
|
+
injectedRunCodeReview,
|
|
417
|
+
injectedGh,
|
|
418
|
+
injectedGitSpawn,
|
|
419
|
+
injectedReleaseLease,
|
|
420
|
+
}) {
|
|
421
|
+
const startedAtMs = Date.now();
|
|
276
422
|
const config = injectedConfig || resolveConfig({ cwd: options.cwd });
|
|
277
423
|
const provider = injectedProvider || createProvider(config);
|
|
278
424
|
const baseBranch = config.project?.baseBranch ?? 'main';
|
|
@@ -283,9 +429,15 @@ export async function runSingleStoryClose({
|
|
|
283
429
|
if (story.state === 'closed') {
|
|
284
430
|
progress(
|
|
285
431
|
'NOOP',
|
|
286
|
-
|
|
432
|
+
story.stateReason === 'not_planned'
|
|
433
|
+
? `Story #${options.storyId} is closed as not planned — nothing to land.`
|
|
434
|
+
: `Story #${options.storyId} is already closed. Nothing to do.`,
|
|
435
|
+
);
|
|
436
|
+
return await alreadyClosedResult(
|
|
437
|
+
options.storyId,
|
|
438
|
+
story.stateReason,
|
|
439
|
+
config,
|
|
287
440
|
);
|
|
288
|
-
return alreadyClosedResult(options.storyId);
|
|
289
441
|
}
|
|
290
442
|
|
|
291
443
|
const worktreePath = resolveWorktreePath({
|
|
@@ -314,6 +466,7 @@ export async function runSingleStoryClose({
|
|
|
314
466
|
worktreePath,
|
|
315
467
|
injectedSync,
|
|
316
468
|
injectedGitSpawn,
|
|
469
|
+
setPhase,
|
|
317
470
|
}),
|
|
318
471
|
leaseArgs,
|
|
319
472
|
);
|
|
@@ -329,9 +482,11 @@ export async function runSingleStoryClose({
|
|
|
329
482
|
provider,
|
|
330
483
|
injectedGh,
|
|
331
484
|
injectedRunCodeReview,
|
|
485
|
+
setPhase,
|
|
332
486
|
}),
|
|
333
487
|
leaseArgs,
|
|
334
488
|
);
|
|
489
|
+
setPhase('auto-merge');
|
|
335
490
|
const { autoMergeEnabled, autoMergeReason } = await runAutoMergePhase({
|
|
336
491
|
cwd: options.cwd,
|
|
337
492
|
prNumber,
|
|
@@ -362,32 +517,68 @@ export async function runSingleStoryClose({
|
|
|
362
517
|
});
|
|
363
518
|
const leaseReleased = await releaseLease(leaseArgs);
|
|
364
519
|
|
|
365
|
-
// Story #4428
|
|
366
|
-
// PR to merge confirmation
|
|
367
|
-
// `merge.unlanded
|
|
368
|
-
//
|
|
369
|
-
//
|
|
370
|
-
|
|
520
|
+
// Close-and-land (Story #4428; default since `delivery.routing.closeAndLand`
|
|
521
|
+
// — Story #4539): poll the just-armed PR to merge confirmation, or block
|
|
522
|
+
// explicitly with `merge.unlanded`, instead of resting at `agent::closing`.
|
|
523
|
+
// This is the DEFAULT path for attended and headless runs alike.
|
|
524
|
+
//
|
|
525
|
+
// Resolved here rather than at parse time because two inputs do not exist
|
|
526
|
+
// until now: the resolved config (whose cwd the parse produces) and the
|
|
527
|
+
// actual arm outcome. A PR the operator deliberately left un-armed
|
|
528
|
+
// (`--no-auto-merge` / `autoMerge: "strict"`) has nothing to land, so it
|
|
529
|
+
// rests at `agent::closing` for the human instead of burning the poll
|
|
530
|
+
// budget and then blocking a healthy Story.
|
|
531
|
+
const { waitForMerge, reason: waitForMergeReason } = resolveWaitForMerge({
|
|
532
|
+
waitForMergeExplicit: options.waitForMergeExplicit,
|
|
533
|
+
noWaitForMerge: options.noWaitForMerge,
|
|
534
|
+
config,
|
|
535
|
+
autoMergeReason,
|
|
536
|
+
});
|
|
537
|
+
if (waitForMergeReason === 'operator-merge') {
|
|
538
|
+
progress(
|
|
539
|
+
'MERGE',
|
|
540
|
+
`⏭ Not waiting for merge (${autoMergeReason}) — the operator owns this merge; ` +
|
|
541
|
+
`Story #${options.storyId} rests at agent::closing.` +
|
|
542
|
+
(options.waitForMergeExplicit === true
|
|
543
|
+
? ' --wait-merge cannot land a PR that was deliberately left un-armed.'
|
|
544
|
+
: ''),
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
const gates = {
|
|
548
|
+
validation: options.skipValidation ? 'skipped' : 'passed',
|
|
549
|
+
baseSync: options.skipSync ? 'skipped' : 'passed',
|
|
550
|
+
codeReview: 'passed',
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
if (waitForMerge) {
|
|
554
|
+
setPhase('confirm-merge');
|
|
371
555
|
const waitOutcome = await runConfirmMergePhase({
|
|
372
556
|
cwd: options.cwd,
|
|
373
557
|
storyId: options.storyId,
|
|
558
|
+
storyBranch,
|
|
559
|
+
baseBranch,
|
|
374
560
|
prNumber,
|
|
375
561
|
prUrl,
|
|
376
562
|
autoMergeEnabled,
|
|
377
563
|
autoMergeReason,
|
|
378
564
|
provider,
|
|
379
565
|
config,
|
|
566
|
+
maxWaitSeconds: options.maxWaitSeconds,
|
|
380
567
|
progress,
|
|
381
568
|
injectedGh,
|
|
382
569
|
injectedNotify,
|
|
383
570
|
});
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
571
|
+
const terminal = terminalFromWaitOutcome({
|
|
572
|
+
waitOutcome,
|
|
573
|
+
storyId: options.storyId,
|
|
574
|
+
storyBranch,
|
|
575
|
+
baseBranch,
|
|
576
|
+
prNumber,
|
|
577
|
+
prUrl,
|
|
578
|
+
autoMergeEnabled,
|
|
579
|
+
gates,
|
|
580
|
+
elapsedSeconds: Math.round((Date.now() - startedAtMs) / 1000),
|
|
581
|
+
});
|
|
391
582
|
const result = closeResult({
|
|
392
583
|
storyId: options.storyId,
|
|
393
584
|
storyBranch,
|
|
@@ -399,16 +590,29 @@ export async function runSingleStoryClose({
|
|
|
399
590
|
worktreeReaped,
|
|
400
591
|
leaseReleased,
|
|
401
592
|
waitedForMerge: true,
|
|
402
|
-
merged: true,
|
|
593
|
+
merged: waitOutcome.confirmed === true,
|
|
403
594
|
});
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
595
|
+
await emitTerminal({ terminal, result, config });
|
|
596
|
+
|
|
597
|
+
if (terminal.status === 'landed') {
|
|
598
|
+
progress('DONE', `✅ Story #${options.storyId}: PR merged → ${prUrl}`);
|
|
599
|
+
} else if (terminal.status === 'pending') {
|
|
600
|
+
// NOT a failure and NOT a block — the wait reached the edge of its
|
|
601
|
+
// host slot with the PR healthy and in flight. The CLI maps this to
|
|
602
|
+
// its own exit code so a caller can resume without classifying.
|
|
603
|
+
progress(
|
|
604
|
+
'PENDING',
|
|
605
|
+
`⏸ Story #${options.storyId}: PR ${prUrl} still in flight — resume with: ${terminal.nextCommand}`,
|
|
606
|
+
);
|
|
607
|
+
} else {
|
|
608
|
+
progress(
|
|
609
|
+
'BLOCKED',
|
|
610
|
+
`🛑 Story #${options.storyId}: PR ${prUrl} did not land ` +
|
|
611
|
+
`(blockClass=${terminal.blocked?.blockClass}). Story is at agent::blocked. ` +
|
|
612
|
+
`Next: ${terminal.nextCommand}`,
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
return { success: terminal.status === 'landed', result, terminal };
|
|
412
616
|
}
|
|
413
617
|
|
|
414
618
|
const result = closeResult({
|
|
@@ -422,13 +626,30 @@ export async function runSingleStoryClose({
|
|
|
422
626
|
worktreeReaped,
|
|
423
627
|
leaseReleased,
|
|
424
628
|
});
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
629
|
+
// `--no-wait-merge` / operator-merge: the PR is open and the human owns
|
|
630
|
+
// the land. That is a `pending` terminal by definition — the work is not
|
|
631
|
+
// done, nothing is broken, and one named command finishes it — rather
|
|
632
|
+
// than a fourth status invented for this one case.
|
|
633
|
+
const terminal = buildTerminalEnvelope({
|
|
634
|
+
storyId: options.storyId,
|
|
635
|
+
status: 'pending',
|
|
636
|
+
phase: 'auto-merge',
|
|
637
|
+
storyBranch,
|
|
638
|
+
baseBranch,
|
|
639
|
+
pr: {
|
|
640
|
+
number: prNumber,
|
|
641
|
+
url: prUrl ?? null,
|
|
642
|
+
state: 'OPEN',
|
|
643
|
+
autoMergeEnabled: Boolean(autoMergeEnabled),
|
|
644
|
+
},
|
|
645
|
+
gates,
|
|
646
|
+
nextCommand: NEXT_COMMANDS.confirmMerge(options.storyId),
|
|
647
|
+
elapsedSeconds: Math.round((Date.now() - startedAtMs) / 1000),
|
|
648
|
+
});
|
|
649
|
+
await emitTerminal({ terminal, result, config });
|
|
429
650
|
progress(
|
|
430
651
|
'DONE',
|
|
431
|
-
`✅
|
|
652
|
+
`✅ Story #${options.storyId}: PR ready → ${prUrl} (${waitForMergeReason})`,
|
|
432
653
|
);
|
|
433
|
-
return { success: true, result };
|
|
654
|
+
return { success: true, result, terminal };
|
|
434
655
|
}
|
|
@@ -116,7 +116,7 @@ export async function acquireStoryLease({
|
|
|
116
116
|
anchorHeartbeatToNow: true,
|
|
117
117
|
renderRefusal: (result) =>
|
|
118
118
|
`single-story lease: Story #${storyId} is currently held by @${result.owner}. ` +
|
|
119
|
-
'Another /
|
|
119
|
+
'Another /deliver run owns this Story. Coordinate with that ' +
|
|
120
120
|
'operator, or re-run with --steal to forcibly transfer the claim once you ' +
|
|
121
121
|
'have confirmed the other run is dead. (The standalone path has no Epic ' +
|
|
122
122
|
'heartbeat ledger, so a foreign assignee always blocks unless stolen.)',
|
|
@@ -328,7 +328,7 @@ export function runFormatAutofix({
|
|
|
328
328
|
}
|
|
329
329
|
|
|
330
330
|
/**
|
|
331
|
-
* List the files changed between `
|
|
331
|
+
* List the files changed between `baseBranch` and `storyBranch` using the
|
|
332
332
|
* three-dot merge-base diff. Delegates parsing to `diffNameOnly` from
|
|
333
333
|
* `changed-files.js` so the stdout → path-list conversion lives in one place.
|
|
334
334
|
*
|
|
@@ -336,10 +336,10 @@ export function runFormatAutofix({
|
|
|
336
336
|
* `(args: string[], opts: object) => string`. A bridge adapter wraps it into
|
|
337
337
|
* the `gitSpawn(cwd, ...args)` shape that `diffNameOnly` expects.
|
|
338
338
|
*
|
|
339
|
-
* @param {{ cwd: string,
|
|
339
|
+
* @param {{ cwd: string, baseBranch: string, storyBranch: string, git: Function }} opts
|
|
340
340
|
* @returns {string[]}
|
|
341
341
|
*/
|
|
342
|
-
function listChangedFiles({ cwd,
|
|
342
|
+
function listChangedFiles({ cwd, baseBranch, storyBranch, git }) {
|
|
343
343
|
// Bridge the (args, opts) → string interface into gitSpawn(cwd, ...args).
|
|
344
344
|
const gitSpawn = (_cwd, ...args) => {
|
|
345
345
|
try {
|
|
@@ -358,7 +358,7 @@ function listChangedFiles({ cwd, epicBranch, storyBranch, git }) {
|
|
|
358
358
|
}
|
|
359
359
|
};
|
|
360
360
|
return diffNameOnly({
|
|
361
|
-
range: `${
|
|
361
|
+
range: `${baseBranch}...${storyBranch}`,
|
|
362
362
|
cwd,
|
|
363
363
|
gitSpawn,
|
|
364
364
|
});
|
|
@@ -401,7 +401,7 @@ function listChangedFiles({ cwd, epicBranch, storyBranch, git }) {
|
|
|
401
401
|
* cwd: string,
|
|
402
402
|
* worktreePath?: string,
|
|
403
403
|
* storyId: number|string,
|
|
404
|
-
*
|
|
404
|
+
* baseBranch: string,
|
|
405
405
|
* storyBranch: string,
|
|
406
406
|
* config?: object,
|
|
407
407
|
* logger?: object,
|
|
@@ -420,7 +420,7 @@ export function runScopedFormatAutofix({
|
|
|
420
420
|
cwd,
|
|
421
421
|
worktreePath,
|
|
422
422
|
storyId,
|
|
423
|
-
|
|
423
|
+
baseBranch,
|
|
424
424
|
storyBranch,
|
|
425
425
|
config,
|
|
426
426
|
logger = DefaultLogger,
|
|
@@ -428,8 +428,8 @@ export function runScopedFormatAutofix({
|
|
|
428
428
|
gitSync,
|
|
429
429
|
} = {}) {
|
|
430
430
|
if (!cwd) throw new Error('runScopedFormatAutofix: cwd is required');
|
|
431
|
-
if (!
|
|
432
|
-
throw new Error('runScopedFormatAutofix:
|
|
431
|
+
if (!baseBranch)
|
|
432
|
+
throw new Error('runScopedFormatAutofix: baseBranch is required');
|
|
433
433
|
if (!storyBranch)
|
|
434
434
|
throw new Error('runScopedFormatAutofix: storyBranch is required');
|
|
435
435
|
|
|
@@ -449,13 +449,13 @@ export function runScopedFormatAutofix({
|
|
|
449
449
|
|
|
450
450
|
const changed = listChangedFiles({
|
|
451
451
|
cwd: workTree,
|
|
452
|
-
|
|
452
|
+
baseBranch,
|
|
453
453
|
storyBranch,
|
|
454
454
|
git,
|
|
455
455
|
});
|
|
456
456
|
if (changed.length === 0) {
|
|
457
457
|
logger.info?.(
|
|
458
|
-
`${SCOPED_TAG} skipped — no changed files between ${
|
|
458
|
+
`${SCOPED_TAG} skipped — no changed files between ${baseBranch} and ${storyBranch}.`,
|
|
459
459
|
);
|
|
460
460
|
return { ran: false, committed: false, reason: 'no-changed-files' };
|
|
461
461
|
}
|