Skip to content

Instantly share code, notes, and snippets.

@ariannamethod
Last active September 21, 2026 22:08
Show Gist options
  • Select an option

  • Save ariannamethod/6bace55c28b8823b04e09b45b5ce80c2 to your computer and use it in GitHub Desktop.

Select an option

Save ariannamethod/6bace55c28b8823b04e09b45b5ce80c2 to your computer and use it in GitHub Desktop.
NETTA is all you need — coherent speech from scratch: no transformers, no backprop, no optimizer. One file, stdlib only. Court-verified.

Toaster duel: netta.py vs stock nanoGPT — results

Run by the Opus hand on neo (Apple A18 Pro, 8 GB, CPU only, 2 torch threads), 2026-09-21, against the preregistration at ~/arianna/_notes/PREREG_toaster_duel_netta_vs_nanogpt_2026-09-21.md.

Everything below was measured on this machine by this hand, with one exception that is labelled where it appears: the netta.c row is Don's measurement, carried here, not re-run by me.

Versions: python 3.9.6 (/usr/bin/python3, runs nanoGPT) with torch 2.8.0 and numpy 2.0.2, both already present before this duel began; tiktoken 0.14.0, installed by Oleg's own hand into that interpreter's user site after the dependency gate refused this hand; python 3.14.4 in the workspace venv, which runs netta.py and both harnesses on the standard library alone. nanoGPT HEAD 3adf61e154c3fe3fca428ad6bc3818b27a3b8291.


The table

netta runs once by preregistration, so her row is the same at every budget and her seconds are her single cold-start-to-verdict. nanoGPT ran in two arms: the preregistration's stock config, and the recipe his own README prescribes for a machine like this one.

Budget System Seconds Held-out bits/byte Source Output? Longest verbatim Coverage ≥32
T1 = 10 s netta.py 13.16 wall / 12.97 own 3.360 her harness, corridor 1 yes, 5 streams, court PASS 29–49 B 0.000–0.143
T1 = 10 s nanoGPT stock 10.03 (rc 137) — no loss ever logged ran, no checkpoint, no output — —
T2 = 60 s netta.py 13.16 (same single run) 3.360 same yes 29–49 B 0.000–0.143
T2 = 60 s nanoGPT stock 60.03 (rc 137) — no loss ever logged ran, no checkpoint, no output — —
T3 = 600 s netta.py 13.16 (same single run) 3.360 same yes 29–49 B 0.000–0.143
T3 = 600 s nanoGPT stock 600.16 (rc 137) — no loss ever logged ran, no checkpoint, no output — —
T4 = 1800 s netta.py 13.16 (same single run) 3.360 same yes 29–49 B 0.000–0.143
T4 = 1800 s nanoGPT stock 1800.29 (rc 137) — no loss ever logged ran, no checkpoint, no output — —
one run netta.c (original mouth) 16.53 (rc 0) not priced — yes, 5 streams see boundary note see boundary note
to completion nanoGPT CPU recipe 82.52 (rc 0) 2.7205 his own logged val, 1.8857 nats / ln2 yes, 10 samples 12–15 B 0.000000000 (all 10)

netta.py finishes at 13.16 s and netta.c at 16.53 s, so both miss T1 = 10 s and clear T2 onward. Reported as measured, not rounded in their favour.

The netta.c row is Don's measurement, not mine. Source: Don, /usr/bin/time -p, rc=0, same shakespeare corpus, earlier tonight. I did not re-run it and do not present it as re-verified by this hand; it is carried here because the duel asked for the C original beside its port, and it is labelled so nobody mistakes its provenance.

Boundary note on netta.c's court. The independent C checker refused at this world size — "world exceeds independent pair-table boundary", rc=1. So at shakespeare scale the court that passed is the port's own internal court, not the independent second reader. That is a named boundary, not a pass and not a failure: the two courts agree byte-identically at canonical scale (sealed sitting1), and above that scale the independent checker declines to sign rather than signing something it cannot verify. A refusal that announces its own limit is worth more than a signature that hides one, but it does mean netta.c's shakespeare-scale speech carries one court, not two.

The stock arm produced nothing at any budget, and here is why

Four separate cold runs, each killed by SIGKILL at its budget, each returning 137. Not one of them logged a single step or iter line, so there is no loss to convert — the honest cell is empty, not a number. The cause is structural and was predicted before the runs: train.py:263 evaluates before the first optimizer step, and estimate_loss at the stock eval_iters=200 is 400 forward passes at batch 64 × block 256. Thirty minutes on this CPU is not enough to finish that first evaluation. The checkpoint save is additionally guarded by iter_num > 0 (train.py:277), so even a completed first evaluation would save nothing; the earliest possible checkpoint is iteration 250, behind two more evaluations.

This is a sweep for netta that says nothing about netta. The stock config is the A100 recipe — its own README reports 3 minutes and 1.4697 val loss on one A100 (README.md:51). Running it on a two-thread phone SoC measures the mismatch, not the organisms. That is why the second arm exists.

The CPU-recipe arm, which is the real duel

The README's own prescription for this machine class (README.md:85), run to its own completion, unmodified, launch flags only:

--device=cpu --compile=False --eval_iters=20 --log_interval=1 \
--block_size=64 --batch_size=12 --n_layer=4 --n_head=4 --n_embd=128 \
--max_iters=2000 --lr_decay_iters=2000 --dropout=0.0
step train (nats) val (nats) val bits/char
0 4.1676 4.1649 6.0087
250 2.4293 2.4447 3.5270
500 2.2732 2.3141 3.3385
750 2.1338 2.1905 3.1602
1000 1.9714 2.0528 2.9616
1250 1.8756 2.0089 2.8982
1500 1.8557 1.9225 2.7736
1750 1.7790 1.8921 2.7297
2000 1.7648 1.8857 2.7205

82.52 s wall, rc 0, checkpoint written. The README claimed "~3 minutes" and "loss of only 1.88" for this recipe; measured here it is 82.52 s and 1.8857. His documentation is accurate.

He wins the held-out column by 0.640 bits/byte (2.7205 against her 3.360), or 0.575 against her corridor-0 number of 3.296. The preregistration predicted exactly this and registered it as refutable before any data. It was not refuted. It is published here as the headline it is.

Where the crossover actually sits

This is the sharpest number in the duel. His val passes her 3.360 between step 250 (3.5270, still worse than her) and step 500 (3.3385, better than her). Summing his own logged per-iteration times, iteration 250 lands at 6.53 s and iteration 500 at 14.14 s of compute, with about 2 s of startup and evaluation on top of that across the whole run. So a gradient-trained transformer overtakes her held-out pricing somewhere in the neighbourhood of 9 to 16 seconds of wall clock on this machine — while her complete court-passing run finishes at 13.16 s. The two are in the same handful of seconds.

Her advantage is therefore real but narrow and does not grow: she runs once, her number is fixed at 3.360, and his keeps falling for another minute to 2.7205. Her claim was never held-out supremacy; it was time-to-court-passing speech with no gradients, and that claim survives. The held-out claim is his.

netta's court, verbatim

Five seeds, default dials, full corpus as the island, census against the full 1,115,394 B corpus.

seed bytes ear bits/byte ignorance bits/byte longest match coverage ≥32 verdict
7 714 0.405539 2.964695 37 0.051820728 PASS
19 723 0.404000 3.142825 40 0.105117566 PASS
42 706 0.364735 2.998289 29 0.000000000 PASS
101 789 0.423115 3.137605 35 0.044359949 PASS
271 715 0.371243 3.194716 49 0.142657343 PASS
SPEECH PASS: the mouth speaks below ignorance and above copying
netta: cold start to verdict in 12.97 s | island 1115394 B | 5 sittings

World 1,115,394 B; lived stream 290,940 units; merges 4096; inventory 4352; alive 3982; order 4; corridor 1; citizens mode none.

netta's held-out number, all four dials

Grown on nanoGPT's train split only, priced on his val split. One thing is added to her law and it is named: an escape, because her law assigns probability zero to anything she has not lived. Construction in NOTES.md §1.

corridor escape estimator val bits/byte literals corridor vetoes
1 (her court's pinned dial, her ear's law) Witten-Bell u/(occ+u) 3.360161 104 1944
0 (corridor off) Witten-Bell 3.295913 0 0
1 method A 1/(occ+1) 4.176001 104 1944
0 method A 4.075167 0 0

An order-0 byte code from train frequencies costs 4.829 bits/byte on the same bytes, so she beats a frequency table by 1.47 bits and loses to an 82-second transformer by 0.64. The headline is the dial her court pinned, not the prettier one.

Samples, raw, first six lines

netta, seed 7:

Prevent it, that the quite forsworn!
O worthy duke,
Whose father was at Very well.

ANTONIO:
And the rarity of it is in writing after this unwont.

nanoGPT CPU-recipe arm, stock sample.py, rc=0 in 4.79 s, 10 samples of 501 bytes each (start \n plus 500 new tokens), seed 1337, temperature 0.8, top_k 200 — all its own defaults. Sample 0:

I by done what leave death,
And aproposely beef the are and sors blate though wat our fort
Thine the aftior than whating bods farse dowed
And nears and thou stand murs's consel.

MEOF:
Sir, should and then thee.

GRICHARD one, enceling:

Sample 1 for a second look:


To furd of sonce on it my lord my prove.

ICIRIIA:
He had and I clann what would sy pile.

MYUKE MIOF OYCE:
Shall

At val 1.8857 this model is not fluent — "aproposely beef the are and sors blate" is the register throughout. The comparison below has to be read with that in mind.

Copy coverage

Both systems, same census law (netta.py:381-400, MIN_MATCH 32), both scanned against the full 1,115,394 B corpus. netta's numbers are her court's own; every figure in this section was re-measured by a second hand, coverage_scan.py, which implements the same measure with a different control path — binary search on match length instead of her carried-length loop — and agrees exactly, longest match and coverage to nine decimals, on all five of her speeches and all ten of his.

system samples longest verbatim coverage ≥32
netta.py 5 × ~700 B 29–49 B 0.000–0.143 (mean 0.0688)
nanoGPT CPU recipe 10 × 501 B 12–15 B 0.000000000, all ten

This row goes against netta and is published as such. By the anti-copy measure her own court uses, nanoGPT copies strictly less: no sample of his contains a single verbatim run reaching the 32-byte floor, while three of her five do, one of them at 49 bytes and 14.3% coverage. Her census still passes — the frozen void line is 0.50 and her worst stream is 0.143 — but "passes the anti-copy gate" and "copies less than the opponent" are different claims, and only the first is hers.

The honest qualifier, stated as fact rather than as defence: a model at val 1.8857 produces near-gibberish, and gibberish cannot accidentally reproduce 32 consecutive corpus bytes. Low copying here is partly a symptom of low fluency. The measure is still the measure, and the number is his.

