FFI probe, 2026-08-19 — what a first-time reader hits
Provenance. This run was a Claude subagent spawned inside the author's own session, not an independent third party — see probe-model-comparison-2026-08-20.md. Every finding below was rebuilt on
mainbefore being written down, so the findings stand; the independence does not.
Verdict: two defects, both fixed in 669d0a50. Probed 2026-08-19 on the v0.7.0 tarball against extern fn, handle, and soa. Outcome: is_null accepts handles, and docs/reference/ffi.md states the returned-pointer lifetime rule.
The agent built a 245-line directory-size analyser with a 42-line C shim. The program was discarded; this record is the artifact. It is not in the tree and does not need to be.
The probe aimed at ground no earlier record covered: tools/tycho-diff was algorithms, tools/tycho-hash concurrency, and an earlier markov run maps and structs. The FFI boundary, handle, and soa had never been driven from outside.
What it changed
Two fixes, both in 669d0a50:
is_nullrefused a handle. The agent wroteif is_null(d)after an opener, gotargument 1 of 'is_null' is DirStream, expected ptr, and deleted the check — leaving no way to test whether the open succeeded, with the destructor then freeing NULL at scope exit.is_nullaccepts a handle now.docs/reference/ffi.mddid not state that a returned C string must still be live. It said the string is "copied into the caller's arena", which reads as permission to return anything. The copy happens after the C function returns.
The finding the agent did not make
Its own shim did closedir(d); return ent->d_name;. The dirent belongs to the stream it had just closed. The plain build printed correct output; ASan reported a heap-use-after-free in tycho_str_from_c.
That is the most useful result of the run, and it is not in the agent's log. A probe's report is not its whole yield — the code it wrote has to be built and run under the sanitizers too, or the class of defect the FFI boundary exists to contain is the one that goes unnoticed.
Reported and already fixed
bytes as a parameter name was reported as giving expected a parameter name. On main it gives 'bytes' is a reserved keyword and cannot be used as a parameter name. The probe ran against the v0.7.0 tarball, so part of its report was archaeology. Give the next agent a build of main.
Reported, checked, not defects
chr is byte-ranged; int and float never mix implicitly; structs do not cross the FFI; sink consumes. All four are documented and the agent's own log records them as things learned, not complaints. The inout overlap refusal on &stats[j], &stats[j + 1] is real and is the limitation recorded as FRICTION #48.
What went right, in its words
The handle pattern once declared; soa being invisible at the use site; the --shim flag; and "the compiler catches real issues with clear messages — the overlapping mutable access error prevented a real aliasing bug." A log that only complains is not evidence, and this one does not.