Skip to content

fix(search): Hold a line of check extensions to the ply rail - #253

Merged
aywrite merged 1 commit into
masterfrom
claude/extension-rail-verify-ba46f1
Sep 14, 2026
Merged

aywrite merged 1 commit into
masterfrom
claude/extension-rail-verify-ba46f1

Conversation

@aywrite

@aywrite aywrite commented Sep 14, 2026

Copy link
Copy Markdown
Owner

The in-check extension fires at two nodes, the root and every full width
node under it. The root's is held inside a byte already. The one under it
holds a node's depth where it was, so a falling depth is not what ends a
line of checks.

What can end one is the repetition and fifty move rules, and neither is
bound to fire first: a chain that captures keeps resetting the counter,
and one that never repeats is never caught by the other. So a line could
reach the ply a mate score stops being told from an eval by, a thousand,
and the history ring a repetition is read out of, a thousand and twenty
four less the fifty move window.

A node standing on the rail now answers from the static eval, which is
what quiescence has answered with at the same ply since it was given a
rail, and it gives up the mate such a node may be in on the same terms.
The rail sits after the repetition and fifty move tests, so no draw and no
mate on the hundredth half move is masked, and before the extension, so
nothing carries a line past.

Placed on the rail, a node searched 1840 nodes past it before this and
searches none after. Both new tests fail with the rail taken out again.
The bench, the tactical count and the strategic total are unchanged, since
nothing any of them searches comes within a hundred plies of the rail.
That says the change is not a play change at the depths anything here
measures; it is not an Elo claim, since the lines it touches are ones no
measurement visits.

One thing it deliberately does not claim: has_legal_move plays a move
above the rail, so a node at the rail with an expired counter touches the
ply above it and puts it back. Nothing indexes by the line ply in there
and the ring is a thousand and twenty four, so nothing breaks, but the
bound is on what the search evaluates rather than on every move it makes.

🤖 Generated with Claude Code

The in-check extension fires at two nodes, the root and every full width
node under it, and the one under it holds a node's depth where it was. So
a falling depth is not what ends a line of checks. What can end one is the
repetition and fifty move rules, and neither is bound to fire first: a
chain that captures keeps resetting the counter, and one that never
repeats is never caught by the other. A line can therefore reach the ply a
mate score stops being told from an eval by, a thousand, and the history
ring a repetition is read out of, a thousand and twenty four less the
fifty move window.

The rail stops it instead. A node standing on the rail answers from the
static eval, which is what quiescence has answered with at the same ply
since it was given one, and it gives up the mate such a node may be in on
the same terms.

Nothing measured has ever crossed the rail, and the comments beside the
extension said so and said that nothing proved it could not. Placed on the
rail, a node searched 1840 nodes past it before this and searches none
after. The bench, the tactical count and the strategic total are all
unchanged, since nothing any of them searches comes within a hundred plies
of the rail.

Bench: 3868987
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Speed against 378c1483

Both sides built and run on this runner in this job, the way
scripts/speed.sh measures a perf commit. The change is between
medians, and the spread beside it is what the change has to be
read against. The node counts are the search's: they move when
the search does, and a speed change leaves them alone.

round     base nps  candidate nps
    1      7737834        7426249
    2      7696581        7714906
    3      7836034        7748418
    4      7737045        7632884
    5      7697485        7739723
    6      7745316        7733813
    7      7762036        7697975
    8      7773592        7625001
    9      7793824        7599839

             nodes    time  median nps  fastest nps
base       3868987  0.50 s     7745316      7836034
candidate  3868987  0.50 s     7697975      7748418
change                           -0.6%        -1.1%

the change is inside the spread, so the medians make no claim; the
fastest column is the steadier comparison when the machine was not
quiet

Speed: -0.6% (bench nps, 9 interleaved rounds vs 378c1483, spread 4.2%)

@aywrite
aywrite merged commit 48bd556 into master Sep 14, 2026
20 checks passed
@aywrite
aywrite deleted the claude/extension-rail-verify-ba46f1 branch September 14, 2026 12:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant