Skip to content

feat: upstream SDK codegen and runtime foundations#4087

Open
hellovai wants to merge 15 commits into
canaryfrom
codex/upstream-sdk-codegen-foundations
Open

feat: upstream SDK codegen and runtime foundations#4087
hellovai wants to merge 15 commits into
canaryfrom
codex/upstream-sdk-codegen-foundations

Conversation

@hellovai

@hellovai hellovai commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Why this PR exists

This PR extracts the compiler, runtime, bridge, and wire-format work shared by every host SDK from the Go bridge work. It intentionally does not add a Go generator/runtime, Go tests, Go release configuration, or generated Go bindings.

The central problem is that a payload's runtime shape does not always identify its BAML type. That matters at host boundaries, especially callbacks, because the engine must distinguish the exact type chosen by the producer and validate it against the declared BAML signature.

function nested(value: (int[] | string[])[]) -> string
function state(value: "draft" | string) -> string

Both of these host values are ambiguous from payload shape alone:

await nested([[]])       // int[] or string[] for the empty child?
await state("draft")     // literal "draft" or broad string?

The final design uses one sparse type annotation on any inbound node that needs it, recursively threads contextual types through unannotated descendants, and uses one canonical selected-arm discriminator for outbound unions. The same PR also lowers class-method Self before SDK generation, centralizes boundary type matching, and completes the shared host-callable dispatch path.

Surface area

Area Change
Compiler/client IR Lower class-method Self to the owning concrete or generic class before language generators see it
Shared external values Represent runtime type identity, reflected type values, sparse inbound annotations, and canonical selected union arms without engine-only types
Inbound protobuf Add optional InboundValue.value_type; remove typed envelopes and container/class-local descriptors
Outbound protobuf Keep selected_option_index; remove the redundant selected_type field
Engine conversion Validate annotations against context and payload, then propagate effective types recursively through list items, map entries, and class fields
Boundary validation Share structural matching for literals, containers, classes/generics, enums, media, callables, optionals, unions, and baml.json.json
Host callbacks Preserve declared argument identity and validate return/throw contracts, cancellation, lifetime, and error paths
Producers/artifacts Update C++, Python, Rust, legacy TypeScript, and TypeScript2 protobuf producers/generated files

Suggested review order:

  1. baml_inbound.proto and baml_outbound.proto for the contract.
  2. bex_external_types for the shared value/type representation.
  3. bex_engine/src/conversion.rs for recursive contextual materialization.
  4. baml_project/src/client_codegen.rs for Self lowering.
  5. sys_native, sys_wasm, and bridge_cffi for callback dispatch/lifecycle.
  6. SDK producers, generated bindings, and tests.

1. Inbound: one sparse annotation on every value node

Final wire shape

message InboundValue {
  BamlTy value_type = 1; // optional by message presence

  oneof value {
    string string_value = 2;
    int64 int_value = 3;
    double float_value = 4;
    bool bool_value = 5;
    InboundListValue list_value = 6;
    InboundMapValue map_value = 7;
    InboundClassValue class_value = 8;
    InboundEnumValue enum_value = 9;
    BamlHandle handle = 10;
    bytes uint8array_value = 11;
    string bigint_value = 12;
    BamlTy ty_value = 13;
  }
}

message InboundListValue {
  repeated InboundValue values = 1;
}

message InboundMapValue {
  repeated InboundMapEntry entries = 1;
}

message InboundClassValue {
  reserved 1;
  repeated InboundMapEntry fields = 2;
}

This replaces all earlier, redundant type channels:

  • InboundTypedValue / InboundValue.typed_value;
  • InboundListValue.item_type;
  • InboundMapValue.key_type and value_type;
  • InboundClassValue.class_ty.

value_type always describes that node. It is never a second copy of the enclosing union. The normal form is exact; the one deliberate partial form is a generic class annotation that carries nominal class identity while omitting host-erased type arguments, described below.

Semantics

For each inbound node:

  1. Start with its declared/contextual type, if one exists.
  2. If value_type is present, require it to be assignable to that context.
  3. Require the payload to inhabit the annotated type.
  4. Use the resulting effective type as recursive context for list items, map values, and class fields.
  5. If no annotation is present, select from context plus payload shape. If multiple contextual arms remain valid, return a precise ambiguity error asking for value_type.

Annotations are therefore optional and sparse. Producers do not need an expensive pass that serializes a complete recursive type tree for every value. They emit a descriptor only where payload evidence and context cannot make the selection uniquely.

Typical annotation sites are:

  • empty containers inside overlapping union arms;
  • literal-vs-primitive selection;
  • classes or enums whose payload/name alone is ambiguous;
  • generic inference where the payload supplies no evidence;
  • a context-free host throw whose class identity must survive until a typed consumer receives it.

The canonical encoding is the least-annotated valid encoding.

Exact nested example

For a declared argument (int[] | string[])[], only the ambiguous empty child needs a hint:

list_value {
  values {
    value_type: int[]
    list_value {}
  }
  values {
    list_value {
      values { string_value: "hello" }
    }
  }
}

The outer list is known from the function parameter. The second child is known from its string payload. Serializing either type would be redundant. The first child's empty payload matches both union members, so its exact int[] annotation is necessary.

Literal example

For "draft" | string, the raw string payload matches both arms. This encoding selects the literal:

value_type: literal("draft")
string_value: "draft"

Without the annotation, conversion fails as ambiguous rather than silently choosing an arm. If the contextual type were only string, the same annotation remains valid because the literal is assignable to the broad primitive.

Class and generic behavior

A class field map does not need class_ty embedded in InboundClassValue. The declared class type becomes the context for the class node, and the class schema supplies each field's recursive context. A sparse node-level annotation is still available when there is no unique class context or when a concrete generic instantiation must be stated.

For example, an unannotated payload for Box<T> can use the realized Box<string> parameter context. An explicit Box<int> annotation in that same slot is rejected as conflicting instead of being ignored. Context-free throws retain their exact class annotation as a transient carrier until materialization, which preserves class identity without inventing a container-specific field.

TypeScript and Python have one important erased-generic case: the host can know that an object is a Box while not knowing its concrete T. After removing InboundClassValue.class_ty, dropping that name would also drop the only nominal discriminator. These producers therefore encode value_type = Box with zero type arguments as a sparse nominal hint. The engine may refine that hint against exactly one contextual Box<...> type, but it will not use it to choose between Box<int> | Box<string>; that genuinely ambiguous union requires concrete arguments. Generated non-generic class instances carry their exact class annotation for the same nominal-union reason.

Plain object literals remain unannotated. When selecting a class union arm, the engine compares their keys, required/optional fields, aliases, and recursively visible payload shapes against the loaded class definitions. Distinct structural arms such as CardPayment | WirePayment need no annotation; overlapping class shapes still do.

Reflected types remain values

ty_value is separate from value_type:

  • value_type describes an inbound node;
  • ty_value is the node's payload when a BAML type value itself is being passed as data.

This keeps reflection round-trippable without reducing type values to display strings.

2. Outbound unions: index is the only selected-arm identity

Final wire shape

message BamlValueUnionVariant {
  string name = 1;
  bool is_optional = 2;
  bool is_single_pattern = 3;
  BamlTy self_type = 4;
  string value_option_name = 5; // display only
  BamlOutboundValue value = 6;
  reserved 7;                   // former selected_type
  optional uint32 selected_option_index = 8;
}

The selected type is derived canonically:

selected_type = self_type.options[selected_option_index]

For an empty int[] selected from int[] | string[], the wire representation is conceptually:

union_variant_value {
  self_type: union([list(int), list(string)])
  selected_option_index: 0
  value: list([])
}

selected_type was removed because it could disagree with the index. The optional index preserves member order and equivalent/duplicate-arm position, while presence distinguishes arm zero from a missing discriminator. value_option_name remains display-only and is not type authority.

The nested value's extracted type is useful for compatibility validation and best-effort inference, but cannot identify the selected arm. A string payload cannot distinguish literal "draft" from broad string; an empty list cannot distinguish int[] from string[].

Decoders validate that the index is in bounds and that the nested payload is compatible with self_type.options[index].

3. Self is lowered once, in the compiler

Class methods previously exposed Self in SDK-facing IR. That would require every language generator to reconstruct the enclosing class and recursively substitute through optionals, lists, maps, unions, and generic arguments.

The compiler now performs that lowering while it owns the authoritative class context:

class Mirror {
  value string

  function clone(self, other: Self?) -> map<string, Self> {
    {"value": self}
  }
}

class GenericMirror<T> {
  value T

  function nested(self, other: Self?) -> map<string, Self[]> {
    {"value": [self]}
  }
}

The generator sees:

Mirror::Self            -> Mirror
GenericMirror<T>::Self  -> GenericMirror<T>
Self?                   -> Optional<OwningClass>
Self[]                  -> List<OwningClass>
map<string, Self>       -> Map<string, OwningClass>
Self | int              -> OwningClass | int

This applies to instance and static methods. The instance receiver remains excluded from public SDK arguments.

4. Shared boundary matching

The bridge and engine now use common runtime identity/compatibility rules instead of several payload-shape checks.

Case Behavior
Literal vs primitive Preserve an explicitly selected literal; otherwise reject multiple matching arms
Empty typed container Use sparse node annotation to select the arm; recursively use context elsewhere
Enum Match enum identity and exact variant before broad-enum compatibility
Class Use nominal identity when supplied; otherwise match loaded class field shape, then validate realized generic arguments and fields
Media Distinguish concrete media kinds while accepting generic media where declared
Reflected type Encode/decode through ty_value as BamlTy
Callable Structurally compare argument and return signatures
Optional/union Preserve canonical selected arm and tolerate the legacy root-optional runtime form
baml.json.json Accept only recursive JSON values; reject BAML-only values and non-finite floats

The accepted JSON algebra is null | bool | int | finite float | string | list<json> | map<string, json>. Big integer type literals also get pre-parse length and post-parse bit-width limits.

5. Host-callable boundary

When BAML invokes a registered host function, arguments are externalized using the realized callable signature, not just payload inference:

message BamlToHostArg {
  BamlOutboundValue value = 1;
  string arg_name = 2;
  bool is_optional_arg = 3;
}

The shared dispatch sequence is:

  1. Check cancellation before externalizing a potentially large argument pack.
  2. Read the realized HostClosure parameter types.
  3. Validate positional arity and optional argument names.
  4. Encode each value using its declared parameter type and canonical union index.
  5. Keep omitted optional arguments absent rather than synthesizing null.
  6. Dispatch through the native host-call registry.
  7. Decode returned or thrown inbound values with sparse annotations.
  8. Validate the result against the declared returns or throws type before resuming the VM.

Contract mismatches are host contract violations. Pointer/protobuf/transport failures remain bridge failures, so callers can distinguish an off-contract callback result from a broken protocol.

The lifecycle work also covers cancellation eviction, registry teardown, malformed pointer and is_error checks, GC-safe ownership of return/throw types across suspension, and deterministic release of late or accepted host-error handles. After merging current canary, the new C++ host-callable producer and the WASM host path use the same sparse node annotation.

6. SDK producer behavior

Generated class instances and user-defined typed wrappers can opt into value_type; ordinary arrays and plain objects remain minimally encoded. A generated class emits its nominal identity, plus concrete generic arguments only when the host actually has them.

For example, the TypeScript encoder conceptually emits:

{
  valueType: literalType("draft"),
  value: { $case: "stringValue", stringValue: "draft" },
}

Plain arrays and objects do not recursively serialize type descriptors. Only an ambiguous child needs a wrapper/annotation. Explicit enum markers remain validated, and malformed typed markers fail at the producer boundary.

Generated protobuf artifacts are updated for C++, Python, Rust, legacy TypeScript, and TypeScript2. Go generated bindings are intentionally excluded because this is the shared upstream-only PR; proto backward compatibility is not a design constraint for this work.

Validation

Local validation on the final canary-merged branch:

Suite Result
Full workspace pre-commit Clippy (--workspace --all-targets --all-features -- -D warnings) passed
Rust formatting, C++ formatting, merge markers, whitespace passed
bridge_ctypes, bex_external_types, bex_engine library suites 51 + 19 + 94 tests passed
Generic inbound/context integration suite 11 passed
Rust SDK bridge 25 unit + 13 conversion + 13 live-engine tests passed
Generated Rust type_shapes SDK all 70 round-trip tests passed against the rebuilt C-FFI engine
C-FFI 29 unit tests passed; ABI layout and generated-header drift passed
C++ protobuf drift passed; regeneration-only tests ignored by design
Generated C++ type_shapes SDK all 85 tests passed against the rebuilt C-FFI engine
Legacy TypeScript bridge rebuilt native module; 112 tests passed, including 20 host-callable tests
TypeScript sparse-annotation unit tests 7 passed
Generated TypeScript type_shapes SDK all 128 tests passed against the rebuilt Node bridge
TypeScript2 bridge 27 tests passed
Generated Python type_shapes SDK all 108 tests passed against the rebuilt Python bridge

Focused conversion tests cover the exact nested empty-list example above, unannotated empty-container ambiguity, literal-vs-primitive ambiguity, exact annotation/payload mismatch, recursive aliases, nominal generic refinement, ambiguous concrete generic arms, recursive class/generic propagation, conflicting type arguments, outbound index derivation, and malformed/out-of-range selection.

Commit structure

The feature commits remain separated by shared concern:

  1. feat(bridge): preserve canonical selected union arms
  2. feat(codegen): lower class Self before SDK generation
  3. feat(runtime): enforce shared boundary type matching
  4. feat(runtime): preserve declared host callback arguments
  5. feat(bridge): generalize inbound typed values
  6. fix(bridge): address union boundary review findings
  7. refactor(bridge): use sparse inbound type annotations
  8. merge current canary and reconcile its C++/WASM host-callable paths
  9. align context-free host-throw assertions with sparse annotation carriers
  10. make unordered union identity preserve duplicate multiplicity, update the direct Rust fixture initializer, and canonicalize duplicate-equivalent inbound/outbound matches
  11. complete recursive alias/context propagation, nominal erased-generic refinement, structural plain-object class selection, and exact Python media annotations

Deliberately out of scope

  • Go SDK generator/runtime code, Go tests, Go release configuration, and generated Go bindings.
  • Host-language-specific guessing of ambiguous union arms.
  • Requiring every inbound value to be recursively self-describing.
  • Broader decisions about how recursively self-describing generated outbound Go values should be; this PR only settles canonical selected-union identity.

The result is one shared contract: compiler-lowered types, sparse inbound evidence, recursively applied context, canonical outbound union selection, and the same callback validation semantics for every SDK.

Summary by CodeRabbit

  • New Features

    • Added support for preserving exact inbound type metadata, including literals, generic classes, media, and union selections.
    • Union values now retain reliable selected-arm information across language bridges and host callables.
    • Self in class methods now resolves to the owning class, including nested generic types.
  • Bug Fixes

    • Improved coercion and validation for sparse annotations, empty containers, enums, JSON values, and generic instances.
    • Host-call cancellation and resource cleanup are handled more reliably.
    • Invalid oversized bigint literals now produce clear errors.

@cursor

cursor Bot commented Jul 18, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
beps Ready Ready Preview, Comment Jul 21, 2026 12:23am
promptfiddle Ready Ready Preview, Comment Jul 21, 2026 12:23am
promptfiddle2 Ready Ready Preview, Comment Jul 21, 2026 12:23am

Request Review

@github-actions

Copy link
Copy Markdown

⏭️ Performance benchmarks were skipped

Perf benchmarks (CodSpeed) are opt-in on pull requests — they no longer run on every push. They always run automatically after merge to canary/main.

To run them on this PR, do any of the following, then push a commit (or re-run CI):

  • Add RUN_CODSPEED=1 to the PR description, or
  • Include run-perf or /perf in the PR title or any commit message.

@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f9f45c00-4e6e-4acf-bf5f-ef2faa60f78b

📥 Commits

Reviewing files that changed from the base of the PR and between e2646ad and 46093c5.

⛔ Files ignored due to path filters (3)
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.d.ts.map is excluded by !**/dist/**, !**/*.map
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.js is excluded by !**/dist/**
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.js.map is excluded by !**/dist/**, !**/*.map
📒 Files selected for processing (7)
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_engine/tests/media_roundtrip.rs
  • baml_language/sdks/python/src/baml_bridge/proto.py
  • baml_language/sdks/python/tests/test_proto_generics.py
  • baml_language/sdks/typescript/bridge_typescript/tests/test_typemap.test.ts
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • baml_language/sdks/python/tests/test_proto_generics.py
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts
  • baml_language/crates/bex_engine/src/lib.rs

📝 Walkthrough

Walkthrough

This change adds Self-aware class method codegen and revises typed inbound values, union-arm discrimination, literal preservation, protobuf bindings, SDK serializers, host-call conversion, cancellation, and host-release lifecycle handling.

Changes

Typed bridge and codegen flow

Layer / File(s) Summary
Self-aware class method lowering
baml_language/crates/baml_project/src/client_codegen.rs
Class method parameters and returns resolve Self to the owning class, including generic and nested cases.
Union identity and contextual conversion
baml_language/crates/bex_external_types/*, baml_language/crates/bex_engine/src/conversion.rs
Union matching uses selected metadata, structural equality, exact literals, enum variants, contextual containers, media kinds, and JSON validation.
Typed protobuf contract
baml_language/crates/bridge_ctypes/types/.../*.proto, baml_language/crates/bridge_ctypes/src/*
Inbound values gain sparse value_type metadata, outbound unions gain selected_option_index, and literal decoding preserves exact identity.
Generated bindings and SDK serializers
baml_language/sdks/{cpp,python,rust,typescript}/..., typescript2/pkg-proto/*
Generated bindings and SDK encoders use revised inbound class metadata and outbound union discriminator fields.
Host-call runtime lifecycle
baml_language/crates/bex_engine/src/lib.rs, baml_language/crates/bridge_cffi/src/ffi/host_value.rs, baml_language/crates/bex_engine/tests/*
Host-call parameters are resolved from closure contracts, cancellation is checked before sys-op conversion, releases drain at FFI return, and union-callable behavior is tested.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Host
  participant Bridge
  participant BexEngine
  participant VM
  Host->>Bridge: send value with sparse type metadata
  Bridge->>BexEngine: decode typed value and selected union arm
  BexEngine->>VM: coerce contextual payload and execute call
  VM->>BexEngine: return typed union or host result
  BexEngine->>Bridge: encode canonical selected option index
  Bridge->>Host: deliver converted value
Loading

Possibly related PRs

  • BoundaryML/baml#3571: Both changes modify host-callable conversion and host-return validation paths.
  • BoundaryML/baml#3680: Both changes modify VM host-call argument and throw handling.
  • BoundaryML/baml#4045: This change extends the Rust bridge wire protocol with sparse inbound type metadata and canonical union-arm indexes.

Suggested reviewers: sxlijin

Poem

A rabbit hops through unions bright,
With Self resolved just right.
Typed bridges carry clues,
Empty lists choose news,
And host calls land light.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is broad, but it accurately reflects the PR’s main theme of SDK codegen and runtime foundation work.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/upstream-sdk-codegen-foundations

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 18, 2026

Copy link
Copy Markdown

Binary size checks passed

7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
baml-cli Linux 🔒 25.3 MB 10.7 MB file 25.3 MB +58.6 KB (+0.2%) OK
packed-program Linux 🔒 17.1 MB 7.0 MB file 17.0 MB +41.2 KB (+0.2%) OK
baml-cli macOS 🔒 19.6 MB 9.3 MB file 19.5 MB +66.3 KB (+0.3%) OK
packed-program macOS 🔒 13.3 MB 6.2 MB file 13.2 MB +49.8 KB (+0.4%) OK
baml-cli Windows 🔒 21.2 MB 9.6 MB file 21.1 MB +74.2 KB (+0.4%) OK
packed-program Windows 🔒 14.2 MB 6.3 MB file 14.1 MB +47.8 KB (+0.3%) OK
bridge_wasm WASM 16.2 MB 🔒 4.4 MB gzip 4.4 MB +14.0 KB (+0.3%) OK

🔒 = the size this artifact is GATED on (ceiling + delta). Binaries gate on file size (installed binary); WASM gates on gzip (download size). The other size is shown for information only.


Generated by cargo size-gate · workflow run

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/sdks/rust/bridge_rust/src/baml_value.rs (1)

219-223: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Populate collection type descriptors instead of always sending None.

These implementations know T, K, and V, but every Rust collection decodes with fallback metadata. For example, Vec<String> becomes an array typed with the scalar fallback, while non-string maps default to string keys.

Expose a wire-type descriptor through the value/key traits and emit Some(...) for all three implementations. Add unit coverage for empty collections.

As per coding guidelines, "Prefer writing Rust unit tests over integration tests where possible."

Also applies to: 260-271, 296-307

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/sdks/rust/bridge_rust/src/baml_value.rs` around lines 219 -
223, The Rust collection implementations of __BamlValuePrivate for Vec, HashMap,
and BTreeMap currently send item_type/key_type/value_type as None. Extend the
relevant value/key traits with wire-type descriptor support, use T/K/V to emit
Some(...) descriptors in all three implementations, and add Rust unit tests
covering empty collections and their descriptors.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/bex_engine/src/conversion.rs`:
- Around line 919-928: Update the BexExternalValue::Union branch in
convert_external_to_vm_value_with_ty to validate metadata.selected_option
against the declared expected_ty union and verify that value inhabits the
selected arm before materializing it. Reuse the existing membership and payload
validation logic from coerce_arg_to_declared_type, rejecting malformed arms or
callable signatures before the recursive conversion.
- Around line 2239-2304: The union coercion logic does not recognize function
arms or callable host values. Extend runtime_ty_structurally_equal to compare
RuntimeTy::Function signatures recursively, and update value_matches_type to
accept the HostValue/FunctionRef representations for function types. Add a
regression test covering a host-selected callback in a union and preserve
existing matching behavior for other arms.
- Around line 2033-2067: Update both shared and schema-aware host-return
validators to mirror the matching rules used by runtime_ty_compatible: compare
float literals with float_literal_matches, recursively validate list and map
element/key/value descriptors even when containers are empty, and require the
returned media kind to match the declared type. Preserve exact literal checks
for other scalar values so mismatched literals, empty string arrays, and audio
returned for image declarations are rejected.

In `@baml_language/crates/bridge_ctypes/src/ty_decode.rs`:
- Around line 256-261: Update the BigintValue branch in the literal decoding
logic to reject decimal strings exceeding the existing value-level bigint length
limit before calling parse_bytes or formatting the input. Return a length-only
CtypesError for oversized values, while preserving the current invalid-format
error for bounded inputs.

In `@baml_language/crates/bridge_ctypes/src/value_decode.rs`:
- Around line 95-129: Update convert_union_variant and the corresponding
list/map decoding paths to validate each decoded payload recursively against its
wire-declared RuntimeTy, not only union membership. Ensure selected_type,
item_type, key_type, and value_type reject mismatched values before constructing
BexExternalValue containers or unions, while preserving valid nested values. Add
Rust unit tests covering mismatched union, list, and map payloads.

---

Outside diff comments:
In `@baml_language/sdks/rust/bridge_rust/src/baml_value.rs`:
- Around line 219-223: The Rust collection implementations of __BamlValuePrivate
for Vec, HashMap, and BTreeMap currently send item_type/key_type/value_type as
None. Extend the relevant value/key traits with wire-type descriptor support,
use T/K/V to emit Some(...) descriptors in all three implementations, and add
Rust unit tests covering empty collections and their descriptors.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e7abb70f-b98f-4744-aab8-627f95646797

📥 Commits

Reviewing files that changed from the base of the PR and between 972fdd2 and edf8aad.

⛔ Files ignored due to path filters (2)
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.d.ts is excluded by !**/dist/**
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.js is excluded by !**/dist/**
📒 Files selected for processing (31)
  • baml_language/crates/baml_project/src/client_codegen.rs
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • baml_language/crates/bex_external_types/src/host_return.rs
  • baml_language/crates/bex_external_types/src/lib.rs
  • baml_language/crates/bridge_cffi/src/ffi/host_value.rs
  • baml_language/crates/bridge_ctypes/README.md
  • baml_language/crates/bridge_ctypes/src/error.rs
  • baml_language/crates/bridge_ctypes/src/ty_decode.rs
  • baml_language/crates/bridge_ctypes/src/ty_encode.rs
  • baml_language/crates/bridge_ctypes/src/value_decode.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_inbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_outbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_type.proto
  • baml_language/crates/sys_native/src/host_dispatch.rs
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.h
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.h
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.pyi
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.pyi
  • baml_language/sdks/rust/bridge_rust/src/baml_value.rs
  • baml_language/sdks/rust/bridge_rust/src/wire/baml_bridge.cffi.v1.rs
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.d.ts
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.js
  • typescript2/pkg-proto/src/encode.ts
  • typescript2/pkg-proto/src/test/encode-decode.test.ts

Comment thread baml_language/crates/bex_engine/src/conversion.rs
Comment thread baml_language/crates/bex_engine/src/conversion.rs Outdated
Comment thread baml_language/crates/bex_engine/src/conversion.rs Outdated
Comment thread baml_language/crates/bridge_ctypes/src/ty_decode.rs Outdated
Comment thread baml_language/crates/bridge_ctypes/src/value_decode.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/bridge_ctypes/src/value_encode.rs`:
- Around line 366-387: The selected option lookup in selected_union_option_index
currently uses strict RuntimeTy equality; replace the members.iter().position
comparison with the existing structural union-member equality helper used by
selected_arm_equal/runtime_ty_structurally_equal. Preserve the current
not-member error and u32 index conversion behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cb915954-7a05-482f-a70c-affbdaf2b612

📥 Commits

Reviewing files that changed from the base of the PR and between edf8aad and de6eadb.

⛔ Files ignored due to path filters (2)
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.d.ts is excluded by !**/dist/**
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.js is excluded by !**/dist/**
📒 Files selected for processing (32)
  • baml_language/crates/baml_project/src/client_codegen.rs
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • baml_language/crates/bex_external_types/src/bex_external_value.rs
  • baml_language/crates/bex_external_types/src/host_return.rs
  • baml_language/crates/bex_external_types/src/lib.rs
  • baml_language/crates/bridge_cffi/src/ffi/host_value.rs
  • baml_language/crates/bridge_ctypes/README.md
  • baml_language/crates/bridge_ctypes/src/error.rs
  • baml_language/crates/bridge_ctypes/src/ty_decode.rs
  • baml_language/crates/bridge_ctypes/src/ty_encode.rs
  • baml_language/crates/bridge_ctypes/src/value_decode.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_inbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_outbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_type.proto
  • baml_language/crates/sys_native/src/host_dispatch.rs
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.h
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.h
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.pyi
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.pyi
  • baml_language/sdks/rust/bridge_rust/src/baml_value.rs
  • baml_language/sdks/rust/bridge_rust/src/wire/baml_bridge.cffi.v1.rs
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.d.ts
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.js
  • typescript2/pkg-proto/src/encode.ts
  • typescript2/pkg-proto/src/test/encode-decode.test.ts
🚧 Files skipped from review as they are similar to previous changes (20)
  • baml_language/crates/bridge_ctypes/src/error.rs
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_type.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_outbound.proto
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.pyi
  • baml_language/crates/bex_external_types/src/lib.rs
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.py
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • typescript2/pkg-proto/src/encode.ts
  • baml_language/crates/bridge_ctypes/src/ty_encode.rs
  • baml_language/crates/bridge_cffi/src/ffi/host_value.rs
  • baml_language/sdks/rust/bridge_rust/src/baml_value.rs
  • baml_language/crates/bridge_ctypes/src/ty_decode.rs
  • baml_language/crates/sys_native/src/host_dispatch.rs
  • baml_language/crates/bex_external_types/src/host_return.rs
  • baml_language/crates/baml_project/src/client_codegen.rs
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.py
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.h
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.cc

Comment thread baml_language/crates/bridge_ctypes/src/value_encode.rs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@baml_language/crates/bex_external_types/src/runtime_ty_identity.rs`:
- Around line 72-79: Update the T::Union branch of runtime_ty_structurally_equal
to require every member in both unions to have a structurally equal counterpart
in the other union, while retaining the length check. Ensure duplicate-member
cases produce the same result regardless of operand order.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9741d196-0a3c-4943-b752-a4bffa9d463e

📥 Commits

Reviewing files that changed from the base of the PR and between de6eadb and 35ded35.

📒 Files selected for processing (7)
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/crates/bex_external_types/src/lib.rs
  • baml_language/crates/bex_external_types/src/runtime_ty_identity.rs
  • baml_language/crates/bex_project/src/lib.rs
  • baml_language/crates/bridge_ctypes/src/error.rs
  • baml_language/crates/bridge_ctypes/src/ty_decode.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • baml_language/crates/bridge_ctypes/src/ty_decode.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs
  • baml_language/crates/bex_engine/src/conversion.rs

Comment thread baml_language/crates/bex_external_types/src/runtime_ty_identity.rs Outdated
@blacksmith-sh

This comment has been minimized.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts (1)

243-263: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Missing fqn guard for generic class instances — inconsistent with sibling encode paths.

The non-generic class branch (Lines 219-233) and setInboundTypedThrowValue (Lines 913-930) both guard with if (fqn) { ...; return; }, falling back to a safer encoding when the class isn't registered in the type map. This generic-instance branch skips that guard and unconditionally returns iv.valueType = { classTy: { name: fqn, typeArgs } } even when fqn is falsy, producing a malformed wire message (empty/undefined class name) instead of falling through to the plain-map encoding used elsewhere for this exact situation.

🐛 Proposed fix
         if (isClassInstance) {
             const params = genericParamNames(value);
             if (params) {
                 const fqn = getTypeMap().jsTypeToBamlType((value as object).constructor);
+                if (fqn) {
                 const userTypes = (value as { $types?: Record<string, BamlType> }).$types;
                 const typeArgs = params.map((p) => lowerTypeToWireTy(userTypes?.[p]));
                 const classFields: baml_bridge.cffi.v1.IInboundMapEntry[] = [];
                 for (const [k, v] of Object.entries(value)) {
                     if (typeof v === 'function') continue;
                     if (k === '$types') continue;
                     const childVal: baml_bridge.cffi.v1.IInboundValue = {};
                     setInboundValue(childVal, v, ctx);
                     classFields.push({ stringKey: k, value: childVal });
                 }
                 iv.valueType = { classTy: { name: fqn, typeArgs } };
                 iv.classValue = { fields: classFields };
                 return;
+                }
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts`
around lines 243 - 263, Guard the generic class-instance encoding in the
isClassInstance branch with the resolved fqn from getTypeMap().jsTypeToBamlType,
matching the non-generic branch and setInboundTypedThrowValue. Only assign
classTy/classValue and return when fqn is present; otherwise fall through to the
existing plain-map encoding.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_inbound.proto`:
- Around line 66-69: Update the InboundClassValue message to reserve tag 3 in
addition to the existing reserved tag 1, preserving field 2 and preventing reuse
of the removed class_ty field number.

---

Outside diff comments:
In `@baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts`:
- Around line 243-263: Guard the generic class-instance encoding in the
isClassInstance branch with the resolved fqn from getTypeMap().jsTypeToBamlType,
matching the non-generic branch and setInboundTypedThrowValue. Only assign
classTy/classValue and return when fqn is present; otherwise fall through to the
existing plain-map encoding.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 812d5a58-c349-4b88-8624-f9b8b9fa7052

📥 Commits

Reviewing files that changed from the base of the PR and between 35ded35 and e2646ad.

⛔ Files ignored due to path filters (5)
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.d.ts.map is excluded by !**/dist/**, !**/*.map
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.js is excluded by !**/dist/**
  • baml_language/sdks/typescript/bridge_typescript/dist/proto.js.map is excluded by !**/dist/**, !**/*.map
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.d.ts is excluded by !**/dist/**
  • baml_language/sdks/typescript/bridge_typescript/dist/proto/baml_cffi.js is excluded by !**/dist/**
📒 Files selected for processing (37)
  • baml_language/crates/bex_engine/src/conversion.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_engine/tests/generics_explicit.rs
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • baml_language/crates/bex_external_types/src/bex_external_value.rs
  • baml_language/crates/bex_external_types/src/host_return.rs
  • baml_language/crates/bex_external_types/src/runtime_ty_identity.rs
  • baml_language/crates/bridge_cffi/src/ffi/host_value.rs
  • baml_language/crates/bridge_ctypes/src/value_decode.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_inbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_outbound.proto
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_type.proto
  • baml_language/crates/bridge_wasm/tests/host_callable.rs
  • baml_language/crates/sys_wasm/src/host_value.rs
  • baml_language/sdk_tests/crates/rust/type_shapes/customizable/roundtrip_tests/test_primitives.rs
  • baml_language/sdks/cpp/bridge_cpp/include/baml/detail/host_value.h
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_inbound.pb.h
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.cc
  • baml_language/sdks/cpp/bridge_cpp/pb/baml_bridge/cffi/v1/baml_outbound.pb.h
  • baml_language/sdks/cpp/sdkgen_cpp/src/lib.rs
  • baml_language/sdks/python/rust/bridge_python/src/host_value.rs
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.pyi
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.pyi
  • baml_language/sdks/python/src/baml_bridge/proto.py
  • baml_language/sdks/python/tests/test_proto_generics.py
  • baml_language/sdks/rust/bridge_rust/src/baml_value.rs
  • baml_language/sdks/rust/bridge_rust/src/encode.rs
  • baml_language/sdks/rust/bridge_rust/src/wire/baml_bridge.cffi.v1.rs
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto.ts
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.d.ts
  • baml_language/sdks/typescript/bridge_typescript/typescript_src/proto/baml_cffi.js
  • typescript2/pkg-proto/src/encode.ts
  • typescript2/pkg-proto/src/test/encode-decode.test.ts
🚧 Files skipped from review as they are similar to previous changes (9)
  • baml_language/crates/bridge_ctypes/types/baml_bridge/cffi/v1/baml_type.proto
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_inbound_pb2.py
  • baml_language/sdks/python/src/baml_bridge/cffi/v1/baml_outbound_pb2.py
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • baml_language/crates/bex_external_types/src/host_return.rs
  • baml_language/crates/bridge_cffi/src/ffi/host_value.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_external_types/src/runtime_ty_identity.rs
  • baml_language/crates/bridge_ctypes/src/value_encode.rs

@hellovai

Copy link
Copy Markdown
Contributor Author

Final validation for 46093c57b is complete.

The final GitHub Actions matrix has 35 successful jobs and no non-Go implementation failures. In particular, core Cargo tests pass on Linux/macOS/Windows/WASM; C++, Python Pydantic 2, Rust, TypeScript, and TypeScript Web SDK jobs pass on every configured platform; pre-commit, docs, snapshots, webview, and size gates pass.

The only intentional failures are the upstream-only boundary documented in this PR:

  • Go SDK tests on Linux, macOS, and Windows;
  • proto generated files sync, whose output contains only these three omitted Go generated bindings:
    • sdks/go/bridge_go/cffi/proto/baml_bridge/cffi/v1/baml_inbound.pb.go
    • sdks/go/bridge_go/cffi/proto/baml_bridge/cffi/v1/baml_outbound.pb.go
    • sdks/go/bridge_go/cffi/proto/baml_bridge/cffi/v1/baml_type.pb.go
  • the aggregate CI failure-alert job, which reflects the intentional failures above.

Those Go files remain deliberately excluded because this is the shared upstream foundations PR; the Go bridge/generator/runtime work stays in its downstream PR.

Additional local focused verification on the final implementation included:

  • bex_engine library: 94 passed;
  • canonical media integration test: passed;
  • generated Python type_shapes: 108 passed;
  • generated TypeScript type_shapes: 128 passed;
  • generated C++ type_shapes: 85 passed;
  • workspace Clippy with warnings denied: passed.

@hellovai

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant