Skip to content

feat: Add Relay integration to codex-cli adapter - #30

Merged
dagardner-nv merged 27 commits into
NVIDIA:mainfrom
dagardner-nv:david-expand-codex
Jul 2, 2026
Merged

feat: Add Relay integration to codex-cli adapter#30
dagardner-nv merged 27 commits into
NVIDIA:mainfrom
dagardner-nv:david-expand-codex

Conversation

@dagardner-nv

@dagardner-nv dagardner-nv commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator
  • Spin up nemo-relay in gateway mode independently of Codex
  • Write out a temporary Codex config profile and remove it at the end of the run (unfortunately this needs to be in ~/.codex)
  • Consolidate profiles so that they stack on each other ex:
    fabric run examples/code-review-agent \
      --profile hermes_sdk \
      --profile relay_otel \
      --input "who are you?"
  • Move load_payload and runtime_session_id to common/utils.py
  • write_relay_plugins_toml now writes both a config.toml and a plugins.toml, the method was renamed to write_relay_configs

Summary by CodeRabbit

  • New Features
    • Added optional Codex routing via NeMo Relay gateway, including automatic gateway startup/shutdown and generated Codex/Relay profile handling.
    • Added new telemetry profiles for Relay (OpenTelemetry and OpenInference).
  • Bug Fixes
    • Improved payload input loading (invocation file or stdin fallback).
    • Made session identifier derivation consistent across Hermes components.
    • Enhanced Relay TOML config generation and wiring.
  • Tests
    • Expanded smoke, unit, and E2E coverage to support multiple profiles per run and validate gateway/profile lifecycle.

Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
…based upon it being named 'plugins.toml' and being in the same directory as the config.toml. There is no way to provide an explicit path to the plugin toml

Signed-off-by: David Gardner <dagardner@nvidia.com>
…trings not a path to a toml file, fix placement of the codex --config flag to not conflict with relay's flags

Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
…rmes specific. Update codex adapter to use it

Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
@dagardner-nv dagardner-nv added the enhancement New feature or request label Jul 1, 2026
@coderabbitai

coderabbitai Bot commented Jul 1, 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

Codex CLI now supports optional local Relay gateway startup and generated profiles. Shared helpers centralize payload loading, session-id derivation, and Relay config writing. Relay profiles and smoke/E2E tests were updated for multi-profile invocation and relay-specific artifact paths.

Changes

Codex CLI Relay integration

Layer / File(s) Summary
Profile and command wiring
adapters/codex-cli/.../adapter.py
Relay imports, constants, config override helpers, generated profile writing, and updated build_command argument construction are added to support Relay-linked Codex execution.
Gateway lifecycle and env
adapters/codex-cli/.../adapter.py, tests/test_codex_cli.py
build_env can inject NEMO_RELAY_GATEWAY_URL, and helpers plus tests cover free-port selection, /healthz polling, and nemo-relay process shutdown.
Relay orchestration and output
adapters/codex-cli/.../adapter.py, tests/test_codex_cli.py
run_codex validates session inputs, loads or resumes thread identity, starts and stops Relay when enabled, cleans up generated profiles in finally, and appends Relay metadata to the returned payload; main() reads payloads through shared loading.
Codex command and profile tests
tests/test_codex_cli.py
The Codex CLI tests validate generated profile contents, CODEX_HOME handling, Relay command wiring, and config_overrides path resolution.

Shared payload loading and relay config writing

Layer / File(s) Summary
Payload and relay config helpers
adapters/common/.../utils.py, tests/test_adapaters_common_utils.py
load_payload() reads JSON from FABRIC_INVOCATION or stdin, runtime_session_id() derives the session id from runtime context, and write_relay_configs() writes config.toml and plugins.toml under relay-config/.
Hermes adapters use shared session helper
adapters/common/.../hermes.py, adapters/hermes-cli/.../adapter.py, adapters/hermes-sdk/.../adapter.py, tests/test_adapaters_common_hermes.py
hermes.py removes its local session helper and uses write_relay_configs(), while the Hermes CLI and SDK adapters now derive session ids from common_utils.runtime_session_id().

Relay profiles and multi-profile tests

Layer / File(s) Summary
Standalone relay profiles
examples/code-review-agent/profiles/relay*.yaml
New relay.yaml, relay-otel.yaml, and relay-openinference.yaml profiles are added, replacing the removed combined Hermes relay profile files.
Multi-profile test updates
tests/smoke_cli.py, tests/smoke_relay_integration.py, tests/test_hermes_e2e.py, .agents/skills/python-tests/SKILL.md
Smoke and E2E tests now pass multiple profiles, assert combined profile lists, and check relay artifacts under relay-specific output roots; the test style guide notes the environment fixture behavior.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • NVIDIA/NeMo-Fabric#27: Extends the same Codex CLI adapter codepaths (build_command, build_env, run_codex, shared payload loading) that this PR further modifies.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title follows Conventional Commits and accurately summarizes the main codex-cli Relay integration change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

@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 `@adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py`:
- Around line 283-287: The generated Codex profile name is derived directly from
runtime_id, which can allow unsafe path-like values and collisions between
invocations. Update the profile creation and cleanup flow around
profile_path.write_text and the later unlink/delete logic to normalize
runtime_id into a safe slug and append a request/invocation-specific suffix
before using it as the profile filename. Make the same naming change wherever
the profile name/path is reused so each invocation gets a unique, sanitized
profile and cannot overwrite or delete another run’s profile.
- Around line 529-542: Relay startup failures currently bypass the JSON result
contract because `load_relay_plugin_config`, `write_relay_configs`, or
`start_relay_gateway` can raise before the Codex launch error handling runs.
Update `main()` in `adapter.py` to catch Relay startup exceptions around the
`use_relay` block and route them into the same structured failure path used for
Codex launch errors, returning a failed `CompletedProcess` with `launch_error`
set. Keep the public Fabric contract unchanged by preserving the existing
successful flow and only converting Relay startup exceptions into the structured
adapter output.
- Around line 476-477: The adapter is forwarding the entire parent environment
to nemo-relay via the env merge in the Codex CLI adapter, which can leak
secrets; update the environment construction in the relevant adapter method to
use the same sanitized/allowlisted env used for Codex and then explicitly add
only the Relay-specific variables needed. Locate the change in the adapter.py
logic around the cwd/env launch setup and adjust the env passed to the Relay
subprocess so it no longer includes os.environ wholesale.

In `@tests/test_adapaters_common_utils.py`:
- Around line 210-246: Add tests for the error handling in write_relay_configs,
not just the successful TOML write path. Add one case around
common_utils.write_relay_configs that unsets FABRIC_RELAY_CONFIG_PATH and
asserts it raises RuntimeError, and another that simulates tomli_w being
unavailable and asserts the function returns (None, None) while emitting the
stderr fallback message. Use the existing test_write_relay_configs pattern and
the write_relay_configs symbol to keep the new coverage adjacent to the current
happy-path test.

In `@tests/test_codex_cli.py`:
- Around line 392-399: The test test_wait_for_relay_gateway_times_out takes the
monkeypatch fixture but never uses it, so remove that unused parameter from the
function signature to keep Ruff ARG001 clean. Keep the rest of the test logic
unchanged, including the calls to load_codex_adapter, MagicMock, and
wait_for_relay_gateway.
- Around line 330-350: The run_codex test is missing coverage for the new
adapter output contract, since it ignores the returned value after calling
adapter.run_codex. Capture the output from run_codex and assert that it includes
the new relay_runtime and relay_artifacts fields alongside the existing
command/gateway assertions, using the run_codex method and its returned object
to keep the changed API surface covered.
- Around line 353-389: The current test only covers the successful Relay startup
path in start_relay_gateway; add an error-path test that forces
wait_for_relay_gateway to raise after subprocess.Popen returns a process. In
that test, use the same adapter fixture and mocks, then assert
start_relay_gateway propagates the failure and invokes stop_relay_gateway with
the spawned process so cleanup is verified.

In `@tests/test_hermes_e2e.py`:
- Around line 55-65: The async fixture run_hermes_with_relay is triggering Ruff
ASYNC240 because Path(...).resolve() is being called in the event loop; to keep
the test clean, avoid the pathlib I/O call there by computing the resolved paths
outside the async context or replacing the resolve usage with a non-blocking
path normalization approach. Update the artifact path setup for artifact_root
and relay_artifact_root in run_hermes_with_relay so the async fixture no longer
performs blocking filesystem work.
- Around line 62-64: The test setup in self.relay_artifact_root hardcodes the
"relay" profile name, which ties the artifact path to one specific relay
variant. Update the path construction to derive the relay artifact directory
from the selected relay-family profile in profile_names (or a shared relay
profile identifier) so subclasses like relay-otel or relay-openinference
continue to resolve the correct artifacts path without changing this test.
🪄 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: bf9ba382-4b8a-42f6-9fd4-2098ddd0e918

📥 Commits

Reviewing files that changed from the base of the PR and between a7bc73a and b56252f.

📒 Files selected for processing (20)
  • adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
  • adapters/common/src/nemo_fabric_adapters/common/hermes.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py
  • adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py
  • examples/code-review-agent/profiles/hermes-cli-relay-otel.yaml
  • examples/code-review-agent/profiles/hermes-cli-relay.yaml
  • examples/code-review-agent/profiles/hermes-relay-cli-openinference.yaml
  • examples/code-review-agent/profiles/hermes-relay-openinference.yaml
  • examples/code-review-agent/profiles/hermes-relay-otel.yaml
  • examples/code-review-agent/profiles/hermes-relay.yaml
  • examples/code-review-agent/profiles/relay-openinference.yaml
  • examples/code-review-agent/profiles/relay-otel.yaml
  • examples/code-review-agent/profiles/relay.yaml
  • tests/smoke_cli.py
  • tests/smoke_relay_integration.py
  • tests/test_adapaters_common_hermes.py
  • tests/test_adapaters_common_utils.py
  • tests/test_codex_cli.py
  • tests/test_hermes_e2e.py
💤 Files with no reviewable changes (7)
  • examples/code-review-agent/profiles/hermes-relay.yaml
  • examples/code-review-agent/profiles/hermes-relay-cli-openinference.yaml
  • examples/code-review-agent/profiles/hermes-cli-relay.yaml
  • examples/code-review-agent/profiles/hermes-relay-openinference.yaml
  • examples/code-review-agent/profiles/hermes-cli-relay-otel.yaml
  • examples/code-review-agent/profiles/hermes-relay-otel.yaml
  • tests/test_adapaters_common_hermes.py
📜 Review details
🧰 Additional context used
📓 Path-based instructions (3)
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public Fabric contracts.

Files:

  • examples/code-review-agent/profiles/relay-openinference.yaml
  • examples/code-review-agent/profiles/relay.yaml
  • examples/code-review-agent/profiles/relay-otel.yaml
  • adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py
  • adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py
  • adapters/common/src/nemo_fabric_adapters/common/utils.py
  • adapters/common/src/nemo_fabric_adapters/common/hermes.py
  • adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
tests/**/*.py

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

tests/**/*.py: Use pytest to run tests.
Do not add @pytest.mark.asyncio to test functions; async tests are detected and run automatically by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock (with spec when needed) instead of defining a new class.
Name mocked classes with a mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures across test files; if a fixture is needed in multiple test files, place it in conftest.py.
Define fixtures with @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not Preferpytest.mark.parametrizeover creating individual tests for different input types. If a fixture is needed for a test but does not return a value or the value is unused, use@pytest.mark.usefixtures`.

Files:

  • tests/smoke_cli.py
  • tests/smoke_relay_integration.py
  • tests/test_adapaters_common_utils.py
  • tests/test_codex_cli.py
  • tests/test_hermes_e2e.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/smoke_cli.py
  • tests/smoke_relay_integration.py
  • tests/test_adapaters_common_utils.py
  • tests/test_codex_cli.py
  • tests/test_hermes_e2e.py
🧠 Learnings (1)
📚 Learning: 2026-06-29T22:34:57.500Z
Learnt from: AjayThorve
Repo: NVIDIA/NeMo-Fabric PR: 27
File: tests/smoke_codex_cli.py:19-31
Timestamp: 2026-06-29T22:34:57.500Z
Learning: In NVIDIA/NeMo-Fabric, treat files in `tests/` named `smoke_*.py` as directly executed (gated) smoke scripts rather than pytest-collected tests. When reviewing or adding code to these files, keep them runnable as standalone scripts (e.g., via a script `main`/entrypoint) and avoid patterns that would make pytest collect them as normal test cases (e.g., defining pytest-style `test_*` functions/classes meant for collection). For Codex CLI adapter behavioral/pytest coverage, use the separate `tests/test_codex_cli.py` rather than adding pytest tests into `tests/smoke_*.py`.

Applied to files:

  • tests/smoke_cli.py
  • tests/smoke_relay_integration.py
🧬 Code graph analysis (4)
adapters/common/src/nemo_fabric_adapters/common/utils.py (2)
adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py (1)
  • runtime_context (105-105)
tests/test_adapaters_common_utils.py (1)
  • runtime_context (53-53)
adapters/common/src/nemo_fabric_adapters/common/hermes.py (1)
adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py (1)
  • configure_hermes_relay (67-67)
tests/test_codex_cli.py (1)
adapters/common/src/nemo_fabric_adapters/common/utils.py (1)
  • config_root (34-35)
adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py (1)
adapters/common/src/nemo_fabric_adapters/common/utils.py (3)
  • load_relay_plugin_config (103-127)
  • settings_payload (72-74)
  • collect_relay_artifacts (317-335)
🪛 ast-grep (0.44.0)
tests/test_adapaters_common_utils.py

[info] 65-65: use jsonify instead of json.dumps for JSON output
Context: json.dumps({"request": {"input": "from file"}})
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py

[warning] 423-423: Request-controlled URL passed to urlopen; validate against an allowlist to prevent SSRF.
Context: urllib.request.urlopen(health_url, timeout=1)
Note: [CWE-918] Server-Side Request Forgery (SSRF).

(urlopen-unsanitized-data)


[error] 467-480: Command coming from incoming request
Context: subprocess.Popen(
[
relay_command,
"--config",
str(relay_config_path),
"--bind",
f"127.0.0.1:{port}",
],
cwd=cwd,
env={**os.environ, **build_env(payload)},
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)


[error] 555-567: Use of unsanitized data to create processes
Context: subprocess.run(
command,
cwd=cwd,
env=build_env(
payload,
relay_gateway_url=relay_gateway_url,
),
input=request_to_prompt(payload),
text=True,
capture_output=True,
check=False,
timeout=timeout,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(os-system-unsanitized-data)


[error] 555-567: Command coming from incoming request
Context: subprocess.run(
command,
cwd=cwd,
env=build_env(
payload,
relay_gateway_url=relay_gateway_url,
),
input=request_to_prompt(payload),
text=True,
capture_output=True,
check=False,
timeout=timeout,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)


[info] 647-647: use jsonify instead of json.dumps for JSON output
Context: json.dumps(output, sort_keys=True)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🪛 Ruff (0.15.20)
adapters/common/src/nemo_fabric_adapters/common/utils.py

[warning] 347-347: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 363-363: Consider moving this statement to an else block

(TRY300)

tests/test_codex_cli.py

[warning] 180-180: Yoda condition detected

(SIM300)


[warning] 183-183: Yoda condition detected

Rewrite as command[-3:-1] == ["--model", "gpt-5.4"]

(SIM300)


[warning] 392-392: Unused function argument: monkeypatch

(ARG001)

tests/test_hermes_e2e.py

[warning] 61-61: Async functions should not use pathlib.Path methods, use trio.Path or anyio.path

(ASYNC240)

adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py

[warning] 172-174: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 215-215: Prefer TypeError exception for invalid type

(TRY004)


[warning] 215-215: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 227-227: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 232-234: Prefer TypeError exception for invalid type

(TRY004)


[warning] 232-234: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 298-300: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 397-397: Prefer TypeError exception for invalid type

(TRY004)


[warning] 397-397: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 420-422: Avoid specifying long messages outside the exception class

(TRY003)


[error] 424-424: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 430-430: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 437-440: Use contextlib.suppress(ProcessLookupError) instead of try-except-pass

Replace try-except-pass with with contextlib.suppress(ProcessLookupError): ...

(SIM105)


[error] 468-468: subprocess call: check for execution of untrusted input

(S603)


[warning] 514-514: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 537-537: Avoid specifying long messages outside the exception class

(TRY003)


[error] 556-556: subprocess call: check for execution of untrusted input

(S603)

🔇 Additional comments (14)
examples/code-review-agent/profiles/relay-openinference.yaml (1)

1-34: LGTM!

examples/code-review-agent/profiles/relay-otel.yaml (1)

1-30: LGTM!

examples/code-review-agent/profiles/relay.yaml (1)

1-30: LGTM!

tests/smoke_cli.py (1)

63-87: LGTM!

tests/smoke_relay_integration.py (1)

37-47: LGTM!

tests/test_hermes_e2e.py (2)

24-24: LGTM!

Also applies to: 124-124, 145-147, 192-194, 211-219, 233-241


229-230: 🎯 Functional Correctness

plugins.toml path matches write_relay_configs. The helper writes Relay plugin config to config_path.parent / "relay-config" / "plugins.toml", so the new assertion is correct.

			> Likely an incorrect or invalid review comment.
adapters/common/src/nemo_fabric_adapters/common/utils.py (1)

41-66: LGTM!

Also applies to: 337-366

tests/test_adapaters_common_utils.py (1)

7-16: LGTM!

Also applies to: 59-99

adapters/common/src/nemo_fabric_adapters/common/hermes.py (1)

152-152: LGTM!

adapters/hermes-cli/src/nemo_fabric_adapters/hermes_cli/adapter.py (1)

31-37: LGTM!

Also applies to: 64-64

adapters/hermes-sdk/src/nemo_fabric_adapters/hermes_sdk/adapter.py (1)

187-187: LGTM!

adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py (1)

13-19: LGTM!

Also applies to: 36-57, 151-207, 210-237, 239-282, 387-430, 433-453, 605-633, 647-647

tests/test_codex_cli.py (1)

40-43: LGTM!

Also applies to: 162-258, 259-283, 402-414

Comment thread adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
Comment thread adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py Outdated
Comment thread adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
Comment thread tests/test_adapaters_common_utils.py
Comment thread tests/test_codex_cli.py Outdated
Comment thread tests/test_codex_cli.py
Comment thread tests/test_codex_cli.py Outdated
Comment thread tests/test_hermes_e2e.py
Comment thread tests/test_hermes_e2e.py
Signed-off-by: David Gardner <dagardner@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 `@adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py`:
- Around line 435-439: The Relay cleanup in the shutdown path only kills the
direct process, so descendants can survive and keep resources open. Update the
Relay termination logic in the adapter’s process cleanup flow to restore
process-group signaling when `nemo-relay` is started in its own session, and
keep a Windows-safe fallback for terminating the tree. Make the fix in the Relay
shutdown/cleanup code that currently calls `process.terminate()`,
`process.wait()`, and `process.kill()`, ensuring behavior stays compatible with
existing Fabric/public contract handling.
🪄 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: 1f4c816b-e6a3-42a7-9f27-4e4b10a966a7

📥 Commits

Reviewing files that changed from the base of the PR and between b56252f and a25bcc1.

📒 Files selected for processing (2)
  • adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
  • tests/test_codex_cli.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Test
⚠️ CI failures not shown inline (2)

GitHub Actions: Rust / Test: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 5.59s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok

GitHub Actions: Rust / 0_Test.txt: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 5.59s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok
🧰 Additional context used
📓 Path-based instructions (3)
{adapters/**,examples/**}

⚙️ CodeRabbit configuration file

{adapters/**,examples/**}: Review adapter and example changes for command correctness, config/schema consistency, artifact handling, and compatibility with the public Fabric contracts.

Files:

  • adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
tests/**/*.py

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

tests/**/*.py: Use pytest to run tests.
Do not add @pytest.mark.asyncio to test functions; async tests are detected and run automatically by the async runner.
Do not add a -> None return type annotation to test functions.
When mocking a class, use unittest.mock.MagicMock or unittest.mock.AsyncMock (with spec when needed) instead of defining a new class.
Name mocked classes with a mock prefix, not fake.
Prefer pytest fixtures over helper methods.
Do not repeat fixtures across test files; if a fixture is needed in multiple test files, place it in conftest.py.
Define fixtures with @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) and def <fixture_name>_fixture() -> <return_type>:; only specify scope when it is not Preferpytest.mark.parametrizeover creating individual tests for different input types. If a fixture is needed for a test but does not return a value or the value is unused, use@pytest.mark.usefixtures`.

Files:

  • tests/test_codex_cli.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/test_codex_cli.py
🔇 Additional comments (1)
tests/test_codex_cli.py (1)

8-8: LGTM!

Also applies to: 402-423

Comment thread adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@nvidia.com>
@dagardner-nv
dagardner-nv marked this pull request as ready for review July 1, 2026 21:12

@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 (1)
tests/test_codex_cli.py (1)

260-286: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Misplaced relay-output assertions break this test's own premise.

test_relay_requires_exact_true sets FABRIC_RELAY_ENABLED="TRUE" specifically to prove relay is not activated (confirmed by mock_load_config.assert_not_called() at line 286). But run_codex only adds relay_runtime/relay_artifacts to the output when relay_plugin_config is not None, which only happens inside the use_relay branch. Since that branch is never entered here, assert "relay_runtime" in result and assert "relay_artifacts" in result (lines 280-281) will fail against the real adapter — these assertions contradict the test's intent and appear to have been added to the wrong test case.

🐛 Proposed fix: remove the relay-output assertions from this negative test
     result = adapter.run_codex(codex_payload)

     command = mock_run.call_args.args[0]
-    assert "relay_runtime" in result
-    assert "relay_artifacts" in result
     assert command[0] == "codex"
🤖 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 `@tests/test_codex_cli.py` around lines 260 - 286, The relay-output checks in
test_relay_requires_exact_true contradict the test’s purpose because
FABRIC_RELAY_ENABLED="TRUE" is meant to keep relay disabled and mock_load_config
uncalled. Remove the assertions that expect relay_runtime and relay_artifacts
from this negative case, and keep the test focused on verifying that run_codex
does not enter the relay path, does not add --config, and uses the
fabric-runtime-1 profile.
♻️ Duplicate comments (1)
tests/test_codex_cli.py (1)

289-350: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Relay-enabled test still discards run_codex return value — past review comment not addressed.

test_run_codex_configures_relay is the test that actually enables relay end-to-end, yet line 330 calls adapter.run_codex(codex_payload) without capturing the result, so relay_runtime/relay_artifacts in the output remain unasserted here — the coverage gap the prior review flagged is still open (the assertions appear to have been misapplied to a different, non-relay test instead; see the comment on lines 260-286).

✅ Proposed fix: capture and assert the relay output contract
-    adapter.run_codex(codex_payload)
+    result = adapter.run_codex(codex_payload)
 
     command = mock_run.call_args.args[0]
     assert command[0] == "codex"
     assert "nemo-relay" not in command
     assert command[command.index("--profile") + 1] == profile_name
     assert mock_run.call_args.kwargs["env"]["NEMO_RELAY_GATEWAY_URL"] == gateway_url
     assert "CODEX_HOME" not in mock_run.call_args.kwargs["env"]
     assert not profile_path.exists()
+    assert result["relay_runtime"]["enabled"] is True
+    assert "relay_artifacts" in result
🤖 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 `@tests/test_codex_cli.py` around lines 289 - 350, In
test_run_codex_configures_relay, the relay-enabled path still ignores the return
value from run_codex, so the relay output contract is not verified. Capture the
result of adapter.run_codex(codex_payload) and assert the relay-specific fields
on it, especially relay_runtime and relay_artifacts, using the existing test
setup around run_codex, start_relay_gateway, and stop_relay_gateway. Keep the
existing command/env assertions, but add checks that the returned structure
reflects relay being enabled end-to-end.
🤖 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 `@tests/test_codex_cli.py`:
- Around line 474-476: The test setup in the CODEX CLI test uses a hardcoded
temporary directory path, which should be replaced with a path derived from
tmp_path for portability and isolation. Update the environment assignment near
the OPENAI_API_KEY and FABRIC_UNRELATED_SECRET setup to build CODEX_HOME from
tmp_path instead of "/tmp/codex-home", keeping the unrelated secret fixture
unchanged since it is intentional test data. Use the existing test context in
test_codex_cli to ensure the temporary home directory is unique per test run.

---

Outside diff comments:
In `@tests/test_codex_cli.py`:
- Around line 260-286: The relay-output checks in test_relay_requires_exact_true
contradict the test’s purpose because FABRIC_RELAY_ENABLED="TRUE" is meant to
keep relay disabled and mock_load_config uncalled. Remove the assertions that
expect relay_runtime and relay_artifacts from this negative case, and keep the
test focused on verifying that run_codex does not enter the relay path, does not
add --config, and uses the fabric-runtime-1 profile.

---

Duplicate comments:
In `@tests/test_codex_cli.py`:
- Around line 289-350: In test_run_codex_configures_relay, the relay-enabled
path still ignores the return value from run_codex, so the relay output contract
is not verified. Capture the result of adapter.run_codex(codex_payload) and
assert the relay-specific fields on it, especially relay_runtime and
relay_artifacts, using the existing test setup around run_codex,
start_relay_gateway, and stop_relay_gateway. Keep the existing command/env
assertions, but add checks that the returned structure reflects relay being
enabled end-to-end.
🪄 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: 64334793-c149-4617-8213-d72c32739cf4

📥 Commits

Reviewing files that changed from the base of the PR and between 3067812 and bb2c224.

📒 Files selected for processing (2)
  • .agents/skills/python-tests/SKILL.md
  • tests/test_codex_cli.py
📜 Review details
⚠️ CI failures not shown inline (4)

GitHub Actions: Rust / Test: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 6.17s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok

GitHub Actions: Rust / 0_Test.txt: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 6.17s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok

GitHub Actions: Python / 0_Test.txt: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1muv sync --group test --no-group dev --extra hermes --extra relay�[0m
 �[36;1muv run pytest�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
   UV_PYTHON_INSTALL_DIR: /home/runner/work/_temp/uv-python-dir
   UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
 ##[endgroup]
 Resolved 138 packages in 672ms
 Downloading pydantic-core (2.0MiB)
 Downloading cryptography (4.3MiB)
 Downloading openai (1.1MiB)
 Downloading pillow (6.8MiB)
 Downloading pygments (1.2MiB)
 Downloading uvloop (4.2MiB)
 Downloading nemo-relay (6.0MiB)
 Downloading hermes-agent (8.8MiB)
  Downloaded pydantic-core
  Downloaded uvloop
  Downloaded pillow
  Downloaded nemo-relay
  Downloaded pygments
  Downloaded cryptography
  Downloaded openai
  Downloaded hermes-agent
 Prepared 67 packages in 893ms
 Installed 67 packages in 82ms
  + annotated-doc==0.0.4
  + annotated-types==0.7.0
  + anyio==4.14.1
  + certifi==2026.5.20
  + cffi==2.0.0
  + charset-normalizer==3.4.7
  + click==8.4.2
  + coverage==7.14.3
  + croniter==6.0.0
  + cryptography==46.0.7
  + distro==1.9.0
  + fastapi==0.139.0
  + fire==0.7.1
  + h11==0.16.0
  + hermes-agent==0.18.0
  + httpcore==1.0.9
  + httptools==0.8.0
  + httpx==0.28.1
  + idna==3.18
  + iniconfig==2.3.0
  + jinja2==3.1.6
  + jiter==0.16.0
  + markdown==3.10.2
  + markdown-it-py==4.2.0
  + markupsafe==3.0.3
  + mdurl==0.1.2
  + nemo-relay==0.4.0
  + openai==2.24.0
  + packaging==26.0
  + pathspec==1.1.1
  + pillow==12.2.0
  + pluggy==1.6.0
  + prompt-toolkit==3.0.52
  + psutil==7.2.2
  + ptyprocess==0.7.0
  + pycparser==3.0
  + pydantic==2.13.4
  + pydantic-core==2.46.4
  + pygments==2.20.0
  + pyjwt==2.13.0
  + pytest==9.1.1
  + pyt...

GitHub Actions: Python / Test: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run set -euo pipefail
 �[36;1mset -euo pipefail�[0m
 �[36;1muv sync --group test --no-group dev --extra hermes --extra relay�[0m
 �[36;1muv run pytest�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
   UV_PYTHON_INSTALL_DIR: /home/runner/work/_temp/uv-python-dir
   UV_CACHE_DIR: /home/runner/work/_temp/setup-uv-cache
 ##[endgroup]
 Resolved 138 packages in 672ms
 Downloading pydantic-core (2.0MiB)
 Downloading cryptography (4.3MiB)
 Downloading openai (1.1MiB)
 Downloading pillow (6.8MiB)
 Downloading pygments (1.2MiB)
 Downloading uvloop (4.2MiB)
 Downloading nemo-relay (6.0MiB)
 Downloading hermes-agent (8.8MiB)
  Downloaded pydantic-core
  Downloaded uvloop
  Downloaded pillow
  Downloaded nemo-relay
  Downloaded pygments
  Downloaded cryptography
  Downloaded openai
  Downloaded hermes-agent
 Prepared 67 packages in 893ms
 Installed 67 packages in 82ms
  + annotated-doc==0.0.4
  + annotated-types==0.7.0
  + anyio==4.14.1
  + certifi==2026.5.20
  + cffi==2.0.0
  + charset-normalizer==3.4.7
  + click==8.4.2
  + coverage==7.14.3
  + croniter==6.0.0
  + cryptography==46.0.7
  + distro==1.9.0
  + fastapi==0.139.0
  + fire==0.7.1
  + h11==0.16.0
  + hermes-agent==0.18.0
  + httpcore==1.0.9
  + httptools==0.8.0
  + httpx==0.28.1
  + idna==3.18
  + iniconfig==2.3.0
  + jinja2==3.1.6
  + jiter==0.16.0
  + markdown==3.10.2
  + markdown-it-py==4.2.0
  + markupsafe==3.0.3
  + mdurl==0.1.2
  + nemo-relay==0.4.0
  + openai==2.24.0
  + packaging==26.0
  + pathspec==1.1.1
  + pillow==12.2.0
  + pluggy==1.6.0
  + prompt-toolkit==3.0.52
  + psutil==7.2.2
  + ptyprocess==0.7.0
  + pycparser==3.0
  + pydantic==2.13.4
  + pydantic-core==2.46.4
  + pygments==2.20.0
  + pyjwt==2.13.0
  + pytest==9.1.1
  + pyt...
🧰 Additional context used
📓 Path-based instructions (3)
.agents/skills/python-tests/**

⚙️ CodeRabbit configuration file

.agents/skills/python-tests/**: ---
name: python-tests
description: Python tests for Fabric; use this when writing tests
author: NVIDIA Corporation and Affiliates
license: Apache-2.0

Python Test Style

  • 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; the decorator is unnecessary clutter.
  • Do not add a -> None return type annotation to test functions. This is not a common convention in pytest and adds unnecessary verbosity.
  • 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.
  • The name of the mocked class should be prefixed with mock, 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 follow this pattern:
    `@pytest.fixture`(name="<fixture_name>"[, scope="<scope>"])
    def <fixture_name>_fixture() -> <return_type>:
        ...
    Only specify the scope argument when the value is something other than "function".
  • Prefer pytest.mark.parametrize over creating individual tests for
    different input types.
  • If a fixture is needed for a test, but either does not return a value or the value is not used in the test, use the @pytest.mark.usefixtures decorator.

Common Commands

# Focused test loop
uv run pytest -k "<pattern>"

# Run all tests
uv run pytest

References

  • pyproject.toml
  • tests/conftest.py

Files:

  • .agents/skills/python-tests/SKILL.md
tests/**/*.py

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

tests/**/*.py: Use Pytest to run Python tests.
Do not add @pytest.mark.asyncio to tests; 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, use unittest.mock.MagicMock or unittest.mock.AsyncMock (with spec when needed) instead of defining a new class.
Name mocked classes with a 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 tests/conftest.py.
When creating a fixture, use @pytest.fixture(name="<fixture_name>"[, scope="<scope>"]) with a matching <fixture_name>_fixture() function, and only specify scope when it is not function.
Prefer pytest.mark.parametrize over creating individual tests for different input types.
If a fixture is needed but does not return a value or its value is unused, use @pytest.mark.usefixtures.
When modifying environment variables in tests, use os.environ; tests/conftest.py provides an autouse restore_environ_fixture that restores the environment after each test, so monkeypatch.setenv is unnecessary.

Files:

  • tests/test_codex_cli.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/test_codex_cli.py
🧬 Code graph analysis (1)
tests/test_codex_cli.py (1)
adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py (2)
  • run_codex (497-619)
  • codex_home (290-292)
🪛 ast-grep (0.44.0)
tests/test_codex_cli.py

[info] 474-474: Do not hardcode temporary file or directory names
Context: "/tmp/codex-home"
Note: [CWE-377] Insecure Temporary File.

(hardcoded-tmp-file)

🪛 GitHub Actions: Python / 0_Test.txt
tests/test_codex_cli.py

[error] 280-280: Pytest failure in test_relay_requires_exact_true: AssertionError: expected 'relay_runtime' in result, but it was not found. FAILED tests/test_codex_cli.py::test_relay_requires_exact_true

🪛 GitHub Actions: Python / Test
tests/test_codex_cli.py

[error] 280-280: Test failed: test_relay_requires_exact_true. AssertionError: expected 'relay_runtime' to be present in result, but it was missing.

🪛 Ruff (0.15.20)
tests/test_codex_cli.py

[error] 475-475: Probable insecure usage of temporary file or directory: "/tmp/codex-home"

(S108)


[error] 476-476: Possible hardcoded password assigned to: "FABRIC_UNRELATED_SECRET"

(S105)

🔇 Additional comments (5)
.agents/skills/python-tests/SKILL.md (1)

28-28: LGTM!

tests/test_codex_cli.py (4)

353-425: 🎯 Functional Correctness

Confirm the Relay startup cleanup error-path test was added.

The prior review requested a test where wait_for_relay_gateway raises after subprocess.Popen returns a process, asserting stop_relay_gateway is invoked with that process for cleanup. The line-range summary for this block only mentions healthz polling, timeout raising, and stop-then-kill behavior — no mention of the start_relay_gateway failure path calling cleanup. As per path instructions, {tests/**,python/tests/**} tests should cover error paths and lifecycle cleanup for the changed API surface.

#!/bin/bash
# Check whether start_relay_gateway's failure path (wait_for_relay_gateway raising) is tested with stop_relay_gateway assertions
rg -n -B3 -A20 'def test_start_relay_gateway' tests/test_codex_cli.py

Source: Path instructions


453-501: LGTM! Direct os.environ manipulation here correctly relies on the autouse restore_environ_fixture for cleanup, per coding guidelines.


667-671: LGTM!


6-9: LGTM! os.environ usage for CODEX_HOME and FABRIC_RELAY_ENABLED correctly leverages the autouse env-restore fixture instead of monkeypatch.setenv.

Also applies to: 41-44, 204-215

Comment thread tests/test_codex_cli.py
Signed-off-by: David Gardner <dagardner@nvidia.com>
Signed-off-by: David Gardner <dagardner@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 `@tests/test_codex_cli.py`:
- Around line 301-305: The test in test_codex_cli should verify the actual relay
contract, not just that relay_runtime and relay_artifacts exist. Strengthen the
assertions around adapter.run_codex(codex_payload) by checking the expected
relay_runtime fields (enabled, emitter, mode, config_path) and the
relay_artifacts payload content, so malformed metadata from run_codex would
fail. Use the existing result object and command capture in this test to assert
the specific relay shape and values.
🪄 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: 4da3fe9b-dc63-4d43-815b-bdf6ec1a5ef4

📥 Commits

Reviewing files that changed from the base of the PR and between bb2c224 and cddffff.

📒 Files selected for processing (1)
  • tests/test_codex_cli.py
📜 Review details
⏰ Context from checks skipped due to timeout. (1)
  • GitHub Check: Test
⚠️ CI failures not shown inline (2)

GitHub Actions: Rust / 0_Test.txt: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 5.98s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok

GitHub Actions: Rust / Test: feat: Add Relay integration to codex-cli adapter

Conclusion: failure

View job details

##[group]Run cargo test --workspace --locked
 �[36;1mcargo test --workspace --locked�[0m
 shell: /usr/bin/bash --noprofile --norc -e -o pipefail {0}
 env:
   CARGO_INCREMENTAL: 0
   CARGO_PROFILE_DEV_DEBUG: 0
   CARGO_TERM_COLOR: always
   RUST_BACKTRACE: short
   RUSTFLAGS: -D warnings
   CARGO_UNSTABLE_SPARSE_REGISTRY: true
   CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
   CACHE_ON_FAILURE: false
 ##[endgroup]
 �[1m�[92m   Compiling�[0m fabric-core v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-core)
 �[1m�[92m   Compiling�[0m fabric-cli v0.1.0 (/home/runner/work/NeMo-Fabric/NeMo-Fabric/crates/fabric-cli)
 �[1m�[92m    Finished�[0m `test` profile [unoptimized] target(s) in 5.98s
 �[1m�[92m     Running�[0m unittests src/main.rs (target/debug/deps/fabric-c3572a4d481471cf)
 running 0 tests
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
 �[1m�[92m     Running�[0m unittests src/lib.rs (target/debug/deps/fabric_core-9b081566c1d472c1)
 running 41 tests
 test config::tests::errors_for_unknown_manifest_profile ... ok
🧰 Additional context used
📓 Path-based instructions (2)
**/tests/**/*.py

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

**/tests/**/*.py: Use Pytest for Python tests; do not add @pytest.mark.asyncio to tests because async tests are automatically detected 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 argument when necessary.
Name mocked classes with a 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 function as <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.
If a fixture is needed for a test but does not return a value or its value is unused, use @pytest.mark.usefixtures.
If a test needs to modify environment variables, use os.environ; tests/conftest.py provides an autouse restore_environ_fixture that restores the environment after each test, so monkeypatch.setenv is unnecessary.

Files:

  • tests/test_codex_cli.py
{tests/**,python/tests/**}

⚙️ CodeRabbit configuration file

{tests/**,python/tests/**}: Tests should cover the behavior promised by the changed API surface, including error paths, lifecycle cleanup, and SDK/native parity where relevant.

Files:

  • tests/test_codex_cli.py
🧬 Code graph analysis (1)
tests/test_codex_cli.py (1)
adapters/codex-cli/src/nemo_fabric_adapters/codex_cli/adapter.py (1)
  • run_codex (497-619)

Comment thread tests/test_codex_cli.py

@AjayThorve AjayThorve left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The intended Codex + Relay flow works end to end, including gateway lifecycle and ATOF/ATIF generation. Requesting changes only for the cross-run artifact contamination above.

Comment thread adapters/common/src/nemo_fabric_adapters/common/utils.py
…d-codex

Signed-off-by: David Gardner <dagardner@nvidia.com>
…file

Signed-off-by: David Gardner <dagardner@nvidia.com>
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Signed-off-by: David Gardner <dagardner@nvidia.com>
@dagardner-nv
dagardner-nv merged commit 40870d0 into NVIDIA:main Jul 2, 2026
5 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jul 7, 2026
2 tasks
@dagardner-nv
dagardner-nv deleted the david-expand-codex branch July 9, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants