Skip to content

fix(claude-config): audit Category G cannot be executed — TTY-only detector, unreachable levers, no table, and no stated budget #2274

Description

@kyle-sexton

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

  • B1plugins/claude-config/skills/audit/context/validation-categories.md:134-137/doctor is the only overflow detector named, and it is TTY-bound; the documented headless --debug warning is unmentioned, so the whole category yields nothing in the harness's own headless mode.
  • B2plugins/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 the does NOT apply to plugin skills caveat itself), and /plugin — the lever upstream prescribes as the substitute — appears nowhere in the file.
  • B3plugins/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 at plugins/skill-quality/scripts/check-listing-budget.sh, the measurement this marketplace already owns.
  • B4plugins/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.
  • B5plugins/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:

- **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 -ch 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions