The generated C, amalgams, vendored runtime pins, and compiled binaries from the Claude Code era are removed from the worktree. The El sources survive; this tree is now source-only for the first-principles rebuild. Per Principal direction 2026-08-19.
8.4 KiB
CLAIMS — v2 cycle 02, "seven tables, and a defect I shipped that morning"
Re-run 2026-08-17. Source doc: docs/v2/experiments/cycles/02-seven-tables-and-a-shipped-defect.md
(not modified; it is the historical record).
Pinned commits
| Role | SHA | Subject |
|---|---|---|
| BUGGY — before the repair | 1a5246667d80e0bf5830a06a04e1140c2867e4ce |
log the experiments that were run and never written down |
| FIXED — the code commit | abb0ab41980f577ee8bfda04d4339e6355adfa48 |
finish the seven tables, and repair a worse defect they exposed |
| Doc commits | 69f2ab2, ebb4f1f |
v2 cycle 02: … |
Two detached worktrees (/tmp/rerun-v2-02a at the buggy parent,
/tmp/rerun-v2-02b at the fix), a compiler built in each, and the probe
programs compiled and linked against each worktree's own runtime — so the
runtime under test is the historical one, not today's. Every .prov carries
1a5246667… or abb0ab419…; none is flagged DIRTY-TREE by
verify-manifest.sh.
Verdicts
| # | Claim as stated in the doc | Artifact | Commit | Verdict |
|---|---|---|---|---|
| A1 | P1 — six of seven are one shape; 5 take a call_expr, 1 takes a name | 0021, 0022 |
both | REPRODUCED — six collapse to 3–6 line wrappers, duration_unit_nanos does not |
| A2 | P2 — 49 names moved to signatures.rel |
0020 |
both | REPRODUCED exactly — 51 → 100 entries, delta 49 |
| A3 | P3 — duration_unit_nanos breaks the shape (name → MULTIPLIER, not name → type) |
0022 |
abb0ab4 |
REPRODUCED — it alone keeps 17 lines and all 14 string literals |
| A4 | P4 — codegen drops ~120 lines: FALSE, 80 | 0019 |
both | DIVERGED (−79, off by one) — see below |
| A5 | each table became a 3-line wrapper | 0022 |
abb0ab4 |
REPRODUCED — five are exactly 3 lines; is_void_builtin is 6 |
| A6 | P5 — fixpoint holds | 0031 |
abb0ab4 |
REPRODUCED — gen2 == gen3, identical sha256 |
| A7 | P5 — all suites hold; 105/105 native, 253 assertions | 0032 |
abb0ab4 |
REPRODUCED exactly |
| A8 | P5 — 10/10 harnesses, 53 → 58 assertions | 0033 |
abb0ab4 |
REPRODUCED exactly — 10 harnesses, 0 failed, 58 assertions |
| A9 | P8 — an unrecognised unit silently becomes nanoseconds: FALSE | 0023, 0024 |
1a52466 |
REPRODUCED — it is compiled as a field access on the integer |
| A10 | 5.fortnights → el_get_field(5, …) → exit 139, SIGSEGV |
0023 |
1a52466 |
REPRODUCED — exit code 139, captured directly |
| A11 | as_map was a bare cast with no validation |
0028 |
1a52466 |
REPRODUCED verbatim: static ElMap* as_map(el_val_t v) { return (ElMap*)(uintptr_t)v; } |
| A12 | P9 — routing through el_tagged stops the SIGSEGV |
0026, 0028 |
abb0ab4 |
REPRODUCED — same program now prints survived, exit 0 |
| A13 | P10 — legitimate access unaffected: {"a":1}["a"] → 1, [10,20,30][1] → 20 |
0025, 0027 |
both | REPRODUCED — and the two outputs are byte-identical (same sha256 783e5bca6f66) before and after the fix |
| A14 | P11 — el_list_get / el_list_len had the identical defect, now gated |
0028 |
both | REPRODUCED |
| A15 | the guard was looks_like_heap_obj, testing p & 0x7 |
0004 |
1a52466 |
REPRODUCED verbatim |
| A16 | the shipped fallback was if (!looks_like_heap_obj(...)) return 0; return strlen(s); |
0003 |
1a52466 |
REPRODUCED verbatim |
| A17 | el_input_len silently returned 0 for roughly 60% of string literals |
0006, 0007, 0008, 0009 |
1a52466 |
DIVERGED — the real rate is worse. See below |
| A18 | which literals failed depended on link layout | 0008, 0009 |
1a52466 |
REPRODUCED — and this is the reason A17 diverges |
| A19 | "abc" happened to land on an 8-boundary in that build |
0008 |
— | REPRODUCED — abc is still aligned8=1 today, while 4 of the doc's other 5 are not |
| A20 | sha256_hex would hash the empty string instead of its input |
0006, 0007 |
1a52466 |
REPRODUCED — 20 of 20 literals return the empty-string hash |
| A21 | the repair checks "not a small integer, not a low address", and nothing more | 0013 |
abb0ab4 |
REPRODUCED |
| A22 | tagged_gate.sh grew from 5 assertions to 10 |
0016, 0017, 0018 |
both | REPRODUCED — 10 assertions, 10 passed |
| A23 | including an explicit assertion that no literal silently hashed as empty | 0018 |
abb0ab4 |
REPRODUCED — the assertion is present and green |
Counts: 21 REPRODUCED, 2 DIVERGED, 0 NOT-REPRODUCIBLE, 0 NOT-CAPTURED.
A17 — THE DIVERGENCE THAT MATTERS: the shipped defect was worse than recorded
The doc says el_input_len returned 0 for "roughly 60%" of string literals.
Measured today at the buggy commit, with a probe of 20 string literals of
lengths 1 through 20, compiled and linked against 1a52466's own runtime and
checked against Python's hashlib.sha256 as an independent oracle (0007):
literals tested = 20
correct vs hashlib.sha256 = 0
incorrect = 20
hashed as the EMPTY STRING = 20
failure rate = 100%
Every one of the twenty. Not 60% — 100% in this build. The same program at
the fixed commit (0015):
literals tested = 20
correct vs hashlib.sha256 = 20
incorrect = 0
hashed as the EMPTY STRING = 0
failure rate = 0%
The mechanism is exactly as the doc describes — looks_like_heap_obj rejects
any pointer with p & 0x7 set, and the linker packs string literals
contiguously with no padding, so consecutive short literals are almost never
8-aligned. Measured directly (0008, 0009):
the doc's own five literals 4 of 5 misaligned (80%)
twenty literals, lengths 1..20 18 of 20 misaligned (90%)
Note abc is still aligned8=1 today — the doc's explanation for why the test
passed reproduces precisely — while hello, which the doc measured as aligned,
is not aligned in today's build. The specific set of victims is link-layout
dependent, exactly as the doc says. The rate is not a stable quantity at all,
and the doc's 60% was a five-literal sample.
This divergence flatters nothing and is reported for that reason: the doc understates the severity of the worst defect in the v2 record. And it understates it by the same error the cycle exists to teach — a sample of five is not a measurement of a rate. That is defect 7's shape ("a single literal is not a test of a property"), committed inside the paragraph that names it.
The correct statement, with artifacts behind it: el_input_len returned 0 for
between 80% and 100% of string literals in the builds measured, the exact set
depending on link layout, and in the end-to-end El program measured today it was
all of them.
A4 — the second divergence, cosmetic
doc: codegen drops 80 lines
measured: TOTAL 4524 -> 4445 = -79 (0019)
CODE 3277 -> 3209 = -68
Off by one, the same +1 pattern that appears in cycle 05's totals (documented in that cycle's CLAIMS.md). The cycle's conclusion — that the predicted ~120 did not materialise because each table left a wrapper — is unaffected: 79 is as far from 120 as 80 is. Recorded because a document about miscounting should not carry an uncorrected miscount.
Cycle 05 later established that the metric worth quoting is code, not total. On that metric the drop is 68, not 80.
Instruments
instruments/verify-hashes.py and instruments/run-oracle.sh are in this
directory rather than inline, so the oracle's criterion is auditable and so the
capture command stays one line — capture.sh records commands with
printf '%s\n' "$*", and a multi-line command splits one manifest record across
several lines and desynchronises its artifact counter. That happened on the first
pass of this cycle. The whole directory was deleted and every capture re-run
from scratch rather than repairing MANIFEST.tsv by hand, because editing a
manifest to match its artifacts inverts what the manifest is for. The final
manifest is 33 records, 9 fields each, and verify-manifest.sh reports 0 altered
and 0 missing.
0029 is a real failed run, retained: scripts/verify.sh did not exist at
abb0ab4 (exit 127). It first appears in 6c80dee, cycle 04. P5 was therefore
verified by running fixpoint, the native suite and every harness directly
(0031, 0032, 0033) rather than through a gauntlet that had not been built
yet.