zig
Use when requests involve Zig code or toolchain work: editing `.zig`, changing `build.zig`/`build.zig.zon`, running or fixing `zig build|test|run|fmt|fetch`, debugging compile/runtime/test failures, comptime/reflection/codegen, allocator ownership, SIMD (`std.simd`/`@Vector`), threads (`std.Thread`/`Thread.Pool`), cross-compilation, zero-copy parsing, or C interop (`@cImport`). Enforce correctness-first validation with tests, `std.testing.fuzz`, and allocation-failure checks.
SKILL.md
| Name | zig |
| Description | Use when requests involve Zig code or toolchain work: editing `.zig`, changing `build.zig`/`build.zig.zon`, running or fixing `zig build|test|run|fmt|fetch`, debugging compile/runtime/test failures, comptime/reflection/codegen, allocator ownership, SIMD (`std.simd`/`@Vector`), threads (`std.Thread`/`Thread.Pool`), cross-compilation, zero-copy parsing, or C interop (`@cImport`). Enforce correctness-first validation with tests, `std.testing.fuzz`, and allocation-failure checks. |
name: zig description: "Use when implementing or reviewing Zig 0.15.2 code and toolchain workflows: editing .zig files, build.zig/build.zig.zon changes, zig build/test/run/fmt/fetch/lint commands, zlinter integration, comptime/reflection/codegen, allocator ownership and zero-copy parsing, C interop, and performance work (latency, throughput, profiling, SIMD, threading) that must preserve correctness with lint, fuzz, and allocation-failure checks."
Zig
Operating contract
- Assume Zig 0.15.2 unless the user explicitly requests another version.
- Treat
zlinter(zig build lint) as a required gate for Zig codebases. - Treat correctness as a hard gate before optimization and release work.
- Prefer minimal incisions with explicit proof signals.
- Keep fast paths benchmarked, but keep safety checks on during correctness validation.
Baseline requirements
- Confirm toolchain version first:
zig version
- If the version is not
0.15.2, stop and state the mismatch. - If
zig build lintis missing in the target repo, stop implementation and bootstrap lint first. - If the request is performance-focused, run in two lanes:
- Correctness lane (
DebugorReleaseSafe). - Performance lane (
ReleaseFast) only after correctness passes.
- Correctness lane (
Lint bootstrap (required when missing)
- Add
zlinterfor Zig 0.15.x:
zig fetch --save git+https://github.com/kurtwagner/zlinter#0.15.x
- Add a
lintstep inbuild.zig(all built-in rules baseline):
const zlinter = @import("zlinter");
// ...
const lint_cmd = b.step("lint", "Lint source code.");
lint_cmd.dependOn(step: {
var builder = zlinter.builder(b, .{});
inline for (@typeInfo(zlinter.BuiltinLintRule).@"enum".fields) |f| {
builder.addRule(.{ .builtin = @enumFromInt(f.value) }, .{});
}
break :step builder.build();
});
Core workflow
- State the contract: domain, invariants, error model, and complexity target.
- Confirm Zig version and required lint wiring (
zig build lint). - Run lint autofix pass (
zig build lint -- --fix). - Re-run strict lint gate with zero warnings (
zig build lint -- --max-warnings 0). - Build or identify a reference path before touching optimized code.
- Add or extend unit tests around edge cases and regressions.
- Run fuzz and allocation-failure checks for safety-sensitive paths.
- Optimize in order: algorithm -> data layout -> vectorization -> threading -> micro-tuning.
- Re-run lint and correctness gates after each optimization step.
- Report proof with exact commands and outcomes.
Lint gate (required)
- Every Zig implementation turn must include lint evidence.
- Default lint flow:
zig build lint -- --fixzig build lint -- --max-warnings 0
- If
--fixchanges code, review the diff before continuing. - Do not mark work complete if lint step is missing or failing.
Correctness gate (required)
- Every Zig change needs at least one correctness signal.
- For parsing, allocation, arithmetic, or safety-sensitive code:
std.testing.fuzzis required.std.testing.checkAllAllocationFailuresis required for allocator-using functions.
- Prefer differential fuzzing (optimized path vs reference path).
Standard correctness commands
# Project build/test
zig build
zig build test
# Single-file test
zig test src/main.zig
# Integrated fuzz path (requires a test step in build.zig)
zig build test --fuzz
Allocation-failure pattern
const std = @import("std");
fn parseWithAlloc(alloc: std.mem.Allocator, input: []const u8) !void {
_ = alloc;
_ = input;
}
test "allocation failure coverage" {
try std.testing.checkAllAllocationFailures(
std.testing.allocator,
parseWithAlloc,
.{"seed"},
);
}
Performance lane and $lift handoff
Use this lane when the request is about speed, latency, throughput, memory, or profiling.
- If you can run a workload: produce baseline + after numbers and bottleneck evidence.
- If you cannot run a workload: mark output
UNMEASUREDand provide exact commands. - Keep correctness gates before and after each performance change.
Minimal measured loop
# 1) correctness first
zig build test
# 2) baseline perf sample
zig build -Doptimize=ReleaseFast -Dtarget=native -Dcpu=native
# 3) variant perf sample (after one change)
zig build -Doptimize=ReleaseFast -Dtarget=native -Dcpu=native
When the request is a broader perf pass with explicit reporting format, apply $lift conventions (contract -> baseline -> bottleneck -> experiments -> result -> regression guard).
Build and project commands
# Initialize project
zig init
zig init --minimal
# Format
zig fmt src/main.zig
# Build and run
zig build
zig build run
zig build lint -- --fix
zig build lint -- --max-warnings 0
# Release-oriented build
zig build -Doptimize=ReleaseFast
# Cross-compile examples
zig build -Dtarget=x86_64-linux
zig build -Dtarget=aarch64-macos
# Cleanup
rm -rf zig-out zig-cache
Comptime and invariants
- Prefer compile-time invariant checks for shape, ABI, and required methods.
- Use
@compileErrorto make illegal states unrepresentable at build time. - Keep generated specialization knobs small and measurable.
Template
const std = @import("std");
fn assertHasRead(comptime T: type) void {
if (!std.meta.hasMethod(T, "read")) {
@compileError(@typeName(T) ++ " must implement read()");
}
}
Serde-style derive patterns
- Centralize reflection in one classifier/helper layer instead of scattering raw
@typeInfoswitches across serializers, deserializers, and adapters. - Guard
@hasDecland@hasFieldbehind type-shape checks before duck-typing or custom-hook detection;@hasDeclis not valid on every Zig type. - Let explicit custom hooks opt out of auto-derivation first, then fall back to a small
Kindor policy dispatcher. - Put rename, flatten, skip, tag, default, and
withpolicies in a per-typepub constoptions surface and query it at comptime; keep runtime loops limited to I/O and data movement. - Precompute wire names and naming-convention transforms at comptime so field matching and emission do not redo string work per value.
- Initialize flattened or defaulted sub-objects in dedicated helpers before parsing so the runtime path only fills observed fields and validates missing ones.
Safe decl template
const std = @import("std");
fn hasDeclSafe(comptime T: type, comptime name: []const u8) bool {
return switch (@typeInfo(T)) {
.@"struct", .@"union", .@"enum", .@"opaque" => @hasDecl(T, name),
else => false,
};
}
fn classify(comptime T: type) enum { auto, custom } {
if (hasDeclSafe(T, "serialize") or hasDeclSafe(T, "deserialize")) {
return .custom;
}
return .auto;
}
Zero-copy and ownership checklist
- Parse into spans/slices that point to stable backing storage.
- Do not return slices backed by temporary buffers.
- Make borrowed vs owned states explicit in API types.
- Fail fast on over-capacity streaming tokens instead of truncating.
SIMD and threading policy
- Use SIMD/threading only when profiling shows CPU-bound hot paths.
- Keep scalar fallback paths and deterministic behavior.
- Avoid hidden allocations inside hot loops.
- Re-run fuzz/tests after vectorization or parallelization changes.
macOS fuzz caveat
zig build test --fuzz may fail on macOS (InvalidElfMagic) in Zig 0.15.2.
If this occurs:
- State the local limitation explicitly.
- Keep
std.testing.fuzztargets in-tree. - Run fuzz on Linux/CI or an external harness.
- Add deterministic regression seeds under
testdata/fuzz/.
Trigger-audit workflow ($seq backed)
Use this to measure whether Zig intent is being routed to $zig.
# Run audit on full history
uv run python codex/skills/zig/scripts/zig_trigger_audit.py --root ~/.codex/sessions
# Time-windowed audit
uv run python codex/skills/zig/scripts/zig_trigger_audit.py \
--root ~/.codex/sessions \
--since 2026-02-01T00:00:00Z \
--strict-implicit \
--format json \
--output /tmp/zig-audit.json
# Regression tests for the audit script
uv run python codex/skills/zig/scripts/test_zig_trigger_audit.py
Notes:
- The audit uses literal
containsmatching to avoid regex parser limitations inseqfor dotted literals (for example.zig,build.zig,std.simd). - Keep strict-implicit mode enabled when evaluating precision-sensitive routing changes.
- Lint cues (
zig build lint,zlinter) are tracked as Zig intent.
Monthly drift scorecard
Generate a compact scorecard that combines:
$zigSKILL size.- Trigger-audit counts/rates.
- Routing-gap invoked-rate metrics for safe Zig cues.
- Recommendation hints when drift is detected.
uv run python codex/skills/zig/scripts/zig_ops_scorecard.py \
--root ~/.codex/sessions \
--since 2026-02-01T00:00:00Z \
--format text
skills-zig evidence lane
When validating guidance against current Zig production patterns, inspect:
- Source/build/release repo:
/Users/tk/workspace/tk/skills-zig - Formula propagation repo:
/Users/tk/workspace/tk/homebrew-tap
Recommended checks:
git -C /Users/tk/workspace/tk/skills-zig log --oneline --max-count=30
rg -n "std.testing.fuzz|checkAllAllocationFailures|FailingAllocator" /Users/tk/workspace/tk/skills-zig/apps -g"*.zig"
rg -n "std.simd|@Vector|std.Thread.Pool" /Users/tk/workspace/tk/skills-zig/apps -g"*.zig"
rg -n "@typeInfo|@hasDecl|@hasField|@Type|comptime" /Users/tk/workspace/tk/skills-zig/apps -g"*.zig"
Use these results to keep $zig guidance aligned with what is true in active Zig repos.
Pitfalls
- Claiming performance wins without measured baseline/after evidence.
- Running micro-optimizations before removing algorithmic waste.
- Skipping allocation-failure coverage in allocator-heavy code.
- Skipping
zig build lintor allowing warnings to pass. - Running
zig build lint -- --fixon a dirty tree without reviewing the resulting diff. - Scattering
@typeInfoand trait-probe logic across format-specific code instead of centralizing the classifier and policy layer first. - Treating borrowed memory as owned (or vice versa).
- Returning stack-backed slices.
- Assuming regex-like query patterns are portable across all tooling without validation.
References
- Differential fuzz template:
codex/skills/zig/references/fuzz_differential.zig - Type-shape dispatcher example:
codex/skills/zig/references/type_switch.zig @Typepartial-builder example:codex/skills/zig/references/partial_type.zig- Derive-walk policy pipeline:
codex/skills/zig/references/derive_walk_policy.zig