Lab report · treadcore LR 124 · orrery · 28 October 2026

Census to Commit

Abstract

treadcore's bot has two brains. The scripted brain is code steered by 111 named numbers in one Rust table, weights.rs. The learned net is a file of 16,431 floats. The fleet improves the first without a person in the loop:

  1. A search proposes numbers.
  2. A census measures them against today's defaults.
  3. Two held-out censuses confirm them, and nine scenario families must hold their floors.
  4. orrery's auto mode edits weights.rs, runs the brain's tests on a branch, and pushes to main.

From then on, every machine and every fresh clone builds the new defaults. This report follows the one promotion so far, grove/w0034 → dc3df01, minute by minute. It shows how the brains are stored, down to the byte, and explains the Results tab. That covers why 36 candidates read "older", and why one task "failed".

It also records what this step added:

IIntroduction

Create a learning loop and describe how the coefficients get commited back to treadcore, so the it picks it up in the AI. For example, show me commit versions, and timestamps. Actaully, show me hos the brainz and the net data are stored, show me the matrix for the AI. show me its composition, draw diagrams. we want to see the process of discovering a change, and promoting it, so push it on to the next download of treadcore. essentially creating micro versions of this git submission. its part of the program. it has a do it now button to sync and push the values.
also describe the results tab. why do we have failed tasks in family. why are some older, when it says promoted. describe the layout. of what happends. perhaps add a scrollbar and a filter by type.

Paul Richeson, 28 October. Everything below was read from the repository, the queue's files and the fleet on that day. No number in it is illustrative.

IIWhere the brains are stored

A. The scripted brain: 111 named numbers in one table

The scripted brain decides with rules, and every number a rule weighs is one row of the weights! table in source/treadcore-brain/src/weights.rs. A row has three parts:

The macro turns the table into a flat array, Weights, which is lent to whatever scores and never copied per think. The defaults are compiled into every binary built from the commit: the game, the census, the fleet's jobs.

// source/treadcore-brain/src/weights.rs, at dc3df01 (the promotion's lines) weights! { // Tactics: target selection (tactics.rs; the 22 weights and their scales). Near "w_near" 2.0, "target: worth of nearness, w_near / (1 + d / near_scale)"; NearScale "near_scale" 15.0, "target: the distance scale of nearness, tiles"; InRange "w_in_range" 1.4, "target: in direct range now"; KiteIn "kite_in" 4.5, "kite: the ring a step in ends on, tiles"; PostureFortify "w_pos_fortify" 6.0, "posture: worth of Fortify's score, ..."; DodgeHorizon "dodge_horizon" 1.08307, "dodge: seconds before a shell lands that the tank starts out ..."; CastleInside "castle_inside" 8.0, "castle: pillboxes inside the wall, then it stops filling (rounded)"; ... 111 rows in all }
Figure 1. The table is the scripted brain's whole adjustable surface. A promotion is an edit to the defaults in it, nothing else; the green numbers are the ones w0034 changed.

A trial does not edit the table. It is a small text file that names only the numbers it moves. The census passes it with TRIAL= (the game takes it with --weights FILE), and every number it doesn't name keeps the compiled default:

# grove/w0034's trial: census +2.49 ± 0.94, held out +2.67 ± 0.27 and +3.17 ± 0.20 w_near = 2 w_in_range = 1.4 kite_in = 4.5 w_pos_fortify = 6 dodge_horizon = 1.08307 castle_inside = 8 longshot_every = 2.5

B. The learned net: 65,752 bytes

The net that drives a learned tank is a file in nets/. Six are kept: phase1, map4, map5, map7, map7a and bc1/bc2. map7 is the official one, and the official net changes only when Paul says. bc2 is the one the Net page shows.

The format is TCNET1, written by Net::save in net.rs:

nets/bc2.bin, 65,752 bytes, drawn to scale W1 64 × 143 = 9,152 floats W2 64 × 64 W3 47 × 64 header, 28 B: "TCNET1\0\0", 4, then 143 64 64 47 b1, 64 floats b2, 64 b3, 47 28 + 4 × (9,152 + 64 + 4,096 + 64 + 3,008 + 47) = 28 + 4 × 16,431 = 65,752 bytes
Figure 2. The file is the matrices, laid end to end. The first eight bytes read 54 43 4e 45 54 31 00 00 ("TCNET1"), then 04 00 00 00 (four sizes), then 8f 00 00 00 (143), 40 (64), 40 (64) and 2f (47). Those are the first 28 bytes of the real file.

C. The net's composition, and what each matrix does

