Field notes / Experiment guide
Where does the lead slope live?
Does circulation frequency explain why the birth-cost slope falls with loop length?
New to these ants? Start here.
An ant moves on a grid of coloured squares. Each colour tells it to turn left (L) or right (R). It advances that square to the next colour, then steps forward. A rule such as RLR is the list of turns, starting at colour zero. On a blank grid the same rule always makes the same trajectory.
Territory means every cell visited so far. Its boundary consists of visited cells with an unvisited neighbour. A highway is a repeating pattern of motion that carries the ant away. A tether means repeated returns to a small home region.
Growing like the cube root of time means taking eight times as many steps to double a linear size. The rotor walker is a comparison model that follows and rotates arrows stored on cells. A seed makes a random starting pattern reproducible; the ant itself can still move deterministically.
We distinguish c, the chance of discovering a cell per boundary step, from kappa (κ), time at the boundary normalized by perimeter divided by area. Explaining their values is harder than using them to account for growth.
The idea under test
The lattice slope difference might be carried by circulation episodes per birth rather than reads per circulation.
What we learned
The registered circulation decomposition fails. Its headline frequency comparison counts interior loops and is not interpretable as evidence; boundary-only segmentation is the next proposal.
What this leaves open
Per-colour survival patterns are exploratory. A formally passing statistic with an interior-contaminated estimator does not validate the mechanism.
Read the evidence and reproduction recipe ↗
Inside the experiment
Methods, registered predictions, exact commands and saved analysis. These are the working research documents; expand them for the detail behind the introduction.
Methods and original experiment record
Why does the c-law slope b fall with loop length? Circulations per birth versus reads per circulation
Registered 2026-10-20 00:55 (+13:00), base commit 6049d6a plus the uncommitted instrument (src/lattice_trace.rs,
tests/lattice_trace.rs, examples/lattice_c_law_colours.rs), committed together with this file before any run on the
960-ant list. Nothing in this folder has been run on that list. Disclosure of what was seen before: the lattice
c-law results (experiments/2026-10-20-lattice-c-law, slopes b and intercepts per stratum, per-lead medians), and on the
square lattice only, the per-colour and lag-4 findings of experiments/2026-10-19-c-law-mechanism (r_k decays ~0.88 per
read; 48-78% of circulating reads return after exactly 4 steps). No per-colour, lag or run statistic has been looked at
on the triangular or hexagonal lattice. The tracer's own gates (below) ran on short random rules only.
Question
1/c = a + b * lead holds on three lattices (R^2 0.94-0.95 square and triangular), but b falls with the loop that the lead
letter closes: 0.86 (hex R, loop 3), 0.70 (square, 4), 0.34 (triangular, 6), 0.28 (hex r, 6; per-lead medians, since the
OLS is destroyed by one rule). The lattice study's first-principles reading, b = l_b / N_b (boundary cells per lap over
births per circulation episode), was never measured on either side, and its three hypotheses (b grows with l, constant,
~4/z) are all disproven. A fitted b(l) would be a fourth guess. Instead: measure the two factors of b.
Definitions (frozen)
- Everything as in
experiments/2026-10-20-lattice-c-law(windows, boundary step, birth, lead, strata,alpha_wblob filter, kept set). The tracersrc/lattice_trace.rsuses the last octave only (steps (2^26, 2^27]), identical to the probe's window k = 26. - Return: a step that starts on a cell last read exactly
ellsteps earlier;ell= loop length of the rule's first letter: square 4, triangular 6, hexr/l6, hexR/L3 (checked by stepping intests/lattice_probe.rs, gate G3 of the parent). - Circulation: a maximal run of consecutive returns (any colour, boundary or not) of length >=
ell(one full lap re-read).eps= circulations per birth.R= mean run length of circulations (lengths capped at 63).C= total steps inside circulation runs per birth =eps * R.Cb= the same counting only boundary steps in those runs. - Slopes are Theil-Sen over kept rules (primary, robust to the 1/c = 390 outlier) and the slope of per-lead medians (secondary), leads 1..5 for 1/c, and m = lead for C, Cb, eps, R. OLS is reported but is not primary.
b_med= slope of per-lead medians of 1/c, recomputed from this run (not copied).
Gates (instrument; a fail voids the dependent claims)
- T1 (done before this registration,
cargo test --release --test lattice_trace): squareLTraceequalsedge_trace::trace_blankin every field on 30 random rules; hex, triangular and square equal a HashMap re-implementation on 30 random rules each; two one-line mutations (lag off by one, return lagell + 1) were each caught by the tests. - T2 on the 960-ant list: for every rule not escaped on the 8192 board,
birthsandboundary_stepsequalants.csvwindow k = 26 exactly, andsum_k edge_k = boundary_steps. - T3
1/cfrom the tracer equals the parent's to 1e-12 (follows from T2; reported anyway).
Predictions (kept rules, per stratum; my odds in brackets)
- P1 (circulation accounts for the lead slope).
Cbper-lead-median slope is within [0.8, 1.2] xb_medin all four strata. [45%] (If this fails the reads that grow with the lead are not in lag-ellruns, and the loop picture of the slope is incomplete off the square lattice.) - P2 (reads per circulation).
Rper-lead-median slope divided byelllies in [0.5, 1.1] in all four strata (this isl_b / l: the fraction of a loop that is boundary and survives a lap). [50%] - P3 (the discriminator).
b = eps * lambdawithlambda = dR/dm. The drop of b with loop length is carried byeps, not bylambda:epsat lead 3 (stratum median over kept rules) is lower on triangular than on square by a factor >= 2.0, and lower on hex r than hex R by a factor >= 3.0. [55%] Rival (lambda carries it, N_b/l roughly constant): ratios ofepswithin [0.7, 1.4] of 1, withlambda/ellfalling instead. Reported with the measured vector either way. - P4 (primary robust slope). Theil-Sen b is within the parent's OLS bootstrap CI on square, triangular and hex R, and in [0.15, 0.45] on hex r. [60%]
- P5 (hex ring centre). Births into a cell whose neighbours are all already visited (
hole_births, e.g. a 6-ring centre) per birth are >= 2x higher on hex r-lead than hex R-lead (median over kept rules), because only the r ring encloses a cell. [40%] - P6 (letter-typed hex lead). Splitting hex R-lead kept rules by the killing letter (the first letter different from the lead:
r,l,L) and fitting1/c ~ leadper type raises the mean R^2 weighted by n from 0.69 to >= 0.80. [40%] Not registered: any claim about U-turn letters (turn 2 on square, 3 on hex); a second stage with a new loop length (period 2) would need its own registration.
Decision rule
P3 is the headline. Supported if both ratios pass; "rival" if both eps ratios are in [0.7, 1.4]; otherwise undecided with the measured vector. A gate failure (T2) on a lattice means no claim on that lattice.
Commands
bash experiments/2026-10-20-lattice-c-law-colours/scripts/run.sh # fresh target dir, sha256 logged, writes results/traces.jsonl
uv run experiments/2026-10-20-lattice-c-law-colours/scripts/analyse.py
Results
Run 2026-10-20 (scripts/run.sh, 509 s wall on 6 threads, shared with another job; binary sha256 24ea23a2...a8701 in results/run.log;
traces.jsonl 1.8 MB), uv run scripts/analyse.py -> results/analysis.{txt,json}, post hoc scripts/explore.py -> results/explore.txt.
run.log says "dirty: 0 files" but the one-line driver fix (see NOTEBOOK 01:20) was uncommitted at run time (the git status under the
GIT_DIR workaround is unreliable); it is committed with these results.
Gate T2 passed: 855 rules compared (105 escaped in the tracer or the parent), 0 mismatches in births, boundary steps, or per-colour sum.
Registered predictions
| verdict | numbers | |
|---|---|---|
| P1 Cb slope / b_med in [0.8, 1.2] | FAILED | 0.42 / 0.19 / 0.29 / 0.17 (square / tri / hex R / hex r) |
| P2 R slope / ell in [0.5, 1.1] | FAILED | 0.08 / 0.07 / 0.20 / 0.10 |
| P3 eps ratios >= 2.0 and >= 3.0 | formally met (7.2, 75) but not interpretable | see below |
| P4 Theil-Sen b in parent CI | HELD | 0.691, 0.347, 0.850; hex r 0.306 in [0.15, 0.45] |
| P5 hole births r/R >= 2 | HELD | 0.094 vs 0.0097 per birth (9.7x); hex r at lead 1: 0.155 |
| P6 per-kill-letter hex R fits, weighted R^2 >= 0.80 | FAILED (0.781) | kill l: R^2 0.941 (b 0.88); kill L 0.717; kill r 0.696 (pooled 0.689) |
Why P3 is not evidence. My circulation definition (runs of lag-ell returns of any cell, boundary or not) counts interior
loops too. C (steps in such runs) is 30-120 per birth on the square lattice while 1/c is 2-5, so eps (1-19 circulations per
birth) and R are dominated by interior behaviour and say nothing about the boundary episodes in b = l_b / N_b. The boundary-only
version, Cb, is what P1 tests, and it carries only 17-42% of the slope. The decomposition I intended (circulations per birth times
boundary cells per circulation) is therefore not measured; P3 passing is an artefact of a bad estimator and I do not count it.
The headline question is unanswered by the registered tests.
Post hoc (unregistered, results/explore.txt; all non-escaped rules, per-lead medians, no blob filter). Boundary reads per birth by
the colour the cell had when read (1/c is their sum). The lead slope lives in reads at the circulating colours 1..m-1 and is
partly cancelled by falling reads at the killing colour and later:
| stratum | circulating reads added per extra lead step | slope of circ | slope of kill | slope of later | total slope |
|---|---|---|---|---|---|
| hex R (loop 3) | 0.95 each (0.95, 0.96, 0.96, 0.94) | 0.95 | -0.02 | -0.07 | 0.88 |
| square (4) | 0.85, 0.78, 0.79, 0.77 | 0.80 | -0.05 | -0.06 | 0.69 |
| triangular (6) | 0.65, 0.50, 0.37, 0.35 | 0.46 | -0.08 | -0.02 | 0.36 |
| hex r (6) | 0.69, 0.62, 0.53, 0.47 | 0.58 | -0.11 | -0.20 | 0.28 |
Since a cell is read once per colour, "reads per birth at colour k" is the fraction of born cells that are still boundary cells at
their k-th read. So the c law reads: that fraction is a plateau of about 0.95 (hex R), 0.8 (square), 0.4-0.65 (triangular),
0.5-0.7 (hex r) across the lead colours, with a slow decay for the larger loops, and b is that plateau minus the loss at the killing and
later colours. This restates the identity 1/c = E[N_b] and is not a mechanism, but it says what must be explained: why a ring
cell stays a boundary cell through the circulating reads with probability ~1 for loop 3 and ~0.5 for loop 6.
Two further observations. (a) The ring-circulation picture does not carry the circulating reads off the square lattice: the share of
circulating reads with lag exactly ell is 0.67-0.87 square but 0.05-0.44 triangular, 0.28-0.40 hex R, 0.06-0.24 hex r (rising with lead).
(b) Hex r-lead rules fill holes: 15% of births at lead 1 land in a cell whose neighbours are all visited (the ring centre), falling to 6% at lead 5;
hex R and square sit at 1-2%.
Reading. Not explained. What this run adds: the slope is carried by circulating-colour reads at a plateau rate that falls as the
loop lengthens; loop-return (lag-ell) circulation is the dominant carrier only on the square lattice, so the "2x2 loop" picture
should not be transplanted to other lattices; hex r rules behave differently through hole filling; the kill letter's type matters on hex
(l-killed R-lead rules are linear in lead with R^2 0.94 and the other two types are not). Next registered step, if pursued: a
boundary-only episode segmentation (maximal runs of consecutive boundary reads of colours 1..m-1), and the plateau fraction itself
as a function of ring geometry (outside neighbours per ring cell: square 2, triangular 1, hex R 4, hex r 3), which is a measurable
prediction not yet made.
Analysis notebook: notebook.ipynb
3 saved outputs · 3 code cells. Cells are not executed during publication. Download notebook
Where does the lead slope live? Circulations vs boundary reads by colour
Question
On the square lattice, $1/c = 1.36 + 0.70 \times \text{lead}$. Off the square lattice, the slope $b$ falls with the loop length $\ell$ that the lead letter closes: 0.86 (hex R, $\ell=3$), 0.70 (square, 4), 0.34 (triangular, 6), 0.28 (hex r, 6). The first-principles reading $b = \ell_b / N_b$ (boundary cells per lap over births per circulation episode) had never been measured. This experiment measures the two factors of $b$ and tests whether circulation frequency carries the drop across lattices.
Assumptions and definitions
- Everything as in
experiments/2026-10-20-lattice-c-law(octave window $k=26$, steps $(2^{26}, 2^{27}]$). - Return: a step starting on a cell last read exactly $\ell$ steps earlier (square 4, triangular 6, hex r 6, hex R 3).
- Circulation: maximal run of consecutive returns of length $\ge \ell$.
- $\epsilon$: circulations per birth
- $R$: mean run length of circulations (capped at 63)
- $C$: total steps in circulation runs per birth ($= \epsilon R$)
- $C_b$: boundary steps in circulation runs per birth
- Gates: T2 requires births and boundary steps to match parent
ants.csvexactly, with $\sum_k \text{edge}_k = \text{boundary steps}$.
Code
import hashlib, json, subprocess
from pathlib import Path
ROOT = next(p for p in [Path.cwd(), *Path.cwd().parents] if (p / "AGENTS.md").exists())
R = ROOT / "experiments/2026-10-20-lattice-c-law-colours/results"
for n in ["traces.jsonl", "analysis.json", "analysis.txt", "explore.txt", "run.log"]:
print(hashlib.sha256((R / n).read_bytes()).hexdigest()[:16], n)
git_rev = subprocess.run(["git", "rev-parse", "--short", "HEAD"], cwd=ROOT, capture_output=True, text=True).stdout.strip()
print("git", git_rev or "unresolvable")
A = json.loads((R / "analysis.json").read_text())
Results
Gate T2 and Registered Predictions (P1–P6)
Code
# Summary of registered predictions and outcomes
for stratum, data in A.items():
print(f"=== {stratum} (ell={data['ell']}) ===")
print(f" inv_c medians: {[round(x, 3) for x in data['inv_c_meds']]}")
print(f" Cb medians: {[round(x, 3) for x in data['Cb_meds']]}")
print(f" C medians: {[round(x, 1) for x in data['C_meds']]}")
print(f" b_med: {data['b_med']:.3f} | Theil-Sen b: {data['b_ts']:.3f} | Cb slope: {data['Cb_slope']:.3f} | R slope: {data['R_slope']:.3f} (R slope/ell: {data['R_slope']/data['ell']:.3f})")
print(f" eps(lead 3): {data['eps3']:.2f}")
Why P3 is not evidence
The registered discriminator P3 (comparing $\epsilon$ across lattices) appeared to pass formally with huge ratios (7.2 and 75). However, the circulation definition counted interior returns as well as boundary returns. $C$ (steps in circulation runs) is 30–120 per birth on the square lattice while $1/c$ is only 2–5! Interior loops completely swamped the boundary episodes. The boundary-only version $C_b$ carries only 17–42% of the slope ($C_b$ slope / $b_{\text{med}} = 0.42$ square, $0.19$ tri, $0.29$ hex R, $0.17$ hex r), so P1 failed. The circulation-lap decomposition $b = \ell_b / N_b$ was therefore not validly measured by this instrument.
Post hoc: Where does the slope live? By colour!
Code
# Post hoc decomposition of boundary reads by colour group
print((R / "explore.txt").read_text())
Interpretation
- The lead slope lives in circulating colours (1..m-1):
- Hex R (loop 3): ~0.95 boundary reads added per lead colour.
- Square (loop 4): ~0.80 boundary reads added per lead colour.
- Triangular (loop 6): ~0.65 to 0.35 boundary reads added per lead colour.
- Hex r (loop 6): ~0.69 to 0.47 boundary reads added per lead colour.
- Cancellation by killing and later colours: The slope of reads at the killing colour and later colours is negative (-0.05 to -0.31), pulling down the net slope $b$.
- Lag-$\ell$ ring returns do not generalize off the square lattice: On the square lattice, 66–87% of circulating reads return after exactly 4 steps. On triangular and hexagonal lattices, lag-$\ell$ share is small (5–44%). The "2x2 ring loop" picture is square-specific.
- Hex r rules fill ring centres: 15% of lead-1 births on hex r land in cells whose neighbours are all visited (hole births), dropping to 6% at lead 5. Hex R and square sit at 1–2%.
Caveats and Limitations
- The registered circulation estimator was corrupted by interior loops; passing P3 is an artefact.
- Post hoc colour decomposition restates the identity $1/c = E[N_b]$ and shows where the slope lives, but does not yet supply a dynamic mechanism for the plateau survival probability.
- Survivorship selection: kept rules filter out blob/lacy rules, which differ across lattices.
Next steps
- Segment boundary-only episodes (runs of consecutive boundary reads of colours 1..m-1) to isolate genuine boundary revisits.
- Model the per-colour boundary survival plateau as a function of local geometry (e.g. outside unvisited neighbours).
Read the dated lab log
Lab notebook: lattice c law, colours and circulations
Append-only. Newest entry last. Never edit an old entry; correct it with a new one
(corrects: <timestamp>). Tags: intent, assumption, observation, interpretation,
surprise, decision, dead-end, question, deviation. See experiments/README.md.
2026-10-20 00:55 (+13:00) · 6049d6a (+uncommitted instrument) · intent
Take the top open item of notes/ACTIVE.md (why b falls with loop length) and measure the two factors of the parent's
identity b = l_b / N_b rather than fit b(l). Built src/lattice_trace.rs (edge_trace's per-colour, lag, N_b tallies on three
lattices, plus runs of consecutive lag-ell returns and hole births). Gates T1 passed (square equals edge_trace, all lattices
equal a HashMap re-implementation, two mutations caught; the mutation runs left a proptest-regressions file that I deleted
because its cases fail only under the mutants). Predictions P1-P6 are in the README, written before running the 960 ants.
2026-10-20 00:56 (+13:00) · 6049d6a · assumption
A circulation of the lead letter shows up as a run of consecutive reads with lag exactly ell. This can be false where the
ant crosses its own ring or a second loop shares cells; it is checked only through P1 (does Cb carry the slope). Untested.
2026-10-20 00:56 (+13:00) · 6049d6a · assumption
Alternative I cannot exclude: the b(l) drop is survivor selection (the kept set differs by lattice; square lead 2 and hex R lead 2 lose >50%). The tracer is run on all 960 rules and quantities are reported for kept rules only, with retention printed; a check on all non-escaped rules is a planned robustness line.
2026-10-20 01:20 (+13:00) · 34bb110 (+driver fix) · deviation
First two launches failed: (1) git inside WSL cannot read the worktree's Windows gitdir (fixed with GIT_DIR/GIT_WORK_TREE as in
notes/ACTIVE.md); (2) the driver did not strip the ant: prefix of the parent's spec lines, so every rule panicked on the letter a.
Fixed in examples/lattice_c_law_colours.rs only (one line, spec parsing); src/lattice_trace.rs and the tests are unchanged since the
registration commit. run.log will show the binary built from a dirty tree for that reason. No data had been produced by the failed runs.
2026-10-20 01:12 (+13:00) · 34bb110 (+driver fix) · observation
T2 passed (855 rules, 0 mismatches). P1 FAILED, P2 FAILED, P4 and P5 HELD, P6 FAILED (0.781 vs 0.80). Numbers in README Results; results/analysis.txt.
2026-10-20 01:13 (+13:00) · 34bb110 (+driver fix) · surprise
P3 passed formally (eps ratios 7.2 and 75) but I do not trust it: my run definition counts interior lag-ell loops, and C is 30-120 steps per birth while 1/c is 2-5. Interior behaviour swamps the boundary episodes. So the headline discriminator was badly designed, and passing it would have been a false positive. The boundary-only version (Cb) carries 17-42% of the slope. Recorded as a failed instrument design, not a result.
2026-10-20 01:14 (+13:00) · 34bb110 (+driver fix) · observation
Post hoc explore.py: slope lives in circulating-colour reads (0.95 per colour on hex R, 0.8 square, 0.65 to 0.35 triangular, 0.69 to 0.47 hex r), cancelled partly by
the kill and later colours. Lag-ell share of circulating reads is low off the square lattice (0.05-0.44). Hex r lead-1 hole births 15%.
2026-10-20 01:15 (+13:00) · 34bb110 (+driver fix) · interpretation
Reads per birth at colour k = fraction of born cells still on the boundary at their k-th read, so the c law is a boundary-survival plateau rho(geometry), b = rho - losses. This restates 1/c = E[N_b]; it is not a mechanism. The ring-loop explanation looks wrong off the square lattice (lag share is a crude proxy; moderate confidence). Outside neighbours per ring cell (square 2, tri 1, hex R 4, hex r 3) do not order the plateaus monotonically (tri 0.5, square 0.8, hex r 0.6, hex R 0.95): a question, not a hypothesis.
2026-10-20 01:16 (+13:00) · 34bb110 (+driver fix) · deviation
run.log reports "dirty: 0 files" although the driver fix was uncommitted when the binary was built; the git status under the GIT_DIR workaround is unreliable. The fix is committed together with the results.
2026-10-20 21:15 (+13:00) · 44ed81f · decision
Constructed and executed shareable notebook notebook.ipynb using scripts/build_notebook.py. It audits all committed results (traces.jsonl, analysis.json, analysis.txt, explore.txt, run.log), presents the registered prediction outcomes, explains why the interior-contaminated circulation estimator P3 was invalidated, and displays the post hoc colour-decomposition plateaus across square, triangular, and hexagonal lattices. Verified that blog/scripts/experiments.py renders the notebook (38 notebooks published).