Skip to content

feat: Zig 0.16 full migration (build + src/ + Walk + main + ziglint dep)#1

Merged
EugOT merged 8 commits into
mainfrom
fix/0.16-full-migration
May 7, 2026
Merged

feat: Zig 0.16 full migration (build + src/ + Walk + main + ziglint dep)#1
EugOT merged 8 commits into
mainfrom
fix/0.16-full-migration

Conversation

@EugOT

@EugOT EugOT commented May 7, 2026

Copy link
Copy Markdown
Owner

Summary

End-to-end Zig 0.16 migration of zigdoc: build.zig, build_readme.zig, src/Walk.zig, src/main.zig, and the ziglint build dep are all migrated to Juicy Main + std.Io.*. zig build and zig build test exit 0 on Zig 0.16.0.

Five commits, push order below.

WHY

Zig 0.16 reshaped the stdlib surface used throughout zigdoc: std.process.argsAlloc/argsWithAllocator, std.process.Child.run, std.fs.cwd(), std.fs.openDirAbsolute, std.fs.File.{stdout,stderr}, std.zon.parse.fromSlice, std.mem.indexOf (deprecated for .find), Build.Step.Run.captureStdOut signature, and std.StringArrayHashMapUnmanaged (renamed). The rockorager/ziglint v0.5.2 build dep also still used 0.14/0.15 APIs.

WHAT (per commit)

  1. 0560610 fix(build): pass empty Options to captureStdOut for Zig 0.16

    • readme_run.captureStdOut() -> readme_run.captureStdOut(.{})
  2. 4bd2356 fix(deps): repoint ziglint to EugOT fork via local path

    • Switches the .ziglint dep to path = "../ziglint" so we can validate against EugOT/ziglint fix/0.16-build-compat before a tag exists.
    • Note: per the briefing, this PR keeps the local path dep as-is. Once EugOT/ziglint#fix/0.16-build-compat lands and tags a 0.16-compat release, a follow-up will repoint to URL+hash for reproducibility.
    • minimum_zig_version 0.15.1 -> 0.16.0.
  3. 9bc5142 fix(build_readme): migrate to Zig 0.16 Juicy Main + Io.Dir APIs

    • pub fn main(init: std.process.Init) !void
    • init.minimal.args.iterateAllocator(allocator)
    • std.Io.Dir.cwd().readFileAlloc(io, ..., .limited(...)) and writeFile(io, ...)
  4. 1c495ed fix(walk): migrate src/Walk.zig to Zig 0.16 Io-aware APIs

    • Module-level io_global initialized via Walk.init(allocator, io); tests pass std.testing.io
    • std.fs.realpathAlloc -> std.Io.Dir.cwd().realPathFileAlloc
    • dir.readFileAlloc(allocator, path, size) -> readFileAlloc(io, path, allocator, .limited(size))
    • std.StringArrayHashMapUnmanaged -> std.array_hash_map.String (silences Z011)
    • Drops the removed .asm_legacy AST node tag (.asm_simple + .@"asm" cover legacy syntax)
  5. 495eafe fix(main): migrate src/main.zig to Zig 0.16 Juicy Main + Io APIs

    • Juicy Main entry; gpa/io from init
    • argsWithAllocator -> init.minimal.args.iterateAllocator
    • Child.run -> std.process.run(allocator, io, ...) with .stdout_limit = .limited(...)
    • std.fs.cwd().{access,makeDir,writeFile,readFileAlloc,realpath} -> std.Io.Dir.cwd() equivalents
    • std.fs.openDirAbsolute -> std.Io.Dir.openDirAbsolute; std.fs.File.stdout -> std.Io.File.stdout
    • .writer(buf) -> .writer(io, buf) plus explicit .end() flushes
    • std.zon.parse.fromSlice -> fromSliceAlloc (ZigEnv carries slices)
    • Add Zig 0.16 to build_runner switch
    • std.mem.indexOf -> std.mem.find (Z011)
    • Reorder helpers so io: std.Io precedes arena param (Z023)

IMPACT

  • zigdoc builds and tests cleanly on Zig 0.16.0
  • README generator (zig build) emits ../README.md successfully
  • ./zig-out/bin/zigdoc 'std.Io.Writer' returns 108 lines of structured docs (signature, fields, members)
  • ./zig-out/bin/zigdoc 'std.ArrayList' returns full type_function entry with doc comments + source
  • Behaviour unchanged for end users; only IO-layer call shapes changed
  • Note: std.io.Writer lookups now correctly return a "not found" hint because std.io was renamed to std.Io in Zig 0.16 (this is a stdlib rename, not a regression)

Dependency note

build.zig.zon currently has .ziglint = .{ .path = "../ziglint" } so this PR can be validated end-to-end alongside EugOT/ziglint#fix/0.16-build-compat. Once that PR merges and tags a 0.16-compat release, a follow-up will repoint to URL+hash. Reviewers wanting to build locally need a sibling clone of EugOT/ziglint on the fix/0.16-build-compat branch.

VALIDATION

mise x zig@0.16.0 -- zig build                                                # exit 0
mise x zig@0.16.0 -- zig fmt --check src/ build.zig build.zig.zon build_readme.zig  # exit 0
mise x zig@0.16.0 -- zig build test                                           # exit 0 (lint, fmt, unit tests)
./zig-out/bin/zigdoc 'std.Io.Writer' | wc -l                                  # 108
./zig-out/bin/zigdoc 'std.ArrayList'                                          # full type_function entry

Test plan

  • zig build exits 0
  • zig fmt --check clean across src/, build.zig, build.zig.zon, build_readme.zig
  • zig build test exits 0
  • CLI lookups for std.Io.Writer and std.ArrayList return structured docs
  • Follow-up: repoint ziglint dep to URL+hash after EugOT/ziglint#fix/0.16-build-compat merges + tags

Evgenii O. Tretiakov added 5 commits May 7, 2026 04:23
WHY: Zig 0.16 changed the Build.Step.Run.captureStdOut signature
from no-arg to require a CapturedStdIo.Options struct. Without this
fix build.zig fails at the configure step, blocking the build.

WHAT: readme_run.captureStdOut() → readme_run.captureStdOut(.{})

IMPACT: zigdoc build.zig now compiles under Zig 0.16.0. The
remaining build failure is in the rockorager/ziglint v0.5.2
transitive dep (same realpathZ + .Ignore errors fixed in
EugOT/ziglint fix/0.16-build-compat). Repointing zigdoc's
build.zig.zon at the EugOT fork's 0.16-compat tag is the next step.

VALIDATION: cd ~/ghq/github.com/EugOT/zigdoc && mise x zig@0.16.0 --
zig build → zigdoc's own build.zig succeeds; only transitive ziglint
dep errors remain.
WHY: rockorager/ziglint v0.5.2 still uses Zig 0.14/0.15 APIs (realpathZ,
.Ignore variant) that were removed in Zig 0.16. The EugOT/ziglint
fix/0.16-build-compat branch (HEAD e642d49) has the migration.

WHAT:
- Switch .ziglint dependency from URL+hash (rockorager v0.5.2) to local
  path "../ziglint" so we can validate against the in-progress 0.16 fork
  before pinning to a tagged release.
- Bump minimum_zig_version 0.15.1 -> 0.16.0.

IMPACT: build.zig.zon only. Local path requires ../ziglint to exist as a
sibling checkout; once EugOT/ziglint cuts a 0.16-compatible tag this
should be repointed to URL+hash for reproducibility.

VALIDATION: mise x zig@0.16.0 -- zig build (transitive ziglint dep
compiles cleanly; remaining failures are in zigdoc's own src/ which this
commit does not touch).
WHY: Zig 0.16 removed std.process.argsAlloc/argsFree and std.fs.cwd();
files now must opt into the Init struct (Juicy Main) and use
std.Io.Dir.cwd() with explicit io threading.

WHAT:
- main signature: pub fn main(init: std.process.Init) !void
- iterate args via init.minimal.args.iterateAllocator(allocator)
- replace std.fs.cwd().readFileAlloc with std.Io.Dir.cwd().readFileAlloc
  using .limited(...) Io.Limit
- replace std.fs.cwd().writeFile with std.Io.Dir.cwd().writeFile(io, ...)

IMPACT: build_readme.zig only. The README generator still produces the
same markdown content; only the IO-layer call shapes changed.

VALIDATION: mise x zig@0.16.0 -- zig build emits ../README.md
successfully and exits 0.
WHY: Zig 0.16 removed std.fs.cwd(), std.fs.realpathAlloc, and renamed
std.StringArrayHashMapUnmanaged to std.array_hash_map.String. The AST
also removed the .asm_legacy node tag.

WHAT:
- Add a module-level io_global alongside the existing module-level gpa,
  initialized via Walk.init(allocator, io). Tests pass std.testing.io.
- Swap std.fs.realpathAlloc for std.Io.Dir.cwd().realPathFileAlloc.
- Swap std.fs.cwd().readFileAlloc(allocator, path, size) for
  std.Io.Dir.cwd().readFileAlloc(io, path, allocator, .limited(size)).
- Drop the .asm_legacy switch arm: the variant was removed; .asm_simple
  and .@"asm" already cover the legacy syntax.
- Update test_symbol_resolution.zig setupTest to thread std.testing.io
  into Walk.init.

IMPACT: src/Walk.zig and src/test_symbol_resolution.zig. The two static
public mutables (Walk.files, Walk.modules) and the inner Namespace
maps switch to std.array_hash_map.String to silence Z011 deprecation
hits while preserving behaviour.

VALIDATION:
- mise x zig@0.16.0 -- zig build -> exit 0
- mise x zig@0.16.0 -- zig build test -> exit 0 (incl. ziglint rule
  pass and the unit tests in test_symbol_resolution.zig).
WHY: Zig 0.16 reshaped the standard library surface used throughout
src/main.zig: std.process.argsWithAllocator/Child.run/exit, std.fs.cwd,
std.fs.openDirAbsolute, std.zon.parse.fromSlice, and the std.mem.indexOf
deprecation. The previous DebugAllocator/smp_allocator startup dance is
also superseded by the Init struct provided by the new entry point.

WHAT:
- Adopt the Juicy Main signature `pub fn main(init: std.process.Init)`
  and source gpa/io from `init`. Drop the manual DebugAllocator
  bootstrap.
- Replace std.process.argsWithAllocator with
  init.minimal.args.iterateAllocator.
- Replace std.process.Child.run with std.process.run(allocator, io, ...)
  using `.stdout_limit = .limited(...)` and switch on result.term.
- Replace std.fs.cwd().{access,makeDir,writeFile,readFileAlloc,realpath}
  with their std.Io.Dir.cwd() equivalents threading io: createDir uses
  .default_dir, realPath fills a buffer, readFileAlloc returns a slice
  via Io.Limit.
- Replace std.fs.openDirAbsolute / std.fs.File.stdout with
  std.Io.Dir.openDirAbsolute / std.Io.File.stdout.
- Replace std.fs.File.stdout().writer(buf) with .writer(io, buf) and
  add explicit `.end()` flushes alongside `.flush()`.
- Use std.zon.parse.fromSliceAlloc for ZigEnv (the type contains slices
  that require an allocator at parse time).
- Add Zig 0.16 to the supported build_runner switch (uses the 0.15 file).
- Migrate two std.mem.indexOf calls to std.mem.find (Z011 dep deprecated
  the old name in favour of `find`).
- Drop the now-unused `const builtin = @import("builtin")` import.
- Reorder helper functions so `io: std.Io` precedes the arena/`other`
  parameters, satisfying ziglint Z023.
- For walkStdLib, cast the readFileAllocOptions return slice to []u8
  via @constcast since Walk.addFile mutates the trailing newline.

IMPACT: src/main.zig only. Behaviour is unchanged for end users; the
program still resolves std-prefixed symbols against `zig env`'s std_dir
and dumps build.zig imports via the bundled build_runner.

VALIDATION:
- mise x zig@0.16.0 -- zig build -> exit 0
- mise x zig@0.16.0 -- zig fmt --check src/ build.zig build.zig.zon
  build_readme.zig -> exit 0
- mise x zig@0.16.0 -- zig build test -> exit 0 (lint, fmt, unit tests)
- ./zig-out/bin/zigdoc 'std.Io.Writer' -> 108 lines of structured docs
  including signature, fields, and member listings (the briefing's
  literal 'std.io.Writer' lookup returns a "not found" hint because
  std.io was renamed to std.Io in 0.16).
- ./zig-out/bin/zigdoc 'std.ArrayList' -> full type_function entry
  with doc comments and source.
Copilot AI review requested due to automatic review settings May 7, 2026 12:29
@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Review Change Stack
No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 15de38ac-2f20-402d-8264-cdc5c5760305

📥 Commits

Reviewing files that changed from the base of the PR and between 495eafe and 3e0a455.

📒 Files selected for processing (4)
  • build.zig
  • build.zig.zon
  • build_readme.zig
  • src/main.zig

📝 Walkthrough

Summary by CodeRabbit

  • Chores

    • Updated minimum Zig language requirement to 0.16.0
    • Updated lint dependency reference
    • Improved README generation to capture tool help output
  • Refactor

    • Reworked IO and CLI plumbing for more consistent filesystem and process handling
  • New Features

    • Stronger argument validation and clearer usage output for the README generation tool
  • Tests

    • Added tests covering argument parsing and IO-aware behavior

Walkthrough

This PR migrates the entire codebase to Zig 0.16's std.process.Init dependency-injection pattern and std.Io-backed APIs. The version requirement is bumped to 0.16.0, and all filesystem and process operations throughout Walk, build tools, and CLI utilities are refactored to accept and use injected std.Io instances instead of direct std.fs and std.process calls.

Changes

Zig 0.16 I/O Injection Migration

Layer / File(s) Summary
Build Configuration & Dependencies
build.zig.zon, build.zig
Zig version requirement increased to 0.16.0; ziglint dependency switched from remote tarball to git+ref pinned reference; build.zig uses captureStdOut(.{}) and test step now runs build_readme tests.
Walk Module Type Schema
src/Walk.zig
Public hash-map types for files and modules updated from StringArrayHashMapUnmanaged to array_hash_map.String variant; Scope.Namespace maps updated similarly.
Walk Module Initialization & I/O Storage
src/Walk.zig
Walk module adds io_global instance variable and updates init function to accept and store std.Io parameter alongside allocator.
Walk Module File Operations
src/Walk.zig
Path resolution and file reading for @import statements migrate from std.fs.realpathAlloc and std.fs.cwd().readFileAlloc to std.Io.Dir.cwd()-based equivalents using stored io_global.
Build Tool Refactoring
build_readme.zig
main signature updated to accept std.process.Init; added ParsedArgs/parseArgs; arg parsing uses init.minimal.args.iterateAllocator; file I/O uses std.Io.Dir.cwd(); unit tests added for argument parsing and usage output.
Main CLI Entry Point
src/main.zig
main signature updated to accept std.process.Init; argument parsing uses init.minimal.args.iterateAllocator; Walk initialized with injected IO; CLI dispatch updated for help/dump/imports/init flows.
Main CLI Output & Utilities
src/main.zig
printUsage updated to use std.Io-backed stdout writer; initProject refactored to accept io and use std.Io.Dir.cwd() for filesystem and process operations.
Build Processing Pipeline
src/main.zig
dumpImports, setupBuildRunner, and processBuildZig thread io; build-runner creation and zig build runs use std.Io APIs; parseBuildOutput reads module files via cwd.readFileAlloc(io, ...).
Standard Library Access Utilities
src/main.zig
getStdDir and getZigVersion run via injected IO and std.process.run; walkStdLib uses IO-based directory iteration and readFileAlloc.
Documentation Output with I/O Writer
src/main.zig
printDocs signature updated to accept io; all doc output uses std.Io-backed writer and calls end() on success and error paths.
Test Infrastructure Updates
src/test_symbol_resolution.zig, src/main.zig tests
setupTest now calls Walk.init(allocator, std.testing.io); added compile-time test ensuring std.Io.Dir.readFileAlloc returns mutable buffer and Walk.addFile expects mutable slices.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

  • CEL-41: Build configuration updates for Zig 0.16 compatibility (version bump and local dependency path changes in build.zig.zon and build process refactoring).

Possibly related PRs

  • EugOT/gitstore-cli#1: Performs similar refactor to accept std.process.Init and thread std.Io through CLI and I/O operations.

Poem

🐰 The old I/O paths have hopped away,
Now injected flows brighten the day,
With std.Io in hand, so keen,
Zero-point-sixteen makes our code lean,
Walk, build, and CLI all say: hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: Zig 0.16 full migration (build + src/ + Walk + main + ziglint dep)' accurately and comprehensively summarizes the main change: a complete end-to-end migration to Zig 0.16 across multiple core files.
Description check ✅ Passed The description is comprehensive, well-organized, and directly related to the changeset. It explains the 'why' (stdlib API reshaping in Zig 0.16), 'what' (five commits with specific changes to five files), impact (builds and tests cleanly, CLI works), and validation steps.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/0.16-full-migration

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Microsoft Presidio Analyzer (2.2.362)
build.zig

Microsoft Presidio Analyzer failed to scan this file


Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 495eafeea6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread build.zig.zon Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes an end-to-end migration of zigdoc to Zig 0.16 “Juicy Main” and the new std.Io.* APIs across the build scripts and core source, aiming to keep runtime behavior the same while updating IO/process/fs/stdlib call sites for Zig 0.16 compatibility.

Changes:

  • Migrate src/main.zig, src/Walk.zig, and README generator to Zig 0.16 std.process.Init + std.Io APIs.
  • Update build logic for Zig 0.16 API changes (captureStdOut(.{}), minimum Zig version bump).
  • Temporarily repoint ziglint dependency to a local sibling path for Zig 0.16 compatibility testing.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/Walk.zig Updates Walk’s filesystem usage to std.Io.Dir and migrates renamed stdlib hash map types.
src/test_symbol_resolution.zig Adjusts tests to initialize Walk with std.testing.io.
src/main.zig Migrates CLI entrypoint, build runner invocation, stdlib walking, and file IO to Zig 0.16 Init/Io APIs.
build.zig.zon Bumps minimum Zig version to 0.16.0 and changes ziglint dependency source.
build.zig Updates captureStdOut invocation for Zig 0.16 signature changes.
build_readme.zig Migrates README generator tool to Zig 0.16 Init/Io APIs and new args handling.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread build_readme.zig
Comment thread build_readme.zig Outdated
Comment thread src/main.zig Outdated
Comment thread build.zig.zon

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@build.zig.zon`:
- Around line 7-8: The .ziglint dependency currently uses a sibling path (the
.ziglint entry with path = "../ziglint"), which breaks reproducibility; change
this by either replacing the local path with a pinned remote package (set .url
to the fork’s git URL and .hash to the commit/tag hash and remove the local
path) or by making the dependency an opt-in override (remove the path and
document an optional override key such as .overridePath or a comment so
consumers can supply ../ziglint locally); update the .ziglint block accordingly
and ensure any package resolver references (the .ziglint symbol) still match the
new shape.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1f38b436-b655-450c-83f3-19cba54de86b

📥 Commits

Reviewing files that changed from the base of the PR and between 17750ed and 495eafe.

📒 Files selected for processing (6)
  • build.zig
  • build.zig.zon
  • build_readme.zig
  • src/Walk.zig
  • src/main.zig
  • src/test_symbol_resolution.zig

Comment thread build.zig.zon Outdated
Evgenii O. Tretiakov added 3 commits May 7, 2026 15:38
…oducible builds

WHY:
CodeRabbit (PRRT_kwDOST2NKs6ATjJU), copilot (PRRT_kwDOST2NKs6AThNA), and
chatgpt-codex (PRRT_kwDOST2NKs6ATerA) flagged the `.path = "../ziglint"`
dependency as P1/Major: fresh clones, CI runners, and downstream
consumers without a sibling `ziglint` checkout cannot resolve the
build graph, breaking `zig build` and `zig build test` outside the
maintainer's machine.

WHAT:
Replace the local-path dependency in build.zig.zon with a URL+hash
pin against EugOT/ziglint @ fix/0.16-build-compat
(commit 40f26f1283468032ec993c93fca921bd3c106adb), the Zig 0.16
compatibility branch produced for this migration. Hash captured via
`zig fetch --save=ziglint git+https://...#fix/0.16-build-compat`:
ziglint-0.5.2-t0bwLz2XBQCgnA0PTY0KM9YgrmdjLU7wwjXablNijOuq.

IMPACT:
- build.zig.zon (sole change): dependency lookup now goes through the
  Zig package fetcher and global cache, no host filesystem assumption.
- build.zig (unchanged): `b.dependency("ziglint", ...)` and
  `ziglint.addLint(...)` resolve identically.
- No source changes; no behavioral change for tests/lint/fmt steps.

VALIDATION:
- `rm -rf .zig-cache && zig build test` -> exit 0 with sibling present.
- Mutation 1 (independence): renamed `../ziglint` away,
  `rm -rf .zig-cache && zig build test` -> exit 0 (proves URL+hash is
  load-bearing, not the sibling).
- Mutation 2 (hash discipline): corrupted hash to all-A,
  `zig build test` -> "hash mismatch" error (proves the pin is
  enforced; restored after).
- `zig fmt --check build.zig.zon` -> exit 0 (covered by `zig build
  test` -> fmt step).
…ses argv[0]

WHY:
copilot-pull-request-reviewer flagged build_readme.zig in PR #1:
  * PRRT_kwDOST2NKs6AThL_ (line 18): the iterator-based parser silently
    ignores positionals beyond <help-file> <output-file>, regressing
    the previous `args.len == 3` strictness.
  * PRRT_kwDOST2NKs6AThMf (lines 10-13): the usage banner hard-codes
    "gen_readme" instead of echoing argv[0], so a renamed binary
    prints a misleading hint.

WHAT:
1. Extract `pub fn parseArgs(allocator, prog_name, rest, *usage_buf)` —
   exactly two positionals required, any other arity returns
   error.InvalidArgs and writes a `Usage: {prog_name} ...` line into
   the caller-owned ArrayList(u8). Allocator-first parameter order
   satisfies ziglint Z023; explicit `try` on the usage write satisfies
   Z026 (no silent catch).
2. main() now captures argv[0], collects the remaining positionals
   into a heap-owned slice, calls `parseArgs`, prints the buffered
   usage to stderr on error, and only then proceeds to read/write.
3. New unit tests pin five invariants: happy path, zero/one/three/four
   positionals all rejected, and the usage text contains both
   "<help-file>", "<output-file>", and the supplied prog_name.
4. build.zig wires `build_readme_tests` (b.addTest on build_readme.zig)
   into the `test` step so these invariants run alongside src/main.zig
   tests.

IMPACT:
- build_readme.zig: public API gains `parseArgs`, `ParsedArgs`,
  `ParseError`. Behavior change: extra args now error instead of being
  ignored; usage echoes the binary's actual invocation name.
- build.zig: test step gains 5 new tests (no other steps change).
- No source-of-truth changes for src/, lint, or fmt; gen_readme is a
  build helper, not a runtime artifact.

VALIDATION:
- `zig build test` -> exit 0, 5/5 build_readme tests + existing
  src/main.zig tests + lint pass.
- `zig fmt --check build.zig build.zig.zon build_readme.zig src/` -> 0.
- Mutation 1 (arity): weakened `if (rest.len != 2)` to `< 2` ->
  3/5 pass (the 3-arg & 4-arg rejection tests fail as required).
  Restored.
- Mutation 2 (prog_name): replaced `{s}`/`prog_name` with hard-coded
  "gen_readme" -> compile error (unused parameter), proving prog_name
  is load-bearing in the production code path. Restored.
- ziglint clean (Z004/Z023/Z026 all silent on the new code).
…n contract

WHY:
copilot-pull-request-reviewer thread PRRT_kwDOST2NKs6AThMy on src/main.zig
flagged `@constCast(file_content)` at the `Walk.addFile` call site
(walkStdLib, lines 404-406). `Walk.parse` writes a 0 sentinel into the
source buffer in place, so the slice must be mutable; `@constCast`-ing
a `[]const u8` to `[]u8` is unsound when the underlying memory is
genuinely const. The reviewer is correct: the call should rely on a
real mutable slice rather than launder constness.

WHAT:
1. Switch from `entry.dir.readFileAllocOptions(io, ..., .@"1", 0)` to
   `entry.dir.readFileAlloc(io, ...)`. The new call returns `[]u8`
   directly (the same form already used by walkBuildModules at lines
   314 and 338), so the `@constCast` becomes unnecessary and is
   removed. `Walk.parse` continues to handle the trailing-newline →
   sentinel rewrite identically — confirmed by reading
   src/Walk.zig:469-482.
2. Add a regression test in src/main.zig that pins the const-correctness
   contract via `@typeInfo`: `Dir.readFileAlloc` must return mutable
   `[]u8`, and `Walk.addFile`'s second parameter must remain `[]u8`.
   If either side ever flips to `[]const u8`, the test fails to
   compile / asserts, forcing the issue back into review instead of
   silently accepting a future `@constCast` re-introduction.

IMPACT:
- src/main.zig walkStdLib: identical runtime behavior (same bytes, same
  Walk.addFile call, same parse path), no `@constCast`.
- New test (1 case) added to the existing `exe_tests` step; 6 tests
  pass total now.
- No public API changes.

VALIDATION:
- `zig build test` -> exit 0; new test passes alongside the 5 R2
  build_readme tests.
- Mutation 1 (Walk.addFile contract): flipped second param to
  `[]const u8` -> the new test fails (1/1 fail), proving the contract
  pin is load-bearing. Restored.
- Mutation 2 (re-introduce @constcast): re-added the cast on the
  already-mutable slice -> tests still pass (cast is a no-op on
  mutable memory; introspection test still verifies the underlying
  contract). Restored.
- `zig fmt --check src/main.zig build.zig build.zig.zon
  build_readme.zig` -> exit 0.
@EugOT

EugOT commented May 7, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

R1 (cc43198): pinned ziglint via URL+hash against EugOT/ziglint @ fix/0.16-build-compat#40f26f12... — addresses 3 reproducibility threads.
R2 (8c0887d): gen_readme now rejects extra positionals and uses argv[0] in usage; 5 new unit tests wired into zig build test.
R3 (3e0a455): dropped @constCast in walkStdLib, switched to plain readFileAlloc, added type-introspection contract test.

All 6 prior threads resolved with mutation-validated fixes. zig build test exits 0 (lint + fmt + tests).

@coderabbitai

coderabbitai Bot commented May 7, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@EugOT, thanks for the detailed summary of the three fix commits! I'll kick off a full re-review of the PR to assess the current state across all changed files — including the ziglint pin, the gen_readme hardening, the test additions, and the walkStdLib / readFileAlloc cleanup.

[full_review]

@EugOT EugOT merged commit e2cb5fd into main May 7, 2026
2 checks passed
@EugOT EugOT deleted the fix/0.16-full-migration branch May 7, 2026 14:00
This was referenced Jun 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants