Tactical Reroll
⋯

Typical version against best version (T-683, phase 1): a preview

From reports/tiers/2026-10-05-typical.md , rendered when the site is built.

5 Oct 2026. tools/typical.js, tests/unit/typical.test.js. A preview: no ledger file, benchmark or page changes. Not measured: the exam comes after T-673, one change at a time.

In plain English

The ledger ranks every unit at its single best version: the best squad size, loadout and detachment. Reviewers grade the plain unit as people usually field it. Carnifexes show the gap: under Track B they rank 20th of 52 because the ranked version is a pair in Subterranean Assault (Value 921), while one Carnifex with its datasheet wargear in a middle-of-the-road detachment is worth 351. This tool works out each unit's typical version and blends it with the best one through game-wide dials, so "rank what's usually fielded" can be tried without touching the ledger. At 0 the ledger is exactly today's (tested to the bit).

Definitions

A unit's forms here are its own solo forms (one member, the unit itself), valued by the ledger page's compute() at the setting. A form can stand for several detachments (the engine groups detachments that give the same numbers); every detachment counts once, 14 for each Tyranid unit.

Sanity print: Tyranids at typicalW 0.5

Ranks of 52; "size", "det" are the ranks with only that dial at 0.5. Values per 100 points.

Track B (2026-10-05-trackB.json; mean |Δ rank| 1.96, 38 units move)

Unittodayat 0.5ΔsizedetBSDTValuereviewers
Carnifexes2025−52420921505768351636C C C C C
Winged Hive Tyrant89−189119911999999991099A B B B B
Hive Tyrant5505513411341120512051273S A S A A
Old One Eye2119+21822917917744744830B B A B C
Neurotyrant3932+73634472472470470471A – – A B
Hierophant3227+52931601601549549575D C C D –
The Red Terror2218+41921898898778778838A A S – –
Neurolictor118+310811211121107910791100S S S – S
Hormagaunts (×10)1023−1320141141599726453730S A B A S
Pyrovores (×1)3037−73827659193642155412A – – B B
Hive Guard (×3)2834−63329692342536277462A B A C C
Zoanthropes (×3)1520−521151010650849471745S A B B B

At typicalW 1: Carnifexes 20 → 35 (size alone 31, detachment alone 20), Winged Hive Tyrant holds 8, Hive Tyrant 5 → 4.

Track A (2026-10-05-step34b-baseline.json; mean |Δ rank| 1.00, 26 units move)

Unittodayat 0.5ΔsizedetBSDTValue
Carnifexes323203232840840757691782
Winged Hive Tyrant34−13432733273278327833028
Hive Tyrant2202235363536321932193378
Old One Eye13130131318231823166816681746
Neurotyrant3025+53026948948942942945
Hyperadapted Raveners97+29722402240193719372089
Hormagaunts1830−1222221274893782619892
Termagants3843−54338591420514363472
Tyranid Prime with Lash Whip79−27922692269187118712070

Under Track A the ranked Carnifex is already a single one (B = S); the detachment half takes 10% off them as off most units.

Reading it. The size half does the work on units with two sizes (Hormagaunts, Pyrovores, Hive Guard, Zoanthropes, Carnifexes): their best version is the bigger squad. The detachment half takes 10 to 25% off nearly every unit, so it moves ranks only where a unit's best detachment stands far above its median. The Winged Hive Tyrant (1199 → 999 on B) is less of an outlier than expected: every unit has one good detachment. Units the same everywhere (Neurotyrant) rise.

Predictions (written before any measurement)

Doubts

Scoring it, and the exam hook

score(bench, dials, { w, armies, withApart }) runs exam.js's armies through compute() and the dials and scores with exam.js's own pairTable and weighted (checked against ledgerstep.js shape() to 1e-12). At 0 it reproduces exam.js to the bit: Track A 59.43, Track B 59.43 on the 14 armies (59.41 and 60.08 with Votann pooled); Tyranids 79.26 and 77.68. It lacks the paired bootstrap, because exam.js's army() computes the values inside it. The hook: export army and boot from exam.js, and let army(A, settings, w, post) take an optional post(run, D, S) applied to each compute() result before values are read; here post = (run, D, S) => typical.adjust(run, D, S.typical), a setting carrying its typical dials beside sliders. Existing calls pass no post and are unchanged.

Run: node tools/typical.js [--bench <setting>] [--w 0.5] [--size x] [--det x] [--units a,b] [--md].