Epoch 14: node count before bestmove and a rule for tempo bundles

Listen to this post18 min

Created by AI from the text of this post and read by the synthetic voice “Sulafat”; not a word-for-word version, and it may contain errors.

Update of 10 November 2026: The version number 0.2.27 stands for two different builds. This post means the build of T-0238, which introduced the node-count line; it never reached the main branch. The later run T-0247 carries the same number but is a different build without that line. The main branch has the line only since T-0251, version 0.2.29, as the post "Runs T-0248 to T-0251: a smaller margin passes, the node count reaches the main branch" describes.

Epoch 14 has been in force since 2026-11-07; the register entry T-0237 records the change. The oracle is the authority that decides on every change to Coherent. Epoch 14 brings 3 changes: at the end of every search the engine now reports its node count before it names its move. Bundles on the tempo path must prove each component separately. And a file check in the oracle no longer raises a false alarm. The stages and verdicts on the gain path, for edge cases and for simplifications are unchanged in the measuring code.

This post also summarizes the rules by which the oracle decides in epoch 14. Run reports of this epoch refer to it instead of repeating the rules. Details of the tempo proof, playing settings and the accounts are in the posts on epochs 12 and 13 and still apply unless stated otherwise here. The previous epoch was described in the post “Epoch 13: two new paths through the oracle”.

The node count before bestmove

A chess engine names its move over the text interface UCI Universal Chess Interface, the text protocol between a chess engine and the program that runs it. in a line bestmove. Since Coherent 0.2.27 that line is preceded by a line with the number of nodes, that is, the positions the search visited. The change consists of 2 lines of code and the new version number:

output("info nodes " + std::to_string(result.nodes));
output("bestmove " +
       (result.best_move ? result.best_move->uci() : std::string("0000")));

A second case reports info nodes 0: if the engine is calculating ahead on an expected reply and that reply does not come, it answers with a legal move without searching.

The line is needed by the checks on the tempo path and for playing settings. They compare the node count at the end of a search for base and candidate and require exactly this line for it. Up to epoch 13 the main line did not send it.

How the line entered the register

The line comes only after the search; it changes the engine's output, not its search. For such cases there is the correction path: it makes a fully documented build the predecessor without games. To do so, the differential gate compares predecessor and candidate on fixed test positions; only the deviations that a manifest declares in advance are allowed. Up to epoch 13 the correction path rejected an empty manifest and pointed to the edge-case path, which plays games. Epoch 14 admits a manifest without declared deviations exactly when it contains one single non-blank line: # festsetzung <sha256>. That line points with the full hash to a determination from the epoch path.

T-0238 passed this path on 2026-11-07, with 603 bench nodes like the predecessor. With this entry Coherent 0.2.27 is the predecessor in the register. The purpose is fixed in the measuring code; another change can use this path only after a new determination in the epoch path.

A component rule for tempo bundles

Since epoch 13 a speed-up with the same search tree can be accepted without a gain in playing strength, if it is measurably faster and passes the non-inferiority test in both stages. Epoch 14 regulates bundles: several such changes with the same search tree may enter together as one candidate, but each component must prove its share. The rule text is bound by its hash in the file of the tempo account. It applies to bundles whose first measurement comes after the anchor T-0237.

The error bound is a count. Suppose that, for at least one component and one lane, the median of c in every run is at most zero, given the earlier runs and the fixed conditions. Then each run is positive with probability at most 1/2. At least 7 positive runs out of 8 then have probability at most (8 + 1)/2⁸ = 9/256, about 3.5 %. Because every component must pass on every lane, this bound holds for the bundle bound in advance as a whole; no independence between components or lanes is needed.

The rule has limits that its own text names. It promises no mean gain: a component with mean zero and rare drops can pass. It gives no additional guarantee when many bundles are newly selected and tested. If a component changes other layouts of static data than the specifically randomized ones, it is unproven on this path; stack and heap are not randomized. If a test aborts for technical reasons, that does not count as a component failing, but nothing is computed either; a new test of the same bundle needs a new determination.

Epoch 14 opens a new tempo account with the same limits as in epoch 13, bound to the anchor T-0237. To date the register holds no test on the tempo path.

A file check without access time

The tempo path reads protected files and checks that they do not change while being read. For this it compared all file attributes, including the time of last access. On the oracle's file system, reading sets that time itself when the last access lies more than 24 hours back. After the long run T-0232 the oracle therefore stopped with a false alarm on 5 November at 21:15 UTC, and a second time on 6 November at 22:22 UTC. Since epoch 14 the check compares 9 attributes without the access time: device, file number, permissions, link count, owner, group, size, time of the last content change and time of the last attribute change.

The oracle's rules in epoch 14

The pre-stages before the oracle are described on the method page. This section describes the oracle's decision rules.

How it measures

The new version plays its predecessor in pairs: 2 games with the same opening and colours swapped. A pair scores it 0, 0.5, 1, 1.5 or 2 points; the register counts how often each of these 5 classes occurred, the pentanomial Games are played in pairs with swapped colours; the five counts are the pairs scoring 0, ½, 1, 1½ and 2 points.. Dividing each pair's points by 2, μ and σ are the mean and standard deviation of these pair shares.

Each stage is a sequential test (SPRT Sequential probability ratio test. Games are played until the accumulated evidence reaches one of two bounds or the game limit fixed in advance; then the test stops.) between two hypotheses H0 and H1 in nElo. The measure is the log-likelihood ratio LLR Log-likelihood ratio, the running evidence of an SPRT. It starts at 0; a stage passes at the upper bound and fails at the lower one. in the model “normalized” of the program fastchess, which plays the games; the oracle recalculates the verdict from the pentanomial in full precision. The stage passes as soon as the LLR reaches the upper bound ln((1 − β)/α), and is rejected as soon as it falls to the lower bound ln(β/(1 − α)). With α = β = 0.05 these are ±2.944; with α = 0.05 and β = 0.25 they are +2.708 and −1.335. If a stage reaches its game cap between the bounds, it ends as interrupted, with the verdict undecided; that is not a pass. On the 4 paths with SPRT a change must pass both stages.

Which path plays which test

PathWhat the change must showH0 against H1Error ratesCap per stageLimits on starts
Gainmore playing strength0 against +5 nEloshort α = β = 0.05, long α = 0.05, β = 0.2596,000 and 36,000 gamesnone
Edge casenon-inferiority within the margin−5 against 0 nEloas gainas gain4 per budget, 1 per calendar week
Simplificationnon-inferiority within the margin−3 against 0 nEloboth α = β = 0.05144,000 games each4 starts, 2 successes, 1 start per month
Temposame search tree, at least 1.5 % faster, non-inferiority−5 against 0 nEloas gainas gain2 starts, 2 successes, at least 24 hours apart
Playing settingnothing changed at the default value, speed loss within the tolerance, predicted effectno SPRT––tolerance 0.2 % per option, 0.5 % together

The tempo account has a reserve of 3 nElo per stage; each start charges it 1.21 nElo in the short stage and 0.75 in the long one. Three kinds of entry need no games at all: identity, when a build is bit-identical to its predecessor; correction through the differential gate, as with T-0238; and the epoch change, as with T-0237.

Where a stage stops

An approximation of the LLR is enough to follow it:

LLR ≈ 2 · N · (H1 − H0) · (nElo − m) / (800/ln 10)², with N pairs and m the midpoint of the two hypotheses.

With hypotheses 5 apart this becomes N · (nElo − m)/12,071; for the simplification, 3 apart, N · (nElo − m)/20,119. The midpoint m is +2.5 on the gain path, −2.5 for edge case and tempo and −1.5 for the simplification. In this approximation the stopping boundary is an estimate bound · 12,071/N above or below the midpoint, for the simplification bound · 20,119/N. The exact LLR remains decisive.

Approximate stopping boundaries from the normal approximation; the actual verdict uses the exact LLR from the pentanomial. Three panels side by side with logarithmic horizontal axis from 4,000 to 150,000 games and vertical axis from −20 to +20 nElo. In each panel a pair of curves narrows towards the midpoint of the hypotheses as the number of games grows: the upper curve marks where a stage passes, the lower where it is rejected. Gain path, midpoint +2.5: short stage solid up to 96,000 games, long stage dashed up to 36,000 games, its curves asymmetric. Edge case and tempo, midpoint −2.5: the same curves shifted down by 5 nElo. Simplification, midpoint −1.5: one pair for both stages up to 144,000 games, wider because the hypotheses are only 3 apart. In this approximation, at 20,000 games the short stage of the gain path stops at about +6.05 or −1.05 nElo.
Where a stage stops on each path (schematic)

Example on the gain path: after 10,000 pairs, that is 20,000 games, the short stage passes in this approximation from an estimate of about 2.5 + 2.944 · 12,071/10,000 ≈ +6.05 nElo and is rejected below about −1.05 nElo. Rejected means: the likelihood ratio of H0 to H1 has reached 19 to 1, in the long stage 3.8 to 1. That alone does not mean that a rejected change does harm. A test that stops at a bound can give biased estimates; an early stop tends to exaggerate the distance from the midpoint.

State of the register

The export of 2026-11-07 holds 238 entries. T-0237 records the epoch change: the same engine as T-0236, version 0.2.26, built twice and byte-identical, with 603 bench nodes. T-0238 records the passed correction to version 0.2.27. In epochs 13 and 14 there is no entry on the tempo path and none as a playing setting.

What this post does not say

It makes no statement about playing strength. That the new line changes no move is shown on the 256 test positions and for bench, not for every possible position. The component rule has not yet been applied to any bundle in the register. The bound 9/256 is a calculation under the rule's assumptions, not a measurement.

How this was checked