vike
Developer

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.

On this page