Skip to content

Architectural improvements: localize Script and MCP runtime adapters - #3543

Merged
serrrfirat merged 2 commits into
reborn-integrationfrom
arch-improvements/runtime-adapter-locality
May 13, 2026
Merged

serrrfirat merged 2 commits into
reborn-integrationfrom
arch-improvements/runtime-adapter-locality

Conversation

@serrrfirat

@serrrfirat serrrfirat commented May 12, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • keep Script and MCP execution semantics in their owning runtime-lane crates while making dispatcher adapters host-runtime-private composition
  • remove lane-crate public ScriptRuntimeAdapter / McpRuntimeAdapter constructors and public error-kind mapping helpers
  • keep script scoped mounts flowing through the host-runtime adapter, with caller-level tests covering mount forwarding and representative error mapping
  • prune lane-crate dispatcher/filesystem/event dependencies now that dispatch adapter composition lives in host-runtime

Architectural improvements

This remains an architectural cleanup PR for Reborn runtime adapter locality, but now tightens the host-runtime composition boundary per review feedback: lane crates own execution request/client/backend behavior, while HostRuntimeServices is the only place that adapts those executors into the neutral dispatcher port.

Follow-ups from the review that remain intentionally separate: #3492 lane crate naming criteria and the pre-existing blocking-in-async concern for script execution.

Verification

  • cargo test -p ironclaw_scripts --locked
  • cargo test -p ironclaw_mcp --locked
  • cargo test -p ironclaw_host_runtime --no-default-features --locked --quiet
  • cargo test -p ironclaw_architecture --locked --quiet
  • cargo clippy -p ironclaw_scripts -p ironclaw_mcp -p ironclaw_host_runtime --no-default-features --tests --locked --quiet -- -D warnings
  • cargo clippy -p ironclaw_architecture --tests --locked --quiet -- -D warnings
  • cargo fmt --check
  • git diff --check
  • bash scripts/pre-commit-safety.sh (staged diff)

@github-actions github-actions Bot added size: L 200-499 changed lines risk: low Changes to docs, tests, or low-risk modules contributor: core 20+ merged PRs labels May 12, 2026

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request refactors the system by moving the McpRuntimeAdapter and ScriptRuntimeAdapter implementations, along with their associated error mapping logic, from ironclaw_host_runtime into the ironclaw_mcp and ironclaw_scripts crates. This change centralizes the adapter logic within the specific runtime crates and updates dependencies accordingly. A high-severity issue was identified in the ScriptRuntimeAdapter where a synchronous, blocking call to execute_extension_json is made within an asynchronous function. This can block the async runtime's worker threads, so it is recommended to wrap this operation in tokio::task::spawn_blocking to ensure proper performance and avoid potential deadlocks.

Comment thread crates/ironclaw_scripts/src/lib.rs Outdated
Comment on lines +490 to +508
let execution = self
.executor
.execute_extension_json(
request.governor,
ScriptExecutionRequest {
package: request.package,
capability_id: request.capability_id,
scope: request.scope,
estimate: request.estimate,
mounts: request.mounts,
resource_reservation: request.resource_reservation,
invocation: ScriptInvocation {
input: request.input,
},
},
)
.map_err(|error| DispatchError::Script {
kind: script_error_kind(&error),
})?;

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.

high

The executor.execute_extension_json method is synchronous and performs blocking operations (like waiting for a Docker container). Calling it directly within an async function will block the worker thread of the async runtime. This can lead to performance degradation and potential deadlocks under load.

To fix this, the blocking call should be moved to a dedicated thread pool using tokio::task::spawn_blocking. When implementing this, ensure that the JoinError is logged to capture debugging information, specifically distinguishing between panics and cancellations in the error message.

This will likely require some refactoring to handle the lifetimes of the arguments passed to execute_extension_json, as spawn_blocking requires a 'static closure. A potential approach could involve:

  1. Changing ExtensionRegistry::get_extension to return an Arc<ExtensionPackage>.
  2. Creating an owned version of ScriptExecutionRequest that can be moved into the spawn_blocking closure.
  3. Adjusting how the governor is passed down so an owned handle (like an Arc) is available to be moved.
References
  1. In async functions, use asynchronous I/O operations or spawn_blocking for synchronous operations to avoid blocking the async runtime executor.
  2. When handling errors from tokio::task::spawn_blocking, log the JoinError and distinguish between panics and cancellations.

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

Review

Summary: Pure code-motion — ScriptRuntimeAdapter + McpRuntimeAdapter + script_error_kind/mcp_error_kind move from ironclaw_host_runtime::services into their owning lane crates (ironclaw_scripts, ironclaw_mcp). Dedupes the test-only adapter copies in integration tests.

Verified

  • Trust boundary preserved — host-runtime composition seam at crates/ironclaw_host_runtime/src/services.rs:37,61 just imports from the lane crates; signatures identical; RuntimeAdapter::dispatch_json → ResourceGovernor → RuntimeAdapterResult unchanged. Sandboxed (WASM) vs native (script/MCP) separation intact.
  • MCP credential handling unchanged — no touchpoints in RuntimeCredentialInjection, RuntimeHttpEgress, or requires_host_http_egress (ironclaw_mcp/src/lib.rs:993). Credential injection still flows through the executor; adapter is a thin shim.

Findings (non-blocking)

  1. Adapter constructors now pub across crates. Both adapters are now pub struct with pub fn from_executor (crates/ironclaw_mcp/src/lib.rs:946,951; crates/ironclaw_scripts/src/lib.rs:469,474). In the old location they were private to services.rs. Per the #3460 witness pattern, host-runtime is the only legitimate composer; pub(crate) won't work across crates, but a HostAdapter-witness wrapper or a pub constructor sealed by a private witness type would tighten this. Same for mcp_error_kind/script_error_kind — fine as observability helpers but they leak the RuntimeDispatchErrorKind mapping.

  2. Script test path change worth confirming intentional. Old in-test ScriptRuntimeAdapter passed mounts: None. The new shared adapter forwards request.mounts from RuntimeAdapterRequest. Production-correct (mounts must flow), but the integration test now exercises a different code path than before. Worth confirming intentional.

  3. #3492 naming criterion not addressed here. Lane crates aren't renamed to *_native_*/*_host_*. Consistent with the "separate architectural improvements PR" framing — file as follow-up.

  4. Gemini's blocking-in-async flag on execute_extension_json in ScriptRuntimeAdapter is pre-existing (function was already sync, invoked from an async adapter in services.rs) and out of scope for a code-motion PR. Worth a separate tracked issue.

LGTM.

@github-actions github-actions Bot added size: S 10-49 changed lines scope: dependencies Dependency updates and removed size: L 200-499 changed lines labels May 13, 2026
@serrrfirat

Copy link
Copy Markdown
Collaborator Author

Addressed the non-blocking review follow-ups in 13817a1a4:

  • Moved Script/MCP dispatcher adapters back behind the host-runtime composition boundary as private HostRuntimeServices implementation details.
  • Removed public ScriptRuntimeAdapter / McpRuntimeAdapter constructors from the lane crates.
  • Removed public script_error_kind / mcp_error_kind helpers; dispatch error-kind mapping is now private to host-runtime.
  • Dropped lane-crate dependencies on ironclaw_dispatcher now that dispatch adapter composition is host-runtime-owned.
  • Kept script request.mounts forwarding intact; existing scoped-mount caller test confirms that path is intentional.
  • Added architecture guardrails plus host-runtime caller tests for representative Script/MCP error mapping through the private adapters.

Verification run locally:

  • cargo test -p ironclaw_scripts --locked
  • cargo test -p ironclaw_mcp --locked
  • cargo test -p ironclaw_host_runtime --no-default-features --locked --quiet
  • cargo test -p ironclaw_architecture --locked --quiet
  • targeted clippy for changed crates + architecture
  • cargo fmt --check
  • git diff --check
  • bash scripts/pre-commit-safety.sh

CI is passing and the PR remains approved.

@serrrfirat
serrrfirat merged commit 65f2ed4 into reborn-integration May 13, 2026
15 checks passed
@serrrfirat
serrrfirat deleted the arch-improvements/runtime-adapter-locality branch May 13, 2026 10:04
theredspoon pushed a commit to theredspoon/ironclaw that referenced this pull request Jun 21, 2026
…earai#3543)

* refactor(reborn): localize script and mcp runtime adapters

* fix(reborn): address zmanian review — seal runtime adapters (nearai#3543)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: core 20+ merged PRs risk: low Changes to docs, tests, or low-risk modules scope: dependencies Dependency updates size: S 10-49 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants