Skip to content

fix(reborn): align external tool provider names - #5303

Merged
serrrfirat merged 1 commit into
mainfrom
codex/fix-provider-tool-name-boundary
Jun 26, 2026
Merged

serrrfirat merged 1 commit into
mainfrom
codex/fix-provider-tool-name-boundary

Conversation

@serrrfirat

Copy link
Copy Markdown
Collaborator

Summary

  • store local-dev external tool names as ProviderToolName when exposing provider tool definitions
  • reject external tool names that cannot be represented as provider tool names before advertising them to the model
  • mark the external-tool decorator as an allowed local-dev provider boundary in architecture tests

CI root cause

PR #5300 does not touch the failing file. The shared failure was a main-branch compile error in crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs after ProviderToolDefinition and ProviderToolCall moved to ProviderToolName.

Tests

  • cargo test -p ironclaw_reborn_composition external_tool_surface --all-targets
  • cargo test -p ironclaw_architecture provider_tool_names_stay_at_model_protocol_boundaries
  • cargo clippy -p ironclaw_reborn_composition --all-features -- -D warnings
  • cargo fmt --check

@railway-app
railway-app Bot temporarily deployed to ironclaw-ci-preview / ironclaw-pr-5303 June 26, 2026 07:41 Destroyed
@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 Jun 26, 2026
@coderabbitai

coderabbitai Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Improved support for local development tools by handling provider-safe tool names more consistently.
    • External tool names are now validated before use, helping ensure only safe, supported names are exposed.
  • Bug Fixes

    • Fixed capability mapping so tool identifiers are generated and looked up more reliably.
    • Rejected invalid external tool names earlier, reducing unexpected invocation issues.

Walkthrough

Local-dev external tool capability handling now threads ProviderToolName through capability synthesis, descriptor generation, and lookup. External tool names are validated before conversion, and the provider_tool_names_stay_at_model_protocol_boundaries allowlist now includes the capability module.

Changes

Provider tool name handling at the local-dev boundary

Layer / File(s) Summary
Typed name contract
crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs
ResolvedSurface, ToolSpec, descriptor output, and capability_id_for_tool_name now use ProviderToolName values.
Name conversion and synthesis
crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs
provider_tool_name_for_external_tool validates external tool names, and visible_capabilities builds typed tool-name mappings and ToolSpec values from them.
Boundary allowlist and tests
crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs, crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs
The provider_tool_names_stay_at_model_protocol_boundaries allowlist includes the local-dev capability module, and tests cover capability-id mapping plus invalid-name rejection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • nearai/ironclaw#5292: Shares the ProviderToolName boundary work and the dependency-boundary test direction around the same capability module.

Poem

A name once loose now dons a type,
And tools are checked before they leap.
IDs align with careful grace,
While tests keep watch in their place.
Tiny boundaries hum at night.

🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary and tests, but it omits most required template sections like change type, linked issue, impact, and rollback. Fill in the missing template sections: Change Type, Linked Issue, Validation checkboxes, Security Impact, Reborn checklist, Database Impact, Blast Radius, Rollback Plan, and Review Follow-Through.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title is Conventional Commits style and accurately summarizes the provider-name fix in reborn local-dev tooling.
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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 external tool capability implementation to use the strongly-typed ProviderToolName instead of raw strings, ensuring better alignment with model protocol boundaries. It also adds comprehensive unit tests to verify the mapping and validation of external tool names. The reviewer identified two potential issues where invalid or oversized external tool names could cause the entire visible_capabilities call to fail closed (due to CapabilityId validation failures or length mismatches). It is recommended to handle these error states by skipping (not advertising) the problematic capabilities rather than failing the entire call.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

));
}
let tool_name = provider_tool_name_for_external_tool(spec.name())?;
let capability_id = external_tool_capability_id(spec.name())?;

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

If an external tool name starts with _ or - (e.g., _my_tool), ProviderToolName::new will succeed because it allows these characters anywhere. However, external_tool_capability_id will sanitize it to _my_tool and attempt to construct CapabilityId::new("external_tool._my_tool"). This will fail because CapabilityId segments must start with a lowercase ASCII letter or digit. Currently, this failure causes the entire visible_capabilities call to fail closed.

To fix this and align with repository rules, capabilities in an error state should not be advertised on any surface and should be handled consistently across all capability kinds. Instead of failing the entire call, handle the error state by not advertising (skipping) this capability.

References
  1. Capabilities in an error state should not be advertised on any surface and should be handled consistently across all capability kinds.

