Triage a specific Positron e2e test that is already failing or flaking in CI. Given a test name, surface its recent distinct failure modes from history, pull evidence for one mode, and reason to a root cause collaboratively with the engineer, landing on a concrete test fix or a product-bug repro. Test-centric counterpart to e2e-failure-analyzer (run-centric). For authoring a brand-new test, use author-e2e-tests.
74
92%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Low
Low-risk findings worth noting
Start from a test name (not a CI run), find its recent distinct failure modes,
investigate one, falsify a root-cause mechanism, and land on fix-the-test vs.
file-a-bug with the action to match. This is an orchestrator: deterministic work
lives in scripts/, and detailed procedures live in references/ that you read
only when a stage needs them.
author-e2e-tests. For triaging a whole CI run, use e2e-failure-analyzer.Violating the letter of these rules is violating their spirit.
E2E_INSIGHTS_API_KEY set, or present in the repo-root .env.e2e (the query
script falls back to it automatically). Node.js and unzip on PATH.Run from the repo root. All emit compact JSON to stdout and write full
payloads to the per-triage work directory under the shared git common dir
(<git-common-dir>/triage-e2e-test/<id>/, so --resume works from any
worktree). They wrap the e2e-failure-analyzer scripts (no copies).
scripts/triage-history.js -- dual-branch history retrieval + merge. Resolves
the branch, queries the current branch and main, merges patterns by failure
text, computes counts/%/seen-on, classifies zero-run conditions, selects one
representative occurrence per pattern. Defaults to --occurrences-per-pattern 1.scripts/find-prior-triage.js -- filtered prior-triage lookup. Finds PRs whose
body names this spec, extracts diagnosis fields, resolves merge SHAs, and
partitions occurrence SHAs into before-fix / after-fix.scripts/fetch-pattern-evidence.js -- summary-first evidence for one
occurrence. Runs the S3 processor filtered to this test, stores full evidence
on disk, generates a compact summary.md.scripts/checkpoint.js -- durable state for start / resume / status. Setting
phase auto-derives nextAction, so a resume always shows the right next
step (pass --set nextAction=... only to override). Refuses phase=done
until an outcome is set and (for PR/issue outcomes) the diagnosis block is
recorded -- the mechanical guard against calling a triage done before the
block lands.scripts/record-diagnosis.js -- renders the ### E2E Triage Diagnosis block
from the checkpoint + history and appends it to the resolving PR (--pr) or
issue (--issue). Idempotent. Only writer of diagnosisBlockRecorded, so it
is what unblocks phase=done. Opening a PR via positron-pr-helper does NOT
record the block -- run this after. --secondary appends the block to a
second artifact without repointing outcomeRef (see "Split outcome")./triage-e2e-test "<test>" -- start. /triage-e2e-test --resume <triage-id> --
resume. /triage-e2e-test --status -- list saved triages.
On --resume: run node scripts/checkpoint.js --triage-id <id> --read,
validate it, and continue from phase / nextAction. Do not repeat
completed history or evidence work unless the engineer asks to refresh, the
saved data is invalid, or the branch/test identity changed.
On --status: node scripts/checkpoint.js --status.
Otherwise (new triage):
Resolve the exact test identity into a testName|||specPath key. If you only
have a partial name, read references/history-query.md.
Run the history helper:
node .claude/skills/triage-e2e-test/scripts/triage-history.js \
--test-key '<testName>|||<specPath>' --lookback-days 14Act on its verdict. stop: true (zero-runs-both, clean) or an error
field means stop and report -- read references/history-query.md
for what each verdict means. Otherwise continue.
Initialize a checkpoint and record the patterns:
node .claude/skills/triage-e2e-test/scripts/checkpoint.js --triage-id <id> \
--init --test-key '<key>'Check for prior triage before presenting the table:
node .claude/skills/triage-e2e-test/scripts/find-prior-triage.js \
--spec-path '<specPath>' --triage-id <id> \
--occurrence-shas '["<sha1>","<sha2>"]'A non-none verdict changes the plan -- read references/prior-triage.md.
open-attempt-in-flight means stop and point at the open PR.
Present the failure modes as a table (never a run-on sentence). Include a "Seen on" column whenever two branches were queried:
| # | Failure mode | Count | % | Environments | Seen on |
|---|---|---|---|---|---|
| A | toBeVisible() timeout: getByLabel('...') | 104 | 99% | ubuntu/electron | both |
| B | locator.click timeout: .monaco-list-row | 1 | 1% | win/electron | main only |
Ask which pattern to prioritize whenever the table has more than one row.
Give your own read ("A is dominant at 99% -- start there, or focus on B?")
but let the engineer decide; they may know a recent fix made the dominant
share stale, or already know which failure they care about. A single pattern
needs no choice. Save the selection to the checkpoint (--set selectedPattern=A --set phase=pattern-selected).
node .claude/skills/triage-e2e-test/scripts/fetch-pattern-evidence.js \
--report-url '<representativeOccurrence.report_url>' \
--triage-id <id> --pattern Aindex.html#?testId= fragment and filters the report
to this one test itself.)summary.md (failure, timeline tail, sibling tests,
error-shaped logs, unresolved questions). Read only the summary first.references/evidence-escalation.md.phase=evidence-gathered to the checkpoint.This is a collaborative dig, not a rubber-stamped verdict. Use the
e2e-failure-analyzer rubric (../e2e-failure-analyzer/rubric.md)
for the root-cause catalog and how to read each evidence type. Don't force the
failure into a "test-drift vs product-regression" binary -- shared-workspace
races, resource contention, and floated extension builds are none of those.
State: the observed mechanism (citing trace step / log line / screenshot); what the evidence rules in and out; alternatives ruled out; remaining uncertainty; and a fix that could plausibly change the failure rate (a fix that couldn't is not a fix -- keep digging).
Actively try to falsify your leading hypothesis, not just confirm it. When two mechanisms would both explain the symptom, grep the raw logs for evidence that separates them.
Delegate cross-file tracing to an Explore subagent only after the evidence
names a concrete symbol / selector / event / subsystem. Give it the specific
lead; require a compact response and cap it: a probable call chain (<=8
entries), <=5 files with exact line ranges, one mechanism summary, <=3 open
questions. It must not return full file contents, a repo tour, or speculation
unsupported by evidence.
Save the diagnosis to the checkpoint (--patch a diagnosis object) and set
phase=hypothesis-ready. Include the fields record-diagnosis.js renders
(confidence, summary, targetedFailure, signal, hypothesis, optional
supersedes) -- see references/diagnosis-block.md
for what each must contain.
Checkpoint the diagnosis, then /clear and --resume <id> before
implementing. History and evidence are durable on disk, so implementation
should start from a clean context carrying only the compact diagnosis --
don't drag the whole investigation into the fix, where cross-file edits, tests,
and verification runs will grow context fast. Set phase=awaiting-clear before
clearing; on resume, set phase=implementation.
Read references/reproduction.md at this stage.
In short:
author-vitest-tests (it owns the builder/stub
conventions and review-vitest-tests, but drives toward green -- the RED
bar is yours to hold: a valid RED reproduces the diagnosed mechanism inside
the assertion, not an import/compile/setup error. See reproduction.md).--repeat-each
loop is noisy) -- read a summary, don't stream full runs into context.Every triage ends by declaring an outcome and recording its diagnosis -- this
is not optional, and checkpoint.js refuses phase=done until it's satisfied.
The outcome spans two axes (what you found x what you did):
| Outcome | Meaning | Where the block goes | To reach done |
|---|---|---|---|
fix-test | test bug, fixed in a PR | the PR | record-diagnosis.js --pr <n> --outcome fix-test |
fix-product | product bug, fixed in a PR | the PR | record-diagnosis.js --pr <n> --outcome fix-product |
file-issue | product bug, filed not fixed | the new issue | record-diagnosis.js --issue <n> --outcome file-issue |
no-op | not fixed and not filed (accepted flake, dup, backlog, handed off) | checkpoint only | --set outcome=no-op --set outcomeReason="..." |
outcome is the primary artifact -- a secondary note (e.g. mentioning a
product race in the backlog while you fix the test) does not change it. A second
artifact is different: see the split-outcome rule below.
A returning sub-tool is not the end of the triage -- opening the PR via
positron-pr-helper or a passing author-vitest-tests run resolves a step.
Once the PR/issue exists:
record-diagnosis.js --triage-id <id> --pr <n> --outcome <fix-test|fix-product>
(or --issue <n> --outcome file-issue) appends the block and sets outcome +
outcomeRef + diagnosisBlockRecorded in one call. For a no-op, skip this
and checkpoint.js --set outcome=no-op --set outcomeReason="..." instead.checkpoint.js --set phase=done.Split outcome (two artifacts). When the root cause and a mitigation land
separately -- e.g. a product bug filed as an issue plus a fix PR -- the block
goes on both. Do step 1 for the primary (the artifact matching outcome),
then rerun record-diagnosis.js --pr <n> --secondary for the other: it appends
the block but won't repoint outcomeRef/outcome. outcome stays single.
test-health
endpoint and existing S3 reports.e2e-failure-analyzer's job.b87c504
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.