mandrel 2.0.0 → 2.1.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 +9 -7
- package/.agents/agents/story-worker.md +41 -46
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +51 -44
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +32 -56
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +14 -16
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/agentrc.schema.json +34 -45
- 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 +22 -66
- 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/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 +31 -13
- 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/code-review.js +58 -168
- 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 +114 -24
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +3 -7
- 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 -75
- 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/planning/authoring-context.js +41 -40
- 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 +3 -12
- 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 +15 -32
- 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 +6 -5
- package/.agents/scripts/lib/templates/decomposer-prompts.js +7 -3
- 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/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 -6
- 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/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 +79 -4
- 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 +98 -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 +21 -13
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +12 -10
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +193 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +184 -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 +40 -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 +12 -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/package.json +1 -1
- 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
|
@@ -4,15 +4,31 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Ordered, fail-closed pipeline:
|
|
6
6
|
*
|
|
7
|
-
* 1.
|
|
8
|
-
* 2.
|
|
9
|
-
* 3.
|
|
10
|
-
* 4.
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
7
|
+
* 1. Ticket validator + file-assumption + DAG + capacity + budget
|
|
8
|
+
* 2. Draft reachability (named soft failure, exit 3)
|
|
9
|
+
* 3. Split-policy partition (`assertAcceptancePartition`) + spec fold/spill
|
|
10
|
+
* 4. Create Story issues (`type::story` + sanitized authored labels —
|
|
11
|
+
* deliberately NOT `agent::ready`), resumably via a plan fingerprint
|
|
12
|
+
* 5. Upsert `story-plan-state` on every created Story; upsert `plan-summary`
|
|
13
|
+
* on the primary Story
|
|
14
|
+
* 6. Flip every Story to `agent::ready` — the terminal step, so `ready`
|
|
15
|
+
* always implies "checkpoints written"
|
|
16
|
+
* 7. Comment on + close the superseded `--tickets` source issues
|
|
17
|
+
* (Story #4535) — bookkeeping only; never fails the run
|
|
18
|
+
* 8. Temp cleanup at terminal success + a stale-plan-dir reap
|
|
19
|
+
*
|
|
20
|
+
* **Why `agent::ready` moved to the end (Story #4541).** Issues used to be
|
|
21
|
+
* born `agent::ready` in the creating POST while the checkpoints were
|
|
22
|
+
* written afterwards. Anything picking a Story up in that window — or after
|
|
23
|
+
* a comment failure aborted the loop — read the checkpoint as `null`
|
|
24
|
+
* (`story-plan-state.js` degrades missing/malformed to `null`). Creating
|
|
25
|
+
* unlabelled, writing checkpoints, then flipping closes that race.
|
|
26
|
+
*
|
|
27
|
+
* **No authored risk artifact (Story #4542).** Persist neither requires nor
|
|
28
|
+
* accepts a risk verdict, derives no envelope from one, and computes no
|
|
29
|
+
* review routing. Review depth and the acceptance-critic mode are derived from
|
|
30
|
+
* the diff at close time (`review-depth.js#deriveChangeLevel`); `--force-review`
|
|
31
|
+
* is an explicit operator flag, recorded here as a receipt and never inferred.
|
|
16
32
|
*
|
|
17
33
|
* Hard cutover: no Epic parent, no reconciler, no `deliveryShape`, no
|
|
18
34
|
* `--amend` tree cascades. Those surfaces die with Stages 4–5 for any
|
|
@@ -21,8 +37,10 @@
|
|
|
21
37
|
* @module lib/orchestration/plan-persist/run-plan-persist
|
|
22
38
|
*/
|
|
23
39
|
|
|
24
|
-
import { rm } from 'node:fs/promises';
|
|
40
|
+
import { readdir, rm, stat } from 'node:fs/promises';
|
|
41
|
+
import path from 'node:path';
|
|
25
42
|
|
|
43
|
+
import { anchorTempRoot, tempRootFrom } from '../../config/temp-paths.js';
|
|
26
44
|
import { getLimits, PROJECT_ROOT } from '../../config-resolver.js';
|
|
27
45
|
import { gitSpawn } from '../../git-utils.js';
|
|
28
46
|
import { Logger } from '../../Logger.js';
|
|
@@ -37,20 +55,23 @@ import {
|
|
|
37
55
|
evaluateDraftReachability,
|
|
38
56
|
renderReachabilityOrphans,
|
|
39
57
|
} from '../plan-reachability.js';
|
|
40
|
-
import { resolveReviewRouting } from '../plan-review-routing.js';
|
|
41
|
-
import { deriveRiskEnvelope } from '../planning-risk.js';
|
|
42
58
|
import { upsertStructuredComment } from '../ticketing.js';
|
|
43
59
|
import {
|
|
44
60
|
enforceFanOutGate,
|
|
45
61
|
surfaceSoftConflictFindings,
|
|
46
62
|
} from './fan-out-gate.js';
|
|
47
|
-
import { validateTickets } from './persist-helpers.js';
|
|
48
|
-
import {
|
|
63
|
+
import { resolveBaseBranchRef, validateTickets } from './persist-helpers.js';
|
|
64
|
+
import {
|
|
65
|
+
assemblePlanStories,
|
|
66
|
+
createStoryIssues,
|
|
67
|
+
markStoriesReady,
|
|
68
|
+
} from './story-ops.js';
|
|
49
69
|
import {
|
|
50
70
|
buildPlanSummaryCommentBody,
|
|
51
71
|
buildWaveTable,
|
|
52
72
|
PLAN_SUMMARY_COMMENT_TYPE,
|
|
53
73
|
} from './summary.js';
|
|
74
|
+
import { closeSupersededTickets } from './supersede-ops.js';
|
|
54
75
|
|
|
55
76
|
/** Checkpoint schema version written on each Story's story-plan-state. */
|
|
56
77
|
const PLAN_CHECKPOINT_SCHEMA_VERSION_V2 = 2;
|
|
@@ -88,6 +109,25 @@ export async function writeCheckpointV2(provider, storyId, state) {
|
|
|
88
109
|
return state;
|
|
89
110
|
}
|
|
90
111
|
|
|
112
|
+
/**
|
|
113
|
+
* Enforce the ticket validator's findings and report what the file-assumption
|
|
114
|
+
* gate actually concluded.
|
|
115
|
+
*
|
|
116
|
+
* The return value feeds the posted `plan-summary`'s freshness line, which
|
|
117
|
+
* used to be hard-coded `{ stale: 0, ambiguous: 0 }` — so the comment read
|
|
118
|
+
* "Spec freshness: clean" even on the one run where the gate had *given up*
|
|
119
|
+
* (Story #4541's unresolvable-base-ref downgrade). The summary asserted a
|
|
120
|
+
* clean result precisely when it had the least evidence for one.
|
|
121
|
+
*
|
|
122
|
+
* The mapping is deliberate. A confirmed mismatch is never reported here at
|
|
123
|
+
* all — it throws, and the run posts no summary. The only findings that
|
|
124
|
+
* survive to the summary are ones the gate could not *verify*, because the
|
|
125
|
+
* base ref they were computed against does not resolve; unverifiable is
|
|
126
|
+
* `ambiguous`, not `stale`.
|
|
127
|
+
*
|
|
128
|
+
* @returns {{ stale: number, ambiguous: number }} Freshness counts for the
|
|
129
|
+
* posted summary.
|
|
130
|
+
*/
|
|
91
131
|
function enforceTicketValidation(validated, { config, settings, cwd }) {
|
|
92
132
|
const validationErrors = validated.errors ?? [];
|
|
93
133
|
const assumptionFailures = validationErrors.filter((error) =>
|
|
@@ -102,8 +142,15 @@ function enforceTicketValidation(validated, { config, settings, cwd }) {
|
|
|
102
142
|
`hard error(s):\n${blockingErrors.map((error) => ` - ${error}`).join('\n')}`,
|
|
103
143
|
);
|
|
104
144
|
}
|
|
105
|
-
if (assumptionFailures.length === 0) return;
|
|
106
|
-
|
|
145
|
+
if (assumptionFailures.length === 0) return { stale: 0, ambiguous: 0 };
|
|
146
|
+
// Story #4541: resolve through the canonical `project.baseBranch` (the
|
|
147
|
+
// shape `config-resolver` actually emits) with the legacy settings bag as
|
|
148
|
+
// a fallback — reading a bare `config.baseBranch` meant this probe always
|
|
149
|
+
// targeted the literal `main`.
|
|
150
|
+
const gateBaseRef =
|
|
151
|
+
config?.project?.baseBranch ??
|
|
152
|
+
settings?.baseBranch ??
|
|
153
|
+
resolveBaseBranchRef(config);
|
|
107
154
|
const refResolves =
|
|
108
155
|
gitSpawn(
|
|
109
156
|
cwd ?? process.cwd(),
|
|
@@ -122,16 +169,155 @@ function enforceTicketValidation(validated, { config, settings, cwd }) {
|
|
|
122
169
|
`[plan-persist] file-assumption gate skipped: base ref '${gateBaseRef}' ` +
|
|
123
170
|
`does not resolve — ${assumptionFailures.length} finding(s) downgraded.`,
|
|
124
171
|
);
|
|
172
|
+
return { stale: 0, ambiguous: assumptionFailures.length };
|
|
125
173
|
}
|
|
126
174
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Run the supersede close phase behind a belt-and-braces guard.
|
|
177
|
+
*
|
|
178
|
+
* `closeSupersededTickets` already swallows per-ticket failures, but this
|
|
179
|
+
* phase runs *after* `createIssue`, so an unexpected throw anywhere in it
|
|
180
|
+
* would leave the run half-done with Stories already live. Degrade to a
|
|
181
|
+
* reported failure instead.
|
|
182
|
+
*
|
|
183
|
+
* @returns {Promise<import('./supersede-ops.js').SupersedeReport>}
|
|
184
|
+
*/
|
|
185
|
+
async function runSupersedePhase(args) {
|
|
186
|
+
try {
|
|
187
|
+
return await closeSupersededTickets(args);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
Logger.warn(
|
|
190
|
+
`[plan-persist] supersede close phase failed: ${err.message} — ` +
|
|
191
|
+
'Stories were created; close the source tickets by hand.',
|
|
192
|
+
);
|
|
193
|
+
return {
|
|
194
|
+
enabled: true,
|
|
195
|
+
dryRun: args.dryRun === true,
|
|
196
|
+
reason: `phase-error: ${err.message}`,
|
|
197
|
+
closed: [],
|
|
198
|
+
planned: [],
|
|
199
|
+
skipped: [],
|
|
200
|
+
failed: (args.sourceTicketIds ?? []).map((ticket) => ({
|
|
201
|
+
ticket,
|
|
202
|
+
reason: err.message,
|
|
203
|
+
})),
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Render the plan-metrics line for the **posted** `plan-summary` comment,
|
|
210
|
+
* scoped to this invocation.
|
|
211
|
+
*
|
|
212
|
+
* The ordering hazard this closes: the ledger record for the current run is
|
|
213
|
+
* appended by `recordPlanInvocation`'s `finally`, which by construction only
|
|
214
|
+
* fires once `runPlanPersist` has *resolved* — long after this comment body
|
|
215
|
+
* is composed. A plain `since`-filtered ledger read therefore summarized
|
|
216
|
+
* every run except the one being summarized, and on an otherwise-quiet
|
|
217
|
+
* ledger it rendered "plan-metrics: no invocations recorded" onto the very
|
|
218
|
+
* comment reporting the run. (The stdout envelope was always correct: its
|
|
219
|
+
* `attachPlanMetrics` read runs after the wrapper returns.)
|
|
220
|
+
*
|
|
221
|
+
* **The `finally` stays where it is.** It is what guarantees a persist that
|
|
222
|
+
* throws is still recorded, so moving the append earlier — or moving this
|
|
223
|
+
* read later, past the GitHub writes it feeds — would trade a cosmetic bug
|
|
224
|
+
* for a real one. Instead of racing it, fold in a synthetic record standing
|
|
225
|
+
* for the in-flight invocation. It is the same record the wrapper is about to
|
|
226
|
+
* write, minus a final duration; it cannot double-count, because the real one
|
|
227
|
+
* does not exist yet at this point in the pipeline.
|
|
228
|
+
*
|
|
229
|
+
* `ok: true` is honest here: this line is only ever composed on the success
|
|
230
|
+
* path, after `createStoryIssues` has returned.
|
|
231
|
+
*
|
|
232
|
+
* @param {{ config: object, since: string, startedAt: string, mode: string }} args
|
|
233
|
+
* @returns {Promise<string|null>}
|
|
234
|
+
*/
|
|
235
|
+
async function renderRunScopedPlanMetricsLine({
|
|
236
|
+
config,
|
|
237
|
+
since,
|
|
238
|
+
startedAt,
|
|
239
|
+
mode,
|
|
240
|
+
}) {
|
|
241
|
+
try {
|
|
242
|
+
const ledger = await readPlanMetrics(null, config);
|
|
243
|
+
const endedAt = new Date().toISOString();
|
|
244
|
+
const inFlight = {
|
|
245
|
+
v: 1,
|
|
246
|
+
cli: 'plan-persist',
|
|
247
|
+
mode,
|
|
248
|
+
epicId: null,
|
|
249
|
+
startedAt,
|
|
250
|
+
endedAt,
|
|
251
|
+
durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)) || 0,
|
|
252
|
+
ok: true,
|
|
253
|
+
};
|
|
254
|
+
const summary = summarizePlanMetrics(
|
|
255
|
+
{ ...ledger, entries: [...(ledger.entries ?? []), inFlight] },
|
|
256
|
+
{ since },
|
|
257
|
+
);
|
|
258
|
+
return renderPlanMetricsSummaryLine(summary);
|
|
259
|
+
} catch (err) {
|
|
260
|
+
Logger.warn(`[plan-persist] plan-metrics summary skipped: ${err.message}`);
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Age after which an abandoned `temp/plan-*` directory is reaped. A plan run
|
|
267
|
+
* that is still being authored is minutes-to-hours old; a week is far past
|
|
268
|
+
* any live run and comfortably past an operator returning to a paused one.
|
|
269
|
+
*/
|
|
270
|
+
const STALE_PLAN_DIR_MS = 7 * 24 * 60 * 60 * 1000;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Reap abandoned `plan-*` directories under the temp root (Story #4541).
|
|
274
|
+
*
|
|
275
|
+
* Terminal-success cleanup only ever removed the *current* run's `planDir`,
|
|
276
|
+
* so every plan that failed a gate, was abandoned mid-authoring, or ran
|
|
277
|
+
* `--dry-run` left its directory behind forever. This sweeps the stragglers
|
|
278
|
+
* on each persist.
|
|
279
|
+
*
|
|
280
|
+
* Best-effort throughout: this is hygiene, never a reason to fail a run that
|
|
281
|
+
* has already created Stories. The current run's own `planDir` is always
|
|
282
|
+
* excluded — its cleanup is the caller's decision.
|
|
283
|
+
*
|
|
284
|
+
* @param {{ config?: object, keepDir?: string|null, now?: number }} args
|
|
285
|
+
* @returns {Promise<{ reaped: string[] }>}
|
|
286
|
+
*/
|
|
287
|
+
export async function reapStalePlanDirs({
|
|
288
|
+
config = {},
|
|
289
|
+
keepDir = null,
|
|
290
|
+
now = Date.now(),
|
|
291
|
+
} = {}) {
|
|
292
|
+
const reaped = [];
|
|
293
|
+
const tempRoot = anchorTempRoot(tempRootFrom(config));
|
|
294
|
+
let entries;
|
|
295
|
+
try {
|
|
296
|
+
entries = await readdir(tempRoot, { withFileTypes: true });
|
|
297
|
+
} catch {
|
|
298
|
+
return { reaped }; // No temp root yet — nothing to reap.
|
|
299
|
+
}
|
|
300
|
+
const keep = keepDir ? path.resolve(keepDir) : null;
|
|
301
|
+
for (const entry of entries) {
|
|
302
|
+
if (!entry.isDirectory() || !entry.name.startsWith('plan-')) continue;
|
|
303
|
+
const dir = path.resolve(tempRoot, entry.name);
|
|
304
|
+
if (keep !== null && dir === keep) continue;
|
|
305
|
+
try {
|
|
306
|
+
const { mtimeMs } = await stat(dir);
|
|
307
|
+
if (now - mtimeMs < STALE_PLAN_DIR_MS) continue;
|
|
308
|
+
await rm(dir, { recursive: true, force: true });
|
|
309
|
+
reaped.push(dir);
|
|
310
|
+
} catch {
|
|
311
|
+
// A racing writer or a permission error: leave it for the next run.
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (reaped.length > 0) {
|
|
315
|
+
Logger.info(
|
|
316
|
+
`[plan-persist] reaped ${reaped.length} abandoned plan director(ies) ` +
|
|
317
|
+
`older than 7d under ${tempRoot}.`,
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return { reaped };
|
|
135
321
|
}
|
|
136
322
|
|
|
137
323
|
/**
|
|
@@ -141,7 +327,6 @@ function riskVerdictCommentBody(riskVerdict) {
|
|
|
141
327
|
* provider: object,
|
|
142
328
|
* artifacts: {
|
|
143
329
|
* stories: Array<object>,
|
|
144
|
-
* riskVerdict: object,
|
|
145
330
|
* techSpecContent?: string|null,
|
|
146
331
|
* planAcceptance?: string[]|null,
|
|
147
332
|
* },
|
|
@@ -153,10 +338,12 @@ function riskVerdictCommentBody(riskVerdict) {
|
|
|
153
338
|
* allowLargeFanOut?: boolean,
|
|
154
339
|
* skipCleanup?: boolean,
|
|
155
340
|
* dryRun?: boolean,
|
|
156
|
-
* planRunId?: string,
|
|
157
341
|
* planDir?: string,
|
|
158
342
|
* fanOutCounter?: Function,
|
|
159
343
|
* cwd?: string,
|
|
344
|
+
* sourceTicketIds?: number[],
|
|
345
|
+
* sourceTicketOrigin?: 'flag'|'envelope'|'none',
|
|
346
|
+
* closeSuperseded?: boolean,
|
|
160
347
|
* },
|
|
161
348
|
* }} input
|
|
162
349
|
*/
|
|
@@ -169,7 +356,6 @@ export async function runPlanPersist({
|
|
|
169
356
|
}) {
|
|
170
357
|
const {
|
|
171
358
|
stories: rawStories = null,
|
|
172
|
-
riskVerdict,
|
|
173
359
|
techSpecContent = null,
|
|
174
360
|
planAcceptance = null,
|
|
175
361
|
} = artifacts ?? {};
|
|
@@ -179,24 +365,20 @@ export async function runPlanPersist({
|
|
|
179
365
|
allowLargeFanOut = false,
|
|
180
366
|
skipCleanup = false,
|
|
181
367
|
dryRun = false,
|
|
182
|
-
planRunId,
|
|
183
368
|
planDir = null,
|
|
184
369
|
fanOutCounter = undefined,
|
|
185
370
|
cwd = PROJECT_ROOT,
|
|
371
|
+
sourceTicketIds = [],
|
|
372
|
+
sourceTicketOrigin = 'none',
|
|
373
|
+
closeSuperseded = true,
|
|
186
374
|
} = opts;
|
|
187
375
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if ('deliveryShape' in (riskVerdict ?? {})) {
|
|
195
|
-
throw new Error(
|
|
196
|
-
'[plan-persist] risk-verdict.deliveryShape was removed in v2 Stage 3 — ' +
|
|
197
|
-
'delete the field; persist always creates Story issue(s).',
|
|
198
|
-
);
|
|
199
|
-
}
|
|
376
|
+
// Boundary for the plan-metrics summary below: everything this invocation
|
|
377
|
+
// appends to the ledger is stamped at or after this instant, so filtering
|
|
378
|
+
// on it scopes the counts to *this* run rather than every plan ever run
|
|
379
|
+
// through the shared standalone ledger (Story #4541).
|
|
380
|
+
const runStartedAt = opts.metricsSince ?? new Date().toISOString();
|
|
381
|
+
|
|
200
382
|
if (!Array.isArray(rawStories) || rawStories.length === 0) {
|
|
201
383
|
throw new Error(
|
|
202
384
|
'[plan-persist] stories payload must be a non-empty array ' +
|
|
@@ -228,7 +410,11 @@ export async function runPlanPersist({
|
|
|
228
410
|
});
|
|
229
411
|
enforceFanOutGate(validated.findings, allowLargeFanOut, 'plan-persist');
|
|
230
412
|
surfaceSoftConflictFindings(validated.findings, 'plan-persist');
|
|
231
|
-
enforceTicketValidation(validated, {
|
|
413
|
+
const freshness = enforceTicketValidation(validated, {
|
|
414
|
+
config,
|
|
415
|
+
settings,
|
|
416
|
+
cwd,
|
|
417
|
+
});
|
|
232
418
|
|
|
233
419
|
const reachability = evaluateDraftReachability({
|
|
234
420
|
tickets: rawStories,
|
|
@@ -254,7 +440,6 @@ export async function runPlanPersist({
|
|
|
254
440
|
|
|
255
441
|
const critics = evaluatePlanCritics({
|
|
256
442
|
techSpecContent: techSpecContent ?? '',
|
|
257
|
-
riskVerdict,
|
|
258
443
|
tickets: rawStories,
|
|
259
444
|
config,
|
|
260
445
|
});
|
|
@@ -280,18 +465,13 @@ export async function runPlanPersist({
|
|
|
280
465
|
const { stories } = assemblePlanStories(rawStories, {
|
|
281
466
|
sharedSpec: techSpecContent,
|
|
282
467
|
planAcceptance: planAcceptance ?? undefined,
|
|
468
|
+
sourceTicketIds,
|
|
283
469
|
});
|
|
284
470
|
|
|
285
|
-
const
|
|
286
|
-
const reviewRouting = resolveReviewRouting({
|
|
287
|
-
planningRisk,
|
|
288
|
-
forceReview,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
const { created, planRunLabel } = await createStoryIssues({
|
|
471
|
+
const { created } = await createStoryIssues({
|
|
292
472
|
provider,
|
|
293
473
|
stories,
|
|
294
|
-
opts: {
|
|
474
|
+
opts: { dryRun },
|
|
295
475
|
});
|
|
296
476
|
|
|
297
477
|
const primary = created[0];
|
|
@@ -303,44 +483,39 @@ export async function runPlanPersist({
|
|
|
303
483
|
})),
|
|
304
484
|
);
|
|
305
485
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
486
|
+
// Story #4541: `readPlanMetrics` is declared `(epicId, config)` but was
|
|
487
|
+
// called with `config` first, so the ledger path resolver received the
|
|
488
|
+
// config object as an `epicId` and threw its guard on every single run —
|
|
489
|
+
// a throw this try/catch then swallowed into a silently absent summary.
|
|
490
|
+
// v2 persist is always Epic-less, hence the explicit `null`. The `since`
|
|
491
|
+
// filter keeps the counts about this invocation, and the in-flight record
|
|
492
|
+
// is folded in because the wrapper has not written it yet — see
|
|
493
|
+
// `renderRunScopedPlanMetricsLine`.
|
|
494
|
+
const planMetricsLine = await renderRunScopedPlanMetricsLine({
|
|
495
|
+
config,
|
|
496
|
+
since: runStartedAt,
|
|
497
|
+
startedAt: runStartedAt,
|
|
498
|
+
mode: dryRun ? 'dry-run' : 'persist',
|
|
499
|
+
});
|
|
313
500
|
|
|
314
501
|
const summaryBody = buildPlanSummaryCommentBody({
|
|
315
502
|
epicId: primary.id,
|
|
316
503
|
ticketCount: created.length,
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
freshness: { stale: 0, ambiguous: 0 },
|
|
504
|
+
forceReview,
|
|
505
|
+
freshness,
|
|
320
506
|
healthcheck: { skipped: true },
|
|
321
507
|
waveTable,
|
|
322
508
|
mode: 'stories',
|
|
323
509
|
planMetricsLine,
|
|
324
510
|
stories: created,
|
|
325
|
-
planRunLabel,
|
|
326
511
|
});
|
|
327
512
|
|
|
328
513
|
if (!dryRun) {
|
|
329
514
|
for (const story of created) {
|
|
330
|
-
await upsertStructuredComment(
|
|
331
|
-
provider,
|
|
332
|
-
story.id,
|
|
333
|
-
'risk-verdict',
|
|
334
|
-
riskVerdictCommentBody(riskVerdict),
|
|
335
|
-
);
|
|
336
515
|
await writeCheckpointV2(provider, story.id, {
|
|
337
|
-
planningRisk,
|
|
338
|
-
riskVerdict,
|
|
339
|
-
reviewRouting,
|
|
340
516
|
persist: {
|
|
341
517
|
completedAt: new Date().toISOString(),
|
|
342
518
|
storyCount: created.length,
|
|
343
|
-
planRunLabel,
|
|
344
519
|
primaryStoryId: primary.id,
|
|
345
520
|
stories: created.map((createdStory) => ({
|
|
346
521
|
slug: createdStory.slug,
|
|
@@ -355,8 +530,26 @@ export async function runPlanPersist({
|
|
|
355
530
|
PLAN_SUMMARY_COMMENT_TYPE,
|
|
356
531
|
summaryBody,
|
|
357
532
|
);
|
|
533
|
+
|
|
534
|
+
// Terminal step: every checkpoint above is now on every Story, so
|
|
535
|
+
// `agent::ready` can honestly mean "fully persisted" (Story #4541).
|
|
536
|
+
// Anything that picks a Story up from here reads a real checkpoint.
|
|
537
|
+
await markStoriesReady({ provider, created });
|
|
358
538
|
}
|
|
359
539
|
|
|
540
|
+
const supersede = await runSupersedePhase({
|
|
541
|
+
provider,
|
|
542
|
+
stories,
|
|
543
|
+
created,
|
|
544
|
+
sourceTicketIds,
|
|
545
|
+
dryRun,
|
|
546
|
+
closeSuperseded,
|
|
547
|
+
});
|
|
548
|
+
// Record which channel the ids came from so a run that superseded nothing
|
|
549
|
+
// says *why* (`none` = neither the envelope nor --source-tickets carried
|
|
550
|
+
// any) rather than reading as a clean no-op — Story #4554.
|
|
551
|
+
supersede.sourceTicketOrigin = sourceTicketOrigin;
|
|
552
|
+
|
|
360
553
|
if (!skipCleanup && planDir) {
|
|
361
554
|
try {
|
|
362
555
|
await rm(planDir, { recursive: true, force: true });
|
|
@@ -364,21 +557,33 @@ export async function runPlanPersist({
|
|
|
364
557
|
Logger.warn(`[plan-persist] temp cleanup skipped: ${err.message}`);
|
|
365
558
|
}
|
|
366
559
|
}
|
|
560
|
+
// Terminal-success cleanup only ever removes *this* run's planDir, so
|
|
561
|
+
// abandoned ones accumulated forever. Sweep them (Story #4541).
|
|
562
|
+
await reapStalePlanDirs({ config, keepDir: skipCleanup ? planDir : null });
|
|
367
563
|
|
|
564
|
+
const adopted = created.filter((story) => story.adopted);
|
|
565
|
+
if (adopted.length > 0) {
|
|
566
|
+
Logger.info(
|
|
567
|
+
`[plan-persist] resumed ${adopted.length} of ${created.length} Story(ies) ` +
|
|
568
|
+
`from a previous persist: ${adopted.map((s2) => `#${s2.id}`).join(', ')}.`,
|
|
569
|
+
);
|
|
570
|
+
}
|
|
368
571
|
Logger.info(
|
|
369
572
|
`[plan-persist] Persisted ${created.length} Story(ies)` +
|
|
370
|
-
(planRunLabel ? ` under ${planRunLabel}` : '') +
|
|
371
573
|
`; primary #${primary.id} is agent::ready.`,
|
|
372
574
|
);
|
|
575
|
+
Logger.info(
|
|
576
|
+
`[plan-persist] Deliver with: /deliver ${created.map((s2) => s2.id).join(' ')}`,
|
|
577
|
+
);
|
|
373
578
|
|
|
374
579
|
return {
|
|
375
580
|
stories: created,
|
|
376
581
|
primaryStoryId: primary.id,
|
|
377
|
-
|
|
378
|
-
planningRisk,
|
|
379
|
-
reviewRouting,
|
|
582
|
+
forceReview,
|
|
380
583
|
critics,
|
|
381
584
|
reachability,
|
|
585
|
+
freshness,
|
|
382
586
|
waveTable,
|
|
587
|
+
supersede,
|
|
383
588
|
};
|
|
384
589
|
}
|