[Bug]: Successful "skill" tool calls shown as "blocked" when skill content contains trigger keywords (heuristic false positive)
Before submitting
- I searched existing issues and did not find the same problem.
- I have read the relevant documentation (e.g., the README) but could not resolve this issue.
Problem
With the opencode provider, when the agent loads a skill via opencode's skill tool, the tool call succeeds — but Claudian renders the tool card as blocked (orange shield-off icon, aria-label Status: blocked) whenever the loaded skill's SKILL.md happens to contain one of the keywords "outside the vault", "access denied", "user denied", or "approval".
This is a false positive of the keyword heuristic that scans tool-call result text: the heuristic also runs on successful results. opencode's skill tool returns the full SKILL.md text as its output, so any trigger word inside the skill's documentation makes a successful load appear blocked.
Root cause (minified main.js, v2.0.41):
// keyword heuristic over tool result text
function XW(t, e) {
let n = kr(t, { fallbackIndent: 2 }).toLowerCase();
return !!(n.includes("outside the vault") || n.includes("access denied") ||
n.includes("user denied") || n.includes("approval") ||
e && n.includes("deny"));
}
// handleToolResult: status assignment
e.isError ? d.status = "error"
: !hde(d.name) && c ? d.status = "blocked" // c = XW(resultText, isError)
: d.status = "completed";The exclusion list only contains ["EnterPlanMode", "ExitPlanMode", "AskUserQuestion"] — the skill tool is not excluded.
Concrete example: the superpowers skill dispatching-parallel-agents contains the word "approval" in its example text ("Tool approval flow", tool-approval-race-conditions.test.ts). Loading it succeeds (opencode evaluates permission=skill pattern=dispatching-parallel-agents → allow, and the agent continues using the skill), but Claudian shows a blocked card.
Impact beyond cosmetics: tool status feeds the transcript builder used for session history rebuild / resume, which treats blocked the same as error:
let r = n === "error" || n === "blocked";
...
if (!r) return `[Tool ${t.name}${o} status=${n}]`;
...
return `[Tool ${t.name}${o} status=${n}] error: ${a}`; // + up to 500 chars of resultSo on a history rebuild/resume, a successfully loaded skill is serialized to the model as [Tool skill ... status=blocked] error: <first 500 chars of SKILL.md> — presenting a succeeded call as a failure.
Steps to reproduce
- Use the opencode provider with a skill pack installed (e.g. superpowers, which registers its skills via
skills.paths). - In a Claudian chat, ask the agent something that makes it load the
dispatching-parallel-agentsskill (or any skill whoseSKILL.mdcontains "approval" / "access denied" / "user denied" / "outside the vault"). - Observe the
skilltool card. - The card shows the orange shield-off blocked status, although the skill loaded successfully and the agent proceeds to use it.
Expected behavior
Successful tool calls should render as completed. The blocked heuristic should only fire for genuinely denied/failed results. Possible fixes:
- apply the keyword matching only when the result is an error; and/or
- match more specific phrases (e.g. "approval required", "pending approval") instead of the bare word "approval"; and/or
- exclude the
skilltool from the heuristic, like the existingEnterPlanMode/ExitPlanMode/AskUserQuestionexclusions.
Environment
- Claudian version: 2.0.41
- Obsidian version: 1.11.5
- Operating system: Windows 11
- Provider: opencode
- Provider CLI version: 1.18.18
- Provider CLI installation method: npm global install (
npm i -g opencode-ai)
Logs and other evidence
opencode server log confirms the skill loaded with permission allowed and the session continued normally:
level=INFO message=evaluated permission=skill pattern=dispatching-parallel-agents action.permission=* action.action=allow action.pattern=*Scanned all 14 superpowers skills for the trigger keywords: only
brainstorminganddispatching-parallel-agentscontain "approval" — exactly the two that render as blocked; all other skills render as completed.Related UI code:
.claudian-tool-status.status-blocked(orange) instyles.css; icon map{completed: "check", error: "x", blocked: "shield-off"}.
Additional context
The skill actually loads and works in the session — the mislabel is confusing (it looks as if opencode refused to load the skill), and per the transcript path above it can also pollute rebuilt/resumed history sent to the model.
Source: YishenTu/claudian