From 4bc3c01ed68f4a5dc6010e2179da260a806de4c6 Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 15:36:43 +0300 Subject: [PATCH 1/8] feat(reborn): enable progressive tool disclosure by default --- crates/ironclaw_runner/src/runtime.rs | 31 +++++++++++++-------------- tests/integration/support/builder.rs | 7 ++++++ tests/integration/tool_disclosure.rs | 29 +++++++++++++++++++++++++ 3 files changed, 51 insertions(+), 16 deletions(-) diff --git a/crates/ironclaw_runner/src/runtime.rs b/crates/ironclaw_runner/src/runtime.rs index 20dbf7ab8e0..987e7fa9334 100644 --- a/crates/ironclaw_runner/src/runtime.rs +++ b/crates/ironclaw_runner/src/runtime.rs @@ -101,8 +101,8 @@ pub const REBORN_TOOL_DISCLOSURE_ENV: &str = "REBORN_TOOL_DISCLOSURE"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub enum ToolDisclosureMode { - #[default] Off, + #[default] Bridged, } @@ -125,10 +125,9 @@ impl ToolDisclosureMode { } } - /// Progressive tool disclosure defaults **off** — an unset, empty, or - /// unrecognized `REBORN_TOOL_DISCLOSURE` leaves the request path - /// byte-identical to the pre-disclosure behavior. Only an explicit - /// `REBORN_TOOL_DISCLOSURE=bridged` opts into the bridged path. + /// Progressive tool disclosure defaults to bridged for unset or empty + /// configuration. Explicit `off` remains the rollback path, while + /// unrecognized values fail closed to `Off`. fn from_raw(raw: Option<&str>) -> Self { match raw { Some(value) if value.eq_ignore_ascii_case("off") => Self::Off, @@ -137,12 +136,12 @@ impl ToolDisclosureMode { tracing::debug!( target: "ironclaw::reborn::runtime", env = REBORN_TOOL_DISCLOSURE_ENV, - "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to default Off" + "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to Off" ); Self::Off } - // unset / empty -> default off (byte-identical request path). - _ => Self::Off, + // Unset / empty follows the production default. + _ => Self::default(), } } @@ -992,20 +991,20 @@ mod tests { }; #[test] - fn tool_disclosure_mode_defaults_off_with_bridged_opt_in() { + fn tool_disclosure_mode_defaults_bridged_with_off_kill_switch() { use super::ToolDisclosureMode; - assert_eq!(ToolDisclosureMode::default(), ToolDisclosureMode::Off); - // Default off: unset / empty / unrecognized resolve to Off so the - // request path stays byte-identical. Only explicit `bridged` opts in. + assert_eq!(ToolDisclosureMode::default(), ToolDisclosureMode::Bridged); + // Unset / empty use the production default. Invalid configuration and + // explicit `off` fail closed to the rollback path. // `is_bridged()` is what gates whether the gateway attaches the decorator. assert!( - !ToolDisclosureMode::from_raw(None).is_bridged(), - "unset must default OFF (byte-identical request path)" + ToolDisclosureMode::from_raw(None).is_bridged(), + "unset must enable progressive disclosure" ); - assert!(!ToolDisclosureMode::from_raw(Some("")).is_bridged()); + assert!(ToolDisclosureMode::from_raw(Some("")).is_bridged()); assert!( !ToolDisclosureMode::from_raw(Some("garbage")).is_bridged(), - "unrecognized values must fall back to the default Off" + "unrecognized values must fail closed to Off" ); assert!(ToolDisclosureMode::from_raw(Some("bridged")).is_bridged()); assert!(ToolDisclosureMode::from_raw(Some("BRIDGED")).is_bridged()); diff --git a/tests/integration/support/builder.rs b/tests/integration/support/builder.rs index 19ba7207a15..5d06661ece4 100644 --- a/tests/integration/support/builder.rs +++ b/tests/integration/support/builder.rs @@ -453,6 +453,13 @@ impl RebornIntegrationHarnessBuilder { self } + /// Exercise the production enum default without making the general + /// integration harness depend on ambient process configuration. + pub fn with_tool_disclosure_production_default(mut self) -> Self { + self.tool_disclosure = ToolDisclosureMode::default(); + self + } + /// Force `ToolDisclosureMode::Off` for this harness's underlying group, /// bypassing `REBORN_TOOL_DISCLOSURE`/`from_env()`. Use this to pin a /// negative-control test's mode explicitly rather than relying on the diff --git a/tests/integration/tool_disclosure.rs b/tests/integration/tool_disclosure.rs index a71f3ca0f98..24e5b54ce3f 100644 --- a/tests/integration/tool_disclosure.rs +++ b/tests/integration/tool_disclosure.rs @@ -186,6 +186,35 @@ async fn explicit_off_surfaces_the_flat_wide_tool_list() { } } +/// The production default enables progressive disclosure for a wide catalog. +/// The unit contract in `ironclaw_runner::runtime` separately proves that an +/// unset or empty environment value resolves to this default. +#[tokio::test] +async fn production_default_defers_wide_catalog_to_bridge_meta_tools() { + let harness = RebornIntegrationHarness::test_default() + .with_tool_disclosure_production_default() + .with_github_issue_tools() + .script([RebornScriptedReply::text("done")]) + .build() + .await + .expect("production-default disclosure harness builds"); + + harness.submit_turn("hello").await.expect("turn completes"); + + for bridge in [TOOL_SEARCH_NAME, TOOL_DESCRIBE_NAME, TOOL_CALL_NAME] { + harness + .assert_model_tools_contains(bridge) + .await + .unwrap_or_else(|error| { + panic!("production default must advertise bridge {bridge:?}: {error}") + }); + } + harness + .assert_model_tools_excludes(FLAT_GITHUB_TOOL_NAME) + .await + .expect("production default defers the flat wide catalog"); +} + /// General harnesses pin Off rather than inheriting the production environment, /// so unrelated integration tests remain stable when the production default can /// safely change after the authorization prerequisite lands. From 373071b76562d89059cced03a4f2a99055cec6f3 Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 15:56:08 +0300 Subject: [PATCH 2/8] test(reborn): pin scripted QA tool disclosure --- tests/reborn_qa_routines.rs | 1 + tests/support/reborn_parity_qa/binary_e2e.rs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/reborn_qa_routines.rs b/tests/reborn_qa_routines.rs index c6ef83f01ae..4dfd178a861 100644 --- a/tests/reborn_qa_routines.rs +++ b/tests/reborn_qa_routines.rs @@ -283,6 +283,7 @@ async fn build_qa_fire_runtime( .expect("local-yolo runtime input") .with_local_runtime_confirmed_host_home_root(host_home_root); let input = RebornRuntimeInput::from_build_input(input) + .with_tool_disclosure(ironclaw_runner::runtime::ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: QA_TENANT.to_string(), agent_id: QA_AGENT.to_string(), diff --git a/tests/support/reborn_parity_qa/binary_e2e.rs b/tests/support/reborn_parity_qa/binary_e2e.rs index a3f8bdadaea..5b188da6e60 100644 --- a/tests/support/reborn_parity_qa/binary_e2e.rs +++ b/tests/support/reborn_parity_qa/binary_e2e.rs @@ -57,7 +57,7 @@ use ironclaw_runner::{ milestone_events::{DurableLoopHostMilestoneScope, DurableLoopHostMilestoneSink}, runtime::{ DefaultPlannedRuntimeConfig, DefaultPlannedRuntimeParts, ProcessRuntimeSystem, - RebornRuntimeLoopComposition, build_default_planned_runtime, + RebornRuntimeLoopComposition, ToolDisclosureMode, build_default_planned_runtime, }, }; use ironclaw_threads::{ @@ -839,6 +839,9 @@ impl RebornBinaryE2EHarness { // minutes of backoff. Mirrors the integration group harness's // IRONCLAW_REBORN_MODEL_AVAILABILITY_RETRY_ATTEMPTS=1 pin. planned_model_availability_retry_attempts: std::num::NonZeroU32::new(1), + // Scripted replay steps assert exact flat tool surfaces. Keep that + // test contract explicit instead of inheriting production's mode. + tool_disclosure: ToolDisclosureMode::Off, ..DefaultPlannedRuntimeConfig::default() }; if exposes_spawn_subagent { From 2413012b5fbf63a2d1f0be2bce8140ba18b094b2 Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 16:31:33 +0300 Subject: [PATCH 3/8] test(reborn): pin composition tool surfaces --- .../ironclaw_reborn_composition/src/runtime/tests/core.rs | 7 +++++++ .../src/runtime/tests/outbound_delivery.rs | 3 +++ 2 files changed, 10 insertions(+) diff --git a/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs b/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs index ccd3b4ae1db..ffc30cd5d88 100644 --- a/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs +++ b/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs @@ -547,6 +547,7 @@ use ironclaw_product::{ RebornViewPage, RebornViewQuery, SUBMIT_TURN_COMMAND, approval_gate_ref, }; use ironclaw_product::{ProductOutboundPayload, ProductProjectionItem}; +use ironclaw_runner::runtime::ToolDisclosureMode; use ironclaw_skills::SkillTrust; use ironclaw_threads::{ AppendToolResultReferenceRequest, EnsureThreadRequest, LoadContextMessagesRequest, MessageKind, @@ -2810,6 +2811,7 @@ async fn build_reborn_runtime_wires_trajectory_observer_through_unified_runtime( )), ))), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-observer-reject-tenant".to_string(), agent_id: "runtime-observer-reject-agent".to_string(), @@ -3566,6 +3568,7 @@ async fn send_user_message_until_gate_returns_blocked_on_auth_gate() { ) .with_local_runtime_confirmed_host_home_root(host_home), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-auth-gate-tenant".to_string(), agent_id: "runtime-auth-gate-agent".to_string(), @@ -3892,6 +3895,7 @@ async fn standalone_runtime_exposes_host_runtime_capabilities_to_model_calls() { ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-tools-tenant".to_string(), agent_id: "runtime-tools-agent".to_string(), @@ -4036,6 +4040,7 @@ async fn standalone_runtime_forwards_tool_call_trajectory_to_raw_observer() { ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-trajectory-tenant".to_string(), agent_id: "runtime-trajectory-agent".to_string(), @@ -4112,6 +4117,7 @@ async fn standalone_runtime_safe_preview_observer_receives_bounded_payload() { ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-preview-tenant".to_string(), agent_id: "runtime-preview-agent".to_string(), @@ -6455,6 +6461,7 @@ async fn multi_tool_call_response_survives_surface_change_mid_register() { ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-multi-tool-surface-tenant".to_string(), agent_id: "runtime-multi-tool-surface-agent".to_string(), diff --git a/crates/ironclaw_reborn_composition/src/runtime/tests/outbound_delivery.rs b/crates/ironclaw_reborn_composition/src/runtime/tests/outbound_delivery.rs index 23a222d2308..efde9d87dcf 100644 --- a/crates/ironclaw_reborn_composition/src/runtime/tests/outbound_delivery.rs +++ b/crates/ironclaw_reborn_composition/src/runtime/tests/outbound_delivery.rs @@ -13,6 +13,7 @@ use ironclaw_outbound::{ OutboundDeliveryTargetSummary, OutboundError, }; use ironclaw_product::RebornOutboundDeliveryTargetId; +use ironclaw_runner::runtime::ToolDisclosureMode; use ironclaw_threads::{LoadContextMessagesRequest, MessageKind, ThreadHistoryRequest}; use ironclaw_turns::{ ReplyTargetBindingRef, TurnStatus, @@ -267,6 +268,7 @@ async fn production_reply_attachment_capability_registers_durable_run_intent() { "runtime-reply-attachment-owner", root.path().join("standalone"), )) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-reply-attachment-tenant".to_string(), agent_id: "runtime-reply-attachment-agent".to_string(), @@ -343,6 +345,7 @@ async fn standalone_runtime_selects_outbound_delivery_target_before_trigger_crea ) .with_local_runtime_confirmed_host_home_root(host_home), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-outbound-trigger-tenant".to_string(), agent_id: "runtime-outbound-trigger-agent".to_string(), From 213e8f085f5d8adad3b7dc58193d12133fea01eb Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 16:46:20 +0300 Subject: [PATCH 4/8] test(reborn): pin hook runtime tool surface --- crates/ironclaw_reborn_composition/tests/runtime.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/ironclaw_reborn_composition/tests/runtime.rs b/crates/ironclaw_reborn_composition/tests/runtime.rs index 2d778e45460..4cf845fd7c5 100644 --- a/crates/ironclaw_reborn_composition/tests/runtime.rs +++ b/crates/ironclaw_reborn_composition/tests/runtime.rs @@ -28,6 +28,7 @@ use ironclaw_reborn_composition::{ use ironclaw_reborn_composition::{ RebornCompositionProfile, local_runtime_build_input_with_options, }; +use ironclaw_runner::runtime::ToolDisclosureMode; use ironclaw_turns::run_profile::{ LoopCapabilityPort, ProviderToolCall, RegisterProviderToolCallRequest, }; @@ -556,6 +557,7 @@ async fn build_reborn_runtime_wires_third_party_hooks_when_enabled() { ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-hooks-tenant".to_string(), agent_id: "runtime-hooks-agent".to_string(), From 5886468f06bc4414ea008b7ded2651269b7aa87f Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 16:49:51 +0300 Subject: [PATCH 5/8] test(reborn): make flat tool fixtures explicit --- crates/ironclaw_reborn_composition/src/runtime/tests/core.rs | 1 + crates/ironclaw_reborn_composition/tests/runtime.rs | 1 + crates/ironclaw_reborn_composition/tests/webui_v2_e2e.rs | 4 ++++ 3 files changed, 6 insertions(+) diff --git a/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs b/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs index ffc30cd5d88..405eb8a6724 100644 --- a/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs +++ b/crates/ironclaw_reborn_composition/src/runtime/tests/core.rs @@ -4976,6 +4976,7 @@ async fn standalone_runtime_maps_workspace_to_configured_root() { .with_local_runtime_workspace_root(workspace_root.path().to_path_buf()) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: "runtime-workspace-tenant".to_string(), agent_id: "runtime-workspace-agent".to_string(), diff --git a/crates/ironclaw_reborn_composition/tests/runtime.rs b/crates/ironclaw_reborn_composition/tests/runtime.rs index 4cf845fd7c5..e5a1b0d5d3d 100644 --- a/crates/ironclaw_reborn_composition/tests/runtime.rs +++ b/crates/ironclaw_reborn_composition/tests/runtime.rs @@ -1008,6 +1008,7 @@ async fn standalone_test_support_interaction_services_use_supplied_turn_coordina ) .with_runtime_policy(standalone_runtime_policy()), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: format!("{tag}-tenant"), agent_id: format!("{tag}-agent"), diff --git a/crates/ironclaw_reborn_composition/tests/webui_v2_e2e.rs b/crates/ironclaw_reborn_composition/tests/webui_v2_e2e.rs index f0257f9d756..42c81a04ef1 100644 --- a/crates/ironclaw_reborn_composition/tests/webui_v2_e2e.rs +++ b/crates/ironclaw_reborn_composition/tests/webui_v2_e2e.rs @@ -45,6 +45,7 @@ use ironclaw_reborn_composition::{ OAuthClientConfig, PollSettings, RebornRuntime, RebornRuntimeIdentity, RebornRuntimeInput, build_reborn_runtime, }; +use ironclaw_runner::runtime::ToolDisclosureMode; use ironclaw_turns::run_profile::{ CapabilityCallCandidate, LoopCapabilityPort, ProviderToolCall, RegisterProviderToolCallRequest, }; @@ -698,6 +699,7 @@ async fn build_harness_at_with_runtime_owner_auth_user_and_google_oauth_backend( .with_vendor_oauth_client(ironclaw_auth::GOOGLE_PROVIDER_ID, google_oauth_backend); } let input = RebornRuntimeInput::from_build_input(build_input) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: TENANT.to_string(), agent_id: AGENT.to_string(), @@ -765,6 +767,7 @@ async fn build_two_user_harness( .with_runtime_policy(policy) .with_bundled_first_party_for_test(), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: TENANT.to_string(), agent_id: AGENT.to_string(), @@ -1977,6 +1980,7 @@ mod operator_llm_config { .with_runtime_policy(local_host_effective_policy()) .with_bundled_first_party_for_test(), ) + .with_tool_disclosure(ToolDisclosureMode::Off) .with_identity(RebornRuntimeIdentity { tenant_id: TENANT.to_string(), agent_id: AGENT.to_string(), From ace0caeabfa968a82aeba7d11d9600d9d72c928a Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 17:35:51 +0300 Subject: [PATCH 6/8] test(e2e): pin flat disclosure fixtures --- crates/ironclaw_runner/src/runtime.rs | 2 +- tests/e2e/reborn_webui_harness.py | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/ironclaw_runner/src/runtime.rs b/crates/ironclaw_runner/src/runtime.rs index 987e7fa9334..9f86d3b7cac 100644 --- a/crates/ironclaw_runner/src/runtime.rs +++ b/crates/ironclaw_runner/src/runtime.rs @@ -136,7 +136,7 @@ impl ToolDisclosureMode { tracing::debug!( target: "ironclaw::reborn::runtime", env = REBORN_TOOL_DISCLOSURE_ENV, - "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to Off" + "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to default Off" ); Self::Off } diff --git a/tests/e2e/reborn_webui_harness.py b/tests/e2e/reborn_webui_harness.py index a197c6bc585..3497c42707d 100644 --- a/tests/e2e/reborn_webui_harness.py +++ b/tests/e2e/reborn_webui_harness.py @@ -493,6 +493,10 @@ async def start_reborn_webui_v2_server( "IRONCLAW_REBORN_PROFILE": profile, "IRONCLAW_REBORN_WEBUI_TOKEN": REBORN_V2_AUTH_TOKEN, "IRONCLAW_REBORN_WEBUI_USER_ID": USER_ID, + # Recorded provider fixtures assert the pre-disclosure request + # shape. Keep this shared deterministic harness explicit rather + # than inheriting the production default. + "REBORN_TOOL_DISCLOSURE": "off", "MOCK_LLM_API_KEY": "mock-api-key", "NO_PROXY": "127.0.0.1,localhost,::1", "no_proxy": "127.0.0.1,localhost,::1", From 6b28ce5e1b1c1186a84d2ad611c9859472cd9a97 Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Fri, 31 Jul 2026 18:05:13 +0300 Subject: [PATCH 7/8] test(e2e): pin responses fixtures to flat tools --- tests/e2e/scenarios/test_reborn_responses_api.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/scenarios/test_reborn_responses_api.py b/tests/e2e/scenarios/test_reborn_responses_api.py index 01d00874754..ffa19001109 100644 --- a/tests/e2e/scenarios/test_reborn_responses_api.py +++ b/tests/e2e/scenarios/test_reborn_responses_api.py @@ -121,6 +121,9 @@ async def reborn_responses_server( "IRONCLAW_REBORN_PROFILE": PROFILE, "IRONCLAW_REBORN_WEBUI_TOKEN": REBORN_V2_AUTH_TOKEN, "IRONCLAW_REBORN_WEBUI_USER_ID": USER_ID, + # External-tool response fixtures assert the pre-disclosure tool + # surface; keep this deterministic server explicit. + "REBORN_TOOL_DISCLOSURE": "off", "MOCK_LLM_API_KEY": "mock-api-key", "NO_PROXY": "127.0.0.1,localhost,::1", "no_proxy": "127.0.0.1,localhost,::1", From 3d6555d0cdda9013f1aae295d21489f00cbb076a Mon Sep 17 00:00:00 2001 From: serrrfirat Date: Wed, 5 Aug 2026 13:50:16 +0300 Subject: [PATCH 8/8] chore(reborn): clarify tool disclosure wording --- crates/ironclaw_loop_host/src/tool_disclosure_mode.rs | 2 +- tests/CLAUDE.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/ironclaw_loop_host/src/tool_disclosure_mode.rs b/crates/ironclaw_loop_host/src/tool_disclosure_mode.rs index 9f456f12f62..c00ad417a97 100644 --- a/crates/ironclaw_loop_host/src/tool_disclosure_mode.rs +++ b/crates/ironclaw_loop_host/src/tool_disclosure_mode.rs @@ -48,7 +48,7 @@ impl ToolDisclosureMode { tracing::debug!( target: "ironclaw::reborn::runtime", env = REBORN_TOOL_DISCLOSURE_ENV, - "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to default Off" + "unrecognized REBORN_TOOL_DISCLOSURE value; falling back to Off" ); Self::Off } diff --git a/tests/CLAUDE.md b/tests/CLAUDE.md index ba2b0cbd101..d469f783e2f 100644 --- a/tests/CLAUDE.md +++ b/tests/CLAUDE.md @@ -185,7 +185,7 @@ One thread, whole real turn. Grouped by what the user experiences. | Web search/fetch runs the real Exa MCP handshake | `web_access.rs` | | Outbound HTTP crosses the real security pipeline (network policy + leak scan) | `real_egress_pipeline.rs` | | Tools marked host-internal are never advertised to the model, and calls to them are rejected | `extension_visibility.rs`, `surface_disclosure.rs` | -| Bridged tool disclosure mode and the production default reach the decorator wiring | `tool_disclosure.rs` | +| With a large tool catalog, bridged mode and the production default expose `tool_search`, `tool_describe`, and `tool_call` instead of flat tools | `tool_disclosure.rs` | | A capability whose lease expires mid-dispatch does not wedge the run | `lease_wedge.rs` | | Attachments the user uploads are read back byte-for-byte by the model | `attach.rs` | | Skill activation injects skill context into a real turn | `skill_activate.rs` |