3 Oct 2026 (UTC). Step 1 of design/tier-plan.md's queue: the outcome yardsticks (plan section 5, items 2 and 3; design/matrix.md sections 6.3 and 9). This is a report only. Nothing in it reaches the site.
Credit. The tournament lists come from grimstat-corpus at commit 570b8c6. The compilation is published under CC BY 4.0, and the lists were published by their players and organisers on MiniHeadQuarters (miniheadquarters.com). We didn't read the corpus's listhammer/ folder. Everything below is our own counts and averages. It holds no list text and no player or event names.
In one paragraph
From the corpus's last three months (July to September 2026) we placed 382 lists from 23 events across 33 armies. Every list has a final placing and none has a game record: the corpus's top-level files carry final placings only, with no round-by-round results and no win-loss records. We matched unit names to our stubs with the app's own list importer. 6,337 of 7,340 unit lines matched (86%), and every matched name is in docs/data/index.json. We set aside 92 lists because fewer than 85% of their units matched; most were exported from the GW app in French. A unit's lift is the mean finishing percentile of its army's lists that include it, minus the mean of the lists that don't, and it is noisy. Of the 418 units with three or more lists on each side, 33 (7.9%) have a 95% interval clear of zero, where chance alone would give about 21 (5%). There is a signal, but at today's volume it is only strong enough to test the engine across many units at once. It can't grade a single unit. Nobody lets a machine read their faction-versus-faction tables: no candidate's terms allow it:
- Stat Check has no terms and names AI crawlers to keep out in its robots.txt.
- 40kstats (Tabletop Battles) and Goonhammer answer machines with a bot check.
- Games Workshop's Metawatch forbids copying outright.
- Listhammer's tables carry no licence.
All of them are listed in tools/sources.json, switched off. Jordan decides whether to ask.
What was loaded
node tools/outcomes.js --corpus build/grimstat-corpus --out reports/tiers/2026-10-03-outcomes.json runs in about a second. The corpus is cloned at run time (a shallow clone of https://github.com/N041M/grimstat-corpus) into build/grimstat-corpus/, which git ignores like every file under build/.
- Sources read: only the sources switched on in tools/sources.json, which today means
minihq(the corpus's top-level files). They are read through tools/sources/index.js, the same reader build_lists.js uses. - Window: the latest three months the corpus holds (2026-07, 2026-08, 2026-09), the same window docs/data/inclusion.json and design/matrix.md 6.3 use.
--monthschanges it. One event on 4 July is named as a farewell to the old edition and may have been played under it. It is kept and flagged below. - Placing percentile: 1 for first and 0 for last, over the event's field. The field is whichever is larger: the number of lists the corpus holds for the event, or the highest placing seen. The corpus holds nearly every player of these events: of the 35 events it carries, the list count matches the top placing in 25, and is at most two short in the other 10.
- Lists kept: a list is kept when three things hold:
- its army is recognised (all were);
- 85% or more of its units match a datasheet, the same threshold build_lists.js uses;
- its own unit costs add up to 1,500 points or more (
--min-points). This left out 39 lists from 1,000-point events, because squads are judged at 2,000.
- Per unit: a unit is keyed by its name in the army, or by
faction|namefor an allied datasheet. A chapter's Space Marine datasheets count under the chapter's own name. Every Adeptus Astartes chapter is also pooled into one "every chapter" group (poolsin the JSON), because no single chapter has more than 22 lists. - The interval: Student's t at 95%, with the two sides' variance pooled. Pooling stops three lists that happen to agree from claiming a narrow interval. Two things aren't corrected for: lists of one army in one event aren't independent, and many units are tested at once. Read the intervals as a guide.
- Win rate: whenever a source gives game records (
record: { wins, losses, draws }in the source layer's shape), the tool computes the win rate with and without each unit, with its own interval. No source does today, so every win-rate field is null andgameResultssays why.
Coverage
| Count | |
|---|---|
| List entries in the window | 513 |
| Set aside: under 85% of units matched | 92 |
| Set aside: under 1,500 points | 39 |
| Lists kept, all placed | 382 |
| Events | 23 (France and Belgium, mostly 2,000-point singles) |
| Armies | 33 of 36 |
| Unit lines in all parsed lists (matched) | 7,340 (6,337, 86%) |
| Matched names not in our stubs | 0 |
| Distinct army-unit pairs in kept lists | 933 |
| Lists with game records | 0 |
Lists per army:
| Army | Lists |
|---|---|
| Necrons | 32 |
| Adeptus Custodes | 28 |
| T'au Empire | 23 |
| Tyranids | 23 |
| Dark Angels | 22 |
| Emperor's Children | 19 |
| Orks | 16 |
| Chaos Daemons | 15 |
| Aeldari | 15 |
| Death Guard | 13 |
| Astra Militarum | 13 |
| Grey Knights | 13 |
| World Eaters | 12 |
| Adeptus Mechanicus | 12 |
| Imperial Knights | 12 |
| Drukhari | 12 |
| Chaos Space Marines | 11 |
| Adepta Sororitas | 11 |
| Blood Angels | 11 |
| Leagues of Votann | 11 |
| Space Wolves | 10 |
| Ultramarines | 10 |
| Thousand Sons | 8 |
| Salamanders | 7 |
| Chaos Knights | 5 |
| Genestealer Cults | 4 |
| Black Templars | 3 |
| Raven Guard | 3 |
| Imperial Fists | 2 |
| Iron Hands | 2 |
| Space Marines | 2 |
| Deathwatch | 1 |
| Agents of the Imperium | 1 |
| Every Adeptus Astartes chapter, pooled | 73 |
Unmatched names: 1,003 unit lines across 767 distinct names. The JSON's unmatched lists them all, by army, with counts. About four in five come from the GW app's French export ("Ligne 1 : 20 Carnivores Kroot", "Héros épiques 2 : …"), which the importer doesn't read. The rest are spellings and allies the importer misses:
- "Myphitic Blight-haulers" (11 lines);
- "Nurglings" and "Beasts of Nurgle" in Chaos lists;
- "10 Skitarii Vanguards" (a count before the name);
- "Wartrakk";
- "Callidus Assassin";
- "Inquisitor Draxus";
- "Sisters of Battle Immolator" in Imperial Knights lists.
Teaching roster.js these forms would bring back up to 92 lists, about a quarter more data. That is a later card. The prefix forms and counts are a parsing change; French names would need a name table of our own.
Game results: what exists
- The corpus's top-level files (CC BY 4.0, read): final placings only. No rounds, no opponents and no win-loss records.
- The corpus's
listhammer/folder (not read, outside the licence): its README says the folder holds:- a win-loss record for each list;
- for Grand Tournament lists, each round's result and score and the opponent's faction;
- every player's record at each event.
That is exactly what both yardsticks need: the win rate with and without a unit, and faction-versus-faction results we would compute ourselves. It waits on Listhammer's OK (T-411; the permission email is on T-418). tools/outcomes.js will read records as soon as a source that has them is switched on.
The sanity view: top and bottom 10 by placing lift
These tables show units with three or more lists on each side, ranked by lift. The percentiles and lifts are in percentile points, where a list's finish runs from 0 to 100. The samples are small: Tyranids and Orks have only 23 and 16 lists in all.
Tyranids: 23 lists, mean percentile 54; 31 units with 3+ lists each side
Top 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| The Swarmlord | 13 / 10 | 70 | 33 | +37 | 11 to 62 |
| Tyrant Guard | 4 / 19 | 70 | 50 | +20 | -19 to 58 |
| Exocrine | 4 / 19 | 68 | 51 | +18 | -21 to 56 |
| Gargoyles | 3 / 20 | 68 | 51 | +17 | -27 to 61 |
| Tyranid Prime with Lash Whip | 11 / 12 | 60 | 48 | +13 | -17 to 42 |
| Tyranid Warriors with Melee Bio-Weapons | 4 / 19 | 64 | 52 | +12 | -27 to 51 |
| Norn Emissary | 8 / 15 | 61 | 50 | +11 | -20 to 42 |
| Lictor | 11 / 12 | 57 | 50 | +7 | -23 to 37 |
| Broodlord | 9 / 14 | 58 | 51 | +7 | -24 to 37 |
| Norn Assimilator | 5 / 18 | 57 | 53 | +5 | -32 to 41 |
Bottom 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| Zoanthropes | 16 / 7 | 44 | 75 | -30 | -60 to 0 |
| Carnifexes | 4 / 19 | 29 | 59 | -30 | -67 to 8 |
| Old One Eye | 4 / 19 | 32 | 58 | -26 | -64 to 12 |
| Neurotyrant | 17 / 6 | 48 | 71 | -23 | -56 to 9 |
| Von Ryan's Leapers | 12 / 11 | 46 | 62 | -15 | -45 to 14 |
| Hormagaunts | 13 / 10 | 47 | 62 | -15 | -45 to 14 |
| Screamer-killer | 3 / 20 | 43 | 55 | -12 | -56 to 32 |
| Hive Tyrant | 6 / 17 | 48 | 56 | -8 | -42 to 26 |
| The Red Terror | 10 / 13 | 50 | 57 | -7 | -37 to 23 |
| Trygon | 6 / 17 | 49 | 55 | -7 | -41 to 27 |
Space Marines (every chapter pooled): 73 lists, mean percentile 48; 90 units with 3+ lists each side
Top 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| Wolf Scouts | 4 / 69 | 88 | 46 | +42 | 11 to 73 |
| Heavy Intercessor Squad | 3 / 70 | 84 | 46 | +38 | 1 to 74 |
| Venerable Dreadnought | 4 / 69 | 77 | 46 | +31 | -1 to 63 |
| Arjac Rockfist | 5 / 68 | 77 | 46 | +31 | 2 to 59 |
| Company Heroes | 9 / 64 | 68 | 45 | +22 | 0 to 44 |
| Captain | 6 / 67 | 66 | 46 | +19 | -7 to 46 |
| Logan Grimnar | 9 / 64 | 63 | 46 | +17 | -5 to 39 |
| Wolf Guard Terminators | 9 / 64 | 63 | 46 | +17 | -5 to 39 |
| Storm Speeder Hammerstrike | 4 / 69 | 63 | 47 | +16 | -17 to 48 |
| Wolf Priest | 6 / 67 | 62 | 47 | +15 | -12 to 42 |
Bottom 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| Ballistus Dreadnought | 6 / 67 | 19 | 51 | -32 | -58 to -6 |
| Reiver Squad | 4 / 69 | 21 | 50 | -29 | -61 to 3 |
| Vanguard Veteran Squad with Jump Packs | 6 / 67 | 23 | 50 | -28 | -54 to -1 |
| Librarian | 19 / 54 | 28 | 55 | -28 | -43 to -12 |
| Commander Dante | 8 / 65 | 26 | 51 | -25 | -48 to -2 |
| Chaplain with Jump Pack | 6 / 67 | 26 | 50 | -24 | -50 to 3 |
| Librarian in Phobos Armour | 4 / 69 | 27 | 49 | -22 | -54 to 10 |
| Sternguard Veteran Squad | 11 / 62 | 32 | 51 | -19 | -39 to 2 |
| Uriel Ventris | 4 / 69 | 31 | 49 | -18 | -50 to 15 |
| Aethon Shaan | 3 / 70 | 31 | 49 | -17 | -55 to 20 |
Orks: 16 lists, mean percentile 40; 21 units with 3+ lists each side
Top 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| Flash Gitz | 9 / 7 | 58 | 16 | +42 | 17 to 67 |
| Beastboss on Squigosaur | 4 / 12 | 66 | 31 | +35 | 1 to 70 |
| Meganobz | 4 / 12 | 62 | 32 | +31 | -5 to 66 |
| Tankbustas | 6 / 10 | 55 | 31 | +24 | -9 to 57 |
| Bigboss | 4 / 12 | 57 | 34 | +23 | -15 to 61 |
| Painboy | 10 / 6 | 45 | 30 | +15 | -19 to 50 |
| Zodgrod Wortsnagga | 4 / 12 | 50 | 36 | +14 | -25 to 53 |
| Wazdakka Gutsmek | 11 / 5 | 44 | 30 | +14 | -23 to 50 |
| Trukk | 9 / 7 | 44 | 34 | +10 | -24 to 45 |
| Bannernob | 5 / 11 | 42 | 38 | +4 | -33 to 41 |
Bottom 10
| Unit | Lists with / without | Percentile with | without | Lift (points) | 95% interval |
|---|---|---|---|---|---|
| Big Mek Dakkarig | 7 / 9 | 22 | 53 | -31 | -61 to -1 |
| Mozrog Skragbad | 8 / 8 | 27 | 52 | -25 | -56 to 7 |
| Warboss | 9 / 7 | 29 | 53 | -24 | -56 to 8 |
| Beast Snagga Boyz | 5 / 11 | 30 | 44 | -14 | -50 to 23 |
| Lootas [Legends] | 4 / 12 | 30 | 43 | -13 | -52 to 26 |
| Stormboyz | 7 / 9 | 37 | 41 | -4 | -39 to 31 |
| Beastboss | 4 / 12 | 37 | 40 | -4 | -43 to 36 |
| Squighog Boyz | 7 / 9 | 38 | 41 | -3 | -38 to 32 |
| Kommandos | 10 / 6 | 40 | 38 | +2 | -34 to 38 |
| Big Mek with Shokk Attack Gun | 8 / 8 | 41 | 38 | +2 | -32 to 37 |
Reading it
- What looks right: The Swarmlord's lift and Flash Gitz's both have intervals clear of zero, and both units are prominent in the current competitive picture.
- Builds, not single units. Lift often belongs to a whole build rather than one unit. Logan Grimnar and Wolf Guard Terminators share exactly the same lists, and Arjac Rockfist and Wolf Scouts ride with them, so the pooled Space Marine top 10 is mostly "Space Wolves did well". The bottom 10 is mostly "Blood Angels did badly" (Commander Dante, Chaplain with Jump Pack, Vanguard Veterans with Jump Packs). The pool mixes chapter with unit: the per-chapter rows in
armiesare the cleaner read once there are enough lists. - Zoanthropes at the bottom (16 lists with, 7 without) says the lists without them did unusually well. That is a property of seven lists, not a verdict on the unit. Tier-engine rounds that rank Zoanthropes high are not contradicted at this sample size: the interval reaches zero.
- What the engine can use: two things suit the stop rule and the fitter (plan sections 6 and 10, step 5):
- in aggregate, the rank correlation between a letter and its lift across hundreds of units, weighted by the smaller side's count;
- a held-out check: do units the engine grades S/A show positive lift more often than chance?
A single unit's lift should not move its letter.
Faction-versus-faction tables: the terms
Each site was checked on 2 Oct with tools/sources/check.js (its robots.txt and the pages about terms, one request a second under our named agent). statcheck, goonhammer and metawatch were added to its list of platforms, and Stat Check's dashboard, Tabletop Battles and Tableau Public were checked by hand. Nothing was scraped and nothing was stored except our notes.
| Source | What it has | robots.txt | Terms | May a machine read the numbers? |
|---|---|---|---|---|
| Stat Check (stat-check.com/the-meta) | 11th-edition faction and faction-versus-faction win rates, as a Tableau Public dashboard embedded in the page | Lets general agents read the page (keeps them off /api/, /static/, /search and others), and names a list of AI crawlers to keep out entirely | None: no terms, legal or licence page in the site or its sitemap; only a contact address. The dashboard sits on Tableau Public, whose own terms page refused our read (403) | No permission given. Silence isn't a licence, and its robots.txt shows it doesn't want AI-driven crawling. Ask first |
| 40kstats (40kstats.tabletopbattles.com; 40kstats.goonhammer.com redirects there) | Win rate faction against faction, by detachment, by mission | None (the address serves the app page) | None on the stats site. tabletopbattles.com and goonhammer.com answer machines with a bot check (HTTP 202, empty), so their terms are unreadable to us | No. The bot check is a no to machines. Ask first |
| Metawatch (Games Workshop, warhammer-community.com) | Faction win rates in articles | Allows reading | Its terms of use forbid reproducing, duplicating or copying any part of the site without express permission | No, and our hard rule is never to host GW content. Cross-check by eye only |
| Listhammer (/stats) | Faction win rates, from Best Coast Pairings and Tabletop Herald events | Keeps machines off /api/, /events/, /players/ and /list/ | No licence on the site. The corpus's copy of its results is outside CC BY | No, until Listhammer agrees (T-411, T-418). Its data would let us compute faction-versus-faction ourselves, which is the cleanest route |
| Hutber (stats.hutber.com) | Faction stats | HTTP 429 to machines (bot checkpoint) | Unreadable (already noted for T-412) | No |
Verdict: none allows it today. Jordan's 30 Sep rule already treats these tables as a cross-check only (T-332: nothing of theirs on the site, not even a link). That rule still holds.
In what form a yardstick could be stored, if a source agrees:
- Never their table. Their numbers, even reformatted, stay out of the repository and out of docs/.
- Our computed agreement only. From design/matrix.md 9, we would keep the ρ between our predicted pair margins and their pair win rates (over pairs with 30+ games, weighted by games), the Brier score against an army-only baseline, and the number of pairs. Their numbers would be read at run time into memory or
build/, which git ignores, and thrown away. This needs the source's yes for a machine to read it, even though nothing of theirs is kept. - Better: compute faction-versus-faction ourselves from game records a source licenses (Listhammer's games through the corpus, once agreed). Then the yardstick is entirely ours: our counts of games between armies, credited, with no one's table involved. This is the recommendation.
Until one of these is open, the army-against-army yardstick can't run. The per-unit outcomes above can.
Files
- tools/outcomes.js (new): the tool. Its parsing and arithmetic are exported for tests/unit/outcomes.test.js.
- reports/tiers/2026-10-03-outcomes.json: per army and unit, with against without.
poolsholds the Adeptus Astartes chapters pooled,unmatchedthe names that didn't match,coveragethe counts above. - tools/sources.json:
statcheck,ttbattlesandmetawatchadded, switched off, with their terms in our own words, plus a line on Listhammer's /stats. - tools/sources/check.js: the three new platforms added to its list.
Doubts
- The 4 July event named as a farewell to the old edition (26 lists in the corpus) may have been played under 10th-edition rules. It is kept, because the corpus doesn't say which edition an event used.
- An event's size is what the corpus holds. Where an organiser published only some lists, the bottom of the field is missing, and percentiles are a little low for everyone there.
- Lists of one army at one event share a meta, so the intervals assume more independence than there is.
For the supervisor
Ready to paste into design/decisions.md:
3 Oct: T-571 outcomes per unit and the faction tables' terms (queue step 1, yardsticks)
- What changed: tools/outcomes.js reads every placed list from the sources switched on (grimstat-corpus's top-level files, CC BY 4.0;
listhammer/not read) and writes, per army and unit, lists with and without the unit, the mean finishing percentile each side, the lift with a 95% interval (Student's t, pooled variance), and win rates whenever a source gives game records. Report: reports/tiers/2026-10-03-outcomes.md and .json.- Numbers: 382 lists, 23 events, 33 armies (July to September); 86% of unit lines matched, 0 outside our stubs; 92 lists set aside under 85% matched (mostly French exports) and 39 under 1,500 points. Of 418 units with 3+ lists each side, 33 (7.9%) have an interval clear of zero, against about 21 (5%) by chance.
- Game results: none. The licensed files carry final placings only. Round-by-round games exist only in
listhammer/, which waits on Listhammer's OK.- Faction tables: none allows a machine read (Stat Check: no terms, AI crawlers named out; 40kstats and Goonhammer: bot check; Metawatch: no copying; Listhammer: no licence). All are listed off in tools/sources.json.
- Decided alone: the latest 3 months as the window; lists under 1,500 points dropped; the field size taken as the larger of lists held and top placing; Student's pooled-variance t in place of Welch (Welch gave intervals far too narrow for 3-list sides); chapters pooled as an extra view, not in place of the per-chapter rows.
- Use: a yardstick in aggregate only (the correlation of letters with lift, weighted by the smaller side; the S/A hit rate on positive lift). A single unit's lift should never move its letter. If a source agrees, the army-against-army yardstick stores our agreement numbers only, never their table; computing it ourselves from licensed game records is preferred.
- Next: teach roster.js the French export and the missing spellings (up to 92 more lists); Jordan's call on asking Stat Check and Tabletop Battles; the Listhammer reply (T-418).
Ready to paste as a scoring.md changelog line:
- 3 Oct 2026 (T-571): outcome yardstick added, not used in scoring: tools/outcomes.js, the placing lift per unit (mean finishing percentile of the army's lists with it minus without, Student's t 95% interval, pooled variance) and win rate with and without when game records exist (none today); reports/tiers/2026-10-03-outcomes. No published number changes.