Category G is the one Phase 2 category a consumer cannot execute. Its only overflow detector needs an
interactive TTY, so a headless or background run yields nothing while a documented headless detector
goes unmentioned; its ordered "cheapest first" lever list has its top two entries unreachable on a
plugin-skill roster and omits the lever upstream actually prescribes; it is the only category with no
checklist table, in a skill whose scripts/ directory is the precedent, and it never points at the
measurement engine this marketplace already ships; it never states the budget constant, so overflow is
not computable by hand even after a successful measurement; and no eval exercises it positively.
Rows
Evidence
B1 — the only detector named is TTY-bound
context/validation-categories.md:134-137, verbatim at HEAD:
- **Overflow check** — if `/doctor` reports dropped skill descriptions, the skill listing has exceeded
its budget and the least-invoked skills' trigger keywords are silenced (names still resolve;
auto-invocation degrades silently). `/doctor` needs an interactive TTY — prompt the user to run it.
Repos with large skill rosters overflow routinely
grep -c "debug" over the whole file → 0. SKILL.md:127 restates the same single route:
"G — Skill-listing budget: /doctor overflow check and trim levers".
The headless detector, https://code.claude.com/docs/en/skills.md, fetched 2026-08-11 via the repo's
verbatim raw-.md route (per #2185), line 943:
When the listing exceeds its budget, Claude Code also writes a warning to the debug log, visible with --debug.
Scoping correction the fix must respect: the evidence packet claimed two unmentioned
non-interactive detectors, the second being the /context Skills row. That framing is over-broad —
/context is a slash command evaluated inside an interactive session, not something a -p/background
run can invoke. Only --debug is genuinely headless. Add --debug as the headless route; /context
may be mentioned only as an interactive alternative to /doctor.
B2 — the levers are out of the consumer's reach
context/validation-categories.md:138-143, verbatim at HEAD:
- **Levers, cheapest first** — trim `description` / `when_to_use` frontmatter (key use case first;
1,536-char cap per entry), `skillOverrides: { <skill>: "name-only" }` in a contributor's
`settings.local.json` (does NOT apply to plugin skills), then `skillListingBudgetFraction` /
`SLASH_COMMAND_TOOL_CHAR_BUDGET` in project settings as a last resort (costs context every turn)
- **Recommend, don't apply the list** — `skillOverrides` is contributor-scoped; surface the candidate
least-invoked skills, leave the actual name-only list to the developer
grep -c "/plugin" over the whole file → 0. So the caveat correctly tells the operator that lever
(ii) is dead for plugin skills and then offers no replacement, and the ordered "cheapest first" list
degenerates to the entry it labels "last resort". The category also never measures roster composition
(plugin vs project vs user skills), which is the single input deciding which levers apply.
Both substitute-naming sentences confirmed verbatim on 2026-08-11 via the raw .md route:
B3 — G is prose where the skill's own precedent is a script, and the engine already exists
reference/audit-checklist.md:5-7, verbatim at HEAD:
Category G (skill-listing budget) has no table here — its checks are procedural and live in
[context/validation-categories.md](../context/validation-categories.md), which carries every
category's criteria.
Verified by reading every category, not only G. Table presence in audit-checklist.md: A, B
(B.1/B.2/B.3, B.4, B.5), C (C.1, C.2, C.3), D, E, F, H (plus a second table for effort:/model:
frontmatter), I — all present. G is the only Phase 2 category with no table, and the only letter
absent from the file's heading sequence (headings run A, B, C, D, E, F, H, I). The scripts/
precedent is real at HEAD: check-structure.sh, check-plugin-drift.sh, fix-plugin-drift.sh, each
with a paired .test.sh.
The remedy is wiring, not a build — this is the correction that matters most for whoever picks this
up. The item that produced this finding proposed shipping
scripts/check-skill-listing-budget.sh. That measurement already exists in this marketplace, as
plugins/skill-quality/scripts/check-listing-budget.sh (+ its .test.sh), surfaced as
skill-quality:check's listing-budget action, closing #1404.
That does not make this row fixed — Category G still has no table, still names only /doctor, and
still never points at the script — but it changes the work, and it exposes a gap neither the item nor
its packet stated: the two tools measure different populations. check-listing-budget.sh walks
this repository's skills roots (a marketplace publisher gate). Category G audits a consumer
machine's installed roster, which needs a cache walk, version resolution across version directories,
and enabledPlugins reading that the existing script has no step for. So G cannot simply call it. The
honest options are (a) point G at it as an in-repo proxy and say so, or (b) extend it with a cache-walk
mode. That distinction is the substance of the fix.
One platform trap in the item's list is falsified; do not carry it forward. The item states as fact
that a CRLF SKILL.md breaks awk '/^---$/' ("1 skill found instead of 219"). Not reproduced, at two
independent levels: no CR exists in the corpus (grep -lUP '\r' over all SKILL.md files in the
plugin cache and at repo HEAD → zero hits; .gitattributes pins * text=auto eol=lf, which forces LF
checkout on Windows regardless of core.autocrlf), and even given CRLF the stated mechanism does not
hold on this platform (GNU Awk 5.4.0 from Git for Windows strips the CR before the regex sees it). The
packet had already downgraded this to "plausible, not reproduced here" and the item's body dropped the
hedge. The reported symptom is better explained by the jq CRLF-on-stdout trap, which is
reproducible (jq -rn '"hello"' | od -c → h e l l o \r \n) and whose own description — CRs
overwriting the rendered line — produces exactly a "1 instead of N" appearance. Restate trap 1 as a
portability hazard on other awk builds, not as an observed symptom.
The remaining traps hold: jq CRLF on stdout (reproduced); process-spawn cost against the default Bash
timeout (measured at ~169 ms per spawn on this machine, which is the generalizable constant); and
version directories needing sort -V rather than lexical ordering (twelve plugins in the cache where
the two disagree, claude-config itself among them — but note that version directories are a property
of the installed cache, not of HEAD, so a script encoding this must be tested against fixtures, never
the live cache).
B4 — the constant is never stated
context/validation-categories.md:132-143 is the complete category. It names the per-entry cap
(1,536-char cap per entry) and both scaling settings (skillListingBudgetFraction /
SLASH_COMMAND_TOOL_CHAR_BUDGET) — and states no budget value and no formula.
grep -n "8000\|8,000\|40,000\|0\.01" over the file → no match. Confirmed by reading the whole
category, not by keyword absence alone.
Sources confirmed verbatim on 2026-08-11 via the raw .md route:
The arithmetic checks: 200,000 × 4 × 0.01 = 8,000, matching the documented fallback. Worth knowing
for whoever writes the sentence: plugins/skill-quality/scripts/check-listing-budget.sh already
carries this derivation in its header comment including the same doc quote, so the text can be cited
rather than re-derived.
B5 — no positive eval
plugins/claude-config/skills/audit/evals/evals.json at HEAD carries 10 cases. Every mention of
the category in the file is two lines, both inside eval 1 (scope-filter-permissions-only), both
negative:
evals.json:8: "… Categories C-I (MCP, hooks, plugins, env vars, skill-listing budget, model and effort settings, deep-link registration) are not run."
evals.json:12: "Does NOT run MCP, hooks, plugins, environment-variable, skill-listing-budget, model-and-effort, or deep-link-registration checks",
Established structurally rather than by line proximity — a jq filter over every case's
expected_output plus expectations for skill-listing|skill listing returns eval 1 and only
eval 1. Eval 5 (findings-table-output-contract, /audit all) does say "A full nine-category audit",
but all five of its expectations are about table shape and severity vocabulary and none asserts any
Category G behavior. (The three cases added by #2206 are Category B, not G.) The authors demonstrably
write this shape: eval 7 is a positive behavioral case for the adjacent Read-deny scope question.
Adjacent, deliberately not duplicated
Provenance
Severity: MED · Provenance: AUDITOR_VERIFIED (five rows; trap corrections and the route correction are the validation pass's own)
Origin: handoff-inbox item 20260810-225906-claude-config-category-g-unmeasurable
Ledger: .work/handoff-inbox-batch-4/ledgers/I3-claude-config-category-g.md § B1–B5
Verified against repo HEAD 9b34a82a. Line anchors re-derived at that commit — the ledger's anchors were taken at dacb8661, before #2206 shifted this file by 20 lines.
Category G is the one Phase 2 category a consumer cannot execute. Its only overflow detector needs an
interactive TTY, so a headless or background run yields nothing while a documented headless detector
goes unmentioned; its ordered "cheapest first" lever list has its top two entries unreachable on a
plugin-skill roster and omits the lever upstream actually prescribes; it is the only category with no
checklist table, in a skill whose
scripts/directory is the precedent, and it never points at themeasurement engine this marketplace already ships; it never states the budget constant, so overflow is
not computable by hand even after a successful measurement; and no eval exercises it positively.
Rows
plugins/claude-config/skills/audit/context/validation-categories.md:134-137—/doctoris the only overflow detector named, and it is TTY-bound; the documented headless--debugwarning is unmentioned, so the whole category yields nothing in the harness's own headless mode.plugins/claude-config/skills/audit/context/validation-categories.md:138-143— the lever list's top two entries are unreachable on a plugin-skill roster (the file carries thedoes NOT apply to plugin skillscaveat itself), and/plugin— the lever upstream prescribes as the substitute — appears nowhere in the file.plugins/claude-config/skills/audit/reference/audit-checklist.md:5-7— G is the only Phase 2 category with no checklist table, and it never points atplugins/skill-quality/scripts/check-listing-budget.sh, the measurement this marketplace already owns.plugins/claude-config/skills/audit/context/validation-categories.md:132-143— the budget value and formula are never stated, so "have I overflowed, marginally or catastrophically?" is unanswerable by hand.plugins/claude-config/skills/audit/evals/evals.json— no eval exercises Category G positively. Ten cases at HEAD; the only two mentions of the category are negative assertions inside eval 1 (scope-filter-permissions-only).Evidence
B1 — the only detector named is TTY-bound
context/validation-categories.md:134-137, verbatim at HEAD:grep -c "debug"over the whole file → 0.SKILL.md:127restates the same single route:"G — Skill-listing budget:
/doctoroverflow check and trim levers".The headless detector, https://code.claude.com/docs/en/skills.md, fetched 2026-08-11 via the repo's
verbatim raw-
.mdroute (per #2185), line 943:Scoping correction the fix must respect: the evidence packet claimed two unmentioned
non-interactive detectors, the second being the
/contextSkills row. That framing is over-broad —/contextis a slash command evaluated inside an interactive session, not something a-p/backgroundrun can invoke. Only
--debugis genuinely headless. Add--debugas the headless route;/contextmay be mentioned only as an interactive alternative to
/doctor.B2 — the levers are out of the consumer's reach
context/validation-categories.md:138-143, verbatim at HEAD:grep -c "/plugin"over the whole file → 0. So the caveat correctly tells the operator that lever(ii) is dead for plugin skills and then offers no replacement, and the ordered "cheapest first" list
degenerates to the entry it labels "last resort". The category also never measures roster composition
(plugin vs project vs user skills), which is the single input deciding which levers apply.
Both substitute-naming sentences confirmed verbatim on 2026-08-11 via the raw
.mdroute:skillOverridesrow): "Does not apply to plugin skills, which are managed through/plugin."skillOverrides. Manage those through/plugininstead."B3 — G is prose where the skill's own precedent is a script, and the engine already exists
reference/audit-checklist.md:5-7, verbatim at HEAD:Verified by reading every category, not only G. Table presence in
audit-checklist.md: A, B(B.1/B.2/B.3, B.4, B.5), C (C.1, C.2, C.3), D, E, F, H (plus a second table for
effort:/model:frontmatter), I — all present. G is the only Phase 2 category with no table, and the only letter
absent from the file's heading sequence (headings run A, B, C, D, E, F, H, I). The
scripts/precedent is real at HEAD:
check-structure.sh,check-plugin-drift.sh,fix-plugin-drift.sh, eachwith a paired
.test.sh.The remedy is wiring, not a build — this is the correction that matters most for whoever picks this
up. The item that produced this finding proposed shipping
scripts/check-skill-listing-budget.sh. That measurement already exists in this marketplace, asplugins/skill-quality/scripts/check-listing-budget.sh(+ its.test.sh), surfaced asskill-quality:check'slisting-budgetaction, closing #1404.That does not make this row fixed — Category G still has no table, still names only
/doctor, andstill never points at the script — but it changes the work, and it exposes a gap neither the item nor
its packet stated: the two tools measure different populations.
check-listing-budget.shwalksthis repository's skills roots (a marketplace publisher gate). Category G audits a consumer
machine's installed roster, which needs a cache walk, version resolution across version directories,
and
enabledPluginsreading that the existing script has no step for. So G cannot simply call it. Thehonest options are (a) point G at it as an in-repo proxy and say so, or (b) extend it with a cache-walk
mode. That distinction is the substance of the fix.
One platform trap in the item's list is falsified; do not carry it forward. The item states as fact
that a CRLF
SKILL.mdbreaksawk '/^---$/'("1 skill found instead of 219"). Not reproduced, at twoindependent levels: no CR exists in the corpus (
grep -lUP '\r'over allSKILL.mdfiles in theplugin cache and at repo HEAD → zero hits;
.gitattributespins* text=auto eol=lf, which forces LFcheckout on Windows regardless of
core.autocrlf), and even given CRLF the stated mechanism does nothold on this platform (GNU Awk 5.4.0 from Git for Windows strips the CR before the regex sees it). The
packet had already downgraded this to "plausible, not reproduced here" and the item's body dropped the
hedge. The reported symptom is better explained by the
jqCRLF-on-stdout trap, which isreproducible (
jq -rn '"hello"' | od -c→h e l l o \r \n) and whose own description — CRsoverwriting the rendered line — produces exactly a "1 instead of N" appearance. Restate trap 1 as a
portability hazard on other awk builds, not as an observed symptom.
The remaining traps hold:
jqCRLF on stdout (reproduced); process-spawn cost against the default Bashtimeout (measured at ~169 ms per spawn on this machine, which is the generalizable constant); and
version directories needing
sort -Vrather than lexical ordering (twelve plugins in the cache wherethe two disagree,
claude-configitself among them — but note that version directories are a propertyof the installed cache, not of HEAD, so a script encoding this must be tested against fixtures, never
the live cache).
B4 — the constant is never stated
context/validation-categories.md:132-143is the complete category. It names the per-entry cap(
1,536-char cap per entry) and both scaling settings (skillListingBudgetFraction/SLASH_COMMAND_TOOL_CHAR_BUDGET) — and states no budget value and no formula.grep -n "8000\|8,000\|40,000\|0\.01"over the file → no match. Confirmed by reading the wholecategory, not by keyword absence alone.
Sources confirmed verbatim on 2026-08-11 via the raw
.mdroute:SLASH_COMMAND_TOOL_CHAR_BUDGET): "The budget scales dynamically at 1% of the context window, with a fallback of 8,000 characters."skillListingBudgetFraction): "Default:0.01. Fraction of the model's context window reserved for the skill listing Claude sees each turn, so the default reserves 1%."skillListingMaxDescChars): "Default:1536."The arithmetic checks:
200,000 × 4 × 0.01 = 8,000, matching the documented fallback. Worth knowingfor whoever writes the sentence:
plugins/skill-quality/scripts/check-listing-budget.shalreadycarries this derivation in its header comment including the same doc quote, so the text can be cited
rather than re-derived.
B5 — no positive eval
plugins/claude-config/skills/audit/evals/evals.jsonat HEAD carries 10 cases. Every mention ofthe category in the file is two lines, both inside eval 1 (
scope-filter-permissions-only), bothnegative:
Established structurally rather than by line proximity — a
jqfilter over every case'sexpected_outputplusexpectationsforskill-listing|skill listingreturns eval1and onlyeval 1. Eval 5 (
findings-table-output-contract,/audit all) does say "A full nine-category audit",but all five of its expectations are about table shape and severity vocabulary and none asserts any
Category G behavior. (The three cases added by #2206 are Category B, not G.) The authors demonstrably
write this shape: eval 7 is a positive behavioral case for the adjacent Read-deny scope question.
Adjacent, deliberately not duplicated
check-listing-budget.sh's Windows runtime. B3's process-spawn trap is context for the fix, not a second filing of that defect.Provenance
Severity: MED · Provenance: AUDITOR_VERIFIED (five rows; trap corrections and the route correction are the validation pass's own)
Origin: handoff-inbox item
20260810-225906-claude-config-category-g-unmeasurableLedger:
.work/handoff-inbox-batch-4/ledgers/I3-claude-config-category-g.md§ B1–B5Verified against repo HEAD
9b34a82a. Line anchors re-derived at that commit — the ledger's anchors were taken atdacb8661, before #2206 shifted this file by 20 lines.