Skip to content

feat(experimentalist): isolate optimization with OpenShell and Harbor - #1008

Open
ryana wants to merge 16 commits into
mainfrom
aire-823-experimentalist-openshell-harbor/rangilly
Open

feat(experimentalist): isolate optimization with OpenShell and Harbor#1008
ryana wants to merge 16 commits into
mainfrom
aire-823-experimentalist-openshell-harbor/rangilly

Conversation

@ryana

@ryana ryana commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • make nemo experimentalist run resolve inputs on the trusted host and execute the optimization loop inside OpenShell, with no host-side fallback
  • add a narrow authenticated Harbor bridge with strict request models, safe archives, content-addressed task envelopes, bounded dependency sessions, and authenticated artifact transport
  • run candidate code through a fixed bridge-owned Harbor adapter using a fixed python -m main entrypoint
  • preserve each trusted task's existing Harbor verifier mode instead of allowing the sandbox to select or rewrite it
  • route Harbor evaluation and task dependency analysis through remote clients whenever the packaged OpenShell runtime is active
  • package the non-root Experimentalist runtime image, strict and Docker Desktop policies, provider profile, launcher, and host/sandbox entrypoints

Trust boundary

The trusted host owns input resolution, the task catalog, OpenShell lifecycle, the bridge, Harbor orchestration, and Docker. The OpenShell sandbox owns the LLM-driven optimization loop and receives only a run-specific input snapshot. Candidate Python is inert data on the host and executes only after the trusted adapter uploads it into a Harbor task container.

The bridge does not accept a Harbor job config, image, mount, environment map, Compose document, import path, entrypoint, Docker option, or verifier mode. Unknown fields fail validation. Task overlays are restricted to host-declared mutable paths, and catalog/archive digests are reverified before execution.

The bridge credential is stored in an OpenShell provider. The sandbox sees an opaque placeholder; OpenShell replaces it with the real bearer token only on policy-approved bridge requests.

Validation

  • uv run --frozen pytest plugins/nemo-experimentalist/tests -q — 636 passed
  • focused Ruff format/lint and ty checks — passed
  • repository pre-commit suite — passed; Helm-docs and Studio hooks were skipped on the final review-only patch because their local Docker/Node dependencies were unavailable and the patch touches neither area
  • built the plugin wheel and verified the OpenShell assets and executable modes
  • built local/nmp-experimentalist:local for linux/arm64
  • with OpenShell 0.0.92 and its Docker driver, verified:
    • strict Landlock policy reaches Ready
    • no host worktree, home, common credential paths, Docker CLI, or Docker socket is visible
    • the only host mounts are OpenShell's supervisor and per-sandbox JWT
    • the bridge provider exposes only an opaque placeholder in the sandbox and rewrites it to the real host token at the approved endpoint
    • the exact create/upload/exec/cleanup script lifecycle reaches the packaged inner entrypoint and cleans up on failure

Scope

This prototype does not add an inference relay, change Platform Inference Gateway behavior, force a separate verifier container, add source-control publishing, or redesign Harbor's trial mounts.

Related

Summary by CodeRabbit

  • New Features

    • Added isolated OpenShell execution for Experimentalist runs.
    • Added a secure Harbor bridge for remote evaluations, dependency sessions, artifacts, and cancellation.
    • Added credential-free sandbox preparation with trusted dataset handling and controlled candidate execution.
    • Added Docker build and runtime support, including configurable providers and inference credentials.
  • Bug Fixes

    • Improved propagation of dependency runtime failures.
    • Added stricter validation, timeout handling, cleanup, authentication, and sensitive-data protection.
  • Documentation

    • Updated execution instructions, environment variables, provider configuration, and supported limitations.

ryana added 2 commits July 30, 2026 20:20
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
@ryana
ryana force-pushed the aire-823-experimentalist-openshell-harbor/rangilly branch from cfbfa29 to 2b6c594 Compare July 31, 2026 02:21
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
@ryana
ryana force-pushed the aire-823-experimentalist-openshell-harbor/rangilly branch from 3c76fd5 to ff05339 Compare July 31, 2026 02:26
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The plugin now prepares credential-free inputs on the trusted host, runs the Experimentalist in OpenShell, and evaluates candidates through an authenticated Harbor bridge with content-addressed datasets, bounded dependency sessions, trusted candidate execution, and secure artifact handling.

Changes

OpenShell execution

Layer / File(s) Summary
Sandbox preparation and launch
plugins/nemo-experimentalist/Dockerfile, plugins/nemo-experimentalist/docker-bake.hcl, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/*, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
Adds the container image, OpenShell policies, input preparation, provider setup, bridge lifecycle, sandbox execution, output download, and CLI routing.
Runtime checks and validation
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py, plugins/nemo-experimentalist/tests/test_preflight.py, plugins/nemo-experimentalist/tests/test_openshell_runtime.py, plugins/nemo-experimentalist/tests/test_experiment_cli.py
Adds OpenShell-specific credential and runtime checks, launch failure handling, path and policy validation, and CLI integration coverage.
Packaging and execution documentation
plugins/nemo-experimentalist/pyproject.toml, plugins/nemo-experimentalist/README.md, plugins/nemo-experimentalist/tests/test_cli_profile.py
Adds Harbor bridge dependencies and entry points. Documents OpenShell execution and separate optimizer and candidate credentials.

Trusted Harbor bridge

Layer / File(s) Summary
Contracts, envelopes, and archive transport
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/contracts.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/archives.py
Adds strict wire models, content-addressed dataset registration, trusted task materialization, declared overlays, tree digests, and safe archive creation and extraction.
Bridge service and dependency sessions
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/service.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py
Adds authenticated evaluation and dependency APIs, asynchronous job state, cancellation, artifact export, redaction, bounded uploads, and concurrent session management.
Bridge boundary tests
plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py, plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py, plugins/nemo-experimentalist/tests/test_remote_harbor.py
Tests schema rejection, archive safety, catalog integrity, overlay restrictions, authentication, digest validation, artifact handling, capability authorization, and session cleanup.

Trusted candidate execution

Layer / File(s) Summary
Candidate adapter and runner
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/runner.py
Validates candidate bundles, installs dependencies with a pinned architecture-specific uv, runs only python -m main, injects scoped inference credentials, and records summary metadata.
Candidate execution tests
plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
Tests candidate import isolation, link rejection, finite timeouts, trusted adapter wiring, inference configuration, and Harbor job construction.

Remote evaluator integration

Layer / File(s) Summary
Remote Harbor evaluator
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/factory.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py
Adds bridge-backed evaluation submission and polling, remote dependency runtimes, artifact retrieval, evaluator selection, scoped imports, model settings, and command execution.
Dataset and shell runtime integration
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/base.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/tools.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/rationalizer.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/trace_analyzer.py, plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/analyzer.py
Adds evaluator dataset preparation, context-local dependency command routing, active-runtime analysis execution, and propagation of dependency runtime errors.
Evaluator integration tests
plugins/nemo-experimentalist/tests/test_remote_harbor.py, plugins/nemo-experimentalist/tests/experimentalist/*
Tests remote submissions, dataset preparation, dependency command timeouts, evaluator selection, runtime authorization, cleanup, and updated evaluator mocks.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant OpenShellLauncher
  participant OpenShellSandbox
  participant HarborBridge
  participant RemoteHarborEvaluator
  CLI->>OpenShellLauncher: prepare and launch run
  OpenShellLauncher->>HarborBridge: start authenticated bridge
  OpenShellLauncher->>OpenShellSandbox: create sandbox and upload manifest
  OpenShellSandbox->>RemoteHarborEvaluator: execute Experimentalist workflow
  RemoteHarborEvaluator->>HarborBridge: submit evaluation and dependency commands
  HarborBridge-->>RemoteHarborEvaluator: evaluation status and artifacts
  RemoteHarborEvaluator-->>OpenShellSandbox: evaluation result
  OpenShellSandbox-->>CLI: run summary
Loading

Possibly related PRs

Suggested reviewers: svvarom, ngoncharenko, sandychapman

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.18% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the PR's main change: isolating Experimentalist optimization with OpenShell and Harbor.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch aire-823-experimentalist-openshell-harbor/rangilly

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

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

⚠️ Outside diff range comments (2)
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py (2)

1-1: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Expose Harbor command execution through the returned runtime

HarborDependencyContext.__aenter__ returns HarborDependencyRuntime, which does not implement execute(). use_dependency_runtime() therefore selects no executor, and GuardedShellTools.run() executes commands on the host instead of inside Harbor. Make the active returned runtime implement DependencyCommandExecutor while preserving the DependencyRuntime API.

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py`
at line 1, Update HarborDependencyRuntime and HarborDependencyContext.__aenter__
so the returned active runtime implements DependencyCommandExecutor while
retaining the existing DependencyRuntime API. Ensure use_dependency_runtime()
recognizes it as the command executor, allowing GuardedShellTools.run() to
execute commands inside Harbor rather than on the host.

219-346: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Expose execute() from HarborDependencyContext.__aenter__.

__aenter__ returns self._runtime, but execute() exists only on HarborDependencyContext. Local Harbor commands therefore fail the DependencyCommandExecutor check and fall back to host execution instead of running in the Harbor container. Make the returned object expose execute().

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py`
around lines 219 - 346, Update HarborDependencyContext.__aenter__ to return an
object exposing the context’s execute() method, rather than returning
self._runtime alone. Preserve the existing runtime startup and readiness checks,
and ensure the returned object satisfies DependencyCommandExecutor so local
commands execute inside the Harbor environment.
🧹 Nitpick comments (9)
plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py (2)

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

Add the missing type hint and import json normally.

registered has no annotation; RegisteredEnvelope is already imported. __import__("json") hides a plain module import.

♻️ Proposed refactor
-def _request_parts(tmp_path: Path, registered) -> tuple[dict[str, str], dict[str, tuple[str, bytes, str]]]:
+def _request_parts(
+    tmp_path: Path, registered: RegisteredEnvelope
+) -> tuple[dict[str, str], dict[str, tuple[str, bytes, str]]]:
+import json
-            data={"metadata": __import__("json").dumps(metadata)},
+            data={"metadata": json.dumps(metadata)},

As per coding guidelines: "In Python code, prefer concrete type hints over string-based type hints".

Also applies to: 228-228

🤖 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 `@plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py` at line 55,
Update _request_parts to annotate registered with the imported
RegisteredEnvelope type, and replace any __import__("json") usage in the
affected code with a normal top-level json import. Keep the existing behavior
and return annotations unchanged.

Source: Coding guidelines


283-294: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Dependency-session and cancellation endpoints have no test coverage.

The tests cover evaluation submission, polling, and artifacts. They omit POST /v1/dependencies, POST /v1/dependencies/{session_id}/exec, DELETE /v1/dependencies/{session_id}, and DELETE /v1/evaluations/{job_id}. The capability-token check at service.py Lines 553-558 is untested, including the 403 path. Add cases for those routes.

🤖 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 `@plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py` around
lines 283 - 294, Add tests alongside test_job_api_requires_authentication
covering POST /v1/dependencies, POST /v1/dependencies/{session_id}/exec, DELETE
/v1/dependencies/{session_id}, and DELETE /v1/evaluations/{job_id}. Verify
successful behavior and authentication failures, including the capability-token
validation and its 403 response, using the existing app factory, TestClient, and
RecordingRunner fixtures.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py (2)

197-201: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

One mkdir exec per uploaded file adds avoidable container round trips.

Each file triggers a separate exec_as_root call. Compute the unique parent directories first, then create them in one command.

♻️ Proposed refactor
-        for source in candidate_files:
-            relative = source.relative_to(candidate_root).as_posix()
-            target = f"{REMOTE_PROJECT}/{relative}"
-            await self.exec_as_root(environment, command=f"mkdir -p {shlex.quote(str(Path(target).parent))}")
-            await environment.upload_file(source, target)
+        relatives = [source.relative_to(candidate_root).as_posix() for source in candidate_files]
+        parents = sorted({f"{REMOTE_PROJECT}/{relative}".rsplit("/", 1)[0] for relative in relatives})
+        if parents:
+            quoted = " ".join(shlex.quote(parent) for parent in parents)
+            await self.exec_as_root(environment, command=f"mkdir -p {quoted}")
+        for source, relative in zip(candidate_files, relatives, strict=True):
+            await environment.upload_file(source, f"{REMOTE_PROJECT}/{relative}")
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`
around lines 197 - 201, Update the upload flow around the candidate_files loop
to collect unique parent directories for all target paths, then invoke
exec_as_root once with a command that creates every directory. Keep each
environment.upload_file call unchanged and ensure directory paths remain safely
shell-quoted.

189-195: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

chmod -R a+rwx {REMOTE_ROOT} makes the venv and the uv binary world-writable.

The later chmod 0755 {REMOTE_UV} and chmod -R a+rX {REMOTE_PROJECT} restore only two paths. {REMOTE_VENV}, {REMOTE_PYTHON_INSTALLS}, and {REMOTE_UV_CACHE} stay mode 0777, so candidate code can replace the interpreter that the adapter executes. Grant write access to the agent user only, for example with chown, or restrict the mode to a+rX plus targeted writable directories.

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`
around lines 189 - 195, The setup command in the trusted-agent environment
initialization currently makes REMOTE_ROOT recursively world-writable, including
REMOTE_VENV, REMOTE_PYTHON_INSTALLS, and REMOTE_UV_CACHE. Update the command
passed to exec_as_root so agent-required writes use ownership or targeted
directory permissions, while preserving read/execute access and ensuring the
interpreter and uv binary cannot be replaced by candidate code.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py (1)

451-461: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Shield the cancellation cleanup request.

After asyncio.CancelledError, the task is marked cancelled. The next await on the new client can raise CancelledError again, so the job cancellation DELETE may never reach the bridge. This leaks a running remote evaluation. Wrap the cleanup in asyncio.shield with a bounded timeout.

♻️ Proposed change
         except asyncio.CancelledError:
             if job_id is not None:
-                async with httpx.AsyncClient(
-                    timeout=options.request_timeout_sec,
-                    transport=self._transport,
-                ) as client:
-                    await client.delete(
-                        f"{str(options.bridge_url).rstrip('/')}/v1/evaluations/{job_id}",
-                        headers=headers,
-                    )
+                async def _cancel_remote_job() -> None:
+                    async with httpx.AsyncClient(
+                        timeout=options.request_timeout_sec,
+                        transport=self._transport,
+                    ) as client:
+                        await client.delete(
+                            f"{str(options.bridge_url).rstrip('/')}/v1/evaluations/{job_id}",
+                            headers=headers,
+                        )
+
+                with contextlib.suppress(Exception):
+                    await asyncio.shield(asyncio.ensure_future(_cancel_remote_job()))
             raise
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`
around lines 451 - 461, Update the asyncio.CancelledError cleanup in the
evaluation flow to shield the job-cancellation DELETE request from task
cancellation and enforce a bounded timeout using the existing request-timeout
configuration. Preserve the conditional job_id check and re-raise the original
cancellation after cleanup is attempted.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py (1)

95-101: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

close() can raise on a benign race.

stop() raises KeyError when a session id is already removed. close() reads the id list, releases the lock, then calls stop() for each id. A concurrent stop() turns shutdown into an ExceptionGroup. Ignore KeyError in close().

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py`
around lines 95 - 101, Update Harbor dependency manager close() to filter out
benign KeyError results from the asyncio.gather() calls to stop(), while
preserving propagation of all other exceptions through ExceptionGroup. Keep
stop() behavior unchanged and continue raising only when non-KeyError failures
remain.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py (1)

397-410: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Guard against a non-table [task] value.

If task.toml sets task to a scalar, task_table.get("name") raises AttributeError instead of the intended ValueError. Check the type first.

Proposed fix
-    task_table = document.get("task")
-    if task_table is None or not isinstance(task_table.get("name"), str):
+    task_table = document.get("task")
+    if not isinstance(task_table, dict) or not isinstance(task_table.get("name"), str):
         raise ValueError(f"Trusted Harbor task has no [task].name: {path}")
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py`
around lines 397 - 410, Update _set_materialized_identity to validate that the
parsed task value is a table or mapping before accessing task_table.get("name").
Raise the existing ValueError for both missing and non-table [task] values,
while preserving the current name validation and rewrite behavior for valid
tables.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/run.sh (1)

95-101: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Download /sandbox/output even when the inner run fails.

set -e aborts at line 95 on a non-zero exit. The trap then deletes the sandbox, so all partial artifacts and logs are lost for exactly the runs that need inspection.

Proposed fix
-openshell sandbox exec --name "$sandbox_name" --workdir "$remote_input" -- \
-  /app/.venv/bin/python -m nemo_experimentalist_plugin.openshell.inner \
-    --manifest "$remote_input/run.json" \
-    --output /sandbox/output
-
-mkdir -p "$output_dir"
-openshell sandbox download "$sandbox_name" /sandbox/output "$output_dir"
+run_status=0
+openshell sandbox exec --name "$sandbox_name" --workdir "$remote_input" -- \
+  /app/.venv/bin/python -m nemo_experimentalist_plugin.openshell.inner \
+    --manifest "$remote_input/run.json" \
+    --output /sandbox/output || run_status=$?
+
+mkdir -p "$output_dir"
+openshell sandbox download "$sandbox_name" /sandbox/output "$output_dir" || true
+exit "$run_status"
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/run.sh`
around lines 95 - 101, Update the openshell execution flow around the inner
Python command so a non-zero exit does not immediately terminate the script
before downloading artifacts. Preserve the inner command’s exit status, run the
existing mkdir and sandbox download steps regardless of success or failure, then
return the original failure status after cleanup/download completes.
plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py (1)

113-127: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for the extraction size cap.

No test exercises max_bytes or max_files on extract_directory_archive. A test that declares small header sizes but ships a large payload would catch the header-trust gap flagged in archives.py lines 96-113.

🤖 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 `@plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py` around
lines 113 - 127, Add a test alongside test_archive_rejects_duplicate_paths that
calls extract_directory_archive with small max_bytes and/or max_files limits,
using an archive whose header declares a small size but whose payload exceeds
that size. Assert extraction raises ValueError and matches the size-limit error,
covering enforcement against actual payload bytes rather than trusting tar
headers.
🤖 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 `@plugins/nemo-experimentalist/README.md`:
- Around line 70-77: Update the documented EXPERIMENTALIST_SMART_MODEL_NAME and
EXPERIMENTALIST_FAST_MODEL_NAME values to remove the repeated openai/ prefixes,
leaving each model name with the format expected after configure-providers.sh
removes one prefix.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`:
- Around line 239-256: Update __aexit__ to log shutdown failures instead of
raising DependencyRuntimeError during cleanup. Preserve any active body
exception by returning False without raising when exc is present, and only
re-raise the shutdown error when exc is None; continue clearing the runtime
session and capability state.
- Around line 214-219: Propagate the configured max_archive_bytes value into
RemoteHarborDependencyRuntime and use it when creating the dependency overlay
archive in the digest handling path around create_directory_archive, instead of
relying on the fixed 512 MiB default. Preserve the existing archive opening and
files mapping behavior.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py`:
- Around line 62-68: Update session ID construction in the session-start flow to
ensure the value stored in _sessions never exceeds the
DependencySession.session_id Identifier limit, accounting for the request_id and
generated suffix. Preserve uniqueness while truncating or otherwise bounding
request_id before composing session_id, and ensure any validation failure cannot
leave the already-started Harbor environment running.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/service.py`:
- Around line 318-321: Update require_auth and require_capability to compare
authorization/token values as encoded bytes rather than strings, preventing
hmac.compare_digest from raising TypeError for non-ASCII header input. Preserve
the existing 401 Unauthorized behavior for missing or mismatched credentials.
- Around line 276-281: Update the trial sanitization loop around trial.metadata
and trial.metrics in the export flow to also redact every value in trial.outputs
using the existing _redact_value helper and sensitive configuration. Preserve
the outputs structure while replacing each output value with its redacted form,
ensuring host paths and sensitive data cannot pass through unredacted.
- Around line 412-445: Update the submission validation try/except around
extract_directory_archive to handle tarfile.TarError, either by adding it to the
caught exceptions or converting it to ValueError at the extraction boundary.
Ensure malformed candidate or overlay archives follow the existing 422 rejection
path and work_dir cleanup via the logger.exception, shutil.rmtree, and
HTTPException flow.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/configure-providers.sh`:
- Around line 36-50: Make the bridge-provider setup in the bridge creation flow
idempotent by deleting any existing "$bridge_provider" before invoking openshell
provider create, matching the inference path behavior. Move the
cleanup_failed_setup definition and its EXIT trap before provider creation so
failures during openshell provider create trigger deletion of a partially
configured provider.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py`:
- Around line 187-205: Update the bridge launch arguments in the
subprocess.Popen call to use an explicit, configurable bind-host value instead
of hardcoded 0.0.0.0, defaulting to 127.0.0.1 and allowing the Docker/OpenShell
gateway address to be supplied when required; keep the existing localhost probe
and port behavior aligned with the selected bind address.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/preparation.py`:
- Around line 71-77: Update _copy_file and its caller so symlink validation
occurs before path resolution: stop resolving inputs.agent_spec before passing
it to _copy_file, and resolve only within _copy_file after checking
source.is_symlink() (or otherwise preserve the original path for that check).
Keep regular-file and hard-link rejection behavior unchanged.

In `@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py`:
- Around line 145-160: Update the openshell-inference-credential check in the
preflight flow so it provides an independent signal: remove INFERENCE_API_KEY
from the optimizer_credential candidate set, or remove the redundant check
entirely. If retaining the check, align its hint with the credentials it
actually evaluates.

In
`@plugins/nemo-experimentalist/tests/experimentalist/test_dataset_staging_loop.py`:
- Around line 64-71: Move the EvaluatorFactory monkeypatch out of the
MutatingEvalAuthor class body and into the enclosing test function alongside the
other monkeypatch.setattr calls. Keep the patch behavior unchanged and ensure it
is applied before the class or test logic that depends on it executes.

---

Outside diff comments:
In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py`:
- Line 1: Update HarborDependencyRuntime and HarborDependencyContext.__aenter__
so the returned active runtime implements DependencyCommandExecutor while
retaining the existing DependencyRuntime API. Ensure use_dependency_runtime()
recognizes it as the command executor, allowing GuardedShellTools.run() to
execute commands inside Harbor rather than on the host.
- Around line 219-346: Update HarborDependencyContext.__aenter__ to return an
object exposing the context’s execute() method, rather than returning
self._runtime alone. Preserve the existing runtime startup and readiness checks,
and ensure the returned object satisfies DependencyCommandExecutor so local
commands execute inside the Harbor environment.

---

Nitpick comments:
In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`:
- Around line 451-461: Update the asyncio.CancelledError cleanup in the
evaluation flow to shield the job-cancellation DELETE request from task
cancellation and enforce a bounded timeout using the existing request-timeout
configuration. Preserve the conditional job_id check and re-raise the original
cancellation after cleanup is attempted.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py`:
- Around line 95-101: Update Harbor dependency manager close() to filter out
benign KeyError results from the asyncio.gather() calls to stop(), while
preserving propagation of all other exceptions through ExceptionGroup. Keep
stop() behavior unchanged and continue raising only when non-KeyError failures
remain.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py`:
- Around line 397-410: Update _set_materialized_identity to validate that the
parsed task value is a table or mapping before accessing task_table.get("name").
Raise the existing ValueError for both missing and non-table [task] values,
while preserving the current name validation and rewrite behavior for valid
tables.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`:
- Around line 197-201: Update the upload flow around the candidate_files loop to
collect unique parent directories for all target paths, then invoke exec_as_root
once with a command that creates every directory. Keep each
environment.upload_file call unchanged and ensure directory paths remain safely
shell-quoted.
- Around line 189-195: The setup command in the trusted-agent environment
initialization currently makes REMOTE_ROOT recursively world-writable, including
REMOTE_VENV, REMOTE_PYTHON_INSTALLS, and REMOTE_UV_CACHE. Update the command
passed to exec_as_root so agent-required writes use ownership or targeted
directory permissions, while preserving read/execute access and ensuring the
interpreter and uv binary cannot be replaced by candidate code.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/run.sh`:
- Around line 95-101: Update the openshell execution flow around the inner
Python command so a non-zero exit does not immediately terminate the script
before downloading artifacts. Preserve the inner command’s exit status, run the
existing mkdir and sandbox download steps regardless of success or failure, then
return the original failure status after cleanup/download completes.

In `@plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py`:
- Around line 113-127: Add a test alongside test_archive_rejects_duplicate_paths
that calls extract_directory_archive with small max_bytes and/or max_files
limits, using an archive whose header declares a small size but whose payload
exceeds that size. Assert extraction raises ValueError and matches the
size-limit error, covering enforcement against actual payload bytes rather than
trusting tar headers.

In `@plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py`:
- Line 55: Update _request_parts to annotate registered with the imported
RegisteredEnvelope type, and replace any __import__("json") usage in the
affected code with a normal top-level json import. Keep the existing behavior
and return annotations unchanged.
- Around line 283-294: Add tests alongside test_job_api_requires_authentication
covering POST /v1/dependencies, POST /v1/dependencies/{session_id}/exec, DELETE
/v1/dependencies/{session_id}, and DELETE /v1/evaluations/{job_id}. Verify
successful behavior and authentication failures, including the capability-token
validation and its 403 response, using the existing app factory, TestClient, and
RecordingRunner fixtures.
🪄 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: CHILL

Plan: Enterprise

Run ID: 8b3293ae-48d3-4d1c-85e1-62b2da06e982

📥 Commits

Reviewing files that changed from the base of the PR and between 84d9476 and cfbfa29.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (42)
  • docker-bake.hcl
  • plugins/nemo-experimentalist/Dockerfile
  • plugins/nemo-experimentalist/README.md
  • plugins/nemo-experimentalist/pyproject.toml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/analyzer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/base.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/factory.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/models.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/rationalizer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/tools.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/trace_analyzer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/archives.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/contracts.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/runner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/service.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/configure-providers.sh
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/inner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.docker-desktop.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/preparation.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/provider-profiles/nemo-experimentalist-harbor-bridge.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/run.sh
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_dataset_staging_loop.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_failure.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_insight_suite.py
  • plugins/nemo-experimentalist/tests/test_cli_profile.py
  • plugins/nemo-experimentalist/tests/test_experiment_cli.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py
  • plugins/nemo-experimentalist/tests/test_openshell_runtime.py
  • plugins/nemo-experimentalist/tests/test_preflight.py
  • plugins/nemo-experimentalist/tests/test_remote_harbor.py

Comment thread plugins/nemo-experimentalist/README.md
Comment thread plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py Outdated
Comment thread plugins/nemo-experimentalist/tests/experimentalist/test_dataset_staging_loop.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py (1)

220-232: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Replace __import__("json") with a module-level import.

Line 228 uses __import__("json").dumps(...). Add import json at the top of the file and call json.dumps(metadata).

♻️ Proposed change
 import time
+import json
 from pathlib import Path
-            data={"metadata": __import__("json").dumps(metadata)},
+            data={"metadata": json.dumps(metadata)},
🤖 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 `@plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py` around
lines 220 - 232, Add a module-level json import in test_harbor_bridge_service.py
and update test_job_api_returns_422_for_unknown_authority to call
json.dumps(metadata) instead of dynamically importing json.
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py (1)

451-463: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Cancel the remote job on every failure path, and do not let cleanup mask the original error.

The DELETE runs only for asyncio.CancelledError. A TimeoutError at line 429, a polling RuntimeError, or an artifact failure leaves the bridge job running until application shutdown. The bare await client.delete can also raise and replace the original exception.

♻️ Proposed change
-        except asyncio.CancelledError:
+        except BaseException:
             if job_id is not None:
-                async with httpx.AsyncClient(
-                    timeout=options.request_timeout_sec,
-                    transport=self._transport,
-                ) as client:
-                    await client.delete(
-                        f"{str(options.bridge_url).rstrip('/')}/v1/evaluations/{job_id}",
-                        headers=headers,
-                    )
+                try:
+                    async with httpx.AsyncClient(
+                        timeout=options.request_timeout_sec,
+                        transport=self._transport,
+                    ) as client:
+                        await client.delete(
+                            f"{str(options.bridge_url).rstrip('/')}/v1/evaluations/{job_id}",
+                            headers=headers,
+                        )
+                except Exception:
+                    logger.warning("Failed to cancel Harbor bridge job %s", job_id, exc_info=True)
             raise
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`
around lines 451 - 463, Update the remote evaluation cleanup around the job
execution flow and its existing asyncio.CancelledError handler so any failure
after job creation, including timeouts, polling RuntimeError, artifact errors,
and cancellation, attempts to DELETE the remote job. Make the deletion
best-effort by catching cleanup failures and preserving the original exception,
while retaining staging removal in finally via shutil.rmtree.
plugins/nemo-experimentalist/tests/test_remote_harbor.py (1)

89-91: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Make the trace directory creation idempotent.

_RecordingRunner.run executes twice. mkdir() without exist_ok=True raises if the bridge ever reuses a work_dir. Use trace.parent.mkdir(parents=True, exist_ok=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 `@plugins/nemo-experimentalist/tests/test_remote_harbor.py` around lines 89 -
91, Update the trace directory creation in _RecordingRunner.run to call
trace.parent.mkdir with parents=True and exist_ok=True, so repeated executions
safely reuse an existing work_dir without raising.
🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`:
- Around line 345-353: Resolve artifact_root before iterating over result
resources, then use that resolved path for both local artifact resolution and
the is_relative_to containment check in the resource-rewriting flow. Preserve
the existing missing/unsafe artifact validation and URI conversion behavior.
- Around line 141-167: Update execute so the _request call uses an HTTP client
timeout derived from the timeout argument, with sufficient allowance for the
requested command duration instead of always using self.request_timeout_sec;
keep the existing timeout_sec conversion and response handling unchanged.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`:
- Around line 277-283: Update the candidate execution call in trusted_agent’s
execution flow to pass a bounded, non-None timeout_sec to exec_as_agent. Use the
existing agent timeout configuration when available, with a safe finite fallback
for tasks that omit [agent].timeout_sec, so environment.exec cannot run
indefinitely.

In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py`:
- Around line 296-314: Update the cleanup state in the launch flow around
_configure_providers so _delete_bridge_provider is attempted whenever provider
configuration was invoked, including when it fails partway through. Set the
relevant state before calling _configure_providers and preserve the existing
cleanup behavior for cases where configuration is not attempted.

---

Nitpick comments:
In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py`:
- Around line 451-463: Update the remote evaluation cleanup around the job
execution flow and its existing asyncio.CancelledError handler so any failure
after job creation, including timeouts, polling RuntimeError, artifact errors,
and cancellation, attempts to DELETE the remote job. Make the deletion
best-effort by catching cleanup failures and preserving the original exception,
while retaining staging removal in finally via shutil.rmtree.

In `@plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py`:
- Around line 220-232: Add a module-level json import in
test_harbor_bridge_service.py and update
test_job_api_returns_422_for_unknown_authority to call json.dumps(metadata)
instead of dynamically importing json.

In `@plugins/nemo-experimentalist/tests/test_remote_harbor.py`:
- Around line 89-91: Update the trace directory creation in _RecordingRunner.run
to call trace.parent.mkdir with parents=True and exist_ok=True, so repeated
executions safely reuse an existing work_dir without raising.
🪄 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: CHILL

Plan: Enterprise

Run ID: 51fd0d95-11d3-4d3c-bfac-f47c90ab7cd4

📥 Commits

Reviewing files that changed from the base of the PR and between cfbfa29 and ff05339.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (42)
  • docker-bake.hcl
  • plugins/nemo-experimentalist/Dockerfile
  • plugins/nemo-experimentalist/README.md
  • plugins/nemo-experimentalist/pyproject.toml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/analyzer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/base.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/factory.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/models.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/rationalizer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/tools.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/trace_analyzer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/archives.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/contracts.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/envelopes.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/runner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/service.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/configure-providers.sh
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/inner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.docker-desktop.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/preparation.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/provider-profiles/nemo-experimentalist-harbor-bridge.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/run.sh
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_dataset_staging_loop.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_failure.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_insight_suite.py
  • plugins/nemo-experimentalist/tests/test_cli_profile.py
  • plugins/nemo-experimentalist/tests/test_experiment_cli.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_service.py
  • plugins/nemo-experimentalist/tests/test_openshell_runtime.py
  • plugins/nemo-experimentalist/tests/test_preflight.py
  • plugins/nemo-experimentalist/tests/test_remote_harbor.py
🚧 Files skipped from review as they are similar to previous changes (30)
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/analyzer.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/rationalizer.py
  • plugins/nemo-experimentalist/pyproject.toml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.docker-desktop.yaml
  • plugins/nemo-experimentalist/tests/experimentalist/test_dataset_staging_loop.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/base.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_failure.py
  • docker-bake.hcl
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/trace_analyzer.py
  • plugins/nemo-experimentalist/tests/experimentalist/test_loop_insight_suite.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/policy.yaml
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/provider-profiles/nemo-experimentalist-harbor-bridge.yaml
  • plugins/nemo-experimentalist/README.md
  • plugins/nemo-experimentalist/Dockerfile
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/factory.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/runner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/configure-providers.sh
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
  • plugins/nemo-experimentalist/tests/test_cli_profile.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/contracts.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/dependencies.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_contracts.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/tools.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/preflight.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/models.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/archives.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/service.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/harbor.py

Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 29101/37023 78.6% 63.1%
Integration Tests 17215/35741 48.2% 20.7%

Signed-off-by: Ryan Angilly <rangilly@nvidia.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (2)
plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py (2)

219-223: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Bound the candidate-controlled summary read.

The candidate writes /logs/agent/summary.json at Lines 270-271. read_text() loads the complete file before parsing. A candidate can write an oversized JSON document and exhaust bridge memory. Read at most a fixed byte limit and reject oversized input before calling json.loads.

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`
around lines 219 - 223, Update the summary read in the trusted-agent method to
enforce a fixed maximum byte limit before parsing: inspect the summary file size
or bounded-read result, reject oversized input, and only pass accepted content
to json.loads. Preserve the existing silent handling for OSError and JSON
decoding failures, using the surrounding summary-reading symbols and
summary_path flow.

198-202: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use POSIX semantics for remote paths. On Windows, Path(target).parent produces backslashes. Nested candidate uploads then target a directory that mkdir did not create. Use PurePosixPath(target).parent or posixpath.dirname.

🤖 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
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`
around lines 198 - 202, Update the candidate upload loop around the relative and
target path construction to use POSIX path semantics for the remote destination,
replacing the platform-dependent Path handling in the mkdir command with
PurePosixPath or posixpath. Preserve the existing quoting and upload behavior
while ensuring nested remote directories are created correctly on Windows.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py`:
- Around line 219-223: Update the summary read in the trusted-agent method to
enforce a fixed maximum byte limit before parsing: inspect the summary file size
or bounded-read result, reject oversized input, and only pass accepted content
to json.loads. Preserve the existing silent handling for OSError and JSON
decoding failures, using the surrounding summary-reading symbols and
summary_path flow.
- Around line 198-202: Update the candidate upload loop around the relative and
target path construction to use POSIX path semantics for the remote destination,
replacing the platform-dependent Path handling in the mkdir command with
PurePosixPath or posixpath. Preserve the existing quoting and upload behavior
while ensuring nested remote directories are created correctly on Windows.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7874e48e-07ad-4662-8c71-05f8dda8f559

📥 Commits

Reviewing files that changed from the base of the PR and between 7f3abcb and de0bbcc.

📒 Files selected for processing (6)
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/harbor_bridge/trusted_agent.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
  • plugins/nemo-experimentalist/tests/test_openshell_runtime.py
  • plugins/nemo-experimentalist/tests/test_remote_harbor.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • plugins/nemo-experimentalist/tests/test_harbor_bridge_runner.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py

Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
@ryana
ryana marked this pull request as ready for review July 31, 2026 14:40

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In `@plugins/nemo-experimentalist/docker-bake.hcl`:
- Line 1: Add the standard NVIDIA copyright and Apache-2.0 SPDX license headers
at the beginning of docker-bake.hcl, before the existing “Experimentalist
control plane” comment, following the repository’s established header format.
🪄 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: CHILL

Plan: Enterprise

Run ID: 590355de-18a4-4abe-91a7-9f571ba23a02

📥 Commits

Reviewing files that changed from the base of the PR and between de0bbcc and 0b6586c.

📒 Files selected for processing (10)
  • plugins/nemo-experimentalist/README.md
  • plugins/nemo-experimentalist/docker-bake.hcl
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/base.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py
  • plugins/nemo-experimentalist/tests/test_cli_profile.py
  • plugins/nemo-experimentalist/tests/test_experiment_cli.py
  • plugins/nemo-experimentalist/tests/test_openshell_runtime.py
🚧 Files skipped from review as they are similar to previous changes (8)
  • plugins/nemo-experimentalist/tests/test_cli_profile.py
  • plugins/nemo-experimentalist/tests/test_experiment_cli.py
  • plugins/nemo-experimentalist/README.md
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/cli.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/evaluator/remote_harbor.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/experimentalist/components/loop.py
  • plugins/nemo-experimentalist/tests/test_openshell_runtime.py
  • plugins/nemo-experimentalist/src/nemo_experimentalist_plugin/openshell/launcher.py

@@ -0,0 +1,15 @@
# Experimentalist control plane. Harbor and Docker remain host-side.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add the required SPDX header.

Line 1 starts with a project comment, but the file has no SPDX copyright or license identifiers. Add both headers before the existing comment.

Proposed fix
+# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+# SPDX-License-Identifier: Apache-2.0
 # Experimentalist control plane. Harbor and Docker remain host-side.

As per coding guidelines, every file under plugins/nemo-experimentalist/**/* must include the NVIDIA copyright header and Apache-2.0 license.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Experimentalist control plane. Harbor and Docker remain host-side.
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# Experimentalist control plane. Harbor and Docker remain host-side.
🤖 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 `@plugins/nemo-experimentalist/docker-bake.hcl` at line 1, Add the standard
NVIDIA copyright and Apache-2.0 SPDX license headers at the beginning of
docker-bake.hcl, before the existing “Experimentalist control plane” comment,
following the repository’s established header format.

Source: Coding guidelines

gaiadilorenzo and others added 9 commits July 31, 2026 10:14
Signed-off-by: Gaia Di Lorenzo <gaia.dilorenzo01@gmail.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Gaia Di Lorenzo <gaia.dilorenzo01@gmail.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Signed-off-by: Ryan Angilly <rangilly@nvidia.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants