mandrel 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/README.md +59 -28
- package/.agents/agents/acceptance-critic.md +20 -9
- package/.agents/agents/story-worker.md +45 -48
- package/.agents/audit-checklists/performance.md +1 -1
- package/.agents/docs/SDLC.md +60 -46
- package/.agents/docs/agentrc-reference.json +8 -13
- package/.agents/docs/configuration.md +33 -57
- package/.agents/docs/execution-reference.md +39 -10
- package/.agents/docs/quality-gates.md +17 -19
- package/.agents/docs/workflows.md +6 -6
- package/.agents/instructions.md +64 -79
- package/.agents/rules/ci-remediation.md +3 -3
- package/.agents/rules/gherkin-standards.md +10 -0
- package/.agents/rules/git-conventions-reference.md +42 -51
- package/.agents/schemas/acceptance-eval-verdict.schema.json +2 -2
- package/.agents/schemas/agentrc.schema.json +35 -46
- package/.agents/schemas/audit-rules.json +59 -1
- package/.agents/schemas/audit-rules.schema.json +33 -1
- package/.agents/schemas/lifecycle/README.md +1 -2
- package/.agents/schemas/lifecycle/ledger-record.schema.json +1 -1
- package/.agents/schemas/lifecycle/merge.flip-failed.schema.json +33 -0
- package/.agents/schemas/lifecycle/merge.unlanded.schema.json +1 -0
- package/.agents/schemas/lifecycle/story.merged.schema.json +1 -1
- package/.agents/schemas/signal-event.schema.json +3 -3
- package/.agents/schemas/story-deliver-terminal.schema.json +152 -0
- package/.agents/schemas/validation-evidence.schema.json +1 -1
- package/.agents/scripts/acceptance-eval.js +24 -68
- package/.agents/scripts/agents-bootstrap-github.js +1 -1
- package/.agents/scripts/bootstrap.js +3 -3
- package/.agents/scripts/check-dead-exports.js +43 -104
- package/.agents/scripts/check-doc-links.js +2 -2
- package/.agents/scripts/check-lifecycle-lint.js +1 -1
- package/.agents/scripts/check-workflow-cli-lint.js +91 -0
- package/.agents/scripts/deliver-recover.js +122 -0
- package/.agents/scripts/drain-pending-cleanup.js +1 -1
- package/.agents/scripts/evidence-gate.js +20 -50
- package/.agents/scripts/generate-skills-index.js +17 -1
- package/.agents/scripts/generate-workflows-doc.js +4 -4
- package/.agents/scripts/lib/ITicketingProvider.js +1 -19
- package/.agents/scripts/lib/audit-suite/selector.js +323 -23
- package/.agents/scripts/lib/baselines/kinds/maintainability.js +0 -11
- package/.agents/scripts/lib/bootstrap/ci-workflow-template.js +28 -33
- package/.agents/scripts/lib/bootstrap/manifest.js +8 -11
- package/.agents/scripts/lib/bootstrap/project-bootstrap.js +30 -53
- package/.agents/scripts/lib/bootstrap/quality-bootstrap.js +0 -2
- package/.agents/scripts/lib/checks/core-bare-clean.js +4 -1
- package/.agents/scripts/lib/checks/index.js +1 -1
- package/.agents/scripts/lib/checks/loop-health.js +12 -11
- package/.agents/scripts/lib/checks/state.js +17 -248
- package/.agents/scripts/lib/checks/story-init-not-backgrounded.js +3 -3
- package/.agents/scripts/lib/checks/subagent-agent-tool-required.js +3 -4
- package/.agents/scripts/lib/checks/worktree-bootstrap-env.js +2 -2
- package/.agents/scripts/lib/checks/worktree-residue-biome.js +3 -3
- package/.agents/scripts/lib/cli-args.js +23 -2
- package/.agents/scripts/lib/close-validation/gates.js +13 -13
- package/.agents/scripts/lib/close-validation/projections/inputs.js +7 -7
- package/.agents/scripts/lib/close-validation/projections/maintainability.js +12 -12
- package/.agents/scripts/lib/close-validation/runner.js +13 -21
- package/.agents/scripts/lib/close-validation/telemetry.js +17 -8
- package/.agents/scripts/lib/config/acceptance-eval.js +2 -2
- package/.agents/scripts/lib/config/delivery-routing.js +7 -6
- package/.agents/scripts/lib/config/explain.js +10 -16
- package/.agents/scripts/lib/config/github.js +7 -5
- package/.agents/scripts/lib/config/limits.js +15 -25
- package/.agents/scripts/lib/config/quality.js +11 -14
- package/.agents/scripts/lib/config/runners.js +8 -21
- package/.agents/scripts/lib/config/temp-paths.js +18 -56
- package/.agents/scripts/lib/config-settings-schema-delivery.js +34 -16
- package/.agents/scripts/lib/config-settings-schema-quality.js +9 -2
- package/.agents/scripts/lib/config-settings-schema.js +48 -22
- package/.agents/scripts/lib/dead-exports-knip.js +105 -0
- package/.agents/scripts/lib/dead-exports-mode.js +51 -0
- package/.agents/scripts/lib/duplicate-search.js +38 -7
- package/.agents/scripts/lib/findings/promote-finding.js +23 -14
- package/.agents/scripts/lib/format-generated-json.js +97 -0
- package/.agents/scripts/lib/framework-version.js +19 -189
- package/.agents/scripts/lib/gh-exec.js +8 -0
- package/.agents/scripts/lib/git-branch-lifecycle.js +0 -158
- package/.agents/scripts/lib/git-utils.js +0 -14
- package/.agents/scripts/lib/json-utils.js +1 -2
- package/.agents/scripts/lib/label-constants.js +0 -15
- package/.agents/scripts/lib/label-taxonomy.js +1 -12
- package/.agents/scripts/lib/observability/active-story-env.js +42 -163
- package/.agents/scripts/lib/observability/runtime-friction.js +243 -0
- package/.agents/scripts/lib/observability/signal-validator.js +4 -4
- package/.agents/scripts/lib/observability/signals-writer.js +6 -82
- package/.agents/scripts/lib/observability/source-classifier.js +2 -2
- package/.agents/scripts/lib/observability/tool-trace-hook.js +2 -12
- package/.agents/scripts/lib/orchestration/acceptance-clusters.js +1 -1
- package/.agents/scripts/lib/orchestration/acceptance-eval-decision.js +2 -2
- package/.agents/scripts/lib/orchestration/ceremony-routing.js +43 -45
- package/.agents/scripts/lib/orchestration/change-set.js +103 -0
- package/.agents/scripts/lib/orchestration/code-review.js +70 -191
- package/.agents/scripts/lib/orchestration/consolidation-precondition.js +3 -3
- package/.agents/scripts/lib/orchestration/deliver-recover.js +328 -0
- package/.agents/scripts/lib/orchestration/detectors-phase.js +12 -6
- package/.agents/scripts/lib/orchestration/git-cleanup/phases/fast-forward.js +34 -0
- package/.agents/scripts/lib/orchestration/lease-guard-shared.js +3 -2
- package/.agents/scripts/lib/orchestration/lifecycle/emit-ledger-event.js +142 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-loop-tick.js +9 -11
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-flip-failed.js +86 -0
- package/.agents/scripts/lib/orchestration/lifecycle/emit-merge-unlanded.js +37 -103
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/README.md +7 -3
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/watcher.js +50 -85
- package/.agents/scripts/lib/orchestration/lifecycle/trace-logger.js +3 -14
- package/.agents/scripts/lib/orchestration/merge-block-class.js +76 -20
- package/.agents/scripts/lib/orchestration/merge-poll.js +104 -0
- package/.agents/scripts/lib/orchestration/plan-context.js +116 -33
- package/.agents/scripts/lib/orchestration/plan-critic-conditions.js +26 -36
- package/.agents/scripts/lib/orchestration/plan-critics-evaluate.js +31 -22
- package/.agents/scripts/lib/orchestration/plan-metrics.js +38 -6
- package/.agents/scripts/lib/orchestration/plan-persist/fan-out-gate.js +16 -6
- package/.agents/scripts/lib/orchestration/plan-persist/persist-helpers.js +173 -25
- package/.agents/scripts/lib/orchestration/plan-persist/plan-context-source.js +116 -0
- package/.agents/scripts/lib/orchestration/plan-persist/run-plan-persist.js +280 -100
- package/.agents/scripts/lib/orchestration/plan-persist/story-ops.js +472 -55
- package/.agents/scripts/lib/orchestration/plan-persist/summary.js +21 -16
- package/.agents/scripts/lib/orchestration/plan-persist/supersede-ops.js +509 -0
- package/.agents/scripts/lib/orchestration/plan-text-hygiene.js +230 -0
- package/.agents/scripts/lib/orchestration/planning/authoring-context.js +41 -40
- package/.agents/scripts/lib/orchestration/planning/decomposer-context.js +1 -2
- package/.agents/scripts/lib/orchestration/planning/spec-authoring-grounding.js +1 -1
- package/.agents/scripts/lib/orchestration/resolve-stories.js +344 -0
- package/.agents/scripts/lib/orchestration/retro-proposals.js +7 -7
- package/.agents/scripts/lib/orchestration/review-depth.js +105 -40
- package/.agents/scripts/lib/orchestration/review-providers/findings-renderer.js +3 -13
- package/.agents/scripts/lib/orchestration/review-providers/native.js +1 -154
- package/.agents/scripts/lib/orchestration/review-providers/review-depth.js +3 -2
- package/.agents/scripts/lib/orchestration/review-providers/security-review.js +1 -1
- package/.agents/scripts/lib/orchestration/review-providers/types.js +5 -4
- package/.agents/scripts/lib/orchestration/review-providers/ultrareview.js +1 -1
- package/.agents/scripts/lib/orchestration/run-epilogue.js +374 -16
- package/.agents/scripts/lib/orchestration/single-story-close/phases/auto-merge.js +24 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/base-sync.js +11 -9
- package/.agents/scripts/lib/orchestration/single-story-close/phases/close-validation.js +4 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/code-review.js +4 -13
- package/.agents/scripts/lib/orchestration/single-story-close/phases/confirm-merge.js +608 -152
- package/.agents/scripts/lib/orchestration/single-story-close/phases/options.js +72 -30
- package/.agents/scripts/lib/orchestration/single-story-close/phases/post-land.js +305 -0
- package/.agents/scripts/lib/orchestration/single-story-close/phases/pull-request.js +1 -1
- package/.agents/scripts/lib/orchestration/single-story-close/phases/review-block.js +12 -8
- package/.agents/scripts/lib/orchestration/single-story-close/phases/worktree-reap.js +37 -4
- package/.agents/scripts/lib/orchestration/single-story-close/phases/wrong-tree-guard.js +2 -2
- package/.agents/scripts/lib/orchestration/single-story-close/runner.js +264 -43
- package/.agents/scripts/lib/orchestration/single-story-lease-guard.js +1 -1
- package/.agents/scripts/lib/orchestration/story-close/format-autofix.js +10 -10
- package/.agents/scripts/lib/orchestration/story-close/phases/code-review.js +104 -279
- package/.agents/scripts/lib/orchestration/story-close/phases/local-lens-review.js +191 -0
- package/.agents/scripts/lib/orchestration/story-close/phases/review-core.js +120 -0
- package/.agents/scripts/lib/orchestration/story-deliver-terminal.js +360 -0
- package/.agents/scripts/lib/orchestration/story-follow-ups.js +75 -14
- package/.agents/scripts/lib/orchestration/story-init-remote.js +12 -8
- package/.agents/scripts/lib/orchestration/story-plan-state.js +14 -29
- package/.agents/scripts/lib/orchestration/task-body-validator.js +52 -7
- package/.agents/scripts/lib/orchestration/ticket-lease.js +27 -74
- package/.agents/scripts/lib/orchestration/ticket-validator-conflicts.js +119 -14
- package/.agents/scripts/lib/orchestration/ticket-validator-sizing.js +3 -4
- package/.agents/scripts/lib/orchestration/ticket-validator.js +121 -18
- package/.agents/scripts/lib/orchestration/ticketing/bulk.js +14 -47
- package/.agents/scripts/lib/orchestration/ticketing/reads.js +19 -32
- package/.agents/scripts/lib/orchestration/ticketing/transition.js +61 -1
- package/.agents/scripts/lib/orchestration/ticketing.js +0 -1
- package/.agents/scripts/lib/plan-phase-cleanup.js +12 -14
- package/.agents/scripts/lib/planning-corpus.js +12 -286
- package/.agents/scripts/lib/preflight-runner.js +2 -2
- package/.agents/scripts/lib/qa/qa-context-hydrator.js +5 -5
- package/.agents/scripts/lib/signals/index.js +4 -17
- package/.agents/scripts/lib/signals/read.js +35 -35
- package/.agents/scripts/lib/signals/schema.js +8 -11
- package/.agents/scripts/lib/signals/span-tree.js +7 -7
- package/.agents/scripts/lib/signals/write.js +0 -1
- package/.agents/scripts/lib/single-story/story-merged-notify.js +13 -2
- package/.agents/scripts/lib/skills/parse-skill.js +16 -3
- package/.agents/scripts/lib/story-adjacency.js +8 -7
- package/.agents/scripts/lib/story-body/story-body.js +81 -13
- package/.agents/scripts/lib/templates/decomposer-prompts.js +15 -16
- package/.agents/scripts/lib/test-env.js +14 -1
- package/.agents/scripts/lib/test-tiers.js +0 -3
- package/.agents/scripts/lib/ticket-body-sections.js +0 -14
- package/.agents/scripts/lib/validation-evidence.js +31 -59
- package/.agents/scripts/lib/wave-runner/live-probe.js +315 -0
- package/.agents/scripts/lib/wave-runner/ready-set.js +32 -6
- package/.agents/scripts/lib/worktree/lifecycle/pending-cleanup.js +1 -1
- package/.agents/scripts/lib/worktree/lifecycle/reap.js +68 -19
- package/.agents/scripts/lib/worktree/lifecycle-manager.js +1 -2
- package/.agents/scripts/plan-context.js +38 -7
- package/.agents/scripts/plan-critics.js +203 -0
- package/.agents/scripts/plan-persist.js +145 -35
- package/.agents/scripts/plan-run-epilogue.js +83 -38
- package/.agents/scripts/post-structured-comment.js +0 -38
- package/.agents/scripts/pr-watch-with-update.js +43 -22
- package/.agents/scripts/providers/github/compose.js +0 -1
- package/.agents/scripts/providers/github/errors.js +0 -19
- package/.agents/scripts/providers/github/issues.js +1 -11
- package/.agents/scripts/providers/github/mappers.js +5 -0
- package/.agents/scripts/providers/github/sub-issues.js +0 -47
- package/.agents/scripts/providers/github/tickets.js +33 -153
- package/.agents/scripts/providers/github.js +17 -6
- package/.agents/scripts/quality-preview.js +13 -6
- package/.agents/scripts/resolve-stories.js +236 -0
- package/.agents/scripts/run-coverage.js +4 -1
- package/.agents/scripts/run-lint.js +2 -2
- package/.agents/scripts/run-verify.js +31 -2
- package/.agents/scripts/signals-view.js +9 -10
- package/.agents/scripts/single-story-close.js +173 -18
- package/.agents/scripts/single-story-confirm-merge.js +288 -15
- package/.agents/scripts/single-story-init.js +6 -10
- package/.agents/scripts/stories-wave-tick.js +380 -53
- package/.agents/scripts/story-plan.js +3 -3
- package/.agents/scripts/update-ticket-state.js +8 -50
- package/.agents/skills/core/code-review-and-quality/SKILL.md +28 -450
- package/.agents/skills/core/code-review-and-quality/reference.md +458 -0
- package/.agents/skills/core/debugging-and-error-recovery/SKILL.md +22 -315
- package/.agents/skills/core/debugging-and-error-recovery/reference.md +323 -0
- package/.agents/skills/core/diagnose-friction/SKILL.md +14 -18
- package/.agents/skills/core/documentation-and-adrs/SKILL.md +25 -397
- package/.agents/skills/core/documentation-and-adrs/reference.md +403 -0
- package/.agents/skills/core/gates-and-baselines/SKILL.md +12 -12
- package/.agents/skills/core/idea-refinement/SKILL.md +3 -3
- package/.agents/skills/core/scope-triage/SKILL.md +3 -0
- package/.agents/skills/core/security-and-hardening/SKILL.md +22 -367
- package/.agents/skills/core/security-and-hardening/reference.md +375 -0
- package/.agents/skills/skills.index.json +2 -12
- package/.agents/skills/stack/qa/playwright-bdd/SKILL.md +2 -4
- package/.agents/skills/stack/qa/qa-explore-driving/SKILL.md +1 -1
- package/.agents/skills/stack/qa/qa-harness/SKILL.md +1 -3
- package/.agents/workflows/audit-architecture.md +3 -4
- package/.agents/workflows/audit-clean-code.md +4 -4
- package/.agents/workflows/audit-documentation.md +4 -5
- package/.agents/workflows/audit-lighthouse.md +8 -0
- package/.agents/workflows/audit-navigability.md +10 -0
- package/.agents/workflows/audit-performance.md +2 -3
- package/.agents/workflows/audit-quality.md +8 -9
- package/.agents/workflows/audit-security.md +1 -2
- package/.agents/workflows/audit-seo.md +10 -0
- package/.agents/workflows/audit-ux-ui.md +7 -0
- package/.agents/workflows/deliver.md +133 -45
- package/.agents/workflows/git-cleanup.md +2 -2
- package/.agents/workflows/git-deliver.md +1 -1
- package/.agents/workflows/helpers/acceptance-self-eval.md +34 -17
- package/.agents/workflows/helpers/code-quality-guardrails.md +15 -12
- package/.agents/workflows/helpers/code-review.md +14 -12
- package/.agents/workflows/helpers/deliver-story-reference.md +73 -32
- package/.agents/workflows/helpers/deliver-story.md +209 -118
- package/.agents/workflows/helpers/parallel-tooling.md +2 -2
- package/.agents/workflows/helpers/worktree-lifecycle.md +28 -32
- package/.agents/workflows/plan.md +239 -19
- package/.agents/workflows/qa-assist.md +6 -6
- package/.agents/workflows/qa-explore.md +3 -3
- package/.agents/workflows/qa-run.md +1 -5
- package/bin/mandrel.js +12 -1
- package/docs/CHANGELOG.md +62 -0
- package/lib/cli/registry.js +262 -19
- package/lib/cli/sync-agents.js +157 -0
- package/lib/cli/sync-commands.js +115 -6
- package/lib/cli/sync.js +168 -6
- package/lib/cli/update.js +105 -8
- package/lib/cli/version-helpers.js +131 -0
- package/lib/migrations/README.md +7 -5
- package/lib/migrations/index.js +17 -9
- package/lib/migrations/steps/2.1.0-retire-mi-drop-knobs.js +100 -0
- package/lib/migrations/steps/2.1.0-retire-verify-concurrency-cap.js +101 -0
- package/lib/migrations/steps/2.2.0-retire-epic-ac-tags.js +154 -0
- package/package.json +2 -2
- package/.agents/schemas/epic-perf-report.schema.json +0 -89
- package/.agents/schemas/lifecycle/acceptance.reconcile.failed.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.ok.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.skipped.schema.json +0 -13
- package/.agents/schemas/lifecycle/acceptance.reconcile.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/acceptance.reconcile.waived.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.automerge.end.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.automerge.start.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.blocked.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.cleanup.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.cleanup.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.close.end.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.complete.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.end.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.finalize.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.merge.armed.schema.json +0 -13
- package/.agents/schemas/lifecycle/epic.merge.blocked.schema.json +0 -14
- package/.agents/schemas/lifecycle/epic.merge.confirmed.schema.json +0 -17
- package/.agents/schemas/lifecycle/epic.merge.ready.schema.json +0 -15
- package/.agents/schemas/lifecycle/epic.plan.end.schema.json +0 -18
- package/.agents/schemas/lifecycle/epic.plan.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.snapshot.end.schema.json +0 -16
- package/.agents/schemas/lifecycle/epic.snapshot.start.schema.json +0 -12
- package/.agents/schemas/lifecycle/epic.watch.end.schema.json +0 -29
- package/.agents/schemas/lifecycle/epic.watch.start.schema.json +0 -16
- package/.agents/schemas/lifecycle/story.heartbeat.schema.json +0 -20
- package/.agents/schemas/risk-verdict.schema.json +0 -53
- package/.agents/schemas/story-perf-summary.schema.json +0 -73
- package/.agents/scripts/analyze-execution.js +0 -444
- package/.agents/scripts/check-prepush-recovery.js +0 -90
- package/.agents/scripts/lib/git-merge-orchestrator.js +0 -261
- package/.agents/scripts/lib/observability/baseline-refresh-rate.js +0 -221
- package/.agents/scripts/lib/observability/hook-heartbeat.js +0 -187
- package/.agents/scripts/lib/observability/perf-aggregator.js +0 -813
- package/.agents/scripts/lib/observability/perf-report-readers.js +0 -328
- package/.agents/scripts/lib/observability/perf-report-render.js +0 -182
- package/.agents/scripts/lib/orchestration/audit-lens-routing.js +0 -128
- package/.agents/scripts/lib/orchestration/bookkeeping-outbox.js +0 -273
- package/.agents/scripts/lib/orchestration/error-journal.js +0 -139
- package/.agents/scripts/lib/orchestration/lifecycle/emit-story-heartbeat.js +0 -155
- package/.agents/scripts/lib/orchestration/lifecycle/ledger-diff.js +0 -140
- package/.agents/scripts/lib/orchestration/lifecycle/listeners/merge-watcher.js +0 -665
- package/.agents/scripts/lib/orchestration/plan-review-routing.js +0 -63
- package/.agents/scripts/lib/orchestration/planning/risk-verdict.js +0 -104
- package/.agents/scripts/lib/orchestration/planning-context-budget.js +0 -213
- package/.agents/scripts/lib/orchestration/planning-risk.js +0 -194
- package/.agents/scripts/lib/orchestration/post-merge/phases/branch-cleanup.js +0 -56
- package/.agents/scripts/lib/orchestration/post-merge/phases/dashboard-refresh.js +0 -21
- package/.agents/scripts/lib/orchestration/post-merge/phases/notification.js +0 -78
- package/.agents/scripts/lib/orchestration/post-merge/phases/temp-cleanup.js +0 -68
- package/.agents/scripts/lib/orchestration/post-merge/phases/ticket-closure.js +0 -118
- package/.agents/scripts/lib/orchestration/post-merge/phases/worktree-reap.js +0 -397
- package/.agents/scripts/lib/orchestration/preflight-cache.js +0 -187
- package/.agents/scripts/lib/orchestration/resolve-plan-run.js +0 -155
- package/.agents/scripts/lib/orchestration/retro-perf-heuristics.js +0 -275
- package/.agents/scripts/lib/orchestration/story-progress/story-run-progress-writer.js +0 -400
- package/.agents/scripts/lib/single-story/confirm-merge-follow-ups.js +0 -36
- package/.agents/scripts/resolve-plan-run.js +0 -117
- package/.agents/skills/core/analyze-execution/SKILL.md +0 -101
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* emit-merge-flip-failed.js — Story #4539.
|
|
3
|
+
*
|
|
4
|
+
* Appends a single `merge.flip-failed` NDJSON record when a delivery run
|
|
5
|
+
* observed a **confirmed merge** but the `agent::closing → agent::done`
|
|
6
|
+
* label write itself failed.
|
|
7
|
+
*
|
|
8
|
+
* Why this is not `merge.unlanded`: the merge landed. Reporting it as
|
|
9
|
+
* unlanded points the operator at branch protection, required checks, and
|
|
10
|
+
* poll budgets — none of which are the fault — while the real remedy is a
|
|
11
|
+
* re-run of `single-story-confirm-merge.js`, which is idempotent and flips
|
|
12
|
+
* the label from the already-merged PR. The two events are deliberately
|
|
13
|
+
* distinct so the ledger attributes a label-write API failure differently
|
|
14
|
+
* from a merge that never happened.
|
|
15
|
+
*
|
|
16
|
+
* The Story is still transitioned to `agent::blocked` by the caller: a
|
|
17
|
+
* merged PR whose Story sits at `agent::closing` is exactly the silent
|
|
18
|
+
* strand the must-land contract exists to prevent, so it terminates
|
|
19
|
+
* explicitly — but it terminates with the truth.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
appendLedgerEvent,
|
|
24
|
+
assertMergeTerminalFields,
|
|
25
|
+
} from './emit-ledger-event.js';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The only block class this event carries. Distinct from every
|
|
29
|
+
* `merge.unlanded` attribution (`MERGE_UNLANDED_BLOCK_CLASSES`) because the
|
|
30
|
+
* merge is not in question here — only the label write is.
|
|
31
|
+
*/
|
|
32
|
+
export const MERGED_FLIP_FAILED_BLOCK_CLASS = 'merged-flip-failed';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Append exactly one `merge.flip-failed` record to the resolved ledger.
|
|
36
|
+
*
|
|
37
|
+
* @param {object} opts
|
|
38
|
+
* @param {'story'} opts.scope
|
|
39
|
+
* @param {number} opts.ticketId The storyId.
|
|
40
|
+
* @param {number} opts.prNumber The PR that merged.
|
|
41
|
+
* @param {string} opts.reason What failed on the label write.
|
|
42
|
+
* @param {number} opts.elapsedSeconds Elapsed poll time when the flip failed.
|
|
43
|
+
* @param {string} [opts.timestamp]
|
|
44
|
+
* @param {object} [opts.config]
|
|
45
|
+
* @param {string} [opts.ledgerPath]
|
|
46
|
+
* @returns {{ ledgerPath: string, record: object }}
|
|
47
|
+
*/
|
|
48
|
+
export function emitMergeFlipFailed(opts) {
|
|
49
|
+
const {
|
|
50
|
+
scope,
|
|
51
|
+
ticketId,
|
|
52
|
+
prNumber,
|
|
53
|
+
reason,
|
|
54
|
+
elapsedSeconds,
|
|
55
|
+
timestamp = new Date().toISOString(),
|
|
56
|
+
config,
|
|
57
|
+
ledgerPath,
|
|
58
|
+
} = opts ?? {};
|
|
59
|
+
|
|
60
|
+
assertMergeTerminalFields('emitMergeFlipFailed', {
|
|
61
|
+
scope,
|
|
62
|
+
ticketId,
|
|
63
|
+
prNumber,
|
|
64
|
+
reason,
|
|
65
|
+
elapsedSeconds,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
return appendLedgerEvent({
|
|
69
|
+
emitter: 'emitMergeFlipFailed',
|
|
70
|
+
schemaFile: 'merge.flip-failed.schema.json',
|
|
71
|
+
payload: {
|
|
72
|
+
event: 'merge.flip-failed',
|
|
73
|
+
scope,
|
|
74
|
+
ticketId,
|
|
75
|
+
prNumber,
|
|
76
|
+
blockClass: MERGED_FLIP_FAILED_BLOCK_CLASS,
|
|
77
|
+
reason,
|
|
78
|
+
elapsedSeconds,
|
|
79
|
+
timestamp,
|
|
80
|
+
},
|
|
81
|
+
ticketId,
|
|
82
|
+
timestamp,
|
|
83
|
+
config,
|
|
84
|
+
ledgerPath,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -6,23 +6,16 @@
|
|
|
6
6
|
* finishes its work without a confirmed merge. Pattern mirrors
|
|
7
7
|
* `emit-story-heartbeat.js`: direct schema validation via Ajv followed by
|
|
8
8
|
* a synchronous `appendFileSync` — this event is NOT routed through the
|
|
9
|
-
* bus (unlike `emit-loop-tick.js`) because it
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* which has no bus at all. A bare append keeps both call sites simple
|
|
13
|
-
* and dependency-free.
|
|
9
|
+
* bus (unlike `emit-loop-tick.js`) because it fires from the standalone
|
|
10
|
+
* `single-story-close` flow, which has no bus at all. A bare append keeps
|
|
11
|
+
* the call site simple and dependency-free.
|
|
14
12
|
*
|
|
15
|
-
* Ledger destination
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* `temp/standalone/stories/story-<id>/lifecycle.ndjson`. `ticketId`
|
|
22
|
-
* is the storyId. The standalone `single-story-close` path has no
|
|
23
|
-
* parent Epic to anchor a `temp/epic-<id>/` directory to, mirroring
|
|
24
|
-
* the `eid === null` standalone convention `signalsFile` already
|
|
25
|
-
* uses for Story-level signals.
|
|
13
|
+
* Ledger destination (Story #4426 AC4): `storyLedgerPath(null, ticketId)`
|
|
14
|
+
* — the standalone story-scope destination
|
|
15
|
+
* `temp/standalone/stories/story-<id>/lifecycle.ndjson`, where `ticketId`
|
|
16
|
+
* is the storyId. The `single-story-close` path has no parent Epic to
|
|
17
|
+
* anchor a run directory to, mirroring the `eid === null` standalone
|
|
18
|
+
* convention `signalsFile` already uses for Story-level signals.
|
|
26
19
|
*
|
|
27
20
|
* A caller may always override the destination via `ledgerPath` (tests,
|
|
28
21
|
* or a future caller with a non-default temp layout).
|
|
@@ -57,50 +50,20 @@
|
|
|
57
50
|
* `predicate-refused` / a classified arm failure directly.
|
|
58
51
|
*/
|
|
59
52
|
|
|
60
|
-
import { appendFileSync, mkdirSync, readFileSync } from 'node:fs';
|
|
61
|
-
import path from 'node:path';
|
|
62
|
-
import { fileURLToPath } from 'node:url';
|
|
63
|
-
|
|
64
|
-
import Ajv2020 from 'ajv/dist/2020.js';
|
|
65
|
-
import addFormats from 'ajv-formats';
|
|
66
|
-
|
|
67
|
-
import { epicLedgerPath, storyLedgerPath } from '../../config/temp-paths.js';
|
|
68
53
|
import { isValidBlockClass } from '../merge-block-class.js';
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
'..',
|
|
74
|
-
'..',
|
|
75
|
-
'..',
|
|
76
|
-
'..',
|
|
77
|
-
'schemas',
|
|
78
|
-
'lifecycle',
|
|
79
|
-
'merge.unlanded.schema.json',
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const VALID_SCOPES = new Set(['epic', 'story']);
|
|
83
|
-
|
|
84
|
-
let _validator;
|
|
85
|
-
|
|
86
|
-
function getValidator() {
|
|
87
|
-
if (_validator) return _validator;
|
|
88
|
-
const schema = JSON.parse(readFileSync(SCHEMA_PATH, 'utf8'));
|
|
89
|
-
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
90
|
-
addFormats(ajv);
|
|
91
|
-
_validator = ajv.compile(schema);
|
|
92
|
-
return _validator;
|
|
93
|
-
}
|
|
54
|
+
import {
|
|
55
|
+
appendLedgerEvent,
|
|
56
|
+
assertMergeTerminalFields,
|
|
57
|
+
} from './emit-ledger-event.js';
|
|
94
58
|
|
|
95
59
|
/**
|
|
96
60
|
* Append exactly one `merge.unlanded` NDJSON record to the resolved
|
|
97
61
|
* lifecycle ledger.
|
|
98
62
|
*
|
|
99
63
|
* @param {object} opts
|
|
100
|
-
* @param {'
|
|
64
|
+
* @param {'story'} opts.scope Which delivery path is reporting the
|
|
101
65
|
* unlanded merge.
|
|
102
|
-
* @param {number} opts.ticketId
|
|
103
|
-
* storyId when `scope === 'story'`.
|
|
66
|
+
* @param {number} opts.ticketId The storyId.
|
|
104
67
|
* @param {number} opts.prNumber The PR number that did not land.
|
|
105
68
|
* @param {string} opts.blockClass A valid `merge.unlanded` attribution
|
|
106
69
|
* (`MERGE_UNLANDED_BLOCK_CLASSES` in
|
|
@@ -130,64 +93,35 @@ export function emitMergeUnlanded(opts) {
|
|
|
130
93
|
ledgerPath: ledgerPathOverride,
|
|
131
94
|
} = opts ?? {};
|
|
132
95
|
|
|
133
|
-
|
|
134
|
-
throw new Error(
|
|
135
|
-
`emitMergeUnlanded: scope "${scope}" must be one of: ${[...VALID_SCOPES].join(', ')}`,
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
if (!Number.isInteger(ticketId) || ticketId < 1) {
|
|
139
|
-
throw new Error('emitMergeUnlanded: ticketId must be a positive integer');
|
|
140
|
-
}
|
|
141
|
-
if (!Number.isInteger(prNumber) || prNumber < 1) {
|
|
142
|
-
throw new Error('emitMergeUnlanded: prNumber must be a positive integer');
|
|
143
|
-
}
|
|
144
|
-
if (!isValidBlockClass(blockClass)) {
|
|
145
|
-
throw new Error(
|
|
146
|
-
`emitMergeUnlanded: blockClass "${blockClass}" is not a recognised merge-block-class value`,
|
|
147
|
-
);
|
|
148
|
-
}
|
|
149
|
-
if (typeof reason !== 'string' || reason.length === 0) {
|
|
150
|
-
throw new Error('emitMergeUnlanded: reason must be a non-empty string');
|
|
151
|
-
}
|
|
152
|
-
if (typeof elapsedSeconds !== 'number' || elapsedSeconds < 0) {
|
|
153
|
-
throw new Error(
|
|
154
|
-
'emitMergeUnlanded: elapsedSeconds must be a non-negative number',
|
|
155
|
-
);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
const payload = {
|
|
159
|
-
event: 'merge.unlanded',
|
|
96
|
+
assertMergeTerminalFields('emitMergeUnlanded', {
|
|
160
97
|
scope,
|
|
161
98
|
ticketId,
|
|
162
99
|
prNumber,
|
|
163
|
-
blockClass,
|
|
164
100
|
reason,
|
|
165
101
|
elapsedSeconds,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const validator = getValidator();
|
|
170
|
-
if (!validator(payload)) {
|
|
171
|
-
const detail = (validator.errors ?? [])
|
|
172
|
-
.map((e) => `${e.instancePath || '/'} ${e.message}`)
|
|
173
|
-
.join('; ');
|
|
102
|
+
});
|
|
103
|
+
if (!isValidBlockClass(blockClass)) {
|
|
174
104
|
throw new Error(
|
|
175
|
-
`emitMergeUnlanded:
|
|
105
|
+
`emitMergeUnlanded: blockClass "${blockClass}" is not a recognised merge-block-class value`,
|
|
176
106
|
);
|
|
177
107
|
}
|
|
178
108
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
109
|
+
return appendLedgerEvent({
|
|
110
|
+
emitter: 'emitMergeUnlanded',
|
|
111
|
+
schemaFile: 'merge.unlanded.schema.json',
|
|
112
|
+
payload: {
|
|
113
|
+
event: 'merge.unlanded',
|
|
114
|
+
scope,
|
|
115
|
+
ticketId,
|
|
116
|
+
prNumber,
|
|
117
|
+
blockClass,
|
|
118
|
+
reason,
|
|
119
|
+
elapsedSeconds,
|
|
120
|
+
timestamp,
|
|
121
|
+
},
|
|
122
|
+
ticketId,
|
|
123
|
+
timestamp,
|
|
124
|
+
config,
|
|
125
|
+
ledgerPath: ledgerPathOverride,
|
|
126
|
+
});
|
|
193
127
|
}
|
|
@@ -8,12 +8,16 @@ live in this folder**.
|
|
|
8
8
|
|
|
9
9
|
## Files here
|
|
10
10
|
|
|
11
|
-
- [`merge-watcher.js`](./merge-watcher.js) — polls `gh pr view` after
|
|
12
|
-
auto-merge is armed until the PR's `mergeCommit` is observed, then emits
|
|
13
|
-
the merge-confirmed event.
|
|
14
11
|
- [`watcher.js`](./watcher.js) — the CI-poll loop (`watchPrToTerminal`)
|
|
15
12
|
driven by `pr-watch-with-update.js`.
|
|
16
13
|
|
|
14
|
+
`merge-watcher.js` was deleted in Story #4545: the Epic-era `MergeWatcher`
|
|
15
|
+
listener had no production caller after the v2.0.0 Story-only cutover. The
|
|
16
|
+
poll defaults and `deriveChecksStatus` the live close path did import from it
|
|
17
|
+
now live in
|
|
18
|
+
[`lib/orchestration/merge-poll.js`](../../merge-poll.js), a home the close
|
|
19
|
+
path owns.
|
|
20
|
+
|
|
17
21
|
Other close-tail side effects (ledger write, finalize/PR open, automerge
|
|
18
22
|
arm, branch cleanup, label transition) are owned by the Story delivery
|
|
19
23
|
path (`helpers/deliver-story` / `single-story-close.js`) rather than a
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Subscribes to:
|
|
7
7
|
* - `pr.created` → resolve the required-check names from GitHub at
|
|
8
|
-
* runtime via `gh pr checks <pr> --required`,
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* expires), then emit `epic.watch.end` with the outcome map.
|
|
8
|
+
* runtime via `gh pr checks <pr> --required`, then poll until every
|
|
9
|
+
* check reaches a terminal state (or a per-listener wall-clock
|
|
10
|
+
* deadline expires), recording the outcome in `classifications`.
|
|
12
11
|
*
|
|
13
12
|
* Critical contract:
|
|
14
13
|
* - Required-check **names** are resolved from `gh pr checks` at
|
|
@@ -28,11 +27,13 @@
|
|
|
28
27
|
* re-runs the poll loop (which is itself idempotent: the outcome map
|
|
29
28
|
* always reflects the live GitHub state).
|
|
30
29
|
*
|
|
31
|
-
* Side-effect firewall: the listener
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
30
|
+
* Side-effect firewall: the listener shells out to `gh` and records
|
|
31
|
+
* its outcome in the in-memory `classifications` log. It does NOT
|
|
32
|
+
* mutate ticket labels, post comments, call `notify`, or emit on the
|
|
33
|
+
* bus — the production consumer (`pr-watch-with-update.js`) drives
|
|
34
|
+
* `watchPrToTerminal` directly without a bus, and the retired
|
|
35
|
+
* `epic.watch.start` / `epic.watch.end` bus events went with the
|
|
36
|
+
* Epic-orchestration stratum.
|
|
36
37
|
*/
|
|
37
38
|
|
|
38
39
|
import { spawnSync } from 'node:child_process';
|
|
@@ -40,20 +41,20 @@ import { spawnSync } from 'node:child_process';
|
|
|
40
41
|
import { parsePrNumberFromUrl } from '../../../github-url.js';
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
* Map `gh pr checks` `state` values to the canonical outcome
|
|
44
|
-
*
|
|
45
|
-
* in-flight checks. Pure — exported for
|
|
46
|
-
* and reviewable.
|
|
44
|
+
* Map `gh pr checks` `state` values to the canonical lowercase outcome
|
|
45
|
+
* vocabulary (`success` | `failure` | `timed_out` | `skipped`), with a
|
|
46
|
+
* fourth `'pending'` sentinel for in-flight checks. Pure — exported for
|
|
47
|
+
* tests so the pin is explicit and reviewable.
|
|
47
48
|
*
|
|
48
49
|
* `gh` returns capitalized SCREAMING_SNAKE values (`SUCCESS`,
|
|
49
|
-
* `FAILURE`, `TIMED_OUT`, etc.)
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* `
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
50
|
+
* `FAILURE`, `TIMED_OUT`, etc.). An empty / queued / in_progress state
|
|
51
|
+
* collapses to `'pending'` so the poll loop can distinguish "still
|
|
52
|
+
* running" from terminal outcomes. `'pending'` is intentionally NOT a
|
|
53
|
+
* terminal outcome — `reduceOutcomes` is called only on the live state,
|
|
54
|
+
* and the final outcome map gates on `allTerminal()` so no `'pending'`
|
|
55
|
+
* ever leaks into a finished watch. Unknown / non-pending unrecognized
|
|
56
|
+
* values collapse to `'skipped'` so any future GitHub state we haven't
|
|
57
|
+
* enumerated still maps into the vocabulary.
|
|
57
58
|
*/
|
|
58
59
|
/**
|
|
59
60
|
* The raw check-state tokens `normalizeCheckState` recognizes (lowercased).
|
|
@@ -264,8 +265,8 @@ export function parseGhPrChecks(stdout) {
|
|
|
264
265
|
}
|
|
265
266
|
|
|
266
267
|
/**
|
|
267
|
-
* Reduce a list of check entries to the `{ checkName: outcome }`
|
|
268
|
-
*
|
|
268
|
+
* Reduce a list of check entries to the canonical `{ checkName: outcome }`
|
|
269
|
+
* map. Pure — exported for tests.
|
|
269
270
|
*
|
|
270
271
|
* When `gh` returns the same `name` more than once (parallel matrix
|
|
271
272
|
* builds, retries), the LAST entry wins. The poll loop calls this on
|
|
@@ -431,9 +432,9 @@ export async function pollUntilTerminal({
|
|
|
431
432
|
* @param {{status:number,stdout:string,stderr:string}} [opts.firstProbe]
|
|
432
433
|
* Optional already-issued `gh pr checks` result. When the caller (the
|
|
433
434
|
* `Watcher` listener) has already probed once to resolve the required
|
|
434
|
-
* names
|
|
435
|
-
*
|
|
436
|
-
*
|
|
435
|
+
* check names, it threads that result here so the loop does not
|
|
436
|
+
* double-spend the first `gh pr checks` call. Omit it (the CLI path)
|
|
437
|
+
* and the loop issues the first probe itself.
|
|
437
438
|
* @returns {Promise<{
|
|
438
439
|
* outcomes: object,
|
|
439
440
|
* requiredChecks: string[],
|
|
@@ -466,8 +467,8 @@ export async function watchPrToTerminal({
|
|
|
466
467
|
firstProbe,
|
|
467
468
|
}) {
|
|
468
469
|
// First probe: resolve the required-check name set at runtime. Reuse a
|
|
469
|
-
// caller-supplied probe (the listener already issued one
|
|
470
|
-
//
|
|
470
|
+
// caller-supplied probe (the listener already issued one to resolve the
|
|
471
|
+
// required check names) so we never double-spend the first `gh` call.
|
|
471
472
|
const first = firstProbe ?? ghPrChecksFn({ prUrl, cwd });
|
|
472
473
|
// `gh` exits 8 when checks are still pending; this is expected and
|
|
473
474
|
// does not indicate failure. Any other non-zero status with no
|
|
@@ -619,12 +620,8 @@ export class Watcher {
|
|
|
619
620
|
* @param {{ info?: Function, warn?: Function, debug?: Function }} [opts.logger]
|
|
620
621
|
*/
|
|
621
622
|
constructor(opts = {}) {
|
|
622
|
-
if (
|
|
623
|
-
|
|
624
|
-
typeof opts.bus.on !== 'function' ||
|
|
625
|
-
typeof opts.bus.emit !== 'function'
|
|
626
|
-
) {
|
|
627
|
-
throw new TypeError('Watcher requires a bus with on() and emit()');
|
|
623
|
+
if (!opts.bus || typeof opts.bus.on !== 'function') {
|
|
624
|
+
throw new TypeError('Watcher requires a bus with on()');
|
|
628
625
|
}
|
|
629
626
|
this.bus = opts.bus;
|
|
630
627
|
this.cwd = opts.cwd ?? process.cwd();
|
|
@@ -688,11 +685,10 @@ export class Watcher {
|
|
|
688
685
|
return;
|
|
689
686
|
}
|
|
690
687
|
|
|
691
|
-
// First probe: resolve the required-check name set at runtime
|
|
692
|
-
//
|
|
693
|
-
//
|
|
694
|
-
//
|
|
695
|
-
// double-spending the first `gh pr checks` call.
|
|
688
|
+
// First probe: resolve the required-check name set at runtime BEFORE
|
|
689
|
+
// the (potentially long) poll loop runs. We thread this probe into
|
|
690
|
+
// `watchPrToTerminal` (via `firstProbe`) so the shared loop reuses it
|
|
691
|
+
// instead of double-spending the first `gh pr checks` call.
|
|
696
692
|
const first = this.ghPrChecksFn({ prUrl, cwd: this.cwd });
|
|
697
693
|
const firstEntries = parseGhPrChecks(first.stdout);
|
|
698
694
|
if (firstEntries.length === 0 && first.status !== 0 && first.status !== 8) {
|
|
@@ -710,45 +706,24 @@ export class Watcher {
|
|
|
710
706
|
|
|
711
707
|
const requiredChecks = firstEntries.map((e) => e.name);
|
|
712
708
|
|
|
713
|
-
try {
|
|
714
|
-
await this.bus.emit('epic.watch.start', { prUrl, requiredChecks });
|
|
715
|
-
} catch (err) {
|
|
716
|
-
this.classifications.push({
|
|
717
|
-
event,
|
|
718
|
-
seqId,
|
|
719
|
-
outcome: 'failed',
|
|
720
|
-
reason: `start-emit-failed:${err?.message ?? err}`,
|
|
721
|
-
});
|
|
722
|
-
this.logger.warn?.(
|
|
723
|
-
`[Watcher] epic.watch.start emit failed: ${err?.message ?? err}`,
|
|
724
|
-
);
|
|
725
|
-
return;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
709
|
// Delegate the poll + BEHIND-recovery loop to the shared plain
|
|
729
710
|
// primitive so the CLI (`pr-watch-with-update.js`) and this listener
|
|
730
711
|
// run identical logic.
|
|
731
|
-
const {
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
ghPrViewFn: this.ghPrViewFn,
|
|
747
|
-
ghPrUpdateBranchFn: this.ghPrUpdateBranchFn,
|
|
748
|
-
sleepFn: this.sleepFn,
|
|
749
|
-
logger: this.logger,
|
|
750
|
-
firstProbe: first,
|
|
751
|
-
});
|
|
712
|
+
const { polls, updatesApplied, resumesApplied, terminal, stillRunning } =
|
|
713
|
+
await watchPrToTerminal({
|
|
714
|
+
prUrl,
|
|
715
|
+
cwd: this.cwd,
|
|
716
|
+
maxPolls: this.maxPolls,
|
|
717
|
+
maxUpdates: this.maxUpdates,
|
|
718
|
+
maxResumes: this.maxResumes,
|
|
719
|
+
pollIntervalMs: this.pollIntervalMs,
|
|
720
|
+
ghPrChecksFn: this.ghPrChecksFn,
|
|
721
|
+
ghPrViewFn: this.ghPrViewFn,
|
|
722
|
+
ghPrUpdateBranchFn: this.ghPrUpdateBranchFn,
|
|
723
|
+
sleepFn: this.sleepFn,
|
|
724
|
+
logger: this.logger,
|
|
725
|
+
firstProbe: first,
|
|
726
|
+
});
|
|
752
727
|
|
|
753
728
|
// `still-running` (slow CI, not red) is a distinct classification from
|
|
754
729
|
// a genuine `timed-out` — reserved for a check that never went
|
|
@@ -768,16 +743,6 @@ export class Watcher {
|
|
|
768
743
|
resumesApplied,
|
|
769
744
|
requiredChecks: requiredChecks.length,
|
|
770
745
|
});
|
|
771
|
-
try {
|
|
772
|
-
await this.bus.emit('epic.watch.end', {
|
|
773
|
-
prUrl,
|
|
774
|
-
checkOutcomes: emitOutcomes,
|
|
775
|
-
});
|
|
776
|
-
} catch (err) {
|
|
777
|
-
this.logger.warn?.(
|
|
778
|
-
`[Watcher] epic.watch.end emit failed (swallowed): ${err?.message ?? err}`,
|
|
779
|
-
);
|
|
780
|
-
}
|
|
781
746
|
}
|
|
782
747
|
|
|
783
748
|
reset() {
|
|
@@ -40,28 +40,17 @@ function formatClock(iso) {
|
|
|
40
40
|
* mapping is stable across runs.
|
|
41
41
|
*/
|
|
42
42
|
const PHASE_BY_PREFIX = Object.freeze({
|
|
43
|
-
'epic.snapshot': 'Snapshot',
|
|
44
|
-
'epic.plan': 'Plan',
|
|
45
43
|
'story.dispatch': 'Waves',
|
|
46
44
|
'story.merged': 'Waves',
|
|
47
45
|
'story.blocked': 'Waves',
|
|
48
|
-
'epic.blocked': 'Waves',
|
|
49
|
-
'epic.close': 'Close-tail',
|
|
50
|
-
'acceptance.reconcile': 'Acceptance Reconciliation',
|
|
51
|
-
'epic.finalize': 'Finalize',
|
|
52
46
|
'pr.created': 'Finalize',
|
|
53
|
-
'epic.watch': 'Watch',
|
|
54
|
-
'epic.automerge': 'Automerge',
|
|
55
|
-
'epic.merge': 'Automerge',
|
|
56
|
-
'epic.cleanup': 'Cleanup',
|
|
57
|
-
'epic.complete': 'Complete',
|
|
58
47
|
'notification.emitted': 'Notifications',
|
|
59
48
|
'checkpoint.written': 'Checkpoint',
|
|
60
49
|
});
|
|
61
50
|
|
|
62
51
|
function phaseFor(eventName) {
|
|
63
|
-
// Match longest prefix first so `
|
|
64
|
-
// `
|
|
52
|
+
// Match longest prefix first so `story.dispatch.start` resolves before
|
|
53
|
+
// `story.dispatch` would match an unrelated `story.*` block.
|
|
65
54
|
const keys = Object.keys(PHASE_BY_PREFIX).sort((a, b) => b.length - a.length);
|
|
66
55
|
for (const k of keys) {
|
|
67
56
|
if (eventName === k || eventName.startsWith(`${k}.`)) {
|
|
@@ -277,7 +266,7 @@ export function render(ledger, opts = {}) {
|
|
|
277
266
|
*
|
|
278
267
|
* The wildcard-firewall rule (Tech Spec § Bus contract) requires that
|
|
279
268
|
* trace observers do NOT import any module that mutates GitHub state,
|
|
280
|
-
* the worktree, or the filesystem outside `temp/
|
|
269
|
+
* the worktree, or the filesystem outside `temp/run-<id>/`. This
|
|
281
270
|
* module satisfies that constraint: the only filesystem writes are to
|
|
282
271
|
* the companion path under the same temp directory the ledger lives
|
|
283
272
|
* in.
|