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.8 KiB
async — half expressible, and the cycle that was dogma
Status: measured on a branch, not merged. Two runs — the first was invalid.
The first attempt was DOGMA, not science
I had just finished arguing that @async was expressible, then ran something to
confirm it. No prediction was committed. The test was rigged in a way that
should have been visible while writing it:
pthread_create(&t,NULL,runner,NULL); pthread_join(t,NULL);
join immediately after create — the caller blocks until the body finishes.
That is a thread round-trip, not deferral. And the test printed the word
DEFERRED itself: I wrote the conclusion into the output and read it back.
Ishikawa on the rigged test
method ran after concluding, not to decide
machine nothing forces a prediction before execution
material the assertion was written into the output string
measurement no falsification criterion existed, so nothing could fail
root cause the test was authored by the party holding the conclusion,
with no commitment made before it ran
Discarded and re-run properly.
Second run — predictions committed first
P1 the caller proceeds while the body runs expect TRUE
P2 interleaving is observable in timestamps expect TRUE
P3 the result cannot be retrieved — one 64-bit slot, no
future type, so the wrap either blocks or returns
something that is not the result expect TRUE
P4 therefore HALF expressible: fire-and-forget yes, await no expect TRUE
Results — 4/4
[ 18 us] wrap RETURNS to caller
[ 29 us] body START
caller continues, got 0
[ 50176 us] body END (computed 42)
caller done
The caller got 0, not 42. Both of my earlier claims were wrong in opposite directions: "not expressible" was too strong — fire-and-forget works today, bound after the build, no compiler change. "Expressible" was too strong the other way.
Follow-on cycle — a future is one more tagged object
P1 el_val_t already carries tagged heap objects TRUE 5 magic tags exist
P2 a future is one more TRUE
P3 the caller awaits and gets 42 TRUE
P4 ZERO compiler changes TRUE runtime C + one binding
P5 the unbound path still works FALSE SIGSEGV
P4 is the result. @async — called unexpressible for hours — needs no
compiler change. A future is one more magic-tagged heap object; defer returns
the handle, el_await blocks.
P5 is the failure that mattered. Sixty seconds after diagnosing
let s: String = 42 as an arbitrary read, I wrote the identical defect into
el_await: reading ->magic off an unvalidated slot. That opened cycle 19.