Nothing here ran zig build; it does not build on the version it declares - #97
Merged
Conversation
…be read src/root.zig is what every consumer imports, and no test target rooted it, so the one surface that matters was the one never compiled. Zig analyses top-level declarations lazily: a green test run proved only that the declarations the other tests happened to reference compile. That gap is not hypothetical. The same omission in gHashTag/zig-hdc hid five distinct API-drift errors against the version of THIS package that it pins, while its CI stayed green throughout. The new workflow is separate from test-bindings.yml on purpose. That one has been failing for its own reasons, and a verdict arriving into an already-red workflow carries no information -- the point of this file is to produce a verdict that can be read.
zig build failed on 0.15.2, the version declared in minimum_zig_version and the one both consumers use. The library was never the problem: the only failure was tools/gen/tri_gen.zig, which is written against Zig 0.16 on purpose -- its own comments say so, and it uses std.Io, std.Io.Dir and std.process.Init, none of which exist in 0.15. Installing a 0.16-only tool by default made the LIBRARY unbuildable for everybody in order to keep a tool nobody can run at that version. It moves behind an explicit `zig build tools` step, so the version claim becomes true rather than aspirational. This is not turning a build green by deleting what failed. What failed is still built, by a step that names the toolchain it needs, and CI asserts that step still exists so it cannot be silently dropped. None of this was visible before, because nothing in this repository ran zig build at all.
…lares
Eleven errors across five files, from the full-surface analysis added in the
previous commit. They fall into two kinds and it is worth keeping them apart.
Four were APIs that existed in 0.14 and were removed in 0.15, so this code had
been left behind by a version bump nobody could see because nothing ran
zig build:
std.fmt.fmtSliceHexLower -> {x} on the slice
std.io.getStdOut -> std.fs.File.stdout().writer(&buf).interface
std.atomic.fence(.acquire)-> folded into fetchSub(1, .acq_rel), which is
where that acquire always belonged
OpenFlags .read/.write -> .mode = .read_write
Seven were plain defects, none of them about versions:
sacredChecksum multiplied a u64 by the f64 PHI and then called @intFromFloat
on the u64 result. It has never compiled, so no stored checksum can depend on
it; it now multiplies by 2^64/phi, which is what phi IS in integer hashing
rather than a number I picked.
getEffectivePriority switched on a runtime value into comptime_float arms.
Annotated f64.
qbind declared *const and called bind, which calls ensureUnpacked, which fills
a cache inside the value and so must mutate it. qbind takes mutable pointers
now: making bind const would have been a lie about what it does, and qbind has
no callers yet.
applyPhase took @abs of an i32, which is a u32, and asked for it modulo an
i32. The modulus is unsigned now.
Three sites assigned an i8 from unpacked_cache into an i2 without a cast. The
compiler named three; there are six, the other three differing only in whether
they read a or b. Fixing the named three would have left half the defect
standing.
Fixing eleven errors uncovered five more, which is the normal shape: a compiler stops at the first failure in a unit and the ones behind it are invisible until it does not. applyPhase declared *const and called permute, which takes a mutable pointer for the same reason bind does -- the value caches its own unpacked form. Mutable now, and it has no callers, so nothing else moves. Three shift sites packed a u2 into a byte with `encoded << bit_offset`. Zig types a shift amount by the width of what is being shifted, so a u2 admits only a u1 -- meaning a bit_offset of 0, 2, 4 or 6 could not be used there at all. The value is going into a u8, so it is widened first and the amount typed to match. init bound the interface with const and then called ping, which takes *Self. A const binding cannot hand out the mutable pointer a method asks for. I left one thing alone deliberately. The `if (bit_offset >= 6)` branch writes the spill of a 2-bit code into the next byte, and a 2-bit code at offset 6 occupies bits 6 and 7 without spilling, so that branch looks dead. That is a question about the packing, not about the types, and answering it by changing behaviour while fixing a compile error is how a repair becomes a regression.
…ready done Two more shift sites, this time reading rather than writing: response[byte_idx] is a u8, so the amount has to be a u3. Same rule as the packing side. And the sign extension in similarity. `if (dot_msb & 0x04)` handed a u8 to an `if`, which takes a bool, so this line never compiled -- and what it reached for had already happened: @bitcast from u11 to i11 reads the top bit as the sign, and bit 2 of dot_msb IS that bit. The line also or-ed 0xF800 into an i11, a value that does not fit in one, so it could not have run even with a well typed condition. Removed rather than repaired, because there is nothing left for it to do.
The mask already narrows the value to two bits; the cast states what the mask guarantees, and decodeTrit takes a u2.
With the surface compiling, the tests ran, and 266 of 267 passed. The one that did not panicked on integer overflow at its first iteration: i is a usize, so @Rem(i, 3) is a usize, and 0 - 1 in unsigned arithmetic is not -1. The cycle it wanted is -1, 0, +1, which needs a signed type to live in. This test has never run. It could not have: the file it is in did not compile until four commits ago.
gHashTag
added a commit
to gHashTag/zig-hdc
that referenced
this pull request
Aug 11, 2026
gHashTag/zig-golden-float#97 repaired sixteen defects across that package, including the two this one tripped over: HybridBigInt without an allocator field, and the u32/i32 mismatch. Both originated there.
gHashTag
added a commit
to gHashTag/zig-hdc
that referenced
this pull request
Aug 11, 2026
The hash came from the runner, which has the toolchain that computes it. The previous pin predated gHashTag/zig-golden-float#97, so sixteen repaired defects were still arriving here through the dependency.
gHashTag
added a commit
to gHashTag/zig-hdc
that referenced
this pull request
Aug 11, 2026
* One implementation of src/vsa/*, not two Both this repository and gHashTag/zig-golden-float carried src/vsa/core.zig, common.zig, concurrency.zig, 10k_vsa.zig, hrr.zig and fpga_bind.zig. They were edited independently and they diverged, which is why repairing sixteen defects in golden-float (#97) left every one of them standing here: the fixes went into different files with the same names. The decision was made by measurement rather than preference. Comparing the two public surfaces symbol by symbol, they are identical -- one constant apart -- so neither copy is more capable and there is nothing to lose by choosing. The direction then follows the dependency that already exists: this package depends on golden-float, golden-float's copies build and pass 267 tests on 0.15.2, and these do not. Each duplicated file becomes a one-line re-export instead of being deleted. That keeps all twenty-three relative imports across this repository working exactly as they did, so nothing else in the tree moves, and it makes re-divergence impossible rather than merely discouraged: there is one implementation behind the path now. gen_core.zig and gen_encoding.zig stay as they are. golden-float does not export them, so they are genuinely this package's own, and so are the other eighteen files under src/vsa/ that have no counterpart there. * Name the re-exports one by one: usingnamespace is gone in 0.15 The first attempt re-exported each duplicated file with usingnamespace, which was removed in Zig 0.15 -- the version this package targets -- so the build answered 'expected function or variable declaration after pub'. The names are listed individually now, generated from the upstream sources rather than typed. That is a real cost and worth naming: a symbol added upstream does not appear here until it is added here too. It is still cheaper than a second implementation, and unlike a second implementation it fails loudly, because the name is simply missing rather than quietly different. MAX_PACKED_BYTES has no counterpart in golden-float's vsa/common.zig, so it comes from packed_trit, where the value actually lives, rather than being written out as a literal. Nothing here uses it, but it was in this module's public surface and dropping it silently would break a consumer who does. * Analyse the whole public surface, now that there is one implementation behind it The build went green with the duplicated files re-exporting golden-float, but green without this line means only that the declarations the tests happen to walk compile. That is exactly the condition that hid five API-drift errors here while the badge stayed green. Now the surface goes through the compiler as a whole, and the green means what a consumer would take it to mean. * Re-pin golden_float, and drop a re-export that never pointed at anything Two things the full-surface analysis found, neither of them caused by the deduplication. This branch was cut from main, which pins the golden-float from before its sixteen defects were repaired -- so atomic.fence and the rest of the 0.14 API came back through the dependency rather than from any file here. Re-pinned. And src/vsa.zig re-exported concurrency.LockFreePool, which has never existed in this repository's concurrency module nor in golden-float's. Both export the same twenty-three names and that is not one of them. The line referred to nothing and nothing complained, because lazy analysis never asked what it pointed at. * Temporary: have CI print the golden_float hash The local zig used to compute it does not survive between sessions here and the download keeps timing out, so the runner that already has the right toolchain computes it instead. Removed in the next commit. * Pin the repaired golden_float The hash came from the runner, which has the toolchain that computes it. The previous pin predated gHashTag/zig-golden-float#97, so sixteen repaired defects were still arriving here through the dependency. * text_encoding calls bundle2 and add with an allocator they no longer take Nine call sites passed std.heap.page_allocator to core.bundle2 and to HybridBigInt.add. Both take their operands and nothing else -- bundle2(a, b) and add(self, other) -- so the argument is dropped rather than replaced: there is nothing for it to become. This file is one of the eighteen under src/vsa/ that have no counterpart in golden-float, so it is genuinely this package's own, and it had been calling an API that moved without it. It compiled for nobody and nothing said so, because nothing referenced it. * permuted must be var: add takes a mutable pointer The value caches its own unpacked form, so filling that cache is a mutation and add is declared *Self. A const binding cannot hand out the pointer it asks for.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Nothing in this repository ran
zig build. Adding a CI that does, plusrefAllDeclsRecursiveinsrc/root.zig— the module root every consumer imports, which no test target rooted — makes the state visible for the first time.The package does not build on the version it declares
minimum_zig_version = "0.15.0", and both consumers (zig-hdc→trinity) build with 0.15.2. The public surface spans three incompatible Zig versions:Io.getStdOut,atomic.fence,fmt.fmtSliceHexLower,fs.File.OpenFlags.read— all removed in 0.15tools/gen/*usestd.Io,std.Io.Dir,std.process.Init, and say so in their own commentsPlus five genuine defects that are not version drift at all:
The first and third are the same errors that surfaced from
zig-hdcin gHashTag/zig-hdc#2 — so they originate here, not there.What this PR does and does not do
It does: add a readable CI, force the module root through the compiler, and move the 0.16-only
tri_genout of the default install so the library is not made unbuildable to keep a tool nobody can run at 0.15. That tool is still built byzig build tools, and CI asserts the step still exists so it cannot be silently dropped.It does not repair the drift. That is nine sites across three version targets, and each needs a decision about which version this package is actually for.
Left open rather than merged. Landing it turns
mainred with no repair attached, and a permanently red build is the condition that destroys a signal's information content. The finding belongs in review, not in a build nobody can read.