cycles/ one file per Ishikawa -> scientific method -> Six Sigma loop, named
for the DEFECT not the fix, carrying the commit record as written at
the time
findings/ what the cycles produced, cross-cut: live bugs, architecture answers,
and defects in my own measurement
The organising finding is that predictions which came back FALSE produced every
significant result. Eleven of sixty-one failed, and those eleven found: that the
arity table was not drifted but 40% incomplete; that the AST traversal is
irreducible and only rules and judgments move; that guards could refuse through
the seam after all; and that routing el_bin_lookup through the gate did NOT fix
the SIGSEGV, because the fallback strlen was the hazard -- a wrong fix I would
otherwise have shipped as verified.
One cycle was run without committing predictions first and had to be discarded
as rigged. It is kept, in full, as 18-async-half-expressible.md.
2.5 KiB
Live defects found
Every one compiled clean, ran, and produced a wrong result or a crash with no diagnostic at any layer. All four were present before this session; none was introduced by it.
Silent miscompilations
1. An unannotated let loses its type
let a = str_len("hello") // no annotation
let b = str_len("hi")
let c = a + b // → el_str_concat(a, b) on two integers
Compiled clean. Printed nothing where it should print 7. Fixed: an
unannotated let takes its type from what its initialiser returns. The return
types were already required for dispatch and were simply never consulted at the
binding site.
2. Reserved keywords that reserved nothing
let seed = 42
let impl = seed + 1
sealed, activate, seed, protocol, impl were keywords in the lexer and
consumed by no parser or codegen path. Using one did not fail to parse — it
compiled clean, with zero cc errors, and printed 0 instead of 44. Fixed by
removing all five.
3. Instant + Int was never refused
let t: Instant = now()
let u: Instant = t + 3 // → (t + 3), reported clean
Duration + Int was refused — "an Int carries no unit" — while adding a
dimensionless number to a point silently moved the instant by an
unspecified amount. Three of what? Whatever the representation happens to be.
The rule was simply never written.
Security-relevant
4. Annotations are never verified
let x: Int = "hello"; x + 1 → 4343631981 a string POINTER used as an integer
let s: String = 42; println(s) → nothing address 42 dereferenced
The first leaks a raw memory address into program output. The second is an arbitrary-read primitive if the integer is ever attacker-influenced.
5. sha256_hex(<integer>) segfaults
let h: String = sha256_hex(50000) → exit 139, SIGSEGV
Compiled clean. el_bin_lookup checked only a 4096 floor — no alignment, no
small-int, no negative — and reads eight bytes backward from the pointer.
And the actual crash was one level further on: el_input_len fell through to
strlen() on address 50000, because a NULL check does not establish that a slot
is a pointer.
Fixed, and the guard is now a gate: el_tagged() is exported in
el_runtime.h. geom_of and mfld_of were always correct because their authors
knew to call looks_like_heap_obj; el_bin_lookup and el_input_len were wrong
because theirs did not, and the function was static, so every sibling
translation unit re-derived it.