mandrel 1.94.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 +116 -99
- package/.agents/agents/acceptance-critic.md +9 -7
- package/.agents/agents/story-worker.md +45 -51
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +498 -1287
- package/.agents/docs/agentrc-reference.json +185 -80
- package/.agents/docs/configuration.md +132 -190
- package/.agents/docs/execution-reference.md +51 -25
- package/.agents/docs/quality-gates.md +25 -33
- package/.agents/docs/workflows.md +8 -8
- package/.agents/instructions.md +113 -139
- package/.agents/rules/ci-remediation.md +11 -15
- package/.agents/rules/git-conventions-reference.md +48 -58
- package/.agents/rules/git-conventions.md +16 -22
- package/.agents/schemas/acceptance-eval-verdict.schema.json +1 -1
- package/.agents/schemas/agentrc.schema.json +83 -254
- 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/retro.end.schema.json +1 -1
- 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/audit-to-stories.js +7 -7
- package/.agents/scripts/boot-sweep.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 +7 -10
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/cleanup-repo-test-temp.js +6 -1
- 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/Logger.js +6 -10
- package/.agents/scripts/lib/audit-suite/runner.js +2 -2
- package/.agents/scripts/lib/audit-suite/selector.js +328 -28
- 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 -15
- package/.agents/scripts/lib/baselines/reader.js +1 -6
- package/.agents/scripts/lib/bdd-runner-detect.js +5 -9
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/issue-forms-template.js +32 -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 +6 -3
- package/.agents/scripts/lib/checks/index.js +3 -2
- 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 +26 -24
- 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/standard-args.js +13 -22
- package/.agents/scripts/lib/cli-args.js +39 -9
- package/.agents/scripts/lib/close-validation/gates.js +15 -15
- 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/ci.js +6 -31
- package/.agents/scripts/lib/config/delivery-routing.js +52 -35
- package/.agents/scripts/lib/config/explain.js +61 -48
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +29 -80
- package/.agents/scripts/lib/config/paths.js +0 -2
- package/.agents/scripts/lib/config/quality.js +12 -15
- package/.agents/scripts/lib/config/runners.js +20 -66
- package/.agents/scripts/lib/config/temp-paths.js +30 -63
- package/.agents/scripts/lib/config/worktree-isolation.js +0 -5
- package/.agents/scripts/lib/config-resolver.js +2 -7
- package/.agents/scripts/lib/config-settings-schema-delivery.js +55 -161
- package/.agents/scripts/lib/config-settings-schema-quality.js +17 -16
- package/.agents/scripts/lib/config-settings-schema.js +100 -60
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/dependency-parser.js +3 -2
- package/.agents/scripts/lib/doc-tiers.js +2 -2
- package/.agents/scripts/lib/duplicate-search.js +242 -41
- package/.agents/scripts/lib/feedback-loop/retro-proposals-graduator.js +1 -1
- 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 -190
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-cleanup.js +1 -10
- package/.agents/scripts/lib/git-branch-lifecycle.js +17 -180
- package/.agents/scripts/lib/git-utils.js +32 -20
- package/.agents/scripts/lib/github/framework-repo.js +6 -0
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +10 -38
- package/.agents/scripts/lib/label-taxonomy.js +10 -55
- package/.agents/scripts/lib/observability/active-story-env.js +44 -165
- 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 +5 -5
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/onboard/init-tail.js +1 -3
- 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 +105 -44
- package/.agents/scripts/lib/orchestration/code-review.js +78 -436
- package/.agents/scripts/lib/orchestration/column-sync.js +1 -1
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +4 -4
- package/.agents/scripts/lib/orchestration/context-envelope.js +2 -5
- 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/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/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 +17 -19
- 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/ledger-writer.js +6 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +21 -43
- 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/lint-baseline-service.js +4 -4
- package/.agents/scripts/lib/orchestration/merge-block-class.js +77 -21
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/phase-runner.js +3 -2
- package/.agents/scripts/lib/orchestration/plan-context.js +354 -282
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +11 -22
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +4 -8
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-navigation.js +92 -0
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +71 -0
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +245 -0
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +432 -858
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +778 -0
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +47 -115
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- 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 +52 -51
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +27 -0
- package/.agents/scripts/lib/orchestration/{epic-plan-spec/phases → planning}/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/pr-base-guard.js +18 -28
- package/.agents/scripts/lib/orchestration/remote-verifier.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/resolves-token.js +1 -1
- package/.agents/scripts/lib/orchestration/retro-proposals.js +140 -79
- package/.agents/scripts/lib/orchestration/review-depth.js +126 -47
- package/.agents/scripts/lib/orchestration/review-providers/codex.js +2 -2
- 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/review-provider-factory.js +21 -56
- 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 +784 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +25 -1
- 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 +86 -41
- 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 +77 -32
- 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 +44 -0
- 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 +275 -46
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- 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/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +28 -42
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +298 -0
- package/.agents/scripts/lib/orchestration/story-init-remote.js +51 -0
- package/.agents/scripts/lib/orchestration/story-plan-state.js +33 -0
- package/.agents/scripts/lib/orchestration/structured-comment-parser.js +1 -1
- package/.agents/scripts/lib/orchestration/task-body-validator.js +60 -25
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +130 -75
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +192 -377
- package/.agents/scripts/lib/orchestration/ticket-validator.js +123 -25
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -72
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +45 -55
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +66 -6
- 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 -281
- package/.agents/scripts/lib/preflight-runner.js +4 -4
- package/.agents/scripts/lib/qa/coverage-verdict.js +5 -5
- 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/single-story-sweep/protection-ctx.js +1 -1
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +17 -19
- package/.agents/scripts/lib/story-body/story-body.js +130 -75
- package/.agents/scripts/lib/story-plan.js +2 -4
- package/.agents/scripts/lib/templates/decomposer-prompts.js +51 -46
- package/.agents/scripts/lib/templates/spec-author-prompts.js +47 -45
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/{epic-body-sections.js → ticket-body-sections.js} +25 -39
- package/.agents/scripts/lib/validation-evidence.js +32 -60
- package/.agents/scripts/lib/wave-runner/ready-set.js +38 -12
- package/.agents/scripts/lib/workspace-provisioner.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +72 -25
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- 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 +102 -80
- package/.agents/scripts/plan-persist.js +230 -279
- package/.agents/scripts/plan-run-epilogue.js +142 -0
- 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 +13 -39
- package/.agents/scripts/providers/github/mappers.js +5 -12
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -156
- 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 +25 -21
- package/.agents/scripts/single-story-close.js +178 -26
- package/.agents/scripts/single-story-confirm-merge.js +313 -24
- package/.agents/scripts/single-story-init.js +35 -30
- package/.agents/scripts/stories-wave-tick.js +85 -10
- package/.agents/scripts/story-plan.js +28 -49
- package/.agents/scripts/update-ticket-state.js +14 -65
- 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 +9 -9
- package/.agents/skills/core/scope-triage/SKILL.md +31 -172
- 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 +3 -53
- 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/starter-agentrc.json +0 -5
- package/.agents/templates/agent-protocol.md +9 -10
- package/.agents/workflows/audit-architecture.md +6 -7
- package/.agents/workflows/audit-clean-code.md +7 -7
- package/.agents/workflows/audit-dependencies.md +3 -3
- package/.agents/workflows/audit-devops.md +3 -3
- package/.agents/workflows/audit-documentation.md +9 -10
- package/.agents/workflows/audit-lighthouse.md +11 -3
- package/.agents/workflows/audit-navigability.md +13 -2
- package/.agents/workflows/audit-performance.md +5 -6
- package/.agents/workflows/audit-privacy.md +3 -3
- package/.agents/workflows/audit-quality.md +11 -12
- package/.agents/workflows/audit-security.md +4 -5
- package/.agents/workflows/audit-seo.md +13 -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 +10 -3
- package/.agents/workflows/deliver.md +177 -176
- package/.agents/workflows/git-cleanup.md +5 -6
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/_merge-conflict-template.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +35 -40
- package/.agents/workflows/helpers/code-quality-guardrails.md +7 -7
- package/.agents/workflows/helpers/code-review.md +75 -196
- package/.agents/workflows/helpers/{single-story-deliver-reference.md → deliver-story-reference.md} +83 -44
- package/.agents/workflows/helpers/deliver-story.md +606 -0
- package/.agents/workflows/helpers/diagnose.md +10 -10
- package/.agents/workflows/helpers/parallel-tooling.md +3 -3
- package/.agents/workflows/helpers/signals.md +16 -16
- package/.agents/workflows/helpers/worktree-lifecycle.md +66 -86
- package/.agents/workflows/mandrel-update.md +2 -1
- package/.agents/workflows/plan.md +277 -145
- package/.agents/workflows/qa-assist.md +27 -33
- package/.agents/workflows/qa-explore.md +29 -38
- package/.agents/workflows/qa-run.md +2 -6
- package/README.md +9 -8
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +70 -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 -8
- 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 +2 -2
- package/.agents/agents/retro.md +0 -42
- package/.agents/personas/architect.md +0 -113
- package/.agents/personas/devops-engineer.md +0 -38
- package/.agents/personas/engineer.md +0 -33
- package/.agents/personas/project-manager.md +0 -114
- package/.agents/personas/qa-engineer.md +0 -95
- package/.agents/personas/security-engineer.md +0 -111
- package/.agents/personas/technical-writer.md +0 -101
- package/.agents/schemas/dispatch-manifest.json +0 -232
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/epic-spec.schema.json +0 -153
- 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/slice.end.schema.json +0 -21
- package/.agents/schemas/lifecycle/slice.heartbeat.schema.json +0 -20
- package/.agents/schemas/lifecycle/slice.start.schema.json +0 -17
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -66
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/acceptance-spec-reconciler.js +0 -642
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/bookkeeping-reconcile.js +0 -117
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/dispatcher.js +0 -295
- package/.agents/scripts/epic-audit-prepare.js +0 -497
- package/.agents/scripts/epic-audit-recheck.js +0 -274
- package/.agents/scripts/epic-deliver-note-intervention.js +0 -192
- package/.agents/scripts/epic-deliver-preflight.js +0 -462
- package/.agents/scripts/epic-deliver-prepare.js +0 -852
- package/.agents/scripts/epic-execute-record-wave.js +0 -449
- package/.agents/scripts/epic-plan-clarity.js +0 -211
- package/.agents/scripts/epic-plan-healthcheck.js +0 -581
- package/.agents/scripts/epic-reconcile.js +0 -625
- package/.agents/scripts/lib/baseline-snapshot.js +0 -979
- package/.agents/scripts/lib/checks/epic-merge-lock-stale.js +0 -54
- package/.agents/scripts/lib/checks/stale-origin-epic.js +0 -49
- package/.agents/scripts/lib/config/lifecycle.js +0 -40
- package/.agents/scripts/lib/config/preflight.js +0 -58
- package/.agents/scripts/lib/config/retro.js +0 -77
- package/.agents/scripts/lib/epic-merge-lock.js +0 -322
- package/.agents/scripts/lib/epic-plan-clarity.js +0 -181
- package/.agents/scripts/lib/epic-plan-ideation.js +0 -261
- package/.agents/scripts/lib/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 -219
- 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/bookkeeping-outbox.js +0 -270
- package/.agents/scripts/lib/orchestration/context-hydration-engine.js +0 -539
- package/.agents/scripts/lib/orchestration/deliver-route.js +0 -173
- package/.agents/scripts/lib/orchestration/dispatch-engine.js +0 -134
- package/.agents/scripts/lib/orchestration/dispatch-pipeline.js +0 -183
- package/.agents/scripts/lib/orchestration/epic-cleanup.js +0 -801
- package/.agents/scripts/lib/orchestration/epic-deliver-lease-guard.js +0 -310
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/context.js +0 -163
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/creation.js +0 -140
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/dag.js +0 -64
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/diagnostics.js +0 -72
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist-helpers.js +0 -156
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/persist.js +0 -345
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/planning-artifacts.js +0 -41
- package/.agents/scripts/lib/orchestration/epic-plan-decompose/phases/reconcile-spawn.js +0 -86
- package/.agents/scripts/lib/orchestration/epic-plan-lease-guard.js +0 -391
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/drain.js +0 -94
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/plan-epic.js +0 -236
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/risk-verdict.js +0 -105
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/run-spec-phase.js +0 -307
- package/.agents/scripts/lib/orchestration/epic-plan-spec/phases/spec-freshness.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-plan-state-store.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-run-state-store.js +0 -621
- package/.agents/scripts/lib/orchestration/epic-runner/concurrency-gate.js +0 -186
- package/.agents/scripts/lib/orchestration/epic-runner/deliver-phases.js +0 -50
- package/.agents/scripts/lib/orchestration/epic-runner/phases/build-wave-dag.js +0 -129
- package/.agents/scripts/lib/orchestration/epic-runner/phases/snapshot.js +0 -103
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/composition.js +0 -267
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/signals.js +0 -210
- package/.agents/scripts/lib/orchestration/epic-runner/progress-reporter/transport.js +0 -238
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/_bullet-format.js +0 -32
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/component-drift.js +0 -203
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/crap-drift.js +0 -227
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/maintainability-drift.js +0 -117
- package/.agents/scripts/lib/orchestration/epic-runner/progress-signals/stalled-worktree.js +0 -37
- package/.agents/scripts/lib/orchestration/epic-runner/story-launcher.js +0 -127
- package/.agents/scripts/lib/orchestration/epic-runner/story-run-progress-writer.js +0 -400
- 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/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/finalize/open-or-locate-pr.js +0 -306
- package/.agents/scripts/lib/orchestration/finalize/post-handoff-comment.js +0 -489
- package/.agents/scripts/lib/orchestration/finalize/sanitize-skip-ci.js +0 -88
- package/.agents/scripts/lib/orchestration/lifecycle/emit-slice-lifecycle.js +0 -270
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-dispatch-end.js +0 -147
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/acceptance-reconciler.js +0 -465
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-armer.js +0 -501
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/automerge-predicate.js +0 -984
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/branch-cleaner.js +0 -264
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/checkpoint-pointer-writer.js +0 -284
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/cleaner.js +0 -355
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/finalizer.js +0 -673
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/index.js +0 -378
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/intervention-recorder.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/label-transitioner.js +0 -144
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- 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/plan-review-routing.js +0 -63
- 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 -33
- 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 -396
- package/.agents/scripts/lib/orchestration/post-merge-pipeline.js +0 -205
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- 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-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/retro-runner.js +0 -197
- package/.agents/scripts/lib/orchestration/spec-renderer.js +0 -447
- package/.agents/scripts/lib/orchestration/story-close/auto-refresh-runner.js +0 -747
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/gate-failure.js +0 -211
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/pre-merge-attribution.js +0 -158
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/refresh-commit.js +0 -446
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/regression-projection.js +0 -297
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution/phases/scope-discovery.js +0 -48
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution-wiring.js +0 -67
- package/.agents/scripts/lib/orchestration/story-close/baseline-attribution.js +0 -161
- package/.agents/scripts/lib/orchestration/story-close/baseline-friction-body.js +0 -117
- package/.agents/scripts/lib/orchestration/story-close/cd-out-guard.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/cleanup-reconciler.js +0 -147
- package/.agents/scripts/lib/orchestration/story-close/close-inputs.js +0 -142
- package/.agents/scripts/lib/orchestration/story-close/comment-bodies.js +0 -62
- package/.agents/scripts/lib/orchestration/story-close/merge-runner.js +0 -658
- package/.agents/scripts/lib/orchestration/story-close/merge-subject.js +0 -198
- package/.agents/scripts/lib/orchestration/story-close/phases/branch-restore.js +0 -105
- package/.agents/scripts/lib/orchestration/story-close/phases/close.js +0 -222
- package/.agents/scripts/lib/orchestration/story-close/phases/gates.js +0 -292
- package/.agents/scripts/lib/orchestration/story-close/phases/locked-pipeline.js +0 -270
- package/.agents/scripts/lib/orchestration/story-close/phases/preflight.js +0 -110
- package/.agents/scripts/lib/orchestration/story-close/phases/refresh.js +0 -86
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked-emitter.js +0 -112
- package/.agents/scripts/lib/orchestration/story-close/phases/timeout-blocked.js +0 -157
- package/.agents/scripts/lib/orchestration/story-close/post-merge-close.js +0 -421
- package/.agents/scripts/lib/orchestration/story-close/pre-merge-validation.js +0 -302
- package/.agents/scripts/lib/orchestration/story-close/shared-checkout-guard.js +0 -163
- package/.agents/scripts/lib/orchestration/story-close-recovery.js +0 -690
- package/.agents/scripts/lib/orchestration/wave-marker.js +0 -28
- package/.agents/scripts/lib/orchestration/wave-record-io.js +0 -218
- package/.agents/scripts/lib/orchestration/wave-record-notifications.js +0 -145
- package/.agents/scripts/lib/orchestration/wave-record-projection.js +0 -212
- package/.agents/scripts/lib/presentation/dispatch-manifest-render.js +0 -111
- package/.agents/scripts/lib/presentation/manifest-builder.js +0 -239
- package/.agents/scripts/lib/presentation/manifest-formatter.js +0 -242
- package/.agents/scripts/lib/presentation/manifest-helpers.js +0 -213
- package/.agents/scripts/lib/presentation/manifest-persistence.js +0 -261
- package/.agents/scripts/lib/presentation/manifest-procedures.js +0 -55
- package/.agents/scripts/lib/presentation/manifest-render-waves.js +0 -306
- package/.agents/scripts/lib/presentation/manifest-renderer.js +0 -188
- package/.agents/scripts/lib/presentation/manifest-story-views.js +0 -110
- package/.agents/scripts/lib/push-epic-retry.js +0 -209
- package/.agents/scripts/lib/spec/index.js +0 -36
- package/.agents/scripts/lib/spec/loader.js +0 -425
- package/.agents/scripts/lib/spec/state.js +0 -208
- package/.agents/scripts/lib/story-init/blocker-validator.js +0 -68
- package/.agents/scripts/lib/story-init/branch-initializer.js +0 -408
- package/.agents/scripts/lib/story-init/context-resolver.js +0 -92
- package/.agents/scripts/lib/story-init/donor-precheck.js +0 -207
- package/.agents/scripts/lib/story-init/state-transitioner.js +0 -80
- package/.agents/scripts/lib/story-init/task-graph-builder.js +0 -124
- package/.agents/scripts/lib/story-init/transition-summary.js +0 -34
- package/.agents/scripts/lib/test-reserved-epic-temp-ids.js +0 -35
- package/.agents/scripts/lib/wave-runner/tick.js +0 -754
- package/.agents/scripts/lib/wave-runner/wave-runner-error.js +0 -20
- package/.agents/scripts/lifecycle-emit-story-dispatch.js +0 -194
- package/.agents/scripts/lifecycle-emit.js +0 -510
- package/.agents/scripts/retro-run.js +0 -218
- package/.agents/scripts/slice-phase.js +0 -361
- package/.agents/scripts/standalone-feedback-rollup.js +0 -188
- package/.agents/scripts/story-close.js +0 -294
- package/.agents/scripts/story-init.js +0 -599
- package/.agents/scripts/story-phase.js +0 -369
- package/.agents/scripts/wave-tick.js +0 -464
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -98
- package/.agents/skills/core/epic-plan-consolidate/SKILL.md +0 -172
- package/.agents/skills/core/epic-plan-consolidate/examples.md +0 -51
- package/.agents/skills/core/epic-plan-decompose-author/SKILL.md +0 -441
- package/.agents/skills/core/epic-plan-decompose-author/examples.md +0 -47
- package/.agents/skills/core/epic-plan-premortem/SKILL.md +0 -146
- package/.agents/skills/core/epic-plan-premortem/examples.md +0 -53
- package/.agents/skills/core/epic-plan-spec-author/SKILL.md +0 -383
- package/.agents/skills/core/epic-plan-spec-author/examples.md +0 -91
- package/.agents/workflows/helpers/deliver-epic-reference.md +0 -547
- package/.agents/workflows/helpers/deliver-epic-single.md +0 -331
- package/.agents/workflows/helpers/deliver-epic.md +0 -998
- package/.agents/workflows/helpers/deliver-stories.md +0 -450
- package/.agents/workflows/helpers/epic-audit.md +0 -189
- package/.agents/workflows/helpers/epic-deliver-story.md +0 -436
- package/.agents/workflows/helpers/epic-testing.md +0 -125
- package/.agents/workflows/helpers/plan-epic-reference.md +0 -160
- package/.agents/workflows/helpers/plan-epic.md +0 -353
- package/.agents/workflows/helpers/plan-story.md +0 -251
- package/.agents/workflows/helpers/scope-triage-gate.md +0 -108
- package/.agents/workflows/helpers/single-story-deliver.md +0 -557
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* format-generated-json.js — run generated JSON through the project
|
|
3
|
+
* formatter (Biome) so a generator's output matches what the repo's
|
|
4
|
+
* commit-time formatting would have produced anyway.
|
|
5
|
+
*
|
|
6
|
+
* Why this exists. Generators serialize with `JSON.stringify`, which
|
|
7
|
+
* expands every array across multiple lines. Biome collapses short ones
|
|
8
|
+
* that fit inside `lineWidth` (`"allowedTools": ["Read", "Bash"]`), and
|
|
9
|
+
* lint-staged runs `biome format --write` over staged JSON at commit
|
|
10
|
+
* time — so the committed artifact is Biome-shaped while a fresh
|
|
11
|
+
* generator run is not. The gap means regenerating on a clean tree
|
|
12
|
+
* always leaves format drift, and a `<generate>` → `lint` sequence fails
|
|
13
|
+
* on `biome ci` even when the generator's own `--check` reports the
|
|
14
|
+
* artifact semantically fresh (Story #4546).
|
|
15
|
+
*
|
|
16
|
+
* Running the real formatter, rather than hand-matching its array
|
|
17
|
+
* collapsing, keeps this correct by construction across future formatter
|
|
18
|
+
* and `lineWidth` changes.
|
|
19
|
+
*
|
|
20
|
+
* Stdin mode is deliberate: Biome's configured `formatWrite` command is
|
|
21
|
+
* whole-tree (`biome format --write .`), which is a far broader side
|
|
22
|
+
* effect than a generator should have. `--stdin-file-path` makes this a
|
|
23
|
+
* pure content transform with no filesystem writes.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { spawnSync } from 'node:child_process';
|
|
27
|
+
import process from 'node:process';
|
|
28
|
+
import { Logger } from './Logger.js';
|
|
29
|
+
|
|
30
|
+
/** Wall-clock ceiling for the formatter spawn, so a hung child cannot wedge a generator run. */
|
|
31
|
+
const FORMATTER_TIMEOUT_MS = 30_000;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Warn once about a fallback and return null for the caller to act on.
|
|
35
|
+
* Owning the message here keeps every caller's write path a single
|
|
36
|
+
* `formatGeneratedJson(...) ?? serialized` expression.
|
|
37
|
+
*/
|
|
38
|
+
function fallback(filename) {
|
|
39
|
+
Logger.warn(
|
|
40
|
+
`project formatter (biome) unavailable — writing unformatted ${filename}; ` +
|
|
41
|
+
'run your formatter over it if a format gate rejects it',
|
|
42
|
+
);
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Format `source` as JSON using the project formatter.
|
|
48
|
+
*
|
|
49
|
+
* Best-effort by design: `.agents/` is materialized into consumer
|
|
50
|
+
* projects that need not have Biome installed, so an unavailable or
|
|
51
|
+
* failing formatter warns and returns `null` for the caller to fall back
|
|
52
|
+
* on its own serialization rather than failing the generator. That is
|
|
53
|
+
* safe wherever the artifact's freshness check compares parsed objects
|
|
54
|
+
* rather than bytes — formatting carries no semantic content.
|
|
55
|
+
*
|
|
56
|
+
* `--no` keeps npx from reaching the network to install a missing Biome.
|
|
57
|
+
* `filename` is passed as a bare basename: Biome only needs it to infer
|
|
58
|
+
* the language and match config overrides, and a basename cannot carry
|
|
59
|
+
* the spaces that would break arg quoting under the Windows `shell:
|
|
60
|
+
* true` spawn.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} source Text to format.
|
|
63
|
+
* @param {object} opts
|
|
64
|
+
* @param {string} opts.cwd Directory to resolve the formatter and its config from.
|
|
65
|
+
* @param {string} [opts.filename] Basename Biome attributes the stdin text to.
|
|
66
|
+
* @param {typeof spawnSync} [opts.spawn] Injection seam for tests.
|
|
67
|
+
* @returns {string|null} Formatted text with a trailing newline, or null to fall back.
|
|
68
|
+
*/
|
|
69
|
+
export function formatGeneratedJson(
|
|
70
|
+
source,
|
|
71
|
+
{ cwd, filename = 'generated.json', spawn = spawnSync },
|
|
72
|
+
) {
|
|
73
|
+
let result;
|
|
74
|
+
try {
|
|
75
|
+
result = spawn(
|
|
76
|
+
'npx',
|
|
77
|
+
['--no', 'biome', 'format', `--stdin-file-path=${filename}`],
|
|
78
|
+
{
|
|
79
|
+
cwd,
|
|
80
|
+
input: source,
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
// npm/npx ship as `.cmd` shims on Windows, which Node refuses to
|
|
83
|
+
// spawn without a shell since CVE-2024-27980.
|
|
84
|
+
shell: process.platform === 'win32',
|
|
85
|
+
timeout: FORMATTER_TIMEOUT_MS,
|
|
86
|
+
},
|
|
87
|
+
);
|
|
88
|
+
} catch {
|
|
89
|
+
return fallback(filename);
|
|
90
|
+
}
|
|
91
|
+
if (!result || result.error || result.status !== 0) return fallback(filename);
|
|
92
|
+
const stdout = result.stdout;
|
|
93
|
+
if (typeof stdout !== 'string' || stdout.trim() === '') {
|
|
94
|
+
return fallback(filename);
|
|
95
|
+
}
|
|
96
|
+
return stdout.endsWith('\n') ? stdout : `${stdout}\n`;
|
|
97
|
+
}
|
|
@@ -1,108 +1,35 @@
|
|
|
1
1
|
// .agents/scripts/lib/framework-version.js
|
|
2
2
|
/**
|
|
3
|
-
* framework-version.js —
|
|
4
|
-
*
|
|
3
|
+
* framework-version.js — the visible authoring-marker surface for the legacy
|
|
4
|
+
* ticket-body provenance stamp.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* Tickets authored under v1 carry a hybrid stamp: a hidden
|
|
7
|
+
* `<!-- meta: {"mandrel_version":"…","authored_at":"…"} -->` block plus a
|
|
8
|
+
* single visible footer line
|
|
9
|
+
* `> 🏷️ Authored with Mandrel v<version> · <YYYY-MM-DD>`. The producer of
|
|
10
|
+
* new stamps was retired with the Epic write surface (#4574) — nothing stamps
|
|
11
|
+
* a new ticket — but bodies already stamped are live in the backlog, so the
|
|
12
|
+
* Story-body serializer/parser must keep round-tripping them:
|
|
7
13
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* `getVersion()` in `lib/orchestration/context-hydration-engine.js`
|
|
13
|
-
* delegates here (DRY — one manifest reader).
|
|
14
|
+
* - {@link AUTHORED_MARKER_LINE_RE} lets the parser skip the marker line during
|
|
15
|
+
* section parsing so it never pollutes the last structured section.
|
|
16
|
+
* - {@link authoredMarkerLine} lets the serializer re-emit a byte-identical
|
|
17
|
+
* marker line for a stamp it parsed, preserving provenance verbatim.
|
|
14
18
|
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* surface:
|
|
18
|
-
* - a hidden machine-readable field in the trailing
|
|
19
|
-
* `<!-- meta: {"mandrel_version":"…","authored_at":"…"} -->` block
|
|
20
|
-
* (the source of truth, queryable by tooling), and
|
|
21
|
-
* - a single visible footer line
|
|
22
|
-
* `> 🏷️ Authored with Mandrel v<version> · <YYYY-MM-DD>` so a human
|
|
23
|
-
* reading the raw GitHub issue sees the provenance without any tooling.
|
|
24
|
-
*
|
|
25
|
-
* The stamp is **immutable**: {@link stampFrameworkVersion} is a no-op when
|
|
26
|
-
* the body already carries a `mandrel_version`, so a later re-render or
|
|
27
|
-
* Epic-body edit preserves the originally-authored version verbatim rather
|
|
28
|
-
* than bumping it to whatever version happens to be running.
|
|
29
|
-
*
|
|
30
|
-
* This module imports only Node builtins so it can be pulled in from the
|
|
31
|
-
* story-body serializer, the ticket provider, and the Epic ideation renderer
|
|
32
|
-
* without risking an import cycle.
|
|
19
|
+
* This module imports nothing so it can be pulled in from the story-body
|
|
20
|
+
* serializer without risking an import cycle.
|
|
33
21
|
*/
|
|
34
22
|
|
|
35
|
-
import fs from 'node:fs';
|
|
36
|
-
import path from 'node:path';
|
|
37
|
-
import { fileURLToPath } from 'node:url';
|
|
38
|
-
|
|
39
|
-
/** Returned when the package manifest is absent or unreadable. */
|
|
40
|
-
export const FALLBACK_VERSION = 'unknown';
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Trailing machine-metadata comment block: `<!-- meta: {...} -->`. Mirrors the
|
|
44
|
-
* regex the Story-body parser uses so both surfaces recognise the same block.
|
|
45
|
-
*/
|
|
46
|
-
const META_BLOCK_RE = /<!--\s*meta:\s*(\{[\s\S]*?\})\s*-->/;
|
|
47
|
-
|
|
48
23
|
/**
|
|
49
24
|
* The visible authoring marker line. A blockquote so GitHub renders it as a
|
|
50
|
-
* callout. Used
|
|
51
|
-
*
|
|
52
|
-
* never pollutes the last structured section.
|
|
25
|
+
* callout. Used in the Story-body parser to skip the line during section
|
|
26
|
+
* parsing so it never pollutes the last structured section.
|
|
53
27
|
*/
|
|
54
28
|
export const AUTHORED_MARKER_LINE_RE = /^\s*>\s*🏷️\s+Authored with Mandrel\b/;
|
|
55
29
|
|
|
56
30
|
/**
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
* so the manifest sits three directories up — the same layout in the dev repo
|
|
60
|
-
* and the published tarball.
|
|
61
|
-
*
|
|
62
|
-
* @returns {string}
|
|
63
|
-
*/
|
|
64
|
-
function defaultPkgPath() {
|
|
65
|
-
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
66
|
-
return path.resolve(moduleDir, '../../..', 'package.json');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Resolve the running framework version from the root `package.json`. Degrades
|
|
71
|
-
* to {@link FALLBACK_VERSION} (never throws) on any read or parse failure so a
|
|
72
|
-
* missing/unreadable manifest can never crash an authoring or hydration path.
|
|
73
|
-
*
|
|
74
|
-
* @param {{ pkgPath?: string }} [opts] - `pkgPath` override (test seam).
|
|
75
|
-
* @returns {string}
|
|
76
|
-
*/
|
|
77
|
-
export function resolveFrameworkVersion({ pkgPath } = {}) {
|
|
78
|
-
try {
|
|
79
|
-
const resolved = typeof pkgPath === 'string' ? pkgPath : defaultPkgPath();
|
|
80
|
-
const parsed = JSON.parse(fs.readFileSync(resolved, 'utf8'));
|
|
81
|
-
return typeof parsed.version === 'string' && parsed.version.trim()
|
|
82
|
-
? parsed.version.trim()
|
|
83
|
-
: FALLBACK_VERSION;
|
|
84
|
-
} catch {
|
|
85
|
-
return FALLBACK_VERSION;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Format an authoring date as `YYYY-MM-DD` (UTC). Matches the date shape the
|
|
91
|
-
* rest of the authoring path uses (e.g. `qa-session`).
|
|
92
|
-
*
|
|
93
|
-
* @param {Date} [date=new Date()]
|
|
94
|
-
* @returns {string}
|
|
95
|
-
*/
|
|
96
|
-
export function formatAuthoredDate(date = new Date()) {
|
|
97
|
-
const d =
|
|
98
|
-
date instanceof Date && !Number.isNaN(date.getTime()) ? date : new Date();
|
|
99
|
-
return d.toISOString().slice(0, 10);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Build the visible authoring marker line for a given stamp. Centralised so
|
|
104
|
-
* the string is byte-identical across the two producers (the Story-body
|
|
105
|
-
* serializer and {@link stampFrameworkVersion}).
|
|
31
|
+
* Build the visible authoring marker line for a given stamp. The Story-body
|
|
32
|
+
* serializer uses this to re-emit a legacy stamp it parsed, byte-identically.
|
|
106
33
|
*
|
|
107
34
|
* @param {{ version: string, authoredAt: string }} stamp
|
|
108
35
|
* @returns {string}
|
|
@@ -110,101 +37,3 @@ export function formatAuthoredDate(date = new Date()) {
|
|
|
110
37
|
export function authoredMarkerLine({ version, authoredAt }) {
|
|
111
38
|
return `> 🏷️ Authored with Mandrel v${version} · ${authoredAt}`;
|
|
112
39
|
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Read the framework stamp from a body's trailing meta block. Returns
|
|
116
|
-
* `{ version, authoredAt }` when a non-empty `mandrel_version` is present, or
|
|
117
|
-
* `null` when the body carries no stamp (or the meta block is malformed).
|
|
118
|
-
* `authoredAt` is `null` when the version is present but the date is absent.
|
|
119
|
-
*
|
|
120
|
-
* @param {string} markdown
|
|
121
|
-
* @returns {{ version: string, authoredAt: string|null }|null}
|
|
122
|
-
*/
|
|
123
|
-
export function extractFrameworkStamp(markdown) {
|
|
124
|
-
if (typeof markdown !== 'string') return null;
|
|
125
|
-
const match = markdown.match(META_BLOCK_RE);
|
|
126
|
-
if (!match) return null;
|
|
127
|
-
let parsed;
|
|
128
|
-
try {
|
|
129
|
-
parsed = JSON.parse(match[1]);
|
|
130
|
-
} catch {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
const version =
|
|
137
|
-
typeof parsed.mandrel_version === 'string' && parsed.mandrel_version.trim()
|
|
138
|
-
? parsed.mandrel_version.trim()
|
|
139
|
-
: null;
|
|
140
|
-
if (version === null) return null;
|
|
141
|
-
const authoredAt =
|
|
142
|
-
typeof parsed.authored_at === 'string' && parsed.authored_at.trim()
|
|
143
|
-
? parsed.authored_at.trim()
|
|
144
|
-
: null;
|
|
145
|
-
return { version, authoredAt };
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Stamp a ticket body (Epic or Story markdown) with the framework version and
|
|
150
|
-
* authoring date — **once**. When the body already carries a `mandrel_version`
|
|
151
|
-
* the body is returned verbatim (immutability: never re-derive or bump an
|
|
152
|
-
* already-authored stamp). Otherwise the version keys are merged into (or
|
|
153
|
-
* create) the trailing `<!-- meta -->` block — appended **last** so the key
|
|
154
|
-
* order stays stable with the Story-body serializer — and the visible marker
|
|
155
|
-
* line is (re)emitted just above it.
|
|
156
|
-
*
|
|
157
|
-
* The `version` / `authoredAt` overrides let a caller (e.g. the Epic edit path)
|
|
158
|
-
* preserve a previously-authored stamp; both default to the running version and
|
|
159
|
-
* today's date when omitted.
|
|
160
|
-
*
|
|
161
|
-
* @param {string} markdown
|
|
162
|
-
* @param {{ version?: string, authoredAt?: string }} [stamp]
|
|
163
|
-
* @returns {string}
|
|
164
|
-
*/
|
|
165
|
-
export function stampFrameworkVersion(markdown, stamp = {}) {
|
|
166
|
-
const body = typeof markdown === 'string' ? markdown : '';
|
|
167
|
-
|
|
168
|
-
// Immutability: a body that already carries a version is preserved verbatim.
|
|
169
|
-
if (extractFrameworkStamp(body) !== null) return body;
|
|
170
|
-
|
|
171
|
-
const version =
|
|
172
|
-
typeof stamp?.version === 'string' && stamp.version.trim()
|
|
173
|
-
? stamp.version.trim()
|
|
174
|
-
: resolveFrameworkVersion();
|
|
175
|
-
const authoredAt =
|
|
176
|
-
typeof stamp?.authoredAt === 'string' && stamp.authoredAt.trim()
|
|
177
|
-
? stamp.authoredAt.trim()
|
|
178
|
-
: formatAuthoredDate();
|
|
179
|
-
|
|
180
|
-
// Merge into any existing (version-less) meta block, appending the version
|
|
181
|
-
// keys last for stable key order.
|
|
182
|
-
const metaMatch = body.match(META_BLOCK_RE);
|
|
183
|
-
const meta = {};
|
|
184
|
-
if (metaMatch) {
|
|
185
|
-
try {
|
|
186
|
-
const parsed = JSON.parse(metaMatch[1]);
|
|
187
|
-
if (
|
|
188
|
-
parsed !== null &&
|
|
189
|
-
typeof parsed === 'object' &&
|
|
190
|
-
!Array.isArray(parsed)
|
|
191
|
-
) {
|
|
192
|
-
Object.assign(meta, parsed);
|
|
193
|
-
}
|
|
194
|
-
} catch {
|
|
195
|
-
// Malformed meta comment — drop it and re-emit a clean block.
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
meta.mandrel_version = version;
|
|
199
|
-
meta.authored_at = authoredAt;
|
|
200
|
-
|
|
201
|
-
// Strip any existing meta block / marker so both re-append canonically.
|
|
202
|
-
const head = body
|
|
203
|
-
.replace(META_BLOCK_RE, '')
|
|
204
|
-
.replace(new RegExp(AUTHORED_MARKER_LINE_RE.source, 'm'), '')
|
|
205
|
-
.replace(/\n{3,}/g, '\n\n')
|
|
206
|
-
.trimEnd();
|
|
207
|
-
|
|
208
|
-
const marker = authoredMarkerLine({ version, authoredAt });
|
|
209
|
-
return `${head}\n\n${marker}\n\n<!-- meta: ${JSON.stringify(meta)} -->`;
|
|
210
|
-
}
|
|
@@ -505,6 +505,14 @@ export function createGh(execImpl = exec, defaultExecOpts = {}) {
|
|
|
505
505
|
execWithDefaults({ args: ['pr', 'edit', idStr(id), ...flags] }),
|
|
506
506
|
merge: (id, flags = []) =>
|
|
507
507
|
execWithDefaults({ args: ['pr', 'merge', idStr(id), ...flags] }),
|
|
508
|
+
/**
|
|
509
|
+
* Bring a `mergeStateStatus: BEHIND` PR up to date with its base
|
|
510
|
+
* (Story #4543). The close-and-land merge wait calls this a bounded
|
|
511
|
+
* number of times rather than waiting out its budget behind a base it
|
|
512
|
+
* could have caught up to.
|
|
513
|
+
*/
|
|
514
|
+
updateBranch: (id, flags = []) =>
|
|
515
|
+
execWithDefaults({ args: ['pr', 'update-branch', idStr(id), ...flags] }),
|
|
508
516
|
list: (flags = [], fields) =>
|
|
509
517
|
execWithDefaults({
|
|
510
518
|
args: ['pr', 'list', ...flags, ...jsonFlag(fields)],
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* git-branch-cleanup.js — Shared branch deletion helpers (local + remote).
|
|
3
3
|
*
|
|
4
4
|
* Consolidates the "delete this branch from local and/or origin" pattern
|
|
5
|
-
* used by `story-close.js` and other branch-reaping flows. Originally
|
|
5
|
+
* used by `single-story-close.js` and other branch-reaping flows. Originally
|
|
6
6
|
* carved out when more than one caller re-implemented the same idempotency
|
|
7
7
|
* rules with subtle drift.
|
|
8
8
|
*
|
|
@@ -117,15 +117,6 @@ export function deleteBranchEverywhere(name, opts = {}) {
|
|
|
117
117
|
return { deleted: bothOk, reason, local, remote };
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/**
|
|
121
|
-
* Backwards-compatible alias for `deleteBranchEverywhere`. Older call
|
|
122
|
-
* sites and tests imported this name; keeping the alias avoids touching
|
|
123
|
-
* them in the migration.
|
|
124
|
-
*
|
|
125
|
-
* @deprecated prefer `deleteBranchEverywhere`.
|
|
126
|
-
*/
|
|
127
|
-
export const deleteBranchBoth = deleteBranchEverywhere;
|
|
128
|
-
|
|
129
120
|
/**
|
|
130
121
|
* Delete N branches in a single batched git call (push --delete X Y Z, or
|
|
131
122
|
* branch -D X Y Z), falling back to per-ref delete via
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Consolidates the "does this branch exist locally / remotely?" and
|
|
5
5
|
* "ensure this branch exists and is checked out" logic that
|
|
6
|
-
* `story-init.js`
|
|
6
|
+
* `single-story-init.js` centralizes (formerly duplicated across the deleted
|
|
7
|
+
* `story-init.js` and `dispatch-engine.js` entry seams).
|
|
7
8
|
*
|
|
8
9
|
* All helpers take an explicit `cwd`. Callers with worktree isolation
|
|
9
10
|
* enabled pass the worktree path; single-tree callers pass `PROJECT_ROOT`.
|
|
@@ -87,18 +88,16 @@ export function branchExistsViaTrackingRef(branch, cwd) {
|
|
|
87
88
|
|
|
88
89
|
/**
|
|
89
90
|
* Pure: classify how a `story-<id>` branch should be seeded from the (local,
|
|
90
|
-
* remote) ref-presence matrix. This is the single source of truth
|
|
91
|
-
*
|
|
92
|
-
* - `single-story-init.js#decideStoryBranchSeed` (standalone path)
|
|
93
|
-
* - `story-init/branch-initializer.js#planStoryBranchSeed` (Epic path)
|
|
91
|
+
* remote) ref-presence matrix. This is the single source of truth for
|
|
92
|
+
* `single-story-init.js#decideStoryBranchSeed` (v2 `/deliver` path).
|
|
94
93
|
*
|
|
95
|
-
*
|
|
96
|
-
* fetch, else create` decision tree;
|
|
97
|
-
* logic lives in exactly one place. The
|
|
94
|
+
* The init path previously re-implemented the same `local → no-op, remote →
|
|
95
|
+
* fetch, else create` decision tree; it now delegates here so the branching
|
|
96
|
+
* logic lives in exactly one place. The caller keeps its own keyword for
|
|
98
97
|
* the "local ref already exists" outcome (`reuse` vs `none`) — synonyms for
|
|
99
|
-
* "do not re-create / do not re-seed" preserved for
|
|
100
|
-
*
|
|
101
|
-
*
|
|
98
|
+
* "do not re-create / do not re-seed" preserved for its existing public/test
|
|
99
|
+
* contract — so this classifier returns the neutral `'local'` keyword and
|
|
100
|
+
* the caller maps it onto its own vocabulary.
|
|
102
101
|
*
|
|
103
102
|
* @param {{ localHas: boolean, remoteHas: boolean }} presence
|
|
104
103
|
* @returns {'local'|'fetch'|'create'}
|
|
@@ -116,22 +115,18 @@ export function classifyBranchSeed({ localHas, remoteHas }) {
|
|
|
116
115
|
|
|
117
116
|
/**
|
|
118
117
|
* Single-home for the story-branch seed-action *switch shell* that
|
|
119
|
-
* `single-story-init.js#seedStoryBranch` (
|
|
120
|
-
* `story-init/branch-initializer.js
|
|
121
|
-
*
|
|
122
|
-
* remote) decision to `classifyBranchSeed`; only the act-on-the-decision
|
|
123
|
-
* shell (reuse / fetch / create) was duplicated, and that shell was the
|
|
124
|
-
* drift surface for the seed-decision contract.
|
|
118
|
+
* `single-story-init.js#seedStoryBranch` owns (Story #4255). The deleted
|
|
119
|
+
* Epic `story-init/branch-initializer.js` path had duplicated the same
|
|
120
|
+
* shell before v2 cutover.
|
|
125
121
|
*
|
|
126
122
|
* The two callers differ in exactly two behavioural axes, both of which are
|
|
127
123
|
* parameters here — no other conditional branching is introduced:
|
|
128
|
-
* - **`baseRef`** — the ref to branch from on `create` (`main` for
|
|
129
|
-
*
|
|
124
|
+
* - **`baseRef`** — the ref to branch from on `create` (`main` for v2
|
|
125
|
+
* `/deliver`; pre-v2 Epic close used the Epic branch).
|
|
130
126
|
* - **`swallowCreateRace`** — when `true`, a `git branch` that exits
|
|
131
127
|
* non-zero with an "already exists" stderr is treated as reuse rather
|
|
132
|
-
* than a fatal error (
|
|
133
|
-
*
|
|
134
|
-
* throws (the standalone path has no concurrent creator to race).
|
|
128
|
+
* than a fatal error (pre-v2 concurrent wave dispatch). When `false`, any create failure
|
|
129
|
+
* throws (the v2 standalone path has no concurrent creator to race).
|
|
135
130
|
*
|
|
136
131
|
* The asymmetric surrounding wrappers (merged-sweep, fast-forward,
|
|
137
132
|
* donor-prime, workspace-verify, phase-timer) are deliberately NOT folded
|
|
@@ -204,90 +199,6 @@ export function seedStoryBranchRef({
|
|
|
204
199
|
}
|
|
205
200
|
}
|
|
206
201
|
|
|
207
|
-
/**
|
|
208
|
-
* Ensure an Epic branch exists and is published to `origin`. Handles all
|
|
209
|
-
* four states of the (local, remote) matrix.
|
|
210
|
-
*
|
|
211
|
-
* @param {string} epicBranch
|
|
212
|
-
* @param {string} baseBranch
|
|
213
|
-
* @param {string} cwd
|
|
214
|
-
* @param {{ progress?: (phase: string, message: string) => void }} [opts]
|
|
215
|
-
*/
|
|
216
|
-
export async function ensureEpicBranch(epicBranch, baseBranch, cwd, opts = {}) {
|
|
217
|
-
assertBranchSafe(epicBranch, baseBranch);
|
|
218
|
-
const progress = opts.progress ?? (() => {});
|
|
219
|
-
|
|
220
|
-
// Short-circuit: if we're already on the epic branch, just sync with remote.
|
|
221
|
-
// This avoids redundant checkout calls and prevents the edge case where
|
|
222
|
-
// branchExistsLocally returns false while we're on the branch (detached HEAD,
|
|
223
|
-
// worktree race), which would route into the create path and fail on -b.
|
|
224
|
-
const onBranch = currentBranch(cwd);
|
|
225
|
-
if (onBranch === epicBranch) {
|
|
226
|
-
const remote = branchExistsRemotely(epicBranch, cwd);
|
|
227
|
-
if (remote) {
|
|
228
|
-
progress('GIT', `Already on ${epicBranch}. Syncing with remote.`);
|
|
229
|
-
await gitPullWithRetry(cwd, 'origin', epicBranch);
|
|
230
|
-
} else {
|
|
231
|
-
progress('GIT', `Already on ${epicBranch}. Publishing to remote.`);
|
|
232
|
-
gitSync(cwd, 'push', '--no-verify', '-u', 'origin', epicBranch);
|
|
233
|
-
}
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
const local = branchExistsLocally(epicBranch, cwd);
|
|
238
|
-
const remote = branchExistsRemotely(epicBranch, cwd);
|
|
239
|
-
|
|
240
|
-
if (!local && !remote) {
|
|
241
|
-
progress('GIT', `Creating Epic branch: ${epicBranch} (from ${baseBranch})`);
|
|
242
|
-
gitSync(cwd, 'checkout', baseBranch);
|
|
243
|
-
await gitPullWithRetry(cwd, 'origin', baseBranch);
|
|
244
|
-
gitSync(cwd, 'checkout', '-b', epicBranch);
|
|
245
|
-
gitSync(cwd, 'push', '--no-verify', '-u', 'origin', epicBranch);
|
|
246
|
-
_assertOnBranch(cwd, epicBranch);
|
|
247
|
-
return;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
if (local && !remote) {
|
|
251
|
-
progress(
|
|
252
|
-
'GIT',
|
|
253
|
-
`Epic branch exists locally only: ${epicBranch}. Publishing.`,
|
|
254
|
-
);
|
|
255
|
-
gitSync(cwd, 'checkout', epicBranch);
|
|
256
|
-
gitSync(cwd, 'push', '--no-verify', '-u', 'origin', epicBranch);
|
|
257
|
-
_assertOnBranch(cwd, epicBranch);
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
if (!local && remote) {
|
|
262
|
-
progress('GIT', `Tracking remote Epic branch: ${epicBranch}`);
|
|
263
|
-
gitSync(cwd, 'checkout', '-b', epicBranch, `origin/${epicBranch}`);
|
|
264
|
-
await gitPullWithRetry(cwd, 'origin', epicBranch);
|
|
265
|
-
_assertOnBranch(cwd, epicBranch);
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
progress('GIT', `Epic branch exists. Syncing: ${epicBranch}`);
|
|
270
|
-
gitSync(cwd, 'checkout', epicBranch);
|
|
271
|
-
await gitPullWithRetry(cwd, 'origin', epicBranch);
|
|
272
|
-
_assertOnBranch(cwd, epicBranch);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* Post-operation assertion: verify HEAD is on the expected branch.
|
|
277
|
-
* Guards against TOCTOU races where a parallel agent switches branches
|
|
278
|
-
* between our checkout and pull.
|
|
279
|
-
*/
|
|
280
|
-
function _assertOnBranch(cwd, expected) {
|
|
281
|
-
const actual = currentBranch(cwd);
|
|
282
|
-
if (actual !== expected) {
|
|
283
|
-
throw new Error(
|
|
284
|
-
`[git-branch-lifecycle] Branch assertion failed after checkout. ` +
|
|
285
|
-
`Expected HEAD on '${expected}', found '${actual}'. ` +
|
|
286
|
-
`A concurrent process may have switched branches.`,
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
202
|
/**
|
|
292
203
|
* Check out a Story branch, creating it from `epicBranch` if neither local
|
|
293
204
|
* nor remote exists. Non-destructive: if the branch already exists, this
|
|
@@ -342,80 +253,6 @@ export async function checkoutStoryBranch(
|
|
|
342
253
|
gitSync(cwd, 'checkout', '-b', storyBranch, epicBranch);
|
|
343
254
|
}
|
|
344
255
|
|
|
345
|
-
/**
|
|
346
|
-
* Ensure an Epic branch ref exists locally and is published to `origin`,
|
|
347
|
-
* **without moving HEAD**. Designed for the worktree bootstrap path where
|
|
348
|
-
* the main checkout must not switch branches (a parallel agent may be
|
|
349
|
-
* working there, or the tree may be dirty).
|
|
350
|
-
*
|
|
351
|
-
* Uses `git branch` (not `checkout -b`) to create refs, and `git push` to
|
|
352
|
-
* publish. Callers that need HEAD on the epic branch should use
|
|
353
|
-
* `ensureEpicBranch()` instead.
|
|
354
|
-
*
|
|
355
|
-
* @param {string} epicBranch
|
|
356
|
-
* @param {string} baseBranch
|
|
357
|
-
* @param {string} cwd
|
|
358
|
-
* @param {{ progress?: (phase: string, message: string) => void }} [opts]
|
|
359
|
-
*/
|
|
360
|
-
/**
|
|
361
|
-
* Pure: choose the action to take for `ensureEpicBranchRef` given whether
|
|
362
|
-
* the branch exists locally and/or remotely. One of:
|
|
363
|
-
* - `'noop'` — both refs exist; nothing to do.
|
|
364
|
-
* - `'fetch'` — only remote exists; fetch into local.
|
|
365
|
-
* - `'publish-existing'` — only local exists; push it.
|
|
366
|
-
* - `'create-and-publish'` — neither exists; create from base then push.
|
|
367
|
-
*
|
|
368
|
-
* @param {boolean} local
|
|
369
|
-
* @param {boolean} remote
|
|
370
|
-
* @returns {'noop' | 'fetch' | 'publish-existing' | 'create-and-publish'}
|
|
371
|
-
*/
|
|
372
|
-
export function planEnsureEpicBranchRefAction(local, remote) {
|
|
373
|
-
if (local) return remote ? 'noop' : 'publish-existing';
|
|
374
|
-
return remote ? 'fetch' : 'create-and-publish';
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
export function ensureEpicBranchRef(epicBranch, baseBranch, cwd, opts = {}) {
|
|
378
|
-
assertBranchSafe(epicBranch, baseBranch);
|
|
379
|
-
const progress = opts.progress ?? (() => {});
|
|
380
|
-
|
|
381
|
-
// `ensureEpicBranchRef` is always called after a `git fetch origin` (via
|
|
382
|
-
// `cachedGitFetch` / `fetchMainRefs` in `bootstrapWorktree`). The
|
|
383
|
-
// remote-tracking refs are therefore authoritative, so we check
|
|
384
|
-
// `refs/remotes/origin/<branch>` locally rather than issuing a second
|
|
385
|
-
// network `ls-remote` call.
|
|
386
|
-
const action = planEnsureEpicBranchRefAction(
|
|
387
|
-
branchExistsLocally(epicBranch, cwd),
|
|
388
|
-
branchExistsViaTrackingRef(epicBranch, cwd),
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
if (action === 'noop') {
|
|
392
|
-
progress('GIT', `Epic branch ref exists (local+remote): ${epicBranch}`);
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
if (action === 'fetch') {
|
|
397
|
-
progress('GIT', `Fetching remote Epic branch ref: ${epicBranch}`);
|
|
398
|
-
const res = gitSpawn(cwd, 'fetch', 'origin', `${epicBranch}:${epicBranch}`);
|
|
399
|
-
if (res.status !== 0) {
|
|
400
|
-
throw new Error(
|
|
401
|
-
`ensureEpicBranchRef: failed to fetch ${epicBranch}: ${res.stderr}`,
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (action === 'create-and-publish') {
|
|
408
|
-
progress(
|
|
409
|
-
'GIT',
|
|
410
|
-
`Creating Epic branch ref: ${epicBranch} (from ${baseBranch})`,
|
|
411
|
-
);
|
|
412
|
-
gitSync(cwd, 'branch', epicBranch, baseBranch);
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
progress('GIT', `Publishing Epic branch: ${epicBranch}`);
|
|
416
|
-
gitSync(cwd, 'push', '--no-verify', '-u', 'origin', epicBranch);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
256
|
/**
|
|
420
257
|
* Ensure an arbitrary branch exists locally, creating from `baseBranch` if
|
|
421
258
|
* missing. Used by the dispatcher's task-dispatch path where the expected
|