Docs gates
Six gates over prose — citations, documented defaults, documented commands, duplicated facts, and the two hand-maintained indexes.
Six gates in crates/vike-ops/tests/ walk markdown and doc comments rather than code. Every one of
them exists because a specific written claim was measured wrong, and all of them are text-only — no
cargo invocation, no symbol resolution, no new dependency. One is the exception: it runs a process.
Because markdown belongs to no workspace member, the CI derivation cannot reach these gates by
reverse-dependency closure. xtask/src/ci/tables.rs's DOC_GATE_CRATE force-adds the one crate that
owns them whenever a .md file changes. Before that existed, a documentation-only pull request
planned zero crates and not one prose gate ran.
citation_gate.rs — a citation must resolve, and must not pin a line
Four rules. A backticked path anchored at a repository top-level directory must exist on disk; in
`path`'s `SYMBOL` the file must contain SYMBOL as a whole token; a path:NNN that resolves to
exactly one in-repo file is rejected outright; and in `page.md`'s "Title" the cited markdown
must still carry a section with that title.
The ban on line numbers is the interesting part. 26 of the 33 in-repo path:line citations were
stale, and the only ones that had not rotted pointed into frozen vendored code — so a line number
into living code is structurally doomed, and the repair is the symbol form the tree had already
invented on its own. Rule 4 exists because a move rots a pointer the other three cannot see: a
documentation reorganisation left two section pointers behind, both green, one of them sending an
operator triaging a red CI gate to a runbook at an address that no longer held it.
The gate checks the unambiguous subset and declines the rest on purpose — a bare shorthand path is
relative to an unstated base and cannot be resolved without guessing, and a false positive teaches
people to write exceptions. Coverage is therefore partial, a floor test pins it so it cannot decay to
checking nothing, and every blind spot is written down. One exclusion is conditional: a frozen design
note is not scanned unless it declares a **Status:** line, because that line answers "where does
this stand" and a reader takes it as being about now — one such spec declared a gap that had already
been closed, and an orchestrator briefed an agent to implement code that already existed.
docs_constants_gate.rs — a documented default must equal its constant
One operator page said a SIGTERM loses up to 5,000 rows per series. Both numbers on it were right
and the sentence was wrong: the recorder has two in-memory stages and the page named one. The real
bound for a single-series recorder is 70,536 rows — a 14× understatement that survived months.
Each row proves four independent things: that one line of the markdown carries both the row's context
anchor and its number verbatim as the page spells it; that the matcher finds the symbol in the cited
Rust file and the literal beside it equals the claimed value; that the two are the same quantity
(5,000 == 5_000, 30 s == 30, 10µs == 10_000 nanoseconds); and that a row converting between
units names both of them. Without the third rule a row could pin a page number and a constant that have
nothing to do with each other and stay green forever; without the fourth, a row free to assert a 1000×
difference could hide a real disagreement behind a made-up factor.
one_authority_gate.rs — one fact, one authority
That same 5,000 was stated in two documents, and the failure mode is what makes duplication expensive rather than untidy: fixing one file leaves the number alive in the other, where it reads as independent confirmation and grows back.
The rule is deliberately narrow. A bare number is never flagged — years, record ids, ports and
percentages all repeat legitimately, and a gate on bare numbers would report hundreds of coincidences
and earn an exception table until it meant nothing. What is flagged is a number in the same semantic
company twice: the pair (identifier, number), either by proximity to the nearest qualifying
backticked identifier on the line or as an explicit key = number. Measured filters kill the
coincidence classes — single digits are excluded, date-shaped numbers are incident prose, line pins
belong to the citation gate, and digits inside an identifier's own name are part of the name.
unrun_command_gate.rs — a documented command's output, checked by running it
The other five compare prose to a file; this one compares prose to a process. Three consecutive
review rounds on one pull request found nothing but this defect — including an emergency-halt recipe,
on the page whose first heading is "STOP EVERYTHING, RIGHT NOW", whose sed anchor never matched, so
the variable came back empty and the next line failed.
A harvested command is never executed
Harvesting reads markdown to decide which claims exist; it never feeds a harvested string to a
process. Every argument vector this gate spawns comes from a const array in its own source, so the
set of executable commands is fixed at compile time and changing it is a reviewed diff — not
something a markdown edit can reach. Do not "finish the job" by running the harvested pairs: that
would hand anyone who can edit a .md file the ability to run a command in CI. An unrunnable claim is
declared instead, and a test asserts that every harvested claim is either checked or declared, so
the unverifiable population cannot grow unnoticed.
The harvester narrows by adjacency: 3,325 program-shaped candidates, 631 with an argument shape, 78 with a claim phrase somewhere in the block, and 14 with that phrase adjacent to the command. A module doc is one comment hundreds of lines long, so a block-wide search paired unrelated sentences — 64 of the 78 were that.
The two index gates
decision_index_gate.rs holds the decision-records index to the records it indexes. The incident: the
index carried a standing warning that one record had already retracted in its own text, and
nothing compared the two. An index that omits a record is worse than a missing record — the reader
who checks the table concludes the question was never settled and re-litigates it. It gates four
directions plus a floor of three tests, because the gate had three ways to be silently disarmed, and
the status vocabulary is derived from the index's own format table rather than copied into a
constant.
superpowers_index_gate.rs is the same shape over the design-document index, which opened by
claiming to be a 1:1 list and carried six hand-written numbers that nothing checked. Both gates leave
the semantic half alone: no text rule can tell whether a narrative note about a record is still true,
and stating that limit beats implying coverage that does not exist.
How to read a settled verdict is on Decision records.
Settings gates
The environment-variable registry and its two ratchets, plus the two gates that refuse a settings key nothing reads.
Safety gates
Six gates over the mechanisms that stop trading — the kill switches, the graceful stop, the feed-thread stop budget, held divergences, the live-account lock, and every paper mount.