meticulous-increase-coverage
Increase coverage for a Meticulous project by tracing specific under-covered files back to a real UI action in the codebase, driving that action with a real recorded browser session, and validating the improvement with a clean coverage comparison. Also opens a PR proposing .meticulousignore entries for code that structurally never executes in-browser. Use when asked to "increase coverage", "find untested code", or "add .meticulousignore entries" for a project.
来源信息
- 仓库
- alwaysmeticulous/skills
- 最近来源活动
- 2026年8月24日 13:05
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 11
- 分支
- 3
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
文件资源管理器
2 个文件正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- name
- meticulous-increase-coverage
- description
- Increase coverage for a Meticulous project by tracing specific under-covered files back to a real UI action in the codebase, driving that action with a real recorded browser session, and validating the improvement with a clean coverage comparison. Also opens a PR proposing .meticulousignore entries for code that structurally never executes in-browser. Use when asked to "increase coverage", "find untested code", or "add .meticulousignore entries" for a project.
- user-invocable
- true
# Increase coverage for a Meticulous project
> Run `meticulous-cli-update` first if you haven't already this conversation
> (it also covers authentication and project selection).
## What you deliver
Two separate outputs, both expected — neither substitutes for the other:
1. **One or more test runs from newly recorded sessions that provably extend
coverage.** "Provably" means a comparison against the baseline naming the
files whose coverage went up, and by how much (Step 7). If a target turns
out not to be coverable, say so rather than padding the list.
2. **A PR proposing `.meticulousignore` changes.** Only include paths — most
often whole directories — that you are _confident_ are not coverable at
all. Anything you merely failed to reach in one sitting does not belong
there; leave it out and mention it in the PR description instead.
## Before you start: run this on `main`, with a clean tree
Every command below relies on the CLI resolving things from your local
checkout: `js-coverage` defaults to the current git HEAD, `trigger-test-run`
defaults to HEAD for the deployment and to the merge-base with the origin
default branch for the base. On `main` with a clean tree those collapse to a
single commit, which is exactly what you want — no diff, a head-only run, and
a union in Step 7 that the API will actually accept.
Off `main` this breaks in ways that are tedious to unpick: union coverage is
rejected unless every run executed the _exact same_ commit, and a PR's merge
commit is recomputed whenever its base branch moves, so a run triggered
earlier against a since-advanced base no longer unions with a new one.
So: check out `main`, pull, and make sure `git status` is clean before Step 1.
Then confirm there is actually a test run to work from:
```bash
meticulous agent test-run-for-commit
```
Keep the id it prints — Step 7 falls back to it. If it reports **"No test run
found for commit …"**, stop and report that to the user; you cannot baseline
without it. The most common cause is the CLI pointing at the wrong project, so
suggest they check with `meticulous auth get-project`. `meticulous auth
set-project` only applies for OAuth tokens; API tokens are bound to one
project (so `set-project` fails), and injected credentials leave no local
token to select — in those cases the fix is a different credential, not
`set-project`.
## Step 1 — Baseline coverage
```bash
meticulous agent js-coverage --includeAllFiles --includeCoveragePercentage \
> /tmp/baseline-coverage.tsv
```
If this reports **"No test run found for commit …"** (it shouldn't, if the
check above passed), stop and report to the user. Do not work around it by
baselining against some other commit's run: Step 7's union requires your new
run and the baseline to have executed the same commit.
The base run's sessions often haven't all been replayed yet, which understates
its coverage — if `js-coverage` says so, run `meticulous agent
complete-base-run` (it waits by default until nothing more can be scheduled;
it can take a while, so check back or re-run rather than assuming it hung),
then re-run `js-coverage`. Don't expect `unexecutedSessionCount` to always
reach `0` — some sessions can be permanently unobtainable, and `js-coverage`
tolerates a small share of those rather than refusing forever.
## Step 2 — Separate dead code from real targets
You are looking for two different things in this file, and it helps to keep
them apart:
- **`.meticulousignore` candidates** — files that are _uniformly_ at 0%
across a whole directory, which suggests they never execute in a browser at
all.
- **Coverage targets** — files a real user flow could reach but no recorded
session happens to. These are **not only the 0% files**. A file at 12% or
40% usually means one path through it runs and the rest doesn't, and those
partial files are often the cheapest wins: the module already loads, so a
single extra interaction can light up a large block. Sort ascending by
percentage and work up from the bottom, rather than stopping at 0%. Some
0%/low files are gated behind a feature toggle that's off by default rather
than a UI path nobody's driven — check the toggle registry and the file's
gating condition before assuming it needs a brand-new flow, since flipping
the toggle on locally can turn a dead-looking file into an easy target.
Start with the ignore candidates, since they shrink the list. Break the
0%-coverage files down by top-level directory, so you are reasoning about
groups rather than 100s of individual files. The exact command depends on how
the repo is laid out — a monorepo wants the first two path segments, a
single-app repo wants something deeper. For example, in a
`packages/<name>/…` monorepo:
```bash
# example only — adjust the segment depth to this repo's layout
awk -F'\t' 'NR>1 && $2=="0.0" {split($1,a,"/"); print a[1]"/"a[2]}' \
/tmp/baseline-coverage.tsv | sort | uniq -c | sort -rn
```
A directory where **every single file** is at 0% (not just some) is a strong
signal it never ships to the browser (backend, CLI tooling, docs, e2e test
harness). Those are your `.meticulousignore` candidates.
There is a second, stronger signal that doesn't depend on coverage data at
all and catches individual dead files scattered inside an otherwise-live
directory, which the uniformly-0% heuristic above misses entirely. For any
file sitting at 0%, grep the codebase for its actual exported symbol — not
just its filename:
```bash
# example only — adjust the source root and extensions to this repo
grep -rn "theActualExportedName" <src-root> --include="*.ts" --include="*.tsx"
```
If the only match is the file's own definition, nothing imports it, so no
session — however comprehensive — can ever execute it. That is proof of
unreachability, not an inference from silence, and it is worth checking even
when you already have a directory-level rule elsewhere in this file: dead
exports accumulate inside packages that are otherwise very much alive.
Some categories are **safe exclusions almost everywhere** and are worth
proposing without further tracing, provided the coverage data agrees they are
uniformly 0%:
- test files and their directories — `__tests__/`, `__mocks__/`, `*.test.*`,
`*.spec.*`
- Storybook — `*.stories.*`, `__stories__/`, `.storybook/`
- test/mock harness directories — `testing/`, `mocks/`, fixtures
- build, lint and codegen config executed only by Node — `*.config.*`,
`setupTests.*`, scripts directories
Note that some of these may already be outside the coverage report entirely;
check the baseline before adding a rule that does nothing.
Be suspicious of a directory showing 0% everywhere if you _know_ it is bundled
into the frontend (a shared component/utils library the main app imports).
That pattern is more likely a source-map/path-attribution gap than genuinely
dead code — leave it out of the ignore list and flag it as unresolved.
Now pick the coverage targets. Filter the generated/config noise out of the
real app package first, then order what's left by how little of it runs —
keeping the partially-covered files in, not just the 0% ones. The patterns are
repo-specific; inspect the actual paths in your baseline rather than copying
this verbatim:
```bash
# example only — derive the patterns from the paths this repo actually has
grep -v "/__tests__/\|\.test\.\|\.stories\.\|/testing/\|/mock" \
/tmp/baseline-coverage.tsv | sort -t$'\t' -k2 -g > /tmp/candidates.tsv
```
Group the candidates by feature area rather than picking the single worst
files: one recorded flow usually moves a whole cluster of related files at
once, so a directory sitting at 5-20% across a dozen files is a better target
than an isolated 0% file behind an obscure branch.
## Step 3 — Trace, don't guess
For each candidate file, find its actual caller(s) — for example:
```bash
# example only — adjust the source root and extensions to this repo
grep -rln "<ExportedThing" <src-root> --include="*.tsx" | grep -v test
```
Read the caller. Confirm:
- It's reachable via a simple, describable UI action (a specific button, a
specific menu item) — not buried behind a feature flag, a disabled config
(e.g. billing/SSO toggles that are off in this environment), or a
conditional branch that only fires for certain object types.
- If the target is a **hook**, check every branch that calls it. Hooks are
often called conditionally — one branch might route through a completely
different mechanism (a plain router `<Link>` instead of the app's own
navigation hook, a side-panel open instead of a full navigate). Confirm
which branch your candidate action actually hits.
## Step 4 — Drive the flow
**Any of these three drivers records correctly** — verified against two
different apps:
- **Claude in Chrome** — the only one that drives the user's own signed-in
Chrome profile, so an authenticated app needs no login flow. Its input path
is also the one that wedges (see below), so verify early and be ready to
switch.
- **Playwright** and **`agent-browser`** — CDP-launched browsers, faster and
more scriptable, and reliable in every test here. The trade-off is a fresh
profile each time, so you have to sign in. `agent-browser` additionally
refuses a click when the target is covered by another element, naming the
covering node, which catches a class of silent mis-click the other two will
happily perform.
**Set a realistic viewport** whichever you pick. The CDP browsers default to
something small (around 1280px wide) where a real Chrome window is often twice
that. Responsive layouts render different components at different widths, so
the default viewport can quietly cover different code — or hide the control
you were aiming for.
What matters far more than the choice of tool is the one rule below.
**Only trusted events are recorded.** The recorder ignores anything
synthesised in page JS, so `element.click()`, assigning `input.value`, or
dispatching your own events all drive the app convincingly and record
_nothing_. The page looks right, the session comes back empty. Drive
everything through the tool's real input actions.
When something doesn't take effect, find out which half is broken before
changing tactics — install a capturing probe and repeat the action:
```js
window.__ev = [];
["pointerdown", "click", "keydown", "change"].forEach((t) =>
window.addEventListener(
t,
(e) => window.__ev.push({ t, trusted: e.isTrusted }),
true,
),
);
```
- nothing captured → your input never reached the page; a different selector
or coordinate won't help (see the wedged-extension note below)
- captured but `trusted: false` → it reached the page but will not be
recorded; you are synthesising somewhere
### Three ways an action records as nothing
All three look like success in the browser, which is what makes them
expensive — you find out from the coverage numbers, long after the fact.
**Native `<select>`s.** Setting the value through a form-fill action, or
assigning it in JS, fires a `change` with `isTrusted: false` — the app reacts
and the value visibly updates, so it looks like it worked, but the recorder
ignores it and the sort/filter never happens on replay. Clicking the
`<select>` is no good either: that opens an OS-level popup the driver can't
see. What works is to focus the element and press the **first letter of the
option's visible text** (`"p"` → "Priority"), which yields a trusted `keydown`
_and_ a trusted `change`. Repeat the letter to cycle options sharing an
initial. Verify with a `change` listener reading `e.isTrusted` — the value
updates either way, so the value alone tells you nothing.
**Modifier shortcuts.** Replay reproduces a modifier only if a _discrete_
modifier keydown was recorded and is still held. Some drivers send one only
for the base key: Claude in Chrome's `cmd+k` and `agent-browser`'s
`press Alt+ArrowRight` both record a single keydown with the modifier flag set
and no separate modifier press, so on replay the flag is cleared and the
handler body never runs — while working perfectly live. Playwright's
`press('Alt+ArrowRight')` does record the discrete press and replays correctly
(verified by coverage).
So: prefer the equivalent _click_ target where one exists, and if you must use
a chord, drive it with Playwright and confirm from coverage afterwards. If the
line holding `if (… && event.metaKey)` is covered but the body is not, the
keydown was delivered and the condition evaluated false — that is this.
**Double-clicks.** A double-click may be recorded as a single click, in which
case the replay never fires `onDoubleClick` and every later event in that
session targets UI that never opened — so coverage _drops_. Check the recorded
event count looks like two press/release pairs, and treat any
double-click-only feature as suspect until coverage confirms it.
### Some interactions may not survive agent-driven recording at all
Occasionally an interaction records cleanly and visibly works live, but the
handler it's meant to trigger never fires on replay — with no error, and the
affected file sitting at _exactly_ its baseline percentage in Step 7's union.
One case seen: typing a value into an input inside a dropdown's own
portal-rendered content (a filter chip, a view rename behind a "..." menu).
Plain clicks in the same portal, and the identical type-then-Enter sequence
on an input in the main page tree, both replay fine — so this is specific to
keyboard/text input inside a portal, not a driver issue.
Don't assume a typed-value target worked just because the live interaction
did — check Step 7's diff. If a target only reproduces through this kind of
interaction and won't move, that's a shortcoming of agent-driven recording
for this flow: report it as unresolved and suggest a human drive that one
flow manually, rather than continuing to pad the list with retries.
### `claude-in-chrome` specifics
- **Screenshots are downscaled** (~0.6x), so they are not CSS pixels. Click
coordinates read off the screenshot, or scale a `getBoundingClientRect()`
centre by `screenshotWidth / window.innerWidth`. Re-derive after any resize,
and re-screenshot rather than reusing coordinates from an earlier page —
layout shifts, and a stale coordinate can land on the wrong element and
record an interaction you did not intend.
- **Re-acquire a `ref` immediately before clicking it**, and never reuse one
across pages or tabs. Don't predict a number — `read_page` does not emit
them in order. Refs and coordinates are equally reliable; pick whichever is
convenient. This is not unique to `claude-in-chrome` — `agent-browser`'s
`snapshot` refs go stale the same way, and reusing one silently clicks
whatever now occupies that ref, not what you intended. It cost a real
mistake once: a stale ref landed on a table's "Add New" affordance and
created a blank record. Take a fresh snapshot immediately before every
click when the DOM might have changed, and treat an unexpected
page/record-count change right after a click as a sign a ref just misfired,
not as an unrelated bug — clean up whatever it created before continuing.
- **Input delivery wedges intermittently.** Every click and keystroke reports
success, reads keep working, and nothing reaches the page. Refs and
coordinates die together, so this is never a selector problem — and it is
not cleared by a new tab, a fresh navigation, waiting, or retrying. A full
Chrome restart helps but does not durably fix it. **Check the probe after
your first interaction, before driving a whole flow**, and if input isn't
landing switch to Playwright or `agent-browser` rather than switching
selector method. Both stayed reliable throughout, including on the same
page at the same moment that Claude in Chrome was dead.
### Drive it, then verify
1. Navigate to the target URL and drive the real action.
2. Confirm it worked against the DOM (e.g.
`document.body.innerText.includes(...)`), not just a screenshot — a
tooltip appearing can look like success. Check you are still on the page
you think you are: an app that has quietly redirected you to a login screen
will absorb blind coordinate clicks into empty space and hand you a session
with zero events.
3. **Close the tab.**
Then sanity-check the recording, before you trigger anything. Wait ~10s after
closing the tab, so the session is complete, and check that it captured
something:
```bash
meticulous agent sessions --limit 10 --excludeSyntheticSessions \
--includeDurationSeconds --includeNumberUserEvents \
--includeNumberUrlsVisited --includeStartUrl --includeAbandonedReason
```
Read the row you just produced:
- **no row at all** — nothing was uploaded yet; wait a little longer before
concluding the recording failed
- **`numberUserEvents` of 0** — the recorder saw no user input. Your clicks
were not reaching the page, or were synthesised rather than trusted; go back
to the input-delivery probe above. Replaying this session is pointless.
- **`numberUrlsVisited` of 1 when you navigated several times** — the later
pages did not make it into _this_ session. They either landed in their own
sessions (fine, collect those ids too) or were swallowed as an unreplayable
tail (see the hard-navigation note in Step 5).
- **`durationSeconds` over 300** — everything past the 5-minute mark will be
silently trimmed on replay (Step 5). Re-record the overflowing part as its
own session rather than hoping it survives.
- **populated `abandonedReason`** — the recorder gave up on the session (see
the 10-minute cap in Step 5); it is not worth replaying.
- **`startUrl` that is not the page you drove** — the navigation you cared
about belongs to a different session than you assumed.
**A session is only complete once its tab is closed.** While the tab is open
the row reflects only the chunks uploaded so far, so a low or zero
`numberUserEvents` there means "not flushed yet", **not** "the recording
failed". A session measured for this skill read **0** events with the tab open
and **12** once it was closed. Judging it early nearly caused a perfectly good
recording to be re-driven from scratch.
Events upload on a short interval (a few seconds), but anything still
unflushed at unload is only stashed in `sessionStorage` and re-sent on a
_later page load to the same origin_ — so a session's tail can be delayed
until the next visit. Prefer navigating away over hard-closing the browser,
and never judge a recording immediately.
**Recording several targets in one sitting? Run this check after _each_ one**,
not just once at the end. Checking only at the end makes it impossible to
tell which action lost a session, and you'll have to re-drive all of them
just to find out which one needs redoing.
## Step 5 — Session-time budget and close discipline
- **Cloud replays cap at 5 minutes of session time.** Everything recorded
after that is silently trimmed from the replay — the run still succeeds and
reports itself accurate, so the loss is invisible unless you look for it
(snapshot routes stop early; far fewer allowed events than the session has
clicks). Don't leave this to feel: `agent sessions
--includeDurationSeconds` gives you the number, and anything over 300 is
losing its tail.
- **Your interaction pace eats this budget.** Each find/click/verify round
trip is 10-60s of _recorded_ session time. Plan the flow completely before
opening the tab, batch your actions into as few round trips as possible
with short waits between them, verify from the recording afterwards rather
than mid-flow, and aim to stay under 4 minutes. Several short sessions beat
one long sweep.
- **Direct URL navigation is a legitimate fast path** to each target page
and replays fine — prefer it over slow click-paths. But hard navigations
split the recording into multiple sessions, so collect every resulting
session id afterwards and pass them all to `--sessionIds` (Step 6). Mostly
this is fine, each piece staying under the replay cap. The trap is
navigating again too quickly: a page reached a second or two after the
previous one gets appended as the _tail_ of that session instead of
starting its own, and tails frequently do not replay — so the page renders
perfectly while you drive it and still contributes no coverage. Give each
page you actually care about its own dwell time (~10s) before moving on,
and check in Step 6 that it shows up as a `startUrl` in its own right. The
same caution applies to a plain `<form>` submit with no wired `onSubmit`
handler — it triggers a real browser reload rather than an SPA transition,
and can just as easily drop the just-recorded, unflushed session if you
navigate on immediately afterward.
- **Recorder limits:** a 10-minute hard cap on tab-open time marks the whole
session "abandoned"; uploads flush on a 5s interval — wait ~6-8s after the
last interaction before closing the tab.
## Step 6 — Collect the session ids and trigger the test run
First list what you actually recorded, newest first:
```bash
meticulous agent sessions --limit 20 --excludeSyntheticSessions \
--includeDurationSeconds --includeNumberUserEvents \
--includeNumberUrlsVisited --includeStartUrl
```
Skip any row with `numberUserEvents` of 0 — it will replay as nothing and
only dilutes the run. Note any row with `durationSeconds` over 300: it will
replay, but only its first five minutes, so treat coverage from its tail as
absent rather than assuming the whole flow ran.
Identify your sessions by recorded-at time and `startUrl`. Be careful here:
other people — and other apps pointed at the same project — record too, so
never assume the newest N rows are yours. `--recordedSince` and
`--visitedUrlFilter` are the quickest way to narrow it down when the list is
busy.
Expect **more sessions than pages you drove**: a hard navigation usually ends
one session and starts another, so a five-page sweep can produce five ids.
Collect all of them. A page whose URL never shows up as a `startUrl` was
probably swallowed as the tail of the previous session and will not replay —
re-record it on its own if you need it covered.
Then trigger:
```bash
meticulous agent trigger-test-run --sessionIds "<id1>,<id2>,..."
```
## Step 7 — Compare with a union, not a raw diff
```bash
meticulous agent js-coverage --headPlusTestRunIds "<newRunId>" \
--includeAllFiles --includeCoveragePercentage > /tmp/combined-coverage.tsv
```
This unions your new run into the baseline run resolved from HEAD — the same
run Step 1 used — so it is baseline coverage **plus** your new sessions'
coverage. Commit resolution skips runs over an explicit session set, so the
run you just triggered won't be picked as the baseline. The union is needed
because `--sessionIds` _replaced_ the selected set for that run rather than
adding to it, so your run on its own covers far less than the baseline and a
raw diff would read as mass regressions. Diff the union against
`/tmp/baseline-coverage.tsv`: you should see zero regressions, and only the
files your new flow touched improve.
Pass only your new run — a run cannot be unioned with itself.
Two signs HEAD resolved to something other than the golden-set run: it is
**rejected** with "is the run being queried", or _every_ file has dropped
(which means you unioned into another narrow session set, not a regression).
The most likely cause is a pinned-session run predating the skip. Either way,
name both sides explicitly, using the baseline id from Step 0:
```bash
meticulous agent js-coverage --testRunIds "<baselineRunId>,<newRunId>" \
--includeAllFiles --includeCoveragePercentage > /tmp/combined-coverage.tsv
```
If the union is rejected because the runs executed different commits, that is
the `main`/clean-tree precondition biting — see the top of this skill.
## Step 8 — Verify and report
For each traced target file: did coverage move? For each presumed-dead file
(the `.meticulousignore` candidates from Step 2): did it stay at 0% in the
union comparison (confirming it's genuinely unreachable)?
If a well-traced target didn't move, don't assume the recording failed —
re-check the session first (Step 4: does it exist, did it capture user events
and URL visits, is it abandoned, is its `startUrl` the page you drove?), then
re-check whether the click actually goes through the file you expected
在 GitHub 查看这个 SKILL.md 很大,SkillsMP 这里只预览前一段内容。 在 GitHub 查看