v0.19.2
Fixed
-
alef-backend-swift: honour the full set of
serde_rename_allstrategies (lowercase,UPPERCASE,PascalCase,kebab-case) in the unit-enum raw-value emitter and the tagged-Codable variant-tag emitter so generated Swift round-trips match serde's wire format. Bothunit_enum_raw_value(src/backends/swift/gen_bindings/mod.rs) and the twoserde_rename_allmatcharms insideemit_serde_tagged_codableonly handledsnake_case,camelCase, andSCREAMING_SNAKE_CASE. Hitting#[serde(rename_all = "lowercase")](e.g.EmbeddingFormat::{Float,Base64}) fell through to_ => variant.name.clone()and emittedcase float = "Float"— Swift then refused to decode"float"from the JSON wire (Cannot initialize EmbeddingFormat from invalid String value 'float'). Added explicit arms forlowercase(variant.name.to_lowercase()),UPPERCASE(to_uppercase()), andPascalCase/kebab-case(which were missing from the tagged-Codable emitter even thoughunit_enum_raw_valuealready handled them). Importedheck::ToKebabCaseat module level so both call sites can reach it. Surfaced astestEmbedEncodingFormatfailure on liter-llm swift e2e run 26363741106. (src/backends/swift/gen_bindings/mod.rs) -
alef-e2e-codegen-{csharp,go,kotlin_android,swift,php,r,ruby,rust,zig,dart,python,typescript}: wire the
test_backendarg-type dispatcher into each language'sCallConfig::argswalker, plus drive Plugin super-trait lifecycle methods (name/version/initialize/shutdown) from the IR'sMethodDef.trait_source == bridge_cfg.super_traitfilter instead of hardcoded literals. Phase 1 added theemit_test_backenddispatcher and Phase 2 filled in per-language stub emitters, but neither phase wired the dispatcher into the actual args walker, so generated tests emittedregister_X()with zero args. Each language'srender_test_function/build_argsnow matchesarg.arg_type == "test_backend", resolves the matchingTraitBridgeConfig, looks up the trait'sMethodDeflist viaTraitBridgeConfig::resolve_methods, and dispatches toemit_test_backend— the returnedsetup_blockprepends to the test's setup_lines and thearg_expris used as the call argument. Separately, the C# and Go emitters previously hardcoded literalName/Version/Initialize/Shutdownmethod stubs whensuper_trait.is_some(); both now filtermethodsfor entries whoseMethodDef::trait_source == bridge_cfg.super_trait.as_deref()so the super-trait method list is driven from the IR (and any rename ofPluginsurvives without a codegen edit). (src/e2e/codegen/{csharp,dart,go,java,kotlin_android,php,python,r,ruby,rust,swift,typescript,zig}/...rs,src/core/config/e2e.rs) -
alef-backend-swift: emit a
singleValueContainer-based customCodableconformance for#[serde(untagged)]data-variant enums so JSON round-trips through serde's untagged wire shape (bare value), not Swift's auto-derived{"variant": payload}form. Untagged enums (EmbeddingInput,ModerationInput,UserContent,RerankDocument,StopSequenceetc.) used to fall through to the auto-derivedCodablepath on Swift. Swift's compiler-synthesisedinit(from:)for enum data variants assumes externally-tagged shape — decoding"hello"(a bare string) againstcase single(field0: String)fails withtypeMismatch(Dictionary<String, Any>, … Expected to decode Dictionary<String, Any> but found a string instead). Surfaced as 19 runtimeXCTestfailures across the swift e2e Embed/Moderate suites on liter-llm CI run 26363182895. The newemit_serde_untagged_codablehelper insrc/backends/swift/gen_bindings/mod.rsemits a manualinit(from:)that walks the variants in declaration order and triestry? container.decode(T.self)for each payload type — first match wins, mirroring serde's untagged deserialiser; anencode(to:)companion writes the payload directly viasingleValueContainer().encode(value). The dispatcher inemit_enumnow branches:has_serde_tag→emit_serde_tagged_codable,serde_untagged && single-field variants→emit_serde_untagged_codable, else the existing auto-derived path. Multi-field positional variants (zero in the current corpus) fall through and would need further work — gated via the.fields.len() == 1check. (src/backends/swift/gen_bindings/mod.rs) -
alef-backend-jni,backend-kotlin-android: remove special-casing of trait-bridge register shim name extraction and bridge class name. The JNI register shim unconditionally called
jni_call_string_method(env, impl_obj, "name", ...)regardless of whether the bridge had asuper_trait(which providesname()on the impl). For bridges withoutsuper_trait, the Kotlin side now passesnameas an explicit JString parameter instead of asking the impl to provide it. Updatedemit_trait_register_shimto accepthas_super_trait: booland conditionally emit either a single-arg signature (impl) for super_trait bridges or a two-arg signature (impl + name JString) for non-super_trait bridges. On the Kotlin side,gen_trait_bridge_objectnow accepts abridge_class_nameparameter (derived fromconfig.namein the caller atgen_bindings.rs:emit_trait_interfaces) instead of hardcodingKreuzbergBridge, allowing non-kreuzberg consumers (e.g. liter-llm) to use the generated bridges with their own bridge class names. Updated all seven test cases inkotlin_android::trait_bridge::teststo pass"TestBridge"and verify bridge-class-name substitution in assertions. All calls togen_trait_bridge_objectin actual codegen (bothgen_bindings.rsand test fixures) now pass the computed bridge class name. (src/backends/jni/gen_shims.rs,src/backends/kotlin_android/trait_bridge.rs,src/backends/kotlin_android/gen_bindings.rs) -
alef-backend-kotlin-android,backend-swift: replace hardcoded
kreuzberg::rust_path prefix in trait-bridge test helpers withtestcrate::. Themake_trait_defhelpers insrc/backends/kotlin_android/trait_bridge.rsandsrc/backends/swift/gen_bindings/trait_bridge.rsusedformat!("kreuzberg::{}", name)for the rust_path of synthetic trait definitions, coupling tests to the kreuzberg domain. Changed totestcrate::so test assertions remain domain-neutral and future consumers can run the same tests with their own crate names. (src/backends/kotlin_android/trait_bridge.rs,src/backends/swift/gen_bindings/trait_bridge.rs) -
alef-backend-jni: emit jni 0.22-compatible JNI shims with RuntimeMethodSignature parsing, borrow semantics, and unsafe blocks. The
jni_call_string_methodhelper emitted bysrc/backends/jni/gen_shims.rshad five misalignments with jni 0.22.4 API: (1) passed raw&strdirectly toget_method_idwhich requiresAsRef<MethodSignature>(jni 0.22 removed the string-direct path); (2) used the wrong pathjni::objects::ReturnType::Object(moved tojni::signature::ReturnType); (3) calledJString::from_raw(ptr)with one arg, but jni 0.22 requires two:JString::from_raw(env, ptr)for lifetime binding; (4) movedobjby passing it tocall_method_unchecked, but it was already moved byget_object_class(obj)— both expect references in jni 0.22; (5) called unsafe functioncall_method_uncheckedwithout anunsafeblock. Fix: (1) parsemethod_sig: &strtoRuntimeMethodSignatureviaFromStr, then call.method_signature()to get theMethodSignaturerequired byget_method_id; (2) use the correctjni::signature::ReturnType::Objectpath; (3) pass bothenvandresult.into_raw()toJString::from_raw; (4) borrowobjwith&objin bothget_object_classandcall_method_uncheckedcalls; (5) wrapcall_method_uncheckedinunsafeblock with SAFETY comment explaining the method_id validity. Blocks all Kotlin Android trait-bridge bindings (jni-rs is JNI layer, kreuzcrawl is the test bed). (src/backends/jni/gen_shims.rs) -
alef-backend-swift: route tagged-data enums through
JSONDecoderlike untagged ones so generated Swift FFI inits compile. The struct-field init emitter inswift_ffi_read_expr(src/backends/swift/gen_bindings/mod.rs) only short-circuited Named enum fields onto theJSONDecoderpath when the enum was#[serde(untagged)]. Tagged enums —#[serde(tag = "type")](internally tagged, e.g. liter-llmOcrDocument) and the default external-tag form with data variants (e.g. liter-llmAuthHeaderFormat::ApiKey(String)) — bridge as JSONRustStringat the swift-bridge boundary too, but the codegen fell through totry {Name}(rb.field())which expects aninit(_ rb: RustBridge.{Name}Ref) throwsinitializer that swift-bridge never emits for enum types. Swift reportedmissing argument label 'from:'andargument type 'RustString' does not conform to expected type 'Decoder'. Collapsed the two separatetagged_enum_names/untagged_enum_namessets into a single Codable-data-enum set (kept theuntagged_enum_nameslegacy name so call sites at lines 1148/1151/1165/1186/1208/1213 keep working) by dropping the!e.serde_untaggedfilter — any Codable enum with at least one data variant now uses theJSONDecoderdecode path. Also fixes the closure signature in the Vec map emitters: theRustVec<RustString>iterator yieldsRustStringRef, notRustString, so(s: RustString) -> {name} inbecomes(s: RustStringRef) -> {name} inands.toString()becomess.as_str().toString()(mirroring theVec<String>mapper above). Surfaced as 10 hardswift builderrors in liter-llm'sRerankRequest/OcrRequest/CustomProviderConfig/ModerationRequestinits after the v0.19.1 regen. (src/backends/swift/gen_bindings/mod.rs) -
alef-backend-csharp,backend-zig: expose
type_mapaspub(crate)so the e2e codegen helpers added in### Added(Phase 2emit_test_backendimpls) canuse crate::backends::{csharp,zig}::type_map::…withoutE0603: module is private. The Go backend already exposed itstype_mapaspubfor the same reason; C# and Zig were the remaining holdouts. No external API change —pub(crate)keeps the symbols invisible outside the crate. (src/backends/csharp/mod.rs,src/backends/zig/mod.rs) -
alef-backend-kotlin-android: scope the
ToSnakeCaseheck import to the test module so non-test builds (cargo build --release) do not emitunused_importswarnings. Production code intrait_bridge.rsonly usesToUpperCamelCase; the tests useto_snake_casefor syntheticTraitBridgeConfigfixtures. Moveduse heck::ToSnakeCase;intomod teststo makecargo clippy -D warningsandcargo build --releaseclean. (src/backends/kotlin_android/trait_bridge.rs)
Added
-
alef-backend-{swift,kotlin-android}: extract canonical bridge-name helpers (
swift::naming::bridge_protocol_name,kotlin_android::naming::bridge_object_name) so production wrapper codegen and e2e stub emitter share one source of truth. Bothformat!("Swift{trait}Bridge", ...)(swift) andformat!("{trait}Bridge", ...)(kotlin) were duplicated at the production site and the e2e emitter site, risking silent drift if either ever renames. Both sites now call the new helpers. (src/backends/swift/naming.rs,src/backends/kotlin_android/naming.rs,src/backends/swift/gen_bindings/trait_bridge.rs,src/e2e/codegen/swift.rs,src/e2e/codegen/kotlin_android.rs) -
alef-scaffold: emit a root-level
.gitattributesmarking all generated output directories aslinguist-generated=true. Runningalef scaffoldnow produces a.gitattributes(create-once seed; not overwritten on re-scaffold unless--cleanis passed) that collapses generated files in GitHub PR diffs. Coverage:packages/{lang}/**for language-native packages;crates/{name}-py/**,crates/{name}-php/**,crates/{name}-ffi/**,crates/{name}-jni/**for Rust binding crates that sit alongside their package directories;crates/{name}-node/**(resolvescrate_diroverride, bypassesscaffold_output);packages/kotlin-native/**orpackages/kotlin-mpp/**for non-JVM Kotlin targets; and the configured[e2e] outputdirectory (defaulte2e/). (src/scaffold/mod.rs)