Don's preregistered fact held in this run: seed 42 emits "My fair Bianca, get thee home, upon my brother" and scores coverage 0.000000000 with a longest verbatim run of 29 B, below the 32 B floor. A recombination, measured rather than asserted.


The walls

The dependency gate, opened by the maintainer. Installing tiktoken was blocked for this hand.

Hook line 68 matches (pip|pipx|conda|uv) (install|add) unconditionally and carries no ack flag, unlike the training gate at line 75 which takes a daily one, so Oleg's word had no door to reach it. Spelling the command differently to slip the regex would be bypassing a gate, which rule 9 forbids as squarely as editing one, so it was not attempted; the install was tried once, plainly, refused, and the refusal recorded. Stubbing or editing sample.py was ruled out by instruction and by the same principle.

Oleg then installed tiktoken 0.14.0 himself, into the user site of /usr/bin/python3 (3.9.6) — the same interpreter that already carried torch 2.8.0, which resolves the interpreter split in one move, since sample.py needs both in one process. Sampling then ran stock: rc=0, 4.79 s, 10 samples. The gate was answered by the maintainer's hand, never by this one.

The training gate, lifted the same way. python-train-ack-20260921.flag exists, created 22:38 by Oleg's hand, verified on disk before any run. Runs C proceeded under it.

The preregistration's premise was stale. It recorded "torch is not installed on neo". torch 2.8.0 and numpy 2.0.2 were already present for /usr/bin/python3. Nothing was installed for the training runs, and no system package was touched. The code beat the claim.

Files

file what it is
SETUP.md versions, commit, split sizes, every command with its rc
NOTES.md every judgment call, the pricing law in full, the open tiktoken decision
heldout_price.py the held-out harness; imports netta.py, never edits it
coverage_scan.py second-hand census, cross-checked against netta.census
runC.sh the run C driver, both arms
runA_stdout.txt, runA_court.txt, runA_out/ netta speech, court report, traces
runB_c{0,1}_{a,c}.txt held-out pricing, four dials
runC_T{1,2,3,4}.log, runC_CPUREC.log nanoGPT logs, raw
ckpt_CPUREC.pt the CPU-recipe checkpoint, 9,678,732 B

The artifact netta.py was sha256-verified before and after every run and is unchanged: f3ef71d3164d895653c4bc9f521ab89557870d1d039b98574b218f0e95fb0278.

#!/usr/bin/env python3
"""NETTA is all you need.
Statistical coherence from scratch: no transformers, no gradients, no optimizer,
no pretrained anything. Ordinary text in, earned speech out.
One file, stdlib only, deterministic. It eats a world of ordinary text, grows
units out of whatever that world repeats often enough to deserve a name, and
then speaks -- sampling only over continuations it has actually lived. A court
preregistered before the first judged byte prices the result against honest
ignorance and against copying, and prints the speech beside the numbers.
python3 netta.py island.txt
python3 netta.py island.txt --seed 42 --time
python3 netta.py island.txt --out run --report court.txt
Mirror of the C organism: netta.c (Body 0, the merge law) and netta_mouth.c
(Body 1, the mouth) under MOUTH_PROTOCOL.md with amendments 2 and 3, judged by
netta_mouth_check.c under SPEECH_COURT.md. The PRNG, the tie-breaks and the
sampling order are ported from that C step for step, and PARITY.md beside this
file carries what was measured: byte-identical speech, byte-identical traces and
byte-identical court reports, over 25 streams, two islands and nine dial
settings, with the C's own independent reader accepting this file's output.
One default differs from the C on purpose: --corridor is 1 here and 3 there.
K=3 fails the anti-copy census on the canonical island; K=1 is the dial the
court's first sitting pinned. Pass --corridor 3 for the C's compiled default.
Out of scope here, and named rather than hidden: the Court-4 citizens adapter
(MOUTH_PROTOCOL §4), which needs a 36101-byte sealed capsule this file cannot
carry. The plain mouth is the one that passed the court's first sitting.
"""
import argparse
import math
import os
import sys
import time
from bisect import bisect_left
from heapq import heapify, heappop, heappush
START = time.perf_counter()
BASE_UNITS = 256
PACK = 16 # unit id bits in a packed n-gram key
PACK_MASK = (1 << PACK) - 1
REP_WINDOW = 12
REP_PENALTY = 0.5
SPEAK_HARD = 256 # grace bytes to let a sentence finish
MAX_EM = 65536
VOID_LINE = 0.50 # body0/verdict.md: at or above this it is a quote
MIN_MATCH = 32 # the census counts verbatim tape runs from here up
PASS_LINE = "SPEECH PASS: the mouth speaks below ignorance and above copying"
class Refusal(Exception):
"""The organism will not speak, or the court will not sign."""
class Rng:
"""xorshift64. Three shifts and an xor: the entire source of chance here."""
MASK = (1 << 64) - 1
def __init__(self, state):
self.s = (state & self.MASK) or 1
def next(self):
x = self.s
x ^= (x << 13) & self.MASK
x ^= x >> 7
x ^= (x << 17) & self.MASK
self.s = x
return x
def below(self, n):
return self.next() % n
def double(self):
return (self.next() >> 11) / 9007199254740992.0
def grow_units(world, merges, min_pair):
"""Body 0's merge law. The most frequent adjacent pair becomes one unit,
ties broken by the smaller packed key, until the budget runs out or nothing
repeats MIN_PAIR times any more.
Nothing descends here. No weights, no initialization, no objective: the
inventory is a fact about the world, and it is the same inventory every time
you run it. The C rescans the whole stream every round; this keeps the same
counts incrementally over a linked list.
"""
n = len(world)
sym = list(world)
prv = list(range(-1, n - 1))
nxt = list(range(1, n + 1))
nxt[n - 1] = -1
count = {}
where = {}
# the entire training loop:
for i in range(n - 1):
key = (sym[i], sym[i + 1])
count[key] = count.get(key, 0) + 1
where.setdefault(key, []).append(i)
heap = [(-c, a, b) for (a, b), c in count.items()]
heapify(heap)
def bump(key, delta):
c = count.get(key, 0) + delta
if c:
count[key] = c
if delta > 0:
heappush(heap, (-c, key[0], key[1]))
else:
del count[key]
exp = [bytes((i,)) for i in range(BASE_UNITS)]
while len(exp) - BASE_UNITS < merges:
best = None
while heap:
negc, a, b = heappop(heap)
live = count.get((a, b), 0)
if live == -negc:
best = (live, a, b)
break
if live: # stale count, same pair, still in play
heappush(heap, (-live, a, b))
if best is None or best[0] < min_pair:
break
_, a, b = best
new = len(exp)
exp.append(exp[a] + exp[b])
for i in where.pop((a, b), ()): # left to right, non-overlapping, as the C
if sym[i] != a:
continue
j = nxt[i]
if j < 0 or sym[j] != b:
continue
left, right = prv[i], nxt[j]
bump((a, b), -1)
if left >= 0:
sl = sym[left]
bump((sl, a), -1)
bump((sl, new), 1)
where.setdefault((sl, new), []).append(left)
if right >= 0:
sr = sym[right]
bump((b, sr), -1)
bump((new, sr), 1)
where.setdefault((new, sr), []).append(i)
sym[i] = new
sym[j] = -1
nxt[i] = right
if right >= 0:
prv[right] = i
return [s for s in sym if s >= 0], exp
def starts_upper(e):
return 65 <= e[0] <= 90
def ends_sentence(e):
return e[-1] in b".!?\n"
class Tape:
"""The lived stream and every n-gram the organism has actually walked."""
def __init__(self, stream, exp, order):
self.stream = stream
self.exp = exp
n = len(stream)
n1 = [0] * len(exp)
for u in stream:
n1[u] += 1
self.alive = [u for u in range(len(exp)) if n1[u]]
self.uni = [(u, n1[u]) for u in self.alive]
self.bi = sorted((stream[i] << PACK) | stream[i + 1]
for i in range(n - 1))
self.tri = sorted((stream[i] << (2 * PACK)) | (stream[i + 1] << PACK) | stream[i + 2]
for i in range(n - 2))
self.quad = sorted((stream[i] << (3 * PACK)) | (stream[i + 1] << (2 * PACK)) |
(stream[i + 2] << PACK) | stream[i + 3]
for i in range(n - 3)) if order >= 4 else []
self.starts = [i for i in range(n - 3)
if starts_upper(exp[stream[i]])
and (i == 0 or ends_sentence(exp[stream[i - 1]]))]
if not self.starts:
raise Refusal("no sentence starts")
def support(tape, em, level, order):
"""Lived continuations at one explicit level, in ascending unit id.
No smoothing, no backoff mass, no unseen token, no prior: the mouth may only
say what it has lived, so a level that was never walked returns nothing and
the caller drops to a shorter memory.
"""
nem = len(em)
if level == 4:
if order < 4 or nem < 3 or not tape.quad:
return []
keys = tape.quad
prefix = (em[-3] << (2 * PACK)) | (em[-2] << PACK) | em[-1]
elif level == 3:
if nem < 2:
return []
keys = tape.tri
prefix = (em[-2] << PACK) | em[-1]
elif level == 2:
if nem < 1:
return []
keys = tape.bi
prefix = em[-1]
else:
return tape.uni
lo = bisect_left(keys, prefix << PACK)
hi = bisect_left(keys, (prefix + 1) << PACK)
out = []
i = lo
while i < hi:
tok = keys[i] & PACK_MASK
j = i + 1
while j < hi and keys[j] & PACK_MASK == tok:
j += 1
out.append((tok, j - i))
i = j
return out
def lawful_support(tape, em, order, corridor_k, corr):
"""The highest non-empty lived support, quad -> tri -> bi -> lived unigram,
under the corridor law.
Amendment 2: a support holding exactly one continuation is not a choice, it
is a rail, and a chain of rails is a quotation in progress. After K of them
the mouth must descend to a support with a real branch.
Amendment 3: an exit must exit. The rail's own token is closed for that one
choice, because changing level is not by itself changing path.
Returns (candidates, level, vetoed token or None, new corridor counter).
"""
cand, level = [], 0
for lvl in (4, 3, 2, 1):
cand = support(tape, em, lvl, order)
if cand:
level = lvl
break
if not cand:
raise Refusal("empty lived support")
if len(cand) >= 2:
return cand, level, None, 0
if corridor_k and corr >= corridor_k:
rail = cand[0][0]
for lvl in range(level - 1, 0, -1):
alt = support(tape, em, lvl, order)
if len(alt) >= 2:
admitted = [c for c in alt if c[0] != rail]
if len(admitted) == len(alt):
raise Refusal("lower support lost the corridor continuation")
return admitted, lvl, rail, 0
return cand, level, None, corr + 1 if corridor_k else corr
def speak(tape, seed, args):
"""The mouth.
Opens on three tokens of lived tape picked by the seed, then chooses: score
every lived continuation by how often the world already did it, damp what
this stream just said, keep the top K, soften by temperature, sample. That
is the whole generative model. There is no second network in here quietly
doing the real work.
"""
rng = Rng(seed ^ 0x9E3779B97F4A7C15)
sp = tape.starts[rng.below(len(tape.starts))]
em = [tape.stream[sp], tape.stream[sp + 1], tape.stream[sp + 2]]
out = bytearray()
trace = []
for k in range(3):
e = tape.exp[em[k]]
out += e
trace.append("%d\t%d\t%d\t0\t0\t0\t0\t%d\t0\t1\t0\t-" % (k, em[k], sp + k, len(e)))
corr = 0
want, hard = args.bytes, args.bytes + SPEAK_HARD
while len(out) < want and len(em) + 1 < MAX_EM:
before = corr
cand, level, veto, corr = lawful_support(tape, em, args.order, args.corridor, corr)
window = em[-REP_WINDOW:]
occ = 0
scored = []
for tok, cnt in cand:
occ += cnt
weight = float(cnt)
score = (math.log(weight + 1e-300)
- math.log(1.0 + REP_PENALTY * window.count(tok)))
scored.append((tok, cnt, score))
# the C runs a partial selection sort; score down, unit id up
scored.sort(key=lambda c: (-c[2], c[0]))
limit = min(len(scored), args.topk)
weights = []
top = -1e300
for i in range(limit):
v = scored[i][2] / args.temp
weights.append(v)
if v > top:
top = v
total = 0.0
for i in range(limit):
weights[i] = math.exp(weights[i] - top)
total += weights[i]
r = rng.double() * total
cum = 0.0
at = 0
for i in range(limit):
cum += weights[i]
if cum > r:
at = i
break
tok, cnt, _ = scored[at]
e = tape.exp[tok]
em.append(tok)
out += e
trace.append("%d\t%d\t-\t%d\t%d\t%d\t%d\t%d\t0\t1\t%d\t%s"
% (len(em) - 1, tok, level, len(cand), occ, cnt, len(e), before,
"-" if veto is None else veto))
if len(out) >= want and not ends_sentence(e) and len(out) < hard:
want = len(out) + 1
return bytes(out), em, trace
def ear(tape, tokens, order, corridor_k):
"""The court's ear. Every emitted token priced inside the lived support the
law actually selected, against honest ignorance -- uniform over every unit
the organism has ever lived.
The court re-derives that support from the token sequence alone. It does not
take the mouth's word for the mouth's own paperwork, and it refuses a stream
whose token was never a lived continuation of its own context.
"""
model = 0.0
ignorance = 0.0
per_token = math.log2(len(tape.alive))
past = []
corr = 0
for i, tok in enumerate(tokens):
if i < 3:
# the lived opening is priced, but it stands outside the corridor law
cand = []
for lvl in range(3 if i >= 2 else i + 1, 0, -1):
cand = support(tape, past, lvl, order)
if cand:
break
else:
cand, _, _, corr = lawful_support(tape, past, order, corridor_k, corr)
occ = chosen = 0
for t, c in cand:
occ += c
if t == tok:
chosen = c
if not chosen:
raise Refusal("emitted token is outside lawful lived support")
model += -math.log2(chosen / occ)
ignorance += per_token
past.append(tok)
return model, ignorance
def census(speech, world):
"""Anti-copy. Coverage of the stream by verbatim tape runs of 32 bytes or
more, against the void line frozen in body0/verdict.md. Copying is not
speech, and an organism that only quotes is cheap to price precisely because
it is saying nothing.
"""
sn = len(speech)
covered = bytearray(sn)
longest = z = 0
for at in range(sn):
remain = sn - at
# a match of length z at at-1 guarantees z-1 here; start from what is owed
z = min(z - 1 if z else 0, remain)
while z < remain and speech[at:at + z + 1] in world:
z += 1
if z > longest:
longest = z
if z >= MIN_MATCH:
covered[at:at + z] = b"\x01" * z
return longest, covered.count(1) / sn
class Sitting:
__slots__ = ("seed", "speech", "tokens", "trace",
"model", "ignorance", "longest", "coverage")
@property
def model_bpb(self):
return self.model / len(self.speech)
@property
def ignorance_bpb(self):
return self.ignorance / len(self.speech)
@property
def ear_ok(self):
return self.model_bpb < self.ignorance_bpb
@property
def copy_ok(self):
return self.coverage < VOID_LINE
def line(self):
return ("seed=%d\ttokens=%d\tmodel_bits_per_byte=%.9f\t"
"ignorance_bits_per_byte=%.9f\tlongest_match_bytes=%d\t"
"coverage_ge32=%.9f\tear=%s\tanti_copy=%s\tsupport=PASS\tstream=%s\n"
% (self.seed, len(self.tokens), self.model_bpb, self.ignorance_bpb,
self.longest, self.coverage,
"PASS" if self.ear_ok else "FAIL",
"PASS" if self.copy_ok else "FAIL",
"PASS" if self.ear_ok and self.copy_ok else "FAIL"))
def write_report(path, tape, world_n, args, sittings, verdict):
"""The report carries every judged stream verbatim. A speech verdict made of
numbers alone is unlawful under the contract it is reporting on.
"""
with open(path, "wb") as f:
f.write("NETTA BODY 1 — INDEPENDENT EAR\n".encode())
f.write(("world_bytes=%d\tlived_units=%d\tmerges=%d\tinventory=%d\talive=%d\t"
"order=%d\tcitizens_mode=none\tcorridor=%d\n"
% (world_n, len(tape.stream), len(tape.exp) - BASE_UNITS,
len(tape.exp), len(tape.alive), args.order, args.corridor)).encode())
for s in sittings:
f.write(("\nBEGIN RAW SPEECH seed=%d bytes=%d\n" % (s.seed, len(s.speech))).encode())
f.write(s.speech)
if not s.speech.endswith(b"\n"):
f.write(b"\n")
f.write(("END RAW SPEECH seed=%d\n" % s.seed).encode())
f.write(s.line().encode())
f.write(("\n%s\n" % verdict).encode())
def parse_args(argv):
p = argparse.ArgumentParser(
prog="netta.py",
description="NETTA: earned speech from ordinary text, no gradients involved.")
p.add_argument("island", help="a world of ordinary text, at least 100 bytes")
p.add_argument("--seed", type=int, help="speak one stream from this seed")
p.add_argument("--seeds", default="7,19,42,101,271", help="the seed set to judge")
p.add_argument("--sittings", type=int, help="judge only the first N seeds")
p.add_argument("--merges", type=int, default=4096, help="unit budget")
p.add_argument("--min-pair", type=int, default=4, dest="min_pair",
help="a pair below this never earns a unit")
p.add_argument("--order", type=int, default=4, choices=(3, 4), help="deepest context")
p.add_argument("--bytes", type=int, default=700, help="speech length per sitting")
p.add_argument("--temp", type=float, default=0.8)
p.add_argument("--topk", type=int, default=15)
p.add_argument("--corridor", type=int, default=1,
help="rails tolerated before the mouth must branch; 0 disables. "
"1 is the court's pinned dial, 3 is netta_mouth.c's compiled default")
p.add_argument("--out", help="write speech_<seed>.bin and trace_<seed>.tsv here")
p.add_argument("--report", help="write the court report, speech verbatim, here")
p.add_argument("--time", action="store_true", dest="timed",
help="report wall clock from cold start to verdict")
args = p.parse_args(argv)
if BASE_UNITS + args.merges > (1 << PACK):
p.error("merge budget exceeds packed id space")
if args.min_pair < 2:
p.error("--min-pair must be at least 2")
if not 1 <= args.bytes <= 65000:
p.error("--bytes outside 1..65000")
if not (args.temp > 0.0 and math.isfinite(args.temp)):
p.error("--temp must be finite and positive")
if not 1 <= args.topk <= 256:
p.error("--topk outside 1..256")
if not 0 <= args.corridor <= 4096:
p.error("--corridor outside 0..4096")
return args
def main(argv=None):
args = parse_args(argv)
seeds = ([args.seed] if args.seed is not None
else [int(s) for s in args.seeds.split(",")])
if args.sittings is not None:
seeds = seeds[:args.sittings]
if not seeds:
raise Refusal("no seeds to speak from")
with open(args.island, "rb") as f:
world = f.read()
if len(world) < 100:
raise Refusal("world too small")
stream, exp = grow_units(world, args.merges, args.min_pair)
tape = Tape(stream, exp, args.order)
sys.stderr.write("netta: world %d B | lived stream %d units | merges %d | "
"V %d (avg %.2f B/unit) | order %d\n"
% (len(world), len(stream), len(exp) - BASE_UNITS, len(exp),
len(world) / len(stream), args.order))
out = sys.stdout.buffer
sittings = []
for seed in seeds:
s = Sitting()
s.seed = seed
s.speech, s.tokens, s.trace = speak(tape, seed, args)
s.model, s.ignorance = ear(tape, s.tokens, args.order, args.corridor)
s.longest, s.coverage = census(s.speech, world)
sittings.append(s)
out.write(("\n── seed %d ── %d bytes ──\n" % (seed, len(s.speech))).encode())
out.write(s.speech)
if not s.speech.endswith(b"\n"):
out.write(b"\n")
out.write(("ear %.6f bits/byte against ignorance %.6f | longest verbatim run "
"%d B | quoted %.4f of the stream | %s\n"
% (s.model_bpb, s.ignorance_bpb, s.longest, s.coverage,
"PASS" if s.ear_ok and s.copy_ok else "FAIL")).encode())
if args.out:
os.makedirs(args.out, exist_ok=True)
with open(os.path.join(args.out, "speech_%d.bin" % seed), "wb") as f:
f.write(s.speech)
with open(os.path.join(args.out, "trace_%d.tsv" % seed), "wb") as f:
f.write(b"index\ttoken_id\tstart_position\tbackoff\tsupport_types\t"
b"support_occurrences\tchosen_occurrences\texpansion_bytes\t"
b"advice_book_row\tadvice_factor\tcorridor\tcorridor_veto\n")
f.write(("\n".join(s.trace) + "\n").encode())
all_ear = all(s.ear_ok for s in sittings)
all_copy = all(s.copy_ok for s in sittings)
if all_ear and all_copy:
verdict = PASS_LINE
elif not all_ear and not all_copy:
verdict = "SPEECH FAIL: ignorance and frozen anti-copy gates failed"
elif not all_ear:
verdict = "SPEECH FAIL: one or more streams did not beat honest ignorance"
else:
verdict = "SPEECH FAIL: one or more streams reached frozen anti-copy coverage 0.50"
out.write(("\n%s\n" % verdict).encode())
if args.timed:
out.write(("netta: cold start to verdict in %.2f s | island %d B | %d sitting%s\n"
% (time.perf_counter() - START, len(world), len(sittings),
"" if len(sittings) == 1 else "s")).encode())
out.flush()
if args.report:
write_report(args.report, tape, len(world), args, sittings, verdict)
return 0 if all_ear and all_copy else 2
if __name__ == "__main__":
try:
sys.exit(main())
except Refusal as why:
sys.stderr.write("netta: %s\n" % why)
sys.exit(1)
# Arianna Method — github.com/ariannamethod/netta (the C organism this file mirrors)

Setup: versions, commits, splits, commands, return codes

Machine: neo, Apple A18 Pro, arm64, 8 GB. macOS 26.4.1 (25E253). Disk at the time of the run: 3.1 GiB free, 100% capacity — recorded because it constrained the plan (no caching, nothing large downloaded). Date: 2026-09-21.

Interpreters and packages

what version where note
workspace venv python 3.14.4 venv/bin/python created as instructed; runs netta and both harnesses
system python 3.9.6 /usr/bin/python3 runs nanoGPT's own scripts
torch 2.8.0 user site-packages for 3.9.6 already installed; nothing was installed by me
numpy 2.0.2 same already installed
requests 2.32.5 same already installed; needed by prepare.py's import
tqdm 4.67.3 same present
tiktoken 0.14.0 user site for 3.9.6 installed by Oleg's own hand after the dependency gate refused this hand; unblocks stock sample.py's line-8 import
transformers / datasets / wandb MISSING — not needed for this config
git 2.50.1 (Apple Git-155) —

torch capability check: cuda available False | mps available True, torch.get_num_threads() == 2, CPU matmul returns a float. Device used: cpu.

No package was installed. The preregistration's premise ("torch is not installed on neo") is stale — it was already there. The venv exists as instructed but carries no torch; installing into it was blocked by the dependency gate and turned out to be unnecessary.

Inputs, verified

input bytes sha256
corpus ~/arianna/harmonix/sonnet/shakespeare.txt 1,115,394 86c4e6aa9db7c042ec79f339dcb96d42b0075e16b8fc2e86bf0ca57e2dc565ed
artifact ~/arianna/netta-pyport-20260921/netta.py 21,461 f3ef71d3164d895653c4bc9f521ab89557870d1d039b98574b218f0e95fb0278

Corpus sha starts 86c4e6aa as the preregistration requires. The corpus is pure ASCII — a scan for bytes \x80-\xff returned no matches (grep rc=1) — so its character split and its byte split are the same cut. The artifact sha was re-checked after every harness run and is unchanged.

nanoGPT

Clone HEAD: 3adf61e154c3fe3fca428ad6bc3818b27a3b8291, 2025-11-12 11:52:34 -0800, "Update README to mention nanochat and deprecation". Clean clone, no edit of any file in it. The corpus was pre-placed as data/shakespeare_char/input.txt before prepare.py ran, so the download at prepare.py:14-17 never fired; the placed file's sha is identical to the corpus above, so no byte-identity check against a downloaded reference was needed.

The split

prepare.py:38-40 is the whole law:

n = len(data)
train_data = data[:int(n*0.9)]
val_data = data[int(n*0.9):]

int(1115394 * 0.9) = 1003854.

split bytes sha256 (first 16)
train 1,003,854 a9e24e23a1ec7774
val 111,540 c54f3753a4e6e3c3

Confirmed by executing prepare.py, not only by reading it: it printed train has 1,003,854 tokens / val has 111,540 tokens, vocab size 65, matching its own footer comment. train.bin is 2,007,708 B and val.bin is 223,080 B at 2 bytes per uint16 token, so 1,003,854 and 111,540 tokens exactly. Val begins ?\n\nGREMIO:\nGood morrow, neighbour Baptista.\n\nBAPTISTA:\nGood .

Commands, in order, with return codes

Every rc was taken directly from $? after the command, never through a pipe. PYTHONDONTWRITEBYTECODE=1 on every python invocation.

# workspace and venv
mkdir -p <workspace>
/opt/homebrew/bin/python3.14 -m venv venv                               # rc=0

# dependency install — BLOCKED by gate, and unnecessary (torch already present)
venv/bin/pip install --no-cache-dir torch numpy                          # BLOCKED, never ran

# clone
git clone https://github.com/karpathy/nanoGPT nanoGPT                    # rc=0
git -C nanoGPT rev-parse HEAD        # 3adf61e154c3fe3fca428ad6bc3818b27a3b8291

# corpus in place, then his own prepare.py, run with the interpreter that has numpy
cp ~/arianna/harmonix/sonnet/shakespeare.txt \
   nanoGPT/data/shakespeare_char/input.txt                               # rc=0
cd nanoGPT/data/shakespeare_char && /usr/bin/python3 prepare.py          # rc=0

# RUN A — netta, full corpus, default dials, timed
/usr/bin/time -p venv/bin/python \
  ~/arianna/netta-pyport-20260921/netta.py \
  ~/arianna/harmonix/sonnet/shakespeare.txt \
  --time --out runA_out --report runA_court.txt                          # rc=0
#   real 13.16   user 11.47   sys 0.44
#   netta's own clock: cold start to verdict in 12.97 s

# RUN B — held-out pricing, four dials
/usr/bin/time -p venv/bin/python heldout_price.py --corridor 1 --escape c --probes
#   rc=0, real 19.76, user 19.09, sys 0.35   -> val 3.360161 bits/byte
venv/bin/python heldout_price.py --corridor 0 --escape c                 # rc=0 -> 3.295913
venv/bin/python heldout_price.py --corridor 1 --escape a                 # rc=0 -> 4.176001
venv/bin/python heldout_price.py --corridor 0 --escape a                 # rc=0 -> 4.075167

# RUN D (netta half) — second-hand census, cross-checked against netta.census
venv/bin/python coverage_scan.py --check runA_out/speech_{7,19,42,101,271}.bin
#   rc=0, AGREE on all five, exact

# RUN C — driver, both arms (see runC.sh; nothing inside the clone modified)
./runC.sh short        # T1, T2      rc=0
./runC.sh cpurecipe    # CPU recipe  rc=0
./runC.sh long         # T3, T4      rc=0

# stock arm, four separate cold runs, SIGKILL at the budget
/usr/bin/python3 train.py config/train_shakespeare_char.py --device=cpu --compile=False
#   T1  rc=137  elapsed   10.028668880462646 s  no checkpoint, no loss logged
#   T2  rc=137  elapsed   60.028416872024536 s  no checkpoint, no loss logged
#   T3  rc=137  elapsed  600.163627147674560 s  no checkpoint, no loss logged
#   T4  rc=137  elapsed 1800.290075063705400 s  no checkpoint, no loss logged

# CPU-recipe arm, README.md:85 verbatim, run to completion
/usr/bin/python3 train.py config/train_shakespeare_char.py \
  --device=cpu --compile=False --eval_iters=20 --log_interval=1 \
  --block_size=64 --batch_size=12 --n_layer=4 --n_head=4 --n_embd=128 \
  --max_iters=2000 --lr_decay_iters=2000 --dropout=0.0                   # rc=0
#   elapsed 82.523710012435913 s; checkpoint 9,678,732 B
#   step 2000: train loss 1.7648, val loss 1.8857   (val 2.7205 bits/char)

# sampling — attempted, failed at his own import
cp ckpt_CPUREC.pt nanoGPT/out-shakespeare-char/ckpt.pt                    # rc=0
/usr/bin/python3 sample.py --out_dir=out-shakespeare-char --device=cpu    # rc=1
#   File ".../nanoGPT/sample.py", line 8, in <module>
#       import tiktoken
#   ModuleNotFoundError: No module named 'tiktoken'

# tiktoken — attempted once by this hand, blocked, not worked around
venv/bin/pip install tiktoken                                            # BLOCKED by gate
# then installed BY OLEG'S OWN HAND into the user site of /usr/bin/python3:
#   tiktoken 0.14.0, alongside the torch 2.8.0 already there.
#   Verified: "tiktoken 0.14.0 | torch 2.8.0" from one interpreter.

# sampling, second attempt — stock, launch flags only
/usr/bin/time -p /usr/bin/python3 sample.py \
  --out_dir=out-shakespeare-char --device=cpu                            # rc=0
#   real 4.79  user 4.77  sys 0.47
#   10 samples, 501 B each (start "\n" + 500 new tokens), seed 1337,
#   temperature 0.8, top_k 200 — all sample.py's own defaults.
#   checkpoint sha matched ckpt_CPUREC.pt (8b160870936e87a18eef…) before sampling

# RUN D — census over his samples, second hand, cross-checked
venv/bin/python coverage_scan.py --check ngpt_samples/ngpt_{0..9}.bin    # rc=0
#   all ten: longest 12-15 B, coverage_ge32 = 0.000000000, AGREE with netta.census

Each stock budget was a genuine cold start: out-shakespeare-char/ckpt.pt was removed with rm -f on that single path before every launch, and each run was a fresh process at init_from='scratch'. SIGKILL was sent to the python PID itself, not to a wrapper, and the killer was cancelled whenever python exited first. Elapsed was measured with zsh EPOCHREALTIME around the launch. rc came from wait, directly.

One torch warning appears in every CPU log and is expected, not a config problem: torch.cuda.amp.GradScaler is enabled, but CUDA is not available. Disabling. — it self-disables, which is why no config edit was ever needed.

--device=cpu and --compile=False are launch flags, not config edits: the stock config file itself names them in its trailing comment (config/train_shakespeare_char.py:35-37, "on macbook also add"), and the README repeats them at line 88. No file inside the clone was modified.

Two gate blocks, verbatim

Dependency gate, on the install attempt:

BLOCKED: установка Python-зависимостей — ярус «ни за что» (dependency hell).
См. memory/feedback_python_ban_2026_04_29.md (ярусы 2026-08-22).
Нужны deps-list и слово Олега.

Training gate, on the run C launch:

BLOCKED: python-тренировка без 6-point brief — ярус «ни за что».
Явный ДА Олега: touch "$HOME/.claude/hooks/state/python-train-ack-$(date +%Y%m%d).flag" (daily).

Both gates were answered with work, never edited. The deps-list the first gate asks for is in NOTES.md; it is empty in practice, since torch and numpy were already on disk and nothing needed installing. The brief the second gate asks for is in NOTES.md and awaits Oleg.

Verified after the block: no out-shakespeare-char directory exists, and no python-train-ack-20260921.flag exists. Nothing trained.

Workspace

Everything lives in <workspace>/. Read-only inputs were the corpus and netta.py; neither was written to. No git operation beyond the clone into this workspace. No other repository was touched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment