build(toolchain): bump Zig to dev.2085 — @hasDecl is now pub-only - #351
Merged
Merged
Conversation
`nix flake update` moved the zig-overlay pin from 0.17.0-dev.1786+75044cb04
to 0.17.0-dev.2085+5e36170b5, and every `zig build` broke with 26 comptime
errors of the form:
src/grants.zig:186:13: error: type 'grants.FakeProbes' is not Grant
Probes: missing method 'micGranted'
Cause: upstream Zig narrowed `@hasDecl` to report only *public*
declarations. The langref shipped with each compiler states the change
outright — dev.1786 says "has a declaration matching name", dev.2085 says
"has a **public** declaration matching name". Confirmed by differential
probe on both toolchains: `@hasDecl(T, "m")` for a non-pub `m`, queried
from T's own file, returns true on dev.1786 and false on dev.2085.
The five comptime contract assertions (`assertProbes`, `assertHelper`,
`assertTransport`, `assertDeps` x2) check a type's method surface with
`@hasDecl`. Every production adapter already declares those methods `pub`;
the test fakes declared them bare `fn`, which was legal only because the
assertion sits in the same file as the fake and same-file visibility used
to satisfy `@hasDecl`. Under the new rule the fakes stop matching their own
contracts and each assertion fires `@compileError`.
Fix: `pub` on the contract methods of FakeProbes, FakeDeps (undo and
insertion_runner), FakeHelper, and FakeTransport, plus FakeTransport's
`Reader` handle. Nothing else changes — no runtime behaviour, no production
code. Test-only helpers such as `FakeProbes.requested` stay private, so the
`pub` marker now says precisely which members are the contract.
That the seam was always fragile is visible in std: `std.meta.hasFn` lives
in a different file and therefore already returned false for private decls
on dev.1786. These hand-rolled checks were leaning on a visibility rule
std's own helper never offered.
Worth knowing for the next reader: `local_backend.zig:194`'s
`if (comptime !@hasDecl(Helper, "usesModel")) return true;` is the one
*optional* `@hasDecl` in the tree — it degrades silently instead of
erroring, and under the new semantics it would have quietly reported
"installation still valid" forever rather than failing to compile. It is
saved only because `usesModel` is also in `assertHelper`'s required list,
so the hard assertion fires first. That redundancy is load-bearing.
Verified: `zig build`, `zig build test`, and `zig build install-agent` all
exit 0 on dev.2085 (install signed pair-1789068005-16478).
Per docs/toolchain.md this bump is deliberately partial — see the PR body.
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.
What broke
nix flake updatemoved the zig-overlay pin0.17.0-dev.1786+75044cb04→0.17.0-dev.2085+5e36170b5, and everyzig build— includingzig build install-agent— died with 26 comptime errors:Why
Upstream Zig narrowed
@hasDeclto report onlypubdeclarations. This is a deliberate, documented language change — the langref shipped inside each compiler says so:@hasDecl(Priv,"m")from Priv's own filedev.1786(before)truedev.2085(after)falseVerified by differential probe on both toolchains, not inferred.
Five files define comptime contract assertions —
assertProbes,assertHelper,assertTransport, andassertDeps(undo + insertion_runner) — that check a type's method surface with@hasDecl. Every production adapter already declares those methodspub. Every test fake declared them barefn, which was legal only because the assertion lives in the same file as the fake, and same-file visibility used to satisfy@hasDecl. Under the new rule the fakes stop matching their own contracts and each assertion fires@compileError.A tell that the seam was always fragile:
std.meta.hasFnlives in a different file and so already returnedfalsefor private decls ondev.1786. These hand-rolled@hasDeclchecks were leaning on a visibility rule std's own equivalent never offered.The fix
pubon the contract methods ofFakeProbes,FakeDeps×2,FakeHelper, andFakeTransport, plusFakeTransport'sReaderhandle. 46 lines,puband nothing else — no runtime behaviour change, no production code touched. Test-only helpers likeFakeProbes.requestedstay private, so thepubmarker now says precisely which members are the contract.Verified
zig buildzig build testzig build install-agentpair-1789068005-16478docs/toolchain.mdrequires the compiler and websocket.zig to move as one atomic change. This PR does step 1 and the fallout only. Outstanding:docs/research/websocket-zig-bump.md(untracked onmainat time of writing) recommends folding the pin bump tobfe761959b05030eaf4943fcf0fd5ecd1daca68ainto "the next compiler bump" — which is this one. Not done here. Note that research proved the current pin4b475a8compiles underdev.1786; it has not been re-proved underdev.2085.minimum_zig_versionis0.17.0-dev.1267+300116b02in all sixbuild.zig.zonfiles. This drift is pre-existing (it was already two nightlies stale before this PR), not introduced here.docs/toolchain.md's "currently pinned pair" table is stale for the same reason.wss://api.openai.com/v1/realtimewas run.zig build testdoes not exercise the live transport.Happy to do any of these in follow-ups; they were left out to keep this PR to the diagnosed break.
One thing worth a second look
src/local_backend.zig:194:This is the only optional
@hasDeclin the tree — it degrades silently rather than erroring. Under the new semantics it would have quietly reported "installation still valid" forever for any Helper with a non-pubusesModel, instead of failing to compile. It's saved only becauseusesModelis also inassertHelper's required list, so the hard assertion fires first. That redundancy is load-bearing, and the line is now dead weight — left alone here, but it's the shape of bug this bump could have caused instead of a clean compile failure.🤖 Generated with Claude Code
https://claude.ai/code/session_01JGU4Zq5h2Hf7pTXDuQNa3w