Skip to content

feat!: add async middleware across bindings - #564

Open
willkill07 wants to merge 34 commits into
NVIDIA:mainfrom
willkill07:wkk_relay-509-async-middleware
Open

feat!: add async middleware across bindings#564
willkill07 wants to merge 34 commits into
NVIDIA:mainfrom
willkill07:wkk_relay-509-async-middleware

Conversation

@willkill07

@willkill07 willkill07 commented Jul 27, 2026

Copy link
Copy Markdown
Member

Overview

Add async middleware support across the Rust runtime and all supported bindings while preserving synchronous scope, mark, and manual lifecycle event-emission APIs.

Warning

BREAKING CHANGE: Rust middleware callbacks now return futures. Update guardrails, request/execution intercepts, tool/LLM sanitizers, and event sanitizers to return Box::pin(async move { ... }) (or an equivalent future). Rebuild native plugins against ABI v3 and migrate raw C/Go consumers that need asynchronous callbacks to the completion-based APIs. Python and Node.js support awaitables/Promises additively; scope, mark, and manual lifecycle event-emission APIs remain synchronous.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Make Rust middleware callbacks and chains async, including conditional guardrails, request/execution intercepts, and sanitizers.
  • Queue scope and mark event sanitization/publication with FIFO snapshots so emission remains synchronous.
  • Add awaitable/Promise middleware support for Python and Node.js.
  • Add completion-based async C ABI and Go registration variants, including native and worker dynamic-plugin paths.
  • Update docs and cross-binding coverage for ordering, failures, cancellation, and async event publication.

Where should the reviewer start?

Start with crates/core/src/api/runtime/callbacks.rs and crates/core/src/api/runtime/subscriber_dispatcher.rs for the asynchronous middleware contract and delayed event publication. Then review crates/ffi/src/callable.rs and the Python/Node callback bridges.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

  • Relates to RELAY-509

Summary by CodeRabbit

  • New Features
    • Added async/Promise/awaitable support for guardrails, sanitizers, and request/execution intercepts across Rust, Python, Node.js, Go, and native plugins.
    • Introduced completion/next-based async middleware support for native plugins and standardized native ABI v3 with legacy compatibility.
  • Behavior Improvements
    • Sanitizer delivery now queues/snaps asynchronously while preserving event/observability data on failures.
    • Improved stream finalization and lifecycle event emission to complete asynchronously.
  • Documentation
    • Updated middleware/ABI v3 behavior docs and migration guidance for the new async contract.

@willkill07
willkill07 requested review from a team as code owners July 27, 2026 15:28
@github-actions github-actions Bot added size:XXL PR is very large Feature a new feature breaking PR introduces a breaking change lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code labels Jul 27, 2026
@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 94470cbb-7b9f-4dd0-820a-1c6a17ce5bc2

📥 Commits

Reviewing files that changed from the base of the PR and between 4724e51 and 8501193.

📒 Files selected for processing (1)
  • crates/ffi/build.rs
📜 Recent review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (11)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/ffi/build.rs
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/build.rs
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/build.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/ffi/build.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/ffi/build.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/ffi/build.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/ffi/build.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/ffi/build.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/ffi/build.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/ffi/build.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/ffi/build.rs
🔇 Additional comments (1)
crates/ffi/build.rs (1)

74-80: LGTM!


Walkthrough

The PR migrates middleware callbacks and lifecycle processing to asynchronous fallible futures, adds queued sanitized event publication, introduces native ABI v3 completion-based middleware, and updates Rust, Node, Python, Go, worker, FFI, plugin, test, and documentation surfaces.

Changes

Asynchronous middleware runtime

Layer / File(s) Summary
Callback contracts and snapshot chains
crates/core/src/api/runtime/*, crates/core/src/api/shared.rs
Middleware callbacks now accept owned inputs and return boxed asynchronous Result values. Sanitizer, conditional, and interception chains await callbacks while preserving short-circuiting and last-valid-value behavior.
Lifecycle and event publication
crates/core/src/api/llm.rs, crates/core/src/api/tool.rs, crates/core/src/api/scope.rs, crates/core/src/api/runtime/subscriber_dispatcher.rs, crates/core/src/stream.rs
LLM, tool, scope, mark, and stream paths await middleware and route transformed or sanitized events through queued FIFO dispatcher delivery. Stream END finalization is tracked and awaited before closure.
Native and language adapters
crates/core/src/plugin/dynamic/*, crates/ffi/*, crates/node/src/*, crates/python/src/*, go/nemo_relay/*, crates/plugin/src/lib.rs
Native ABI v3 completion/continuation handles and asynchronous registration are added. Worker, C, Node Promise, Python awaitable, and Go callback adapters await results and propagate callback errors.
Validation and migration coverage
crates/core/tests/*, crates/ffi/tests/*, crates/node/tests/*, crates/pii-redaction/tests/*, crates/adaptive/tests/*, go/nemo_relay/*, docs/*
Tests are updated for asynchronous callbacks, queued publication, preserved sanitizer payloads, stream finalization, ABI v3 registration, and language-binding error behavior. Documentation describes the new contracts and migration requirements.

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

Possibly related PRs

  • NVIDIA/NeMo-Relay#306 — Introduces the earlier native plugin SDK and ABI surface extended here with ABI v3 async middleware.
  • NVIDIA/NeMo-Relay#307 — Shares native dynamic plugin host and middleware plumbing updated by this PR.
  • NVIDIA/NeMo-Relay#562 — Shares managed LLM and tool execution paths updated for asynchronous interception and lifecycle handling.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title follows Conventional Commits, uses the breaking-change marker, and clearly summarizes the async middleware changes.
Description check ✅ Passed The description includes the required Overview, Details, reviewer start, and related issues sections, with clear breaking-change context.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@willkill07
willkill07 force-pushed the wkk_relay-509-async-middleware branch from dadfc9a to 0da27bc Compare July 27, 2026 15:31
@willkill07 willkill07 self-assigned this Jul 27, 2026
@willkill07 willkill07 added this to the 0.7 milestone Jul 27, 2026
@github-actions

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 49

Caution

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

⚠️ Outside diff range comments (4)
python/tests/test_builtin_codecs.py (1)

435-459: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Test name still says "raises" but the test no longer raises.

The docstring and body now assert deferred/non-raising behavior, but the function name test_manual_call_end_response_codec_failure_raises_after_end_event still implies an exception is raised. Rename for clarity.

♻️ Suggested rename
-    def test_manual_call_end_response_codec_failure_raises_after_end_event(self):
+    def test_manual_call_end_response_codec_failure_defers_without_raising(self):
🤖 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 `@python/tests/test_builtin_codecs.py` around lines 435 - 459, Rename
test_manual_call_end_response_codec_failure_raises_after_end_event to reflect
the current deferred, non-raising behavior validated by its docstring and
assertions; leave the test logic unchanged.
crates/python/tests/coverage/py_callable_coverage_tests.rs (1)

156-218: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate repeated per-assertion Tokio runtime construction. Both files' async migrations construct a brand-new Tokio runtime at nearly every assertion instead of reusing one runtime for the whole test function; the shared fix is to build the runtime once and call .block_on(...) on it repeatedly (or extract a tiny block_on helper).

  • crates/python/tests/coverage/py_callable_coverage_tests.rs#L156-L218: reuse the runtime variable already bound at Line 113 for these four assertions instead of calling tokio::runtime::Runtime::new().unwrap() each time.
  • crates/python/tests/coverage/coverage_tests.rs#L666-L777: hoist a single Builder::new_current_thread().enable_all().build().unwrap() (or a local block_on helper) to the top of test_sync_wrapper_fallbacks_and_helpers and reuse it across all 8 call sites instead of rebuilding a runtime each time.
🤖 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 `@crates/python/tests/coverage/py_callable_coverage_tests.rs` around lines 156
- 218, Reuse the existing runtime variable in
crates/python/tests/coverage/py_callable_coverage_tests.rs lines 156-218 for all
four async assertions, removing per-assertion Runtime::new construction. In
crates/python/tests/coverage/coverage_tests.rs lines 666-777, update
test_sync_wrapper_fallbacks_and_helpers to create one current-thread Tokio
runtime with enable_all at the test start (or use a local block_on helper) and
reuse it across all eight call sites.
docs/reference/migration-guides.mdx (1)

202-212: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Duplicated guidance in consecutive paragraphs.

Lines 205-207 and 210-212 state the same Python worker rule ("can return either an immediate value or an awaitable, but Python LLM sanitizers must still accept both the payload and directional context") almost verbatim. Drop one.

🤖 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 `@docs/reference/migration-guides.mdx` around lines 202 - 212, Remove the
duplicated Python worker middleware guidance from the consecutive paragraphs in
the migration documentation, retaining one clear statement that Python worker
sanitizers may return an immediate value or awaitable and Python LLM sanitizers
must accept the payload and directional context.
crates/ffi/tests/unit/api/registry_tests.rs (1)

1-429: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add async sanitizer test coverage
This module still covers only the sync sanitizer registrations. Add cases for the new async global and scope-local sanitizer registration APIs so the wrap_async_event_sanitize_fn paths get exercised too.

🤖 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 `@crates/ffi/tests/unit/api/registry_tests.rs` around lines 1 - 429, Add
asynchronous sanitizer coverage to
test_ffi_event_sanitizer_registries_and_error_paths, exercising both global and
scope-local async registration APIs and their corresponding deregistration and
callback paths. Ensure the cases invoke events/scopes so
wrap_async_event_sanitize_fn is executed, and verify successful registration,
sanitization behavior, invalid-argument handling, and cleanup alongside the
existing synchronous tests.

Source: Coding guidelines

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

Inline comments:
In `@ATTRIBUTIONS-Rust.md`:
- Around line 24923-24924: Regenerate ATTRIBUTIONS-Rust.md by running uv run
pre-commit run --all-files, then include the hook’s resulting attribution
changes in the commit. Verify the generated file is clean and matches the
pre-commit output before handoff.

In `@crates/adaptive/tests/unit/runtime_tests.rs`:
- Around line 639-644: In the existing async test around llm_request_intercepts,
remove the nested tokio::runtime::Builder construction and block_on call. Await
llm_request_intercepts("anthropic", request.clone()) directly, preserving the
existing expect message and result handling.

In `@crates/core/src/api/llm.rs`:
- Around line 711-748: Update the event construction around
dispatch_transformed_event so its fallback snapshot never contains unsanitized
request data; seed it with a sanitized or redacted payload, and apply the same
treatment to the llm_call_end response path. Also replace the dispatcher-thread
state.read().expect in build_llm_start_event handling with non-panicking
poisoned-lock behavior so sanitizer or event processing cannot publish raw
payloads through panic fallback.
- Around line 590-636: Refactor emit_optimization_marks_with and
emit_optimization_marks_with_async to share a helper for optimization
contribution iteration and Event construction, including offset and timestamp
calculation. Keep each seam responsible only for applying its sanitizer
synchronously or asynchronously, then enqueueing, marking emitted contributions,
and preserving existing break behavior so both paths use identical accounting
logic.

In `@crates/core/src/api/runtime/subscriber_dispatcher.rs`:
- Around line 262-281: Prevent raw payload publication when queued sanitization
fails: in subscriber_dispatcher.rs, split transform execution from
event_sanitize_snapshot_chain so transform panics log an error and drop the
event, while sanitizer-chain panics retain the fail-open original fallback. In
crates/core/src/api/tool.rs lines 261-302 and crates/core/src/api/llm.rs lines
711-748, seed start and end events without raw payload data so fallback
snapshots contain no payload; update every cited raw start/end seed, including
the tool end event and LLM end event.
- Around line 242-261: Update sanitize_event_snapshot to reuse a single cached
Tokio runtime instead of building one per event, and configure that runtime with
both time and I/O drivers enabled so invoke_event_sanitize can await tonic
Channel RPCs. Preserve the existing fallback that logs runtime creation failure
and publishes the original event snapshot.

In `@crates/core/src/api/shared.rs`:
- Around line 61-70: Update snapshot_event_sanitizers and every queued caller to
preserve fail-closed behavior when the global context lock is poisoned: do not
convert its None result to an empty sanitizer list via unwrap_or_default, and
propagate the unavailable state so the event is not published unsanitized. Keep
the existing sanitize_event_with_scope_stack behavior consistent, using an
explicit Result or equivalent unavailable-state representation across the API.

In `@crates/core/src/api/tool.rs`:
- Around line 261-302: Ensure tool lifecycle events are seeded with sanitized
snapshots rather than raw arguments or results before dispatch. Update the
start-event construction around build_tool_start_event and the corresponding
tool_call_end path to sanitize raw_args and raw result first, then use those
sanitized values in the initial event; retain the transform only as needed for
subsequent processing.

In `@crates/core/src/plugin/dynamic/native.rs`:
- Around line 2399-2400: Update the legacy synchronous wrapper paths around
call_event_sanitize_callback and the other listed native callback invocations to
execute blocking plugin FFI work through Tokio’s spawn_blocking rather than
directly inside Box::pin(async move). Preserve each callback’s existing
arguments and result/error propagation while ensuring runtime worker threads are
not blocked.

In `@crates/core/src/plugin/dynamic/worker.rs`:
- Around line 1122-1131: The event sanitizer callbacks around
invoke_event_sanitize and the analogous tool/LLM and scope/mark callbacks no
longer preserve fallback behavior on worker RPC errors. Restore the previous
error handling so failures retain the original value and invoke
log_callback_fallback, or add equivalent structured logging if propagation is
intentional; apply the same policy consistently across all referenced surfaces.

In `@crates/core/src/stream.rs`:
- Around line 310-320: Update the no-runtime branch in the finalization spawning
logic near tokio::runtime::Handle::try_current so it returns a joinable
completion handle instead of spawning an untracked OS thread and returning None.
Use a runtime that supports the full async finalization work, including timers
and I/O, and ensure close() can await the resulting handle through the
finalization field before callers flush subscribers.
- Around line 304-308: Update the finalization delivery block around
sanitize_event_with_scope_stack and NemoRelayContextState::emit_event to use the
established queued event path that carries scope_stack with the message. Ensure
subscriber callbacks execute with the stream’s original scope stack, preserving
ambient context and root_uuid isolation when finalization runs in a spawned
task.

In `@crates/core/tests/fixtures/native_plugin/src/lib.rs`:
- Around line 656-658: Update every early-return branch in the native async
callback fixture, including the null-host, invalid host-string, and related
branches in the callback flow, to call async_completion_reject with the relevant
error before returning. Do not return
NemoRelayNativeAsyncCallbackState::Complete without settling the completion;
preserve the existing return state after rejection so
invoke_native_async_callback always resolves.
- Around line 293-298: Move the `NemoRelayNativeHostApiV2` cast in the V2 host
validation path until after validation. First read `abi_version` and
`struct_size` through a `NemoRelayNativeHostApiV1` pointer, return
`NemoRelayStatus::InvalidArg` when the version or size is insufficient, then
cast `host` to `NemoRelayNativeHostApiV2` only for subsequent access.

In `@crates/core/tests/integration/native_plugin_tests.rs`:
- Around line 708-717: Replace the single tokio::task::yield_now synchronization
in the async-pending native intercept test with an mpsc handshake, following
plugin_host_clear_allows_an_in_flight_native_callback_to_finish. Signal once
tool_request_intercepts has entered its pending-registration/continuation state,
await that signal before clear_plugin_configuration(), and preserve the existing
pending task completion and native_async assertion.

In `@crates/core/tests/integration/scope_local_tests.rs`:
- Line 113: Remove the redundant flush_subscribers().unwrap() call from the path
where captured_snapshot already performs the flush, while retaining the call at
the separate path around line 358 that has no captured_snapshot invocation.

In `@crates/core/tests/unit/context_tests.rs`:
- Around line 254-266: Update the enclosing tests that call
NemoRelayContextState::tool_conditional_execution_snapshot_chain to use
#[tokio::test] and await the chain directly. Remove the
tokio::runtime::Runtime::new().unwrap().block_on wrappers in both occurrences,
preserving the existing assertions and test behavior.

In `@crates/core/tests/unit/llm_api_tests.rs`:
- Around line 1381-1384: Update the sanitizer callback closure around
sanitizer_inputs so it performs the lock and push inside the returned async
future rather than when constructing the future. Clone context.codec() before
entering the async block as needed, and preserve the existing
redacted_response() result and captured input ordering based on polling.

In `@crates/core/tests/unit/plugin_tests.rs`:
- Around line 649-671: Replace the futures::executor::block_on calls around
llm_request_intercepts with the already-constructed tokio current-thread
runtime’s block_on method, including the matching calls in the later test
section. Keep the existing request setup and assertions unchanged so all async
middleware runs within the tokio runtime.

In `@crates/ffi/nemo_relay.h`:
- Around line 2570-2605: Add the missing async FFI declarations to nemo_relay.h
for every new LLM, event, and tool registration entry point exported under api,
matching each symbol’s signature, callback type, and ownership parameters. Keep
the declarations grouped with the existing async guardrail and intercept
functions so C/C++ consumers can access the complete published ABI.

In `@crates/ffi/src/api/event_registry.rs`:
- Around line 47-105: Add clear_last_error() as the first operation in
register_global_async and register_scope_async in
crates/ffi/src/api/event_registry.rs; add it before c_str_to_string in both
async registration macros in crates/ffi/src/api/llm_registry.rs; and add it to
nemo_relay_register_tool_sanitize_request_guardrail_async,
nemo_relay_register_tool_sanitize_response_guardrail_async,
nemo_relay_register_tool_conditional_execution_guardrail_async, and
nemo_relay_register_tool_execution_intercept_async in
crates/ffi/src/api/tool_registry.rs, matching the existing
async_tool_json_registration! pattern.

In `@crates/ffi/src/api/mod.rs`:
- Line 150: Replace the direct tokio_runtime().block_on call in the nemo_relay_*
synchronous FFI entrypoints with runtime-thread-safe handling: route async
callers through the completion-based ABI, or detect and reject calls made from a
Tokio runtime thread before blocking. Apply this consistently to every affected
entrypoint and add coverage invoking each one from a Tokio task.

In `@crates/ffi/src/api/scope_registry.rs`:
- Around line 69-110: Regenerate the C header for all new
scope_async_registration exports, including the tool and LLM guardrail and
request-intercept functions. Ensure the generated declarations include
break_chain before cb for both request-intercept variants, matching the
corresponding nemo_relay_register_*_async signatures.

In `@crates/ffi/src/callable.rs`:
- Around line 253-258: Capture a tokio runtime Handle when constructing
NemoRelayAsyncNext, store it in the wrapper, and use handle.spawn for both
continuation spawn sites in crates/ffi/src/callable.rs (253-258 and the related
site near line 310). Apply the same change to NativeAsyncNext in
crates/core/src/plugin/dynamic/native.rs (1570-1576), replacing bare
tokio::spawn with the stored handle.
- Around line 877-891: Async stream intercepts buffer the entire LlmJsonStream
because the ABI resolves one JSON value, preventing incremental delivery. Update
the NemoRelayAsyncInterceptCb stream registration around
crates/ffi/src/callable.rs lines 877-891 and the matching
NemoRelayNativeAsyncMiddlewareKind::LlmStreamExecutionIntercept implementation
around crates/core/src/plugin/dynamic/native.rs lines 1547-1568 to use
chunk-delivery callbacks and forward chunks as they arrive; alternatively,
explicitly document the buffering limitation on both
wrap_async_llm_stream_execution_intercept_fn and the native middleware kind.
- Around line 342-345: Replace poisoning-sensitive Mutex lock unwraps with
PoisonError::into_inner in all listed settle paths: callable.rs functions
nemo_relay_async_completion_resolve, nemo_relay_async_completion_reject, and
nemo_relay_async_next_invoke (both locks), plus native.rs functions
native_async_completion_resolve_json, native_async_completion_reject, and
native_async_next_invoke (both locks). Apply the change at
crates/ffi/src/callable.rs lines 342-345, 369, and 220, 235, and
crates/core/src/plugin/dynamic/native.rs lines 1445-1448, 1474, and 1538, 1553;
preserve the existing sender take/send behavior.
- Around line 135-145: Update the async callback completion handling in the
shown invocation path, plus invoke_async_intercept and
invoke_native_async_callback, to detect when a Complete callback returned
without consuming the completion sender. Fail immediately with the existing
internal error instead of awaiting a receiver that cannot resolve, while
preserving normal resolve/reject behavior.

In `@crates/node/src/api/mod.rs`:
- Around line 3557-3567: Update the ts_arg_type for
scope_register_llm_request_intercept to include the Promise variant of the
callback return type, mirroring the exact type shape used by the sibling API at
Line 2953. Keep the existing synchronous callback type unchanged and align it
with wrap_js_llm_request_intercept_promise_fn.

In `@crates/node/src/callable.rs`:
- Around line 210-233: Update the Promise wrappers
wrap_js_tool_conditional_promise_fn, wrap_js_tool_request_intercept_promise_fn,
the LLM conditional wrapper, and the LLM intercept outcome-deserialization
branch to call record_callback_error whenever the callback or deserialization
fails. Preserve each wrapper’s existing success and return-value behavior while
ensuring getLastCallbackError() can surface every failure path.
- Around line 497-516: Update the TSFN-backed async wrappers around the shown
Arc closure and the other wrapper implementations to avoid
recv_middleware_json_result’s blocking std::sync::mpsc::recv. Use
tokio::sync::oneshot for callback delivery and await the receiver, matching the
existing PromiseAwareFn::call_inner pattern while preserving status and error
handling.

In `@crates/node/src/callback_factory.rs`:
- Around line 78-88: Update the rejection handler in the promise chain to
preserve non-Error rejection details, matching the execution wrapper’s
String(error?.message ?? error) behavior within the existing try handling. Keep
direct string messages supported, but avoid defaulting numbers, objects without
message, or other primitives to “unknown error”; ensure reject receives the
resulting diagnostic string.

In `@crates/node/tests/tools_tests.mjs`:
- Around line 611-622: Add failure-path tests alongside the conditional
guardrail and request-intercept tests around toolCallExecute: cover a guardrail
that rejects and intercept callbacks that throw synchronously or return rejected
Promises. Assert each documented error propagates from toolCallExecute and
verify the corresponding guardrail/intercept registrations are cleaned up in
finally blocks.

In `@crates/plugin/src/lib.rs`:
- Around line 839-847: Rename the public ABI extension struct from
NemoRelayNativeHostApiV2 to NemoRelayNativeHostApiV3 and update all references
to this type, preserving its existing layout and fields.
- Around line 2386-2390: Update the ABI version check in the host validation
block to compare against the fixed version that introduced
NemoRelayNativeHostApiV2 (version 3), rather than the moving
NEMO_RELAY_NATIVE_ABI_VERSION constant. Keep the existing struct_size validation
unchanged.

In `@crates/python/src/py_api/mod.rs`:
- Around line 1273-1286: The four chain-execution functions currently return
either a value or awaitable while the Python stubs promise only Awaitable;
settle on one consistent contract. In crates/python/src/py_api/mod.rs:1273-1286,
either remove the get_running_loop synchronous fallback and always return an
awaitable, or preserve both modes while adding the TASK_SCOPE_STACK.scope(...)
wrapper to the synchronous path. In python/nemo_relay/_native.pyi:1675-1721,
update the return annotations and Returns prose for tool_request_intercepts,
tool_conditional_execution, llm_request_intercepts, and
llm_conditional_execution to match the implemented contract.
- Around line 1273-1286: Update each synchronous branch around the repeated
runtime.block_on calls to execute tool_request_intercepts within
TASK_SCOPE_STACK.scope using the captured scope_stack, matching the async path’s
scope propagation. Preserve the existing result conversion and error handling,
and apply the same change consistently at all three locations.

In `@crates/python/src/py_callable.rs`:
- Around line 1154-1165: Replace the synchronous asyncio.run handling in the
Python::attach callback with the existing split_py_object_or_future and
resolve_py_object_or_future helpers used by sibling wrappers, so awaitables
resolve on the shared runtime without holding the GIL. Update the outer
Python::attach return type to FlowResult<Result<Py<PyAny>, PyValueFuture>> and
adjust earlier return Err arms to match.

In `@crates/python/tests/coverage/coverage_tests.rs`:
- Around line 675-684: Update the assertion around wrap_py_tool_fn and tool_fail
to expect the returned future to fail rather than unwrap a successful value.
Preserve the existing runtime setup and inputs, but assert the error path
produced when tool_fail raises RuntimeError.

In `@docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx`:
- Line 119: Update the Native ABI v3 section beneath the “Native ABI v3” heading
to clarify that the entry symbol still receives the v1-prefixed table, with the
v3 extension appended afterward, before describing the v2/v3 prefix layering.
- Around line 138-142: Format the ABI completion-state names as code on both
documentation sites: in
docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx lines 138-142, wrap
Complete and Pending in backticks; in docs/reference/event-sanitizers.mdx lines
191-195, wrap the state names in backticks and change their casing to Complete
and Pending to match the native ABI page.

In `@docs/reference/migration-guides.mdx`:
- Around line 276-283: Update the “For the complete ABI contract” link in the
migration guide to use descriptive “Native ABI v3” anchor text and target the
Native ABI v3 section anchor instead of the stale v2 destination. Run just
docs-linkcheck to verify the changed documentation link.
- Around line 52-61: Update the closure parameters in
register_tool_conditional_execution_guardrail so the example does not trigger
unused-parameter warnings: rename name and args to _name and _args, or reference
name in the policy logic/comment while preserving the existing guardrail
behavior.
- Around line 37-42: Update the migration table headers from “0.6 callback” and
“0.7 callback” to title case, matching the “0.6 Contract” and “0.7 Contract”
convention used by the sibling table; leave the table content unchanged.

In `@go/nemo_relay/callbacks.go`:
- Line 616: Replace the unchecked type assertions in the callback handlers
around lookupClosure at the referenced locations, including the paths at lines
653 and 664, with comma-ok assertions. When the token is unknown, freed, or
resolves to an incompatible value, reject the completion and return through the
existing failure path instead of allowing a panic across the cgo boundary.
- Around line 689-708: The callback flow invoking fn with nextFn does not
protect against delayed use after fn returns and the next handle is released.
Either enforce and document synchronous-only nextFn usage, or add lifetime
tracking with a released state so nemo_relay_async_next_invoke_callback rejects
calls after release instead of dereferencing freed memory.
- Around line 618-643: Update the async callback goroutine and the intercept
trampoline to propagate cancellation for the entire callback lifetime: use a
shared cancellable context, drive cancel from a completion-bound cancellation
poller or available FFI callback, and pass that context instead of
context.Background(). Route resolve_json and reject results through the existing
checkStatus handling so failed settlement is reported rather than ignored.

In `@go/nemo_relay/nemo_relay.go`:
- Around line 47-50: Define a Go constant representing the pending async
callback state, matching NemoRelayAsyncCallbackState::Pending, and replace the
C.uint32_t(1) returns in both async trampolines with that named constant. Keep
the existing callback behavior unchanged while removing the magic number.

In `@python/nemo_relay/_native.pyi`:
- Line 1675: Update the type annotations for tool_request_intercepts and the
three related tool interception functions to return _Json | Awaitable[_Json],
matching the native implementation’s synchronous fallback and asynchronous
behavior. Revise each corresponding Returns: docstring to describe both possible
return modes; do not remove the native synchronous path.
- Around line 60-62: Update the _LlmRequestIntercept type alias so the entire
return-type union is enclosed in a single quoted forward reference, matching the
established pattern used nearby. Preserve both LLMRequestInterceptOutcome and
Awaitable alternatives, and ensure the resulting annotation passes ty type
checking.

---

Outside diff comments:
In `@crates/ffi/tests/unit/api/registry_tests.rs`:
- Around line 1-429: Add asynchronous sanitizer coverage to
test_ffi_event_sanitizer_registries_and_error_paths, exercising both global and
scope-local async registration APIs and their corresponding deregistration and
callback paths. Ensure the cases invoke events/scopes so
wrap_async_event_sanitize_fn is executed, and verify successful registration,
sanitization behavior, invalid-argument handling, and cleanup alongside the
existing synchronous tests.

In `@crates/python/tests/coverage/py_callable_coverage_tests.rs`:
- Around line 156-218: Reuse the existing runtime variable in
crates/python/tests/coverage/py_callable_coverage_tests.rs lines 156-218 for all
four async assertions, removing per-assertion Runtime::new construction. In
crates/python/tests/coverage/coverage_tests.rs lines 666-777, update
test_sync_wrapper_fallbacks_and_helpers to create one current-thread Tokio
runtime with enable_all at the test start (or use a local block_on helper) and
reuse it across all eight call sites.

In `@docs/reference/migration-guides.mdx`:
- Around line 202-212: Remove the duplicated Python worker middleware guidance
from the consecutive paragraphs in the migration documentation, retaining one
clear statement that Python worker sanitizers may return an immediate value or
awaitable and Python LLM sanitizers must accept the payload and directional
context.

In `@python/tests/test_builtin_codecs.py`:
- Around line 435-459: Rename
test_manual_call_end_response_codec_failure_raises_after_end_event to reflect
the current deferred, non-raising behavior validated by its docstring and
assertions; leave the test logic unchanged.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5739b182-4979-4b86-9f7a-f107131ccd0a

📥 Commits

Reviewing files that changed from the base of the PR and between 42c7655 and 0da27bc.

📒 Files selected for processing (76)
  • ATTRIBUTIONS-Rust.md
  • crates/adaptive/src/acg_component.rs
  • crates/adaptive/src/adaptive_hints_intercept.rs
  • crates/adaptive/tests/integration/runtime_integration_tests.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/adaptive/tests/unit/plugin_component_tests.rs
  • crates/adaptive/tests/unit/runtime_features_tests.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/cli/src/sessions/mod.rs
  • crates/cli/tests/coverage/shared/server_tests.rs
  • crates/core/src/api/llm.rs
  • crates/core/src/api/runtime/callbacks.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/stream.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/integration/native_plugin_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/integration/scope_local_tests.rs
  • crates/core/tests/integration/worker_plugin_tests.rs
  • crates/core/tests/unit/context_tests.rs
  • crates/core/tests/unit/dynamic_worker_tests.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/core/tests/unit/plugin_tests.rs
  • crates/core/tests/unit/shared_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/ffi/src/callable.rs
  • crates/ffi/tests/integration/callable_extra_tests.rs
  • crates/ffi/tests/unit/api/registry_tests.rs
  • crates/ffi/tests/unit/callable_tests.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
  • crates/node/src/callback_factory.rs
  • crates/node/src/promise_call.rs
  • crates/node/tests/callback_error_tests.mjs
  • crates/node/tests/event_sanitizers_tests.mjs
  • crates/node/tests/llm_tests.mjs
  • crates/node/tests/tools_tests.mjs
  • crates/pii-redaction/src/builtin.rs
  • crates/pii-redaction/tests/unit/component_tests.rs
  • crates/plugin/README.md
  • crates/plugin/src/lib.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_callable.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/python/tests/coverage/py_api_coverage_tests.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • docs/about-nemo-relay/concepts/middleware.mdx
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
  • go/nemo_relay/callbacks.go
  • go/nemo_relay/nemo_relay.go
  • python/nemo_relay/__init__.py
  • python/nemo_relay/__init__.pyi
  • python/nemo_relay/_native.pyi
  • python/tests/test_adaptive.py
  • python/tests/test_builtin_codecs.py
  • python/tests/test_context_isolation.py
  • python/tests/test_event_sanitizers.py
  • python/tests/test_llm.py
  • python/tests/test_tools.py

Comment thread ATTRIBUTIONS-Rust.md Outdated
Comment thread crates/adaptive/tests/unit/runtime_tests.rs Outdated
Comment thread crates/core/src/api/llm.rs
Comment thread crates/core/src/api/llm.rs Outdated
Comment thread crates/core/src/api/runtime/subscriber_dispatcher.rs
Comment thread go/nemo_relay/callbacks.go Outdated
Comment thread go/nemo_relay/callbacks.go Outdated
Comment thread go/nemo_relay/nemo_relay.go
Comment thread python/nemo_relay/_native.pyi Outdated
Comment thread python/nemo_relay/_native.pyi Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review continued from previous batch...

Comment thread crates/core/src/plugin/dynamic/worker.rs
Comment thread crates/core/tests/fixtures/native_plugin/src/lib.rs Outdated
Comment thread crates/core/tests/fixtures/native_plugin/src/lib.rs Outdated
Comment thread crates/ffi/nemo_relay.h Outdated
Comment thread crates/ffi/src/api/event_registry.rs
Comment thread crates/ffi/src/api/scope_registry.rs
Comment thread crates/ffi/src/callable.rs
Comment thread crates/ffi/src/callable.rs Outdated
Comment thread crates/plugin/src/lib.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (3)
crates/core/src/api/runtime/subscriber_dispatcher.rs (1)

217-231: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Async middleware now serializes behind one dispatcher thread.

Every transform and sanitizer chain is block_on-ed on the single dispatcher thread, so one slow worker-plugin RPC sanitizer stalls publication of all unrelated events and any concurrent flush_subscribers caller. Worth a bounded per-event timeout and a queue-depth/latency metric before this ships.

🤖 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 `@crates/core/src/api/runtime/subscriber_dispatcher.rs` around lines 217 - 231,
The deliver_event path currently blocks the single dispatcher thread while
running transform and sanitizer futures. Add a bounded per-event timeout around
sanitize_event_snapshot (including transform and sanitizers), ensure timed-out
events cleanly restore the prior scope stack and IN_DISPATCHER state, and record
queue-depth or dispatch-latency metrics using the dispatcher’s existing metric
facilities.
crates/python/tests/coverage/coverage_tests.rs (1)

664-696: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Ten separate current-thread runtimes in one test function. Build one runtime at the top of the closure and reuse it for every block_on; the repetition obscures what each assertion checks.

🤖 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 `@crates/python/tests/coverage/coverage_tests.rs` around lines 664 - 696, In
the test closure containing the tool_ok, tool_fail, and tool_cond assertions,
construct a single current-thread Tokio runtime before the assertions and reuse
it for every block_on call. Remove the repeated Builder chains while preserving
each assertion’s existing result checks.
go/nemo_relay/callbacks.go (1)

715-742: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

nextOpen does not prevent use-after-free of next. A goroutine can pass the nextOpen.Load() check at Line 719, then be descheduled; fn returns, nextOpen.Store(false) runs, the deferred C.nemo_relay_async_next_release(next) frees the handle, and the stalled caller then passes the freed pointer to nemo_relay_async_next_invoke_callback. The atomic flag orders nothing with respect to the release. Guard the handle with a sync.RWMutex (read-lock around the invoke, write-lock around the close + release) or track in-flight calls with a sync.WaitGroup that the goroutine waits on before releasing.

🛡️ Serialize invocation against release
-		var nextOpen atomic.Bool
-		nextOpen.Store(true)
+		var nextMu sync.RWMutex
+		nextOpen := true
+		defer func() {
+			nextMu.Lock()
+			nextOpen = false
+			nextMu.Unlock()
+		}()
 		nextFn := func(ctx context.Context, payload json.RawMessage) (json.RawMessage, error) {
-			if !nextOpen.Load() {
-				return nil, context.Canceled
-			}
+			nextMu.RLock()
+			defer nextMu.RUnlock()
+			if !nextOpen {
+				return nil, context.Canceled
+			}

Note the deferred release must then run after the write-lock defer (defers run LIFO), so move C.nemo_relay_async_next_release(next) into the same closure that flips the flag.

🤖 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 `@go/nemo_relay/callbacks.go` around lines 715 - 742, Replace the nextOpen
atomic flag in the callback setup with synchronization that tracks in-flight
nextFn calls, such as a sync.RWMutex or sync.WaitGroup. Ensure nextFn holds the
guard through nemo_relay_async_next_invoke_callback and rejects calls after
shutdown; move nemo_relay_async_next_release(next) into the shutdown closure so
it executes only after the write lock or in-flight wait completes, preserving
defer ordering and preventing use-after-free.

Source: Path instructions

♻️ Duplicate comments (1)
crates/python/src/py_api/mod.rs (1)

1273-1289: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Scope propagation is fixed, but the sync branch still block_ons while holding the GIL. If any middleware in the chain returns an awaitable, py_callable::split_json_or_future hands it to pyo3_async_runtimes::tokio::into_future, which resolves on the shared runtime and needs the GIL to complete — the blocked thread holds it. Wrap the block_on in py.detach(...) (as flush_subscribers does), and consider a cached runtime instead of building one per call.

🤖 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 `@crates/python/src/py_api/mod.rs` around lines 1273 - 1289, Update the
synchronous branch around `runtime.block_on` to execute the blocking runtime
work inside `py.detach(...)`, releasing the GIL while middleware awaitables
resolve; preserve `TASK_SCOPE_STACK.scope` and the existing result/error
conversion behavior. Reuse the established cached-runtime pattern if available
instead of constructing a new runtime for every call.

Source: Path instructions

🤖 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 `@crates/core/src/api/llm.rs`:
- Around line 896-908: Update the queued transform around state access in the
END event path to bind its event parameter and replace the panicking
context.read().expect call with graceful poisoned-lock handling. On read
failure, return the seeded end event supplied to the closure; otherwise continue
building the enriched event through build_llm_end_event with the existing
metadata and response fields.

In `@crates/core/src/api/runtime/subscriber_dispatcher.rs`:
- Around line 288-306: Update the event sanitization flow around
event_sanitize_snapshot_chain to avoid cloning transformed when
sanitizers.is_empty(); return the transformed event directly in that case. Only
create the fallback clone when sanitizers are present and catch_unwind may need
to publish the original transformed snapshot after a panic, preserving the
existing panic logging and fallback behavior.

In `@crates/core/src/api/scope.rs`:
- Around line 382-389: Preserve poisoned-lock signals instead of silently
dropping events: update snapshot_sanitizer guards to restore the
default-dispatch behavior or explicitly log an error on None. Apply this in
scope.rs:382-389 and the push_scope/pop_scope paths, tool.rs:304-307 for
skill-load and tool-call dispatches, llm.rs:709-710 for llm_call start/end
events, and stream.rs:351-359 in emit_chunk_mark; preserve scope propagation,
callback lifetimes, stream finalization, and root_uuid isolation.

In `@crates/ffi/build.rs`:
- Around line 20-26: Update the header rewriting flow around the `replacen` call
to first assert that the expected `\n#endif  /* NEMO_RELAY_H */\n` marker exists
in the generated header, and fail fast with a clear message when it is absent;
only perform the replacement and subsequent write when the marker is present.

In `@crates/node/src/callable.rs`:
- Around line 753-761: Update the TSFN sanitizer handling in the request and
response paths around await_middleware_json_or_value to use
await_middleware_json_result instead, preserving the existing error propagation
behavior of wrap_js_llm_sanitize_request_promise_fn and
wrap_js_llm_sanitize_response_promise_fn. Keep successful null results mapped to
Ok(None), but propagate callback failures as Err rather than treating them as
payload omission.
- Around line 753-761: Align sanitizer failure handling with the Promise
wrappers by propagating callback errors instead of treating them as omitted
payloads: update await_middleware_json_or_value usage in
crates/node/src/callable.rs:753-761 and wrap_js_llm_sanitize_response_fn at
crates/node/src/callable.rs:799-805 to use await_middleware_json_result and
record_callback_error. Update the corresponding Python coverage assertions at
crates/python/tests/coverage/coverage_tests.rs:699-713 and 764-778 to expect
deserialization or RuntimeError failures via unwrap_err rather than Ok(None).

In `@python/nemo_relay/_native.pyi`:
- Around line 56-62: Update the return annotation of _ToolExecutionIntercept to
quote the entire union of ToolExecutionInterceptOutcome and
Awaitable[ToolExecutionInterceptOutcome], matching the established
_LlmRequestIntercept pattern while leaving its callable parameters unchanged.

---

Outside diff comments:
In `@crates/core/src/api/runtime/subscriber_dispatcher.rs`:
- Around line 217-231: The deliver_event path currently blocks the single
dispatcher thread while running transform and sanitizer futures. Add a bounded
per-event timeout around sanitize_event_snapshot (including transform and
sanitizers), ensure timed-out events cleanly restore the prior scope stack and
IN_DISPATCHER state, and record queue-depth or dispatch-latency metrics using
the dispatcher’s existing metric facilities.

In `@crates/python/tests/coverage/coverage_tests.rs`:
- Around line 664-696: In the test closure containing the tool_ok, tool_fail,
and tool_cond assertions, construct a single current-thread Tokio runtime before
the assertions and reuse it for every block_on call. Remove the repeated Builder
chains while preserving each assertion’s existing result checks.

In `@go/nemo_relay/callbacks.go`:
- Around line 715-742: Replace the nextOpen atomic flag in the callback setup
with synchronization that tracks in-flight nextFn calls, such as a sync.RWMutex
or sync.WaitGroup. Ensure nextFn holds the guard through
nemo_relay_async_next_invoke_callback and rejects calls after shutdown; move
nemo_relay_async_next_release(next) into the shutdown closure so it executes
only after the write lock or in-flight wait completes, preserving defer ordering
and preventing use-after-free.

---

Duplicate comments:
In `@crates/python/src/py_api/mod.rs`:
- Around line 1273-1289: Update the synchronous branch around `runtime.block_on`
to execute the blocking runtime work inside `py.detach(...)`, releasing the GIL
while middleware awaitables resolve; preserve `TASK_SCOPE_STACK.scope` and the
existing result/error conversion behavior. Reuse the established cached-runtime
pattern if available instead of constructing a new runtime for every call.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 8bed11a4-1d8f-4249-80c1-e241ae540e7c

📥 Commits

Reviewing files that changed from the base of the PR and between 0da27bc and 2df9573.

📒 Files selected for processing (29)
  • ATTRIBUTIONS-Rust.md
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/core/src/api/llm.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/stream.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
  • crates/node/src/callback_factory.rs
  • crates/plugin/src/lib.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_callable.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
  • go/nemo_relay/callbacks.go
  • python/nemo_relay/_native.pyi
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (49)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/core/src/stream.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/api/llm.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/core/src/stream.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • go/nemo_relay/callbacks.go
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • go/nemo_relay/callbacks.go
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • docs/reference/migration-guides.mdx
  • python/nemo_relay/_native.pyi
  • ATTRIBUTIONS-Rust.md
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • go/nemo_relay/callbacks.go
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/core/src/stream.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • go/nemo_relay/callbacks.go
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • go/nemo_relay/callbacks.go
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/core/src/stream.rs
  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/core/src/api/llm.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/unit/native_plugin_tests.rs
  • crates/adaptive/tests/unit/runtime_tests.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/build.rs
  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/ffi/src/callable.rs
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/build.rs
  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/ffi/src/callable.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/ffi/build.rs
  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/python/src/py_callable.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
crates/adaptive/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep crates/adaptive aligned with the canonical adaptive config schema, built-in section helpers, plugin lifecycle, and validation/report behavior.

Files:

  • crates/adaptive/tests/unit/runtime_tests.rs
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

In MDX files, top-of-file comments must use JSX comment delimiters ({/* to open and */} to close); do not use HTML comments for MDX SPDX headers

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
  • ATTRIBUTIONS-Rust.md
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
  • ATTRIBUTIONS-Rust.md
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • crates/plugin/src/lib.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
{crates/core/src/plugin/dynamic/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Native and worker plugins are trusted extensions; document that native plugins are in-process and unsandboxed, and worker plugins provide process isolation but not a security sandbox.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
{docs/build-plugins/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

When detailed dynamic plugin guides exist, keep Rust native, Python worker, and grpc-v1 protocol details on separate pages.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs,examples}/**/*

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update docs and examples.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
docs/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/migration-guides.mdx
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/src/api/mod.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/event_registry.rs
  • crates/ffi/src/api/tool_registry.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/ffi/src/api/mod.rs
  • crates/node/src/callback_factory.rs
  • crates/core/src/stream.rs
  • crates/plugin/src/lib.rs
  • crates/core/src/api/scope.rs
  • crates/ffi/src/api/llm_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/python/src/py_api/mod.rs
  • crates/core/tests/fixtures/native_plugin/src/lib.rs
  • crates/core/src/api/tool.rs
  • crates/ffi/src/api/event_registry.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/python/src/py_callable.rs
  • crates/core/src/plugin/dynamic/native.rs
  • crates/ffi/src/api/tool_registry.rs
  • crates/core/src/api/llm.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/api/mod.rs
  • crates/node/src/callable.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/_native.pyi
  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
  • crates/node/src/api/mod.rs
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/_native.pyi
  • go/nemo_relay/callbacks.go
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/_native.pyi
**/*.{md,rst,html,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

**/*.{md,rst,html,txt}: Always spell NVIDIA in all caps. Do not use Nvidia, nvidia, nVidia, nVIDIA, or NV.
Use an NVIDIA before a noun because the name starts with an 'en' sound.
Do not add a registered trademark symbol after NVIDIA when referring to the company.
Use trademark symbols with product names only when the document type or legal guidance requires them.
Verify official capitalization, spacing, and hyphenation for product names.
Precede NVIDIA product names with NVIDIA on first mention when it is natural and accurate.
Do not rewrite product names for grammar or title-case rules.
Preserve third-party product names according to the owner's spelling.
Include the company name and full model qualifier on first use when it helps identify the model.
Preserve the official capitalization and punctuation of model names.
Use shorter family names only after the full name is established.
Spell out a term on first use and put the acronym in parentheses unless the acronym is widely understood by the intended audience.
Use the acronym on later mentions after it has been defined.
For long documents, reintroduce the full term if readers might lose context.
Form plurals of acronyms with s, not an apostrophe, such as GPUs.
In headings, common acronyms can remain abbreviated. Spell out the term in the first or second sentence of the body.
Common terms such as CPU, GPU, PC, API, and UI usually do not need to be spelled out for developer audiences.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst,html}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-brand-terminology.md)

Link the first mention of a product name when the destination helps the reader.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst,txt}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

Spell NVIDIA in all caps. Do not use Nvidia, nvidia, or NV.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.{md,rst}

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-guide.md)

**/*.{md,rst}: Format commands, code elements, expressions, package names, file names, and paths as inline code.
Use descriptive link text. Avoid raw URLs and weak anchors such as "here" or "read more."
Use title case consistently for technical documentation headings.
Introduce code blocks, lists, tables, and images with complete sentences.
Write procedures as imperative steps. Keep steps parallel and split long procedures into smaller tasks.
Prefer active voice, present tense, short sentences, contractions, and plain English.
Use can for possibility and reserve may for permission.
Use after for temporal relationships instead of once.
Prefer refer to over see when the wording points readers to another resource.
Avoid culture-specific idioms, unnecessary Latinisms, jokes, and marketing exaggeration in technical docs.
Spell out months in body text, avoid ordinal dates, and use clear time zones.
Spell out whole numbers from zero through nine unless they are technical values, parameters, versions, or UI values.
Use numerals for 10 or greater and include commas in thousands.
Do not add trademark symbols to learning-oriented docs unless the source, platform, or legal guidance explicitly requires them.

Files:

  • ATTRIBUTIONS-Rust.md
**/*.md

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/assets/nvidia-style-technical-docs.md)

**/*.md: Use title case consistently in technical documentation headings
Avoid quotation marks, ampersands, and exclamation marks in headings
Keep product, event, research, and whitepaper names in their official title case
Use title case for table headers
Do not force social-media sentence case into technical docs
Format code elements, commands, parameters, package names, and expressions in monospace
Format directories, file names, and paths in monospace using backticks
Use angle brackets inside monospace for variables inside paths, such as /home/<username>/.login
Format error messages and strings in quotation marks, keeping literal code strings in code formatting when clearer
Format UI buttons, menus, fields, and labels in bold
Use angle brackets between UI labels for menu paths, such as File > Save As
Use italics for new terms on first use, sparingly and only when introducing the term
Use italics for publication titles
Format keyboard shortcuts in plain text, such as Press Ctrl+Alt+Delete
Use owner/repo link text for GitHub repositories, preferring [NVIDIA/NeMo](link) over prose references like 'the GitHub repo'
Introduce every code block with a complete sentence
Do not make a code block complete the grammar of the previous sentence
Do not continue a sentence after a code block
Use syntax highlighting when the format supports it for code blocks
Avoid the word 'snippet' unless the surrounding docs already use it as a term of art
Keep inline method, function, and class references consistent with nearby docs, omitting empty parentheses for prose readability when no call is shown
Use descriptive anchor text that matches the destination title when possible for links
Avoid raw URLs in running text
Avoid generic anchor text such as 'here,' 'this page,' and 'read more'
Include acronyms in link text when a linked term includes an acronym
Do not link long sentences or multiple sentences
Avoid links that pull readers away from a procedure unless the link is a p...

Files:

  • ATTRIBUTIONS-Rust.md
{crates/core/src/plugin/dynamic/**/*.rs,crates/plugin/**/*.rs,crates/worker/**/*.rs,crates/worker-proto/**/*.rs,python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Manifest validation must cover kind, compatibility, load contract, integrity, capability mismatch, and disabled-plugin behavior.

Files:

  • crates/plugin/src/lib.rs
  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
{crates/plugin/**/*.rs,python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Rust and Python SDKs must expose every supported registration surface.

Files:

  • crates/plugin/src/lib.rs
go/nemo_relay/**/*.go

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

go/nemo_relay/**/*.go: Format changed Go packages with cd go/nemo_relay && go fmt ./...
Run Go tests with just test-go to build and test the NeMo Relay Go binding
Use just build-go when you want an explicit build-only pass or need the artifact for other work
Use just ci=true test-go when you need the CI-style coverage and JUnit path
On macOS, set DYLD_LIBRARY_PATH to the ../../target/release directory before running the raw go test command directly

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/callbacks.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/callbacks.go
**/*.{py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • go/nemo_relay/callbacks.go
go/nemo_relay/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/callbacks.go
go/nemo_relay/**/*

⚙️ CodeRabbit configuration file

go/nemo_relay/**/*: Review Go binding changes for cgo memory ownership, race safety, callback cleanup, idiomatic exported APIs, and parity with Rust/FFI behavior.
Any API change should include focused Go tests and consider race-test behavior.

Files:

  • go/nemo_relay/callbacks.go
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/{tool,llm,shared,scope}.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Wire the new middleware chain into the appropriate lifecycle owner and pipeline stage: tool and LLM execution paths use tool.rs or llm.rs; shared mark and scope event sanitization uses shared.rs and is called from scope.rs.

Files:

  • crates/core/src/api/scope.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Preserve the documented pipeline order: conditional guardrails, request intercepts, request sanitization, execution intercepts, and response sanitization for tool and LLM execution; specialized sanitization, event creation, and dispatch for mark and scope events.

Files:

  • crates/core/src/api/scope.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/tool.rs
  • crates/core/src/api/llm.rs
crates/ffi/nemo_relay.h

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Check the generated header diff when any exported symbol or type changed in the FFI surface

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/core/src/plugin/dynamic/**/*.rs,examples/rust-native-plugin/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not pass Rust runtime types, trait objects, futures, or allocator-owned strings across the native dynamic-library boundary.

Files:

  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
crates/core/src/plugin/dynamic/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

The native loader must keep libraries alive until registered callbacks are cleared and must deregister plugin kinds before unload.

Files:

  • crates/core/src/plugin/dynamic/worker.rs
  • crates/core/src/plugin/dynamic/native.rs
🪛 markdownlint-cli2 (0.23.0)
ATTRIBUTIONS-Rust.md

[warning] 24924-24924: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 24924-24924: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 24925-24925: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Above

(MD022, blanks-around-headings)


[warning] 24925-24925: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 25131-25131: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below

(MD022, blanks-around-headings)


[warning] 25132-25132: Fenced code blocks should be surrounded by blank lines

(MD031, blanks-around-fences)


[warning] 25132-25132: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (35)
crates/core/src/plugin/dynamic/native.rs (3)

2442-2443: Legacy sync wrappers still execute plugin FFI directly on runtime worker threads; previously flagged.


796-799: LGTM!

Also applies to: 866-880


1377-1454: LGTM!

Also applies to: 1539-1620

crates/core/src/api/runtime/subscriber_dispatcher.rs (1)

254-287: LGTM!

crates/core/src/api/scope.rs (1)

248-255: LGTM!

Also applies to: 314-321

crates/core/src/api/tool.rs (1)

284-303: LGTM!

Also applies to: 459-489

crates/plugin/src/lib.rs (1)

43-44: LGTM!

Also applies to: 847-888, 2388-2393

crates/core/src/plugin/dynamic/worker.rs (2)

1122-1131: LGTM!

Also applies to: 1161-1212, 1250-1304


1495-1545: LGTM!

Also applies to: 1872-1888

crates/core/tests/fixtures/native_plugin/src/lib.rs (1)

293-309: LGTM!

Also applies to: 651-727, 729-795

crates/core/tests/unit/native_plugin_tests.rs (1)

260-260: LGTM!

crates/ffi/build.rs (1)

30-56: LGTM!

crates/adaptive/tests/unit/runtime_tests.rs (1)

639-641: LGTM!

crates/ffi/src/api/mod.rs (1)

108-115: LGTM!

Also applies to: 159-159, 197-197, 251-251, 414-414

crates/node/src/callback_factory.rs (1)

69-88: LGTM!

python/nemo_relay/_native.pyi (1)

1675-1740: LGTM!

crates/ffi/src/api/llm_registry.rs (1)

5-15: LGTM!

Also applies to: 17-105

crates/ffi/nemo_relay.h (1)

135-149: LGTM!

Also applies to: 258-267, 467-493, 2570-2605, 2668-2709, 3213-3239

crates/ffi/src/api/event_registry.rs (1)

5-7: LGTM!

Also applies to: 47-75, 77-106, 166-199, 201-233

crates/ffi/src/api/tool_registry.rs (1)

5-11: LGTM!

Also applies to: 13-38, 40-63, 65-88, 90-113, 115-118, 120-144

crates/ffi/src/callable.rs (6)

261-286: LlmStream continuation still buffers the whole generation into a Json::Array. Incremental delivery is lost for async stream intercepts; same shape as nemo_relay_async_next_invoke_callback (Lines 332-346). Already raised on this file.


136-156: LGTM!


158-204: LGTM!


218-233: LGTM!

Also applies to: 287-299


304-363: LGTM!


368-423: LGTM!

docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx (2)

139-143: Complete and Pending are ABI return values and still need monospace. Same wording appears in docs/reference/event-sanitizers.mdx with different casing. As per coding guidelines, "Format code elements, commands, parameters, package names, and expressions in monospace".

Source: Coding guidelines


119-137: LGTM!

Also applies to: 145-152

crates/node/src/api/mod.rs (1)

3544-3570: LGTM!

crates/node/src/callable.rs (2)

210-245: LGTM!

Also applies to: 247-281, 374-442


548-626: LGTM!

Also applies to: 663-722, 956-981, 1092-1172

crates/python/src/py_callable.rs (1)

410-426: LGTM!

Also applies to: 1109-1177

go/nemo_relay/callbacks.go (1)

192-213: LGTM!

Also applies to: 639-672, 674-697

docs/reference/migration-guides.mdx (1)

29-72: LGTM!

Also applies to: 276-300

ATTRIBUTIONS-Rust.md (1)

24923-24925: Regenerate the attribution file with the pre-commit hook.

These blocks still trigger markdownlint for missing blank lines and an unspecified fenced-code language. Because ATTRIBUTIONS-Rust.md is generated, run the hook rather than editing this file manually, then commit its output and verify a clean result. This duplicates the previously reported stale-generated-file issue.

As per coding guidelines, run uv run pre-commit run --all-files before handoff.

Also applies to: 25131-25132

Sources: Coding guidelines, Linters/SAST tools

Comment thread crates/core/src/api/llm.rs
Comment thread crates/core/src/api/runtime/subscriber_dispatcher.rs
Comment thread crates/core/src/api/scope.rs
Comment thread crates/ffi/build.rs
Comment thread crates/node/src/callable.rs Outdated
Comment thread python/nemo_relay/_native.pyi

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (2)
crates/node/src/callable.rs (1)

753-766: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Record request-deserialization failures.

Invalid non-null sanitizer output returns FlowError without updating getLastCallbackError(), unlike the Promise path. Record the serde_json::from_value error too.

Proposed fix
-                serde_json::from_value(value)
-                .map(Some)
-                .map_err(|error| FlowError::Internal(format!(
-                    "JS LLM sanitize request callback failed: failed to deserialize LlmRequest: {error}"
-                )))
+                serde_json::from_value(value)
+                    .map(Some)
+                    .map_err(|error| FlowError::Internal(format!(
+                        "JS LLM sanitize request callback failed: failed to deserialize LlmRequest: {error}"
+                    )))
+                    .inspect_err(|error| record_callback_error(error.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 `@crates/node/src/callable.rs` around lines 753 - 766, Update the non-null
deserialization path after the null check in the sanitizer callback flow to
record the serde_json::from_value error via record_callback_error, matching the
existing Promise path behavior, before returning the FlowError. Preserve the
current successful Some(value) result and error message.
crates/core/src/api/shared.rs (1)

67-84: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Poisoned-lock handling now graceful for the context lock, but the scope-stack lock still panics.

Lines 76-83 added graceful logging + fail-closed None when context.read() fails, addressing the earlier fail-open concern (raw unsanitized event no longer gets published — every caller shown here treats None as "drop the event"). However, line 72's scope_stack.read().expect("scope stack lock poisoned") will still panic the entire call path if that lock is poisoned, defeating the resilience just added for the sibling lock in the same function.

♻️ Proposed fix to fail closed on both locks
-    let scope_guard = scope_stack.read().expect("scope stack lock poisoned");
-    let context = global_context();
-    let state = match context.read() {
-        Ok(state) => state,
-        Err(error) => {
+    let scope_guard = match scope_stack.read() {
+        Ok(guard) => guard,
+        Err(error) => {
+            log::error!(
+                target: "nemo_relay.runtime",
+                event = "event_sanitizer_snapshot_failed";
+                "Event was dropped because the scope stack lock is poisoned: {error}"
+            );
+            return None;
+        }
+    };
+    let context = global_context();
+    let state = match context.read() {
+        Ok(state) => state,
+        Err(error) => {
             log::error!(
                 target: "nemo_relay.runtime",
                 event = "event_sanitizer_snapshot_failed";
                 "Event was dropped because the runtime context lock is poisoned: {error}"
             );
             return None;
         }
     };
🤖 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 `@crates/core/src/api/shared.rs` around lines 67 - 84, Update
snapshot_event_sanitizers to handle scope_stack.read() failure without
panicking: log the poisoned-lock error with the same event-sanitizer snapshot
context and return None, matching the existing fail-closed handling for
context.read(). Remove the expect call while preserving normal scope-stack
access 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 `@docs/reference/event-sanitizers.mdx`:
- Around line 191-195: Update the async sanitizer registration documentation
around the source-first C API description to state that every callback returning
Pending must settle its completion handle exactly once by resolving or
rejecting; otherwise serial publication remains blocked indefinitely. Also
document how cancellation is handled and whether late settlement after
cancellation is ignored or otherwise constrained, without changing the existing
callback or registration API descriptions.

---

Outside diff comments:
In `@crates/core/src/api/shared.rs`:
- Around line 67-84: Update snapshot_event_sanitizers to handle
scope_stack.read() failure without panicking: log the poisoned-lock error with
the same event-sanitizer snapshot context and return None, matching the existing
fail-closed handling for context.read(). Remove the expect call while preserving
normal scope-stack access behavior.

In `@crates/node/src/callable.rs`:
- Around line 753-766: Update the non-null deserialization path after the null
check in the sanitizer callback flow to record the serde_json::from_value error
via record_callback_error, matching the existing Promise path behavior, before
returning the FlowError. Preserve the current successful Some(value) result and
error message.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 2ab99778-f2c3-4400-9762-db9719324f57

📥 Commits

Reviewing files that changed from the base of the PR and between 2df9573 and 4d24e18.

📒 Files selected for processing (15)
  • ATTRIBUTIONS-Rust.md
  • crates/core/src/api/llm.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/stream.rs
  • crates/ffi/build.rs
  • crates/node/src/callable.rs
  • crates/node/src/callback_factory.rs
  • crates/node/tests/tools_tests.mjs
  • crates/python/tests/coverage/coverage_tests.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
  • python/nemo_relay/_native.pyi
  • python/tests/test_builtin_codecs.py
💤 Files with no reviewable changes (1)
  • ATTRIBUTIONS-Rust.md
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Preview docs
  • GitHub Check: Check / Run
🧰 Additional context used
📓 Path-based instructions (33)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/node/src/callback_factory.rs
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/node/tests/tools_tests.mjs
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/node/src/callback_factory.rs
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/core/src/stream.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/node/src/callback_factory.rs
  • docs/reference/event-sanitizers.mdx
  • python/tests/test_builtin_codecs.py
  • crates/node/tests/tools_tests.mjs
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • python/nemo_relay/_native.pyi
  • docs/reference/migration-guides.mdx
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/node/src/callback_factory.rs
  • python/tests/test_builtin_codecs.py
  • crates/ffi/build.rs
  • crates/core/src/stream.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/node/src/callable.rs
  • crates/core/src/api/llm.rs
  • crates/python/tests/coverage/coverage_tests.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/node/src/callback_factory.rs
  • crates/node/tests/tools_tests.mjs
  • crates/ffi/build.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/node/src/callable.rs
  • crates/python/tests/coverage/coverage_tests.rs
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

In MDX files, top-of-file comments must use JSX comment delimiters ({/* to open and */} to close); do not use HTML comments for MDX SPDX headers

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
{docs,examples}/**/*

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update docs and examples.

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
docs/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_builtin_codecs.py
**/*.{py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • python/tests/test_builtin_codecs.py
python/tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

  • python/tests/test_builtin_codecs.py
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • python/tests/test_builtin_codecs.py
  • crates/node/tests/tools_tests.mjs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/python/tests/coverage/coverage_tests.rs
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/tools_tests.mjs
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/build.rs
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/build.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/stream.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/stream.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/stream.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/stream.rs
  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/{tool,llm,shared,scope}.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Wire the new middleware chain into the appropriate lifecycle owner and pipeline stage: tool and LLM execution paths use tool.rs or llm.rs; shared mark and scope event sanitization uses shared.rs and is called from scope.rs.

Files:

  • crates/core/src/api/shared.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Preserve the documented pipeline order: conditional guardrails, request intercepts, request sanitization, execution intercepts, and response sanitization for tool and LLM execution; specialized sanitization, event creation, and dispatch for mark and scope events.

Files:

  • crates/core/src/api/shared.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • python/nemo_relay/_native.pyi
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • python/nemo_relay/_native.pyi
python/nemo_relay/**/*

⚙️ CodeRabbit configuration file

python/nemo_relay/**/*: Review Python wrapper changes for typed API consistency, contextvars-based scope isolation, async behavior, and parity with the native extension.
Stubs and runtime implementations should stay aligned.

Files:

  • python/nemo_relay/_native.pyi
🔇 Additional comments (19)
crates/core/src/api/llm.rs (1)

839-911: LGTM!

crates/core/src/api/runtime/subscriber_dispatcher.rs (1)

288-290: LGTM!

crates/core/src/stream.rs (1)

351-359: LGTM!

crates/ffi/build.rs (1)

21-30: LGTM!

crates/node/src/callable.rs (1)

799-805: LGTM!

python/tests/test_builtin_codecs.py (1)

435-456: LGTM!

crates/node/tests/tools_tests.mjs (2)

611-623: Async success tests still omit rejection coverage.

The existing finding remains applicable: add cases where the conditional guardrail rejects and the request intercept throws or returns a rejected Promise, including propagation and cleanup assertions. As per path instructions, tests should cover promised API error paths.

Also applies to: 798-810

Source: Path instructions


963-979: LGTM!

crates/node/src/callback_factory.rs (1)

69-77: LGTM!

Also applies to: 78-84

docs/reference/event-sanitizers.mdx (2)

56-69: LGTM!


197-202: LGTM!

docs/reference/migration-guides.mdx (4)

3-3: 🎯 Functional Correctness

Verify the omitted migration examples against the current API contracts.

The supplied excerpt does not include these ranges. Confirm that the async callback table, migration text, and pinned Box::pin(async move { ... }) example match the current Rust and worker APIs before publishing them.

Also applies to: 16-73, 99-103

Source: Path instructions


202-210: LGTM!

Also applies to: 219-227


274-281: 🗄️ Data Integrity & Integration

Verify the ABI v3 fallback wording against the loader contract.

This text makes a compatibility guarantee about preserving the v2 table prefix and retrying a legacy v2 table after v3 rejection. Confirm it matches the native loader and header behavior exactly, since incorrect migration guidance can strand existing plugins.

Source: Path instructions


298-298: 📐 Maintainability & Code Quality

Run just docs-linkcheck for the updated ABI link.

As per coding guidelines, changed documentation links require just docs-linkcheck.

Source: Coding guidelines

crates/python/tests/coverage/py_callable_coverage_tests.rs (1)

113-215: LGTM!

crates/core/src/api/shared.rs (1)

48-59: LGTM!

Also applies to: 221-296

python/nemo_relay/_native.pyi (1)

40-62: LGTM!

Also applies to: 1675-1734

crates/python/tests/coverage/coverage_tests.rs (1)

664-749: LGTM!

Comment thread docs/reference/event-sanitizers.mdx Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

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

⚠️ Outside diff range comments (1)
crates/node/src/callable.rs (1)

735-735: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Preserve serialization failures instead of converting them into omission values.

Both adapters turn serialization errors into null/None, allowing sanitizer processing to continue with incomplete input and potentially clear fields. Return a FlowError and record it at each site.

  • crates/node/src/callable.rs#L735-L735: replace unwrap_or(Json::Null) with fallible serialization and error propagation.
  • crates/node/src/callable.rs#L461-L466: replace and_then(...ok()) with fallible transpose() handling for category_profile.

As per path instructions, binding changes must preserve stable async and type-conversion 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 `@crates/node/src/callable.rs` at line 735, The request serialization at
crates/node/src/callable.rs#L735 must propagate serialization failures as
FlowError instead of converting them to Json::Null; record the error while
preserving the existing async and type-conversion behavior. At
crates/node/src/callable.rs#L461-L466, update category_profile serialization to
use fallible transpose handling and propagate/record errors rather than dropping
them through and_then(...).ok().

Source: Path instructions

🤖 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 `@crates/core/src/api/shared.rs`:
- Around line 72-80: Apply the same fail-closed poisoned-lock handling to the
scope_stack reads in the public mark emission path at scope.rs methods around
the existing expect calls near lines 360 and 365. Replace those panicking expect
calls with error handling that logs the poisoned lock and drops the event,
matching the None-return behavior already used by the shared snapshot helper and
preserving normal successful reads.

In `@docs/reference/event-sanitizers.mdx`:
- Around line 191-197: Update the migration guidance near the source-first C API
discussion to state that callbacks returning Pending must call
nemo_relay_async_completion_release after resolving or rejecting the completion
handle, ensuring the callback-owned reference is released while preserving the
existing exactly-once settlement requirement.

---

Outside diff comments:
In `@crates/node/src/callable.rs`:
- Line 735: The request serialization at crates/node/src/callable.rs#L735 must
propagate serialization failures as FlowError instead of converting them to
Json::Null; record the error while preserving the existing async and
type-conversion behavior. At crates/node/src/callable.rs#L461-L466, update
category_profile serialization to use fallible transpose handling and
propagate/record errors rather than dropping them through and_then(...).ok().
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 5167972f-c0da-480d-90e8-8fc856e6bad7

📥 Commits

Reviewing files that changed from the base of the PR and between 3252169 and fd1a8c3.

📒 Files selected for processing (4)
  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
  • crates/node/tests/tools_tests.mjs
  • docs/reference/event-sanitizers.mdx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (25)
crates/node/**/*.{js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use camelCase for Node.js public APIs.

Files:

  • crates/node/tests/tools_tests.mjs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/node/tests/tools_tests.mjs
  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/node/tests/tools_tests.mjs
  • docs/reference/event-sanitizers.mdx
  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/node/tests/tools_tests.mjs
  • crates/node/src/callable.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/node/tests/tools_tests.mjs
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

In MDX files, top-of-file comments must use JSX comment delimiters ({/* to open and */} to close); do not use HTML comments for MDX SPDX headers

Files:

  • docs/reference/event-sanitizers.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/reference/event-sanitizers.mdx
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/reference/event-sanitizers.mdx
{docs,examples}/**/*

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update docs and examples.

Files:

  • docs/reference/event-sanitizers.mdx
docs/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/reference/event-sanitizers.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/reference/event-sanitizers.mdx
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/src/api/shared.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/src/api/shared.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/api/shared.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/src/api/shared.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
crates/core/src/api/{tool,llm,shared,scope}.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Wire the new middleware chain into the appropriate lifecycle owner and pipeline stage: tool and LLM execution paths use tool.rs or llm.rs; shared mark and scope event sanitization uses shared.rs and is called from scope.rs.

Files:

  • crates/core/src/api/shared.rs
crates/core/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Preserve the documented pipeline order: conditional guardrails, request intercepts, request sanitization, execution intercepts, and response sanitization for tool and LLM execution; specialized sanitization, event creation, and dispatch for mark and scope events.

Files:

  • crates/core/src/api/shared.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/core/src/api/shared.rs
  • crates/node/src/callable.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/src/api/shared.rs
🔇 Additional comments (4)
crates/node/src/callable.rs (1)

210-246: LGTM!

Also applies to: 247-269, 271-281, 283-295, 297-341, 343-371, 373-395, 397-442, 444-460, 467-509, 552-570, 579-597, 606-624, 668-718, 732-734, 736-767, 773-785, 794-807, 961-980, 1097-1171

docs/reference/event-sanitizers.mdx (1)

56-69: LGTM!

Also applies to: 200-205

crates/node/tests/tools_tests.mjs (1)

624-637: LGTM!

Also applies to: 818-846

crates/core/src/api/shared.rs (1)

72-80: 📐 Maintainability & Code Quality

Run the required core-runtime validation matrix.

Provide successful cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, cargo deny check, validate-change, and the configured Rust/Python/Go/Node matrix before merge.

As per coding guidelines, “Any Rust change must run just test-rust,” “Any Rust change must run cargo fmt --all,” and “Changes to crates/core or crates/adaptive must run the full language matrix.”

Source: Coding guidelines

Comment thread crates/core/src/api/shared.rs
Comment thread docs/reference/event-sanitizers.mdx Outdated
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 (1)
go/nemo_relay/callbacks.go (1)

194-216: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Wait for the cancellation poller before releasing completion.

Line 213 stops the poller but does not join it. It can still be inside nemo_relay_async_completion_is_cancelled when the caller’s next defer releases completion, causing a use-after-free across cgo.

Proposed fix
 func contextForCompletion(completion *C.NemoRelayAsyncCompletion) (context.Context, func()) {
 	ctx, cancel := context.WithCancel(context.Background())
 	done := make(chan struct{})
+	stopped := make(chan struct{})
 	var doneOnce sync.Once
 	go func() {
+		defer close(stopped)
 		ticker := time.NewTicker(10 * time.Millisecond)
 		defer ticker.Stop()
 		for {
@@
 	return ctx, func() {
-		doneOnce.Do(func() { close(done) })
-		cancel()
+		doneOnce.Do(func() {
+			close(done)
+			cancel()
+		})
+		<-stopped
 	}
 }
🤖 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 `@go/nemo_relay/callbacks.go` around lines 194 - 216, Update
contextForCompletion so its returned cleanup function stops and joins the
polling goroutine before returning, ensuring no call to
nemo_relay_async_completion_is_cancelled remains in progress before completion
can be released. Preserve the existing once-only cleanup and context
cancellation behavior.

Sources: Coding guidelines, Path instructions

🤖 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 `@go/nemo_relay/callbacks.go`:
- Around line 194-216: Update contextForCompletion so its returned cleanup
function stops and joins the polling goroutine before returning, ensuring no
call to nemo_relay_async_completion_is_cancelled remains in progress before
completion can be released. Preserve the existing once-only cleanup and context
cancellation behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 6fee23b3-4b8c-4f00-8b6e-b5bcd7d0ee46

📥 Commits

Reviewing files that changed from the base of the PR and between c36a4a5 and ada9062.

📒 Files selected for processing (17)
  • crates/core/src/api/llm.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/src/callable.rs
  • crates/node/src/callable.rs
  • crates/plugin/src/lib.rs
  • crates/python/src/py_api/mod.rs
  • crates/python/src/py_callable.rs
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
  • go/nemo_relay/callbacks.go
  • python/tests/test_llm.py
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (43)
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

In MDX files, top-of-file comments must use JSX comment delimiters ({/* to open and */} to close); do not use HTML comments for MDX SPDX headers

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • crates/plugin/src/lib.rs
{crates/core/src/plugin/dynamic/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Native and worker plugins are trusted extensions; document that native plugins are in-process and unsandboxed, and worker plugins provide process isolation but not a security sandbox.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs/build-plugins/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

When detailed dynamic plugin guides exist, keep Rust native, Python worker, and grpc-v1 protocol details on separate pages.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs,examples}/**/*

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update docs and examples.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • go/nemo_relay/callbacks.go
  • docs/reference/migration-guides.mdx
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
docs/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • docs/reference/event-sanitizers.mdx
  • docs/reference/migration-guides.mdx
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/plugin/src/lib.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/callable.rs
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/python/tests/coverage/py_callable_coverage_tests.rs
  • crates/core/tests/unit/llm_api_tests.rs
  • python/tests/test_llm.py
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/plugin/src/lib.rs
  • crates/python/src/py_api/mod.rs
  • crates/ffi/src/api/scope_registry.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/ffi/src/callable.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
  • python/tests/test_llm.py
  • crates/node/src/callable.rs
  • crates/python/src/py_callable.rs
{crates/core/src/plugin/dynamic/**/*.rs,crates/plugin/**/*.rs,crates/worker/**/*.rs,crates/worker-proto/**/*.rs,python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Manifest validation must cover kind, compatibility, load contract, integrity, capability mismatch, and disabled-plugin behavior.

Files:

  • crates/plugin/src/lib.rs
{crates/plugin/**/*.rs,python/plugin/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Rust and Python SDKs must expose every supported registration surface.

Files:

  • crates/plugin/src/lib.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/core/tests/unit/llm_api_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/unit/llm_api_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/core/tests/unit/llm_api_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/core/tests/unit/llm_api_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
go/nemo_relay/**/*.go

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

go/nemo_relay/**/*.go: Format changed Go packages with cd go/nemo_relay && go fmt ./...
Run Go tests with just test-go to build and test the NeMo Relay Go binding
Use just build-go when you want an explicit build-only pass or need the artifact for other work
Use just ci=true test-go when you need the CI-style coverage and JUnit path
On macOS, set DYLD_LIBRARY_PATH to the ../../target/release directory before running the raw go test command directly

Use PascalCase for public Go APIs.

Files:

  • go/nemo_relay/callbacks.go
**/*.go

📄 CodeRabbit inference engine (CONTRIBUTING.md)

When changing the experimental Go binding, format Go code with gofmt and keep go vet ./... passing.

Files:

  • go/nemo_relay/callbacks.go
{crates/python/src/py_api/mod.rs,python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go,crates/node/src/api/**/*.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update the language-native bindings for every exposed surface in Python, Go, and Node.js.

Files:

  • go/nemo_relay/callbacks.go
  • crates/python/src/py_api/mod.rs
{python/nemo_relay/**/*.py,python/nemo_relay/**/*.pyi,go/nemo_relay/**/*.go}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Update language wrapper helpers such as Python wrapper modules, Python type stubs, and Go shorthand packages when the new behavior belongs in those helper layers.

Files:

  • go/nemo_relay/callbacks.go
**/*.{py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/maintain-observability/SKILL.md)

Keep Python, Go, and Node.js config objects and subscriber/exporter methods aligned so all bindings expose the same logical knobs and semantics.

Files:

  • go/nemo_relay/callbacks.go
  • python/tests/test_llm.py
go/nemo_relay/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep shared plugin helpers in go/nemo_relay aligned with plugin registration, composition, and lifecycle behavior.

Files:

  • go/nemo_relay/callbacks.go
go/nemo_relay/**/*

⚙️ CodeRabbit configuration file

go/nemo_relay/**/*: Review Go binding changes for cgo memory ownership, race safety, callback cleanup, idiomatic exported APIs, and parity with Rust/FFI behavior.
Any API change should include focused Go tests and consider race-test behavior.

Files:

  • go/nemo_relay/callbacks.go
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/nemo_relay.h
  • crates/ffi/src/callable.rs
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/src/callable.rs
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/src/api/scope_registry.rs
  • crates/ffi/nemo_relay.h
crates/ffi/nemo_relay.h

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Check the generated header diff when any exported symbol or type changed in the FFI surface

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Preserve the documented pipeline order: conditional guardrails, request intercepts, request sanitization, execution intercepts, and response sanitization for tool and LLM execution; specialized sanitization, event creation, and dispatch for mark and scope events.

Files:

  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/runtime/state.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/runtime/state.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

crates/core/src/api/runtime/state.rs: Add a SortedRegistry<GuardrailEntry<...>> or SortedRegistry<Intercept<...>> field to NemoRelayContextState for the new middleware type.
Add chain-execution helpers to NemoRelayContextState, following existing helpers such as tool_sanitize_request_chain or tool_request_intercepts_chain.

Files:

  • crates/core/src/api/runtime/state.rs
crates/core/src/api/{tool,llm,shared,scope}.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Wire the new middleware chain into the appropriate lifecycle owner and pipeline stage: tool and LLM execution paths use tool.rs or llm.rs; shared mark and scope event sanitization uses shared.rs and is called from scope.rs.

Files:

  • crates/core/src/api/llm.rs
**/*.py

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.py: When changing the Python wrapper package, tests, or docs tooling, lint with Ruff (E, F, W, I), format with Ruff formatter (120-character lines, double quotes), and pass ty type checking.
Add the SPDX license header to all Python source files using the # comment form.

Files:

  • python/tests/test_llm.py
python/tests/**/*.py

📄 CodeRabbit inference engine (.agents/skills/test-python-binding/SKILL.md)

python/tests/**/*.py: Pytest is used to run tests.
Do not add @pytest.mark.asyncio to any test; async tests are automatically detected and run by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, do not define a new class; use unittest.mock.MagicMock or unittest.mock.AsyncMock, with the spec constructor argument when necessary.
Name mocked classes with the mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures; if a fixture is needed in multiple test files, place it in a conftest.py file.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and define the fixture function as def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.

Files:

  • python/tests/test_llm.py
🔇 Additional comments (9)
python/tests/test_llm.py (1)

170-170: LGTM!

Also applies to: 279-302, 303-326, 327-348, 349-369, 404-404, 416-416

crates/core/src/api/runtime/state.rs (1)

647-648: 📐 Maintainability & Code Quality

Attach the required cross-layer validation results.

  • crates/core/src/api/runtime/state.rs#L647-L648: run Rust formatting, strict Clippy, just test-rust, validate-change, and the core full language matrix.
  • crates/core/src/api/llm.rs#L710-L712: include lifecycle and credential-redaction coverage in that matrix.
  • crates/core/src/api/runtime/subscriber_dispatcher.rs#L168-L190: include async dispatch, ordering, and failure-path coverage.
  • crates/python/src/py_api/mod.rs#L1373-L1381: run the Python binding test target.
  • crates/python/src/py_callable.rs#L1037-L1041: run Python async-callback coverage.
  • crates/core/tests/unit/llm_api_tests.rs#L298-L298: include this updated async sanitizer test.
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx#L121-L145: run full pre-commit validation.
  • docs/reference/migration-guides.mdx#L209-L210: run just docs-linkcheck for the changed documentation links.

As per coding guidelines, “Any Rust change must run just test-rust,” “If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js,” and “Before review or handoff, run uv run pre-commit run --all-files.”

Source: Coding guidelines

crates/ffi/src/callable.rs (1)

105-109: LGTM!

crates/plugin/src/lib.rs (1)

832-834: LGTM!

Also applies to: 2390-2407

crates/ffi/nemo_relay.h (1)

489-493: LGTM!

crates/ffi/src/api/scope_registry.rs (1)

33-67: LGTM!

Also applies to: 113-141

crates/node/src/callable.rs (1)

454-512: LGTM!

crates/python/tests/coverage/py_callable_coverage_tests.rs (1)

703-728: LGTM!

docs/reference/event-sanitizers.mdx (1)

52-54: 📐 Maintainability & Code Quality

No action needed.

Signed-off-by: Will Killian <wkillian@nvidia.com>
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

/coderabbit review

@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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 `@crates/adaptive/src/test_support.rs`:
- Around line 6-12: Move the test-only block_on helper from the production
test_support module into the existing test-support module under
crates/adaptive/tests, preserving its current implementation and imports. Remove
the production src definition and update test references to use the relocated
helper.

In `@crates/ffi/build.rs`:
- Around line 6-8: Update the Rust validation workflow associated with main and
validate_async_registration_parity to run and report cargo fmt --all, cargo
clippy --workspace --all-targets -- -D warnings, just test-rust, the complete
core/adaptive language matrix, and FFI surface/header validation; include the
results for each required check.
- Around line 39-97: Update validate_async_registration_parity to validate
complete async ABI prototypes rather than only function names and callback
typedefs. Parse or derive structured declarations containing each exported
function’s full return type, parameter types and order, including scope_uuid,
break_chain, and ownership parameters, then compare them against
ASYNC_REGISTRATIONS so any nemo_relay.h prototype drift fails validation.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: acced103-fd3d-4e5f-9a7b-bacd4cbeaa82

📥 Commits

Reviewing files that changed from the base of the PR and between 44ca679 and 2196ff2.

📒 Files selected for processing (13)
  • crates/adaptive/src/lib.rs
  • crates/adaptive/src/test_support.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/src/api/llm.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/integration/test_support.rs
  • crates/ffi/build.rs
  • crates/ffi/nemo_relay.h
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
📜 Review details
🧰 Additional context used
📓 Path-based instructions (32)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/adaptive/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep crates/adaptive aligned with the canonical adaptive config schema, built-in section helpers, plugin lifecycle, and validation/report behavior.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/nemo_relay.h
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/ffi/build.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/adaptive/src/test_support.rs
  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
crates/core/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/core or shared runtime semantics, also use validate-change for broader validation

Files:

  • crates/core/tests/integration/test_support.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
  • crates/core/src/api/llm.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/core/tests/integration/test_support.rs
  • crates/adaptive/tests/unit/acg_component_tests.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/core/tests/integration/pipeline_tests.rs
  • crates/core/tests/integration/api_surface_tests.rs
  • crates/core/tests/integration/middleware_tests.rs
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/build.rs
  • crates/ffi/nemo_relay.h
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/build.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/ffi/build.rs
  • crates/ffi/nemo_relay.h
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/review-doc-style/SKILL.md)

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

In MDX files, top-of-file comments must use JSX comment delimiters ({/* to open and */} to close); do not use HTML comments for MDX SPDX headers

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
**/*.{md,mdx}

📄 CodeRabbit inference engine (AGENTS.md)

Update README.md, fern/, package READMEs, and binding-support notes when public behavior, package names, examples, or supported bindings change.

**/*.{md,mdx}: Prefer the documented public API, not internal shortcuts
Keep package names, repo references, and build commands current
Keep release-process and release-notes guidance in repo-maintainer docs such as RELEASING.md, not as user-facing docs pages or CHANGELOG.md
Keep stable user-facing wrappers at scripts/ root in docs and examples; only point at namespaced helper paths when documenting internal maintenance work
When detailed dynamic plugin guides exist, keep Rust native plugin examples, Python worker plugin examples, and grpc-v1 protocol details on separate pages

If links in documentation change, run just docs-linkcheck.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
**/*.{md,markdown,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Markdown/MDX documentation files using the HTML comment block form.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{crates/core/src/plugin/dynamic/**,crates/plugin/**,crates/worker/**,crates/worker-proto/**,crates/types/**,python/plugin/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Keep the stable boundary explicit: native plugins cross a C ABI, and worker plugins cross grpc-v1.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{crates/core/src/plugin/dynamic/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**,docs/build-plugins/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Native and worker plugins are trusted extensions; document that native plugins are in-process and unsandboxed, and worker plugins provide process isolation but not a security sandbox.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs/build-plugins/**,examples/rust-native-plugin/**,examples/python-grpc-worker-plugin/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

When detailed dynamic plugin guides exist, keep Rust native, Python worker, and grpc-v1 protocol details on separate pages.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs,examples}/**/*

📄 CodeRabbit inference engine (.agents/skills/rename-surfaces/SKILL.md)

Update docs and examples.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
docs/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If documentation examples or commands under docs/ change, run the targeted docs checks appropriate to the change.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx
crates/ffi/nemo_relay.h

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Check the generated header diff when any exported symbol or type changed in the FFI surface

Update generated or generated-from-build surfaces such as crates/ffi/nemo_relay.h through the proper build step.

Files:

  • crates/ffi/nemo_relay.h
{crates/ffi/src/api/*.rs,crates/ffi/nemo_relay.h}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Add or update the shared C/FFI surface in the relevant crates/ffi/src/api/*.rs module, re-export it through crates/ffi/src/api/mod.rs, and keep the generated crates/ffi/nemo_relay.h header correct.

Files:

  • crates/ffi/nemo_relay.h
crates/core/src/{api/**/*.rs,api/runtime/**/*.rs,codec/**/*.rs,json.rs}

📄 CodeRabbit inference engine (.agents/skills/add-binding-feature/SKILL.md)

Implement the new or changed public runtime behavior first in the Rust core, especially under crates/core/src/api/ and related core modules such as crates/core/src/api/runtime/, crates/core/src/codec/, and crates/core/src/json.rs.

Files:

  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Preserve the documented pipeline order: conditional guardrails, request intercepts, request sanitization, execution intercepts, and response sanitization for tool and LLM execution; specialized sanitization, event creation, and dispatch for mark and scope events.

Files:

  • crates/core/src/api/runtime/subscriber_dispatcher.rs
  • crates/core/src/api/llm.rs
crates/core/src/api/{tool,llm,shared,scope}.rs

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Wire the new middleware chain into the appropriate lifecycle owner and pipeline stage: tool and LLM execution paths use tool.rs or llm.rs; shared mark and scope event sanitization uses shared.rs and is called from scope.rs.

Files:

  • crates/core/src/api/llm.rs
🧠 Learnings (1)
📚 Learning: 2026-07-28T03:31:05.964Z
Learnt from: willkill07
Repo: NVIDIA/NeMo-Relay PR: 564
File: crates/core/src/api/runtime/subscriber_dispatcher.rs:297-314
Timestamp: 2026-07-28T03:31:05.964Z
Learning: In this codebase’s runtime API, do not implement incremental native LLM stream forwarding via the native ABI v3 asynchronous middleware protocol (it can only settle a single JSON value via a one-shot completion handle and cannot forward stream chunks incrementally). If a latency-sensitive plugin needs streaming behavior, review for use of synchronous native stream intercepts or worker plugins instead of trying to chunk-deliver or incrementally forward over the ABI v3 async path.

Applied to files:

  • crates/core/src/api/runtime/subscriber_dispatcher.rs
🔇 Additional comments (11)
crates/adaptive/src/lib.rs (1)

17-18: 🎯 Functional Correctness

Drop this concern The adaptive_hints_intercept_tests.rs file is included as a unit test from crates/adaptive/src/adaptive_hints_intercept.rs, so crate::test_support::block_on is available in the crate test context.

			> Likely an incorrect or invalid review comment.
crates/core/src/api/runtime/subscriber_dispatcher.rs (1)

54-54: LGTM!

Also applies to: 98-355

crates/core/src/api/llm.rs (1)

23-24: LGTM!

Also applies to: 549-571, 860-860

crates/ffi/nemo_relay.h (1)

3221-3225: LGTM!

docs/build-plugins/dynamic-plugins/native-dynamic/about.mdx (1)

151-156: LGTM!

crates/adaptive/tests/unit/acg_component_tests.rs (1)

1090-1095: LGTM!

Also applies to: 1338-1343

crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs (1)

199-207: LGTM!

Also applies to: 269-277, 308-316

crates/core/tests/integration/api_surface_tests.rs (1)

10-11: LGTM!

Also applies to: 71-71, 710-758

crates/core/tests/integration/middleware_tests.rs (1)

16-17: LGTM!

crates/core/tests/integration/pipeline_tests.rs (1)

12-13: LGTM!

crates/core/tests/integration/test_support.rs (1)

7-18: LGTM!

Comment thread crates/adaptive/tests/support/mod.rs
Comment thread crates/ffi/build.rs
Comment thread crates/ffi/build.rs
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@crates/ffi/build.rs`:
- Around line 69-74: In the async declaration collection in build.rs, replace
the deduplication performed after sorting with validation that rejects any
adjacent declarations sharing the same name. Perform this uniqueness check
before signature validation, and preserve the declarations for subsequent
processing instead of silently discarding duplicates.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: c545ce61-ccee-4efc-b9f4-5332952e8070

