Skip to content

Publish each new champion as a GitHub Release (with a provable SPRT) - #5

Open
DevomB wants to merge 2 commits into
mainfrom
publish-champions-as-github-releases
Open

DevomB wants to merge 2 commits into
mainfrom
publish-champions-as-github-releases

Conversation

@DevomB

@DevomB DevomB commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

Summary

Until now the training loop published nothing: new networks stayed in runs/<run>/models/ on the training machine. Now the loop tracks a champion (the best network so far) and can publish each new one as a GitHub Release, so the newest model is always one download away:

https://github.com/DevomB/Gofer/releases/latest/download/gofer-9x9.onnx

How it works

  • Who becomes champion. This check runs on anchor generations, every anchor_every laps. If the new network is rated above the current champion on the Elo ladder, it plays the champion directly: up to champion_games games at champion_visits, with defaults of 200 games and 200 visits. It's promoted only if the match's SPRT (champion_sprt, default 0,50) accepts H1. An undecided match promotes nothing. The first network rated at least champion_min_elo (default 1500) becomes the first champion, so the early, weak generations never get released. Training itself still has no gating: the newest network always plays the next self-play.
  • What's kept locally. Each champion goes to runs/<run>/champion/gen-NNNN/: the network, champion.json (rating, champion match, anchor results, games and positions so far, SHA-256) and notes.md. champion/gofer-9x9.onnx always holds the current champion.
  • Publishing. With --release-repo OWNER/REPO (or GOFER_RELEASE_REPO), the loop runs gh release create model-<run>-gNNNN … --latest with the network as gofer-9x9.onnx plus champion.json, and release notes covering the match result, rating, anchors, network size and checksum.
  • Failures. If publishing fails (no gh, bad token, no network), the release stays pending. It's retried on the next generation and at startup, or by hand with python -m training.release --run runs/9x9 --repo OWNER/REPO. If a release already exists it counts as published, and only the newest pending champion is published, so an outage doesn't produce a burst of stale releases.
  • Unchanged. The committed models/gofer-9x9.onnx, which the website uses, isn't touched automatically.

The PR also documents the setup in docs/training.md ("Champions and releases") and the README, adds a CHANGELOG entry, and names the MIT license in the README and the site footer.

SPRT fix (second commit)

The arena's SPRT had three defects that could promote a network that isn't actually better:

  • It accepted H1 after two games. It estimated variance from the games so far, so after a 2–0 start the variance was 0. The 1e-3 floor then pushed the log-likelihood ratio to about 66 against a bound of 2.94.
  • It tested games in the order they finished, so quick results such as resignations were seen first.
  • Games finishing after a decision re-ran the test and could overwrite the decision.

The new test counts opening pairs that one side wins twice, takes them in pair order, and runs Wald's SPRT on them. Under the logistic Elo model, P(A sweeps | decisive pair) = 1/(1 + 10^(-2d/400)) whatever the color or opening advantage, because those terms cancel. So Wald's inequality bounds false promotion by alpha/(1-beta) = 5.3% at any game limit, and the decision is taken once, at the first crossing.

TestSPRTErrorRates checks this by simulating 4000 matches per case, with a color advantage and random per-opening advantages:

True situation Event Measured rate
Equal strength false promotion 4.1%
Challenger 50 Elo weaker false promotion 0.0%
Challenger 50 Elo stronger false rejection 4.5%
Challenger 150 Elo stronger promoted within 200 games 99.95%

On the real engine, the final network against the random generation 0 was accepted after exactly 12 sweeps, the theoretical minimum. The network against itself went 35–25 by chance and stayed undecided.

Setup on the training machine

gh auth login          # or: export GH_TOKEN=<fine-grained token, Contents: read and write on DevomB/Gofer>
python -m training.loop --run runs/9x9 --gofer bin/gofer --release-repo DevomB/Gofer

Test plan

  • go test ./... (including the SPRT simulation) and pytest training: 21 passed (7 new)
    • The win decision: only SPRT H1 promotes
    • The first champion needs champion_min_elo
    • A lower-rated generation never plays the champion
    • A challenger that loses isn't promoted
    • A promotion writes the files and the state
    • Publishing through a fake gh: correct tag, assets, --repo and --latest; older pending releases superseded; failures stay pending; existing releases count as published; no repository means no publishing
  • End-to-end with the real engine: a tiny 4-generation run with a fake gh on PATH
    • Gen 2 became the first champion and was published.
    • Gen 4, rated higher, played the champion with -sprt 0,50, lost 3–6, and was correctly not promoted.
    • python -m training.release reports the state.
  • First real release from a training machine with gh authenticated

https://claude.ai/code/session_01JyK2tUFLPsLyWjy2h2erVw


Generated by Claude Code

The loop now tracks a champion: on anchor generations, a network rated
above the current champion plays it directly (SPRT, champion_games at
champion_visits) and takes over if it wins. Each champion is kept in
runs/<run>/champion/ with its rating, match and anchor results, and with
--release-repo OWNER/REPO (or GOFER_RELEASE_REPO) it is published with the
GitHub CLI as a release tagged model-<run>-gNNNN. The network asset is
always named gofer-9x9.onnx, so releases/latest/download/gofer-9x9.onnx is
always the newest champion.

A release that fails to publish stays pending and is retried on the next
generation or with `python -m training.release`; only the newest pending
champion is published. README and docs/training.md document the setup;
the site footer and README now name the MIT license.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyK2tUFLPsLyWjy2h2erVw
@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gofer Ready Ready Preview Oct 24, 2026 8:31pm UTC

…n H1

The SPRT estimated the score variance from the games so far. After a 2-0
start that variance was zero, the 1e-3 floor made the log-likelihood ratio
about 66 against a bound of 2.94, and the match accepted H1 after two
games. It also tested games in completion order, so quick results (often
resignations by the weaker side) were seen first, and games finishing after
a decision re-ran the test and could overwrite it.

The test now counts opening pairs (the same opening with both colors) that
one side wins twice, in pair order, and runs Wald's SPRT on them. Under the
logistic Elo model, P(A sweeps | decisive pair) = 1/(1 + 10^(-2d/400)) for
any color or opening advantage, so decisive pairs are Bernoulli trials and
Wald's inequality bounds false acceptance by alpha/(1-beta) = 5.3% at any
game limit. The decision is taken once, at the first crossing.

TestSPRTErrorRates simulates 4000 matches per case with a color advantage
and random opening advantages: false promotion 4.1% at equal strength,
0.0% for a weaker challenger, false rejection 4.5% at +50 Elo, and a
+150 Elo challenger is promoted within 200 games 99.95% of the time.

Champion promotion now requires SPRT acceptance (no fallback on an
undecided match), champion_games defaults to 200, and release notes show
the evidence (pairs and sweeps).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JyK2tUFLPsLyWjy2h2erVw
@DevomB DevomB changed the title Publish each new champion as a GitHub Release Publish each new champion as a GitHub Release (with a provable SPRT) Oct 24, 2026

This branch was successfully deployed

1 active deployment
Preview — f6d6712b Deployed Oct 24, 2026 by vercel[bot]
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.

2 participants