Replay & verify

Verdicts & divergences

The rules are identical for every runtime, live in lib/src/core/verdict.dart, and are pinned by spec/fixtures/conformance/verdicts.json. They are deliberately strict: an attempt that didn't run as recorded is never evidence of a fix.

Attempts

Attempt statusWhen
reproducedThe original failure assertion fired (same signature).
cleanIt ran to the end as recorded: there is a failure assertion to check, every recorded input was dispatched, required restoration happened, no other failure fired, and nothing material diverged.
invalidAnything else: errors, timeouts, crashes without a report, incompatible capsules, no assertion, material divergences.

Batches

bugcapsule runbugcapsule verifyRuleExit
REPRODUCED—Every requested attempt reproduced0
—FIXEDEvery requested attempt clean0
NOT REPRODUCED—Every requested attempt clean1
—STILL FAILINGReproduced, no clean attempt1
FLAKYFLAKYBoth reproduced and clean attempts2
INCONCLUSIVEINCONCLUSIVEAnything else3
INCOMPATIBLEINCOMPATIBLEWrong runtime, or state schema changed3

Attempts that never ran count against the requested number. One clean attempt plus one error is INCONCLUSIVE, never FIXED. The verdict is recorded on your project with the number of valid attempts; a capsule moves to fixed only when every requested attempt was valid.

In regress run: FIXED → pass; STILL FAILING → fail; FLAKY → the test is quarantined (skipped unless --include-quarantined); INCOMPATIBLE → stale (schema changed without a migration); anything else → inconclusive, counted as a failure.

Divergence catalogue

Types not marked expected are material: an attempt with one can't count as clean. Unknown divergence types are material too.

TypeMeansWhat to do
build.gitShaexpectedReplayed build is a different commit (normal for verify)Nothing
screen.sizeexpectedDifferent screen; pointer coordinates were scaledUse the capture device, or add semantic actions
network.unusedexpectedA recorded response was never requestedNothing
network.unrecordedmaterialThe app sent a request the recording never sawAdd an endpoint rule for volatile fields; if a fix changed the requests, capture a new capsule on the fixed path
network.reusedmaterialMore requests with one key than recorded; the last fixture was served againCheck for retries or duplicate calls in the replayed build
network.noBodymaterialA fixture without a body was servedKeep body capture on for that endpoint; raise maxBodyBytes
network.escapedmaterialWeb: a browser request bypassed the SDK to another originInit before code grabs fetch; add static origins to web.allowOrigins
action.unregisteredmaterialA recorded action id isn't registered in the replayed buildRegister it (renamed? registered late?)
text.untrackedmaterialA recorded text id isn't tracked in the replayed buildTrack it before the replay's input reaches it
text.redacted, storage.redacted, state.redacted, route.redactedmaterialA value was redacted on device and replayed as a placeholderFor QA builds, keep test-account tokens out of the deny list, or exclude that flow
pointer.mismatch, selector.ambiguous, selector.missingmaterialWeb: the element at the recorded point isn't the recorded targetAdd stable data-testid / data-bugcapsule-action attributes
pointer.unsupportedmaterialiOS: the capsule needs pointer replayWrap the taps in BugCapsule.action
lifecycle.unsupportedmaterialThe runtime can't drive a recorded lifecycle stateUsually unavoidable (iOS hidden)
restore.<integration>materialA restorer threwFix the restore callback / serializer
restore.missing, restore.missingIntegration, integration.missingmaterialCaptured data has no integration to restore itAdd the same integrations to the replayed build
restore.latematerialA restorer registered after the first input, or storage was read before restorationInit first; register state earlier
bloc.unrestored, riverpod.unrestored, notifier.unrestored, getx.unrestored, mobx.unrestoredmaterialFlutter: state couldn't be injected from a periodic checkpointRegister a serializer; avoid constructor-dispatched events
capability.unsupportedmaterialThe capsule needs a capability the replaying runtime lacksSee the feature matrix
dispatch.errormaterialDispatching a recorded input threwRead the attempt's details
failure.othermaterialA different failure firedIt may be a new bug; inspect it
runtime.mismatchincompatibleCapsule from another runtimeReplay in the right project (-p, --runtime)

Reading a result

$ bugcapsule verify 35f3c4ba-… -n 3 -p examples/shop
  verify #1  PASS  9.6s  pointer, 6 inputs, 6 responses   (×3)
FIXED — failure gone in 3/3 valid runs
recorded: status fixed; runs: replay reproduced 3/3, verify not_reproduced 3/3 (validAttempts 3)
$ VITE_BUGCAPSULE_DEMO_FIX=discontinued bugcapsule verify 5200d4bc-… -n 2
  verify #1  INVALID  8.3s  … unexpected failure: …no recorded response for POST …/api/checkout
INCONCLUSIVE

The second one is correct: the fix makes the app send a request the recording never saw, so strict replay can't prove the bug is gone. Capture a capsule that covers the fixed path.

Login-gated flows

Tokens and OTPs are redacted on device, so replaying a flow behind a login often hits *.redacted and becomes inconclusive (a missing token can send the app down another path). For QA builds, use a test account whose token you deliberately leave out of the deny list, or start the failing flow after login so the token isn't part of it.