143 senses compass 888 sectors × 11 channels:hostile tank, allied tank,pillboxes, depots, shellcoming, ground, cover, call body 12 navigation 6 contacts 18enemy, take, home, call hints 9 shells 5 air 5 W19,152 layer 164 unitsmax(0,·) W24,096 layer 264 unitsmax(0,·) W33,008 47 scores, by head drive 10 gun 9 lead 3 mortar 9 drone 5 squad 7 signal 4 highestin eachhead isthe act scores = W3 · max(0, W2 · max(0, W1 · senses + b1) + b2) + b3 16,431 numbers: 9,152 + 64 + 4,096 + 64 + 3,008 + 47
Figure 3. One think.
  • The senses: 143 numbers from 0 to 1 (bearings as a sine and a cosine, −1 to 1), from senses.rs.
  • Matrix 1 (64 rows by 143 columns): each of layer 1's 64 units weighs every sense, adds its bias and bends the sum at zero. Column i of matrix 1 is everything sense i says to the net.
  • Matrix 2 mixes those 64 into 64 more.
  • Matrix 3 (47 rows by 64 columns): each row makes one score. Row o of matrix 3 is everything that reaches score o.
  • The heads split the 47 scores into seven choices, and in each head the highest score is what the tank does.
orrery's Results, Net page: the three matrices as heat maps, a bar for every sense and every score, and how often the net makes the scripted brain's choice
Figure 4. The same net in orrery (Results → Net).
  • Heat maps: the three matrices, green adding and red taking away.
  • Every sense (new): a bar for each of the 143, the size of its column of matrix 1, grouped compass, body, navigation, contacts, hints, shells, air.
  • Every score (new): a bar for each of the 47, the size of its row of matrix 3, grouped by head.
  • Hover: pointing at a bar names it (for example compass 2 (right): shell coming), says how it is transformed on its way, and marks its column or row on the matrix above.
  • Accuracy: the last block is how often the net makes the scripted brain's choice on matches it never saw. bc2: drive 66.6%, gun 62.7%, mortar 89.9%, drone 96.8%, lead 99.8%, against 36.5%, 37.9%, 87.7%, 94.0% and 99.8% for always picking the commonest choice.

IIIThe learning loop

The scripted brain's numbers improve in a loop that closes itself in auto mode. Each box below is a kind of job in grove's queue, and every arrow is a file the next step reads.

1 · Search spsa, tune, suggest.py over the fleet's ledger (4,230 censuses) 2 · Trial census 55 seeds, 900 s, every map, against today's defaults 3 · Two held-out reads 400 fresh seeds each; both past two standard errors 4 · Candidate current if all three reads ran on today's weights.rs; else older 5 · Families nine scenario families, each held to its floor (85 of 85 hold) 6 · Promote (auto) the strongest current candidate; a run, rNNNN, asked by auto mode 7 · Branch promote/grove-rNNNN edit weights.rs defaults cargo test -p treadcore-brain every_family_holds_its_floor 8 · Approve: push to main fast-forward, or replayed if only records moved on main tag brain-rNNNN (new) 9 · Every machine builds it prep job per host: worktree, census binary, defaults.toml fetched back as proof 10 · Propagation each machine's defaults compared with the promoted values: 7 of 7 11 · Re-read the older (new) the 3 strongest older candidates, measured again on the new defaults Next download git clone, make: the brain plays with main's defaults a re-read is a new trial: back to 2 main is what every clone gets; a v* tag packages a release (GitHub Actions), only when Paul asks a brain-rNNNN tag marks each promotion: a micro version, which starts no workflow
Figure 5. The loop.
  • Boxes 1–6 run as ordinary jobs on whichever CPU lane is free.
  • Box 7 runs on the Mac, in a worktree of its own.
  • Box 8 is the only step that changes what anyone downloads.
  • Box 11 and the tag in box 8 are this step's additions.

The rule a candidate must pass

ReadSeedsBarw0034
Trial census, every map, 900 s55past two standard errors above the defaults; no map past two below+2.49 ± 0.94
Held-out read 1400past two standard errors again, seeds never searched on+2.67 ± 0.27
Held-out read 2400the same, other seeds+3.17 ± 0.20
Families400–1,000 a familyeach of nine families at or above its floorholds
The brain's tests—109 pass on the promotion branchpass

IVOne promotion, minute by minute

grove/w0034 is the only candidate promoted so far. These are its times from the queue's run files and from git, Pacific time, 28 October 2026.

  1. 13:38
    w0034 passes both held-out readsa candidate: +2.67 and +3.17. Its census and reads ran on commit 92972b7.
  2. 14:14:02
    Auto mode asks for run r0019promote grove/w0034: the strongest current candidate whose families hold.
  3. 14:20:54
    Commit dc3df01 on promote/grove-r0019weights.rs edited (7 lines); 109 tests and every family pass on the branch. The commit message carries the evidence.
  4. 14:28:08
    Approved: pushed to mainauto mode approves its own prepared promotion; main is dc3df01. From here a clone gets the new brain.
  5. 14:30
    LR 119 recorded on top: c1e00c0the records commit; it moves no brain source, so the defaults stay dc3df01's.
  6. 16:05:21
    Confirmed on 7 of 7 machineseach machine built a commit since the approval and its defaults.toml has the promoted values (whitewolf by c1e00c0, the rest by dc3df01).
  7. 19:09:33
    Tag brain-r0019 pushedthe micro version, made for r0019 after the fact (tag object c066c65, signed); every promotion from now on is tagged as it is approved.
$ git show --stat dc3df01 commit dc3df0196d0a1ba8076f66592f88d60a940a3cb2 Author: Paul Richeson <paulr@sdf.org> Date: Wed Oct 28 14:20:54 2026 -0700 promote grove/w0034 as the brain's defaults -- w_near 1.0 -> 2.0; w_in_range 0.7 -> 1.4; kite_in 9.0 -> 4.5; w_pos_fortify 3.0 -> 6.0; dodge_horizon 2.0 -> 1.08307; castle_inside 4.0 -> 8.0; longshot_every 5.0 -> 2.5; ... census +2.49 +/- 0.94, held out +2.67 +/- 0.27 and +3.17 +/- 0.20; families holds; prepared by orrery (queue grove, run r0019), approved by orrery's auto mode (Paul, 28 October) source/treadcore-brain/src/weights.rs | 14 +++++++------- $ git describe --tags --match 'brain-*' origin/main brain-r0019-5-g19638a5 # main is 5 commits past the micro version: records only
Figure 6. The promotion as git records it. A micro version is the commit, its tag and its message. They say which numbers changed, by how much, on what evidence, and which run made it. git describe names the brain any checkout plays: the last brain- tag, and how far past it.
19638a518:38 LR 123: orrery's story 8df8ab118:22 LR 122: brushed metal ebb2bb917:47 LR 121: crucible c29a2ed15:13 LR 120: the Machines view c1e00c014:30 LR 119: the first auto promotion dc3df0114:20 promote grove/w0034 as the brain's defaults brain-r0019 bd31f8f14:16 LR 118: auto mode 9c9abc114:03 reports: the AppKit lab report
Figure 7. main, newest at the top. Only the green commit changed the brain; the others are records and reports. The brain any of them builds is brain-r0019's.

VThe Results tab

orrery's Results, Candidates page
1
2
3
4
5
6
7
8
Figure 8. Results → Candidates, at the time of writing. The numbers refer to the key below.
  1. 1
    The chapters. Results is chapter 4 of five. Machines adds a computer, Projects gives it work, Queue runs it, Results reads what it found, and Watch looks in.
  2. 2
    The pages. Candidates (with its count), Found (improvements by variable and machine), Net (Figure 4) and Flags (what waits for a person). A green lamp means there is something new; a red lamp on Flags means something needs answering.
  3. 3
    The headline, and Do it now (new). Press it twice. It syncs every machine's ledger, then promotes the strongest current candidate; auto mode pushes the promotion to main and tags it once its tests and families pass. With no current candidate, it re-reads the strongest older ones instead. The menu bar has it too: Fleet → Do It Now, which arrives armed.
  4. 4
    The newest change any census reported: the coefficient, its value, its offset from the default, the census, and the machine that read it.
  5. 5
    Auto mode and its switch. With auto on, nothing on this page waits for a person.
  6. 6
    Promotions, newest first. Under the newest approved one is each machine's confirmation (ok: its build has the values). r0009 is grey, not red: it failed before r0019 landed, and it is marked superseded.
  7. 7
    The filter (new): All, Promoted, Current and Older, each with its count.
  8. 8
    The list and its scroll bar (new): the wheel, or drag the bar. Each line shows when, the trial, its two held-out reads, its standing, and what it moves.

Why 36 candidates say "older" while one says "promoted"

A census measures a trial against the defaults of its day: +2.67 means w0034 beat the brain of commit 92972b7. When w0034 was promoted, the defaults became w0034's own numbers. Every other candidate's evidence then describes a race against a brain the game no longer has.

So the page labels each with the commit it was read at, older (89371b6), and auto mode promotes only a current candidate. Before this step, nothing turned an older candidate current. The loop stood still at "0 current", and Do it now would have had nothing to push.

Box 11 of Figure 5 is the fix. With none current, auto mode re-reads the three strongest older candidates on today's defaults. Each is a new trial with its own census and two held-out reads, and one that still wins becomes current and promotes itself. The first round started at 19:11: w0045 (held out +2.45 and +3.09) and w0003 (+2.50 and +2.80). It also re-read w0034 against its own values. That is wasted work, kept as a control: it should read near zero. Promoted candidates are now excluded.

Why "failed tasks in family"

No family failed: grove has read 85 candidate-and-family sets, and all 85 hold. The one failed job in the queue is r0009, a promotion, at 10:12 that morning. Its message said "the tests or a family failed", which pointed at the families. Its log says otherwise:

---- weights::tests::default_weights_are_todays_constants stdout ---- assertion `left == right` failed: the weights at their defaults are the Tactics of 17 October left: "Tactics { w_near: 2.0, ... w_in_range: 1.4, ... right: "Tactics { w_near: 1.0, ... w_in_range: 0.7, ... test result: FAILED. 108 passed; 1 failed; 14 ignored

r0009 changed the table's defaults but not Tactics::default(), which a test holds equal to them. LR 119 fixed the promotion step to change both (changed_vars), and r0019 passed with 109 tests four hours later. A failed promotion now names the step that failed, read from its log:

$ python3 -I tools/plans/treadcore.py build/queue/grove why r0009 the brain's tests failed (weights::tests::default_weights_are_todays_constants): see logs/r0009-promote.log

VIThe second job: crucible on the GPUs

orrery's Projects page with treadcore and crucible
Figure 9. Projects, with crucible in the queue.

Add the GPUs to crucible (Projects, two presses) gives every machine with a card and CUDA a second lane, NAME-cuda. It is the same machine again, taking only crucible's work, so its CPUs go on with treadcore's censuses.

Each lane is an island:

  • its own population, trained 500 generations a segment, its state kept on the card's machine;
  • six segments queued ahead, each after the last, so the card is not left idle between plans;
  • each segment sends back its champion (8.6 KB) and takes the other islands' champions into its hall of fame.
calber-cuda · 1080 Tiwhitewolf-cuda · 1080boburner-cuda · 1070 Ti

crucible trains its own arena, not treadcore's brain. It reaches treadcore's candidates only through a GPU arena built from treadcore's combat rules (LR 121 D). That is the next step if Paul wants it.

VIITutorial: following a change by hand

  1. See what the fleet is doing.
    python3 ~/code/orrery/tools/fleet-queue.py build/queue/grove status
  2. Read every candidate's evidence, strongest first, with its standing:
    python3 -I ~/code/orrery/tools/plans/treadcore.py build/queue/grove evidence grove/w0045 held +2.45 +3.09 families holds older defaults grove/w0003 held +2.50 +2.80 families holds older defaults
  3. Try a candidate's numbers yourself. Write them as a trial file and play a census, or the game, with them:
    printf 'w_near = 2\nkite_in = 4.5\n' > my.weights TRIAL=my.weights SEEDS=55 scripts/census.sh
  4. Do it now, from orrery (Results, two presses) or the command line:
    python3 ~/code/orrery/tools/fleet-queue.py build/queue/grove now no candidate on today's defaults (19638a5) to promote; read again: ...
  5. Find the micro versions and the brain your checkout plays:
    git tag -l 'brain-*' git show brain-r0019 git describe --tags --match 'brain-*'
  6. Read a net's matrices in a few lines of Python:
    import struct b = open("nets/bc2.bin", "rb").read() assert b[:8] == b"TCNET1\0\0" k = struct.unpack_from("<I", b, 8)[0] sizes = struct.unpack_from(f"<{k}I", b, 12) # (143, 64, 64, 47) off = 12 + 4 * k for a, o in zip(sizes, sizes[1:]): W = struct.unpack_from(f"<{a*o}f", b, off); off += 4 * a * o # o rows of a bias = struct.unpack_from(f"<{o}f", b, off); off += 4 * o
  7. Give the GPUs to crucible, or take them back:
    python3 ~/code/orrery/tools/machines.py build/queue/grove cuda-lanes add all python3 ~/code/orrery/tools/machines.py build/queue/grove cuda-lanes remove all

VIIIResults and discussion

Measure, 28 October 19:15Value
Censuses in the fleet's ledger4,230
Weight trials finished378
Candidates (past both held-out reads)37: 1 promoted, 0 current, 36 older
Family reads85 of 85 hold
Promotionsr0019 approved (dc3df01, brain-r0019); r0009 failed (the tests), superseded
Machines with the promoted values7 of 7
Older candidates being re-readw0045, w0003 (and w0034 as a control)
crucible islands3 lanes, 6 segments queued each

The path from a number found by a search to the brain in a fresh clone has no manual step left. It is measured three times, held to nine families and the brain's tests, committed with its evidence, tagged, built and checked on every machine. It took 50 minutes from candidate to main, and 1 h 37 min more until every machine had built it. Most of that was waiting for busy lanes.

The limit is the evidence's shelf life. A promotion makes every other candidate's evidence stale. Until this step, the loop stopped there, waiting for new searches to find a new candidate. Re-reading the strongest older ones turns that stock back into candidates. Whether any of them still beats the new defaults is what the re-reads will tell.

Errors and limits