feature-flag-conventions
GitHub定义 Rust Cargo 特性标志的命名与语义规范,统一 tracing、otel、mcp 等共享关注点,确保跨库行为一致并管理依赖关系。
Trigger Scenarios
Install
npx skills add xberg-io/xberg --skill feature-flag-conventions -g -y
SKILL.md
Frontmatter
{
"name": "feature-flag-conventions",
"type": "Playbook",
"paths": [
"**\/Cargo.toml",
"scripts\/ci\/check-feature-parity.py"
],
"title": "Feature Flag Conventions",
"x-ai-rulez": {
"id": "feature-flag-conventions",
"kind": "skill",
"metadata": {
"name": "feature-flag-conventions"
}
},
"description": "Edit Cargo features: ORT-free targets, aggregates, PDF backends and parity."
}
Feature-flag conventions
Shared concerns use the same flag name with the same semantics in every library. A consumer that learns otel or mcp once should get identical behavior across every library and its bindings. Repo-specific flags stay local — this contract governs only shared concerns.
Standard flags
| Flag | Meaning | Default | Notes |
|---|---|---|---|
(none) tracing |
Structured tracing is always-on, never a feature. | — | tracing is a non-optional dependency. Do not gate span emission. Un-gate any legacy optional tracing feature. |
otel |
Opt-in OpenTelemetry / OTLP export layer, on top of always-on tracing. | off | Pulls tracing-opentelemetry + the opentelemetry* stack. This is the only observability feature. Reconcile any always-on OTLP deps behind it. |
mcp |
Embedded Model Context Protocol server (stdio transport). | off | |
mcp-http |
MCP Streamable HTTP transport; implies mcp. |
off | mcp-http = ["mcp", ...]. |
mimalloc |
Select the mimalloc global allocator. | off | Mutually exclusive with jemalloc; see the allocator rule below. |
jemalloc |
Select the jemalloc global allocator. | off | Mutually exclusive with mimalloc. |
cuda / metal / mkl / accelerate |
Device / BLAS acceleration backends where a crate exposes them. | off | Same names wherever acceleration is offered. |
Rules
- Names are the contract. Never invent a synonym for a shared concern (
opentelemetry,observability,otlpfor what isotel;model-context-protocolformcp). Rename divergent flags to match. - Semantics travel with the name.
otelmust mean the OTLP export layer everywhere, not "enable tracing" (tracing is always-on) and not a metrics-only subset. - Always-on tracing, opt-in export. Do not force the
opentelemetrydependency tree on library consumers — keep it behindotelso a plain dependency stays lean. - Implication edges are fixed:
mcp-http⇒mcp;otel⇒ (always-on) tracing. - Mutually-exclusive allocators carry a both-enabled
compile_error!guard by default. Exception: a crate that must build with--all-features(a coverage gate,cargo doc --all-features) resolves by precedence instead,jemallocwinning viacfg(all(feature = "mimalloc", not(feature = "jemalloc"))), and states why in a~keepcomment at the#[global_allocator]. - Adding a shared flag to a new library? Copy the name, default, and implication edges from this table rather than redefining them. Coordinate a change to this contract across all libraries before landing it — no repo diverges unilaterally.
Xberg features
All Xberg features are in crates/xberg/Cargo.toml.
ORT-Incompatible Targets (WASM, Android x86_64 emulator)
Only ORT-dependent paths are incompatible. The same paths block both WASM (no native ORT linkage at all) and the x86_64-linux-android emulator triple (no pyke prebuilt; aarch64-linux-android does ship a prebuilt and gets full ORT):
paddle-ocr— ONNX Runtime + native C++ deps: not WASM-safe; no Android x86_64 prebuiltlayout-detection— depends on ONNX Runtime layout models: not WASM-safe; no Android x86_64 prebuilt (RT-DETR + table classifier are available off-ORT via thelayout-tractsibling, below)embeddings— depends on ONNX Runtime sentence-transformer models: not WASM-safe; no Android x86_64 prebuiltauto-rotate— depends on ONNX Runtime orientation classifier: not WASM-safe; no Android x86_64 prebuilt (available off-ORT via theauto-rotate-tractsibling, below)formula-recognition— LaTeX recognition for layout formula regions (RapidLaTeXOCR ONNX): ORT-dependent, opt-in, excluded fromfull/formats
Pure-Rust type-only companion features expose the public config/result types for the above without pulling in ORT:
layout-types—LayoutDetectionConfig,TableModel,BBox,DetectionResult,LayoutClass,LayoutDetection,RecognizedTable.layout-detectionimplieslayout-types.auto-rotate-types—OrientationResult.auto-rotateimpliesauto-rotate-types.embedding-presets—EmbeddingPreset(already existed; pure-Rust preset metadata).
Pure-Rust tract inference variants run select ONNX models where native ORT cannot link, loading the
same .onnx artifacts through the tract engine (CPU-only, no native library):
layout-detection(ORT) →layout-tract(tract): RT-DETR layout detection + the PP-LCNet wired/wireless table classifier run on WASM and the Android x86_64 emulator. TATR, SLANeXT, PP-DocLayout-V3, and YOLO stay ONNX Runtime-only (tract 0.23.4 op-coverage gaps).auto-rotate(ORT) →auto-rotate-tract(tract): PP-LCNet document-orientation on WASM and the Android x86_64 emulator.
WASM/Android-safe variants:
ocr(native) →ocr-wasm(usestesseract-wasm+ safe image deps) — Android keeps nativeocrexcel(native) →excel-wasm(dropstokio-runtime) — Android keeps nativeexceltree-sitter(native dlopen) →tree-sitter-wasm(statically-linked grammar pack). NOT inwasm-target: the 371-language grammar pack pushes the browser.wasmpast jsDelivr's 50 MB per-file cap, breaking the CDN-hosted demo. WASM has no code intelligence; Android keeps nativetree-sitter.liter-llm— NOT inno-ort-target: tokenizers → hf-hub → ureq → rustls + socks are wasm-incompatible. Enabled explicitly byandroid-targetandfull; WASM has no LLM pathstopwords— pure-Rust, included inno-ort-targetkeywords— pure-Rust YAKE/RAKE, included inno-ort-target
The no-ort-target aggregate is the shared no-ORT base used by both wasm-target and android-target. wasm-target = no-ort-target + excel-wasm + ocr-wasm + layout-tract + auto-rotate-tract + ner-candle-wasm (NO tree-sitter — see above). android-target adds to no-ort-target: excel, tree-sitter, ocr, sceptre-ocr-tract, paddle-ocr-tract, layout-tract, auto-rotate-tract, api, mcp, liter-llm, structured, hwpx, diff, classification, captioning, ner-llm, url-ingestion. Read the manifest before assuming a member — both lists are edited often.
PDF Backend
pdf— the canonical PDF feature. It is a pure alias:pdf = ["pdf-native"], andpdf-nativecarries the dependency set (dep:xberg-native-pdf, lopdf, image, flate2, html, image-encode). There is no feature literally namedxberg-native-pdf. Included informats,full,no-ort-target, andwasm-target.pdf-pdfium— the pdfium FFI comparison backend (crates/xberg-pdfium-render). Impliestokio-runtimebecause the pdfium calls are synchronous and must run underspawn_blocking. Opt-in; in no aggregate. Without it, selecting the pdfium backend fails loudly rather than falling back.
Config value, not feature: PdfConfig.backend (core/config/pdf.rs) parses exactly "native" and "pdfium". The pre-1.1.0 spelling "pdf_oxide" is rejected, not aliased — deliberately, so it cannot survive in configs. Omitting the field defaults to Native.
ORT Variants (Mutually Exclusive)
ort-bundled— downloads official Microsoft ORT binaries; default when OCR/ML features activeort-dynamic— load ORT from system; only when system ORT is guaranteed present
Platform-Conditional
xberg-paddle-ocr,hf-hub,pprof— excluded onwasm32ureq:rustlson non-Windows;native-tlson Windowsxberg-ffiandxberg-dartcargo dependencies are target-conditional:cfg(target_os = "android")(both ABIs —aarch64andx86_64) andcfg(target_os = "ios")selectandroid-target, the pure-Rust tract ML surface with no ONNX Runtime;cfg(target_os = "windows")selectswindows-target(full ORT ML viaort-bundled);cfg(all(target_os = "macos", target_arch = "x86_64"))selectsmacos-intel-target. All other targets (Linux, macOS arm64) get the full ORT-enabled feature set. Android and iOS run inference throughtract, not ORT, on every ABI — there is no arch split that gives arm64 Android phones a native ORT build.
Aggregate Sets
| Feature | Description |
|---|---|
formats |
Document formats only: pdf, excel, office, notebook, hwp, hwpx, iwork, email, html, xml, archives, mdx, svg, heic, wordperfect. No api/mcp/otel, no chunking, no OCR, no ML |
analysis |
language-detection, chunking, quality, keywords, markdown-footnotes, diff |
services |
api, mcp, otel |
full |
formats + analysis + services + url-ingestion + every OCR backend + every ML feature |
formats-no-heic / full-no-heic |
Same lists minus heic (and minus candle for full-no-heic), for targets with no libheif / no aarch64 fullfp16 |
no-ort-target |
Pure-Rust base: every capability that does not depend on ONNX Runtime |
wasm-target |
no-ort-target + excel-wasm + ocr-wasm + layout-tract + auto-rotate-tract + ner-candle-wasm (no tree-sitter — grammar pack exceeds the CDN 50 MB cap) |
android-target |
no-ort-target + the native-Linux/tract set (see above); for the x86_64-linux-android emulator |
windows-target |
Curated Windows host set: full minus heic (no libheif on the runner) |
windows-gnu-target |
windows-target minus everything reaching ORT — pyke ships no gnu-ABI prebuilt |
macos-intel-target |
["full-no-heic", "ort-dynamic"] — no osx-x86_64 ORT prebuilt exists past 1.23 |
mobile |
formats + analysis + ocr + tree-sitter + api-types + tokio-runtime |
Member lists change often. Read crates/xberg/Cargo.toml before relying on any of these
descriptions for a specific feature.
CI parity guard (GH#1443)
mise run verify:feature-parity runs
python3 scripts/ci/check-feature-parity.py crates/xberg/Cargo.toml.
It compares only code-bearing features — those whose definition pulls a dep:, or whose
name appears in a #[cfg(feature = "…")] under crates/xberg/src. Pure aliases drop out
while their members are still compared, so the rule cannot manufacture a false pass.
Guarded pairs, with the deltas each subset is allowed:
| Superset | Subset | May lack |
|---|---|---|
full |
windows-target |
heic |
formats |
formats-no-heic |
heic |
full |
full-no-heic |
heic + candle |
full |
macos-intel-target |
heic + candle |
full |
windows-gnu-target |
heic + candle + ORT-dependent |
android-target and no-ort-target are deliberately unguarded (UNGUARDED in the
script): they substitute the tract stack or drop ORT outright, so encoding their deltas would
turn the check into a change-detector that fires on every legitimate edit.
Consequence: adding a feature to full without adding it to windows-target fails CI.
Build Profiles
release— LTO thin, codegen-units=1, stripprofiling— inherits release, retains debug infoxberg-wasmoverride:opt-level="z"(size-optimized)sevenz-rust2override:opt-level=1— not 2. The manifest comment is explicit: "opt-level=2 still SIGBUS'd in go:e2e". Raising it reintroduces the crashzipoverride:opt-level=2(prevents SIGBUS on macOS ARM64)
Version History
- 844bb91 Current 2026-11-03 09:36


