What this is
The schema carries 21 refinements beyond the field types. The fixtures below demonstrate 21 of them. Those two numbers are counted separately on purpose: one is read out of the schema source, the other out of what the validator actually returned, and a gap between them means a rule exists that nothing tests.
Passing all of them is not the bar. Every defect this project has shipped passed every rule here — they stop a page being malformed, and nothing in this repository can see a claim that is simply wrong. That is what the citations are for.
tests/fixtures/invalid-franchise.mdx
A franchise built to be wrong in every way the schema knows about.
- field type
entries.3.featToo small: expected string to have >=40 characters - forms must name a peak state
entries.0.forms"dupe" lists forms but marks none of them peak — the rank has to name the state it comes from - kill cross-references must resolve
entries.0.kills"dupe" is credited with killing "not-an-entry", which is not an entry in this franchise - nobody kills themselves
entries.0.kills"dupe" is credited with killing itself - a matchup reading needs a ranked victim
entries.0.kills"dupe" carries a matchup reading for "Unranked Victim", who is not a ranked entry here. A matchup page asks whether the death moves the ranking; there is no ranking to move. Set `of`, or drop the reading. - brief entries are barred from the top of the ladder
entries.0.feat"dupe" is rank 1 with no feat — the top 20 carry the argument. Write one, or move it below 20. - scaled-only citations in a top-two tier (§2.2)
entries.0.citations"dupe" sits in a top-two tier on scaled citations alone. Cite something shown or stated, or move it down. - tier id must resolve
entries.1.tier"dupe" is in tier "nonexistent-tier", which is not one of: top, two - entry continuity must be declared
entries.1.continuity"dupe" claims continuity "not-declared", which this franchise doesn't declare. Known: top-continuity, unused-continuity - entry ids are unique (vote keys)
entries.1.idduplicate id "dupe" (also entries[0]) — ids are vote keys and must be unique - ranks are unique
entries.1.rankduplicate rank 1 — "dupe" collides with "dupe" - off-scale entries cannot be votable
entries.2.eligible"offscale-but-votable" is off-scale (rank: null) so it cannot be votable — set eligible: false - off-scale entries must still be argued
entries.2.feat"offscale-but-votable" is off-scale and has no feat. Off-scale entries are deliberate editorial asides — being unranked is itself the claim, so it needs arguing. - declared continuity must be used
continuitiescontinuity "unused-continuity" is declared but no entry uses it — it would render a filter that matches nothing - a work string names exactly one work
entriesthe work "W" is cited as film and novel — one string naming two works tells a reader to check a source without saying which. Qualify both, e.g. "W (novel)". - armament wielderId must resolve
armoury.items.0.wielderId"arm-a" names wielder "no-such-entry", who is not an entry on this page - armament ids are unique (vote keys)
armoury.items.1.idduplicate armament id "arm-a" — these are vote keys of their own and must be unique - armament ids cannot reuse an entry id
armoury.items.2.idarmament "dupe" reuses an entry id. The two ladders keep separate id spaces so a weapon can be rated without touching its wielder's rating. - armament ranks are contiguous from 1
armoury.itemsarmament ranks must run 1..3 with no gaps — found 9 where 3 was expected - entry armament must exist in the armoury
entries.3.armament"gap" carries armament "no-such-armament", which this franchise's armoury does not list - ranks are contiguous from 1
entriesranks must run 1..2 with no gaps — found 9 where 2 was expected
tests/fixtures/orphan-armament-franchise.mdx
A second file, for the one rule the first cannot reach: an armament reference on a page that declares no armoury.
- an armament reference needs an armoury
entries"orphan" references an armament but this franchise declares no armoury
Not rules
1 of the messages above come from the field types rather than from a house rule — a string too short, a value outside an enum. They are shown because a reader of a failing build sees them, and counted separately because calling them rules is how the advertised count drifted in the first place.