Structure gates
Three gates over the shape of the workspace — dependency direction, compile-time paths, and re-spelled primitives — and how to read each one's failure.
Three gates fail on the shape of a change rather than on its behaviour. The rule the first one enforces is explained on Architecture; this page is about reading the failure when one of them goes red, because in all three cases the obvious repair is the wrong one.
layer_gate.rs — dependency direction
Every workspace member declares its rank in its own manifest, and
crates/vike-ops/tests/layer_gate.rs's every_normal_vike_dependency_goes_strictly_down asserts
that for every normal (non-dev) vike-* edge c -> d, layer(d) < layer(c). Text-only over the
manifests, no cargo invocation.
Reading the failure. The edge is the defect; the number rarely is. The ranks in TIERS were
established by reading the real graph — all 275 normal vike-* edges in the tree the gate was
written against — and every one of them satisfied the rule as merged, so the tree had no layering
violation to fix when the gate landed. Raising the rank of the crate that failed will make the gate
pass and will also pull whatever that dependency drags in.
That is not abstract. One normal edge from the headless trading daemon to the GUI's shared crate
dragged the entire egui widget stack — ten crates — into the daemon, for three pure modules. The
cost was never CI minutes: it was daemon build time, binary size, and the cargo deny --all-features
supply-chain audit surface on the box that signs real orders. It was found by somebody noticing build
times.
A rank is a total order; the architecture is a partial one
Where two families are genuinely incomparable the numbers still put one above the other, and the gate then permits the wrong-direction edge in whichever direction was chosen. Two of those choices are deliberate, made toward the mistake that has actually happened: the whole UI stack ranks above everything headless, which is what makes the motivating edge fail; and venue bridges rank below the simulator, so a bridge reaching for the backtester is red while the reverse edge — the far less dangerous one — is not.
Dev-dependencies are exempt, and that is not a loophole: a dev-dep is a test edge, and this workspace
dev-depends upward on purpose. dev_dependencies_genuinely_point_upward asserts the exemption is
still needed, so nobody can fold dev edges into the rule and have it pass for the wrong reason.
[build-dependencies] are not exempt, and optional versus non-optional is not distinguished — an
optional normal dep is still an edge somebody can turn on.
compile_time_path_gate.rs — a running binary may not use its build tree
env! over the manifest directory expands at compile time to the directory of the crate being
built, so the value names the tree the binary was built in, never the project it is later run from.
On the box that built it those two coincide, which is exactly why the defect is invisible everywhere
except in production.
The tree convicted itself before the gate existed: a venue bridge built its sidecar-jar and JVM fallbacks that way, so a binary built elsewhere and deployed into a project folder found neither and degraded to paper with nothing in the log but "bridge jar not found". Both reads were already pinned by the settings registry — and the deployment consequence was gated by nothing at all. Both sites were retired in the batch that added this gate; it exists to stop the class growing back.
Reading the failure. Every use in non-test src/ of a workspace member is keyed on
(file, enclosing fn) and carries a Disposition — a closed three-variant classification, not a
free-text reason column, because these are not exemptions: several are debt somebody should still
retire, and a reason column is an invitation to write a plausible sentence and join the list. The
class that claims mitigation is machine-checked: every_ladder_rung_row_is_corroborated_by_its_file
requires such a file to actually reach the runtime store-root resolver, and requires every other
row's file not to. So the repair is either to route the path through the runtime resolver or to
classify the site honestly — never to add a row that says something plausible.
Three exclusions fall out of deriving the roster from [workspace] members rather than maintaining a
list: vendored third-party code, the excluded protobuf regeneration recipe (whose own compile-time
path is correct — the build tree is its subject), and tests//benches/, where resolving a
committed fixture from the manifest directory is right and a gate arguing with roughly 50 such sites
would teach people to write exceptions.
duplicate_shape_gate.rs — a hoisted primitive may not be re-spelled
The motivating commit added four fresh copies of the closing-side ternary to the backtest engine six days after that helper was hoisted into the shared scalar module. CI was green.
The gate walks the real crates/ tree, masks comments and string literals, and looks for the banned
expression shapes textually. Each shape names the one helper it should call: the closing-side
ternary, and a hand-rolled quantity·price·multiplier product — which resolves to one of three
notional helpers that are not interchangeable, because picking the wrong one silently changes risk
arithmetic.
Reading the failure. ALLOWLIST carries an exact count per (file, shape), and it fails in
both directions: too many sites is a new re-spelling, too few is a stale exemption. So converting an
allowlisted site forces its entry to be revisited rather than quietly outliving its reason. The gate
is textual on purpose and must not become an AST pass — an approximate matcher plus an explicit,
reasoned allowlist beats a precise matcher nobody maintains. Its limits are declared: four-or-more
factor products are margin formulas with their own helpers and are out of scope, and factors are
classified by identifier word, so a price bound as a bare p carries no price word and escapes.
CI derivation
One implementation of what CI runs, its selection rules and diff-base ladder, and the five gates that hold the local recipes and CI to a single spelling.
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.