T27.AI

Blog

Ninety tests were present, but unreachable

2026-09-07 · 6 min read

[measured in merged PR #778] A Zig codegen subtree became importable, exposing 90 previously unreachable tests; the reported verification moved from 155 to 157 steps and from 2,832 to 2,939 tests.

Three-panel engraved illustration for: Ninety tests were present, but unreachable
Three panels, left to right
  1. THE TEST ROOTNinety tests became reachable.
  2. THE FAILURESTwo hidden failures surfaced.
  3. THE LIMIT66 identifier errors remain.
View the complete triptych at full size
#Zig#Compiler#Testing#Reproducibility

[measured in merged PR #778] A Zig codegen subtree became importable, exposing 90 tests that had been present but unreachable. The reported verification moved from 155 to 157 steps and from 2,832 to 2,939 tests.

The interesting part is not the size of the diff. It is the distinction between a gate that can parse a file and a test root that can execute it. Several Sema errors prevented the tree from being imported, while ast-check and the tree-wide format gate still treated the files as clean.

The hidden gate was a missing field default

[reported verification] Ten .test_cases = .{} sites were missing the capacity field after the Zig 0.16 ArrayListUnmanaged change. Twelve Behavior.owner literals also lacked a default. These were compile-time shape errors, not runtime failures, and they kept the codegen tree outside the test graph.

After those declarations were repaired, src/vibeec/zig_codegen.zig became a test root. The merged PR reports 157/157 build steps and 2,939 tests, compared with 155/155 and 2,832 before the tree was reachable.

Reachability exposed two different failures

The second failure matters because it distinguishes an implementation defect from an expectation defect. The implementation already preserved nested list structure; the expectation had never run far enough to be corrected by the test suite.

The signature story was narrower than expected

[measured in the merged PR] A sweep covered 558 combinations: 62 behaviour-name prefixes crossed with 9 then phrases. The emitter wrote pub fn <name>() !void in every case. The inference code is consulted by body_emitter and tests_gen, but not by the code that writes the generated function header.

That explains the earlier failure without claiming a repair that was not made. Two tests now pin the current boundary: a signature promising a value must eventually have a returning body, and the current emitter still produces !void. Wiring inferred return types into headers remains a separate change.

One error class became a scope problem

[reported verification] The undeclared-identifier census fell from 151 errors across 36 files to 66 errors, while total errors fell from 354 to 272 and rejected files from 100 to 94. The dominant shape was an ArrayList 0.16 migration that added allocator arguments without binding an allocator in the enclosing function.

The safe repair was scoped structurally: walk to the enclosing function and use self.allocator when the function has a self binding. A same-line text heuristic missed calls inside methods and could have changed a legitimate local allocator. The remaining 66 errors are a different class and were left for reading rather than folded into this fix.

What is established, and what is not

The merged PR establishes a reachable Zig codegen test root, two newly visible test failures that were corrected or pinned, and a narrower classification of the remaining compiler errors. It is a software repository result.

What this does not settle

Receipts

Work with me

Want this kind of check on your own design?

I audit RTL and build independent, bit-exact models, then take the result through synthesis and, when useful, onto an Artix-7 board. The first conformance module is free.