📥 Commits

Reviewing files that changed from the base of the PR and between 2196ff2 and 4724e51.

📒 Files selected for processing (4)
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Check / Run
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (17)
**/*.rs

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

**/*.rs: Any Rust change must run just test-rust
Any Rust change must run cargo fmt --all
Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all for all FFI work since it is Rust work
Run just test-rust to validate FFI changes
Run cargo clippy --workspace --all-targets -- -D warnings to enforce strict linting on FFI work

When Rust files changed as part of Go work, also run cargo fmt --all, just test-rust, and cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Run cargo fmt --all when Rust files are changed as part of Node work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files are changed as part of Node work
Run just test-rust when Rust files are changed as part of Node work

When changing the core Rust runtime or Rust-facing API surface, format Rust code with cargo fmt (rustfmt defaults), keep cargo clippy -- -D warnings clean, and satisfy cargo deny check per deny.toml.

**/*.rs: If any Rust code changed, always run just test-rust.
If any Rust code changed, also run cargo fmt --all.
If any Rust code changed, also run cargo clippy --workspace --all-targets -- -D warnings.
For Rust changes headed for review, run cargo fmt --all and cargo clippy --workspace --all-targets -- -D warnings even if relying on pre-commit.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
{crates/core,crates/adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/prepare-pr/SKILL.md)

Changes to crates/core or crates/adaptive must run the full language matrix

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
**/*.{rs,py}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions in Rust and Python: use snake_case.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
**/*.{rs,py,js,mjs,cjs,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{rs,py,js,mjs,cjs,ts,tsx}: Use Json = serde_json::Value in Rust-facing runtime APIs where the existing code expects JSON payloads.
Use Result<T> with FlowError in core runtime paths, and keep errors explicit and binding-appropriate at the wrapper layer.
Keep async behavior on the existing tokio-based model; bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
**/*.{rs,py,go,js,ts,c,h}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Use language-appropriate naming conventions: Rust snake_case, C FFI exports prefixed nemo_relay_, Go PascalCase, Node.js camelCase, and Python snake_case.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
**/*.{rs,go,js,ts}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Add the SPDX license header to all Rust, Go, JavaScript, and TypeScript source files using the corresponding // comment form.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
crates/adaptive/**

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Keep crates/adaptive aligned with the canonical adaptive config schema, built-in section helpers, plugin lifecycle, and validation/report behavior.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

**/*: Format changed files with the language-native formatter before the final lint/test pass.
If dynamic plugin behavior changed, use maintain-dynamic-plugins and include the native SDK, worker protocol, Python SDK, docs, packaging, and Codecov surfaces in the validation plan.
If code changes alter APIs, bindings, commands, paths, packaging behavior, observability/adaptive semantics, or documented best practices, update any dependent maintainer or consumer skills in the same branch.
During iteration, prefer uv run pre-commit run --files <changed files...>.
Before review or handoff, run uv run pre-commit run --all-files.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
crates/{core,adaptive}/**/*

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If crates/core or crates/adaptive changed, run the full validation matrix across Rust, Python, Go, and Node.js.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
**/*.{rs,py,go,js,ts}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

If a language surface changed, always run that language's test target even when Rust core did not change.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
**/*.{rs,py,js,ts,tsx,go,java,kt,swift}

📄 CodeRabbit inference engine (.agents/skills/add-middleware/SKILL.md)

Add tests covering registration and duplicate names, deregistration and missing names, priority ordering, callback failure policy, scope-local inheritance and cleanup, event payload semantics, immutable mark and scope fields, and parity across affected bindings.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
  • crates/ffi/build.rs
crates/{core,adaptive}/**/*.rs

⚙️ CodeRabbit configuration file

crates/{core,adaptive}/**/*.rs: Review the Rust runtime for async correctness, scope isolation, middleware ordering, and event lifecycle regressions.
Pay close attention to task-local/thread-local scope propagation, callback lifetimes, stream finalization, and root_uuid isolation.
Public API changes should preserve existing behavior unless tests and docs show the intended migration path.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/src/lib.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}

⚙️ CodeRabbit configuration file

{crates/**/tests/**,python/tests/**,go/nemo_relay/**/*_test.go}: Tests should cover the behavior promised by the changed API surface, including error paths and cross-request isolation where relevant.
Prefer assertions on lifecycle events, scope stacks, middleware ordering, and binding parity over shallow smoke tests.

Files:

  • crates/adaptive/tests/support/mod.rs
  • crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs
{crates/**/src/**/*.rs,python/**/*.py}

📄 CodeRabbit inference engine (.agents/skills/maintain-dynamic-plugins/SKILL.md)

Do not add tests under src; Rust tests belong in crate tests/ trees, and Python SDK tests belong under python/tests.

Files:

  • crates/adaptive/src/lib.rs
crates/ffi/**

📄 CodeRabbit inference engine (.agents/skills/test-ffi-surface/SKILL.md)

Rebuild the FFI crate in release mode so the shared library and header stay in sync when making changes to crates/ffi

Files:

  • crates/ffi/build.rs
crates/ffi/**/*.rs

📄 CodeRabbit inference engine (.agents/skills/test-go-binding/SKILL.md)

If the change touched crates/ffi, also use test-ffi-surface for validation

Use C FFI export names prefixed with nemo_relay_ in the raw C FFI layer.

Files:

  • crates/ffi/build.rs
crates/{python,ffi,node}/**/*

⚙️ CodeRabbit configuration file

crates/{python,ffi,node}/**/*: Treat binding changes as public API changes. Check for parity with the other language bindings, FFI ownership/lifetime safety,
callback error propagation, stable type conversion, and consistent async/stream semantics.
Flag changes that update one binding without corresponding tests or documentation for the same surface elsewhere.

Files:

  • crates/ffi/build.rs
🔇 Additional comments (4)
crates/adaptive/src/lib.rs (1)

18-18: LGTM!

crates/adaptive/tests/support/mod.rs (1)

1-12: LGTM!

crates/adaptive/tests/unit/adaptive_hints_intercept_tests.rs (1)

7-7: LGTM!

Also applies to: 200-207, 270-277, 309-316

crates/ffi/build.rs (1)

36-68: LGTM!

Also applies to: 84-160

Comment thread crates/ffi/build.rs Outdated
Signed-off-by: Will Killian <wkillian@nvidia.com>
@willkill07

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

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

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

Labels

breaking PR introduces a breaking change Feature a new feature lang:go PR changes/introduces Go code lang:js PR changes/introduces Javascript/Typescript code lang:python PR changes/introduces Python code lang:rust PR changes/introduces Rust code size:XXL PR is very large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant