Skip to content

feat: add dynamic plugin lifecycle CLI commands#292

Open
afourniernv wants to merge 19 commits into
NVIDIA:mainfrom
afourniernv:afournier/relay-340-add-dynamic-plugin-lifecycle-cli-commands-and-operator
Open

feat: add dynamic plugin lifecycle CLI commands#292
afourniernv wants to merge 19 commits into
NVIDIA:mainfrom
afourniernv:afournier/relay-340-add-dynamic-plugin-lifecycle-cli-commands-and-operator

Conversation

@afourniernv

@afourniernv afourniernv commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

Overview

Add the dynamic plugin lifecycle CLI surface for RELAY-340.

  • 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

  • add dynamic plugin lifecycle commands for add, validate, list, inspect, enable, disable, and remove
  • persist lifecycle state through a local dynamic-plugins.json registry file so desired-state mutations are durable across CLI runs
  • hydrate lifecycle state from discovered plugins.toml dynamic plugin refs when no state file exists yet
  • keep remove tombstone-based and add rollback for multi-file mutation failure on the host config path
  • add plugins list --all, revival-aware add messaging, and richer inspect output for source and lifecycle state
  • add machine-readable JSON output for plugins validate, plugins list, and plugins inspect
  • add explicit plugin lifecycle exit-code mapping for success, not-found, refused, and generic failure cases
  • add focused CLI coverage for lifecycle flows, JSON envelopes, exit-code behavior, and a real binary plugins validate --json success path

Where should the reviewer start?

Start in crates/cli/src/plugins/lifecycle.rs, then review crates/cli/src/plugins/lifecycle/json.rs and crates/cli/tests/coverage/plugins_lifecycle_tests.rs.

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

  • Relates to RELAY-340

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dynamic plugin lifecycle commands (add, remove, enable, disable, list, inspect, validate) with per-scope state persistence.
    • Implemented versioned --json output for lifecycle commands (including structured success and error payloads).
  • Bug Fixes

    • Standardized lifecycle error classification and consistent exit codes for JSON and non-JSON runs.
  • Improvements

    • Enhanced target handling to accept both path-like targets and plugin IDs with consistent scope selection.
    • Added support for dynamic plugin references in plugins.toml.
    • Host-config values are redacted in outputs.

Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 23, 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

Walkthrough

Introduces full dynamic plugin lifecycle CLI commands (add, validate, list, inspect, enable, disable, remove) with scoped registry state persistence, TOML reference mutation, JSON envelope and terminal output rendering, structured error types, and end-to-end coverage tests.

Changes

Dynamic Plugin Lifecycle CLI

Layer / File(s) Summary
CLI contracts, error types, and registry primitives
crates/cli/src/config.rs, crates/cli/src/error.rs, crates/core/src/plugin/dynamic/registry.rs
Adds PluginsScopeArgs with clap mutual-exclusivity, lifecycle command argument structs (PluginsAddCommand, PluginsValidateCommand, PluginsListCommand, PluginsInspectCommand, PluginsEnableCommand, PluginsDisableCommand, PluginsRemoveCommand), PluginJsonContext and json_context(), resolve_plugins_config, PluginLifecycleFailureKind enum (Failed/NotFound/Refused), CliError::PluginLifecycle variant and plugin_lifecycle() accessor, and DynamicPluginRegistry::from_records/cloned_records APIs.
Scoped lifecycle state model and persistence
crates/cli/src/plugins/lifecycle/state.rs
Defines RegistryScope (User/Project/Global/Explicit) with label(), ScopedRegistry bundling metadata + filesystem paths + loaded registry, ScopedDynamicPluginRecord with scope metadata; implements scope layout resolution, JSON load/save with schema_version validation, record collection/sort, uniqueness-enforced ID lookup, and sibling state-path derivation.
TOML reference mutation and target parsing
crates/cli/src/plugins/config_io.rs, crates/cli/src/plugins/lifecycle/target.rs
Refactors target_scope to accept PluginsScopeArgs, adds append_dynamic_plugin_reference and remove_dynamic_plugin_reference for plugins.toml mutation with manifest ID matching and pruning, adds internal TOML root read/write helpers with path resolution, and adds PluginTarget::parse path-vs-id heuristic using filesystem classification (absolute/relative paths, .toml, separators) with unit tests.
Lifecycle JSON envelope and terminal renderers
crates/cli/src/plugins/lifecycle/responses.rs, crates/cli/src/plugins/lifecycle/render.rs
Adds versioned ResponseEnvelope<T> with typed payloads (ListEntryResponse, InspectResponse, ValidateResponse), print_response_json, success/failure envelope builders with stable error codes, and terminal renderers for list/inspect/validation-summary with state/kind labeling, last-error formatting, and redacted_host_config_json that masks sensitive config values.
Lifecycle command orchestration and helpers
crates/cli/src/plugins/lifecycle.rs
Implements add/validate/list/inspect/enable/disable/remove handlers managing registry mutations, manifest loading, validation status updates, and lifecycle transitions; includes load_and_hydrate_scopes (seeding registries from resolved config), record construction/helpers, TOML rollback on save failure, and render_plugin_error/render_generic_plugin_json_error exit-code mapping.
Main dispatch and module wiring
crates/cli/src/main.rs, crates/cli/src/plugins.rs
Adds pub(crate) mod lifecycle declaration, updates edit scope derivation to &command.scope, and rewrites run_plugins to accept server, dispatch all subcommands to lifecycle handlers (passing server), and apply JSON-aware plugin error fallback with conditional exit-code mapping.
Integration, coverage, and lifecycle behavior tests
crates/cli/tests/cli_tests.rs, crates/cli/tests/coverage/main_tests.rs, crates/cli/tests/coverage/plugins_lifecycle_tests.rs, crates/cli/tests/coverage/plugins_tests.rs
Adds JSON CLI integration tests for validate/list/inspect/list-all with tombstone records; expands main dispatch coverage with EnvScope and CurrentDirGuard hermetic environment helpers; updates scope resolution tests to PluginsScopeArgs; and adds lifecycle end-to-end coverage for add/validate/list/inspect/enable/disable/remove, tombstone revival, explicit config paths, bootstrap hydration, removal robustness, and JSON response envelope/error stability with host-config redaction assertions.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant run_plugins
  participant lifecycle as plugins::lifecycle::*
  participant load_and_hydrate_scopes
  participant ScopedRegistry
  participant config_io as plugins::config_io

  User->>run_plugins: plugins <subcommand> [--json]
  run_plugins->>run_plugins: json_context() → Option~PluginJsonContext~
  run_plugins->>lifecycle: add/validate/list/inspect/enable/disable/remove(command, server)
  lifecycle->>load_and_hydrate_scopes: resolve_plugins_config + load_scoped_registries
  load_and_hydrate_scopes-->>lifecycle: Vec~ScopedRegistry~
  lifecycle->>ScopedRegistry: mutate records / save()
  lifecycle->>config_io: append/remove_dynamic_plugin_reference(plugins_toml)
  alt save fails
    lifecycle->>config_io: restore_plugins_toml (rollback)
  end
  alt --json flag
    lifecycle->>lifecycle: print_response_json(envelope)
    lifecycle-->>run_plugins: Ok or CliError::PluginLifecycle
    run_plugins->>lifecycle: render_plugin_error → ExitCode
  else terminal output
    lifecycle->>User: rendered text output
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • NVIDIA/NeMo-Relay#279: Core dynamic plugin types and registry (DynamicPluginRecord, DynamicPluginManifest, registry state/lifecycle) introduced in this PR; the current PR implements CLI lifecycle operations and error mapping on top of that foundation.
  • NVIDIA/NeMo-Relay#290: Main PR's lifecycle commands write/read [[plugins.dynamic]] entries via append_dynamic_plugin_reference/remove_dynamic_plugin_reference, directly depending on the plugins.toml dynamic plugin discovery/parsing into ResolvedConfig introduced in this PR.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 35.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title follows Conventional Commits format with type 'feat', lowercase, imperative phrasing, and is 47 characters—well under the 72-character limit.
Description check ✅ Passed Description includes all required template sections: Overview with confirmation checkboxes, detailed change list, reviewer guidance, and related issue reference.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions github-actions Bot added size:XL PR is extra large Feature a new feature lang:rust PR changes/introduces Rust code labels Jun 23, 2026
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv afourniernv marked this pull request as ready for review June 23, 2026 20:47
@afourniernv afourniernv requested a review from a team as a code owner June 23, 2026 20:47
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Collaborator Author

/ok to test 086832f

@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: 9

🤖 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/cli/src/plugins/config_io.rs`:
- Around line 181-185: The issue is that DynamicPluginManifest::load_from_path
is being called without error handling, causing the entire removal operation to
abort if any manifest in the plugins.dynamic list is stale or broken, even if
that manifest is not the target plugin being removed. To fix this, modify the
code around the DynamicPluginManifest::load_from_path call to handle the error
gracefully instead of propagating it. When a manifest cannot be loaded, attempt
to match the target plugin using the registry's recorded source or manifest
reference instead of loading the full manifest. For non-target entries that
cannot be read, log a warning and continue scanning rather than failing the
whole remove operation. This allows the requested plugin to be removed even when
unrelated broken manifests exist in the configuration.
- Around line 165-170: The code currently returns Ok(false) when the [plugins]
section is not a table or when plugins.dynamic is not an array, which silently
masks configuration errors. Instead of treating these as missing sections,
return proper error results that indicate the configuration is malformed. In the
first else block (when root_table.get_mut("plugins") doesn't yield a Table), and
in the second else block (when plugins.get_mut("dynamic") doesn't yield an
Array), return an Err with a descriptive error message explaining that the
configuration format is invalid, so callers can properly handle and report the
configuration problem rather than proceeding as if the sections simply don't
exist.

In `@crates/cli/src/plugins/lifecycle.rs`:
- Around line 64-76: The rollback logic in the error handler after
scopes[scope_index].save() fails is using remove_dynamic_plugin_reference to
undo the append_dynamic_plugin_reference call, but this approach deletes all
references matching the plugin_id, including any pre-existing manual entries in
plugins.toml. Instead, capture a snapshot of the plugins_toml_path file before
calling append_dynamic_plugin_reference at line 74, then on save failure restore
the exact previous snapshot content rather than attempting selective removal.
This ensures only the newly appended reference is rolled back without affecting
any pre-existing configuration.

In `@crates/cli/src/plugins/lifecycle/render.rs`:
- Around line 123-132: The host_config_json formatting is currently exposing the
full plugin configuration including potential secrets by cloning and
pretty-printing the entire config object. Replace the map chain starting with
plugin.config.clone() through the serde_json::to_string_pretty call to instead
return a redacted placeholder string such as "<redacted>" rather than the actual
configuration content, ensuring sensitive plugin secrets are not exposed in
terminal output or support bundles while maintaining consistent JSON-style
formatting in the terminal inspect output.

In `@crates/cli/src/plugins/lifecycle/responses.rs`:
- Around line 208-210: The host_config field at line 209 is directly serializing
plugin.config without redacting sensitive information like credentials or tokens
that could be exposed in CI logs through the inspect --json output. Replace the
direct clone of plugin.config with a redaction function that masks or removes
sensitive values from the configuration, only exposing full values if an
explicit reveal flag is provided. Apply this same redaction logic consistently
to both the JSON serialization in the host_config mapping and to any terminal or
text-based inspect output to ensure credentials and tokens are never
inadvertently exposed.

In `@crates/cli/src/plugins/lifecycle/state.rs`:
- Around line 68-85: The save() method writes directly to self.state_path
without atomic operations, which can corrupt the registry if the process crashes
or disk fills during write. Instead of writing directly to self.state_path,
write the rendered content to a temporary file in the same directory (obtained
from self.state_path.parent()), ensure the file is flushed to disk, and then
atomically replace the original file using a rename operation. This ensures the
state file is only updated when the write is completely successful.

In `@crates/cli/src/plugins/lifecycle/target.rs`:
- Around line 22-29: The `looks_like_path` function in the target.rs file uses
`path.exists()` to determine if a string is a path, which creates ambiguity when
a plugin has a registered ID like "acme.plugin" and a file with that same name
exists in the current directory. Remove the `path.exists()` check and rely only
on explicit path syntax indicators such as the `.toml` file extension and path
separators (`/` or `\` or the platform-specific separator). This ensures that
registered plugin IDs are not incorrectly treated as paths merely because a
same-named file happens to exist in the filesystem.

In `@crates/cli/tests/coverage/main_tests.rs`:
- Around line 37-129: The dispatch tests for run_plugins are reading ambient
user/global plugin configuration from the environment, which causes flakiness
outside clean CI environments. Use the existing env lock strategy to set HOME
and XDG_CONFIG_HOME environment variables to temporary directories before
executing the test assertions. This will isolate the tests from ambient
configuration and ensure deterministic behavior for the exit-code assertions on
missing plugins in the dispatch test cases.

In `@crates/cli/tests/coverage/plugins_lifecycle_tests.rs`:
- Around line 68-73: The test suite is not fully isolating the environment for
plugin config resolution. While CurrentDirGuard handles changing the current
working directory, the HOME and XDG_CONFIG_HOME environment variables are not
being controlled, allowing ambient user/global plugin configs to leak into the
test and break assertions on plugin counts and lookups. Create an additional
environment guard similar to CurrentDirGuard that sets both HOME and
XDG_CONFIG_HOME to temporary directory paths for the duration of the test,
ensuring complete isolation of the plugin configuration environment. This guard
should be applied alongside or as part of the existing setup in this test suite
where resolve_plugins_config(None) is called.
🪄 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: 7d25b628-5095-4042-995a-b0ff0aa260ce

📥 Commits

Reviewing files that changed from the base of the PR and between 67606b5 and 90d89c7.

📒 Files selected for processing (15)
  • crates/cli/src/config.rs
  • crates/cli/src/error.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/core/src/plugin/dynamic/registry.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (15)
**/*.rs

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

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.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

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.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
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.rs
**/{Cargo.toml,**/*.rs}

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

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.rs
**/*.{h,hpp,c,cpp,rs}

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

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.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/plugin/dynamic/registry.rs
**/*.{rs,toml}

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

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.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

crates/core/**/*.rs: 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. Keep errors explicit and binding-appropriate at the wrapper layer.

Files:

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

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

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

Files:

  • crates/core/src/plugin/dynamic/registry.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/core/src/plugin/dynamic/registry.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/src/main.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/src/error.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/config.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/plugin/dynamic/registry.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

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

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_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/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_tests.rs
  • crates/cli/tests/cli_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*config*.{rs,ts,py,go,js,json,yaml,yml}

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

Ensure dynamic config shape still matches the documented canonical model

Files:

  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/config.rs
🔇 Additional comments (14)
crates/cli/src/config.rs (1)

200-244: LGTM!

Also applies to: 341-420, 734-740

crates/cli/src/error.rs (1)

8-18: LGTM!

Also applies to: 39-45, 60-73

crates/core/src/plugin/dynamic/registry.rs (1)

25-41: 📐 Maintainability & Code Quality

Please confirm the required core-change validation matrix was run.

This PR changes crates/core, so please provide command results for cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, validate-change, and the affected language test matrix (just test-python, just test-go, just test-node, just test-wasm) before merge.

As per coding guidelines, "Any Rust change must run just test-rust", "Run cargo fmt --all", "Run cargo clippy --workspace --all-targets -- -D warnings", and for crates/core changes "also use validate-change" plus "run the full matrix across Rust, Python, Go, Node.js, and WebAssembly".

Source: Coding guidelines

crates/cli/src/plugins/config_io.rs (2)

9-246: 📐 Maintainability & Code Quality

Confirm the required Rust validation passed.

This cohort changes Rust files; please confirm cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, just test-rust, and uv run pre-commit run --all-files were run before merge. As per coding guidelines, **/*.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”, and “Validate Rust code with uv run pre-commit run --all-files”.

Source: Coding guidelines


9-17: LGTM!

Also applies to: 29-54, 107-151, 209-246

crates/cli/src/plugins.rs (1)

25-25: LGTM!

Also applies to: 95-95

crates/cli/src/plugins/lifecycle/target.rs (1)

1-20: LGTM!

crates/cli/src/plugins/lifecycle/responses.rs (1)

1-207: LGTM!

Also applies to: 215-316

crates/cli/src/plugins/lifecycle/render.rs (1)

1-122: LGTM!

Also applies to: 137-273

crates/cli/tests/cli_tests.rs (2)

36-67: LGTM!

Also applies to: 133-301


133-145: 📐 Maintainability & Code Quality

Confirm these Rust validation commands were executed for this change.

Required checks for any Rust changes in this repository:

  • just test-rust
  • cargo fmt --all
  • cargo clippy --workspace --all-targets -- -D warnings
  • uv run pre-commit run --all-files
crates/cli/tests/coverage/plugins_tests.rs (1)

6-7: LGTM!

Also applies to: 94-122

crates/cli/src/plugins/lifecycle.rs (1)

1-508: 📐 Maintainability & Code Quality

Confirm the required Rust validation passed.

This cohort changes Rust CLI code; please record/pass just test-rust, cargo fmt --all, cargo clippy --workspace --all-targets -- -D warnings, and uv run pre-commit run --all-files before merge.

As per coding guidelines, "**/*.rs: Any Rust change must run just test-rust", "cargo fmt --all", "cargo clippy --workspace --all-targets -- -D warnings", and "Validate Rust code with uv run pre-commit run --all-files".

Source: Coding guidelines

crates/cli/src/main.rs (1)

87-87: LGTM!

Also applies to: 104-138

Comment thread crates/cli/src/plugins/config_io.rs Outdated
Comment thread crates/cli/src/plugins/config_io.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle.rs
Comment thread crates/cli/src/plugins/lifecycle/render.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/responses.rs
Comment thread crates/cli/src/plugins/lifecycle/state.rs
Comment thread crates/cli/src/plugins/lifecycle/target.rs Outdated
Comment thread crates/cli/tests/coverage/main_tests.rs
Comment thread crates/cli/tests/coverage/plugins_lifecycle_tests.rs
@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: 1

♻️ Duplicate comments (1)
crates/cli/src/plugins/lifecycle/target.rs (1)

38-42: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Remove path.exists() check to prevent ID-to-path misclassification.

Line 40 still uses path.exists(), creating ambiguity when a registered plugin ID matches a filesystem entry name. If a user runs plugins validate acme.plugin and both a registered plugin "acme.plugin" and a file "acme.plugin" exist, the parser chooses Path, skipping the registry ID branch and preventing validation status persistence. The comment at lines 35-37 states the goal is to keep canonical IDs on the ID branch; path.exists() violates this. Remove the exists check and rely only on explicit path syntax (is_absolute() and the separator/prefix/suffix checks).

🔧 Proposed fix
 fn should_treat_target_as_path(target: &str) -> bool {
     let path = Path::new(target);
-    if path.exists() || path.is_absolute() {
+    if path.is_absolute() {
         return true;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/cli/src/plugins/lifecycle/target.rs` around lines 38 - 42, In the
function should_treat_target_as_path, remove the path.exists() check from the
condition on line 40. Keep only the path.is_absolute() check to determine if the
target should be treated as a path. This prevents registered plugin IDs from
being misclassified as filesystem paths when a file with the same name happens
to exist, ensuring that canonical IDs are properly routed to the registry lookup
branch instead of being incorrectly treated as filesystem paths.
🤖 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/cli/src/plugins/lifecycle/target.rs`:
- Around line 116-124: The test
parse_treats_existing_filesystem_entries_as_paths is now invalid because it
relies on the removed path.exists() check that incorrectly treated "acme.worker"
as a path when it is actually a valid plugin ID format. Update the test by
passing explicit path syntax like "./acme.worker" to PluginTarget::parse()
instead of just the directory path string, so it correctly tests that the parser
treats explicitly-formatted paths as paths rather than IDs, or alternatively
remove the test entirely if it no longer serves a valid purpose after the parser
refactoring.

---

Duplicate comments:
In `@crates/cli/src/plugins/lifecycle/target.rs`:
- Around line 38-42: In the function should_treat_target_as_path, remove the
path.exists() check from the condition on line 40. Keep only the
path.is_absolute() check to determine if the target should be treated as a path.
This prevents registered plugin IDs from being misclassified as filesystem paths
when a file with the same name happens to exist, ensuring that canonical IDs are
properly routed to the registry lookup branch instead of being incorrectly
treated as filesystem paths.
🪄 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: 44dd9809-af44-4692-be9b-1cf25708201d

📥 Commits

Reviewing files that changed from the base of the PR and between 90d89c7 and 086832f.

📒 Files selected for processing (1)
  • crates/cli/src/plugins/lifecycle/target.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (26)
  • GitHub Check: Python / Test (macos-arm64)
  • GitHub Check: Rust / Test (linux-amd64)
  • GitHub Check: Node.js / Test (macos-arm64)
  • GitHub Check: Rust / Test (linux-arm64)
  • GitHub Check: Go / Test (windows-amd64)
  • GitHub Check: Go / Test (macos-arm64)
  • GitHub Check: Rust / Test (windows-arm64)
  • GitHub Check: Go / Test (linux-amd64)
  • GitHub Check: Rust / Test (windows-amd64)
  • GitHub Check: Rust / Test (macos-arm64)
  • GitHub Check: Go / Test (linux-arm64)
  • GitHub Check: Go / Test (windows-arm64)
  • GitHub Check: Node.js / Test (windows-amd64)
  • GitHub Check: Node.js / Test (windows-arm64)
  • GitHub Check: WebAssembly / Test (linux-amd64)
  • GitHub Check: Node.js / Test (linux-arm64)
  • GitHub Check: Node.js / Test (linux-amd64)
  • GitHub Check: Python / Test (windows-arm64)
  • GitHub Check: Python / Test (windows-amd64)
  • GitHub Check: Python / Test (linux-amd64)
  • GitHub Check: WebAssembly / Test (windows-amd64)
  • GitHub Check: Python / Test (linux-arm64)
  • GitHub Check: WebAssembly / Test (windows-arm64)
  • GitHub Check: WebAssembly / Test (linux-arm64)
  • GitHub Check: WebAssembly / Test (macos-arm64)
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (8)
**/*.rs

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

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.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

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.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
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**/{Cargo.toml,**/*.rs}

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

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**/*.{h,hpp,c,cpp,rs}

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

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**/*.{rs,toml}

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

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
🔇 Additional comments (3)
crates/cli/src/plugins/lifecycle/target.rs (3)

12-19: LGTM!


21-33: LGTM!


12-52: 📐 Maintainability & Code Quality

Run Rust validation for this change

Run cargo fmt --all -- --check, cargo clippy --workspace --all-targets -- -D warnings, and just test-rust for this Rust change.

Comment thread crates/cli/src/plugins/lifecycle/target.rs Outdated
@willkill07 willkill07 added this to the 0.5 milestone Jun 24, 2026
Comment thread crates/cli/src/plugins/lifecycle/render.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/state.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/state.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/render.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/render.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/render.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle.rs
Signed-off-by: Alex Fournier <afournier@nvidia.com>
…dd-dynamic-plugin-lifecycle-cli-commands-and-operator
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>

@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

♻️ Duplicate comments (1)
crates/cli/src/plugins/lifecycle.rs (1)

68-78: 🗄️ Data Integrity & Integration | 🟠 Major

Fix .ok() suppression that can delete existing plugins.toml on rollback.

std::fs::read(&plugins_toml_path).ok() collapses all IO errors into None, including non-NotFound errors like permission or transient failures. The restore_plugins_toml function treats None as "file did not previously exist" (line 451), so a save failure triggers deletion of the existing config rather than restoration — silent data loss.

Distinguish NotFound from real IO errors in both locations:

  • Line 68–78 in add()
  • Line 252 in remove()

Apply this pattern to both:

-    let original_plugins_toml = std::fs::read(&plugins_toml_path).ok();
+    let original_plugins_toml = match std::fs::read(&plugins_toml_path) {
+        Ok(bytes) => Some(bytes),
+        Err(error) if error.kind() == std::io::ErrorKind::NotFound => None,
+        Err(error) => return Err(error.into()),
+    };
🤖 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/cli/src/plugins/lifecycle.rs` around lines 68 - 78, The `.ok()` call
in the `add()` function at line 68 and similar logic in the `remove()` function
at line 252 suppress all IO errors when reading the plugins.toml file, treating
permission errors and transient failures the same as file-not-found. This causes
the rollback function `restore_plugins_toml` to delete the existing
configuration file instead of restoring it when a save failure occurs. Replace
the `.ok()` suppression pattern in both locations with explicit error handling
that distinguishes the NotFound error case (which should be suppressed as
`None`) from other IO errors (which should be propagated). Use pattern matching
on the error kind to only suppress NotFound errors while allowing real IO errors
to bubble up and prevent silent data loss during rollback.
🤖 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/cli/src/plugins/config_io.rs`:
- Around line 187-202: The `target_manifest_ref` is converted to a bare PathBuf
without being resolved relative to the TOML file path, while `manifest_ref`
entries are resolved via resolve_manifest_ref which produces absolute paths.
This asymmetry causes the comparison `manifest_ref == target_manifest_ref` to
fail for relative path references, breaking the fast-path match and forcing
fallback to the slower load_from_path approach. Resolve `target_manifest_ref`
symmetrically by applying the same resolve_manifest_ref function to it, passing
the path parameter to convert it to an absolute path before the comparison,
ensuring both sides of the equality check use the same path resolution logic.

---

Duplicate comments:
In `@crates/cli/src/plugins/lifecycle.rs`:
- Around line 68-78: The `.ok()` call in the `add()` function at line 68 and
similar logic in the `remove()` function at line 252 suppress all IO errors when
reading the plugins.toml file, treating permission errors and transient failures
the same as file-not-found. This causes the rollback function
`restore_plugins_toml` to delete the existing configuration file instead of
restoring it when a save failure occurs. Replace the `.ok()` suppression pattern
in both locations with explicit error handling that distinguishes the NotFound
error case (which should be suppressed as `None`) from other IO errors (which
should be propagated). Use pattern matching on the error kind to only suppress
NotFound errors while allowing real IO errors to bubble up and prevent silent
data loss during rollback.
🪄 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: cff1aabe-15c8-48aa-b201-6aa18c49cb0a

📥 Commits

Reviewing files that changed from the base of the PR and between 086832f and ec081cd.

📒 Files selected for processing (8)
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.rs

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

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.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

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.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
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**/{Cargo.toml,**/*.rs}

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

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**/*.{h,hpp,c,cpp,rs}

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

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**/*.{rs,toml}

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

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/cli/src/plugins/lifecycle/target.rs
  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/responses.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/state.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

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

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_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/cli/tests/coverage/main_tests.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
**/*config*.{rs,ts,py,go,js,json,yaml,yml}

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

Ensure dynamic config shape still matches the documented canonical model

Files:

  • crates/cli/src/plugins/config_io.rs
🔇 Additional comments (10)
crates/cli/tests/coverage/main_tests.rs (2)

79-82: LGTM!

Also applies to: 92-187


5-64: 📐 Maintainability & Code Quality

Include results from just test-rust, cargo fmt --all, and cargo clippy --workspace --all-targets -- -D warnings to confirm this change meets Rust validation requirements.

Per coding guidelines, all Rust changes require execution of these three commands. The sandbox environment cannot execute them, so manual verification is needed before approval.

crates/cli/tests/coverage/plugins_lifecycle_tests.rs (1)

32-82: LGTM!

Also applies to: 116-757

crates/cli/src/plugins/config_io.rs (1)

166-183: Malformed [plugins]/plugins.dynamic now surface CliError::Config instead of silent not-found, and a single unreadable manifest no longer aborts the whole scan (Line 199 .unwrap_or(false)). Both prior concerns addressed.

crates/cli/src/plugins/lifecycle/state.rs (1)

82-113: LGTM!

crates/cli/src/plugins/lifecycle/target.rs (1)

38-52: LGTM!

crates/cli/src/plugins/lifecycle/responses.rs (1)

207-212: LGTM!

crates/cli/src/plugins/lifecycle.rs (1)

245-266: Remove flow looks sound, aside from the shared .ok() snapshot risk. The load-then-hydrate fallback, registry remove, reference removal, and save-failure restore are ordered correctly and operate on a consistent scopes binding. See the Line 68 comment for the original_plugins_toml snapshot fix that also applies to Line 252.

crates/cli/src/plugins/lifecycle/render.rs (2)

123-132: Redaction correctly applied. Host config values are now redacted before pretty-printing, and empty config falls through to <none>. This resolves the prior secret-leak concern in terminal inspect output.


274-287: 📐 Maintainability & Code Quality

No refactor needed: redacted_host_config_json is already defined once in render.rs and imported by responses.rs.

The function has a single definition in crates/cli/src/plugins/lifecycle/render.rs:274 and is imported into responses.rs (line 21), not duplicated. The recommended pattern of a shared, single definition is already in place.

			> Likely an incorrect or invalid review comment.

Comment thread crates/cli/src/plugins/config_io.rs Outdated
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>

@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/cli/src/plugins/lifecycle.rs`:
- Around line 80-81: Remove the `let _ = (scope, manifest_ref,
plugins_toml_path, revived);` scaffolding line in the `add` function at line 80
since most bindings don't need explicit discards and `revived` is dead code
(computed but never used). Either incorporate `revived` into the println message
to implement the intended "revival-aware messaging for add operations", or drop
the binding entirely if that messaging is not needed. Also remove the similar
`let _ = ...` lines in the `remove` function at line 263 and the
`mutate_enabled_state` function at line 305, optionally binding their function
call results to `_` at the call sites instead if you want to suppress
unused-value warnings.
🪄 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: 9066a65e-ee37-4010-919a-e55e4158858b

📥 Commits

Reviewing files that changed from the base of the PR and between ec081cd and b73b13e.

📒 Files selected for processing (6)
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/src/plugins/lifecycle.rs
  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
📜 Review details
🧰 Additional context used
📓 Path-based instructions (11)
**/*.rs

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

Use snake_case naming convention for Rust identifiers (e.g., nemo_relay_tool_call)

**/*.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

**/*.rs: Run cargo fmt --all to format all Rust code
Run cargo clippy --workspace --all-targets -- -D warnings to enforce all clippy lints as errors

**/*.rs: Run cargo fmt --all when Rust files changed as part of WebAssembly work
Run cargo clippy --workspace --all-targets -- -D warnings when Rust files changed as part of WebAssembly work

**/*.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
Run Rust formatting with cargo fmt --all
Run Rust linting with cargo clippy --workspace --all-targets -- -D warnings

**/*.rs: Use cargo fmt for Rust code formatting
Run cargo clippy -- -D warnings to lint Rust code and treat all warnings as errors
Use Rust snake_case naming convention for Rust identifiers
Include SPDX license header in all Rust source files using double-slash comment syntax
Validate Rust code with uv run pre-commit run --all-files to enforce cargo fmt formatting check, cargo clippy lints, and cargo deny aud...

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/{Cargo.toml,**/*.rs}

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

Maintain consistency between Rust package names in Cargo.toml and their actual usage across the codebase

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/*.{h,hpp,c,cpp,rs}

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

Ensure FFI header and library naming follows consistent conventions across platform-specific builds

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/*.{rs,toml}

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

Update Rust crate names and module prefixes during coordinated rename operations

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
crates/**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

crates/**/*.rs: Keep async behavior on the existing tokio-based model. Bindings should preserve callback and future lifetimes rather than blocking or hiding async work unexpectedly.
Use Json = serde_json::Value in Rust-facing runtime APIs for JSON payload handling.

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**

⚙️ CodeRabbit configuration file

**:

AGENTS.md

This file provides guidance to agents, including Claude Code and OpenAI Codex, when working in this repository.

Project Overview

NeMo Relay is a multi-language agent runtime framework for execution scopes, lifecycle events, middleware, plugins, and observability around tool and LLM calls. The core runtime is Rust. Primary supported bindings are Rust, Python, and Node.js. Go, WebAssembly, and the raw C FFI are experimental and source-first.

The shared runtime model is:

  1. Scope stacks decide where work belongs and which scope-local behavior is visible.
  2. Middleware registries decide what guardrails and intercepts run around managed calls.
  3. Plugins install reusable runtime behavior from configuration.
  4. Events record runtime behavior in ATOF form.
  5. Subscribers and exporters consume events in-process or export them to ATIF, OpenTelemetry, OpenInference, or other backends.

Repository Structure

The repository layout separates the Rust runtime, language bindings, documentation,
integration patches, and agent-facing skills.

crates/
  core/       # Rust core runtime crate, published as nemo-relay
  adaptive/   # Adaptive runtime primitives and plugin components
  python/     # PyO3 native extension for the Python package
  ffi/        # Raw C ABI layer used by downstream bindings such as Go
  node/       # NAPI Node.js binding and JavaScript/TypeScript entry points
  wasm/       # wasm-bindgen WebAssembly binding and JS wrappers
python/
  nemo_relay/  # Python wrapper package: scopes, tools, LLM, middleware, typed helpers, plugins, adaptive helpers
  tests/      # Python tests
go/
  nemo_relay/  # Experimental Go CGo binding and tests
fern/         # Fern documentation site
scripts/      # Stable wrappers and helper scripts; build/test/docs entry points live in justfile
third_party/  # P...

Files:

  • crates/cli/src/plugins/lifecycle/render.rs
  • crates/cli/src/plugins/lifecycle/state.rs
  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
  • crates/cli/tests/coverage/plugins_lifecycle_tests.rs
  • crates/cli/src/plugins/lifecycle.rs
**/*config*.{rs,ts,py,go,js,json,yaml,yml}

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

Ensure dynamic config shape still matches the documented canonical model

Files:

  • crates/cli/src/config.rs
  • crates/cli/src/plugins/config_io.rs
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

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

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • crates/cli/tests/coverage/plugins_lifecycle_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/cli/tests/coverage/plugins_lifecycle_tests.rs
🔇 Additional comments (5)
crates/cli/src/config.rs (1)

833-833: 📐 Maintainability & Code Quality

Please confirm Rust validation gates were run for this change.

I can’t verify from this diff whether the required Rust checks were executed. Please confirm just test-rust, cargo fmt --all, and cargo clippy --workspace --all-targets -- -D warnings were run for this PR.

As per coding guidelines: “Any Rust change must run just test-rust”, “Any Rust change must run cargo fmt --all”, and “Any Rust change must run cargo clippy --workspace --all-targets -- -D warnings.”

Source: Coding guidelines

crates/cli/src/plugins/lifecycle/render.rs (1)

22-39: LGTM!

Also applies to: 65-66, 78-82, 153-157

crates/cli/src/plugins/config_io.rs (1)

187-188: Symmetric resolution fix is correct.

target_manifest_ref is now resolved via resolve_manifest_ref(path, ...), matching the per-entry resolution at Line 194, so relative stored refs compare correctly and the fast-path match works for stale/moved manifests. Resolves the earlier asymmetry concern.

crates/cli/src/plugins/lifecycle/state.rs (1)

12-14: LGTM!

Also applies to: 19-20, 147-147, 248-248, 270-270

crates/cli/tests/coverage/plugins_lifecycle_tests.rs (1)

422-422: LGTM!

Also applies to: 688-729

Comment thread crates/cli/src/plugins/lifecycle.rs Outdated
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Collaborator Author

/ok to test f35f8d1

Comment thread crates/cli/src/plugins/lifecycle/render.rs
Comment thread crates/cli/src/plugins/lifecycle/state.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/state.rs Outdated
Comment thread crates/cli/src/plugins/lifecycle/target.rs Outdated
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv afourniernv requested a review from a team as a code owner June 24, 2026 14:30
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown

@afourniernv

Copy link
Copy Markdown
Collaborator Author

/ok to test c993948

@github-actions

Copy link
Copy Markdown

License Diff

Compared against origin/main.

Lockfile license changes

Lockfile License Changes

Rust

Added

  • strum 0.27.2 (MIT)
  • strum_macros 0.27.2 (MIT)

Removed

  • None

Updated/Changed

  • None

Node

Added

  • None

Removed

  • None

Updated/Changed

  • None

Python

Added

  • None

Removed

  • None

Updated/Changed

  • None
Status output
[license-diff] selected languages: rust, node, python
[license-diff] generating current inventory
[license-diff] current: generating Rust inventory
[license-diff] current: Rust inventory complete (344 packages)
[license-diff] current: generating Node inventory
[license-diff] current: Node inventory complete (389 packages)
[license-diff] current: generating Python inventory
[license-diff] current: Python inventory complete (104 packages)
[license-diff] current inventory complete
[license-diff] checking out base ref origin/main into a temporary worktree
[license-diff] base: generating Rust inventory
[license-diff] base: Rust inventory complete (342 packages)
[license-diff] base: generating Node inventory
[license-diff] base: Node inventory complete (389 packages)
[license-diff] base: generating Python inventory
[license-diff] base: Python inventory complete (104 packages)
[license-diff] base inventory complete
[license-diff] removing temporary base worktree
[license-diff] comparing inventories
[license-diff] rendering Markdown output
[license-diff] done

…dd-dynamic-plugin-lifecycle-cli-commands-and-operator
Signed-off-by: Alex Fournier <afournier@nvidia.com>
@afourniernv

Copy link
Copy Markdown
Collaborator Author

/ok to test 518648e

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

Labels

Feature a new feature lang:rust PR changes/introduces Rust code size:XL PR is extra large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants