{"record":{"id":"c5dcd9a2ced29ba7","repo":"Hmbown/CodeWhale","slug":"tool-name-is-not-available-to-this-read-only-wor","errorCode":null,"errorMessage":"Tool {name} is not available to this read-only worker because its process path does not share the hardened evidence boundary. Use read/search, classifier-bounded bash reads, or the verifier's bounded Run tool instead.","messagePattern":"Tool (.+?) is not available to this read-only worker because its process path does not share the hardened evidence boundary\\. Use read/search, classifier-bounded bash reads, or the verifier's bounded Run tool instead\\.","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tools/subagent/mod.rs","lineNumber":17483,"sourceCode":"        match &self.allowed_tools {\n            None => Vec::new(),\n            Some(list) => list\n                .iter()\n                .filter(|name| !is_tool_search_tool(name) && !self.registry.contains(name))\n                .cloned()\n                .collect(),\n        }\n    }\n\n    async fn execute_full(\n        &self,\n        agent_id: &str,\n        tool_id: &str,\n        name: &str,\n        input: Value,\n    ) -> Result<RichToolResult> {\n        if self.role_blocks_unhardened_process_tool(name) {\n            return Err(anyhow!(\n                \"Tool {name} is not available to this read-only worker because its process path does not share the hardened evidence boundary. Use read/search, classifier-bounded bash reads, or the verifier's bounded Run tool instead.\"\n            ));\n        }\n        let action = input.get(\"action\").and_then(Value::as_str);\n        if matches!(&self.agent_type, FleetRole::Scout | FleetRole::Reviewer)\n            && name == \"Web\"\n            && !matches!(action, Some(\"search\" | \"fetch\"))\n        {\n            return Err(anyhow!(\n                \"Tool Web is limited to search/fetch in the read-only evidence profile\"\n            ));\n        }\n        // Catalog shaping is not authority. `agent` clears both name-keyed\n        // gates below by design, so the per-action gate has to be repeated\n        // here or a hand-written call would reach an action the role's own\n        // catalog withheld.\n        if name == \"agent\"\n            && matches!(parse_agent_tool_action(&input), Ok(AgentToolAction::Claim))","sourceCodeStart":17465,"sourceCodeEnd":17501,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/crates/tui/src/tools/subagent/mod.rs#L17465-L17501","documentation":"Read-only Fleet roles (e.g. scout/reviewer workers) run with a hardened evidence boundary: their process path cannot execute unhardened process tools. `role_blocks_unhardened_process_tool` intercepts such a call and rejects it, directing the worker to the read/search tools, classifier-bounded bash reads, or the verifier's bounded Run tool.","triggerScenarios":"A read-only role sub-agent invokes a process-execution tool (e.g. `Bash`/`Run` in unhardened form) whose process path does not share the hardened evidence boundary; detected by `role_blocks_unhardened_process_tool(name)` before dispatch.","commonSituations":"The delegating prompt tells a scout to run arbitrary shell commands; a generic agent loops over its full tool catalog without honoring role restrictions; a child attempts the same tool a parent (with write authority) just used successfully.","solutions":["Use the read/search tools or classifier-bounded bash reads instead of the unhardened process tool.","Route the execution through the verifier's bounded Run tool, which shares the hardened boundary.","Re-delegate the task to an `implement` or `general` role that has write/execute authority.","Update the orchestrating prompt/tool catalog so read-only workers are not offered the unhardened tool."],"exampleFix":"// before\n// read-only worker:\nBash.run(\"cat /evidence/log.txt\")\n// after\nRead.read(\"/evidence/log.txt\")","handlingStrategy":"validation","validationCode":"const UNHARDENED = ['Bash', 'Run'];\nif (role === 'scout' || role === 'reviewer') {\n  if (UNHARDENED.includes(toolName)) throw new Error('use read/search or bounded Run instead');\n}","typeGuard":"const isReadOnlyRole = (t) => ['scout','reviewer'].includes(t);","tryCatchPattern":"try { result = await callTool(name, input); } catch (e) { if (String(e).includes('hardened evidence boundary')) { result = await readOrBoundedAlternative(name, input); } }","preventionTips":["Match tool choice to the role: read-only workers use read/search or verifier-bounded Run","Do not copy parent tool usage patterns into read-only children","State execution constraints in the delegation prompt"],"tags":["subagent","permissions","read-only","security"],"backgroundTag":"permission-denied","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}