TASKS.md as cards, read when the pages are built (as of 6 Oct). The file stays the source; this page only shows it.
217Open5Yours212Claude's4Blocked79Parked170Done this week
For Jordan 5
T-418open
Permission emails to send (1 Oct)
More
Jordan (30 Sep). All from hello@tacticalreroll.com; two links per email (the site, /rankings/), no attachments. The drafts in design/sources.md ("Drafts for Jordan") are the source of truth; the first lines only here. In this order: 1. Tabletop Herald, info@code-b.com (confirmed on their imprint; their Discord discord.gg/MqNmSuxVmn as a second route): "Hi there, I'm Jordan, and I run Tactical Reroll, a free mathhammer calculator for Warhammer 40,000…" Most likely yes, so first: an early yes gives the others a "Tabletop Herald has already agreed" line if we need it. 2. Longshanks, admin@longshanks.org (from their help and about pages; Discord discord.gg/2CjNkGHpYa): "Hi there, I'm Jordan… I'd like to ask permission to read the public results of ended 40k events on Longshanks…" 3. Listhammer: no address published anywhere on the site (about, privacy, home and footer read in full 30 Sep). Route: an issue on github.com/N041M/grimstat-corpus (its README invites issues) saying who we are, that we'd like Listhammer's permission to use the listhammer/ part, and asking the maintainer to pass the message on or say how to reach Listhammer, with the Listhammer email verbatim in the issue so it can be forwarded; keep it short and polite, as the corpus's maintainer and Listhammer's author may be the same person. In parallel: a message to the account that posts Listhammer's updates on r/WarhammerCompetitive, if Jordan finds one. First line: "Hi there, I'm Jordan, and I run Tactical Reroll, a free mathhammer calculator for 40k…" 4. T3: the contact form on tabletopturniere.de/impressum (click the operator's name, Matthias "Blackhawk" Althaus; needs a free T3 account), the German draft with the English under it; feedback.tabletoptournaments.net as a fallback. Lowest yield, so last; skip if time is short. First line: "Hallo, mein Name ist Jordan und ich betreibe Tactical Reroll…" Not sent: Best Coast Pairings only if Listhammer says no or stays silent for two weeks (then support@bestcoastpairings.com, asking whether they have a data programme a small free site can join); List Engine and Hutber later, only if these four leave a gap. After sending: a follow-up nudge after two weeks of silence, once; on any yes, reply with thanks and, once it's live, the link to the page where their credit appears; each send date and each answer recorded here (T-411 and T-412 switch on per source as answers arrive). Sent: (none yet) · Answers: (none yet)
T-412blocked
T-409: the platforms' terms, then a reader for each that allows it (waits on Jordan)
More
Checked 30 Sep with tools/sources/check.js on GitHub's runners (.github/workflows/sources-check.yml, by hand; our container's proxy blocks these sites; one request a second as TacticalRerollBot). Findings, in design/sources.md: Longshanks: robots.txt allows reading (20 s between requests), no terms page; its /results/ page is public (players' records and armies, filterable to 3+ wins), per-event faction stats need a login. Tabletop Herald: robots.txt allows everything, no terms page; each tournament page embeds its size, rounds, country and each player's army, but army lists need a login and standings come from an undocumented API; its events already reach us through Listhammer with full results. T3: its terms need written consent to reuse content. tabletop.to: the domain is parked (dropped). List Engine: no terms, one developer on Discord. Hutber: a bot checkpoint. Listhammer's own robots.txt disallows its events and API (so only through the corpus, with their OK). No reader built yet: none states a licence to reuse its data, so each waits on Jordan (the four questions tonight) and, where needed, an email; drafts for Listhammer, Longshanks, Tabletop Herald, T3, List Engine and Hutber in design/sources.md. Each is listed off in tools/sources.json with what was found Jordan, 30 Sep: yes to direct readers wherever robots.txt and terms allow, one request a second (Longshanks asks 20 s), a named agent, no player names, a credit and a link on every page that shows the data, never BCP. From the check (30 Sep): Longshanks (robots.txt and its About page read) and Tabletop Herald (robots.txt, privacy and imprint read; no terms page) allow it; T3's terms (tabletopturniere.de/terms §8) need written consent, so not T3; tabletop.to is gone. Next: the Longshanks and Tabletop Herald readers, each off until switched on Readers, 30 Sep 03:46Z: Longshanks now answers our runner's reads with a bot challenge (a captcha page, HTTP 202) instead of its results, so it isn't open to machines after all: no reader, and we don't work around it; Tabletop Herald's standings only come from an undocumented API and its lists need a login, so a reader would have to use a private API: not built. Both go the email route (drafts in design/sources.md); each stays off in tools/sources.json. The only source open to machines with results today is Listhammer's, through the corpus, which waits on the T-411 email Addresses (30 Sep 04:56Z, check.js --contacts): Longshanks admin@longshanks.org; Tabletop Herald info@code-b.com (confirmed); T3 only an on-site form behind the operator's name on its imprint (for registered users); Listhammer publishes none (routes in design/sources.md). Jordan's warmer drafts (30 Sep) are in design/sources.md verbatim, T3's in German with the English underneath
T-706open
More 11th-edition reviewer lists (Jordan to source)
More
The armies where one more list would help most (single-list armies with many graded units and low agreement); Jordan sources, the panels are added in our own words. The list (5 Oct, from tools/edition.js and the 11e exam): (1) Thousand Sons: one list, 28 units, we score 38% (below chance): a second list says whether it is us or that reviewer; (2) Dark Angels: one list, 77 units, 54%; (3) Death Guard: 36 units, 55%; (4) Astra Militarum: 61 units, 68%; (5) Orks: 45 units, 64%; (6) Space Wolves: 20 units, 51%. Full videos or transcripts preferred (their reasons feed T-705)
T-230open
A plan to climb Google for "mathhammer"
More
Jordan (27 Sep): tacticalreroll.com is last on the first page for "mathhammer", just under honorrollmath.com. Google hides results "very similar" to ones it shows, and honorrollmath.com carries the same title and description as ours, so the two domains compete; a permanent redirect (or taking the old copy down) would give its standing to tacticalreroll.com. That's Jordan's domain (hands off for Claude). Jordan (27 Sep): honorrollmath.com is the kids' app now, so that result is stale and will drop out as Google recrawls it. Then: Search Console, links from the community (Reddit, Discord, YouTube mathhammer channels), and pages for searches we can own (tier lists, unit mathhammer, loadouts, T-225) 27 Sep, Jordan picked: the faction tier list (T-257), best units for the job (T-258) and shorter snippets (T-259), all built. Search Console now comes in weekly (T-309). Left: the wording pass with T-129, and community posts if wanted Jordan, 30 Sep: the wording pass over the static pages on main this week, by T-129's rules; the community posts wait until beta is on main (T-416)
T-073built
Optional: more tournament sources for the winning lists
More
Today's source is European events on MiniHeadQuarters. For US/UK GTs, email Listhammer (listhammer.info), Best Coast Pairings (support@bestcoastpairings.com) and List Engine asking permission. Template: "Hi, I run tacticalreroll.com, a free Warhammer 40k mathhammer site, and I'd like to show a small 'recent tournament-winning lists' panel per faction using your data, with a clear credit and link back to you on every list. Would you be willing to allow this, or point me to an API, feed or licence terms you'd like me to follow?" Jordan, 30 Sep: one email, to Listhammer (the draft is on T-411); none to Best Coast Pairings, ever; the others by T-412's findings
Building now and next 206
Housekeeping and errands
T-316open
Monday 5 Oct: the first weekly "what players test" report and the second analytics pull
More
The meta report's first full week of counts ends 4 Oct; check it renders on /meta/ and in the app's Overview, and read the analytics report for the Monday round-up (T-065). Prep done 30 Sep: the report is one command on main, node tools/report.js (the tournament data's coverage, T-413; the goals still to add, T-317; the cards by status; the last weekly analytics), and the weekly workflow posts it to its run's summary after the Monday pull. T-398's data audits and placings tiers (beta) join it when beta comes to main
T-761built
CI red on main (6 Oct): Tests' docs drift, Deploy beta's file size, the daily refresh's tests
More
Built on branch ci-fix (from main 36d189db65), three commits, waiting for Jordan's push (git push origin ci-fix:main). (1) Docs drift: /lab/reports/ runs tools/report.js, which read reports/analytics/2026-10-05.md raw; a Windows checkout's CRLF put "\r" into nine lines of the committed page (2eea84ad27), so CI's LF build always differed. report.js now folds line endings to LF; the page rebuilt as CI builds it (pinned BSData and corpus): drift-free. The weekly-analytics and daily-demand bots commit files the site renders (reports/analytics/*.md through report.js; pulse.json into unit pages and the Lab) with [skip ci], so main drifted after each one: both workflows now fetch the pinned BSData and corpus, run CI's four build commands and commit docs/ with the report, and keep nothing when the rebuild fails. (2) Deploy beta (red since #265): Cloudflare Pages refuses files over 25 MiB; beta.yml publishes a copy of docs/ without them, a warning naming each (today docs/lab/ledger/space-marines.json, 47.4 MiB; beta's Lab goes without it; T-762). (3) Refresh 11e data (red since #14, 4 Oct): the three failing tests assumed the data of their day: points (Termagants and Tyrant Guard's points), detachments (pairs that fit the Detachment Points) and /meta/'s army filter (Tyranids with a winning detachment in the last two months; the months Jun to Sep). T-579 moved two to the 30 Sep update, but that reached main after the last run (#16 tested main at 45bdf24945); all three now read today's values from the data, and pass on the pinned BSData and on BSData's 5 and 6 Oct heads with the newest corpus. No site bug.
T-762open
The Lab's Space Marines ledger file out of docs/ (47.4 MiB; found by T-761)
More
docs/lab/ledger/space-marines.json is 12,326 forms over 84 units (Librarian 940: every size, loadout, detachment and leader; forms merge across the 16 detachments only when dice and rules match), each with kill and killNC per horizon and class (44% of the bytes), lend (10%), rules, weapons. The Lab's ledger page never loads it (index.html fetches tyranids.json and benchmarks.json only): it is a working file of the ledger tools (ledger.js, charge.js, burst.js, killrobust.js, dgchecks.js, ledger-steps.js, ledgercheck.js read it from docs/), published to both sites for nothing, and close to GitHub's 50 MB file warning. Proposed, not done (the ledger's frozen-input rules): write it to build/ledger/space-marines.json like the other seven armies (the tools' path helpers already know build), or to a tracked folder outside docs/ if it must stay in git; the page unchanged. Slimming alone doesn't reach 25 MiB: dropping lend and weapons (unused by the page) 40.5 MiB, byClass as arrays 30.0, two decimals 27.5. Until then beta deploys without it (T-761)
T-763open
Re-bless the visual baselines after review
More
The Visual workflow's screens drifted from tests/visual/base/ since 3 Oct (the T-579 data and the pages since). Review the drifted shots and diffs its runs upload, then run Visual by hand with mode "update" on the branch that should carry them. Its commit adds or renames files only when screens change, and /lab/reports/ lists the screens: a run that adds or drops a screen also needs the pages rebuilt (T-761's bots do this; Visual doesn't yet)
Money
T-042open
Switch the affiliate links on
More
Needs T-041. Fill in docs/shop.js IDS, rebuild pages, bump the SW cache; check our eBay link format against the one EPN's link generator makes
T-067open
Add each approved retailer to docs/shop.js
More
A search link plus the network's deep-link wrapper per store, shown in the visitor's region. Needs T-066
Meta data sources (Jordan via the design session, 30 Sep: "expanding where you are pulling tournament data and similar for our meta ratings")
T-409open
Epic: several tournament sources for the meta ratings
More
The Meta score (T-198), the tracker (T-161), the army tiers (T-257, T-332) and the sparklines (T-387) all rest on one source, grimstat-corpus's MiniHeadQuarters lists: 150–190 lists and 6–10 events a month, almost all French. That's why the tiers went wrong and why they're off. Goal: several sources, many more events, coverage by region, so per-army counts can carry tiers again. Sub-cards: T-410 the source layer; the Listhammer part (win-loss lists and every event's results; needs Listhammer's OK); the platforms' terms and readers (Longshanks, Tabletop Herald, tabletop.to, T3; List Engine and Hutber by permission); the coverage report. Never Best Coast Pairings directly (its EULA; Jordan won't email them). Stat Check, Goonhammer, Listhammer's /stats and GW's Metawatch are a monthly cross-check only (T-332), never copied into our data
T-451built
The ⓘ unit sheet (Jordan, 30 Sep, via the design session; main, and the face-off label on beta)
More
The header gains the unit's pixel figure beside the tier badge; scores, tags, wounds-per-100-pts bars and rating stay; the Simulate box goes; it ends with one gradient button "Open the unit page ›" and a text link "Compare in Rankings ›". It stays a sheet (reading, not editing). The T-383 face-off button becomes a small text link "📷 Share picture: default vs best loadout" under the best-loadout chip (or in the 🔗 menu), here and on the unit page Built on main 30 Sep: the header carries the unit's figure beside the tier (figHead); from a calculator card ({ onCalc: true }) the Simulate box goes; the foot is "Open the unit page ›" in the site gradient and "Compare in Rankings ›", which opens the tier list on the unit's army. From the tier list, 🔍 and My army the Simulate box stays (there it is the way into the calculator). The face-off picture's label (T-383) is beta code: it lands with the merge (T-452). Test in unitcard.test.js
T-454built
CLAUDE.md and design/streamline.md up to date (beta, for review)
More
The accordion, docked row, lists on the card, four toggles, Simulate row, corner pill, new keys and link bits, the removed surfaces; the repo tree for tools/sources/, icons.js, pixelfit.js and the rest Main's half done 30 Sep: CLAUDE.md (layout.js, myarmy.js, the ✨ key's opener) and design/streamline.md (the pieces table, a built note for T-434 to T-439) match the work branch; beta's half comes with the merge (T-452)
T-465built
The weapon-effects gallery lives on the site (Jordan, 30 Sep, via the design session; main)
More
All the families in one place he can revisit any time: design/weaponfx-preview.html stays the source of truth, and tools/stamp_assets.js (run by build_pages.js, so CI's drift check keeps it current) copies it to docs/lab/effects/index.html with noindex and the site's title pattern; not linked, not in the sitemap. It shows only our own effects and weapon names (names only, no rules text or profiles), so it sits within the hard rules. Every gallery round updates the source, so the page moves with the push; the artifact keeps updating too until Jordan says the page is enough Built 30 Sep:https://tacticalreroll.com/lab/effects/; design/weaponfx.md names the address. CLAUDE.md's layout line waits for the design session (the build session's edits to CLAUDE.md are stopped by the permission check)
T-579built
The 30 Sep update isn't in our data yet: refresh BSData, fix Frantic Focus (Jordan, 2 Oct (Pacific): "see if we are already using them or not yet")
More
Checked 2 Oct: GW updated 16 PDFs on 30 Sep (14 faction packs, 2 Legends-only packs; links and versions in reports/dataslate/2026-09-30.json, the PDFs not kept: GW data). The site, beta and the branch use BSData 374f505 (10:16 UK, 30 Sep); BSData added the update at 15:17 and 16:02 that day ("20260930 MFM and Faction Packs"), fixed through 2 Oct (cc1830f). Moving to it changes 240 units (188 points, 131 profiles), a balance update. The daily refresh's branch hasn't moved since 13:01 UTC on 30 Sep. To do: (1) refresh to BSData's latest on beta, rebuild rankings and pages, record the patch; (2) data/detfx.json, Emperor's Children Frenzied Host: Frantic Focus is now Sustained Hits 1 for Battleline after advancing or falling back, not +1 S (scoring.md changelog); (3) Legion of Excess's stratagem note: Thieves of Pain is now Rapturous Agony; (4) find out why the daily refresh stopped. Report: reports/dataslate/2026-09-30.md. Built 3 Oct (local commit, not pushed): BSData 374f505 → 8b91e6b (3 Oct, 06:44 UK); index, detachments, rankings, pages rebuilt and stamped; the patch recorded (241 units: 188 points, 132 profiles; a balance update, /meta/patch/; 21 letters move; Detachment Points changed on 9 detachments). Frantic Focus → Sustained Hits 1 (detfx.json; moves no score, only Frenzied Host's synergy line); Legion of Excess's note names Rapturous Agony. The refresh: see the report (main's own tests fail on the new data, so the run never pushes). Tests fixed for the new data: points (Termagants 60 → 55), detachments (Lords of the Forge 2 DP), hill (risers keyed by id, tools/hill.js; the verdict line awaited). Ledger outputs byte-identical (frozen inputs). Matrix test's 0.5% gap: T-606. Report: reports/dataslate/2026-10-03-refresh.md.
The queue (29 Sep, from the design session; in this order)
T-560built
Tier test round 8: step grading, fixed letter cuts in two views, the sliders page (Jordan, 2 Oct, via the design session; child of T-524)
More
Built 2 Oct (local commit, not pushed): tools/tiertest.js switch r8 (over r7, so round 7's Feed the Swarm cap and one-drop Tyrannocyte stay), --r8json, --r8sliders: (1) score = best allowed job + V8C.ELITE_STEP 5 for each other allowed job ≥ V8C.ELITE 90, + V8C.STRONG_STEP 2 for each ≥ V8C.STRONG 80, never capped, ties by the mean of the others; (2) letters on fixed cuts over every army (S ≥ 95, A ≥ 88, B ≥ 80, C ≥ 65) and a within-army view by position (10/15/25/30/20%); strict on our ruler now against the within-army view; (3) the Lab's sliders page, docs/lab/reports/tiers/sliders/ (four sliders, the view toggle, three yardsticks live; data reports/tiers/2026-10-02-round8-sliders.json, a copy beside the page). Numbers (7 → 8): Tyranids ρ 0.50 → 0.47, strict on our ruler 20 → 20 within army (chance 10.6), raw 12 → 19 all armies, within one 29 → 31, Tyranid list share 0.39 → 0.41, every unit 0.164 → 0.162, Space Marines −0.05 → −0.18, Orks −0.13 → −0.12; 561 of 1,161 letters move (520 up); 246 S on the fixed cuts (Space Marines 35 of 84; ρ between an army's S share and its top-quarter lists −0.37). Tyrannofex S → A (94.4: its Hammer is 89.2 in round 8's pool). Report: reports/tiers/2026-10-02-tyranids-round8.md. Open: the fixed cuts make S common in armies with many similar datasheets; Jordan's read of the cuts.
T-562built
Tier test round 9: Anvil divides by points once and gets a survival gate; the sensitivity sweep; every version against three panels; cut calibration (Jordan, 2 Oct, via the design session, from the independent critique's points 4, 5 and 9; child of T-524)
More
Built 2 Oct (local commit, not pushed): tools/tiertest.js switch r9 (over r8), --r9json, --sweep, V9C { GATE1 0.5, GATE2 0.8, PEAK 0.75, REVIVE 1.15, SETDOWN 3 }: (1) Anvil = hits to destroy × OC ÷ points (the geometric mean was rank-identical to round 8, so not used) × the gate on Hammer's turns alive; (2) the sweep, 39 constants at low and high; (3) V2b to round 9 rerun against Auspex, Tista Minis and Sprues & Brews, with an SVG chart (tools/md.js shows images, build_pages.js copies them beside a tier page); (4) proposed frozen cuts S 98.79, A 94.49, B 86.13, C 71.15 (8/15/27/30/20%), not adopted. Numbers (8 → 9): Tyranids ρ 0.47 → 0.49, Space Marines −0.18 → −0.10, Orks −0.12 → −0.12, every unit 0.162 → 0.077, Tyranid list share 0.41 → 0.45; 240 of 1,161 letters move; S-share ρ with top-quarter lists −0.37 → −0.39 (current cuts), −0.24 (proposed). Fixed on the way: mwkOf's cache keyed on variant objects (a memory leak across runs). Report: reports/tiers/2026-10-02-tyranids-round9.md (data beside it, .json). Open: Jordan's call on the proposed cuts; Runner's points gate and the movement geometry are the most sensitive constants.
T-563built
Tier test round 10: Anvil as OC-turns per point, a fieldable pool, the final rank on frozen cuts, the list-share headline, the explainer (Jordan, 2 Oct, via the design session, from the method appraisal's experiments 1 to 4; child of T-524)
More
Built 2 Oct (local commit, not pushed): tools/tiertest.js switch r10 (over r8), V10C { TMAX 5, MAXPTS 2000, S 92.02, A 77.02, B 49.96, C 20.03 }, --r10json, --explain "<unit>" [--army] [--out] (standalone about 25 s): (1) Anvil = OC × min(turns alive, 5) ÷ points × Battle-shock × position × revive, round 9's gate and its constants removed; (2) the list-share headline (per-army median ρ, AUC, hit rate, ρ among taken, the Tyranid overfit gauge) over every version V2b to 10; (3) units over 2,000 pts out of the pool (8: six Titanicus Titans, the Phantom, the Manta); (4) final score = the pool percentile of the composite, letters on cuts frozen at 8/15/27/30/20%; (5) eight explainer files (reports/tiers/2026-10-02-explain-*.md). Numbers (9 → 10): Tyranids ρ 0.49 → 0.51, Space Marines −0.10 → −0.07, Orks −0.12 → −0.18; pooled list share 0.077 → 0.157; per-army median 0.113 → 0.165; AUC 0.536 → 0.582; Anvil's ρ with points 0.41 → 0.19 (target ±0.15: missed); no unit over 1,000 pts at S; S-share ρ with top-quarter lists −0.39 → −0.16. Report: reports/tiers/2026-10-02-tyranids-round10.md (data beside it, .json). Open: Anvil still leans to cost (0.19); the S-per-army check stays negative; the explainers point to three missing raw quantities (hidden arrival, protection lent, Battle-shock's worth) and three weights.
T-564built
The Tyranid workbench: the raw table and compose(raw, config) (Jordan, 2 Oct, via the design session: no more every-army runs until the Tyranid table is within one letter of Auspex for 85% of its 44 graded units; the method appraisal's experiment 7; child of T-524)
More
Built 2 Oct (local commit, not pushed):node tools/tiertest.js --rawdump tyranids (about 35 s) writes build/tier-raw-tyranids.json, reports/tiers/2026-10-02-tyranids-raw.json and .csv: every Tyranid per detachment, Norn branch, datasheet size and BSData leader pairing (15 pairs, scored as one unit; leaders' own attacks also delivered inside each squad), each way in, plus every pool unit's chosen context; tools/tierbench.js compose(raw, config) (about 12 ms a run) with every knob of the appraisal's spec and ten switches, one per residual-report finding (--fix a,b, --all, --config, --check). With every switch off it reproduces round 10 exactly (52 of 52 Tyranids, every other unit's composite). First pass: within one 31 of 44 (70%) baseline, 30 all on (ρ 0.51 → 0.71), best of the 1,024 subsets 36 (82%); the line is 38. bannerLendOnly alone 35; bestSize costs (all but it: 34). 14 units two or more letters off all on. tools/banner.js's onSquad exported (lendOnly, models; its report unchanged). Report: reports/tiers/2026-10-02-tyranids-bench-1.md. Next: the 14 gaps from section 3; reasons for each switch beyond the panel; the null columns (loadouts, Battle-shock lend) Superseded on the gate (3 Oct, T-580): the every-army gate is lift over shuffle on every fitting panel, none worse than its frozen predecessor; 38 of 44 within one of Auspex is a bench sanity number only.
T-565built
Tier test round 11: the workbench's fixes made permanent, every army, and the investigators' bugs (Jordan, 2 Oct, via the design session: best legal size, turns capped at 5, a CP 30 pts; child of T-524)
More
Built 2 Oct (local commit, not pushed): tools/tiertest.js o.r11 switches, all on, every unit of the pool: bannerLendOnly, bannerModelCount, turnsHarmonic, reArriveAfterKill, reArriverGateExempt, lendOnBestSquads (every lend on the two squads of its army it lifts most; stand-ins and the Feed the Swarm cap gone), stealthSurvival, bestSize, bestLeader, sameLoadout (effects on the squad the damage uses); --r11json (standalone, about 2 min: each fix alone, all on, all on but one). Bug fixes: docs/bsdata.js rule links read as a bare unit has them (76 unit rows, 21 traits; tests/unit/ruleslinks.test.js) and a model's maximum raised by another's count (Neurogaunts 22); the aura pattern widened; tools/banner.js matches squads ignoring case; tools/tierbench.js banOf takes off the stored multiplier; --rawdump holds a lock and renames into place (--raw-out). Numbers (round 10 on today's data → 11): Tyranids ρ 0.52 → 0.66, Space Marines −0.07 → 0.14, Orks −0.19 → −0.15; per-army median 0.139 → 0.173; AUC 0.582 → 0.585; Tyranid within one 31 (ruler) / 36 (Auspex's shape) of 44, the 85% line not reached. Live data: 21 units lose a trait (Utility only; no letter on the site moves); Neurogaunts 11 to 22. Not done: wargear costs (documented, scoring.md's gaps). Report: reports/tiers/2026-10-02-tyranids-round11.md (data beside it, .json). Documents from the worktrees folded in (design/decisions.md, the brief and critique, residuals 1 to 3, benches 2 to 4).
T-566open
The charge distance in the 11th edition (residuals 1's claim: charges played 2" too long)
More
Checked 2 Oct (T-565), Wahapedia's 11th-edition core rules with their errata and FAQ: engagement range is 2" horizontally and 5" vertically; a charge declares on an enemy unit within 12"; the 2D6 roll is the charge move's maximum distance, and only enemy units within that distance can be charge targets; after the move the unit must be engaged with every target. The rules' own example: a unit beyond the roll can't be a target even when the move could end within 2" of it. So the claim doesn't hold: a charge from a gap of d" needs a roll of d, as tiertest.js pChg (and V4's pCh) play it; a Deep Strike charge (set up more than 8" away) needs a 9, 28%, as design/scoring.md says. No change needed to the calculator or the tier test. One small difference found: Deep Strike sets up "more than 8"" away; V3C.DS places it at 9" for range checks (the charge roll needed is the same). Jordan: close, or move V3C.DS to 8" for ranges.
T-567built
The tier engine in plain English: design/tier-engine.md, the living description of the engine (mission, what other fields do, the four layers and why, each layer in detail, the validation harness, where we are, glossary) for a reader who knows nothing of the game (Jordan, 2 Oct, via the design session; child of T-524)
More
Written 2 Oct (local commit, not pushed): about 5,000 words, built on design/tier-plan.md v2, its critique, scoring.md, decisions.md, the brief, the method appraisal and round 11; five ideas taken from sports ratings, credit scorecards, search ranking, critic aggregation and chess-engine tuning. CLAUDE.md points to it; it is updated in the same commit as every tier round ("Where we are today", section 7 since v3, each round). Not in the Lab's docs list yet (tools/build_pages.js LAB_DOCS, a code change for a later card).
T-568built
The attacker × defender matrix: layer one of the tier engine against the real field (Jordan, 2 Oct evening, via the design session; child of T-524; design/tier-plan.md v3 sections 3.1, 8 and queue step 3a)
More
Every real weapon profile (about 3,000) against every distinct defender profile (a few hundred once grouped), as expected damage per attack in each context that changes it (cover, a detachment's effect, a leader's buff), by ev.js's closed form, cached by the BSData commit's and the engine's hashes. A report and a cached file only; nothing on the site; no hypothetical profiles. Spec: design/matrix.md (sections 2 to 5, build plan 11). Round one of three built 3 Oct (local commit, not pushed; steps 1 and 2): tools/matrix.js (profiles: attacker dice keys, defender grouping keys, mixed units' groups, the id indexes, the keywords not read in full; the cell kernel: profileVs, the renewal count, variance, for one context) and tests/unit/matrix.test.js. At BSData 374f505, pool 1,161 rows: 2,121 attacker dice keys (spec 2,125; 1,304 ranged, 817 melee), 2,164 with range kept (2,207); defenders 608 on the spec's basis (614), 681 on the full key (expected 650 to 750); 75 mixed datasheets (98). Parity of r with profileVs 1.8e-15 on 10,000 real cells; e within 0.474% of killDP's 300-attack volley; section 10's cells to 3 decimals. 34 keywords not read in full (24 unread, 10 read in part) touch 136 of the 2,121 profiles. Round two of three built 3 Oct (local commit, not pushed; steps 3 to 5): context keys (H of the canonical merged modset per weapon and for the defending side; conditions on the target kept in the key) and each pool unit's contexts (own and army rules always; no detachment or each of its army's, every way to answer their choices; alone or joined; marks off), deduplicated, each unit carrying its context count (detachments × joins × sizes × ways in): median 24, 90th percentile 150, max 2,992 (Bladeguard Veterans; distinct dice contexts median 8, max 374); 2,174 attacking modsets, 107 defending, 32,867 (profile, modset) rows. node tools/matrix.js --build --bsdata <checkout> writes build/matrix/<hash12>/ (layers base, cover, half, half-cover, charge; attackers, defenders, contexts, the unit table, manifest): 55.8 MB in 38 s (the unit table 26 s of it). The unit table: 1,161 units × 681 target columns, kills and P(destroy) from killDP's pAll, exported from docs/ev.js with no change in behaviour (ev and volley tests unchanged and passing; rankings.json rebuilt byte-identical). 14 matrix tests. Built, three rounds: round three 3 Oct (local commit, not pushed; steps 6 and 7 and a fix to round two): vouched conditions (when.manual) are a flag on the modset, met for the tier test's round 5 shares (objective 50%, Synapse 75%, below half and below Starting Strength 35%, Battle-shocked 20%; any other condition still all game) instead of always: of 969 units with one on an attacking effect (532 with a share below 100%), the pooled lift over never-met falls from 3,409 to 2,834 wounds per 100 pts (574 of 20,441). Step 6: cells addressed by content; a build copies every cell an older folder with the same cell hash holds (engine, effects files, layers; not the commit or rankings.json); one army's edited catalogue (a copy) computes 1 row and 1 column, 2,801 of 1.44 M base cells, 1.8 s against 13.9 s, byte-identical to a build from scratch. Step 7: node tools/matrix.js --check: parity 1.8e-15 (float32 read-back 5.2e-8); today's Offense from the cells with the unit pages' squads (rankings.json lt), a volley read with VOLLEY.FRESH = 0.5 and a normal taper: ρ 0.9968 to 0.9999, medians 0.02% to 1.57%, 2 pairs beyond 15% (Corvus Blackstar into Terminators, mixed damage; no panel-graded unit). Report reports/tiers/2026-10-03-matrix.md (--report); scoring.md section 7b; 18 matrix tests. Next: T-569 reads it; the keywords not read in full are T-582.
T-569built
The allocation rule and meta states (Jordan, 2 Oct evening, via the design session; child of T-524, follows T-568; design/tier-plan.md v3 sections 3.1, 3.2 and queue step 3b)
More
The allocation rule (each weapon fires at its best target in the field, with a cap on the fire one target absorbs; the game-plan sheet's pairing to start), and meta states clustered from winning lists by composition, each with its field and prevalence; a tier list per state, the generic one as their prevalence-weighted aggregate. List data enters only through the field, labelled; a unit's own score never uses its own popularity. Spec: design/matrix.md (sections 6 to 9, build plan 11, open questions 12). Steps 1 and 2 built 3 Oct (local commit, not pushed): tools/field.js (+ tools/field-names.json, 277 French and variant unit names; tests/unit/field.test.js), corpus 570b8c6, BSData 374f505. Step 1: 512 lists read, 447 in the field's pool (45 under 1,500 points, 18 off their size by more than 5%, 2 under 85% matched); 99.70% of unit lines matched (99.94% top quarter; roster.js alone 86.3%; the 208 French lists 74% to 99.4%). Step 2: the top quarter (115 lists) holds one stable state at seed 569: its best split, 4 states (heavy infantry, armour, mixed, monsters; silhouette 0.23), misses the 0.6 bar by 0.005 on one state and passes at 4 other seeds; every list (447) keeps 4 stable states (Jaccard 0.76 to 0.85). Report: reports/tiers/2026-10-03-field.md (states: -field-states.json). Decided 3 Oct (supervisor and design advisor): v1 runs on one meta state, the whole top-quarter field; the k = 4 split becomes four lenses by composition (each state's class shares re-weight the whole field; tools/field.js lensField). Step 3 built 3 Oct (local commit, not pushed): tools/allocate.js (the allocation kernel: soft best target, β, saturation and spill, reach; the reference battle; the matrix read through one interface, live or stored cells) and tests/unit/allocate.test.js (7 tests) plus a lens test in field.test.js. β = ∞ and one round give the game-plan sheet's best targets 54/54; the matrix's own choices agree with the sheet 65%, 61%, 59% at β 3, 6, 12; anti-tank fire into chaff 1.30%, 0.32%, 0.04% of round one; a five-round battle of the 630-unit field 0.75 s. Lenses: heavy infantry (43%), armour (heavy vehicles 45%), mixed (light vehicles 30%, chaff 13%), monsters (39%), names provisional. Report: reports/tiers/2026-10-03-allocation.md. Waiting on: Jordan's names for the lenses and the proposed meta-state convention (report, For the supervisor); then step 4 (X as a marginal entrant) after T-568 step 7.
T-570built
The baseline battery and locked panels: list share, points and frozen round 11 on every panel, each as lift over a shuffle (design/tier-plan.md section 2 and queue step 1; child of T-524)
More
Built 3 Oct (local commit, not pushed): tools/tierbaseline.js (its own script; reads docs/data/rankings.json and round 11's JSON only): per panel, ρ with a bootstrap 95% interval, within one and same on the reviewer's shape, each as lift over 2,000 shuffles; list share alone, points alone (both signs), frozen round 11, the shuffle; the pooled held-out ρ, round 11's per-army median, the two-reviewer ceiling (no pair yet); fixed seeds; unmatched panel units listed. A panel file's "locked": true is counted, not scored, without --final-check (tests/unit/tierbaseline.test.js); none of the three is locked. Numbers: held-out pooled ρ (Marines + Orks, 75 units) list share 0.21 [−0.03, 0.44], points ±0.01, round 11 −0.03 [−0.27, 0.20]; round 11 beats list share on 0 of 3 panels; per-army median 0.173 (10 of 27 under 0.10). Verdict: near zero on several armies: redesign the jobs (T-568, T-569) before fitting. Waiting on Jordan: the new lists to lock. Report: reports/tiers/2026-10-03-baselines.md (.json beside it).
T-574built
A locked panel: Auspex Tactics' Astra Militarum tier list (11th edition, about 11 to 12 Sep 2026), transcribed from Jordan's summary (Jordan, 3 Oct UTC, via the design session; child of T-524; design/tier-plan.md section 5 item 4)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-03-astra-militarum-auspex.json, "locked": true (one of the plan's two locked panels: nobody scores it or compares it with the engine until the final check). Tier 1 to 5 as S to D; 61 datasheets graded after expanding the families (S 26, A 15, B 10, C 9, D 1; Leman Russ, Rogal Dorn, Baneblade and Heavy Weapons families recorded in groups). Unmatched: Tempestor Prime (no datasheet; the command squad is graded) and "Lord Marshal Draxus" (best guess Lord Marshal Dreir, not graded). BSData 074eabc (5 Sep), the site's commit on the video date; no Astra Militarum points moved since, so patchDrop is empty. Open: the video's title, address and exact date, and the Draxus name, if Jordan has them.
T-577built
Reference only: a video discussion of Leagues of Votann units from early April 2026 (10th edition), transcribed from Jordan's summary (Jordan, 3 Oct UTC, via the design session; child of T-524)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-03-leagues-of-votann-discussion.json. Locked at first as the plan's second held-back panel; unlocked the same day once Jordan dated it to early April 2026, before 11th edition came out in June: "use": "reference: 10th edition, not scored or fitted, not compared with the engine", "locked": false, nothing of ours scored against it while locked. 13 datasheets graded (S 2, A 6, B 4, C 1); Hearthguard's C and Grimnyr's D second mentions kept in variants; Hearthkyn Theyn unmatched. Not patch-aligned (10th edition). With no more lists the second lock is waived (supervisor's call; Jordan: "use what you can"); Astra Militarum (T-574) is the one locked panel.
T-578built
Reference only: a Leagues of Votann tier list from about Oct 2025 (most likely 10th edition), transcribed from Jordan's summary (Jordan, 3 Oct UTC)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-03-leagues-of-votann-2025.json, "use": "reference: …", "locked": false; never scored, fitted or compared with the engine, and never a fitting panel (it is the locked panel's army). tools/tierbaseline.js now skips any file whose use starts with "reference", even at --final-check (unit tests added). 17 datasheets graded (S 2, A 5, B 5, C 4, D 1); the two Steeljacks loadouts are separate datasheets in our stubs, so both are graded. Unmatched: "Grimnyr Strategist" (Grimnyr or Memnyr Strategist; ungraded). No BSData commit exists for its date. Reviewer against reviewer, across editions, with T-577: ρ 0.14 on 10 shared units (within one 7, same 5).
T-572built
Jordan's tier conventions written into the documents, and a root package.json (Jordan, 2 and 3 Oct, via the design session; child of T-524; design/tier-plan.md queue step 2)
More
Written 3 Oct (local commit, not pushed): design/tier-plan.md section 4 (every convention frozen: minimum size, turns capped at 5, every lend on the two squads of its army it lifts most, the site's job names, the matrix's six answers; β joins the fitted weights) and section 10 step 2 done; design/tier-engine.md (conventions, the healer item closed, site job names in the glossary, "Next" item 2 done); design/matrix.md section 12 decided (questions 1 and 3 overridden by Jordan: the top-quarter lists make the field; β = 6 to start, fitted later, leave-one-army-out, never on locked panels) with sections 6.3, 6.4, 7.2, 7.6 and 8 to match; design/decisions.md 3 Oct entry, sections 3 and 4. package.json at the root ({"type": "commonjs"}, Node's default made explicit; the Workers keep their own "type": "module").
T-573open
Rename the jobs to Jordan's site names (Jordan, 2 Oct): Tank (was Anvil), DPS (was Hammer), Support (was Banner), Action (was Runner)
More
In the engine (tools/tiertest.js, tools/tierbench.js, reports' headings), design/scoring.md, design/tier-engine.md and anything the site shows; the data's "Support character" label is renamed (e.g. "joining character") so the job owns the word "Support". Internal names stay until this card.
T-571built
Outcomes per unit and the faction-versus-faction yardsticks (step 1 of design/tier-plan.md v3 queue, data items 2 and 3; child of T-524)
More
Built 3 Oct (local commit, not pushed): tools/outcomes.js reads every placed list from the sources switched on (grimstat-corpus top level, CC BY 4.0; listhammer/ not read) and writes per army and unit: lists with and without, mean finishing percentile each side, lift with a 95% interval, win rates whenever a source gives game records; reports/tiers/2026-10-03-outcomes.md and .json; tests/unit/outcomes.test.js. 382 lists, 23 events, 33 armies (July to September), 86% of unit lines matched, none outside our stubs; final placings only, no game results in the licensed files. 33 of 418 units clear zero (chance: about 21): a yardstick in aggregate, never per unit. Faction tables: none lets a machine read them (Stat Check, 40kstats/Goonhammer, Metawatch, Listhammer, Hutber); statcheck, ttbattles and metawatch added to tools/sources.json switched off. Waiting on: Jordan whether to ask Stat Check and Tabletop Battles; Listhammer reply (T-418) for game records; a later card to teach roster.js the French export (up to 92 more lists).
T-575built
A second Space Marine panel: Auspex Tactics on the generic datasheets (about 2 to 3 Sep 2026), and the two-reviewer ceiling against Tista Minis (Jordan, 3 Oct, via the design session; child of T-524; design/tier-plan.md sections 2 and 5 item 4)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-03-space-marines-auspex.json, a fitting panel (not locked): 46 units with the reviewer's seven-step grade (ord) and a letter, Company Champions unmatched (no stub). BSData commit in force: 0c02471 (2 Sep, from BSData's commit list; our history starts 19 Sep); patchDrop empty, but 3 to 19 Sep is unchecked. Ceiling on 15 shared units: ρ 0.69 (95% 0.33 to 0.92), √ρ 0.83; the two videos straddle the 30 Aug points update. Report: reports/tiers/2026-10-03-space-marines-ceiling.md. Not scored against our engine (another card's battery).
T-576built
A Tyranid fitting panel: Hivemind Hobbies (11th edition, about early July 2026), and the Tyranid two-reviewer ceiling; closes step 1 of the queue (Jordan, 3 Oct, via the design session; child of T-524; design/tier-plan.md sections 2, 5 items 4 and 7, 10 step 1)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-03-tyranids-hivemind.json ("locked": false, fitting): 49 graded (S 4, A 12, B 19, C 11, D 3; "Detachment Dependent" as B, a condition rather than a rank), Sky-slasher Swarms unmatched (Legends). BSData 80568cc (2 Jul); points 2 Jul to 30 Sep read from BSData's files at run time, four units in patchDrop (Pyrovores, Neurotyrant, Tyrannocyte, Toxicrene). Ceiling against Auspex's Tyranids: ρ 0.73 [0.53, 0.86] on 38 units, √ρ 0.86 (about two months apart). T-575's points gap checked: no Space Marine move 2 to 30 Sep. tools/tierbaseline.js honours patchDrop and gives ceilings an interval; the step-1 round's report reports/tiers/2026-10-03-step1.md (held-out pooled ρ over 166 units: list share 0.41, round 11 0.19), with scoring.md, decisions.md and tier-engine.md section 7.
T-582built
Keyword reader gaps: Cleave, the Anti-Monster/Vehicle spellings, unhyphenated Twin Linked, Rapid Fire D3 and the rest; the weapon keywords docs/ev.js doesn't read, or reads only in part (T-568 round two, 3 Oct; child of T-568)
More
34 keywords at BSData 374f505 (node tools/matrix.js --unread), ranked by the corpus lists holding a unit they touch (grimstat-corpus, July to September, every placing, lists of 1,500+ points; list points = lists × the unit's minimum-size points, a floor; T-569's top-quarter field isn't built yet): (1) Lethal Hits only against non-Monster/Vehicle, read as unconditional (26 profiles, 39 units, 141 lists, 17.6k list pts; Orks mostly); (2) Cleave 1, 2, 2+, 3, 4, unread (25 profiles, 24 units, 99 lists, 15.1k; Boyz, Battlewagon, Ghazghkull, Kommandos, Squighog Boyz, Gorkanaut, Stompa); (3) Sustained Hits D3, read as 1 (13 profiles, 14 units, 41 lists, 8.7k; Avatar of Khaine, Commander Dante, Doom Scythe, Armiger Moirax); (4) Blast X (Blast 1, 2, 3), read as plain Blast (23 profiles, 24 units, 68 lists, 6.6k); (5) Conversion (5 profiles, 7 units, 25 lists, 4.8k; Knights, Hekaton, Thunderkyn); (6) the Anti-Monster/Vehicle spellings (Anti-Monster/Vehicle 2+, 3+, 4+; Anti-Non-Monster/Vehicle 2+, 3+; "Anti Vehicle 3+" without its hyphen; "Anti-Fly 2" without its "+"), unread (19 profiles, 15 units, 35 lists, 4.5k; Beast Snagga Boyz, Breaka Boyz, Hunta and Kill Rigs, Stonesinger, Mozrog); (7) Rapid Fire D6+3 and D3, unread (5 profiles, 9 units, 14 lists, 4.5k; four Knights, Deathwatch kill teams); (8) Twin Linked without its hyphen (5 profiles, 11 units, 4.0k; Aeldari vehicles, Hellions); (9) a stray "-" keyword on 5 profiles (Deathwatch Terminators, Kastelan Robots, Lychguard, Blight-hauler: data noise, nothing to read); (10) the one-offs: Harpooned (Norn Assimilator), Reverberating Summons (Great Unclean One), Devastating Wounds and Sustained Hits 2 against Monster/Vehicle, Infantry or non-Monster/Vehicle, Linked Fire, Overcharge, Plasma Warhead, Defensive Array, Hive Defences, Psychic Assassin. Each one read changes ev.js's numbers, so it moves the live Offense numbers and letters: per keyword, before it ships, list the live units whose Offense moves and by how much (rankings.json before and after), add a scoring.md changelog line, and run the stamps on Linux. Until then each stays in the matrix's dice key as x:<keyword>. Built 3 Oct (local commit, not pushed): 27 of the 34 read in docs/ev.js and the calculator's dice engine with the same counts: each keyword read whole ("Psychic Assassin" isn't Psychic); an ability followed by keywords counts only against a target that meets them (Lethal Hits, Devastating Wounds, Sustained Hits); Anti-X in every spelling, and the calculator now reads every weapon's Anti-X against the target's keywords (it read none, only the manual pick); Blast X, Cleave X, Sustained Hits D3, Rapid Fire D3 and D6+3 (the calculator's field takes dice), Twin Linked, Conversion (beyond half range), Psychic Assassin (6 attacks into a Psyker), Overcharge (hazard fails on 1 to 4). Not read, not dice: Harpooned, Reverberating Summons, Linked Fire, Plasma Warhead, Defensive Array, Hive Defences (and the data's lone "-"). 81 units' Offense moves (71 live; Stonesinger +57.2%, Gunwagon +41.0%, Deffkilla Wartrike +31.9%, Warboss in Mega Armour +30.0%, Night Spinner +22.8%, Beastboss −16.6%); 16 letters (9 live by their own numbers: Gunwagon, Warboss in Mega Armour, Ancient in Terminator Armour B → A, Stonesinger C → B, Night Spinner and both Acastus Asterius D → C, Wartrakks and Wazbom Blastajet C → D; 3 live by re-sorting). Each keyword's own moves from ten isolation builds. The ledger: byte-identical with this ev.js (sha256, all nine outputs); with the matrix.js edit only meta.engine moves, committed outputs kept. tests/unit/keywords.test.js (hand values, parity with the dice engine, the two readers on every weapon); scoring.md changelog line per keyword; report reports/tiers/2026-10-03-keywords.md.
T-580built
Housekeeping from Jordan's 3 Oct answers to the design review: the Ork panel retired, a third Tyranid reviewer, "held out" defined, the gate written, the review's paste text applied, the supervisor's stop rule and the two sessions' protocol (Jordan, 3 Oct, via the supervisor; child of T-524)
More
Built 3 Oct (local commit, not pushed): reports/tiers/panel/2026-10-02-orks-spruesandbrews.json moved to panel/retired/ (tools/tierbaseline.js reads it only with --sanity, a sanity list outside every pool, ceiling and gate; tools/tiertest.js's hand list drops it, the old rounds' Orks columns print "–", and adds Auspex's Space Marines and both new Tyranid files); tierbaseline.js classes panels (held out = armies never tuned on, same army and new reviewer, tuned, sanity), pools held out, new reviewer, unseen and fitting, gives an army with three reviewers the ceiling √ of the mean pairwise ρ, and runs the gate (gates: pooled lift over shuffle on the unseen panels rises above frozen round 11's with a paired bootstrap interval; no fitting panel more than one SE below); unit tests added. Jordan's ruling: the two new Tyranid files are two creators' lists, both fitting (attribution uncertain, noted in the files); the second drops Neurotyrant, Tyrannocyte and Toxicrene for points (builder's call, flagged). Gate text in tier-plan.md, tier-engine.md, tier-briefing.md (with the two sessions' protocol, section 3) and T-564; the list-share correction (entrants', not top-quarter) in decisions.md, tier-plan.md, the baselines report and tierbaseline.js; tier-plan.md section 2's cross-army checks and floor; matrix.md 7.5's size note, T-569 step 4's test and question 7 (not yet asked); decisions.md entries for the answers and the stop rule. Battery: held out (76 units) entrants' list share 0.23, round 11 0.30; same army, new reviewer (86) 0.72 and 0.38; unseen (162) 0.49 and 0.34; Tyranid ceiling √0.65 = 0.80. Report: reports/tiers/2026-10-03-step1.md, addendum (.json: reports/tiers/2026-10-03-battery-t580.json).
T-581built
Round 12: a compose-only round from the design review (reports/tiers/2026-10-02-design-review.md, "The next round"; first carded as T-575, then T-579) (Jordan, 3 Oct, via the supervisor; child of T-524)
More
Built 3 Oct (local commit, not pushed): tools/tierbench.js knobs cost.alpha, cost.runner (both, gate, cheap), composite (best, allowed, log2), fixDet, and every unit's result (every); --check still reproduces round 10. tools/round12.js runs the six experiments in about 7 s, choosing each setting on a rule that reads no panel (the supervisor's "choose, then check") and checking it with the gate. tools/tyrtable.js prints the Tyranid table in a fixed layout every round (Jordan). Numbers: nothing passes the gate. The chosen candidate (Runner's points gate dropped) takes ρ(score, points) −0.16 → +0.10 pooled, but unseen pooled ρ falls 0.27 → 0.16 (frozen round 11 0.34) and Mortarion and friends return as mission units (round 3's failure, tried twice: flagged). Gate kept and cheapness part dropped: −0.10, agreement unchanged (+0.01). α 0.75 alone: cost-neutral pooled. Per army, 18 to 22 of 29 outside ±0.15 in every version. List share counted both ways orders the rounds the same within one SE. Option premium +0.33 [−1.26, 1.94]. The sweep's top ten have owners. Report: reports/tiers/2026-10-03-round12.md (.json beside it), reports/tiers/2026-10-03-tyranids-round12-table.md. Next: carry "cheapness part dropped" into the matrix's first comparison as one change; card the four unowned constants (gate, half weight, Advance and objective and reserve distances).
T-583built
Config and compose over every army, reproducing round 11 exactly (tier plan queue step 4, first half; Jordan, 3 Oct, via the supervisor; child of T-524)
More
Built 3 Oct (local commit, not pushed):node tools/tiertest.js --rawdump all (about 40 s, peak 2.0 GB) records every context round 11 scores into build/tier-raw-all/<hash12>.json (70,376 rows, 73.6 MB, git-ignored; latest.txt names it; per-process temporary name; the engine's B0 and B1 keys stored as a check); tools/tierconfig.json (every knob of layers 2 to 4 with value, default, source, date, owner; the layer-1 constants for provenance; profiles B0 = round 11, B1 = Jordan's conventions, minimum size); tools/tierbench.js composeAll (about 130 ms), loadConfig, checkR11; tools/round13.js; tools/tyrtable.js shows the baseline when nothing is adopted and the trial in a last column, with the site's job names; tests/unit/round13.test.js on a 200 KB cut (tests/fixtures/tier-raw-mini.json.gz). Reproduction: 0 differences over 1,153 units and 52 Tyranids; battery identical. Frozen round 11's ρ(score, points) −0.414 (the review's −0.42). B1: unseen 0.33 (round 11 0.34), lean −0.30. Round 12's experiments 3 and 4 on B1 (rules that read no panel): Runner's cheapness part dropped + α 0.6, lean −0.01, unseen 0.36, rise +0.01 [−0.10, 0.13]: not adopted. Report: reports/tiers/2026-10-03-round13.md (.json), reports/tiers/2026-10-03-tyranids-round13-table.md. Next: step 4's second half, the field in place of the five targets, measured as one change on B1.
T-584open
The unsourced constants: a source or a fitter slot for each, never dropped (round 12's sweep and T-583's config; child of T-524)
More
Each in tools/tierconfig.json with its owner. (1) Runner's points gate (runner.gateFull 75, runner.gateZero 260, V4C, King of the Hill's weight classes; sweep 363 letters): derive it from the points of units that do actions in the corpus's top-quarter lists (grimstat-corpus, the field T-569 builds), else a fitter slot; owner: builder, then fitter. (2) Runner's half weight (runner.half 0.5, V2C.RUNNER_HALF; 162): fitter slot. (3) Advance (V3C.ADV 3.5"; round 9's sweep 288): source, the core rules (the mean of a D6), read on Wahapedia's 11th-edition page and cited in scoring.md. (4) The objectives' distances (V6C.OBJ_MID 15", OBJ_FAR 21", OBJ_DEEP 27"; 286): source, the mission pack's layouts (our measurement, no text kept). (5) Strategic Reserves' arrival distance (V3C.RES 18"; 206): source, the rules' arrival limits and the deployment maps. (6) A Lone Operative's survival (runner.lone ×2; 78): sourced once the matrix's reach rule (matrix.md 7.3) plays the rule, fitter slot until then. (7) Hammer's peak weight (V9C.PEAK 0.75; 60): fitter slot. (8) A spawn's Runner share (runner.spawn 0.5): fitter slot. (9) the estimates in layer 1 with no source: the revive ×1.15 (V9C.REVIVE), below half 0.35 (V3C.BELOW), within Synapse 0.75 (V5C.SYN), near a debuff 0.5 (V5C.NEAR), the position floor 0.5 (V6C.POS_FLOOR): each a source (the matrix or the corpus) or a fitter slot. The steps, cuts, turns cap and a CP's price are Jordan's conventions, not on this card. Done when every row of tierconfig.json names a source or a fitter slot, and scoring.md says which.
T-585open
Are the four jobs real? A factor analysis and a free fit (Jordan, 3 Oct, via the supervisor; child of T-524)
More
On the every-army raw table at round 11's settings (T-583: build/tier-raw-all/<hash>.json from node tools/tiertest.js --rawdump all; each fieldable unit's B1 context from tools/tierbench.js compose(raw, loadConfig("B1")), its row's f and its ways' v): (a) PCA with a rotation and the scree, every fieldable unit, about 30 raw variables standardised: how many dimensions explain the spread, what loads on each, and where the hand gates (joining characters barred from Action and Tank, no-OC units, one-use units) sit against them; (b) a regularised ordinal fit on all raw variables, leave-one-army-out over the fitting panels, as the ceiling benchmark; (c) the four-job composite on the same held-out panels beside it, with bootstrap intervals. Decision rule, written before the run: if (b) beats (c) by more than the interval, the jobs are redesigned from the factors (Jordan names them); otherwise the jobs stay, with that evidence. Nothing tuned on a panel; the locked panel stays shut; the matrix version runs later.
T-586built
The size oddity: why DPS (Hammer) falls at minimum size (the pause analysis's "one oddity"; Jordan, 3 Oct, via the supervisor; child of T-524)
More
Done 3 Oct (local commit, not pushed; analysis only, nothing adopted): the every-army raw table (BSData 374f505) composed at B0 and B1, each unit's DPS move split exactly into five parts (pool shift, context re-picked, per-turn damage per point, turns alive through delivery, way in). Hive Guard 86 → 65 and Termagants 52 → 30 are all turns alive (T 4.62 → 2.70, 3.52 → 2.09; per-turn damage per point unchanged). Zoanthropes compose at 79 at B1 (with the Neurotyrant), not 65: their 85 → 79 is mostly the leader's fixed points diluting per-turn damage. The Tyrannofex's 84 → 88 is the pool shift alone (median Hammer raw 218 → 195). The two Space Marine squads lose about as much to turns but a leader pair re-picked at minimum size lifts them (Hellblasters 84 → 91, Bladeguard 92 → 97). Over 298 multi-size units, ΔDPS on the parts R² 0.82: turns 58% (61% of the absolute moves), context 21%, per-turn 10%, way 6%, pool 4%. Answer: (a), mostly turns alive: Hammer is damage per point × survival in absolute hits (round 5), so minimum size costs a multi-model damage unit about 20 points; minimum size is neutral only with a size-free survival term, which the matrix's mirror (a hazard per wound) is, for a reason unrelated to targets. Report: reports/tiers/2026-10-03-size-oddity.md (with a paste-ready decisions.md line).
T-587built
Plan v4 step 1a: the Tyranid numbers and the walk (design/tier-plan-v4.md sections 2, 3, 6, 8; Jordan, 3 Oct, via the supervisor; child of T-524)
More
Built 3 Oct (local commit, not pushed; a report, nothing adopted, no form chosen): tools/v4walk.js (node tools/v4walk.js --write, about 8 s): the 52 Tyranid pool units at minimum size, best legal loadout (most field offense) and, for the record, default wargear; offense ranged and melee (enemy points destroyed per 100 points in one phase into the top-quarter field, each gun by the one-phase targeting rule at β 6, every charge landing) and into each of seven unit classes; defense per point from the field's shooting and fight phases (field points spent to destroy it per 100 points, the unit as a marginal target of the targeting rule) and from six weapon classes; defense absolute (share of the field's shooting phase); lends (banner.js lend-only, and in field points); mortal wounds per use (kindfx.json, mortalfx.json); body and reach. The walk: offense, defense, the scaled sum and the geometric mean, each with ρ against the three reviewers (SE, lift; Auspex tuned on), ρ(score, points) and within one as lift over shuffle. tools/tyrtable.js walkTable (the fixed layout); tools/allocate.js keeps weapon names on its groups; tests/unit/v4walk.test.js (parity of 200 cells against ev.js, worst 2.2e−16; a second run byte-identical; volleys equal allocate.js's; shares conserved). Report: reports/tiers/2026-10-03-v4-step1a.md (.json), reports/tiers/2026-10-03-tyranids-v4-walk-table.md. Next: a full stop. Jordan reads the order against the reviewers unit by unit before step 1b or anything else runs. Not computed: spawning (no numbers in the effects files), Tyrant Guard's reverse lend (noted in words only).
T-589built
The points ledger, card 2: the slider page (Jordan, 3 Oct; design/ledger.md)
More
A Lab page, noindex: the 52 Tyranids re-sorting live as sliders move (line weights, the Kill premium, melee lands, holding, the 10% combo threshold, rule multipliers, band cuts), each unit's ledger expandable, its best form re-picked live, Auspex beside it and the other two Tyranid lists faded; standing lines: ρ against each reviewer, ρ(value, points), the knob count. Built 3 Oct at /lab/ledger/ (docs/lab/ledger/index.html, ledger.js's compute), tested on a made-up fixture (tests/fixtures/ledger-tyranids-sample.json, tests/unit/ledgerpage.test.js); waits for T-588's tyranids.json, and shows a "the engine hasn't run" message until then. Score, Actions and Hold start at 0.
T-588built
The points ledger, card 1: the engine for the 52 Tyranids (Jordan, 3 Oct; design/ledger.md)
More
A typical 2,000-point army from the top-quarter meta; Kill (enemy points removed in one turn, best targets first, limited by what that army holds) and Soak (enemy points that must activate to remove it, the enemy's best tools first, limited by what it carries), per 100 points; OC and actions per 100; every form (size, loadout, detachment, alone or joined to a leader); rules sorted into dice (computed), positional (a multiplier on their line) and army-wide (a multiplier on the total). Writes the JSON the ledger page reads. Pre-dataslate data (BSData 374f505), Tyranids only. Built 3 Oct (local commit, not pushed): tools/ledger.js (node tools/ledger.js --write, about 15 s) writes docs/lab/ledger/tyranids.json (section 11's contract; 1,106 forms, 2.5 MB) and reports/tiers/2026-10-03-ledger-engine.md; tests/unit/ledger.test.js (parity of 200 cells against ev.js 4.4e−16; a second run byte-identical; the contract; supply conserved; a Norn Emissary's Soak against a toy army by hand). metaKillRate 0.444 at β 6; thin classes: aircraft and other (absent). Next: T-589, the page.
T-591built
The ledger's sliders searched against the Tyranid reviewers, honestly (3 Oct, via the supervisor; child of T-589)
More
Built 3 Oct (local commit, not pushed; a report, nothing adopted): tools/ledgercrack.js (node tools/ledgercrack.js --write, about 48 s on 12 threads): the five line weights, melee lands, β and one multiplier per kind of rule carried by 3+ units (11 kinds), coordinate search from 40 seeded restarts per fit, every Value through ledger.js (valueForm, re-checked with compute()); tests/unit/ledgercrack.test.js. Report: reports/tiers/2026-10-03-ledger-crack.md (.json). Numbers: best Auspex ρ 0.65 (defaults 0.19), unseen -hivemind 0.40 and -second 0.32, ρ(Value, points) 0.35; 20 shuffles × 8 restarts reach median 0.42 (0.16 to 0.55), the real search 0.64 at that budget, above all of them; 40 distinct solutions, 10 near the best, agreeing on order at median ρ 0.95; Objective B 0.50; leave one out 0.57 / 0.35 / 0.33; pinned: melee lands, Lone Operative, debuffs. Nine units stay two bands or more from Auspex. Flagged: a kind's multiplier compounds over a form's rules of that kind on the page.
T-590open
The charge model (Jordan, 3 Oct)
More
Replaces the ledger's "melee lands" slider per unit: the tournament layouts' distances, Move and Advance, the 2D6 charge odds, Deep Strike, Scouts and Infiltrators, transports. Not started.
T-592open
Deployment distances for the charge model (Jordan, 3 Oct)
More
11th edition's tournament deployment maps read on Wahapedia (understood, never copied): each zone as our own polygon, the gaps between zones and to objectives in inches, averaged; tools/deployments.json and a report. Feeds T-590.
T-593open
Sight lines on the tournament terrain layouts (Jordan, 3 Oct; later)
More
The share of shots blocked between deployment zones on the standard layouts (GW, WTC), by ray-casting over terrain footprints, if the layouts are public in a usable form (T-592 reports on that); a scenario block for the ledger, never a free slider.
T-594open
The ledger's rule multipliers compound per rule (a unit with three command-point rules at ×3 got ×27): count each kind once, rerun the search (Jordan, 3 Oct)
More
Fix in docs/lab/ledger/ledger.js; the search report (T-591) rerun before and after.
T-595open
Spawning measured in points for the ledger (Jordan, 3 Oct)
More
tools/spawn.js: expected points placed per turn per 100 points (Biovores' and Sporocysts' spores, the Tervigon's Termagants and any other Tyranid spawner), the rules as our own numbers in tools/spawn-rules.json.
T-596open
Wire the charge model (T-590) and spawning (T-595) into the ledger (Jordan, 3 Oct)
More
Each form's own "melee lands" from tools/charge-tyranids.json and a Spawn line from tools/spawn-tyranids.json in tools/ledger.js and the page; rerun the search; republish the page.
T-597open
Threat-weighted Soak and the solo comparison (Jordan, 3 Oct)
More
A tough unit only soaks fire if the enemy must shoot it: Soak × threat (its non-Soak lines over the army median, capped at 1) under a slider; each unit compared with the reviewers on its best solo form, combos listed apart; the search rerun; tools/ledger-best.json.
T-598open
Role scarcity (Jordan, 3 Oct)
More
Kill into a target class weighted by how short the army is of good killers of it (the Tyrannofex as Tyranids' scarce anti-tank); tools/scarcity.js.
T-599open
Leave-and-come-back and healing received, measured into Soak (Jordan, 3 Oct)
More
The Red Terror's return to reserves (exposed less often), tunnels for others, regeneration in Assimilation Swarm and other healing; tools/soakfx.js.
T-627built
Fighting back gets its own modifiers, and the defender equips for the unit it fights (Jordan, 3 Oct: "the defender tab for fight back is missing everything that it needs for modifiers, it should really have all the options the attacker has, plus a couple more, like cover… hive guard vs hekaton… they don't have things like anti vehicle 2+. They don't have that auto equipped either")
More
Built 3 Oct, on beta: (1) the defender's Modifiers part has its defense set and a FIGHT-BACK set for its strike-back: +attacks, crits on, Anti-X, +1 hit/wound/Strength, Lethal, Devastating, Sustained, Ignores Cover, Precision, re-rolls to hit and wound (backModOverrides, one set for the unit, laid over each of its weapons' own keywords by mergeBack in strikeBackParams); (2) the attacker's Modifiers part has a DEFENSE set for when the target shoots or fights back: cover (on by default, as before, unseen until now), −1 AP, −1 to hit, −1 to wound, −1 damage, half damage (defModOverrides by the attacker's own unit, which the strike-back already read); both show as ↩ chips on their side's line while Fight back or To the death is on; (3) the defender auto-equips into the attacker (defVsPick, goal "vs"): its guns when the attacker shoots, its melee when it fights, solved again when the attacker, a size or the mode changes, while nobody has edited it; "Best into <attacker>" heads its Auto-equip menu. Hive Guard into a Hekaton now carry 3 shockcannons (Anti-Vehicle 2+) where the all-rounder gave impaler cannons. Tests: browser/fightback (new), browser/autoequip (the defender's default is now "vs").
T-628built
The folded Modifiers line shows the detachment's effects (Jordan, 3 Oct, screenshots of a Hierophant in Crusher Stampede: "The detachment modifiers are not being visible from the collapsed modifier row when added")
More
Built 3 Oct, on beta: the line under each card (layout.js cardLines) took only the leader's buffs and the unit's own abilities; a detachment's effects in play ("Re-roll hit rolls of 1") showed only inside the open Modifiers part. Now they ride the line too, as ⚡ chips that switch off on a tap (detOff, as in the open part), and count in the Modifiers head's badge; an army rule joins once it's switched on (they start off). Test: browser/modline.
T-600open
The ledger for Space Marines, an untouched check (Jordan, 3 Oct)
More
tools/ledger.js and friends with --army; Space Marines scored with the Tyranid search's settings, never tuned, against both Marine panels and B1.
T-601open
Jordan's calls on the ledger page (3 Oct)
More
One-of-a-kind abilities as hand-set values outside the search: spore mine placement, Better Overwatch; labelled as Jordan's judgement.
T-602open
Integrate T-597 to T-601 and rerun (3 Oct)
More
Wire scarcity and the Soak effects into the page behind sliders, rerun the search, republish the page.
T-603open
Freeze the points ledger's inputs (Jordan, 3 Oct: "don't change the tier calculator inputs yet, or the Auspex benchmark doesn't make sense")
More
tools/ledger.js and its modules load docs/ev.js, bsdata.js, the effects files and build/units.json from a recorded commit, every ledger output records it, and a run fails if an input differs; T-579 and T-582 only after this.
T-604open
Leaders compared on their best joined form (3 Oct, from the Space Marine check)
More
Reviewers grade a leader by what it does for its squad; the single-unit comparison scores Librarians, Lieutenants and the Apothecary Biologis alone and sinks them (A → D). Squads compare as single units, characters that can lead on their best joined form. Waits for Jordan's read.
T-605open
The typical army's characters as targets (3 Oct, from T-598)
More
Kill goes into the meta army's characters as if they stood alone; most are joined to squads and can't be picked out. Treat joined characters as part of their squad's target profile (Precision aside). Waits for Jordan's read.
T-611open
Synapse valued as what one more Synapse source adds, not a multiplier on the whole unit (T-609's fix A)
More
The Synapse multiplier lifts Ranged Warriors' whole Value (12th); at ×1 they fall to 22nd. Measure the marginal worth of an extra Synapse source to an army.
T-612open
The melee-lands scale applied to the odds so it never clamps (T-609's fix C)
More
Above 1.07 the clamp at 1 erases Vanguard Onslaught's advance-and-charge edge (0.93 against 0.84).
T-613open
Kill at the margin: a unit's Kill measured with the rest of its list firing too (T-609's fix D)
More
Generalists' Kill expected to fall 10–20% against specialists'.
T-614open
Conditional abilities as shares, Genestealers' objective re-roll first (T-609's fix E)
More
About +8% to their melee.
T-615open
A rules inventory per unit (Jordan, 3 Oct)
More
Every unit's non-dice abilities sorted into kinds and checked against the datasheet (the ledger's list misses Biovores' spawning and Raveners' burrowing); a page per army for Jordan to sanity-check.
T-616open
The staged fit (Jordan, 3 Oct: "start with what we all agree on")
More
Plain units set Kill, Soak and melee lands; each rule kind fitted only on the units carrying it against that baseline; Jordan's calls last. Agreement reported at every stage. After T-608 and T-615.
T-617open
A pairwise-likelihood fitter for the ledger (3 Oct; design/ranking-methods.md)
More
Bradley–Terry over cross-tier pairs, ridge toward simple weights, per-reviewer reliability, a budget of ~8 knobs (the rest hibernated), every-weight-1 baseline, leave-one-list-out, scrambled letters; tools/ledgerfit.js. In progress.
T-618open
Diamonds in the rough (Jordan, 3 Oct; extends T-534)
More
Units our ledger rates in their army's top quarter, taken by few winning lists, rated lower by reviewers, and robust across every saved slider setting; each with its one-line ledger reason. A report for Jordan first, after the switch steps.
T-619open
The public ledger and sliders page (Jordan, 3 Oct: "incredible amounts of metadata for the numbers nerds")
More
Every unit's full ledger, what it kills best, what kills it, its forms, lens views; the sliders with presets and shareable settings (a supporter perk later, under the house rules). Mockup first; after the model settles.
T-620open
The matrix tests after the refresh (3 Oct)
More
matrix.test.js's profile counts and --check compare the matrix with the live site's data and Offense, which moved to BSData 8b91e6b and the keyword reader (T-579, T-582) while the ledger stays frozen at 374f505: the tests should read the ledger's frozen inputs (tools/ledger-inputs.js), or check the live data separately.
T-621open
Popularity partialled out of the reviewers (design/ranking-methods.md, move 4)
More
Reviewers who rank "what wins" echo list share. In the fitter (tools/ledgerfit.js), model each reviewer as ledger + γ·log(list share); drop γ when publishing. List share and placings stay as checks, never as training targets in the same fit. A method step, logged in reports/tiers/ledger-steps.md.
T-622open
Uncertainty per unit (design/ranking-methods.md, "missing")
More
The fitter bootstraps the weights only. Add a rank interval per unit (bootstrap over units and lists), shown on the ledger page and in the diamonds report (T-618). A wide interval marks a unit we cannot place, not a diamond.
T-623open
Kill over the game (Jordan, 3 Oct: "if a magma rail cannon one-shots your 200-point monster this turn, it can do the same to the next one next turn")
More
The ledger counts Kill for one turn and durability on a separate line that the fit weights at nearly nothing, so a unit that dies on turn 2 keeps a full turn's Kill. Switch killOverGame: Kill × the unit's expected share of the game still fighting, against the meta army's fire (best tools first, from the Soak numbers the page already has), squads losing output as models die and single models all at once, normalised so the median unit keeps its Kill. Page-only (no engine field, frozen inputs untouched); one ledger step with a written prediction.
T-624open
A unit's worth inside its best joined unit, at the margin (Jordan, 3 Oct: Gargoyles and Tyrant Guard show D alone but B and A joined)
More
Switch joinedMarginal: a unit ranks at the better of its solo worth and its marginal worth in its best joined form, (combo value × combo points − the partner alone × its points) ÷ its own points, for squads and leaders alike, labelled. Supervisor's check at the step-6 baseline: Genestealers with a Broodlord 1,943 per 100 (about 13th; the reviewers 13.5), Gargoyles with a Winged Prime 1,235 (about 20th; 23), Tyrant Guard with a Hive Tyrant 1,456 (about 16th; 29), the Neurotyrant with Zoanthropes about 10th (13.5). Unlike T-604 (ranked at the whole combo's value), the partner's own worth is taken out. Page-only; one ledger step.
T-625open
Detachments combined (Jordan, 3 Oct)
More
An army takes up to three detachments (Detachment Points: 3 at 2,000) and all their rules apply, so a unit can carry two detachments' rules at once (for example the Warrior detachment, 1 point, with Crusher Stampede, 2). The ledger values each form under one detachment, so a combination such as the Norn detachment's 4+ invulnerable save on the Norn Assimilator with another detachment's rule is missed. Forms for every legal pair (and triple) within 3 points, from the frozen detfx data; an engine change, not an input change.
T-626open
Kill ranked against peers in its job (Jordan, 3 Oct: "some units are taken explicitly to kill one target, i.e. anti-tank, others are general killers")
More
Today Kill is one number against the typical army, and most units' Kill lands on lone characters (Lictor 73%, Exocrine 55%), so specialists don't stand out. Switch killByJob: Kill per 100 into each target class (byClass, already in the data), each class ranked against every unit's Kill into it; a unit's Kill = its best class's standing, with a step (not a slope) for a second strong class, as Jordan's grading rules say (design/tier-briefing.md section 2: 100/20/20/20 is S). Page-only; one ledger step.
T-629open
Transports valued for what they carry (signal from the all-armies run, 3 Oct)
More
Across 14 armies dedicated transports rank about 21% of the list lower than the reviewers put them (Chaos Rhino 47 to 77% lower in all four Chaos armies; Venom and Raider 85% and 77%): nothing in the ledger values carrying a unit forward. A Delivery line or a joined-style "unit inside" form: the carried unit's Kill and Score arriving a turn earlier and protected, from the charge model and tools/transports.js; one ledger step, on every army.
T-630open
Roles found in the data, and each role scored by its own weights (Jordan, 3 Oct: "score units based on roles where certain stats are ignored ... see what roles fall out naturally")
More
(1) Factor analysis and archetypal analysis on every unit of the 16 armies' ledgers (raw lines per 100 points, Kill by target class, movement and plain traits): the roles that fall out, each unit as a mix of them, compared with the four hand-made jobs (Hammer, Anvil, Banner, Runner). (2) A mixture of regressions: each role's own weights fitted to the reviewers by the pairwise likelihood, a unit's Value its membership-weighted sum, judged by leave-one-army-out against the single-weight ledger. Roles shown to Jordan before anything is adopted.
T-631open
Units valued from tournament results, and why (Jordan, 3 Oct: "the 'are they good?' is one answer ... it's going to vary based on meta and the tournament players' skills and matchups etc. The WHY is what we are interested in as well")
More
Sports analytics calibrates to outcomes, not pundits (regularised plus-minus, box-score models fitted to it, WAR). (1) A deep dive into the related methods (RAPM and its priors, BPM/EPM fitted to it, WAR's replacement level and positional adjustment, player-skill models such as Elo/TrueSkill/Bradley–Terry with covariates, hierarchical models of meta and matchups, selection bias in who picks what, causal and IRT angles) and how each would fit 40k list data; (2) an audit of what grimstat-corpus holds (games or placings per list, players, rounds, opponents, dates, units per army); (3) a design that keeps the ledger as the "why": results give each unit's worth to winning, the ledger's lines explain it (the ledger as the stat model fitted to the results, as BPM is to RAPM), with meta, player skill and matchups as their own terms. Report and design first; no model adopted.
T-632open
The three-way diagnosis on Tyranids (Jordan, 3 Oct: "we should be able to achieve the same consensus % with the expert opinions on tyranids at least ... either we are not attacking this with the correct statistical approach, or we have not enough variables isolated, or not enough rigor")
More
Every variable we have per Tyranid unit (datasheet numbers, weapon summaries, rule flags in our own words, the ledger's lines) and models from linear to flexible (boosted trees with monotone constraints) fitted to the reviewers' consensus. (1) In-sample: can any model reach the consensus (missing variables if not); (2) units held out, rotating over all 52 (rigor and data if (1) passes and this fails); (3) the flexible model against the ledger on held-out units (the ledger's form if it wins). Then what the flexible model keys on (thresholds, interactions) as the "why", for new ledger mechanics.
T-641built
Secondary VP priced in army points (Jordan's colleague, 4 Oct: "if you have a 2000 pt army, the max # of secondary points is X and a biovore can consistently score ... 10 secondary points ... that's 1/9 × 2000 pts worth of your army")
More
Switch secondaryVP, off, in place of the crude Actions line (1 per unit per 100 points): each unit's expected secondary VP per game from the 11th-edition mission deck (which cards it can score: actions, positions, reach (arrival, Scouts, Infiltrators), staying alive and hidden (Indirect, Stealth, cheap), bodies), counted at the margin (what the army loses without it, since secondaries are capped and other units score them too), converted at a rate set by the game rather than fitted: points per VP = army points ÷ the VP a typical game hands out. One step with a prediction, judged as steps 20–23. Blocked: the secondary deck's numbers (cap per game, VP per card, how often each scores) must be read on Wahapedia's 11th-edition pages, which this cloud environment's network policy blocks (add wahapedia.ru to Allowed domains), or given by Jordan. The primaries are already read (tools/dispositions.js: 15 VP a round, 45 a game); secondaries were left out there. Partly unblocked 4 Oct: design/rules-notes.md (11th-edition core rules in our own words) gives the caps: secondaries 45 VP a game, at most 15 a round, a fixed card at most 20 (primary 45, painted 10); tactical cards drawn two a Command phase, discards earn 1 CP; actions cost the unit its shooting that turn. Still missing: the individual secondary cards (what each pays for, how much), from the mission deck. Step 27 not kept 4 Oct: headline 72.3% → 73.5% but its aimed units mostly moved away (Biovores, Hormagaunts, Gargoyles) while big tough units rose; back to its builder to find the driving term. Step 27b (fire in proportion to points) not kept 4 Oct either: ρ 0.520 → 0.512; Biovores and Neurolictor nearer the reviewers, but Hormagaunts fell and the Deathleaper and Leapers rose past their grades.
T-647open
Every mission card scored per unit, primaries and secondaries as points (Jordan's colleague, 4 Oct: "score each unit on how likely it is to achieve it, also some units exist for sitting on a point and surviving ... 20 termagants ... norn with oc15 ... gargoyles who can steal or sit a point from reserves ... just like scoring secondaries should be counted as points, so should scoring primaries. An exocrine is never going to recover assets, but a lictor will, and it will remain out of line of sight and with lone op while it does")
More
Switch missionVP, off: a catalogue of every secondary card and every primary scoring line (design/mission-notes.md), each with its requirements (OC on which objectives, action and what it costs, surviving the opponent's turn, reach to which zone by which round, hidden, number of units) and its VP; each unit's profile from the rules; P(unit achieves card) × VP; credited at the margin (the typical army's best few scorers per card; what it loses without the unit); primaries weighted by the field's dispositions; converted at about 31 points per VP. Replaces Score, Hold, Actions and own-bodies Presence. Kill cards stay with Kill. One step with a prediction, judged on the step-26 baseline.
T-648open
An independent review of the ledger's maths against industry methods (Jordan's colleague, 4 Oct, overnight)
More
A reviewer reads the whole problem (design/ledger.md, the step log, the diagnoses T-632/T-635/T-636, the burst checks) and compares it with established methods for valuing pieces from expert rankings: sports player value (value over replacement / WAR), paired-comparison rating (Bradley–Terry, Elo, TrueSkill), learning to rank, hedonic pricing, conjoint and expert-elicitation, Bayesian hierarchical models across armies. Report: what we do well, what's wrong or missing, ranked recommendations.
T-649open
The knobs established, and principled ways to tune them until the gap closes (Jordan's colleague, 4 Oct: "establish the knobs, come up with several rational methods of intentionally tweaking slider tools until we crack the case")
More
Inventory every slider and switch (what it means in the game, its sensible range, whether it's fitted or set by reasoning). Several tuning methods with overfitting guards (held-out lists, units and the 14 never-fitted armies): coordinate search one knob at a time, sensitivity sweeps, Bayesian/CMA-style search on a small set, hierarchical shrinkage across armies, expert priors. Every tuned setting saved as a versioned benchmark, judged by the provisional keep rule and the movement view.
T-654built
Support as a credit, not a multiplier on the carrier's own Kill (T-652 proposal 2)
More
Switch supportCredit, off: Value = worth without support + κ × Σ(m − 1) × the army's median support-free Value per 100 points (the detachment kind stays a multiplier); κ re-fitted by ledgerfit with the support multipliers, judged on the 14 never-fitted armies and held-out units. One step. 5 Oct: 35a not kept; 35b provisional (fixes the Tyranid order in sample; the 14 armies and held-out re-fits fall).
T-655open
Kill by preferred targets, weighted by the meta's composition (Jordan's colleague, 5 Oct: "damage allocation needs to be implicit to the unit's weapons choices ... excellent at burst killing elite infantry, great; tanks great; chaff probably less good based on the current meta ... the value is scaled on the meta composition of units for future use ... units preferentially targeting their desired targets")
More
Switch preferredTargets, off: each unit's Burst Kill by target class from its weapons (the matrix's cells against the field's units of each class); its fire goes first to the classes it's best at, limited by how much of each class the field holds; characters treated as joined at the field's real join rate (targetable alone only by Precision, or when fielded alone); the field's class composition read from one swappable file (pooled today; per faction, detachment or list style later). One step with a prediction, judged as usual. Also fixes the finding that most units' damage lands on characters.
T-656open
Fields by faction, detachment and list style (Jordan's colleague, 5 Oct: "how much infantry with statline X orks are playing in what detachment ... how much terminators Blood Angels are playing ... to really hone how armies match up against factions, detachments within that faction")
More
From the tournament lists (grimstat-corpus, CC BY 4.0; and our own data later), a field file per enemy faction, per detachment and per meta state; the ledger and the calculator read any of them in place of the pooled field, so a unit's value can be shown against a chosen opponent. Data is thin today (about 700 lists), so each field carries its list count and is pooled toward the whole field when thin.
T-657open
Support measured as its own line (Jordan's colleague, 5 Oct; after T-654/step 35: a fixed credit helped Tyranids and hurt other armies)
More
Switch supportMeasured, off: the support kinds stop multiplying the carrier's own Kill; instead each support rule's worth is measured with the engine on the friends it reaches: the typical friendly unit's Kill (and Soak, for defensive buffs) with and without the effect, × how many friends a game it reaches (aura range, Synapse coverage), in points, per 100 of the carrier's points. Game-derived, no fitted strength. One step.
T-658open
The mission line and Kill in one currency (after steps 28/28b: full scale swamped Kill; within the old budget it sank)
More
Switch vpLedger, off: Kill converted to VP (enemy points removed → VP denied from the enemy's scoring units and kill cards scored, at the game's rates in design/mission-notes.md), the mission line (T-647) in VP as built, Soak as VP saved; Value = VP a game per 100 points × 2,000 ÷ 65. Neither line dominates by its units; the conversion rates are the game's. One step.
T-659open
Trade value: enemy points removed minus points lost in return (supervisor's proposal, 5 Oct; after T-655)
More
A unit's burst on its preferred targets minus the expected points the field takes back from it in its reply (its Soak against the field's preferred targeting), over the turns it survives. Joins burst, toughness-with-threat and the colleague's cat-and-mouse point into one number. After T-655 lands.
T-660built
Points killed over a unit's life: burst, average and trading in one number (Jordan's colleague, 5 Oct: "points killed/100pt is super important ... calculate it for every unit in the game it could shoot into ... exclude characters ... keep burst kill, points from overall damage to every unit averaged, and trading efficiency ... likelihood of being killed back burst style after shooting"; the heavy weapons team vs the Tyrannofex)
More
Switch lifeKill, off: per activation, a blend of Burst (best 3 targets) and Average (points finished per activation over every non-character unit in the field, weighted by how often it's fielded); × the expected number of activations it gets before the field bursts it down (from tools/burst.js --defend's finish chances, fire in proportion to points); characters excluded as targets; the blend fixed by reasoning. Replaces burstKill's Kill. One step. Absorbs T-659 (trade value). Built 5 Oct (switch lifeKill, off; tools/burst.js --life for every army; dials lifeSurvive, lifeBlend, lifeMode; design/ledger.md section 26): step 39 on the Tyranids, Track A 79.3% → 78.0%, Track B 77.7% → 76.4%; tuned as dials (reports/tiers/2026-10-05-dials.md: s > 0 costs). Nothing adopted
T-661built
Every idea as a dial, and a sweep tool to tune them (Jordan's colleague, 5 Oct: "the point of these iterations are to identify knobs and sliders that can be tweaked to reflect things that are harder to model ... if it isn't working quite right then it needs adjustment, not to be thrown out ... cheap units whose purpose is to screen and do secondaries, and whether that's worth ×1.005 their points or 2× their points")
More
Each switch built since step 18 gets a strength dial (0 = off, 1 = as built, up to ~2–3): vpLedger (the mission line's weight, and a per-unit vs per-point split for screens and secondary scorers), missionVP, secondaryVP, scoreFloor, killClassWeight, arrivalKill, scarceAnswers, carryDedicated, threatSoak, shockPressure, supportMeasured, preferredTargets (its caps), lifeKill when built. tools/knobsweep.js sweeps one dial on a base (Track B) over a grid and reports, per value: the five Tyranid lists, held-out Tyranid units (diagnose --quick), the Tyranid movement and the units that move most; and a combined pass over the few best dials. Strengths are set from the curves with held-out units as the guard. Built 5 Oct (ledger.js DIALS, 17 dials, the fingerprint test; tools/knobsweep.js; design/ledger.md section 27): four help the Tyranids held out (arrivalKillW 1.5, secondaryVPW 0.5, supportMeasuredW 3, shockPressureW 2; Track B 77.7% → 81.4% stacked), saved as the candidate tools/ledger-benchmarks/2026-10-05-trackB-dials4.json; they don't transfer (T-668's exam). reports/tiers/2026-10-05-dials.md. Nothing adopted
T-662built
The big crack: every dial against the Tyranid reviewers, exhaustively where it can be (Jordan's colleague, 4 Oct: "every variable a game-wide dial ... coarse scales fine"; the blind appraisal's controls)
More
Built: ledger.js profileDials (whole Value, hibernated), profileLines (each family on the parts of Value it affects) and the pointsExp dial, all off (benchmarks reproduce); tools/ledgerprofile.js (the datasheet features as data), tools/crack2model.js (the ledger's maths in flat arrays, exact to 1e-15 against compute()), tools/crack2core.js and tools/ledgercrack2.js (G2: switches and dials with the weights, 10.1M combinations; G3: twelve family dials, 531,441 combinations on each of 62 settings, 32.9M; 3 shuffles, 8 fake worlds, placebo families, leave-one-list-out, leave-units-out; the wide net and the staged exam, --net, --exam --armies a,b [--on <gate>]), tools/crack2report.js. 5 Oct: G3 best 84.3% on the five lists (rows) against built A 78.9% (+5.4); the fake worlds gain +0.1 to +0.6 (p ≤ 1/9) but held-out units only +0.4 (79.7% against 79.3%), the shuffles climb +9 to +19 over the built setting, and the placebos sit where the real families do: mostly these 52 units, not a code. Exam: of 16.9M stored Tyranid combinations none reaches 70% on both Space Marines and World Eaters (best mean 72.9% against A's 66.1% ± 3.4); the 2,000 best on all 15: none clears 70% everywhere, the most armies cleared 3 of 15, best mean 61.7% against A 59.4% ± 2.2, B 60.1%. Rung (c) (every feature as a game-wide slope, 152–155 knobs, searched): 92.9% / 92.8% on the five lists, but shuffled letters reach 91.5% / 92.6%, held-out units +0.9 / +2.2 and the 14 armies fall (58.9% → 57.1%, 59.9% → 54.0%): 52 units cannot identify the knobs. The ceiling scan (all 16.9M stored combinations on all 15 armies, in-sample): the best mean is 62.7% (G2; G3 61.6%), none reaches 63%, the best clears 70% on 1 army; picked on 10 armies and scored on the 5 held out, 60.2% (B 59.4%, A 57.5%). This knob space cannot reach 70% broadly: the next gain needs new ingredients, not new weights. Nothing adopted; the rung (c) candidates (tools/ledger-benchmarks/2026-10-05-crack-candidate-A/B.json) are noise fits, not for adoption. reports/tiers/2026-10-05-crack.md, -crack-exam.md. Next: new ingredients for the ledger (what it measures); the exam stays a cheap re-runnable gate (--exam --armies … --on …).
T-663built
The designers' price list (the blind appraisal's idea 1, reports/tiers/2026-10-05-blind-appraisal.md: "regressing every unit's points on its perks across ~1,100 units gives what the designers charge for Stealth, Deep Strike and the rest")
More
tools/pricelist.js: log points at the minimum size (and per model) of every datasheet of the 16 ledger armies (829 once shared ones count once and Crucible is out) regressed on the engine's capabilities (rankings.json's damage into five targets, durability against five attackers, OC, Move; absolute) and 73 perk variables in families, ridge on within-army deviations, λ by leaving one army out, 500 bootstraps, a lasso, collinear groups jointly, the ledger's own capabilities as a cross-check. Built 5 Oct: left-out R² 88.4% (capabilities alone 85.0%); prices e.g. Fly +10%, Lone Operative +10%, command points rule +8%, Indirect Fire +7%, Stealth +5%, Blast +4%, Feel No Pain −10% at equal durability, Fights First −8%. Under-priced units don't get better letters: Tyranid residual alone 50.9% (Track B 77.7%), blend 67.0%, log blend 76.2%; other armies 49.2%. Dial scale for T-662: weapon keywords 1.12×, Feel No Pain 0.23×, Fights First −0.06× (increment), Lone Operative 8.1×; arrival and Stealth not meaningful as a ratio. reports/tiers/2026-10-05-pricelist.md. A report: nothing adopted
T-668built
The exam: Track B's inputs for every army, so the other armies can examine a Tyranid-fitted setting
More
Built: preferred targets and life for all 16 armies (tools/burst.js --prefer/--life, merged; tools/life-space-marines.json; tools/ledger-all.js's chain now builds burst, prefer, life and supportfx), tools/exam.js (per army and pooled pairwise over the Space Marines and the 14 at wCarry 0, paired bootstrap intervals, 19 s), the candidate tools/ledger-benchmarks/2026-10-05-trackB-dials4.json. 5 Oct: pooled A 59.4%, B 60.1% (+0.7 [−2.0, +3.2]), B + four dials 60.0% (−0.1 ± 2.4 against B): neither transfers. The Space Marines' scarcity and soakfx still missing (would move their Track B). reports/tiers/2026-10-05-exam.md. Nothing adopted
T-669built
What the model misses, across all 16 armies (supervisor, 5 Oct: no weighting lifts the 15 armies past ~61%, so is the gap in what the lines measure?)
More
Built: tools/misses.js (a report, read-only). For each graded unit it takes the reviewers' percentile minus ours at Track A (step 34b, wCarry 0, the exam's numbers) and tests 1,250 one-condition groups beside our position, held out by army. Report: reports/tiers/2026-10-05-misses.md and .json. Found: our position explains 8% of the reviewers' (slope 0.29). There's no single missing ingredient: ten groups together reach 22% and a lasso on 340 columns 2%. Underrated: psykers (+14 beyond our position, 13 armies), Lone Operative (+18), leaders with a squad buff (+9; every unit's Value is its solo form, so a leader never gets credit for its buff), Epic Heroes (+11). Overrated: aircraft (−26), overwatch (−14), cheapest size ≤70 pts (−6), older multi-pistol line squads (−10). Top three fixed: pooled 59.4 → 63.5 in sample (oracle), 63.0 with the moves held out, 58.3 with the search itself rerun without each army. Next: try a leader credited with its buff, an aircraft board-time discount and Lone Operative in Soak, each as a switch through tools/exam.js.
T-670built
New ingredients from the cross-army misses (supervisor, 5 Oct: T-669's groups as dials, chosen with some armies left out)
More
Built: ledger.js dials aircraftBoard and smallPiece, on /lab/ledger/ as "Ingredients (T-670)" and off by default; leaderCredit and loneOpSoak as code only, off; tests/unit/ledgerpage.test.js; design/ledger.md section 29. Held out on the 15 armies: aircraftBoard +0.2 ± 0.1, smallPiece +1.2 ± 1.0 (Tyranids −3.1), with attachChars +2.2 ± 1.3 (exam: 61.9% pooled, +2.5 [+0.3, +4.7]). Leaders' measured buff hurts (−0.8 at the best λ, and the existing leadersLeading and joinedMarginal −2.3 and −3.4). Lone Operative on Soak gives 0.0. Report: reports/tiers/2026-10-05-ingredients.md. Waiting on the supervisor: adopt any into Track A or B?
T-667built
Reviewer grades made on different points or rules (supervisor, 5 Oct: does the ledger's gap to the reviewers come from units graded on an older game?)
More
tools/panelstale.js → tools/panelstale.json: every panel's BSData commit at its date (git log by date, never a checkout) against the frozen 374f505; per graded unit, points at every size (10%+ flags), the datasheet by field name (patches.js fields() after BSData's bookkeeping is evened out), 10th-edition panels flagged "edition" (and whether the unit is in 11e); severity points > rules > keywords > edition. Then the five Tyranid lists' Track B pairwise and the reviewers' ceiling with and without the flagged units, against random drops of the same size. Finding: no. Gap 12.7 points (ledger 77.7%, reviewers 90.4%); dropping stale units narrows it 0.3 to 1.4 points, random drops 0.1 to 0.7: at most ~0.7 points beyond chance; 3 to 14% of the 4,457 pairs dropped. Keeping only the 11th-edition lists widens it (13.2). Two hygiene notes: patchDrop checked the smallest size only (Leapers −12.5% at 6), and Into the Hive Mind's patchDrop was read at 2 Jul though the video is 27 Jul. reports/tiers/2026-10-05-panelstale.md
T-606open
The matrix test's e against killDP misses its 0.5% bound on the new data (3 Oct, from T-579)
More
tests/unit/matrix.test.js samples the pool's own profiles; at BSData 8b91e6b one cell (S6 AP-2 D3+3 into T7 3+ W5) has e 0.4167 against killDP 0.4190, a 0.55% gap, so the test fails though nothing in the matrix changed. Decide: widen the bound for variable damage into multi-wound models, fix the cell's sum, or fix the sample to a frozen pool (T-568's owner).
T-607built
A benchmarks picker on the ledger page (Jordan, 3 Oct)
More
One tap sets every slider to a saved benchmark (tools/ledger-benchmarks/, docs/lab/ledger/benchmarks.json).
T-608open
The ledger's lines on one scale, arrival reach on Presence, Jordan's Biovore dial, the joint search rerun (Jordan, 3 Oct)
More
Raw lines differed by two orders of magnitude, so Presence and Actions could never matter; now Maelstrom included as a fifth list. In progress.
T-609built
Audit of Ranged Warriors' and Genestealers' Kill (3 Oct)
More
No bug: Warriors legal and right (Synapse lifts their whole value); Genestealers right (Hyper-Aggression wins on scarcity; the lands clamp hides Vanguard Onslaught). Fixes carded T-611 to T-614. reports/tiers/2026-10-03-kill-audit.md
T-610built
Maelstrom Gaming's Tyranid list (March 2026) transcribed; a fitting panel by Jordan's call (3 Oct)
More
Agrees with the 11th-edition Tyranid lists at 0.84 / 0.83 / 0.72.
T-672built
Flesh out World Eaters: the army's own quirks as dials (supervisor, 5 Oct: the cheapest army to tune, three full reviewer lists)
More
Built: ledger.js dial modeScarcity (the army's scarcer attack mode worth more, its summed Kill kept), on /lab/ledger/ as "Army quirks (T-672)" and off by default; armyPick (Burst with an army rule's own choice taken: tools/ledger.js setArmyPick, tools/burst.js --field --army-pick, merged as burstPick) as code only, off; tests/unit/ledgerpage.test.js; design/ledger.md section 30. World Eaters 67.8%, the reviewers' ceiling 82 to 85%. One list left out: modeScarcity 0.2 +2.4 held out (with smallPiece 0.15 +4.5), the other 14 armies −0.1 ± 0.4, Tyranids +0.0. Blessings of Khorne in Burst hurt (−2.2). Leaders cut by 0.2 help the World Eaters only (+2.3 held out, others −0.2): handed to T-673. Candidate: tools/ledger-benchmarks/2026-10-05-we-modescarcity.json. Report: reports/tiers/2026-10-05-world-eaters.md. Waiting on the supervisor: adopt modeScarcity into Track A or B?
T-671built
The cascade: the whole Tyranid net, army by army (the user, 5 Oct: "no top-N cut ... keep only combinations ≥ 67% on every army so far")
More
tools/crackcascade.js filters all 16.9M stored T-662 combinations (G2 + G3) with the ceiling scan's scores (letters, as tools/exam.js), no re-scoring, about a minute. Chain A: Tyranids → World Eaters → Space Marines → the rest by bootstrap SE (Dark Angels, Astra Militarum, Death Guard, Adeptus Mechanicus, Orks, Thousand Sons, Genestealer Cults, Leagues of Votann, Drukhari, Emperor's Children, Agents, Space Wolves, Blood Angels); chain B without Space Marines until last; A2 with Astra Militarum, Orks, Leagues of Votann, Emperor's Children first. 5 Oct, at 67%: A 16,886,724 → WE 3,054,652 → SM 3 → DA 0; B → WE 3,054,652 → DA 0; A2 → SM 3 → AM 3 → Orks 0. At 65%: A → WE 5,318,799 → SM 208,595 → DA 0; A2 reaches 7 steps (1,587 through Emperor's Children) then DA 0. At 70%: WE 723,060 → SM (or DA) 0. Dark Angels is the wall (no combination reaches 65% there; best 62.7%), then Thousand Sons 65.7%, Death Guard 66.2%, Space Marines 67.2%. No Tyranid-fitted code holds at 67% everywhere in this knob space. Follow-ups (yes from the user): Dark Angels without its stale grades (T-667: 30 points flags; and with rules and absent, 39) stays the wall (best any 63.0% / 63.4%, none at 65%; Track A falls to 52.1% / 50.4%); with Dark Angels last the chains stop at Death Guard instead (65%: chain B 181 through Death Guard, then Adeptus Mechanicus 0). T-670's dials (aircraftBoard 1, smallPiece 0–1, attachChars on/off) on the furthest 1,590: the most armies at 67% stays 4, the best mean over 15 60.9% → 61.4%; attachChars inert there (lifeKill on); smallPiece costs the Tyranids up to 8 points. tools/crackfollow.js; tools/crack2model.js reproduces aircraftBoard and smallPiece (1e-15). reports/tiers/2026-10-05-cascade.md.
T-673built
Every character and its special rules, in the ledger (Jordan's colleague, 4 Oct: "we need to build out all the characters and their special rules after the next attempt")
More
Jordan, 5 Oct: go, the next card. The cross-army trio (aircraftBoard 1, smallPiece 0.4, attachChars) is judged together with it; measure after T-680 and T-681. T-669 found characters are the biggest cross-army miss (leaders with a squad buff underrated in 16 armies, psykers in 13, Epic Heroes in 14, Lone Operatives in 11), and T-670 found crediting a leader's measured dice buff alone makes the order worse: what reviewers value in characters isn't in our numbers yet. Build out every character across the 16 armies: what it gives the squad it leads (dice buffs from leaderfx.json, and the non-dice ones: movement, Deep Strike or Infiltrate for the squad, OC, Fights First, Battle-shock, re-rolls, command points), its own abilities and psychic powers (unitfx.json; status modelled, partly or noted, per the coverage reports in reports/tiers/<date>-<army>-coverage.md), Epic Hero rules, auras, and what it unlocks (a squad becomes worth taking, an extra activation, a key unit's best home). Each kind of rule a dial (0 = off = today), general across armies, measured with tools/exam.js held out by army (as T-670) and on Tyranids. Our own words; Wahapedia's 11th-edition pages to understand a rule, never to copy Phase 1 built (5 Oct): tools/characters.js catalogues 316 characters (tools/characters.json); fourteen character dials charLead … charHome (with charShockAmp) and cpOnce in ledger.js, on the page at 0 (design/ledger.md section 33, reports/tiers/2026-10-05-characters.md, predictions as ledger-steps step 40). Phase 2: measure each with tools/exam.js held out by army, after T-680 and T-681. Phase 2 measured (5 Oct): no kind holds out beyond noise (charEpic +0.29 ± 0.54, charAura +0.09 ± 0.12; the rest below 0); with the trio +3.2 [+0.7, +6.0] vs the trio's +2.8; nothing adopted (reports/tiers/2026-10-05-characters-measured.md). Phase 3 (what a kind does on the table) not worth it on this evidence, except perhaps Epic Heroes; charHome and charShockAmp to T-676.
T-674built
Rebuild the transport line so it works across armies (supervisor, 5 Oct: at any wCarry the Carry line throws every transport to the top of its army, right and wrong ones alike: Trukk 9→96, SM Rhino 6→100, Drop Pod 0→98, Raider 19→86)
More
Built: tools/carry.js ridersOf (each transport's riders from its army's ledger: BSData keywords, exclusions, the most models that fit; returned by build(), not written); tools/carrycargo.js, the line rebuilt from the cargo as sliders over the page's Values (rider's delivery carryRide, protection carryShield × fragility carryFrag, carryPer, carryDedOnly, carryCap, carryOnce, carryNeed, carrySquads), with leave-armies-out (3 seeds × 3 folds, 10 choose, 5 score) and exam.js's bootstrap. Why: capacity per point × the Kill weight, so a 60-pt Trukk earns +1,341 whatever it carries. Best shape (protection of fragile riders, dedicated only, capped at the rider): +1.2 pooled in-sample (SE 0.8; Drukhari +14, Emperor's Children +5.2; Tyranids −1.7, or ±0 with carryOnce 1), but chosen honestly it holds out at −0.0 ± 0.5 (strengths alone on the fixed shape +0.7 ± 0.4, leaky). Not productionised: ledger.js unchanged. Report: reports/tiers/2026-10-05-transports.md. Next: a per-army transport dial under T-676 (Drukhari want it, Orks don't)
T-675built
Value cheap screening and scoring units, across armies (supervisor, 5 Oct: Kill is about 90% of Value, so nothing measures what a screen does; Jakhals S S S at our 34th percentile, Flesh Hounds A A A at the 28th)
More
Built: ledger.js five screen dials (screenArea board denied to arrivals, screenBlock frontage × speed, screenTrade enemy fire to remove it with a one-activation floor, screenScore a scoring slot × OC, screenHide Infiltrators/Scouts/Stealth/Lone Operative) and the screenJob switch, each a worth in points added as w × Q × P ÷ points (0 = today; a dial moves a unit from × 1.005 to × 2 its points and past); tools/screens.js; tests/unit/ledgerpage.test.js; design/ledger.md section 31. Held out (3 splits, 10 armies choose, 5 score): only screenTrade is positive, +0.15 ± 0.06 at 0.01 (exam +0.1 [−0.3, +0.7], Tyranids +0.2), on /lab/ledger/ as "Screens (T-675)", off; area −0.02, block −0.05, score −0.95, hide −1.29 stay in code at 0. World Eaters alone, one list left out: area +5.9, score +5.5, block +4.1 (Jakhals to the 76th to 93rd), but those strengths cost the pooled exam 1 to 3 and the Tyranids 2.5 to 8: cheap squads we overrate elsewhere (Wracks, Hearthkyn, Daemonettes, Cadians) rise with the ones we underrate. Candidate: tools/ledger-benchmarks/2026-10-05-screens.json. Report: reports/tiers/2026-10-05-screens.md. Waiting on the supervisor: adopt screenTrade (noise-sized), or the World Eaters' screen dials as an army-level setting?
T-676open
Army-specific sliders, after the game-wide ones (Jordan's colleague, 4 Oct: "some sliders are common amongst armies and some different... use these knobs to get them above 60% and then fine-tune the army-specific ones after")
More
Jordan, 5 Oct: after T-673. Note from Jordan's Tyranid notes: the Toxicrene's unwieldy model and slow speed (low priority). Order: (1) game-wide sliders whose INPUT depends on the army (context: what the army needs carried, how scarce its anti-tank or ranged Kill is, as modeScarcity, role scarcity and scarceAnswers do), until every army is at or above about 60% on the exam; (2) then army-specific sliders for each army's genuine quirks (army rule, detachment synergies, unique units such as Biovores), each fitted on that army's own lists, held out list by list where an army has 2+ lists, and pulled toward the game-wide value (army value = game-wide + a small adjustment, kept near 0 unless its lists clearly ask), so an army with one list of 20 units can't fit noise. Report per army what the army-specific layer adds on held-out lists
T-677built
Screens as a job with a few slots per army (supervisor, 5 Oct, after T-675: an army needs only a couple of screens, and reviewers credit the best ones for that job)
More
Built: ledger.js screenSlots (k 1 to 3: only the army's top k units by screen worth, Σ w × Q over area, block, score and trade, get the T-675 credit), screenNext (x: the next unit x of it, the one after x²) and screenRank (0 per point among units that can do actions with OC above 0 and 3+ models, 1 the worth overall); all 0 = today. Battle-shock's reference Value now leaves the screen credit out, as the screens' own did (T-675's numbers barely move). tools/screenslots.js (a fast path checked against compute() on all 16 armies). Held out (10 armies choose, 5 score, 3 splits): no structure positive (best k 3 per point +0.03 ± 0.41; structure chosen too −0.33); exam of the in-sample pick +0.6 [−0.4, +1.6], Tyranids −2.6. The slot goes to the wrong squad: Drukhari's best screen per point is the Wracks (D), Votann's the Hearthkyn (C), Death Guard's the Poxwalkers, not the Nurglings (A). Kept at 0, not on the page. World Eaters alone, one list left out: slotted +6.7 at a pooled cost of 0.5 to 0.9 (unslotted +7.2 at 1.8 to 4.9); Jakhals to the top, Flesh Hounds 28th to 83rd. Candidates: tools/ledger-benchmarks/2026-10-05-screen-slots.json, -we.json, -we0.json. Report: reports/tiers/2026-10-05-screen-slots.md. Waiting on the supervisor: the slotted dials as the World Eaters' army-level setting (T-676)?
T-678built
Organise the points ledger for whoever comes next (the user, 5 Oct: "this model is going to be the basis of all future models... clean it up, summarize its provenance easily, make it easier for the next AI or human to follow")
More
Documentation only, nothing deleted, no tool code touched. Built 5 Oct: design/ledger-start-here.md, the front door (what it is and the north star, how to run it, where we stand in one table, every switch and dial of ledger.js checked against SWITCHES/DIALS/BASE plus the research-only sliders, the provenance timeline from 3 Oct, what we learnt, open threads in order, the working rules and a builder's brief); reports/tiers/README.md (every report by date, superseded ones marked); tools/ledger-benchmarks/README.md (every saved setting and its status); pointers from design/tier-engine.md, design/ledger.md, reports/tiers/ledger-steps.md and CLAUDE.md; statuses of T-660, T-661 (built: 🟡) and T-668 (🟡: ✅ is for Jordan's or a merge)
T-679built
T-674's transport sliders into the ledger itself, at 0 (the user: sliders are kept, never deleted, and live in the model)
More
Built: ledger.js switch carryCargo (off) and eleven dials at 0 (carryRide, carryShield, carryFrag, carryFragRef, carryPer, carryDedOnly, carryCap, carryOnce, carryArrive, carryNeed, carrySquads; cargoOf, cargoApply): with it on and Transports above 0, a transport is worth what a ride adds to its best rider, in place of the capacity line. Riders: tools/carry.js ridersOf written to tools/carry.json riders, merged per army as meta.carryRiders by tools/ledger-merge.js (names and numbers only; all 16 armies, Tyranids and Space Marines in docs/lab/ledger/, the rest in build/ledger/). Off or at 0: every saved setting on every army is today's to the bit (30 benchmarks + the page's 11, 16 armies). Shape A (w 0.5, shield 0.4, frag 2 at 150, dedicated only, cap 1) reproduces tools/carrycargo.js unit for unit: exam pooled +1.2 [−0.1, +2.8], Drukhari +14.0, Tyranids −1.7 (tools/exam.js: a carryCargo setting keeps its own wCarry). On /lab/ledger/ as "Transports (T-674)" at 0, with the "Transports by their cargo" switch; tests/unit/ledgercargo.test.js; design/ledger.md section 32.
T-680built
Leagues of Votann out of the pooled exam, shown apart (Jordan, 5 Oct)
More
Built: tools/exam.js pools 14 armies; Votann's row is printed apart with its own interval (APART; --with-votann pools the 15 again and reproduces T-668 to the bit; the bootstrap draws over every army in the same order either way); tests/unit/exam.test.js pins the 14. Re-stated on the new pool (against Track A): Track B −0.0 [−2.7, +2.6] (was +0.7), B + four dials +0.1 [−3.0, +3.0] (was +0.6), the cross-army trio +2.0 [−0.3, +4.3] (was +2.5 [+0.3, +4.7]); Track A 59.4 either way. reports/tiers/2026-10-05-exam14.md; ledger-steps row exam14, decisions.md, the front door. Its two panels are 10th-edition reference lists that design/decisions.md (3 Oct) set as never scored. Moves no ledger value, only the yardstick
T-681built
Space Marines' scarcity and Soak-effects inputs, and a re-freeze of the ledger inputs (Jordan, 5 Oct: yes, as a new snapshot)
More
Track A's scarcity and soakfx fall back on Space Marines because their per-army files were never built. Build them with the existing tools (scarcity.js, soakfx.js), re-freeze (node tools/ledger-inputs.js --freeze <commit>, T-603) and report it as a new snapshot: Space Marines' lists and exam before and after, Tyranids unchanged to the bit. After T-680. Built (5 Oct): tools/scarcity-space-marines.json, tools/soakfx-rules-space-marines.json and tools/soakfx-space-marines.json, merged into docs/lab/ledger/space-marines.json. No re-freeze needed: the files are the ledger's own live files (tools/ledger-inputs.js LIVE), so the snapshot stays eb3590a (BSData 374f505, 30f0056e6103), pre-dataslate. Found: predicted Space Marines +1 to +3; got scarcity −4.2 [−9.1, +0.7] on their exam row (64.5 → 60.3), Soak effects 0, Tyranids unchanged to the bit; pooled A 59.4 → 59.1, B 59.2. Scarcity, fitted on the Tyranids, hurts out of sample; whether it stays on in Track A is open. reports/tiers/2026-10-05-refreeze-sm.md; ledger-steps step 42
T-683built
Typical version against best version (Jordan, 5 Oct: Carnifexes rank A under Track B from a pair in their best detachment; reviewers grade the plain single one)
More
A game-wide dial blending each unit's best form with its typical form (smallest squad, datasheet wargear, its army's middle detachment; no tournament popularity), with sub-dials for size and detachment. Preview tool first; measured after T-682. Phase 1 built (5 Oct): tools/typical.js, typicalSize/typicalDet (typicalW), 0 = today to the bit; sanity print and predictions in reports/tiers/2026-10-05-typical.md; the exam needs a post hook in exam.js (described there). Phase 2: measure after T-673 Jordan, 5 Oct: the size half is tried three game-wide ways, chosen held out: best size (today), the average over all legal sizes, the smallest; no per-unit size and no popularity Phase 2 measured (5 Oct):typicalSizeRule (smallest, mean, best) built in tools/typical.js, with a span placebo. Only the size half helps, read as the mean of a unit's sizes: held out +0.26 ± 0.35 (placebo −0.02), rule chosen too +0.23 ± 0.32 (placebo −0.38); exam at mean 0.25 +0.4 [−0.3, +1.2]; smallest −1.61, detachment half −0.38. For the cascade: typicalSize 0, 0.25, 0.5 at rule mean, typicalDet 0. reports/tiers/2026-10-05-previews-measured.md
T-682built
One per job: the better option takes the slot (Jordan, 5 Oct, from his Tyranid notes: Winged Hive Tyrant vs Hive Tyrant, ranged vs melee Warriors, Leapers vs Lictors, Carnifexes and Toxicrene vs Haruspex)
More
A game-wide dial: a unit loses worth to a same-army rival that does the same job better per point, scaled by how alike they are (what their damage lands on, shooting or melee, unit type, speed; an overlap 0 to 1, no hand labels) and by how much better the rival is; the better unit untouched. Preview tool first, no ledger.js change; a placebo with scrambled rivals beside it; measured after T-673, first of the three Phase 1 built (5 Oct): tools/rivals.js, a preview (no ledger file changed): likeness = kill² × mode × type × speed^½ (target tilt, ranged share, body/Character/Fly, Move), the dial rivalW with gap capped at twice as good per point, a scrambled-likeness placebo, scoreArmy/scorePool scoring as the exam does (rivalW 0 = the exam to the bit); tests/unit/rivals.test.js; design/ledger.md section 34; reports/tiers/2026-10-05-rivals.md; predictions as ledger-steps step 41. Phase 2: measure a rivalW sweep with its placebos through the exam, held out by army, after T-673 (exam.js needs a small hook, section 34). Phase 2 measured (5 Oct): not kept. Held out −0.73 ± 0.73, placebo +0.00; exam at 0.5 −0.3 [−1.6, +1.1]; Tyranids −0.5 at 0.5. Each army's top rival pairs printed for Jordan in reports/tiers/2026-10-05-previews-measured.md (tools/previews.js). Hand pairs from Jordan, if any, are scored next.
T-684built
Gargoyles' two tricks: jailing and the objective steal (Jordan, 5 Oct)
More
Jailing: turn-one reach into the enemy deployment zone (move and pre-game moves), width strung out, cheapness. Objective steal: arrives from reserve, moves after shooting, OC enough to take an enemy objective, priced as the primary VP denied in army points. Each a game-wide dial; rules from Wahapedia's 11th-edition pages in our own words; may prove army-specific (then T-676). Preview tool first; measured after T-683. Phase 1 built (5 Oct): tools/steal.js previews jailW and stealW (0 = today to the bit, tests/unit/steal.test.js), placebos and exam-style scoring; the steal qualifies 5 datasheets in 16 armies (Gargoyles, Eliminators, Land Speeder, Pteraxii Skystalkers, Scourges with heavy weapons), Gargoyles 46th → 37th at steal 0.5, Tyranid lists 79.3 → 79.9 (placebo 79.0); jailing barely exists under the 9" coherency rule. reports/tiers/2026-10-05-steal.md, design/ledger.md section 34. Next: measure after T-673 (needs exam.js's post-compute hook or the dials in ledger.js) Phase 2 measured (5 Oct): neither is a game-wide dial. jailW held out −0.00 ± 0.01; stealW held out +0.00 (every fold picks 0), 1/15 armies better, Tyranids +0.5 at 0.5 (placebo +0.1): a Tyranid matter for T-676. reports/tiers/2026-10-05-previews-measured.md
T-685built
One exam hook for the previews (the design session, 5 Oct; serves T-682, T-683, T-684)
More
Built 5 Oct (local commit, not pushed): tools/exam.js scores a setting through an optional post: { module, args } (or --post tools/<x>.js --post-args k=v, which adds the first setting again through it): the module's post(D, run, sliders, args) → run rewrites unit values after compute(), the exam re-sorts on them, then pair tables, the shape() check and the paired bootstrap. tools/rivals.js, tools/typical.js and tools/steal.js each export a thin post (their own adjust names were taken, so the hook is post). No number moves: every committed benchmark through exam.js gives today's output to the character; each preview at 0 is Track A to the bit; at 0.5 each matches its own scorer to the bit (smoke test, pooled 14: rivalW 0.5 −0.4 [−1.6, +0.9], typicalW 0.5 +0.1 [−1.3, +1.4], stealW 0.5 −0.1 [−0.3, +0.1]). Measuring each preview is its own card. tests/unit/exam.test.js covers the hook.
T-686built
The dated Marine checks read the Marines' ledger as it was before T-681
More
Two tests pinned numbers made before T-681 built the Marines' scarcity and Soak-effects files (the step-6 baseline fingerprint; the 3 Oct report's Tista Minis ρ 0.0477). tests/unit/ledger-before-t681.js reads docs/lab/ledger/space-marines.json at e678d9ed81 (git show, sha256 checked; a shallow clone fetches it); ledgerpage and ledgercheck use it, tools/ledgercheck.js run() takes opts.data. Fixture hashes and the report unchanged. Waiting: CI on a shallow checkout
T-687built
The ledger on today's engine and rules work, before the dataslate (Jordan, 5 Oct: "bring that engine into the ledger, and anything else that is pre dataslate update but relevant to the snapshot")
More
A deliberate re-freeze (T-603): the ledger takes every improvement to our own code and rules files since eb3590a (T-582's full keyword reading in docs/ev.js, effects-file corrections, the BSData reader) but keeps the datasheets, points and panels of BSData 374f505 (before the 30 Sep update the reviewers graded). Each drifted input classed as our improvement (take) or the new data (keep old), build/units.json rebuilt from 374f505 with today's code; Track A, B and the trio re-scored on the new snapshot (Tyranids, exam), old snapshot kept so every past number reproduces. Before T-673 phase 2 measures Built (5 Oct): snapshot 8eb69bc7af with the data kept as at eb3590a, inputs hash a20dbd2c3316 (old lock kept, LEDGER_INPUTS_LOCK); Drukhari on their nine; Track A Tyranids 79.3 → 79.1, exam 59.1 → 59.0; reports/tiers/2026-10-05-refreeze-engine.md.
T-688built
Best matchup: the attacker set up for the most damage into what it faces (Jordan, 4 Oct: "equip the best weapons, detachment, squad leader, all possible legal options to fight against the opponent in front of it… options for vs Infantry, vs Elites, vs monsters")
More
Built 4 Oct, on the branch (not on beta: no beta push for now): docs/bestmatch.js, a 🎯 Best matchup menu beside the attacker's list chip: Into <defender>, then vs Hordes, Infantry, Elites, Monsters, Vehicles, Heavy vehicles (stand-in targets under a pseudo-army, the rankings' five plus a monster T10 Sv2+ W12). It searches the squad's legal wargear (Loadout.solve against the target, in the mode played), the leader and support characters (Legends left out) and their wargear, the army's detachments greedily up to 3 DP (the player's own kept where nothing beats them) and every rule option (detachments', characters', the unit's own), scored by the calculator's dice engine on common seeded rolls (600 trials; wounds taken off the target, overkill not counted). Keeps size, mode and situational switches. app.js: recompute's volley building is now attackPlan(), shared. Under a second on a phone-class search. Round 2 (4 Oct): (1) bsdata.js reads which chapter a detachment is for (BSData hides it unless the main catalogue is that chapter's) into detachments.json only; 37 Space Marine detachments are a chapter's and 9 of the Aeldari's are the Drukhari's; Best matchup offers only what the army may take (the Detachment picker is unchanged). (2) Conditional effects (taken on trust: after charging, when Battle-shocked, vs the Oath target) count half in the search, so it no longer chose Rage-Cursed Onslaught for its "+1 A when Battle-shocked". (3) Fixed in the calculator: a joined character's gun read as melee in detInPlay (it isn't on its squad's datasheet) and took melee-only effects (a Judiciar's bolt pistol got +1 A after charging). (4) A character joins once. (5) The menu rises over the swap button on a phone. Round 3 (Jordan, 4 Oct: "hide other chapters' detachments in the picker too"): app.js detPickable/detLegal: the Detachment part, a list's and Army vs Army's pickers offer only what the army may take (Space Marines 16 of 53; Blood Angels their own and the Codex ones); a saved or linked one of another chapter no longer applies (detsOf, fitDets); findDetachment still knows them all for imported lists. Not changed: unit pages' best detachment (tools/build_rankings.js, a scoring change). tests/browser/bestmatch.test.js; screenshots desktop and 390px, light and dark. Open: the defender has no Best matchup; a leader's squad-wide manual buffs are the calculator's as before.
T-689open
A second yardstick: the span match across squad sizes (Jordan, 5 Oct: "if we have an S for one sized squad and a C for another, and the reviewer says C, we call it matched")
More
Per unit, our letters across its legal squad sizes (each size at its best detachment, the army cut as today); a reviewer's letter inside that span scores 1 ÷ the number of letters spanned, outside 0. Reported beside the strict pairwise score in tools/exam.js and the Tyranid headline, per army and pooled, with intervals; a placebo (spans of the same widths placed at random) beside it. Changes no ledger value; dials must hold on both yardsticks
T-688built
Best matchup: the attacker set up for the most damage into what it faces (Jordan, 4 Oct: "equip the best weapons, detachment, squad leader, all possible legal options to fight against the opponent in front of it… options for vs Infantry, vs Elites, vs monsters")
More
Built 4 Oct, on the branch (not on beta: no beta push for now): docs/bestmatch.js, a 🎯 Best matchup menu beside the attacker's list chip: Into <defender>, then vs Hordes, Infantry, Elites, Monsters, Vehicles, Heavy vehicles (stand-in targets under a pseudo-army, the rankings' five plus a monster T10 Sv2+ W12). It searches the squad's legal wargear (Loadout.solve against the target, in the mode played), the leader and support characters (Legends left out) and their wargear, the army's detachments greedily up to 3 DP (the player's own kept where nothing beats them) and every rule option (detachments', characters', the unit's own), scored by the calculator's dice engine on common seeded rolls (600 trials; wounds taken off the target, overkill not counted). Keeps size, mode and situational switches. app.js: recompute's volley building is now attackPlan(), shared. Under a second on a phone-class search. Open: (1) BSData files chapter detachments (Rage-Cursed Onslaught, Unforgiven Task Force…) under Space Marines, so a plain Marine squad can be given another chapter's detachment, as the Detachment picker already allows; needs a chapter tag on detachments.json. (2) Check one result that looked high: Intercessors + Judiciar + Ferren Areios in Rage-Cursed Onslaught into five Terminators at 14.3 of 15 wounds. (3) A browser test; screenshots at 390px and desktop, both themes; the defender side has no Best matchup yet.
T-689built
A second yardstick: the span match across squad sizes (Jordan, 5 Oct: "if we have an S for one sized squad and a C for another, and the reviewer says C, we call it matched")
More
Per unit, our letters across its legal squad sizes (each size at its best detachment, the army cut as today); a reviewer's letter inside that span scores 1 ÷ the number of letters spanned, outside 0. Reported beside the strict pairwise score in tools/exam.js and the Tyranid headline, per army and pooled, with intervals; a placebo (spans of the same widths placed at random) beside it. Changes no ledger value; dials must hold on both yardsticks. Built 5 Oct (local commit): tools/spanmatch.js and tools/exam.js --span (byte-identical without it). Pooled 14 armies span / strict / placebo: A 25.2 / 25.2 / 18.9, B 26.5 / 26.8 / 19.2, trio 26.1 / 26.3 / 19.3; Tyranids 36.5 / 36.7 / 22.3 (A). Span equals strict (−0.1 [−1.4, +1.1] pooled on A): one graded unit in seven has a span. reports/tiers/2026-10-05-spanmatch.md
One yardstick step, no ledger value moves: the Tyranid headline scores the three 11th-edition transcripts (Auspex, Hivemind, Into the Hive Mind), with the five-list number beside it (Maelstrom and Astrategas are 10th edition, March and April 2026); every army's panels marked 11e or 10e by date (11th edition from June 2026); Leagues of Votann rejoin the exam's pool on Jordan's 11th-edition Auspex list (29 Aug), its 10th-edition lists beside. Benchmarks re-stated on both; tools/exam.js, ledgerstep.js and the front door follow. After T-687's rebuild. Built 5 Oct (waits for review): tools/edition.js; Tyranid headline on the three (A 82.4, five 79.1); the exam pools 15 on 11e lists (A 59.1, trio 61.9, +2.8 [+0.4, +5.4]; without Votann +2.0 [−0.4, +4.3]); APART removed; reports/tiers/2026-10-05-headline11e.md
T-690built
Audit every army's detachments and rules coverage in the ledger (Jordan, 5 Oct: "are the detachments all loaded and their math implemented correctly?")
More
No rebuild. Per army of the 16: which detachments the ledger builds forms for (against detachments.json, the matched-play list and chapter legality), and for each detachment its rules, enhancements and stratagems as dice, partly or noted; then two or three detachments per army hand-checked: a unit's Kill in that detachment worked by hand from our effects data and compared with the ledger's form. A one-page table per army; every mismatch a line with its cause. Found already: Drukhari built with no detachment (detfx key under Aeldari) and Genestealer Cults missing Infestation Swarm, both folded into T-687. Built (5 Oct): tools/detaudit.js, reports/tiers/2026-10-05-detachment-audit.md: 15 of 16 load every matched-play detachment (Drukhari none; Infestation Swarm is Boarding Actions, rightly out); 40 hand checks reproduce the ledger exactly, 35 read the rule right; rolls, ladders and once-a-battle picks kept all game (Emperor's Children, Space Wolves, Ordo Xenos, Orks), Wrath of Khorne on Epic Heroes, checkable conditions written as vouched; a ×1.065 on every unit for noted rules picks the winner in seven armies; no enhancements or stratagems. Waiting on the supervisor: which fixes go into T-687
T-695built
Army rules and the units that enable them, in every army (Jordan, 5 Oct: the gap Tyranids don't have, where Synapse is credited)
More
Each army's army rule as dice where our data can say it (armyfx.json; 0% dice in 11 of 16 armies today), and the units that switch it on or carry it credited the way Synapse carriers are: order-givers (Astra Militarum), Cabal psykers (Thousand Sons), Pain (Drukhari), Waaagh! (Orks), Contagion (Death Guard), Cult Ambush (Genestealer Cults), Blessings of Khorne (World Eaters, T-672's armyPick) and the rest. Game-wide dials at 0, a placebo beside each, measured held out by army; likely where much of a character's worth runs (T-673). Our own words; after T-687's rebuild. Phase 1 built (5 Oct): tools/armyrules.js catalogues each army rule of the 16 armies, its shape, its enablers with the reviewers' misses and a plan (tools/armyrules.json; reports/tiers/2026-10-05-army-rules.md; design/ledger.md section 38). Found: the ledger applies no army-rule dice in any army (Oath of Moment never reached the Marine ledgers); enablers the reviewers rate above us (Cabal casters +29, officers +13, Ork bosses +11). tools/armyrules-draft.json: four armyfx.json entries, to merge after T-694. Phase 2: build and measure the eight dials (armyMarks, armyRounds, armyStates, armyAura, armyEnabler, armyOrders, armySpawn, armyMove) one at a time after T-687, predictions in the report. Phase 2 built and measured (5 Oct): the four armyfx.json corrections merged and the ledger re-frozen (d8b5a1bb14, data at eb3590a, hash c5e7d85b43f8; Death Guard 53.4 → 54.6, nothing else moves). Five dials in ledger.js, all 0 (armyMarks, armyRounds, armyStates as Burst variants from tools/armyburst.js; armyEnabler, armyOrders as credits), measured by tools/armymeasure.js. armyEnabler is the one above its placebo (+0.3 pooled; Thousand Sons +4.9); armyOrders is small (+0.1); the other three stay at 0. armyAura, armySpawn and armyMove were not built. reports/tiers/2026-10-05-army-rules-measured.md. Waiting: the cascade decides on armyEnabler (and armyOrders).
T-696open
Soak effects (healing, models returning, time off the table) for the other 14 armies (Jordan, 5 Oct)
More
soakfx-rules files as Tyranids and Space Marines have them (T-599, T-681), built with tools/soakfx.js from our effects data; today their soakfx files are empty. Expected gain small (Space Marines: 0 in T-681). Ledger-own files, no re-freeze; after T-687
T-698built
How robust is Kill: each unit's rank under plausible changes, and the burst spike (Jordan, 5 Oct)
More
On the rebuilt snapshot: vary the uncertain pieces of Kill within plausible ranges (melee landing scale, target-choice sharpness β, the enemy field's class mix) and report how far each unit's rank moves; steady units solid, swinging units flagged "uncertain" (for T-697's pages). Fix the burst stage switching between ranged and melee that gives Mephiston 4× in one detachment (T-690 audit; 9 cases), as a correction measured before and after. Measured alongside the other cards after T-691. Built (5 Oct, snapshot c5e7d85b43f8): the mode is chosen by Burst (step 45: Mephiston 4.0× → 1.14×, Tyranids 82.4 unchanged, exam 59.2 → 59.4); tools/killrobust.js, 41% of 733 units uncertain (Tyranids 13%), lands the main driver; reports/tiers/2026-10-05-kill-robustness.md and .json
T-694built
Detachment corrections from the T-690 audit, before T-687's rebuild (Jordan, 5 Oct: fix items 1 to 4, then one rebuild; corrections in the shared detfx.json)
More
(1) Choices that are really rolls, ladders or once-a-battle picks (Coterie of the Conceited, Saga of the Great Wolf, Ordo Xenos Mission Tactics, Madcap Meks, Dread Mob, Khorne Daemonkin) get a tag the ledger reads and count at a share of the game, not their best case; the calculator still lets a player pick. (2) A noted (non-dice) detachment rule's multiplier reaches only the units the rule names. (3) ledger.js detRulesOf reads choice options' unit keywords (Idols of Khorne). (4) Data: Wrath of Khorne excludes Epic Heroes; Born Soldiers' and two Drukhari rules' target-keyword conditions, Dark Age Arsenal by weapon, the three S-above-T rules by sVsT, Blood Tithe's Feel No Pain written checkable. Our own words; the ledger's frozen detfx is the pre-dataslate one plus these corrections Built (5 Oct): 8eb69bc7af (data, ledger mixing, scoping, notTargetKw/notUnitKw); in the ledger through T-687's re-freeze; not stamped (pending).
T-700open
Per point or total worth: are we measuring the wrong quantity? (Jordan, 5 Oct)
More
A switch the cascade searches: rank each army by Value per 100 points (today) or by a unit's total worth in a list (Value × points ÷ 100), and blends between. Front door open question 10: total worth lifted 14 armies +1.5 and cost Tyranids −2.9 (4 Oct). Searched in the cascade from World Eaters on
T-701built
How much of the reviewers is popularity? A report, never in the letters (Jordan, 5 Oct)
More
Add tournament list share (tools/popularity.js, T-693, grimstat-corpus CC BY 4.0, 11th edition only) to Track A's Value as an experiment and measure agreement per army and pooled, held out, with a scrambled control. If agreement leaps toward the reviewers' ceiling, the gap is what players take rather than our maths; if not, something structural is off. Popularity never enters a published letter. Measured 5 Oct (tools/popshare.js, the exam's post hook): Value × (1 + p × z) held out +12.5 ± 3.2 (scrambled +0.6); exam at p 2 59.1 → 72.2, +13.1 [+8.9, +17.2], 14/15 armies; popularity with Track A breaking ties 73.0; World Eaters 83.2 (reviewers 82–85), Space Marines 80.0 (91). The gap is mostly what players take. reports/tiers/2026-10-05-is-the-maths-wrong.md
T-702built
Free fit on World Eaters, beside scrambled letters (Jordan, 5 Oct)
More
Every knob free (rung (c)'s per-feature slopes, T-662) fitted on World Eaters' three 11e lists, and the same fit on scrambled letters (several seeds), and on the other lists held out (fit on two lists, score the third): how high it goes, and how much of that memorising produces. A report. Measured 5 Oct (tools/wefit.js, 136 knobs): 67.5 → 94.3 in sample, scrambled letters 94.3 too (5 seeds); held-out list 85.5 (the other two reviewers' consensus 89.3); held-out units 64.4 against Track A's 67.3. All memorising. reports/tiers/2026-10-05-is-the-maths-wrong.md
T-703built
Popularity as a pointer: where players and our maths disagree, army by army (Jordan, 5 Oct: yes as a diagnostic; letters stay rules-only)
More
For every army: the units players take far more than our Value says, and the ones we rate high that nobody takes (11th-edition list share, tools/popularity.js, T-693; grimstat-corpus CC BY 4.0), each with what its lines credit and a short guess at the missing ingredient, grouped into kinds that recur across armies. A report; popularity never enters a letter. Built 5 Oct (tools/poppointer.js; reports/tiers/2026-10-05-popularity-pointer.md and .json): 15 armies (Agents skipped, 1 list), 6 underrated and 6 overrated each on Track A. Underrated units take 20% of Value from non-Kill lines, overrated 9%. Kinds by armies (lift over the base rate): like-job rival 15 (×1.24), Deep Strike 13 (×1.26), leader 11 (×2.1), fitted multiplier 11 (×1.35), durable 11 (×0.81), enabler 10, forward deploy 10 (×1.6), transport 9; sibling datasheet 6 (×3.1). Every kind but indirect fire has a dial, and the dials measured close to nothing game-wide. Jordan's Tyranid notes: Gargoyles, Winged Hive Tyrant and ranged Warriors found alone (with the rivals he named); Leapers, Carnifexes and Toxicrene not
T-705built
Reviewers' reasons from the transcripts, as tags (Jordan, 5 Oct)
More
From the full 11th-edition transcripts (Tyranids' three, and any other full ones), each graded unit's stated reasons as short tags in our own words (screen, scorer, unlocks a squad, durable, delivery, damage into X, overcosted, competing sibling, army-rule engine), never quoting; per unit and per reviewer; then which tags go with which letters. Feeds T-704's variables. Built 5 Oct (tools/reasons.js; reports/tiers/2026-10-05-reasons.md and .json): 27 tags (positive, negative, context), each stated or inferred, from our why notes, the older panels' tags and the source tables' tier names and notes (no transcript opened, no text copied); 24 11e panels, 928 graded units, 290 tagged. The Tyranid transcripts were read for grades, not reasons: 22 of 156 units give anything taggable, one a role reason. Stated tags (883 units): leader-unlock +1.0 letter, sibling-better −0.6, overcosted −0.26, detachment-dependent level. Gaps with no dial: situational, copies, unreliable dice, indirect fire. Jordan's six: only the Carnifexes have a stated reason (one, led by Old One Eye); the letters put his named rivals higher. Next: reasons written into the panels' why in our own words, unit by unit from the transcripts (Tyranids first), then a re-run; that needs the transcripts in hand (Jordan)
T-704built
The pooled readable model: what reviewers value, across all armies (Jordan, 5 Oct; beside the solution cascade)
More
One model on all 16 armies (about 730 units): about 25 variables a player recognises (Kill by target class, Soak, OC, speed, Deep Strike, forward deploy, Lone Operative or Stealth, unlocks a squad, army-rule enabler, Synapse, transport, points, and the like), each army a shrunken adjustment, fitted on reviewers' consensus letters (ordinal), tested on whole armies held out; output: each variable's value relative to Kill with uncertainty, and where armies differ. Second target, discovery only: 11th-edition list share (never in a letter). Built 5 Oct, version 1 (tools/readable.js; reports/tiers/2026-10-05-readable-model.md and .json): 595 graded units, 9,354 consensus pairs, pairwise logistic with army deviations shrunk by cross-validation; the variables are a swappable table for T-707's list (version 2). Held out by army 60.1% against Track A 61.9 (15 without Tyranids 59.5 against 60.1), Kill alone 58.7, scrambled 50.7 (20 seeds); beside Track A +0.2. Relative to Kill: Epic Hero 0.8, OC per point 0.8, Psyker 0.7, Deep Strike 0.6, Move 0.6, enabler 0.5, points 0.4, Fly −1.0; placebo 0.16 [−0.30, +0.74]. Players weigh OC per point about 3× reviewers. Nothing adopted. Version 2, 5 Oct (tools/readable.js, --v1 keeps version 1; reports/tiers/2026-10-05-readable-model-v2.md and .json): design/jobs.md's priorities 1 to 25 (all built; the five partly ones as small proxies), unlocks a squad as its own column, slot competition among siblings, five interactions and seven saturation ranks, 42 columns. Held out by army 59.8 against version 1's 60.1 and Track A's 61.9; scrambled 50.2; interactions and saturation add nothing held out; version 1's Epic Hero and Psyker added back give 61.9 (61.1 against 60.1 without the Tyranids): jobs.md lacks a named-centrepiece variable. Reviewers: anti-tank, anti-elite, reserves, enablers, Hidden, CP up; Fly and the army's cheapest bodies and action-doers down; the leader package +0.01. List share (grimstat-corpus, CC BY 4.0): version 2 61.2 against version 1's 58.6, players take the squads leaders lift (+0.10). Nothing adopted
T-705open
Reviewers' reasons from the transcripts, as tags (Jordan, 5 Oct)
More
From the full 11th-edition transcripts (Tyranids' three, and any other full ones), each graded unit's stated reasons as short tags in our own words (screen, scorer, unlocks a squad, durable, delivery, damage into X, overcosted, competing sibling, army-rule engine), never quoting; per unit and per reviewer; then which tags go with which letters. Feeds T-704's variables
T-707built
What 40k players actually value: 25–30 jobs and qualities, each with a measurement recipe (Jordan, 5 Oct: "antitank vs horde clear vs screen vs action monkey… I have no idea how you will measure those things")
More
design/jobs.md: per variable, what players mean (plain words), why it matters in 11th-edition missions, a measurement recipe from our data (ledger lines, unitfx/leaderfx/armyfx kinds, the charge model, mission notes), the data missing, and confidence; built on the existing jobs work (T-524, T-537, T-626), mission and rules notes, the popularity pointer (T-703) and reasons tags (T-705). Community understanding in our own words only. Feeds T-704's variables (version 2) Written 5 Oct (design/jobs.md; summary reports/tiers/2026-10-05-jobs.md): 30 variables in six groups, each with what players mean, why 11e missions pay for it, a recipe naming our fields (ledger form fields, effects kinds, build/units.json, the charge model, mission notes, deployments.json), what is missing, confidence and the existing dial. Priority list for T-704: 1–25 (20 buildable today, 5 need a small field or script), 26–30 wait for terrain, variance or a moving opponent. Hard ones with proxies: screening per unit (the 9" coherency cap makes the 8" arrival bubble about 345 sq in whatever the size), zoning and tarpits, unlocking a squad (the joined synergy as its own column), army-rule engines (marginal coverage, as synapseAdd), hiding, reliability. T-705 tags mapped to the variable keys
T-709built
Leaders compete for the best squad (Jordan, 5 Oct, from his Votann notes)
More
A leader is worth what it adds to the squads worth leading, over the best alternative leader for that squad (the Kâhl on Hearthguard leaves Grimnyr, the Iron-master and the Champion without a squad worth leading; the same for Space Marines' Phobos and Reiver Captains and Lieutenants). Sharpens leader-unlocks-a-squad. A game-wide dial at 0 as an exam post step, measured held out with a placebo; the wall units (reports/tiers/2026-10-05-solution-cascade-walls.md) reported. Built 5 Oct (tools/leadercomp.js): doesn't hold out (every fold picks 0; pooled −0.6 to −2.0, no better than its placebo); the engine's synergy picks the reviewers' preferred leader less often than chance, so the leader effects need an audit first; nothing adopted (reports/tiers/2026-10-05-leadercomp.md)
T-709open
Leaders compete for the best squad (Jordan, 5 Oct, from his Votann notes)
More
A leader is worth what it adds to the squads worth leading, over the best alternative leader for that squad (the Kâhl on Hearthguard leaves Grimnyr, the Iron-master and the Champion without a squad worth leading; the same for Space Marines' Phobos and Reiver Captains and Lieutenants). Sharpens leader-unlocks-a-squad. A game-wide dial at 0 as an exam post step, measured held out with a placebo; the wall units (reports/tiers/2026-10-05-solution-cascade-walls.md) reported
T-710built
A transport is worth what only it can carry (Jordan, 5 Oct, from his Votann notes)
More
Its own guns and toughness plus the delivery of riders that are slow, valuable and have no other carrier (the Hekaton for Votann's exosuit infantry); who may ride what from tools/transports.js. Sharpens transports per army. A game-wide dial at 0, held out, placebo, wall units. Built 5 Oct (tools/exclusivecarry.js, an exam post step; reports/tiers/2026-10-05-exclusivecarry.md and .json): not above its placebo game-wide (held out +1.33 ± 1.80 against +0.54; exam at 0.5 +0.7 [−0.4, +2.0] against +0.9; 5 armies better, 7 worse). Hekaton 10th → 5th and Sagitaur 14th → 4th of 22 (Votann +10.5, placebo +7.6); Drukhari +4.3, Emperor's Children +2.6; Agents −2.2, Orks −1.5, Tyranids −0.5 (Tyrannocyte 48th → 23rd). Nothing adopted; an army-specific candidate for the next cascade at 0, 0.5 and 1
T-710open
A transport is worth what only it can carry (Jordan, 5 Oct, from his Votann notes)
More
Its own guns and toughness plus the delivery of riders that are slow, valuable and have no other carrier (the Hekaton for Votann's exosuit infantry); who may ride what from tools/transports.js. Sharpens transports per army. A game-wide dial at 0, held out, placebo, wall units
T-711built
Objective presence: scoring units on objectives (Jordan, 5 Oct, from his Votann notes)
More
How many scoring units a unit (or its transport) puts on objectives: squad splitting (a Sagitaur splits ten Hearthkyn into two fives, one at home, one to the expansion), objectives kept after leaving; beyond OC per point. A game-wide dial at 0, held out, placebo. Built 5 Oct (tools/objpresence.js; reports/tiers/2026-10-05-objpresence.md): not a game-wide dial (held out +0.00, every fold picks 0; placebo −0.46; pooled −0.2 at 0.5); splitting and stickiness only, with the split's gain on its transport too, holds out +0.61 (placebo −0.06) on Votann's one list (Sagitaur 14th → 7th): an army-specific cascade axis at 0, 0.5, 1, and a lead for T-710
T-711open
Objective presence: scoring units on objectives (Jordan, 5 Oct, from his Votann notes)
More
How many scoring units a unit (or its transport) puts on objectives: squad splitting (a Sagitaur splits ten Hearthkyn into two fives, one at home, one to the expansion), objectives kept after leaving; beyond OC per point. A game-wide dial at 0, held out, placebo
T-712built
Unavoidable burst: Burst weighted by how sure its delivery is (Jordan, 5 Oct, from his Votann notes)
More
Burst delivered by Deep Strike, Rapid Ingress, strategic reserves or a transport after the enemy has moved (Thunderkyn, Hearthguard) counts more than Burst that must walk into range; the sharper version of arrivalKill. A game-wide dial at 0, held out, placebo. Built and measured (5 Oct), nothing adopted: tools/surefire.js (post step, dial sureFire); held out +2.7 ± 0.6 (placebo 0; without strategic reserves +3.5), Tyranids +0.2 at 0.5; moves melee walkers down more than Thunderkyn and Hearthguard up. For the next cascade at 0.25 and 0.5. reports/tiers/2026-10-05-surefire.md
T-713built
Stratagems in the ledger, the pilot on Tyranids and Leagues of Votann (Jordan, 5 Oct, from his Votann notes; pilot approved 6 Oct)
More
Done as a pilot (steps 44 and 44b, nothing adopted): the catalogue tools/stratfx/<army>.json and core.json (every detachment by slug, each dice stratagem as engine effects in our words, no names), tools/stratuplift.js (each unit's Burst or Soak with one use added, the ledger's own dice and field), tools/stratagems.js (10 CP a game spent by points per CP on a typical list by Value; credits; the post step; the cross-army fit). Points per CP at the margin by detachment: Tyranids 0 to 26, Votann 6 to 22; the Swarmlord's 4 CP buy +106 points a game in synaptic-nexus, +9 in subterranean-assault where the ledger ranks it. Face value: Tyranids −0.1, Votann +9.5 to +12.4 (one list; placebo credits +4.7 to +7.2); the cross-test fails its rule. Report reports/tiers/2026-10-06-stratagem-pilot.md. Rollout: T-742
T-714built
Audit the leaders' measured buffs (found by T-709)
More
T-709 judged leaders by what our engine says each adds to a squad, and that number looks wrong: the Apothecary's healing measures up to 6,113 per 100 points and wins ten squads; on Hearthguard Ûthar measures 2,730 against the Kâhl's 1,594; in 129 squad cases where reviewers separate the candidate leaders, our best leader holds the best letter 30% of the time (random 37%). A report like T-690: per army, hand-check two or three leaders' joined forms against leaderfx.json and the dice engine (heal, re-rolls, Lethal or Sustained, Feel No Pain, Deep Strike for the squad), find the cause (data, heal pricing, stacking), list the fixes. No rebuild until Jordan approves the fixes; then T-709 is re-tested. 5 Oct (the builder): the dice are right, the pricing is wrong: a leader's own command-point, healing and Synapse multipliers scale the whole squad (57% of the measured synergy), Kill is read on two scales (Burst alone, both phases joined; 27%), Support characters compete with Leaders, joining effects are divided by the leader's points; eight fixes proposed, none applied. reports/tiers/2026-10-05-leader-audit.md
T-716built
Job profiles, rivals by dominance, archetypes from the numbers (Jordan, 5 Oct: "clearly defining all of the different archetypes… grounded in the numbers")
More
Each unit a vector of measured job scores per point from design/jobs.md's buildable recipes; a unit loses its slot to a same-army unit at least as good per point on every job it is good at (Pareto dominance; multi-role units escape it); archetypes found by archetypal analysis on all armies' profiles (each unit a mix of pure types), the future role labels; rival pairs checked against list co-occurrence (tools/colist.js: substitutes rarely share a list) and Jordan's notes. Replaces T-682's likeness matching; a post step at 0, scored on Tyranids, Votann, Thousand Sons, then all
T-716open
Job profiles, rivals by dominance, archetypes from the numbers (Jordan, 5 Oct: "clearly defining all of the different archetypes… grounded in the numbers")
More
Each unit a vector of measured job scores per point from design/jobs.md's buildable recipes; a unit loses its slot to a same-army unit at least as good per point on every job it is good at (Pareto dominance; multi-role units escape it); archetypes found by archetypal analysis on all armies' profiles (each unit a mix of pure types), the future role labels; rival pairs checked against list co-occurrence (tools/colist.js: substitutes rarely share a list) and Jordan's notes. Replaces T-682's likeness matching; a post step at 0, scored on Tyranids, Votann, Thousand Sons, then all
T-717built
Bonus share by keyword, and bonus casters (Jordan, 5 Oct; from the Thousand Sons research)
More
(1) How much of its army's bonus package a unit can receive: the army rule, the detachments winning lists run and the army's auras, by the keywords each needs (PSYCHIC attacks, PSYKER, INFANTRY, the army keyword) — Rubrics and Scarab Occult collect them, generic engines don't; (2) casters credited for making the hard rituals succeed (Magnus +2 and two attempts, Ahriman +1) rather than for filling one of four slots. Post steps at 0, scored on Thousand Sons first, then all, with placebos Built 5 Oct (tools/bonusshare.js, an exam post step; reports/tiers/2026-10-05-bonusshare.md): neither dial holds out (bonusShare held out −1.22, pooled −1.2 at 1, placebo −2.0; bonusCaster ±0.0); Thousand Sons alone gains from the share (38.2 → 46.6 at 1, placebo 39.5), by pushing down units that receive nothing rather than lifting the Psychic infantry. Nothing adopted
From reports/tiers/2026-10-05-leader-audit.md: (1) a leader's army-wide multipliers act on its own share only; (2) one Kill scale for solo and joined forms; (3) Support characters as Support, beside a Leader; (4) healing scoped and counted once; (5) Lone Operative on a joined form only when every member has it; (6) the leader's dice gain priced apart from joining; (7) the missing grants (Deep Strike from Teleport Crest, the Judiciar's Fights First, +1 OC banners), pre-dataslate sense; (8) alone at the joined form's loadout. Data fixes need a re-freeze (approved with the fixes). Then rebuild, report Track A before and after, re-test T-709 and every leader toggle, and feed the next cascade. Built (5 Oct): all eight applied, re-frozen at 8dcd24032d (hash dcd477d8a78c), every ledger rebuilt; Track A 59.37 → 59.32 (Tyranids 82.45 → 81.61); leader check 25.8% → 50.9% by role on the dice lent (chance 38.7%); T-709 penalty half holds out +0.58 (placebo 0); no leader toggle holds. reports/tiers/2026-10-05-leader-fixes.md
T-718built
Archetypes, second pass: the missing types Jordan named (5 Oct: durable midboard shooting, durable midboard melee, very quick melee, shooting infantry)
More
T-716's 7 archetypes miss them because the 20 jobs describe what a unit does, not how: add melee-or-shooting share, speed, body type, engagement range, a cost band and a rough board position; run clustering (dense groups, about 10–14) beside archetypal analysis (extremes); durability also compared across armies, not only within; Jordan's groups as the test set (Genestealers, Leapers, melee Warriors, Necron Wraiths together; Haruspex and melee Steeljacks; Barbgaunts, Termagants, ranged Warriors and shooting Steeljacks; Maleceptor and Dreadnoughts). Built 5 Oct: tools/clusters.js, 16 groups; two of Jordan's four types emerge; the groups are kinds, not slots (reports/tiers/2026-10-05-clusters.md)
T-719open
Burst counts one phase only: units that shoot and fight in the same turn (found by T-718)
More
Burst values a form's activation in its one natural mode (tools/burst.js), so a unit that shoots then charges gets only the larger phase: Tyranid Warriors with Ranged Bio-Weapons measure about 97 ranged Kill a turn at β 6 but rank on melee Burst alone. Measure how many forms lose a large second phase, and test a two-phase Burst (shooting then fighting in one turn, the second phase against what the first left) as a correction, before and after on every army
T-721open
Titles and descriptions that read well in Google, and earn the click (Jordan, 5 Oct: "card it… a lot of the titles as they appear in google dont really read well")
More
Why: the 5 Oct weekly report (reports/analytics/2026-10-05.md): 13,285 impressions in 7 days at position 8.7 but a 1.0% click rate; /best/anti-tank/ 115 impressions, 3 clicks; the home page alone earns 14%. Searches are "<unit> 11th edition", "<unit> datasheet", "<unit> points", "best <army> detachment", "<unit> loadout". What reads badly today (tools/build_pages.js): unit titles are a keyword list ("Killa Kans 11th Edition: Points, Tier, Damage"); descriptions pack shorthand Google shows raw ("A tier, 13th of 58 Orks units; best detachment Dread Mob (+56%)", "Best loadout: 1 × Siege cannon"); filler at the end ("Free, no sign-up."); army descriptions cut off mid-name ("Top pick: Ironkin Steeljacks with Melee…"); /best/ pages name surprising leaders without context ("Cthonian Beserks… lead the game" for anti-tank). Do: (1) a gallery first (an Artifact, before and after as Google would show them: title ≤ 60 characters, description ≤ 155, desktop and phone) for each page kind: home, army, unit (with and without a loadout choice), detachment, /best/, /meta/, /meta/patch/, /hill/; Jordan picks; (2) then build_pages.js writes them: plain sentences, the searcher's words first (the unit's name, "11th edition", points), one clear number at most, nothing cut mid-word (trim at a word and drop a list item rather than end on "…"), no filler; (3) never promise what we don't host: no "datasheet" in a title (no GW rules text on our pages); (4) the weekly report gains a CTR-by-page-kind line so we see whether it worked (measure 2 to 4 weeks after, Search Console runs 3 days behind). Our words only. Static pages only: the app's own title (index.html) stays unless Jordan picks a change in the gallery.
T-724open
Squad-wide wargear rules: "all models the same", "at most 2 of these", "no mixing" (found with T-723)
More
T-723 reads BSData's "cannot have both" errors per model; most of the 20 are squad-wide (Thunderkyn, Yaegirs, Cthonian Beserks, Lychguard, Immortals' gauss/tesla, Aggressors, Space Wolves and Vanguard Veterans' kits; Grey Knights' at most 2 heavy weapons; Blood Angels' one pistol swap; Recon Troopers), and loadout.js's squads can still mix them across models. Do: read those shapes from BSData (the error rules' conditions: atLeast/greaterThan on entries with scope the unit) into a squad-level rule (same kit for every model of a kind, or a count across kinds), enforce it in Loadout.squads and the Weapons sheet, test on each named unit. The other ~220 error rules (squad size and "1 per N models") are checked against limitAt (design/wargear-limits.md) in the same pass; list any it misses.
T-726built
Dial inventory: every dial, searched or not (Jordan, 5 Oct: every dial goes into the searches; off is one of a dial's options, never its permanent home)
More
Built 5 Oct: tools/dialinventory.js and tools/dial-inventory.json: 161 dials (the page's 118 slider keys, 13 rule multipliers, 4 letter cuts, 24 post dials over 10 exam post modules and gradeBy, the feature family, the popularity diagnostic) plus 35 constants that could be dials; --check fails when a new slider key, rule kind or post module is missing. Each dial tested live on Track A on all 16 armies (snapshot dcd477d8a78c): 145 live, 11 inert at the test value (7 of them live once a sibling is set); cascade2 searched 26, left 119 live dials out (supportMeasured and synapseMarginal among them, though asked for). Duplicates: prem = wKill exactly; only the weights' ratios matter; smallPiece = pointsExp with the sign turned; scarceAnswers ≈ scarcity off (ρ 0.997); objPresence ≈ screenScore in direction (0.963); joinedShapley ≈ joinedMarginal at half strength; typicalW a shorthand. Removal candidates for Jordan: prem, pointsExp or smallPiece, typicalW, r.arrival, killMargin, armyPick, charsJoined, profileDials, popshare; bonusShare's use of list shares is his call. Next search proposed: a space-filling screen of every live dial (about 4,550 settings, 2–3 h) then a full factorial on what passes (0.5–4.7 h). Report reports/tiers/2026-10-05-dial-inventory.md. Nothing removed or adopted.
T-727built
Held-back dials on the cascade survivors: partial, superseded (Jordan, 5 Oct: every toggle goes into the searches)
More
Built 5 Oct: tools/heldback.js (cascade2.js extended with options, its Track A check unchanged): the 87 dials cascade2 held back (every remaining ledger slider and post step; the leader-share switches leadersLeading, joinedMarginal, joinedShapley and leaderCredit among them), two values each, added one at a time to the main chain's 213 and the clean chain's 240 survivors, scored as cascade2 with its scrambled seeds. Stopped by design after 1 h 35 min (Jordan's OK): superseded by the dial-inventory screen (T-726), then pairs, then one sampled search. Finished: Dark Angels (125 values, all survivors) and jailW and stealW in every army. Dark Angels falls to single dials that beat their scrambled control (arrivalKill 1.5, r.buff 2.12, charPsychic 1.5: up to 119 survivors past 60), unchecked on the other armies; the leader-share switches cost it; jail and steal are noise-sized. reports/tiers/2026-10-05-heldback-partial.md
T-728built
Every dial screened, then one sampled search (Jordan, 5 Oct: every live dial in the search, off one level among others)
More
1a done 5 Oct (the builder): tools/everydial.js, 95 factors covering every live dial of the inventory (exclusions written in the report), each alone from Track A on all 16 armies against three scrambled letter sets (4.9 min): 36 pass on the fair-test seven; top preferredTargets C16, attachChars, sureFire 0.5, lifeKill, smallPiece 0.2; the leader credits, killByJob, the screens' area and block, scarcity off cost most; Dark Angels best +4.3 (sureFire 0.5nr), Agents past 60 under r.heal 1, supportMeasured 3, charPsychic 1.5. Under the stricter bar (the scrambled gains' 95th percentile) no single level survives on the fair-test seven. 1b stopped by Jordan's call (random settings cost about 7× the estimate, about 22 h; the Plackett–Burman 192 that replaced it stopped before scoring); in its place ten hand-built candidates on cascade2's headline: none clears every bar; C1 + arrivalKill 1.5 best (all 16 69.2 against C1 68.3 and Track A 61.9; Dark Angels 59.7); preferredTargets and attachChars cost Adeptus Mechanicus; Agents at most 59.6. Stage 2 done (Jordan's go): the 2^7 factorial on C1 (1.7 min): nothing beats C5 cleanly; best all-16 C5 + charPsychic 1.5 + attachChars (69.5, fair-test seven down to 73.5); with sureFire 0.5 too, 2 bars missed (Adeptus Mechanicus, Emperor's Children) and both walls past 60 (Dark Angels 60.1, Agents 60.5); in sample. reports/tiers/2026-10-05-every-dial-screen.md
T-729open
Bonus share without popularity (Jordan, 5 Oct: "rebuild without popularity")
More
tools/bonusshare.js weights each detachment and aura bearer by its share of tournament lists; rebuild it with rules-only weights (every detachment equal, or the army's best), so no list share feeds a letter. Until then it is measured only, flagged. Then re-score it in the next search.
T-730open
A Necron ledger, and AI-aggregate lists as a yardstick (Jordan, 5 Oct: "list its provenance as GEMINI AGGREGATE")
More
Jordan supplied a Necron tier list written by Google Gemini from community sources (reports/tiers/panel/2026-10-05-necrons-gemini-aggregate.json; 29 units, reference only, reliability low). Necrons have no ledger yet, so it scores nothing. Two parts: (1) build the Necron ledger on the frozen snapshot (tools/ledger-all.js chain), with its effects coverage checked; (2) before any GEMINI AGGREGATE list joins a yardstick, calibrate one on armies with several human lists (Tyranids, World Eaters): it earns a place only if it agrees with the human lists about as well as they agree with each other; until then AI aggregates are kept apart from reviewer consensus.
T-731built
GEMINI AGGREGATE lists join the yardstick at half weight (Jordan, 5 Oct: "Half a reviewer")
More
Calibration (design/decisions.md, 5 Oct): Gemini agrees with human lists 3-8 points less than humans agree with each other. Add the 25 GEMINI AGGREGATE panels (reports/tiers/panel/2026-10-05-*-gemini-aggregate.json) to the 16 ledger armies' yardsticks at weight 0.5: a deliberate re-freeze adding only the panels (tools/ledger-inputs.js), the ledger merge reading them as reviewers with a weight, and the exam, cascade2 and everydial scores counting each list's pairs by its weight (consensus pairs: a pair agreed by the humans and Gemini counts fully; Gemini's own pairs at half). Report every army's agreement before and after, today's model and the C5 setting. The 9 armies without a ledger keep their Gemini lists as reference until they get one. Built (6 Oct, overnight; brief amended: no re-freeze, no rebuild): tools/yardstick.js reads the panels at scoring time by unit name, behind a flag (LEDGER_YARDSTICK=half for cascade2 and everydial, tools/exam.js --gemini 0.5); the rule is written in its header; without the flag every score is as before, to the pair. 15 ledger armies have a panel (Emperor's Children none). Consensus score barely moves (Track A 61.9 → 61.7, C5 69.2 → 69.2 on 16); the exam's list mean loses 1 to 2 (Track A 59.3 → 58.4, C5 66.7 → 64.9 on 15). Jordan to pick which is the headline. [yardstick-exam](reports/tiers/2026-10-06-yardstick-exam.md)
T-737built
Death Guard: targeted checks from Jordan's notes (5 Oct: the Biologus, Rhino and Icon Bearer rated far above our ledger, Poxwalkers below)
More
Report 6 Oct (reports/tiers/2026-10-06-death-guard-checks.md, tools/dgchecks.js; frozen snapshot dcd477d8a78c, Track A; nothing adopted). (1) Biologus: the dice are fine. The gain is small because 11e Plague Marine weapons already have Lethal Hits (+7–10% on the all-melee brick, +36–56% into vehicles); what's missing is two-leader forms and leader credit. (2) Fire Support: noted in unitfx.json, not counted. It's worth about +26 per 100 Rhino points the turn a boltgun brick unloads, shooting only; the Rhino's A is delivery (carry weighted 0). (3) Poxwalkers: the dice are modest; the heal kind's ×1.805 on their whole Value lifts them from 15th to 2nd of 36. (4) T-723/T-724: the Helbrute's (Death Guard) and the Thunderkyn's (Votann) best forms are illegal (next legal 959 and 674, lower bounds). Waiting on Jordan: the Fire Support data patch in the report (a kindfx.json lend, the unitfx status), not applied.
T-738built
Listhammer for the model, and a results yardstick (Jordan, 6 Oct: "Yes, internal use")
More
Internal, never shown: grimstat-corpus's listhammer/ folder read for model-building only (anonymized, list names dropped; data in the git-ignored build/tournaments/). tools/sources/listhammer.js (header: internal use), tools/sources/merge.js (Listhammer added; a record-only list matches an earlier source's on event, army and record; within one source two lists never merge; lists after the 1 Oct dataslate marked and kept apart; --without, --out), tools/unitoutcomes.js (per army and unit: share in winning lists, an outcome score with an empirical-Bayes shrink, and Spearman against the reviewers' consensus and Track A's Value). Numbers stay in the internal report (reports/tiers/2026-10-06-listhammer-outcomes-internal.md, never rendered in the Lab: build_pages.js skips -internal.md) and design/decisions.md. Nothing adopted. Waiting on Jordan: whether internal aggregates may stay committed, and which results yardstick (if any) the model should be judged on.
T-735built
11th-edition tournament data: every source found and vetted (Jordan, 6 Oct: "leverage Gemini to hoover up as much tournament data for 11e as possible"; then "ignore 30/sept rule and just acquire as much anonymized data as you can, for building our model, not publishing")
More
Internal model use only: data in the git-ignored build/tournaments/, never committed, published or shown, fed to no ledger, popularity file or yardstick. Found with Gemini (15 grounded calls), each source vetted by hand (robots.txt, terms, what's public; tools/sources/check.js now knows 13 more platforms and searches GitHub, Hugging Face, Zenodo). Acquired: MiniHeadQuarters direct (tools/sources/mhqsite.js: standings and lists, +24 lists the corpus lacks, a W/L/D record on 651 of 653 lists) and Tabletop Herald's public pages (tools/sources/tabletopherald.js: 119 German, Austrian and Spanish events since 20 June, 2,402 players, 1,930 armies; field only, its API not called). Merged (tools/sources/merge.js; event name + date within 10 days + army + placing): 653 lists and 153 events since 20 June, against the corpus's 629 and 33; all European. Not read: grimstat-corpus's listhammer/ folder and Lexmechanic's relay of it: the read was refused by the session's permission check, so Jordan's own call; bot checks (Longshanks, Hutber, Goonhammer), BCP, Reddit (robots.txt), Spikey Bits and shop blogs (terms), New Recruit (robots.txt), app-only platforms; no open 11th-edition dataset exists. Report: reports/tiers/2026-10-06-tournament-sources-internal.md; table in design/sources.md. Off in tools/sources.json (internal: true). Next: Jordan decides whether to allow the listhammer/ read and how the set is used
T-734built
The held-out exam of the leading settings, and the "Gemini weights" test (6 Oct, overnight)
More
tools/yardexam.js. Held out (15 exam armies, 3 seeds × 3 folds, picked on 10, scored on 5; leave one out; paired bootstraps; both yardsticks): the best of Track A, C1, C5, #3 beats Track A +6.6 [+3.5, +9.8] but C1 by +0.0 ± 0.8; C1's game-wide part alone +2.7 on the 10 armies its choice never saw. Weights solved to hit Kill/Soak/scoring/support shares (a) 30/25/20/15, (b) 50/25/25, (c) 70/15/15, wKill fixed, on Track A and on C5: every one below its base (on C5 −1.8 to −4.9), never picked held out. Discovery only. Next if Jordan wants it: cascade2's game-wide choice re-run by folds from its stored records (minutes). [yardstick-exam](reports/tiers/2026-10-06-yardstick-exam.md)
T-732open
Eight roles: every unit given a role from its measured jobs, ranked within its role (Jordan, 5 Oct, from a GEMINI AGGREGATE taxonomy)
More
The roles: Anvil (objective anchors, tarpits), Hammer (primary strike), Heavy anti-armour and artillery, Action units (secondary scorers), Screens and chaff, Force multipliers (buffers, command engines), Disruptors (debuffs, Battle-shock, precision), Transports. Generic names, no GW text. Map them onto T-716's 7 archetypes and T-718's 16 clusters; assign each unit a primary role (and a second where it has one) by rules from design/jobs.md's measured jobs, never from Gemini's examples (those only to check). Then: (1) rank within role and test whether reviewers' gaps shrink when units compete only inside their role (slot competition, T-708's replacement idea); (2) list the roles we measure badly (Anvil needs T-720's tarpit value, Disruptors the debuff auras, Action units the secondary scoring) as the build list; (3) the role names for the public tier pages (T-697). Discovery first; nothing adopted.
T-733built
Second human tier lists for the single-list armies (the design session, 6 Oct, for Jordan)
More
Twelve armies have one 11th-edition human list in the yardstick. Searched overnight 6 Oct (YouTube search and watch pages, Reddit through pullpush, 4 Gemini search questions; window 1 Jun to 30 Sep 2026; Space Wolves on pre-codex rules): 7 new candidates, all verified (Orks 4: Auspex Tactics, Art of War, Secondary Objective, The Daily Krumper; Dark Angels: Grimdark Breakdown's buy-first ranking; Agents: Brother Tobias, as allies for Ultramarines; Death Guard: a Reddit picture list); none for the other eight, Space Wolves included. YouTube refused every caption request, so one panel only: reports/tiers/panel/2026-08-22-orks-auspex-tactics.json (Gemini draft checked against the video's chapter list, reliability low-medium; Orks' only human list, Sprues & Brews being retired). Four Gemini drafts wait for captions (build/gemini/t733/, git-ignored, with the tools). Nothing added to a ledger, the frozen inputs or a yardstick. Jordan: use the Orks panel (after a caption spot-check?); transcribe the other two Ork lists; whether a buy-first list, an allies list or a picture list counts. Report: reports/tiers/2026-10-06-second-lists.md.
T-739built
The new recipe, steps 1 and 2: the stable layer, then speed (Jordan, 6 Oct: "recipe first", one measured step at a time)
More
tools/recipestep.js (held out on the 15 exam armies, 3 seeds × 3 folds, paired bootstrap; leave one army out; Gemini at half; shuffled letters; Tyranid three-list consensus; the keep rule written before scoring). Step 41, the stable layer (Track A + charEpic 0.5 + aircraftBoard 1 + typicalSize 0.25): kept as Track A2 (candidate), held out +1.2 [−0.4, +2.6], 11 of 15 not falling, shuffled about 0; tools/ledger-benchmarks/2026-10-06-step41-stable.json. Adoption as the published benchmark is Jordan’s call. Speed: tools/movecheck.js first (Move is speed masked by unit type, +1.8 [+0.1, +3.5] over the ledger beside type controls, but −0.9 per 100 points); step 42, Reach as briefed (tools/reach.js, board share by turn 2 per 100 points against the army’s median): fitted wReach 0 in every fold, reverted; step 42b, Reach against its own kind (declared before scoring): +0.6 [−1.7, +2.7], 8 of 15, reverted. Report reports/tiers/2026-10-06-recipe-steps-1-2.md. Next: step 3 (durability), checked with movecheck.js first.
T-746built
Breakdown by army, rebuilt on Track A2 (Jordan, 6 Oct: the breakdown page on today's numbers)
More
tools/breakdownpage.js writes design/handover/2026-10-06/ledger-breakdown.html ("Ledger Breakdown by Army", the 4 Oct page's look) from the frozen ledger (dcd477d8a78c; build/ledger/ and docs/lab/ledger/), in about 10 s. Every unit under two models, Track A (step 34b) and Track A2 (step 41, its typical-size post step), each through the exam's path (recipestep runsOf: wCarry 0, post applied, re-sorted): rank and Value under both, our letter under both cut at the reviewers' tier shares, the human reviewers' letters (11e lists first, 10e after a bar, dimmed, not counted), the GEMINI AGGREGATE letter in its own column ("Gemini aggregate, half weight"; not in any figure), the Track A2 line bar with the rule bonus hatched, what its damage lands on, role chips; the tier board switches between the models. Agreement per army, both models, in plain words: "Pairs all the reviewers agree on" (cascade2 lettersOf/pairsOf; one list: its pairs) and "Each list on its own, averaged" (the exam's measure), with a "how to read the numbers" note; the Tyranids' old five-list figure beside, marked as the old measure. Tyranids: 89.5 → 90.4 agreed pairs (583), 81.6 → 82.6 each list, old five-list 78.8 → 79.4. Known issues flagged beside their rows, not fixed: Poxwalkers, Neophyte Hybrids and both Acolyte Hybrids (a heal rule ×1.8), the Death Guard Helbrute and Brôkhyr Thunderkyn (illegal wargear in the best form). Role chips are a reconstruction (the 4 Oct page had no generator; rules in the script's head). Screenshots 1200 px light and 390 px dark, no page-wide horizontal scroll. Not published by the builder: the main session publishes it
T-743built
Winning-list shape: replicate and test its ideas (Jordan, 6 Oct: approved, light and targeted; Night Geist Gaming's list-shape video, 5 Oct 2026: his framework, tested on our data)
More
Internal (Listhammer-derived lists; aggregates only, per-list data in the git-ignored build/listshape/). tools/listshape.js: --bands (his six Toughness bands with our exact dice, frozen snapshot dcd477d8a78c, Track A), --lists (stage 1), --unitfeats; tools/statlearn.js's shape features and --shape-md; tests/unit/listshape.test.js. Stage 1 on the winners' lists: his 3 / 4 / 6 shape replicates, a valley near 115 and none at 200, losing lists in whole fields share the shape, and the damage quota depends on the reading (the numbers are Listhammer-derived: in the internal report only, kept off the Lab board, T-744). Stage 2: no feature beats the ledger out of sample (bands hurt and coverage +1.0 [−1.4, +3.4] on 15 armies; anchor ratio −0.7; tier −0.2; within-tier rank +0.2). Nothing adopted; none declared. Idea for Jordan: the shape as a list checklist in My army (our words, our dice; it describes lists, not winners). Report reports/tiers/2026-10-06-list-shape-internal.md
T-744built
The Rhino's Fire Support in the calculator (Jordan, 6 Oct: approved the patch in reports/tiers/2026-10-06-death-guard-checks.md, beta push included)
More
Built 6 Oct, on beta for review: a transport whose passengers re-roll wound rolls against the unit it hit is a lend in docs/data/kindfx.json (rrWound all, our words; shooting only, the Falcon's for the turn; "got out of it this turn" a manual condition; the target the Marked chip) and partly in unitfx.json with a cant. Read on the 11th-edition pages, the same rule on 13 datasheets: Death Guard Chaos Rhino; Space Marines Razorback and Relic Razorback (every chapter through Space Marines); Coronus Grav-carrier; Skorpius Dunerider; Falcon; Manta (Aggressive Deployment); Goliath Truck; Crassus and Valkyrie Sky Talon (Astra Militarum and Genestealer Cults); Grey Knights Relic Razorback. Not the same rule, left noted: the Chaos Space Marines', Space Marines', World Eaters', Emperor's Children's, Sororitas' and Grey Knights' Rhinos (none has it), Thousand Sons' Rhino (Sorcerous Support, psychic +1 hit and wound), Taurox Prime (re-roll hits). In the calculator: the Army part's Nearby pick (Chaos Rhino for a Death Guard unit) gives a ⚡ chip, in play once the defender is Marked. Tests: unit and browser nearby; suite 603 passed, 2 failed as known (matrix killDP bound T-606, the Windows clipboard step in share). The rankings and the frozen ledger don't read it. docs/ regenerated (T-723's and T-688's pages, metatrack's detachment spelling, the Lab's new reports); T-743's Listhammer-derived stage 1 numbers taken off this card so the Lab board doesn't show them
T-745built
Bug-fix snapshot, and Track A2 as the ledger's benchmark (Jordan, 6 Oct: "go ahead with all of those, in the most sensible order"; T-737's two bugs)
More
Built 6 Oct: (1) rules that bring slain models back to a unit (44; unitfx.json returns: true) leave the heal kind's ×1.805 for their own kind, "Returns slain models" (r.return, × 1; the Spawn line's measured ones stay measured); (2) T-723's one-model wargear pairs via the re-freeze, T-724's squad-wide rules enforced in the ledger's Auto-equip (SQUAD_SAME: Thunderkyn, Beserks, Yaegirs, Aggressors, Headtakers; the rest still T-724). Re-frozen at b3c416933e, hash dcd477d8a78c → 1b88e955b9b3 (unitfx.json, docs/bsdata.js, build/units.json moved); every ledger rebuilt; the merge keeps panels that join no yardstick out of the comparators. Track A exam 59.3 → 59.7 (Agents +7.1, GSC −6.3; Tyranids unchanged); Poxwalkers 2 → 17, Neophytes 9 → 22; Helbrute 11 → 9 and Thunderkyn 5 → 4 (legal kits score higher). Step 41 still passes (+1.1 [−0.3, +2.7], 12 of 15): Track A2 is the benchmark. reports/tiers/2026-10-06-bugfix-snapshot.md
T-747built
Returning models measured on the Spawn line, every army (found by T-745)
More
T-745 took the rules that bring slain models back (unitfx.json returns) out of the heal kind; they count × 1 (r.return) until measured. Write each army's spawn rules (tools/spawn.js type: "return", cost tables, our numbers: Curse of the Walking Pox per model its attacks slay, Cult Icon up to D3 a Command phase, the Medi-packs, Narthecium beyond the Marines, Rebind Rubricae, Fleshcraft, Rage Eternal, Healing Balms), rebuild the chain (spawn is a ledger stage, no re-freeze), and score on Track A2 with tools/recipestep.js. Predicted: the Genestealer Cults recover part of their −6.3. Built and measured (6 Oct), reverted (hibernated): nine armies' rules files (tools/spawn-rules-<army>.json), nine ledgers rebuilt on 1b88e955b9b3 (no re-freeze: live files); held out +0.0 [+0.0, +0.0] over step 41, 15 of 15 not falling, every fold picks the base, so the keep rule's "positive gain" fails. Returning units gain 15 to 37 Value (Poxwalkers 814 → 831, Neophytes 28th → 27th); the leaders' rules (Slaughterbound, Exalted Sorcerer, Haemonculus, Wolf Priest, Ravenwing Command Squad) land on joined forms only and move nothing. The Spawn line's per-army median scale is why: carded as a question in the report. Files kept, read with LEDGER_SPAWN_RETURNS=1. reports/tiers/2026-10-06-spawn-returns.md
T-749built
Stratagems in the calculator, under the detachment (Jordan, 6 Oct: "in our own words, like re-roll ones or whatever", no stratagem or detachment names)
More
Stage 1, a mockup for Jordan (nothing in docs/ changes): design/mockups/2026-10-06-calculator-stratagems.html, screenshots beside it (phone 390 and desktop, light and dark; no horizontal scroll). A side's Detachment part gets a Stratagems block under its detachment chips: this unit's dice stratagems from the detachments picked (keywords checked; the rest a faint count), in our words with their CP ("Re-roll 1s to hit and wound · 1 CP", "−1 Damage · 2 CP"), grouped Shooting, Fight, Defence; a group out of play is faint and names its toggle. Tapped, one joins the Modifiers line as a teal ⚡ chip with its cost; every one starts unused. The CP total: one number in the block's heading, "CP this matchup" (a defence one counts once per enemy phase it covers), nothing on the result, no budget. Examples from the catalogue: Tyranids Hive Tyrant in Invasion Fleet, Death Guard Plague Marines in Virulent Vectorium, Termagants in Synaptic Nexus. Also proposed: the 282 "Stratagems, by name only" notes in docs/data/detfx.json (1,382 names) rewritten in our words from the catalogue (154 detachments covered, 297 dice stratagems; 128 get a count only), so no stratagem name stays in site data. Stage 2 built on beta (Jordan approved, 6 Oct, "Yes"): tools/stratcalc.js writes docs/data/stratfx.json (296 dice stratagems in 136 detachments and the core re-roll; our words, no names); docs/detfx.js strats/stratEffects; docs/stratui.js draws the block, the Modifiers line's teal ⚡ chips and "CP this matchup"; flag "strats": "beta" (the beta site and local copies; ?strats=1 anywhere). Beyond the mockup: a stratagem whose versions are true alternatives (Lethal Hits or Sustained Hits; the core re-roll's hit or wound) is a chip per version with "or" between, one use. The 282 notes rewritten in a separate commit (151 from the catalogue; 131 a count, the catalogue's empty entries counted as not covered); a unit test keeps names out. Waiting for Jordan's look on beta; main untouched. Handoff: reports/handoff.md (6 Oct, T-749 stage 2)
T-751built
The datasheet signals as ledger steps, one at a time (Jordan, 6 Oct: approved the five steps T-748 proposed, one at a time, each a declared step on the current benchmark; targeted, about 3 hours)
More
Built 6 Oct: all five reverted; Track A2 stays the benchmark, no Track A3 candidate. tools/sheetlayer.js (a post step chained after the stable layer: sheetSpeed, sheetEpicPsyker, sheetInfantry, each 0 = today) and tools/sheetstep.js (tools/recipestep.js's protocol, one dial per step on a small grid chosen on the training folds, three shuffled controls through the same fit; keep = held-out gain > 0, low end above −0.5, 9+ of 15 not falling, above the shuffled controls' mean). Each prediction committed before its score (ledger-steps rows 46 to 50). Held out over step 41: 46 speed within its kind +1.1 [−0.6, +2.7], 10 of 15 (k 1 in every fold; the shuffles pick 1 too, mean +0.3; fails the floor); 47 Epic Hero or Psyker −0.3 [−1.2, +0.5], 8; 48 Infantry −0.2 [−1.0, +0.7], 8; 49 the Soak weight ×2/×4 −0.1 [−0.3, +0.0], 7; 50 Lone Operative 2.5/3 +0.0, 15 (only the Orks move). The five together (a diagnostic) +1.3 [−0.6, +3.1], 9. Movers at speed 1: Land Speeders 29th → 6th and 37th → 14th (S), Helbrute 11th → 23rd (C), Wulfen and Wolf Scouts up, Logan Grimnar down. Report: reports/tiers/2026-10-06-datasheet-steps.md. Next: more human lists before speed is tried again; Toughness against the army's median as a flag (T-748's fallback, not run).
T-748built
Confirm the datasheet model on lists none of the fits saw (Jordan, 6 Oct: approved, targeted, about 1.5 hours)
More
Confirmed by the rule frozen beforehand (ledger-steps row ensemble-confirm); nothing adopted. T-736's declared ensemble (ledger Value rank + datasheet-model rank), re-fitted on snapshot 1b88e955b9b3 with Track A2 (tools/ensconfirm.js; statlearn.js gains --bench/--out and applies a setting's post step; the re-implementation equals statlearn's fit to the digit). Over Track A2: the 11 held-out human lists (the T-733 Orks candidate and each list of Tyranids, World Eaters, Space Marines and Genestealer Cults held back from its own fit) +3.9 [+1.3, +6.6], 9 up / 2 down; GEMINI AGGREGATE +5.3 [+1.8, +9.1] (already seen, weaker); the Orks list alone +1.6 [−7.8, +9.8]; without the Genestealer Cults +2.1 [+0.3, +3.9]. Leave one army out it shrinks to +2.4 [−0.8, +5.6] on 15 (Track A2's charEpic already holds part of it). Winners' list share (internal) as a diagnostic only. Proposed, for Jordan: five one-dial recipe steps on step 41, in order: speed within kind, Epic Hero and Psyker, Infantry, Soak's weight (toughness and wounds), Lone Operative (not built). Report: reports/tiers/2026-10-06-ensemble-confirm.md
tools/kindlayer.js (a post step chained after the stable layer: Value × exp(kInfantry, kMonster, kVehicle, kFly, kEpic, kPsyker on the datasheet's keywords) × (Move ÷ its kind's median)^kSpeed) and tools/kindstep.js (recipestep.js's protocol over a 2,916-candidate coarse grid, fitted on each training fold; base step 41). Reverted: held out +0.2 [−1.6, +1.9], 7 of 15 not falling; Fly −0.3, Epic +0.3, Psyker +0.3, kSpeed 1 stable, Infantry, Vehicle, Monster split; shuffled letters gain as much. Report reports/tiers/2026-10-06-recipe-step-43.md. Nothing saved as a benchmark.
T-736built
Different models, not more dials: statistical learning on the reviewer letters (Jordan, 6 Oct: "Trying 1000 large shifts to how we try to score these things beats 100000000 permutations of the same wrong approach")
More
tools/statlearn.js: one feature table (75 facts × 733 units at Track A: lines, Kill by class, datasheet, keywords, leading, support, jobs; human 11e letters, GEMINI AGGREGATE letters apart, list share as a diagnostic only), then 34 structurally different models fitted leave one army out on the human letters, each against 20 scrambled letter sets, bootstrapped against the ledger by army. Result: every re-fit of the ledger's own information (logistic L1/L2, ordinal, trees, neighbours, roles, value over replacement, leaders joined) lands within 2 of the ledger (61.9; 60.0 on 15 without Tyranids); facts it doesn't use do better: the declared ensemble (ledger rank + datasheet-model rank) +3.9 [+0.4, +7.3] on 15, the datasheet-only model +3.2, ranks of every fact +4.0, and on Gemini's never-trained-on letters +6 to +7 with intervals clear of 0. Reviewers value Move, Epic Hero, Psyker, Lone Operative, Infantry, durability (Soak about level with Kill, against the ledger's 85:3); Fly, debuff rules, transports, vehicles down; within-role scoring loses 4 to 5. Nothing adopted. Next: a confirmation run of the ensemble on unseen human lists | reports/tiers/2026-10-06-statlearn.md
T-559built
Tier test round 7: best-only grading, Feed the Swarm and the Tyrannocyte fixed, three yardsticks against the panel (Jordan, 2 Oct, via the design session; child of T-524)
More
Built 2 Oct (local commit, not pushed): tools/tiertest.js switch r7 and --r7json <file>: (1) best-only grading, score = best allowed job + V7C.BONUS 0.25 × each other allowed job's points above V7C.BAR 60, at most 100 (a weak job never lowers a unit; letters by quota as before); (2) Feed the Swarm lent to a Harvester at most one squad's aura worth (V7C.REGEN_UNITS, 38 pts): Pyrovores Banner 89 → 62 (B → D), Ripper Swarms 94 → 83 (B → C); the Tyrannocyte's cargo set down once (deliver's drop): Banner 91 → 21 (A → D); (3) yardsticks for every panel: ρ with SE, strict on our ruler (the panel re-cut at our quota), raw, within one, and shuffle baselines (2,000 permutations). Numbers (6b → 7): Tyranids ρ 0.39 → 0.50 (SE 0.12), strict on our ruler 19 → 20 (chance 10.3), raw 12 → 12, within one 27 → 29 (chance 20.4), Tyranid list share 0.32 → 0.39, every unit's list share 0.171 → 0.164, Space Marines −0.03 → −0.05, Orks −0.16 → −0.13; 291 of 1,161 letters move (150 up, 141 down). Up: Hormagaunts, Norn Assimilator, Tyrannofex, Gargoyles, Winged Tyranid Prime to S, Norn Emissary B → A; down: Neurotyrant, Psychophage, Venomthropes B → C, Lictor A → B. Report: reports/tiers/2026-10-02-tyranids-round7.md; scoring.md (V7C, a changelog line). Open: the healed squad's own Anvil doesn't count Feed the Swarm yet.
T-558open
CP worth from the stratagems a unit can actually use (Jordan, 2 Oct; child of T-524, follows T-557)
More
Why: round 6 (T-557) counted a Command Point at 0 in the tier test's letters; since round 6b it is a flat 30 pts (V6C.CP_PTS, Jordan, 2 Oct) until a CP has a measured worth (abilities that give, save or tax CP: the Hive Tyrant's, the Swarmlord's, the Lictor's and others, tierfx.json cp). What to build (tools/tiertest.js, a report; nothing on the site): (1) For each army and each of its detachments, list the stratagems a unit can use: names and our own one-line description only, never GW's wording (Wahapedia's 11th-edition faction pages, curl -sS https://wahapedia.ru/wh40k11ed/factions/<army>/, the errata on the same page; plus the core stratagems: Command Re-roll and the rest). Keep them in a data file of our own words, e.g. docs/data/stratfx.json { army: { detachment: [{ name, cp, phase, who (keywords), effects (detfx.js's mods: rrHit, rrWound, lethal, ap, fnp, inv, …), note }] } }, with the effects in the same vocabulary as detfx.json so ev.js can play them. (2) For each unit in each detachment, take the best stratagems it can use (its keywords against who) and work out with the engine what one CP buys it: the low end is a Command Re-roll on one attack roll or one failed save (ev.js's one-die re-roll, rrOneHit / rrOneWound, or one save re-rolled), the high end a re-roll-hits or similar stratagem on its best volley, both into the five targets (enemy points destroyed, step g's ppw) and against the six attackers (own points saved). The CP's worth for that unit = the expected gain per CP of the best stratagem it can use, in points; a unit with nothing better than a Command Re-roll gets the low end. (3) Use it in Banner where tierfx.json's cp gives or saves CP (the CP goes to the army's best user: the median of the army's top units, or the co-listed squads as variable 5 picks them) and where an enemy-CP tax costs the enemy (the enemy's median). (4) Re-run round 6 (--round6) with it as a switch and write the yardsticks against round 6 (reports/tiers/<date>-tyranids-round7.md or a section), and update design/scoring.md (V6C.CP_PTS replaced, a changelog line; tests/unit/scoringdoc.test.js names any new constant). Inputs: build/units.json, the BSData checkout, the round-5/6 command (reports/handoff.md, T-557's entry). | Carded 2 Oct (T-557's round); not started
T-555open
Visual baselines refresh with every UI change (the design session, 2 Oct)
More
The Visual workflow (.github/workflows/visual.yml, T-396/T-491) has been red on every beta push since 1 Oct: its baselines are only refreshed by hand (a manual run with mode "update" commits them to the branch). Decide: the workflow refreshes its own baselines on a push to beta whose commit message carries a marker, or the builder of any UI card triggers the update run. For now the design session triggers the refresh by hand after T-554. visual.yml unchanged so far.
T-556built
The live Defense counts a unit's own heals (Jordan, 2 Oct)
More
Built 2 Oct, on beta: build_rankings.js reads the coverage files' heal rules (to itself, each Command phase) and lifts hits to destroy by ev.js's healLift, the tier test's closed form, on the turns alive without the heal; rankings.json keeps def bare and adds hl and hr; unit pages' Defense table and the app's Rankings use def × hl, with a note on the page. 50 rows lift (49 Necron, Old One Eye); 19 pool letters change (7 Necrons up, 12 elsewhere down a band at an edge). Not counted: kill and fight heals, heals from other units (Technomancer, Ghost Ark), heals without numbers. Doubt: the lift reaches ×2 (Skorpekh Lord); a cap would be one constant. Report: reports/tiers/2026-10-02-defense-heals.md
T-546built
A mixed unit's Toughness by the core rule (found in T-545)
More
11th edition's core rules (Wahapedia, 05.02.01, and Attacking Attached Units): attacks on a unit whose models have different T use the highest T among its models on the battlefield (the bodyguard models' when a character is attached). Built 2 Oct for the live rankings, on beta (Jordan's go, 2 Oct): build_rankings.js unitT, the highest T among bsdata.js models; 13 rows take a higher T (Decimus and Aquila Kill Teams 4 → 6, Traitor Enforcer 3 → 6, Talonstrike 4 → 6); 7 pool letters change (5 up, the Artillery Team down at the B/C edge). Black Spear Task Force's tMajority is a detachment rule, which Defense doesn't count. Still open: the calculator (seedDefence) and ev.js take the first statline; app.js was another builder's lane. Report: reports/tiers/2026-10-02-defense-heals.md
T-542built
Every datasheet ability and every detachment rule as effects, faction by faction, by lists' share (Jordan, 2 Oct, before sleeping: "add all special rules to units, all detachment rules, at the very least")
More
Child of T-523 (Tier system v2); carries on T-343's batches until every army is covered. For each army: every datasheet's abilities (non-Legends first, then Legends) in data/unitfx.json, leaderfx.json for what a leader gives its unit, armyfx.json for army rules; every detachment's rule and enhancements in detfx.json (enhancements as per-detachment upgrades, scored later and never in the letter yet), stratagems by name only in a note. Our own words read on Wahapedia's 11th-edition pages, never rules text. Each rule records a status (modelled, partly, noted), a kind for what isn't dice (arrival, move, enemy, shock, mortal, spawn, heal, cp, aura, mark, synapse, overwatch, chosen, once, oc, lead, list) and, where it would touch the dice, why it can't yet; rare or once-a-battle effects carry off: true and start switched off in the calculator. One coverage report per army, reports/tiers/<date>-<army>-coverage.md (rendered in the Lab), with the vocabulary gaps counted. One commit per army. Order (Jordan, 2 Oct, via the supervisor): Tyranids (T-541, first), then Necrons, Adeptus Custodes, T'au Empire, Dark Angels, Emperor's Children, Orks, Chaos Daemons, Death Guard, Adeptus Mechanicus, Astra Militarum, Aeldari, then the rest by share. The corpus's count for reference (top-quarter lists, metatrack.json June to September, 150 lists; non-Legends datasheets in our BSData build): Adeptus Astartes all chapters 21 lists (14%; the Space Marines' 87 datasheets plus each chapter's own: Space Wolves 21, Blood Angels 16, Dark Angels 16, Black Templars 19, Ultramarines 11, Deathwatch 10), Necrons 15 (10%, 55), T'au Empire 12 (8%, 46), Adeptus Custodes 11 (7%, 34), Emperor's Children 9 (29), Adeptus Mechanicus 9 (37), Tyranids 8 (59), Death Guard 7 (42), Chaos Space Marines 6 (61), Thousand Sons 6 (41), Adepta Sororitas 6 (36), Aeldari 6 (79), Orks 6 (58), Chaos Daemons 5 (80), World Eaters 5 (36), Leagues of Votann 5 (25), Astra Militarum 4 (75), Grey Knights 3 (29), Genestealer Cults 2 (100, allies included), Imperial Knights 1 (27), Drukhari 1 (39), Chaos Knights 1 (27), Agents of the Imperium 1 (32), the Titanicus lists 0 (4 each). About 1,400 non-Legends datasheets in all. Done so far: Tyranids (T-541); Necrons (2 Oct: 52 of 55 datasheets, 186 abilities with 44 modelled, 11 partly, 105 noted, 26 unread (the three Crucible characters; the six crypteks' "Support" noted since); 12 detachments, 42 enhancements; Reanimation Protocols in armyfx.json; reports/tiers/2026-10-02-necrons-coverage.md); Adeptus Custodes (2 Oct: 31 of 34 datasheets, 86 abilities, the three Crucible characters unread; Martial Ka'tah in armyfx.json; their 9 detachments are BSData's (T-543 found the build skipped the Custodes' plural "Detachments" entry; fixed, so the calculator offers them))); T'au Empire (2 Oct: reports/tiers/2026-10-02-t-au-empire-coverage.md; For the Greater Good in armyfx.json)); Dark Angels (2 Oct: reports/tiers/2026-10-02-dark-angels-coverage.md; their own 19 datasheets and 8 detachments; the Space Marine datasheets they share come with the Space Marines)); Emperor's Children (2 Oct: reports/tiers/2026-10-02-emperor-s-children-coverage.md); Orks (2 Oct: reports/tiers/2026-10-02-orks-coverage.md); Chaos Daemons (2 Oct: reports/tiers/2026-10-02-chaos-daemons-coverage.md); Death Guard (2 Oct: reports/tiers/2026-10-02-death-guard-coverage.md); Adeptus Mechanicus (2 Oct: reports/tiers/2026-10-02-adeptus-mechanicus-coverage.md); Astra Militarum (2 Oct: reports/tiers/2026-10-02-astra-militarum-coverage.md); Aeldari (2 Oct: reports/tiers/2026-10-02-aeldari-coverage.md; the Drukhari detachments in BSData's Aeldari list wait for Drukhari); Chaos Space Marines (2 Oct: reports/tiers/2026-10-02-chaos-space-marines-coverage.md); Thousand Sons (2 Oct: reports/tiers/2026-10-02-thousand-sons-coverage.md); Adepta Sororitas (2 Oct: reports/tiers/2026-10-02-adepta-sororitas-coverage.md; Acts of Faith in armyfx.json, noted: Miracle dice aren't in the maths); Blood Angels (2 Oct: reports/tiers/2026-10-02-blood-angels-coverage.md; their 27 datasheets and 8 detachments); Grey Knights (2 Oct: reports/tiers/2026-10-02-grey-knights-coverage.md; Gate of Infinity in armyfx.json); Drukhari (2 Oct: reports/tiers/2026-10-02-drukhari-coverage.md; their 9 detachments under BSData's Aeldari list; Power from Pain in armyfx.json, Pain abilities set by hand as Empowered); Leagues of Votann (2 Oct: reports/tiers/2026-10-02-leagues-of-votann-coverage.md; Prioritised Efficiency in armyfx.json as a pick of footing); World Eaters (2 Oct: reports/tiers/2026-10-02-world-eaters-coverage.md; Blessings of Khorne in armyfx.json as a pick of the dice blessings); Ultramarines (2 Oct: reports/tiers/2026-10-02-ultramarines-coverage.md; four of their sheets aren't on Wahapedia 11e and stay unread); Imperial Knights (2 Oct: reports/tiers/2026-10-02-imperial-knights-coverage.md; Code Chivalric and Bondsman in armyfx.json); Space Wolves (2 Oct: reports/tiers/2026-10-02-space-wolves-coverage.md; their 7 detachments; Curse of the Wulfen in armyfx.json); Space Marines (2 Oct: reports/tiers/2026-10-02-space-marines-coverage.md; the 161 shared datasheets, 16 Codex detachments, Oath of Moment in armyfx.json); Black Templars (2 Oct: reports/tiers/2026-10-02-black-templars-coverage.md; Templar Vows in armyfx.json); Chaos Knights (2 Oct: reports/tiers/2026-10-02-chaos-knights-coverage.md; Harbingers of Dread in armyfx.json); Genestealer Cults (2 Oct: reports/tiers/2026-10-02-genestealer-cults-coverage.md; the allies copied from the Astra Militarum and Tyranids; Cult Ambush in armyfx.json); Deathwatch (2 Oct: reports/tiers/2026-10-02-deathwatch-coverage.md; Black Spear Task Force; Mission Tactics in armyfx.json); Agents of the Imperium (2 Oct: reports/tiers/2026-10-02-agents-of-the-imperium-coverage.md; Assigned Agents in armyfx.json); Salamanders, Raven Guard, Imperial Fists, Iron Hands and White Scars (2 Oct: their own sheets and one detachment each, a report each); Adeptus Titanicus and Titanicus Traitoris (2 Oct: the four Titans for both lists, no detachments; reports/tiers/2026-10-02-adeptus-titanicus-coverage.md and -titanicus-traitoris-coverage.md). Next: none left in the order: every army in BSData's build has its coverage report. Unread on purpose: the Crucible characters and the sheets Wahapedia 11e doesn't have (each army's note lists them). The engine gaps the reports name are in reports/handoff.md's last T-542 entry (reports/handoff.md has the method).
T-541built
Tyranid rules coverage: every datasheet ability and every detachment rule as effects (Jordan, 2 Oct, before sleeping)
More
Child of T-523 and the first batch of T-542. Units: every Tyranid datasheet in BSData's catalogue (61 of 64; the three Crucible organisms aren't on Wahapedia's 11th-edition page, so unread) in data/unitfx.json, each ability with its status, kind and reason; the leaders' abilities for the unit they lead in leaderfx.json (the Broodlord's Hypnotic Gaze, the Hyperadapted Raveners' Alpha Invader, new). New: an effect with off: true starts switched off in the calculator (app.js fxOff): the damaged brackets' -1 to hit, Battle-shocked and below-half conditions, the Harridan's Frenzied Metabolism. Detachments: all 10 matched-play detachments and the 4 Boarding Actions ones, each rule's status and kind, each enhancement as an upgrade in detfx.json's enhancements (effects where our vocabulary has them; never in the calculator or the letter yet) and the stratagems by name in a note. Report: reports/tiers/2026-10-02-tyranids-coverage.md, with the vocabulary gaps by how many units need each, and the tier test's Tyranid table with the data switched in. Supersedes the T-540 list's data side (T-540's tier-test kinds stay in tierfx.json, round 5). Built 2 Oct: 151 abilities (30 modelled, 11 partly, 74 noted, 36 unread: the Crucible organisms); 28 detachment rules; 41 enhancements. tools/coverage.js writes the report; tiertest.js --own-out measures the units' own abilities (one letter moves, ρ 0.38 unchanged). The detachments' movement rules are written under move, not round 5's tier: read as tier they move 15 letters and ρ falls to 0.28, so their constants (tunnel distance, regeneration credit, OC share) come first. Waits on the supervisor's read.
T-540built
Tyranid rules to model, from the Auspex comparison (Jordan, 2 Oct, via the supervisor)
More
Child of T-523 (Tier system v2), from T-524's Tyranid table; nothing built yet, and each rule is modelled one at a time under Jordan's Tyranids-first rule. The rules Auspex rates units for and V4 doesn't play out, each with a proposed shape in our effect vocabulary and the job it feeds (the full table is in reports/tiers/2026-10-02-tyranids.md, "Tyranid rules to model"): Biovores' Spore Mines as a new spawn kind (Runner, Banner); Raveners' Death From Below and the Red Terror's Subterranean Hunter as rearrive (Runner and Hammer; the Red Terror's also cuts exposed turns); the Red Terror's healing as heal (turns alive); the Neurolictor's forced Battle-shock and its aura as shock plus an enemy-side aura (Banner); the Maleceptor's −1 to hit within 6" as an enemy-side aura (Anvil, Banner); the Norns' chosen target or objective as a chosen condition (Hammer or Anvil); the Hive Tyrant's CP discount as cp (Banner, a new constant) and its Assault and Lethal Hits aura in armyfx's shape at its real lift (Banner); a leaderfx entry for the Hyperadapted Raveners. Our own words only, never rules text. Modelled 2 Oct in round 5 (T-524, V5):spawn, rearrive (as arrival), shock, the enemy-side auras (enemy), chosen, cp and the Hive Tyrant's aura at its real lift, in docs/data/tierfx.json (our words; the tier test only). Not yet: the Red Terror's healing (heal), the re-deploy and Rapid Ingress stratagems, and the detachment movement rules (Subterranean Assault's tunnels, Vanguard Onslaught's charge after an advance, Crusher Stampede's OC, Assimilation Swarm's regeneration, Talons of the Norn Queen's swap and invulnerable upgrade). The tier test reads those from a detfx.json rule's tier key (shape in design/scoring.md, V5 item 7) once the Tyranid coverage card writes them. Engine side (2 Oct, a builder sub-agent on the supervisor's brief): the kinds the coverage files record in words now count, one commit each. The numbers live in new docs/data/kindfx.json, keyed like the coverage files (file, army, owner, rule), until the coverage builder writes them on the rule itself (the proposed shape, which the engine already prefers: heal: { n, per: cmd/kill/fight, models, to: self/unit/near, who, weapon, off } on a rule of kind heal; detfx.js withNums). 1. heal: heals are chips of their own on the side's Modifiers line and play in To the death turn by turn (its Command phase first, a kill heal after its blows); the answer says how many wounds came back a fight on average and the round strip says who healed how many; armyfx.json's Reanimation Protocols reaches every Necron unit whose datasheet lists it; ev.js healLift is the closed form (k = (W − h) ÷ (D − h) of 5 rounds), not in the live rankings yet (they read no unitfx rule); the tier test's --kinds uses it for turns alive and Anvil in place of ×1.15 where the data has a heal. Tyranids: ρ 0.38 → 0.38, Old One Eye A → S, the Tervigon's Termagants +140% durable in Banner. 2. mortal: mortal wounds outside attacks (shape mortal: { n, on, d, pm, when, range, vs, near, max, v }) are chips and a "weapon" of their own in the volley (a bullet per mortal wound, one damage, no save, Feel No Pain applies, spilling over), "+N mortal" on the funnel's wound gate and the answer; the tier test's Hammer reads them in place of mortalfx.json where the data has them (5 Tyranid units, the same numbers, nothing moves), mortalfx.json kept for the 101 others. Not yet: the defender's mortals in its strike-back. 3. aura: a coverage rule's aura object (range, to, not) with its effects (the rule's own, or aura.effects in kindfx.json for one noted in words) reaches a joined character's squad in the calculator, keywords checked, range taken as met; enemy-side auras come with their bearer; a rule leaderfx.json already gives isn't given twice; the tier test's variable 5 reads them in place of tierfx.json's (the same squads and lifts, nothing moves). Left for later: the unit pages' aura worth and tools/banner.js (still its proxy); the defender's mortals in its strike-back; rearrive and spawn (already in V5 through tierfx.json, variables 2 and 8; the coverage files hold no numbers for them); the live rankings' Defense with heals (a decision: it lifts every Necron).
T-539open
The Context rule: a letter is the unit's best legal context, with the asterisk and the floor (Jordan, 1 Oct (Pacific), via the design session)
More
A design rule for Tier v2 (T-523), written into design/scoring.md §6. The letter is the best legal context (loadout, delivery, its army's best detachment for it, the Current meta lens and the meta's disposition mix); leaders don't inflate a squad's letter. One line under the letter names the context. The floor (no detachment, alone, Even lens) shows as a range, "B–S", and the tier list sorts by the ceiling with a toggle for the floor. Axes are shown one at a time, never crossed: per detachment (precomputed) and per disposition (recomputed in the browser); the same cells give "best units for <detachment>" lists on the detachment pages. Cost measured 2 Oct: 24,999 detachment cells, 8,805 disposition and 21,132 archetype cells at 6 + 6; build 28 s to about 40 s; rankings.json 356 KB to under 400 KB gzipped. Builds only when v2 does.
T-538built
Delivery modes and the transport bundle in the tier test, with the round-3 fixes (Jordan, 1 Oct (Pacific), via the design session)
More
The bundle rule (05:28Z), delivery modes (05:38Z) and the round-3 fixes (05:44Z), as V3 in tools/tiertest.js, report-only. Built 2 Oct: walk, Deep Strike, Strategic Reserves and the Dedicated Transport bundle (tools/transports.js reads who may ride what from BSData at run time); the standard table of distances from the 11th-edition rules; fixes (a) mortal wounds (reports/tiers/2026-10-02-mortals.json, 34 abilities read on 11e datasheets, our words), (b) joiners aren't Runners, (c) Battle-shock below half, (e) Indirect Fire from turn 1. (d) transport capacity in Banner lowered every yardstick and is out. Result: ρ with list share 0.164 (V1 0.174, V2 0.127, V2b 0.087); Space Marines' order −0.18 (the least wrong of every system), Orks 0.18; Mortarion A, Ghazghkull B, the first new system to rate both. Decisions taken alone: Dedicated Transports only; characters aren't bundled. Doubt: the transport's guns are 57% of a bundle's damage at the median, so a weak passenger's letter partly measures its ride. Report: reports/tiers/2026-10-02-round3.md. Waits on Jordan's read.
T-537built
Force Dispositions as job demand: a report (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; report-only. Built 2 Oct: tools/dispositions.js reads the 11th-edition mission deck's 25 primaries (Wahapedia, our own words and numbers). The pairing comes from the mission generator: each disposition has five primaries, one for each opposing disposition. It sorts each primary's VP by the job that earns it and weights a disposition's five by how often the field plays the opposing disposition. Coverage: about a quarter of lists name a disposition (77 in Aug and Sep; none in June). Reconnaissance 30%, Priority Assets 23%, Take and Hold 21%, Purge the Foe 14%, Disruption 12%. Demand: Anvil earns 50% to 87% of every disposition's primary VP. Purge the Foe asks for the most Hammer (29%), Reconnaissance the most Runner (41%). The lens gives each job half today's S and A split plus half the demand. 73 units move one letter between the Take and Hold and Purge the Foe lenses (troops up under Take and Hold, big killers up under Purge the Foe). The check fails: the lists that play each disposition don't carry what its primaries pay for (Anvil ρ −0.70, Hammer −0.40, Runner +0.20); an army can only pick the dispositions its detachments offer. Recommendation: no disposition lens on the letters yet; keep the demand table as a teaching aid, and rerun once twice as many lists name one. Report: reports/tiers/2026-10-02-dispositions.md.
T-535built
design/scoring.md: the maths in plain English, every factor and constant (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523, started now. Jordan: "are we keeping track of all the factors that go into how we score? Could you show me the math behind the scenes if I asked? Is there a dev page on the website that explains it in plain English, every factor? Maybe there should be." Built 2 Oct: design/scoring.md, rendered at /lab/docs/scoring/: the one-attack rule and the neutral table, with the reason for each assumption; every target and attacker (key, profile, stand-in, weight, where in the code); Offense, Defense, Value, Utility's parts and traits, Meta, today's letter with the meta lift; the effects files with their counts, and the loadout solver with its real limits (400 kits, 200,000 squads); Tier system v2 marked proposed: the four jobs, the composite and quotas, V2b's delivery model with every constant, army rules, Lenses, the Banner method; the known gaps, and a dated changelog. Linked from /about/'s "How the scores work" (a new line) and every "How we score this" (method.more: "Every formula and constant"). tests/unit/scoringdoc.test.js fails when the code has a target, attacker, Utility part, trait, job or delivery constant the doc doesn't name. The CLAUDE.md line (added in this commit; the design session can reword it): "design/scoring.md is the canonical spec of the maths (T-535)… A change to tools/build_rankings.js, tools/targets.js, the effects files (detfx, leaderfx, unitfx, armyfx) or the tier tools updates scoring.md in the same commit, with a changelog line; tests/unit/scoringdoc.test.js fails when a target, attacker, Utility part, trait, job or delivery constant in the code isn't named there."
T-534open
Sleepers of the week, and the Workhorses (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; report-only until T-524 is back and Jordan has read it. A Monday paragraph in the weekly analytics report and a block on /meta/ and /lab/reports/: three units our maths rates high (A or S by job) while their share of winning lists is low (below their army's median), with the numbers behind each (the job, the composite, damage before death or the Banner multiplier, the best loadout and target) and one line on why lists might be missing them. Gated: on the site only once the calibration score (T-533) is above a bar Jordan sets (proposed: 70% within one tier); until then in the Lab only, marked "not yet public". Outcomes tracked: when a past Sleeper's list share rises, say so ("called 6 Oct, now in 18% of lists"); the hit record is the point. The mirror list, Workhorses (low letter, high share), stays in the Lab as our to-do list of what the maths misses, never published as advice (Mortarion is one, T-524).
T-533open
A standing calibration score: our letters against a panel of public tier lists, over time (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; report-only until T-524 is back and Jordan has read it. Makes T-524 repeatable. The panel: 3 to 5 armies' reputable, text-based public tier lists, each recorded as unit → tier plus a one-phrase paraphrase of the reason (our words only), with source URL and date, refreshed when the source updates, in reports/tiers/panel/ (the first entry exists: reports/tiers/panel/2026-10-02-space-marines-tistaminis.json, Tista Minis). tools/calibrate.js scores our current letters against the panel: agreement, mean tier distance, and the per-unit disagreements, each in a diagnosis bin (missing rule / utility we don't measure / consensus likely wrong / acceptable variance / data error). A bin set once stays set until the unit's numbers change. In CI on every change to build_rankings.js, the effects files (detfx, leaderfx, unitfx, armyfx) and the archetypes, as a non-blocking report: the number goes into reports/analytics and onto /lab/reports/ as a line over time ("agreement 58% → 71% since 1 Oct"), each change's delta attributed to the commit that moved it. Rule: a change to the maths that lowers agreement needs a reason written in its card. Plus a coverage line: how many of our units the panel grades.
T-532open
Support characters in the published Leader score (found 2 Oct by T-524's comparison)
More
build_rankings.js's leaderLift (T-062) reads only BSData's Leading links (rec.leads), so 11th edition's Support characters (48 of them: the Apothecary Biologis, for one), who join squads through Supporting links (rec.supports), get no lift in Utility's support part on the tier list and unit pages. Score them the same way (their effects on each squad they can support), check which have leaderfx.json entries, rebuild the rankings and report the letters that move. T-527's report tool already does this. On beta with the next rankings build.
T-531open
Lenses: re-weightable archetypes as a product feature (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; design now (in T-529's report), mockup after T-524. Jordan: "if the meta shifts to all monsters we change the weighting on the back end… a toggle for skews… units that are S-tier in one meta and D-tier in another… reweight what enemies they're coming across and the calculator… all the pros would come to us for it. Data is cheap and compute is cheap: more archetypes, stress-test every unit against them, toggle the weighting by the current meta." Precompute wide, weight late: rankings.json keeps every unit's damage into each target and hits-to-destroy from each attacker for the archetype set T-529 recommends; a lens is a weight vector, and Offense, Defense, the jobs, the composite, the quotas and the letter recompute in the browser (a dot product and a re-percentile; measure the file's growth and the time on a phone, target under 100 ms). Built-in lenses: Current meta (the default, from winning lists' points by target class and weapon class, refreshed daily; the static pages and Google see this one), Even (today's), Monster mash, Horde, Elite infantry, Armour, Mortal wounds heavy, Last month's meta; each with a line and its weights on a tap. Custom lens: sliders, or "from these lists" (paste opponents' lists or pick winning lists or a tournament); saved per device (hre.lens.v1, into CLAUDE.md's key list when built); carried by links (?lens=). A candidate supporter perk later (our software); free while memberships are off. Where it applies: the tier list and faction pages (letters re-rank live, with a lens chip and a line like "S in the current meta · B against hordes"), unit pages (job bars, the Banner table), the calculator's stand-ins (decide: fixed or the lens's top targets). Static HTML always shows Current meta. Lens page: /meta/lens/ or a section of /meta/: the current weights and their change since last month ("monsters up 6 points"), a Monday paragraph. Risks: moving letters (chip and line), SEO (one canonical lens), file size and CPU, the job quotas being lens-dependent too (from the same lists). Design in T-529's report (2 Oct): storing the recommended set adds about 25 KB gzipped to rankings.json's 343 KB (12 targets and 8 attackers, about 66 KB). A lens recompute for 1,241 units takes 35 ms in Chromium at 4× CPU slowdown (target: 100 ms). Seven test lenses move 590 units one letter and 263 two, but none from S to D: a target lens only re-weights Offense, half of Value. A meta lens should shift the attackers too, and the mockup should use V2 letters. Proposed: the calculator's stand-ins stay fixed; the default lens is the one that best predicts what wins (the meta weighting lowered agreement in this test). Mockup after T-524. Decided 2 Oct (Jordan, 1 Oct (Pacific), via the design session): the custom lens is an eventual supporter perk; the built-in lenses stay free forever. Until launch (T-187) it ships free, behind the same supporter gate as the other perk candidates and labelled "Free while we're in early days". When it goes behind the perk: design/kofi-tiers.md (T-186) and the Ko-fi text for Jordan.
T-530built
Army rules as effects: data/armyfx.json, Tyranids first (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; report and data, no site change (the calculator's chips come later under their own card). We model detachments (detfx.json), leaders (leaderfx.json) and units' own abilities (unitfx.json) but not army rules. armyfx.json in the same shape and our own words: each army rule as dice or stat effects with its condition (in range of a Synapse unit, Battle-shock re-rolls and the like) and who grants it. A Synapse creature's aura then counts in its Banner score at the 2-units-in-range assumption, and Leadership re-rolls count in Anvil's Battle-shock resilience and Runner's action eligibility. Tyranids first as the pilot (Jordan recalls a Strength bonus and extra dice on Leadership: read the 11th-edition army rule on Wahapedia, never copy its wording). Then the 36 armies' rules with a one-line "how we'd model it / can't yet" each, and the order to write them (by share of winning lists). Report plus the Tyranids file; wired into T-524's V2 numbers if time allows. Done 2 Oct (report and pilot): docs/data/armyfx.json with Tyranids written (Synapse, Shadow in the Warp), and tools/tiertest.js --armyfx. Report at reports/tiers/2026-10-02-armyfx.md, with a one-line plan for every army's rule (surveyed on Wahapedia, our words) and the order to write them. Wired into V2, it lifts ρ from 0.127 to 0.146 but takes Tyranid S letters from 6 to 25. Lesson: army rules go into letters only once every army's are written; Battle-shock weighs too much in Anvil, so damp it. New engine vocabulary needed: target Toughness −1, S>T and S≤T conditions, Rapid Fire-only, a save capped at 3+, a single-die re-roll. Waits on Jordan's read. Checked 2 Oct on Jordan's question (11th edition, not 10th): every page read was the 11th-edition one. Oath of Moment as it stands there: every Adeptus Astartes army re-rolls hits against the Oath target; +1 to wound only with a Codex: Space Marines detachment and no Blood Angels, Dark Angels, Deathwatch or Space Wolves units. The errata dropped Black Templars from that list, and Black Templars armies take Templar Vows instead. A leaked future change isn't on the page; re-check at the next balance update.
T-529built
Archetype review: are five targets and five attackers enough? (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; a report, no site change. Today: GEQ, MEQ, TEQ, a light vehicle (T10 W12 3+), a heavy vehicle (T12 W16 2+); small arms, special, plasma, elite, anti-tank; equal weights; the neutral table. Jordan: "are we oversimplifying, are we missing anything?" Test with the real data, reporting the units that change letter under each and the agreement with the community list: (1) a big target with an invulnerable save (T11-12, W18-20, 3+, 4++: Knights, Daemon Princes, wraith constructs, big monsters); (2) a tough horde (T5, 5+ or 6+, W1-2); (3) a mortal or devastating-wounds attacker (saves ignored, Feel No Pain counts) and a precision attacker (character sniping), so FNP, invulnerable saves and Lone Operative count in Defense; (4) meta weights: each target weighted by the points share of winning lists' units in its class (unitType and stats), each attacker by the share of the weapons winning lists carry in its class (from their loadouts), refreshed with the corpus; the weights and their effect against equal weights; (5) how much cover and half range move the letters. Recommend a set (5 to 7 targets, 5 to 7 attackers, weighted or not) with reasons, noting the cost of more columns on the unit pages and the calculator's five stand-ins (app.js STAND_INS must stay in step). reports/tiers/<date>-archetypes.md. Done 2 Oct (report): reports/tiers/2026-10-02-archetypes.md, with tools/archetypes.js. The best single change is a mortal-wounds attacker (ρ with list share 0.119 → 0.136). The lists field big targets with a 4++ or 5++ (15% of points) more than plain tanks (8%). Weighting by the meta makes agreement worse (0.088): keep equal weights. Cover on lifts agreement (0.134); half range doesn't. Recommended: six targets (the heavy vehicle swapped for a big 4++ target, plus a tough horde) and six attackers (plus mortal wounds), equal weights: ρ 0.147, 295 letters changed. The cost: a column more on the unit pages, and app.js STAND_INS and tools/targets.js changed together, as a build card once Jordan picks.
T-528open
The unit page shows its work: why this letter, and the Banner table: MOCKUP (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; mockup-stage, after T-524. Jordan: "this extra metadata is super interesting to math nerds and should be on full display when someone clicks a unit's page… land on the Tyranid Prime and recognise: attached to each squad it can lead, here's the damage multiplier, here's the anticipated damage output, for each squad against each of our five enemy types; and it's still a real beatstick." Mockup as an Artifact, 390px and desktop, both themes, the Tyranid Prime as the example. (1) Under the letter and job tag ("A · Banner"), a "Why this letter" block: the four job bars with each one's raw ingredients in a line (Offense 71 · survives 2.4 turns → damage before death 84; Defense 55 × OC 2/pt × Ld pass 72% → Anvil 48; …) and which job earned the letter. (2) For characters, the Banner table: a row per squad it can lead, a column per target; each cell "without → with (×1.6)"; per row, per 100 pts of the character and "beats N more bodies: yes/no"; the best squad highlighted and named ("best with Termagants ×20"); defensive effects as a second compact set of rows (hits to destroy without → with); its own attacks as "and on its own: Hammer 63, a real fighter" with the melee numbers. (3) Army-rule and detachment contributions as labelled lines (T-530), each a chip that explains itself on a tap, in our words. (4) Everything also in plain text for Google, a "How we score" link, no statlines or profiles (the kill switch rule stands): only our derived numbers. Stop at the mockup for Jordan's yes.
T-527built
Banner scored from effects: what a character adds to its squad per point, better than bodies (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; a report, no site change. Generalises T-062's leader lift into the Banner job. For each character and each squad it can lead (BSData's Leading links): the squad's damage into all five targets with the leader's effects (leaderfx.json) on and off (Offense's rule, best loadout), and its hits to destroy from the five attackers with the defensive effects on and off; the leader's own attacks count (it fights with the squad). Added value per point: the damage and durability gained, each in Offense's and Defense's currencies, over the character's points. Better than bodies: against spending those points on more models of the same squad (up to the datasheet's maximum); a Banner earns the job only if it beats bodies, and the ratio is reported. The score is the best squad's result (or the mean of its top two), percentiled; the squad is recorded ("best with Intercessors ×10"). Auras assume 2 units in range, detachment- or army-wide effects 3; transports a proxy (the carried squad's Hammer × a modest factor); a CP valued once as the average worth of the core stratagems we can model; the report says which are modelled and which are proxies. Coverage first: characters with effects in leaderfx.json (offensive, defensive, both, neither) and the leaders taken in winning lists that have none yet (the next hand-writing batch, like T-343). Output reports/tiers/<date>-banner.md: every character's Banner score, the top and bottom 20 with best squad and multiplier, Mortarion's aura, agreement with the community list on characters; its numbers feed T-524's V2 Banner job. Built 2 Oct:tools/banner.js, report at reports/tiers/2026-10-02-banner.md (/lab/reports/tiers/2026-10-02-banner/). Coverage: 451 non-Legends characters; 184 have dice effects (127 offensive, 44 defensive, 13 both), 129 an entry without dice effects, 138 none; 291 lead or support a squad we could score (Support characters added on 2 Oct after the public list showed them missing). 203 of those beat bodies. ρ with list share among characters is 0.060, against 0.052 for today's support part. Gap list: 85 characters taken in winning lists with no dice effects yet (Logan Grimnar, Lucius, the Coldstar Commander, the Dawneagle Shield-Captain and the Swarmlord head it): the next hand-writing batch. Fed into T-524's V2 (--banner), it lifts V2's ρ from 0.113 to 0.127 and Mortarion from C to B. Proxies stated: auras, transports. Army-wide effects and CP aren't modelled. Waits on Jordan's read.
T-526built
Review STcrafter/40k-tierlist, the nearest competitor (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; a report, no site change. An independent 11th-edition tier list on GitHub that reads the same BSData: Monte Carlo damage, damage and survivability per 100 pts with a "melee tax", a utility score, min-max normalisation, percentile tiers (README in Russian). Read their scoring code (src/tier/, src/combat/): survivability on their bounded scale, the melee tax, the utility flags, the 2,000-point cap, Legends. Compare with ours on 30 shared units: where we disagree and who's right by the tier test's community list. Note what's worth adopting (as our own implementation, never their code) and what we think is wrong. List what we have that they don't (jobs, leaders, detachments, meta kept separate, the calculator, the Lab) for the Method page and a future Reddit post. Output reports/tiers/<date>-stcrafter.md, linked from /lab/reports/. Done 2 Oct (report): reports/tiers/2026-10-02-stcrafter.md, with tools/compare_stc.js. Their code was read at e8298cf and their output built offline (939 shared units). They edge us on list share (ρ 0.154 against our Value's 0.140) and are more often right on the big disagreements, because they credit cheap units with Scouts, Infiltrators or Deep Strike (our V2 Runner is the fix). We're right where their squad sizes break (Terminators, Sternguard and Eradicators scored as one model). The report lists five ideas to take as our own (targets from the data, a floor term, a tier-stability test, price-band durability, once-a-battle shown as a difference), their rules errors, and what we have that they don't. Waits on Jordan's read.
T-524built
Tier-system test: one army's public tier list against our three ways of lettering, a report (Jordan, 1 Oct (Pacific), via the design session)
More
First child of T-523; a report, no site change (no change to rankings.json or any page). Built 2 Oct:tools/tiertest.js gives every unit today's letter (Value lifted by the meta), the maths-only letter (Value, fixed bands) and the proposed one (Hammer = Offense; Anvil = Defense's hits-to-destroy × the unit's OC, ranked, a product so no weight to tune; Banner = Utility's four parts weighted and ranked over every unit; composite 0.75 × best + 0.25 × mean; S 7% / A 15% / B 28% / C 30% / D quotas, S and A split by the jobs' share of the winning lists' points). Report at reports/tiers/2026-10-02-space-marines.md, rendered at /lab/reports/tiers/2026-10-02-space-marines/ and linked from /lab/reports/. Findings: 264 units wear S today (80 by Value alone; the quota holds 81); job split 37 / 40 / 22% (S quotas 30 / 33 / 18); fitted Banner weights board control 0.7, reach 0.2, support 0.1, action economy 0, ρ with list share 0.174 against 0.153 equal and 0.113 Value (all weak); Leaders and Auras twice as common among units players take that we grade C or D (Azrael D in 22 of 23 lists); global quotas leave Space Marines almost no S or A. Blocked: the public tier lists (goonhammer.com, reddit.com, tabletopbattles.com, tistaminis.com, spruesandbrews.com, belloflostsouls.net, spikeybits.com) are denied by this environment's network policy, so the agreement tables, the 15 biggest disagreements each way, the count of cited reasons and the test of the fitted weights on that list wait; a stand-in (one review's 11 best-in-role picks, names and roles only, through a search summary) is in the report. To finish: allow one of those hosts, then node tools/tiertest.js --community <file> (this report: add --named Mortarion --named-note reports/tiers/2026-10-02-mortarion-note.md --flip-note reports/tiers/2026-10-02-flip-note.md --picks reports/tiers/2026-10-02-space-marines-picks.json --summary reports/tiers/2026-10-02-space-marines-summary.md) (names, letters, our paraphrase of each reason). Jordan's "rank 62" example is Mortarion (design session, 04:09Z): S today by the meta, B on maths, B under the proposal (Anvil 92, 110th of 341 Holders, S quota 33); the report's "acid test" section sets out what we miss on him (Host of Plagues' mortal wounds: a rule we don't model; Lord of the Death Guard's buffs: a synergy we don't model; the contagion aura: counted only as one aura) and proposes Utility v2's first two parts (passive damage as a hand-written effect; lift to nearby units); Death Guard is the faction to test once a public list is readable. All 44 meta-lifted S units with a Value in the 60s are listed too. Amended 2 Oct, 04:21Z (Jordan, via the design session): two versions side by side. V1 is the three domains above. V2 is four jobs from how a game is won: Hammer = damage before death (damage per turn × turns alive under typical fire, at most 5); Anvil = Defense per point × OC per point × the chance to pass Battle-shock × 1.15 for healers; Runner = reach, units per 100 pts, footprint and Leadership; Banner = a Leader's lift, auras, Transport and revives per point. Same composite and quotas. Offense and Defense stay the published raw pair under both. V2 result: job demand 49 / 18 / 17 / 16% (S quotas 40 / 15 / 14 / 13); ρ with list share 0.113 against V1's 0.174; 835 units change letter and 664 change job; V2 rates characters (25 of its 82 S units, none in V1) but overshoots cheap leaders through Runner; Mortarion is C under V2, B under V1. Recommendation in the report: V2's Hammer and Banner, V1's Anvil, and Runner only for units that act alone, tested against the community list once one can be read. Community comparison done 2 Oct (Jordan opened the network): Tista Minis' 11th-edition "Best Space Marine Units" (22 Jul), 30 units, ranks mapped to letters. Mean distance: today 1.40, maths 1.43, V1 1.43 (equal weights 1.47), V2 1.67. None agrees well, and V2 least. They rate close-range melta and flamer units, mission units, transports and support characters higher; we rate the slow, expensive efficient-on-paper units higher. Additions to the shopping list: Overwatch, melta within half range, delivery, support synergy, a price on slowness. The comparison also showed the Banner tool skipping Support characters, now fixed (T-527). Goonhammer and Tabletop Battles answer with a bot check, and Reddit refuses. Round 2 done 2 Oct (05:14Z brief): the yardstick within the army (rank order, and letters re-cut inside the army), V2b (Hammer as damage delivered before death), and a second panel list, Sprues & Brews' 11th-edition Orks codex review (22 Aug, 45 units, its verdicts graded by strength; worth about half a ranked list). A third could not be read: the candidates are behind bot checks or are videos. Both panel files are in reports/tiers/panel/. ρ with the list's order, Space Marines / Orks: today −0.31 / 0.24, maths −0.31 / 0.25, V1 −0.27 / 0.22, V2 −0.23 / 0.12, V2b −0.24 / 0.06. Answer: the fairer yardstick helps partly, and V2b doesn't help; V1 fitted is best overall. Mortarion and Ghazghkull go opposite ways (V1 holds Mortarion and drops Ghazghkull to C; V2 the reverse). Only today's meta-lifted letter rates both highly. Write-up: reports/tiers/2026-10-02-round2.md (/lab/reports/tiers/2026-10-02-round2/). Round 3 done 2 Oct (T-538): V3, with delivery modes, the Dedicated Transport bundle and four fixes. ρ with list share 0.164; Space Marines −0.18; Orks 0.18 (reports/tiers/2026-10-02-round3.md). Waits on Jordan's read. Round 3, V2c (2 Oct, a builder sub-agent on the supervisor's brief, with Jordan's refinements): V2c is V2b with six steps, each measured on top of the last: (a) mortal wounds from abilities (docs/data/mortalfx.json, 83 datasheets, our numbers; Devastating Wounds were already in ev.js); (b) Support characters can't be Runner or Anvil alone, Leaders can, but Runner counts survival (reach, turns alive and Leadership, plus cheapness and footprint at half weight; Lone Operatives' survival doubled); OC 0 can't be Anvil; (c) Battle-shock weighted by the share of the game spent below half strength; (d) Banner as beats-bodies per point for everyone (Leader multiplier, auras, transport capacity); (e) Indirect Fire from turn 1 (no effect); (f) the diagnosis: V2b multiplied per-point damage by absolute survival, so big units won on size. Turns alive now come from durability per point, a unit is exposed only on the table outside a transport, and a unit rides only where a Dedicated Transport can carry it and the pair beats both parts per point (Jordan's bundle test). Numbers: Space Marine ρ with the public list −0.24 → −0.08 (mean distance 1.57 → 1.43), ρ with list share 0.087 → 0.137, Orks 0.06 → 0.01, Tyranids (Auspex Tactics' list, the third panel file, reports/tiers/panel/tyranids-auspex.json) 0.07 → 0.21 (Value 0.33, today 0.65); almost all the Space Marine gain is durability per point. Mortarion C → S (2nd of 36). The first session's V3 (above) scores −0.18 / 0.164 / 0.18: V2c is better on the Space Marine list, V3 on list share and Orks, and both reach 0.21 on Tyranids; both are in the report. Verdict: partly. The test cases still miss for reasons that are rules, not dice: Biovores (Spore Mines), the Venom (split squads) and Eradicators (the target average). The open questions are how to price size and Runner for big durable models. Report: reports/tiers/2026-10-02-space-marines.md, "Round 3". Constants and rules in design/scoring.md. Waits on Jordan's read. Round 4 done 2 Oct (a builder sub-agent on the supervisor's brief, with Jordan's step g and Tyranid section): V4 merges the strands (V3's arrival modes and the bundle with the beats-both-parts test; V2c's durability per point, exposure on the table, mortal wounds, Battle-shock damping, the Support/Leader rule, Banner per point, Indirect Fire), adds no charge after an advance (11th-edition core rules), Runner's points gate (1 up to 75 pts, 0 at 260), solo characters' turns alive × 0.5, and step g (Hammer as enemy points destroyed, ppw, weighted by the meta's target classes, peak plus breadth, tagged anti-tank / anti-elite / anti-chaff); every part a switch, each measured by leaving it out. Numbers: Space Marine ρ 0.04 (first positive; V2c −0.08, V3 −0.18), distances 1.33; Tyranids 0.19 (both strands 0.21); Orks −0.19 (V3 0.18); list share 0.104 (V2c 0.137, V3 0.164). Mortarion is no longer a Runner (B · Hammer); the Lieutenant with Combi-weapon keeps C; Eradicators D → C (anti-tank), Hive Guard S; the Chaplains stay B or A (Banner carries them now). Panel ρ has a standard error of 0.15 to 0.19, so most single-panel differences are noise. Verdict: partly. Lessons: merging didn't add the strengths; the gate exposes the size question (Ghazghkull C, Wazdakka D); the Chaplains are a Banner problem (lift credited on squads nobody fields). Tyranids against the Auspex list: every graded unit, the 20 biggest disagreements diagnosed, a plain paragraph; reports/tiers/2026-10-02-tyranids.md. Report: reports/tiers/2026-10-02-space-marines.md, "Round 4". Waits on Jordan's read; no round 5 until then. One army until the variables are found (Jordan, 2 Oct, via the supervisor): "For the purposes of ironing this out, stick to Tyranids until we can identify all the variables." The tier work now iterates on Tyranids alone, judged by the Auspex panel list, Tyranid list share and Jordan's own reading (he plays Tyranids, so he can say when a number is wrong); each missing variable is named and modelled one at a time until the Tyranid table is explained. The Space Marine and Ork panels run only as a check at the end of a round, never to steer it. 2 Oct, every domain shown (Jordan: "are we not scoring all domains anymore?"): reports/tiers/2026-10-02-tyranids.md now shows, for every Tyranid row, the four V4 job percentiles, raw Offense and Defense per 100 pts with their percentiles, turns in reach, turns alive and the arrival mode. It adds five-turn profiles, every arrival mode and the cost of each Hammer term for Raveners, the Red Terror, the Neurolictor, Biovores, Hormagaunts and Von Ryan's Leapers. Raveners' and the Red Terror's Hammer is sunk by turns alive (living all five turns would make each an A), not by step g. The Neurolictor misses A in Runner by 0.1. The rules list became T-540. Round 5 done 2 Oct (a builder sub-agent on the supervisor's brief, Jordan's eight variables, Tyranids first): V5 = V4 plus (1) survival absolute, (2) arrival rules, (3) a mortal-wound attacker with the attackers weighted by the meta's incoming fire (small 8 / special 20 / plasma 37 / elite 7 / anti-tank 22 / mortal 6%), (4) enemy debuffs and Battle-shock as Banner, (5) buff auras and Synapse through the engine on the squads co-listed in winning lists (tools/colist.js), (6) leader pairs shown, (7) detachment context with Singular Purpose and the floor letter, (8) one-use units, CP at 15 pts, Biovores' spawning; plus (9) Banner's smooth start, found this round. The non-dice rules are in docs/data/tierfx.json (our words, not loaded by the site). Each switch was measured alone and stacked on the Tyranid units against a frozen pool. Numbers: ρ with Auspex 0.19 → 0.38, same letter 14 → 16 of 44, within one 26 → 31, ρ with Tyranid list share 0.12 → 0.24 (today's letter 0.65 / 21 / 37). Check: Space Marines 0.04 → −0.01, Orks −0.19 → −0.12, list share 0.104 → 0.129. Norns C → S, Hive Guard S → A (not a Sleeper), Biovores D → C, Hive Tyrant and Neurotyrant C → B, Ripper Swarms A → C. Verdict: partly. Still unexplained: Raveners, Hyperadapted Raveners and the Red Terror (re-deploy and Rapid Ingress stratagems, and detachment movement rules not yet in detfx.json); the support monsters' worths (CP, Battle-shock, debuffs: stated guesses for Jordan to set); the Tyrannocyte (V2c's capacity credit). Report: reports/tiers/2026-10-02-tyranids.md, "Round 5". Waits on Jordan's read. Detachment movement rules (T-541, the supervisor's decision, 2 Oct): written in detfx.json under move, not tier. Read as written, they lower agreement until their worths are set (tunnels, Crusher OC, Feed the Swarm: V5's ρ 0.38 → 0.28, 15 letters move); the worths are Jordan's to set, so round 5's result stands. Coverage re-run (2 Oct, after T-542): V5 with every kind and every Tyranid unit's own abilities: 1 letter against the last V5 + kinds run (Venomthropes C → B, Foul Spores), 5 against V5; ρ with Auspex 0.39 → 0.39, same letter 17 → 18 of 44, within one 30, list share 0.24 → 0.23. Sitewide 0 letters move: the tier test reads no other army's coverage (own abilities and kinds are Tyranid-only, --armyfx reads only the Synapse pilot's shape), so the frozen-pool caveat stands; --armyfx as it is double-counts Synapse and lifts 26 Tyranid letters, so it stays off. Report: reports/tiers/2026-10-02-coverage-rerun.md and the Tyranid report's "Coverage re-run". Next: the tool re-scoring every army with its coverage (another builder's), then Jordan's constants (CP, Battle-shock, debuff share, movement rules). Every army scored (T-552, 2 Oct): with every unit carrying its own abilities, kinds, army rules and best detachment (--every), Tyranids against Auspex ρ 0.39 → 0.37 (same 17 → 13, within one 30 → 23), Tyranid list share 0.24 → 0.28, Space Marines −0.01 → −0.05, Orks −0.12 → −0.14, every unit's list share 0.129 → 0.156 (0.173 before detachments); 423 of 1,161 letters move by army (Space Marines 45 up, Tyranids 27 down). reports/tiers/2026-10-02-tyranids.md, "Every army scored".
T-523open
Tier system v2: peak and breadth, jobs, quotas (Jordan, 1 Oct (Pacific), via the design session)
More
Parent card; nothing under it builds until Jordan says so. From the brainstorm with Jordan: three job domains per unit, each a percentile over all non-Legends units: Hammer (today's Offense), Anvil (durability weighted by objective control per point; plain Defense stays a shown number), Banner (today's Utility: board control, action economy, reach, support). Composite = 0.75 × best domain + 0.25 × mean of the three; the unit's job is its best domain, a second job when another is 80 or more. Letters by quota on the composite's rank, not fixed cut-offs: S top 7% (about 85 units), A next 15%, B 28%, C 30%, D the rest; the S (and A) quota split across jobs in proportion to how much of a winning army each job makes up (points share of top-quarter lists by job), so each job's S is as scarce as armies' demand for it. Meta leaves the letter entirely: a pixel crown with list share, and a one-word read where they disagree (Safe pick, Sleeper, Workhorse, Pass). Utility's part weights fitted to list share given Hammer and Anvil. Children: T-524 (the test, first; Jordan wants its result before committing to the redesign, and before the mockups). 2 Oct, 04:21Z (Jordan): two versions are tested side by side (T-524): V1, the three domains, and V2, four jobs (Hammer as damage before death, Anvil, Runner, Banner). Offense and Defense stay the published raw pair under either design ("quite solid math… more data is better"), shown on every unit page and Rankings row with their percentile chips. The jobs are built from them and never replace them. Damage before death becomes a third shown number if V2 is adopted. The letter and the job tag are what count. Names (Jordan, 2 Oct, via the design session, 04:29Z): the four jobs are Hammer (hitting), Anvil (holding), Runner (running the secondaries) and Banner (enabling others). These are our own words, not GW keywords. A unit's tag reads "S · Hammer" or "A · Banner", and code uses hammer, anvil, runner and banner. Jordan's aim (1 Oct (Pacific), via the design session), in his words: "my dream is that our calculator accounts for so many scenarios, interactions and rules that when people come up with consensus tier lists, our rankings map closely; where there's discordance there's a good reason or acceptable variance. Then we'll find things others don't, and they'll look to us for leads." The calibration score (T-533) measures the first half; Sleepers (T-534) the second. One army until the variables are found (Jordan, 2 Oct, via the supervisor): "For the purposes of ironing this out, stick to Tyranids until we can identify all the variables." The tier work now iterates on Tyranids alone, judged by the Auspex panel list, Tyranid list share and Jordan's own reading (he plays Tyranids, so he can say when a number is wrong); each missing variable is named and modelled one at a time until the Tyranid table is explained. The Space Marine and Ork panels run only as a check at the end of a round, never to steer it.
T-519built
The patch pulse: which units people look at after a balance update, as data and a report, no public pages (Jordan, 1 Oct (Pacific), via the design session)
More
Built 2 Oct: (1) Patch detection, tools/patches.js: the daily refresh now builds the BSData commit the site was on beside the new one, ranks both with today's code, and adds the update to docs/data/patches.json: per unit its points at the smallest size before→after, the *names* of the fields that changed (a stat's letters, <weapon>: AP, +/- a weapon, size, keywords, abilities; never values), our Value score and tier before→after, and the units added and removed. 15+ units with points or profile changes (Legends aside, a unit shared by armies once) is a balance update. Backfilled from 24 Aug: the 5 Sep points update (BSData 46d8cc5→074eabc) is a balance update, 145 units (128 points, 62 profiles; 5 added, 24 removed, the Legends cut); 19 Sep 13 profile fixes; 27 and 30 Sep nothing that moves a unit. (2) Search demand, tools/demand.js (Search Console helpers shared with the weekly pull in tools/gsc.js): each day's searches, three days behind (the last three pulled again), and the live site's matchups per unit and pair from the data Worker's new GET /tests?day= (names and counts only), into reports/analytics/demand.json, 90 days rolling; daily by .github/workflows/demand.yml (14:37 UTC, 07:37 Pacific, [skip ci] like the weekly report). Searches are matched to our unit names at read time (whole name, name without "Squad", a long first word only it starts with; a phrase two units share finds neither; an army's word is never a unit; a Legends unit needs two words), so 26 of the weekly pull's 30 searches find their unit; the other four name none (two calculator searches, "centurion", which two squads share, and "hunter clade"). Seeded from the weekly pull's 28 days until daily days exist. (3) The pulse, tools/pulse.js → reports/analytics/pulse.json: patch (1 per 10% points, 1 per 5 points of score, ½ a tier step, ½ a profile change) + search spike (last 7 days against the 28 before, smoothed by 5; a level until 14 days are kept) + the same for tests; top 20, each with its three most tested opponents over 28 days, else its best and worst standard targets; a top unit and its first opponent is a suggested clash page (with no tests yet, its best standard target, marked stand-in). (4) Report: a "Patch pulse" section in tools/report.js (so /lab/reports/) and a Monday paragraph in the weekly analytics; names and numbers only, the searches stay in reports/. Tests: pulse.test.js (a made-up update through the detector, the matcher on real searches, the pulse ranking a moved and searched unit with its tested opponents), the Worker's /tests in dataworker.test.js. First real pulse (2 Oct): last balance update 5 Sep, 145 units; top movers Warbuggies (80→70 pts, tier D→A), Painboy (90→45), Wolf Guard Headtakers (profile: size and invulnerable save), Zodgrod Wortsnagga (80→50), Warboss in Mega Armour (80→125, A→B), Bannernob (50→35), Big Mek in Mega Armour (80→90), Squighog Boyz (profile), Hunta Rig (125→165, S→A), Mek Gunz (45→55); 15 of the top 20 Orks (their faction pack came with that update). Searches: the seed only; tests: none yet. On beta (2 Oct, main frozen): the Worker's /tests, the refresh's patch step and the daily demand job only run from main, so they start when beta goes to main with Jordan's OK; search spikes from about 14 days after that.
Search fixes from the 6 Oct site review (reports/site-review/2026-10-06/seo-report.md)
T-770built
Unit pages that share a title get their army in it (the review's H3: 46 groups, 113 pages; Chaos Rhino and Chaos Spawn 5 each)
More
A unit name with pages in more than one army: the home army's page (build_pages.js homeRank, the rule exact copies already use) keeps the plain name, each other army's page says "{unit} ({army}) 11th Ed: …". Pages stay self-canonical (their scores differ by army rules); a build check that no two indexable pages share a title. Built 6 Oct, on beta for review: 226 unit pages name their army (exact copies already canonical elsewhere included) ("Chaos Rhino (Death Guard) 40k 11th Ed: Points & Best Loadout"); the 14 detachment pages whose name has a page in two armies (Drukhari's and their Aeldari copies) say "Skysplinter Assault (Drukhari): Best Units & Meta" (det.titleArmy). Sitemap pages sharing a title: 49 groups (120 pages) → 0; build_pages.js warns if any come back
T-771built
Unit title pattern "{unit} 40k 11th Ed: Points & Best Loadout" (the review's H4; part of T-721)
More
Falls back to "{unit} 11th Ed: Points & Best Loadout", then "{unit} 11th Ed: Points, Loadout"; units without loadout choices "… Points & Tier". "11th Ed" in every step, "Damage" dropped. Was "{unit} 11th Edition: Points, Best Loadout, Damage" with a fallback that lost "11th Edition" on 28% of pages. T-721's description rewrite and gallery stay open. Built 6 Oct, on beta for review: unit titles without "11th Ed" 299 → 1 (a 52-character name), with "40k" 0 → 1,016 of 1,083; none over 60
T-772built
The app copies (the review's H5): /trends/ canonical to /meta/, /rankings/ canonical to /factions/, both out of the sitemap; /army/ and /lists/ their own h1 and intro
More
stamp_assets.js writes each copy; Built 6 Oct, on beta for review: /trends/ and /rankings/ name /meta/ and /factions/ as canonical (and og:url) and leave the sitemap (1,397 → 1,395 URLs); on /army/ and /lists/ their screen's heading is the h1 ("MY ARMY: RATE YOUR 40K LIST", "WINNING 40K TOURNAMENT LISTS") and the calculator's becomes an h2; the headings and their one-line intros are reworded on every copy, so the screens read the same whichever address opened them. Not done: /matchup/ (the review suggests canonical to /) and a crawlable index of the 85 winning-list pages on /lists/ (the review's H5, second half)
T-773built
The homepage's h1 and a short intro for "40k calculator" and "mathhammer" (the review's M5, first part)
More
h1 "40k Damage Calculator (Mathhammer) for 11th Edition"; the brand stays the site name; links to the tier lists, /best/ and /mathhammer/. Built 6 Oct, on beta for review: the logo is a span (.brand-wm, as the static pages have it), the h1 sits above the introduction, which names "40k calculator" and "mathhammer" and links /meta/, /factions/, /best/ and /mathhammer/; layout.js still moves both below the calculator, so the screen opens as before. The tabs' copies lose the brand h1 too (T-772)
T-774built
weekly_analytics.js reads more of Search Console (the review's H1)
More
1,000 rows sorted by impressions for queries and pages, a query × page pull, devices; the report gains "high impressions, CTR under 2%", "position 5 to 20" and "queries with two or more of our pages" tables. Plausible devices too. Moving demand.yml's schedule to main stays Jordan's call. Built 6 Oct, on beta for review: Search Console has no sort, it sends rows by clicks, so the pull asks for its most (25,000) and keeps the most seen: 1,000 queries, pages and query × page pairs over 28 days, 250 over 7, and devices; the report adds "Seen often, under 2% clicks" (20+ impressions), "In reach of the first page: positions 5 to 20", "Searches two or more of our pages share" and devices; Plausible adds devices and the pages visits from Google land on (visit:entry_page, source Google). --from latest.json renders the report again with no keys (checked on the 5 Oct pull and a made-up one; the live APIs are first called by the next Monday run, which only runs from main)
T-775built
A stub for /factions/space-marines/detachments/legacy-of-grace/, which 404s since the 3 Oct data (the review's L5)
More
GitHub Pages can't 301: a small page with a canonical to the army's page and a link on, out of the sitemap. Built 6 Oct, on beta for review: Legacy of Grace is a Blood Angels detachment (the 3 Oct data stopped offering it to Space Marines, and Blood Angels have no detachment pages), so the old address names /factions/blood-angels/ as canonical and goes there at once (meta refresh, which search engines read as a redirect); not noindex, so the redirect carries. build_pages.js MOVED holds such addresses, skipped if the data makes the page again. Already 404 on the live site since main took the 3 Oct data
Features and data
T-343open
Units' own abilities in the maths (Jordan, 29 Sep: "are the units special rules like the Skorpekh Destroyers' Whirling Onslaught and Plasmacyte being populated in the modifier tabs?")
More
Not yet: only detachments (detfx.json) and joined characters (leaderfx.json) change the dice; a unit's own datasheet abilities don't. Plan: unitfx.json, the same effect fields in our own words read from Wahapedia's 11e datasheets (never their wording), most-played units first (the corpus's top units, then each army's); always-on abilities apply by themselves, conditional or once-a-battle ones (a Plasmacyte's boost, charging, below half strength) as chips on the unit's Modifier tab, off by default; rankings and ev.js read them the same way Batch 2 (1 Oct): the next 38 most-taken units in data/unitfx.json, read on Wahapedia and written in our own words: 17 change the dice (Crisis Fireknives and the Riptide ignore hit modifiers and re-roll, Hekaton, Hearthguard, Jain Zar against characters, Morvenn Vahl's Miracle attacks, Wardens, Sacresants and Scarab Occult −1 to wound, the winged Slaanesh Prince −1 Damage, the Khorne Prince's Devastating charge, Exalted Eightbound +1 Damage into monsters and vehicles, Eightbound +1 to hit, the Nurgle Prince's cover, the Lord of Virulence's re-rolls, the Neurolictor's +1 to wound, the Defiler's Daemonforge), 21 noted as not touching the dice. 78 units in all. Batch 3 (1 Oct): the seven whose Wahapedia address differs, as ten entries (both Bloat-drones, both armies' Manipulus, both Terminator Sorcerers): the Enforcer Commander's weapon support system ignores hit modifiers, the Chaos Sorcerer's Death Hex gives +1 AP and the Thousand Sons one's Marked by Fate +1 to hit against the unit picked, the Thunderkyn's conversion beamers score critical hits on 4+ beyond 12"; the rest noted (once per battle, movement, Stratagem costs; their leading abilities are leaderfx.json's). 88 units in all. Batch 4 (1 Oct): each army's next two most taken (in two or more top lists), 48 units: 17 change the dice (Be'lakor re-rolls 1s to hit, Angron ignores hit modifiers in melee, the Canoness with Jump Pack's and the Captain's once-a-battle +3 attacks with Devastating Wounds, Bladeguard's pick of +1 to hit or −1 to be hit, Assault Intercessors, Custodian Guard and Purifiers re-roll wounds (all of them on an objective), Corsair Voidreavers the same for hits, Death Company with Jump Packs re-roll hits in melee, Deathwing Knights −1 Damage, Wolf Guard Terminators −1 to wound against stronger attacks, Cawl's Machine Vengeance re-rolls and Shroudpsalm cover, Krieg's +1 to hit and wound as they fall, the Dreadknight's re-rolls into monsters and vehicles, the Beastboss's +1 to hit, Hyperadapted Raveners' Sustained Hits 1), the rest noted. A Space Marine chapter's list now finds the Space Marines' own units' entries (detfx.js), as its unit list does. 136 units in all. Batch 5 (1 Oct): every unit taken in four or more top lists once the chapters' lists count toward the Space Marines' own units (the Intercessor Squad was in 13), 30 units: 6 change the dice (Company Heroes −1 to wound while a character leads them, the Phobos Lieutenant's objective re-rolls, the Lokhust Heavy Destroyers' wound re-rolls of 1, the Silent King's Triarch ability as a pick, Boyz' once-a-battle +1 to hit when shooting and Lethal Hits on the charge), the rest noted. Hail of Bolts (+2 attacks for the Intercessors' bolt rifles) waits on effects for one weapon only, carded as T-516. 166 units in all. Still to read: the units taken in three or fewer (380)
Approved, with mockups 3
T-457built
A weapon-effects catalogue for the pixel battle (Jordan, 30 Sep; beta, proposals only)
More
Every weapon family in build/units.json (keywords and name families, ranged and melee) with a proposed effect each (particles, count, colours, spread, duration, muzzle/impact, a one-line feel); Jordan: flamers "way more flames"; bio-spray cannons and alien torrents "lots of effects and be purple in colour"; melee its own set (slashes, sparks, arc flashes, shockwave rings, impact bursts, bites); "the more toggles the better": a switch per family under Weapon effects with a master switch; colour from the faction palette except bio-weapons (purple). Deliver design/weaponfx.md (the table, match rules, counts, leftovers) and a preview gallery (2–3 frames each) for his phone; no change to shootout.js beyond a sandbox. Our own drawings only Proposed 30 Sep: design/weaponfx.md (30 families, 21 shooting and 9 melee, with counts from build/units.json, match rules in order, the 130 leftovers, where it would live and three questions) and the live gallery https://claude.ai/artifact/7sFr4pH868w6k8pZagNMMg (each family animated with its own switch and a master switch, army colours or natural, a 3-frame view; a copy in design/weaponfx-preview.html). Nothing in shootout.js changed. Waits for Jordan's picks Round 2, 30 Sep (Jordan, 30 Sep, reviewed with the design session): the catalogue is approved as the plan; the gallery went back and round 2 is at the same link (the battle's scale and figures, natural colours with Army colours as a toggle, dithered pixels instead of soft glows, the Tyranid split into eight families, dark light, rail and sniper, lasguns apart from lascannons, whips and hammers, crackle, hit-stop, weapons in hand, bites with jaws; 41 families). The rules moved to tools/weaponfx.js with tests/unit/weaponfx.test.js (the Fire Prism and Blastmaster fixes and each family's oddest members); design/weaponfx.md and the preview copy updated. The build is T-458 to T-463 once Jordan approves round 2 Round 3, 30 Sep (Jordan's review of round 2): most families approved as they were; new shotgun, grenade and bolter families; bio-sprays, plague, stranglers, venom and bio-plasmic cannons, plasma (straight through, burn marks), lasguns, gauss, rail and sniper, helfrost, warp (the dark ball), missiles, cannons, chain teeth, plague and burning blades and whips redone (44 families; design/weaponfx.md has the list and the readings chosen). Same link Round 4, 30 Sep (Jordan's review of round 3): melta, gauss, arc and lightning, helfrost, shotguns and bolters approved; flames curl up at the end; spinefist, spine-bank, shuriken and splinter spikes stay stuck; bio-plasma slimes; plasma stutters through each model; lasguns the lascannon's beam, thinner; rail and sniper through everyone with holes, a shock-coned dart and a longer trail; warp a trippy cluster of coloured orbs that spins and shrinks the target; missiles straight and snappy with wavy smoke and a debris crunch; rotary guns spray an area; bolters shake the target; all melee 1.5 times the size, twice the particles, shake and a longer hit-stop, accelerating swings; bigger bites with follow-ups and a monster's crunch; whips wind up and snap Round 5, 30 Sep (Jordan's notes on round 4): the rail and sniper dart slowed enough to see, a melee-style stutter at each model, smaller holes that fade, a spray of sparks at each hit, a longer linger; every target that stood still now flinches at least a pixel when hit (contact measured from each family's drawing) Round 6, 30 Sep (Jordan's notes on round 5): flames and bio-plasma widen to the tip; webs corkscrew; helfrost grows twice and shatters, leaving frost; the venom slug stutters and rips out the back, leaving slime; plague showers; lasguns one solid line; pulse sprays; the rail charges, then fires like lightning with 45-degree shock waves; warp proposed as an eye in a rift; missile smoke only rises; melee winds up slow and comes down fast; weapon effects run all the time; whips two long, heavy strokes; the monster bite shows Round 7, 30 Sep (Jordan's notes on round 6): melee rests at 30° on a clock face, a slower backswing to 315°, a strike to 120° over a frame or two with a smear, a slow return; chain teeth slower; bolters clear before the loop; artillery a six-shell burst in front, behind and on top with one big dust cloud; the missile's warhead flashes harder and its smoke stops short; warp charges and opens a big rift and four small ones, shaking the target; the rail's line burns and drips; lasguns two longer shots; bio-plasmic sludge; the venom slug passes right through and slimes the ground behind Round 8, 30 Sep (Jordan's notes on round 7): every family fades back to neutral at the end of its run (burning and dripping weapons keep going); the rail sticks in the first model for about a second, then rips through, leaving big molten holes and drips on the models only, one two-pixel colour for the line; warp charges in the hands and knocks the model's ghost out of its body, leaving it grey; the missile's flash stays inside its blast; melee strikes throw white, red and grey specks; whips add a third, heavy stroke with three hit bursts; the particle sliders (T-474) are in the gallery, all and per family. The rail (2.2 s) and whip (1.8 s) run past the volley's 1.4 s cap, a call for the build Round 9, 30 Sep (Jordan: "we're almost there"): the rail's pause in the first model halved (the run 1.8 s); the warp's spirit leaves the body plainly: a full, detailed ghost tethered to the body, rising, holding, then dissolving pixel by pixel into nothing (the run 1.7 s). Same link, and on the site at /lab/effects/ (T-465); waits for Jordan's sign-off
T-521built
The balance-update page: one live page at /meta/patch/ for the latest balance update, older ones archived at /meta/patch/<date>/, with King of the Hill at the top (Jordan, 1 Oct (Pacific), via the design session)
More
Reshaped 2 Oct (Jordan, 1 Oct (Pacific), via the design session): /meta/patch/ is the live page and the canonical, always the newest balance update; older ones archived at /meta/patch/<date>/ and listed at the bottom of the live page. T-519's data still feeds it. Winners and losers grouped Up / Down / New / Gone with our values before→after, and a faction toggle like /meta/'s (All / Imperium / Chaos / Xenos, then an army). At the top, the patch's top 5 units (the biggest winners by our score) in a King of the Hill: Jordan's words: "like the normal damage calculator where the attacker attacks the defender starting with shooting or melee. A queue of sprites of the units waiting their turn, bobbing up and down, five in waiting. On a podium or the top of a literal hill the current King stands with a crown on his head or a count over it. Each fight uses the calculator settings: both units at optimal loadout, To the death, fight back enabled. When the attacker kills the defender he moves to the defender slot and a new enemy moves in. When the defender wins the attacker goes to the back of the line. If the defender fails to kill the attacker, the attacker wins. Just automatic but real calculator output and funnel." Spec: the King (defender slot) on the hill with a crown sprite and a reign counter (bouts survived); up to 5 challengers at the foot, idle-bobbing in order (our figures, hero sprites where they exist). Each bout a real run of the engine: Auto-equip's best loadout for both, To the death with fight back, the challenger opening with Shoot if it has ranged weapons, else Melee; the funnel compact beside the hill and the pixel volley played on the hill. The unit that dies goes to the back of the queue at full strength; a stalled fight (no death within the engine's turn cap) is the challenger's win; the winner takes or keeps the hill. Runs on its own while in view, pauses out of view, one bout at a time at the volley's pace, Skip and Pause; reduced motion shows the standings only. Under it the standings (longest reign this run, each unit's wins and losses) and "Run this bout yourself" into the calculator on the current pair. Seed: the top 5 by score change; a shuffle button. The same component on /meta/ with the top 5 meta units, in place of T-518's daily Clash strip. Amended 2 Oct (Jordan, via the design session, 02:34Z): (1) the crown is a pixel crown in figures.js's style (a few tones, crisp at whole pixels), never an emoji; the reign digits, the queue's places and Pause/Play are our pixel glyphs too (pause and play join icons.js; Skip and Shuffle use its skip and swap). (2) Two hills, same layout, a tab each: Shooting (volleys only, alternating, challenger first, no melee at all; seeded by the top 5 by our ranged score) and Melee (the charge and the fight only, no shooting, not even an opening volley; seeded by our melee score). A unit strong in both is on both; a unit with no weapons in a lane never enters that hill. Stall rule: no death by the turn cap goes to whoever dealt more damage (ties to the challenger). Default tab: the lane with the bigger total score swing in the update (5 Sep: ranged 722 points of percentile score moved, melee 572, so Shooting); Shooting on /meta/; the last tab remembered per device (a small new key, hre.koth.v1, into CLAUDE.md's key list when built). A mixed hill was considered and left out: Jordan chose two clean lanes (a shooting unit stuck in melee "beating its head to do zero damage" is boring). Amended again 2 Oct (Jordan, via the design session, 02:40Z): weight classes, like boxing (a cheap unit "won't hold a candle to a Titan"): by the points a unit fights at, cut for roughly equal thirds of our 1,106 units (non-Legends rankings rows): Light up to 75 pts (363 units), Middle 76 to 130 (395), Heavy 131 and up (348; wide, 135 to 3,500 pts, median 180, 22 over 500). The design session's first cuts (120 / 250) would have split 707 / 283 / 116. The class is a second selector beside the Shooting / Melee tabs (pixel glyphs: one figure, a squad, a walker), six hills in all, one on screen. Default class: the one the update moved most (5 Sep: Middle, 660 points of lane score, against Light 582 and Heavy 302); Middle on /meta/. A unit enters only its own class; bouts run at each unit's real points. Seeding per hill: the update's top 5 risers in that class and lane, among units scoring 50 or more in the lane (so a tank's tracks don't make it a fighter); a hill with fewer than 3 fills from the meta's units of that class and lane. Standings show each unit's points and its wounds dealt per 100 pts. The last hill and class remembered per device (hre.koth.v1). Rejected: stacking cheap units up to the King's points (three squads against a Titan): the engine fights one unit a side, and stacking hides what the hill is for. Layout settled 2 Oct (Jordan, via the design session, 02:50Z): three pages, one component. (1) /hill/, King of the Hill's own page (title "King of the Hill: 40k units fight to the death, 11th edition"), the one people share: lane tabs, class selector, the hill, standings, the calculator; ?seed=patch or ?seed=meta. (2) /meta/patch/ stays its own page (title, date, Article): at the top a compact "On the hill now" strip (one bout of the default hill, no selectors, "Watch all six hills ›" to /hill/?seed=patch), then winners and losers with the faction toggle, then the archive. (3) /meta/: under the podium, a card "Last balance update: <date> · N units changed · biggest winner X, biggest loser Y ›" to /meta/patch/ (5 Sep: Warbuggies, our Value 12 → 78; Squighog Boyz, 85 → 48), and the same compact strip seeded by the meta, to /hill/?seed=meta; Meta's own sections as in T-518. The hill is illustrative, the calculator demonstrative (Jordan: "a fairly static picture of the unit with the slightly larger heroic sprite on top of the hill, the queue slowly ascending the hill and trailing off it; as they fight there is also a calculator, just like on the calculator page, that populates the full unit"): the King's hero sprite (T-378's where the army has one, else its figure a step larger) on the summit under the pixel crown, idle breathing only; one figure per waiting unit walking up the slope, the challenger at the brow, the rest trailing off the left edge; the loser walks down and off, the winner steps up; no damage on the hill. Amended 02:56Z (Jordan): the queue climbing the hill is hero sprites too (T-378's, in their four-step walk at the hero's normal scale), not the battle figures; the King is the same hero a step larger under the pixel crown. Gaps: every look (23) and every army already has a hero, so none is missing; but there is one hero per army, so a hill of five Ork units shows five identical Ork heroes (the name tags tell them apart; on a phone only the King's and the challenger's show), and vehicles, monsters and riders walk as their army's hero on foot. Proposed follow-up, not drawn: a hero per figure slot (vehicle, monster, walker, rider, flyer) for each look. Below, the calculator page's own result card for the bout: both units at full size, the funnel, the pixel volley with weapon effects, the To the death round strip with fight back, the result; Skip/Pause in place of Simulate; "Open in the calculator" takes the pair to /. Weapons at their most effective: each side's loadout solved against this opponent (most expected kills on it, not the all-rounder), the best profile per target, every free modifier that helps (the lane's own abilities, unitfx); one line under the result: "Both at their best loadout against each other." Selectors and standings beside the hill on desktop, between it and the calculator on a phone. Article with dateModified; linked from /meta/ and each changed unit's page. Mockup first (390px and desktop, both themes), then stop for Jordan's yes. Mockup 2 Oct, waiting on Jordan:https://claude.ai/artifact/FR8sqwMYA6nx6qKhRsmnKv (/hill/ and /meta/patch/, the settled layout; two hills × three classes; e.g. Light shooting: Warbuggies, Weirdboy, Mek, Warbikers, Warlock Skyrunners; Heavy melee: Battlewagon, Breaka Boyz, Beastboss on Squigosaur, Khorne Berzerkers, War Dog Karnivore; the pixel crown; bouts from our expected-value engine, the real one rolls dice; winners and losers with the group and army filter; the archive). Built before the reshape (2 Oct, on beta, 81b9d7ba): a page per update at /meta/patch/<date>/ with an index at /meta/patch/; the grouped tables, the /meta/ line, the unit-page line, the Article and test unit/patchpage carry over; the live page, the faction toggle and King of the Hill are the new work. Built on beta 2 Oct (Jordan: "yes, build it on beta"): /hill/ (six hills: Shooting and Melee × Light, Middle and Heavy; Patch and Meta seeds; the King's hero sprite a step larger under our pixel crown, the queue walking up as hero sprites, pixel digits; standings with points, wins, losses, longest reign and damage per 100 pts; Pause, Skip, Shuffle; the last hill, class and seed in hre.koth.v1) and under it the calculator's own result card, a real run each bout through the calculator in a frame (/?hill=1, TRHill.bout: each side's loadout solved against its opponent in the lane, To the death with fight back, the volley and funnel; no counts or analytics from the frame). /meta/patch/ is the live page (the newest balance update; older ones at /meta/patch/<date>/ under "Earlier balance updates"), with the "On the hill now" strip, winners and losers in Up / Down / New / Gone and the group and army filter; /meta/ has the update's card and the meta's strip. The strips show one bout from our expected values (illustrative), the full hill rolls dice. Lane scores per update (ls in patches.json) come from tools/patches.js. With Legends and Crucible out, the classes hold 333 / 372 / 339 units. Stall rule as built: no wipe goes to whoever destroyed the larger share of the other (ties to the challenger), the share rather than raw damage, so a cheap unit isn't punished for its foe's wounds. Tests: unit/hill, browser/hill, unit/patchpage. Waiting on Jordan's look at beta.
T-518built
A live pixel fight on every static page, and Meta without the wall of text: MOCKUP (Jordan, 1 Oct (Pacific), via the design session: "there are no animations linking back to the calculator… the whole page reads like a wall of text… I would like an animated calculator linking back to the main calculator on each page… for the meta pages, take an epic hero fighting another epic hero from each faction, alternating")
More
Mockup 2 Oct, waiting on Jordan's yes:https://claude.ai/artifact/PAJhgKypz3b2gCt1WtS5YP (the /meta/ page, Old One Eye's page and notes; phone and desktop, light and dark). (1) The Clash strip near the top of every static page: our own pixel figures fighting the calculator's real result, one line of real numbers under it, "Run it yourself" opening the calculator on that matchup; plays once in view, again on a tap, still with reduced motion; loaded after the page in a fixed box. /meta/: an epic hero pair a day, factions alternating (today Lion El'Jonson deals 7.7 of the Swarmlord's 10 wounds, 4.8 back); with an army picked, its hero against a rotating rival. Unit pages: best and worst common matchup (Old One Eye: 5.6 of 10 Termagants; 7.7 of a Land Raider's 16 wounds). Faction pages: its hero against a rotating rival. (2) Meta: a podium of the top three armies (Necrons 13%, Custodes 10%, AdMech 8%) with their figures, a Riser of the week (Space Wolves, none to 6%), army bars with glyphs, Test it on each unit row; every sentence kept, explanations moved down. (3) The drawers keep their questions, each with a small visual: a funnel (15 events → 71 lists → 1,241 units), dice score against meta share for three units, the last and next refresh. One epic hero per army, from the data, is in the mockup's notes for Jordan to override (the Tyranids' is the Swarmlord, 7 lists; Jordan named Old One Eye, 2). Open: the count of lists before the top-quarter cut isn't kept yet 2 Oct (Jordan, 1 Oct (Pacific), via the design session): the /meta/ Clash strip gives way to King of the Hill (T-521); settled 02:50Z: on /meta/ a compact "On the hill now" strip (Middle shooting, the meta's best shooters at 76 to 130 pts, to /hill/?seed=meta) and, under the podium, the last balance update's card (to /meta/patch/); the full hill lives on /hill/. Earlier plan: two hills: Shooting with the 5 units in the most winning lists that shoot best by our score (Inquisitorial Agents, Inquisitor Draxus, Rubric Marines, Paragon Warsuits, Chief Librarian Tigurius), Melee with the 5 that fight best (Deathwatch Veterans, Emperor's Champion, Celestian Sacresants, Logan Grimnar, Death Company Marines with Jump Packs); with Tyranids picked, its own; the podium, Test-it rows and the rest kept; the same mockup link, updated. Amended 2 Oct, 03:21Z (Jordan, 1 Oct (Pacific), via the design session, from his phone review of /meta/): (1) the three main tables become tabs, "Armies · Detachments · Units", under the faction row, swapping the table in place; swipe left or right on touch; the hash follows (#armies, #detachments, #units, each linkable); all three stay in the HTML, hidden with [hidden], so search engines still read them; the same control on desktop; the army card that opens on a tap stays as it is. Already there: since T-312 a smaller switch inside "This month's meta" swaps the three, with no hash or swipe, and with an army picked its detachments and units show one under the other; the build turns that switch into the tabs. (2) The faction row takes our pixel glyphs, the unit picker's (icons.js shield, flame, alien) in the group's colour, glyph above the word, count under it, and a new "All" glyph (four squares). Checked: T-476's glyphs only reached the group sheets' army tiles on /meta/; the row was text only. (3) Pictures: army rows the army's glyph (T-375) and its hero sprite (T-378) at 24px at the row's start; detachment rows the army's glyph only; unit chips in "Most taken units" (and the Units tab's rows, 20px) the unit's role figure at 16px (figures.js pick from the stub's keywords); rows stay one line at 390px, a long name truncates with "…", never the number. (4) Cut: "Two lists, head to head" leaves Where to next (Jordan: Meta visitors are browsing, not holding two lists, and "Winning lists: open one, then test it against yours" covers it); three cards remain: Tier list, Winning lists, Test a matchup. Amendments mockup 2 Oct, waiting on Jordan:https://claude.ai/artifact/KHkzFiNE9icfDse7xXa7pG (the real beta /meta/ with the four changes, clickable: Armies with an army opened, Units, Where to next at 390px, Detachments on desktop; light and dark).
Waiting 3
T-332blocked
Army tiers from win rates, not our small sample (Jordan, 28 Sep: "Our factions meta lists are dead wrong… Orks are top tier and we have them as C")
More
Why: our tiers ranked armies by where their lists finished at the ~20 mostly French events MiniHeadQuarters covers (3 months; Orks 21 lists); the public win-rate tables (Stat Check, Listhammer) had Orks among the top armies and AdMech, GSC and Death Guard among the weaker ones, where we had them S, S and A (their figures, Listhammer-derived, were taken off this card: T-750). The method is fine: finish percentile tracks win rate within 1–2 points on the same data. Done 28 Sep: tiers off (ARMY_TIERS_ON in build_pages.js): gone from the Meta hub (site and app), /factions/ lists armies by their units with a note and links to Stat Check and Listhammer, armytiers.json has no tiers. Next (Jordan, 28 Sep: "I don't want to email them. Let's just wait until more events release"): no email. The tiers stay off until our own data can carry them: our source adds ~170 lists a month (~10 events, mostly France), ~5 per army, so ~40 lists per army over 3 months is a long way off unless MiniHeadQuarters grows. Monthly check (first 28 Oct): lists per army over the last 3 months, and our finish order against the public win-rate tables (Stat Check, Listhammer, via web search); back on (per army, those with ~40+ lists) only when both hold. For reference, the corpus's listhammer/results-*.json already holds every player's faction and win–loss record from BCP and Tabletop Herald events (checked locally, not published; the figures were taken off this card, T-750). If that ever changes: win rate per army over the last 4 weeks (singles, 5+ rounds, 16+ players; RTTs out), S ≥55%, A ≥52.5%, B ≥49%, C ≥46%, D below, fewer than ~40 players unranked, credited to Listhammer on every tier; weekly with the data refresh. Force Disposition tiers (T-318) are off too (Jordan, 29 Sep: "Yes"), same switch; the per-army counts stay. (Listhammer's data needs their OK; not asked, Jordan's call.) Jordan, 30 Sep: the rule stays as it is (last 4 weeks, singles, 5+ rounds, 16+ players, RTTs out; S 55%+, A 52.5%+, B 49%+, C 46%+, D below; under ~40 players, no tier). A tier switches on per army only when the army clears that floor AND our order matches the public tables within a couple of points, checked monthly; the sample size shows beside each tier. The public tables (Stat Check, Goonhammer 40kstats, Listhammer's /stats, GW's Metawatch) are a cross-check only: nothing of theirs on the site, not even a link (the paused note's links to Stat Check and Listhammer came out 30 Sep); the monthly comparison is logged in the report
T-411blocked
T-409: the Listhammer part (waits on Listhammer's OK)
More
grimstat-corpus's listhammer/ folder: undefeated and one-loss lists from GTs and RTTs on Best Coast Pairings and Tabletop Herald, with records instead of placings, and every event's full results (each player's faction, detachments, Force Disposition, record). Not under the corpus's licence: "on": false in tools/sources.json, and nothing of it is published until Listhammer says yes (Jordan is deciding whether to email them, 30 Sep; template in T-073). Built, one flag away:tools/sources/listhammer.js; build_lists.js adds per-army win rates to inclusion.json (winrates, only when a source has results) by T-332's method: the last 4 weeks, singles, 5+ rounds, 16+ players, RTTs out, ~40 players to be tiered, S 55%+, A 52.5%+, B 49%+, C 46%+, D below; an event both sources carry keeps the corpus's lists and takes Listhammer's results; its spellings added ("Imperial Agents", "Space Marines (Astartes)"). Local test (corpus 27c1ea7, 30 Sep, not published): win rates and list counts read from the listhammer/ folder with the source switched on locally; Listhammer-derived (Best Coast Pairings and Tabletop Herald results), so their numbers were taken off this card (T-750) and stay out of anything shown. Its lists have no placing, so they feed the tracker, inclusion and Meta score but not the "Recent winning lists" panel until that shows a record (a follow-up once it's on) Jordan, 30 Sep: send the one email; nothing from the folder is published until Listhammer agrees. Why ask rather than read it ourselves: the facts are free, but Listhammer's data comes from Best Coast Pairings (whose terms forbid automated reading) and Tabletop Herald, and the folder is their compilation, so their permission is the clean route to the US events. Email for Jordan to send (from hello@tacticalreroll.com; his wording, 30 Sep; the address in design/sources.md): "Hi there, I'm Jordan, and I run [Tactical Reroll](https://tacticalreroll.com), a free mathhammer calculator for 40k: you pick a unit and a target and it works out the damage, with a little pixel battle on top. I've been a fan of Listhammer for a while; the site is the best read on the competitive scene I know. I'm writing to ask a favour. I'd love to use your event results (which armies played and how they did, nothing about individual players) to give our [unit rankings](https://tacticalreroll.com/rankings/) a 'what's actually winning' column and a fairer sense of where each army stands. We're not a lists or results site and don't want to be one: anywhere we showed a number from your data we'd credit Listhammer by name and send people to you for the lists and the events themselves. The corpus on GitHub (grimstat-corpus) already relays your feed, so reading it there would be the least work for you, but whatever route or terms you're comfortable with is fine by me, including none at all. If our own numbers (per-unit damage, loadout comparisons) would ever be useful to Listhammer, they're yours. Thanks for reading, and for the site. Jordan"
T-059blocked
Stratagems per detachment under each army
More
Blocked: BSData has no stratagem text, and publishing GW's wording is an IP risk. Needs a data source we can use
Parked 79
T-327parked
The My army popout over the calculator: today's My army page (upload, list, rating, units with tiers) in a sheet on phones and a window on desktop; opened from a My army bar at the top of the calculator, the menu's My army, and the attacker picker
More
Mockup sent 28 Sep; parked the same day (Jordan: "put a pin in this for now", to think through how it should work)
T-328parked
"Match up vs" in the popout: a unit (the picker; the pair loads and the popout closes, Simulate ready), a whole army (recent tournament lists, their list by paste or upload; ⭐ random and supporters' armies shown, parked), or "just use it"
More
After T-327
T-329parked
The matchup inside the popout: your units as chips (desktop: the list, with each one's best target), their units ranked best trade first in one column, each row in words ("takes 61 of 70 pts, loses 21 of 85") with T-314's colours and two bars; "Their units attack" turns it round; cover, half range, Game plan; a row loads the pair
More
After T-328
T-330parked
Tabs: Calculator · My army · Meta (My army opens the popout; the Matchup tab goes); /army/ and /matchup/ open the calculator with the popout; tour, first visit and tests moved over
More
Last
T-323parked
The unit picker's source chips (Any · Benchmarks · Saved · Winners), used by the popout's "A unit"
More
Benchmarks as real units (Jordan, 28 Sep: "just pick a unit that fits the bill"): targets Termagants, Intercessors, Terminators, Rhino, Repulsor, Land Raider, Carnifexes, C'tan Shard of the Nightbringer; attackers Tactical Squad (boltguns), Heavy Intercessors, Hellblasters, Eliminators, Devastators (lascannons). The five ranked targets keep their exact stand-ins so the calculator matches the unit pages' rows; Jordan 28 Sep: list both the Rhino and the Gladiator Lancer (the Light vehicle row's size), Rankings unchanged
T-325parked
⭐ Random opponent: the corpus's lists as a lazy data file, one free roll a day, a Reroll perk
More
Parked with the money; design/kofi-tiers.md text if adopted (T-186)
T-326parked
⭐ Supporters' armies in both pickers
More
Parked; extends T-104, needs T-169 and T-170
T-041parked
Sign up for Amazon Associates (.com and .ca) and eBay Partner Network; send Claude the IDs
More
Amazon closes accounts without 3 sales in 180 days Deferred (Jordan, 30 Sep): until the site reaches 10,000 views a week; the weekly report (T-316) prints weekly views against that
T-066parked
Join an affiliate network (Awin or Impact) and apply to one US and one UK hobby retailer
More
e.g. Miniature Market / Noble Knight (US), Element Games / Wayland Games (UK): check each store's footer for an "Affiliates" link. Hobby retailers usually pay 5–10% vs Amazon's ~3%. Avoid Etsy (3D-printed GW copies) Deferred (Jordan, 30 Sep): until the site reaches 10,000 views a week; the weekly report (T-316) prints weekly views against that
T-043parked
Create a Ko-fi page (with the site email, T-113): one-time coffees plus a monthly "Supporter" membership; send the link
More
In progress (25 Sep): ko-fi.com/tacticalreroll is set up with Stripe (payouts to Jordan's bank, monthly; statement name TACTICALREROLL.COM; Radar Lite; support phone hidden), CAD with coffee CA$4 (minimum 3, "Tip" wording), the 0% plan on one-time tips (memberships 5%), one Supporter tier CA$4/month (honest description, perks "arriving soon", a dice-gods joke benefit), terms and thank-you messages, bio written. PayPal skipped for now. Left: profile picture (waits for the logo, T-144), the test payment and refund, then send Claude the link for T-044. Prices (T-121): coffee $3; Supporter membership $3 a month or $30 a year; free plan, not Gold. Check that 0% on tips is switched on. Then, in Ko-fi's settings, point its webhook at the Worker (T-115) and send Claude the verification token. Jordan (25 Sep): three tiers instead of one, see T-158 26 Sep: the test payment and refund are done (it tested T-115 end to end). Parked with the money parts (26 Sep).
T-044parked
Add the Ko-fi link: footer, about page and the support card (T-112)
More
The paywall and its empty WAITLIST_URL are gone (T-111). Needs T-043 Parked with the money parts (26 Sep).
T-051parked
Get an IP lawyer's view before billing goes live
More
Before T-052 (real billing), not before Ko-fi tips and memberships
T-052parked
Billing and accounts
More
Only once there are ~50 supporters. Payments (Stripe), accounts, a real Supporter unlock instead of the code, a server-side usage counter, cloud roster sync. Needs T-043, T-051
T-115parked
Verified supporters: a Ko-fi webhook Worker
More
Code merged in #45 (24 Sep). Reworked 25 Sep (Jordan: no permanent links, they could be shared): sign-in by a one-time 6-digit code emailed to the Ko-fi address (/signin, /verify: one use, 10 minutes, 5 wrong tries, 5 codes an hour; the same answer whether or not an email has supported), a device token stored hashed, at most 3 devices (a 4th signs out the oldest), /status with tier (Ko-fi tier name → reroll / critical / natural-six) and founder (the first 100 monthly supporters, T-158), /signout. Deployed by .github/workflows/worker.yml on pushes to beta (makes its KV namespace and PEPPER on the first run). Left: Jordan adds repository secrets KOFI_TOKEN (Ko-fi → Settings → API) and RESEND_KEY (T-168), and points Ko-fi's webhook at the Worker's /kofi; the site side is T-116 Tested end to end 26 Sep: KOFI_TOKEN set and Ko-fi's webhook pointed at the Worker; a real CA$4 coffee made a record, the thank-you email arrived, and sign-in with the emailed code worked on beta (then refunded). Parked with the money parts (26 Sep).
T-116parked
Support card knows who's supported
More
"I'm a supporter" asks once for the Ko-fi email, T-115 emails a one-time code, and the device unlocks. One-time coffee: a thank-you card, then the card pauses for 3 months or 200 uses and comes back gently ("Thanks again for the coffee! Fancy making it monthly?"). Monthly: no card while active, a ☕ badge and a thank-you line. Lapsed: one friendly "welcome back" card, not the escalation. The card itself offers both side by side: [☕ Buy me a coffee, once] [⭐ Become a supporter, monthly] with the perks under the monthly one. Needs T-115. Built 25 Sep (docs/supporter.js, on beta): "I'm a supporter" in the footer (and the support card's "I already support") opens a sheet: Ko-fi email → emailed 6-digit code → this device is signed in (token in hre.sup.v1, rechecked every 3 days); shows the tier, badges and founder; sign out. Sets the hre.pro flag while active. Nothing is sent to the Worker unless someone signs in. Privacy page has a Supporters section. Still to do on this card: the support card's coffee/monthly/lapsed behaviours; end to end needs KOFI_TOKEN and RESEND_KEY (T-115, T-168) Parked with the money parts (26 Sep).
T-117parked
Supporter duration badges
More
☕ First Coffee, 🎲 3 Months, 🛡️ 6 Months, 🏆 1 Year, 🐉 Gerald's Chosen (2 years). Our own names, nothing from GW ranks or titles. Shown on the supporters wall (T-109) and share images (T-050). Durations from T-115's payment dates. Jordan (25 Sep): also a badge per tier (⚀ Reroll, ⚄ Critical, ⚅ Natural Six) and a Founding badge for the first 100 (T-158) Open question for Jordan (26 Sep): keep "🐉 Gerald's Chosen" for 2 years (the joke comes from the coffee card's "Meet Gerald" stage), or rename it (e.g. "🐉 Two-Year Veteran")? The thank-you email no longer mentions Gerald.
T-110parked
Decide later: tease locked results with a blur, or not
More
Parked by Jordan (24 Sep). Army vs Army no longer has a limit (T-111), so this is only Meta check's free view (the mockup's "view as free visitor" shows 2 lists then a blur). Until decided, nothing on the site is blurred
T-102parked
⭐ Meta check: your army against every winning list of the last 30 days
More
⚄ Critical (25 Sep). Also against the meta tracker's flagged meta lists (T-161). Our own computation on the lists (CC BY): per opponent list, what your army removes and what it loses; best and worst matchups; which of your units carry. Uses ev.js like the comparator. Mockup first(mockup for review: https://claude.ai/artifact/RKN5NfoAst3gBzZibC2gox)
T-046parked
⭐ Paint your army: your colours on your pixel figures
More
Supporter headline perk. Primary / secondary / trim for your side's sprites; the site accent follows. Plain-named presets only (see rules above). Mockup first(mockup for review: https://claude.ai/artifact/RKN5NfoAst3gBzZibC2gox)
T-047parked
Nicknames for your squads and the enemy's
More
Supporter. Shown in the funnel caption, pinned bar and share images: "Brother Castus's squad slew 7 of Dave's Gaunts". Stored with the roster on the device
T-104parked
Featured army: supporters' lists showcased to everyone
More
Supporter perk, seen by all (supporters too): "Featured army: Sarah's Leviathan swarm" in her colours and squad names, one tap to load as the opponent in the calculator or Army vs Army. Jordan (25 Sep): each supporter uploads one of their lists from the site and names it ("Clyde's Assimilation Swarm"); it's stored by the Worker, goes live after the profanity filter with a report button, and visitors' Army vs Army opens with a featured list as the opponent by default (rotating). Consent line when submitting; privacy page updated to say featured lists and names are shown. Needs T-043, T-046, T-047. Mockup first(mockup for review: https://claude.ai/artifact/RKN5NfoAst3gBzZibC2gox)
T-049parked
Game tracker (mark dead models, record results)
More
Supporter. New Recruit's paid feature
T-105parked
Flair: original effects and backdrops
More
Supporter. Death / impact effects by type (ichor splats for bugs, sparks for vehicles, dust), battlefield backdrops (ruins, ash wastes, jungle, void ship), a banner over your named warlord. Our own inventions, never recognisable GW designs
T-106parked
Dice-tray mode
More
Supporter flair: pixel dice actually rolling for one volley, next to the funnel
T-107parked
Early access for supporters
More
A flag that shows features still in testing (e.g. T-091 size 1) to supporters first. Jordan (25 Sep): an opt-in switch in the supporter's settings
T-108parked
Roadmap vote for supporters
More
A monthly Ko-fi members' post with 3 cards to pick from; Claude builds the winner. ⚅ Natural Six (T-158)
T-109parked
Supporters wall on the about page
More
Opt-in names only. Jordan (25 Sep): a wall of gratitude with tiers by how long they've supported (T-117's badges), each with their named squad and its fame (T-155). Names opt in when signing in, through the same filter and report button Jordan (26 Sep): the wall starts at ⚄ Critical; Critical supporters sit below the Natural Six section.
T-152parked
Leaders become a Reroll perk
More
Jordan (25 Sep): no monthly allowance; the free week (T-157) is the taste. After it, + Leader / + Support explain the perk. Assumed, to confirm in the mockup: shared links and imported lists still show the leaders they came with (read-only), so shared matchups keep working. Mockup first. Needs T-116 Parked with the money parts (26 Sep).
T-153parked
Saved lists become a supporter perk, on the device and across devices
More
Jordan (25 Sep). Free: the two lists being compared survive a reload. Supporter: the library of saved lists (today free, up to 30 on the device) and the same library on every device they sign in on, kept by the Worker. Lists people saved before the change stay readable. Privacy page says lists are stored for supporters. Mockup first. Needs T-116 Parked with the money parts (26 Sep).
T-154parked
A squad named after you
More
Jordan (25 Sep). A supporter picks the datasheet they like best and names it ("Clyde's Necron Warriors"); they can change it once a week. When they use that datasheet themselves (attacking or defending) it's their squad; when anyone else's matchup has it, the site rotates through supporters' names for it: in the answer card, the funnel caption and Army vs Army rows. Visitors can't name their own. Hiding other people's names is a Natural Six perk, and Natural Six names 3 squads (Jordan, 25 Sep). Stored by the Worker; profanity filter and report button. Mockup first. Needs T-116 Jordan (26 Sep): moves down to ⚀ Reroll; 1 squad at Reroll, 2 at Critical, 3 at Natural Six.
T-155parked
Squad fame: counters for supporters' squads and lists
More
Jordan (25 Sep). For each named squad (T-154) and featured list (T-104): times faced, models it slew, times it was wiped. Counted by the Worker as totals only (no visitor tracked); shown to the supporter and on the wall (T-109). Privacy page updated. Needs T-154
T-163parked
Squad RPG: experience, levels, achievements
More
Jordan (25 Sep), for named squads (T-154). Every matchup anyone runs with a supporter's squad (attacking or defending) earns it experience, counted by the Worker as totals: models slain, attacks survived, which units it killed most, matchups fought (matchups, not players: no visitor IDs, so the privacy page's no-tracking promise holds). Levels show as rank chevrons over its pixel figure and a colour trim at high levels; achievements and titles ("Survived 1,000 volleys", "Slew 500 Terminators", "The Unkillable"); a profile page per squad with its kill log, trophies and level history, and a "ridiculous amount of stats" (Jordan, 25 Sep): matchups as attacker and defender, models slain, attacks survived, dice rolled on its behalf and its sixes, favourite prey, nemesis, best single volley, factions faced (heat map), first blood, level-up dates, leaderboard ranks, and how it compares with every other squad of that datasheet. Experience capped per day and per device against farming, and worked out by the Worker from the matchup with our own maths (never taken from what a browser reports), so the leaderboards (T-165) can't be faked. Our own names for titles, never GW ranks. Mockup first. Needs T-154
T-169parked
Moderation for what supporters publish
More
For list names (T-104), squad names (T-154) and captions (T-156): a profanity filter (our own word list plus look-alike letters), a report button on every published name, a report hiding it until Jordan looks, an email to the site Gmail per report, and a one-tap pull for Jordan. Needs T-170
T-170parked
Worker storage for supporter features
More
Extends T-115: storage (Cloudflare KV or D1) for saved lists across devices (T-153), featured lists (T-104), named squads and their counters (T-154, T-155, T-163), matchup counts (T-167) and rate limits; an API the site calls with the device's sign-in token. The privacy page lists each thing stored, in the same PR. Needs T-053. Started 25 Sep: saved lists across devices (GET/PUT /lists, 256 KB per supporter) on the same KV; the rest (featured lists, named squads, counters, matchup counts) follow with their cards, probably on D1 for counts Parked with the money parts (26 Sep).
T-172parked
List doctor: swaps that improve your matchups
More
From the 25 Sep brainstorm (not picked yet): "swap your Hellblasters for Eradicators and your matchups against this month's winning lists improve by 8%", from our own engine. A Critical candidate
T-173parked
Points-update alerts
More
From the 25 Sep brainstorm: when the daily data update changes points or stats for a unit in a supporter's saved lists, email what changed in their maths. Needs T-153, T-168
T-174parked
Event prep matrix
More
From the 25 Sep brainstorm: your list against the top 10 lists of an event or region, as a win/lose heat map. Overlaps Meta check (T-102)
T-175parked
Loadout face-off images
More
From the 25 Sep brainstorm: two wargear options side by side (plasma or melta?) as a shareable image. Pairs with T-050
T-176parked
Homebrew and playtest stats
More
From the 25 Sep brainstorm: edit a datasheet's numbers (balance rumours, house rules) and save them; the player's own numbers, never GW's text
T-177parked
The volley animation as a GIF or video
More
From the 25 Sep brainstorm, for social posts. Pairs with T-050
T-178parked
Club plan
More
From the 25 Sep brainstorm: one subscription for a gaming club's members, with the Discord bot (T-160) for its server
T-180parked
Colosseum pool: submit a 500-point list, meet random opponents
More
Jordan (25 Sep), a card for later. Upload your Colosseum list (500 pts) to an online pool; the site pairs it with random other lists from the pool and plays out each pairing with our engine (Army vs Army, both ways), so players can theorycraft their Colosseum armies. Thoughts: a rating per list from its results (Elo-like), a weekly leaderboard, "your list's worst matchups", and the lists that win most become a Colosseum meta view (T-161). It measures damage traded, not a whole game (no movement, objectives or charges declined), so say so. Needs the Worker's storage (T-170) and moderation for list names (T-169); likely free to enter, with the deep breakdown as a Critical perk. Check the Colosseum format's 11e rules on Wahapedia before building
T-181parked
Last Squad Standing: a duel to the death
More
Jordan (25 Sep), a card for later. Two units fight until one is wiped, turn by turn, run many times: (1) both shoot in turn until one is gone; (2) shoot then charge and fight, the other fights back; (3) full alternation: you shoot and assault, they shoot and assault, and so on. Both "who goes first" orders shown. Answers: who wins how often, in how many turns, how many models the winner has left, and a round-by-round funnel. Thoughts: the engine already carries casualties between volleys (T-145's tgt state), so losses shrink the next volley; charges need a success chance (2D6 against a distance the player sets, or taken as made), Fights First and leaders' buffs matter here; the volley animation can play it round by round, and named squads (T-163) earn experience from it. Name picked by Claude; alternatives: "Fight to the Last", "Duel to the Death". Mockup first
T-164parked
Squad looks: rank-up effects and customisation
More
Jordan (25 Sep). As a named squad ranks up it earns looks: colour trims, glows, auras, a banner, a grander death effect. Supporters customise colours, armour textures (plain, camo, hazard stripes), bullet and tracer colours, muzzle flash and impact effects in the volley. Gear upgrades are looks and titles only, never the dice (Jordan: looks only), so every squad of a datasheet has the same maths. Our own designs, never GW's. Needs T-163
T-165parked
Squad leaderboards
More
Jordan (25 Sep). Most experienced squad overall, per datasheet ("the most decorated Necron Warriors"), per faction and this week; kills, survivals, dice rolled. From the Worker's own counts (T-163), so they can't be faked. Mockup first. Needs T-163
T-166parked
16-bit hero sprites for named squads (a Final Fantasy VI-like look)
More
Jordan (25 Sep): try it with a mockup first: one original squad as a shaded 32×48 sprite at three rank stages, gear as layers, on a battle backdrop. If it lands, supporters' named squads get hero sprites; everyone else keeps today's figures. Original designs only (more detail is closer to GW's, so extra care). Mockup first
T-158parked
Three Ko-fi tiers and the founders offer
More
Jordan (25 Sep): ⚀ Reroll CA$4, ⚄ Critical CA$8, ⚅ Natural Six CA$15 a month (yearly prices if Ko-fi offers them, about 10 months' worth); the first 100 supporters keep their price for life with a Founding badge. Jordan sets the tiers up on Ko-fi (T-043 has one CA$4 tier today); Claude reads the tier from Ko-fi's payment notices in the Worker (T-115), counts founders, and writes the tier comparison for the site and Ko-fi. Tier names and badges are our own words, never GW ranks Parked with the money parts (26 Sep).
T-160parked
Discord bot
More
Jordan (25 Sep). /reroll Intercessors vs Termagants posts the answer in a club's Discord, with a link back to the matchup. For growth: every answer carries a tasteful footer link to tacticalreroll.com (Jordan, 26 Sep: always, like share images); clubs with a Natural Six member get named squads in its answers, never a mark-free bot. Runs on the Worker with the same maths (ev.js); Discord app registration is Jordan's Parked (Jordan, 27 Sep): the full answer means crunching datasheets on our server, beyond Cloudflare's free plan (~$5/month for Workers Paid); a card-and-link bot would be free. Revisit when money is unparked
T-185parked
A Tactical Reroll Discord server, with Ko-fi roles
More
Jordan (26 Sep) asked when. A free community server: announcements (what's new), feedback and bugs, rules questions, list sharing, and supporter channels. Ko-fi gives members their Discord role by tier by itself (Ko-fi's Discord rewards), so no code of ours is needed: roles ⚀ Reroll, ⚄ Critical, ⚅ Natural Six, and a Founding role later. Jordan makes the server and connects it in Ko-fi; Claude drafts the channels, rules and welcome text, and links it from the site (footer, what's-new card) once it exists. Our own bot (T-160) comes later Jordan (26 Sep): later.
T-186parked
Keep the Ko-fi tier text up to date (monthly, and whenever a perk ships)
More
Jordan (26 Sep): so the tier descriptions and welcome messages don't go stale. The text lives in design/kofi-tiers.md (the source of truth). Claude: whenever a batch ships, moves or drops a supporter perk, update that file in the same PR and tell Jordan exactly which parts to paste into Ko-fi. Monthly review (first Monday of each month): Claude checks the file against what's live on the site and the task board ("arriving over the coming months" becomes "live now" as perks land; drop promises that changed), updates "Last reviewed", and lists the changes for Jordan to copy. This card stays open; it's a standing chore
T-187parked
Launch checklist: nobody can pay until everything is ready
More
Jordan (26 Sep): "let's get things more buttoned up before we risk charging money; make sure no one is accidentally buying until we're absolutely ready." Until launch day, nothing on Ko-fi can be bought and nothing on the site links there. Now (Jordan): set the gift item and the three membership tiers back to hidden/draft on Ko-fi; one-time coffees can't be switched off on Ko-fi, but nothing links to the page yet, and a coffee is handled (a thank-you and a month's pass). Before launch (Claude unless marked): (1) the batch with sign-in, coffee months and gift codes (T-115, T-116, T-182, T-183) live on tacticalreroll.com, since every Ko-fi email points there; (2) Jordan's call: which perks must exist on day one (suggested: the Reroll basics, leaders T-152 and saved lists T-153 across devices, and the free week T-157, so a first supporter gets something the day they pay); (3) a monthly membership tested end to end (subscribe at CA$4, sign in, the tier shows, then cancel; the month runs out 35 days after); (4) one gift bought and redeemed (the variant named exactly "Natural Six"), then refunded; (5) replies to hello@ reach a person (T-113); (6) the Ko-fi tier text matches what's live (T-186); (7) the logo on Ko-fi: dice-only as the profile picture, the banner as the cover (T-144); (8) the privacy page and Ko-fi's terms and refund wording read right; (9) the site's Ko-fi links and coffee card on (T-044), last of all. Launch = Jordan re-publishes on Ko-fi and says go Parked with the money parts (26 Sep). Jordan, 30 Sep: revisit once beta is on main and has been stable for a week; nothing changes until then and every perk stays off Perk candidates, each labelled "Free while we're in early days" from the day it ships (Jordan, 2 Oct, via the design session): the custom lens (T-531); saved lists beyond the free count (T-153). Add new candidates here as they're carded.
T-157parked
A free week of Supporter, started by the visitor
More
Jordan (25 Sep): new visitors try every feature before paying, no account; the public ones (a named squad, a featured list) are shown to them as a preview and go live once they subscribe and sign in. The first time they reach a supporter feature (a 6th leader join, saving a list, colours, a reaction), a card offers "Try every Supporter perk free for 7 days" [Start my free week] [Not now]; a "⭐ Try Supporter free" chip in the header does the same. While it runs: a "⭐ Free week: n days left" pill in the header, and on the first visit each day a toast with the days left and one thing they used; days 6–7 add the Ko-fi button. Day 8: one closing card with what they did that week and [Become a Supporter], then the free limits. Unlocks every tier's features on the device (Meta check and the Game plan card included); lists on every device need a sign-in, so they wait for subscribing. Kept on the device, so clearing the browser restarts it (accepted). Plausible events to judge the gates later: trial offered, started, ended, and Ko-fi clicked from it (Jordan: gates as decided, revisit with 1–2 months of data). Mockup first Parked with the money parts (26 Sep).
T-182parked
A one-time coffee unlocks a month
More
Jordan (25 Sep): a coffee unlocks the supporter perks for 31 days at the tier its amount would buy monthly (CA$4 Reroll, CA$8 Critical, CA$15 Natural Six; under CA$4, a thank-you badge only), from Ko-fi's amount. A coffee during a running month adds a month on the end, at the higher tier. Coffees don't count towards the 100 founders. Built 25 Sep (worker passTier/addPass, /status gains via: monthly or pass; the sign-in sheet says "Unlocked until …"), on beta Parked with the money parts (26 Sep).
T-183parked
Gift a month: gift codes from the Ko-fi shop
More
Jordan (25 Sep). Ko-fi can't gift memberships, so: a "Gift a month" item in the Ko-fi shop, with variations named Reroll, Critical and Natural Six. Ko-fi's "Shop Order" notice makes one code per item bought (10 letters, XXXXX-XXXXX, no look-alike characters, stored hashed, valid a year) and emails them to the buyer (a 🎁 Gift Giver badge, no month for them). Whoever has a code taps "I'm a supporter" → "I have a gift code", types it with their own email (POST /redeem: once, 10 tries an hour per email) and gets that tier for 31 days (🎁 Gifted a Month badge), then signs in with the usual emailed code. Other shop items are ignored; once there are some, set GIFT_ITEMS ("linkcode:tier,…") so only the gift items count. Built 25 Sep, on beta. Jordan: make the shop item on Ko-fi (Shop → Add item: digital, "Gift a month of Tactical Reroll", variations "Reroll" CA$4, "Critical" CA$8, "Natural Six" CA$15; no file needed) once KOFI_TOKEN and RESEND_KEY are in (T-168) Parked with the money parts (26 Sep).
T-208parked
Supporter perk: choose your charge, death and particle effects
More
Jordan (27 Sep). Supporters pick their squad's charge animation, death animations (vaporise, splatter, knock-off, …) and particle effects for T-207's charge, with a free default for everyone. Our own art, so it's allowed under the supporter rules. Tier: ⚄ Critical (Jordan, 27 Sep); design/kofi-tiers.md updated. Goes with T-156 (reactions) and T-164 (squad looks) Parked with the money parts.
T-217parked
Achievements for using the app
More
Jordan (27 Sep), to think over: vanity unlocks such as a squad reaching a level, models slain, your units slain by others, lists added, simulations run. They live in the supporter panel (T-216); maybe earned by everyone but shown in full to supporters. Counted on the device (and by the Worker for supporters), never tracking visitors Parked with the money parts (it lives in the supporter panel).
T-484parked
Deaths by weapon (Jordan, 30 Sep: "a whole host of death animations relevant to the weapon that kills them"; epic, for later)
More
One killing animation per weapon family on the slain model in place of today's plain fall, original pixel art, 0.5 to 0.9 s, within the family's volley time: melted to goo (melta, acid), bitten in half (bites), vaporised (rail, plasma, dark light, lascannon), blown apart (missiles, grenades, cannons, bio-plasmic), cleaved (blades, claws, chain, whips), husked to ash (warp, force), burnt (flame), flayed (gauss, arc, lightning), crushed flat (grav, hammers), today's fall with the family's particles for the rest. Readable in silhouette; counts and funnel timing unchanged; marks fade at the next round; kills in one volley stagger ~80 ms; vehicles wreck (smoke, a hulk), never bleed; each lists the reactions (T-156) that may follow. Catalogue in design/weaponfx.md under Deaths; drawn in the gallery when Jordan calls it up. Needs T-485 first
T-498parked
Epic mode (Jordan, 30 Sep, via the design session: "future, not actionable now; entertaining ideas to keep")
More
Once the weapon effects, fates and deaths are in: a full-screen battle. The battle box widens to the whole screen; the funnel gives way to a battlefield with selectable terrain (our own pixel backdrops, T-373's five to start); many more particles; a volley's scale shows (every shot, background fighting among the other models); melee an interactive back-and-forth. Same engine and numbers underneath, only the stage changes. Needs the effects catalogue built, the fates, the deaths, the Violence switch, the melee choreography and a phone performance budget. Entered from a button on the battle card, escapes back to the calculator
T-499parked
A mini game (Jordan, 30 Sep, via the design session; an idea only)
More
A small Metal Slug-style run-and-gun with our pixel figures and effects, for fun; no rules content and no GW art; perhaps a supporter perk, being our software and our art (T-187's rules apply)
T-664parked
Two versions of every ranking: vanilla and full (Jordan's colleague, 4 Oct: "for each ranking we do 2 versions - one is vanilla, not factoring detachments squads or other advanced things... we can try and determine which approach reviewers may have taken")
More
Later, not now. Vanilla: each unit on its datasheet alone (no detachment, no joined characters, no preferred targets or other advanced lines); full: the ledger as it stands. Score both against each reviewer and fit, per reviewer, which version (or blend) they resemble, to shrink the discrepancy that comes from reviewers grading on a different basis. A report first; the dials stay game-wide Jordan, 5 Oct: the destination. The reviewer matching is calibration while the method is established; once confident we publish both letters (as usually fielded and at its best), each with its working, "and they can chase us rather than us chase them"
T-665parked
Force Dispositions and detachments in unit tiers (Jordan's colleague, 4 Oct: "if a unit is only good in Assimilation Swarm, but that detachment's disposition sucks, it will negatively impact the ability to play that unit")
More
Later, not now. A unit's worth depends on the detachments it shines in and the Force Dispositions those detachments can play; disposition matchups (tools/dispositions.js, T-537) weight that. Plus each army's idiosyncrasies (unique unit or detachment interactions, e.g. Biovores) as their own dials, added army by army as the data grows
T-666parked
Tiers are a snapshot (Jordan's colleague, 4 Oct: "any tier ranking is a snapshot in time and a generalization of the current meta, and metas vary by area")
More
When unit tiers are published: every tier list says it's a snapshot at a date (the points and data it used), a generalisation of the current competitive meta, and that local metas differ. Our own words, on the tier pages and How we score this
T-697parked
Public tier pages from the ledger: every army's tier list and every unit's ranking, with charts showing how we got there (Jordan, 5 Oct: "dump ungodly amounts of data onto the screen in beautiful charts to show how we arrived at those conclusions"; "even if our tiers don't match perfectly, we can at least explain how we ranked them")
More
Mockup after the new recipe (Jordan), on the final numbers. Army page: tier board, worth against points, role coverage, agreement with reviewers stated openly. Unit page: its letter both ways (as usually fielded, at its best; T-664), worth by line, by squad size and detachment, what its damage lands on, rank among same-job rivals, which dials move it most, our letter beside the reviewers' (credited). The ledger writes per-army data into the site build; our numbers, letters and charts only, never datasheets, profiles or rules text on indexed pages. Prototype: the Ledger Breakdown by Army page
T-699parked
11th edition's Hidden rule in the ledger, with the indirect-fire penalty (Jordan, 5 Oct: "basically gave all infantry in cover lone operative"; after the cascade)
More
The ledger has no terrain (design/rules-notes.md: Hidden is the biggest absent shooting rule). One game-wide dial at 0, three parts from the enemy's own weapon ranges: (1) hiding Soak: a non-firing INFANTRY/BEASTS/SWARM unit faces only the fire from within 15" for the share of turns it can hide; (2) targets out of sight: a shooter's Kill into enemy infantry shrinks by its fire from beyond 15", Indirect Fire exempt; (3) shoot or hide: shooting infantry keep it only on turns they don't fire. With it, docs/ev.js reads 11th edition's Indirect Fire penalty (hits on 6s, 4+ with a spotter, cover), not read today. Soak is ~2% of Value, so most of the effect is expected through Kill. Our own words; measured held out, then a cascade re-run
T-708parked
Proposal: value over replacement, in victory points, inside an army's needs (Jordan, 5 Oct: "make it a proposal, don't act on it yet")
More
design/proposals/value-over-replacement.md: a unit worth what it adds over its army's next-best option for the same job, with saturating job needs, in VP a game; tested on reviewers' consensus, tournament outcomes (tools/outcomes.js, grimstat-corpus CC BY 4.0) and list share as a diagnostic. Parked until Jordan decides; a Tyranid prototype first if approved
T-720parked
Price what players check in their heads: effective wounds with revival, tarpit value, supported value (Jordan, 5 Oct; after the cascade)
More
Jordan's reasoning on Termagants (6 points a wound, OC 2, revived by Tervigon, Assimilation Swarm and the Unending Swarm stratagem; poke back with Prime, Swarmlord and Invasion Fleet buffs; Neurogaunts cheaper but less OC and no guns; Barbgaunts tougher but not revivable): (1) effective wounds per point over a game, counting the army's revival sources that can reach the unit; (2) tarpit value, Soak × OC properly weighted; (3) supported value, the unit scored with the buffs its own army can realistically put on it. Gaunt family first with Jordan's reasoning as the prediction, then all armies held out. Quick check on 5 Oct: "best at any one job" alone averages 56% vs 61% for today's worth, but lifts Thousand Sons 38→57 and Space Wolves 51→68: the idea is right, the job calibration is off
T-741parked
Credit units for primary and secondary scoring better (Jordan, 6 Oct: "let's come back to that later")
More
Today: the Score, Hold and Actions lines (small in Value), objective presence (tools/objpresence.js), the mission model (design/mission-notes.md, tools/missions.js, missionvp.js, secvp.js); step 38 (a VP ledger) failed; T-736 found OC per point and holding at chance as predictors. Rethink how a unit earns victory points: holding primaries under fire, taking actions, denying the enemy's, Battle-shock and OC, cheap scorers and Lone Operatives, in the points-exchange frame. Parked until after step 43 and the stratagem pilot (T-713).
T-536parked
The north star: eight steps from a dice calculator to a coach (Jordan, 1 Oct (Pacific), via the design session)
More
Parked, a design note only: design/north-star.md, rendered at /lab/docs/north-star/. The steps: the meta, the mission (dispositions), scoring, list building, testing, the whole game, the counter, play and learn. Each has the maths it needs, a rough size (weeks or months) and what it unlocks, and the note lists what stays true at every step. Nothing builds from it; cards that serve a step say which one.
T-525parked
The tier system's lineage on the Method page, when v2 ships (Jordan, 1 Oct (Pacific), via the design session)
More
Child of T-523; builds only when v2 ships. One paragraph on /about/ and in the tier list's "How we score this". The paragraph, drafted now: "Our four jobs have ancestors. Goonhammer's unit-role series for 9th edition sorted units into Position Holders, Brawlers, Elimination and Attrition, with Objective Utility and Force Multipliers beside them (goonhammer.com, the \"Unit Roles\" articles), and the hammer and the anvil is an older idea still. Our Anvil is closest to their Position Holders, our Hammer to Brawlers and Elimination together, our Runner to Objective Utility and our Banner to Force Multipliers; Attrition we split between the Anvil and the Runner. What we changed, and why we think it's better: our jobs are computed from every datasheet, every day, not assigned by a writer per article; a letter means \"best at its job for the points\", with each job's S as scarce as winning armies' demand for it; how often players take a unit is shown beside the letter as evidence, never folded into it; and the Lab publishes where the system misses. Our Hammer counts damage before death, an idea Goonhammer's Hammer of Math column has used for years; we're the first we know of to publish it for every unit." Links by name and address in our own words, no quoting; check the series' exact title and address when it builds (goonhammer.com was blocked from the build session on 2 Oct).
T-522parked
The first clash pages: /clash/<a>-vs-<b>/, at most 50 (Jordan, 1 Oct (Pacific), via the design session)
More
After T-519 and T-521. Chosen from the pulse's shortlist (top movers × their most tested opponents), never generated across all pairs. The pair in a stable order; the reverse address points to it (a meta refresh and canonical). Each page: a verdict line in our words with the numbers ("X kills 4.2 of 5 Y in a fight; they kill it back 1 in 6 times"), the fight both ways, the loadout that wins each way, what tips it (cover, a leader, the detachment that matters most, from our data), the Clash strip's battle for that pair (T-518), "Run it yourself" into the calculator on that matchup, and the two units' recent share in winning lists. Pages must differ in substance: a test asserts no two share more than 60% of their sentences. In the sitemap with lastmod from the data. Guardrails: no statlines or profiles on the page, no rules text, names only as text. Mockup first (one clash page, 390px and desktop, both themes), then stop for Jordan's yes on the design. On beta (main frozen, Jordan 2 Oct). Mockup 2 Oct, waiting on Jordan:https://claude.ai/artifact/V4JUXuKt4eapsa2XA1fe19 (Squighog Boyz vs Intercessor Squad, real numbers from our engine; the detachment line is its one placeholder). Parked 2 Oct (Jordan, 1 Oct (Pacific), via the design session: "parked as just an idea for now; it adds to the page count and we can do something similar and tidier"). The mockup stays for reference.
T-401parked
Named squads, the site side (T-154, T-155)
More
Off until Jordan says (perks stay off on main, T-187); needs T-379's Workers deployed and T-116 on. My army's "Your named squads" card (a slot per tier, the datasheet from the unit picker, the name, when it may change, our filter's message), fame under each (faced, slain, wipes, wiped out), the names rotating on those datasheets for everyone in the answer card and battle caption with ⚑ to report, /fame sent after a matchup with a named squad, and the privacy page's line for fame totals. Mockup first
T-402parked
Squad RPG: levels and achievements (T-163)
More
The proposal in design/squad-rpg.md (XP from the local record, level n at 10 × n² XP, chevrons and a trim, nine achievements with our own names and pixel badges, what could be a perk later). Waits on Jordan's three questions there
T-415parked
The art contest (split from T-211, Jordan 30 Sep)
More
Players submit figures drawn in the editor; parked until there's moderation (T-169) and a submission licence for what people send us
T-212parked
Desktop: more on one screen?
More
Jordan (27 Sep): the PC has far more room than a phone; should Army vs Army, winning lists and rankings share a screen there, or a middle ground? 27 Sep, both reviews: keep the four screens (merging brings back the long scroll); fill the empty space on wide screens. Calculator: the attacker faction's top units in the empty space under the unit cards, one tap to swap. Army vs Army: a list browser in the empty right third only while no pairing is open (Fable: a permanent fourth column would squeeze the lists). Winning lists: six cards a row, all lists shown, filters as a left rail. Rankings: all units shown, a value bar in the empty gap, a "use in calculator" button. Each panel added one at a time, with a toast when it changes another screen. Jordan's 1,570px screenshot (27 Sep) showed the empty left column and bottom third; ideas for the calculator: "Best attackers into this target" and "Their best answers" under the unit cards. Jordan (27 Sep): most people won't have a monitor that big; leave it for now
T-213parked
A list builder like New Recruit?
More
Jordan (27 Sep): how much more to build one; are we nearly there? Claude's estimate (27 Sep): about 70% of the data exists (units, sizes, points, wargear limits, leaders, detachments, importers, saved lists); a light builder inside Army vs Army about 1–2 weeks, New Recruit parity months. Jordan (27 Sep): not now. Claude's advice (28 Sep, was T-284): not a full builder (list validation across 36 armies and every points update is a long tail New Recruit already covers, and a builder competes with GW's own paid app); instead "tune your list" inside My army: change loadouts, model counts and units of an uploaded list and see the points and the maths move. Revisit on how people use My army Jordan, 30 Sep: not a full builder: the direction is T-414, "tune your list" inside My army. T-213 stays parked
T-223parked
Unit pages: statline, points and weapons near the top
More
Jordan (27 Sep): the stats and points at the top, each weapon's profile in a drawer. Parked by T-231 (Jordan and Fable, 27 Sep): full datasheets stay off the Google-indexed unit pages; they keep points and our numbers, and the calculator and Rankings show the inputs on request
T-416parked
Two community posts for Jordan to paste (T-230 follow-up, Jordan 30 Sep)
More
One for r/WarhammerCompetitive and one Discord announcement, drafted once beta is on main
T-120parked
First translated languages
More
Claude translates the strings; a native-speaking player checks each language before it goes live. Then the static pages (faction, about, mathhammer guide) in those languages for search traffic, with hreflang links. The privacy page is translated too and must stay accurate. Needs T-118, T-119 Parked (Jordan, 27 Sep): wait for more visitors; look again at about 500 visitors a month and start with any language that brings 10% or more of them (T-118: about 80% English-speaking countries so far)
T-091parked
Pixel tactics: a top-down hex-grid mini wargame with battle pop-ups
Done this week 170
T-750done6 Oct · branch claude/tyranid-points-efficiency-3fexxf (not on beta)
Lab housekeeping: no Listhammer-derived numbers in the Lab (Jordan approved, 6 Oct): reports/tiers/2026-10-06-tournament-sources.md renamed -internal (build_pages.js skips it; its stale Lab page deleted; links updated in reports/tiers/README.md, design/sources.md, T-735); design/sources.md (rendered at /lab/docs/sources/) loses the listhammer/ index size and Lexmechanic's list count; the board loses T-735's listhammer/ size, T-332's Listhammer and local listhammer/ win rates and event counts, T-411's local test (win rates, players, list counts; from the listhammer/ folder, commit 82efdef0e6, 30 Sep) and T-413's Listhammer coverage counts (commit 10671bed59, same source), each saying the numbers were taken off. docs/ regenerated as CI does; no -internal report rendered, no Listhammer-derived figure under docs/
T-742done6 Oct · branch claude/tyranid-points-efficiency-3fexxf (not pushed)
Stratagems for all 16 armies (from T-713's pilot; Jordan approved the catalogue and the step, 6 Oct): the catalogue (tools/stratfx/<army>.json, our words, no names: every detachment the ledger knows in all 16 armies; reports 2026-10-06-stratagem-catalogue-a.md and -b.md), every army's uplift and spend on snapshot 1b88e955b9b3 (points per CP by detachment, +core average 8 to 67, margin 0 to 35), then the held-out step (tools/stratagems.js --step; w and the new shoot-back share fitted on the training folds, placebo credits as control). Reverted: held out −0.6 [−1.8, +0.4] over Track A2, 5 of 15 not falling, the placebo credits' 95th percentile +0.4 (the real credit beats 3 of 20); the folds' w 2, share 1 comes from Votann alone (+21.9; the other 14 lose at every w); face value +0.3 [−1.0, +1.7]. Stratagems stay out of Value. Report reports/tiers/2026-10-06-stratagem-step.md. Later, their own cards: the calculator's stratagem chips, the detfx.json stratagem notes in our words
T-317done5 Oct
Plausible: the goals to add by hand (kept current)
More
Done 5 Oct (Jordan added List Tune Saved, Meta Share, Share and Best matchup; the four stale goals stay, harmless). Checked 5 Oct against Jordan's goal list: every event the live code sends has a goal except List Tune Saved, Meta Share, Share; add those three. When Best matchup (T-688) reaches the live site: add Best matchup. Goals for events the code no longer sends (safe to delete): Loadout Goal, Beta Layout, Paywall Shown, Pro Waitlist Click. Plausible's own (keep): Outbound Link: Click, File Download, Form: Submission, 404. Matchup Pair is still sent by the code and has a goal; harmless. The Sites API (goals by API) is Enterprise-only, so goals stay by hand: Plausible → Site settings → Goals → + Add goal → Custom event, names exactly as written; goals count from the day they are added (no backfill).
T-650done5 Oct
Jordan's Biovore dial limited to the Biovores (the review, recommendation 6: the dial's 'placed anywhere' lever also lifts the Harpy about 13 places; consensus ~50th)
More
Switch dialBiovoresOnly, off: the dial's effect applies to the Biovores' forms only; one step on the latest confirmed base. Done 5 Oct: step 33 confirmed (Harpy 36 → 47; seven lists 73.7%; re-fitted +0.033).
T-651done5 Oct
The line weights fitted to tournament inclusion, judged on the reviewers (the review, recommendation 7: list share orders the Tyranid reviewers 86% right against the ledger's 75%; across 16 armies 70.9% against 59.4%)
More
Hedonic pricing: fit the few active weights so Value per point predicts each unit's tournament inclusion (grimstat-corpus, CC BY 4.0, already used by the repo; never BCP or Listhammer) over the armies with 12+ lists, with strong shrinkage; judged on the seven reviewer lists, held-out units and the 14 never-fitted armies; the setting saved as a benchmark. Also list share as a yardstick column in reports. Done 5 Oct (reports/tiers/2026-10-05-hedonic.md, tools/hedonic.js): fitted to inclusion over 11 armies (474 units), the weights barely move (λ chosen on inclusion alone); the candidate equals step 30 within noise, not adopted. List share orders the reviewers 70.2% over 16 armies (Tyranids 85.8%) against the ledger's 61.1%; where players and reviewers agree against the ledger, the ledger is at chance (52%): re-weighting can't reach those units; it needs a new line.
T-652done5 Oct
A fresh critique when stuck (Jordan's colleague, 4 Oct: "if you ever get stuck, get [a reviewer] to critically appraise ... or restart")
More
Re-weighting can't reach the remaining misses (T-651). A fresh reviewer appraises the approach, explains each Tyranid miss unit by unit from the rules and the owner's notes, and proposes 2–3 new lines with read-only previews and an order to build them. Done 5 Oct (reports/tiers/2026-10-05-critique.md): the support multipliers (Synapse, cp, heal, buff) multiply the carrier's own Kill, which explains most misses (every 'too high' unit carries ×1.8 or more; most 'too low' carry none). Proposals: T-653 Battle-shock pressure in points, T-654 support as a credit; hygiene first.
T-653done5 Oct
Battle-shock pressure priced in points, plus the critique's hygiene (T-652 proposal 1 and step 0)
More
Switch shockPressure, off: rules that force Battle-shock tests without first dealing damage, priced through the 31-points-a-VP bridge (constants by reasoning); every army's Battle-shock and cp rules classified (unconditional or after damage); the Leapers' counter-charge rule re-filed as a ledger override (not the frozen data). One step. Done 5 Oct: steps 34 and 34b confirmed (seven lists 75.0%, held-out Tyranid units 78.7%; Neurolictor 28 → 12, Neurotyrant 37 → 30, Leapers 2 → 11).
T-692done5 Oct
Leagues of Votann reviewer lists for 11th edition (Jordan, 5 Oct: "find some 11e votann tier lists to add to our repo for the tier list component"; then "use the astrategas wargames list as well, its basically unchanged")
More
Done 5 Oct: no lettered 11th-edition Votann unit tier list exists yet from our reviewers (searched Auspex, Art of War, Tista, Tactical Sugar, Goonhammer, 6++, Grimdark Breakdown, Love & Wargames, TierMaker, 1d6chan). Added two panels in reports/tiers/panel/: 2026-08-29-leagues-of-votann-auspex.json (Auspex Tactics' 11th-edition army overview, three bands standout/solid/niche as A/B/C, 18 units, from Jordan's summary) and 2026-05-14-leagues-of-votann-astrategas.json (Astrategas Wargames, Spanish, 10th edition, S to D, all 22 non-Crucible datasheets, read from the auto-captions; five letters read from context). Both use: reference as the ledger session asked; whether Votann rejoins the pool is the ledger's yardstick step (T-691). Our words only: names, letters, our paraphrase.
T-693done5 Oct
Unit popularity since 11th edition, from the tournament data we may use (Jordan, 5 Oct: Listhammer "would be very helpful for finding out unit popularity since 11e"; then "is there any issue with taking grim corpus? … just do that for now")
More
Done 5 Oct: Listhammer itself stays off (no licence, robots.txt; T-411, the email waits on Jordan, T-418). tools/popularity.js reads every source switched on (grimstat-corpus's top-level files, CC BY 4.0, latest 570b8c6; listhammer/ not read) from events on or after 20 Jun 2026 (earlier June events are 10th edition's farewells; the first V11 event is 27 Jun) and writes reports/tiers/2026-10-05-unit-popularity.md/.json: per army lists and top-quarter lists, per datasheet lists that take it, copies, top-quarter lists. 487 lists, 30 events, 27 Jun to 26 Sep, 33 armies; 102 lists left out (under 85% of units matched). Mostly French events, small samples. Counts only; the site's data and the ledger's pinned copy untouched.
T-722done5 Oct
Obvious ways to collapse what opens (Jordan, 5 Oct, a screenshot of a Tyranid list under the card: "put a chevron to collapse this list in the top banner area, I always struggle to find it. As a general rule it should be very obvious how to collapse the banners")
More
Done 5 Oct: the list under a desktop card has a top bar, the list's name and "Hide list ▴", that folds it (layout.js desktopList, [data-listhide]); each open part of a card (Weapons, Leader, Detachment, Modifiers, the defender's Stats) has a round ▴ at the right of its title bar beside the Close bar at its foot (T-486). The phone list sheet already ends in Done (T-480). tests/browser/closers.test.js (both new controls); screenshots desktop and 390px, light and dark. Also: tests/browser/marks.test.js no longer gives a Space Marines army Saga of the Bold (T-688 drops another chapter's detachment), the Codex rule stays covered by unit/marks.test.js.
T-723done5 Oct
A Helbrute can't take both a hammer and a power scourge (Jordan, 5 Oct, screenshots of the calculator and the datasheet: "the hellbrute isnt actually able to take hammer and scourge?")
More
Done 5 Oct: BSData raises it as an error modifier ("cannot have both a hammer and a scourge"), which bsdata.js didn't read. Now an error whose conditions are all "at least 1" of named entries makes them a pair one model can't carry (choices' excl, which loadout.js already enforced for T-364's pairs): 10 units gain pairs (Helbrute; Aggressor Squad; Lychguard; Cthonian Beserks; the Exalted Champion, Sorcerous Champion and Soul Forge Tyrant [Crucible] baleflamer/autocannon; the Inspiring Devotee, Militant Commander and Reliquant Knight [Crucible] heavy flamer/multi-melta). Auto-equip, Best matchup and the Weapons sheet's − and + follow (adding a scourge swaps the hammer out); nothing else in build/units.json moved. tests/unit/loadout.test.js. Not done here: the published rankings and unit pages still show the old best loadouts until the rankings are rebuilt (a rebuild today moves 1,301 units, for older reasons: Jordan's call); the ledger keeps its frozen copy until a re-freeze. BSData's 20 "cannot have both" error rules (catalogue | entries | its message, at the site's pin 8b91e6b): Chaos Space Marines | Baleflamer + Helstalker autocannon (one model); Death Guard | Helbrute hammer + Power scourge (one model); Adepta Sororitas | Ministorum heavy flamer + Multi-melta (one model); Astra Militarum | 2 Recon Troopers w/ Long-las + a Weapons Team (squad); Black Templars | Emperor's Champion + Anointed Emperor's Champion (army); Blood Angels | max 1 pistol replacement (squad); Grey Knights | Gatling psilencer, Heavy incinerator, Heavy psycannon, Sublimator: at most 2 of these (squad); Space Marines | Aggressors: Auto Boltstorm Gauntlets & Fragstorm Grenade Launcher + Flamestorm Gauntlets, no mixing (squad); Space Marines | Vanguard Veterans: every model replaces its weapon and bolt pistol (squad); Space Wolves | Master-crafted power weapon and storm shield + paired master-crafted power weapons, all the same (squad); Leagues of Votann | Thunderkyn: bolt cannon, graviton blast cannon, SP conversion beamer, all the same (squad); Leagues of Votann | Cthonian Beserks: concussion maul + heavy plasma axe, all the same (squad); Leagues of Votann | Yaegirs: bolt shotgun + revolver and knife, all the same (squad); Necrons | Gauss blaster + Tesla carbine, all the same (squad); Necrons | Lychguard: hyperphase sword and dispersion shield + warscythe, all the same (squad).
T-515done5 Oct
Search snippets: titles and descriptions that fit, structured data Google reads (the design session's notes, 1 Oct 07:06Z and 07:10Z)
More
Every indexed page (1,544) now has a title of 60 characters or fewer and a description of 140 to 155, none shared: unit pages pick the longest title pattern that fits ("{unit} 11th Edition: Points, Best Loadout, Damage", then shorter) and lead the description with the points; winning lists, detachments and the guide get short titles; the home page is "40k Damage Calculator (Mathhammer), 11th Edition" (Jordan's pick). JSON-LD: a WebSite with our publisher logo (icon-512.png) and a BreadcrumbList on every page (Home first; the tab pages Home › tab), an Article on /mathhammer/ and /about/. Each army's own share image (docs/icons/og/<army>.png, our figure on our card, tools/png.js) on its faction, unit and detachment pages. Test: unit/seo (lengths, unique descriptions, valid JSON-LD with one WebSite and one BreadcrumbList, breadcrumbs that follow the address); pages and newlayout follow the new titles, and the machine drafts (i18n/drafts) the new title shape. The sitemap is resubmitted by Monday's weekly analytics run (5 Oct, 8:07 am Pacific [15:07 UTC]), after these pages are live. Before → after (title/description characters): / 72/142 → 48/152; /army/ 57/178 → 45/149; /lists/ 60/128 → 51/147; /mathhammer/ 76/147 → 51/147; /about/ 57/137 → 57/150; /meta/ 61/151 → 56/151; /factions/space-marines/ 62/145 → 54/145; /factions/space-marines/intercessor-squad/ 57/182 → 60/153; /factions/adepta-sororitas/detachments/bringers-of-flame/ 78/153 → 47/153; /best/anti-tank/ 60/156 → 60/151; a winning list (/lists/adepta-sororitas-1st-fwc-…/) 115/168 → 53/152
T-633done4 Oct
Kill in the turn a unit arrives forward (Jordan's go-ahead, 3 Oct; T-632 candidate 1)
More
Switch arrivalKill, off: a unit that can be placed forward (deep strike, tunnels, infiltrate, scouts, re-arrival) gains its Kill in the turn it arrives, × the chance it survives to strike, in place of arrival as Presence reach. One ledger step with a written prediction; judged by the keep rule, after a re-fit, and on units held out (tools/diagnose.js's folds). Tyranids first. Done 4 Oct: ledger step 18, reverted (fixed ρ 0.472 → 0.464; re-fitted −0.006; held out on units −0.4 as is, −1.4 re-fitted). Switch stays off; tools/diagnose.js --on <switch> judges any switch on held-out units.
T-634done4 Oct
Scarce answers: the army's loss without the unit (Jordan's go-ahead, 3 Oct; T-632 candidate 2)
More
Switch scarceAnswers, off: the army's Kill into each target class with and without the unit, weighted toward the classes few of its units can reach; the gap the unit leaves, not Kill ranked by job (T-626 threw away how much a job is worth). One ledger step, judged as T-633. Done 4 Oct: ledger step 19, reverted (fixed ρ 0.472 → 0.380, Tyranids −0.130; re-fitted and held out agree). The step bundled two changes (gap in, role scarcity out); with role scarcity off on both sides the gap alone moves 0.382 → 0.380, so the loss is dropping role scarcity and the gap is worth about nothing by per-100-points Kill. Switch stays off.
T-635done4 Oct
T-632's diagnosis pooled over all 16 armies, tested leaving one army out (Jordan's go-ahead, 3 Oct)
More
About 733 graded units through the same variables, per-army scales; does any flexible model beat the ledger on an army it never saw, and what does it key on across armies. A report; no ledger number changes. Done 4 Oct (reports/tiers/2026-10-04-pooled-diagnosis.md, node tools/diagnose.js --pooled): leaving one army out, every model 58–63% (ledger 60.0%, trees +2.2 ± 2.2 per army, inside noise); the learning curve over 3 to 15 armies is flat; pooling doesn't help Tyranids. Within an army a reviewer's taste is learnable, across armies it isn't. Signals that travel: Kill itself, Kill into elite/monster/vehicle classes, a floor on Score; transports and melee Soak as corrections.
T-636done4 Oct
Every datasheet variable in the diagnosis's list (Jordan's colleague, 4 Oct: "any variable we haven't added to be added to the list ... all unit profile items, weapons, etc.")
More
Audit tools/diagnose.js's 144 variables against every field the unit records carry (build/units.json: stats, keywords, size, points, weapons and their keywords, core rules, abilities, choices, limits, leads, models, damaged, fnp) over all 16 armies; add every missing one (e.g. Vehicle/Walker/Mounted/Aircraft keywords, transport capacity, size range, anti-X targets and thresholds, keyword values, Lance, Hazardous, Rapid Fire, Firing Deck, weapon and ability counts) or say why it's left out; a coverage table in the report; re-run the Tyranid and pooled diagnoses on the full list. A report; no ledger number changes. Done 4 Oct (reports/tiers/2026-10-04-variables.md): 144 → 341 variables; every record field is covered or left out with a reason (names, ability text, faction keywords, tags no graded unit carries). On Tyranid units held out nothing beats the ledger (74.5% on the headline lists); leaving one army out the flexible models rise (interactions 59 → 66%) mostly by recognising Space Marine datasheets across the four Marine armies; on the 12 others the gain is 1.4–3 points, within noise counted by pairs. The same units are missed as before, so no datasheet field explains them. --base-vars reproduces the 144-variable runs.
T-637done4 Oct
Transports counted only when dedicated, then the transport slider set (T-629's 58.1% finding; Jordan's colleague, 4 Oct)
More
Switch carryDedicated, off: Carry counts only Dedicated Transports, not walkers or monsters that can carry; one step with a prediction, judged by the keep rule, re-fit and held-out units, plus the 16-army mean. Then the slider's setting as its own step. Done 4 Oct: steps 20 reverted, 21 skipped (dedicated only at 1: ρ 0.472 → 0.455, both armies down, re-fit and held out agree; 16 armies 57.7% → 58.1%; the Rhino overshot to 1st of 84). The slider stays at 0.
T-638done4 Oct
A floor on Score (T-635: units at the bottom of their army for objective bodies lose about half a letter)
More
Switch scoreFloor, off: Score as a floor/saturating curve rather than linear, constants set by reasoning before the run; one step, judged as T-637. Done 4 Oct: step 22 reverted (ρ 0.472 → 0.461; re-fitted flat 0.464 → 0.462; held out −0.8; 16 armies 57.5%): the cap on hordes moved the cheap Tyranid bodies the wrong way.
T-639done4 Oct
Kill weighted by the class it lands in (T-635: Kill into elite infantry, monsters and vehicles travels across armies)
More
Switch killClassWeight, off: Kill per class × a class weight (elite, monster, vehicle above chaff and lone characters), added on top of role scarcity (one change only); one step, judged as T-637. Done 4 Oct: step 23 reverted (Tyranids unchanged: role scarcity already tilts them past the weight; Marines −0.018; re-fitted 0.464 → 0.456; 16 armies unchanged).
T-640done4 Oct
The fuller Tyranid transcripts as comparators (batch 3; Jordan's colleague, 4 Oct)
More
Replace three Tyranid panels' summaries (Auspex, HivemindHobbies, Into the Hive Mind) with batch 3's fuller transcripts (reports/tiers/sources/2026-10-03-tier3-other-factions.csv), as auspexFull did for Marines; a yardstick step, the old panels kept as superseded. Done 4 Oct: step 24 kept (yardstick). The headline is now Tyr auspexFull/hivemindFull/secondFull/maelstrom/astrategas + SM auspexFull/tobias: step 6 reads ρ 0.490, pairwise 70.8% (Tyranids 75.4%, Marines 59.2%); re-fitted 0.486; held out on Tyranid units 74.5% (re-fitted 71.8%) against a ceiling of 90.4%. The summaries stay selectable (diagnose.js --summaries, ledgerstep SUMMARY_LISTS).
T-642done4 Oct
Burst: the chance a unit finishes a target in one activation (Jordan's colleague, 4 Oct: "burst damage is key ... if you can't kill a unit in a burst, they will run, hide, shoot back with themselves and all their friends"; Exocrine, Tyrannofex, Maleceptor, Haruspex, Genestealers, Raveners)
More
Kill today is average wounds × points per wound, so half a Carnifex counts half and a reliable gun equals a swingy one of the same mean. The rule (the colleague's): a MONSTER or VEHICLE target counts only when the whole unit is killed (a bracketed one still fights); any other unit counts when brought below 25% of its starting models. Burst = the points a unit finishes in one activation from the dice distribution (not the mean), against the typical army. First a read-only check (step A): do the units the reviewers rate above the ledger have unusually high burst for their average Kill, and the over-rated ones low? Pre-registered before looking. Then, if it holds, a switch burstKill (off), one step judged as the others; and later the same rule on the defending side (Soak counts only enemy activations that can finish the unit). Step A done 4 Oct (reports/tiers/2026-10-04-burst-check.md, tools/burst.js, exact dice walk): on Tyranids it doesn't hold as pre-registered (residual vs Burst-over-Kill ρ 0.08, p 0.29; 4 of 10 named units moved as predicted; Burst and Kill rank Tyranids alike, ρ 0.87). Not pre-registered: on Space Marines it does (ρ 0.26, p 0.009), and a preview with Burst in place of Kill reads Tyranids 75.4% → 75.4%, Marines 59.2% → 63.9%, seven lists 70.8% → 72.1% (ρ +0.026, under the bar). The switch waits for the colleague's call. Step B done 4 Oct (reports/tiers/2026-10-04-burst-defend.md, tools/burst.js --defend): the defending half doesn't hold on Tyranids either (residual vs Soak-to-Answer-Soak shift ρ −0.04, p 0.62); the burst-and-hard-to-answer combination moves 5 of 7 named units the right way but not across all 52 (ρ 0.13, p 0.18); the Hierophant (6th → 1st) and Harridan fell into step 12's trap as predicted. Previews: Answer Soak 70.8% → 69.5%, both halves 71.6%, Burst alone 72.1%. Neither half built; the one live lead is step A's Burst on Space Marines. Steps A and B done; the attacking half became T-644 (step 25, confirmed).
T-643done4 Oct
The 11th-edition mission deck in our own words (Jordan's colleague, 4 Oct: "rewrite any secondary rules or primary rules into our own words for the repo")
More
design/mission-notes.md from Wahapedia's mission-deck-2026-27 page: every primary (the 25) and every secondary card (tactical and fixed): what scores, how many VP, when, the caps, and which kind of unit can earn it (kills, positions, actions, bodies). Our own words and numbers only, never the cards' wording. Unblocks T-641. Done 4 Oct: design/mission-notes.md: 18 secondary cards (4 also fixed) and 25 primaries; tactical deck by kind kill 28% / position 39% / bodies 22% / action 11%; about 30 secondary VP a typical game (reasoned, 20–40); six disagreements with tools/dispositions.js listed, not fixed.
T-644done4 Oct
Burst as a ledger switch (T-642 step A's preview: seven lists 70.8% → 72.1%)
More
Switch burstKill, off: Kill read as Burst (points finished in one activation, tools/burst.js) in the natural mode's slot, rescaled so the median form's Kill is unchanged; one step (25) with a prediction, judged one at a time. Done 4 Oct: step 25 confirmed, the new baseline (tools/ledger-benchmarks/2026-10-04-step25-baseline.json): seven lists 70.8% → 72.1%, re-fitted and left out agree, 16 armies 57.8% → 59.2% (the 14 never fitted 56.4% → 57.7%).
T-645done4 Oct
Toughness that counts only with threat (Jordan's colleague, 4 Oct; T-642 step B: the Tyrannofex is 1st on Soak but 25th overall because the fitted weights give durability almost nothing, held down by tough units with little threat: Toxicrene, Tervigon, Hierophant)
More
Switch threatSoak, off: Soak counted only in proportion to the unit's threat (its Kill and burst against the field), a stronger and sharper version of the threat-weighting slider; one step (26), judged one at a time. Step 26 kept provisionally 4 Oct on top of 25 (72.1% → 72.3%, every list up a little); confirmation waits for the fitter to handle threatSoak. Confirmed 4 Oct (re-fitted, left out, held out and 16 armies all up a little over step 25): baseline tools/ledger-benchmarks/2026-10-04-step26-baseline.json.
T-646done4 Oct
How each step moved the units (Jordan's colleague, 4 Oct: "if things are more correct now for some but others moved up it doesn't mean it wasn't a step in the right direction")
More
tools/stepmoves.js: for any switch (or a saved step), every unit's rank before → after against the reviewers' consensus: closer, farther, by how much, the units that moved most each way, and a count of units now in the right tier; a report for steps 18–23 and the burst preview, and run with every new step from now on. Done 4 Oct (tools/stepmoves.js; reports/tiers/2026-10-04-step-moves.md). Steps 18–23 under the provisional keep rule: none qualifies (aimed units nearer: 18 4/15, 19 7/13 but 35 farther vs 24 closer and the gap grew, 20 2/7, 22 1/7, 23 1/5), so none is replayed. The burst preview is the one step that brought the field nearer the reviewers overall (gap 18.1 → 17.4, all of it Space Marines); it is step 25.
T-441done2 Oct
Our own glyphs for ranged and melee (Jordan, 30 Sep, via the design session; main)
More
Two 9-pixel single-colour glyphs in the icons.js style (a crosshair or bolt, a blade), currentColor, 10–12px, in place of 🎯 and ⚔ in the ranking tables' number cells, the calculator's weapon chips and the toggles row. Add to icons.js; a test that no static page or calculator chip still holds those two emoji Done 2 Oct: most of it had already gone with T-513/T-514 (the toggles, the weapon list's headings and pills, the static pages' Best tables, i18n marks drawn by Icons.label()); the rest of the calculator's markup now draws our glyphs through app.js gly(): the To the death key, the charts' "each weapon's part" tooltip (bow and swords), the weapon strip and the old weapon row (both hidden in the one layout, done anyway), and the list comparator's lines (target and swords; its 👑 lead tag as our crown). Left: the battle canvas's "⚔ n" counter over a charging squad (canvas text, not markup), the welcome's ⚔️ in a sentence (T-514's list for Jordan). Test: unit/noemoji.test.js (no generated page outside the Lab, and no line of app.js, layout.js, picker.js or rankings.js outside comments and the no-icons fallback, holds 🎯 or ⚔). Screenshots: t441-death-*/t441-share-* (dark and light, 390px and desktop)
T-557done2 Oct
Tier test round 6: Jordan's settings for the open constants and the detachments' movement rules read in full (Jordan, 2 Oct, via the supervisor; child of T-524)
More
Built 2 Oct: tools/tiertest.js --round6 <file> (--round6-note, --r6json), four switches on top of T-552's every-army run, each alone and cumulative: cp0 (a CP worth V6C.CP_PTS = 0; T-558 to price it), tmax (the tier test's own hits-to-destroy at a mixed unit's highest Toughness, matching T-546), pos (the position track: the delivery modes followed round by round; Anvil × 0.5 to 1 by the share of the game it holds the midfield and enemy-half objectives, Runner's reach from the same track over those and the enemy's deployment zone, replacing Utility's fixed reach; V6C) and mv (the detachments' move keys read in full, so tunnels, surge, a Deep Strike grant and advance-and-charge feed Hammer's turns in reach, Anvil's position and Runner's reach; Crusher Stampede's OC and the Norns' re-pick read too; Feed the Swarm through the heal closed form on the squad a Harvester helps most). Battle-shock 10% and debuffs 50% kept, marked tunable in scoring.md; Oath at full share and army rules on (Jordan confirmed). Numbers (round 5 → 6): Tyranids ρ Auspex 0.37 → 0.34, same letter 13 → 12, within one 23 → 26, Tyranid list share 0.28 → 0.27; every unit's list share 0.155 → 0.169; Space Marines −0.06 → −0.03, Orks −0.14 → −0.16; 146 of 1,161 letters move. The position track alone: 0.41 and 0.36; tunnels on top hold 0.40 (list share 0.36); Crusher Stampede's OC (0.35) and Feed the Swarm (0.38) cost agreement; the movement rules in Hammer only give 0.27 (the old what-if's 0.28). Biggest movers: Pyrovores D → B and Ripper Swarms C → B (Feed the Swarm, Banner), the Psychophage, Mawloc, Maleceptor and Trygon up a letter (Crusher OC), Biovores C → B (tunnels); the Hive Tyrant B → C (CP 0), the Parasite of Mortrex B → C (Runner's reach). Verdict: keep the movement reading through position; recheck Crusher Stampede's OC share and Feed the Swarm's lent heal (Jordan's read). Report: reports/tiers/2026-10-02-tyranids-round6.md; a Round 6 section in reports/tiers/2026-10-02-tyranids.md. Round 6b (Jordan, 2 Oct: "a CP gained or saved is worth 30 points for now"):V6C.CP_PTS 0 → 30, --r6yard (the yardsticks as JSON); ρ Auspex 0.34 → 0.39, same 12 → 12, within one 26 → 27, Tyranid list share 0.27 → 0.32, every unit's list share 0.169 → 0.171, Space Marines −0.03 unchanged; letters: the Hive Tyrant C → B, the Swarmlord B → A, the Lictor B → A (the Winged Hive Tyrant and Hyperadapted Raveners stay D). T-558 stays open as the method. Report: reports/tiers/2026-10-02-tyranids-round6b.md.
T-554done2 Oct
Oath of Moment starts switched off in the calculator (Jordan, 2 Oct, answering T-548's open question)
More
Built 2 Oct: every mark starts off on the defender's Marked chip, Oath of Moment's included (T-548 started an army rule's mark on); the chip and its effects work the same when ticked. app.js MARK_ON now reads "hre.mark.v1": "on" (the old default, a test's { marks: true }); tests/run.js no longer sets "off". Default matchup (5 Intercessors vs 10 Termagants) back to the datasheet's numbers: 9.6 slain (23.4 hits, 15.7 wounds) against 10.0 (31.1 hits, 25.9 wounds) with Oath on. Live rankings and the tier test unchanged. Tests: browser/marks (now checks the default leaves Oath out).
T-552done2 Oct
Every army scored in the tier test, not only the Tyranids (child of T-524; the supervisor, 2 Oct, from the coverage re-run)
More
Built 2 Oct: tools/tiertest.js --every: each unit of the pool scored as a Tyranid is, four switches cumulative (ev own, kinds, army, det): its own abilities (unitfx.json, a chapter's from the Space Marines' entry), the engine kinds (heal, mortal per unit, auras on the squads its army takes most, Damaged X, T-545's vocabulary), its army rules through DetFx (a chapter's with the Space Marines'; Synapse's pilot shape stays variable 5's; Oath's Codex +1 only outside the excluded chapters; a pick's best option per target and attacker) and its army's best detachment (the Context rule, picked against the bare pool). --own-kinds (the Tyranids' own abilities and every kind in one run, a row of the Engine kinds table), --v5dump (every unit's V5 letter, job, jobs, percentiles, detachment: reports/tiers/2026-10-02-v5-every.json); --tyr4-out keeps hand-appended sections on regeneration (writeKeeping; regenerated, the Tyranid file is unchanged but one blank line). No constant changes. Numbers (base → every switch on): Tyranids ρ Auspex 0.39 → 0.37, same letter 17 → 13, within one 30 → 23, Tyranid list share 0.24 → 0.28; Space Marines ρ −0.01 → −0.05 (0.07 with own abilities only); Orks −0.12 → −0.14; every unit's list share 0.129 → 0.156 (0.173 with army rules, before detachments); 423 of 1,161 letters move (197 up, 226 down): Space Marines 45 up, Tyranids 27 down, Chaos Daemons 23 down, Imperial Knights 19 down. Report: reports/tiers/2026-10-02-tyranids.md, "Every army scored".
T-551done2 Oct
Buffs from another unit in the calculator: the Bondsman duties, the War Dog auras, marks lent by a third unit (the supervisor's brief, 2 Oct)
More
Built 2 Oct: a Nearby pick in the Army part of each side's card (layout.js, data-near): one unit of the side's army whose aura reaches its unit or that lends a mark (detfx.js nearBearers, nearEffects); its effects are ⚡ chips like a joined character's aura, and the Army line carries "Near X" (opens the Army part); kept per army and unit in hre.det.v1 ("near\|army\|unit"). kindfx.json: numbers for 11 Imperial Knights auras (the Bondsman duties that are dice, to an ARMIGER within 12"; Ion Aegis), 7 Chaos Knights War Dog auras, and a new field lend (a mark for friends, against the marked unit only) on 23 rules: Guide, Doom, Death Hex, Blight Bombardment, Multi-spectrum Array, the three Storm Speeders, Symbiotic Targeting, Bio-stimulus, Burning Spray, Crystalline Targeting, Withering Hail, Flush Them Out, Guidance of the Ancients, Priority Target, Crossfire, Survey Augur, Murderous Crossfire, Rites of Castigation, Bust 'Em, Advanced Scouting, Targeting Relay. 15 Knight unit pages gain T-547's aura line (shown, not scored). Tests: unit/nearby, browser/nearby. Tyranid table: nothing moves; live rankings unchanged. Left: enemy debuffs from a third enemy unit (the defender's Nearby does the defender's own auras, enemy-side ones included), range.
T-550done2 Oct
Tests a unit takes on itself: Dark Pacts' Leadership test, the Thousand Sons' Psychic test, Hazardous as the same kind (the supervisor's brief, 2 Oct)
More
Built 2 Oct: one shape, selfTest { on: d6 / ld / double, fail, mw }, in ev.js (selfTestFail, selfTestCost; hazardPer now through it, the same numbers; a line's selfTest with opts.selfTests, added to its hazard) and detfx.js (selfTests: a unit's own rules and its army rules, whatever their kind). kindfx.json: Dark Pacts (2D6 under its Leadership, D3 mortal wounds) and Cabal of Sorcerers (a double, D3). Calculator: a chip, in play while the rule's dice effects are (a Pact taken, a Ritual cast), and a line under the answer beside the hazard rolls: "Dark Pacts: its own test fails 28% of the time, 1.1 mortal wounds to itself on average" (Legionaries, Leadership 6+, shooting and fighting). Not in To the death's turns yet. Tests: unit/selftest, browser/selftest. Tyranid table: nothing moves; live rankings unchanged.
T-549done2 Oct
Resource pools as a stated flat value: Miracle dice, Fate dice, Pain tokens, Yield points, the Blessings' dice, the Cabal's rituals (the supervisor's brief, 2 Oct)
More
Built 2 Oct: a rule that replaces one roll with a chosen result once a phase is the mod swapSix, valued by one rule: the expected gain of the volley's worst die of the best roll turned into a 6 (ev.js swapGain: 1 − (1 − q)^n times one success of that roll carried to damage; hit, wound, or the target's save). ev.js gives the attacker's swap to the weapon and roll that gain most, the target's to the save of the weapon that costs most; the dice engine puts it on the volley's first failed roll of the roll sixStage picks (the target's on its lowest save). kindfx.json: Acts of Faith (every Adepta Sororitas unit) and Strands of Fate (reach AELDARI), each an attacking and a defending effect, on by default; pool on Power from Pain and the 14 (Pain) abilities with effects, Prioritised Efficiency, the Arkanyst Evaluator, Hearthfyre Arsenal's Optimal Application, Blessings of Khorne, Cabal of Sorcerers, Morvenn Vahl: the hand pick stays, the chip carries a small "pool" and its tip "needs the pool: …". A pooled die is rolled when gained, so the 6 is its best case (scoring.md says so). Tests: unit/pools (closed form, dice against it, the data). Tyranid table: nothing moves. Live rankings unchanged.
T-548done2 Oct
Marks on an enemy unit: Oath of Moment's target, Doom and Guide, the Norns' prey (the supervisor's brief, 2 Oct, from the coverage reports' gaps)
More
Built 2 Oct: detfx.js conditions chosen (the target is the unit the attacker's rules marked; ctx.marked, unknown: the player's call as before) and codex ({ dets, not }: Oath of Moment's +1 to wound, by the 2 Oct errata reading, with a Codex detachment, or none picked, and no Blood Angels, Dark Angels, Deathwatch or Space Wolves unit in the list or army). kindfx.json patches 42 effects to chosen (Oath ×2, the Norns ×2, Guide, Eldrad's Doom, Death Hex, Marked by Fate, quarries, preys, the Oath-target unit and detachment rules, For the Greater Good's Spotted unit; their other words stay the player's) and Oath's effects start on. Calculator: a Marked chip on the defender's Modifiers line while the attacker has such a rule, on by default for an army rule (Oath), off for a unit's own; a tap holds for the pairing this visit. "hre.mark.v1": "off" starts every mark off (the tests' default; { marks: true } for the site's). Tests: unit/marks, browser/marks; browser/newlayout's Mods line now counts only chips in play (it had broken when T-542 put Oath on Intercessors). Tyranid table: nothing moves (Singular Purpose stays tierfx.json's). Live rankings unchanged (they load no kindfx.json). Left: marks the defender puts on the attacker (Misfortune, Mind Control, the Masque's dance), Harbingers' Doom (a Battle-shocked target, not a mark), enhancements.
T-547done2 Oct
An aura's worth, measured by the engine, on unit pages and in Banner (T-540's "left for later")
More
Built 2 Oct: new tools/aurafx.js plays each coverage aura (unitfx.json's aura object; effects from the rule or kindfx.json) on every squad of the bearer's army it reaches (keywords; range taken as met), against the squad without: damage into the five standard targets and hits to destroy from the five standard attackers (cover only against shooting), and the bearer's points as more bodies of that squad. Unit pages: a ✨ line in Utility, "Aura, Foul Spores: +76% hits to destroy for Tyrant Guard, the most of any squad it reaches (20 in its army)", on the bearers whose aura has effects (10 pages today: the four Tyranid bearers and the coverage's newer ones); the Support score doesn't count it yet. tools/banner.js: an aura with effects is measured (its 2 best squads by multiplier vs bodies, × 0.5 in range, AURA_IN_RANGE) in place of the proxy, which stays for the rest (3 characters measured: the Blue Scribes, the Hive Tyrant, the Tervigon); ρ among characters 0.060 unchanged. The dated banner report and its json (an input of rounds 2 to 5) aren't regenerated, per the brief. Test: unit/aurafx.
T-545done2 Oct
The effect vocabulary the coverage keeps hitting (the supervisor's brief, 2 Oct, from T-542's coverage reports)
More
Built 2 Oct: detfx.js, app.js and ev.js, each with unit tests (unit/vocab, unit/kinds) and a browser test (browser/kinds "vocabulary"). New mods: tMinus (the target's Toughness −N, not +N S: S4 against T9 wounds on 6s, not 5s), tPlus, svUp (never past 3+), rrOneHit / rrOneWound (one die a volley, shared by its weapons, fresh each turn), tMajority (most of its kinds of model; BSData has no counts, so each kind counts once). New conditions: sVsT gt/ge/lt/le and rapid. kindfx.json gains effects for noted rules, extra, patch (by label) and reach (army rules). Army rules (armyfx.json) are now chips in the calculator for a unit whose datasheet lists them, or that they reach, each starting off, a choice picked in the Army part. What each unlocks (kindfx.json): tMinus 5 rules (Death Guard's Nurgle's Gift, from +1 S; Nurgle's Rot ×3: both Great Unclean Ones and Scabeiathrax; Kaptin Badrukk's Ded Glowy Ammo); tPlus 3 (the Tyranid Warriors' Adaptive Instincts option, the Datasmith's Aegis Protocol, Haloscreed's Microactuator Bracing); svUp 1 (Take Cover!, reaching every Astra Militarum unit through reach); rapid 1 (First Rank, Fire!); one-die re-rolls 7 (Ascended Daemon, Targeting Array ×3, Crystal Matrix, Called Shots, Ironstorm's Armoured Wrath); sVsT 24 conditions checked now instead of vouched for (15 Gloam Rot, the Barge, Lychguard, Wardens, R'varna, Wave Serpent, the Lion, Wolf Guard Terminators, the Painboss; Lysander's "at least"); tMajority 1 (Kill Teams, which waits on T-546 to matter). Left noted: auras of +1 T on other units (the Great Unclean Ones'), enhancements (Talisman of Burgeoning, It Came from da Drops, Stalwart's Honours: not in the calculator yet), "one roll becomes a 6" and "one attack's damage cancelled" (other kinds). The coverage files' statuses aren't flipped (not ours to edit): the coverage builder can mark these rules modelled. Tyranid table re-run: nothing moves (Engine kinds section). Live rankings unchanged.
T-544done2 Oct
Damaged X from the datasheet (T-542's note: the Orks' new codex makes it a core ability, not listed per unit in our data)
More
Found: BSData has it: the Orks' datasheets link the core rule "Damaged" with X appended to its name (Battlewagon 6, Stompa 10), and older datasheets carry a "Damaged: 1-X wounds remaining" ability. Built 2 Oct: bsdata.js reads X (damaged, the number only; a bracket is taken only when its effect is -1 to hit, so the C'tan's and the Canoness's aren't), 481 datasheets. In the calculator: a chip on each side whose unit has it; its own attacks take -1 to hit (inside the ±1 cap; psychic attacks and "ignores hit modifiers" skip it) while the squad's last model has X wounds or fewer: Fight back's reply and To the death's later rounds, and the played-out fight; never a first volley. The coverage's hand-set brackets (unitfx.json, off) give way to it. ev.js damagedAt(W, X, T) for the durability-over-turns maths; the tier test's kdmg (--kinds) scales a single model's later turns: Tyranids ρ 0.38 → 0.41 alone, 0.39 cumulative; Toxicrene and Haruspex A → B, Trygon A → C (an upper bound: the pool's other armies aren't re-scored with it yet). Not in the live rankings (one volley at full wounds). Not yet: a squad's wounded model before its last (the engine keeps one wounded model a group), OC. Tests: unit/damaged, browser/kinds "damaged". Follow-up (the supervisor, 2 Oct): where the coverage data carries a unit's own damaged bracket that isn't the usual -1 to hit (Skarbrand's +2 Attacks), the generic reading yields to it: no Damaged chip or -1, its own chip stays (detfx.js ownDamaged; the tier test's kdmg too); Skarbrand's BSData bracket isn't read anyway (it adds attacks). Test: unit/damaged's Skarbrand case.
T-543done2 Oct
The Custodes' detachments in the data build (T-542's note: BSData lists none at our commit, so the calculator offered none)
More
Found: the data is there. Every army's catalogue names its root entry "Detachment"; the Custodes' names it "Detachments" (plural, one level of group deeper), and bsdata.js detachments() matched the singular only, so the build (and the browser's own read) skipped them. Fixed 2 Oct: the reader takes either spelling. detachments.json gains the 9 Custodes detachments with their Detachment Points (Shield Host, Talons of the Emperor, Null Maiden Vigil, Auric Champions, Solar Spearhead, Lions of the Emperor, Might of the Moritoi, Silent Hunters, Tharanatoi Hammerblow), which are the 9 detfx.json carries, so no "ours alone" fallback was needed; their detachment pages now build. No ranked number moves (the rankings read no detachment). Test: browser/detachments "the calculator offers the Custodes' detachments"
T-520done2 Oct
Patch pages and clash pages, noted for later
More
Replaced 2 Oct by T-521 (the balance-update page) and T-522 (the first clash pages), once Jordan said yes (1 Oct (Pacific), via the design session: "Sure, card it").
T-422done1 Oct
BSData spells a detachment "Haloscreed Battleclade"; its name is Haloscreed Battle Clade (found in T-421's pass)
More
Fixed 1 Oct: renamed where it comes in from BSData (BSData.DET_SPELLING in bsdata.js, so detachments.json and every place it shows follow), detfx.json's key with it; picks saved in hre.det.v1, old det= links and lists naming it the old way (any case) map to the new name in app.js (detCanon); its page keeps the address /factions/adeptus-mechanicus/detachments/haloscreed-battleclade/ (build_pages.js detSlug). No tournament list or ranking names it yet. Tests: unit/detnames, browser/detachments
T-453done1 Oct
Post-merge audits on the new surface (beta, for review)
More
T-389's accessibility pass on the accordion, docked row, list chips and static header/footer (focus order, aria-pressed on the toggles and chips, 44px targets, Esc closes a part); T-391's dark-mode audit; T-390's performance before/after at 390px; T-393's pseudo-language over tonight's strings; T-395's privacy re-read against the events Done 1 Oct, on main: reports/audits/2026-10-01.md. Fixed: the toggles before SIMULATE in tab order; white text on --att-fill for the toggles and the dock's ⇅; the Modifiers head, count and ✦ on --def-text/--def-fill; hit areas for the Swap pill and the static header and footer links; Dynamic and Classic 24px. Clean: aria-pressed and aria-expanded, Esc, labels, pseudo-language, privacy. Carded: T-502 (the effects' cost on a slow phone)
T-455done1 Oct
T-316 dry run (beta, for review)
More
Run tools/report.js as Monday's report would; check /meta/ and the app's Overview render the first week's counts; fix anything that would embarrass the 5 Oct run 30 Sep: main's node tools/report.js runs clean (coverage 5 events and 32 winning lists in Sep, the goals still to add, the cards by status, the last analytics); the players column and the week's counts on /meta/ come from beta's report (T-398), so the rest waits for the merge (T-452) Done 1 Oct:node tools/report.js runs clean and is now the Lab's /lab/reports/ (T-472). The week itself couldn't be fetched from here (the session's network doesn't reach the data Worker; the refresh workflow does), so the real Worker code ran locally on a plausible week (420 matchups, 47 lists through its own /e and /report) and that reply went through build_pages.js: /meta/'s "What players test each week" and /meta/2026-09-28/ render it whole at 390px (attackers, feared targets, matchups, lists, every unit linked with its tier), no overflow, no errors; reverted after. Nothing to fix before Monday 5 Oct (the 06:23 UTC refresh fetches the finished week)
T-456done1 Oct
Static pages' wording pass (beta, for review)
More
T-129's rules over every static page now the header and footer are new; titles and descriptions still hold for the "best loadout"/"best detachment" searches (T-362) after GEQ and the tiles Done 1 Oct, on main: every static page's title and description read by a script. Holding: T-129's points-first unit titles ("Kor'sarro Khan (55 pts) Tier & Mathhammer | White Scars 40k 11th Ed"); T-362's searches (621 unit descriptions open with their best loadout, 273 name a best detachment, 750 the detachment most played; all 165 detachment titles say "Best Units"); GEQ everywhere, no Chaff; no duplicate titles or descriptions. Fixed: "a Adeptus Mechanicus detachment" (now "Luminen Auto-choir (Adeptus Mechanicus detachment, 40k 11th edition)…", no article to get wrong); a cut-short loadout ending "…."; the 49 detachments that lift no unit no longer read "the units it lifts most (Adeptus Mechanicus)" but say what they change. Left as they are (for Jordan): 356 unit descriptions run past 160 characters (their best loadout and tier come first, so a cut loses only the tail), and long event names make 75 winning-list titles longer than 70
T-466done1 Oct
The Lab: a dev index at /lab/ (Jordan, 30 Sep, via the design session; epic, main)
More
Our own pages for revisiting the site's parts, the weapon-effects gallery (T-465) its first entry. After the beta merge (T-452) and T-465, ahead of the rest of the beta-lane queue. Every Lab page: generated by build_pages.js from files already in the repo (never hand-written HTML), so a push updates it; noindex, not in the sitemap or the site's navigation; only our own material (sprites, effects, colours, our reports and docs), no datasheets and no rules text; a corner line with the commit and build time; the static pages' header and footer; phone-friendly; screenshots at 390px and desktop, both themes; one commit per card. CLAUDE.md's layout section gets a Lab line (the design session adds it; the build session's CLAUDE.md edits are stopped by the permission check) and the drift check covers it through build_pages.js. Phase 1: T-467 to T-470; phase 2: T-471 to T-473 1 Oct: phase 1 on main: /lab/ (T-467), /lab/effects/ as a sandbox (T-487), /lab/sprites/ (T-468), /lab/ui/ (T-469), /lab/switches/ (T-470); phase 2 (T-471 to T-473) next in the Lab's queue. The corner line reads the build in the browser, since a commit or time written in would fail CI's drift check Done 1 Oct: phase 1 (T-467 to T-470, T-487, T-492) and phase 2 (T-471 to T-473) are live: board, effects, sprites, UI, switches, motion, reports, docs
T-467done1 Oct
The Lab 1: the /lab/ index (T-466)
More
A tile per Lab page with a one-line description; the effects page (/lab/effects/) first Done 1 Oct:https://tacticalreroll.com/lab/ by build_pages.js (LAB, labIndex()): the static pages' header and footer, noindex (a noindex option on page()), kept out of the sitemap; a tile per page that exists, the effects page first. The corner line reads the build in the browser (the service worker's cache name, a hash of the site's files, and the page's published date), not the commit and build time written in at build: those would change on every build and fail CI's docs drift check. Test: lab.test.js
T-468done1 Oct
The Lab 2: /lab/sprites/ (T-466)
More
Every look (22) in every slot it fields (troop, elite, heavy, walker, flyer, rider, character), the 36 army glyphs (T-375), the portraits (T-371, T-405 to T-408), the hero sprites (T-378), the pixel reactions (T-156, T-381), the five backdrops (T-373), the dice, lockup and icons (T-299 to T-301); each with its name, size and where it's used. Drawn from figures.js and the data files at whole pixels (pixelfit). The glyphs, portraits, hero sprites and backdrops are beta's, so this waits for the merge Done 1 Oct:https://tacticalreroll.com/lab/sprites/ by build_pages.js (labSprites()), drawn from figures.js at build: each of the 23 looks in every slot it fields (name, size, which units take it), the 36 army glyphs, the portraits (behind the Army pictures switch), each hero's stand, two steps and victory, the five backdrops, the dice and icon files. The reactions are named, not drawn: our tumbleweed and headstone are drawn live by the battle, and the meme reactions come from others' GIFs, so they stay off a page of our own art. Test: lab.test.js
T-469done1 Oct
The Lab 3: /lab/ui/ (T-466)
More
The colour tokens in dark and light with names and values; buttons, chips, the four card heads in their three states, the accordion open and closed, the four toggles, the funnel ramp --c0..--c5, the type scale. Real CSS from style.css, so it drifts with the site Done 1 Oct:https://tacticalreroll.com/lab/ui/ by build_pages.js (labUi()): every token in style.css's dark :root and light blocks side by side with a swatch and its value (read at build, so it moves with the file); then the live parts in the site's own classes: buttons and the list chips, the card's four heads empty, holding and open, the four toggles, the funnel's ramp --c0 to --c5 and a type scale. Test: lab.test.js
T-470done1 Oct
The Lab 4: /lab/switches/ (T-466)
More
Each key in docs/data/flags.json with its value on main and on beta (both branches' files at build time, or main's with a note where beta differs), the per-effect switches from hre.fx.v1's defaults, and what each does in one line Done 1 Oct:https://tacticalreroll.com/lab/switches/ by build_pages.js (labSwitches()): data/flags.json's keys (main's file, plus stats, which is unset) with their values and a line each, and ✨ Effects' settings with their defaults read from shootout.js's FX_DEFAULT, labelled as the panel labels them. Beta's file isn't read at build: CI's checkout has only the branch it tests, so its values would make the drift check flap; the page says what "beta" means instead. Test: lab.test.js
T-471done1 Oct
The Lab 5 (phase 2): /lab/motion/ (T-466)
More
The funnel, a volley, a reaction and a docked-row run side by side, with their timings Done 1 Oct:https://tacticalreroll.com/lab/motion/ by build_pages.js (labMotion()): every timing constant in shootout.js with its value and what it times (read at build, so it drifts with the code), and four calculators at a phone's width side by side (a flamer volley, a Genestealer charge, To the death, the docked row), played together by one button. A calculator framed by our own Lab is not counted: no Plausible pageview (index.html) and no counts (stats.js), so looking at the Lab never moves the numbers. Test: unit/md.test.js
T-472done1 Oct
The Lab 6 (phase 2): /lab/reports/ (T-466)
More
tools/report.js coverage, the terms check, metatrack history, the twelve visual-regression screens with baselines, the latest Lighthouse Done 1 Oct:https://tacticalreroll.com/lab/reports/ by build_pages.js (labReports()): node tools/report.js as printed, run at build (coverage, data audits, army tiers, the goals, cards by status, the last weekly analytics, the cross-check); tools/sources.json's sources with their terms; data/metatrack.json's months with each month's most taken armies; the twelve visual-regression screens, linked to their baselines on GitHub (copying them in would go stale each time CI re-baselines). No Lighthouse run is kept yet, and the page says so. Same output on every build (checked). Test: unit/md.test.js
T-473done1 Oct
The Lab 7 (phase 2): /lab/docs/ (T-466)
More
streamline.md, weaponfx.md, leaders.md and sources.md rendered, and a link to the board Done 1 Oct:https://tacticalreroll.com/lab/docs/ by build_pages.js (labDocs(), LAB_DOCS): a page per note (streamline, weaponfx, leaders, sources) rendered at build by our own small Markdown reader, tools/md.js (headings with anchors, lists nested by indent, tables, code, quotes, links; everything escaped first), noindex and out of the sitemap, a tile on the Lab's index, and a link to the board. Test: unit/md.test.js. Screenshots 390px dark, desktop light
T-474done1 Oct
A particle slider for the weapon effects (Jordan, 30 Sep: "turn the total particle effects down for everything or up for everything or select one at a time"; part of the build, T-458 to T-463)
More
Every drawing takes its particle counts through one helper, n(count) = count × the master level × the family's own level, rounded, at least 1 where the effect needs one. In ✨ Effects: a master Particles slider (a quarter to double, 1× the gallery's standard) and a slider beside each family's switch; both saved in hre.fx.v1. A phone that drops frames during a volley turns the master down on its own for that visit (a note says so), and the cap per frame (T-459) still applies. The gallery gets the same sliders so Jordan can try levels before the build In the gallery 30 Sep (round 8): "Particles, all" in the controls and the panel, one per family on each card, saved on the device; they thin or add loose particles, and beams, blobs and shapes stay whole Done 1 Oct: in the battle the gallery's own density (DENS: loose particles thin out or gain neighbours, beams, blobs and shapes stay whole) takes the master level × the family's (particles() in shootout.js), on top of the per-frame budget. ✨ Effects › Weapon effects: a Particles slider (¼× to 2×, live ×n) and one beside each family's switch, kept as wfx.dens and wfx.fd in hre.fx.v1. A phone that drops a third of its frames while effects play halves the master for the visit, and the panel says so (moving the slider takes over again). Test: browser/weaponfx (both sliders kept, effects still drawn at half)
T-475done1 Oct
The calculator's list picker: a mini My army upload and plain rows (Jordan, 30 Sep, via the design session; main, mockup first)
More
Behind the beta merge (T-452) and the Lab's phase 1. The four scrolling tiles under "Your army: which list?" become: (1) a mini My army upload panel on top, a smaller /army/ dropzone (dashed border, hollow, one line of copy, "Choose a file" and "Paste the text", drop works); (2) single-line full-width rows, no horizontal scroll: "My lists · n", "Competitive · 80 top placings this quarter", "Benchmarks · 10 unit types or a whole faction", "Random list · a real tournament list", each with its glyph at one size and a chevron. A paper token, --paper (cream, about #FBF6E6 light; a low-alpha warm tint on dark) and --paper-ink, marks "your list" everywhere: this panel, /army/'s dropzone, the unit card's list chip; cream, not yellow (--lead is the leaders' yellow). States: no list = dashed, hollow, "Upload your list"; a list loaded = the same border filled with paper, the list's name and points ("Necrons · 2,000 pts"), "Change" and "Review" (Review opens My army's rated view). Mockup at phone width, light and dark, both states; build after Jordan's yes Mockup 30 Sep:https://claude.ai/artifact/LAsWtt1Ba4eBgqLHnge5Ue (empty and loaded, the paper chip and dropzone beside the leaders' yellow, a Dark/Light switch); waits for Jordan Approved for build (Jordan, 30 Sep: yes), as mocked upDone 1 Oct: as mocked up. picker.js's list level: a tray on top (hollow and dashed with "Upload your list", one line, Choose a file and Paste the text, and a dropped file uploads; with a list, filled with paper: its name (tap to use it), points, army, detachments and units, Change and Review, Review opening My army's rated view through MyArmy.review); the opponent's side wears its violet instead of paper and says "Upload their list"; then My lists, Competitive, Benchmarks and Random list (and your own list on their side) as one-line rows with a glyph and a chevron. New tokens --paper, --paper-ink, --paper-edge, --paper-strong (light and dark), also on My army's dropzone and your own list's chip. Test: listtray.test.js
T-476done1 Oct
Meta: faction-first (Jordan, 30 Sep, via the design session; main, mockup first)
More
Behind the beta merge and the Lab's phase 1. The army dropdown becomes a large segmented row, All · Imperium · Chaos · Xenos; a group opens a sheet over the dimmed page with its armies as big tiles (the T-375 glyph and name), one tap picks. With an army picked the whole page is that army's: its T-425 panel top and centre, then its detachments, most-taken units, winning lists, tier list, month-by-month trend, and its rank among all armies as the one comparison; the all-armies chart goes. "All" is today's state of the game. The choice sticks (hre.meta.fac) and /meta/?faction= still works. The grouping of the 36 armies (Imperium: Space Marines and every chapter, Custodes, Sisters, Guard, Mechanicus, Knights, Agents, Grey Knights; Chaos: CSM, Death Guard, Thousand Sons, World Eaters, Emperor's Children, Daemons, Chaos Knights; Xenos: the rest) in one data file the static pages and the app share. Mockup at phone width, light and dark; build after Jordan's yes Mockup 30 Sep:https://claude.ai/artifact/VPaQjWZi2PVvV7YtjEEneK (All, the Xenos sheet, Necrons picked; the first phone live; real Necrons figures where the data has them, examples marked); waits for Jordan Approved for build (Jordan, 30 Sep: yes), as mocked upDone 1 Oct: as mocked up, on /meta/ and in the app's Meta (the same metahub.js). The dropdown became the row All · Imperium · Chaos · Xenos (each with its count, the picked army's group underlined in its colour, --imp, --cha, --xen); a group opens a sheet over the dimmed page of its armies as tiles (the T-375 glyph and name; Escape or the scrim closes it; Change opens it again). With an army picked the page is its own: its panel on top (rank among all armies as the one comparison, then its month-by-month line, new, its detachments, Force Dispositions and most-taken units), its detachments and units panels both at once, the Winning lists door on its lists, its six best units; the all-armies chart, the army tiers, the Force Disposition tiers and the directory wait under All. hre.meta.fac and ?faction= unchanged. The grouping is each army's own prefix, in one place both read (MetaHub.groupOf), not a data file: the prefixes already say it. meta.test.js updated
T-482done1 Oct
The battle card: a round and turn strip for To the death (Jordan, 30 Sep, via the design session; main)
More
A compact strip at the battle card's top edge, small caps: five round pips filling as rounds pass, then "Round 2 · Their turn · Defender strikes first" (the acting side's name in its colour), a "Fights First" tag when that rule sets the order, the phase when Shoot and Melee are both on; at the end "Dead in round 3" or "Both standing after 5". In the docked row one line under the halves, or the pips alone. Screen readers get the same words. First check the engine's order against the rules (Wahapedia, read never copy): the charger fights first in the round it charged; later rounds alternate with the turn; Fights First before units without it, and the turn's owner first when both have it. Fix the To the death loop with a unit test if it doesn't; the strip shows what the engine does. The rule in our words in design/todeath.md. Screenshots and a clip of a 3-round fight Done 1 Oct: the engine's order checked against the rules (Wahapedia's core rules, read not copied): Fights First before units without it, the turn's owner first among each, the charger having Fights First in its turn; fightOrder already did exactly that, so no engine change; the rule in our words in design/todeath.md. The strip (#soStrip) along the battle's top edge while To the death plays: a pip per battle round (played filled, the current yellow), "Round 2 · Their turn · Termagants strike first" (the side's name in its colour), a Fights First tag when that rule set the order (deathPlay.ffs), "Shooting" or "Fight" on the first part with Shoot and Melee on; at the end "Plague Marines dead in round 3" or "Both standing after 5". Text, so screen readers get it; hidden until Simulate. The docked row shows the pips alone (#dkStrip). Found and fixed on the way: a To the death part's start overwrote a shoot-then-fight volley's own split, so the shooting never played before the charge (start now). Test: strip.test.js; a clip of a 3-round fight for Jordan
T-483done1 Oct
The battle card: a ⇄ swap pill (Jordan, 30 Sep, via the design session; main)
More
A ⇄ pill in the battle card's corner opposite the Charts / Battle pill (T-438), same size and style: swaps the sides (the existing swap code) and re-runs the battle with the same toggles; visible in the docked row too. No new Plausible event. Screenshots at 390px and desktop, both themes Done 1 Oct: "⇄ Swap" in the battle's top-right corner (opposite the Charts pill, which sits left), same size and style: the calculator's own Switch, then the battle runs again with the same toggles; the docked row keeps its ⇅. No new Plausible event. Tested in strip.test.js
T-487done1 Oct
The Lab 8: /lab/effects/ as a sandbox with shareable GIFs (T-466; Jordan, 30 Sep, via the design session)
More
Attacker and defender pickers at the top (picker.js and BSData at runtime, as the app does): every loop plays with the chosen units' real figures and the attacker's real weapons (families by the weaponfx rules; one loop per weapon); default 5 Intercessors vs 10 Termagants. A "GIF" button on every loop, the fates row included: one full loop with gif.js at whole-pixel scale, our mark and the two unit names (names only), through sharekit's path (share sheet, else download); "GIF of all" on the fates row. Files tacticalreroll-<family>-<attacker>-vs-<defender>.gif. Self-contained apart from the app's own scripts by relative path. After the Lab's phase 1 Done 1 Oct:https://tacticalreroll.com/lab/effects/ opens on "Your matchup": an attacker and a defender, each by army then unit (two selects each, filled from data/index.json; picker.js needs the calculator's own state, so the page doesn't carry it), their datasheets read from BSData at runtime with bsdata.js as the app does; every weapon the attacker carries plays as its family's loop (tools/weaponfx.js's rules, put in the page by stamp_assets.js) with the two units' own figures, default 5 Intercessors vs 10 Termagants (10 loops); the fates row uses the same figures. "GIF" on every loop and every fate, "GIF of all eight" on the fates: gif.js at 2× whole pixels, a band with our dice and the two names, a phone's share sheet else a download (sharekit's path), named tacticalreroll-<family>-<attacker>-vs-<defender>.gif. The choice is kept on the device (wfx.sbx). docs/labfx.js; the gallery engine takes the chosen figures through SBX. The family catalogue below keeps its own army select. Test: lab.test.js
T-488done1 Oct
Melee choreography: meet in the middle (Jordan, 30 Sep: "meet in the middle", via the design session; main; queued with T-482 and T-483)
More
Approved 30 Sep: option (a). Both sides charge to the centre and stay locked there for every round; blows alternate in the engine's real order (the order T-482 checks); no run back; the killing blow drops the model in place. With Shoot and Melee both on, the shooting plays from range first, then both charge to the middle. Compact gate lines over whoever is struck, one per side. In the docked row the same at half width. Keep today's two-charge look as a classic choice in ✨ Effects only if it costs nothing. design/weaponfx.md "Melee choreography" records the pick; the gallery's other two loops go once it's built. Screenshots and a clip of a 3-round fight, 390px and desktop Done 1 Oct: as decided: the first charge and the target meet half way, then every round is fought locked there in the engine's order (the strip, T-482, says whose turn and who strikes), no run back, the killing blow drops the model in place, the striker's fallen too; with Shoot and Melee on, the shooting plays from range first (it had been skipped, fixed with T-482); later parts skip the run-up. The classic charge ("New charge" off in ✨ Effects) keeps today's look, at no cost. The gallery keeps only this loop (design/weaponfx-preview.html, /lab/effects/); design/weaponfx.md updated. Not done: compact gate lines over the struck side (the funnel behind still carries the gates). Clips of a melee-only and a shoot-then-melee fight for Jordan
T-491done1 Oct
Post-merge check of the accordion decisions on main (Jordan, 30 Sep: "what else regressed?", via the design session)
More
Walk the decisions of T-430 to T-451 and T-437/T-438 against main: cards closed by default; four heads on one line with corner counts; one line of chips per category, sliding, never wrapping; one expander at the foot; the list chip under the statline and its sheet; the four toggles with their on/off state visible (Jordan's screenshot showed all four solid blue); the docked half-and-half row and the cards coming back whole; SIMULATE + ✨ Effects only; the corner pill top-right; the funnel staying after changes; GEQ naming; the static pages' header and footer; Meta's compact header and army panel. The deltas listed here, the clear ones fixed in the same commit, the debatable ones to Jordan. The visual-regression suite (T-396) against main's pre-merge commit e251196d (not beta's baselines), re-baselined from the fixed main. The news card and the tour's words checked against main (×10, Run again, the old sheet). Screenshots at 390px Done 1 Oct: walked at 390px, both themes (a script in the build session: each decision a check). Holding: cards closed on arrival, four heads on one line with corner counts, a line per category in one sliding row, one expander at the card's foot (one part at a time, its head again closes it), the list chip between the statline and the heads, the four toggles (on: filled blue; off: clear with grey words, so Jordan's all-blue screenshot had all four on), SIMULATE docks the half-and-half row and stays SIMULATE before, during and after a run, a half brings the cards back, the Charts pill top-right with Swap top-left, the funnel staying through toggles, GEQ on every page, the static pages' header (nav, 🔍, ⋯) and footer links with no overflow, /meta/'s one-line stats and its army groups on the first screen. Deltas fixed here: (1) the statline split "OC" from "2" across lines (each stat now keeps together); (2) back from the docked row, a card's lines lost their ‹ › and fade (measured while hidden; measured again on the way back); (3) the glossary still said "Chaff" (now GEQ); (4) three tour steps described gone parts: My army's "quick picks above the attacker" (the strip went in T-437), the target's "+ Modify" (now Modifiers), "⇅ Battle on top" (now ⚔ Battle in the corner); (5) a dead ×10 condition in shootout.js. The news card holds nothing stale (its items predate ×10 and the sheet). Visual check: the twelve screens shot at e251196d and at the fixed tree on the same machine: ten within tolerance; the calculator and its open part drift (15%, 12–14%) by the merge's intended changes only (the metric-matched fallback font the test pages fall back to, the Swap pill, our own tab and swap glyphs, the contrast fills). Re-baseline: visual.yml may now commit its baselines on main as well as beta; run it on main with "update" (the design session, or the build session through the Actions API). For Jordan (debatable): the off toggles have no outline of their own, only the group's (enough?); with the pixel battle off, ✨ Effects sits alone in its row, since SIMULATE goes with the battle
T-492done1 Oct
The Lab: /lab/board/ from TASKS.md (Jordan, 30 Sep, via the design session: the board artifact up to date, the duplicate "Open Cards" one consolidated)
More
Generated by build_pages.js from TASKS.md at build (never by hand), noindex, out of the sitemap, the Lab's header and footer. In the spirit of the board artifact (https://claude.ai/artifact/EL7Rj5yV9J7uEPxGh9r8oP): grouped cards with title, id, who and status chips, filters Everyone / Jordan's / Claude's kept in localStorage. Groups from the file: For Jordan (open 🧑 or 🧑🤖 cards, each with its ask), Building now / next (open 🔲 and 🟡 by section, the title the first sentence and the rest folded), Waiting (⛔), Approved mockups, Parked (folded, counted), Done this week (folded, newest first, with commit or PR), a stats row. docs/lab/board.json (the parsed cards) for reuse; a unit test that the parser reads every T-### row. CLAUDE.md's Lab line gets the address (the design session's edit) Done 1 Oct:https://tacticalreroll.com/lab/board/ (the Lab's first tile), by build_pages.js (labBoard()) with tools/board.js reading TASKS.md: a stats row; filters Everyone / Jordan's / Claude's kept in lab.board.who; For Jordan; Building now and next by section; Approved, with mockups; Waiting; Parked and Done this week folded (Done with its commit or PR). "This week" counts back from the newest Done / On main / Built / Fixed date on the board, not today's date, so the page is the same whatever day it's built (CI's drift check). docs/lab/board.json holds the parsed cards. Tests: unit/board.test.js (the parser reads every row: 495), lab.test.js. CLAUDE.md's Lab line is the design session's edit (mine are stopped by the permission check)
T-493done1 Oct
Weapon effects in the battle, batch (a): the Tyranid families and flamers (T-457, round 9 the version to build; via the design session, 30 Sep)
More
The plumbing first (T-458: tools/weaponfx.js into docs/, each bullet and blow keeps its weapon's family) and the ✨ Effects panel's Weapon effects part (T-462: master, Shooting and Melee, a switch per family, Show rare folded, Army colours toggle, wfx in hre.fx.v1); then the Tyranid families (bio-plasma, bio-plasmic, venom, spine-bank, spinefist, strangler, grub, warp, web, bio-spray) and flamers, drawn in the battle as in the gallery: natural colours by default, the army tint with the toggle, the volley capped at 1.4 s, a family switched off drawing today's bullet. Screenshots and a clip; lands on main Done 1 Oct: the families in the app: docs/weaponfx.js (moved from tools/, shared with Node and the Lab's sandbox), each battle weapon carries its fam (app.js weaponFamily()). The drawings are the gallery's own: tools/stamp_assets.js lifts them from design/weaponfx-preview.html into docs/wfxdraw.js (generated, deferred, 42 KB gzipped) with the battle's door (tools/wfxdoor.js), so each approved gallery round reaches the battle unchanged. shootout.js draws a built family's shot (effect()) on the shot's own line, skewed from gun to target and scaled to the battle's pixels (never below the gallery's), the target's feet as the gallery's ground; a torrent weapon draws one gout per model; extra hits ride inside; failed shots keep their fates. Built: flamers, bio-sprays, grub guns, spinefists, spine banks, stranglers, venom cannons, bio-plasma, bio-plasmic cannons, psychic and warp, webs; the rest fire as before. A particle budget (about 400 a frame on a phone, 900 wider) shared by each family's measured weight, never below 30%; the volley lasts as long as its longest effect, at most 1.4 s. ✨ Effects › Weapon effects: every weapon effect, Shooting, Melee, Army colours, a switch per built family (under ten weapons folded under Show rare), kept as wfx in hre.fx.v1. Tests: browser/weaponfx (a flamer drawn, its switch and the master; bolters as before; a fleshborer a grub gun). Screenshots 390px and desktop, both themes; a GIF through the share export (63 frames). For Jordan: a shot that fails at the Hit gate ends there with its full effect, mid-funnel (the funnel's rule), which reads large for the Exocrine's burst
As batch (a), with hit-stop (T-461) for rail and melta Done 1 Oct: the seven families in WFX_BUILT (shootout.js), drawn from the gallery's own code as batch (a). Hit-stop (T-461's part): the heaviest single hits (the gallery's stop families: melta and venom now, cannon and hammer with their batches; the rail and sniper stutter on their own) hold the whole battle still 60 ms at contact, at most four a volley and hits within 120 ms held once, by remapping the frame's clock (battleTime()), so the volley's own timings never shift; ✨ Effects › Weapon effects › Hit-stop switches it; nothing with reduced motion. Also: the plasma incinerators and the hellfire plasma carronade were classed as flamers ("incinerat"): a plasma rule now runs first (the phased plasma-flamer stays a flamer). Tests: browser/weaponfx (melta drawn, a hit-stop or a few, off with its switch; a plasma incinerator is plasma), unit/weaponfx (the plasma cases). Screenshots: Cadian lasguns, Devastators, Eradicators, Hellblasters, Immortals, Broadsides, a Vindicare
T-495done1 Oct
Weapon effects, batch (c): bolters and guns, rotary, shotguns, missiles and grenades, cannons (T-457)
More
As batch (a); hit-stop on the cannon shell Done 1 Oct: bolters, autoguns, stubbers and pistols (the catch-all), rotary and heavy guns, shotguns, missiles, grenades and cannons fire as the gallery draws them (the cannon shell's hit-stop with T-494's switch); most weapons in the game now have their effect, so the default matchup's bolters play it on every visit. Tests: browser/weaponfx (bolters drawn; pulse rifles, batch (d), as before). Screenshots: Intercessors, Devastators, a Leman Russ, Scouts, Ork Boyz, Aggressors
As batch (a) Done 1 Oct: grav, sonic, radium, shuriken and splinters (crystal), pulse, helfrost, dark light and lightning, with the plague guns that no batch named; webs and warp came with batch (a). Every shooting family now fires as the gallery draws it; melee (batch (e)) is next. Tests: browser/weaponfx (pulse drawn; a close combat weapon, a fist, as before until (e)). Screenshots: a Strike Team, Guardian Defenders, Centurion Devastators, Skitarii Rangers and Vanguard, Noise Marines, a Ravager
T-497done1 Oct
Weapon effects, batch (e): every melee family (T-457, T-460)
More
Our weapon sprites in hand (blade, chain, claws, hammer and fist, staff, the whip), the family's effect on top, bites as a jaw, hit-stop on the hammer; with the locked melee of T-488. As batch (a); T-463's tests and screenshots close the set Done 1 Oct: every melee family swings as the gallery draws it (blades, claws, chain, fists and hammers, power weapons, bites, force, plague blades, burning melee, whips): each front-rank model its own weapon in hand (blow()), drawn 1.5x about the hit point as the gallery does, its crackle when the weapon has an energy field (app.js weaponFamily() now hands crackle), the first blow at the charge's contact and again after a pause while the fight goes on; with the locked melee of T-488 and in the classic charge too. A fist or hammer holds the battle once on contact (hit-stop). Tests: browser/weaponfx (claws and fists drawn; one hit-stop for Terminators). Screenshots: Assault Intercessors, Genestealers, Terminators, Bladeguard, Hormagaunts, Ork Boyz. Every family of T-457 is now in the battle
T-500done1 Oct
SIMULATE in the dock once the row scrolls off (Jordan, 1 Oct, his phone screenshots: "where should SIMULATE live once I scroll into the funnel or the charts?", via the design session; a variation on T-434)
More
On a phone, when the Simulate row has gone off the top and the battle or the charts are on screen, the docked row is one line: the attacker and defender as name pills (name ×n, their colour) and the SIMULATE pill between them in place of ⇅; it runs the battle as the row's button does; Swap stays in the result's ⇄ pill and the dock's ⇅ when scrolled up; a name pill scrolls back to its card; back up, the dock returns to its two halves. Desktop unchanged; no pulse with reduced motion; the pill is the same button, moved (aria-label "Simulate") Done 1 Oct: layout.js simWatch()/setCompact(): on scroll and resize, the row (.so-actions) above the header while .breakdown shows turns the dock .compact, and #soReplay itself moves into its slot (#dkSim; a hidden #soHome marks its place in the row). Before any run (the cards still in the page) the line floats under the header (.float, fixed) so nothing below moves; SIMULATE from there docks as usual and keeps the result where it was. The button's accessible name is "Simulate" wherever it sits (shootout.js). Tests: newlayout (the pill visible and clickable from the charts, still there while it plays, back in its row at the top, a name pill back to its card; a wide screen never moves it; the T-434 test now checks the one line before ⇅). Screenshots 390px, both themes, in the funnel before a run, while it plays and in the charts
T-501done1 Oct
A chart's hover tip clipped at the screen's edge (found in T-500's screenshots, 1 Oct)
More
On a phone, a tap on the Total Damage chart (the right-hand column) leaves its "Each weapon's part" tip open, and it runs off the right edge ("…PART OF T"). Keep the tip inside the viewport (flip or clamp), and close it on scroll; check the other charts' tips too Done (1 Oct): the chart tips and the modifier chips' tips move back inside the screen (8px in from its edge) and a scroll closes them; the funnel's own tip was already kept in its box. Test: newlayout's "a chart's tip stays on screen"
T-502done1 Oct
Weapon effects: cheaper on a slow phone (T-453's audit, 1 Oct)
More
At 390px with the CPU slowed 4×, a flamer volley's 95th-percentile frame is 67 to 83 ms with effects on against 17 ms off. The dithered clouds and halos (dith(), halo()) aren't thinned by the particle budget: cache each radius and density as a small sprite, or thin them with the density too; then measure again (reports/audits/2026-10-01.md has the method) Done (1 Oct): each cloud is one shape filled in one call, kept per size, density and pattern; clouds count in a family's weight (a quarter per cell) and thin with the budget (by its square root). At 4× slower CPU the 95th percentile went from 50–67 ms to 33–50 ms, frames over 50 ms from 8–9 to 3–6 (effects off: 3–4); figures in reports/audits/2026-10-01.md
T-503done1 Oct
Effects come from the model that carries the weapon (Jordan's phone, 1 Oct, via the design session)
More
Termagants ×20 with a Tyranid Prime joined, Melee on: the Prime's lash whip played from random Termagants. Done (1 Oct): each attack is fired by a figure that carries its weapon: a joined character's weapons from its own figure (behind the squad), the squad's from a run of figures in proportion to the models carrying each (ranged and melee counted separately, so a model's pistol and blade share a figure); a figure with two melee weapons swings both; the character fires and swings too. Test: weaponfx's "each attack comes from a model that carries its weapon"
T-504done1 Oct
A miss at the Hit roll plays no impact, in every family (the design session, 1 Oct: fate 1 wins)
More
Done (1 Oct): a shot that fails to hit flies as its family only until it would reach the target, clipped short of it, then its fate veers and fades it; no impact of any family (the Exocrine's bio-plasmic burst included). In melee, a figure whose blows with a weapon all missed swings short, no contact effect. Test: weaponfx's "a miss at the Hit roll plays no impact"
T-505done1 Oct
Auto-equip for the defender, and as the default on both sides (Jordan's phone review, 1 Oct)
More
Done (1 Oct): a unit picked on its own, either side (a pick, a plain unit link), comes with its best legal squad (Auto-equip's all-rounder); a list's or a shared link's loadout wins. While nobody edits it by hand, a new model count solves again at that size within the datasheet's limits (20 Boyz → 10 → 20 comes back the same); a hand edit is kept, and the defender's counts are held to the datasheet's limits when the count drops. The defender's Weapons part has the same Auto-equip menu. Tests: unit/autoequip, browser/autoequip
T-506done1 Oct
Points in the statline (Jordan's phone review, 1 Oct)
More
Done (1 Oct): each side's statline ends with the unit's points at its size ("… OC 2 · 180 pts"), from the datasheet's price table as the rankings read it, following the count; the docked row shows them too. BSData prices no wargear in our records, so the wargear doesn't move them. The result panel's names are left as they are (no room on a phone)
T-507done1 Oct
The defender's list chip on paper (Jordan's phone review, 1 Oct)
More
Done (1 Oct): a held list's chip is paper on both sides (T-475's token); "Pick a list" stays dashed and hollow. your lists are paper on My army too (each saved list's card and an opened list's header) and on the game plan sheet (your list's name)
T-458done1 Oct
Weapon effects build 1: the families in the app (T-457; beta, after Jordan approves round 2)
More
tools/weaponfx.js into docs/ (shared with Node); app.js hands each weapon's family and crackle to the battle beside weapons:; each bullet track and each blow keeps its weapon, so a mixed volley draws each its own way. The test moves with it 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-459done1 Oct
Weapon effects build 2: the shooting families in shootout.js (T-457)
More
One drawing per family from the round-2 gallery, pixel-crisp (dithered clouds, stepped halos, no soft glows); the impact stays after the path has gone; natural colours with the army tint; bio purple always. Today's torrent, spray, rapid, blast, precision, melta and laser drawings fold in. Particle cap per frame (about 400 on a phone, halved past three weapons) 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-460done1 Oct
Weapon effects build 3: melee weapons in hand and bites (T-457, T-372's swing frame)
More
Our weapon sprites (blade, toothed chainsword, claws, hammer/fist, staff; the whip as a rolling line) in the attacker's hand for the swing, across the target, in the side's palette; each model swings its own weapon; the family's effect on top (sparks, arc flash, shockwave); crackle on energy weapons; bites draw a jaw that snaps shut over the target (wide for monsters, a nip for small models) 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-461done1 Oct
Weapon effects build 4: hit-stop (T-457)
More
The whole battle freezes two or three frames on contact for the heaviest single hits (rail, melta, venom cannon, cannon shell, hammer); its own switch under ✨ Effects; off with reduced motion 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-462done1 Oct
Weapon effects build 5: the ✨ panel and the volley's length (T-457)
More
✨ Effects › Weapon effects: one master, Shooting and Melee masters, a switch per family, families under 10 weapons behind "Show rare" (folded); "Army colours" toggle; kept in hre.fx.v1 as wfx. The volley lasts as long as the longest effect among the weapons firing, capped at 1.4 s 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-463done1 Oct
Weapon effects build 6: tests and screenshots (T-457)
More
A family per weapon in the battle's data, switches honoured (off draws today's bullet), hit-stop switch, reduced motion; screenshots of a few families at 390px and desktop, both themes; the GIF/clip export still works (sharekit.js) 30 Sep (design session, 23:33Z): built in visible family batches instead, T-493 to T-497; this card's part lands with them.Done 1 Oct with T-493 to T-497.
T-417done1 Oct
Wargear readings T-399 left inexact
More
Jakhals read 10–19 models, not 10–20 (the second Dishonoured unlocks through a condition we don't read), and their Mauler chainblade shows "up to 4" at 10 models where it's 1 (the package rule keeps the squad search right); Wolf Scouts' specialists and the Kroot Farstalkers' Pech'ra can't be taken (each should lower the plain models' count, a modifier we don't read) Fixed 1 Oct (bsdata.js, each general): (1) a group's max set higher once one of the unit's packages is taken counts toward its size (Jakhals 10–20), and as a pool it is raised with those packages (loadout.js) and from the size they make (the calculator); one raised at a model count is a step from that count (Purifier Squad's specials: 2, then 4 at 10); raises on anything else (a wargear pick, another entry's count) are left alone; (2) packages are alternatives in the limits: within a package carriers add up, across a group's packages the most counts, from the smallest squad that package makes (Mauler chainblade 1 at 10, 2 from 19); (3) a kind's count lowered by a modifier reads as a lower min, the squad's size settling the rest (Kroot Farstalkers' Pech'ra, Wolf Scouts' specialists, and so also Skitarii Rangers' specials, Pathfinders' ion and rail rifles, Goremongers, Sanctifiers, the Fortis Kill Team); (4) a group in a group belongs to it, not to each member (Kroot's one special weapon, 1 not 9; Raptors', Traitor Guardsmen's, Exaction Squad's specials). Value: Pathfinder Team 50.1 → 77.6 and Skitarii Rangers 41.8 → 58.3 (both scored without their special guns before), Wolf Scouts 32.9 → 38.2; every other unit 0.6 at most. Tests: unit/limits, unit/loadout, browser/weaplist. Left: T-508
T-508done1 Oct
Model maxes raised by the squad's size (found in T-417)
More
BSData raises some kinds' maxes once the unit reaches a size (Crusader Squad: power fists and pyreblasters 2, then 4 together at 20), which we don't read, so a 20-model Crusader Squad stops at 2 of each; read it as a step in the kind's max (choices) and in the limits, and check which units it moves Fixed 1 Oct (bsdata.js): a kind's or group's max set or raised ("set", or "increment") once the squad is so big is read as a step from that size: a count of model, of a category, or of the group that holds the raised entry and nearly the whole squad (the Crusader Squad's "Crusaders"); a count of something else is left alone (the Legends Crusader Squad's Neophytes, raised by the Initiates beside them). Kinds that each count one fewer for every one of the others share one slot, raised with them (Crusader Squad: power fists and pyreblasters, 2 between them, 4 at 20; Reavers: blaster and heat lance, 1, 2 at 6). The limits take the step only where nothing else carries the weapon. Read now: 21 units' extra specials at 10 (or 6) models, among them Terminator, Sternguard, Heavy Intercessor and Scout Squads, Death Company, Strike Squads, Seraphim, Eradicators; Boyz take 6 specials at 20 (one of each per 10 models: the old test said 3). Value: Indomitor Kill Team 60.8 → 63.7 (its second heavy bolter at 10, its only size); every other unit 0.2 at most (ranked at their smallest size). Left: Reavers at 3 models (their plain Reavers' minimum of 2 leaves no room for the special they may take)
T-509done1 Oct
The weapons list lets one kind of model take more of its alternatives than it has models (found in T-508)
More
A Terminator Squad of 5 with its assault cannon can still add a cyclone missile launcher: each weapon's + checks its own limit and the shared slots, not that the heavy gunner (1 model, 2 at 10) has already taken one of its picks. Auto-equip and the rankings' squad search are right (they place kinds); the calculator's + should count a kind's other picks too Fixed 1 Oct (app.js sharedSlots): one kind's pick between weapons only it carries is a shared slot of the kind's count (raised at its size, T-508), so the others' + stops once it's full; the datasheet's own weapons and weapons another kind also carries stay out of it, so a swap still takes their place. Test: browser/weaplist
T-510done1 Oct
A weapon's sibling profiles go missing: the Nightbringer showed only "Scythe of the Nightbringer - strike" (Jordan, 30 Sep (Pacific), via the design session)
More
Fixed 1 Oct: the attacker's side was right (one row, a strike / sweep switch, the best for the target). The defender's side (T-433) folded a weapon's profiles into one row with no switch, and its strike-back, like a joined character's default kit, took the first profile it met, never the best. Now the defender's row has the same switch (data-dwp, kept per unit in S.defPick, through ⇄ and share links), its strike-back takes the profile picked or the best against the attacker (defProfile), and joined characters' defaults take their best too. Affected in practice: any unit as the defender (162 with several melee profiles struck back with the first, 513 with several ranged in a firefight) and the 187 characters with several profiles when joined on their default kit. Tests: browser/weaplist (the Nightbringer both sides, Auto-equip on and off; every ➤ profile in four armies reaches both sides)
T-511done1 Oct
Weapon effects don't play when the enemy attacks back (Jordan, 30 Sep (Pacific), via the design session: "The weapon animations when enemies attack back aren't happening.")
More
Reproduce on 5 Intercessors vs 10 Termagants with Shoot and Melee on and Keep attacking / To the death: the defender's return volley (and the fight's second side) shows no family effect. Likely the attack-back path never reaches weaponfx's per-attack hook, or T-503's carrier mapping only resolves for the attacker's figures. Both sides' attacks play their family effects from the carrying figure; a weaponfx test for the return fire; screenshots 390px both themes mid-return-volley Fixed 1 Oct, two causes: (1) the target's weapons reached the battle with only a name and damage (app.js built the return side's list by hand), so they had no family or carrier to draw; both sides now go through one shotInfo (family, kind, carrier: a joined character's place or the squad's models); (2) a charge that slays nothing (a bounce) drew no blows at all on either side, and a target striking back often slays nothing; it now swings at contact, parried, before it bounces (a swing that misses its Hit roll still shows none, T-504). Test: browser/weaponfx (the Termagants' bite plays when they strike back)
T-512done1 Oct
Devourer bullets invisible on the light theme (Jordan, 30 Sep (Pacific), via the design session: "All of the bullets for the devourers are too light to see on light theme.")
More
Audit every family's projectile and tracer colours against the light theme's battle ground (and the colour switch's palettes): under about 3:1 gets a darker light-theme variant (theme-aware, not a global darken). Tyranid organic guns first, then the rest; before/after contrast per family in reports/audits/2026-10-01.md; screenshots light and dark Fixed 1 Oct: the battle passes the light theme's ground (--panel, white) to the drawings, and tools/wfxdoor.js darkens any of a family's colours under 3:1 against it (its own hue, less light) until it reaches 3:1; the dark theme keeps the gallery's colours. 44 of 48 families had a colour under 3:1 (the grub guns' white and lime at 1.0); all reach 3:1, and every army's tint does too. Before/after per family and army in reports/audits/2026-10-01.md (node tools/audit_fxcolours.js). Not covered: a few fixed white highlights inside some drawings, drawn on the family's darker colours rather than the ground. Test: unit/weaponfx
T-513done1 Oct
Our own pixel icons in place of the emoji (Jordan, 30 Sep (Pacific), via the design session: "The emojis we use need to be replaced with pixel versions, I mean the target, swords, lists etc.")
More
Inventory every emoji used as a UI icon (index.html, layout.js, app.js, shootout.js, sharekit.js, static pages, the Lab); draw a matching set of original pixel icons (16×16 or the figures grid, 2–3 tones, crisp through pixelfit.js) as inline SVG or one sprite, same accessible names; light and dark where needed; keep the meme reactions and the dice. Show the set on /lab/sprites/. Emoji inside user-facing text (the tour, the welcome) listed here for Jordan's call. Screenshots desktop and 390px, both themes: header, dock, picker, Lab page Done 1 Oct (the controls drawn in markup): our 9-pixel set (docs/icons.js, T-374) gains a lightning bolt, a repeat, a skull and an arrow for ranged; the four toggles (target, swords, repeat, skull), the card's line and section icons (swords, crown, lightning, spark, chart), the weapon headings and chips (arrow, swords) and the crown and lightning chips now draw them, in the text's colour on both themes. The whole set is on /lab/sprites/ › Glyphs, on a dark and a light panel, with the emoji each replaced. Left for T-514: the labels whose emoji sit inside translated text. Tests: newlayout, leaders, weaplist, weapons
T-514done1 Oct
The rest of the emoji in the controls, then Jordan's call on those in sentences (found in T-513)
More
Controls whose emoji sit inside translated strings (i18n.js), so each needs its markup split into a glyph and the words: ✨ Effects and 🎲 roll (Simulate row), 📊 Charts / ⚔ Battle (chartSwap), 🔗 Share and the share menu's 📋 🎞️ 📸, the list's 🏆 best and ⭐ supporter badges, the static pages' 🎯 ⚡ 👑 (build_pages.js), the Lab board's 🤖 / 🧑. In sentences, for Jordan's call (left as they are): the welcome's ⚔️ / 🛡️ / 👾, the tour's 👋 🧭 💡, the What's new notes (🚧 ☕ ❤️), the feedback form's 🐞 💬, the theme toggle's 🌙 ☀️, the supporter's ★ ⚀ ⚄ ⚅. Unicode marks that aren't emoji stay (✕ ✎ ▾ ▴ ➤ → ⇄ ↻ ☆ ★) Done 1 Oct: the strings keep their emoji as the icon's mark (a translation carries its icon), and Icons.label() (docs/icons.js) draws our glyph in its place, the words escaped; data-ico does the same for the page's own data-i18n controls. Now glyphs: ✨ Effects, Auto-equip and What's new; the funnel's 🎲 Roll row; 📊 Charts / ⚔ Battle; 🔗 Share and the share menu (link, picture, GIF, clip, game plan); ⚔ Fights / ➶ Fires and the Fight line; Skip and Again; the ⋯ menu (tour, news, supporter, figure, about, privacy); the New here? buttons, My army's buttons, the list sources and doors, the list's 🏆 best and the fan tag; on the static pages the trait badges (twelve, six new glyphs: run, chute, hood, scope, fog, revive), the Best tables' shooting/fighting marks and their note, the leader bonus and the load buttons; the Lab board's 🧑/🤖 and the Lab's toggles. 15 new glyphs in all, each on /lab/sprites/ › Glyphs with the emoji it replaced. Left as they are, for Jordan's call: the theme toggle (🌙 ☀️), Feedback / 🐞 Bug, the toasts (🎞️ Making a GIF…, 🎬 Recording…), the tour's and the welcome's sentences (which still name 📊 Charts, ⚔ Battle and 🔗 Share as emoji), the classic charts' 📈 lines, the supporter's ★. Tests: unit/icons (new), newlayout, shootout, layout, gif, fightsfirst; weaponfx's strike-back test now gives the target 20 models (ten could all fall to the charge, leaving none to strike back: it failed about one run in three)
T-517done1 Oct
Post GIF beside Share; the full-width GIF line goes (Jordan, 1 Oct (Pacific), via the design session: "The share as GIF button should exist where the share button is, right beside it, and just say Post GIF, and remove the old line.")
More
Done 1 Oct: after a battle the result's top line has two pills at the right, "Post GIF" (our film glyph) then "Share", the same size and style; Post GIF opens the same GIF flow as before, still counted as "GIF Offer Used". The full-width "Post this matchup as a GIF" button under the battle is gone. On a phone (560px and under) the two pills shrink to their glyphs, named by their aria-labels, so "This roll: 10 wounds · 10 slain" stays on one line. Found on the way: Share's bigger tap area was anchored to the group once Share sat in it, and covered Post GIF; each pill now anchors its own. Strings: flow.gif "Post GIF", flow.gifTitle for its tooltip; the tour's Share step names it. Army vs Army has no such line. Tests: gif (clicks the pill: the GIF flow starts and is counted; both fit beside the roll at 390px)
T-516done1 Oct
Effects for one weapon only (found in T-343)
More
An ability can raise one weapon and not the rest (the Intercessors' Hail of Bolts: +2 attacks for bolt rifles only), but detfx.js's effects apply to every weapon of a side. Add a when.weapon (names or a word in the name) that the dice engine checks per weapon, then record Hail of Bolts; careful: the Intercessor Squad is the calculator's opening unit, so the default numbers change and tests that pin them need updating Built 1 Oct: an effect can name its weapons (when.weapon, words in the weapon's name); detInPlay passes each weapon, so the effect is in play for that weapon only, and never where no weapon is in view (Army vs Army). Tested in unit/unitfx. Waits on Jordan: Hail of Bolts is written but held off. Switched on, the opening matchup's 5 Intercessors fire 4 shots a bolt rifle, not 2, so the opening numbers rise, the guide's worked example would no longer match what its Try it links open, and about 11 tests that pin the opening numbers change. On says yes to the real rule (then the guide's example gets the effect switched off in its links, or its numbers redone) Done 1 Oct (Jordan: "Switch it on, update guide"): Hail of Bolts is on: the opening 5 Intercessors fire 20 bolt rifle shots, not 10. The guide's worked example now has the troopers' ability too: 5 × 4 shots, 7.41 failed saves, about 7.3 models removed (ten at most, so shots past the tenth are lost), 4 or fewer 8.5% of the time and 9 or more 30.2% (the cut-offs now follow the average); its Try it link lands on the same 7.3. The drafts (i18n/drafts) follow. Tests that pinned the opening numbers now expect them (answer, calc, share, weapons, shootout, pages, newlayout, detachments: the detachment test leaves out the unit's own chips); fire's Then fight test gives the target 20 models, since 20 shots can wipe ten before the charge (CI went red on it once) Basis (recorded 2 Oct for the design session): Jordan in the build session, 1 Oct 07:08 Pacific (14:08Z), after asking what Hail of Bolts is: "Switch it on, update guide". It stays on.
T-211done1 Oct
Custom unit art: the pixel editor (a free page)
More
Jordan (27 Sep), "if we can swing that". A page where anyone draws their own pixel figure on our grid (the figures.js format: a few colours on a small map), saved on their device, and (supporters) used for their squads. Plus a community contest: submissions through the site, Jordan picks winners, they join the figures with the artist credited. Rules to settle: original work only (no copies of GW models), moderation (T-169), a licence for submitted art Jordan, 30 Sep, split: T-211 is the editor, a free page on main built after T-409's data work: draw on our grid in the figures.js format, saved on the device, exportable as our JSON, usable for a squad's figure locally. The contest is T-415, parked until moderation (T-169) and a submission licence exist Mockup (1 Oct), for Jordan:https://claude.ai/artifact/WxNXa6A45uYUfatARnepvD (a phone screen that works: start from a trooper, a leader or blank, pen, fill and mirror, our five inks and an eraser, three colours of your own, the squad of five in the battle as you draw, save and use for a squad, export as our JSON). Builds on his yes Built (1 Oct, Jordan's yes on the mockup): /pixel/, a free page (docs/pixel.js; linked from the footers and both ⋯ menus): start from a trooper, a leader or blank; pen, fill and mirror on a canvas (touch, mouse, or arrow keys and Space); our five inks and an eraser, three colours of your own; undo and redo; the squad of five as it stands in the battle; save on the device ("hre.pix.v1", up to 40), edit and delete; export our JSON (copy or download) and a PNG, import JSON back (checked: three colours, a map of . P S A D H, 24×24 at most); give a figure to a unit from your recent picks or your lists, and that unit fights as it in the battle and the docked row, on this device. The contest stays T-415. Test: browser/pixel
T-414done1 Oct
Tune your list, in My army (Jordan, 30 Sep: the direction instead of a full builder, T-213)
More
On an uploaded list: change a unit's loadout, its model count, add or drop units; the points and the maths follow. The next My army feature, after T-409 and the beta merge Mockup (1 Oct), for Jordan:https://claude.ai/artifact/LVb7UmLrQao344tGdqWUUa (a phone screen that works: tap a unit for its sizes and loadouts, drop it, add one from a sheet; points against the limit, and what the list does in a turn against Marines, Terminators and a tank, each with its change since the upload; Undo, Reset, save as a new list). Builds on his yes Built (1 Oct, Jordan's yes on the mockup): My army's ✎ Tune on a saved list (docs/tune.js): each unit opens to its datasheet's squad sizes (the points follow, keeping the list's own extras) and its loadout (as uploaded, or Auto-equip's best all-round or into one target, so always legal), and Drop; Add a unit searches the army; the points against a limit you pick (500 to 3,000) with the change since the upload, over-limit warned; what the list removes in a turn (each unit's own wargear through ev.js, shooting and fighting, into MEQ, TEQ and a heavy vehicle) with its change; Undo, Reset, and Save as a new list ("… (tuned)"), the upload kept. Test: browser/tune
T-420done30 Sep
Two Carnifexes into Skorpekh Destroyers fought with their claws (Jordan, 30 Sep, from his phone: 0.1 slain)
More
Found and fixed 30 Sep: a unit with no guns started on the first melee weapon listed (the Carnifex's chitinous claws, A4 S6 AP0 D1), alone: 8 attacks from two Carnifexes, ~0.7 damage into T6 Sv3+ W3, 0.1 slain. Now it fights with every melee weapon it carries, and each model takes its best one into the target plus its Extra Attacks weapons, in the same order as ev.js (damage per model): scything talons and extra scything talons, 16 attacks, 8 hits, 5.3 wounds, 3.6 unsaved, 7.8 damage into 3 Skorpekh (ev.js the same). Also found: a shared link with n= and no loadout of its own gave the datasheet's one-model counts to n models (each weapon from one Carnifex); the datasheet's counts now grow to n. Tests: tests/browser/copies.test.js (the pairing by link and by the stepper, against ev.js), and tests/unit/copies.test.js now compares every unit's melee weapons one by one with ev.js, not just their total
T-421done30 Sep
"Hyper Adaptions" under Invasion Fleet (Jordan, 30 Sep)
More
Fixed 30 Sep: the misspelling is BSData's own (the name the rule carries there), so the name stays the key (picks are stored and shared as "detachment|rule") and DetFx.ruleName() shows the right spelling wherever a rule's name appears (the detachment picker, the ⚡ chips' tips, the list's rules). The pass over every detachment rule name, against Wahapedia's faction pages (names compared only) found seven more of BSData's: Synaptic Imperatives, Blood-Tainted, Debt to the Soul Forge, Fanatical Celerity, Superior Craftsmanship, Rules Adaptations (the boarding detachments), and Subversion Assets' placeholder "New Rule" (Nowhere to Hide). Our option labels had one slip: Hyper-aggression → Hyper-Aggression. Tests: tests/unit/detnames.test.js flags any option label that is neither a name in BSData (spelt and capitalised as one) nor in its list of our own words; detachments.test.js checks the picker shows Hyper-Adaptations. The detachment name "Haloscreed Battleclade" (Battle Clade) is T-422
T-423done30 Sep
Weapons sheet: an Auto-equip button (Jordan, 30 Sep: "an auto-equip button with a very small dropdown … best all around, chaff, MEQ etc., and all weapons that are chosen to have an equipped model should be toggled FIRE on by default, and FIGHTS ON by default")
More
Built 30 Sep: "✨ Auto-equip ▾" at the top of the Weapons sheet (units with a real wargear choice): Best all-round, then Best into Chaff, MEQ, TEQ, Light vehicle, Heavy vehicle. Each is the unit's best legal squad at its current size, from the same loadout.js search the unit pages use (T-225, T-360), on the rankings' targets and neutral table (app.js keeps its own copy, LO_TARGETS, equal to tools/targets.js by tests/unit/autoequip.test.js). Every weapon it carries then fires or fights; weapons at 0 are off. A pistol fires only where a model has no gun (the rules' either/or). And as a rule: a weapon taken up from 0 turns its Fires or Fights on (a melee weapon while shooting turns the fight on; a gun while only fighting shoots first). Tests: copies.test.js (Carnifexes against loadout.js, every non-zero weapon on; 0 → 1 fights). A Carnifex's all-round kit: devourers, crushing claws, bio-plasma and spine banks, 5.7 Termagants slain against 3.3 with its datasheet default
T-425done30 Sep
Meta: the chosen army gets its own panel, not an inline fold (Jordan, 30 Sep: "the fold out drawer mid tier is clunky")
More
Built 30 Sep: with the filter naming an army, a panel at the top of the dashboard: "Tyranids in the winning lists", Share (the phone's share sheet, else the /meta/?faction= link copied) and Change (back to the filter), its rank, then three labelled rows: Detachments (small bars with their share), Force Dispositions (bars by count), Most taken units (chips with their share, the tier list last). It stays over all three tabs; the ranked list stays whole below with the army's row lit, no fold open (its row scrolls to the panel). Without the filter a row still folds open, in the same three rows. On phones each bar sits under its name. Plausible: "Meta Share". Tests: meta.test.js
T-426done30 Sep
/mathhammer/: the calculator button in an old flat blue; add "See it in action" (Jordan, 30 Sep)
More
Built 30 Sep: the static pages' calculator buttons (.cta .btn.primary: the guide, the detachment pages' "Try … with …", the army guide, the Meta report, a winning list's Load) and the phones' sticky try bar now use SIMULATE's blue-to-purple gradient (--att to --def, both themes). Under the guide's button, "See it in action ›" and docs/assets/battle.gif (382×356, about 5.5 s, 101 KB): the calculator's own pixel battle, Intercessors vs Termagants, recorded by shootout.js with our gif.js (tools/capture_battle_gif.js, run by hand), the funnel behind it left out so it holds our figures and the unit names only; it opens the calculator on that matchup. Tests: pages.test.js
T-427done30 Sep
Meta page screenshots come out black on Jordan's phone (Android Chrome; the other pages are fine, one Meta capture worked)
More
Checked 30 Sep: the page is clean; it's the browser. Nothing on /meta/ or the app's Meta tab can blank a capture: no canvas, video, WebGL, will-change, mix-blend-mode, backdrop-filter or page-wide transform in view, and no animation running once loaded (the calculator's two bridge canvases and the news sheet's blur are on other screens, hidden). Playwright at 390px as an Android phone, live and local, dark and light, 32 captures (just loaded, mid-scroll, while scrolling, full page): none black. But a capture taken while the page is still scrolling can catch it before Chrome has painted (a long page is drawn in tiles): the Meta page came out with a blank band, and faction, unit and guide pages came out wholly blank (the dark background alone) in the same test, with their content there. So a black Meta screenshot is a capture mid-scroll (Meta is the longest page, flicked through most) or an Incognito tab, which blocks captures. For Jordan: let the page stop moving before taking the shot. Fixed on the way: the static pages loaded their extra scripts (metahub.js on /meta/, tablesort, figures, simcta, rate, stats, i18n, shop) without a version stamp, so a phone could run stale code for up to ten minutes after a deploy; they're stamped by content hash now, like style.css
T-428done30 Sep
Weapons sheet: the count cramped between − and +, and "✎ Change its numbers" on every row (Jordan, 30 Sep, from his screenshots)
More
Built 30 Sep: every stepper's number (the weapon rows, the sheet's header, the card) has two digits' room (2ch plus a margin); the edit control is a blue pencil at the end of each weapon's profile line, a tick while its numbers are open, named "Change its numbers" for screen readers and on hover. Applies to whatever replaces the sheet too. Test: weaplist.test.js
Replaces the bottom unit sheet (layout.js) entirely; attacker and defender are one identical component. Sub-cards T-430 to T-439, all built by 30 Sep (T-430 to T-438 on main; T-439 on the work branch, waiting for main); Auto-equip (T-423) lives in the Weapons part. Recorded in design/streamline.md. The beta merge (T-452) follows it
Built 30 Sep: layout.js builds each card with the four heads (colour, dashed + empty, solid with a corner count, filled open) and the expander; the sheet's parts moved into it (the attacker's Weapons with Auto-equip, Leader with the joins and a note when nobody can lead, Detachment with "Set once for the army · n/3 DP" and its chips, Modifiers in three columns, the defender's numbers opened by its statline); one part at a time, its head again or Esc closes, cards start closed. The bottom sheet and its grabber (T-419), the fold arrow and the compact state (T-424) are gone; the old #attPop/#defPop stay in the page, empty. Tests rewritten for it (answer, detachments, fire, leaders, weaplist, share, newlayout, the helpers); the tour opens the card's parts
Built 30 Sep: the lines: weapons (every one carried, ranged then melee, ×N, greyed and struck when held back; a tap is the same as its Fires/Fights pill), 👑 leaders (a tap detaches), ⚡ detachments and each rule's option (a tap drops the detachment), the modifiers (a tap turns one off); one row each that slides, fading at an edge with more, ‹ › sliding half its width; hidden when empty. Chips at most 170px. Tests in newlayout.test.js
Built 30 Sep: a joined character's squad-wide buffs (detInPlay's leader effects) are gold 👑 chips on its side's Modifiers line, counted in the head: a tap switches one off for this matchup (struck, the leader stays), again back on; one not in play here is faint. They left the Modifiers part's own chips. The join menu shows each character's buffs in gold after its name. Tests: leaders.test.js
Built 30 Sep: the defender's Weapons part lists its weapons: − and + for how many models carry each (its list's loadout, else its datasheet's at its size; within the datasheet's limits; with every model armed, one more takes one from the option it replaces in its wargear pick, a spinefist for a fleshborer), and a Fights/Fires pill; its Weapons line has a chip each that switches it out. Its strike-back (To the death, the battle) uses them: defFightWeapons() in place of the datasheet's default, pistols for models left without a gun. Its leader was already there (T-145). Test in newlayout.test.js
Rewritten 30 Sep (08:03Z note, overrides the two strips): after SIMULATE on a phone, the two cards become ONE row docked under the site header, sticky while the page scrolls to the funnel and answer: two equal halves by construction (attacker left in blue, defender right in purple), each the side's pixel figure, the unit name (ellipsised) and ×N (the list it came from, or its weapon count), ⇅ swap at the seam, ~44px. A half tapped opens that side's list sheet when it has a list (T-353), else reopens its card; reopening a card brings back the two full cards (the row is the strips, not a third component). SIMULATE reads "⟲ Run again" while docked. The T-353 floating list pills and their CSS go (their sheet stays, opened from a half or the card's list chip, T-437). Nothing floats above the tab bar. Tests at 390px: the row under the header, equal halves, fixed while scrolling, a half opens the right sheet or card. (A first build with two strips in place was superseded before it reached main.) Built 30 Sep: below 900px SIMULATE (not Skip) docks #nlDock as the grid's first row, sticky at the header's foot; each half the side's figure (figures.js), name ×N, and "▤ <list>" or its weapon count; a half with a list opens its list sheet, one without brings the cards back; ⇅ swaps. The pills and their CSS are gone; tests and the tour open a list from the card. Test in newlayout.test.js
Rewritten 30 Sep (08:10Z note: toggles, not exclusive modes). Built 30 Sep: four equal segments, icon over label, an "on" one filled in the site blue; Shoot and Melee as before (what our side does; one greys out when the unit has only one kind of weapon); Fight back: the target's survivors reply (in melee when Melee is on, with their guns when only Shoot is); To the death: repeat it all up to 5 battle rounds until one side is gone. deathOdds takes the toggles: Fight back off is an empty strike-back, To the death off is one round (one fight phase, or our volley and theirs). Fight back + To the death is the fight to a finish as it was (its odds); every other pairing counts both sides' losses and how long: "Intercessor Squad slay 7.9 · lose 0.05 in reply · one round" / "… over 3.2 rounds on average", with the chance of a wipe-out when it repeats. Share links carry the two bits (k: "b", "d", "bd"). Tests: all twelve reachable combinations give a result of the right kind; the old fight's odds match; Shoot-only to the death without a reply loses nothing; one round's losses equal To the death's first phase
newlayout.test.js: the four heads share one row at 390px; an untouched card has no lines; a weapon chip toggled off changes the answer and back; a leader's buff chip shows in Modifiers and switches off while the leader stays (leaders.test.js); SIMULATE docks the row and a tap reopens one; the toggles (T-435). Screenshots 390px and desktop, both themes: closed, all four lines, each expander, the docked row. tour.js's steps on the card; its modes step names the four toggles. Done 30 Sep: all of it, with the docked row's test (T-434), the toggles' twelve combinations (T-435), the list chips (T-437) and the Simulate row (T-438); screenshots taken at 390px and 1280px in both themes
Under the statline, above the heads, a list row: no list, a dashed grey chip "▤ Pick a list" (the empty heads' style) that opens the picker on its Lists branch for that side (T-352); a list, a solid role-coloured chip "▤ <name> · 14 units" that opens that side's list sheet (T-353), and ✕ to clear it. Removed from the calculator page: the MY ARMY chip strip at the top, the "Load a list" door (and the T-353 floating pills, in T-434). The My army door moves above Meta: line 1 bold "<army> · <pts> · <detachments>", line 2 muted the list's own title, then "Open in My army" · "Set as attacker" · "Set as defender" (the last two put the list on that card and open its list sheet). Plausible "List Load"/"List Pick" keep their meanings with via "card". Tour and welcome copy follow. Tests: the dashed chip on an empty side; a picked list fills it; the strip and door are gone from the DOM; ✕ clears; the door's two buttons fill the right card. design/streamline.md: doors = My army + Meta; lists live in My army and on the cards. Built 30 Sep: the list row is each card's #attFrom/#defFrom (dashed "▤ Pick a list" → openListPicker(side); the list's chip → its sheet on a phone, under the card on desktop; ✕ → dropList). The MY ARMY strip (#mineChips, MyArmy.chips) and the Load a list door (#nlBridge) are gone. The My army door names your list (army · pts · detachments, its title), with Open in My army, Set as attacker, Set as defender (MyArmy.useMine(side): side 0's own list, a copy for the defender), which open that side's list. With no list it still offers Upload. Tour step "loadlist" points at the cards' list rows. Test in myarmy.test.js
T-438done30 Sep
The Simulate row: SIMULATE and ✨ Effects; the Charts/Battle corner pill (Jordan, 30 Sep, the 07:04Z and 07:05Z notes)
More
The row becomes two controls: SIMULATE (filling the row on phones, natural width left-aligned on desktop) and a labelled "✨ Effects" pill at its right end (same height as SIMULATE), replacing the floating ✨ fab. The "🎲 ×10" button goes: its count fixed at today's default for what it fed, its strings and any hre key gone (the old key read once if needed). "⇅ Charts on top" leaves the row for a small pill in the result panel's top-right corner, the same spot in both views, naming the destination: "📊 Charts" in the battle view, "⚔ Battle" in the charts view (icons.js glyphs if they exist); the legend's ⓘ moves left of it; hre.chartmode unchanged. NOT in the Effects panel. Tests at 390px: the row is SIMULATE + Effects, Effects opens the panel, the pill sits at the same coordinates in both views and flips view and label, the choice survives a reload, ×10 and its key gone. Screenshots both themes. Built 30 Sep: the row is #soReplay and #soFx ("✨ Effects", labelled; the fixed ✨ fab and 🎲 ×10 gone with their strings; ×10 wrote no key); on phones SIMULATE takes the width. #chartSwap is a fixed-width pill in .bd-corner at the result's top-right, the same coordinates in both views, "📊 Charts" / "⚔ Battle" (no icons.js on main); hre.chartmode unchanged. The ×10 play code left behind (simulate(), #soRuns) goes in T-439. Tests in newlayout.test.js; the ×10 test in shootout.test.js went with it
T-439done30 Sep
Clean up after the accordion (the 08:15Z queue, item i)
More
T-419 and T-424 are superseded: remove their leftover CSS and the empty #attPop/#defPop, so no stale key, test or tour step can reopen a dead surface. Check the ⓘ unit sheet does not depend on the removed editing sheet's code. Also the ×10 play code T-438 left unreachable (shootout.js simulate(), renderRuns, #soRuns and its strings) Done 30 Sep: the old sheets lend the expander their contents and are removed from the page (layout.js sheet()), with their title, model stepper and syncing; [data-pop] buttons without a sheet do nothing; T-419's phone-sheet CSS (grabber, 85% height, full screen) and the tab row (.sh-nav, T-290/T-297) are gone. The ⓘ sheet is unitcard.js, which never used them. The ×10 play code is gone: simulate(), renderRuns, #soRuns, the tally's odds in the calculator's result and 13 strings (the tour no longer mentions 🎲 ×10)
T-440done30 Sep
The funnel vanishing (Jordan, 30 Sep, on beta: "the funnel was disappearing for no reason")
More
Reproduce with lists on both sides (Lychguard ×5 vs Devastator Squad ×5): after Simulate, pick another unit from a list, switch the toggles, flip charts/battle, open and close a card's part, change the count, load and clear a list, resize/rotate; watch for an error in shootout.js's draw and any path that hides the result panel other than an empty answer. Fix, and a test that the funnel stays after each. Check main's new card too. Report the cause (or what was tried) in the summary Checked 30 Sep: not reproduced. Lychguard ×5 vs Devastator Squad ×5 and two uploaded lists, on the beta branch and on main with the accordion: after Simulate, a unit from a list (pill on beta, docked half on main), Shoot/Melee/Fight back/To the death, charts on top and back (with a Simulate between), a card's part opened and closed, the model count, a list let go, rotate, desktop and back: the funnel stayed each time, with no console error, and nothing but an empty answer hides #bdFlow. Main's newlayout.test.js now walks those steps (T-440 test). If Jordan sees it again, the unit pair and the last tap will find it
T-442done30 Sep
Ranking tables: rows of constant height (Jordan, 30 Sep, via the design session; main, and the best pages on beta)
More
The unit cell two fixed lines: the name with its tier badge; "Space Wolves · 5 models · 100 pts" muted and ellipsised. The dice (run it) in a fixed slot at the far right of every row. Number columns narrower, right-aligned, tabular numerals. At 390px no row wraps for the 50 longest names Done 30 Sep: the army pages' table and the best pages' tables (both on main): the unit cell is .u1 (name, the tier fixed beside it, 24px) and .u2 ("army · models · pts"), each cut with …; the dice has its own last column (td.run, its tooltip anchored right); numbers right-aligned, nowrap; under 560px the army pages hide Utility and Meta so the names get the width (the unit page keeps both). .tbl-wrap gets contain: paint (an overflowing table otherwise widened the page in Chromium). Test in pages.test.js: one row height at 390px on three pages, the dice last, nothing wider than the phone
T-443done30 Sep
Static pages' header = the app's header (Jordan, 30 Sep, via the design session; main)
More
Logo left, 🔍 and ⋯ right on one line; the nav (Calculator · Meta · Guide · Method) a compact row under it on phones, inline on desktop; the same on every static page Done 30 Sep: build_pages.js's header is a grid: the lockup, the nav (.ph-nav) and the tools (🔍 and a ⋯ <details> menu: Calculator, My army, Meta, Winning lists, Best units, Guide, About & method, Privacy); under 700px the nav is its own compact row. Every static page
T-444done30 Sep
Static pages' footer (Jordan, 30 Sep, via the design session; main)
More
Calculator · My army · Meta · Winning lists · Best units · Guide · Method · Privacy in one wrapping row above the disclaimer, on every static page Done 30 Sep:.foot-links above the disclaimer on every static page (the old four-link line goes). The unit pages' Simulate box also lost its "in our free, comprehensive mathhammer calculator…" caption, for the reason T-445 gives for the best pages
T-445done30 Sep
Best-units pages: the five jobs as tiles (Jordan, 30 Sep, via the design session; beta, where those pages live)
More
Five tiles (Anti-tank, Anti-elite, Anti-infantry, Anti-horde, Toughest): the job, its current #1's pixel figure (figures.js), its name; the current page highlighted; the same row at the top of each best page as its navigation. Under the Simulate box, delete the "in our free, comprehensive mathhammer calculator at tacticalreroll.com" caption Done 30 Sep (the best pages are on main, not only beta):jobTiles() in build_pages.js heads the hub and each job page: five tiles, the job, its current #1's pixel figure (Figures.pick) and name, the page's own job aria-current; on a phone the row scrolls sideways and opens on this page's tile (pagesearch.js). The caption under the Simulate box went with T-444. Test in pages.test.js
T-446done30 Sep
The animated "Simulate it" box (Jordan, 30 Sep, via the design session; main and beta)
More
Pips under the box for the cycle position (tap to jump); three volleys per opponent instead of two; unique characters drawn once (the figure count follows the unit's real size; an Epic Hero or single model is one figure: Roboute Guilliman draws twice today) Done 30 Sep: simcta.js adds a pip per opponent under the box (.cta-pips, 24px targets; the current aria-current; a tap jumps and restarts the turn; taps work with reduced motion too); the turn is three loops (4.8 s) there and on the app's unit card; Figures.FOES carries each opponent's usual size and firingSvg(…, tn) draws a single model once (Guilliman, the vehicles). The unit page's loadout note now names the dice (T-449). Test in pages.test.js. The guide's box is T-447
T-447done30 Sep
The guide (/mathhammer/): the live Simulate box (Jordan, 30 Sep, via the design session; amends T-426)
More
The live animated box with rotating matchups (T-446) in place of the button and the GIF, linking into the calculator on the matchup showing; drop the GIF; keep T-426's gradient elsewhere Done 30 Sep: the guide opens with the live box (guideSim() in build_pages.js: Intercessors against the rotating opponents, pips, a tap opens the calculator on the matchup showing); docs/assets/battle.gif and tools/capture_battle_gif.js are gone, with their CSS and strings. Test in pages.test.js
T-448done30 Sep
/meta/: a compact header (Jordan, 30 Sep, via the design session; main, on top of T-425)
More
The four stat tiles become one small line under the title ("70 winning lists · 14 events · Aug–Sep 2026 · 36 armies · 1,241 units scored", small caps, from the same data); the intro one sentence with a "How we count ▸" disclosure; wordmark, date and "Updated daily" compact, so the army filter and the top of the chart show on a phone's first screen. Before/after at 390px Done 30 Sep: in metahub.js (the /meta/ page and the app's Overview alike): the four tiles are one small-caps line (hub.statline), the intro one sentence and "How we count ▸" holds the rest, the wordmark smaller; and the dashboard now comes before the four doors, so the army filter and the top of the armies chart are on a 390px phone's first screen (before/after in the summary)
T-449done30 Sep
Unit pages' loadout table: the dice for "runs that squad" (Jordan, 30 Sep, via the design session; main)
More
The ▶ becomes the dice icon, the same "simulate this" mark as the rankings and best pages Done 30 Sep: the loadout table's run link carries the site's dice mark (MARK, as the rankings and best pages) in its 36×32 box
T-450done30 Sep
"Chaff" becomes "GEQ" (Jordan, 30 Sep, via the design session; main)
More
Guard-equivalent, 10 × T3 Sv5+ W1, with its statline under it as today; MEQ, TEQ, Light and Heavy vehicle stay. Everywhere the five appear (unit pages, the unit sheet's bars, best pages, Auto-equip, i18n); the best pages' names (Anti-horde…) unchanged Done 30 Sep: the label in tools/targets.js and the rk.tgt.chaff strings ("GEQ", Guard-equivalent; the statline under it unchanged); the key stays chaff so links (vs=chaff) keep working; rankings and pages rebuilt
T-452done30 Sep
Merge beta into main (Jordan, 30 Sep, before bed: "Merge.")
More
Right after the accordion epic is finished and green: merge origin/beta into the work branch (a merge commit), every conflict in favour of main's accordion, docked row, lists on the card and Simulate row; beta's data, art, glyphs, portraits, share images, Meta panel, report.js, tests and audits come over. Rebuild, stamp, the full suite with beta's suites (visual baselines re-taken for the accordion), screenshots (calculator, a unit page, Meta, a best page; 390px and desktop, both themes); push, fast-forward main, watch CI, check the live stamp. Flags: unitfx on everywhere; the 16-bit hero sprites (T-378) off on main (flag kept); glyphs kept; memes/profiles unchanged. T-369 to T-398, T-405 to T-408 and T-383's label to Done with the merge commit. Then reset beta to main: tonight's extra queue lands on beta for Jordan's morning review, deployed by hand Prepared 30 Sep, waiting for Jordan's own OK: the merge is resolved in a scratch worktree (conflicts in 20 files, main's way where the accordion replaced beta's surfaces, beta's additions kept; pages rebuilt; unit tests 65/65; the calculator loads clean), but committing it was stopped by the session's permission check, since CLAUDE.md keeps beta off main without his OK and this one came relayed. Once he confirms here, it commits, gets the full suite and pushes Done 30 Sep (Jordan: "ok lets do the beta merge"): merged in the main checkout; the scratch resolutions carried over, with main's work since (T-437 to T-477) laid on top: main's header, footer links, accordion and Meta order won where the same lines met, beta's additions kept (nicknames, face-off label, print styles, glossary, trends, glyphs, portraits, icons.js, nicknames.js, the i18n drafts). Flags: main's values; beta's switches off on main (unitfx, heroes false; army tiers off in code). Every generated file rebuilt with the tools. The Block 2 cards T-369 to T-398 and T-405 to T-408, already ✅ on beta, are now on main; beta was reset to main
T-477done30 Sep
The battle: misses fade past the line instead of stopping dead (Jordan, 30 Sep, via the design session; main)
More
Behind the beta merge (shootout.js changed on beta too) and the Lab's phase 1. A shot that fails a gate keeps its momentum past the funnel line for about a quarter of the remaining gap, fading to nothing over ~150 ms, never reaching the unit; at the line its word (today's "miss", "no wound", "saved") appears in small white text that drifts down a few pixels and fades over ~400 ms. Hits unchanged. Today's behaviour stays as a switch in ✨ Effects, "Misses: fade past / stop dead", default fade (in hre.fx.v1). The weapon-effects families (T-459 to T-461) do the same, a beam's word at the gate line. Screenshots or a short clip for Jordan 30 Sep, extended (the design session, 22:19Z): "One shot, eight fates" at the top of the weapon-effects gallery, for Jordan's review before any build: miss, failed to wound, saved by armour, saved by an invulnerable save, Feel No Pain, damage, slain, and the specials (Sustained, Lethal, Devastating, Hazardous), each told by shape and place; design/weaponfx.md "Fates" has the table and rules. Engine change: T-478 Approved for build (Jordan, 30 Sep: yes): the eight fates as in the gallery row and weaponfx.md's Fates, the specials, one "urgh"; with T-478 and the misses fadeDone 30 Sep: the fates in the real battle (shootout.js), behind "Misses fade past the line" in ✨ Effects (fade in hre.fx.v1, on by default; off or Classic keeps the stop-dead blink): misses veer past the hit line and fade before the model; no wound touches with a grey tink; armour sparks and a one-pixel rock; an invulnerable save bursts on a shield ring; Feel No Pain flinches; small words at the gate lines ("miss", "no wound", "saved" with a shield, "urgh", "+n" for Sustained Hits), dark-outlined in both themes; Lethal Hits and Devastating Wounds light their lines. Not yet: the Devastating spill and Hazardous (not per shot in the engine). design/weaponfx.md "Fates"; tests/browser/fates.test.js
T-478done30 Sep
The save gate records which save it used (T-477's fates; review first)
More
rollSingleSequence picks the better of armour and invulnerable save without saying which. Add one field to each bullet track, save: "armour" | "invuln", where a save is rolled, so the battle can draw fate 3 (sparks skate off) apart from fate 4 (a shield flickers). The counts stay identical with or without T (volley.test.js), and a test checks the field on a unit with both saves. Build only after Jordan's yes on the fates row Approved for build with T-477 (Jordan, 30 Sep: yes)Done 30 Sep: each bullet track gets save: "armour" | "invuln" where a save is rolled, fnp (points shrugged off), lethal and dev; counts unchanged with or without the track; volley.test.js checks a unit with both saves
T-479done30 Sep
The list sheet's look-alike rows (Jordan's screenshot, 30 Sep, via the design session; main)
More
A Black Templars list (3rd · FWC n85) shows "Assault Intercessor Squad 🔗" twice at 10 of 10 and a third at 7.5, and "Sword Brethren Squad 🔗" twice. First check that list's roster: if the importer or the joined-units pass duplicates a squad per leader, fix it with a test on that list. If the rows are real: under a joined squad's name, "led by <character>" in the leader gold in place of the bare 🔗; rows that still read identical (unit, models, points, leader state) collapse into one with "×2" and the shared score. The same in Army vs Army's pairing lists and the game plan if they share the row code Done 30 Sep: the rows were real, distinct units (three Assault Intercessor Squads, two led by different characters and one alone; two Sword Brethren Squads each led by a Marshal), not an import bug. A joined squad's row now says "led by <character>" in the leader colour in place of the 🔗, and rows that read the same (unit, models, points, leader, nickname) show once with ×n. Army vs Army's rows and the game plan use their own code, unchanged. Test on that list in lists.test.js
T-480done30 Sep
The list sheet shows how to close it (Jordan, 30 Sep, via the design session; main)
More
#listPop gets a sticky footer like the unit picker's: a full-width "Done" (primary) with "Change list" beside it, always visible on a phone; Esc, the chip's × and a tap outside keep working; the same for the picker's list mode if it lacks one. The list chip shows it is a drawer: a chevron at its right end, ▾ closed and ▴ open, turning with a short transition; the chip reads as pressed while open (the paper token once T-475 lands, the primary tint until then); the × for letting the list go stays separate. The same for any other chip on the card that opens a sheet. Screenshots at 390px, both states and themes Done 30 Sep: the sheet's Done and Change list footer was already fixed at the foot at 390px; the list chip now ends with a ▾ that turns over while its sheet is open, the chip reads pressed while open, and closing a sheet resets the chip (it stayed "open" before). The × stays separate. Test in closers.test.js
T-481done30 Sep
The unit picker's units level: role tiles, one role open at a time (Jordan, 30 Sep: "small icons at the top for Epic hero, character, battleline… when clicked they open up the drawer for those units"; main)
More
No long grouped list. A row of small role tiles at the top in the army tiles' style, smaller (glyph over a short label and a count): Recent, Saved (when non-empty), Epic Heroes, Characters, Battleline, Infantry, Mounted, Monsters, Vehicles, Dedicated Transports, Flyers, Fortifications, only the roles the faction has; each an original 9-pixel glyph (T-374's set); the row wraps on a phone. A tile opens its units below (today's rows); another switches; the open one again closes; one open at a time, the open tile pressed. Default: Recent open if it has entries, else none. Search covers the whole faction whatever is open. The last open role remembered per device (hre.pickrole.v1). Header and FROM / ALLY / FACTION chips as today. The 🔍 unit card and list mode only if they show the same grouped list. Screenshots at 390px, light and dark, closed and one open; picker tests updated Done 30 Sep: picker.js's units level is a row of role tiles (our 9-pixel glyphs in icons.js: crown, one figure, three, a trooper, a rider, a paw, a claw, a tank, a truck, a wing, a wall), each with its count, wrapping on a phone; Recent and Saved first when they have entries; Vehicles split into Dedicated Transports and Flyers in the picker only. One role open at a time below the row, the open tile pressed; another switches, the open one again closes; the last choice kept in hre.pickrole.v1; nothing open and a one-line hint otherwise. Search unchanged. List mode keeps its role headings (a list is short). Tests in newlayout.test.js
T-485done30 Sep
A Violence switch in ✨ Effects (Jordan, 30 Sep, via the design session; main, soon)
More
Two settings in hre.fx.v1: "Full" (the deaths, T-484, once they exist; blood specks and pools today) and "Mild" (today's plain fall, no blood or pools, no gore words); default Full. Mild also governs the static pages' Simulate it boxes and shared GIFs when set. In design/weaponfx.md under Violence Done 30 Sep: "Violence: Full / Mild" under Everywhere in ✨ Effects (violence in hre.fx.v1, default Full); Mild skips the new charge's deaths for the plain fall; GIFs and clips follow it (same drawing code); the static Simulate it boxes draw no deaths, so nothing changes there yet. Tested in fates.test.js
T-486done30 Sep
A Close bar at the foot of each accordion part (Jordan's screenshot, 30 Sep, via the design session; main)
More
Every open part (Weapons, Leader, Detachment, Modifiers, the defender's Stats) ends with a full-width thin button, "▴ Close", muted, a chip row's height: it closes the part and scrolls the heads back into view if they left the screen. "Close", not "Done": changes apply as tapped. It replaces the head's "tap again to close" hint (the head and Esc still close it). Screenshots at 390px, one part open, both themes Done 30 Sep: each open part (Weapons, Leader, Detachment, Modifiers, the defender's Stats) ends with a full-width "▴ Close" bar (32px, muted) that closes it and brings the heads back into view; the old "tap the head again" hint went. Test in closers.test.js
T-489done30 Sep
Army pictures: the pixel figures by default, the portraits behind a switch (Jordan, 30 Sep, via the design session: the portraits on the picker's tiles "are tough"; main, with the quick fixes)
More
One switch in ✨ Effects beside the colour switch, "Army pictures: Figures / Portraits", in hre.fx.v1 (art), default figures. Figures: the army's own pixel figure from figures.js (its troop figure, or the elite where the troop is a swarm), at whole pixels (pixelfit). Everywhere an army is pictured: the picker's faction tiles, the side's chips if pictured, My army's cards, Meta's army rows and T-476's sheet, the unit card's header, the static faction and winning-list pages (built with figures; the app swaps in portraits on load when set), share pictures (T-382), the game plan. The army glyphs (T-375) unchanged. The portraits (T-371, T-405 to T-408) are kept behind the switch, not retired; Jordan may redraw them. Screenshots of the tiles at 390px both ways Done 30 Sep:Figures.armyPic(army, slot) gives the army's figure (Tyranids stand as their elite; a gaunt reads as a speck), or its portrait when ✨ Effects' new "Army pictures: Figures / Portraits" (in Everywhere, art in hre.fx.v1, figures by default) says so; Figures.picSvg draws it at a whole scale for its frame (figures 3×, portraits 2×); Figures.faceOf does the same for a unit's face. On the picker's faction tiles, My army's cards (which redraw on a change), the share pictures (T-382 and the face-off) and a static unit page's header (built with the figure; infantry and characters carry data-port, and figures.js swaps the portrait in on load for a visitor who picked portraits). Meta's army rows, the unit card and the game plan never showed portraits, so they're unchanged; T-476's sheet will use armyPic. Tests: armypics.test.js, figures.test.js
T-490done30 Sep
Two live regressions of T-438 (Jordan's phone screenshot, 30 Sep, via the design session)
More
(1) The 📊 Charts pill sat top-left over the legend ("et this far"): a later hit-area rule gave #chartSwapposition: relative, overriding its absolute top-right; now top-right in the battle, the charts and while docked, and the legend clears both corner pills (T-483's ⇄ Swap moved to the top-left, opposite it). (2) SIMULATE read "⟲ Run again" after a run and while docked (T-434's running state): it reads SIMULATE always now, and the flow.rerun string is gone; the tour and the news card never used it. Test: corners.test.js (both pills clear of every legend item at 390px, in each state; SIMULATE after a run)
T-365done30 Sep
Two wargear readings loadout.js couldn't reproduce (found by T-364's test)
More
Fixed 30 Sep (Jordan via the design session): (1) the Crucible heroes (Daemonic Charioteer, Daemonic Herald, Immortal Champion in five Chaos armies) have ~1,500 kits, more than the 400 the search keeps: a model with more combinations than that now tries its default wargear's options first (loadout.js kits), so the default is always among those kept; every other model keeps its order (rankings unchanged but for two squad counts). (2) The Grey Knights' Venerable Dreadnought: BSData keeps its two arms in one unlimited "Wargear" group whose default names neither; bsdata.js read the group as one pick, so the Dreadnought carried either a gun or a fist. A group with no limits holding only groups now just holds them (the only other one in BSData, the Knights' Chivalric Quests, isn't wargear). It now fires its default assault cannon and storm bolter: Value 56.8 → 59.1, Offense 45.1 → 49.6 (other units' percentiles move by 0.1 at most). Both off the known list in tests/unit/loadout.test.js
T-399done30 Sep
Six units whose default wargear and wargear choices disagreed (found by T-386 on beta)
More
Fixed 30 Sep, all six our reading of BSData (each fix general, in bsdata.js): (1) preset squads: a pick-one group whose options bring models at set counts (Jakhals' "8 chainblades", "1 mauler chainblade, 7 chainblades") is read as packages, and loadout.js takes one package per group; (2) a model's own weapons join its fixed wargear (Agathae Dolan's bolt pistol); (3) a group's nested groups count toward its minimum in the default (Crusader Squad: 1 Sword Brother, not 10; Kroot Farstalkers: 10 firearms, not 11); (4) where a group's default names no member, the fill takes members that can hold the whole count first, and the smallest preset squad (Ynnari Kabalite Warriors: rifles, not 9 shredders); (5) an option with no weapons of its own brings its optional parts (the Solarite's choice). The invariant's known list is empty; a loadout test holds the Jakhals' packages. Also read now: Cadian Shock Troops and Catachan Jungle Fighters' special weapons (Offense 6 → 22.9 and 4.1 → 14.9), the Aquila and Decimus Kill Teams, Wolf Scouts (scored at 6 models, not the 12-model kit), Burna Boyz and Lootas, the Hornet's hull. Value: Jakhals 46.7 → 50.4, Crusader Squad 80.7 → 74 (was scored on ten power weapons), Ynnari Kabalites 58.4 → 64, Wolf Scouts 37.2 → 32.9; other units moved on percentiles, 0.8 at most. Checked in the calculator (Jakhals, Cadians; desktop and phone). Left: T-417
T-400done30 Sep
Ordinals in the site's own words (found by T-393's drafts on beta)
More
layout.js built "2nd " in English and passed it into door.metaArmyS ("the {rank}most played army"), so a translation couldn't order the words or pick its own ordinal. Done 30 Sep: two sentences, door.metaArmyS (the most played army) and door.metaArmyRank ("the {rank} most played army"), with the ordinal from the translatable lists.ord.* keys (lists.js wlOrd)
T-369done30 Sep
A1: every look its own figure in every slot
More
Troops, elite, heavy, big, vehicle, walker, flyer, mounted, swarm for each of the 22 looks; FAMILIES only as the fallback. List the slots shared before and after. Original archetypes, never a GW model's likeness. On beta: 67 new figures of ours (docs/figures.js OWN), so no look borrows a family figure any more. Before, borrowed from their family: marines heavy/flyer/mounted; tyranids heavy/flyer; necrons heavy/walker/flyer/mounted; orks heavy/flyer/mounted; aeldari heavy/flyer/mounted; csm, sororitas, gsc, deathguard, thousandsons, worldeaters, emperorschildren, agents, drukhari heavy/walker/flyer/mounted; tau flyer; guard heavy/flyer/mounted; custodes walker/flyer; daemons walker/flyer; mechanicus heavy/flyer/mounted; greyknights flyer/mounted; votann heavy/walker/flyer. After: none (own 163, family 0). Slots a look doesn't field stay empty and fall back as before (e.g. marines walker: their walkers use the big figure; tau heavy; knights' flyer). Drawn by five parallel passes against one brief (docs-free, previewed in node tools/sprites.js --png, the new gallery of every look and slot) and reviewed here. Test: no look borrows a family figure On main 30 Sep with the beta merge (T-452).
T-370done30 Sep
A2: looks that share a sprite told apart
More
Distinct silhouettes where a look reuses another's (check Agents, Grey Knights, Custodes, Votann, GSC, Drukhari vs Aeldari, Chaos Knights vs Knights, Titanicus); chapter recolours stay recolours. On beta: the check (identical and near-identical maps across looks) found: the Space Marine tank reused by Emperor's Children, Grey Knights and (near) Thousand Sons; the Chaos Space Marine tank by World Eaters; the Custodes bike by Votann; the Imperial Knights' two figures by the Chaos Knights; near-copies of the Astra Militarum trooper and tank in Agents and Death Guard; a T'au-like Custodes tank; and big = vehicle in Sororitas, Agents, Votann, Custodes and T'au (elite = big). 16 new figures: a skirted Grey Knights tank, an arcane Thousand Sons tank, a ram-fronted World Eaters tank, a horn-cannon Emperor's Children tank, a rounded mortar tank for Death Guard, a hover-pad Votann bike and a Votann heavy tank, a hooded Agents trooper and an armoured car, a wedge Custodes grav tank, a Sororitas flame tank, a big T'au heavy battlesuit (big battlesuits use it now), and a horned small walker and war engine for the Chaos Knights. GSC and Drukhari were already their own. Chapter recolours stay. Test: no two looks share a figure (a look may reuse one of its own, e.g. Tyranids' beast as big and vehicle) On main 30 Sep with the beta merge (T-452).
T-371done30 Sep
A3: portraits
More
About 24×24 portraits of each look's troops and elite, a Figures.portrait(faction, slot) helper, used on the picker's faction tiles, the unit page header (T-357) and My army cards (T-354). On beta: 46 original portraits (23 looks × troops and elite, head and shoulders, our palette letters) as Figures.PORTRAITS; Figures.portrait(army or look, slot) wears a Space Marine chapter's colours. The picker's army tiles show the troops portrait in a rounded frame, My army's list cards the elite one, and a unit page's header its slot's portrait for infantry and characters (bigger units still stand whole). node tools/sprites.js --portraits adds them to the gallery. Tested in figures.test.js On main 30 Sep with the beta merge (T-452).30 Sep: kept behind the Army pictures switch (T-489), figures by default; not retired, Jordan may redraw them.
T-372done30 Sep
A4: animation frames
More
2-frame idle sway, a hit flash, an aim/fire frame, a swing frame, and each look's own fallen frame; shootout.js uses them behind the ✨ Effects switches; reduced motion stays still | On beta:Figures.pose(map, kind) makes each figure's frames from its own map (so all 83+ figures and every future one have them): sway (the body above the waist a pixel forward: the 2-frame idle, each figure on its own beat), aim (a pixel back: the recoil as a shooter fires, with the existing muzzle flash) and swing (leaning in, the head two pixels forward: the front rank while its blows land, in both charges). The hit flash and the fallen frames (lying, belly-up or wrecked by family) were already there and stay. A new ✨ Effects switch, "Figures sway, aim and swing" (on; under Dynamic), turns them off; reduced motion and the finished battle stay still. figures.test.js checks every figure's frames keep their pixels and feet. Choice made overnight: frames derived from each sprite rather than hand-drawn per family, so none is missing; hand-drawn fallen frames per look can follow if Jordan wants them On main 30 Sep with the beta merge (T-452).
T-373done30 Sep
A5: battle backdrops (the free part of T-105)
More
Five original pixel backdrops (open ground, ruins, desert, ice, void), a tile strip each, picked in the ✨ panel and kept in hre.fx.v1; default plain. On beta: five strips of our own scenery (16×48 pixels, seamless; no insignia or iconography): open ground (earth, grass tufts, rocks), ruins (broken wall, arch, rubble), desert (dunes, stones, a dry plant), ice field (snow banks, ice spikes, a crack), the void (stars, a ringed planet, a floating rock, a thin deck). Figures.BACKDROPS and Figures.backdropSvg; the battle draws the chosen one faint along the ground behind the chart (45% in dark, 55% in light; 2× on phones, 3× wider). ✨ Effects › Scene › Backdrop: Plain (the default) or one of the five, kept in hre.fx.v1 as bg, off with Classic. Free, like the rest of ✨ Effects. Test in shootout.test.js On main 30 Sep with the beta merge (T-452).
T-374done30 Sep
A6: docs/icons.js, the 9-pixel glyph set
More
The picker's glyphs (list, target, die, clock, shield, flame, alien, trophy, star, swords, upload, person, army) as one module, replacing emoji in controls (tabs, sheets, footer, share menu, Meta); emoji stay in the reactions and where they are content. On beta: docs/icons.js holds the picker's twelve glyphs plus eleven new ones of ours (upload, chat, swap, sparkle, link, film, chart, print, camera, compass, search), Icons.svg(name) and Icons.fill(root) for [data-icon] markup; picker.js now draws from it. Emoji replaced in controls: the My army and Meta tabs, the attacker/defender labels and modifier headings, Switch roles, ✨ Effects, Share, the GIF button, The full odds, the My army/Matchup/winning lists/rankings headings, Game plan (button, Print, Save as image), the footer's Pick your army and Take the tour, the feedback strip, the side cards' role marks, the My army door, the list sheet's Game plan and best-unit badges, the joined-unit mark, the picker's search, the Meta hub's doors (built into /meta/ too). The strings lost their emoji (the glyph sits beside the text). Left as they are: 👑 and ⚡ on leader and detachment chips (markers the effects are named by), ✕ ✎ ↺ ⇄ (plain symbols), and emoji inside the pixel reactions and content On main 30 Sep with the beta merge (T-452).
T-375done30 Sep
A7: army glyphs
More
36 original abstract emblems in the 9- or 12-pixel style (our shapes and colours; nothing like GW's faction icons) for the picker tiles' corner, list cards and the Meta filter | On beta: 36 glyphs, 12×12, in each army's look colours (a chapter's own), as Figures.GLYPHS, Figures.glyph(army) and glyphSvg(army, scale): the picker's army tiles (top-left corner), My army's list cards (before the name), the Meta filter's army link and the Meta hub's army list. Each is our own shape, named for what it is (spiral, keyhole, anvil, hazard plate…); four were flagged for Jordan to look at first: Space Marines' arrowhead, Genestealer Cults' ripple, Salamanders' heat haze, Adeptus Titanicus' hazard plate. invariants.test.js checks every army has one and no two are alike Jordan (29 Sep): keep all four flagged glyphs as drawn.On main 30 Sep with the beta merge (T-452).
T-376done30 Sep
A8: the brand lockup, wherever it isn't live yet
More
Check header, static pages, OG image, favicons and the run-it mark against the 28 Sep brief; finish what's missing | On beta: checked against the 28 Sep lockup (design/logo/README.md): the app header, the footers, the OG image, the favicons and home-screen icons and the run-it mark were live; the static pages' header lacked the tagline ("Purge the odds.") under the name, which now shows there on wide screens as in the app (hidden under 900px, as in the app) On main 30 Sep with the beta merge (T-452).
T-377done30 Sep
B1: squad nicknames, the free part of T-047
More
✎ beside a list unit's name in My army and the calculator's card; shown in the battle, the tally, the game plan and share images; kept per list in hre.rosters.v1; our own name generator (army-flavoured adjective + noun from our word lists, no GW names or lore), one tap to reroll | On beta: docs/nicknames.js: Nicknames.suggest(army, seed) (an adjective and a noun from our own everyday word lists, six families: imperial, chaos, bio, xenos, orky, necron; no GW names or lore terms), clean() (28 characters, plain text) and the editor (a small dialog: the name, a die for another suggestion, Save, Cancel, Remove). ✎ sits beside the unit's name on a side's card while its unit is one of the side's list, and on each unit row of My army's list; the name is kept on the unit (nick) in hre.rosters.v1 and in the list's saved copy in hre.library.v1. It shows on the card's button (the datasheet's name under it), the list pill and list rows, the battle's banners and the ten-roll tally (the shootout's attName/defName), the GIF and clip caption, the share PNG and the game plan ("Name (Unit)"). Plausible: Nickname with set yes/no (never the name). Tests: unit/nicknames.test.js, browser/nicknames.test.js On main 30 Sep with the beta merge (T-452).
T-378done30 Sep
B2: 16-bit hero sprites (T-166), behind a flag
More
A hero variant of each look's elite figure (chunky outline, 3-tone shading, our own sprites; not Square Enix's or GW's art): a 4-frame walk and a victory pose, for a nicknamed squad in the battle; flags.json on beta only | On beta, behind a flag: 23 hero sprites (Figures.HEROES, Figures.hero(army)), one per look: the look's elite drawn bigger (17 to 22 pixels wide) in a 16-bit battle-sprite style of our own (a full dark outline, three tones: two new palette letters, p and s, are the shades of the primary and secondary), each with a 4-frame walk (the stand and two stepping frames, the far leg shaded) and a victory pose. Our own names (Veteran captain, War boss, Haloed champion, Suit captain…); nothing traced from any game or GW model, no symbols. In the battle, a nicknamed squad (T-377) fights as its hero: walking when it charges, its victory pose once it has wiped its target out; a nicknamed target stands as its hero. "heroes": "beta" in flags.json (on the beta site only; ?heroes=1 anywhere; true everywhere, false off). node tools/sprites.js --heroes=file.json previews them. Tested in figures.test.js and browser/nicknames.test.js Jordan (29 Sep): the flag stays "beta" for now, heroes on the beta site only, even after Block 2 reaches main.Held (Jordan, 29 Sep): until the portraits pass review under T-405's house style; then the heroes are redrawn to the same rules plus the walk and victory frames. "heroes": false in flags.json meanwhile.Redrawn (30 Sep) under T-405's house rules: all 23 in the portrait outline O, human faces and hands in the one skin pair (K/k), dark only at the eyes, one faction cue each, and their walk and victory frames redrawn to match; figures.test.js allows the new letters. "heroes": false still, until Jordan reviews the gallery. Open for his call: Custodes' hero carries a spear where its elite portrait has a shield; Daemons' hero stands on two legs where its portrait is a hound; the two Knights share their leg frames; the orks' tusks and horns may read as two cues On main 30 Sep with the beta merge (T-452).
T-379done30 Sep
B3: a squad named after you (T-154) and squad fame (T-155), data side only
More
Worker storage schema and T-169 moderation hooks, UI off; the card says what a supporter would see | On beta (data side only; nothing on the site calls it, and the Workers only deploy from main): the supporters Worker keeps named squads in KV (worker/src/squads.js): /squads gives a signed-in supporter 1, 2 or 3 slots by tier (active payments or a pass only), one datasheet and name per slot, a slot changing once a week; each named squad gets a random id (sq_…), never linked to the email; /names?f=&u= lists a datasheet's live names for the site to rotate through. T-169's hooks: worker/src/moderation.js (our own short swear list with look-alike digits folded, whole-word checks so "Titan" and "Assault" pass, links and markup refused, and a BLOCKLIST secret for anything harsher so slurs never sit in the repo); /report hides a name at once and emails REPORT_TO; /reports and /moderate (pull or restore, with the ADMIN_KEY secret) are Jordan's. The data Worker keeps fame totals in D1 (fame: faced, slain, wiped, lost per squad id, live and beta apart; /fame add and read, from the site's pages only, capped per network). Tested in tests/unit/squads.test.js. What a supporter would see (T-401): in My army, a "Your named squads" card with a slot per tier (an empty slot: "Name a datasheet"; a filled one: the name, its datasheet's figure, "can change on 6 Oct"), picking the datasheet from the unit picker and typing the name, with our filter's message if it's refused; their squad's fame under it (faced 214 times, 1,380 models slain, 37 wipes, wiped out 12 times). Everyone else sees supporters' names rotating on those datasheets in the answer card and battle caption ("Clyde's Necron Warriors"), with a small ⚑ to report one On main 30 Sep with the beta merge (T-452).
T-380done30 Sep
B4: squad RPG groundwork (T-163)
More
A local-only experience counter per nicknamed squad (fights simulated, targets wiped) as a small badge; no server; the levels and achievements design carded for Jordan | On beta: a nicknamed squad (T-377) keeps its record on this device, on the unit in its list (xp: {f, w}, saved with hre.rosters.v1 and the library copy): each battle played with it (a volley, or each of a ×10's ten; as attacker or target) and each target it wiped out. A small badge beside its nickname on the side card and in My army reads "12 fights · 3 wipes". No server, nothing sent. Fixed on the way: a second ×10 drew its end first and counted its ten results twice in the answer card's tally. The levels and achievements are proposed in design/squad-rpg.md with three questions for Jordan (T-402). Tested in browser/nicknames.test.js On main 30 Sep with the beta merge (T-452).
T-381done30 Sep
C1: six more pixel reactions (T-156)
More
Overkill, whiff, clutch, standoff, flawless, massacre, original; the tumbleweed/headstone fallback when memes are off | On beta: six original pixel reactions (32×32, 14 to 16 frames, our own little troopers, bugs and dice; no text, no GW art, nothing from an existing meme): whiff (three shots swerve round a bullseye, the trooper slumps), standoff (a dented helmet, a plaster, both shrug), flawless (three dice land on six and a tiny crown drops), overkill (a bug under ever bigger dice), massacre (a squad topples like dominoes; one helmet rolls away), clutch (the last die teeters and lands on six). When: not one hit → whiff (hits that hurt nobody stay Jordan's "zero"); hurt but none slain → standoff; every attack hitting and wounding → flawless; a wipe in one volley with twice the damage it needed → overkill, of ten or more models → massacre, by the volley's last attack → clutch, else the wipe as before. Sources in design/reactions/, built into docs/data/reactions.json by tools/build_reactions.js (drawn facing the way they play, not mirrored). With memes off (flags.json) they fall back to the tumbleweed and the headstone (and the new calm ones play nothing but the whiff's tumbleweed). Still behind the ✨ Effects reactions switch (off by default); its label now reads "Reactions to misses, wipes and flawless rolls". Captions ours ("Not one hit", "That was a bit much"…). Tested in unit/reactions.test.js and browser/shootout.test.js On main 30 Sep with the beta merge (T-452).
T-382done30 Sep
C2: share images
More
The PNG/GIF with the lockup and backdrop; a vs card for a pair with both portraits; the game plan PNG's header | On beta:ShareKit.lockup() draws the site's lockup on a canvas (the pixel dice beside TACTICAL over REROLL in Cinzel, REROLL 1.25×, as the header), after lockReady() loads the dice and the font. The matchup picture is now a vs card: the lockup top left, the battle's backdrop across (the one picked in ✨ Effects, else the ruins), each side's portrait (T-371) in a framed tile facing the other, VS between, then the answer and the funnel as before. The GIF and the clip carry the lockup in their top band; the game plan PNG opens with a dark band with the lockup and our address, as the site's header. The loadout face-off (T-383) uses the same lockup. Existing share, GIF and game plan tests pass On main 30 Sep with the beta merge (T-452).
T-383done30 Sep
C3: loadout face-off image (T-175)
More
Two loadouts of one unit side by side as a PNG | On beta: the unit card's best-loadout section has "Face-off picture: as it comes vs best" when the best all-rounder differs from the default wargear (943 units have a best loadout). It makes a 1200 × 630 PNG with our lockup: the unit's portrait (or figure), name, army, models and points; "As the datasheet comes" (its default weapons, names only) against "Best all-rounder" (the tier list's squad); into each of the five standard targets, the two numbers back to back from the middle, the better one lit; and a verdict line ("does more into 4 of 5 targets"). The numbers are the tier list's own (wounds removed per 100 points, the better of shooting and fighting). Shared like the matchup picture (share sheet, clipboard or download); Plausible Faceoff Shared. Tested in browser/rankings.test.js. Choice made overnight: default vs best all-rounder, the pair every unit with choices has; picking any two loadouts can follow (it needs the solver in the browser) On main 30 Sep with the beta merge (T-452).
T-384done30 Sep
D1: units' own abilities in the maths (T-343)
More
data/unitfx.json in our own words (like detfx.json), the 40 most-taken units first: to-hit/to-wound bonuses, re-rolls, charge attacks, anti-X, damage reduction; engine behind flags.json "unitfx"; parity tests; a coverage list. On beta: docs/data/unitfx.json: the 40 units most taken in winning lists (June to September), each datasheet read on Wahapedia's 11th edition pages and written as dice effects in our words (the format of leaderfx.json): 25 have effects (36 in all), 15 have none that touch one attack's dice (each recorded with a short reason). Left out on purpose: invulnerable saves and Feel No Pain BSData already gives (four duplicates removed), wargear abilities, Leader abilities (leaderfx.json), damaged profiles. In the calculator only when flags.json's "unitfx" is true, or "beta" on the beta site (set to "beta" here: on at tacticalreroll-beta.pages.dev, off everywhere else), or with ?unitfx=1 for a look: each effect a chip among the modifiers (switchable), a choice in the unit's sheet, conditions it can't know start on with their words shown ("on the turn it charged"). Not yet in Army vs Army, Rankings or unit pages (the scores stay datasheet-only). Coverage and the research's open questions: design/unitfx.md (Flayed Ones' every-hit-critical approximated as criticals on 3+; the Sunforge's damage re-roll and Prosecutors' Precision have no mod yet). Tests: unit/unitfx (real units, known mods, short labels), browser/unitfx (off by default, on with the flag, chip switches off, a choice in the sheet, the Nightbringer's -1 Damage from the real data) Jordan (29 Sep): on everywhere once Block 2 reaches main; flags.json now says "unitfx": true on the beta branch.On main 30 Sep with the beta merge (T-452).On main (Jordan, 30 Sep 19:20): flags.json "unitfx": true, 1 Oct; 24 of the 40 units change the dice in the calculator (the rest only note an ability), shown as ⚡ chips on the card's Modifiers line (switched like the leaders' crowns) and counted on its head; the rankings and unit pages don't read them yet
T-385done30 Sep
D2: data audits
More
Reports in build/: wargear that didn't read cleanly, weapons with no profile, units with zero damage into every target, points/size against BSData, same-name profile pairs, copies over models, duplicate-pick options. Fix ours, list BSData's. On beta:node tools/audit.js writes build/audit/audit.md and .json (summary: --json); unit test tests/unit/audit.test.js. Found and fixed ours: the best-loadout search priced a Hazardous weapon at the whole unit's points on one model, so a pistol beside a two-profile gun (Hellblasters) looked unreadable; now the squad's points per wound (tiers: Abaddon C→D, Aquilon Custodians A→B). Left: 3 kits still unread (Ynnari Kabalite Warriors, Crusader Squad, Centurion Assault Squad; with T-365), 10 units that remove nothing (all without attacks: fortifications, Drop Pod, Spore Mines), 50 vehicles with 3+ of one gun (real), 124 dual-profile names (T-350, fine). BSData's: Kill Tank's Deff Rolla has no profile; Obliterators, Emperor's Children Chaos Terminators and Atalan Jackals price sizes outside their size range On main 30 Sep with the beta merge (T-452).
T-386done30 Sep
D3: DB-wide invariant tests
More
Every default loadout legal; ev.js and app.js attack counts equal for every unit; every unit a figure; every army a page and a glyph; every i18n key used exists in every language. On beta: tests/unit/invariants.test.js: every unit's default wargear fits a legal squad at its smallest size (733 units; the known gaps are T-365's and T-399's, and the test fails when one closes so the list shrinks); every weapon profile's mean attacks equal in ev.js and app.js, worked out exactly (app.js's own dice fed every face once, not sampled), with Rapid Fire and Blast, the twelve keyword flags read the same, and each unit's default wargear summed; every unit gets a figure; every army and every unit on the search list has its page; every i18n key named in any script or page exists in every language. ev.js exposes _kw and _defaultLoadout for it. The army glyph check arrives with T-375 On main 30 Sep with the beta merge (T-452).
T-387done30 Sep
D4: "on the rise" sparklines
More
Per unit, from metatrack.json, on unit pages and in the Meta filter. On beta:MetaHub.series/sparkSvg (metahub.js, shared by /meta/ and the app's Overview): a unit's share of its own army's winning lists, month by month, as a small line of our own (a dot on the latest month, a faint baseline, gaps where the army had no lists; the words in its aria-label and tooltip). In the Meta hub's Units panel it replaces "of its army" in each row's last column (the panel note says "and month by month"), so the army filter (T-359) shows each of the army's units with its months; on a unit page, a "Month by month" line under the Meta sentence when some month took it (35 Tyranid pages, most Necron ones). Four months of data so far (June to September), so the lines are short and jumpy; each line scales to its own highest month On main 30 Sep with the beta merge (T-452).
T-388done30 Sep
D5: army tiers from placings (T-332 groundwork)
More
Compute and store once the corpus is large enough; shown only on beta. On beta: build_pages.js placings() writes armytiers.json's placings: each army's lists over the corpus's last three months, average finish, top-4 count, and a tier only at 40+ lists (T-332's threshold), by T-332's bands on the finish (S 55%+, A 52.5%+, B 49%+, C 46%+). The Meta hub (/meta/ and the app's Overview) draws an "Army tiers from placings" panel only on beta hosts (MetaHub.betaHost: tacticalreroll-beta.pages.dev, beta.tacticalreroll.com, local copies): fetched there, never written into the page, so tacticalreroll.com can't carry it even after a merge. Today: 0 of 33 armies are there (Necrons 33 lists, Custodes 28, Dark Angels and T'au 23). node tools/report.js prints the same for T-332's monthly check (28 Oct). ARMY_TIERS_ON is untouched On main 30 Sep with the beta merge (T-452).
T-389done30 Sep
E1: accessibility pass
More
Focus traps in every sheet, Esc closes, tab order, aria-pressed on toggles, 44px targets, contrast in both themes (tier chips too), reduced motion. On beta: Tab stays inside the open sheet or dialog on top (app.js trapTab/topLayer: modal first, then the highest stacked), wrapping both ways, and steps in from outside; Esc already closed every sheet and focus goes back to the opener. Contrast: --text-faint was 3.7–4.0:1 in dark and 4.3:1 in light, now #8391A7 / #5B6B80 (4.5:1+ on every panel); light --lead #806500; white text sits on new --att-fill/--def-fill/--green-fill (the unit name buttons, primary buttons, the Meta switch, the profile switch were 2.5–3.7:1); --def-text for purple text on dark panels; the five tier chips all pass in both themes. Targets: every control is 24px or more to tap (WCAG 2.2 AA; small-looking ones keep their look and get an invisible hit area; inline links in sentences are exempt): the weapons sheet's profile switch and Change its numbers, the fold arrows, Charts on top, 🔗, the Rankings sort buttons, the Meta FAQ, See every army. The 44px of the card went to 24px (decision for Jordan: 44 would mean re-laying out the steppers, chips and sort row). Rankings sort buttons say aria-pressed; the other toggles already did. Reduced motion: a catch-all under prefers-reduced-motion stops every animation and transition. Test: tests/browser/a11y.test.js (the trap, contrast of tokens and chips in both themes, targets on four screens and the sheet) On main 30 Sep with the beta merge (T-452).
T-390done30 Sep
E2: performance
More
Lighthouse on phone for /, the /matchup/ redirect, a unit page and Meta; lazy-load figures and gif.js; the sw.js cache; before and after numbers. On beta: measured with Lighthouse 12 (mobile) on the live site, before: calculator 32 (FCP 1.8 s, LCP 4.9 s, TBT 1.15 s, CLS 0.52), /army/ 33 (CLS 0.32), a unit page 92 (CLS 0.15), /meta/ 77 (CLS 0.15). The biggest loss was layout shift, so the fixes go there: the calculator's footer started on screen while the page waited for its data and was pushed down twice (0.32 of its 0.37 shift with data held back 3 s); main now starts a screen tall, which leaves 0.04 (the "New here?" doors folding away). On static pages the shift was the web fonts swapping in: text now waits in Arial sized to Inter's metrics (Inter Fallback), so the swap barely moves it; a preconnect to the font files' host too. Tried and dropped: loading the font stylesheet without blocking the first paint halved FCP (3.6 s to 1.8 s locally) but made the calculator's shift 0.54 as the fonts arrived. Not changed: gif.js is 6 KB and figures.js 48 KB, both needed by the pixel battle on first paint, so lazy-loading them buys nothing; sw.js is network-first for site files and keeps BSData by commit, fine. Found for later: the calculator fetches rankings.json (354 KB) on start, and a first visit's LCP is the thank-you card (news.js) appearing after the data. After numbers on the live site need a beta deploy (the GitHub connector was down) On main 30 Sep with the beta merge (T-452).
T-391done30 Sep
E3: dark mode audit
More
Every new component (picker, pills, Meta filter, unit page cards), screenshots. On beta: dark screenshots at 390px and desktop of the list pills with a list loaded, the picker (units and its Upload level), My army with a list, the Meta filter's units with their sparklines, and a unit page top to bottom. Fixed: the picker's paste box was the browser's own grey box (now the panels' colours, with a faint placeholder); every white-on-blue control used the bright blue at 3.7:1 (the picker's and sheets' Done, the modifier chips, the segment switches, the weapon-sheet profile switch, the unit page's Best per target / One loadout for all, the vote and tag badges), now --att-fill at 5.2:1. Fine as they were: pills, My army cards and strength tiles, the Meta rows and sparklines, the unit page cards and meters (T-389 had already fixed the faint text and chips). Headless Chromium draws a native select light whatever its colours; real browsers follow the theme (the select's own colours are right) On main 30 Sep with the beta merge (T-452).
T-392done30 Sep
E4: print styles
More
The game plan and unit pages. On beta: every static page (unit, army, detachment, guide, Meta) prints black on white whatever the theme, with every folded section opened for the print and folded back after (a one-line inline script on beforeprint/afterprint), the header's nav and search, the rating box, Simulate it, Try it, the run marks and the loadout switch left out, tables in 8pt, army tables without the five per-target columns (they don't fit A4; the unit's own page has them), and a "Printed from tacticalreroll.com/…" line at the bottom. A Hive Tyrant page prints on 3 A4 pages, Tyranids on 4. The game plan keeps its own print rules (T-159), not re-checked in this card. Test in pages.test.js (sections open and fold back, nothing wider than A4, no controls, white, the source line) On main 30 Sep with the beta merge (T-452).
T-393done30 Sep
E5: translations groundwork (T-120)
More
The pseudo-language over every string since T-347; the site's own words as a translatable set; machine drafts of French, German and Spanish marked draft and OFF until reviewers are found; GW names never translated. On beta: the pseudo-language pass (?lang=xx over the calculator, the weapons sheet, the picker, My army and Meta) found no hard-coded site words: every unbracketed text left is a game name from BSData (units, weapons, profiles, armies), which we never translate. node tools/i18n_extract.js writes i18n/en.json (1786 strings, the source for translators); i18n/drafts/fr.json, de.json and es.json are complete machine drafts, status: draft, live: false, never loaded by the site (a test checks), with game names, stat abbreviations, keywords and brand names kept in English. --check (in the unit tests) fails when en.json is behind docs/i18n.js or a draft loses a {placeholder} or tag. Reviewers' notes from the drafting: door.metaArmyS gets an English ordinal ("2nd ") from layout.js, so French and German read badly there (T-400); role labels that double as keywords (Battleline, Characters, Mounted…) are kept English in all three, a reviewer may prefer them translated; FR uses vous, DE du, ES tú; the pixel battle's sound words are invented per language. Decision for Jordan: where to find reviewers (T-120) On main 30 Sep with the beta merge (T-452).
T-394done30 Sep
E6: glossary
More
Our own terms (share slain, points removed, all-round loadout, benchmark targets, percentile, tier) on the Method page and guide, linked where they appear. On beta: "Words we use" on /about/#glossary: eleven terms in our words (share slain, wounds removed per 100 points, points removed, benchmark targets, best loadout, all-round loadout, percentile, Value score, tier, Meta score, and the funnel's 9 in 10 / average / lucky 1 in 10), each with its own anchor. Linked with a quiet dotted underline where they appear: a unit page's Value and Meta scores, Tier, and its Offense table's Target, Best squad, All-rounder and %ile headings; the guide lists every term before its FAQ; the calculator's funnel legend has an ⓘ to the bands. Test: every glossary link on those pages lands on a definition On main 30 Sep with the beta merge (T-452).
T-395done30 Sep
E7: privacy page against every new event
More
Re-read after each card that adds an event. On beta: re-read against all 46 events the site sends (none of Block 2's cards so far adds one): still true. The Analytics paragraph now says what events carry (a button or screen, at most an army or unit name, a side, a file type or a choice), that the page search sends only whether it found a unit, an army or nothing, never what you typed, and that no event carries lists, ratings or anything identifying. tests/unit/privacy.test.js lists every event with what it carries: a new or removed event fails it until someone re-reads /privacy/ and updates the list, so the re-read happens on every card by construction On main 30 Sep with the beta merge (T-452).
T-396done30 Sep
F1: visual regression baseline
More
The twelve key screens, both themes and widths, compared in CI with a tolerance. On beta: tests/browser/visual.test.js: calculator, weapons sheet, unit picker, My army, Meta overview, Rankings, winning lists, unit card, and the static /meta/, a unit page, an army page and the guide; dark and light, 390px and 1280px (48 shots), made still (seeded dice, fixed clock, reduced motion, the pixel battle off). Compared in the browser itself (no new dependency): a pixel differs past 40/255 in a channel, a screen drifts past 0.6%; drifted shots and red-marked diffs go to tests/visual/out/. Opt-in (VISUAL=compare or update) because pixels depend on fonts: .github/workflows/visual.yml compares on every push to beta and, run by hand with "update", makes the baselines on the CI runner and commits them to beta. Checked here: two runs match exactly; a swapped baseline fails at 23.8%. To do once the GitHub connector is back: run Visual with "update" on beta to make the first baselines On main 30 Sep with the beta merge (T-452).
T-397done30 Sep
F2: tests/run.js --changed and timings
More
A --changed mode, a per-suite timing report, the flaky-test list emptied. On beta:node run.js --changed runs only the test files touched by what differs from origin/main (--base=<ref> to change it): changed tests, tests naming a changed module, every browser test when a shared file changed (app.js, index.html, style.css, layout.js, i18n.js, run.js, helpers.js). Every run ends with the six slowest files (newlayout 54s, comparator 43s, shootout 30s). No flaky list exists: none failed twice in a row tonight On main 30 Sep with the beta merge (T-452).
T-398done30 Sep
F3: tools/report.js
More
Prints the data audits (T-385), the Plausible goals (T-317) and the open cards by status, for the morning summary. On beta:node tools/report.js prints the audits (T-385), T-317's goals and the weekly report's events with no goal data, the cards by status, and the latest weekly numbers (with the T-362 search line) On main 30 Sep with the beta merge (T-452).
T-405done30 Sep
Portraits: the house style, and a redraw pass (Jordan, 29 Sep)
More
The rules, verbatim, in design/portraits.md: the house style for portraits (T-371) and for the hero sprites (T-378). On beta: 21 portraits redrawn by the rules (marines, agents, drukhari, deathguard and chaosknights from scratch; tyranids' elite; sororitas' halo a 1-px ring, aeldari's elite hair one shape, thousandsons down to one cue, greyknights' visor slit, csm's mace clear of the horns), in a new outline letter O (#140e2b); the nine Jordan passed are unchanged. A slot with no portrait gets the trooper figure at 2× (the reject rule). The gallery (node tools/sprites.js --portraits) shows each on dark and in the boxed light render. figures.test.js checks the redrawn ones: outline exactly around the silhouette, dark only at the eyes, something on row 9. Not yet reviewed: gsc, daemons, knights, tyranids' troops. Before/after gallery in the summary for Jordan On main 30 Sep with the beta merge (T-452).30 Sep: kept behind the Army pictures switch (T-489), figures by default; not retired, Jordan may redraw them.
T-406done30 Sep
Portrait follow-ups from Jordan's review (29 Sep)
More
Accepted the T-405 set with these: skin (a rule, added verbatim to design/portraits.md: any visible human face or hand in one neutral skin pair, K #d9a877 and k #a9764b, never the look's colours); repainted guard, agents and sororitas (both slots) and votann's elite cheek; Emperor's Children and the Votann troops are helmet faceplates and visors, so left. Redraws: the tyranid elite (the troops' head, one crest, a bone blade) and Imperial Knights (both: a red helm with the visor slit and one crest, as the Chaos Knights). Sororitas troops: a 1-px ring above the hood, so it isn't Mechanicus's red hood. Picker tiles: the corner glyphs gone (portrait, name, count); glyphs stay at 16 px or more: the Meta filter and army list, My army's list cards, now the static faction pages' heading (24 px) and a unit page's army line. Titanicus (both) listed last in its alliance's tiles. Found by the repaint, not fixed (older portraits Jordan passed): guard, Emperor's Children and Votann outline in D, not O; guard's elite eye is 1 px; Votann's troops visor is 10 px On main 30 Sep with the beta merge (T-452).30 Sep: kept behind the Army pictures switch (T-489), figures by default; not retired, Jordan may redraw them.
T-407done30 Sep
Portraits: the last three looks to the house rules (Jordan, 30 Sep)
More
Guard, Emperor's Children and Votann (both slots) still outlined in the old dark and drew inner lines with it. On beta: outlined in O, inner lines in the surface's own shade, dark only at the eyes or the visor; Guard's faces redrawn with two-pixel eyes (the officer's were one) under the helmet's brim, the officer's sabre raised clear of the head; Votann's troops visor a 4-px slit (it was 10), the elite's eyes lit over their sockets on row 9; Emperor's Children's visor a 4-px slit, the elite's floating sound dots one outlined arc. figures.test.js now holds all three to the rules. Before/after in the summary for Jordan On main 30 Sep with the beta merge (T-452).30 Sep: kept behind the Army pictures switch (T-489), figures by default; not retired, Jordan may redraw them.
T-408done30 Sep
Portraits: GSC, Daemons and Tyranids' troops to the house rules (reviewed 30 Sep)
More
The last five portraits drawn before the house style, reviewed against design/portraits.md; all five miss it the same way: outlined in the old dark with inner dark lines (mouths, teeth, brims, skull ridges); no shade tone on any surface (base and highlight only), and highlights in two clusters; dark mouths well below the eye line. Per portrait: gsc troops (Cult miner) goggles on row 10, head rows 2–16, shoulders from row 18; gsc elite (Four-armed ambusher) ridged skull drawn in dark lines, eye on row 10, head rows 1–17; daemons troops (Horned imp) eye 3 px on row 10, head rows 6–16, shoulders from row 19; daemons elite (Hell hound) eye 3 px on row 10, no elite accessory, and a hound where its hero stands on two legs (Jordan's call, from T-378); tyranids troops (Swarm gaunt) eye right (2 px, row 9), bone crest in one tone. Non-human, so they keep their own skin tones. The redraw keeps each silhouette and cue, then adds them to figures.test.js's house list On beta (30 Sep): all five redrawn to the rules, each keeping its silhouette and cue: the O outline only around the figure, dark only at the 2-px eye sockets on row 8 over 2-px lit eyes on row 9, mouths and seams in the shade, three tones per surface, one highlight cluster; the miner's head on rows 3–14 and shoulders from row 15; the ambusher's plain dome and a talon each side clear of the figure; the imp's head raised; the hound kept a hound (Jordan hasn't said otherwise) with a broken chain from its collar as its accessory; the gaunt's crest in two tones. figures.test.js holds all of them to the rules now. Open for Jordan: the ambusher and the hound have one eye (in profile, as the Tyranids' elite) On main 30 Sep with the beta merge (T-452).30 Sep: kept behind the Army pictures switch (T-489), figures by default; not retired, Jordan may redraw them.
T-464done30 Sep · main
Session housekeeping (Jordan, 30 Sep): .claude/settings.json allows the build session's push to main and its merge commands (d308f4d0); CLAUDE.md gains the "Two sessions" rule under How work flows (8393971c), both committed by Jordan
T-419done30 Sep · main
The unit sheet opens to one fixed height (Jordan, 30 Sep, from his phone: "the drawers open from the bottom when clicked which doesn't look quite right"): built and live 30 Sep, then superseded by the accordion (Jordan, 30 Sep): T-429 replaces the sheet and the compact cards
T-424done30 Sep · main
Compact unit cards on the calculator are far too small (Jordan, 30 Sep: "the attacker buttons and defender cards are waaay too small when compacted, have them more opened and equal sizes, approximately the size of the defender card in the first picture with pop out arrows as they exist"): built and live 30 Sep, then superseded by the accordion (Jordan, 30 Sep): T-429 replaces the sheet and the compact cards
T-040done30 Sep · main
The repo renamed to tactical-reroll (Jordan, 30 Sep). Nothing on the site or in the workflows named the old repo (contact goes through hello@ and the feedback Worker, not GitHub issues); the tests and the Pages deploy ran under the new name and tacticalreroll.com serves the latest build; the two Worker deploys only run when their folders change, so they'll first run under the new name on their next change. Left for Jordan: this session's checkout still pushes to the old address through GitHub's redirect, and the claude.ai environment's repository setting still names honor-roll-engine