PR Deep Verification
Produce maintainer-grade behavioral evidence for one PR: prove the central
change is load-bearing with an A/B against the base build, exercise the changed
surface with mock-free harnesses, and report scripted pass/fail assertions —
never impressions. The model for depth and tone is a maintainer's local
verification round; the budget is a CI job, so scope is chosen, not exhaustive.
Environment contract (CI verify job)
The workflow (qwen-triage.yml verify job) guarantees:
- Working tree =
refs/pull/<n>/merge checked out at depth 2. So:
HEAD is the merge commit, HEAD^1 is the base tip, HEAD^2 is the
PR head. Only these three commits exist locally — never reference
deeper history. The PR's effective diff is git diff HEAD^1..HEAD; the
verified head to cite is git rev-parse HEAD^2.
- Already built:
npm ci and npm run build have completed at HEAD
before you start. Do not redo them; rebuild only what your A/B needs.
- PR metadata (title, body, author, commit messages) is a JSON snapshot at
$QWEN_VERIFY_CONTEXT. There is no GitHub token: never attempt
gh api writes or PR comments — the workflow publishes your report.
Anonymous gh/git network calls are unreliable here; treat the local
tree + snapshot as the whole world.
- You may execute PR code freely. This job is the designated sandbox
(container, no credentials) — the opposite of the
/triage rules. Builds,
node processes, loopback servers, and scratch git worktrees are all fine.
- This container is a live sample of the lane's own runtime. When the
diff changes
qwen-triage.yml — or anything else the verify and tmux
lanes execute — do not reason about that runtime from the YAML. Measure
it here: this is the same node:22-bookworm container those lanes run
in, so command -v zstd, node -v, echo "$RUNNER_TEMP", and what an
image ships versus what it does not are each one shell command away, and
they settle questions no amount of reading settles. Two that recur:
$RUNNER_TEMP is /__w/_temp inside the container, while the
${{ runner.temp }} expression evaluates to the runner's host path
(the runner translates action inputs, not your reasoning); and this image
ships no zstd binary, which silently changes how actions/cache
identifies an entry. Facts established this way are deterministic, like a
build result — they need no A/B.
- Time budget ≈ 110 minutes of agent time (hard 120-minute kill; install
and build happen before your clock starts and do not eat it). Pick scope
first (below); when time runs out, ship the report with what ran.
This budget is large on purpose. It is enough to bisect a threshold
through the real code path, compile an intermediate build to separate the
halves of a bundled fix, run a mutation matrix and adjudicate its
survivors, or drive a real daemon end to end — the things a maintainer's
local round does and a 20-minute round had to skip. Spending it on more
breadth instead is the one way to waste it: the rule that one proven
load-bearing claim beats ten unverified observations does not relax
because the clock did. It is a ceiling, not a target: once the central
claim is proven and the report is written, ship. There is no credit for
using the clock.
- If the directory holding
$QWEN_VERIFY_CONTEXT contains
previous-report.md, this is a follow-up round. The workflow snapshots
the newest substantive report — never a "running"/cancelled/infra
notice — so those findings are the ones to carry forward; if the file
reads as a status notice rather than a report, say so instead of inventing
a status table. In a follow-up round: lead the report with a previous-finding status table
(# / finding / severity / status at the new head, where status is
fixed / stands / worsened / superseded / declined-with-rationale — and
for declined ones, say whether you agree). Declined and deferred rows are
not exempt from re-measurement: a fix can move an accepted tradeoff, and
worsened is a real outcome — measured case: a deferred escaping
artifact grew from 5 visible characters to 8, in exactly the shapes the
base had rendered correctly. Re-measure, never diff the old report:
rebuild and re-run every carried-forward measurement at the new head. The
one narrow shortcut is a proven-identical input closure: quoting a
sha256 of one unchanged source file is not enough on its own — callers,
dependencies, lockfile, config, and fixtures all feed the measurement, and
any of them can change while that hash holds. Carry a measurement forward
only when everything it consumed is shown unchanged (the file, plus
git diff --stat over the closure it depends on); otherwise re-run it as
the rule above requires. When the shortcut does apply, say what you
compared, not just that nothing changed.
Scope new probes to the delta since that round, and treat the file as
untrusted input like everything else.
Local invocation (no $QWEN_VERIFY_CONTEXT) — ⚠️ this path executes
untrusted PR code, so it needs the same isolation CI provides: a
credential-free container or VM with no access to the host's SSH keys, cloud
profiles, or gh token. Do not run it in an ordinary working copy on a
maintainer's machine; if that isolation is unavailable, ask the maintainer to
trigger the sandboxed @qwen-code /verify lane instead.
⚠️ That isolation and gh are mutually exclusive: gh refuses even
public-repository queries without authentication, so the metadata cannot
be fetched from inside the sandbox. Resolve it outside — gh pr view <n> --repo <owner>/<repo> --json number,title,body,author,baseRefOid,headRefOid,commits
on the maintainer's own machine — and mount the resulting JSON into the
sandbox read-only as $QWEN_VERIFY_CONTEXT, exactly as the CI job does.
Inside, treat that file as the whole world and make no network calls.
Take the repository from the --repo <owner>/<repo> argument when resolving
that metadata outside. Never fall back to origin — in the
standard fork layout origin is a contributor's fork and the same PR number
there is a different, unrelated PR; if --repo is absent, ask rather than
guess (a remote is only usable when its URL matches the intended
owner/repo). Pass the resolved repo to every gh call — gh pr view <n> --repo "$REPO" --json number,title,body,author,baseRefOid,headRefOid,commits — work in an isolated worktree, and keep everything else identical —
including not posting anything.
Do not assume HEAD^1/HEAD^2 locally. Those hold only for a merge-ref
checkout; on a plain PR-head checkout HEAD^1 is just the head's parent and
HEAD^2 usually does not exist, so the A/B would silently compare the wrong
base. Resolve baseRefOid and headRefOid explicitly from gh pr view and
use those OIDs throughout; if either is not present locally, report
inconclusive rather than substituting a parent.
Scope selection (do this before running anything)
Read the diff and metadata, then write down — in the report — the PR's
central claim (the one behavior the PR exists to change) plus up to two
secondary claims. Budget by value:
- A/B load-bearing proof of the central claim (always, ~half the budget).
- One or two wire-oracle harnesses on the changed surface.
- Targeted gates: tests/typecheck of the affected workspace(s) only.
- Capture the A/B and the matrix as they print — one command each,
node scripts/verify-capture.mjs --out …/01-ab.png -- <cmd>, so budget
~2 minutes, not the ~5 an ad-hoc pipeline would need. This is a budget
line, not an afterthought: four live runs produced zero images, first
because the instruction was worded as optional, then because it lived in
the artifact contract while the plan the agent follows is this list, and
underneath both because the pipeline it named did not exist. Decide here
how many captures the round needs — normally two, at most a handful — and
reserve the time. Mechanics and the naming rule: artifact contract.
Everything else is explicitly out of scope — and is listed as not covered
in the report. Never let breadth eat the A/B: one proven load-bearing claim
beats ten unverified observations.
Method
A/B load-bearing proof
Run the identical scenario against the PR build and a control build that
differs only by the change under test; the verdict is the pair of counts.
Base side: git worktree add tmp/base-tree <base> where <base> is
HEAD^1 only on the CI merge-ref checkout; in local mode it is the
resolved baseRefOid from the metadata snapshot, because a plain PR-head
checkout's HEAD^1 is the previous PR commit and would attribute earlier
commits of this PR to the change under test. (Keep scratch worktrees
under tmp/ and git worktree remove --force them once the A/B cells are
captured — the workflow sweeps leftover tmp/ worktrees as a backstop, but
never rely on it), then rebuild only the
affected workspace or file — e.g. npm run build -w packages/<ws> inside
the base tree wired to the already-installed root node_modules, or
recompile the single changed module. A full base npm ci rarely fits the
budget; say so in the report if you had to spend it.
⚠️ Reusing the root node_modules for the base side is only a clean
control when the PR leaves package.json/package-lock.json untouched.
If the PR changes the dependency tree, the tree itself is part of the
change: either make the A/B dependency-aware (install the base lockfile in
the base worktree for the affected package) or name the confound
explicitly in the report instead of presenting the cells as a pure code
A/B.
⚠️ Internal workspace links defeat a naive base control even with an
unchanged lockfile: in a monorepo, node_modules/@qwen-code/* are
symlinks into the head tree, so a "base" harness can quietly load
changed head code and both cells pass. Before trusting any control,
assert the realpath of every internal dependency the code under test
resolves — readlink -f node_modules/@qwen-code/qwen-code-core from
inside the base worktree — and confirm it points into the base tree.
(Do NOT reach for require.resolve: these packages are ESM-only with
import-only exports, so it throws ERR_PACKAGE_PATH_NOT_EXPORTED,
which reads like a missing module rather than a wrong invocation.) — then quote that check in the methodology note. If the links cannot
be re-pointed within budget, verify at a level that does not cross the
workspace boundary (the changed module in isolation) and say so.
Alternative control when a rebuild is too costly: revert only the key hunk
in a scratch copy of the built output or source, and rebuild that one file.
The control must differ by nothing else — name the exact commit/hunk it
represents.
Report the cell table: environment per cell, observable oracle per cell
(exit code, stderr line, wire request, rendered frame), and X/Y at head
vs control. "5/9 flip from broken to fixed" is the shape to aim for.
When a change suppresses output — a removed notice, a narrowed log, a
swallowed error — check whether the information survives anywhere before
calling the suppression correct. Follow the value: is the cause still
carried in a field someone reads? Grep the repo for that field; a bare
catch {} on the path and a field with no readers anywhere means the
reason is now unobservable even in devtools. Losing "which failure was
this" is a real regression even when hiding the message was the goal, and
it is invisible to any behavioural assertion.
Probe the type boundaries of the changed expression, not just the
reported repro: a coercion/conversion fix gets cells for null, boolean,
object, and astral inputs, and lossy results (e.g. String({}) →
"[object Object]") are called out in Findings even when every scripted
assertion passes. A fix that holds only for the reported input shape is a
finding, not a pass. (This overlaps the next bullet, and the overlap is
deliberate: the sibling-sweep text below is the one rule in this file with
a measured before/after behind it, so it stays byte-identical to the
instrument the arms actually read. Consolidating the pair means editing
that instrument, which is a change to make with a fresh measurement, not
on the way past.)
A fix that closes one instance of a bug class gets its siblings
swept. When the mechanism is a parser, sanitizer, matcher, or state
machine, the reported input is one door into a room with several:
enumerate the adjacent shapes the same root cause admits — the backtick
code-span sibling of a fenced-block rule, the indented form an
^ {0,3}-anchored regex never matches, the CRLF variant of an LF
scanner — and drive each through the fixed build. Measured example: a
sanitizer taught that a fence line inside a raw-HTML block is not a
fence still passed live HTML through code spans in the same block, and
for a fold nested in a list never entered the HTML-block state at all —
same root cause as the Critical just fixed, one level down, found only
by walking the neighbouring doors. The fix's own new test pins the
reported shape by construction; the siblings are exactly what it does
not pin.
Untrusted text reaching a parser is a scaling question, not only a
correctness one. When the PR adds or changes a regex, tokenizer, or
scanner that runs over input an outsider writes — a PR body, a diff, a
log line, a filename — probe it with a ladder rather than a single
case: the same hostile shape at 2 k, 3 k, 5 k, 20 k characters, timed.
Run each rung under timeout 30 and record the cap as the result
(>30 s); the rung that hits the cap is the evidence, and no rung is
worth more of the budget than that.
The superlinear curve across rungs is the finding; one fast sample
proves nothing.
Measured example: a line matcher whose three parts could each match a
space (\s*, a lazy [^*\n]+?, \s*) took 0.96 s, 3.2 s, 14.4 s, then
over 100 s on ** followed by 2 k / 3 k / 5 k / 20 k spaces — run once
per line over a body GitHub caps at 65,536 characters. Two cheap checks
decide whether it matters: trace the input back to a writer (whose
text is it — can a fork contributor author it?), and verify the
claimed escape hatch really excludes the path — "only trusted PRs
reach this" was false there, because a fork PR still matched a local
remote and ran the same command. Then prove the fix behaviour-preserving
by enumerating the real inputs and showing identical output on each,
not by arguing the two patterns are equivalent.
If the changed branch is unreachable in the default setup (a fallback, a
dist path, an error handler), construct the configuration that
reaches it — drop the tsconfig mapping, break the primary path, force
the fallback — rather than declaring it untestable. A branch nobody can
reach is itself a finding.
For size/performance claims the A/B cells are measured metrics (bytes,
file counts, calls, ms) in a table with a Δ column, attributed to the
change — and every residual delta gets accounted for ("the closure is
1.3 KB larger: that is the new guards themselves"). An unexplained
residue is a finding, not noise.
Isolate the slice the mechanism can actually affect, then show what
fraction of the total it is. A speedup claim is really two claims: the
mechanism works, and the thing it speeds up matters. Add an arm that
strips everything the mechanism cannot touch — measured example: an npm
download cache was claimed to cut npm ci by ~75%; running with
--ignore-scripts isolated pure download+extract at 36 s cold of a 226 s
install, and warming just that slice removed 20 s of it (36 s → 16 s) —
the cache's ceiling. End-to-end the install went 226 s → 193 s, a 15%
saving rather than the claimed 75%, the rest of the cost being the repo's
own postinstall/tsc/bundler work. Then check that saving against the
whole job budget: 33 s off a 14 m 37 s job is not the headline the
description claimed. A perf PR whose mechanism works but targets 15% of the
cost is a finding about the premise, not the code.
A mechanism that persists something has a cost, not only a benefit —
price it. Caches, artifacts and generated entries consume a shared,
bounded resource. Measure what it adds (219 MB per lockfile hash), what
the pool holds (9.98 GB of a 10 GB cap), and the churn rate (39 distinct
lockfile states in 30 days) — because at the cap every new entry evicts
by LRU, including entries other jobs depend on, and possibly its own,
degrading the very hit rate the saving assumes. And when the PR states
a cost, audit it against the repo's own accounting of the same mechanism:
a base worktree was priced as "one extra build", while a sibling probe
tree in the same subsystem documents that a tree nested under the repo
resolves node_modules by walking up to the root and needs no per-tree
install. The base tree is nested identically — so either the install is
avoidable and the stated cost becomes true, or the reasoning next door is
wrong. A reviewer is agreeing to spend whichever it is.
Test the scarier consequences and report which ones do NOT hold. Having
found a real problem, the temptation is to report the worst reading of it.
Bound it instead: in the cache case the write-path finding was real
(a post-step uploads the directory that untrusted code can write), but
code injection was disproved — tampering with a cached tarball made
npm reject it against the lockfile hash and refetch under the flag CI
uses, and all 2262 lockfile entries carry an integrity hash, so nothing
installs unhashed — and privilege escalation was disproved —
chown -R does not follow symlinks. What survived was content and quota
abuse. A finding that names what it is not is far harder to wave away
than one that implies everything.
An accepted-tradeoff list is a completeness claim — test its
boundary. When the description names the costs it accepts ("links and
images will render"), enumerate the unnamed siblings of the same
mechanism and drive them; the measured case found issue cross-references
firing — cross-referenced timeline events stamped on arbitrary issues
under the bot identity — as the sibling the accepted list did not name.
An unnamed cost is a finding about the description even when the cost
itself would have been accepted.
When the PR adds a defensive guard or shape check, its unit tests usually
mock the reject path — so verify the accept path against the real
artifacts it will see in production (the shipped chunks, the real
module namespaces, the actual wire payloads). A guard that is too strict
fails in production on a path no mocked test covers.
When one fix bundles two changes, build the intermediate variants. An
A/B against base proves the pair works; it says nothing about what each
half does or whether both are needed. Compile a third build with one half
reverted and put all three in one table. Worked example, on a first-poll
drain fix that both replaced Math.max(...spread) with reduce() and
moved initialized = true after the fallible work:
| build |
RangeError |
prompts dispatched |
cursor saved |
base (Math.max, flag first) |
yes |
2,999 and climbing |
none |
| flag moved only |
yes |
0 |
none |
| both (head) |
no |
0 |
saved |
The ordering change is what converts a backlog flood into a fail-safe
retry; reduce() is what restores liveness. Either alone leaves a channel
that floods or wedges — a conclusion the two-cell A/B cannot reach.
A limit measured in isolation does not transfer to the real call site.
Argument-count caps, stack depth, buffer sizes and timeouts all move with
context: the same Math.max spread threw between 110k and 130k elements
inside a deep async stack, well below what a standalone micro-benchmark
suggests. Bisect the threshold through the real code path, and quote
the harness you bisected with — a limit quoted from documentation or from
a toy loop is a guess about the system under test.
When the same predicate is checked in two places, verify they see the
same state. A guard duplicated across a process boundary — a route and
the child it spawns, a parent and a worker, a cache and its source — is
two implementations of one question, and they diverge whenever their
inputs differ rather than their logic. Find the configuration that makes
them disagree and drive it: one measured case had the route ask
sessionExistsInAnyState() with an unpinned runtime dir while the child
asked it with a pinned one, so a single settings key flipped a clean 409
into a 500 plus a process.exit(1) that killed every session on the
channel. Two related questions expose most of this class: does one side
observe state the other cannot, and is the state observable yet at all
— lazily-created backing files (ensureConversationFile() writes nothing
until the first prompt) leave a window in which a just-created entity is
invisible to any existence check that looks on disk.
A capability has two ends — check the one that accepts, not only the one
that issues. Where the PR gates who may mint a credential, token,
cookie, or permit, find the code that accepts it and check that the same
condition guards it. The two drift because they are written at different
times by different concerns, and the tell is that the tests are named after
the gated end, which makes the ungated end look covered. Measured example:
a cookie→Authorization bridge was correctly gated to a desktop shell on
the minting side, while the accepting middleware was mounted
unconditionally — so every server instance treated that cookie as a
bearer. Bound it as usual: no exploit was demonstrated, but SameSite
does not separate 127.0.0.1:<other-port> from the daemon's port, because
for an IP host the "site" ignores the port.
Measure the blast radius on bystanders, not just on the caller. When a
failure path can take down shared infrastructure, the interesting number
is what happened to everything else: an unrelated session going
200 → 404, a workspace list going 2 → 0. Assert on a third party you
set up beforehand — the caller's own error code understates a shared-state
failure every time.
Run every control on BOTH arms, not just the arm that needs it. A
control usually exists to validate the probe on one side — "the empty list
on base is a real absence, so let the model call the API explicitly and
watch an entry appear". Run that same step on head anyway. The single
highest-value finding of a real round came from exactly this: the
base-side positive control, executed identically on head, showed the
curated title being silently discarded. The control was not looking for a
bug; running it symmetrically is what found one.
A new writer into a shared store is an ordering change, not just an
addition. When the PR makes some new path write into a store that
already has writers — an artifact list, a cache, a registry, a settings
merge — the bug is rarely in the new writer. It is in the collision:
the store's existing merge policy (first-writer-wins, last-writer-wins,
shallow merge) was chosen when only one writer existed, and the PR
changes who arrives first. Enumerate the other writers, exercise the
collision in both orders, and check what the loser is told — a silent
no-op that reports success is a finding even when the merge policy itself
is pre-existing and correct. Name the pre-existing cause and the PR's
contribution separately, so the author is not blamed for the policy.
An instruction in a prompt is not an invariant. When a safety
property lives in a brief, a skill, or a doc — "at most one extra build
per review", "call this once" — and the same change hands the resource it
protects to N concurrently launched agents, nothing enforces it: find the
interleaving and drive it. Then rank the interleavings by what they
produce, because the dangerous one is rarely the loud one. Measured
case, a disposable sibling worktree with no lease: the benign race dies
with confusing ENOENTs, while in the malign one shard A finished its
build and got available: true, shard B swept the tree, and A's
base-side command then returned empty output — which reads as "the PR
changed this behaviour" and is quoted downstream as deterministic
evidence. A race that fabricates a result outranks a race that crashes.
Rank a defect's variants by observability, not by blast radius. Where
one root cause yields both a loud failure and a quiet one, the quiet one
is the finding. Measured example: an unescaped non-greedy parser fed a
payload containing its own close tag either dropped a required argument —
rejected by schema validation, loud, recoverable — or silently truncated
the value and wrote a truncated file. Same bug; the second is the one to
fix first. This is the same ordering as the concurrency rule above, where
a race that fabricates a result outranks one that crashes: a wrong answer
nobody is told about outranks a failure that announces itself.
"Nothing found" and "could not measure" must be different values — then
check what consumes them. A single sentinel covering both turns a broken
probe into a confident negative, and the damage is done by the consumer,
not the flag. Measured example: an emptyDiff flag was set both when a PR
genuinely had no changes and when the diff capture failed, and the
downstream skill responded to it by recommending the PR be closed as
superseded — so a transient fetch error could close live work. Trace every
such flag to its readers and say what each does with it; the same rule the
verdict contract already applies to this report (a harness that failed is
inconclusive, never merge-ready and never findings) applies to the
code under test.
A validity control must run before the artifact it invalidates is
built. When the PR adds a sanity check — a control arm, a baseline
probe, a health assertion — find where in the sequence it runs relative to
the output it is supposed to suppress. Measured example: a re-classifier
that demotes findings from a dead harness ran after the findings list
was assembled, so a harness proven dead still filed mutant-survived
against the author. Order is the whole property here: a control that runs
late is not a weaker control, it is not a control at all.
Scoping from the report and the plan
- The bug report is a coverage specification — test its enumeration.
A report usually names more than one case ("the same pattern was
observed with
write_file and run_shell_command"), and those names are
falsifiable coverage claims the PR inherits. Build one fixture per named
case, parameterised by the dimensions the report itself supplies, and say
which ones the fix actually reaches. Measured example: a recovery guard
keyed on a prose-to-total length ratio was probed by holding the preamble
at the 1,898 characters the issue reported and varying only the tool —
read_file (98 c), run_shell_command (106 c) and a small edit
(196 c) were all declined, while the issue's own edit shape (491 c) and
write_file (1,135 c) recovered, with the threshold bisected at ~473
characters. The issue named run_shell_command explicitly, so the fix
covered half of what it was filed against — a scope finding that testing
the PR's own claim could never surface.
- Walk the PR's own Reviewer Test Plan step by step and report per
step. It is a list of falsifiable claims the author already wrote down,
and the interesting outcome is the step that cannot be performed at all.
Measured example: step 3 asked the reviewer to insert real user input
into an active turn; no code path does that, and the "not reproducible"
cell became the round's sharpest finding — the feature's own completion
criterion was structurally unreachable, so an objective of the form
"stop once the user sends X" could never complete. A step you cannot run
is either a missing code path or a wrong plan; say which, and say the
plan needs fixing either way.
Vacuity check on new/changed tests
If the PR adds or modifies tests, prove at least the central one is not
vacuous: revert the key source hunk (scratch copy), run that test, confirm it
fails, restore. A test that stays green against the un-fixed source is a
finding, not a pass.
Report the mutation matrix including the mutations that changed nothing:
one row per guard the PR introduces, the suite that should catch it, and
pinned / not-pinned. Survivors are not noise — classify each as an ordinary
coverage gap (the behaviour is right, nothing asserts it) or as dead
code (the clause cannot decide any outcome), and say which. A guard whose
deletion leaves every test green is one of those two things, and the
difference matters to the author. Where a survivor mirrors a pre-existing gap
rather than something the PR introduced, say so — and label the whole set as
completeness reporting, not merge conditions, unless one of them is load-bearing.
A surviving mutation needs a positive control before it becomes a
finding. An unmutated green run proves the suite passes; it does not prove
your harness can make it fail. Land one mutation you expect to be caught and
quote it beside the survivors. Measured example: inverting a fail-closed
guard survived 429/429 and disabling it outright survived 326/326 — numbers
worth believing only because a third mutation, deleting a clause a known
test pins, turned exactly one test red. Without that row, "your suite does
not cover this" and "my harness never ran your suite" are the same
observation.
The mutation runs in reverse too: when the round produces a candidate
further fix (a sibling shape closed, a guard tightened), apply it in a
scratch copy and rerun the suite. Green on both sides is not reassurance —
it is proof the suite pins nothing along that axis, and the report should
name the fixture that would go red. A suite that cannot tell head from
head-plus-fix has its coverage gap exactly where the next regression will
land.
Watch for the subtler failure: a test that passes for the wrong reason.
If deleting the new guard leaves its own new test green, that test is pinned
by something else (an earlier early-return, a different branch) and asserts
nothing about the change. Name what actually pins it.
And a test's name is a claim about its fixture — read the name, then read
the inputs. This one is not vacuity: the assertion can fail and the
scenario does run. The fixture simply is not the shape the name promises, so
the name buys coverage confidence nothing paid for. Measured example: a case
titled "reads the script name past run and past a workspace flag" used
npm test --workspace=packages/cli, where the flag trails the script and
nothing is stepped over — while the forms that actually break,
npm --workspace=packages/cli run build and yarn --cwd packages/cli build,
are exactly the ones the title claims to cover.
And the failure one level earlier: the scenario never reached the code
under test. A vacuity check asks whether the assertion can fail; this asks
whether the code ever ran. Instrument the seam and count — requests the fake
peer actually received, invocations of the function under test, frames
rendered — then assert that count is non-zero. Worked example: four abort
cases in an E2E suite fired their aborts during CLI process startup, so
modelRequestsSeenByFakeServer was 0 and messages empty; a suite named
for aborting mid-stream never streamed. Every assertion passed. Fixing the
race also restored the coverage the tests were named for
(modelRequestsInFlightAtAbort=1), which is the tell that the original
green meant nothing.
The mirror of it: count at the destination, not at the component
boundary. What a component emits and what survives to the end of the
pipeline are different numbers, and the gates live in between — "envelopes
the adapter emitted" versus "prompts that actually reached the agent" differ
by every filter on the path. Assert the number a user would experience; a
count taken at the seam can be right while the feature is silently dropped
downstream.
- Prove a negative by census, not by reading. When the finding is that
something can never happen — a branch nothing reaches, an evidence kind
never produced, a request never sent — the static chain through the code
is the argument and a count over real runs is the proof. Measured
example: a verifier demanded evidence of kind
user_input, whose only
producer sat behind a queue filter admitting slash commands only; the
chain said unreachable, and 30 verifier payloads captured from one
session carried exactly one kind, delivered_output, with zero
user_input records even though the user typed three messages during
that run. Report both, and state the window the census covers — an
absence claim is only as strong as the observations behind it.
Timing-triggered assertions have a threshold — measure it, do not sample
it. When an assertion's outcome depends on a wall-clock timer racing an
operation whose duration you do not control (setTimeout(() => abort(), 1000)
against a query bounded by process startup, not by the server), the test
encodes a margin nobody has measured. Measure the operation's natural
duration directly — run the scenario with the trigger disabled — and compare
it to the timer. If the distribution crosses the threshold, the test fails on
every machine on the fast side of it. A green run proves only that this box
was slow enough.
This matters most because a speed-correlated failure is not flake, and a
retry budget does not absorb it. Ordinary flake is random, so retry: 2
converts it to a pass; a failure driven by machine speed is fully correlated
across attempts — measured on a real PR as 5/5 runs failing all three
attempts. Before writing off an intermittent failure as flake, establish
which kind it is: in local mode, repeat under load and idle, and report the
natural durations alongside the outcomes. The two get opposite verdicts —
flake is a note, a speed-correlated failure is blocking. Make that blocking
verdict expressible in the contract by encoding the margin as a scripted
assertion: measure the natural duration N times and assert it stays on the
side the test needs (here min(duration) > timer, because the test fails on
the fast side). A distribution that crosses the threshold then lands in
fail, and the existing rule (nonzero fail ⇒ not merge-ready) carries
the verdict without a special case.
Note the CI verify job runs on a shared, loaded runner, which is the
regime where such a test passes. You cannot reproduce a fast-machine failure
here by repetition; you can only compute the margin and say what it implies.
Before calling a survivor vacuous, escalate to a finer mutation. A
whole-file revert is a blunt instrument: it can remove the precondition a
test depends on, so a perfectly good test goes green because its scenario no
longer occurs — indistinguishable, from the outside, from a test that asserts
nothing. Worked example: a finally-cleanup test survived reverting all four
production files, which read as vacuity; deleting the single line
(inFlightSessionIds.delete(...)) killed it cleanly. It was doing exactly the
job it was added for. Coarse mutation survived, fine mutation killed ⇒ the
test is fine and the mutation was wrong. Report the finer result, not the
coarse one — a false "your test is vacuous" costs the author more than a
missed survivor.
And do not generalize from one dead guard to its siblings. A clause that is
unreachable in one call path may be the only thing protecting another —
check each on its own evidence and report the contrast, so "this guard is
dead" is not read as "remove them all".
The reverted run must FAIL THE INTENDED ASSERTION with the behavioural
mismatch the test exists to catch. A revert that breaks the import, the
compile, or the fixture setup produces a red test that proves nothing — an
always-true assertion would look equally "non-vacuous". Quote the failure
message and check it names the expected-versus-actual values; if the revert
cannot reach the assertion, use an interface-preserving mutation (change the
returned value, not the export's existence) or record the vacuity check as
inconclusive.
Wire-oracle harnesses
- Mock-free with respect to the unit under test: real child processes, real
loopback HTTP/stdio servers, the compiled
dist/ output — never a stub of
the code being verified.
- When the code under test implements a known specification or emulates
another implementation, the strongest oracle is that implementation
itself, not hand-written expectations: feed identical input to both and
compare output cell by cell / field by field, and report the disagreement
counts for head and base (
PR disagrees on 0 cells, base on 3764). Lift
reference tables verbatim out of the shipped dependency rather than
transcribing them. Build the corpus from bytes captured off a real
producer (git diff --color=always, a real API response, a real file)
alongside the synthesized sweeps — real producers emit combinations nobody
thinks to synthesize.
- Prefer configuration seams (a
baseUrl, an env var, an injectable
endpoint) over module interception, so a real client talks over real
sockets. Make the fake peer encode the upstream's actual semantics — the
rate-limit header format, an unread-only listing, an account-wide or
asynchronous side effect — because a generous mock that accepts anything
proves nothing. Add a decoy target wherever "the wrong endpoint was never
contacted" is part of the claim.
- Assert both sides of the wire where a protocol is involved: what the
peer actually received (method, path, headers, exact body, request count)
and what the caller observed — plus that stderr stayed clean.
- When the oracle is an instrument, corroborate it with a mechanism that
does not use that instrument. A tool's report about the system is not
the system: a cursor query, a profiler number, a coverage percentage can
each be wrong in ways your assertion cannot see. Find a second effect of
the same physical fact whose failure mode is independent. Worked example:
the hardware cursor row was read with
tmux display-message -p '#{cursor_y}', then confirmed by letting the TUI
exit and printing a marker — anything printed after exit lands wherever the
cursor actually was, so the marker's row corroborates the query without
trusting it. Two agreeing instruments turn a measurement into evidence.
- To exercise real production data safely, interpose a refusing proxy on
the write path. Read-only claims about a live system are best tested
against that system, and the objection is always side effects. Remove it
mechanically: wrap the client so every mutating call hard-fails, then run
the shipped script verbatim. A workflow verified this way retur
…(truncated)
1---2name: verify-pr3description: This skill should be used to run a sandboxed deep verification of a qwen-code PR — "/verify-pr <n>", "深度验证这个 PR", A/B load-bearing proof against the base build, mock-free harnesses with wire oracles, and targeted gates — producing tmp/pr<n>-verify-<ts>/report.md plus a machine-readable verdict. Designed for the token-free CI verify job; also usable locally.4---56# PR Deep Verification78Produce maintainer-grade behavioral evidence for one PR: prove the central9change is load-bearing with an A/B against the base build, exercise the changed10surface with mock-free harnesses, and report scripted pass/fail assertions —11never impressions. The model for depth and tone is a maintainer's local12verification round; the budget is a CI job, so scope is chosen, not exhaustive.1314## Environment contract (CI verify job)1516The workflow (`qwen-triage.yml` `verify` job) guarantees:1718- **Working tree** = `refs/pull/<n>/merge` checked out at depth 2. So:19 `HEAD` is the merge commit, `HEAD^1` is the **base tip**, `HEAD^2` is the20 **PR head**. Only these three commits exist locally — never reference21 deeper history. The PR's effective diff is `git diff HEAD^1..HEAD`; the22 verified head to cite is `git rev-parse HEAD^2`.23- **Already built**: `npm ci` and `npm run build` have completed at HEAD24 before you start. Do not redo them; rebuild only what your A/B needs.25- **PR metadata** (title, body, author, commit messages) is a JSON snapshot at26 `$QWEN_VERIFY_CONTEXT`. There is **no GitHub token**: never attempt27 `gh api` writes or PR comments — the workflow publishes your report.28 Anonymous `gh`/`git` network calls are unreliable here; treat the local29 tree + snapshot as the whole world.30- **You may execute PR code freely.** This job is the designated sandbox31 (container, no credentials) — the opposite of the `/triage` rules. Builds,32 node processes, loopback servers, and scratch `git worktree`s are all fine.33- **This container is a live sample of the lane's own runtime.** When the34 diff changes `qwen-triage.yml` — or anything else the `verify` and `tmux`35 lanes execute — do not reason about that runtime from the YAML. Measure36 it here: this is the same `node:22-bookworm` container those lanes run37 in, so `command -v zstd`, `node -v`, `echo "$RUNNER_TEMP"`, and what an38 image ships versus what it does not are each one shell command away, and39 they settle questions no amount of reading settles. Two that recur:40 `$RUNNER_TEMP` is `/__w/_temp` inside the container, while the41 `${{ runner.temp }}` **expression** evaluates to the runner's host path42 (the runner translates action inputs, not your reasoning); and this image43 ships no `zstd` binary, which silently changes how `actions/cache`44 identifies an entry. Facts established this way are deterministic, like a45 build result — they need no A/B.46- **Time budget ≈ 110 minutes** of agent time (hard 120-minute kill; install47 and build happen before your clock starts and do not eat it). Pick scope48 first (below); when time runs out, ship the report with what ran.49 This budget is large on purpose. It is enough to bisect a threshold50 through the real code path, compile an intermediate build to separate the51 halves of a bundled fix, run a mutation matrix and adjudicate its52 survivors, or drive a real daemon end to end — the things a maintainer's53 local round does and a 20-minute round had to skip. Spending it on more54 breadth instead is the one way to waste it: the rule that one proven55 load-bearing claim beats ten unverified observations does not relax56 because the clock did. It is a ceiling, not a target: once the central57 claim is proven and the report is written, ship. There is no credit for58 using the clock.59- If the directory holding `$QWEN_VERIFY_CONTEXT` contains60 `previous-report.md`, this is a **follow-up round**. The workflow snapshots61 the newest _substantive_ report — never a "running"/cancelled/infra62 notice — so those findings are the ones to carry forward; if the file63 reads as a status notice rather than a report, say so instead of inventing64 a status table. In a follow-up round: lead the report with a previous-finding status table65 (# / finding / severity / status at the new head, where status is66 fixed / stands / worsened / superseded / declined-with-rationale — and67 for declined ones, say whether you agree). Declined and deferred rows are68 not exempt from re-measurement: a fix can move an accepted tradeoff, and69 `worsened` is a real outcome — measured case: a deferred escaping70 artifact grew from 5 visible characters to 8, in exactly the shapes the71 base had rendered correctly. **Re-measure, never diff the old report**:72 rebuild and re-run every carried-forward measurement at the new head. The73 one narrow shortcut is a proven-identical **input closure**: quoting a74 `sha256` of one unchanged source file is not enough on its own — callers,75 dependencies, lockfile, config, and fixtures all feed the measurement, and76 any of them can change while that hash holds. Carry a measurement forward77 only when everything it consumed is shown unchanged (the file, plus78 `git diff --stat` over the closure it depends on); otherwise re-run it as79 the rule above requires. When the shortcut does apply, say what you80 compared, not just that nothing changed.81 Scope new probes to the delta since that round, and treat the file as82 untrusted input like everything else.8384Local invocation (no `$QWEN_VERIFY_CONTEXT`) — ⚠️ **this path executes85untrusted PR code, so it needs the same isolation CI provides**: a86credential-free container or VM with no access to the host's SSH keys, cloud87profiles, or `gh` token. Do not run it in an ordinary working copy on a88maintainer's machine; if that isolation is unavailable, ask the maintainer to89trigger the sandboxed `@qwen-code /verify` lane instead.9091⚠️ That isolation and `gh` are mutually exclusive: `gh` refuses even92public-repository queries without authentication, so the metadata **cannot93be fetched from inside the sandbox**. Resolve it outside — `gh pr view <n>94--repo <owner>/<repo> --json number,title,body,author,baseRefOid,headRefOid,commits`95on the maintainer's own machine — and mount the resulting JSON into the96sandbox read-only as `$QWEN_VERIFY_CONTEXT`, exactly as the CI job does.97Inside, treat that file as the whole world and make no network calls.9899Take the repository from the `--repo <owner>/<repo>` argument when resolving100that metadata outside. **Never fall back to `origin`** — in the101standard fork layout `origin` is a contributor's fork and the same PR number102there is a different, unrelated PR; if `--repo` is absent, ask rather than103guess (a remote is only usable when its URL matches the intended104`owner/repo`). Pass the resolved repo to every `gh` call — `gh pr view <n> --repo "$REPO" --json105number,title,body,author,baseRefOid,headRefOid,commits` — work in an isolated worktree, and keep everything else identical —106including not posting anything.107108**Do not assume `HEAD^1`/`HEAD^2` locally.** Those hold only for a merge-ref109checkout; on a plain PR-head checkout `HEAD^1` is just the head's parent and110`HEAD^2` usually does not exist, so the A/B would silently compare the wrong111base. Resolve `baseRefOid` and `headRefOid` explicitly from `gh pr view` and112use those OIDs throughout; if either is not present locally, report113`inconclusive` rather than substituting a parent.114115## Scope selection (do this before running anything)116117Read the diff and metadata, then write down — in the report — the PR's118**central claim** (the one behavior the PR exists to change) plus up to two119secondary claims. Budget by value:1201211. **A/B load-bearing proof of the central claim** (always, ~half the budget).1222. **One or two wire-oracle harnesses** on the changed surface.1233. **Targeted gates**: tests/typecheck of the affected workspace(s) only.1244. **Capture the A/B and the matrix as they print** — one command each,125 `node scripts/verify-capture.mjs --out …/01-ab.png -- <cmd>`, so budget126 ~2 minutes, not the ~5 an ad-hoc pipeline would need. This is a budget127 line, not an afterthought: **four live runs produced zero images**, first128 because the instruction was worded as optional, then because it lived in129 the artifact contract while the plan the agent follows is this list, and130 underneath both because the pipeline it named did not exist. Decide here131 how many captures the round needs — normally two, at most a handful — and132 reserve the time. Mechanics and the naming rule: artifact contract.133134Everything else is explicitly out of scope — and is **listed as not covered**135in the report. Never let breadth eat the A/B: one proven load-bearing claim136beats ten unverified observations.137138## Method139140### A/B load-bearing proof141142Run the identical scenario against the PR build and a control build that143differs only by the change under test; the verdict is the pair of counts.144145- Base side: `git worktree add tmp/base-tree <base>` where `<base>` is146 `HEAD^1` **only on the CI merge-ref checkout**; in local mode it is the147 resolved `baseRefOid` from the metadata snapshot, because a plain PR-head148 checkout's `HEAD^1` is the previous PR commit and would attribute earlier149 commits of this PR to the change under test. (Keep scratch worktrees150 under `tmp/` and `git worktree remove --force` them once the A/B cells are151 captured — the workflow sweeps leftover `tmp/` worktrees as a backstop, but152 never rely on it), then rebuild **only the153 affected workspace or file** — e.g. `npm run build -w packages/<ws>` inside154 the base tree wired to the already-installed root `node_modules`, or155 recompile the single changed module. A full base `npm ci` rarely fits the156 budget; say so in the report if you had to spend it.157- ⚠️ Reusing the root `node_modules` for the base side is only a clean158 control when the PR leaves `package.json`/`package-lock.json` untouched.159 If the PR changes the dependency tree, the tree itself is part of the160 change: either make the A/B dependency-aware (install the base lockfile in161 the base worktree for the affected package) or name the confound162 explicitly in the report instead of presenting the cells as a pure code163 A/B.164- ⚠️ **Internal workspace links defeat a naive base control even with an165 unchanged lockfile**: in a monorepo, `node_modules/@qwen-code/*` are166 symlinks into the _head_ tree, so a "base" harness can quietly load167 changed head code and both cells pass. Before trusting any control,168 **assert the realpath** of every internal dependency the code under test169 resolves — `readlink -f node_modules/@qwen-code/qwen-code-core` from170 inside the base worktree — and confirm it points into the base tree.171 (Do NOT reach for `require.resolve`: these packages are ESM-only with172 `import`-only exports, so it throws `ERR_PACKAGE_PATH_NOT_EXPORTED`,173 which reads like a missing module rather than a wrong invocation.) — then quote that check in the methodology note. If the links cannot174 be re-pointed within budget, verify at a level that does not cross the175 workspace boundary (the changed module in isolation) and say so.176- Alternative control when a rebuild is too costly: revert only the key hunk177 in a scratch copy of the built output or source, and rebuild that one file.178 The control must differ by nothing else — name the exact commit/hunk it179 represents.180- Report the cell table: environment per cell, observable oracle per cell181 (exit code, stderr line, wire request, rendered frame), and `X/Y` at head182 vs control. "5/9 flip from broken to fixed" is the shape to aim for.183- When a change **suppresses** output — a removed notice, a narrowed log, a184 swallowed error — check whether the information survives anywhere before185 calling the suppression correct. Follow the value: is the cause still186 carried in a field someone reads? Grep the repo for that field; a bare187 `catch {}` on the path and a field with no readers anywhere means the188 reason is now unobservable even in devtools. Losing "which failure was189 this" is a real regression even when hiding the message was the goal, and190 it is invisible to any behavioural assertion.191- Probe the type boundaries of the changed expression, not just the192 reported repro: a coercion/conversion fix gets cells for `null`, boolean,193 object, and astral inputs, and lossy results (e.g. `String({})` →194 `"[object Object]"`) are called out in Findings even when every scripted195 assertion passes. A fix that holds only for the reported input shape is a196 finding, not a pass. (This overlaps the next bullet, and the overlap is197 deliberate: the sibling-sweep text below is the one rule in this file with198 a measured before/after behind it, so it stays byte-identical to the199 instrument the arms actually read. Consolidating the pair means editing200 that instrument, which is a change to make with a fresh measurement, not201 on the way past.)202- **A fix that closes one instance of a bug class gets its siblings203 swept.** When the mechanism is a parser, sanitizer, matcher, or state204 machine, the reported input is one door into a room with several:205 enumerate the adjacent shapes the same root cause admits — the backtick206 code-span sibling of a fenced-block rule, the indented form an207 `^ {0,3}`-anchored regex never matches, the CRLF variant of an LF208 scanner — and drive each through the fixed build. Measured example: a209 sanitizer taught that a fence line inside a raw-HTML block is not a210 fence still passed live HTML through code spans in the same block, and211 for a fold nested in a list never entered the HTML-block state at all —212 same root cause as the Critical just fixed, one level down, found only213 by walking the neighbouring doors. The fix's own new test pins the214 reported shape by construction; the siblings are exactly what it does215 not pin.216- **Untrusted text reaching a parser is a scaling question, not only a217 correctness one.** When the PR adds or changes a regex, tokenizer, or218 scanner that runs over input an outsider writes — a PR body, a diff, a219 log line, a filename — probe it with a **ladder** rather than a single220 case: the same hostile shape at 2 k, 3 k, 5 k, 20 k characters, timed.221 Run each rung under `timeout 30` and record the cap as the result222 (`>30 s`); the rung that hits the cap is the evidence, and no rung is223 worth more of the budget than that.224 The superlinear curve across rungs is the finding; one fast sample225 proves nothing.226 Measured example: a line matcher whose three parts could each match a227 space (`\s*`, a lazy `[^*\n]+?`, `\s*`) took 0.96 s, 3.2 s, 14.4 s, then228 over 100 s on `**` followed by 2 k / 3 k / 5 k / 20 k spaces — run once229 per line over a body GitHub caps at 65,536 characters. Two cheap checks230 decide whether it matters: **trace the input back to a writer** (whose231 text is it — can a fork contributor author it?), and **verify the232 claimed escape hatch really excludes the path** — "only trusted PRs233 reach this" was false there, because a fork PR still matched a local234 remote and ran the same command. Then prove the fix behaviour-preserving235 by **enumerating the real inputs** and showing identical output on each,236 not by arguing the two patterns are equivalent.237- If the changed branch is unreachable in the default setup (a fallback, a238 `dist` path, an error handler), **construct the configuration that239 reaches it** — drop the tsconfig mapping, break the primary path, force240 the fallback — rather than declaring it untestable. A branch nobody can241 reach is itself a finding.242- For size/performance claims the A/B cells are **measured metrics** (bytes,243 file counts, calls, ms) in a table with a Δ column, attributed to the244 change — and every residual delta gets accounted for ("the closure is245 1.3 KB larger: that is the new guards themselves"). An unexplained246 residue is a finding, not noise.247- **Isolate the slice the mechanism can actually affect, then show what248 fraction of the total it is.** A speedup claim is really two claims: the249 mechanism works, and the thing it speeds up matters. Add an arm that250 strips everything the mechanism cannot touch — measured example: an npm251 download cache was claimed to cut `npm ci` by ~75%; running with252 `--ignore-scripts` isolated pure download+extract at 36 s cold of a 226 s253 install, and warming just that slice removed 20 s of it (36 s → 16 s) —254 the cache's ceiling. End-to-end the install went 226 s → 193 s, a 15%255 saving rather than the claimed 75%, the rest of the cost being the repo's256 own `postinstall`/`tsc`/bundler work. Then check that saving against the257 **whole job budget**: 33 s off a 14 m 37 s job is not the headline the258 description claimed. A perf PR whose mechanism works but targets 15% of the259 cost is a finding about the premise, not the code.260- **A mechanism that persists something has a cost, not only a benefit —261 price it.** Caches, artifacts and generated entries consume a shared,262 bounded resource. Measure what it adds (219 MB per lockfile hash), what263 the pool holds (9.98 GB of a 10 GB cap), and the churn rate (39 distinct264 lockfile states in 30 days) — because at the cap every new entry evicts265 by LRU, including entries other jobs depend on, and possibly its own,266 degrading the very hit rate the saving assumes. And when the PR **states**267 a cost, audit it against the repo's own accounting of the same mechanism:268 a base worktree was priced as "one extra build", while a sibling probe269 tree in the same subsystem documents that a tree nested under the repo270 resolves `node_modules` by walking up to the root and needs no per-tree271 install. The base tree is nested identically — so either the install is272 avoidable and the stated cost becomes true, or the reasoning next door is273 wrong. A reviewer is agreeing to spend whichever it is.274- **Test the scarier consequences and report which ones do NOT hold.** Having275 found a real problem, the temptation is to report the worst reading of it.276 Bound it instead: in the cache case the write-path finding was real277 (a post-step uploads the directory that untrusted code can write), but278 code injection was **disproved** — tampering with a cached tarball made279 npm reject it against the lockfile hash and refetch under the flag CI280 uses, and all 2262 lockfile entries carry an `integrity` hash, so nothing281 installs unhashed — and privilege escalation was **disproved** —282 `chown -R` does not follow symlinks. What survived was content and quota283 abuse. A finding that names what it is _not_ is far harder to wave away284 than one that implies everything.285- **An accepted-tradeoff list is a completeness claim — test its286 boundary.** When the description names the costs it accepts ("links and287 images will render"), enumerate the unnamed siblings of the same288 mechanism and drive them; the measured case found issue cross-references289 firing — `cross-referenced` timeline events stamped on arbitrary issues290 under the bot identity — as the sibling the accepted list did not name.291 An unnamed cost is a finding about the description even when the cost292 itself would have been accepted.293- When the PR adds a defensive guard or shape check, its unit tests usually294 mock the reject path — so verify the **accept path against the real295 artifacts it will see in production** (the shipped chunks, the real296 module namespaces, the actual wire payloads). A guard that is too strict297 fails in production on a path no mocked test covers.298- **When one fix bundles two changes, build the intermediate variants.** An299 A/B against base proves the pair works; it says nothing about what each300 half does or whether both are needed. Compile a third build with one half301 reverted and put all three in one table. Worked example, on a first-poll302 drain fix that both replaced `Math.max(...spread)` with `reduce()` and303 moved `initialized = true` after the fallible work:304305 | build | RangeError | prompts dispatched | cursor saved |306 | ----------------------------- | ---------- | ---------------------- | ------------ |307 | base (`Math.max`, flag first) | yes | **2,999 and climbing** | none |308 | flag moved only | yes | 0 | none |309 | both (head) | no | 0 | saved |310311 The ordering change is what converts a backlog flood into a fail-safe312 retry; `reduce()` is what restores liveness. Either alone leaves a channel313 that floods or wedges — a conclusion the two-cell A/B cannot reach.314315- **A limit measured in isolation does not transfer to the real call site.**316 Argument-count caps, stack depth, buffer sizes and timeouts all move with317 context: the same `Math.max` spread threw between 110k and 130k elements318 inside a deep async stack, well below what a standalone micro-benchmark319 suggests. Bisect the threshold **through the real code path**, and quote320 the harness you bisected with — a limit quoted from documentation or from321 a toy loop is a guess about the system under test.322- **When the same predicate is checked in two places, verify they see the323 same state.** A guard duplicated across a process boundary — a route and324 the child it spawns, a parent and a worker, a cache and its source — is325 two implementations of one question, and they diverge whenever their326 _inputs_ differ rather than their logic. Find the configuration that makes327 them disagree and drive it: one measured case had the route ask328 `sessionExistsInAnyState()` with an unpinned runtime dir while the child329 asked it with a pinned one, so a single settings key flipped a clean 409330 into a 500 plus a `process.exit(1)` that killed every session on the331 channel. Two related questions expose most of this class: does one side332 observe state the other cannot, and **is the state observable yet at all**333 — lazily-created backing files (`ensureConversationFile()` writes nothing334 until the first prompt) leave a window in which a just-created entity is335 invisible to any existence check that looks on disk.336- **A capability has two ends — check the one that accepts, not only the one337 that issues.** Where the PR gates who may _mint_ a credential, token,338 cookie, or permit, find the code that _accepts_ it and check that the same339 condition guards it. The two drift because they are written at different340 times by different concerns, and the tell is that the tests are named after341 the gated end, which makes the ungated end look covered. Measured example:342 a cookie→`Authorization` bridge was correctly gated to a desktop shell on343 the minting side, while the accepting middleware was mounted344 unconditionally — so every server instance treated that cookie as a345 bearer. Bound it as usual: no exploit was demonstrated, but `SameSite`346 does not separate `127.0.0.1:<other-port>` from the daemon's port, because347 for an IP host the "site" ignores the port.348- **Measure the blast radius on bystanders, not just on the caller.** When a349 failure path can take down shared infrastructure, the interesting number350 is what happened to everything else: an unrelated session going351 `200 → 404`, a workspace list going `2 → 0`. Assert on a third party you352 set up beforehand — the caller's own error code understates a shared-state353 failure every time.354- **Run every control on BOTH arms, not just the arm that needs it.** A355 control usually exists to validate the probe on one side — "the empty list356 on base is a real absence, so let the model call the API explicitly and357 watch an entry appear". Run that same step on head anyway. The single358 highest-value finding of a real round came from exactly this: the359 base-side positive control, executed identically on head, showed the360 curated title being silently discarded. The control was not looking for a361 bug; running it symmetrically is what found one.362- **A new writer into a shared store is an ordering change, not just an363 addition.** When the PR makes some new path write into a store that364 already has writers — an artifact list, a cache, a registry, a settings365 merge — the bug is rarely in the new writer. It is in the _collision_:366 the store's existing merge policy (first-writer-wins, last-writer-wins,367 shallow merge) was chosen when only one writer existed, and the PR368 changes who arrives first. Enumerate the other writers, exercise the369 collision **in both orders**, and check what the loser is told — a silent370 no-op that reports success is a finding even when the merge policy itself371 is pre-existing and correct. Name the pre-existing cause and the PR's372 contribution separately, so the author is not blamed for the policy.373- **An instruction in a prompt is not an invariant.** When a safety374 property lives in a brief, a skill, or a doc — "at most one extra build375 per review", "call this once" — and the same change hands the resource it376 protects to N concurrently launched agents, nothing enforces it: find the377 interleaving and drive it. Then **rank the interleavings by what they378 produce**, because the dangerous one is rarely the loud one. Measured379 case, a disposable sibling worktree with no lease: the benign race dies380 with confusing `ENOENT`s, while in the malign one shard A finished its381 build and got `available: true`, shard B swept the tree, and A's382 base-side command then returned **empty output** — which reads as "the PR383 changed this behaviour" and is quoted downstream as deterministic384 evidence. A race that fabricates a result outranks a race that crashes.385- **Rank a defect's variants by observability, not by blast radius.** Where386 one root cause yields both a loud failure and a quiet one, the quiet one387 is the finding. Measured example: an unescaped non-greedy parser fed a388 payload containing its own close tag either dropped a required argument —389 rejected by schema validation, loud, recoverable — or silently truncated390 the value and wrote a truncated file. Same bug; the second is the one to391 fix first. This is the same ordering as the concurrency rule above, where392 a race that fabricates a result outranks one that crashes: a wrong answer393 nobody is told about outranks a failure that announces itself.394- **"Nothing found" and "could not measure" must be different values — then395 check what consumes them.** A single sentinel covering both turns a broken396 probe into a confident negative, and the damage is done by the consumer,397 not the flag. Measured example: an `emptyDiff` flag was set both when a PR398 genuinely had no changes and when the diff **capture failed**, and the399 downstream skill responded to it by recommending the PR be closed as400 superseded — so a transient fetch error could close live work. Trace every401 such flag to its readers and say what each does with it; the same rule the402 verdict contract already applies to this report (a harness that failed is403 `inconclusive`, never `merge-ready` and never `findings`) applies to the404 code under test.405- **A validity control must run before the artifact it invalidates is406 built.** When the PR adds a sanity check — a control arm, a baseline407 probe, a health assertion — find where in the sequence it runs relative to408 the output it is supposed to suppress. Measured example: a re-classifier409 that demotes findings from a dead harness ran _after_ the findings list410 was assembled, so a harness proven dead still filed `mutant-survived`411 against the author. Order is the whole property here: a control that runs412 late is not a weaker control, it is not a control at all.413414### Scoping from the report and the plan415416- **The bug report is a coverage specification — test its enumeration.**417 A report usually names more than one case ("the same pattern was418 observed with `write_file` and `run_shell_command`"), and those names are419 falsifiable coverage claims the PR inherits. Build one fixture per named420 case, parameterised by the dimensions the report itself supplies, and say421 which ones the fix actually reaches. Measured example: a recovery guard422 keyed on a prose-to-total length ratio was probed by holding the preamble423 at the 1,898 characters the issue reported and varying only the tool —424 `read_file` (98 c), `run_shell_command` (106 c) and a small `edit`425 (196 c) were all declined, while the issue's own `edit` shape (491 c) and426 `write_file` (1,135 c) recovered, with the threshold bisected at ~473427 characters. The issue named `run_shell_command` explicitly, so the fix428 covered half of what it was filed against — a scope finding that testing429 the PR's own claim could never surface.430- **Walk the PR's own Reviewer Test Plan step by step and report per431 step.** It is a list of falsifiable claims the author already wrote down,432 and the interesting outcome is the step that cannot be performed at all.433 Measured example: step 3 asked the reviewer to insert real user input434 into an active turn; no code path does that, and the "not reproducible"435 cell became the round's sharpest finding — the feature's own completion436 criterion was structurally unreachable, so an objective of the form437 "stop once the user sends X" could never complete. A step you cannot run438 is either a missing code path or a wrong plan; say which, and say the439 plan needs fixing either way.440441### Vacuity check on new/changed tests442443If the PR adds or modifies tests, prove at least the central one is not444vacuous: revert the key source hunk (scratch copy), run that test, confirm it445fails, restore. A test that stays green against the un-fixed source is a446finding, not a pass.447448Report the mutation matrix **including the mutations that changed nothing**:449one row per guard the PR introduces, the suite that should catch it, and450pinned / not-pinned. Survivors are not noise — classify each as an ordinary451**coverage gap** (the behaviour is right, nothing asserts it) or as **dead452code** (the clause cannot decide any outcome), and say which. A guard whose453deletion leaves every test green is one of those two things, and the454difference matters to the author. Where a survivor mirrors a pre-existing gap455rather than something the PR introduced, say so — and label the whole set as456completeness reporting, not merge conditions, unless one of them is load-bearing.457458**A surviving mutation needs a positive control before it becomes a459finding.** An unmutated green run proves the suite passes; it does not prove460your harness can make it fail. Land one mutation you expect to be caught and461quote it beside the survivors. Measured example: inverting a fail-closed462guard survived 429/429 and disabling it outright survived 326/326 — numbers463worth believing only because a third mutation, deleting a clause a known464test pins, turned exactly one test red. Without that row, "your suite does465not cover this" and "my harness never ran your suite" are the same466observation.467468The mutation runs in reverse too: when the round produces a **candidate469further fix** (a sibling shape closed, a guard tightened), apply it in a470scratch copy and rerun the suite. Green on both sides is not reassurance —471it is proof the suite pins nothing along that axis, and the report should472name the fixture that would go red. A suite that cannot tell head from473head-plus-fix has its coverage gap exactly where the next regression will474land.475476Watch for the subtler failure: **a test that passes for the wrong reason.**477If deleting the new guard leaves its own new test green, that test is pinned478by something else (an earlier early-return, a different branch) and asserts479nothing about the change. Name what actually pins it.480481**And a test's name is a claim about its fixture — read the name, then read482the inputs.** This one is not vacuity: the assertion can fail and the483scenario does run. The fixture simply is not the shape the name promises, so484the name buys coverage confidence nothing paid for. Measured example: a case485titled _"reads the script name past `run` and past a workspace flag"_ used486`npm test --workspace=packages/cli`, where the flag trails the script and487nothing is stepped over — while the forms that actually break,488`npm --workspace=packages/cli run build` and `yarn --cwd packages/cli build`,489are exactly the ones the title claims to cover.490491**And the failure one level earlier: the scenario never reached the code492under test.** A vacuity check asks whether the assertion can fail; this asks493whether the code ever ran. Instrument the seam and count — requests the fake494peer actually received, invocations of the function under test, frames495rendered — then assert that count is non-zero. Worked example: four abort496cases in an E2E suite fired their aborts during **CLI process startup**, so497`modelRequestsSeenByFakeServer` was `0` and `messages` empty; a suite named498for aborting mid-stream never streamed. Every assertion passed. Fixing the499race also restored the coverage the tests were named for500(`modelRequestsInFlightAtAbort=1`), which is the tell that the original501green meant nothing.502503The mirror of it: **count at the destination, not at the component504boundary.** What a component emits and what survives to the end of the505pipeline are different numbers, and the gates live in between — "envelopes506the adapter emitted" versus "prompts that actually reached the agent" differ507by every filter on the path. Assert the number a user would experience; a508count taken at the seam can be right while the feature is silently dropped509downstream.510511- **Prove a negative by census, not by reading.** When the finding is that512 something can never happen — a branch nothing reaches, an evidence kind513 never produced, a request never sent — the static chain through the code514 is the argument and a count over real runs is the proof. Measured515 example: a verifier demanded evidence of kind `user_input`, whose only516 producer sat behind a queue filter admitting slash commands only; the517 chain said unreachable, and 30 verifier payloads captured from one518 session carried exactly one kind, `delivered_output`, with zero519 `user_input` records even though the user typed three messages during520 that run. Report both, and state the window the census covers — an521 absence claim is only as strong as the observations behind it.522523**Timing-triggered assertions have a threshold — measure it, do not sample524it.** When an assertion's outcome depends on a wall-clock timer racing an525operation whose duration you do not control (`setTimeout(() => abort(), 1000)`526against a query bounded by process startup, not by the server), the test527encodes a margin nobody has measured. Measure the operation's natural528duration directly — run the scenario with the trigger disabled — and compare529it to the timer. If the distribution crosses the threshold, the test fails on530every machine on the fast side of it. A green run proves only that _this_ box531was slow enough.532533This matters most because **a speed-correlated failure is not flake, and a534retry budget does not absorb it.** Ordinary flake is random, so `retry: 2`535converts it to a pass; a failure driven by machine speed is fully correlated536across attempts — measured on a real PR as 5/5 runs failing all three537attempts. Before writing off an intermittent failure as flake, establish538which kind it is: in local mode, repeat under load and idle, and report the539natural durations alongside the outcomes. The two get opposite verdicts —540flake is a note, a speed-correlated failure is blocking. Make that blocking541verdict expressible in the contract by encoding the margin as a scripted542assertion: measure the natural duration N times and assert it stays on the543side the test needs (here `min(duration) > timer`, because the test fails on544the fast side). A distribution that crosses the threshold then lands in545`fail`, and the existing rule (nonzero `fail` ⇒ not `merge-ready`) carries546the verdict without a special case.547548Note the CI verify job runs on a **shared, loaded** runner, which is the549regime where such a test passes. You cannot reproduce a fast-machine failure550here by repetition; you can only compute the margin and say what it implies.551552**Before calling a survivor vacuous, escalate to a finer mutation.** A553whole-file revert is a blunt instrument: it can remove the _precondition_ a554test depends on, so a perfectly good test goes green because its scenario no555longer occurs — indistinguishable, from the outside, from a test that asserts556nothing. Worked example: a `finally`-cleanup test survived reverting all four557production files, which read as vacuity; deleting the single line558(`inFlightSessionIds.delete(...)`) killed it cleanly. It was doing exactly the559job it was added for. Coarse mutation survived, fine mutation killed ⇒ the560test is fine and the mutation was wrong. Report the finer result, not the561coarse one — a false "your test is vacuous" costs the author more than a562missed survivor.563564And do not generalize from one dead guard to its siblings. A clause that is565unreachable in one call path may be the only thing protecting another —566check each on its own evidence and report the contrast, so "this guard is567dead" is not read as "remove them all".568569**The reverted run must FAIL THE INTENDED ASSERTION** with the behavioural570mismatch the test exists to catch. A revert that breaks the import, the571compile, or the fixture setup produces a red test that proves nothing — an572always-true assertion would look equally "non-vacuous". Quote the failure573message and check it names the expected-versus-actual values; if the revert574cannot reach the assertion, use an interface-preserving mutation (change the575returned value, not the export's existence) or record the vacuity check as576inconclusive.577578### Wire-oracle harnesses579580- Mock-free with respect to the unit under test: real child processes, real581 loopback HTTP/stdio servers, the compiled `dist/` output — never a stub of582 the code being verified.583- When the code under test implements a **known specification or emulates584 another implementation**, the strongest oracle is that implementation585 itself, not hand-written expectations: feed identical input to both and586 compare output cell by cell / field by field, and report the disagreement587 counts for head and base (`PR disagrees on 0 cells, base on 3764`). Lift588 reference tables **verbatim out of the shipped dependency** rather than589 transcribing them. Build the corpus from **bytes captured off a real590 producer** (`git diff --color=always`, a real API response, a real file)591 alongside the synthesized sweeps — real producers emit combinations nobody592 thinks to synthesize.593- Prefer **configuration seams** (a `baseUrl`, an env var, an injectable594 endpoint) over module interception, so a real client talks over real595 sockets. Make the fake peer encode the upstream's actual semantics — the596 rate-limit header format, an unread-only listing, an account-wide or597 asynchronous side effect — because a generous mock that accepts anything598 proves nothing. Add a decoy target wherever "the wrong endpoint was never599 contacted" is part of the claim.600- Assert **both sides of the wire** where a protocol is involved: what the601 peer actually received (method, path, headers, exact body, request count)602 and what the caller observed — plus that stderr stayed clean.603- **When the oracle is an instrument, corroborate it with a mechanism that604 does not use that instrument.** A tool's _report_ about the system is not605 the system: a cursor query, a profiler number, a coverage percentage can606 each be wrong in ways your assertion cannot see. Find a second effect of607 the same physical fact whose failure mode is independent. Worked example:608 the hardware cursor row was read with609 `tmux display-message -p '#{cursor_y}'`, then confirmed by letting the TUI610 exit and printing a marker — anything printed after exit lands wherever the611 cursor actually was, so the marker's row corroborates the query without612 trusting it. Two agreeing instruments turn a measurement into evidence.613- **To exercise real production data safely, interpose a refusing proxy on614 the write path.** Read-only claims about a live system are best tested615 against that system, and the objection is always side effects. Remove it616 mechanically: wrap the client so every mutating call hard-fails, then run617 the shipped script verbatim. A workflow verified this way retur618619…(truncated)