fn provider_tool_name_for_external_tool(
tool_name: &str,
) -> Result<ProviderToolName, AgentLoopHostError> {
ProviderToolName::new(tool_name).map_err(|_| {

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.

medium

There is a mismatch between the maximum allowed name length in ExternalToolSpec::new (which is MAX_EXTERNAL_TOOL_NAME_BYTES = 128 in crates/ironclaw_turns/src/external_tool_catalog.rs) and ProviderToolName::MAX_BYTES (which is 64 in crates/ironclaw_host_api/src/ids.rs).

If a client registers an external tool with a name between 65 and 128 bytes, registration will succeed, but visible_capabilities will subsequently fail closed when trying to convert it to a ProviderToolName. To align with repository rules, capabilities in an error state should not be advertised on any surface and should be handled consistently across all capability kinds. Instead of failing the entire visible_capabilities call, ensure that such capabilities in an error state are not advertised.

References
  1. Capabilities in an error state should not be advertised on any surface and should be handled consistently across all capability kinds.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

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

Inline comments:
In
`@crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs`:
- Around line 135-145: The `provider_tool_name_for_external_tool` helper is
discarding the validation error from `ProviderToolName::new`, so preserve the
underlying `HostApiError` cause instead of using `map_err(|_| ...)`. Update the
mapping in `provider_tool_name_for_external_tool` to carry or log the original
error details before converting to `AgentLoopHostError`, keeping the existing
invalid-invocation summary but adding the specific validation reason for
diagnostics.
🪄 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: Pro Plus

Run ID: aec38548-ec9e-4fad-9a57-a157e7d2fb81

📥 Commits

Reviewing files that changed from the base of the PR and between 54ca210 and 8faed8d.

📒 Files selected for processing (2)
  • crates/ironclaw_architecture/tests/reborn_dependency_boundaries.rs
  • crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs

Comment on lines +135 to +145
fn provider_tool_name_for_external_tool(
tool_name: &str,
) -> Result<ProviderToolName, AgentLoopHostError> {
ProviderToolName::new(tool_name).map_err(|_| {
AgentLoopHostError::new(
AgentLoopHostErrorKind::InvalidInvocation,
"external tool name cannot be represented as a provider tool name",
)
})
}

Copy link
Copy Markdown

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

.map_err(|_| …) drops the validation cause.

ProviderToolName::new returns a HostApiError saying why the name is invalid (bad char, length, etc.); the |_| closure discards it, leaving only a generic summary. Carry the cause for diagnostics.

Proposed fix — preserve the cause
-    ProviderToolName::new(tool_name).map_err(|_| {
-        AgentLoopHostError::new(
-            AgentLoopHostErrorKind::InvalidInvocation,
-            "external tool name cannot be represented as a provider tool name",
-        )
-    })
+    ProviderToolName::new(tool_name).map_err(|e| {
+        AgentLoopHostError::new(
+            AgentLoopHostErrorKind::InvalidInvocation,
+            format!("external tool name cannot be represented as a provider tool name: {e}"),
+        )
+    })

Note: the sibling external_tool_capability_id (Line 127) has the same pattern but is pre-existing/untouched. If you keep summaries fixed for the user boundary, at minimum debug! the bound error before mapping.

As per coding guidelines: "Do not use .map_err(|_| OtherError) — a closure that ignores its error binding and substitutes a generic error drops the underlying cause."

📝 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
fn provider_tool_name_for_external_tool(
tool_name: &str,
) -> Result<ProviderToolName, AgentLoopHostError> {
ProviderToolName::new(tool_name).map_err(|_| {
AgentLoopHostError::new(
AgentLoopHostErrorKind::InvalidInvocation,
"external tool name cannot be represented as a provider tool name",
)
})
}
fn provider_tool_name_for_external_tool(
tool_name: &str,
) -> Result<ProviderToolName, AgentLoopHostError> {
ProviderToolName::new(tool_name).map_err(|e| {
AgentLoopHostError::new(
AgentLoopHostErrorKind::InvalidInvocation,
format!("external tool name cannot be represented as a provider tool name: {e}"),
)
})
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@crates/ironclaw_reborn_composition/src/runtime/local_dev/external_tool_capability.rs`
around lines 135 - 145, The `provider_tool_name_for_external_tool` helper is
discarding the validation error from `ProviderToolName::new`, so preserve the
underlying `HostApiError` cause instead of using `map_err(|_| ...)`. Update the
mapping in `provider_tool_name_for_external_tool` to carry or log the original
error details before converting to `AgentLoopHostError`, keeping the existing
invalid-invocation summary but adding the specific validation reason for
diagnostics.

Source: Coding guidelines

@railway-app

railway-app Bot commented Jun 26, 2026

Copy link
Copy Markdown

🚅 Deployed to the ironclaw-pr-5303 environment in ironclaw-ci-preview

Service Status Web Updated (UTC)
ironclaw ✅ Success (View Logs) Web Jun 26, 2026 at 7:47 am

This branch was successfully deployed

No deployments
ironclaw-ci-preview / ironclaw-pr-5303 — 8faed8dc Deployed Jun 26, 2026 by railway-app[bot]
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 size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant