Skip to content

refactor(tracing): trim the event-stream surface for a fresh start#3616

Draft
hellovai wants to merge 10 commits into
canaryfrom
hellovai/trim-events
Draft

refactor(tracing): trim the event-stream surface for a fresh start#3616
hellovai wants to merge 10 commits into
canaryfrom
hellovai/trim-events

Conversation

@hellovai

@hellovai hellovai commented May 31, 2026

Copy link
Copy Markdown
Contributor

Status: DRAFT for handoff. Subtractive refactor that removes the existing event-stream / tracing surface so observability can be rebuilt from a clean slate. The VM perf work this branch was originally bundled with has since landed separately (see below), so the diff here is now almost entirely deletions (~440 added / ~8,900 removed).

What this PR does

Trims ("strips") the current BEX event/tracing system down to nothing, intentionally not preserving any existing contract (protobuf-FFI wire, Collector/EventSink API, JSONL), so a unified always-on observability stream can be built fresh on top of a clean base.

Removed / gutted

  • bex_events internals: event_store.rs, serialize.rs, types.rs, most of collector.rs.
  • bex_events_native — entire crate deleted.
  • bridge_ctypes/event_encode.rs; bridge_cffi host-span plumbing; baml_lsp_server/playground_event_sink.rs.
  • VM: the SpanNotification type + VmExecState::SpanNotify path, the NotifyBlock / VizEnter / VizExit opcodes, and per-call span emission (gated on callee.trace).
  • bex_engine: the SpanNotify handling arm and collector wiring (BexEngine::new / bex_project event-sink params).
  • Tests/fixtures: rust event_system.rs + tracing.rs, python test_tracing.py + test_collector.py, nodejs SDK test_tracing/test_collector, and the event_system compile fixtures + snapshots.

Kept on purpose

  • baml.events.send() still compiles and returns null — the SendEvent opcode is now a no-op (pops its two args, pushes null), so existing BAML referencing it doesn't break.
  • The VM watch/notify variable machinery (Watch/Notify/StoreVarLoadVar) — that's separate from tracing.

Why a clean slate: today BEX has two observability shapes and neither can profile a BAML program — bex_events is opt-in per @trace, deep-copies every payload, and funnels every event through one process-global Mutex<CollectorStore>; native CPU samplers see worker frames but never the logical BAML function. The replacement design (a per-worker lock-free ring, always-on enter/exit/error, a .bamlprof artifact) deliberately changes every contract, so the old surface is removed first. Full spec lives in bex-event-stream-design.md (currently a local doc — see "For the next owner").

Relationship to the VM perf work (already on canary)

The CPython-aligned VM changes that the original title mentions — LoadVar2/StoreVar2 movement superinstructions, lazy faulting PC, unchecked local-slot indexing, call-path deref consolidation, and the in-process kperf PMC probe — were split out and merged to canary in #3646. They are not in this diff; this branch is rebased on top of them and inherits them. This PR is now purely the event trim (+ the perf-comparison tool below).

Perf finding — trimming events is also a small runtime win

Measured with the tool below (Apple Silicon, tracing off, compute workloads), this branch vs canary:

metric result meaning
VM ops identical same bytecode for non-event programs — zero change in VM work
instructions retired ~3% higher misleading codegen artifact, not work
CPU cycles ~4% lower (faster) IPC 5.22 → 5.60

Removing the event arms/opcodes gives LLVM a leaner, higher-ILP dispatch loop, so it completes in fewer cycles despite retiring slightly more instructions. Consistent ~3–9% cycle wins on the heavy, low-variance workloads (closure-apply, fib32-recursive, fib-iterative, collatz, bubble-sort). Lesson baked into the tooling: compare cycles, not instructions — instructions-retired is confounded by codegen when comparing two different binaries.

How to run the perf tests we added

The VM carries an in-process Apple-Silicon PMC probe at crates/bex_vm/src/kperf.rs (from #3646):

  • Cycles + instructions retired: activated at runtime by BAML_KPERF=1 + sudo (PMC access). Printed to stderr at process exit. Works in any normal release build.
  • VM ops + per-op breakdown: additionally needs the kperf cargo feature (it gates a per-op counter — a store on the hottest path, hence off by default).

scripts/kperf_compare.py is a standalone uv script (stdlib only) that A/Bs two baml-cli builds: it packs the same workload with each, runs both under the probe, and prints instructions / cycles / VM ops tables plus a per-op IPC summary that auto-diagnoses stalls vs codegen vs genuinely-faster.

# 1. build BOTH worktrees with the op counter.
#    NB: workspace root, NO -p — bex_vm is a transitive dep so `-p ... --features bex_vm/kperf` is rejected.
cargo build --release --features bex_vm/kperf

# 2. compare, under sudo, on Apple Silicon:
sudo uv run scripts/kperf_compare.py \
  --a /path/to/canary/baml_language/target/release/baml-cli --a-label canary \
  --b target/release/baml-cli                               --b-label trim-events \
  --workload-dir tools/speedtest/workloads/compute --runs 5

Reading the output:

  • Identical VM ops ⇒ any instruction/cycle delta is pure codegen, not work.
  • Δcycles ≈ Δinstructions ⇒ cost is the retired instructions; Δcycles worse than Δinstructions (IPC down) ⇒ stalls (branch mispredicts / I-cache — confirm with Instruments → CPU Counters: BRANCH_MISPRED_NONSPEC, L1I); more instructions yet fewer cycles ⇒ genuinely faster.
  • Cycles are non-deterministic — use --runs 5+ (instructions are exact, fine at 1 run). Rows flagged ~ exceeded 1% run variance; * are below the probe's ~1e6 print resolution. Workloads live in tools/speedtest/workloads/ (.md files with a ## BAML section; the script extracts it).

Validation

  • cargo check --workspace clean; bex_vm / bex_engine / baml_compiler2_emit clippy clean.
  • Snapshots regenerated and verified (lib codegen, baml_src, bytecode_format).
  • Branch is caught up with canary (merged in twice during this work).

For the next owner

  • The fresh-start spec — bex-event-stream-design.md — and its gap review — bex-event-stream-design-GAPS.md — are committed in this PR (repo root). They define what replaces the trimmed surface (unified lock-free event stream → .bamlprof).
  • The python/nodejs SDK tracing + collector tests were deleted; the SDK tracing API needs redefining against the new design before those SDKs expose tracing again.
  • SendEvent is a deliberate no-op placeholder; the new stream should give it real semantics (or remove the opcode).

🤖 Generated with Claude Code

…resh start

Rips out the old observability surface ahead of rebuilding it (see
bex-event-stream-design). No behavior is preserved — tracing is inert
after this change; the goal is a clean slate that still compiles.

Deleted:
- bex_events_native crate (JSONL/stderr EventSink publisher)
- bex_events: event_store (Mutex<CollectorStore>), serialize/JSONL,
  RuntimeEvent/EventKind/SpanContext/FunctionLog, SpanId
- bridge_ctypes event_encode.rs (protobuf-FFI event wire)
- baml_lsp_server playground_event_sink.rs + FanOutEventSink
- Rust suites bex_engine/tests/{tracing,event_system}.rs and the
  python/node tracing+collector test suites

Rewired to keep the workspace + SDKs compiling:
- EventSink/event_sink param dropped from BexEngine/BexProject/new_lsp
  and every caller; C-FFI flush_events() is now a no-op (ABI symbol kept)
- HostSpanManager kept but inert (depth tracker, no sink)
- SDK Collector/FunctionLog/Timing/Usage/LLMCall kept as no-op shims so
  the python/node public surfaces still build

VM-layer span plumbing (current_span_context, Function.trace, SpanNotify)
is deliberately left intact and handled as no-ops to avoid touching the
hot exec loop; it will be revisited in the rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@vercel

vercel Bot commented May 31, 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 Jun 9, 2026 5:49pm
promptfiddle Ready Ready Preview, Comment Jun 9, 2026 5:49pm
promptfiddle2 Ready Ready Preview, Comment Jun 9, 2026 5:49pm

Request Review

@coderabbitai

coderabbitai Bot commented May 31, 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
📝 Walkthrough

Walkthrough

Removes tracing/event infrastructure across the workspace. BexEngine/VM drop span notifications and event sinks. Compiler/bytecode eliminate visualization and custom-event instructions. Bridges, SDKs, LSP, CLI, and tests are updated accordingly. Workspace adopts forked AWS/Google crates and adjusts dependencies.

Changes

Event-system removal and related wiring

Layer / File(s) Summary
Event/tracing system removed and codepaths refactored
crates/* (engine, VM, compiler, SDKs, bridges, LSP, CLI, tests), Cargo.tomls
Deletes event sinks, span notifications, and visualization opcodes; simplifies engine/VM and call context; updates compiler/MIR; rewires CLI/LSP/bridges/SDKs; removes related tests; updates workspace and dependencies.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Poem

I tucked my traces in a clovered hide,
No more footprints, no more tide—
Just hops through bytecode, light and spry,
Engines hum, the spans don’t cry.
With whiskers twitching, I refactor the run—
Quiet carrots compiled, and benchmarks done. 🥕

✨ 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 hellovai/trim-events

@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.

Caution

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

⚠️ Outside diff range comments (2)
baml_language/crates/bex_engine/src/lib.rs (1)

1109-1125: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the rustdoc example to match the new call_function signature.

The snippet still shows the removed None argument and omits the required FunctionCallContext and copy_objects parameters.

Suggested doc fix
-/// let result = engine.call_function("get_user", vec![
-///     "Alice".into(),
-///     42i64.into(),
-/// ], None).await?;
+/// let ctx =
+///     bex_engine::FunctionCallContextBuilder::new(bex_engine::CallId::next()).build();
+/// let result = engine
+///     .call_function("get_user", vec!["Alice".into(), 42i64.into()], ctx, true)
+///     .await?;
🤖 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/crates/bex_engine/src/lib.rs` around lines 1109 - 1125, The doc
example for call_function is outdated: update the example call to match the new
signature of Engine::call_function by supplying a FunctionCallContext and the
copy_objects parameter (and remove the old trailing None). Locate the example
around the call_function docs and construct a FunctionCallContext (or use an
existing helper/context value) and pass an explicit boolean for copy_objects to
the call_function invocation so the example compiles with the current
call_function(FunctionCallContext, Vec<BexExternalValue>, bool) signature.
baml_language/crates/baml_cli/src/run_command.rs (1)

292-409: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject or remove --log-file now that the sink is gone.

RunArgs still exposes --log-file on Lines 150-152, but this path no longer reads self.log_file, so the flag now succeeds and produces nothing. That's a silent CLI contract break; either remove the option from the parser/help in this PR or fail fast here when it is passed.

Suggested guard in the changed path
 fn run_with_reporter(&self, reporter: &Reporter) -> Result<crate::ExitCode> {
+    if let Some(path) = &self.log_file {
+        anyhow::bail!(
+            "`--log-file {}` is temporarily unavailable while runtime tracing is disabled.",
+            path.display()
+        );
+    }
+
     // Dispatch modes are mutually exclusive. Positional target /
     // `-f` (one or many) / `-e` all replace each other.
🤖 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/crates/baml_cli/src/run_command.rs` around lines 292 - 409, The
CLI still accepts --log-file via RunArgs but run_with_reporter no longer uses
self.log_file, causing a silent no-op; either remove the flag from
RunArgs/argument parser and its help text (and delete the self.log_file field)
or add a fail-fast guard in run_with_reporter that checks if
self.log_file.is_some() and returns an error (anyhow::bail!) explaining that
--log-file is no longer supported; locate this in the run_with_reporter method
and the RunArgs struct to implement the chosen fix.
🧹 Nitpick comments (4)
baml_language/crates/bex_engine/src/lib.rs (1)

2418-2437: ⚡ Quick win

Add a focused unit test for the inert Event/SpanNotify path.

VmExecState::Event still depends on pushing null to keep the VM stack balanced, and SpanNotify still needs to resume cleanly. That contract is subtle enough to deserve a library test now that the tracing-specific suites are gone.

As per coding guidelines, **/*.rs: Prefer writing Rust unit tests over integration tests where possible.

🤖 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/crates/bex_engine/src/lib.rs` around lines 2418 - 2437, Add a
focused unit test that exercises the inert Event and SpanNotify branches so the
VM stack contract is verified: create a small VM state, invoke the executor path
that yields VmExecState::Event and assert that thread.vm.stack has a pushed
Value::NULL and execution resumes without altering stack depth; likewise
simulate VmExecState::SpanNotify and assert resume is clean (no stack change).
Target the executor function handling these states (refer to VmExecState::Event,
thread.vm.stack.push(Value::NULL), and VmExecState::SpanNotify) and write the
tests as Rust unit tests in the same module to keep them local.
baml_language/crates/bex_events/src/collector.rs (1)

29-55: ⚡ Quick win

Add a small unit test for the no-op collector contract.

These methods are now the entire compatibility surface for bindings, so a local unit test should pin the expected empty/None/0 behavior.

Based on learnings/coding guidelines, **/*.rs: Prefer writing Rust unit tests over integration tests where possible.

🤖 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/crates/bex_events/src/collector.rs` around lines 29 - 55, Add a
unit test in the same file (#[cfg(test)] mod tests) that constructs the
collector instance and asserts the no-op contract: call
track(&SpanId::default()) to ensure it doesn't panic,
assert_eq!(collector.logs(), Vec::new()), assert!(collector.last().is_none()),
assert_eq!(collector.usage(), Usage::default()), assert_eq!(collector.clear(),
0), and assert!(collector.id("any").is_none()); use the actual collector type
from this file (e.g., Collector) and the SpanId, FunctionLog, and Usage types
already in scope.
baml_language/sdks/nodejs/bridge_nodejs/src/lib.rs (1)

17-19: 💤 Low value

Stale comment references removed event-sink/tracing. This init() block no longer initializes logging via BAML_TRACE_FILE and bridge_cffi's event sink (both removed in this PR), so the comment is now misleading. Consider updating it to reflect that only host-dispatch/release callbacks are wired here.

🤖 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/nodejs/bridge_nodejs/src/lib.rs` around lines 17 - 19, The
existing comment above the init() block is stale — it mentions BAML_TRACE_FILE,
bridge_cffi, and an event sink that were removed; update the comment to
accurately describe what init() now does by removing references to
tracing/event-sink and stating that it only wires the host-dispatch and release
callbacks (e.g., mention init(), host_dispatch wiring and release callbacks
registration) so future readers understand the current behavior.
baml_language/sdks/python/rust/bridge_python/src/runtime.rs (1)

82-84: 💤 Low value

Param docs no longer match behavior. The ctx/collectors docstrings still claim host-trace nesting and per-call tracking, but both are now accepted-and-ignored no-ops. Same applies to the duplicated block at Lines 137-139. Consider noting they are retained for ABI compatibility only.

🤖 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/python/rust/bridge_python/src/runtime.rs` around lines 82
- 84, Update the doc comments in runtime.rs for the functions referencing `ctx`
and `collectors` (the doc block around the `ctx`/`collectors` params near the
comment starting at line ~82 and the duplicated block around lines ~137-139) to
state that `ctx` and `collectors` are accepted but currently ignored (no-ops)
and are retained only for ABI/backward-compatibility; replace the outdated
claims about host-trace nesting and per-call tracking with a concise note that
these parameters are no-ops and will not affect tracing, and keep the rest of
the parameter docs unchanged.
🤖 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.

Outside diff comments:
In `@baml_language/crates/baml_cli/src/run_command.rs`:
- Around line 292-409: The CLI still accepts --log-file via RunArgs but
run_with_reporter no longer uses self.log_file, causing a silent no-op; either
remove the flag from RunArgs/argument parser and its help text (and delete the
self.log_file field) or add a fail-fast guard in run_with_reporter that checks
if self.log_file.is_some() and returns an error (anyhow::bail!) explaining that
--log-file is no longer supported; locate this in the run_with_reporter method
and the RunArgs struct to implement the chosen fix.

In `@baml_language/crates/bex_engine/src/lib.rs`:
- Around line 1109-1125: The doc example for call_function is outdated: update
the example call to match the new signature of Engine::call_function by
supplying a FunctionCallContext and the copy_objects parameter (and remove the
old trailing None). Locate the example around the call_function docs and
construct a FunctionCallContext (or use an existing helper/context value) and
pass an explicit boolean for copy_objects to the call_function invocation so the
example compiles with the current call_function(FunctionCallContext,
Vec<BexExternalValue>, bool) signature.

---

Nitpick comments:
In `@baml_language/crates/bex_engine/src/lib.rs`:
- Around line 2418-2437: Add a focused unit test that exercises the inert Event
and SpanNotify branches so the VM stack contract is verified: create a small VM
state, invoke the executor path that yields VmExecState::Event and assert that
thread.vm.stack has a pushed Value::NULL and execution resumes without altering
stack depth; likewise simulate VmExecState::SpanNotify and assert resume is
clean (no stack change). Target the executor function handling these states
(refer to VmExecState::Event, thread.vm.stack.push(Value::NULL), and
VmExecState::SpanNotify) and write the tests as Rust unit tests in the same
module to keep them local.

In `@baml_language/crates/bex_events/src/collector.rs`:
- Around line 29-55: Add a unit test in the same file (#[cfg(test)] mod tests)
that constructs the collector instance and asserts the no-op contract: call
track(&SpanId::default()) to ensure it doesn't panic,
assert_eq!(collector.logs(), Vec::new()), assert!(collector.last().is_none()),
assert_eq!(collector.usage(), Usage::default()), assert_eq!(collector.clear(),
0), and assert!(collector.id("any").is_none()); use the actual collector type
from this file (e.g., Collector) and the SpanId, FunctionLog, and Usage types
already in scope.

In `@baml_language/sdks/nodejs/bridge_nodejs/src/lib.rs`:
- Around line 17-19: The existing comment above the init() block is stale — it
mentions BAML_TRACE_FILE, bridge_cffi, and an event sink that were removed;
update the comment to accurately describe what init() now does by removing
references to tracing/event-sink and stating that it only wires the
host-dispatch and release callbacks (e.g., mention init(), host_dispatch wiring
and release callbacks registration) so future readers understand the current
behavior.

In `@baml_language/sdks/python/rust/bridge_python/src/runtime.rs`:
- Around line 82-84: Update the doc comments in runtime.rs for the functions
referencing `ctx` and `collectors` (the doc block around the `ctx`/`collectors`
params near the comment starting at line ~82 and the duplicated block around
lines ~137-139) to state that `ctx` and `collectors` are accepted but currently
ignored (no-ops) and are retained only for ABI/backward-compatibility; replace
the outdated claims about host-trace nesting and per-call tracking with a
concise note that these parameters are no-ops and will not affect tracing, and
keep the rest of the parameter docs unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0f17b05e-fc68-4b1d-9a55-74bb308796cf

📥 Commits

Reviewing files that changed from the base of the PR and between 066b6cb and 89ee21b.

⛔ Files ignored due to path filters (1)
  • baml_language/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (73)
  • baml_language/Cargo.toml
  • baml_language/crates/baml_cli/Cargo.toml
  • baml_language/crates/baml_cli/src/pack_command.rs
  • baml_language/crates/baml_cli/src/run_command.rs
  • baml_language/crates/baml_cli/src/test_command.rs
  • baml_language/crates/baml_exec/src/dispatch.rs
  • baml_language/crates/baml_lsp_server/Cargo.toml
  • baml_language/crates/baml_lsp_server/src/lib.rs
  • baml_language/crates/baml_lsp_server/src/playground_event_sink.rs
  • baml_language/crates/baml_pack_host/Cargo.toml
  • baml_language/crates/baml_pack_host/src/main.rs
  • baml_language/crates/baml_tests/benches/cache_profile.rs
  • baml_language/crates/baml_tests/benches/runtime_benchmark.rs
  • baml_language/crates/baml_tests/src/engine.rs
  • baml_language/crates/baml_tests/tests/exceptions.rs
  • baml_language/crates/baml_tests/tests/parallel_scaling.rs
  • baml_language/crates/baml_tests/tests/spawn_array_race.rs
  • baml_language/crates/baml_tests/tests/spawn_parallel.rs
  • baml_language/crates/bex_engine/Cargo.toml
  • baml_language/crates/bex_engine/src/function_call_context.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_engine/tests/build_request.rs
  • baml_language/crates/bex_engine/tests/cancellation.rs
  • baml_language/crates/bex_engine/tests/collect_tests.rs
  • baml_language/crates/bex_engine/tests/common/mod.rs
  • baml_language/crates/bex_engine/tests/concurrent.rs
  • baml_language/crates/bex_engine/tests/early_yield.rs
  • baml_language/crates/bex_engine/tests/event_system.rs
  • baml_language/crates/bex_engine/tests/function_resolution.rs
  • baml_language/crates/bex_engine/tests/future_cleanup.rs
  • baml_language/crates/bex_engine/tests/gc.rs
  • baml_language/crates/bex_engine/tests/gc_during_future_op.rs
  • baml_language/crates/bex_engine/tests/host_argv.rs
  • baml_language/crates/bex_engine/tests/host_value_callable.rs
  • baml_language/crates/bex_engine/tests/llm_render.rs
  • baml_language/crates/bex_engine/tests/media_roundtrip.rs
  • baml_language/crates/bex_engine/tests/orchestration.rs
  • baml_language/crates/bex_engine/tests/registry.rs
  • baml_language/crates/bex_engine/tests/tracing.rs
  • baml_language/crates/bex_events/Cargo.toml
  • baml_language/crates/bex_events/src/collector.rs
  • baml_language/crates/bex_events/src/event_store.rs
  • baml_language/crates/bex_events/src/lib.rs
  • baml_language/crates/bex_events/src/serialize.rs
  • baml_language/crates/bex_events/src/types.rs
  • baml_language/crates/bex_events_native/Cargo.toml
  • baml_language/crates/bex_events_native/src/lib.rs
  • baml_language/crates/bex_project/Cargo.toml
  • baml_language/crates/bex_project/src/bex.rs
  • baml_language/crates/bex_project/src/bex_lsp/multi_project/mod.rs
  • baml_language/crates/bex_project/src/lib.rs
  • baml_language/crates/bex_project/src/project.rs
  • baml_language/crates/bridge_cffi/Cargo.toml
  • baml_language/crates/bridge_cffi/src/ffi/objects.rs
  • baml_language/crates/bridge_cffi/src/host_spans.rs
  • baml_language/crates/bridge_cffi/src/lib.rs
  • baml_language/crates/bridge_ctypes/Cargo.toml
  • baml_language/crates/bridge_ctypes/src/event_encode.rs
  • baml_language/crates/bridge_ctypes/src/lib.rs
  • baml_language/crates/bridge_wasm/Cargo.toml
  • baml_language/crates/bridge_wasm/src/lib.rs
  • baml_language/crates/bridge_wasm/src/wasm_playground.rs
  • baml_language/sdks/nodejs/bridge_nodejs/src/lib.rs
  • baml_language/sdks/nodejs/bridge_nodejs/src/runtime.rs
  • baml_language/sdks/nodejs/bridge_nodejs/src/types/host_span_manager.rs
  • baml_language/sdks/nodejs/bridge_nodejs/tests/test_collector.test.ts
  • baml_language/sdks/nodejs/bridge_nodejs/tests/test_tracing.test.ts
  • baml_language/sdks/python/rust/bridge_python/src/lib.rs
  • baml_language/sdks/python/rust/bridge_python/src/runtime.rs
  • baml_language/sdks/python/rust/bridge_python/src/types/host_span_manager.rs
  • baml_language/sdks/python/tests/test_collector.py
  • baml_language/sdks/python/tests/test_engine.py
  • baml_language/sdks/python/tests/test_tracing.py
💤 Files with no reviewable changes (38)
  • baml_language/crates/bex_engine/tests/collect_tests.rs
  • baml_language/sdks/nodejs/bridge_nodejs/tests/test_collector.test.ts
  • baml_language/sdks/nodejs/bridge_nodejs/tests/test_tracing.test.ts
  • baml_language/crates/bex_events/Cargo.toml
  • baml_language/crates/bex_project/Cargo.toml
  • baml_language/crates/bridge_wasm/Cargo.toml
  • baml_language/crates/bex_engine/Cargo.toml
  • baml_language/crates/baml_pack_host/Cargo.toml
  • baml_language/crates/bridge_ctypes/src/event_encode.rs
  • baml_language/crates/bex_engine/tests/tracing.rs
  • baml_language/crates/bex_events/src/serialize.rs
  • baml_language/crates/baml_pack_host/src/main.rs
  • baml_language/crates/bex_events_native/Cargo.toml
  • baml_language/crates/bex_events/src/event_store.rs
  • baml_language/crates/bex_project/src/bex.rs
  • baml_language/crates/bridge_cffi/Cargo.toml
  • baml_language/crates/bex_engine/tests/event_system.rs
  • baml_language/crates/bex_engine/tests/host_argv.rs
  • baml_language/crates/bex_engine/tests/function_resolution.rs
  • baml_language/crates/baml_lsp_server/Cargo.toml
  • baml_language/crates/bex_events/src/types.rs
  • baml_language/crates/baml_lsp_server/src/lib.rs
  • baml_language/crates/bridge_ctypes/Cargo.toml
  • baml_language/crates/bex_events_native/src/lib.rs
  • baml_language/crates/bex_engine/tests/orchestration.rs
  • baml_language/sdks/python/tests/test_tracing.py
  • baml_language/crates/bridge_wasm/src/wasm_playground.rs
  • baml_language/crates/baml_cli/Cargo.toml
  • baml_language/crates/bex_engine/tests/early_yield.rs
  • baml_language/sdks/python/tests/test_collector.py
  • baml_language/crates/baml_lsp_server/src/playground_event_sink.rs
  • baml_language/crates/bridge_ctypes/src/lib.rs
  • baml_language/crates/bex_engine/tests/gc.rs
  • baml_language/crates/bex_engine/tests/registry.rs
  • baml_language/Cargo.toml
  • baml_language/crates/bex_engine/tests/concurrent.rs
  • baml_language/crates/bex_engine/src/function_call_context.rs
  • baml_language/crates/bex_engine/tests/cancellation.rs

Strips the remaining tracing bias out of the VM so the core executes
without any observability hooks (clean slate for the rebuild).

Removed:
- BexVm::traced_frames and BexVm::current_span_context fields (+ inits)
- Function.trace field and every producer, incl. the @trace/LLM lowering
  (compiled_fn.trace = true) in baml_compiler2_emit
- VmExecState::SpanNotify variant and the SpanNotification enum
- Call-enter arg snapshot (is_traced/trace_data) + FunctionEnter yield
- Return-path span_exit logic + FunctionExit yield
- Both traced_frames cleanup loops in the exception-unwind path
- SpanNotify match arms in bex_engine ($init loop + dispatch) and
  tools_onionskin's VM runner
- bex_vm's now-unused bex_events dependency

The Call/Return hot path now does zero observability work — no branch,
no snapshot, no yield. Removing Function.trace changes the Function borsh
layout, so packed .baml artifacts must be recompiled.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@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.

🧹 Nitpick comments (1)
baml_language/crates/bex_vm/src/vm.rs (1)

2358-2451: ⚡ Quick win

Add a unit test for unwind past the interrupt boundary.

This branch mutates interrupt_frame, which changes how later Return instructions are interpreted. A small unit test here that drives interrupt() through an unwind path would make regressions obvious; please also run cargo test --lib before merge.

As per coding guidelines, **/*.rs: Prefer writing Rust unit tests over integration tests where possibleandAlways run cargo test --lib if you changed any Rust code`.

🤖 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/crates/bex_vm/src/vm.rs` around lines 2358 - 2451, Add a Rust
unit test in the same module as vm.rs (e.g. fn
test_unwind_past_interrupt_boundary) that constructs a VM state with at least
two frames, sets up a bytecode frame that will throw (so unwinding will pop a
frame past the current interrupt boundary), calls the VM interrupt() to set
self.interrupt_frame, triggers the exception/unwind path exercised in the shown
code, and then asserts that self.interrupt_frame has been cleared and that
subsequent Return instruction handling still behaves as expected; use the VM
APIs used in vm.rs (self.interrupt_frame, interrupt(), Frame::Bytecode,
Return/exception dispatch) to drive the scenario and ensure the test fails on
regressions, and run cargo test --lib locally before merging.
🤖 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.

Nitpick comments:
In `@baml_language/crates/bex_vm/src/vm.rs`:
- Around line 2358-2451: Add a Rust unit test in the same module as vm.rs (e.g.
fn test_unwind_past_interrupt_boundary) that constructs a VM state with at least
two frames, sets up a bytecode frame that will throw (so unwinding will pop a
frame past the current interrupt boundary), calls the VM interrupt() to set
self.interrupt_frame, triggers the exception/unwind path exercised in the shown
code, and then asserts that self.interrupt_frame has been cleared and that
subsequent Return instruction handling still behaves as expected; use the VM
APIs used in vm.rs (self.interrupt_frame, interrupt(), Frame::Bytecode,
Return/exception dispatch) to drive the scenario and ensure the test fails on
regressions, and run cargo test --lib locally before merging.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: faf14f70-822c-4e6b-b678-2e5a82a6a1a3

📥 Commits

Reviewing files that changed from the base of the PR and between 89ee21b and 4bc1a50.

⛔ Files ignored due to path filters (1)
  • baml_language/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/baml_compiler2_emit/src/lib.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_vm/Cargo.toml
  • baml_language/crates/bex_vm/src/lib.rs
  • baml_language/crates/bex_vm/src/package_baml/mod.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/bex_vm/tests/load_type.rs
  • baml_language/crates/bex_vm/tests/method_class_type_args.rs
  • baml_language/crates/bex_vm_types/src/types.rs
  • baml_language/crates/tools_onionskin/src/compiler.rs
💤 Files with no reviewable changes (8)
  • baml_language/crates/bex_vm/tests/load_type.rs
  • baml_language/crates/bex_vm/Cargo.toml
  • baml_language/crates/bex_vm/tests/method_class_type_args.rs
  • baml_language/crates/bex_vm_types/src/types.rs
  • baml_language/crates/bex_vm/src/package_baml/mod.rs
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/tools_onionskin/src/compiler.rs
  • baml_language/crates/baml_compiler2_emit/src/lib.rs

@github-actions

github-actions Bot commented May 31, 2026

Copy link
Copy Markdown

Binary size checks passed

7 passed

Artifact Platform File Gzip Gated on Baseline Delta Status
baml-cli Linux 🔒 17.7 MB 7.5 MB file 24.1 MB -6.4 MB (-26.7%) OK
packed-program Linux 🔒 12.9 MB 5.4 MB file 15.1 MB -2.2 MB (-14.8%) OK
baml-cli macOS 🔒 13.5 MB 6.5 MB file 18.2 MB -4.7 MB (-25.9%) OK
packed-program macOS 🔒 9.8 MB 4.7 MB file 11.5 MB -1.7 MB (-14.6%) OK
baml-cli Windows 🔒 14.5 MB 6.7 MB file 19.6 MB -5.1 MB (-26.0%) OK
packed-program Windows 🔒 10.4 MB 4.8 MB file 12.2 MB -1.9 MB (-15.1%) OK
bridge_wasm WASM 11.9 MB 🔒 3.3 MB gzip 3.9 MB -524.5 KB (-13.6%) 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

Continues stripping observability bias from the VM core.

Custom events (kept log.*, dropped the event stream):
- Removed the baml.events.send intrinsic: IntrinsicOp::SendEvent (MIR
  ir/lower), its emit lowering arm, and the stdlib ns_events/events.baml
  declaration. log.debug/info/warn still work (they keep the SendEvent
  instruction + VmExecState::Event path).

Block/loop/header "flagging" (VM + bytecode surface only):
- Removed Instruction/OpCode VizEnter/VizExit/NotifyBlock and all their
  match arms; the runtime types BlockNotification/BlockNotificationType/
  VizNodeMeta/VizNodeType/VizExecEvent/VizExecDelta; the Function
  block_notifications/viz_nodes fields; the VM handlers; WatchNotification
  Block/Viz variants; and the debug-disassembler arms.
- emit now no-ops the MIR StatementKind::{VizEnter,VizExit,NotifyBlock}.

The MIR compiler keeps its own viz/block IR dormant (not lowered to
bytecode). The variable-watch system (Watch/Notify/Variables) is intact.

cargo check --workspace --all-targets is green. The event_system /
ns_watch test fixtures and bytecode-disasm snapshots break by design and
will be regenerated in the tracing rebuild.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
@codspeed-hq

codspeed-hq Bot commented May 31, 2026

Copy link
Copy Markdown

Merging this PR will improve performance by 23.89%

⚡ 1 improved benchmark
✅ 12 untouched benchmarks
⏩ 7 skipped benchmarks1

Performance Changes

Mode Benchmark BASE HEAD Efficiency
WallTime vm_loop_500k 117.7 ms 95 ms +23.89%

Tip

Curious why this is faster? Comment @codspeedbot explain why this is faster on this PR, or directly use the CodSpeed MCP with your agent.


Comparing hellovai/trim-events (855fa73) with canary (066b6cb)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

…han CPython (#3627)

Stacked on #3616 (base = `hellovai/trim-events`). Makes the `bex_vm`
bytecode interpreter **faster than CPython 3.9** across most workloads,
where it started ~2.2× slower.

## Results (Apple M2 Max, `scripts/speedtest run`)
- **`fib` (1M × fib(50))**: total instructions retired **−60%**, CPU
cycles **−51%** vs the pre-fusion baseline — below the cycle count of an
optimal naive hand-written interpreter for the same loop.
- **All ~38 speedtest workloads**: BAML now beats Python on ~22 of them
(several by 2–4×: parallel-sum 0.23×, spawn-fan-out 0.41×, call-chain
0.45×, class-instances 0.50×, method-call 0.56×). Not overfit to fib —
the wins span compute, classes, concurrency, dispatch, and most string
ops.

## What changed (each commit validated + clippy-clean)
All fusion is an **emit-time peephole** that rewrites instructions in
place, confined to the current basic block (mirrors the existing
`StoreVarLoadVar` fusion), so jump targets and block addresses are never
affected:

- **Superinstruction fusion** — fold operand loads (and the store, and
the loop condition's branch) into single ops:
- `AddIntVar/Const`, `SubIntVar/Const`, `CmpIntLtVar/Const` (fold right
operand)
- `AddIntVarVar/VarConst`, `SubIntVarVar/VarConst`,
`CmpIntLtVarVar/VarConst` (fold both operands — no operand stack pushes)
- `AddIntVarVarStore/VarConstStore` (compute + store directly),
`MoveLocal` (fused `x = y`)
- `CmpIntLt*BrFalse/BrTrue` (fuse the loop condition with its branch;
**branch inversion** drops the per-iteration jump-to-body)
- **Unchecked local-slot fast paths** for `LoadVar`/`StoreVar`, and a
**lazy `faulting_pc`** (track `cur_pc` once per dispatch instead of
writing the frame every op).
- **Call path**: consolidated redundant per-call `get_object` heap
derefs with a plain-`Function` fast path.
- **kperf**: an in-process Apple-Silicon PMC probe (cycles +
instructions retired, env-gated `BAML_KPERF=1`, behind an off-by-default
`kperf` cargo feature) for deterministic, frequency-independent
measurement.
- **Toolchain**: pinned the nightly toolchain and dropped the MSRV job.

## Key insight
Cycle cost is dominated by **branch-mispredictions on the giant opcode
dispatch `match`** (an indirect jump). So the real lever is **reducing
the number of *dispatched* ops** — that's why fusion keeps paying off
(branch inversion: −15% cycles for −9% instructions), while removing the
inline per-op counter barely moved cycles. Measure total
instructions/cycles, not `instr/op` (fusion shrinks the op-count
denominator).

## Validation
fib/boundary-loops/if-else correct; `bex_vm`, `interfaces` (337),
`exceptions`, `cancellation`, `host_value_callable`, `errors`, `floats`,
`gc`, `dispatch`, `optimization` all green.

## Known follow-ups (diagnosed, not in this PR)
- `baml pack`'s embedded runtime stub needs rebuilding for the new
opcodes (`baml run` is unaffected).
- Remaining Python losses: strings (`split` ~2.2× — alloc-bound), deep
recursion (~130 cyc/call, frame setup), collatz (`%`/`/`/`*`/`!=`/`==`
still unfused).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: Sam Lijin <[email protected]>
Co-authored-by: Dhilan Shah <[email protected]>
Co-authored-by: aaronvg <[email protected]>
Co-authored-by: 2kai2kai2 <[email protected]>
# Conflicts:
#	baml_language/Cargo.lock
#	baml_language/crates/bex_project/src/lib.rs
#	baml_language/crates/bridge_cffi/src/lib.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: 7

Caution

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

⚠️ Outside diff range comments (1)
baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs (1)

99-124: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include generic args in Ctor::Class identity.

PartialEq, Hash, and covers still collapse Foo<int> and Foo<string> into the same ctor because they only compare/hash the QualifiedTypeName. That breaks dedup_ctors, present_set, and row specialization for unions containing multiple instantiations of the same generic class, so one arm can incorrectly exhaust or shadow another. Keep the class args in ctor identity as well, preferably via ty_ctor_identity(...) so attrs/freshness stay ignored consistently.

Suggested fix
 impl PartialEq for Ctor {
     fn eq(&self, other: &Self) -> bool {
         use Ctor::{
             Class, Interface, Missing, NonExhaustive, Or, Single, Slice, UnionMember, Wildcard,
         };
         match (self, other) {
             (Single(a), Single(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Slice(a), Slice(b)) => a == b,
-            (Class(a, _), Class(b, _)) => a == b,
+            (Class(a, a_args), Class(b, b_args)) => {
+                a == b
+                    && a_args.len() == b_args.len()
+                    && a_args
+                        .iter()
+                        .zip(b_args)
+                        .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b))
+            }
             (Interface(a), Interface(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (UnionMember(a), UnionMember(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Or, Or)
             | (Wildcard, Wildcard)
             | (NonExhaustive, NonExhaustive)
@@
         std::mem::discriminant(self).hash(state);
         match self {
             Single(ty) => ty_ctor_identity(ty).hash(state),
             Slice(s) => s.hash(state),
-            Class(qtn, _) => {
+            Class(qtn, args) => {
                 qtn.hash(state);
+                for arg in args {
+                    ty_ctor_identity(arg).hash(state);
+                }
             }
             Interface(ty) => ty_ctor_identity(ty).hash(state),
             UnionMember(ty) => ty_ctor_identity(ty).hash(state),
             Or | Wildcard | NonExhaustive | Missing => {}
         }
@@
         use Ctor::{
             Class, Interface, Missing, NonExhaustive, Or, Single, Slice, UnionMember, Wildcard,
         };
         match (self, other) {
             (Wildcard, _) => true,
             (_, Wildcard) => false,
             (Single(a), Single(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
-            (Class(a, _), Class(b, _)) => a == b,
+            (Class(a, a_args), Class(b, b_args)) => {
+                a == b
+                    && a_args.len() == b_args.len()
+                    && a_args
+                        .iter()
+                        .zip(b_args)
+                        .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b))
+            }
             (Interface(a), Interface(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Slice(a), Slice(b)) => slice_covers(a, b),
             (UnionMember(a), UnionMember(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (NonExhaustive, NonExhaustive) => true,
             (Missing, Missing) => true,
@@
 fn class_ty_for_ctor(qtn: &QualifiedTypeName, args: &[Ty], fallback: &Ty) -> Ty {
     match fallback {
-        Ty::Class(fallback_qtn, _, _) if fallback_qtn == qtn => fallback.clone(),
+        Ty::Class(fallback_qtn, fallback_args, _)
+            if fallback_qtn == qtn
+                && fallback_args.len() == args.len()
+                && fallback_args
+                    .iter()
+                    .zip(args)
+                    .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b)) =>
+        {
+            fallback.clone()
+        }
         _ => Ty::Class(qtn.clone(), args.to_vec(), TyAttr::default()),
     }
 }

Also applies to: 183-199

🤖 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/crates/baml_compiler2_tir/src/exhaustiveness.rs` around lines
99 - 124, The Class ctor currently ignores generic args when computing identity
(PartialEq, Hash, and covers), so Foo<int> and Foo<string> collapse; update all
Class handling to include the type+args via ty_ctor_identity instead of only the
QualifiedTypeName: in the PartialEq match arm that now reads (Class(a, _),
Class(b, _)) => a == b, call ty_ctor_identity on the full class type constructed
from the QualifiedTypeName and its args (use the same tuple fields, e.g.
Class(qtn, args)) and compare those identities; likewise in std::hash::Hash for
Ctor replace hashing qtn in the Class arm with hashing ty_ctor_identity(...) of
the full class type; also make the same change in covers and anywhere else
referenced (dedup_ctors / present_set logic—see the other Class handling around
lines ~183-199) so class generic arguments participate in ctor identity
consistently.
🧹 Nitpick comments (8)
.github/workflows/cargo-tests.reusable.yaml (2)

803-822: 💤 Low value

Missing blank line before sccache-stats job definition.

There's no blank line between the end of cargo-timings job (line 803) and the start of sccache-stats job (line 804), unlike other job definitions in this file. Minor formatting inconsistency.

Suggested fix
           delete-merged: true
+
   sccache-stats:
     name: "sccache stats (merged)"
🤖 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 @.github/workflows/cargo-tests.reusable.yaml around lines 803 - 822, Add a
blank line before the sccache-stats job definition to match the file's job
separation style: insert one empty line between the end of the previous job
(cargo-timings) and the start of the job named "sccache-stats" so the job block
beginning with "sccache-stats:" sits on its own paragraph.

83-89: ⚖️ Poor tradeoff

Consider pinning third-party actions to commit SHAs for supply-chain security.

Static analysis (zizmor) flags multiple unpinned action references throughout this workflow (e.g., Swatinem/rust-cache@v2, actions/upload-artifact@v7, taiki-e/install-action@v2). Pinning to commit SHAs instead of tags prevents silent changes from compromised or force-pushed tags.

This is a general hardening measure. If the repo's policy accepts tag-based versioning for trusted actions, this can be deferred.

🤖 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 @.github/workflows/cargo-tests.reusable.yaml around lines 83 - 89, Replace
tag-based action references with their corresponding commit SHAs to harden
supply-chain security: locate occurrences of Swatinem/rust-cache@v2,
actions/upload-artifact@v7, taiki-e/install-action@v2 (and any other third-party
actions in the workflow) and update each reference to the full commit SHA (e.g.,
Swatinem/rust-cache@<sha>) obtained from the action repository, then run the
workflow locally or in a branch to verify the SHA pins work as expected.
baml_language/crates/baml_compiler2_emit/src/emit.rs (1)

1137-1152: 💤 Low value

emit_store_var loses metadata for the first store when fusing StoreVar2.

When fusing StoreVar(a); StoreVar(slot) into StoreVar2(a, slot), the metadata for slot a (set by the previous emit_store_var call at line 1150-1151) is overwritten by set_var_operand(n - 1, slot) at line 1146. The operand metadata will only reflect the second slot name, not the first.

For debugging/disassembly, both slot names should ideally be preserved. Consider whether StoreVar2 needs a different OperandMeta variant that holds both names, or if this is acceptable given the instruction semantics.

🤖 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/crates/baml_compiler2_emit/src/emit.rs` around lines 1137 -
1152, emit_store_var currently fuses a previous StoreVar(a) and a new
StoreVar(slot) into Instruction::StoreVar2(a, slot) but then calls
set_var_operand(n - 1, slot), overwriting the metadata for the first operand;
preserve both operand metadata by extending the metadata model and update the
fusion logic: add a new OperandMeta variant (or storage) that can hold two slot
names for Instruction::StoreVar2, or modify set_var_operand to accept/attach
metadata for both operands, and when you replace bytecode.instructions[n - 1]
with Instruction::StoreVar2(a, slot) copy the original metadata for operand a
into the first-slot metadata and set the second-slot metadata to slot so
disassembly/debugging can see both names (symbols: emit_store_var,
Instruction::StoreVar2, set_var_operand, bytecode.instructions).
baml_language/crates/baml_cli/src/grep_command.rs (1)

447-469: ⚡ Quick win

Add a unit test that locks the method JSON schema.

This changes machine-readable CLI output and line-range serialization, so a focused unit test around description_to_json would catch silent drift cheaply. As per coding guidelines **/*.rs: Prefer writing Rust unit tests over integration tests where possible.

Also applies to: 506-507

🤖 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/crates/baml_cli/src/grep_command.rs` around lines 447 - 469,
Add a unit test that locks the JSON schema emitted by
description_to_json/method_json: create a small deterministic ProjectDatabase
fixture (or use an existing test helper), call description_to_json (which uses
method_json) for a known file/class with predictable docstring/signature and
line offsets, then assert the produced serde_json::Value includes the exact keys
("name","signature","docstring","file","line_start","line_end") and that
"line_start" and "line_end" are the expected integers and that "file" matches
the relative path; place the test as a Rust unit test in the same module as
method_json so it runs fast and fails on any silent output/schema drift.
baml_language/crates/baml_compiler2_mir/src/lower.rs (1)

11133-11147: ⚡ Quick win

Add a regression test for the new union/interface dispatch path.

This helper-only test won't catch the new method/field dispatch branches or the generic-interface runtime checks. A Rust unit test that exercises a concrete generic-class union calling an inherited/default interface method would lock this behavior down. As per coding guidelines, **/*.rs: Prefer writing Rust unit tests over integration tests where possible, and always run cargo test --lib if you changed any Rust code.

🤖 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/crates/baml_compiler2_mir/src/lower.rs` around lines 11133 -
11147, Add a regression unit test exercising the new union/interface dispatch
path by creating a concrete generic-class union that implements or inherits an
interface with a default method and then invoking that method through the union
to ensure generic-interface runtime checks and dispatch work; specifically, add
a new #[test] (similar place as
interface_tir_type_args_match_preserves_type_var_identity) that constructs a
generic class/union instance using concrete type parameters, ensures the
interface implementation/default method is available, calls the method via the
union dispatch path, and asserts the correct behavior so that
interface_tir_type_args_match_preserving_typevars and the new union/interface
dispatch are both validated at cargo test --lib time.
baml_language/crates/baml_tests/build.rs (1)

216-231: 💤 Low value

Consider guarding against empty slugs.

If a workload name consists entirely of non-alphanumeric characters (e.g., "!!!"), the slugify function will produce a single underscore which is then trimmed away at line 230, leaving an empty string. This would cause format_ident! at line 131 to panic at build time.

While workload names are controlled repo content and this scenario is extremely unlikely, a defensive fallback (e.g., slug.is_empty() check with a default like "unnamed") would make the build script more robust.

🛡️ Proposed defensive fallback
 fn slugify(name: &str) -> String {
     let mut out = String::with_capacity(name.len());
     let mut prev_underscore = false;
     for ch in name.chars() {
         if ch.is_ascii_alphanumeric() {
             out.extend(ch.to_lowercase());
             prev_underscore = false;
         } else if !prev_underscore {
             out.push('_');
             prev_underscore = true;
         }
     }
-    out.trim_matches('_').to_string()
+    let slug = out.trim_matches('_');
+    if slug.is_empty() {
+        "unnamed".to_string()
+    } else {
+        slug.to_string()
+    }
 }
🤖 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/crates/baml_tests/build.rs` around lines 216 - 231, The slugify
function can return an empty string for names with no alphanumeric characters;
after computing the slug in fn slugify(name: &str) -> String check if the result
is empty and return a safe fallback (e.g. "unnamed" or similar) so callers like
format_ident! don't panic; update slugify to ensure it never returns an empty
string (use the same lowercase/underscore logic but substitute the fallback when
slug.is_empty()).
baml_language/crates/baml_lsp2_actions/src/describe.rs (1)

1675-1680: ⚡ Quick win

Limit comment stripping to the covered CST subtree.

clean_body_source() scans tree.descendants_with_tokens() for every body extraction, so describing several symbols in one file turns into repeated full-file token walks. Each caller already passes a single item/member/function span, so starting from the covering node would keep this proportional to the rendered body instead of the whole file.

🤖 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/crates/baml_lsp2_actions/src/describe.rs` around lines 1675 -
1680, clean_body_source() currently walks tree.descendants_with_tokens() for
every extraction which causes full-file token scans; change it to locate the CST
node that covers the provided span (use the parser/tree covering node API for
the given span) and iterate only that node.descendants_with_tokens() (or the
subtree root) when building deletions and stripping comments. Update the logic
that builds deletions (currently iterating tree.descendants_with_tokens()) to
start from the covering node for the span passed into clean_body_source(),
keeping the rest of the comment-removal logic intact.
baml_language/crates/baml_compiler2_ppir/src/lib.rs (1)

139-161: ⚡ Quick win

Drop the custom salsa::Update impl unless you need behavior beyond the default fallback.

Salsa already falls back to a 'static + PartialEq comparison when a tracked return type has no Update impl, and ProjectExpansionMaps is already fully owned + PartialEq. Keeping a hand-written unsafe impl here adds unsafe maintenance surface without changing the early-cutoff behavior. Please verify this still compiles cleanly after removing the impl. (docs.rs)

Suggested simplification
-/// # Safety
-///
-/// Mirrors [`baml_compiler2_hir::package::PackageItems`]'s impl. The contained
-/// maps hold no Salsa-interned (`'db`) data, so storing them by value is sound;
-/// `maybe_update` uses `PartialEq` for proper Salsa early-cutoff.
-#[allow(unsafe_code)]
-unsafe impl salsa::Update for ProjectExpansionMaps {
-    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-        // SAFETY: `old_pointer` is valid, aligned, and Salsa-owned.
-        #[allow(unsafe_code)]
-        let old = unsafe { &*old_pointer };
-        if old == &new_value {
-            false
-        } else {
-            #[allow(unsafe_code)]
-            unsafe {
-                std::ptr::drop_in_place(old_pointer);
-                std::ptr::write(old_pointer, new_value);
-            }
-            true
-        }
-    }
-}
🤖 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/crates/baml_compiler2_ppir/src/lib.rs` around lines 139 - 161,
Remove the custom unsafe impl salsa::Update for ProjectExpansionMaps (including
the maybe_update function) because ProjectExpansionMaps is already owned and
PartialEq, so Salsa's default fallback handles early-cutoff; delete the entire
unsafe impl block that implements salsa::Update and its maybe_update logic, then
run a build to confirm it compiles cleanly and relies on the default Update
behavior.
🤖 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/Cargo.toml`:
- Around line 169-171: Bump the crypto crate version constraints in Cargo.toml:
leave hex as-is, update the hmac dependency (symbol: hmac) from "0.12" to the
newer "0.13" and update the sha1 dependency (symbol: sha1) from "0.10" to "0.11"
so Cargo can pick the latest compatible minor releases; run cargo update and
cargo build/test to ensure compatibility and fix any API changes if they
surface.

In `@baml_language/crates/baml_cli/src/grep_command.rs`:
- Around line 447-469: The method_json serializer (and the similar serializer at
lines 506-507) drops the previous per-method JSON keys ("kind" and the single
"line") in favor of "line_start"/"line_end", which will break existing
consumers; restore backward compatibility by emitting the old keys alongside the
new ones (e.g., include "kind": m.kind and "line": line_number_at_offset(text,
m.item_range.start().into())) or alternatively gate the richer shape behind a
version flag, updating method_json and the analogous function to populate both
legacy and new fields.

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 6473-6483: The loop over union members currently drops each
member's concrete type arguments (patterning Tir2Ty::Class(qtn, _, _)), so
class_member_method_candidates and later interface-candidate logic lose the
concrete instantiated type; update the match to capture the member's type_args
(e.g., Tir2Ty::Class(qtn, type_args, _)) and thread those concrete args into the
interface-call candidate derivation (so class_member_method_candidates /
candidate construction uses the member's instantiated type), ensuring candidates
include members that arise only via inherited/default/generic interface views or
blanket `implements ... for C` rules; also ensure try_lower_union_iface_dispatch
uses these preserved concrete-member candidates when deciding dispatch.

In `@baml_language/crates/baml_compiler2_tir/src/builder.rs`:
- Around line 12647-12655: The helper pattern_overlaps_scrut_member currently
checks overlap using is_subtype against each member from
flatten_union_optional_members; replace those is_subtype calls with the
runtime-aware types_overlap so pattern dispatch matches runtime behavior (avoid
numeric-tower false positives). Specifically, in pattern_overlaps_scrut_member
change the closure that does self.is_subtype(pat, m) || self.is_subtype(m, pat)
to use self.types_overlap(pat, m) (or an equivalent symmetric types_overlap
check) so the function uses types_overlap for both directions when iterating
union/optional members.
- Around line 13375-13418: The interface destructure branch (matching
Ty::Interface and calling interface_field_infos_ordered) is running before
checking for missing generic args, so type_args can be empty and fields are
lowered with the interface's generic parameters instead of the scrutinee's
concrete args; fix by adding the same missing-type-args guard used for class
destructures or by substituting the scrutinee's concrete interface type
arguments into class_ty before calling interface_field_infos_ordered (i.e., when
handling Ty::Interface(iface_name, type_args, _) use the concrete args from
scrut_ty or bail/report missing args, then proceed to call
interface_field_infos_ordered, analyze_and_lower, build sub_dpats and
DPat::interface, and finally compute matched_ty with
intersect_pattern_flow_types).
- Around line 8099-8137: union_interface_method_ambiguity currently only
considers in-body implements via implemented_interface_method_sources (losing
registry-only rules and distinct instantiations like Getter<int> vs
Getter<string>), so update it to also consult the
package_implements_registry(rule set) when gathering "sources" for a given
class_qtn: for each rule in registry.interface_impl_rules where
rule.for_ty_pattern is the class_qtn and rule.interface_ty is one of the union
interfaces, check whether that rule provides the requested member (reusing
try_registry_member/resolve logic) and add the corresponding (interface QTN +
instantiation args) to the sources list; also stop collapsing members to bare
interface QTN — preserve the interface instantiation (e.g., keep
(QualifiedTypeName, Vec<Ty>) keys) so same-interface different-instantiation
cases are treated as distinct, and deduplicate sources before checking
sources.len() >= 2.

In `@baml_language/crates/baml_lsp2_actions/src/describe.rs`:
- Around line 1507-1523: The function collect_qtn_dep currently drops the
namespace by using qtn.name() and dedupes/looks up by short name, which causes
collisions (e.g., root.a.User vs root.b.User); change it to use the full
qualified path from qtn (e.g., qtn.to_string() or qtn.full_path()) for both
deduplication (the seen HashSet) and for resolving the dependency so you call
resolve_dep_from_outline with the qualified name instead of qtn.name(); update
references to the HashSet key and the resolve_dep_from_outline argument
accordingly in collect_qtn_dep so deps and seen are keyed by the full identity
and duplicates are correctly suppressed.

---

Outside diff comments:
In `@baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs`:
- Around line 99-124: The Class ctor currently ignores generic args when
computing identity (PartialEq, Hash, and covers), so Foo<int> and Foo<string>
collapse; update all Class handling to include the type+args via
ty_ctor_identity instead of only the QualifiedTypeName: in the PartialEq match
arm that now reads (Class(a, _), Class(b, _)) => a == b, call ty_ctor_identity
on the full class type constructed from the QualifiedTypeName and its args (use
the same tuple fields, e.g. Class(qtn, args)) and compare those identities;
likewise in std::hash::Hash for Ctor replace hashing qtn in the Class arm with
hashing ty_ctor_identity(...) of the full class type; also make the same change
in covers and anywhere else referenced (dedup_ctors / present_set logic—see the
other Class handling around lines ~183-199) so class generic arguments
participate in ctor identity consistently.

---

Nitpick comments:
In @.github/workflows/cargo-tests.reusable.yaml:
- Around line 803-822: Add a blank line before the sccache-stats job definition
to match the file's job separation style: insert one empty line between the end
of the previous job (cargo-timings) and the start of the job named
"sccache-stats" so the job block beginning with "sccache-stats:" sits on its own
paragraph.
- Around line 83-89: Replace tag-based action references with their
corresponding commit SHAs to harden supply-chain security: locate occurrences of
Swatinem/rust-cache@v2, actions/upload-artifact@v7, taiki-e/install-action@v2
(and any other third-party actions in the workflow) and update each reference to
the full commit SHA (e.g., Swatinem/rust-cache@<sha>) obtained from the action
repository, then run the workflow locally or in a branch to verify the SHA pins
work as expected.

In `@baml_language/crates/baml_cli/src/grep_command.rs`:
- Around line 447-469: Add a unit test that locks the JSON schema emitted by
description_to_json/method_json: create a small deterministic ProjectDatabase
fixture (or use an existing test helper), call description_to_json (which uses
method_json) for a known file/class with predictable docstring/signature and
line offsets, then assert the produced serde_json::Value includes the exact keys
("name","signature","docstring","file","line_start","line_end") and that
"line_start" and "line_end" are the expected integers and that "file" matches
the relative path; place the test as a Rust unit test in the same module as
method_json so it runs fast and fails on any silent output/schema drift.

In `@baml_language/crates/baml_compiler2_emit/src/emit.rs`:
- Around line 1137-1152: emit_store_var currently fuses a previous StoreVar(a)
and a new StoreVar(slot) into Instruction::StoreVar2(a, slot) but then calls
set_var_operand(n - 1, slot), overwriting the metadata for the first operand;
preserve both operand metadata by extending the metadata model and update the
fusion logic: add a new OperandMeta variant (or storage) that can hold two slot
names for Instruction::StoreVar2, or modify set_var_operand to accept/attach
metadata for both operands, and when you replace bytecode.instructions[n - 1]
with Instruction::StoreVar2(a, slot) copy the original metadata for operand a
into the first-slot metadata and set the second-slot metadata to slot so
disassembly/debugging can see both names (symbols: emit_store_var,
Instruction::StoreVar2, set_var_operand, bytecode.instructions).

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 11133-11147: Add a regression unit test exercising the new
union/interface dispatch path by creating a concrete generic-class union that
implements or inherits an interface with a default method and then invoking that
method through the union to ensure generic-interface runtime checks and dispatch
work; specifically, add a new #[test] (similar place as
interface_tir_type_args_match_preserves_type_var_identity) that constructs a
generic class/union instance using concrete type parameters, ensures the
interface implementation/default method is available, calls the method via the
union dispatch path, and asserts the correct behavior so that
interface_tir_type_args_match_preserving_typevars and the new union/interface
dispatch are both validated at cargo test --lib time.

In `@baml_language/crates/baml_compiler2_ppir/src/lib.rs`:
- Around line 139-161: Remove the custom unsafe impl salsa::Update for
ProjectExpansionMaps (including the maybe_update function) because
ProjectExpansionMaps is already owned and PartialEq, so Salsa's default fallback
handles early-cutoff; delete the entire unsafe impl block that implements
salsa::Update and its maybe_update logic, then run a build to confirm it
compiles cleanly and relies on the default Update behavior.

In `@baml_language/crates/baml_lsp2_actions/src/describe.rs`:
- Around line 1675-1680: clean_body_source() currently walks
tree.descendants_with_tokens() for every extraction which causes full-file token
scans; change it to locate the CST node that covers the provided span (use the
parser/tree covering node API for the given span) and iterate only that
node.descendants_with_tokens() (or the subtree root) when building deletions and
stripping comments. Update the logic that builds deletions (currently iterating
tree.descendants_with_tokens()) to start from the covering node for the span
passed into clean_body_source(), keeping the rest of the comment-removal logic
intact.

In `@baml_language/crates/baml_tests/build.rs`:
- Around line 216-231: The slugify function can return an empty string for names
with no alphanumeric characters; after computing the slug in fn slugify(name:
&str) -> String check if the result is empty and return a safe fallback (e.g.
"unnamed" or similar) so callers like format_ident! don't panic; update slugify
to ensure it never returns an empty string (use the same lowercase/underscore
logic but substitute the fallback when slug.is_empty()).
🪄 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: d0142bf5-233d-4074-acb2-a1a87b42c87d

📥 Commits

Reviewing files that changed from the base of the PR and between 855fa73 and 99f624d.

⛔ Files ignored due to path filters (97)
  • baml_language/Cargo.lock is excluded by !**/*.lock
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__control_describe_shallow_namespace_item_works.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__describe_user_method_drill_in_shows_body.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__dispatch_root_prefix_resolves_user_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__fixed_describe_deep_namespace_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__fixed_describe_deep_namespace_member.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_builtin_package_listing.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_alias_string.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_deep_copy.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_item_by_definition.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_string.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class_with_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class_with_static_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_enum.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_function_with_docstring.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_generic_class_with_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_member_field.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_ns_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_ns_member.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_truncation_hint.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_builtin_deep_copy_with_compiler2_visible_files.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_builtin_string_with_compiler2_visible_files.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_class.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_class_with_refs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__grep_tests__grep_semantic_result_snapshot.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/assignments.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/bigints.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/closures.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/control_flow.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/deep_copy.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/exceptions.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/floats.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/for_loops.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/fs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/functions.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/glob.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/ints.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/is_operator.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/json_auto_derive.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/lambdas.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/lexical_scoping.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/match_basics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/null_handling.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/optional_function_parameters.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/patterns_new_runtime.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/reflect_type_of.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/spawn_semantics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/type_error_repro.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/type_reflection.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/typed_inputs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/watch.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/while_loops.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiler2_emit/baml_tests__compiler2_emit__optional_defaults_emit_snapshot.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__assert_std__/baml_tests__compiles____assert_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____03_hir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__testing_std__/baml_tests__compiles____testing_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/closure_loop_variable/baml_tests__compiles__closure_loop_variable__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/closures/baml_tests__compiles__closures__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/comment_in_type/baml_tests__compiles__comment_in_type__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__01_lexer__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__02_parser__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__03_hir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__10_formatter__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/function_call/baml_tests__compiles__function_call__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/host_callable_call/baml_tests__compiles__host_callable_call__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lambda_advanced/baml_tests__compiles__lambda_advanced__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lambda_fat_arrow/baml_tests__compiles__lambda_fat_arrow__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lexical_scoping/baml_tests__compiles__lexical_scoping__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/literal_union_arithmetic/baml_tests__compiles__literal_union_arithmetic__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/optional_function_parameters/baml_tests__compiles__optional_function_parameters__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/parser_expressions/baml_tests__compiles__parser_expressions__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/parser_statements/baml_tests__compiles__parser_statements__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/patterns_new/baml_tests__compiles__patterns_new__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/testset_vibes_nested/baml_tests__compiles__testset_vibes_nested__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/match_exhaustiveness/baml_tests__diagnostic_errors__match_exhaustiveness__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/match_exhaustiveness/baml_tests__diagnostic_errors__match_exhaustiveness__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/patterns_new/baml_tests__diagnostic_errors__patterns_new__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/patterns_new/baml_tests__diagnostic_errors__patterns_new__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/simple_type_error/baml_tests__diagnostic_errors__simple_type_error__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/simple_type_error/baml_tests__diagnostic_errors__simple_type_error__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/test_expr_name_type_error/baml_tests__diagnostic_errors__test_expr_name_type_error__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/test_expr_name_type_error/baml_tests__diagnostic_errors__test_expr_name_type_error__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/engine/baml_tests__engine__tests__optional_dropping_adapter_preserves_source_defaults_bytecode.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/engine/baml_tests__engine__tests__source_call_omitted_default_executes_in_callee_scope_bytecode.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/src/compiler2_tir/snapshots/baml_tests__compiler2_tir__phase5__snapshot_baml_package_items.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap is excluded by !**/*.snap
  • baml_language/forks/aws-config-systest/Cargo.lock is excluded by !**/*.lock
  • baml_language/forks/google-cloud-auth-systest/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (127)
  • .envrc
  • .github/actions/setup-mise/action.yml
  • .github/workflows/cargo-tests.reusable.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/size-gate-baseline-refresh.yml
  • baml_language/.cargo/size-gate.toml
  • baml_language/.ci/size-gate/aarch64-apple-darwin.toml
  • baml_language/.ci/size-gate/wasm32-unknown-unknown.toml
  • baml_language/.ci/size-gate/x86_64-pc-windows-msvc.toml
  • baml_language/.ci/size-gate/x86_64-unknown-linux-gnu.toml
  • baml_language/.gitignore
  • baml_language/Cargo.toml
  • baml_language/crates/baml_cli/src/describe_command.rs
  • baml_language/crates/baml_cli/src/describe_command_tests.rs
  • baml_language/crates/baml_cli/src/generate.rs
  • baml_language/crates/baml_cli/src/grep_command.rs
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/baml_compiler2_hir/src/file_package.rs
  • baml_language/crates/baml_compiler2_mir/src/lower.rs
  • baml_language/crates/baml_compiler2_ppir/src/lib.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs
  • baml_language/crates/baml_compiler2_tir/src/infer_context.rs
  • baml_language/crates/baml_compiler2_tir/src/ty.rs
  • baml_language/crates/baml_lsp2_actions/src/check.rs
  • baml_language/crates/baml_lsp2_actions/src/describe.rs
  • baml_language/crates/baml_lsp2_actions/src/testing.rs
  • baml_language/crates/baml_lsp2_actions/src/type_info.rs
  • baml_language/crates/baml_lsp2_actions/src/type_info_tests.rs
  • baml_language/crates/baml_lsp2_actions/src/utils.rs
  • baml_language/crates/baml_lsp2_actions_tests/src/runner.rs
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/catch/exhaustiveness_checks.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/hover/class.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/hover/out_of_body_implements_class.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/invalid_assignment/compound_ops.baml
  • baml_language/crates/baml_tests/Cargo.toml
  • baml_language/crates/baml_tests/baml_src/ns_floats/floats.baml
  • baml_language/crates/baml_tests/benches/compiler_benchmark.rs
  • baml_language/crates/baml_tests/benches/runtime_benchmark.rs
  • baml_language/crates/baml_tests/build.rs
  • baml_language/crates/baml_tests/projects/compiles/event_system/main.baml
  • baml_language/crates/baml_tests/tests/dispatch.rs
  • baml_language/crates/baml_tests/tests/interfaces.rs
  • baml_language/crates/baml_tests/tests/optimization.rs
  • baml_language/crates/baml_tests/tests/spawn_specialization.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_project/Cargo.toml
  • baml_language/crates/bex_project/src/lib.rs
  • baml_language/crates/bex_vm/Cargo.toml
  • baml_language/crates/bex_vm/src/debug.rs
  • baml_language/crates/bex_vm/src/indexable.rs
  • baml_language/crates/bex_vm/src/kperf.rs
  • baml_language/crates/bex_vm/src/lib.rs
  • baml_language/crates/bex_vm/src/package_baml/type_class.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/bex_vm_types/src/bytecode.rs
  • baml_language/crates/bridge_cffi/src/lib.rs
  • baml_language/crates/sys_llm/Cargo.toml
  • baml_language/crates/sys_llm/src/auth_request/bedrock.rs
  • baml_language/crates/sys_llm/src/auth_request/vertex.rs
  • baml_language/crates/sys_llm/src/build_request/bedrock.rs
  • baml_language/crates/sys_llm/src/lib.rs
  • baml_language/crates/sys_llm/src/wasm.rs
  • baml_language/crates/tools_size_gate/Cargo.toml
  • baml_language/crates/tools_size_gate/src/ceilings.rs
  • baml_language/crates/tools_size_gate/src/fetch.rs
  • baml_language/crates/tools_size_gate/src/main.rs
  • baml_language/crates/tools_size_gate/src/measure.rs
  • baml_language/crates/tools_size_gate/src/output.rs
  • baml_language/forks/aws-bedrock/Cargo.toml
  • baml_language/forks/aws-bedrock/src/lib.rs
  • baml_language/forks/aws-config-systest/Cargo.toml
  • baml_language/forks/aws-config-systest/README.md
  • baml_language/forks/aws-config-systest/run-e2e.py
  • baml_language/forks/aws-config-systest/src/main.rs
  • baml_language/forks/aws-config/Cargo.toml
  • baml_language/forks/aws-config/src/ini.rs
  • baml_language/forks/aws-config/src/lib.rs
  • baml_language/forks/aws-config/src/profile.rs
  • baml_language/forks/aws-config/src/providers.rs
  • baml_language/forks/aws-config/tests/chain_precedence.rs
  • baml_language/forks/aws-config/tests/common/mod.rs
  • baml_language/forks/aws-config/tests/common_smoke.rs
  • baml_language/forks/aws-config/tests/credential_process.rs
  • baml_language/forks/aws-config/tests/ecs_container.rs
  • baml_language/forks/aws-config/tests/env_credentials.rs
  • baml_language/forks/aws-config/tests/imds.rs
  • baml_language/forks/aws-config/tests/ini_parsing.rs
  • baml_language/forks/aws-config/tests/json_credentials.rs
  • baml_language/forks/aws-config/tests/profile_static.rs
  • baml_language/forks/aws-config/tests/region_resolution.rs
  • baml_language/forks/aws-config/tests/sso_provider.rs
  • baml_language/forks/aws-sigv4/Cargo.toml
  • baml_language/forks/aws-sigv4/src/lib.rs
  • baml_language/forks/google-cloud-auth-systest/Cargo.toml
  • baml_language/forks/google-cloud-auth-systest/README.md
  • baml_language/forks/google-cloud-auth-systest/run-e2e.py
  • baml_language/forks/google-cloud-auth-systest/src/main.rs
  • baml_language/forks/google-cloud-auth/Cargo.toml
  • baml_language/forks/google-cloud-auth/src/lib.rs
  • baml_language/forks/google-cloud-auth/tests/adc_precedence.rs
  • baml_language/forks/google-cloud-auth/tests/auth_flows.rs
  • baml_language/forks/google-cloud-auth/tests/common/mod.rs
  • baml_language/mise.toml
  • baml_language/scripts/bench-compare
  • baml_language/sdk_tests/harness_setup/Cargo.toml
  • baml_language/sdk_tests/harness_setup/src/lib.rs
  • baml_language/sdk_tests/harness_setup/src/python_pydantic2.rs
  • baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi
  • baml_language/sdks/python/rust/bridge_python/src/runtime.rs
  • baml_language/sdks/python/rust/codegen_python/src/lib.rs
  • baml_language/sdks/python/src/baml_core/baml_py.pyi
  • baml_language/stow.toml
  • baml_language/tools/speedtest/export_baml.py
  • baml_language/tools/speedtest/workloads/compute/mixed-ops-5k.md
  • baml_language/tools/speedtest/workloads/compute/wide-nested-class-create-50k.md
  • baml_language/tools_sccache/Cargo.toml
  • baml_language/tools_sccache/src/main.rs
  • mise.toml
  • tools/baml-bench/.env.example
  • tools/baml-bench/.gitignore
  • tools/baml-bench/README.md
  • tools/baml-bench/convex/bamlBuilds.ts
  • tools/baml-bench/convex/crons.ts
  • tools/baml-bench/convex/issues.ts
  • tools/baml-bench/convex/lib.ts
  • tools/baml-bench/convex/maintenance.ts
💤 Files with no reviewable changes (1)
  • baml_language/crates/baml_tests/projects/compiles/event_system/main.baml
✅ Files skipped from review due to trivial changes (8)
  • baml_language/.gitignore
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/catch/exhaustiveness_checks.baml
  • baml_language/.ci/size-gate/x86_64-pc-windows-msvc.toml
  • baml_language/.ci/size-gate/aarch64-apple-darwin.toml
  • baml_language/crates/baml_tests/Cargo.toml
  • baml_language/crates/baml_lsp2_actions/src/check.rs
  • baml_language/.ci/size-gate/wasm32-unknown-unknown.toml
  • baml_language/.ci/size-gate/x86_64-unknown-linux-gnu.toml

@hellovai hellovai changed the title refactor(tracing): trim existing event-stream/tracing surface for fresh start refactor(tracing) + perf(vm): trim event-stream surface for a fresh start; CPython-aligned VM superinstructions Jun 2, 2026

@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.

Caution

Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 7

Caution

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

⚠️ Outside diff range comments (1)
baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs (1)

99-124: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Include generic args in Ctor::Class identity.

PartialEq, Hash, and covers still collapse Foo<int> and Foo<string> into the same ctor because they only compare/hash the QualifiedTypeName. That breaks dedup_ctors, present_set, and row specialization for unions containing multiple instantiations of the same generic class, so one arm can incorrectly exhaust or shadow another. Keep the class args in ctor identity as well, preferably via ty_ctor_identity(...) so attrs/freshness stay ignored consistently.

Suggested fix
 impl PartialEq for Ctor {
     fn eq(&self, other: &Self) -> bool {
         use Ctor::{
             Class, Interface, Missing, NonExhaustive, Or, Single, Slice, UnionMember, Wildcard,
         };
         match (self, other) {
             (Single(a), Single(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Slice(a), Slice(b)) => a == b,
-            (Class(a, _), Class(b, _)) => a == b,
+            (Class(a, a_args), Class(b, b_args)) => {
+                a == b
+                    && a_args.len() == b_args.len()
+                    && a_args
+                        .iter()
+                        .zip(b_args)
+                        .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b))
+            }
             (Interface(a), Interface(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (UnionMember(a), UnionMember(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Or, Or)
             | (Wildcard, Wildcard)
             | (NonExhaustive, NonExhaustive)
@@
         std::mem::discriminant(self).hash(state);
         match self {
             Single(ty) => ty_ctor_identity(ty).hash(state),
             Slice(s) => s.hash(state),
-            Class(qtn, _) => {
+            Class(qtn, args) => {
                 qtn.hash(state);
+                for arg in args {
+                    ty_ctor_identity(arg).hash(state);
+                }
             }
             Interface(ty) => ty_ctor_identity(ty).hash(state),
             UnionMember(ty) => ty_ctor_identity(ty).hash(state),
             Or | Wildcard | NonExhaustive | Missing => {}
         }
@@
         use Ctor::{
             Class, Interface, Missing, NonExhaustive, Or, Single, Slice, UnionMember, Wildcard,
         };
         match (self, other) {
             (Wildcard, _) => true,
             (_, Wildcard) => false,
             (Single(a), Single(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
-            (Class(a, _), Class(b, _)) => a == b,
+            (Class(a, a_args), Class(b, b_args)) => {
+                a == b
+                    && a_args.len() == b_args.len()
+                    && a_args
+                        .iter()
+                        .zip(b_args)
+                        .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b))
+            }
             (Interface(a), Interface(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (Slice(a), Slice(b)) => slice_covers(a, b),
             (UnionMember(a), UnionMember(b)) => ty_ctor_identity(a) == ty_ctor_identity(b),
             (NonExhaustive, NonExhaustive) => true,
             (Missing, Missing) => true,
@@
 fn class_ty_for_ctor(qtn: &QualifiedTypeName, args: &[Ty], fallback: &Ty) -> Ty {
     match fallback {
-        Ty::Class(fallback_qtn, _, _) if fallback_qtn == qtn => fallback.clone(),
+        Ty::Class(fallback_qtn, fallback_args, _)
+            if fallback_qtn == qtn
+                && fallback_args.len() == args.len()
+                && fallback_args
+                    .iter()
+                    .zip(args)
+                    .all(|(a, b)| ty_ctor_identity(a) == ty_ctor_identity(b)) =>
+        {
+            fallback.clone()
+        }
         _ => Ty::Class(qtn.clone(), args.to_vec(), TyAttr::default()),
     }
 }

Also applies to: 183-199

🤖 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/crates/baml_compiler2_tir/src/exhaustiveness.rs` around lines
99 - 124, The Class ctor currently ignores generic args when computing identity
(PartialEq, Hash, and covers), so Foo<int> and Foo<string> collapse; update all
Class handling to include the type+args via ty_ctor_identity instead of only the
QualifiedTypeName: in the PartialEq match arm that now reads (Class(a, _),
Class(b, _)) => a == b, call ty_ctor_identity on the full class type constructed
from the QualifiedTypeName and its args (use the same tuple fields, e.g.
Class(qtn, args)) and compare those identities; likewise in std::hash::Hash for
Ctor replace hashing qtn in the Class arm with hashing ty_ctor_identity(...) of
the full class type; also make the same change in covers and anywhere else
referenced (dedup_ctors / present_set logic—see the other Class handling around
lines ~183-199) so class generic arguments participate in ctor identity
consistently.
🧹 Nitpick comments (8)
.github/workflows/cargo-tests.reusable.yaml (2)

803-822: 💤 Low value

Missing blank line before sccache-stats job definition.

There's no blank line between the end of cargo-timings job (line 803) and the start of sccache-stats job (line 804), unlike other job definitions in this file. Minor formatting inconsistency.

Suggested fix
           delete-merged: true
+
   sccache-stats:
     name: "sccache stats (merged)"
🤖 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 @.github/workflows/cargo-tests.reusable.yaml around lines 803 - 822, Add a
blank line before the sccache-stats job definition to match the file's job
separation style: insert one empty line between the end of the previous job
(cargo-timings) and the start of the job named "sccache-stats" so the job block
beginning with "sccache-stats:" sits on its own paragraph.

83-89: ⚖️ Poor tradeoff

Consider pinning third-party actions to commit SHAs for supply-chain security.

Static analysis (zizmor) flags multiple unpinned action references throughout this workflow (e.g., Swatinem/rust-cache@v2, actions/upload-artifact@v7, taiki-e/install-action@v2). Pinning to commit SHAs instead of tags prevents silent changes from compromised or force-pushed tags.

This is a general hardening measure. If the repo's policy accepts tag-based versioning for trusted actions, this can be deferred.

🤖 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 @.github/workflows/cargo-tests.reusable.yaml around lines 83 - 89, Replace
tag-based action references with their corresponding commit SHAs to harden
supply-chain security: locate occurrences of Swatinem/rust-cache@v2,
actions/upload-artifact@v7, taiki-e/install-action@v2 (and any other third-party
actions in the workflow) and update each reference to the full commit SHA (e.g.,
Swatinem/rust-cache@<sha>) obtained from the action repository, then run the
workflow locally or in a branch to verify the SHA pins work as expected.
baml_language/crates/baml_compiler2_emit/src/emit.rs (1)

1137-1152: 💤 Low value

emit_store_var loses metadata for the first store when fusing StoreVar2.

When fusing StoreVar(a); StoreVar(slot) into StoreVar2(a, slot), the metadata for slot a (set by the previous emit_store_var call at line 1150-1151) is overwritten by set_var_operand(n - 1, slot) at line 1146. The operand metadata will only reflect the second slot name, not the first.

For debugging/disassembly, both slot names should ideally be preserved. Consider whether StoreVar2 needs a different OperandMeta variant that holds both names, or if this is acceptable given the instruction semantics.

🤖 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/crates/baml_compiler2_emit/src/emit.rs` around lines 1137 -
1152, emit_store_var currently fuses a previous StoreVar(a) and a new
StoreVar(slot) into Instruction::StoreVar2(a, slot) but then calls
set_var_operand(n - 1, slot), overwriting the metadata for the first operand;
preserve both operand metadata by extending the metadata model and update the
fusion logic: add a new OperandMeta variant (or storage) that can hold two slot
names for Instruction::StoreVar2, or modify set_var_operand to accept/attach
metadata for both operands, and when you replace bytecode.instructions[n - 1]
with Instruction::StoreVar2(a, slot) copy the original metadata for operand a
into the first-slot metadata and set the second-slot metadata to slot so
disassembly/debugging can see both names (symbols: emit_store_var,
Instruction::StoreVar2, set_var_operand, bytecode.instructions).
baml_language/crates/baml_cli/src/grep_command.rs (1)

447-469: ⚡ Quick win

Add a unit test that locks the method JSON schema.

This changes machine-readable CLI output and line-range serialization, so a focused unit test around description_to_json would catch silent drift cheaply. As per coding guidelines **/*.rs: Prefer writing Rust unit tests over integration tests where possible.

Also applies to: 506-507

🤖 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/crates/baml_cli/src/grep_command.rs` around lines 447 - 469,
Add a unit test that locks the JSON schema emitted by
description_to_json/method_json: create a small deterministic ProjectDatabase
fixture (or use an existing test helper), call description_to_json (which uses
method_json) for a known file/class with predictable docstring/signature and
line offsets, then assert the produced serde_json::Value includes the exact keys
("name","signature","docstring","file","line_start","line_end") and that
"line_start" and "line_end" are the expected integers and that "file" matches
the relative path; place the test as a Rust unit test in the same module as
method_json so it runs fast and fails on any silent output/schema drift.
baml_language/crates/baml_compiler2_mir/src/lower.rs (1)

11133-11147: ⚡ Quick win

Add a regression test for the new union/interface dispatch path.

This helper-only test won't catch the new method/field dispatch branches or the generic-interface runtime checks. A Rust unit test that exercises a concrete generic-class union calling an inherited/default interface method would lock this behavior down. As per coding guidelines, **/*.rs: Prefer writing Rust unit tests over integration tests where possible, and always run cargo test --lib if you changed any Rust code.

🤖 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/crates/baml_compiler2_mir/src/lower.rs` around lines 11133 -
11147, Add a regression unit test exercising the new union/interface dispatch
path by creating a concrete generic-class union that implements or inherits an
interface with a default method and then invoking that method through the union
to ensure generic-interface runtime checks and dispatch work; specifically, add
a new #[test] (similar place as
interface_tir_type_args_match_preserves_type_var_identity) that constructs a
generic class/union instance using concrete type parameters, ensures the
interface implementation/default method is available, calls the method via the
union dispatch path, and asserts the correct behavior so that
interface_tir_type_args_match_preserving_typevars and the new union/interface
dispatch are both validated at cargo test --lib time.
baml_language/crates/baml_tests/build.rs (1)

216-231: 💤 Low value

Consider guarding against empty slugs.

If a workload name consists entirely of non-alphanumeric characters (e.g., "!!!"), the slugify function will produce a single underscore which is then trimmed away at line 230, leaving an empty string. This would cause format_ident! at line 131 to panic at build time.

While workload names are controlled repo content and this scenario is extremely unlikely, a defensive fallback (e.g., slug.is_empty() check with a default like "unnamed") would make the build script more robust.

🛡️ Proposed defensive fallback
 fn slugify(name: &str) -> String {
     let mut out = String::with_capacity(name.len());
     let mut prev_underscore = false;
     for ch in name.chars() {
         if ch.is_ascii_alphanumeric() {
             out.extend(ch.to_lowercase());
             prev_underscore = false;
         } else if !prev_underscore {
             out.push('_');
             prev_underscore = true;
         }
     }
-    out.trim_matches('_').to_string()
+    let slug = out.trim_matches('_');
+    if slug.is_empty() {
+        "unnamed".to_string()
+    } else {
+        slug.to_string()
+    }
 }
🤖 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/crates/baml_tests/build.rs` around lines 216 - 231, The slugify
function can return an empty string for names with no alphanumeric characters;
after computing the slug in fn slugify(name: &str) -> String check if the result
is empty and return a safe fallback (e.g. "unnamed" or similar) so callers like
format_ident! don't panic; update slugify to ensure it never returns an empty
string (use the same lowercase/underscore logic but substitute the fallback when
slug.is_empty()).
baml_language/crates/baml_lsp2_actions/src/describe.rs (1)

1675-1680: ⚡ Quick win

Limit comment stripping to the covered CST subtree.

clean_body_source() scans tree.descendants_with_tokens() for every body extraction, so describing several symbols in one file turns into repeated full-file token walks. Each caller already passes a single item/member/function span, so starting from the covering node would keep this proportional to the rendered body instead of the whole file.

🤖 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/crates/baml_lsp2_actions/src/describe.rs` around lines 1675 -
1680, clean_body_source() currently walks tree.descendants_with_tokens() for
every extraction which causes full-file token scans; change it to locate the CST
node that covers the provided span (use the parser/tree covering node API for
the given span) and iterate only that node.descendants_with_tokens() (or the
subtree root) when building deletions and stripping comments. Update the logic
that builds deletions (currently iterating tree.descendants_with_tokens()) to
start from the covering node for the span passed into clean_body_source(),
keeping the rest of the comment-removal logic intact.
baml_language/crates/baml_compiler2_ppir/src/lib.rs (1)

139-161: ⚡ Quick win

Drop the custom salsa::Update impl unless you need behavior beyond the default fallback.

Salsa already falls back to a 'static + PartialEq comparison when a tracked return type has no Update impl, and ProjectExpansionMaps is already fully owned + PartialEq. Keeping a hand-written unsafe impl here adds unsafe maintenance surface without changing the early-cutoff behavior. Please verify this still compiles cleanly after removing the impl. (docs.rs)

Suggested simplification
-/// # Safety
-///
-/// Mirrors [`baml_compiler2_hir::package::PackageItems`]'s impl. The contained
-/// maps hold no Salsa-interned (`'db`) data, so storing them by value is sound;
-/// `maybe_update` uses `PartialEq` for proper Salsa early-cutoff.
-#[allow(unsafe_code)]
-unsafe impl salsa::Update for ProjectExpansionMaps {
-    unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
-        // SAFETY: `old_pointer` is valid, aligned, and Salsa-owned.
-        #[allow(unsafe_code)]
-        let old = unsafe { &*old_pointer };
-        if old == &new_value {
-            false
-        } else {
-            #[allow(unsafe_code)]
-            unsafe {
-                std::ptr::drop_in_place(old_pointer);
-                std::ptr::write(old_pointer, new_value);
-            }
-            true
-        }
-    }
-}
🤖 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/crates/baml_compiler2_ppir/src/lib.rs` around lines 139 - 161,
Remove the custom unsafe impl salsa::Update for ProjectExpansionMaps (including
the maybe_update function) because ProjectExpansionMaps is already owned and
PartialEq, so Salsa's default fallback handles early-cutoff; delete the entire
unsafe impl block that implements salsa::Update and its maybe_update logic, then
run a build to confirm it compiles cleanly and relies on the default Update
behavior.
🤖 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/Cargo.toml`:
- Around line 169-171: Bump the crypto crate version constraints in Cargo.toml:
leave hex as-is, update the hmac dependency (symbol: hmac) from "0.12" to the
newer "0.13" and update the sha1 dependency (symbol: sha1) from "0.10" to "0.11"
so Cargo can pick the latest compatible minor releases; run cargo update and
cargo build/test to ensure compatibility and fix any API changes if they
surface.

In `@baml_language/crates/baml_cli/src/grep_command.rs`:
- Around line 447-469: The method_json serializer (and the similar serializer at
lines 506-507) drops the previous per-method JSON keys ("kind" and the single
"line") in favor of "line_start"/"line_end", which will break existing
consumers; restore backward compatibility by emitting the old keys alongside the
new ones (e.g., include "kind": m.kind and "line": line_number_at_offset(text,
m.item_range.start().into())) or alternatively gate the richer shape behind a
version flag, updating method_json and the analogous function to populate both
legacy and new fields.

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 6473-6483: The loop over union members currently drops each
member's concrete type arguments (patterning Tir2Ty::Class(qtn, _, _)), so
class_member_method_candidates and later interface-candidate logic lose the
concrete instantiated type; update the match to capture the member's type_args
(e.g., Tir2Ty::Class(qtn, type_args, _)) and thread those concrete args into the
interface-call candidate derivation (so class_member_method_candidates /
candidate construction uses the member's instantiated type), ensuring candidates
include members that arise only via inherited/default/generic interface views or
blanket `implements ... for C` rules; also ensure try_lower_union_iface_dispatch
uses these preserved concrete-member candidates when deciding dispatch.

In `@baml_language/crates/baml_compiler2_tir/src/builder.rs`:
- Around line 12647-12655: The helper pattern_overlaps_scrut_member currently
checks overlap using is_subtype against each member from
flatten_union_optional_members; replace those is_subtype calls with the
runtime-aware types_overlap so pattern dispatch matches runtime behavior (avoid
numeric-tower false positives). Specifically, in pattern_overlaps_scrut_member
change the closure that does self.is_subtype(pat, m) || self.is_subtype(m, pat)
to use self.types_overlap(pat, m) (or an equivalent symmetric types_overlap
check) so the function uses types_overlap for both directions when iterating
union/optional members.
- Around line 13375-13418: The interface destructure branch (matching
Ty::Interface and calling interface_field_infos_ordered) is running before
checking for missing generic args, so type_args can be empty and fields are
lowered with the interface's generic parameters instead of the scrutinee's
concrete args; fix by adding the same missing-type-args guard used for class
destructures or by substituting the scrutinee's concrete interface type
arguments into class_ty before calling interface_field_infos_ordered (i.e., when
handling Ty::Interface(iface_name, type_args, _) use the concrete args from
scrut_ty or bail/report missing args, then proceed to call
interface_field_infos_ordered, analyze_and_lower, build sub_dpats and
DPat::interface, and finally compute matched_ty with
intersect_pattern_flow_types).
- Around line 8099-8137: union_interface_method_ambiguity currently only
considers in-body implements via implemented_interface_method_sources (losing
registry-only rules and distinct instantiations like Getter<int> vs
Getter<string>), so update it to also consult the
package_implements_registry(rule set) when gathering "sources" for a given
class_qtn: for each rule in registry.interface_impl_rules where
rule.for_ty_pattern is the class_qtn and rule.interface_ty is one of the union
interfaces, check whether that rule provides the requested member (reusing
try_registry_member/resolve logic) and add the corresponding (interface QTN +
instantiation args) to the sources list; also stop collapsing members to bare
interface QTN — preserve the interface instantiation (e.g., keep
(QualifiedTypeName, Vec<Ty>) keys) so same-interface different-instantiation
cases are treated as distinct, and deduplicate sources before checking
sources.len() >= 2.

In `@baml_language/crates/baml_lsp2_actions/src/describe.rs`:
- Around line 1507-1523: The function collect_qtn_dep currently drops the
namespace by using qtn.name() and dedupes/looks up by short name, which causes
collisions (e.g., root.a.User vs root.b.User); change it to use the full
qualified path from qtn (e.g., qtn.to_string() or qtn.full_path()) for both
deduplication (the seen HashSet) and for resolving the dependency so you call
resolve_dep_from_outline with the qualified name instead of qtn.name(); update
references to the HashSet key and the resolve_dep_from_outline argument
accordingly in collect_qtn_dep so deps and seen are keyed by the full identity
and duplicates are correctly suppressed.

---

Outside diff comments:
In `@baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs`:
- Around line 99-124: The Class ctor currently ignores generic args when
computing identity (PartialEq, Hash, and covers), so Foo<int> and Foo<string>
collapse; update all Class handling to include the type+args via
ty_ctor_identity instead of only the QualifiedTypeName: in the PartialEq match
arm that now reads (Class(a, _), Class(b, _)) => a == b, call ty_ctor_identity
on the full class type constructed from the QualifiedTypeName and its args (use
the same tuple fields, e.g. Class(qtn, args)) and compare those identities;
likewise in std::hash::Hash for Ctor replace hashing qtn in the Class arm with
hashing ty_ctor_identity(...) of the full class type; also make the same change
in covers and anywhere else referenced (dedup_ctors / present_set logic—see the
other Class handling around lines ~183-199) so class generic arguments
participate in ctor identity consistently.

---

Nitpick comments:
In @.github/workflows/cargo-tests.reusable.yaml:
- Around line 803-822: Add a blank line before the sccache-stats job definition
to match the file's job separation style: insert one empty line between the end
of the previous job (cargo-timings) and the start of the job named
"sccache-stats" so the job block beginning with "sccache-stats:" sits on its own
paragraph.
- Around line 83-89: Replace tag-based action references with their
corresponding commit SHAs to harden supply-chain security: locate occurrences of
Swatinem/rust-cache@v2, actions/upload-artifact@v7, taiki-e/install-action@v2
(and any other third-party actions in the workflow) and update each reference to
the full commit SHA (e.g., Swatinem/rust-cache@<sha>) obtained from the action
repository, then run the workflow locally or in a branch to verify the SHA pins
work as expected.

In `@baml_language/crates/baml_cli/src/grep_command.rs`:
- Around line 447-469: Add a unit test that locks the JSON schema emitted by
description_to_json/method_json: create a small deterministic ProjectDatabase
fixture (or use an existing test helper), call description_to_json (which uses
method_json) for a known file/class with predictable docstring/signature and
line offsets, then assert the produced serde_json::Value includes the exact keys
("name","signature","docstring","file","line_start","line_end") and that
"line_start" and "line_end" are the expected integers and that "file" matches
the relative path; place the test as a Rust unit test in the same module as
method_json so it runs fast and fails on any silent output/schema drift.

In `@baml_language/crates/baml_compiler2_emit/src/emit.rs`:
- Around line 1137-1152: emit_store_var currently fuses a previous StoreVar(a)
and a new StoreVar(slot) into Instruction::StoreVar2(a, slot) but then calls
set_var_operand(n - 1, slot), overwriting the metadata for the first operand;
preserve both operand metadata by extending the metadata model and update the
fusion logic: add a new OperandMeta variant (or storage) that can hold two slot
names for Instruction::StoreVar2, or modify set_var_operand to accept/attach
metadata for both operands, and when you replace bytecode.instructions[n - 1]
with Instruction::StoreVar2(a, slot) copy the original metadata for operand a
into the first-slot metadata and set the second-slot metadata to slot so
disassembly/debugging can see both names (symbols: emit_store_var,
Instruction::StoreVar2, set_var_operand, bytecode.instructions).

In `@baml_language/crates/baml_compiler2_mir/src/lower.rs`:
- Around line 11133-11147: Add a regression unit test exercising the new
union/interface dispatch path by creating a concrete generic-class union that
implements or inherits an interface with a default method and then invoking that
method through the union to ensure generic-interface runtime checks and dispatch
work; specifically, add a new #[test] (similar place as
interface_tir_type_args_match_preserves_type_var_identity) that constructs a
generic class/union instance using concrete type parameters, ensures the
interface implementation/default method is available, calls the method via the
union dispatch path, and asserts the correct behavior so that
interface_tir_type_args_match_preserving_typevars and the new union/interface
dispatch are both validated at cargo test --lib time.

In `@baml_language/crates/baml_compiler2_ppir/src/lib.rs`:
- Around line 139-161: Remove the custom unsafe impl salsa::Update for
ProjectExpansionMaps (including the maybe_update function) because
ProjectExpansionMaps is already owned and PartialEq, so Salsa's default fallback
handles early-cutoff; delete the entire unsafe impl block that implements
salsa::Update and its maybe_update logic, then run a build to confirm it
compiles cleanly and relies on the default Update behavior.

In `@baml_language/crates/baml_lsp2_actions/src/describe.rs`:
- Around line 1675-1680: clean_body_source() currently walks
tree.descendants_with_tokens() for every extraction which causes full-file token
scans; change it to locate the CST node that covers the provided span (use the
parser/tree covering node API for the given span) and iterate only that
node.descendants_with_tokens() (or the subtree root) when building deletions and
stripping comments. Update the logic that builds deletions (currently iterating
tree.descendants_with_tokens()) to start from the covering node for the span
passed into clean_body_source(), keeping the rest of the comment-removal logic
intact.

In `@baml_language/crates/baml_tests/build.rs`:
- Around line 216-231: The slugify function can return an empty string for names
with no alphanumeric characters; after computing the slug in fn slugify(name:
&str) -> String check if the result is empty and return a safe fallback (e.g.
"unnamed" or similar) so callers like format_ident! don't panic; update slugify
to ensure it never returns an empty string (use the same lowercase/underscore
logic but substitute the fallback when slug.is_empty()).
🪄 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: d0142bf5-233d-4074-acb2-a1a87b42c87d

📥 Commits

Reviewing files that changed from the base of the PR and between 855fa73 and 99f624d.

⛔ Files ignored due to path filters (97)
  • baml_language/Cargo.lock is excluded by !**/*.lock
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__control_describe_shallow_namespace_item_works.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__describe_user_method_drill_in_shows_body.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__dispatch_root_prefix_resolves_user_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__fixed_describe_deep_namespace_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__fixed_describe_deep_namespace_member.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_builtin_package_listing.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_alias_string.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_deep_copy.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_item_by_definition.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_builtin_string.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class_with_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_class_with_static_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_enum.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_function_with_docstring.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_generic_class_with_methods.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_member_field.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_ns_item.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_ns_member.snap is excluded by !**/*.snap
  • baml_language/crates/baml_cli/src/snapshots/baml_cli__describe_command_tests__render_describe_truncation_hint.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_builtin_deep_copy_with_compiler2_visible_files.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_builtin_string_with_compiler2_visible_files.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_class.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__describe_tests__describe_class_with_refs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_lsp2_actions/src/snapshots/baml_lsp2_actions__grep_tests__grep_semantic_result_snapshot.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/assignments.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/bigints.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/closures.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/control_flow.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/deep_copy.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/exceptions.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/floats.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/for_loops.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/fs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/functions.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/glob.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/ints.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/is_operator.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/json_auto_derive.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/lambdas.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/lexical_scoping.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/match_basics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/null_handling.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/optional_function_parameters.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/patterns_new_runtime.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/reflect_type_of.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/spawn_semantics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/type_error_repro.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/type_reflection.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/typed_inputs.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/watch.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/baml_src/while_loops.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiler2_emit/baml_tests__compiler2_emit__optional_defaults_emit_snapshot.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__assert_std__/baml_tests__compiles____assert_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____03_hir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__baml_std__/baml_tests__compiles____baml_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/__testing_std__/baml_tests__compiles____testing_std____06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/closure_loop_variable/baml_tests__compiles__closure_loop_variable__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/closures/baml_tests__compiles__closures__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/comment_in_type/baml_tests__compiles__comment_in_type__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__01_lexer__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__02_parser__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__03_hir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__04_5_mir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/event_system/baml_tests__compiles__event_system__10_formatter__main.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/function_call/baml_tests__compiles__function_call__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/host_callable_call/baml_tests__compiles__host_callable_call__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lambda_advanced/baml_tests__compiles__lambda_advanced__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lambda_fat_arrow/baml_tests__compiles__lambda_fat_arrow__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/lexical_scoping/baml_tests__compiles__lexical_scoping__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/literal_union_arithmetic/baml_tests__compiles__literal_union_arithmetic__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/optional_function_parameters/baml_tests__compiles__optional_function_parameters__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/parser_expressions/baml_tests__compiles__parser_expressions__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/parser_statements/baml_tests__compiles__parser_statements__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/patterns_new/baml_tests__compiles__patterns_new__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/compiles/testset_vibes_nested/baml_tests__compiles__testset_vibes_nested__06_codegen.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/match_exhaustiveness/baml_tests__diagnostic_errors__match_exhaustiveness__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/match_exhaustiveness/baml_tests__diagnostic_errors__match_exhaustiveness__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/patterns_new/baml_tests__diagnostic_errors__patterns_new__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/patterns_new/baml_tests__diagnostic_errors__patterns_new__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/simple_type_error/baml_tests__diagnostic_errors__simple_type_error__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/simple_type_error/baml_tests__diagnostic_errors__simple_type_error__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/test_expr_name_type_error/baml_tests__diagnostic_errors__test_expr_name_type_error__04_tir.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/diagnostic_errors/test_expr_name_type_error/baml_tests__diagnostic_errors__test_expr_name_type_error__05_diagnostics.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/engine/baml_tests__engine__tests__optional_dropping_adapter_preserves_source_defaults_bytecode.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/snapshots/engine/baml_tests__engine__tests__source_call_omitted_default_executes_in_callee_scope_bytecode.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/src/compiler2_tir/snapshots/baml_tests__compiler2_tir__phase5__snapshot_baml_package_items.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_expanded_unoptimized.snap is excluded by !**/*.snap
  • baml_language/crates/baml_tests/tests/bytecode_format/snapshots/bytecode_format__bytecode_display_textual.snap is excluded by !**/*.snap
  • baml_language/forks/aws-config-systest/Cargo.lock is excluded by !**/*.lock
  • baml_language/forks/google-cloud-auth-systest/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (127)
  • .envrc
  • .github/actions/setup-mise/action.yml
  • .github/workflows/cargo-tests.reusable.yaml
  • .github/workflows/ci.yaml
  • .github/workflows/size-gate-baseline-refresh.yml
  • baml_language/.cargo/size-gate.toml
  • baml_language/.ci/size-gate/aarch64-apple-darwin.toml
  • baml_language/.ci/size-gate/wasm32-unknown-unknown.toml
  • baml_language/.ci/size-gate/x86_64-pc-windows-msvc.toml
  • baml_language/.ci/size-gate/x86_64-unknown-linux-gnu.toml
  • baml_language/.gitignore
  • baml_language/Cargo.toml
  • baml_language/crates/baml_cli/src/describe_command.rs
  • baml_language/crates/baml_cli/src/describe_command_tests.rs
  • baml_language/crates/baml_cli/src/generate.rs
  • baml_language/crates/baml_cli/src/grep_command.rs
  • baml_language/crates/baml_compiler2_emit/src/emit.rs
  • baml_language/crates/baml_compiler2_hir/src/file_package.rs
  • baml_language/crates/baml_compiler2_mir/src/lower.rs
  • baml_language/crates/baml_compiler2_ppir/src/lib.rs
  • baml_language/crates/baml_compiler2_tir/src/builder.rs
  • baml_language/crates/baml_compiler2_tir/src/exhaustiveness.rs
  • baml_language/crates/baml_compiler2_tir/src/infer_context.rs
  • baml_language/crates/baml_compiler2_tir/src/ty.rs
  • baml_language/crates/baml_lsp2_actions/src/check.rs
  • baml_language/crates/baml_lsp2_actions/src/describe.rs
  • baml_language/crates/baml_lsp2_actions/src/testing.rs
  • baml_language/crates/baml_lsp2_actions/src/type_info.rs
  • baml_language/crates/baml_lsp2_actions/src/type_info_tests.rs
  • baml_language/crates/baml_lsp2_actions/src/utils.rs
  • baml_language/crates/baml_lsp2_actions_tests/src/runner.rs
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/catch/exhaustiveness_checks.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/hover/class.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/hover/out_of_body_implements_class.baml
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/invalid_assignment/compound_ops.baml
  • baml_language/crates/baml_tests/Cargo.toml
  • baml_language/crates/baml_tests/baml_src/ns_floats/floats.baml
  • baml_language/crates/baml_tests/benches/compiler_benchmark.rs
  • baml_language/crates/baml_tests/benches/runtime_benchmark.rs
  • baml_language/crates/baml_tests/build.rs
  • baml_language/crates/baml_tests/projects/compiles/event_system/main.baml
  • baml_language/crates/baml_tests/tests/dispatch.rs
  • baml_language/crates/baml_tests/tests/interfaces.rs
  • baml_language/crates/baml_tests/tests/optimization.rs
  • baml_language/crates/baml_tests/tests/spawn_specialization.rs
  • baml_language/crates/bex_engine/src/lib.rs
  • baml_language/crates/bex_project/Cargo.toml
  • baml_language/crates/bex_project/src/lib.rs
  • baml_language/crates/bex_vm/Cargo.toml
  • baml_language/crates/bex_vm/src/debug.rs
  • baml_language/crates/bex_vm/src/indexable.rs
  • baml_language/crates/bex_vm/src/kperf.rs
  • baml_language/crates/bex_vm/src/lib.rs
  • baml_language/crates/bex_vm/src/package_baml/type_class.rs
  • baml_language/crates/bex_vm/src/vm.rs
  • baml_language/crates/bex_vm_types/src/bytecode.rs
  • baml_language/crates/bridge_cffi/src/lib.rs
  • baml_language/crates/sys_llm/Cargo.toml
  • baml_language/crates/sys_llm/src/auth_request/bedrock.rs
  • baml_language/crates/sys_llm/src/auth_request/vertex.rs
  • baml_language/crates/sys_llm/src/build_request/bedrock.rs
  • baml_language/crates/sys_llm/src/lib.rs
  • baml_language/crates/sys_llm/src/wasm.rs
  • baml_language/crates/tools_size_gate/Cargo.toml
  • baml_language/crates/tools_size_gate/src/ceilings.rs
  • baml_language/crates/tools_size_gate/src/fetch.rs
  • baml_language/crates/tools_size_gate/src/main.rs
  • baml_language/crates/tools_size_gate/src/measure.rs
  • baml_language/crates/tools_size_gate/src/output.rs
  • baml_language/forks/aws-bedrock/Cargo.toml
  • baml_language/forks/aws-bedrock/src/lib.rs
  • baml_language/forks/aws-config-systest/Cargo.toml
  • baml_language/forks/aws-config-systest/README.md
  • baml_language/forks/aws-config-systest/run-e2e.py
  • baml_language/forks/aws-config-systest/src/main.rs
  • baml_language/forks/aws-config/Cargo.toml
  • baml_language/forks/aws-config/src/ini.rs
  • baml_language/forks/aws-config/src/lib.rs
  • baml_language/forks/aws-config/src/profile.rs
  • baml_language/forks/aws-config/src/providers.rs
  • baml_language/forks/aws-config/tests/chain_precedence.rs
  • baml_language/forks/aws-config/tests/common/mod.rs
  • baml_language/forks/aws-config/tests/common_smoke.rs
  • baml_language/forks/aws-config/tests/credential_process.rs
  • baml_language/forks/aws-config/tests/ecs_container.rs
  • baml_language/forks/aws-config/tests/env_credentials.rs
  • baml_language/forks/aws-config/tests/imds.rs
  • baml_language/forks/aws-config/tests/ini_parsing.rs
  • baml_language/forks/aws-config/tests/json_credentials.rs
  • baml_language/forks/aws-config/tests/profile_static.rs
  • baml_language/forks/aws-config/tests/region_resolution.rs
  • baml_language/forks/aws-config/tests/sso_provider.rs
  • baml_language/forks/aws-sigv4/Cargo.toml
  • baml_language/forks/aws-sigv4/src/lib.rs
  • baml_language/forks/google-cloud-auth-systest/Cargo.toml
  • baml_language/forks/google-cloud-auth-systest/README.md
  • baml_language/forks/google-cloud-auth-systest/run-e2e.py
  • baml_language/forks/google-cloud-auth-systest/src/main.rs
  • baml_language/forks/google-cloud-auth/Cargo.toml
  • baml_language/forks/google-cloud-auth/src/lib.rs
  • baml_language/forks/google-cloud-auth/tests/adc_precedence.rs
  • baml_language/forks/google-cloud-auth/tests/auth_flows.rs
  • baml_language/forks/google-cloud-auth/tests/common/mod.rs
  • baml_language/mise.toml
  • baml_language/scripts/bench-compare
  • baml_language/sdk_tests/harness_setup/Cargo.toml
  • baml_language/sdk_tests/harness_setup/src/lib.rs
  • baml_language/sdk_tests/harness_setup/src/python_pydantic2.rs
  • baml_language/sdks/python/rust/bridge_python/src/baml_core/baml_py/__init__.pyi
  • baml_language/sdks/python/rust/bridge_python/src/runtime.rs
  • baml_language/sdks/python/rust/codegen_python/src/lib.rs
  • baml_language/sdks/python/src/baml_core/baml_py.pyi
  • baml_language/stow.toml
  • baml_language/tools/speedtest/export_baml.py
  • baml_language/tools/speedtest/workloads/compute/mixed-ops-5k.md
  • baml_language/tools/speedtest/workloads/compute/wide-nested-class-create-50k.md
  • baml_language/tools_sccache/Cargo.toml
  • baml_language/tools_sccache/src/main.rs
  • mise.toml
  • tools/baml-bench/.env.example
  • tools/baml-bench/.gitignore
  • tools/baml-bench/README.md
  • tools/baml-bench/convex/bamlBuilds.ts
  • tools/baml-bench/convex/crons.ts
  • tools/baml-bench/convex/issues.ts
  • tools/baml-bench/convex/lib.ts
  • tools/baml-bench/convex/maintenance.ts
💤 Files with no reviewable changes (1)
  • baml_language/crates/baml_tests/projects/compiles/event_system/main.baml
✅ Files skipped from review due to trivial changes (8)
  • baml_language/.gitignore
  • baml_language/crates/baml_lsp2_actions_tests/test_files/syntax/catch/exhaustiveness_checks.baml
  • baml_language/.ci/size-gate/x86_64-pc-windows-msvc.toml
  • baml_language/.ci/size-gate/aarch64-apple-darwin.toml
  • baml_language/crates/baml_tests/Cargo.toml
  • baml_language/crates/baml_lsp2_actions/src/check.rs
  • baml_language/.ci/size-gate/wasm32-unknown-unknown.toml
  • baml_language/.ci/size-gate/x86_64-unknown-linux-gnu.toml
🛑 Comments failed to post (7)
baml_language/Cargo.toml (1)

169-171: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check latest versions of the added crypto crates
cargo search hex --limit 1
cargo search hmac --limit 1
cargo search sha1 --limit 1

Repository: BoundaryML/baml

Length of output: 839


Update crypto crate version constraints (hmac/sha1)
In baml_language/Cargo.toml (lines 169-171), hex = "0.4" already allows the latest hex (0.4.3), but hmac = "0.12" (latest 0.13.0) and sha1 = "0.10" (latest 0.11.0) constrain updates to older minor releases. Consider bumping those version constraints if the new forks remain compatible.

🤖 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/Cargo.toml` around lines 169 - 171, Bump the crypto crate
version constraints in Cargo.toml: leave hex as-is, update the hmac dependency
(symbol: hmac) from "0.12" to the newer "0.13" and update the sha1 dependency
(symbol: sha1) from "0.10" to "0.11" so Cargo can pick the latest compatible
minor releases; run cargo update and cargo build/test to ensure compatibility
and fix any API changes if they surface.
baml_language/crates/baml_cli/src/grep_command.rs (1)

447-469: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve the existing method JSON keys or version this schema change.

grep --json is a machine-facing surface. This helper drops the previous per-method kind/line fields in favor of line_start/line_end, which will break existing consumers with no compatibility path. Keep the old keys alongside the new metadata, or gate the richer shape behind a versioned flag.

Also applies to: 506-507

🤖 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/crates/baml_cli/src/grep_command.rs` around lines 447 - 469,
The method_json serializer (and the similar serializer at lines 506-507) drops
the previous per-method JSON keys ("kind" and the single "line") in favor of
"line_start"/"line_end", which will break existing consumers; restore backward
compatibility by emitting the old keys alongside the new ones (e.g., include
"kind": m.kind and "line": line_number_at_offset(text,
m.item_range.start().into())) or alternatively gate the richer shape behind a
version flag, updating method_json and the analogous function to populate both
legacy and new fields.
baml_language/crates/baml_compiler2_mir/src/lower.rs (1)

6473-6483: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Preserve the concrete union member when deriving interface-call candidates.

Line 6476 drops the member’s concrete type_args, and Lines 6529-6540 only look at in-body implements blocks. That means a union member that gets method only through an inherited/default interface view on a generic class, or only through implements ... for C / blanket rules, contributes no usable candidate here, so try_lower_union_iface_dispatch() falls back to the non-dispatch path for a valid call.

Also applies to: 6508-6541

🤖 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/crates/baml_compiler2_mir/src/lower.rs` around lines 6473 -
6483, The loop over union members currently drops each member's concrete type
arguments (patterning Tir2Ty::Class(qtn, _, _)), so
class_member_method_candidates and later interface-candidate logic lose the
concrete instantiated type; update the match to capture the member's type_args
(e.g., Tir2Ty::Class(qtn, type_args, _)) and thread those concrete args into the
interface-call candidate derivation (so class_member_method_candidates /
candidate construction uses the member's instantiated type), ensuring candidates
include members that arise only via inherited/default/generic interface views or
blanket `implements ... for C` rules; also ensure try_lower_union_iface_dispatch
uses these preserved concrete-member candidates when deciding dispatch.
baml_language/crates/baml_compiler2_tir/src/builder.rs (3)

8099-8137: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Ambiguity detection here misses registry-only and same-interface instantiations.

union_interface_method_ambiguity() collapses members to bare interface QTN and then derives sources via implemented_interface_method_sources(), which only sees in-body class implements blocks. That misses cases like Getter<int> | Getter<string> on a shared implementor, and any ambiguity introduced solely by out-of-body/blanket or primitive impl rules. In those cases resolve_union_member_ty() still returns callable arms via try_registry_member(), so the union call gets folded instead of raising E0121.

🤖 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/crates/baml_compiler2_tir/src/builder.rs` around lines 8099 -
8137, union_interface_method_ambiguity currently only considers in-body
implements via implemented_interface_method_sources (losing registry-only rules
and distinct instantiations like Getter<int> vs Getter<string>), so update it to
also consult the package_implements_registry(rule set) when gathering "sources"
for a given class_qtn: for each rule in registry.interface_impl_rules where
rule.for_ty_pattern is the class_qtn and rule.interface_ty is one of the union
interfaces, check whether that rule provides the requested member (reusing
try_registry_member/resolve logic) and add the corresponding (interface QTN +
instantiation args) to the sources list; also stop collapsing members to bare
interface QTN — preserve the interface instantiation (e.g., keep
(QualifiedTypeName, Vec<Ty>) keys) so same-interface different-instantiation
cases are treated as distinct, and deduplicate sources before checking
sources.len() >= 2.

12647-12655: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use runtime overlap here, not subtype.

This helper uses is_subtype() against each union member, but the surrounding pattern-dispatch code deliberately uses types_overlap() to avoid numeric-tower false positives. As written, a pattern like float against an int | string scrutinee now bypasses the mismatch check because int <: float, even though that arm can never match at runtime.

🤖 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/crates/baml_compiler2_tir/src/builder.rs` around lines 12647 -
12655, The helper pattern_overlaps_scrut_member currently checks overlap using
is_subtype against each member from flatten_union_optional_members; replace
those is_subtype calls with the runtime-aware types_overlap so pattern dispatch
matches runtime behavior (avoid numeric-tower false positives). Specifically, in
pattern_overlaps_scrut_member change the closure that does self.is_subtype(pat,
m) || self.is_subtype(m, pat) to use self.types_overlap(pat, m) (or an
equivalent symmetric types_overlap check) so the function uses types_overlap for
both directions when iterating union/optional members.

13375-13418: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Generic interface destructures currently lose the concrete type arguments.

The new interface branch runs before the existing missing-generic-args guard, so Box { value } on a Box<int> scrutinee reaches interface_field_infos_ordered() with type_args == []. That lowers value using the interface's generic parameter instead of int, and unions like Box<int> | Box<string> stop discriminating correctly. This branch needs the same missing-type-args protection as class destructures, or it needs to substitute the scrutinee's concrete interface args before lowering fields.

🤖 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/crates/baml_compiler2_tir/src/builder.rs` around lines 13375 -
13418, The interface destructure branch (matching Ty::Interface and calling
interface_field_infos_ordered) is running before checking for missing generic
args, so type_args can be empty and fields are lowered with the interface's
generic parameters instead of the scrutinee's concrete args; fix by adding the
same missing-type-args guard used for class destructures or by substituting the
scrutinee's concrete interface type arguments into class_ty before calling
interface_field_infos_ordered (i.e., when handling Ty::Interface(iface_name,
type_args, _) use the concrete args from scrut_ty or bail/report missing args,
then proceed to call interface_field_infos_ordered, analyze_and_lower, build
sub_dpats and DPat::interface, and finally compute matched_ty with
intersect_pattern_flow_types).
baml_language/crates/baml_lsp2_actions/src/describe.rs (1)

1507-1523: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Resolve QualifiedTypeName deps by full path, not short name.

This helper throws away the namespace/package and then dedupes + looks up by qtn.name() only. In a project with root.a.User and root.b.User, describe can now either link the dependency to the wrong symbol or drop one entirely, because resolve_dep_from_outline() returns the first matching short name. Since qtn already has the exact path, this should resolve from the qualified name directly and dedupe on that full identity instead.

🤖 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/crates/baml_lsp2_actions/src/describe.rs` around lines 1507 -
1523, The function collect_qtn_dep currently drops the namespace by using
qtn.name() and dedupes/looks up by short name, which causes collisions (e.g.,
root.a.User vs root.b.User); change it to use the full qualified path from qtn
(e.g., qtn.to_string() or qtn.full_path()) for both deduplication (the seen
HashSet) and for resolving the dependency so you call resolve_dep_from_outline
with the qualified name instead of qtn.name(); update references to the HashSet
key and the resolve_dep_from_outline argument accordingly in collect_qtn_dep so
deps and seen are keyed by the full identity and duplicates are correctly
suppressed.

`SystemTime` is only used in the `#[cfg(not(target_arch = "wasm32"))]`
`_default_round_robin_start`, but the import sat at module top level — so on
wasm32 (which CI clippy-checks) it was unused, tripping `-D unused-imports`.
Host clippy compiles the non-wasm branch, so it slipped through locally. Move
it into the function alongside the existing `UNIX_EPOCH` import. Verified clean
via clippy --target wasm32-unknown-unknown.

Co-Authored-By: Claude Opus 4.8 <[email protected]>

@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.

Caution

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

⚠️ Outside diff range comments (2)
baml_language/crates/bex_engine/src/lib.rs (2)

400-415: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update the rustdoc examples to the new public signatures.

Both examples still show the pre-refactor API shape (BexEngine::new(bytecode, sys_ops) and call_function(name, args)), so copying them now produces compile errors for missing argv, FunctionCallContext, and copy_objects.

Also applies to: 1119-1123

🤖 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/crates/bex_engine/src/lib.rs` around lines 400 - 415, The
rustdoc examples use the old API (BexEngine::new(bytecode, sys_ops) and
call_function(name, args)) which no longer compiles; update examples to the
current public signatures by constructing BexEngine with the new constructor
signature and calling the updated method that requires a FunctionCallContext,
argv, and copy_objects parameters (replace BexEngine::new(...) usage and calls
to call_function(...) with the new constructor and calls supplying a
FunctionCallContext, the argv vector, and the copy_objects flag), and make the
same changes in the other example lines referenced (including the example around
lines 1119-1123) so examples match the current public API.

2036-2089: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Drop the now-dead _event_result conversion from the root return path.

After the tracing removal, this tuple still forces an extra vm_value_to_owned(...) / external.clone() on every successful root return. In the copy_objects = false handle path that means we still walk and clone the full object graph before discarding it, which largely erases the fast path this PR is trying to preserve.

Suggested cleanup
-                    let (return_value, _event_result) = if !copy_objects {
+                    if cancelled {
+                        return Err(cancelled_unhandled_throw());
+                    }
+
+                    let return_value = if !copy_objects {
                         if let Some(ptr) = value.as_object_ptr() {
                             // SAFETY: the active thread holds the heap permit
                             // through `thread.proof()`.
@@
                             if matches!(unsafe { ptr.get() }, Object::Float(_)) {
                                 let external = self.convert_vm_value_to_external_with_type(
                                     value,
                                     &return_type,
                                     thread.proof(),
                                 )?;
-                                (external.clone(), external)
+                                external
                             } else {
                                 let handle = self.heap.create_handle(ptr);
-                                (
-                                    BexExternalValue::Handle(handle),
-                                    self.vm_value_to_owned(thread.proof(), value),
-                                )
+                                BexExternalValue::Handle(handle)
                             }
                         } else {
                             let external = self.convert_vm_value_to_external_with_type(
                                 value,
                                 &return_type,
@@
                             let external = crate::conversion::coerce_return_to_declared_type(
                                 external,
                                 &return_type,
                             )?;
-                            (external.clone(), external)
+                            external
                         }
                     } else {
                         let external = self.convert_vm_value_to_external_with_type(
                             value,
                             &return_type,
@@
                         let external = crate::conversion::coerce_return_to_declared_type(
                             external,
                             &return_type,
                         )?;
-                        (external.clone(), external)
+                        external
                     };
-
-                    if cancelled {
-                        return Err(cancelled_unhandled_throw());
-                    }
🤖 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/crates/bex_engine/src/lib.rs` around lines 2036 - 2089, The
tuple (_event_result) forces an extra vm_value_to_owned/external.clone on every
successful root return; remove the unused second element and stop computing it.
In the block building (return_value, _event_result) eliminate the secondary
computation: compute only return_value (using
convert_vm_value_to_external_with_type and, when creating a handle via
self.heap.create_handle(ptr), avoid calling vm_value_to_owned or cloning the
external), and adjust subsequent code to use that single return_value; update
references to _event_result accordingly and remove
vm_value_to_owned/external.clone calls to restore the fast path for copy_objects
= false and handle creation.
🤖 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.

Outside diff comments:
In `@baml_language/crates/bex_engine/src/lib.rs`:
- Around line 400-415: The rustdoc examples use the old API
(BexEngine::new(bytecode, sys_ops) and call_function(name, args)) which no
longer compiles; update examples to the current public signatures by
constructing BexEngine with the new constructor signature and calling the
updated method that requires a FunctionCallContext, argv, and copy_objects
parameters (replace BexEngine::new(...) usage and calls to call_function(...)
with the new constructor and calls supplying a FunctionCallContext, the argv
vector, and the copy_objects flag), and make the same changes in the other
example lines referenced (including the example around lines 1119-1123) so
examples match the current public API.
- Around line 2036-2089: The tuple (_event_result) forces an extra
vm_value_to_owned/external.clone on every successful root return; remove the
unused second element and stop computing it. In the block building
(return_value, _event_result) eliminate the secondary computation: compute only
return_value (using convert_vm_value_to_external_with_type and, when creating a
handle via self.heap.create_handle(ptr), avoid calling vm_value_to_owned or
cloning the external), and adjust subsequent code to use that single
return_value; update references to _event_result accordingly and remove
vm_value_to_owned/external.clone calls to restore the fast path for copy_objects
= false and handle creation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 42cd79aa-6b46-4c20-9ad9-e77457871a17

📥 Commits

Reviewing files that changed from the base of the PR and between 99f624d and de29f2a.

📒 Files selected for processing (1)
  • baml_language/crates/bex_engine/src/lib.rs

Brings in the VM perf work that landed on canary via #3646 (CPython-style
LoadVar2/StoreVar2 superinstructions, lazy faulting PC, kperf fail-closed,
sequence-point fusion guard, StoreVar2 notification merge) plus other
canary changes, while preserving trim-events' event/viz stripping.

Conflict resolution:
- kperf.rs: took canary's Option-based fail-closed version (pure perf).
- emit.rs: took canary's sequence-point fusion guard (pure perf).
- vm.rs: took canary's consolidated call-classify path and cur_pc reads;
  kept trim-events' event handling.
- Removed duplicate LoadVar2/StoreVar2 match arms that the auto-merge
  produced in vm.rs (dispatch) and bytecode.rs (Display / lower_to_compact
  / instruction_to_opcode).
- Kept trim-events' stripping of NotifyBlock/VizEnter/VizExit and the
  deleted node SDK event/tracing tests.

Snapshots verified consistent (lib codegen, baml_src, bytecode_format).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@github-actions

github-actions Bot commented Jun 3, 2026

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.

Brings in 34 commits of canary changes; perf files (vm.rs, bytecode.rs,
emit.rs, debug.rs) now auto-merge cleanly since the VM perf set is
identical on both sides.

Conflict resolution (all event-stripping related):
- bex_engine/lib.rs: kept the event-stripped side — dropped canary's
  VmExecState::SpanNotify handler (trim-events has no SpanNotify variant)
  and the SpanNotification/TlabHolder imports. InterfaceImplementors now
  comes from bex_vm_types::types (canary added it there), so the private
  bex_vm::vm::InterfaceImplementors import was removed; ValueKind dropped
  (only used by the stripped span handler).
- baml_lsp_server/Cargo.toml, bridge_wasm/Cargo.toml: kept baml_version
  (used by lib.rs) but dropped bex_events/bex_events_native (event system).
- Cargo.lock: regenerated against the resolved manifests.

Whole workspace compiles; snapshots verified consistent (lib codegen,
baml_src, bytecode_format).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…C probe

Standalone uv script that packs the same workload with two baml-cli builds,
runs each under the in-VM Apple-Silicon kperf probe (BAML_KPERF=1 + sudo), and
tables instructions retired / CPU cycles / VM ops plus a per-op IPC summary
with a stalls-vs-codegen verdict. Used to compare this branch (events trimmed)
against canary; surfaced that the two retire identical VM ops, so cycles — not
instructions — are the metric that matters.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@hellovai hellovai marked this pull request as draft June 5, 2026 17:49
@hellovai hellovai changed the title refactor(tracing) + perf(vm): trim event-stream surface for a fresh start; CPython-aligned VM superinstructions refactor(tracing): trim the event-stream surface for a fresh start Jun 5, 2026
The spec for the unified always-on observability stream that replaces the
surface trimmed in this PR (per-worker lock-free ring, always-on enter/exit/
error, .bamlprof artifact), plus the adversarial gap review of that design.
Context for whoever builds the replacement.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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