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.
Installs into .claude/skills of the current project.
Are you the author of Alef Generated Bindings?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/xberg-io-alef-generated-bindings)
---
name: alef-generated-bindings
description: 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.
---
# 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.