--- type: knowledge summary: Which suite answers which question, what a batch costs, and why two runs must never overlap --- # What each suite judges **The polyfill suite** exercises the published surface against the real broker: capabilities, reads, inboxes, reactivity. It is the one that judges whether the emulation behaves like the target. **The applicative suite** drives the example application through a browser, as a person would — several identities, several pages, assertions on what is on screen rather than on what the library returns. It judges whether an application built on this package actually works, including the sign-in a person really performs. **The reactivity suite** isolates document subscription. A unit suite cannot replace any of them, and none of them replaces the unit suite: they are slow, they depend on live external services, and they cannot enumerate a case space. ## What a batch costs, and why Every `batch` mints its own physical user by driving the wallet application's real interface, then discards the previous one. That is deliberate — identities must not leak between runs — and it puts a floor under every run that no test filter can remove. Consequences worth knowing before optimizing anything: the setup runs before any `journey`, so a filter saves journey time only; and the wallet profile is a single directory shared by every suite. ## Two runs must never overlap Because the profile is shared and each `batch` discards what it finds, a second run started while a first is alive destroys the first — which then fails in a way that reads as a product defect. Several measurements were lost to this before it was made structural. It is now enforced by a lock rather than by discipline, and a browser left behind by a killed run is reclaimed. If you ever find yourself reasoning about "was another run going?", check the lock rather than your memory. That lock guards **this repository only**. A suite belonging to a consuming application, run from its own checkout against the same broker and the same wallet, collides exactly as two of ours would — observed. Until the shared machinery lives in a package both sides use, that collision is invisible to both. ## A bound must be larger than the sum of what it encloses An enclosing deadline shorter than its own steps can only ever fire first, so every failure underneath it reports the *enclosing* name and none of them can name a cause. The sign-in bound sat at 180 s over steps totalling 270 s, and for days every failure said the same four words while the real step stayed anonymous. Days went into looking for a cause the harness was structurally incapable of reporting. So: compute an enclosing bound from its parts rather than picking a number, and size every leaf bound from a **measured** healthy duration recorded beside it. A bare figure teaches nothing and rots without anyone noticing; a figure with its measurement lets the next reader tell a generous bound from a tight one. ## What a run must report whatever happens Declare the journeys and their checks before anything can fail, so a run reports the same number of rows every time. When the count itself moves with the failure — journeys dying and taking their unreported checks with them — two runs are no longer comparable, and a shrinking total reads like a smaller problem instead of a bigger one. The related trap that made it self-perpetuating: discarding the profile was once conditioned on a marker written at the *end* of a batch, so a run killed before writing it left a profile the next run happily reused — and inherited its breakage. Discarding now keys on the profile itself.