How the Zig↔Python binding layer works (pyzig), including build-on-import, wrapper generation patterns, ownership rules, and where to add new exported APIs. Use when adding Zig-Python bindings, modifying native extensions, or debugging C-API interactions.
cd ~/.claude/skills
git clone https://github.com/atopile/atopile.git atopile mkdir -p ~/.claude/skills/pyzig
curl -fsSL https://raw.githubusercontent.com/atopile/atopile/HEAD/.claude/skills/pyzig/SKILL.md \
-o ~/.claude/skills/pyzig/SKILL.md pyzig is the Zig↔Python interoperability layer used by Faebryk’s native modules (graph, sexp, faebryk typegraph, …).
There are three distinct layers to keep straight:
src/faebryk/core/zig/__init__.py (build-on-import + .pyi syncing)src/faebryk/core/zig/build.zig (builds pyzig.so + pyzig_sexp.so, generates stubs)src/faebryk/core/zig/src/pyzig/* (wrapper generation + minimal C-API surface)ato dev compile
python -c "import faebryk.core.zig; import faebryk.core.graph"
src/faebryk/core/zig/__init__.py (ZIG_NORECOMPILE, ZIG_RELEASEMODE, lock, stub syncing)src/faebryk/core/zig/build.zig (builds extensions + runs .pyi generator)src/faebryk/core/zig/src/pyzig/pybindings.zig (minimal CPython C-API declarations)src/faebryk/core/zig/src/pyzig/pyzig.zig (wrapper generation helpers)src/faebryk/core/zig/src/pyzig/type_registry.zig (global type-object registry)src/faebryk/core/zig/src/pyzig/pyi.zig (stub generation helpers)src/faebryk/core/zig/src/python/graph/graph_py.zigsrc/faebryk/core/zig/src/python/sexp/sexp_py.zigsrc/faebryk/core/zig/src/python/graph/*src/faebryk/core/zig/src/python/sexp/*src/faebryk/core/zig/src/python/faebryk/* (and friends)wrap_in_python(...) / wrap_in_python_simple(...).PyTypeObjects for the same Zig type (type_registry).__init__ (by default): many “reference” types are not meant to be user-constructed; pyzig often installs an init that raises.__zig_address__() to help debug pointer identity.src/faebryk/core/zig/src/pyzig/*src/faebryk/core/zig/src/python/**ato dev compile (imports faebryk.core.zig; editable installs compile-on-import)ZIG_RELEASEMODE=ReleaseFast|ReleaseSafe|Debug as neededsrc/faebryk/core/zig/gen/** gets updated (this is driven by src/faebryk/core/zig/__init__.py)python -m faebryk.core.graph (GraphView allocation/cleanup stress)ato dev test --llm test/core/solver (heavy user of graph + bindings via many subsystems).destroy() vs tp_dealloc calling .deinit()).free(...) path and document it.tp_dealloc that calls deinit..pyi surface accurate; many callers rely on types for navigation.src/faebryk/core/zig/__init__.py is responsible for:
ZIG_NORECOMPILE=1)pyzig.so and pyzig_sexp.so from src/faebryk/core/zig/zig-out/lib/.pyi files into src/faebryk/core/zig/gen/** (black + ruff)You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when receiving code review feedback, before implementing suggestions, especially if feedback seems unclear or technically questionable - requires technical rigor and verification, not performative agreement or blind implementation
Use when completing tasks, implementing major features, or before merging to verify work meets requirements
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback
Use when starting any conversation - establishes how to find and use skills, requiring skill invocation before ANY response including clarifying questions