# Alef Generated Bindings

> Alef-managed generated bindings in packages/* and binding crates — the regeneration workflow (task alef:generate / alef:verify), the alef.toml section layout, the core-side edits that break a regen, and the FFI bridge's JSON marshalling requirement. Load before editing anything under packages/* or a binding crate, before adding a trait method or extractor, or when regenerating or verifying Alef output.

- Skill: `xberg-io/alef-generated-bindings` (Agent Skill)
- Install (CLI): `npx skillmds add xberg-io/alef-generated-bindings`
- Raw SKILL.md: https://api.skillmd.com/api/skills/xberg-io/alef-generated-bindings/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: xberg-io (https://skillmd.com/u/xberg-io)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/xberg-io/alef-generated-bindings

---


# Alef-Generated Bindings

Files under `packages/*/` and in the binding crates are generated by Alef — do not hand-edit.

## Workflow

1. Check `alef.toml` before touching anything in `packages/*/` or a binding crate
2. Modify the Rust source or `alef.toml` itself
3. `task alef:generate` → `alef all --clean`. This formats via poly as part of the run; there
   is no `task alef:format`
4. `task alef:verify` → `alef verify --exit-code`
5. `task e2e:generate` then `task e2e:test` (or `task e2e:all`) to verify behaviour
6. Commit Rust source + `alef.toml` + regenerated output atomically

The `alef:*` namespace is exactly: `generate`, `verify`, `build`, `sync`, `docs`,
`verify:readme-paths`, `verify:ffi-formats`. Formatting is `poly fmt` / `poly lint`.

## Freshness check

`task alef:verify`. A hand-rolled `git diff` over `packages/` is under-scoped — generated
output also lands in `crates/xberg-py/`, `crates/xberg-php/`, `crates/xberg-jni/`,
`crates/xberg-node/`, `crates/xberg-wasm/`, `crates/xberg-ffi/`, `packages/dart/rust/`,
`packages/swift/rust/`, and `e2e/`.

Alef tracks both sides under `.alef/`: `sources_hash.cache` (input hashes over the parsed
Rust sources) and `.alef/hashes/xberg.<lang>.output_hashes` (per-file output content hashes).
Neither makes a generated file safe to hand-fix — a regen restores it. Fix `alef.toml` or the
Rust source, then regenerate and re-verify.

## Key `alef.toml` sections

- `[workspace]`, `[workspace.sync]` (version-sync `extra_paths`), `[workspace.docs]`,
  `[workspace.poly]`, `[workspace.generate]`
- `[[crates]]` — the Rust source crate parsed for type/function extraction, plus
  `[[crates.source_crates]]`
- `[crates.<lang>]` — one table per binding: `python`, `node`, `ruby`, `php`, `elixir`,
  `wasm`, `ffi`, `go`, `java`, `dart`, `kotlin_android`, `jni`, `swift`, `csharp`, `zig`
  (each carrying `exclude_functions`, `target_dep_overrides`, stubs, …)
- `[crates.e2e]` — e2e generation (`output = "e2e"`, `[crates.e2e.call.overrides.<lang>]`)
- `[crates.readme]` — README generation (`template_dir = "templates/readme"`,
  `[crates.readme.languages.<lang>]`)
- `[crates.exclude]`, `[crates.output]`, `[crates.custom_modules]`, `[crates.publish]`

There are no `[crate]`, `[languages.*]`, `[e2e]` or `[readme]` top-level tables, and
`alef.toml` declares **no rename mappings** — the generated Go trait type is
`DocumentExtractor`, unchanged.

Canonical e2e tasks: `task e2e:generate`, `e2e:build`, `e2e:test`, `e2e:all`, plus
`e2e:verify`, `e2e:lint`, `e2e:quick`, `e2e:lang`. Do not add legacy aliases.

## Core-side edits that break a regen

- **New extractor struct** — needs `#[cfg_attr(alef, alef(skip))]` on the struct declaration,
  not the impl block. Without it the regen aborts globally and nothing regenerates, leaving
  the tree silently stale. Check this first when a regen dies. (36 of the 42 modules in
  `crates/xberg/src/extractors/` carry the attribute.)
- **Binding-facing types must not be `#[non_exhaustive]`** — alef generates
  `impl From<Mirror> for xberg::TheType` with a struct literal in ~10 binding crates, and
  `#[non_exhaustive]` forbids that (`E0639`). Derive `Default` for forward-compat instead.
  Keep `#[non_exhaustive]` only on types listed in `[crates.exclude]`.
- **A new enum variant on a binding-facing type** breaks exhaustive matches across the
  generated binding crates.

## Trait return types crossing the generated FFI bridge

The generated `XbergOcrBackendBridge` in `crates/xberg-ffi/src/lib.rs` marshals **every**
trait method's return value through JSON (218 `serde_json::from_str::<xberg::…>` call sites
today) and falls back to `Default::default()` on an uninitialised vtable slot, a failing host
callback, or a null result. Whether a variant carries a payload is irrelevant.

Adding a method to a trait with a generated bridge means its return type needs
`Default + Serialize + Deserialize`. Unit-only enums included.

`crates/xberg-ffi` is built by no `ci-rust.yaml` leg — only publish-path jobs — so
`cargo check --workspace` cannot fail on it. Verify with the scoped `cargo check -p xberg-ffi`.
That check rewrites `crates/xberg-ffi/include/xberg.h` and `packages/go/include/xberg.h` to
match whatever feature set you invoked it with, which is usually not the committed one. Revert
that churn rather than committing it.

## Which alef answered

`task alef:generate` shells out to the globally installed `~/.cargo/bin/alef`, not a
`cargo run` against a sibling checkout. A source fix there has zero effect until
`cargo install --path . --force`. Generator fixes ship in order: land upstream → release alef
→ bump the pinned version → regen. Regenerating against a locally modified alef produces a
tree the pinned version cannot reproduce.

