Adding a venue
The scaffold whose work-list is derived from the tree rather than remembered, the gate that keeps it derived, and the shared homes a new venue consumes without modifying.
Adding a venue touches a per-venue table in a couple of dozen files. Nobody knows how many, and that
is deliberate: no count is written down anywhere, because every hand copy of a roster in this
tree has rotted, and the brief that first asked for a scaffold listed nine such sites from memory and
was already far short. just new-venue-sites prints the derived set. just new-venue NAME fills it
in.
The roster is one const, and it fails everything until you classify
crates/vike-model/src/venues.rs's VENUES is the single canonical roster — the authority on
"every venue Vike Trader has a bridge crate for". Four per-venue capability tables had each grown
their own private copy of the venue list inside their rows and completeness tests, and those copies
had already drifted: one venue was absent from three of the four.
The contract now is that every per-venue table's completeness test iterates VENUES, so adding
an entry fails every table's tests until its rows land. Fallback arms stay for genuinely unknown
strings, but no roster venue may silently ride one: a roster venue's row is always named, even when
its value equals the fallback, because the named row is the declaration that the venue was
classified rather than forgotten.
The scaffold, and why its work-list is derived
Each per-venue table declares its own insertion point, next to its own rows, with a marker comment carrying its own template:
// vike:new-venue:row "{venue}" => VenueCaps::…, // TODO(new-venue: {venue}): …
// vike:new-venue:note <free text — a step a generator cannot safely take>The scaffold walks the tree, renders every template, and inserts it above its marker —
crates/vike-bridge-core/src/tif.rs and crates/vike-bridge-core/src/pump_spec.rs each carry one,
next to the rows it belongs to. The generator knows nothing about which tables exist, so a new
capability table joins the form by adding one marker line — no generator change.
crates/vike-ops/tests/new_venue_gate.rs supplies the missing half: an independent derivation of
which files need a marker, so a table cannot join the tree without joining the form. The scaffold
fills in the shape; the gate makes you fill in the content, because every generated row is tagged
and no_scaffold_placeholder_survives_into_main refuses the tag.
How a row site is derived. row_sites calls a .rs file a row site when it names all but at
most ROW_SITE_MISS_TOLERANCE of the ids in VENUES as quoted string literals — the actual
signature of a per-venue table, and nothing else in the tree names the whole roster. The tolerance
matters: requiring every id erased precisely the tables that were already missing a row, and
erased them because a venue had been added, which is the exact moment the form matters.
The tempting alternative — "a #[test] whose body iterates VENUES" — is measurably worse in both
directions. It over-matches on a test that iterates the roster only to mirror another table; and it
under-matches on a file whose roster loop asserts nothing per venue, so a new venue's missing row
there is invisible to a test-shape derivation and to the suite alike. Literals find it; failures do
not.
The derivation is a generous proxy, and three of its sites gate nothing
It answers "does this file carry a per-venue table", not "does this file go red". Three of the sites it finds gate nothing at all for a new venue — a missing row there is a silent wrong answer, which is strictly worse than a red test. A derivation keyed on redness would have skipped exactly those.
Structural files — the workspace members list chief among them — are not .rs and name no venue
literal, so they sit outside the derivation and are pinned by hand in STRUCTURAL_SITES with the
reason each is still checkable elsewhere. The local CI crate roster used to be one of them and no
longer is: it is derived, so a new venue joins the local gate the same way it joins the merge gate —
by being a workspace member. That is a step deleted from the form rather than automated.
A rendered row's compilation is deliberately not gated — that is the compiler's job, and the round
trip (just new-venue foo → cargo check → just new-venue-remove foo) is how a template change is
proven. One formatting trap is gated, because it defeats the scaffold rather than merely looking
bad: a generated row ending in a trailing // comment directly above a marker is rustfmt-unstable —
the formatter re-indents the marker onto the comment's column, the format check goes red on the
tool's own output, and the moved marker then breaks removal. Measured against the real formatter, a
following statement is not a reprieve; only a following comma-separated element is.
The shared homes are consumed, never modified in passing
A venue conversion consumes three shared homes: the venue family drivers, the vike-bridge-core
pumps, and the per-venue capability tables. A missing knob in any of them is a stop-and-report,
not a local patch — the extension lands as one coordinated pull request informed by every consumer's
report.
The playbook for a behaviour divergence found across adapters is two steps, never one. Step 1
declares today's reality: a static capability map, one row per venue citing the adapter code it was
read from, a verbatim matrix pin, and a completeness test iterating VENUES — merged
byte-identical, with contradictions pinned rather than fixed. Step 2 flips behaviour, one venue
row at a time, behind demo smokes.
What the new bridge must satisfy
Two shared conformance harnesses in crates/vike-bridge-core/tests/ machine-check the venue-adapter
contract, each carrying its own roster-exhaustiveness test over VENUES — so every roster venue is
either a covered implementation or a deferred-with-reason row, and a new venue reddens both until it
is classified. bridge_conformance.rs runs one scenario table against each covered venue's real
event mapper folded through the real order FSM, checking the emitter split, no-silent-vanish, and
exactly-one-terminal; market_data_conformance.rs is the market-data twin over each venue's real
frame router.
The contract those harnesses enforce is described on The ExecutionClient seam and Order lifecycle; the per-venue pages live under Venues.