E2E PR Stabilizer
Stabilize the Playwright E2E suite for a single pull request using evidence, not assumptions.
Spans, traces, and the live app are the source of truth — not the CI dashboard.
This skill never proposes a fix without a measurement to point at, and never commits a fix until three consecutive local runs prove it works.
This SKILL.md is a thin index.
Detailed procedures live in rules/*.md and templates/*.md.
Each phase loads only what it needs.
What this skill combines
| Source |
Role |
| Playwright Healer agent — external; one of the Playwright Test Agents on the Playwright MCP server |
Test-debugging methodology — how to fix a Playwright test correctly. |
/playwright-trace-analyzer |
Per-run trace.zip extraction, hotspot ranking. |
/ci-auto-fix |
Reused only for Phase 7's single push + watch — the iteration loop no longer lives here. |
Dash0 MCP server (dash0-dev or dash0-prod) |
Historical evidence — failure recurrence, retry counts, span-level evidence across CI runs. |
| Local Playwright runner |
Primary evidence source — trace.zip per run, OTel spans to Dash0 (ci.is_ci=false), and the live app for selector verification. |
| GitHub Actions (one call) |
Final CI ratification at Phase 7. |
This skill is the orchestrator over those.
External dependency — the Playwright Healer agent. The healer methodology
this skill drives fixes through is not an agent in this repo; it is
Playwright's own healer (Playwright Test Agents:
planner / generator / healer) running on the
Playwright MCP server. Set it up
with npx playwright init-agents --loop=claude (Playwright ≥ 1.56). At runtime,
Phase 5 uses the healer when the Playwright MCP is connected (mcp__playwright__*
tools present) and falls back to the inline root-cause methodology when it
isn't — so the skill works with or without it, but is strongest with it.
It does not duplicate their content — each phase delegates.
Modes
| Mode |
Default |
Entry rule (what enters the fix queue) |
Phase 5 (edits) |
Phase 6 (local 3-pass gate) |
Phase 7 (CI ratification) |
Phase 8 output |
stabilize |
yes |
failure_rate ≥ 0.10 over ≥ 5 attempts, or flake_count ≥ 2. |
Drafted, then double-gated before commit. |
Required — 3 consecutive local passes per fixed test. |
One push, one CI watch. |
Stabilization report with before / after numbers, local-pass log, CI verdict. |
optimize |
|
Top-N slowest tests by total time, or actions with dur > 5×median. |
Skipped. |
Skipped. |
Skipped. |
Recommendations-only report — humans apply the wins. |
stabilize is the default because optimization edits (tightening timeouts, removing waits) carry flake risk that warrants human judgment.
optimize runs Phases 1–4 only and emits a ranked recommendations report.
Input
$ARGUMENTS is parsed as [mode] [pr-ref] in any order:
optimize (literal token) selects optimize mode; anything else is treated as pr-ref.
pr-ref is a PR URL (https://github.com/<org>/<repo>/pull/13319) or PR number (13319).
- If
pr-ref is missing, auto-detect the open PR for the current branch (same path as /ci-auto-fix Step 0).
- If
mode is missing, default to stabilize.
Resolve mode + PR before doing anything else.
See rules/input-resolution.md.
Workflow
Eight phases.
Do not skip a gate.
Phases 5, 6, and 7 are skipped in optimize mode (the Modes column says so explicitly).
| Phase |
Name |
Modes |
Rule file |
Gate |
| 0 |
Resolve target |
both |
rules/input-resolution.md |
Mode + PR URL + branch + head SHA + owner / repo printed. |
| 1 |
Pull historical telemetry |
both |
rules/telemetry-driven-analysis.md |
Dash0 spans for this PR fetched and grouped by test name; failure recurrence + retry counts measured (stabilize) or action dur distribution measured (optimize). |
| 2 |
Local reproduction + trace capture |
both |
rules/local-iteration.md |
Each queued test run locally with --trace=on; trace.zip + (where available) fresh Dash0 spans tagged ci.is_ci=false captured. |
| 3 |
Correlate spans ↔ traces |
both |
rules/root-cause-and-fix.md |
Each queued test has a span-side signature and a trace-side hotspot. |
| 4 |
Root-cause synthesis |
both |
rules/root-cause-and-fix.md |
A single, evidence-anchored hypothesis per candidate, citing the span signature and the trace hotspot. Speculative hypotheses become recommendation-only entries, not fixes. |
| 5 |
Draft fix + selector-existence check |
stabilize only |
rules/root-cause-and-fix.md, rules/fix-validation.md, rules/guard-rails.md |
Diff drafted; every new locator proven to resolve against source (static grep) or the live app (locator.count() ≥ 1). A locator that fails both checks is hallucinated — discard the diff and re-enter Phase 4 with that evidence. |
| 6 |
Local verification — 3 consecutive passes |
stabilize only |
rules/local-iteration.md |
Fixed test runs locally ≥ 3 times with --trace=on and passes 3 times in a row. A single failure or flake within the streak resets the counter. Maximum 10 attempts per test before escalating. |
| 7 |
CI ratification — one push, one watch |
stabilize only |
rules/verification-loop.md |
All passing fixes committed and pushed in a single push event; the resulting CI run is watched to conclusion and its telemetry compared against the Phase 1 baseline. |
| 8 |
Report |
both |
templates/stabilization-report.md |
Stabilize: report with before / after numbers + local-pass log + CI verdict + residual risk. Optimize: recommendations-only report ranked by measured wall-clock impact. |
Inner iteration in stabilize mode is local and bounded — see rules/local-iteration.md.
The CI step in Phase 7 runs once.
If CI disagrees with the local result, that is a signal to escalate, not to re-enter the loop blindly.
Required reading by phase
Load on demand.
Do not preload.
For trace mechanics (zip → JSONL → action timeline), defer to /playwright-trace-analyzer.
Do not re-implement.
Core principles
- Iterate locally, ratify on CI.
The inner loop is local because it is seconds-per-run, has the same trace artifacts, and emits the same OTel spans.
CI runs once at the end as ground truth.
- Data first, hypothesis second.
Every fix is anchored to (a) a span with a measured failure rate, or (b) a trace action with a measured
dur.
"I think this is flaky" is not a finding.
- Two evidence layers, not one.
Spans tell you which tests fail and how often across runs (historical and local).
Traces tell you why one specific run failed.
A fix is only credible when both layers agree.
- Validate empirically, not predictively.
Confidence in a fix comes from running it, not from scoring it on paper.
The two gates the skill enforces are both deterministic: selectors must exist (verifiable against source or live app), and the fixed test must pass three consecutive local runs (verifiable from exit codes).
- Selectors must exist before they are used.
Every new locator in a draft fix is verified against the component source (static) and, when ambiguous, the running app (live).
The skill never commits a fix that names an element that does not exist.
- Three consecutive local passes or no commit.
A single passing run can be a coin flip on a flake.
Three in a row — hardened with
--repeat-each=3 when the measured flake rate is low (see rules/local-iteration.md) — filters out most unfixed flakes before we spend a CI cycle.
The streak is necessary but not sufficient evidence; Phase 7's CI ratification plus the telemetry comparison is the real confirmation.
- Never weaken the suite.
No
.skip, .fixme, waitForTimeout, continue-on-error, --no-verify, or removed assertions.
The full list lives in rules/guard-rails.md.
- Test-side fix unless the trace proves otherwise.
Most flakes are selector, timing, or state-management bugs in tests.
If the trace evidence points to product code, surface it to the user as a separate recommendation — do not silently mutate app code.
- One PR at a time.
Cross-PR refactors belong in a different skill.
Self-Improvement
/e2e-pr-stabilizer gets better across runs through a two-tier lessons loop
(fast episodic tier + gated promotion), like autonomous-workflow and
fix-bug. In stabilize mode it reads e2e-pr-stabilizer-lessons at
Phase 4 (biasing the P1–P6 pattern classification and the Phase 5 locator
strategy) and writes at Phase 7 — gated on the telemetry ratification
signal, not the local 3-pass streak, so only fixes that CI actually confirmed
fixed accrue a working lesson. The two tiers split naturally: the global
scope holds universal race-shape → fix-shape mappings, the repo::{owner}/{repo}
scope holds app-specific locator robustness. Lessons are advisory — they
never relax a guard-rail, an empirical gate, or the 3-consecutive-pass
requirement. A recurring lesson (seen_count >= 3) is promotion-eligible via
/create-skill diagnose e2e-pr-stabilizer.
On the Phase 7 ratified verdict it also contributes a flaky hotspot to the
shared codebase-knowledge bucket (hotspot::<test.file> counter) — a fact no
other host produces, so every later code-changer plans around a known-unstable file.
Write only, merge-never-clobber, on the ratified SHA (contract:
../../../agents/shared/rules/codebase-knowledge.md; mechanics in the loop file's cross-bucket write section).
optimize mode skips the loop (no fix, no ratification signal). Lessons run
through LoreKit's memory.* tools (the lorekit-memory skill); if LoreKit is
not connected the loop is a silent no-op. Full contract:
rules/self-improvement-loop.md.
Anti-patterns
One-liners; the full list lives in rules/guard-rails.md.
- Pushing a fix because "the diff looks right" without three consecutive local passes.
- Trusting a single local pass as proof — flakes pass once routinely.
- Drafting a fix that uses
getByTestId('foo') when nothing in the component source emits data-testid="foo".
- Patching
waitForTimeout(1500) to mask a race instead of fixing the wait condition.
- Marking a test
.fixme() because it is "flaky" without a measured cause.
- Treating a single failed CI run as evidence — flakes are statistical, so fetch the span history.
- Re-running CI hoping for a green without applying a code change.
- Editing product code based on speculation when the trace points at a selector or test-state issue.
Quickstart
/e2e-pr-stabilizer # stabilize, auto-detect PR
/e2e-pr-stabilizer 13319 # stabilize PR 13319
/e2e-pr-stabilizer https://github.com/<org>/<repo>/pull/13319 # stabilize via URL
/e2e-pr-stabilizer optimize # optimize, auto-detect PR
/e2e-pr-stabilizer optimize 13319 # optimize PR 13319
Once invoked, the skill drives end-to-end:
- Resolves the mode and the PR.
- Queries the Dash0 MCP for E2E spans filtered to this PR (
git.pull_request_link) — historical baseline.
- Reproduces locally with
--trace=on, capturing trace.zip and (where the local OTel reporter is wired) fresh spans.
- Correlates and produces an evidence-anchored finding set.
- stabilize: drafts each fix, verifies every new locator resolves against source or the live app, then commits locally.
- stabilize: runs the fixed test locally until it passes 3 times in a row (per fix).
- stabilize: pushes once; watches the CI run; compares fresh telemetry to baseline.
- Emits the report — stabilization (before / after + local-pass log + CI verdict) or optimization (recommendations).
Definition of Done
Both modes
stabilize only
optimize only
1---2name: e2e-pr-stabilizer3description: Stabilizes or optimizes Playwright E2E tests on one PR via a local-first loop, then ratifies with a single CI run. Pulls Dash0 spans (`git.pull_request_link`) as the historical baseline, then captures every iteration's evidence locally with `--trace=on` (same OTel exporter, same trace schema). Validation is empirical, not predictive: before commit, every new locator must resolve against source (static grep) or the live app (`locator.count()`); after commit, the fixed test must pass three consecutive local runs before the single push. Modes: `stabilize` (default) heals flaky / failing tests; `optimize` is report-only and ranks slow-action wins by measured ms saved. Refuses `.skip`, `.fixme`, `waitForTimeout`, or any check-weakening edit. Use when a PR has flaky or failing E2E tests or when you want to find slow tests worth tightening. Triggers on "stabilize this PR", "fix flaky e2e", "heal playwright on PR", "ui-e2e is failing", "self-heal e2e", "optimize e2e", "/e2e-pr-stabilizer".4license: MIT5---67# E2E PR Stabilizer89Stabilize the Playwright E2E suite for a single pull request using **evidence, not assumptions**.10Spans, traces, and the live app are the source of truth — not the CI dashboard.11This skill never proposes a fix without a measurement to point at, and never commits a fix until three consecutive local runs prove it works.1213> **This `SKILL.md` is a thin index.**14> Detailed procedures live in [`rules/*.md`](./rules) and [`templates/*.md`](./templates).15> Each phase loads only what it needs.1617---1819## What this skill combines2021| Source | Role |22|--------|------|23| [Playwright Healer agent](https://playwright.dev/docs/test-agents) — external; one of the [Playwright Test Agents](https://playwright.dev/docs/test-agents) on the [Playwright MCP server](https://github.com/microsoft/playwright-mcp) | Test-debugging methodology — how to fix a Playwright test correctly. |24| [`/playwright-trace-analyzer`](../../analysis/playwright-trace-analyzer/SKILL.md) | Per-run `trace.zip` extraction, hotspot ranking. |25| [`/ci-auto-fix`](../../delivery/ci-auto-fix/SKILL.md) | Reused only for Phase 7's single push + watch — the iteration loop no longer lives here. |26| Dash0 MCP server (`dash0-dev` or `dash0-prod`) | Historical evidence — failure recurrence, retry counts, span-level evidence across CI runs. |27| Local Playwright runner | Primary evidence source — trace.zip per run, OTel spans to Dash0 (`ci.is_ci=false`), and the live app for selector verification. |28| GitHub Actions (one call) | Final CI ratification at Phase 7. |2930This skill is the orchestrator over those.3132> **External dependency — the Playwright Healer agent.** The healer methodology33> this skill drives fixes through is **not** an agent in this repo; it is34> Playwright's own **healer** ([Playwright Test Agents](https://playwright.dev/docs/test-agents):35> planner / generator / healer) running on the36> [Playwright MCP server](https://github.com/microsoft/playwright-mcp). Set it up37> with `npx playwright init-agents --loop=claude` (Playwright ≥ 1.56). At runtime,38> Phase 5 **uses the healer when the Playwright MCP is connected** (`mcp__playwright__*`39> tools present) and **falls back to the inline root-cause methodology** when it40> isn't — so the skill works with or without it, but is strongest with it.41It does not duplicate their content — each phase delegates.4243---4445## Modes4647| Mode | Default | Entry rule (what enters the fix queue) | Phase 5 (edits) | Phase 6 (local 3-pass gate) | Phase 7 (CI ratification) | Phase 8 output |48|------|---------|----------------------------------------|-----------------|-----------------------------|---------------------------|----------------|49| `stabilize` | **yes** | `failure_rate ≥ 0.10` over ≥ 5 attempts, or `flake_count ≥ 2`. | Drafted, then double-gated before commit. | Required — 3 consecutive local passes per fixed test. | One push, one CI watch. | Stabilization report with before / after numbers, local-pass log, CI verdict. |50| `optimize` | | Top-N slowest tests by total time, or actions with `dur > 5×median`. | **Skipped.** | **Skipped.** | **Skipped.** | Recommendations-only report — humans apply the wins. |5152`stabilize` is the default because optimization edits (tightening timeouts, removing waits) carry flake risk that warrants human judgment.53`optimize` runs Phases 1–4 only and emits a ranked recommendations report.5455## Input5657`$ARGUMENTS` is parsed as `[mode] [pr-ref]` in any order:5859- `optimize` (literal token) selects optimize mode; anything else is treated as `pr-ref`.60- `pr-ref` is a PR URL (`https://github.com/<org>/<repo>/pull/13319`) or PR number (`13319`).61- If `pr-ref` is missing, auto-detect the open PR for the current branch (same path as [`/ci-auto-fix`](../../delivery/ci-auto-fix/SKILL.md) Step 0).62- If `mode` is missing, default to `stabilize`.6364Resolve mode + PR before doing anything else.65See [`rules/input-resolution.md`](./rules/input-resolution.md).6667---6869## Workflow7071Eight phases.72Do not skip a gate.73Phases 5, 6, and 7 are skipped in `optimize` mode (the `Modes` column says so explicitly).7475| Phase | Name | Modes | Rule file | Gate |76|-------|------|-------|-----------|------|77| 0 | Resolve target | both | [`rules/input-resolution.md`](./rules/input-resolution.md) | Mode + PR URL + branch + head SHA + owner / repo printed. |78| 1 | Pull historical telemetry | both | [`rules/telemetry-driven-analysis.md`](./rules/telemetry-driven-analysis.md) | Dash0 spans for this PR fetched and grouped by test name; failure recurrence + retry counts measured (stabilize) **or** action `dur` distribution measured (optimize). |79| 2 | Local reproduction + trace capture | both | [`rules/local-iteration.md`](./rules/local-iteration.md) | Each queued test run locally with `--trace=on`; trace.zip + (where available) fresh Dash0 spans tagged `ci.is_ci=false` captured. |80| 3 | Correlate spans ↔ traces | both | [`rules/root-cause-and-fix.md`](./rules/root-cause-and-fix.md) | Each queued test has a span-side signature **and** a trace-side hotspot. |81| 4 | Root-cause synthesis | both | [`rules/root-cause-and-fix.md`](./rules/root-cause-and-fix.md) | A single, evidence-anchored hypothesis per candidate, citing the span signature and the trace hotspot. Speculative hypotheses become `recommendation-only` entries, not fixes. |82| 5 | Draft fix + selector-existence check | **stabilize only** | [`rules/root-cause-and-fix.md`](./rules/root-cause-and-fix.md), [`rules/fix-validation.md`](./rules/fix-validation.md), [`rules/guard-rails.md`](./rules/guard-rails.md) | Diff drafted; every new locator proven to resolve against source (static grep) **or** the live app (`locator.count() ≥ 1`). A locator that fails both checks is hallucinated — discard the diff and re-enter Phase 4 with that evidence. |83| 6 | Local verification — 3 consecutive passes | **stabilize only** | [`rules/local-iteration.md`](./rules/local-iteration.md) | Fixed test runs locally ≥ 3 times with `--trace=on` and passes **3 times in a row**. A single failure or flake within the streak resets the counter. Maximum 10 attempts per test before escalating. |84| 7 | CI ratification — one push, one watch | **stabilize only** | [`rules/verification-loop.md`](./rules/verification-loop.md) | All passing fixes committed and pushed in a single push event; the resulting CI run is watched to conclusion and its telemetry compared against the Phase 1 baseline. |85| 8 | Report | both | [`templates/stabilization-report.md`](./templates/stabilization-report.md) | Stabilize: report with before / after numbers + local-pass log + CI verdict + residual risk. Optimize: recommendations-only report ranked by measured wall-clock impact. |8687Inner iteration in `stabilize` mode is local and bounded — see [`rules/local-iteration.md`](./rules/local-iteration.md).88The CI step in Phase 7 runs **once**.89If CI disagrees with the local result, that is a signal to escalate, not to re-enter the loop blindly.9091---9293## Required reading by phase9495Load on demand.96Do not preload.9798| Phase | Files |99|-------|-------|100| 0 | [`rules/input-resolution.md`](./rules/input-resolution.md) |101| 1 | [`rules/telemetry-driven-analysis.md`](./rules/telemetry-driven-analysis.md), [`references/dash0-mcp-filters.md`](./references/dash0-mcp-filters.md) |102| 2 | [`rules/local-iteration.md`](./rules/local-iteration.md) |103| 3–4 | [`rules/root-cause-and-fix.md`](./rules/root-cause-and-fix.md), [`rules/self-improvement-loop.md`](./rules/self-improvement-loop.md) (read lessons — stabilize only) |104| 5 | [`rules/root-cause-and-fix.md`](./rules/root-cause-and-fix.md), [`rules/fix-validation.md`](./rules/fix-validation.md), [`rules/guard-rails.md`](./rules/guard-rails.md) |105| 6 | [`rules/local-iteration.md`](./rules/local-iteration.md) |106| 7 | [`rules/verification-loop.md`](./rules/verification-loop.md), [`rules/self-improvement-loop.md`](./rules/self-improvement-loop.md) (write lessons on ratification — stabilize only) |107| 8 | [`templates/stabilization-report.md`](./templates/stabilization-report.md) |108109For trace mechanics (zip → JSONL → action timeline), defer to [`/playwright-trace-analyzer`](../../analysis/playwright-trace-analyzer/SKILL.md).110Do **not** re-implement.111112---113114## Core principles1151161. **Iterate locally, ratify on CI.**117 The inner loop is local because it is seconds-per-run, has the same trace artifacts, and emits the same OTel spans.118 CI runs once at the end as ground truth.1192. **Data first, hypothesis second.**120 Every fix is anchored to (a) a span with a measured failure rate, or (b) a trace action with a measured `dur`.121 "I think this is flaky" is not a finding.1223. **Two evidence layers, not one.**123 Spans tell you *which tests fail and how often across runs* (historical and local).124 Traces tell you *why one specific run failed*.125 A fix is only credible when both layers agree.1264. **Validate empirically, not predictively.**127 Confidence in a fix comes from running it, not from scoring it on paper.128 The two gates the skill enforces are both deterministic: selectors must exist (verifiable against source or live app), and the fixed test must pass three consecutive local runs (verifiable from exit codes).1295. **Selectors must exist before they are used.**130 Every new locator in a draft fix is verified against the component source (static) and, when ambiguous, the running app (live).131 The skill never commits a fix that names an element that does not exist.1326. **Three consecutive local passes or no commit.**133 A single passing run can be a coin flip on a flake.134 Three in a row — hardened with `--repeat-each=3` when the measured flake rate is low (see [`rules/local-iteration.md`](./rules/local-iteration.md)) — filters out most unfixed flakes before we spend a CI cycle.135 The streak is necessary but not sufficient evidence; Phase 7's CI ratification plus the telemetry comparison is the real confirmation.1367. **Never weaken the suite.**137 No `.skip`, `.fixme`, `waitForTimeout`, `continue-on-error`, `--no-verify`, or removed assertions.138 The full list lives in [`rules/guard-rails.md`](./rules/guard-rails.md).1398. **Test-side fix unless the trace proves otherwise.**140 Most flakes are selector, timing, or state-management bugs in tests.141 If the trace evidence points to product code, surface it to the user as a separate recommendation — do not silently mutate app code.1429. **One PR at a time.**143 Cross-PR refactors belong in a different skill.144145---146147## Self-Improvement148149`/e2e-pr-stabilizer` gets better across runs through a two-tier lessons loop150(fast episodic tier + gated promotion), like `autonomous-workflow` and151`fix-bug`. In **`stabilize` mode** it **reads** `e2e-pr-stabilizer-lessons` at152Phase 4 (biasing the P1–P6 pattern classification and the Phase 5 locator153strategy) and **writes** at Phase 7 — gated on the **telemetry ratification154signal**, not the local 3-pass streak, so only fixes that CI actually confirmed155`fixed` accrue a working lesson. The two tiers split naturally: the `global`156scope holds universal race-shape → fix-shape mappings, the `repo::{owner}/{repo}`157scope holds app-specific locator robustness. Lessons are **advisory** — they158never relax a guard-rail, an empirical gate, or the 3-consecutive-pass159requirement. A recurring lesson (`seen_count >= 3`) is promotion-eligible via160`/create-skill diagnose e2e-pr-stabilizer`.161162On the Phase 7 ratified verdict it also contributes a **flaky hotspot** to the163shared **`codebase-knowledge`** bucket (`hotspot::<test.file>` counter) — a fact no164other host produces, so every later code-changer plans around a known-unstable file.165Write only, merge-never-clobber, on the ratified SHA (contract:166[`../../../agents/shared/rules/codebase-knowledge.md`](../../../agents/shared/rules/codebase-knowledge.md); mechanics in the loop file's cross-bucket write section).167168`optimize` mode skips the loop (no fix, no ratification signal). Lessons run169through LoreKit's `memory.*` tools (the `lorekit-memory` skill); if LoreKit is170not connected the loop is a silent no-op. Full contract:171[`rules/self-improvement-loop.md`](./rules/self-improvement-loop.md).172173## Anti-patterns174175One-liners; the full list lives in [`rules/guard-rails.md`](./rules/guard-rails.md).176177- Pushing a fix because "the diff looks right" without three consecutive local passes.178- Trusting a single local pass as proof — flakes pass once routinely.179- Drafting a fix that uses `getByTestId('foo')` when nothing in the component source emits `data-testid="foo"`.180- Patching `waitForTimeout(1500)` to mask a race instead of fixing the wait condition.181- Marking a test `.fixme()` because it is "flaky" without a measured cause.182- Treating a single failed CI run as evidence — flakes are statistical, so fetch the span history.183- Re-running CI hoping for a green without applying a code change.184- Editing product code based on speculation when the trace points at a selector or test-state issue.185186---187188## Quickstart189190```text191/e2e-pr-stabilizer # stabilize, auto-detect PR192/e2e-pr-stabilizer 13319 # stabilize PR 13319193/e2e-pr-stabilizer https://github.com/<org>/<repo>/pull/13319 # stabilize via URL194/e2e-pr-stabilizer optimize # optimize, auto-detect PR195/e2e-pr-stabilizer optimize 13319 # optimize PR 13319196```197198Once invoked, the skill drives end-to-end:1992001. Resolves the mode and the PR.2012. Queries the Dash0 MCP for E2E spans filtered to this PR (`git.pull_request_link`) — historical baseline.2023. **Reproduces locally** with `--trace=on`, capturing trace.zip and (where the local OTel reporter is wired) fresh spans.2034. Correlates and produces an evidence-anchored finding set.2045. **stabilize:** drafts each fix, verifies every new locator resolves against source or the live app, then commits locally.2056. **stabilize:** runs the fixed test locally until it passes 3 times in a row (per fix).2067. **stabilize:** pushes once; watches the CI run; compares fresh telemetry to baseline.2078. Emits the report — stabilization (before / after + local-pass log + CI verdict) or optimization (recommendations).208209---210211## Definition of Done212213### Both modes214215- [ ] Mode (`stabilize` | `optimize`) and PR target resolved and printed.216- [ ] Historical telemetry pulled from the Dash0 MCP using the documented filter set, grouped by test name.217- [ ] Each queued test reproduced locally with `--trace=on`; trace artifacts captured per run.218- [ ] Each candidate has a span-side signature and a trace-side hotspot.219- [ ] Report written using the template, with the mode stated and findings ranked by measured impact.220221### `stabilize` only222223- [ ] Every new locator in every applied fix was verified against the component source or via a live `locator.count() ≥ 1` probe.224- [ ] Every applied fix passed 3 consecutive local runs with `--trace=on` and no failures or flakes within the streak.225- [ ] Fixes committed locally, pushed in one push, CI run watched to conclusion.226- [ ] Fresh telemetry pulled and compared to baseline — failures eliminated, retry counts reduced.227- [ ] No `.skip`, `.fixme`, `waitForTimeout`, or `continue-on-error` introduced (guard-rails check passed).228229### `optimize` only230231- [ ] No commits, no pushes, no edits to test files.232- [ ] Each recommendation cites an estimated wall-clock saving (ms) based on the trace evidence.