Summary
audit-skill-visibility's --installed mode counts plugins that are installed but absent from enabledPlugins as enabled, via its defaultEnabled fallback. On a machine with a large plugin cache this inflates the fleet roughly 3x. Because the fleet is the denominator for the listing-budget contest, every overflow_chars, starved_count, and per-skill starvation.verdict in the report is wrong by that factor.
Observed
Machine: Claude Code 2.1.270, Windows, ~/.claude user scope, 1M-context session.
$ audit_skill_visibility.py --installed --render json --context-window 1000000 --bytes-per-token 4
listing.demand_chars = 300868
listing.budget_chars = 200000
listing.overflow_chars = 100868
listing.starved_count = 467
fleet.plugins_resolved = 204
Bucketing the same run's skills[] by how enabledPlugins resolves each plugin:
| bucket |
skills |
competing demand |
explicit true (63 plugins) |
216 |
108,992 |
explicit false (37 plugins) |
193 |
0 |
absent from enabledPlugins (~104 plugins) |
752 |
190,015 |
The third bucket is 63% of reported demand. It should be zero.
Why the third bucket does not load
Three independent lines of evidence:
-
A live session's own in-context skill listing contains no skill from any absent plugin. Every plugin skill present in the listing comes from the explicit-true set. Spot-checked: posthog, vercel, wix, netlify-skills, huggingface-skills, fastly-agent-toolkit, example-skills — 275 skills, zero appearances.
-
Truncation cannot explain absence. SKILL.md states names always survive the budget and only descriptions are shed. An absent name therefore means the skill was never loaded at all.
-
Per-plugin split matches exactly for enabled plugins. The audit reports claude-ops as 9 competing + 3 exempt-user-only. The live listing shows exactly those 9 at full description length, with lanes, plugins, and setup absent per their disable-model-invocation frontmatter. So the engine is correct for plugins it resolves as enabled — the defect is confined to the absent case.
Root cause
claude-plugins-official's .claude-plugin/marketplace.json declares defaultEnabled on none of its 295 entries. With no marketplace value and no plugin.json value, the resolver falls through to the product default (enabled) and marks the skill model-reachable.
That fallback is right for a plugin the user installed and then left alone, but not for one that is merely present in installed_plugins.json. The reachability model has no state distinguishing the two, so "in the cache" reads as "loaded."
Impact
Beyond wrong numbers, the report actively misleads. Acting on this run's output, I nearly disabled seven vendor plugins to reclaim a reported 96,058 chars of "pure cost, zero visibility." They cost nothing; the saving would have been zero. A report whose remedy is a no-op is worse than no report.
The withheld machinery is doing its job elsewhere in this run — this case simply is not routed into it.
Suggested fix
Give absence from every enabledPlugins scope its own reachability value rather than routing it through defaultEnabled. Candidates: a new not-enabled value, or withheld with a reason naming the scopes checked.
Whatever the value, such rows should contribute no demand_chars, the same way exempt-hidden does today — the existing eligibility machinery already has the right shape, this case just is not mapped onto it.
Worth confirming against the product first: an explicit false and a plain absence may not be the same thing to Claude Code, and the fix should encode whichever it actually does. A live session's own listing is the cheapest oracle.
Correct figures for this machine
For the record, counting only loaded plugins:
|
|
| Demand |
108,992 chars (~27k tokens) across 166 competing skills |
Budget @ skillListingBudgetFraction: 0.05, 1M / 4 bpt |
200,000 — fits |
Budget @ 0.01 (documented default), 1M |
40,000 — would overflow by 68,992 |
Budget @ 0.05, 200k window |
40,000 — overflows by 68,992 |
Two things follow that the inflated run obscured: the 0.05 setting is load-bearing rather than precautionary, and the only real remaining overflow is on 200k-window models, i.e. Sonnet/Haiku subagents.
Environment
- Claude Code 2.1.270, Windows 11, Git Bash
~/.claude/settings.json: skillListingBudgetFraction: 0.05, skillListingMaxDescChars unset (default 1,536)
enabledPlugins: 100 entries (63 true, 37 false); installed_plugins.json: 262 manifest entries → 204 plugins resolved
SLASH_COMMAND_TOOL_CHAR_BUDGET unset; CLAUDE_CODE_DISABLE_1M_CONTEXT unset
Summary
audit-skill-visibility's--installedmode counts plugins that are installed but absent fromenabledPluginsas enabled, via itsdefaultEnabledfallback. On a machine with a large plugin cache this inflates the fleet roughly 3x. Because the fleet is the denominator for the listing-budget contest, everyoverflow_chars,starved_count, and per-skillstarvation.verdictin the report is wrong by that factor.Observed
Machine: Claude Code 2.1.270, Windows,
~/.claudeuser scope, 1M-context session.Bucketing the same run's
skills[]by howenabledPluginsresolves each plugin:true(63 plugins)false(37 plugins)enabledPlugins(~104 plugins)The third bucket is 63% of reported demand. It should be zero.
Why the third bucket does not load
Three independent lines of evidence:
A live session's own in-context skill listing contains no skill from any absent plugin. Every plugin skill present in the listing comes from the explicit-
trueset. Spot-checked:posthog,vercel,wix,netlify-skills,huggingface-skills,fastly-agent-toolkit,example-skills— 275 skills, zero appearances.Truncation cannot explain absence. SKILL.md states names always survive the budget and only descriptions are shed. An absent name therefore means the skill was never loaded at all.
Per-plugin split matches exactly for enabled plugins. The audit reports
claude-opsas 9 competing + 3exempt-user-only. The live listing shows exactly those 9 at full description length, withlanes,plugins, andsetupabsent per theirdisable-model-invocationfrontmatter. So the engine is correct for plugins it resolves as enabled — the defect is confined to the absent case.Root cause
claude-plugins-official's.claude-plugin/marketplace.jsondeclaresdefaultEnabledon none of its 295 entries. With no marketplace value and noplugin.jsonvalue, the resolver falls through to the product default (enabled) and marks the skillmodel-reachable.That fallback is right for a plugin the user installed and then left alone, but not for one that is merely present in
installed_plugins.json. The reachability model has no state distinguishing the two, so "in the cache" reads as "loaded."Impact
Beyond wrong numbers, the report actively misleads. Acting on this run's output, I nearly disabled seven vendor plugins to reclaim a reported 96,058 chars of "pure cost, zero visibility." They cost nothing; the saving would have been zero. A report whose remedy is a no-op is worse than no report.
The
withheldmachinery is doing its job elsewhere in this run — this case simply is not routed into it.Suggested fix
Give absence from every
enabledPluginsscope its own reachability value rather than routing it throughdefaultEnabled. Candidates: a newnot-enabledvalue, orwithheldwith a reason naming the scopes checked.Whatever the value, such rows should contribute no
demand_chars, the same wayexempt-hiddendoes today — the existing eligibility machinery already has the right shape, this case just is not mapped onto it.Worth confirming against the product first: an explicit
falseand a plain absence may not be the same thing to Claude Code, and the fix should encode whichever it actually does. A live session's own listing is the cheapest oracle.Correct figures for this machine
For the record, counting only loaded plugins:
skillListingBudgetFraction: 0.05, 1M / 4 bpt0.01(documented default), 1M0.05, 200k windowTwo things follow that the inflated run obscured: the
0.05setting is load-bearing rather than precautionary, and the only real remaining overflow is on 200k-window models, i.e. Sonnet/Haiku subagents.Environment
~/.claude/settings.json:skillListingBudgetFraction: 0.05,skillListingMaxDescCharsunset (default 1,536)enabledPlugins: 100 entries (63true, 37false);installed_plugins.json: 262 manifest entries → 204 plugins resolvedSLASH_COMMAND_TOOL_CHAR_BUDGETunset;CLAUDE_CODE_DISABLE_1M_CONTEXTunset