Roster exhaustiveness
One canonical venue list, and the completeness tests that force every table and every harness to classify each member — covered, or deferred with a written reason.
The workspace has a recurring failure mode with a single cure. A per-venue table grows, each table
keeps its own private copy of the venue list inside its rows and its tests, and adding a venue forces
nothing globally. crates/vike-model/src/venues.rs records what that cost: four capability tables
each carried their own list, and the lists had already drifted — cTrader was absent from three of
the four.
The one roster
vike_model::VENUES is the authority on "every venue Vike Trader has a bridge crate for", by
canonical lowercase venue id. Its module header states the contract plainly: every per-venue table's
completeness test iterates VENUES and asserts a declared row exists for each entry, so adding a
venue there fails every table's tests until its rows land, and a new bridge crate cannot ship
without declaring its capabilities.
Registry fallback arms still exist for genuinely unknown strings — VenueCaps::UNSUPPORTED,
VenueMarginSupport::UNKNOWN, TifOutcome::NotEmitted, FeeSchedule::Free.
A named row is the declaration that a venue was classified
No roster venue may silently ride a fallback. Its row is always named, even when its value equals the fallback — the named row is the evidence that somebody looked, not that somebody forgot.
The completeness shape
The pattern repeats across the tree under several names, so a grep for any one of them misses the
rest. every_roster_venue_is_classified is the most common — vike_bridge_core::tif, pump_spec,
mainnet and venue_passphrase, plus vike_model::attribution and vike_catalog::symbol.
vike_model's capability tables spell theirs every_roster_venue_has_a_declared_row — venue_caps,
venue_margin_support, venue_amend and venue_rate_limits — while fees and halt_admit each
carry a one-off of their own (every_roster_venue_has_a_fee_schedule,
every_roster_venue_has_a_named_halt_verify_row). Each iterates the roster, asserts per venue, and
names the missing one on failure — never a silent fallback.
The two conformance harnesses
Both crates/vike-bridge-core/tests/ harnesses carry the same gate, and it is stricter than a
presence check. conformance_roster_is_exhaustive and md_conformance_roster_is_exhaustive each
assert four things:
- every deferral reason is non-empty — no blank placeholder deferrals;
- no duplicate venue id appears in the deferred list;
- the covered set and the deferred set are disjoint — no venue is both;
- their sizes sum to
VENUES.len(), and then, per venue,is_coveredexclusive-oris_deferredholds.
The length assertion is deliberately separate from the per-venue loop, so a stray entry in either list — a venue id that is not on the roster at all — trips before the loop even runs. The exclusive-or is the load-bearing check: a new roster venue in neither set fails there, and must be either wired in or deliberately deferred with a reason.
The market-data harness adds a fifth: every covered venue must be MarketPumpSpec::OnDriver in
pump_spec, because a venue needs a live market feed before its book can be conformed at all.
Deferral is a first-class answer
Neither harness pretends to universal coverage, and neither fakes a venue to reach it. A deferral is recorded, never silent, and the printed coverage matrix lists the deferred venues with their reasons beside the grid of cells that actually ran — so what was not proven is as visible as what was.
Name the shape that does not fit, never a feature gate
The exec harness's DEFERRED doc comment carries the incident. The FXCM row read "behind the fxcm
feature; default/CI build is an Unavailable stub — no real event_mapper is compiled to drive", and
every clause was false: the crate's module tree has no cfg at all, its mapper compiled in every
build, and the feature gates only build.rs. The real obstacle was one keyword. The rule that came
out of it: a deferral names the shape that does not fit the seam — a stateful mapper needing a
two-message join, a protobuf-framed event type, a sidecar over JSON-lines — because a feature is a
claim about a build, and a build is exactly the thing a stale reason stops describing.
Reasons that pass that bar read like the surviving rows do: IBKR's mapper is stateful and its fills
need an execDetails plus commissionReport join keyed by exec id over typed, non-JSON reports, so
there is no stateless decode(&Value) shape; cTrader's Open API is protobuf-framed and its event
converter takes proto structs and a symbol map rather than JSON.
The pattern is roster-shaped, not venue-shaped
Venues were the first roster to get this treatment; they are not the only one.
crates/vike-data/src/store_kind.rs's STORE_KINDS is the history store's layout authority, and
crates/vike-data/tests/store_kind_gate.rs derives the live kind set from the code the way the venue
gates derive theirs from VENUES. It landed the same way, too — byte-identical, pinning the
disagreements it found rather than fixing them, because a table that changes behaviour on the way in
cannot be reviewed as a table.
Adding a venue is therefore a form to fill in rather than a scavenger hunt: the sites are discovered one red test at a time, and the scaffold generates a conservative placeholder row in every table so the author fills in content rather than shape. See adding a venue, the exec contract, the market-data contract, and the per-venue pages under venues.
The market-data contract
Three L2-book invariants — monotonic sequence, gap triggers a resnapshot, derived L1 matches the book — run against every covered venue's real book normalizer.
Property tests
Randomized suites over the pure cores where golden fixtures only pin examples — the laws they assert, the never-widen discipline, and why a counterexample seed is committed.