With Opus 5.5's 1M-token window, Claude Code's skill listing still stops at 30,000 characters: of 160 skills, 21 kept their descriptions at 1,000 characters, 42 at 500 and 104 at 200 on both 2.1.319 and 2.1.322, but at 130 all 160 fit on 2.1.319 and only 159 on 2.1.322, because one built-in skill's description had grown by 89 characters. Lowering
skillListingMaxDescCharsto 200 kept 125 on both versions without editing a file, and descriptions cut to 100 characters still sent requests to the right skill 6 of 6 times on each version, but 0 of 2 when the trigger words sat after the cut.
Every skill you install puts a line in the model's context on every turn: its name and, if there is room, its description. The room is fixed. The skills page of the Claude Code docs says "The budget scales at 1% of the model's context window", and that when the listing overflows, "Claude Code drops descriptions starting with the skills you invoke least, so the skills you use most keep their full text."
Last month we measured that budget with 160 dummy skills, each with a 1,000-character description, and found that only 20 of them kept a description. We closed that lab with arithmetic instead of a measurement: if the descriptions were shorter, about 45 should fit at 500 characters and about 108 at 200. We never ran it. So this time we did, and we added the two settings the docs offer for the same problem. One cuts every description to a length you choose. The other collapses chosen skills to their names.
We ended up running the whole lab twice on 2026-11-10. The first pass, from 06:17 to 06:22 UTC, ran on Claude Code 2.1.319. A restart of the machine then emptied the temporary folder the lab lived in, so we rebuilt it and ran everything again from 07:41 to 07:45 UTC, by which time 2.1.322 was installed. The first pass's session transcripts had survived in ~/.claude/projects, and the transcripts are where every listing number below comes from, so we have both versions side by side. Both passes used claude-opus-5-5, selected with --model opus. The docs quoted here were fetched at 07:47 UTC the same day.
What the docs offer
The skills page lists three ways out once the listing is full, in one paragraph of its troubleshooting section. You can raise the budget with skillListingBudgetFraction or SLASH_COMMAND_TOOL_CHAR_BUDGET, which we measured last time and left alone here. You can set low-priority skills to "name-only" in skillOverrides "so they list without a description". Or you can shorten the text itself: "put the key use case first, since each entry's combined text is capped at 1,536 characters regardless of budget."
That 1,536 is a setting. The settings reference describes skillListingMaxDescChars this way: "This key caps how many characters of that text Claude Code shows per skill; longer text is cut at the cap." Its default is 1536, and the entry ends with the use we wanted to test: raise it to keep long descriptions whole, "lower it to fit more skills under skillListingBudgetFraction".
For skillOverrides, the same reference says that with "name-only", "Claude sees the skill by name without its description". Neither page says how many more skills fit when you use either one, which is the number we were after.
The lab
It is the same lab as last month with fresh names. Each condition is a directory that holds nothing but .claude/skills/ with 160 dummy skills, lb3-sk-001 to lb3-sk-160 (lb2- on the first pass). Every description starts and ends with its own codeword (CW001A and CW001Z), so we can see from the model's answer whether a description arrived whole, cut, or not at all. A small Python script writes the set at whatever description length we ask for, padded with neutral filler to exactly that many characters. Claude Code drops descriptions from the skills invoked least, and it keeps invocation counts in ~/.claude.json on each machine, so we used names that had never been invoked. That is also why the second pass got a new prefix: the first pass's routing tests had invoked six of its skills.
Each listing run was one headless call with only the Skill tool available, our user settings and MCP servers left out, and a clean environment (env -i with HOME, PATH and a few basics), so nothing leaked in from the Claude Code session we launched it from:
claude -p "$PROMPT" --setting-sources project,local --strict-mcp-config \
--tools Skill --max-turns 1 --model opus --output-format stream-json --verbose \
--session-id "$SID" --debug-file "runs/$NAME/debug.txt" < /dev/null
The prompt asks the model to list every CW codeword it can see in its context, in order. The evidence comes from the session transcript, where the listing is an attachment record of type skill_listing holding the exact text sent; from the debug log, which names the budget whenever the listing is over it; and from the first request's token count. In all 22 listing runs, 11 per version, the codewords in the model's reply matched the recorded listing exactly. On the second pass, the result event also reported "contextWindow": 1000000 every time.
Eleven listings, two versions
| Descriptions on disk | Setting | With a description on 2.1.319 | With a description on 2.1.322 | Listing sent on 2.1.322, characters | Debug log on 2.1.322 | First request on 2.1.322, tokens |
|---|---|---|---|---|---|---|
| no skills | — | — | — | 6,939 (6,850 on 2.1.319) | no warning | 6,636 |
| 1,000 characters (2 runs) | default | 21 of 160 | 21 of 160 | 29,988 | 169,337 > 30,000 | 14,385 |
| 500 | default | 42 of 160 | 42 of 160 | 29,969 | 89,337 > 30,000 | 14,819 |
| 200 (2 runs) | default | 104 of 160 | 104 of 160 | 29,954 | 41,337 > 30,000 | 16,306 |
| 130 | default | 160 of 160 | 159 of 160 | 29,934 | 30,137 > 30,000 | 16,734 |
| 100 | default | 160 of 160 | 160 of 160 | 25,339 | no warning | 14,786 |
| 1,000 | skillListingMaxDescChars: 200 |
125 of 160 | 125 of 160 | 29,952 | 37,094 > 30,000 | 16,138 |
| 1,000 | skillListingMaxDescChars: 100 |
160 of 160 | 160 of 160 | 19,956 | no warning | 12,638 |
| 1,000 | 40 skills "name-only"
|
21 of 160 | 21 of 160 | 29,988 | 129,257 > 30,000 | 14,364 |
On each version the repeated conditions produced byte-identical listings. The 14 entries in the no-skills row are Claude Code's own, the same 14 as last month in the same order, and they came after our skills in every listing.
Shorter descriptions: 21, 42, 104, then 159 or 160
The budget hasn't moved. Every over-budget run on 2.1.322 logged the same 30,000, which is 1,000,000 tokens × 0.01 × 3 characters, as before, and no listing on 2.1.319 went past it either:
[WARN] Skill listing over budget: 174 skills, 41337 chars > 30000 budget — descriptions will be truncated. Run /skills to disable some, or raise skillListingBudgetFraction in settings.
What changed with length is how many descriptions fit inside it. At 1,000 characters 21 of our 160 kept a description, at 500 it was 42, at 200 it was 104, and at 130 it was all 160 on 2.1.319 and 159 on 2.1.322. Only at 100 did every one of them fit on both. No description was shortened to squeeze in. Each of our skills appeared either with its full text, both codewords present, or as a bare name line such as - lb3-sk-160. The ones that kept their text were always the first ones in name order.
Where the 160th went: 89 characters in a built-in description
We diffed the two no-skills listings. Thirteen of Claude Code's 14 entries were identical. The fourteenth, the bundled claude-api skill, had a description of 1,068 characters on 2.1.319 and 1,157 on 2.1.322: its instructions about when to load it had been reworded and extended. The changelog entries for 2.1.321 and 2.1.322 don't mention it. That is the whole difference between 6,850 and 6,939, and it was enough to push one of our 130-character descriptions out.
The counts follow from the listing's format, and the arithmetic is worth having for your own set. A name-only line is -, the name and a line break: 13 characters for a 10-character name. A description adds : and the text, so D + 2 more. On 2.1.322, with Claude Code's own entries taking 6,939 characters and 160 names taking 2,080, our descriptions had 20,981 characters to share. Divided by D + 2, that gives 20 at 1,000, 41 at 500, 103 at 200 and 158 at 130.
The measured listings held exactly one more than that in all four cases, and the extra one always came from the same place. Claude Code sent its own security-review entry without its 71-character description, which with its : freed 73 characters and made room for one more of ours. At 500 characters that still wasn't enough, and init gave up its 59-character description as well, freeing 61. None of the other twelve built-in descriptions was dropped in any run. At 130 the listing ended 66 characters short of room for the 160th description: the 132 it needed, less the 66 still free after security-review had made room for the 159th. Even init's 61 would not have closed that gap.
On 2.1.319 the same division gives 21, 41, 104 and 159, with 21,070 characters to share, and the same rule fills the gaps: security-review went to its name alone at 500 and at 130, which is how 130 reached 160. At 1,000 and 200 the gap was too wide for it to help, and it kept its description.
So the October arithmetic was close but optimistic. It said 108 at 200 characters, and the listing held 104, for two reasons. We had divided as if every skill had a description, which left out the 56 name-only lines and their 728 characters. And Claude Code's own entries had grown from 6,629 characters on 2.1.289 to 6,850 on 2.1.319 and 6,939 on 2.1.322, through edits to the built-in skills' descriptions that are easy to miss: the one we caught between 2.1.319 and 2.1.322 isn't in the changelog. If you want the same number for your own machine, run one listing with no project skills first, and use that size instead of ours.
skillListingMaxDescChars: 125 at 200, without editing a file
Rewriting 160 descriptions is a lot of work. Lowering skillListingMaxDescChars gets you there from a settings file, and in our runs it did better than rewriting. With the 1,000-character descriptions untouched on disk and the cap at 200, 125 of our skills kept a description on both versions, against 104 when the files themselves held 200-character descriptions. Each one arrived as its first 199 characters followed by ….
The 21 extra came from Claude Code's own entries. The cap applies to every entry in the listing, not just yours. dataviz, whose description runs to 1,436 characters, arrived as 200, and so did claude-api, code-review and seven others. Claude Code's 14 entries went from 6,939 characters to about 2,620, and the space they gave back went to our skills. Because claude-api was cut to 200 either way, its 89 new characters made no difference here: both versions sent a 29,952-character listing with 125 of ours described. At a cap of 100 the whole listing, all 174 entries with a description each, came to 19,956 characters on both versions, two thirds of the budget, and the debug log stayed silent.
So the setting does what its docs entry says. It is also less selective than editing by hand: it shortens everything to the same length, including descriptions written by people who put the most important words last.
"name-only" picks which skills keep descriptions, not how many
For the next condition we put 40 of our skills, lb3-sk-001 to lb3-sk-040, in skillOverrides as "name-only". Those 40 are the ones that had descriptions at the 1,000-character default. After the change, lb3-sk-041 to lb3-sk-061 had descriptions instead, and the listing was again 29,988 characters with 21 described. The first pass gave the same 41 to 61.
That matches the docs, which say the setting frees budget for other skills. It doesn't raise the count, though, because a name-only entry costs the same 13 characters as an entry whose description was dropped for lack of room. When the listing is full, "name-only" decides who gets the space; only shorter text puts more descriptions into it.
Does a description cut at 100 characters still route?
A listing full of short descriptions is only useful if the model can still pick the right skill from them. We built three more sets of 160, this time with the 160 trigger phrases ("fallow hopper", "sable beacon", "quartz ledger" and so on) shuffled by a fixed seed, so that no skill's phrase could be guessed from its neighbors. Then we sent natural requests that matched exactly one skill, such as "The quartz ledger drifted again. Please recalibrate the quartz ledger before the release.", with --max-turns 4. The targets were skills 61, 97 and 133, all of which would have been name-only at the 1,000-character default.
With 100-character descriptions on disk and the trigger phrase near the start, the model invoked the right skill on its first and only tool call 3 of 3 times and replied with that skill's body token. With 1,000-character descriptions cut to 100 by skillListingMaxDescChars, phrase still near the start, it was again 3 of 3. Both versions gave the same results.
Then we moved the phrase to the end of each 1,000-character description, behind the same opening sentence for every skill, and kept the cap at 100. The model invoked nothing, 0 of 2 on each version. Every time it said the descriptions gave it nothing to go on and asked which skill to use. This is the reply to the quartz ledger request on 2.1.319:
On 2.1.322 the wording changed and the diagnosis didn't. The listed descriptions, it wrote, "get cut off before saying which artifact each one covers. None of them mentions the quartz ledger." That is the same failure we saw last month for skills listed by name only, and it is the docs' advice to put the key use case first, seen from the other side. A cap applied in settings cuts every description at the same place, and whatever sat behind that place is gone.
What it costs per turn
The budget keeps the cost in a narrow band whatever you do inside it. On 2.1.322 the first request with no project skills was 6,636 tokens. With 160 skills, it was 14,385 at 1,000 characters, 16,306 at 200 and 16,734 at 130, when 159 had descriptions. The listing is close to 30,000 characters in all three, so going from 21 described skills to 159 cost about 2,350 tokens a turn. The 100-character cap brought it down to 12,638 tokens, because the listing shrank to 19,956 characters.
What we would do with these numbers
Measure your own floor first: one headless run with no project skills, the transcript's skill_listing record, and its length. Then the division above tells you how many of your descriptions fit at a given length, and you can decide whether the skills that miss out matter.
Leave room, and measure again after updates. Our 130-character set fit with 23 characters to spare on 2.1.319, and an update that only edited a built-in description took one of ours out. If a skill matters, don't let it be the one that sits at the edge of the budget.
If the skills that miss out do matter, write the trigger words into the first hundred or so characters of every description you control, whatever you do next. After that, cutting the listing with skillListingMaxDescChars is the cheapest change, and in our runs it also absorbed the claude-api change. Test a few requests afterwards, though, because it cuts Claude Code's own descriptions and anything else you didn't write. Use "name-only" to choose which skills keep their text when you can't fit them all, not to fit more. And if you can only shorten some descriptions, shorten the ones that sort first or get invoked most, since those are the ones Claude Code keeps.
What we did not measure
Each condition ran once per version, except 1,000 and 200 characters, which ran twice per version with identical results. Two versions an hour and a half apart are not a history: we don't know how often the built-in descriptions change, and we don't know why security-review and init are the built-in entries that give way. Everything was headless on one macOS machine with Opus 5.5 and its 1M window; we didn't repeat it at 200K, where last month the built-in descriptions alone filled the budget. We didn't load plugin or user-level skills, try when_to_use, disableBundledSkills or "off", use descriptions with real words in their names, or count non-English text. The routing tests are small, 8 requests per version, and our skills differ only in a two-word phrase, which is a far cleaner choice than real skills offer. Whether a description cut to 100 characters is enough for a real skill depends on how much of it is the part a request would match.
Numbers, for the record
There were 19 claude -p runs per pass, 11 listing runs and 8 routing runs: on 2.1.319 between 06:17 and 06:22 UTC, with a reported cost of $1.87, and on 2.1.322 between 07:41 and 07:45 UTC, with a reported cost of $1.77. The routing replies were BODY061, BODY097 and BODY133 in both the 100-character set and the capped set on both versions, and two requests for clarification per version in the set with the phrase at the end.
If you run a large set of skills, what description length did you settle on, and did lowering skillListingMaxDescChars cut anything that mattered? Tell us in the comments below, with your Claude Code version and how many skills you have.


Top comments (0)