What the system is. onlymove.net is a problem trainer for the game of Go (baduk, weiqi). It serves one problem at a time on a 9×9 or 13×13 board — a whole-board position reached by play, in which exactly one move wins — the player answers by clicking an intersection, and the app tells them their kyu/dan rank. The decisive move may save or kill a group, win a capturing race, hinge on a ko, take the largest endgame point, or leave the local fight altogether; which of those a problem exercises is what the motif axes of section 1.14 name. There is no lesson plan and no curriculum: what a player gets is a stream of problems chosen to sit at their current level, reviews of ones they got wrong, and a statistics view telling them where they stand and what they are weak at. Both board sizes are served from one app and merge into a single rank; the pipeline keeps them as separate corpora but fits one model across both, and where that distinction matters below it is stated explicitly. Everything the player sees is computed on their own device from a published set of per-problem numbers. This document is about those numbers.
Three quantities drive everything a player sees: the per-problem rating (with its two companions, discrimination slope and guessing floor), the rank the rating maps to, and the per-problem and per-cell motif vector that describes what kind of skill a problem exercises.
They are built in that order, each depending on the last. Underneath all three sits a single engine — KataGo — which generates the problems, establishes their solutions, and supplies everything the system knows about a position that did not come from a human: its own move evaluations, and the internal activations of its network. Where the text below says "the solver" or "the engine", that is what it means. The rating is a latent-trait fit over the human attempt log, its per-problem features drawn from the network's activations and from how engines of graded strength fare on each problem; the move evaluations serve the motif rules, the review scoring and the error readings, never the rating's input. The rank is a monotone map from the rating scale to a human kyu/dan scale, calibrated against self-reported ranks on external servers. The motifs are supervised axes fit to whatever between-user response variation the rating model could not explain. Everything after that — problem selection, the displayed rank, spaced repetition, the weakness readouts — is derived from those three on the client from raw numbers.
When the figures were measured. Nearly every choice here is justified by something that was measured, and the figures quoted below are measured against the system as it ships — the published problem set, the fitted model and the attempt log behind it — and re-measured after a refit rather than carried forward. The values shown were last measured between 2026-09-01 and 2026-09-11.
Problems are generated by a solver and identified by a hash taken over all eight dihedral transforms of the board, with a canonical orientation chosen by taking the minimum. Two consequences carry through the whole system: the set cannot contain two problems that are rotations or reflections of each other, and every problem has exactly one correct first move.
Both are load-bearing later. Symmetry-canonical identity is what lets ratings, activations, motif vectors and attempt logs all join on one key without any orientation bookkeeping. The single-solution property is what makes "the correct move" a canonical reference point, which the motif direction test relies on: without it, "the move the user played differs from the correct one along axis k" would have no well-defined sign.
The correct move is also the first variation written, and generation enforces that rather than assuming it. The solver settles which reply is the solution with a deeper re-read than the pass that annotates the candidate replies, so the move it names and the move the published evaluations name can disagree. When they do, the position is still perfectly well posed — exactly one reply wins on both win probability and score, which is the single-solution property above — but the winner is not the reply standing first. Generation resolves this by reordering the variations so the evaluations' winner leads, rather than discarding the problem, since nothing about such a position is wrong except the order.
The invariant earns enforcement because the two kinds of consumer read the solution in incompatible ways. The client picks it by evaluation and is indifferent to order. Every offline stage takes it positionally: the activation capture records the correct cell by looking up the first variation's coordinates, and the solver benchmark grades its own attempts against that same variation. So a misordered file feeds a losing move's activations into the rating features and into the problem's published motif vector, while the problem still plays correctly in the app and every artifact still builds — the failure is silent in both directions at once, which is why it is closed at the point of writing rather than checked for downstream.
A move counts as winning only if it still wins at the end of the game. Admission is not decided on the evaluation of the first move: the solver plays the winning reply out to the double pass and requires the winner to stay ahead at every step, then plays the best losing reply out the same way and requires the loser to stay behind at every step. Both lines have to hold to the finish, so the first-move evaluation and the terminal result must agree before a position exists at all, and a candidate that fails either line is discarded. This is what makes the single-solution property above a statement about the game rather than about a read depth: the one reply that wins wins when played out, and every other reply is refuted when played out.
The id is truncated, and the usual birthday worry does not apply. The identifier is the leading 32 bits of the canonical hash, so at corpus scale a couple of unrelated positions are expected to land on the same value. That is not a data-integrity problem here, because generation refuses to write an id that already exists on any board size and discards the candidate instead. A collision therefore cannot produce two different positions sharing one record, which is the outcome that would actually be harmful — it would silently re-point the attempt log, the activations, the rating and the published shard at different content. What a collision produces instead is a lost problem: a valid position that is never admitted, permanently, since any later rediscovery hashes the same and is skipped again.
That cost is close to nothing at the current scale — collisions grow quadratically but stand at about 6 expected across today's 220,000 problems, with a candidate rejected for an already-taken id about once in 19,000 — and the shape of the failure is the part that is a property of the design: the guard converts every collision into a discarded candidate rather than a corrupted record, and with candidates in effectively unbounded supply, losing a uniformly random handful costs nothing scarce. Widening the id would buy them back at the price of renaming every id-keyed artifact; that trade is worth re-checking, not re-asserting, if the corpus ever grows by an order of magnitude.
Each problem's move tree carries a per-branch evaluation — win probability and score lead from the solver — which is what the candidate-move signal below is read from, and lets a review be scored without any live engine call.
These are what the engine produces about a position, and they divide by where they act rather than by how they are measured: two of them — the activations and the bot pass rates — are the rating model's whole input row, joined there only by the board-size flag of section 1.3, while the other three never enter the model at all. They are the material the motif rules of section 1.14 are written over. Section 1.3 is that division and the reason for it.
Trunk activations. A neural Go engine is run on each position and the post-skip-add output of all eighteen residual blocks (eighteen × 384 = 6912 floats) is captured at every legal cell for the side to move, along with an index marking which cell is the correct move. This is the only signal that describes the position itself rather than the solver's verdict on it, and it is the only one available per cell, which is what later makes per-cell motif vectors possible.
What is stored is not the raw vector but a fixed 512-dimensional linear compression of it — per-channel standardization followed by a principal-component projection, fitted once on the cells the system actually compares: correct moves (drawn both uniformly over the problem set and in proportion to how often each problem is attempted) and the wrong moves players have actually chosen, in equal parts. The full vectors exist only transiently inside the capture; every later fit works inside the compressed space. The width is a measured choice, not a modelling one: it is set where refitting the downstream models inside the compressed space reproduces the uncompressed results within run-to-run noise even when the compression's own fit set is deliberately mis-specified, so the store can survive a later redesign of what "cells that matter" means without the raw vectors ever being recomputed.
Two structural facts about this capture propagate everywhere. Rows are in board-scan order, not move-tree order, so the correct move sits wherever its coordinates put it in that order — usually not the first row, occasionally the first, never reliably either — which is why the index is recorded rather than assumed. And the cell set is a superset of the move tree's root variations — every clickable move is embedded, some embedded cells have no variation, and the "pass" variation is never embedded at all. Every consumer therefore either selects by the recorded correct index or maps by board coordinate; nothing may index positionally across that boundary.
Bot ladder pass rates. A ladder of engine configurations of varying strength solves every problem, giving a per-bot pass rate. This is a difficulty proxy available for the entire problem set, including problems no human has ever attempted — which is exactly the population the rating model has to extrapolate to.
Candidate-move evaluations. Every root variation carries the solver's evaluation of the position after it — win probability and score, read in the mover's own frame — and what the rules take from them is the tightness of a problem: how close the solution and its best rival finish, in points and in win probability. Only variations played out to a conclusion enter such comparisons: an unresolved line's evaluation rests on a shallower search, and most refutations lose by margins that only noise separates, so ranking unresolved ones against each other picks between near-ties on numerical noise.
Game phase. Parsing each move tree gives the number of setup stones, the number of moves leading to the contested position, and the length of the principal line after the correct move. What the rules take from it is where in a game the position sits — the rule naming opening play turns on how few moves led to the position, with its bar set per board size, because game length scales with the board while the quantity being asked about does not.
Ownership swing. The engine also predicts, for every intersection, which side is expected to own it at the end. Reading that map after the correct move and again after the strongest refuted move, and differencing the two at the intersections that already carry a stone, says how much life and death the problem contains and whose. A swing of a whole unit is one stone changing hands, so the quantities are fractional stone counts: each side's stones split by whether they are alive or dead if the solver misses, and by which way the correct move moves them — the group the answer saves and the group it kills being the two that carry most of the signal, and the reverse pair measuring what a solution sacrifices. The territory swing over the empty points is carried alongside them, since a problem whose stones barely move while its territory does is a counting problem rather than a life-and-death one. The refuted move used as the baseline is one the generator read to a scored conclusion, not merely the highest-scoring alternative: most refutations lose by indistinguishable margins, so ranking them all by win probability picks between near-ties on numerical noise. A resolved pass is eligible to be that baseline like any other refutation, on the reasoning that the baseline asks what the position is worth when the solver misses the answer, and where every resolved stone move loses more than doing nothing the pass is the honest answer to that question — ranking it out would leave the comparison against a move no solver would prefer, overstating the swing. That the answering interface offers no way to pass does not bear on it: these quantities describe the position rather than the set of clickable points. On the current corpus the pass is the only refutation resolved for 26 problems, which would otherwise carry no swing measurement at all, and it outranks every resolved stone move on 0.9% of the set.
The two board sizes — 9×9 and 13×13 — share one model, so every feature column must mean the same thing on both. Each block is pooled across sizes before any reduction.
Activations are reduced by a single shared chain: standardize, randomized principal component analysis to 128 components, standardize again. Fitting one basis across both boards is what makes a learned activation weight interpretable on either. The full transform is retained, not just the projected values, because the motif fanout later needs to project arbitrary cells — not only correct ones — into the same space.
That width is as load-bearing as the dropout rate, and it is measured rather than chosen. Re-fitting the model at 16, 64, 128 and 256 and scoring each on problems the fit never saw — whole problems held out, since a row-wise split leaves every problem partly trained and would reward a width for fingerprinting what it has already seen — 128 wins, and it wins from both sides, at every seed run. Cutting to 16 costs 8.8 millinats and to 64 costs 7.1; widening to 256 costs 9.0, each consistent in sign across seeds. So the two directions cost about the same, and the reason to prefer the narrower of two similar arms is the standing one — the wider block gives the difficulty head more room to fingerprint individual problems, which is the exact failure the dropout below exists to suppress. The slope signature that argument is usually told with does not replicate: the widest arm carries the highest median slope at one seed and not the other, so it is the held-out cost, not the slope distribution, that carries the case here. The whole memorization argument in section 1.5 turns on this block being wide and near-unique, so the count belongs beside the dropout rate whenever either is reasoned about.
Bot pass rates are restricted to the bot set common to both boards, clipped away from zero and one, converted to logits, stripped of zero-variance columns and pooled z-scored. A bot missing a problem is a hard error, never a filled cell: after the pooled z-score the column mean IS the population's value, so an imputed cell asserts that the bot found the problem exactly average, and the problem then carries a rating, a slope, a floor and an axis vector priced partly on a number no bot produced. A bot whose coverage is partial is either re-run over the corpus or retired on both boards so its column leaves the layout.
Z-scoring at training time rather than at cache time keeps every cached block reusable across different problem subsets.
A board-size indicator is appended last. The final row is [activations | bot pass rates | size indicator], and that order is what section 1.5's feature-side dropout is defined over: everything ahead of the indicator forms one masked region. Masking as a region rather than one range per block is what makes the boundary the only thing a new block has to respect — placed before the indicator it is regularized automatically, whereas a mask per block has to be widened by hand and a block landing outside every range is regularized by nothing, with no symptom where it is used.
The signals that describe a problem through its answer stay out of that row, and what decides it is the per-cell fanout rather than their content. Move evaluations, ownership swing and game phase are all measured about the correct move: how tightly the answer beats its best rival, what the answer saves or kills, how long the line after it runs. The same network is later evaluated at every legal cell, so that the move a player actually chose can be scored and not only the move that was correct (section 1.14) — and there only the activation part of the row changes, every other column staying at the correct move's value. So each of those blocks hands every wrong cell a description of the right one, and an alternative's loadings come out conditioned on the answer rather than on itself. Measured across the axes, that conditioning reverses the sign of between-cell comparisons within a problem on a tenth to a third of them, depending on the axis — enough to change which cell a reading points at. Removing the blocks costs nothing on the rating: over nine paired holdout folds the two arms sit within a fraction of the fold-to-fold spread of each other on the combined likelihood, and agreement with declared ranks moves in the third decimal. Per-cell reliability is unchanged overall and the two ownership-derived axes improve, which is the direction the argument predicts. The excluded signals are not thereby optional. They are exactly the quantities the motif rules are written over, so they reach the fit as the fixed directions the axes are taught onto (section 1.14), and a corpus top-up that leaves any of them behind halts the labelling step rather than the fit.
Identifiers are checked for uniqueness before the set is built. An identifier is a truncated hash — thirty-two bits — so distinct positions collide at a rate growing with the square of the collection, and the generator resolves that by discarding a candidate whose identifier is already spoken for on any board or on the retired list. Nothing re-checks the result afterwards, so the fit does: an identifier naming a problem on both boards, or naming one that was retired, halts it. Both failures are silent otherwise. Two problems sharing an identifier make every keyed artifact ambiguous, including the published lookup that spans both boards; and a retired identifier back in the collection quietly inherits the attempts recorded against it before it left, so a new problem is rated on answers given to a different one.
The problem set is the corpus, and every block must cover it. A tsumego whose activation or bot row is absent halts the fit, naming the block and the problem; one whose ownership or phase row is absent halts the rule labelling instead, one step earlier — and the rules read a problem's move evaluations out of its own move tree, so those need no stored row to be complete. Neither of the two ways of continuing is available, because both are silent: imputing the block's mean rates the problem on a fabricated row, and dropping the problem removes it from the fit without saying so. The distinction that matters when such a halt fires is whether the identifier still has a move tree in the corpus — a live problem missing a row means the block is behind and needs rebuilding, whereas an identifier with no move tree at all is an artifact of a removed problem and must be deleted rather than regenerated. Deriving the set from any artifact listing instead reintroduces both faults at once, since such a listing can trail the corpus in either direction.
The training target is human first-time attempts only: repeats excluded, de-duplicated to the earliest attempt per (user, problem). Bot results never appear as observations — they enter only through the features. Adding bots as synthetic examinees was tried and rejected: it steepens the mean per-problem slope with no held-out benefit, because a bot ladder's response curve is not a human's.
The examinee is the user, and one ability parameter covers their whole history. There is no windowing and no drift model: every attempt an account ever made, however far apart, is scored against the same ability. Two things recommend that. It measures better on the rating side than the windowed arrangements, which are the obvious alternative and are what a drifting examinee would want. And it puts ability on the same granularity as the axis positions of section 1.5, which are per-user because loadings are identified by variation between people — a windowed ability would leave one factor of the same model resolved per window and the rest per user. That last point is worth stating as a dodge rather than a result: the mixed-granularity question is avoided, not answered, and it would come back the moment a windowed ability measured better.
What the single unit gives up is within-career drift, which lands in the residual. A user who improves substantially over a long career is one number, so their early attempts are scored as though at their final level and their late ones as though at their first. That is tolerable because ability is a nuisance parameter here rather than the estimand — no published quantity is a user's ability — and because the response curve's two asymptotes bound what a badly-placed attempt can do: a far-below clear is discounted by the guessing floor, a far-above slip by the lapse ceiling. Where a user's level at a point in time genuinely is the estimand, the drift is modelled explicitly instead of absorbed. Section 1.12's rank anchors cut each career into overlapping two-month windows for exactly that reason, and divide each attempt's unit mass across the windows covering it so an era cannot be counted twice.
Users are kept only if they have at least one correct and one incorrect first-time attempt, and that bar is an identifiability condition rather than a quality filter. Under the four-parameter response curve an all-correct record's likelihood climbs monotonically toward the ceiling as ability grows without bound, and an all-wrong record's toward the floor as it falls; neither has an interior maximum, so no ability estimate exists to be had. One outcome of each kind is exactly the condition for one to exist. Any higher threshold is a judgement about how precisely the ability is pinned, and the likelihood already makes that judgement on its own by giving a thin record few terms — so raising the bar discards usable rows to buy protection the fit does not need.
A four-parameter logistic with additional skill dimensions, one shared network for both boards:
P(correct) = floor + (1 − floor − lapse) · sigmoid( slope · (ability − difficulty)
+ Σ_k loading_k · position_k )
(difficulty, slope, floor, loading_1 … loading_K) = network(features)
(1 − 2·lapse), so the guessing floor structurally cannot cross the ceiling.The rating and the axes are one fit. Because a linear layer's output rows are independent, emitting the loadings from the same trunk as the response parameters is exactly a shared head, so the axis objective reaches the trunk and the representation is shaped by what the axes need as well as by what the rating needs.
Fitting the axes afterwards, against a frozen response curve, is the standard alternative and it costs the rating. Fitting one dimension first and exploring the residual second is the ordinary way to reach a multidimensional model, and it is cheaper — the response curve is fit once, and candidate axis counts are then explored against it without retraining. What it gives up is that the representation is settled before the axes are asked anything, so it can only offer them what the rating already found useful. Measured on the criterion that matters — a new user, predicted one attempt ahead from their first — the joint fit leads on the combined likelihood across both folds. The comparison is worth reading carefully rather than as a clean win: the staged fit's axes are the stronger of the two, and the joint fit leads anyway because its rating head gains more than the staged axes do. The staged advantage is also concentrated almost entirely in attempts on problems nobody in training had played, which is a fifth of them; across the other four fifths the two are close and both positive.
What keeps the axes from swallowing the rating is an asymmetry in the penalties, not a constraint on the arithmetic. The rating factor — difficulty, slope and the per-user ability — carries no penalty at all, while the positions carry a ridge and enter every prediction centred on their population mean — so a shift every user shares is not expressible through an axis at all — and each loading is normalized to unit root-mean-square across problems before it meets a position, so an axis cannot buy itself scale by inflating a loading and shrinking a position. Variation every user shares is therefore cheapest to express through the rating and expensive to express through an axis. That is what makes the first factor the average and the axes genuinely between-user, and it is checked rather than assumed: the worst correlation between any axis position and ability sits at 0.04, so no axis is doubling as a second ability estimate.
That asymmetry says nothing about an axis tracking a rating parameter problem by problem, and a separate penalty is what closes that route. Centring the positions blocks an axis acting as a global difficulty shift; it does not stop one whose loadings follow difficulty, slope or floor across the set. Left unpenalized a fit spends an axis on exactly that: with the penalty removed the worst axis end carries 0.66 against one of those quantities, against 0.15 under the penalty that ships. So the penalty targets all three of the shipped per-problem quantities together, rating, slope and floor — and closing one of them alone does not work, because the leak moves rather than going away. Penalising the rating column by itself does close it, to 0.19; but the same fit's worst end against the channels it left open then reaches 0.49, against 0.15 when all three are closed. The displacement is concentrated rather than spread: two axes relocate almost all of it, both onto the floor, and they are the same two axes at both seeds measured. Each quantity is centred and normalized before the comparison, so the term is a sum of squared cosines — the floor lives on a bounded interval and the rating on its own much wider scale, so an unnormalized version would be dominated by whichever happens to be widest. The three targets are held out of the gradient, which makes the penalty one-directional: it can push an axis off the rating side but can never degrade the rating to satisfy itself. That direction is the one that matters, because every rank the client computes reads the rating, the slope and the floor and never an axis. The loadings side of the term does reach the shared network, and that path is load-bearing for the rating itself: computing the penalty on loadings taken from a detached copy of the network's output, so that it can only re-weight what the axes read out, leaves the axes' identity where it was and predicts held-out problems -14.9 millinats worse, on every one of 8 seeds. What the term does for the rating through that path is not separated; that it does something is.
It is scored separately on each end of an axis, and the whole-axis form hides most of what it is for. A correlation over all problems is a linear statistic and cannot see a symmetric dependence: an axis whose two ends are rating-laden in opposite directions averages to zero and satisfies such a penalty completely, while both ends — the only part of an axis anyone reads a name off — remain laden. That is what happens when it is tried. A fit penalized on the whole axis reports a worst correlation of 0.21 and reads as rating-orthogonal on that basis, while its ends carry 0.32 — an understatement of 1.7-fold, taken within each fit rather than between averages. The mechanism is visible outright in single axes carrying opposite signs on their two ends against the same quantity, which a linear statistic averages toward zero. Penalizing the two ends separately cuts the worst of them to 0.34 in the shipped fit. The whole-axis figure rises slightly under that, which is expected — it stops being the statistic under optimization and was never the one that mattered.
The held-out cost of the per-end form is not established: against the whole-axis form on one seed every column differs by less than the spread between two fits of one configuration, so a single pair cannot separate them. It is adopted on the leakage measurement instead, which is a direct property of the fit rather than an estimate carrying its own error.
The defining structural choice is that there are no per-problem residual parameters. The shared feature network is the model. This is what gives never-attempted and one-sidedly-attempted problems a well-defined response curve — they get the feature-conditional prediction, which is a real prediction and not an artifact of having no data. An attempted problem departs from the feature-conditional mean only to the extent the network can memorize it through the activation features, which is precisely what the regularization below is designed to limit.
The cost is that a problem the map places wrongly stays wrong. Nothing in the architecture lets one problem's own outcomes speak about that problem: its rating is its fixed features through the shared map, so it moves when the map moves and not when people fail it. Attempts on a mis-placed problem do not correct it — they pull on the shared map along with every other attempt, and a problem nobody has touched since the last run is re-rated by a comparable amount anyway (section 2.5). This is the exact flip side of giving never-attempted problems a real curve, and it holds across the whole set rather than for some suspect fringe.
That per-problem error is not measured, and the one result bounding it bounds only part of it. The bound is section 1.10's frozen empirical-Bayes fit, which estimates the per-problem departure from the feature-conditional mean and finds it real but negligible after shrinkage at the observed density of a couple of attempts per attempted problem. That is the right instrument for whether there should be per-problem parameters — it measures what they would recover, and the answer is that they would recover little. It is a much weaker statement about calibration itself, for two reasons. It is identified only where there are attempts, so it is silent on the problems nobody has attempted — about half the published set on the current corpus, 48.0% of it, and exactly the population the map extrapolates to rather than interpolates over. (Bot pass rates are what cover that population at all, section 1.2; they are a feature rather than an observation, so they shape the map without saying anything about one problem's own departure from it.) And it estimates a population spread rather than issuing a per-problem certificate: it constrains how much idiosyncratic difficulty is distributed across the set, not how far the map can be off on any one position. The defensible reading is that the missing per-problem machinery is not identifiable at this attempt density — not that per-problem calibration has been checked and found good.
Dropout is 0.3, over the feature region as a whole — activations and bot pass rates, every column dropped independently — and between the trunk's hidden layers. The board-size indicator is the sole exception, and on different grounds from the rest: it identifies which board the row belongs to rather than anything measured about the problem, so a row that loses it is not a noisier example of the same thing.
On the feature side it is applied at column granularity — every column dropped independently — and that beats dropping a whole block on or off, for an instructive reason. Block dropout leaves the fingerprint fully intact on the kept fraction of rows, so the network learns a present/absent regime pair and memorizes freely inside the majority regime. Column dropout never presents the same wide code twice, so the exact conjunction that memorization requires is unreliable on every row while the smooth low-order signal survives everywhere. The dose response is a broad plateau that only degrades once so few columns survive that the mechanism reverts to the block regime — which is the mechanism's own prediction, and a useful confirmation of it.
Two further per-problem inputs are available and both cost accuracy at the widths that make them natural. Continuation evaluations — the line after the answer, the opponent's remaining tries, and how many children each node has — describe how much reading a problem takes, which the root cannot express, since the single-solution gate leaves one winning first move with every rival decisively lost. They raise agreement with declared ranks very slightly and cost held-out likelihood on every seed, the loss being around a tenth of the model's whole margin over predicting the base rate. The best alternative move's activations, projected through the same basis so the runner-up lands in the correct move's coordinate system, cost more and return nothing: they are worse on both measures. The reason is width rather than subject matter, and it is the same one section 1.3 gives for the component count — the alternative's vector doubles the activation region, and a wider activation representation gives the difficulty head more room to fingerprint individual problems. This also settles what a low correlation between a new activation block and the existing one is worth: it is not evidence of complementary signal, because a near-independent block is precisely a better second fingerprint. Either input would have to enter far narrower than its natural width to be worth re-measuring.
Weight decay was measured as a substitute and rejected in that role: with both dropouts removed, adding a norm penalty recovers none of what they were doing — the decayed fit is no better than the undropped one, which is what a low-norm readout of a near-unique high-dimensional code still fingerprinting would look like. What it is not is generally harmful: added on top of dropout rather than in place of it, the same penalty is -5.86 millinats better on held-out problems, at both seeds. The gain is not the network's, though: the same decay applied to the network alone, the per-user fields exempt, is 8.87 millinats worse than shipping nothing, at every seed. The improvement belongs entirely to the ridge the full penalty puts on the abilities themselves, and that ridge re-gauges the fit — abilities compress and slopes inflate severalfold to compensate — so what the likelihood prices as an improvement is a geometry change it cannot see the cost of. A re-gauged fit the likelihood happens to prefer is not a better model, and no decay ships. The two dropout sites are the load-bearing part, and they are violently super-additive. Removing the feature-block masking costs 24.55 millinats and removing the trunk's costs 14.40, but removing both together costs 442.92 — an excess of 403.97 over the sum of the two, at every seed. An order of magnitude is not a nudge: either site alone still holds the fit together, and with neither in place it does not hold at all. So the two suppress different failure modes and no single norm penalty stands in for either.
The model has an exact scale freedom. Multiply every difficulty by a constant, divide every slope by it, multiply every ability by it, and every predicted probability is unchanged. Something must pin it or the fit wanders.
The gauge is pinned on the problem side: the difficulty output is hard-whitened to mean zero and unit standard deviation, batch-normalization style — training batches use their own attempt-weighted statistics (differentiable, so the whitening participates in the gradient) and update an exponential moving average, while evaluation uses the moving average, so the ratings scored at the end live in the same gauge the abilities were trained against. Ability is left completely free; its spread then settles wherever the data puts it.
Pinning on the problem side rather than the ability side is a well-posedness choice, and it is worth being exact about what it does and does not buy. The whitening statistics are taken over training batches of attempts, so the distribution the gauge is anchored to is the attempted problem set weighted by attempt count — which moves with the serving policy and the user base, for the same reasons the examinee set does. It therefore does not make the rating scale stable across retrains, and nothing else does either: section 1.11 says outright that every run re-fits the whole set and a rating moves whenever the map moves, and the rank curve is re-fit against whatever scale is shipped precisely so that gauge drift is absorbed downstream instead of displayed. What the problem-side pin buys is that the scale is fixed by a quantity with structure — the spread of difficulty across the problems people actually attempt — rather than by constraining the abilities, which are free per-user parameters with no shrinkage and no reason to have unit spread. Ability is then left alone, which is what lets its spread be read as a result rather than an assumption.
With the gauge in place the fit is a single joint pass: all three per-problem outputs and the global ceiling train together.
The gauge makes the fit well-posed but does not stop the derived rating from running away. The shipped rating is not the response curve's midpoint: it is the ability at which a user clears the problem at a fixed calibration probability of 0.8 — the margin the app both serves at and anchors at — and reaching it from the fitted parameters means adding an offset divided by the slope. Section 1.9 derives it and says why that definition is the right one; here only the division matters.
One penalty is added, on the slope, one-sided: a squared softplus of how far the slope output falls short of a threshold, averaged over the whole problem set with dropout disabled so it reads clean per-problem values. The reciprocal slope is the transition width of the response curve, and against a difficulty spread of order one it is exactly the factor the 80%-clear rating diverges through when an unattempted or never-solved problem's slope collapses flat. Without the penalty that is what happens, and the failure is one-sided: the slope distribution sags, its low tail reaches the clamp, and the derived ratings blow out upward. The 99.9th percentile of the rating runs to 35 where the whole shipped set spans 11.1 units end to end, while the bottom of the distribution barely moves — the divergence is the division, so it goes wherever the collapsing slope's sign sends it, and a flat slope on a hard problem sends it up. That the blow-out happens is reproducible; its size is not, moving by a factor of 3 between two runs differing only in the seed, which is what an unbounded tail does and not a discrepancy to chase. It costs 8 millinats held out — a small price for a large disfigurement, and audible only in a percentile nobody prints by default, which is the reason to hold it structurally rather than to watch for it. (Order one, not exactly one: the gauge pins the spread over the attempted distribution as section 1.6 describes, while the penalty is evaluated over the whole problem set, whose spread is the wider of the two. The weight was swept in these units so the calibration is self-consistent; the distinction would only matter to someone transferring it to a differently-served corpus.)
It acts on the slope head's raw output, before the softplus and the clamp that turn it into a slope, and that space is what makes a one-sided penalty behave. Comparing fitted slopes against a threshold directly is the obvious form and it does not fall away where it must: at a threshold of a tenth and a median slope a few times that, the shortfall term is still substantial on a perfectly healthy problem, so the penalty lifts the whole distribution as hard as it lifts a collapsed one. In the head's own output space a healthy problem sits several units clear of the threshold, where the softplus genuinely vanishes, and the gradient stays alive for the problems on the clamp — which are the only ones the barrier exists for.
The easy side carries no barrier, and not because the symmetric failure cannot happen. A mirror-image penalty on the floor is the obvious companion, since as the floor rises toward the calibration probability the 80%-clear rating runs to negative infinity in exactly the way a flat slope sends it to positive. The two sides differ in what is already holding them. The floor is a sigmoid scaled by the ceiling (section 1.5), so it is structurally barred from crossing the ceiling and the response curve stays well-formed however high it goes; the slope has no such construction behind it, and its clamp kills the gradient rather than supplying a bound. What the floor is not held below is the calibration probability, and a floor above that leaves the rating with no referent — which section 1.9 argues is a true statement about such a problem rather than a divergence to be penalized away, and gives the reasons for leaving it be.
The critical design point is that the penalty acts on the offending parameter, never on the derived rating. A direct penalty on the rating is Goodhart-able through the floor: measured, the optimizer inflates floors on hard problems to cancel the penalized number, and held-out calibration collapses below the constant predictor. The weight is bracketed from below and, on the evidence, not from above. Below it sits the failure just described. Above it the expected failure is compression: a barrier strong enough to lift every slope should crowd the derived ratings into a narrow band and destroy the ordering the rating exists to provide. Ten times the shipped weight does not produce that. The set spans 23.6 rating units rather than fewer — wider than what ships, because lifting the slopes lifts the difficulties they divide too — and its held-out cost against the shipped weight comes out at -1 millinats — a difference whose sign the seeds do not even agree on. The obvious place to look for a cost the likelihood cannot see is the count of problems whose floor rises past the calibration probability and so ship with no defined rating at all — and that count does not establish one either: it is violently unstable under both weights, moving between 561 and 1060 across runs of the shipped weight alone (section 1.9), and the difference between the two weights does not hold its sign across the runs measured. So the shipped weight is a conservative point inside a range whose upper end nothing here has found, rather than an optimum between two demonstrated failures. Raising it is a live question rather than a closed one, and what would settle it is more replicates of both arms, not another metric.
Adam, a large minibatch, a fixed seed, and a fixed budget of seven thousand epochs under a cosine decay of both learning rates to zero, which the run simply spends. There is no stopping rule and no checkpoint selection: the final epoch is what ships. A small fraction of rows is held aside and scored every ten epochs, but nothing is chosen on it — it is the record of where the fit got to, and the signal that would show a run diverging.
The decay is what makes the budget a choice rather than a stopping rule in disguise. A constant rate keeps taking full-size steps after the fit has arrived, so the fit wanders inside a band at its end and the shipped value is whichever point of that band the last epoch lands on: within constant-rate runs, cutting at four fifths of the budget rather than at the end moves the held-out-problem score by 4.6 millinats on average. The decay freezes the fit where it stands as the rate reaches zero. On held-out problems, at a fixed holdout draw, the decayed fit lands at parity with the constant one — -0.7 millinats at the shipped budget, -1.4 pooled over the budgets that plateau — and its run-to-run spread is 1.98 times narrower: 2.2 against 4.3 millinats of standard deviation, one-sided p = 0.06 on 6 constant-rate runs. That spread, not the level, is what the schedule was adopted for: every comparison in this document is read against it.
A schedule is compared at matched work, never at matched epochs. A cosine to zero averages half the base rate, so at equal epochs it has done half the optimization, and against a fit still descending that reads as the schedule being worse when it was only shorter. The shipped budget delivers 3500 constant-rate epochs of work, more than the constant fit's own budget. And the budget is bounded above as well as below. The decayed level improves up to the shipped budget and then plateaus, from 7000 to 10000 epochs; at 12000 it is +5.7 millinats worse, because the held-out score turns during the high-rate half of the run and the decay then locks the overfit in — the widest run climbed 16.2 millinats from its own minimum to its end. Its work, 6000 constant-rate epochs, is past the point where the constant fit turns as well: run to more than twice its budget, the constant fit is +6.7 millinats worse. The shipped budget is the cheapest that has arrived and the farthest from that edge, at 1.88 times the constant fit's measured run time for a budget over twice as long.
An opening ramp of the rate is the standard companion to a decay, and it buys nothing here. Scaling both rates linearly from near zero over the first 250, 500 or 1000 epochs, eight seeds each at one pinned held-out draw, moves the held-out-problem level by +0.5, +0.4 and +0.3 millinats against no ramp — each inside its own 95% interval, the narrowest of them ±1.2, and better on 6 of 8 seeds at every length — with the axes' identity unchanged. So the schedule starts at the full rate.
The shared network and the free per-user fields train at different rates, and what that split buys is narrower than it was once thought to be. The network trains an order of magnitude slower than the fields — the abilities and the axis positions. The split was adopted because a single shared rate made an earlier arrangement of this model diverge late and in evaluation mode, which is the worst shape a failure can take: it appears after the run looks healthy. On the configuration that ships, that failure does not reproduce. Pulling the field rate all the way down onto the network's leaves the fit finite throughout and costs nothing that survives replication: the seeds disagree on the sign. An intermediate ratio reads worse at every seed — 3.2 millinats on average, though most of that is one seed — small beside the network-rate cost below. So the field rate resolves weakly at best, and it is not what the grid was measuring.
The network's own rate is. Training it three times slower, at the shipped field rate, costs 10.63 millinats held out — on every seed, on both readings of where a run settles — and it is not a matter of needing longer, since the slower network reaches its own plateau at 0.6 times the epoch the shipped one does. It settles sooner, at a worse level. That is the one rate the fit is genuinely sensitive to across the range tried, and the split survives as the arrangement in which it was tuned rather than as a bound on either value.
The alternative to a decayed fixed budget is a constant rate stopped on that held-aside score, and it buys variance rather than accuracy. Measured on constant-rate runs: the score improves in bursts separated by long flat stretches, which is what a patience rule is usually sized against; the harder problem is what happens after it settles. And what that band is depends entirely on which signal is watched, which is the whole reason the budget is not chosen on this one. Across the closing third of a constant-rate run the ROW-held-out score still carries a downward slope of -3.9 millinats per five hundred epochs. A held-out row is one more attempt by a user whose ability was fitted and on a problem whose parameters were, so it keeps improving for as long as the fit keeps sharpening on material it has already seen, and it cannot tell that from generalizing. Scored on whole held-out PROBLEMS — the unit that can — the constant-rate fit is still descending too, at -3.6: inside its budget neither signal has flattened, which is the epoch draw measured above. The budget is chosen on the second signal and belongs there — on the sweep of decayed budgets above, where the plateau and the turn are both visible on held-out problems. What is left of the row split's window once its slope is removed, about 1.5 millinats of standard deviation, is the constant-rate fit's own wander; quoting the raw spread as noise overstates it, and the between-run spread that matters under the shipped schedule is measured above. Both parts govern how every comparison in this document should be read. The noise sets a floor no single pair of runs can see past; and a contrast read on the row split is a contrast between two things still sharpening, so where a run is stopped is part of what is being compared — which is why a claim about the feature side is scored on held-out problems instead. Neither is repaired by measuring more carefully within one run, which is why the arms behind these sections are replicated across seeds and why a contrast is reported with the agreement of its seeds rather than as a number alone.
Replaying patience rules on the recorded scores says the rest. Where such a rule stops is decided mostly by its own tuning: changing only the patience setting moves the chosen epoch by about 2,240 epochs, 3.1 times what changing only the seed moves it. And the rule does not merely add variance — it loses. Almost everywhere it stopped inside one of those flat stretches and selected an epoch worse than the final one, by -13.9 millinats on average. Its best case over every setting and seed was 0.1 millinats, and it reached that by running to epoch 2,960 — which is to say by declining to stop early at all. That comparison is scored on the very rows the rule selects with, so it flatters the rule; losing on its own terms is the strong form of the result. Spending the whole budget is therefore the better fit under either schedule, and under the decay it is not optional: the rate reaching zero is the stop. The axes are indifferent either way; this is a rating consideration.
One numerical detail is worth recording: the cross-entropy is reduced explicitly as sum-over-batch-size rather than through the fused mean kernel, because the two differ by a unit in the last place at non-power-of-two batch sizes (the final, smaller minibatch) and that difference compounds over the run in the model's flat slope and floor directions.
That sensitivity implies that anything perturbing the arithmetic at the same magnitude compounds too, so the fixed seed is sufficient only because nothing else in the stack varies — checked rather than assumed: a repeated run on one machine reproduces every per-problem output bitwise, with no determinism-forcing library flag needed. Across environments nothing is pinned — a different GPU or library build lands at a nearby but different fit — which is tolerable for the same reason gauge drift is (section 1.11): the whole set re-fits together and the rank curve re-fits against whatever scale results.
After training, the whole problem set is scored and the shipped rating is
rating = difficulty + logit( (0.8 − floor) / (1 − floor − lapse) ) / slope
that is, the ability at which a user clears this problem 80% of the time, not the 50% midpoint. That 0.8 is the calibration probability: the margin the app both serves at and anchors at, chosen because it is near the sigmoid's maximum-curvature region, so a served problem is maximally informative about ability while still feeling winnable.
The slope-dependent correction is load-bearing and cannot be deferred downstream. A flat, low-discrimination problem genuinely requires more ability to clear at 80% than its midpoint suggests, so two problems with identical midpoints differ in true 80%-difficulty — and a monotone rating-to-rank map, being monotone, can never recover a per-problem reordering. It has to be built into the rating itself.
That argument cuts both ways, and the next section is where it bites: the correction divides by the fitted slope, and a global rescaling of every slope is exactly the standing alternative section 1.10 weighs. Whether the anchor should therefore move is a measured question rather than a definitional one, and it was measured — section 1.11.
Ratings ship raw, and the low-slope tail is not clipped. A percentile clip is the obvious way to tame it and it is the wrong tool twice over: section 1.7's barrier and the floor's structural bound already hold the tails finite at the source, so there is nothing left for it to catch, and where it does bite it destroys the ordering of the extreme percentiles rather than correcting it.
The formula has a divergence case, and the published fit contains none of it. The 80%-clear rating is defined only while the floor sits below the calibration probability. A problem whose floor reached 0.8 would be cleared at least four times in five by any ability, however weak, so the ability that brings it down to 80% does not exist and the honest answer is minus infinity. That would be a true statement about such a problem rather than a fault in the fit — the position trivial enough, or guessable enough, that the quantity being asked for has no referent. Nothing structural rules it out either: the floor is barred only from crossing the ceiling (section 1.5), which sits far above the calibration probability, so the case is reachable in principle, and the derivation clips the inner probability at a small epsilon so that a problem landing there would ship a large negative number rather than an infinity.
Measured on the published set, the floor route is engaged and the slope route is not. 152 problems of 220,000 carry a floor above the calibration probability, the highest reaching 0.935, so each of those ships the clipped sentinel in place of a rating. The clip is also what makes them findable: it sends them to the bottom of the published scale, between 50k and 19k, where they account for most of what sits below thirty kyu. They are a thin tail rather than a population riding the bound — the floor at the 99.9th percentile is 0.70, and only 33 further problems reach even the approach band between 0.75 and the threshold. The slope route is not engaged at all: the lowest published slope is 0.58, forty times the clamp, with nothing sitting on it, which is section 1.7's barrier doing its job. The two routes are not the same failure — a rising floor sends the correction itself to minus infinity, while a collapsing slope magnifies whatever correction there is, since it enters as the divisor.
The tail is left as it is, and the obvious repair costs more than it saves. A penalty holding every floor below the calibration probability is the natural companion to the slope barrier, and it does not stay local: the floor is how the fit says a position is cleared by nearly anyone, so forbidding that sends the same statement into the difficulty instead, and the ratings widen for every problem the penalty touches. That trades a handful of undefined values at the bottom of the scale for a wider scale everywhere, which is the failure section 1.7 exists to prevent, in exchange for one that is confined to the problems it describes. The sentinel is also less a wrong answer than an unavailable one: a position cleared four times in five by any ability genuinely is easier than the scale can express, and what it ships says so in the right direction at an arbitrary magnitude. Their count and their span have to be re-read after a refit rather than assumed, because both are properties of a particular fit rather than of the model — and the size of that effect is easy to underestimate. Two runs of the shipped configuration differing in nothing but the random seed produced 561 such problems and 1060. A tail that reaches problems a player is likely to be served would be a different situation from this one — even the top of that range is a fraction of a percent of the corpus, sitting where floors run high; and the count arriving somewhere else after a refit is the normal case, not a symptom.
The per-problem values come out somewhat scale-compressed toward the feature-conditional mean: a user's true response curve is a little steeper per rating unit than the fitted slope claims. The obvious correction is a single global multiplier on every slope, and it is the natural first move — one scalar, measurable against held-out users, pivoting at the shipped rating so the 80%-clear anchor of section 1.9 is preserved and only the steepness around it changes. It is not applied. Slopes ship exactly as fitted.
The reason is that the measured optimum is close enough to one that applying it buys nothing reliable. Swept over the shipped fit's own held-aside score, pivoting at the shipped rating, the best multiplier is 1.08 — an interior optimum, in the compression direction — and unity costs 0.11 millinats against it, far inside the 7.31-millinat spread between two training seeds. A correction smaller than the noise on the quantity it corrects is not a correction.
The sweep has to be split by whether training saw the problem, and pooling is what makes a multiplier look necessary. About a fifth of a held-out user's attempts fall on problems no training user ever attempted, and those are priced from the feature prior alone. Such a problem honestly wants a shallower slope — the model genuinely knows less about it — so pooling those rows drags the fitted optimum down and attributes to compression what is really the price of extrapolation. Split apart, the optimum is the same on the pooled figure and on the seen partition alone, which is what says the residual compression is not concentrated where the model has data.
Where the compression comes from is settled, and it is the regularization. Regenerating outcomes from the fitted model itself, with known abilities, and retraining the whole recipe on them reproduces the multiplier on that clean data, while applying the same criterion to the true generating parameters returns approximately one — so the machinery manufactures the entire effect rather than measuring a property of players. Narrowing further: it is dropout specifically. With dropout removed the multiplier collapses to near one, and held-out likelihood explodes past the constant predictor with memorized slopes several times healthy. So a multiplier of one and generalization are not independently available with this feature set. Dropout limits how much per-problem fingerprint the shared map can memorize, which caps how much feature-inexpressible difficulty the fitted values can carry, and mild compression is the price. Section 1.5 is why that trade is taken.
The competing explanation — errors in variables, ability noise attenuating the fit — is wrong: within-user drift is far too small to predict the observed compression, and every ability-noise treatment tried was inert or worse.
A per-problem correction is not available at this attempt density, which is a stronger statement than that it was not tried. Explicit per-problem random effects fitted alongside the map always find a scale freedom to exploit. Fitting them the sound way instead — freeze the map, then fit the per-problem departures as an empirical-Bayes variance component — yields a real but tiny component that shrinks to almost nothing at the observed density of a couple of attempts per attempted problem. The idiosyncratic difficulty the compression suppresses is simply not identifiable per problem yet; what that result does and does not say about calibration is section 1.5's caveat, stated there.
Per problem: the 80%-clear rating, the discrimination slope, the guessing floor and the K-dimensional motif vector. Globally: the learned lapse rate, the per-user abilities and each user's axis positions. Everything a consumer receives comes out of the one pass — there is no second stage, and no published value is a rescaling of a fitted one.
Every per-problem number ships exactly as fitted, which makes the published slope and the fitted slope the same quantity. That is worth stating because the alternative — a global multiplier applied on the way out (section 1.10) — leaves a trap for anyone comparing a published value against one read from a training log, and this system does not set it.
The fitted floor and the shipped floor are likewise the same number, and the only thing between it and what is served is a client-side clamp. The client clamps the shipped value below the calibration probability before rebuilding the response curve, so that the curve's offset term stays finite whatever arrives. Neither bound is asserted anywhere, and the clamp does bind: it sits at 0.78, just under the threshold, so it catches every one of the 152 problems whose floor exceeds the calibration probability, and beyond those only the 13 that sit in the sliver between the clamp and the threshold (section 1.9). What it protects is the offset that makes the calibration point exact, which has no defined value above the threshold — so on those problems the client rebuilds a curve from a floor the fit did not produce, which is the price of keeping the offset finite, and is confined to them.
That the rating keeps its place under any global rescaling is a measured result, not a definition. Section 1.9's argument makes it a real fork: the rating's per-problem correction is an offset divided by the fitted slope, so shrinking that correction would move each rating by a problem-specific amount — precisely the per-problem reordering section 1.9 says no downstream monotone map could ever perform. So the alternative is well-defined, and it was fit. Scaling the whole inner logit rather than the slope alone re-derives the anchor toward the midpoint, and is algebraically identical to recomputing the rating from a corrected slope. Head to head under the same held-out walk-forward, keeping the rating and scaling the slope alone dominates the re-derived anchor pointwise through the peak — measured on an earlier configuration rather than the shipping one, so it supports the pivot as chosen rather than certifying the current fit. The fitted ratings are correctly-placed anchors; it is the discrimination that is mildly attenuated.
A word on the term raw, which this document uses in a specific sense. It means not repackaged: no bounded remapping, no rank baked in, no derived quantity substituted for the fitted one. A shipped rating is raw in that sense while still being a derived quantity — it is computed from difficulty, slope and floor rather than being one of them.
Every run re-fits the whole problem set from scratch, and because the fit is entirely in the shared map, a rating moves between runs when the map moves — which new attempts anywhere, a refit gauge and a refit feature basis all cause. A problem's rating is therefore not stable across runs even if nothing about that problem changed, with consequences for how the recorded history reads later (section 2.5). The motif vectors are the one part of the output deliberately held steady across runs, and section 1.14 is how.
The production trainer takes no options; the shipped configuration is fixed in the code. The entire experiment surface — alternative gauges, barrier weights, errors-in-variables probes, staged alternatives to the single joint pass, the held-out and survey-correlation evaluation suite, alternative objectives, and the search for the axis directions themselves — lives in a separate research entry point whose defaults are the shipped configuration. A test tweak cannot reach shipped output.
The rating scale is internal and arbitrary; users want a kyu/dan rank. That map is fit against self-reported ranks on external go servers, and the model has one important shape:
time-adjusted rating ──curve──▶ latent consensus rank ──per-server coupling──▶ declared rank
There is a latent consensus rank and every server, including the European federation's rating, is a free coupling off it. No server is a privileged identity hub. This matters because servers genuinely disagree — a "5 kyu" means different things in different places — and forcing one of them to be the definition imports its idiosyncrasies into the headline. That choice is measurable rather than aesthetic, and what it moves is the meaning of the headline rather than the accuracy behind it: holding the federation's coupling at identity, so that its ladder is the latent, shifts the shown rank by +1.0 of a rank through the kyu bands and +1.3 through the dan bands — that federation's own idiosyncrasy transplanted onto the headline, and uneven along the ladder rather than a relabelling of it — while buying nothing in exchange, the error against dan-band declarations reading +0.38 of a rank that way against +0.39 this one.
Anchors are career segments, not one number per user. Each surveyed user's attempt history is cut into overlapping two-month windows; each window with enough attempts yields its own rating and becomes an anchor row attached to that user's nearest-in-time declaration for that server. The assumption is no rank drift unless a survey says otherwise, which is sound because the survey era is short relative to the attempt history. Rows are weighted by attempt mass — each attempt's unit mass split across the windows covering it, so overlapping windows cannot double-count an era — then normalized so each declaration contributes total weight one regardless of career length. Paired cross-validation showed a clear held-out improvement, concentrated in the deep-kyu band, over a single pooled career rating. The same harness tested weighting each anchor by its own statistical confidence interval and rejected it: declaration noise dominates rating noise, so confidence weighting over-steepens the curve.
The anchor quantity is the user's 80%-clear rating, computed by the same four-parameter fit the client runs, using each attempt's own slope and floor plus the global lapse, pooled across boards. Anchoring on the raw ability instead would mis-place every user by several ranks, because ability is the midpoint and the rating is the 80% point — the mismatch is exactly the offset the rating definition builds in. Keeping the anchor identical to the client's own computation is what closes the loop: the map re-anchors users against whatever scalar is actually shipped, so it stays self-consistent under any change to the rating internals.
Solve time is folded in. Each declaration's effective rating is adjusted by a slope times the de-meaned base-ten logarithm of the user's median solve time, with the slope fit jointly in the same objective. It is identified because the time feature varies per user while the curve is a function of rating alone and each coupling is a per-server constant, so neither can absorb a per-user time shift. A simple log-time slope is all the data supports — no rating interaction, no power law, no separate rank-residualization step, since the joint couplings absorb the rank-correlated part of solve time by themselves.
The curve is monotone by construction: the gap between consecutive rank thresholds is a positive base gap times an exponential deviation term, so monotonicity is structural rather than enforced. The base gaps come from a published even-game win-rate table's fitted shape, used as a gentle prior. Gentleness is deliberate, and it follows from the consensus latent above: a latent anchored on one federation alone is kyu-starved and needs a strong prior to hold its shape, while an all-server latent is not, so a strong prior would over-constrain a curve the data can determine by itself.
Each server coupling is a cumulative-link ordinal fit with free thresholds and a uniform-spacing prior, plus a contamination mixture: with small probability a declaration is junk — a lie or a misclick — uniform over that server's categories. This caps any single junk row's negative log-likelihood and sends its gradient smoothly to zero, so an impossible declaration stops steering a coupling while a merely improbable one keeps most of its weight. The contamination rate is deliberately small: held-out likelihood degrades monotonically in it, because the plain logistic's linear tails already bound junk influence and a large junk floor mostly lets the fit over-sharpen its couplings.
A declaration the mixture cannot absorb is dropped outright, and the fit is what identifies it. Bounding what one junk row can pull still leaves it pulling, and a row that is not a measurement of anything should not be steering a coupling at all. So the fit runs twice: every declaration is scored by the mixture's own posterior — the probability that it is junk, given the thresholds fitted for its server — and those the posterior places past one half, meaning likelier junk than measurement, are removed before a second and final fit. Scoring by that posterior rather than by the size of the miss is what makes the screen comparable across servers, whose categories sit at different spacings and whose populations spread differently: a six-rank miss is unremarkable in one place and impossible in another, and the fitted thresholds already carry the difference. The bar is severe rather than nominal, because the junk prior is small — clearing one half takes a declared category the fitted thresholds give a likelihood below one in 7,000, which no ordinary miss reaches: the mildest declaration the screen removes misses by 6.5 ranks, and it is the fitted thresholds of that row's own server that make a miss of that size impossible there. On the current snapshot it removes 10 declarations and moves the curve by at most 0.233 of a rating unit, which is under 0.79 of a rank anywhere on the ladder, so what the screen mainly buys is an evaluation set free of rows the fit never saw, rather than a different mapping.
Two things that screen deliberately is not. It is not iterated to convergence: each further pass would tighten the curve onto the survivors and re-flag whatever then sits furthest out, which converges on an arbitrarily clean set rather than on a true one, so it runs once against the first fit and stops. And the cut is not placed at the largest gap in the sorted scores, tempting though that is when a handful of declarations stand clear of the rest. The largest gap in a sample is the largest gap somewhere even when the sample is one smooth population, so cutting there descends to wherever the decay happens to flatten and then reports the result as a discovered boundary; the gap is worth printing as a diagnostic of whether a detached group exists, which is a different question from where to stop. One consequence has to be carried into any accuracy figure quoted from this comparison: the excluded rows are excluded from the scoring too, so a statistic computed here describes the declarations the mapping accepts, not every declaration made.
A declared rank is a transcription, not a self-assessment, and that is the first reason the obvious objection to anchoring on them does not apply. The survey asks one thing — what are your current active ranks — and asks it per server, a row for each of the nine with a rank dropdown and nothing selected by default, so a player with no account there simply leaves it empty rather than guessing. On every one of those servers the rank is computed by that server's own rating system from that player's own games and displayed on their profile; the federation entries work the same way from tournament results. A player answering the survey is therefore copying a number, not estimating their strength. The intuition that self-reports run flattering is an intuition about self-assessment, and it does not transfer to transcription — and a player who did want to inflate would have to do it consistently across however many servers they play on, against couplings fitted from everyone else's rows. That is an argument from mechanism; the rest of this section is the measurement, and what it returns is a bound rather than a verdict. The displacement between a self-report and an independently verified rating at the same fitted rank comes out between -0.17 and +0.17 of a rank under two different designs, against a standard error of about 0.32 — so neither design separates it from zero, while a displacement of a full rank now sits outside every interval. The cleanest read of the two is tight and says the same thing: among the users who hold both a self-report and a verified rating, all but one sit within a rank of their verified rating. Inflation on the scale the intuition suggests — a rank or more — is excluded by this data; what the verified count still leaves open is anything finer, a few tenths of a rank either way.
A bias shared by declarers is invisible to this fit by construction, and that is what makes an externally verified rating worth having. Each coupling's thresholds are free in location — only their spacings are penalized — so a uniform inflation on one server is absorbed into that server's own thresholds and leaves no residual behind to detect. A bias shared across every server is absorbed one level up: the mean-offset penalty resists parking it in the couplings, so it lands in the curve's free steepness and location instead, and that is a gauge direction rather than a fitted quantity. Neither absorption changes a single predicted probability, so a per-server residual mean measures the fit's residual on that server's cohort and never its honesty, and no rearrangement of the declarations recovers the global part. This is worth being explicit about because the per-server residual means are the natural thing to reach for and they cannot answer the question: on the current snapshot not one of the nine servers is even nominally distinguishable from zero, before any correction for having looked at nine of them.
An externally verified rating is the way out, and the estimator is a rank-matched contrast. A user who links an external federation account has that account's official rating written into the same survey table by a weekly automated refresh. The residual against a declaration then carries three things at once — the bias the fit absorbed from the declarations it was fitted on, the fit's own residual on that kind of user, and the declaration's displacement — while the residual against a verified rating carries the first two and not the third, since the prediction being compared against is the same fitted prediction in both cases. So neither level answers the question on its own, and the difference of two levels at the same fitted rank answers it exactly: match a verified user against self-reporters the fit places at the same rank, difference the residuals, and the absorbed bias and the fit residual cancel together, leaving the displacement. Two specifics carry that. Match on the fitted rank and never on the declared one, because differencing against a quantity that appears inside the difference manufactures a slope whatever the truth is — the regression of predicted-minus-declared on declared has expectation set by the correlation and the spread ratio alone — and matching on it in bins is the same error wearing a different shape. And exclude from the self-report arm every user who holds a verified rating: those are the users whose declared rank is publicly checkable, so they have the least room to misreport. Comparing a verified rating against the same user's self-report is the exact-match limit of this estimator and reads cleanest of all, but it is selected on the very trait being measured, which makes it an upper bound on honesty rather than an estimate of it.
Both routes read small — one at zero, one slightly positive — and neither separates from zero. On the current snapshot, 22 verified users matched against the 198 self-reporters who hold no verified rating put the displacement between -0.17 and -0.08 of a rank — a shade either side of no displacement at all — stable across matching tolerances from half a rank to two, against a standard error of about 0.32, so every interval contains zero. The exact-match limit, the 17 users who hold both, reads +0.17 of a rank with a spread of 0.45, and it is worth reading through the rows rather than off the mean: 14 of them declare exactly the band their verified rating falls in, and the other 3 miss a band edge by between 0.03 and 0.58 of a rank — the two widest carrying 67% of the mean between them. The gap between the two routes — publicly-checkable users against everyone else — is about 0.30 of a rank against an uncertainty larger than that, so on this data being verifiable does not measurably change what a player reports.
What bounds the conclusion is the verified count, not the declaring population. The interval above is set by 22 verified rows against 198 available self-reporters, so what the measurement licenses is "no consistent displacement beyond about a rank", and more linked accounts is the only thing that tightens it. Three residual leaks are small at this precision and worth knowing before it narrows. A verified rating whose last tournament is old but still inside its activity window reads weak if the player has improved since, which pushes the estimate positive — the direction the only nonzero reading, the exact-match route, already sits in — so the slight inflation measured above is if anything an overstatement, and the same holds for any drift in the same direction. Matching on a fitted rank is matching on an estimate carrying its own error, which leaves a second-order confound wherever the two arms' populations have different densities around the matched value, of unknown sign. And the linkable cohort sits at the strong end — a median fitted rank of 1k against the surveyed population's 4k — so the deep-kyu bands are reached only through the loosest tolerances, and those are the bands where the mapping's mean absolute residual against declarations runs more than double its dan-range value, 2.9 ranks at 15 kyu and weaker against 1.4 through the dan range.
None of that touches precision, which is a separate finding and points the other way. Matching the verified rows' rank range and comparing spreads rather than means, self-reports carry an error spread of about two ranks — 2.1 on the federation coupling alone, 2.5 pooling every server — against the verified rows' 1.3, which under an independence assumption leaves something like 1.6 to 2.1 ranks of declaration noise: 3.6 to 4.7 times the 0.45-rank spread the publicly-checkable users show, and the one place their selection does bite hard. That cut is suggestive rather than settled, since 22 verified rows put a wide interval on the spread being subtracted and the two arms need not share one estimation-error variance. It also does not revive the objection above by another route, because a declaration is the response in the ordinal likelihood rather than a predictor: symmetric noise in a response attenuates a fit, it does not displace it, so the failure mode to look for is a rank scale slightly compressed against the servers' own — a question about the couplings' steepness, not about anyone's honesty, and one this measurement does not settle in either direction. Nor does any of it bound individual rows: the contamination mixture above exists for the declarations that are flatly impossible, and the screen described there removes 10 such rows from the snapshot before the mapping's final fit. So the reading a declaration supports is an approximately unbiased anchor carrying a rank or so of noise, which is what the mapping's ordinal likelihood and its band-wide categories already assume — not a quantity any single row can be trusted on.
A residual quoted in ranks is hard to judge, so it is also quoted in games. A go player has a working intuition for how much a tournament tells them about their own strength and none at all for what a rank of residual is worth. The mapping's accuracy is therefore converted into the length of the tournament record a conventional rating would need in order to be this good, using the same published even-game table the curve's shape prior comes from. That table gives how often the weaker player wins across a gap of one to four stones at each declared grade, and fitting a single stone-value curve through every cell at once — the logit gap bought by crossing one band, free to vary up the ladder — turns it into a win-probability law rather than a table of percentages. A stone is worth about 0.27 of a logit through the middle kyu ranks and 0.75 in the dan ranks, so a dan game carries something like an order of magnitude more information about its players than a kyu game does, and any answer in games is therefore a statement about a particular cohort rather than about the system. A field drawn from the table's own grade distribution is then paired round by round, every player's rating is re-fit from a trailing window of fixed length, and that length is swept until the simulated rank correlation and within-one and within-two rates match the measured ones. The window is what makes the question well posed at all: without one the estimate keeps sharpening for as long as the simulation runs and "this much evidence" has no fixed point. The evaluation sample is resampled to the rank distribution of the cohort being matched, because a rank correlation is not a quantity until the spread it was taken over is named.
The comparison is rating against rating, not rating against truth. The measured residual has a declaration on one side of it, and a declaration carries its own error, so scoring a simulated rating against the strength it was generated from matches an error against something strictly cleaner and returns fewer games than the mapping has earned. The like-for-like statement scores one window of games against a second, independent window for the same player: the shown rank agrees with a declaration about as closely as two independent ratings of that length agree with each other. On the current snapshot, and on the federation cohort — the one where an externally verified rating exists — that length is about 12 games, give or take 2 when the declarations behind the target are resampled. Read it as a floor rather than an estimate: every idealization runs in the tournament's favour, since the simulated rating knows the true win-probability law and the true population, no one's strength drifts, and no one withdraws. The match is made on several statistics at once, and that is the design rather than a hedge. Ordering, placement within one rank and placement within two each read a different part of the error distribution, and that distribution is not the one-parameter family a single summary would assume it to be — so a record length that reproduces one of them need not reproduce the others, and the quoted margin is what their spread comes to. What limits all of them is the number of verified rows, a rate over a couple of dozen of them moving several percentage points per row. On the looser cohorts the statistics spread further still — ordering as good as a longer record, placement as good as a much shorter one — which is the same conclusion the spread comparison above reaches by another route, that what limits those servers is the noise in what is declared rather than the estimate being compared to it.
Pairing each round for maximum information is the obvious way to run that simulation and is measurably the wrong one. Fisher information for a single comparison peaks where the two players are equal, so maximizing it pairs every player with their nearest neighbour — and a field in which everyone only ever plays their own neighbourhood is a chain. A chain pins local order and leaves its own overall length unmeasured, so absolute rank error stops falling with more games while the rank correlation carries on rising, which is exactly the failure that looks like success if only correlation is watched. Some long-range mixing is required, and a real Swiss supplies it for free by pairing on score rather than on rating. The same identification gap shows up in the estimator: a trailing window always separates somebody completely, every game won or every one lost, so a free maximum-likelihood fit sends them to infinity, and renormalizing the mean afterwards does not rescue it — that fixes the location, while the divergence is in the spread. A population prior is doing real work in both places.
The gauge, and what actually pins each part of it. With no server privileged, the likelihood alone leaves the consensus scale free up to a monotone reparameterization: remap the latent, absorb the remap into every server's free thresholds and into the curve's free per-segment gaps, and no predicted probability changes. That is an infinite-dimensional freedom, and no scalar penalty could pin it. Two different priors do the work, and it is worth separating them because they are usually conflated:
Both are soft, so strictly they select a strongly-preferred point on a flat manifold rather than eliminating the freedom. In practice the data-rich servers set the scale.
The output is one shared curve for both board sizes — the rating fit is already cross-board, so there is nothing per-board left to map — plus one set of coupling knots per server, plus the time-adjustment coefficients.
The rank coordinate, and how it relates to rating units. Everything downstream is quoted either in rating units or in rank units. There is exactly one of each:
One rank means one unit everywhere, with no conversion between representations, because there is only one. That is deliberate, and the reason is the failure mode parallel frames invite. Carrying several affine images of one coordinate — a normalized version, an integer index, a differently-centred scalar — makes a stale value not detectably wrong: read in the wrong frame it is still a plausible rank, so it corrupts silently instead of raising. A single frame is what makes a wrong value implausible enough to catch.
Between rating units and rank units there is no fixed factor, and there is not supposed to be. The whole point of the curve is that it is not linear: on the current fit the curve's own knots run from 20 kyu to 9 dan and span 3.8 rating units, but the segments are nothing like equal — one rank costs 0.038 of a rating unit around 10 kyu, where it is cheapest, and between 0.20 and 0.29 of one through the dan range, a factor of 5.3 to 7.6, with the outermost weak-side segment matching the widest of them at 0.27 of a unit. That is a real property of the population, not a fitting artifact: adjacent kyu ranks are close together in latent skill and adjacent dan ranks are far apart. The practical consequence is that a figure quoted in rating units cannot be checked against one quoted in ranks without saying where on the ladder, and this document quotes each quantity in whichever unit the code that computes it works in. Where a rating-unit figure needs a rank-scale intuition, the mid-kyu conversion is the one to reach for, because that is where most of the population sits.
A separate fit produces the population-level constants the client cannot estimate per user from sparse data:
The global lapse rate is read through from the rating fit, so client and backend share one ceiling.
The motif vector is the K axis loadings of section 1.5 — factors two through K+1 of the same response model, fitted in the same pass as the rating rather than afterwards. A problem's loadings say which skill dimensions it exercises; a user's positions say where they stand on each. Multiplied together they are a correction to that user's predicted chance on that problem, over and above what ability and difficulty already say.
The axes are defined by variation between people, and the contrast with an unsupervised decomposition of the same activations is the point. An unsupervised decomposition finds directions along which positions differ a lot; it has no reason to find directions along which players differ, and a direction nobody differs along is useless for personalization however striking it looks. Fitting the axes inside the response model makes the criterion explicit: a direction earns its place only by predicting outcomes that ability and difficulty get wrong, and only in a way that varies from player to player.
The count is however many rules the fit teaches — one axis per rule — so it is a shipping decision rather than a fitted quantity. Seven rules ship. Selecting the count on held-out likelihood instead is the standing alternative and it does not work at this resolution. Re-fitting with the rule banks nested so that only the count varies, four against six against the shipped seven separates the combined likelihood by at most 7.67 millinats, against 15.27 millinats of scatter between three holdout draws of one count. The four-against-six gap does not even hold its sign across the three draws; six-against-seven does, in the shipped count's favour, but inside that scatter. A criterion whose gaps sit inside one count's own draw-to-draw spread cannot be trusted to order the family. The count is in any case fixed rather than selected per run, because it is baked into the shared head — each candidate is a full training run, with no incremental path of the kind a frozen response curve would allow.
The figure that decides this is the combined likelihood, and the axis increment alone is a trap. Zeroing the axis term at the final state moves the combined score by 0.08, 0.12 and -0.03 millinats at four, six and seven axes — a magnitude that is residual absorption as much as prediction, with an ordering no more stable across draws than the criterion it would displace. The trap stands whatever the current arrangement of those numbers: more axes mechanically absorb more residual whether or not the model predicts better, and the same absorption fires whenever the rating is degraded — a worse rating leaves more residual for the axes to explain, so a broken arm can post the largest axis increment in a sweep.
What decides which rules earn axes is how fast a user's position on them becomes identifiable, and this follows from what the axes are for. A motif vector is read to report a user's weaknesses and to tilt which problem they are served next, so an axis is worth shipping when a user's coordinate on it can be pinned from a history someone plausibly has. The measurement is a split-half reliability against history length — the same user's attempts split by parity, each half fitting its own positions against a shared ability, corrected for the halving — summarised as the number of attempts at which reliability reaches a half. Against the response model's own ability, which is the positive control, that number is around a hundred. Across the rules the requirement splits into two ways to fail, and they fail independently. A rule can fail to pin a direction at all: the fits land somewhere different every seed, which is what a criterion resting on noisy evaluations produces, and no amount of data rescues it. And a rule can pin a direction almost perfectly while users fail to differ on it — the direction is real as a property of problems, but a position on it never becomes reliable, either because the between-user variance is small or because the flagged problems are too small a sliver of anyone's history to accumulate evidence from. Narrow coverage does not blur a direction; it only starves the per-user reading, so it bears on the second failure and not the first. Seven rules clear both, reaching that half-reliability point within roughly five hundred to two thousand attempts — a spread whose internal ordering moves from one training seed to the next, so the set is what the measurement supports and the ranking inside it is not — and they are what ship; the rules that pin directions best of all sit out on the second requirement alone, needing history no account contains. One further constraint is mutual: a rule and its mirror — the same contrast with its two sides exchanged — recover one direction approached from opposite ends, the axis taught from either agreeing with the other's negation, so at most one of such a pair can earn a slot.
The decision rule is fixed before the numbers, and that matters at this resolution. Taking the best of several candidates inflates the winner by about one standard deviation under a pure null, which is a substantial fraction of the differences being compared. So a bar is set in advance — beat the incumbent by at least two standard deviations on one fold and replicate on the other — with replication doing the real work, since an out-of-sample confirmation tests the choice rather than adjusting a p-value for it. The tier split above is wide enough not to need it; the likelihood differences are not, which is the other half of why they do not decide the count.
Residualizing the rule columns against the rating parameters before teaching is a standing option, and it costs. The motivation is real: a rule can depend on the rating without being collinear with it — a rule that fires three times as often at the strong end of the scale as the weak one is a plain example — while the rating-orthogonality penalty of section 1.5 pushes the fitted axes off the rating side and nothing constrains the target, so the two pull against each other. Removing the rating, slope and floor components from every rule column resolves the conflict and makes the axes worse where it matters: four of the five rules in the bank it was measured on lose ground, the rule the model otherwise adopts most faithfully loses the most, and the combined likelihood is slightly worse too. Only the rules already too unreliable to ship improve. The reading that fits is that a rule's rating dependence is part of what the rule means rather than contamination of it.
The axes pay for long-history users and nobody else, and the headline figure hides this. Binned by how many attempts precede the one being scored, the axis contribution is indistinguishable from zero below about a hundred prior attempts and substantial above five hundred. The band between them carries no observations, and five hundred is that bin's edge rather than a measured onset: per-axis reliability, which is measured across that range, already runs about a tenth to a half at two hundred and fifty to five hundred prior attempts against a quarter at best just below it, so a user's position on an axis is partly identified well before five hundred and the payoff most likely begins inside the empty band rather than at its far edge. In probability terms a user past that point has their predicted chance moved not quite a percentage point closer to the outcome, with a third of a point more predictions landing on the correct side of even; below it, nothing. Quoted as a single population average the number reads as a fact about players in general and is not one. Two things this cannot separate: the long-history bin is the same accounts as the heavy-usage bin, so "the axes need history" and "the axes work on people who play a lot" are not distinguished, and the upper bin is unbounded, so how far the payoff keeps rising inside it is unknown.
The fit cannot, by itself, produce the same axes twice, and this is structural rather than a tuning failure. The likelihood reads the loadings and the user positions only through their product, so rotating both by the same orthogonal matrix leaves every predicted probability unchanged. The ridge on positions is isotropic and preserves that tie exactly. The rating-orthogonality penalty of section 1.5 does not, since it scores each axis's own two ends against fixed external quantities and rotating the axes changes what those ends contain; it does not pick a basis either. What it does is carve out the directions that track the rating side, leaving a large remaining subspace inside which nothing prefers one solution over another. Measured with the teaching pull off, and with the held-out draw and the feature basis held fixed so that two fits differ in the network's initial weights and the order of its minibatches alone: over six pairs of four such fits, the axes first matched one to one between the two fits so that the number is the best case rather than an artefact of axis order, the matched axes agree at 0.24 on average and 0.05 at the weakest, while the fits land within 2.1 millinats of one another on held-out problems. So which basis comes out is decided by nothing the objective can see, and the freedom is nearly total rather than partial.
The problem is worse than a free choice of basis: the subspace itself is underdetermined, and a free fit does not even hold seven distinct directions. Two fits of the same data at the same configuration and the same held-out draw, predicting held-out problems equally well, share little of their span: the strongest canonical correlation between the two spans is 0.63, the third 0.37, and the weakest 0.01 — past a leading direction or two, one fit holds directions essentially absent from the other. On the extremes, which are what a name is a claim about, the best-matched axis of a pair shares 22% of its thousand most extreme problems at each end, 14% averaged over all seven — above chance, nowhere near a match. And neither the likelihood nor the ridge asks the axes to differ from each other — the ridge on positions is isotropic and every loading is normalized to unit scale, so two axes carrying one direction with half the position on each make every prediction a single axis would and pay half its ridge — and a fit with the pull off, and nothing else pricing that, does not hold seven: its axes span an effective 3.2 distinct directions, and its most correlated pair of its own axes sits at 0.96, one axis being another up to sign, so that the two apparent motifs at its ends are one motif read twice — against 5.3 directions and a worst pair of 0.41 when the pull holds each axis to its own rule. Asking a free fit for distinct axes outright — a penalty on the correlation between the loadings of different axes, the standard remedy — buys the count, an effective 6.5 directions with the worst pair at 0.20, and reproduces nothing: matched axes agree at 0.22, and the best-matched axis shares 7% of its extreme problems. Seven distinct free directions are seven arbitrary ones. Breaking the symmetry inside the objective instead — a distinct ridge per axis, so that rotating the space is no longer free — is the one remaining candidate for identity without a taught target, and it changes nothing: 0.20 agreement across seeds, an effective 2.5 directions, because two axes sharing a direction still pay less ridge than one carrying it whatever the per-axis weights, so the duplicate solution stands and there is no ordering to stabilize.
A refit is not a gentler version of this. Removing the most recent two percent of attempts — the scale of a routine refit — moves the basis as far as a different random seed does, and removing five percent agrees better with the full fit than removing two does. That non-monotonicity is the signature of a re-draw rather than a drift, and it rules out the hope that refitting often enough keeps the axes near where they were. Training to convergence does not help either, and slightly hurts.
So matching a new fit's axes against the previous ones after the fact cannot work, and it is the obvious thing to try. The natural design is a bank of frozen directions with each new axis matched to its best correlate, one-to-one, sign-flipped to the bank's orientation. It fails on the measurements above: at the correlations a refit actually delivers, the match is ambiguous between candidates and the axis being named is not the axis that was named. Allowing the best linear combination of the new axes — what a gauge fix would recover — lifts the agreement somewhat, and what remains after it is genuine subspace movement that no alignment can recover.
Correlation also overstates how well a name transfers, and it does so worst where confidence would be highest. A name is a claim about a motif's extremes, and the extremes are the least reproducible part. The shared fraction of two fits' extreme problems runs well below what the correlation coefficient suggests, and the gap widens as correlation rises — so identity has to be measured on the tails directly. Those tails are also scored on attempted problems only: about half the set is never attempted, its loadings there are unconstrained extrapolation whose extremes churn freely between fits, and the two ends of an axis carry it very unevenly. Against a base rate of 48.0%, the untaught end is the more never-attempted of the two on 4 of the seven axes — 27% to 45% of its thousand most extreme problems have never been attempted, against 16% to 38% at the taught, named end. That is the shape the mechanism predicts — a one-sided rule fires on the material the app actually serves, and the opposite end is "everything else", where the unplayed corpus accumulates — though it holds as a lean rather than a law, one axis running the other way. The practical reading is that the taught end is the better-attempted of the two on nearly every axis, while the unnamed end leans further into extrapolation — one more reason it ships unnamed.
The axes are taught onto fixed target directions. The target is not an initialization: an auxiliary penalty holds each loading toward it at a constant weight for the whole fit. Nothing else in the objective prefers one basis to another — the likelihood reads the loadings and the user positions only through their product, so rotating the axis space leaves it exactly unchanged — and without a standing pull the directions drift off what they were taught, detaching every name from what it describes. Across a real refit the pull takes agreement with the intended directions from almost nothing to 0.66, the average across the shipped axes' taught ends. What it buys is measured against the same fit without it: two taught runs differing only in the seed agree on their directions at 0.93, and at 0.89 on their weakest-matched axis, against 0.24 and 0.05 untaught, and they hold distinct directions where the untaught fit collapses to an effective 3.2. That gap is the whole reason a name written today can be expected to describe the same thing after the next refit. It is not free, and the price is paid by the rating. At a matched seed and held-out draw, the taught fit predicts held-out problems +4.5 millinats worse on average than the same fit with the pull off, and on the worse side at every one of 4 seeds; the gap is the same with the axis term zeroed, so it sits in the difficulty, slope and floor the fit assigns to unseen problems rather than in the axis channel. The pull acts on loadings the shared network computes, so it has a path into the parameters the rating reads, and closing that path shows what it carries: computed on a detached copy of the network's output, the pull recovers the rating's cost — +6.0 millinats better on every seed — and teaches nothing, agreement with the rule ends falling from 0.66 to 0.10 and the axes duplicating as a free fit's do. A rule direction is not linearly readable from a representation the pull did not shape, so the cost is the price of the representation, not of the readout. What the mechanism does establish is that a cost of this shape is permitted and unbounded: the likelihood is indifferent to a rotation, so forbidding one removes solutions it would otherwise have been free to take.
Distinctness is asked for by a separate term, and that term does not reach the network. The pull fixes which direction each axis is and prices nothing about two axes sharing one, so a taught fit on its own holds an effective 5.4 of its seven directions, with its most correlated pair at 0.40. A penalty on the correlation between the loadings of different axes closes that, and it is computed on loadings taken from a detached copy of the network's output — the same values, with no path back into the parameters the rating reads — so that it can only re-weight what the axes read out of the representation and never reshape the representation itself. On the taught fit it takes the count to 6.7 with the worst pair at 0.20, leaves agreement with the rule ends at 0.66 and agreement between seeds at 0.95, where the fit without it stands, and predicts held-out problems +1.9 millinats better, on 7 of 8 seeds, +1.6 of it in the rating's own outputs. The detachment is what makes the term affordable: reaching the network, at twice the weight on a free fit, the same term costs the rating +13.1 millinats, on every seed. The weight is the one point measured; it has not been swept.
Each target is a stated rule rather than a direction some earlier fit arrived at. A rule is a criterion written over the board and the engine's own evaluation — whether a ko shape stands within a couple of plies along a line played out to a conclusion, whether the two best first moves finish within a few points of each other while the win probability separates hard, how much score swings once the stones that change hands are accounted for — and it is evaluated on every problem in the set, both ways. So a target exists before any fit does, it covers the corpus completely rather than only where some previous corpus reached, and it is a fixed reference that can be read back at any time. What that does not establish is that the fitted axis means the rule. The axis is whatever the model settles on once it is pulled toward the rule and then released; the rule is a starting point, and whether it lands close enough to stand as a representation of that motif is measured per fit rather than assumed. A name still has to be checked against the boards — there is no quantifiable certificate for what a motif means, so a strong player's reading of the extreme boards is the evidence a name rests on. For the shipped axes that check has been made: read against their extreme boards by a European-federation 4 dan, each taught end represents its intended motif, and a change to the bank or a taught direction re-poses the question.
Teaching from previously-fitted directions is the natural alternative, and it fails in the one way that is worse than failing outright: it half-works. Directions a fit has been observed to find are explored and inspected outside the shipped pipeline, each one kept stored under a fingerprint of the direction itself, and taught back into the next fit. Measured, they return at agreements of roughly four to seven tenths, which reads as retention. Inspection of the axes at exactly those figures says otherwise: of six taught ends, four had become something else — one narrowed to a specific fight shape, one to answering the opponent's last move, and two were no longer nameable at all. So the number that would have been used to certify identity was satisfied while the names it certified had come loose. What settles it is that the round trip buys no stability in exchange for that risk: teaching from a direction an earlier fit produced holds an axis no better than teaching from the rule it was itself taught from. Since that stored direction is only ever an approximation of the rule behind it, chaining through it inserts a reference that moves and returns nothing, so the fixed reference is the one to teach from each time. The supporting evidence points the same way: the two motifs that reproduce across entirely different model generations are both rule-shaped concepts.
That difference is also why a rule is named directly while a fitted direction needs a fingerprint. A fitted direction has no statable identity — it is a column of numbers, so the only honest key is a hash of the numbers themselves, and a name bound to a position in a file would re-point onto a different direction the moment the file was reordered. A rule's identity is the criterion, so its name is a real primary key, checked to be unique. It could not be fingerprinted anyway: a rule column has one entry per problem and is recomputed as the corpus grows, so a content hash of it would change on every top-up.
A target that is itself rating-correlated fights the penalty of section 1.5, and how much of that is affordable is unresolved — so orthogonality is not a prerequisite to assert. Pulling the axes toward prescribed directions while simultaneously pushing them off every rating channel is over-determined whenever those directions carry rating themselves, and one such set did lose to it outright: the axes ended up worth almost nothing against the same setup without the conflict. But a differently-built set at a lower correlation with the shipped quantities came through with its full value, the leakage still eliminated, and retained its named ends better than it had under the weaker penalty. Those two differ in more than one respect at once, so which difference decides the outcome is not separated, and no threshold should be quoted as though it were. Neither of those banks is the one that ships, so the comparison cannot be re-run against the current fit and no magnitude from it is quoted here — what carries forward is the failure mode and the fact that it is reachable. The rules are not chosen for orthogonality and are not guaranteed to have it; what this says is that the penalty tolerates a correlated target at least that far.
The pull rides the attempt stream, and that is load-bearing rather than an efficiency. Sampling problems separately to teach them makes the trunk adapt to a mixture of two input distributions rather than to the one the rest of the objective sees. Reusing the loadings the model already computes for each batch of attempts means there is no second distribution at all. Measured on one seed against one target set with everything else held fixed, the sampling change alone is worth a substantial share of the total effect. It also matches the population the axes are defined on: loadings are identified by between-user residuals, which exist only where somebody attempted the problem. The never-attempted half is deliberately left unconstrained — teaching it would spend capacity, in competition with the rating, on a region that never carried signal.
The weight is not a lever on identity. Agreement with the rule columns saturates: it does not respond to the pull across a wide range of weights, so a larger multiplier reproduces the same axes and costs a little likelihood. What binds is the shape of the target rather than the strength of the pull — the rule ends are near-independent of each other and a substantial share of attempted problems carry no rule label at all, so an axis asked to be one rule at one end and another at the other is being asked for two unrelated things.
Holding the per-user positions at zero for an opening span is the obvious companion to this, and measured head to head it buys nothing — agreement with the target, the extreme populations and the likelihood all come back the same or a shade better without it — so there is no opening span: the positions and every penalty on them train from the first step, one uniform loop with a single fading weight. What the uniform loop does buy is that the orthogonality penalty never arrives abruptly — a span has to gate it, since asking the loadings to be uncorrelated is meaningless while the positions are pinned, and releasing the span would switch it on at full strength against a map it has never acted on.
Carrying the entire previous fit forward is the standing alternative, and it is cheaper than it sounds. Initializing every parameter from the previous run rather than re-deriving anything keeps the axes recognizably the same across a chain of refits, and it improves the likelihood at every data volume tried while converging in a fraction of the epochs — a refit inheriting a converged model starts from the answer to a strictly easier version of the same problem. Its drawback is what it anchors to. Consecutive refits agree almost perfectly while the chain as a whole drifts away from where it started, so a check between neighbours never fires and the names come loose from their meaning without any single step looking wrong. A stated rule anchors to a fixed reference instead, which is the property a name needs.
What remains is a monitor, not a matcher. Each fit reports, per axis, how much of the target's own extreme population it still holds. That is a measurement against a known quantity rather than a search among strangers, and it is the form of the idea the evidence supports. Read against a rule the report answers a different question than it would against a fitted direction: a low figure means the model cannot express the criterion, not that a motif has drifted — which is the more useful of the two, since the criterion is still there to be re-read.
Scoring the move a player actually chose requires a value per legal cell, and it costs a full pass rather than a projection. Every legal first move gets its own K-dimensional vector, because the system scores the move that was picked and not only the move that was correct. Since the loadings come from a trunk rather than from a linear map over the features, a cell's value is not a matrix multiply away from the problem's — it needs the whole feature reduction and a pass through the trunk. This is arithmetically far heavier than a projection would be, and it is nonetheless not the constraint: the pass is bound by reading the stored activations, so parallel reading is the whole optimization.
Labels are per-tail and optional to publish. A loading is signed and the client reads it as such at both ends, so a negative score is a position on the axis rather than "motif absent". What the two ends mean is not symmetric, though: a target that names a single motif teaches only one tail, and the other is untaught residual with nothing to call it. So each axis may name its positive tail, its negative tail, or both, and the axes as shipped name one side. An unnamed axis ships and is used exactly like a named one — it steers problem selection and its readings are computed like any other's — and every place the client would print its name says nothing instead. The alternative, refusing to publish until every axis is named, reads as the safer rule and is not: publishing is one step for the whole corpus, so a naming task nobody has got to yet would hold back re-rated problems, a refitted rank curve and newly generated positions, none of which have anything to do with the axis in question. There is deliberately no placeholder text either, since a sentence naming a motif by number is not a finding anyone can act on.
Naming carries a caveat that the identity machinery does not remove. Even taught, an axis is a coordinate rather than a discovered fact about the game: the space it is drawn from is weakly differentiated, several different sets of directions predict about equally well, and which set is taught is a choice — which motifs are written as rules, and which two share an axis. Teaching makes a name durable — it will still mean what it meant — without making it canonical. A name is a label on a coordinate, and the coordinate is a choice. What a rule adds over a fitted direction is that the choice is written down and can be read back, so the claim a name makes is checkable against something other than the fit that produced it.
There is no input-attribution tool, and that is a property of the model rather than a gap. Naming an axis by what drives it would need the axis to be a linear map on nameable inputs, so that a problem's value decomposed exactly into per-input contributions. A trunk sits between the features and the loadings, so no such decomposition exists, and any per-input number would be a surrogate model's opinion presented in precisely the place a human is deciding what a motif is. The boards are the evidence.
Problems are sorted by rating and split into equal-count groups, so each shard is a contiguous difficulty band and a client can load only the bands near a user's level. Each shard carries its problems, a manifest of per-problem (rating, slope, floor, motif vector), and an offset index into a companion binary of per-cell motif vectors. Those four are published exactly as the fit produced them — the 80%-clear rating and its two companions exactly as fitted (section 1.11) — with no rank baked in and no remapping of the slope into a bounded score. On the manifest values publishing performs no arithmetic at all: it joins and copies at full precision, and the six-decimal rounding in the code is applied only to the change-detection hash, never to what ships.
The binaries are the one exception, and it is a lossy cast rather than a computation. Per-cell and correct-move vectors ship as half-precision floats, which costs at most 0.0039 of an axis unit and 0.0010 at the 99.9th percentile — each loading is normalized to unit root-mean-square across problems, so that unit is the axis's own scale by construction. It is not a standard deviation: nothing centres an axis over problems, and the axis of a rule that a minority of problems satisfy holds most of the corpus below zero with its rule-positive end above, which is what its readings need and what the gauge preserves. The finest distinction anything downstream draws on them is the mistake-lean sign test of section 2.9, which compares two cells of the same problem: those differ by a median of about 0.23 standard deviations, and only around one comparison in 100 is finer than the worst-case cast error, where a flip is a coin toss either way rather than a bias. The other reading regresses a residual on the loading and carries a standard error orders of magnitude larger. Worth stating rather than eliding, because the number is small for a measured reason and not because the cast is exact.
One row-order contract deserves emphasis because violating it is silent. The per-cell capture is in board-scan order and is a superset of the move tree's variations, while the client identifies a played move by its position among the root variations. So the published binary is rebuilt in move-tree order, with an all-zero placeholder row for the "pass" variation that is never embedded, so that binary row i is exactly variation index i. Since the pass is not the last variation in 3.7% of 9×9 problems and 1.0% of 13×13 ones, omitting the placeholder shifts every later row and returns a neighbouring cell's motif vector for every played move after it. The binary's layout revision is folded into the published motif version, so a layout change re-derives every client's stored vectors through the standing invalidation path.
The same variation order is published a second time as a coordinate map, one byte per cell — the board point it sits on, with a reserved code standing for the pass — running in lockstep with the vector block so a problem's codes occupy the same ordinal positions its rows do. It exists for the one question that otherwise requires a problem's move tree. An attempt arriving from another device names the played move by board coordinate, not by index, and deliberately so: the coordinate stays correct if a problem's variations are ever reordered, while an index silently comes to mean a different move. Turning that coordinate back into an index is then a scan of one problem's codes, which a reader holding none of the archives can do. The reserved pass code cannot collide with a real board point, so the placeholder stays unreachable through this path as well.
Why an all-zero row is a safe placeholder rather than a lie is worth spelling out, because zero is not a neutral value in this space — the loadings are standardized, so an all-zero vector reads as "exactly average on every axis", and if such a row were ever consumed it would enter the motif readings as a confident, wrong observation rather than as missing data. It is safe only because the pass row is structurally unreachable as an answer. The pass is a real variation sitting among the candidate replies — every problem in the corpus has exactly one, carrying its own refutation line — which is precisely why a row must be reserved for it. What it does not have is a board coordinate, and every path that can produce a played-move index resolves one: the live path takes the variation under the clicked point, and the backfill path matches a reported coordinate against each variation's own. The client offers no way to play a pass at all. Neither path can select a variation with no coordinate to match, so no lookup can return the pass index. The placeholder therefore exists purely to keep the row numbering aligned and is never read.
That also makes zero the consistent convention for "no vector" across the published data: a problem absent from the axis fit gets an all-zero row in the set-wide correct-move sidecar, which the client likewise treats as absent rather than as average. The invariant to preserve is that a zero row is only ever emitted where it is provably unreachable or explicitly treated as missing — it must never become the fallback for a vector that failed to load.
Four set-wide files avoid the obvious inefficiency of downloading a multi-megabyte shard to read a hundred bytes. One holds every problem's rating triple by id. The other three are aligned to the sorted problem index the client already has, so a row is addressed by index with no key lookup and no search: every problem's correct-move motif vector, its availability null, and the address of its per-cell block within the companion binary. That last one is what keeps the other reads cheap, because the address is otherwise recorded only inside the shard archive — so without it, reading a few hundred bytes out of a shard's companion binary means fetching the whole multi-megabyte shard first, purely to learn where to look.
The availability null is computed at publication rather than on each device, because it is a property of the problem and its answer alone: per axis, how much of the moves that problem offers loads above and below the correct one. Nothing about a player enters it, so every device deriving it independently is the same fact recomputed many times over — and computing it there is what forces the whole per-cell block, hence the shard, to be read. Its reference is the correct move's own row in that per-cell block, not the nominally equivalent vector in the shard manifest or the set-wide file. Those come from a separate computation of the same quantity and are not bit-identical to it, so referencing one of them compares cells drawn from two different projections; measured on a seeded corpus sample, doing so moves a count on 2.9% of problems against the set-wide file and 4.9% against the shard manifest. Taking the reference from the same block as the alternatives it is compared against also makes the published quantities identical to what a device computes for itself when the block is already in hand.
Each alternative enters weighted by the depth of search behind its own evaluation, not counted as one. Every candidate reply carries the solver's estimate of the position after it together with the number of playouts that estimate rests on, and the generator spends that search where the position invites it — so the recorded depth is a per-move record of how much the position demanded, and the moves a click could pick are not equally pickable. The two published quantities are therefore sums of those integer depths over the alternatives on each side of the correct move, rather than tallies of how many there are. Two consequences carry: the quantities are exact integers, so a device that computes the null for itself and the publication that computed it can only agree or differ, never drift apart by a rounding; and a tie carries no direction and leaves the count on both sides, taking its whole weight with it rather than being split between them. An evaluation recorded without a depth predates the field and is known to rest on the minimum depth the generator ever wrote, so it enters at that value — a fact about the corpus, not a fallback, and part of the null's definition rather than a reader's choice. The reason for weighting at all is in section 2.9.
Three independent version stamps carry three different jobs, and conflating them is a real failure mode: a data version busts caches for the fixed-name files, a ratings version gates the client's re-derivation of stored history, and a motif version gates the embedding re-derivation. A release that only adds problems changes the data version but not the ratings version, so new problems become servable lazily with no history walk; a re-rate changes the ratings version and triggers an immediate re-derive.
Publishing is change-detected by hashing each size's inputs, so a curve-only or constants-only release skips the expensive shard rebuild entirely while still passing through the single writer. That hash covers input data, not the packaging code, so a format change needs an explicit forced run.
Almost everything here runs on the client, which is where the design puts it; the two server-rendered surfaces of section 2.11 are the exception and they deliberately run the same code.
The client receives raw numbers and derives every human-facing quantity itself. Publishing is rank-agnostic: it never computes a rank, never repackages the slope into a bounded quality score, never bakes a rank into a manifest.
On loading a shard, each problem gains exactly two derived fields: a rank, from the rating through the published curve, and a selection weight equal to the Fisher information one at-rank attempt on it carries, for use in section 2.4. Rating, slope, floor and motif vector stay exactly as published.
The rank is never clamped. The kyu/dan ladder is the range we put names on, not a bound on the value (section 1.12 fixes the coordinate). Applying the curve extrapolates linearly past both end knots, so a problem easier than the weakest knot lands below the bottom of the ladder and the unattempted hard tail — material far beyond where human declarations reach — lands above the top. Both are ordinary. Treat it as an unbounded continuous coordinate: never clamp it, never read it as a percentile. Section 2.7 depends on this directly and gives the concrete failure a clamp hidden inside a translation produces.
The selection weight is a client-local derivation with no counterpart upstream — nothing in the backend computes it, and the published slope is the response curve's actual discrimination. Beware that the same word, quality, names the backend column holding the fitted discrimination and this client-side weight, which are different quantities.
The rank curve, the server couplings and the population constants are inlined into the code bundle at build time rather than fetched. That is a robustness choice: they are then as available as the code itself, with no runtime fetch, no cache entry to evict, and therefore no offline path that can prevent the app from starting. It also means a curve change is a code release, which is the honest description of what it is.
One module owns the item-response math for both consumers that need it (the rank estimator and the recall scheduler):
P(clear) = floor + (1 − floor − lapse) · sigmoid( slope · (rank gap) + offset(floor) )
offset(floor) = logit( (0.8 − floor) / (1 − floor − lapse) )
The offset term is what makes P(clear) = 0.8 exactly when the user's rank equals the problem's, for any slope and floor. Without it the published rating's definition would hold only for an average problem. Floors are clamped below the calibration probability so the offset stays finite, and the lapse comes from the fitted population constants. The backend's anchor computation implements the identical formula; if the two drift apart the calibration loop reopens and the displayed rank stops meaning what the ratings say it means.
The two asymptotes are doing visible work in every estimate. The lapse term is why a single careless failure far below one's level barely moves anything: the raw sigmoid saturates, so the gradient vanishes while the variance stays bounded away from zero, and the slip's contribution goes to zero instead of yanking the estimate down. Symmetrically, the guessing floor is why a lucky clear far above one's level is weak evidence: the clear probability there approaches the floor regardless of ability, so success carries little information.
The headline rank is a function of the recent first-time history and nothing else — no accumulated loop state, no running anchor. That is deliberate, and the consequences are worth stating before the mechanics.
Each attempt is first mapped into internal-rank space through its own board size's curve: the position becomes the rank the rating maps to, and the slope is converted from logits-per-rating to logits-per-rank by dividing by the local steepness of the curve. That conversion preserves calibration — the response curve has constant slope in rating space, so in rank space it must vary with the map's local gradient — and it is what lets both board sizes pool onto one axis.
The estimate is then a weighted four-parameter fit over at most a few hundred of the most recent attempts, with each attempt weighted by a linear positional ramp: newest weight one, oldest in window approximately zero. The ramp's purpose is the edge. With a flat window, the attempt aging out at the cap leaves at full weight, so a per-attempt rank change is contaminated by an unrelated old problem dropping off; with the ramp it leaves at approximately zero weight by construction. The window is bounded by sample count only, with no time cutoff, so a long-idle return still uses its real recent history rather than starting cold.
It is a Bayesian posterior rather than a bare maximum likelihood fit, and the prior is the measured population. The prior is the distribution of rank across the app's own users, fitted from the attempt log and shipped as a log-density on a rank grid; the posterior is evaluated on that grid and reported as its mean and standard deviation. The prior is not a regularization convenience. On a short window a user's attempts are frequently all-correct or all-wrong, and there the likelihood has no interior maximum at all — the prior alone decides the answer. What it encodes is a new user's rank until evidence accumulates, which is why it is measured from the population rather than assumed.
The posterior is taken on a grid, not by mode and curvature. The population is bimodal, so a short-window posterior is too, and a Laplace approximation collapses that onto one mode with a falsely tight standard error — the unsafe direction for every offset below, all of which scale with it. The grid costs a few hundred likelihood evaluations per fit. Once a window has filled the likelihood dominates either way and the prior contributes a negligible share of the total information.
The prior is measured from users who reached a moderate attempt count, which makes it survivorship-selected: users who churn early are absent and are plausibly weaker than those who stay, so it slightly over-states a brand-new user's expected strength. That is not corrected for, because any correction would be invented rather than measured; it is stated here because it biases the cold start in the unsafe direction.
The window is positional in both directions, and the cost of that is a horizon that varies with volume. Weighting by position and not by elapsed time means the estimator has no notion of staleness at all: five hundred attempts made in a day and five hundred spread over a year are weighted identically. The benefit above is real — a returning user is measured from their actual record rather than restarted — but the same property means the effective horizon is set by the user rather than by the design. Measured across users with enough history to fill the window, the span of that window is a median of 30 days, 69 days at the upper quartile, and more than three months for about 21% of them, out to 351 days at the extreme. So the headline is a near-instantaneous read for a heavy user and a multi-month average for a light one, from the same formula. The visible consequence is a persistent disagreement for low-volume users between the headline and the career view's skill track, which is time-kernelled at about a month (section 2.6): both are correct, they simply average over different intervals, and for a user whose window spans a year they can differ by however much that user improved in it. Adding a time cutoff would fix the disagreement at the cost of reintroducing the cold-return problem — but the disagreement should be read as designed, not as a bug in either estimator.
The per-problem slope enters through the Fisher information, so a sharp problem pins the rank harder than a flat one — the same discrimination the backend anchor uses, which is what closes the loop between published ratings and displayed rank.
What is shown and what is served are two different offsets from that posterior, and neither is the mean. Over-rating a user costs more than under-rating — too-hard problems, and a rank that later walks back — so the displayed rank sits a fixed multiple of the excess standard error below the mean, where "excess" is the part above the irreducible floor described next. Because it scales with the excess, it converges onto the mean exactly as the estimate converges, leaving no permanent bias.
Serving does not follow the display down. Serving where you display starves the estimator: successes on far-below problems carry almost no information, so the posterior never narrows, the excess never shrinks and the display never comes up — a genuine fixed point, measured as a beginner sitting several ranks under their true rank for hundreds of attempts. Serving is therefore information-seeking, above the mean by one excess standard error, but capped so it never exceeds the displayed rank by more than a margin that grows with demonstrated success. The cap is a product constraint rather than a tuning parameter: uncapped, the same rule hands a 20-kyu a dan-level problem on their first attempt.
Substituting the display offset into the serve rule shows what the cap is really doing. The gap between what is shown and what is served is min(3·excess, cap) — the cap is one of two terms, and while the posterior is wide the other one is tens of ranks. The cap is therefore the binding term throughout onboarding, which makes it, rather than the information-seeking bonus, the thing that decides how fast a strong user is found. A constant margin is the wrong shape for that job, because it does not merely limit the bonus, it reverses its sign: the displayed rank sits two excess standard errors below the mean, so a constant margin of c puts serving below the mean whenever the excess exceeds half of c — which is the whole of onboarding at any margin small enough to satisfy the product constraint. The app would then serve under its own best guess, which is the starvation case this paragraph opens with.
The margin is instead one rank per consecutive cleared problem, plus one, reset by any miss — with two further ranks granted while the window holds no miss at all. This is a closed loop on serving difficulty rather than an open-loop constant: a long run of clears is the signal that serving is too easy and the licence to reach higher, and a miss is the signal to come back. The unblemished bonus exists because the opening is otherwise too cautious to register: without it a new account's first problem sits one rank above its badge, which is not enough for a correct answer to move the badge at all, and an app that visibly ignores your first solve reads as broken rather than as careful. At two ranks the first problem sits three above the badge and a first clear moves the shown rank by a full rank, on every problem a new account can be handed. The bonus is self-limiting in exactly the way the run is — the first miss ends it permanently — so a player who genuinely belongs at the starting rank pays for it once.
The cold start needs no special case, because a window with no attempts in it is not an absent posterior — it is the population prior exactly, so the expression that serves every later problem serves the first one too. All three quantities follow from it: the honest estimate is the prior's own mean, and the shown and served ranks are the two offsets applied to it, none of them a stand-in constant. The margin is at its tightest exactly where the estimator knows least — one rank on the first attempt, where a constant margin is already several ranks out — so the too-hard first problem cannot recur, and it opens as evidence accumulates. Only first-time attempts count toward the run, the same attempts the posterior itself is built from. Repeats are excluded because a review is scheduled against a memory target rather than a difficulty target, so missing one is evidence about forgetting, not about whether serving is pitched too hard; letting it reset the margin would import the review schedule's pacing into problem selection.
What this buys is an asymmetry, obtained without having to identify which kind of player is which. A weaker player misses often, so their run rarely grows long and the margin stays near its floor. A stronger player misses rarely, so their run grows almost monotonically and the margin stops binding within about ten problems, handing serving over to the information-seeking term. The same line of arithmetic is therefore conservative for one and permissive for the other, with no branch and no threshold. Measured by simulation on the shipped corpus, taking a twenty-kyu's first thirty problems: 56% land more than three ranks above their true strength and 35% more than five, against 72% and 62% under a constant margin of five — while an unlimited-strength solver reaches a dan badge in 33 first-time attempts rather than the 40 that constant margin needs. Both shares are large in absolute terms for a reason the margin does not control: the population prior centres a fresh account well above a twenty-kyu, so the opening problems sit above them whatever the margin does, and what the margin decides is how fast that closes. The alternative that also delivers the speed — letting the margin grow with the attempt count alone, so that it stops binding on a fixed schedule rather than an earned one, reaching that same badge in 33 attempts — pays for it exactly where this rule does not: it places 98% of that same twenty-kyu's first thirty problems more than three ranks above them, because a beginner's misses do nothing to slow it down.
The accepted cost is that the gap between the badge and the served problem is bounded only in distribution rather than by a constant: usually about two ranks, occasionally ten or more during a long run while the posterior is still wide, since run lengths are long-tailed and their maximum grows slowly with the number of problems solved. The display multiple continues to govern the endgame, since below the cap the information-seeking term takes over and both offsets collapse onto the mean as the excess vanishes.
The floor both offsets are measured against is rank-dependent, not a constant: it is the standard error a full window of at-rank problems would still leave, which depends on the mean per-attempt information of problems near the user's level — the same floor-aware quantity the selection weight uses, and the same one the estimator itself accumulates. Flat kyu-region curves carry so little rank information that even a full window of them leaves 4.0 times the standard error a window of sharp dan curves would, averaging each region over its own stretch of the ladder: measured on the shipped corpus the floor runs from 0.43 of a rank at its tightest through the dan range to 2.94 ranks at its loosest through the middle kyus, where the curves are flattest — a spread of 6.8 to one between those two extremes — with one narrow trough near the weak end of the rating-to-rank curve's own knots, where it drops back to 0.47 of a rank. That trough is the curve's own doing: its first segment is far shallower in rank per unit rating than its second, by 4.3 to one, so rank-space slopes jump across a single knot and the information they carry with them. A constant floor mis-scales badly across that spread, and which way it errs decides how bad that is: set near the sharp end it asserts a discrimination only dan problems reach, so every kyu user's bonus stays positive forever — served permanently above their level and never converging — while set near the flat end it would stop distinguishing a converged dan user from an unconverged one.
That mean is measured once, over the whole shipped corpus, and published as a curve of information against rank — one artifact, read by the client's badge and by the leaderboard's stored rank alike. The alternative is for each reader to average the problems nearest the estimate among those it happens to be holding: it costs nothing on the network and refines itself as the user's level drifts, and it makes the floor a function of load order rather than of rank. Both consequences reach the number the user is shown, because that number is the mean less twice the excess. The readers hold different things — a client holds the problem sets it has loaded this session, which cluster around the served rank rather than the mean the floor is priced at, while a server-side recompute holds the whole corpus — so a badge and the row on a leaderboard can disagree by more than a rank on identical history. And the reader that holds nothing at all cannot price the floor even in principle, so it concedes the whole width and shows a rank lower by twice the floor — measured against real histories, a whole rank band or more. The next answer recomputes it against a set that has since loaded, it returns to where it belongs, and the app reports that recovery as a promotion into a band the player already held. Crossing a band boundary back and forth, and being told each time, is correct: the shown rank really is moving across it. A crossing no answer caused is not.
That grid is derived from the two distributions that have to agree about it, rather than fixed in advance. It spans four standard deviations either side of the fitted population mean, narrowed wherever the corpus cannot support a measurement, and it is refused outright if what survives no longer covers the labelled kyu/dan ladder — a player holding a nameable rank has to be representable. A fixed pair of bounds can satisfy neither requirement: set too tight it pins an off-scale player at the edge, and set too wide it asserts knots the corpus cannot price, which is not a degradation but a hard stop, since a knot with nothing behind it has no information to publish. The two requirements are also not independent of each other over time — a refit moves both the population and the corpus's spread in rank — so bounds chosen once and left alone drift out of agreement with whichever moved. Deriving them from the same measurement the emitter checks is what keeps the two in step by construction.
Two mechanisms keep the derivation from being merely a truncation, each covering a case the other cannot reach. An outermost knot absorbs everything beyond it rather than letting its kernel fall away into empty space: it has no outward neighbour to borrow from, and this is already the semantics the lookup has, since a rank past the end is served the end value. Absorbing lowers that knot's information, so the floor there rises — the safe direction, because every offset built on the standard error over-reads a floor that is too low. An isolated interior knot that falls under the bar is interpolated from its measurable neighbours, which the sparse regions require because they are pocked rather than cleanly cut off: a single thin knot can sit between two that hold, and truncating there would discard everything past it. Interpolation is local and borrows only across the gap it spans, unlike the obvious repair of widening the kernel, which reaches far enough to import the trough near the weak end of the rating-to-rank curve and lands several times off its own neighbours. A run of thin knots wider than about a rank is refused instead of bridged: beyond that the shape is invented across a real gap rather than filled across a pinhole, and every interpolated knot is reported, since a patched curve is otherwise indistinguishable from a measured one.
The curve's knots are the population prior's own grid, and that identity is what closes the remaining edge case. The posterior is a weighted average over that grid, so any rank reaching the floor lookup lies inside it and the curve is never extrapolated. Extrapolation is not merely inaccurate here, it is silent: what is interpolated is information, a linear extension of it crosses zero within a few ranks of either end, and a non-positive information means an infinite floor, which drops the excess term altogether and shows the raw posterior mean with no symptom at the point of use. The lookup therefore holds its end values rather than extending them, so that a later change to either grid degrades to the nearest measured rank. One further thing follows from the floor being independent of what is loaded: an estimate is equally valid at any moment in a session, including the one a device restoring an account takes before it has fetched a single problem — the route that exists precisely because the recovered rank decides which problems to fetch.
Using the full information here rather than the two-parameter approximation is what makes the floor reachable at all, and this is where the two come apart in kind and not only in accuracy. The approximation claims strictly more information than a real at-rank window can deliver, so it sets the floor below the standard error the estimator actually converges to. Two structural consequences follow, neither a matter of degree: the serve bonus never reaches zero, so every converged user carries a permanent over-serve, and the convergence test fires later than its "within a factor of the best achievable" intent. The selection weight in section 2.4 shares the formula mainly for consistency — within a difficulty-matched pool the floors are similar, so the factor barely moves the ranking there.
Because the estimate is a function of recent history and nothing else, adopting an account on a new device and merging attempts from another device are the same operation — recompute from scratch. There is no loop state to warm and no anchor to recover. The prior's centre is the sole path-dependence and it is a weak one: an adoption seeds it from the data rather than from the previous account's rank, and any residual difference between two devices is scaled by the prior's small share of the total information at every refit, so it contracts geometrically instead of persisting. Every device therefore converges to the same number regardless of how attempts were batched. Merges recompute silently, so the rank change reported after an answer is that problem's effect alone rather than a synced batch's.
Candidates are the fifty problems nearest the serve rank, excluding recently answered ones, drawn from an unbroken run of downloaded bands containing the band the serve rank falls in. The corpus is downloaded band by band, so only part of it is ever in memory, and which part decides what the fifty are. Requiring the run to be contiguous and to reach the user's own band is what makes those fifty trustworthy: no undownloaded band can then sit between the serve rank and a candidate, so the fifty nearest within the run are the fifty nearest available. A short run is still safe on that argument — fewer than fifty, or fifty drawn from a single band, is a genuine neighbourhood of the user's level, and the error against the corpus's true fifty is bounded by the width of one band. What is not safe is choosing from everything in memory regardless of where it sits, which is how a band downloaded for some unrelated reason — following a shared-problem link, say — becomes the candidate set for someone several ranks away from it.
Play never waits on this in the ordinary case, because the run is widened ahead of use: the bands immediately outside the ones the current fifty occupy are downloaded in the background. That is self-limiting rather than capped by fiat — bands are contiguous in rating and the rank map is monotone, so once the band beyond an edge is held, everything in it is farther from the serve rank than the edge band's own problems, the fifty cannot reach past it, and the widening stops finding anything to do. Predicting instead where the rank would land on a correct and on an incorrect answer, and fetching those two bands, is not worth the machinery: a single answer moves the rank by much less than a band is wide, so both outcomes are already inside the widened span, and where the rank does move quickly the two point estimates straddle a gap that a contiguous span covers and they do not.
Only when the run is empty — the user's own band is not held — does selection stop and download, trying the device's cache before the network so a disconnected device keeps playing from what it already has. If even that fails, it serves from the nearest run it does hold and says so in the interface: a distant problem is worth more than re-serving one just answered, but not worth passing off as an at-rank choice. The recently-answered exclusion applies on every one of these paths, the degraded one included.
Selection is then a categorical draw rather than a product of the criteria, which would need one opaque constant to balance incommensurable units. Each serve decision picks one slot uniformly at random and takes the best candidate under that slot's criterion alone — with one exception noted under the information slot below — see section 2.9 for why the pool is not sampled in proportion instead. The slots are:
So a user with n flagged motifs gives each of them a share of one in n+1 once the rank has converged and one in n+2 before it has, no motif ever takes more than half the serves, and a user with nothing flagged and a converged rank gets the neutral serve outright. The pick is deterministic — hashed from the last recorded attempt, as the recall gate's roll is — so a resumed tab re-derives the same decision rather than re-rolling.
How well the rank is known decides whether the information slot is in the draw, and nothing else. A flag is a property of the user's own per-motif readings, which are identified from their own attempts whatever the rank posterior is doing, so conditioning personalization on rank convergence would withhold it from exactly the users whose rank is still moving — and the training-focus list (section 2.9) would name motifs nothing is yet being served for. The information criterion and the motif criteria are not alternatives to one another in any case: they are separate slots of the same draw, applied to the same difficulty-matched pool, and while both are present the user gets some of each.
One criterion at a time, uniformly, is the load-bearing choice, on two measurements. Within a difficulty-matched pool the criteria are near-orthogonal: the best candidate under one criterion almost never coincides with the best under another, and it captures essentially none of any other criterion's achievable-over-average value — so a blended score has no joint value to harvest, and optimizing one criterion forfeits nothing on the rest. And the ever-present neutral slot is the identification mixture that keeps "weak at this motif" separable from "generally weak": a motif loading held near-constant across the served history is collinear with the regression's intercept, so the loading's own coefficient is barely identified — its standard error grows as the variation it needs disappears — and the deficit lands in the intercept, which is the part the rank estimate already carries. The user reads as weaker overall while the panel, whose test is exactly that standard error, goes silent about the motif. Serving the motif exclusively would therefore yield less information about the weakness, not more; a share around a half or below is the informative one. Dropping the neutral slot whenever two or more motifs are flagged, and letting them cap each other, is the cheaper arrangement and reaches the same bound only under an assumption the rules do not supply: that distinct motifs select distinct problems. They are not chosen for orthogonality and their tails overlap in the corpus, so two correlated flags can both take the same corner of the pool and leave a user with no un-criterioned serves at all. Carrying the neutral slot unconditionally costs one slot's share and makes the mixture a property of the draw rather than of which motifs happen to be flagged.
Convergence fires at roughly a quarter of terminal information — deliberately early, because past that point extra rank information is worth less than simply serving at level, and because a user parked in a band depletes its sharp problems and converges to the band average regardless, so slope priority stops changing the served set anyway. That fresh-pool phase is exactly when the standard error is high, which is why the slot's presence tracks where the physics is. A user too new for either reading to resolve contributes no categories — both need a minimum of attempts, and errors, before they are computed at all — so a user with neither a flag nor a converged rank draws between the information slot and the neutral one. Flapping across the convergence boundary between consecutive picks is harmless, every criterion being applied to the same pool.
Selection is difficulty-matched at the moment of serving: the chosen problem's then-current rating sits within about a rank of the then-current serve rank, and the calibration means it should be cleared roughly four times in five.
Measured on production logs, the clear rates as raw means over outcomes needing no ratings and no model, the gaps beside them as the one ratings-joined quantity. First-time attempts clear at 77.8% for users past their first few hundred problems, against the 80% anchor. Repeats are not quoted here: the raw mean over them mixes scheduler-served reviews with re-attempts the scheduler cannot have produced, and a mixture has no target to be checked against. Section 2.8 measures the population that does. By position in the user's own history the rate falls and then flattens: 81.6% over their first twenty-five problems, 78.3% between the twenty-fifth and hundredth, 77.6% to their three hundredth and 77.8% after it. The opening band reads high because a fresh account is served below its own level by design, and that is visible in the gaps rather than inferred: over a user's first twenty-five problems the mean gap between their own fitted rating and the problem's is +0.56 rating units in their favour, falling to +0.08 by their hundredth and slightly negative after it. Section 2.3's margin is at its tightest on the first few attempts, so the flattening is the estimator finding the user rather than the user getting worse. The rate depends on the rating fit through the serving it measures, and has moved by a couple of points across refits — the figures above are re-measured, never carried.
Binning first-time attempts by the gap between the user's fitted rating and the problem's, the clear rate within a tenth of a rating unit of zero gap is 82.0% (±0.1), rising monotonically from 15.1% (±2.0) more than two rating units below rank to 96.5% (±0.4) more than two above. The zero-gap figure is in-sample, each user's rating being fit from the same attempts. The upper asymptote is 1 − lapse, which the fit puts at 0.968; the top bin does not reach it and should not, being a finite gap rather than the limit. What the log does bound is the lapse from above — no attempt clears more often than 1 − lapse, so a top bin at 96.5% admits any lapse under about 0.035 — and the fitted value sits inside that, with little to spare. The far-above bin is consistent with the fitted ceiling rather than a sharp check on it.
It is tempting to carry the at-rank property forward and treat the attempt log as a set of near-zero rating gaps. It is not one, and the gap widens after the fact.
Both sides of the comparison move after the attempt is recorded:
So a serve-time gap of approximately zero decays into a spread. Measured on real logs, 53% of first-time attempts sit more than two ranks from the user's rank and 42% more than three, with a median gap of 2.2 ranks. That is measured against a rating fit over the user's own five-hundred-attempt block rather than their whole career, so it is the spread that remains after within-career drift is taken out; against a single career rating it is essentially unchanged, which says the drift is the smaller of the two sources. Far-from-rank rows are the normal case in the record, not a rare pathology, and no analysis of the log should assume otherwise. This is why nothing downstream pre-filters them: the response model absorbs the bias inside the estimate — the guessing floor discounts far-below clears, the lapse ceiling caps far-above slips — and both the window estimate and the career decomposition are built to stay well-behaved on the whole unfiltered set. A filter would also discard a large fraction of a long career rather than a fringe.
What this does and does not license as a convergence test. It is tempting to read the drift as a per-problem signal — a problem whose rating has stopped moving has been learned, so its remaining gap is irreducible. The intuition is imported from a different model class: where per-problem parameters exist, a problem's estimate really is fed by its own attempts and really does converge as they accumulate, and churn is a per-problem read. Here there are none (section 1.5), so what churns is the map, and the map moves for reasons that have nothing to do with any one problem: attempts anywhere reshape the shared network, the whitening gauge is re-estimated from a changed attempt distribution, a problem-set top-up refits the activation basis so the features themselves shift, and a single-seed fit carries its own stochasticity. The one route by which a problem's own attempts could speak is the idiosyncratic component underneath its feature prediction — suppressed by design and measured negligible at this attempt density (sections 1.5 and 1.10). So per-problem churn is dominated by global refit movement, and reading it as "how much this problem still has to learn" attributes to one problem what is mostly a property of the whole fit.
What survives is a population-level statement, and it needs one further distinction, because the widening has two sources and only one of them is an artifact. Rating revision is retrain-driven and does stop: once successive retrains no longer move ratings materially, that half of the widening has run its course. Genuine ability drift is the other half, it never stops, and it should not — a user who improved really is further from a problem they were served months ago, and the gap that opens is a fact about them rather than an error in the rating. So "the ratings have settled" bounds the artifact, not the total spread, and the residual gap remains a mixture of serve-time estimate error and real ability change.
So no per-problem reading is available from this log — it supports the population statement above and nothing finer.
The statistics view decomposes a full career in internal-rank space, pooling both board sizes through each attempt's own curve. Three tracks:
The headline estimator is also re-run along the chart's time grid, so the plotted headline trace reproduces the live number exactly at its right endpoint, and the solve-time median is computed over the identical weighted window as the rank line it docks.
The user's continuous rank is interpolated through each server's coupling and rounded only for display, so a weak and a strong player within the same band can translate differently. Two deliberate asymmetries:
Spaced repetition consumes the rank, not the rating. A cold-solve log-odds is formed from problem rank against user rank, the user's solving-time and review-time percentiles and a board-size effect, pushed through the same four-parameter response curve as everything else on the client so the problem's own floor and discrimination are carried. That single number is the model's whole picture of how hard this problem is for this person. Memory stability, in days, is exponential in it and in the streak of consecutive successes since the last failure — stability = exp(base + sensitivity·(cold-solve log-odds − reference) + growth·successes + first-success step·[successes ≥ 1]) — and retention decays exponentially in elapsed time over that stability, anchored so a problem just seen is at full retention and nothing is instantly due.
That last term is a state indicator, not a fifth rung of the ladder: the streak counter is zero exactly when the previous attempt was a failure, and one when it was the first success after one, so those two states are the post-lapse and post-success ones rather than two steps of a single ladder. The step is added once the streak leaves zero and never again, and it is an indicator rather than a second growth rate — the second success adds nothing measurable beyond the first (see the measurements below). Three of the five terms are population constants and one is per-user. The sensitivity and the growth per success are fit by retention likelihood in section 1.13. The reference is fixed at the log-odds of 0.7, which is a choice of origin rather than a claim: it only decides what the base term means, namely the base stability of a problem at that difficulty. The base is the per-user durability, the one parameter fit on the device, shrunk to the population value so it is sane at low data and moves with more. The cold-solve log-odds enters that sum linearly, not through a further squashing, because a squashed form flattens on the hard side and collapses every difficult problem onto the stability floor.
The per-user time coefficients in that sum have a second consumer, and it is the whole reason the population time-benefit bias of section 1.13 is shipped. Those coefficients are confounded: a problem harder for this user than its rating says takes them longer and fails more, so a bare fit reads "more time, worse outcome" for almost everyone. The population value is that confound measured across the whole population, and the client spends it twice — as the target the per-user coefficients are shrunk toward, and as the origin the coefficient is recentred against before it is read as evidence about that user. What is read is a one-sided confidence that the recentred coefficient is positive. It surfaces in the statistics view as the chance that spending more time helps this user, and the same number gated at a fixed threshold is what decides whether to nudge someone who is answering too fast or reviewing too briefly. Both readouts must use the same recentred quantity: on the raw coefficient a user with no evidence either way reads as harmed by taking time, which is the confound talking rather than them.
The on-device fit is a damped Newton iteration over two separable blocks, and the separation is exact rather than an approximation. The cold-solve log-odds owns the intercept, the two time coefficients, the difficulty multiplier and the board-size effect; the base durability owns nothing else and appears nowhere in the cold-solve expression, because the retention head reads that log-odds as a fixed input rather than as something it may adjust. So fitting the log-odds block to convergence and then the base against the value that block settled on is the joint optimum, reached in one pass with no alternation. The two blocks take their curvature differently for a structural reason: the log-odds block steps on the expected information, since a four-parameter response curve with a non-zero guessing floor is not concave everywhere and the observed curvature need not be positive definite where the expected one is, while the base durability block uses its exact second derivative, which is non-negative for either outcome.
What the curvature buys is the stopping rule, and that is the point rather than the speed. The fit stops on the Newton decrement — the improvement the local quadratic model predicts is still available — and not on a change in the loss. The distinction bites because the objective is anisotropic in a direction the model is read for: the whole schedule is scaled by the difficulty multiplier, and a window of a few hundred attempts carries well under a nat of information about it, so its prior dominates and the loss is nearly flat along it. A rule watching the loss stops soonest in exactly that direction, leaving the quantity that scales every interval still moving; a curvature-weighted measure does not. Three consequences follow. A fit is tens of passes over the window rather than thousands, so it runs synchronously with no work budget, no cooperative yielding and no queue to coalesce retrains into. It carries no optimizer state and cold-starts every time, which makes it a pure function of the window — two devices holding the same history agree exactly, and adopting an account on a new device is the ordinary path rather than a recovery from one. And the obvious repair for a result screen whose predicted next interval disagrees with the answer just given — take a few more optimizer steps until they agree — is not available here: it works only where the fit stops short, and from a converged fit the residual disagreement is the two heads genuinely disagreeing, which is information rather than a defect to iterate away.
The window is an interval, not a threshold. A repeat becomes eligible when its predicted retention has fallen to 0.80 — the margin novel problems are served at — and it leaves when retention reaches 0.60. The lower bound is the easy one to forget and the consequential one: a review that is not delivered while the window is open is not delivered late, it is not delivered. Within the window stability has no ceiling, and that absence is load-bearing: graduation out of the pool is precisely the streak's exponential interval outgrowing the working history window, so a ceiling would cap the interval, turn graduation into a perpetual recycle, and let the pool grow without bound.
The working history window is a storage bound, and graduation is defined by it rather than by any rule of its own. The recall scan reads only the attempts held in the synchronous working set (section 2.10): the last attempt on each problem inside it is that problem's scheduling row, and a problem whose last attempt has fallen out of the set is not a candidate at all. That length is not a memory-science figure — it is the largest hot window any client consumer reads, chosen so the working set is a superset of every synchronous read. Everything the section says about graduating "past the window" inherits that provenance.
A due problem then wins a serve decision only with a probability set by a volume equilibrium. Each novel served generates a known amount of future review demand: the rate at which novels enter the ladder — the user's own recent fraction of failures on problems below their rank — times the number of reviews an entry consumes before its interval outgrows the working window, which follows once the window-open retention is taken as the per-serve success probability. That last step is an approximation, and a knowingly optimistic one: the window-open value is the most retention a serve can realize, not the average one, and the measured rate is lower (see the measurements below). An entry therefore consumes somewhat more reviews than the equilibrium credits it with and the share solved for is somewhat low — by a few points, which is small against the knob in the next paragraph and is why the exact figure is used as-is. The share that balances that demand against the novel supply is the resulting review-to-novel ratio expressed as a fraction of all serves. A gated-out review is not queued: it stays in its window for a later roll or ages out unserved, and that pruning is the pool's only decay channel, which is what lets the pool self-stabilize instead of growing without bound. The roll is deterministic in the candidate and the last recorded attempt, so resuming a tab re-derives the decision already taken rather than rolling again — independent rolls on every resume would give a due review many chances at a board it lost once. The equilibrium describes what the app serves unprompted, and only that: a review the user asks for outright is served without a roll, because the share exists to pace interruptions of novel practice rather than to ration what someone has chosen to do. Such a serve takes the same most-due candidate and is recorded like any other, so it consumes that entry's ladder step and drains the pool — the same direction the assumed-volume fraction below already errs in, never the direction that lets the pool grow.
The equilibrium is solved at half the user's own trailing-week daily volume, not at that volume itself, and the fraction is the one real knob here. The balance point holds exactly only at the volume it assumes; practise below it and the pool grows faster than it is served, above it and the pool drains. Assuming a light day therefore means a user going at less than their usual pace still gets through every due review, at the cost of over-serving reviews on an average day. The direction is the reverse of what "half" suggests: the working window is counted in attempts, so a lower assumed volume spans more days, an entry must climb further up the interval ladder before it graduates out of that span, each entry costs more reviews, and the served share consequently rises. Lowering the assumed volume buys the headroom by showing more reviews, not fewer.
The realized clear rate is measured from the attempt log, and identifying which repeats the scheduler produced is most of the work. Each scheduled repeat is reconstructed the way the client builds one — the streak walked with the client's own bookkeeping, the elapsed gap taken from the previous attempt on that problem — over the chains section 1.13 calls clean, discarding pairs closer together than the minimum recall interval, since the stability floor makes those unschedulable and something other than the scheduler produced them.
The right test is one-sided, because 0.80 is a ceiling rather than the upper edge of a tolerance. Memory decays from the moment the problem is answered; the window opens once a fifth of it has gone and closes once two fifths have. So 0.80 is the least forgetting the schedule ever serves at, and a serve realizing it is one taken at the very instant the window opened. Where in the window a user actually returns is unobservable and not reconstructable, but they are not sitting in the app waiting, so serves are spread across it and average nearer its middle. That makes a rate below the ceiling consistent with the design however far below it sits, and a rate at or above it consistent with nothing: it says the item was more durable than the model had it, and was called due before the fifth of forgetting the schedule waits for had happened. A raw mean over every repeat in the log is not that quantity and runs higher: it is dominated at the top end by re-attempts closer together than the stability floor allows, which the scheduler cannot have produced.
Separated by rung, only rung zero behaves. Rung zero — the post-lapse state — clears at 67%, near where a serve spread through the window should sit, and the rungs above it are more durable than the model has them. Measuring that takes care, because the correction below moves every realized rate it touches, so a statistic pooled across a schedule change mixes two schedules; the form that survives is within user at a matched relative interval. At the same fraction of their step-less predicted stability, the same user's post-success reviews carry 1.72× the odds of clearing that their post-lapse ones do — and because habit, era and schedule all cancel inside such a stratum, the contrast reads the same on either side of the correction's arrival, 1.69 on serves scheduled without it against 1.75 on serves scheduled with it. On the serves scheduled without it, that durability put every rung above zero at or through the ceiling — 81% pooled across them: items called due before the forgetting the schedule waits for had happened, so their intervals were too short. Rung zero's schedule needs nothing; every rung above it needs stretching.
The within-user comparison is the load-bearing one, and it is the right instrument for a second reason as well. Far more users stand on rung zero than on the rungs above it, so a raw comparison could be measuring who is standing on the rung rather than the rung itself. But there is also the unobservable above: how promptly a user returns once a window opens is a habit — some check daily, some weekly — and it sets where in the window their serves land. That habit is unknown and cannot be reconstructed, yet it is plausibly stable for a given user, so comparing a user only against themselves differences it out, which no amount of care with the pooled numbers can do. Composition is then ruled out rather than assumed away, and it runs the other way: the users who ever reach rung one are the weaker group at rung zero, so composition suppresses the rise rather than creating it. The estimator is a Mantel-Haenszel odds ratio stratified by user — every user compared only against themselves, pooled across strata, which is the right instrument when most users contribute a handful of events per cell. It separates rung one from rung zero decisively, and rung two from rung one not at all. So this is one step change at the first successful review and then a plateau, not a ladder that keeps drifting upward, and a uniform per-success growth term is the wrong shape for it — which is what the indicator below exists to supply.
Within a rung, elapsed time carries no trace of the predicted decay, and this is what the per-rung agreement conceals: the rung rates are group means, and grouping by rung is the only cut on which the model looks calibrated. At rung zero the clear rate sits between 65% and 69% from half a day out to a fortnight, drifting upward across that span rather than decaying. Held out — split by user, and again with the per-user base fit on each user's earlier events and scored forward — the full retention computation predicts served outcomes worse than a constant rate, and worse than simply knowing which rung a serve is at. Its ordering is real but its spread is about 9.2 times too wide, a recalibration slope of 0.11 on its log-odds, and rescaling by that slope recovers no more than the rung alone already gives.
The correction is a scheduling one, and it is derived as a moment condition rather than fitted by likelihood. Under the model's own exponential form, scaling an interval by a factor scales log-retention by the same factor, so the multiplier that moves a rung from its realized rate to a target is the ratio of their logarithms — no fitting required. Put every rung where rung zero lands and the correction is large at the first success and negligible between the rungs above it, which is the conclusion the within-user odds ratios reach from the other direction. That is the first-success step in the model above: the population fit takes the matched-interval contrast above — the within-user odds ratio between the two states at the same relative interval — converts it to an interval multiplier against the post-lapse cells' own realized rate, and ships its logarithm. Rung zero's schedule is untouched; every rung above it is stretched by that one factor. The step's effect is visible in the record it now writes: first-rung serves scheduled under it clear at 75% against the 66% they are matched toward — most of the intended correction — while the matched-interval contrast above stays put, which is the two statistics behaving exactly as they should.
What the step is, mechanically, is a distinction the streak ladder cannot draw by itself. The streak counter is zero exactly when the previous attempt was a failure — that is how an entry gets onto the ladder — and one exactly when the previous attempt was the first success after a failure. So the rung-zero-to-rung-one transition is not one more step along a ladder; it is the move from scheduling after a lapse to scheduling after a success, and those are different states. Established spaced-repetition schemes build that distinction in from the start — an interval reset and a re-learning phase in the older ones, an explicit post-lapse stability function distinct from the post-success one in the newer — whereas here the base durability term does double duty as both the population base and the post-lapse stability, so a uniform per-success growth on its own would multiply the first success by the same constant as the fifth. The indicator is what supplies the missing distinction, and it is the one place this model departs from standard practice in how it supplies it: a single added term on a shared base rather than a separate post-lapse formula. The measurement says that is the right form. What carries the effect is binary — whether the last attempt succeeded, not how many successes have accumulated — since the second success adds nothing distinguishable from zero.
A second reading of the same step — conditioning on a success selects items more memorable than anything the model represents, which likewise predicts a jump at the first success and little after — cannot be separated from the lapse account with this data, and the correction is the same under either. The part of that selection the model can see is netted out: the mean cold-solve log-odds rises 0.08 between the two rungs, which the difficulty term already converts into 1.07× of stability, about a tenth of the gap. Separating the remainder needs repeat density this log does not have — the same wall section 1.10 hits for per-problem rating residuals — and only whether a per-item term would eventually earn its place hangs on it.
It is a population constant because the step is close to identical across users. Estimating each user's own post-lapse versus post-success odds ratio, the pooled value is 1.79, 1.81 and 1.80 as the minimum events per user is raised — stable across entirely different subsets — and the share of users showing the effect in the expected direction rises from 84% to 91% as measurement noise falls. Cochran's heterogeneity statistic is 142 on 125 degrees of freedom at the loosest cut, close to what pure sampling noise alone produces; only among the heaviest users does any real between-user variance appear, and it is not resolved. The density settles it independently: 42% of users have no post-success rows at all inside the window this model trains on, and the median user has 2, so a per-user version would be its own prior for most of the population and would compete with the base durability term for the same rows. This is the same call section 1.10 makes about per-problem rating residuals, on the same grounds. The estimator's stratification is also what makes re-fitting it safe: the fit reads realized rates off whatever schedule is live, and the step it ships moves exactly the rates a naive contrast would be taken over — pooled over serves its own correction produced, such a contrast walks the step down toward no correction at all. Taken at a matched relative interval the schedule cancels inside each stratum, so serves from before and after a schedule change pool into one statistic and a refit measures durability rather than its own history.
Fitting that step by retention likelihood instead recovers almost none of it, and the discrepancy is informative rather than contradictory. Fit on half the users and scored on the other half, a first-success step buys 0.001 nats, and a free effect per rung — the upper bound on what any reshaping of the streak term could achieve — buys 0.003, against a deficit of 0.08 nats to a constant predictor. The likelihood also sizes the step several times smaller than the moment condition requires. This is not evidence that the correction is wrong; it is evidence that the likelihood is the wrong instrument. It is evaluated on the intervals the scheduler already chose, and a scheduling change moves those intervals, so predicting outcomes on the current serve distribution is close to uninformative about a different one. The scheduler's constants should be fit by matching the realized rate to the target, not by retention likelihood on self-selected intervals — the same range restriction that makes the held-out comparisons in this section weak is what disqualifies the likelihood here.
The one cost is that lengthening intervals means fewer reviews inside any given stretch of time. Making reviews land at the intended difficulty and showing more of them are opposed at fixed effort, so the step is a trade taken deliberately rather than a free repair.
The scope of all of this needs stating, because it is easy to over-read. Every interval in the log was chosen by the scheduler, which serves only while its own retention estimate sits inside the window. The predictor is therefore range-restricted by construction, and the log contains no unselected intervals with which to test retention as a law of memory — nor will it, while the scheduler picks them. What the measurement does test is the operational question: whether the policy achieves on its own serves what it is built to achieve, and on that question the per-rung rate is the sharpest instrument available.
Per attempt, the record stores the problem's correct-move motif vector, the index of the move actually played, and that move's motif vector, read out of the published per-cell block for that problem. A wrong attempt additionally stores its problem's availability null: per axis, how much of what the board actually offered — every variation except the pass, each weighted by the search depth behind its own evaluation — loads above and below the correct move. The null is published per problem rather than derived per device (section 1.15). A device answering a problem has that problem's whole position in hand, its per-cell block and the evaluations on its replies alike, so it computes the identical pair directly and a freshly answered attempt never waits on a fetch to be complete; the arithmetic is over integers on both sides, so "identical" is exact rather than close. Every other path stores the published pair instead of computing one — a device re-deriving old attempts holds the per-cell blocks but not the positions the depths are recorded on, so recomputing there would silently produce a differently-defined quantity beside the ones the live path wrote. The index is the move's position among the root variations — the same order publishing wrote — so no coordinate arithmetic crosses the boundary.
A pass can never become that index, which is the client-side half of the placeholder argument in section 1.15: answering is purely geometric — a tap or a typed coordinate becomes an intersection, matched against each reply's own coordinates, and a pass has none to match, so an unmatched click is rejected and no attempt is recorded at all. An attempt arriving from another device is reconstructed the same way, its reported board coordinate matched against the replies in the move tree or the published coordinate map, whose reserved pass code no board coordinate can produce — so the live and the reconstructed index are both non-pass by construction, and the placeholder row stays unread.
Passes do appear in review, where the solution line is walked to its end and a terminating pass is displayed as part of the line. Review records no attempt and produces no played-move vector, so this never meets the embedding path.
A stored value is comparable only with the fit that produced it, and one record per device says which fit that is. Nothing stored on an attempt carries a version of its own. The device holds one value per kind of derived data — the motif fit, the rating fit, the rank curve — naming the generation its stored history is made of, and a change to any of them re-derives everything of that kind.
The scheme is exactly sound under two conditions: nothing writes a derived value while its own build's generation and the device's disagree — a check made at the moment of writing, since a startup check never fires for the one instance that matters, the app that updates between problems — and the re-derivation reaches every holder of such values, an enumeration any new holder of a problem object must join. Any attempt still missing a value is filled on a later load, for the kinds whose versions agree, so an interrupted re-derivation finishes on the next load rather than waiting for the next change; and the per-attempt alternative, stamping every record with the generation that produced it, buys only after-the-fact detection of what the two conditions prevent.
Two readings per motif, both computed from the user's own attempts and their problems' own geometry, and from nothing else.
The first asks whether a motif costs the user accuracy. Every attempt already carries a predicted success probability, from the problem's own difficulty, discrimination and guessing floor together with the user's rank (section 1.5), so the residual — outcome minus prediction — is the part difficulty does not explain. Regressing that residual on the problem's loading gives a per-user slope: negative means the user does worse on problems loading toward the motif than their rank alone accounts for. Dividing by the local steepness of the success curve states it in ranks, the unit the rest of the interface uses: on problems turning on this motif, you play as though you were this much weaker.
The difficulty control is what makes that reading mean anything. Motif loading correlates with problem rank — a motif firing on a few percent of the easiest problems and a third of the hardest is ordinary — so a bare correlation between loading and outcome would score every user weak at whichever motif happens to inhabit hard problems, and strong at whichever inhabits easy ones.
The second reading asks which way the user's mistakes lean. For each wrong attempt, compare the loading of the move played against the loading of the correct move on that same problem. Played higher means the user reached for the motif where the answer lay elsewhere; played lower means they missed it where it was called for. The neutral point is not an even split: it is the share a direction-blind error would have had on the same problems — for each error, the share of that problem's alternatives loading above the correct move, with each alternative carrying the depth of search behind its own evaluation rather than counting as one (section 1.15). Each error therefore contributes its own problem's null probability, the null count is their sum with the matching sum of variances, and the observed toward-count is tested against that. Still a sign test — no model is fitted, no prior enters, and the interval comes from the user's own error count — just with the balance point supplied by each problem instead of assumed. It is reported as the share of the user's mistakes that would have to fall the other way to match a typical error's lean on their own problems.
Weighting the alternatives by search depth is the choice; counting them equally is the standing alternative, and it is worse in the same direction on every axis. Equal counting is what the phrase "the alternatives the board offered" suggests and is the obvious first construction, but it treats a move nobody would consider as being exactly as available as the one move that actually competes with the answer, and within a single position those differ by orders of magnitude in how much attention they draw. The recorded search depth is the only per-move measure of that already in the data, free of any fitting. Scored on the quantity the reading actually consumes — how well the null predicts the direction a recorded error fell — the depth-weighted null beat the equal-count one on 7 of the 7 axes, each by an interval clear of zero. A null that is instead fitted to the recorded errors improves on the depth-weighted one by 8% of the margin the depth weighting itself gains, which does not pay for introducing a shipped parameter into a reading whose whole claim is that it has none. And the reliability of the reading is not the criterion to select on: a null held fixed at an even split scores as the most reliable of all — on every one of the 7 axes, ahead of the reference by 0.26 of a rank correlation — because a constant is perfectly stable across a split of a user's errors while telling them nothing about themselves. Reliability there is rewarding retained confound rather than measurement quality, and the same argument applies to any two nulls differing in how much of the problem they divide out.
The standing alternative is a fixed even-split neutral point, and its cost is that the reading reports the problem menu as a property of the player. Within one problem the available directions are anything but balanced: a position that turns on a single move seldom turns on a quiet play elsewhere, so nearly every alternative loads higher on the motif of playing away, and a coin-flip error leans "toward" it with near certainty. Summed over a window, the even-split point is then a statement about which problems were served, not about the player — and serving itself conditions that mix, since a flagged lean redirects future problems toward exactly the boards whose menus keep the flag alive. Measured against the recorded picks of the attempt log, the most menu-skewed motif's alternatives carry 62% of their weight on the toward side, so the fixed even split flags that motif for 59% of the users with enough errors to test, against 44% once the availability null supplies each problem's own balance point — and subtracting the menu reverses the sign of the population lean on 4 of the 7 axes. The availability null is also free of everything the fixed point was chosen to avoid: it is a property of one problem and its answer, built out of that problem's own published loadings and the solver's own recorded search depths, so nothing is fitted, and nothing is averaged over other players.
The standing alternative is to score both readings against a population average, and its cost is that the result reports the user's rank. Comparing a player against the typical player is the obvious construction and the one most personalization uses. It fails here for a structural reason: which motifs a player meets is set by their rank, because problems are served near it, and motif prevalence varies several-fold across the ladder. A single average over all players therefore encodes the problem mix of whatever rank the average is dominated by, and measuring an individual against it mixes their play together with their level. Measured over 468 accounts with enough recorded mistakes to read, rank alone accounts for 14% of the between-user variance in the lean such a scheme reports on the strongest-affected motif — so a meaningful part of what it calls a tendency is the user's rank wearing a different name. That is what the scheme would report rather than a stand-in for it: what it subtracts is one constant per motif, and a constant cannot move that regression. Against each problem's own neutral point instead, the same measurement leaves rank explaining 5%.
The failure has a sharper form worth stating, because it is invisible when it happens. If the population average is estimated under one convention and subtracted under another — a prior centred at zero, say, while the reading subtracts a measured non-zero centre — every user is displaced by the gap between them. On a motif whose population centre sits a couple of spreads from zero, that is enough to flag a deficit for essentially every user, permanently and with no symptom at the point of use. Neutral points taken from the user's own record cannot fail this way: nothing needs to be estimated, shipped, or kept consistent between two places.
A reading is reported as a finding when its interval clears its neutral point, which is the whole of the test. Confidence alone decides, with no minimum effect size: a lean the user reliably has is worth telling them about however small it is, and the interval is what stops a small unreliable one from being reported. The interval drawn beside each number is exactly the width of that test rather than a conventional ninety-five percent — at any other width a reported finding could sit next to an interval straddling its own neutral point, which reads as the panel contradicting itself. Locking the two makes "interval clear of the neutral line" and "reported finding" the same statement by construction.
How many findings that yields is measured by running the shipped readings over the recorded histories, account by account, with the user's rank re-estimated at every point exactly as the app would have had it. The reference is what the gate passes by chance alone: a gate one standard error wide passes 32% of readings with nothing behind them and twice the gate 5%, and a user carries at most 14 readings, two per axis, so every count below stands beside the count an empty record would show. Over an account's first fifty attempts, across 804 accounts, the pane shows 2.4 findings at the gate and 0.4 past twice it, against 2.3 and 0.3 by chance: only 7.3 readings exist at all at that length — the lean readings need ten directional errors and mostly do not have them yet — and the accuracy readings that do exist have intervals too wide to separate from chance. At the shipped window — the latest five hundred attempts, taken at every point an account passed and averaged so that each of the 120 accounts counts once — it shows 4.9 and 1.2 against 3.9 and 0.6 by chance, from 12.3 readings available: a surplus of 1.0 of a finding at the gate and 0.6 past twice it, which is what the panel is actually reporting. Widening the window to the latest thousand and two thousand attempts, everything else as shipped, moves the counts to 5.5 and 1.7, then 6.0 and 2.3, while the chance count barely moves (4.0 and 0.6): the surplus grows with the record on both bars, which is what intervals narrowing with the square root of the count do to effects that were there all along. The shipped width is the length of record a device keeps motif vectors for, so the wider figures say what a longer record would show, not what a different setting would.
Every motif is listed, always, with both readings and both intervals. Showing only the findings that cleared a bar makes the panel a set of claims, and a set of claims needs multiplicity control — a per-motif bar leaves the expected number of wrong ones growing with however many motifs are screened. A table of measurements needs none, because an interval spanning the neutral point is not a claim: an unresolved motif renders as a value at the neutral point inside a band covering most of the scale, which says "not known" without a sentence having to. The claim-shaped parts of the panel still gate on confidence — colour stays neutral below the bar, and links to past mistakes appear only where a deficit is credible.
The two readings answer different questions, which is why both are shown. The accuracy reading is about the error rate — how often the motif costs the user, with difficulty held fixed; the lean reading is about error composition — where the errors go, given that one happened. The two layers are the two factors of the attempt's likelihood, so they cannot double-count each other. They are not unrelated: a player who reaches for a motif fails on problems whose answer is not it, which makes their residual slope positive and reads as strength, so over-reaching and strength are mechanically linked, as are under-reaching and weakness. There is a limit worth being explicit about: the lean is computed over wrong attempts only, so it cannot see the successes a lean earns — a player whose bias wins more often than it loses still reads as leaning, and a player whose confusion shows only as extra failures, with the failures themselves distributed like anyone's, is visible to the accuracy reading alone. That division is correct rather than a leak: rate deviations belong to the accuracy reading, composition deviations to the lean.
Selection follows the same readings, so what the panel reports and what the user is handed to practise cannot disagree. A problem's motif is the loading of its correct move, which is what separates the serve directions. Weakness and under-reaching both serve problems loading toward the motif: drill the deficit, drill recognising when it applies. Over-reaching serves problems loading away from it — those are where reaching for the motif actually costs the user, so it is a redirect rather than a de-prioritisation, and serving fewer of them would skip the very problems that expose the habit. Strength serves fewer of the motif's own problems, a plain suppression with nothing to drill. The training-focus list shows the deficits and drops strength: "you are good at this, so you will see less of it" is a serving decision, not something to work on.
Every reported deficit is allowed to steer selection. The hazard that would justify barring one is a category carried by most users at once: a near-universal category is near-constant across the population, hence close to collinear with the general level, so the deficit it describes is absorbed into the rank estimate instead of steering anything, and every user is pushed toward the same corner of the collection. The fixed even-split neutral point approaches exactly that on the most menu-skewed motif — the flag shares above — and the availability null attributes most of that flagging to the menu rather than the players. With the menu subtracted, no category is near-universal, so nothing needs barring; the hazard is stated because it is the thing to re-check if one ever becomes so.
Steering on a reading also makes the served history an adaptive sample, and what that buys is measured on a simulated player, not on the recorded histories — those were themselves served under the loop, so they cannot say what it did. A flagged deficit pulls its own motif's problems into the draw, so from the flag onward that motif is sampled above the rate the pool would offer and the reading's interval tightens faster than the corpus rate implies. Put a player of fixed true rank through the shipped estimator, readings and draw, weaker by one rank per unit loading on one axis, and read the accuracy reading after five hundred attempts. With the motif slots removed from the draw the planted motif's tail is 10% of what is served and the reading's standard error is 1.10 ranks per unit loading; under the shipped draw the tail share is 23% and the error 0.82, a cut of 26%; a draw serving the motif's slot from the first attempt — the loop with no flag latency — reaches 48% and cuts 49%. The loop tightens what it has flagged; it does not find. The share of runs that flag the planted weakness at all is the same with and without it, 57% against 55%, since the extra exposure begins only once a flag exists. Its clearest gain is on the other side: a flag with nothing behind it pulls in the very problems that refute it, so on an axis with no effect the share of runs carrying a weakness flag after five hundred attempts falls from 16%, what the gate passes by chance, to 9%.
Two costs come with it, both from selection answering to the outcome. The accuracy reading is a least-squares slope, which centres the loadings on their window mean, and under the loop the later loadings depend on the earlier residuals: a run of misses flags the axis, the flag raises the mean loading, and the earlier attempts, now below that mean, pair their negative residuals with negative centred loadings and push the slope up. A run of positive residuals does the mirror through the strength flag, so both flag directions push the same way. Paired against the opened loop, the reading comes out 0.25 ranks per unit loading optimistic (standard error 0.03) at every non-zero planted size, and 0.04 (standard error 0.06) with nothing planted, where the flags driving it are rare. It is a shift, not a shrinkage: the zero-latency draw, whose loading mix never answers to the noise, shows none, and the runs that ever flagged the planted axis read it at its planted size, -1.02 against −1. The second cost is the collinearity the neutral slot exists to bound. Served mostly the motif's own problems, a player weak on it looks generally weak, and the rank estimate absorbs part of the deficit: for a player two ranks per unit loading weaker on one axis the estimate sits -1.8 ranks off the rank on the pool's own mix under the shipped draw, -3.2 under the zero-latency one and -0.1 with the loop opened. The neutral slot bounds the absorption without removing it, and the served rank follows the estimate down.
Both obvious corrections to the absorption are measured, and neither is adopted. Ranking on the attempts the neutral and information slots served, alone, recovers the pool-mix rank by construction — 0.2 ranks off at the strongest planted effect — at the cost of every attempt the motif slots served, which widens the rank's own standard error by 36%. That trades a bias for a variance, and the mean absolute rank error comes out worse than the shipped estimate for a weakness of one rank per unit loading, 1.35 against 1.24, and better only at two, 1.44 against 2.14. It would also need the serve slot recorded with every attempt, since the draw cannot be replayed from a synced history. Shifting each attempt's difficulty by the user's own flagged readings instead, keeping every attempt, is measured-rejected: the reading is estimated conditional on the rank it is then used to correct, and the loop that closes runs the estimate about a rank optimistic whatever the planted effect — 0.9 ranks with none and 1.2 at the strongest — with the served problems following it up by 1.0 ranks.
In review, a problem's own strongest motifs are surfaced: components clearing a z-score threshold, a small number of them, each labelled from the tail it actually sits on, and a component whose tail has no name passed over in silence like everywhere else.
The attempt record is split in two, and the split is a storage decision that several other sections inherit. The most recent few thousand attempts — unified across both board sizes, ordered by date — are the working set, held in synchronous local storage so no hot path ever has to wait for them. Everything older is evicted to an asynchronous indexed store as the cold tail. The bound on the working set is not chosen for any modelling reason: it is the largest window any synchronous consumer reads, picked so that the working set is a superset of every synchronous read. That is the invariant that makes the split invisible — the models, the repeat check and the recall scan all slice less than the bound and therefore never notice a tail exists. The full-career chart is the only display that reads the tail back, awaiting it and concatenating; the re-derivation gates below are the only other thing that touches it.
Two consequences are load-bearing elsewhere rather than here. Recall graduation is defined by this bound and by nothing else (section 2.8): a problem whose last attempt has fallen out of the working set is simply not a scheduling candidate, so an interval that outgrows the set is what "graduating" means. And a device's tail was frozen at whatever rating era it evicted in, which is why a re-rate has to reach into it — the first gate below.
Stored history also holds derived per-attempt values that go stale when the backend refits, so three independent gates re-derive them:
A problem that has left the set has its rating fields stripped rather than left stale — the attempt remains in the record but is skipped by every estimate, which is the honest representation of "this happened but is no longer scoreable". Version stamps are written last, once, after all work has persisted, so an interruption leaves nothing stamped and the whole re-derivation repeats idempotently — and each pass persists in batches as it goes, so an interruption costs the remainder of a walk rather than the whole of it.
Two server-rendered surfaces consume the same quantities, and both run literally the same window estimator as the client rather than a reimplementation or a proxy. The leaderboard runs it over each user's materialized attempt buffer. The community rank histogram runs it over the same buffers.
The histogram is one player per bin, ranked by their own fit. For every player active in the window it takes the untimed native rank the leaderboard already holds — the same weighted four-parameter fit over the same trailing window the front page displays — rounds it to a rank, and counts players. Players below the leaderboard's uniform minimum-attempt gate carry no rank and are left out, exactly as they are left off the leaderboard. Hidden profiles are counted: hiding withholds a name and a country, never the rank. The country distribution is built from the same single scan of that table, so the whole community section costs one pass over a table that is pruned to roughly weekly-active size — the same pass the leaderboard page itself makes.
A real fit rather than a serving proxy, and the reason is not cost. The cheap alternative is to skip the likelihood entirely and average the difficulty of what the selector recently handed each player — no ability solve, no use of success or failure. It is tempting whenever the cost of a fit is weighed, and it is invalid for the reason section 2.5 gives from the other side: it has to assume the serve gap is zero, which holds only at the moment of serving. Restricting it to a recent window defends against that decay and against nothing else, because the errors below need no time to pass. The selector aims at the serve rank, whose non-negative bonus is largest exactly where the posterior is loose, so an unconverged player reads too strong; and any coarsening of the difficulty column — a band midpoint substituted for a rating, say — carries a per-band offset that averaging shrinks in variance but not in bias, worst in the widest bands, where it reads the strongest players too weak. Opposite ends pushed inward is a compressed distribution, which is the failure hardest to notice in a histogram, because the shape stays plausible. A weighted likelihood fit has none of this: it uses each attempt's current rating, slope and floor, so it is indifferent to how far a problem sat from the player's level, which is what lets it read a whole career including the large fraction of far-from-rank rows.
So the histogram is the rank distribution of ranked players, not of everyone active, and that distinction is larger than it sounds. Counting instead every player who answered anything in the window moves the mean by 3.5 ranks on the production population — more than either bias above — because the players below the gate come in, and with too few attempts to move it their estimate is still mostly the population prior read through the display's own conservatism. The chart answers the narrower question, and a figure computed the other way is not comparable to it.
Sharing the interpolation and label code between server and client is what stops the resulting histogram from drifting against the client's own rank display.