diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Rescan/RescanButton.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Rescan/RescanButton.tsx index cee13a84..cfdc0abe 100644 --- a/src/apps/officer-web/Screens/Dashboard/Layout/Rescan/RescanButton.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Layout/Rescan/RescanButton.tsx @@ -35,11 +35,25 @@ export function RescanButton() { toast.success( `Rescanned items — ${c.tasks ?? 0} tasks, ${c.agents ?? 0} agents, ${c.tools ?? 0} tools, ${c.skills ?? 0} skills, ${c.processes ?? 0} processes, ${c.affordances ?? 0} affordances`, { - // Said here rather than left to be discovered: a rescan cannot reach a chat that is already - // open, because its system prompt was assembled when it started. - description: c.affordances - ? 'Affordances apply to new chats — an open one keeps the set it started with.' - : undefined, + // Two things said here rather than left to be discovered. + // + // A rescan cannot reach a chat that is already open — its system prompt was assembled when it + // started. And a SKILL.md with no `description` is COUNTED as a skill but never offered to an + // agent, because the loader skips it: a line that does not say when to reach for something + // costs context in every conversation and earns nothing. So a skill can be present, listed and + // editable while being invisible to every agent, and the only symptom is two numbers + // disagreeing where nobody is comparing them. + description: + [ + c.affordances || c.skillsOffered + ? 'Applies to new chats — an open one keeps what it started with.' + : null, + typeof c.skills === 'number' && typeof c.skillsOffered === 'number' && c.skillsOffered < c.skills + ? `${c.skills - c.skillsOffered} skill(s) have no description, so no agent is offered them.` + : null, + ] + .filter(Boolean) + .join(' ') || undefined, }, ); } catch {