Problem
A generator needs a field wrapper distinguishing present-typed, present-but-wrong-type, explicit-null, and absent — and round-tripping unknown shapes. Tristate covers three states (absent/null/present) but has no raw/wrong-type escape hatch and no embedded JSON tree.
Proposed change
Design a dep-free sealed JsonField<T> (Known<T:Any> / Missing / Null / Raw(RawJson)) plus a RawJson tree (object/array/string/number/bool/null) in sdk-core, with all Jackson↔RawJson conversion behind the Serde SPI in the adapter — mirror how Tristate is split. Map Tristate onto it. Kotlin sealed class compiles to Java-8 bytecode (no permits).
Prior art: openai-java's JsonField/JsonValue (Values.kt) — but it welds Jackson onto the value type; we must not. Reuse the contextual-type-extraction trick from our TristateModule.
Acceptance
Foundation for the rest of the codegen items.
Dependencies
Priority: highest · Effort: large
Problem
A generator needs a field wrapper distinguishing present-typed, present-but-wrong-type, explicit-null, and absent — and round-tripping unknown shapes.
Tristatecovers three states (absent/null/present) but has no raw/wrong-type escape hatch and no embedded JSON tree.Proposed change
Design a dep-free sealed
JsonField<T>(Known<T:Any>/Missing/Null/Raw(RawJson)) plus aRawJsontree (object/array/string/number/bool/null) in sdk-core, with all Jackson↔RawJson conversion behind theSerdeSPI in the adapter — mirror howTristateis split. MapTristateonto it. Kotlinsealed classcompiles to Java-8 bytecode (nopermits).Prior art: openai-java's
JsonField/JsonValue(Values.kt) — but it welds Jackson onto the value type; we must not. Reuse the contextual-type-extraction trick from ourTristateModule.Acceptance
JsonField+RawJsonin coreTristateinterop documented, incl. the read-path (forward-compat) vs PATCH-path (three-state) boundary so the two are never wrongly mergedFoundation for the rest of the codegen items.
Dependencies
RawJsontreePriority: highest · Effort: large