Les deux textes expliquaient que `bun install` ne va pas chercher le pair optionnel. Mesure : pnpm ne le va pas chercher non plus pour un consommateur qui ne le nomme pas (un paquet qui ne declare que le polyfill recoit zero @ng-org, sans avertissement) - mais ce depot-ci l-obtient quand meme, parce que `packages/polyfill` le declare aussi en devDependency. Un `pnpm install` depuis un `node_modules` vide suffit donc a faire construire le bundle du runner, et le message qui demandait d-installer l-ORM a la main etait devenu un faux conseil.
4.5 KiB
type, summary
| type | summary |
|---|---|
| knowledge | 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.
The ORM live-read measurement asks one question of the deployment rather than of this package: does a live useShape read actually start. It is the only runner that imports @ng-org/orm, which the polyfill declares an OPTIONAL PEER - so an application that does not name the ORM gets nothing, and a checkout without the ORM must still be able to run the other three, which is why this runner stands apart from the polyfill suite instead of inside it. It exited non-zero for as long as the deployed broker and the published client packages disagreed on orm_start_graph; since the packages caught up on 2026-09-21 it is GREEN, and a red run now means they have drifted apart again. Do not read the optional peer as "this checkout has no ORM": packages/polyfill also lists it in its devDependencies, so pnpm install here fetches it and the runner's bundle builds with no extra step - measured 2026-09-24 by installing from an empty node_modules. The optional peer governs what a CONSUMER receives, not what this repository installs.
A unit suite cannot replace any of the first three, 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 run 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.
Consequence worth knowing before optimizing anything: the setup runs before any journey, so filtering journeys saves journey time only — the floor stays.
Two runs may overlap — and that was not free
They once could not: the profile was a single shared directory, and a run starting while another was alive destroyed it, which then failed in a way that read as a product defect. Several measurements were lost to it.
Now each run gets its own profile directory and kills the browser holding it when it ends, so concurrent runs — including a consuming application's suite, from its own checkout — are independent by construction. A lock once guarded the shared directory; it treated the symptom, and removing the sharing removed the need. Prefer that shape whenever two runs contend: make the contended thing per-run rather than serialising access to it.
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.