Skip to content

fix(skill-quality): check the description field cap separately from the listing cap - #3150

Merged
kyle-sexton merged 3 commits into
mainfrom
fix/skill-quality-desc-char-cap
Aug 23, 2026
Merged

kyle-sexton merged 3 commits into
mainfrom
fix/skill-quality-desc-char-cap

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Closes #3119

Summary

skill-quality:check carried one description limit and treated it as the only one. There are two, at different layers, and only the looser was checked — so nineteen skills in this marketplace breach the Agent Skills spec's field maximum and pass the gate clean.

Fix

DESC_CHAR_CAP=1536 is Claude Code's in-context listing truncation for the assembled entry (description + " - " + when_to_use). The Agent Skills spec states a separate, smaller maximum for the description field alone:

description: Must be non-empty / Maximum 1024 characters / Cannot contain XML tags

(platform.claude.com Agent Skills overview, fetched 2026-08-23.)

These do not unify. A description can sit under 1536 combined and still breach 1024 on its own.

Adds check 2b for the field maximum, reported separately from check 2 with its own message.

WARN, not FAIL — on measured evidence, not preference. No local validator enforces the field maximum: claude plugin validate --strict (Claude Code 2.1.241) passes a 1248-char description clean, verified against a throwaway fixture plugin, the only warning raised being an unrelated missing author. The breach is latent for filesystem and plugin skills and hard only for a skill uploaded through the Skills API. Failing the build on it would block the fleet over a limit nothing in the local toolchain applies.

Counted in codepoints, not bytes (fixed in review — see below). The spec says "Maximum 1024 characters", and ${#var} degrades to byte counting under a byte-oriented locale, so 600 é characters report as 1200 under LC_ALL=C. Uses the same UTF-8 → UTF-32BE iconv form check 22 already uses, with the same UTF-8-locale fallback. DESC_LEN stays a byte count for check 2, so check 2 behavior is unchanged.

Numbered 2b, not 26 — same concern as check 2 at a second layer, and renumbering would break the identity of checks 3–25, which are cited by number across this repo and in tracker items. Consequence worth a reviewer's eye: the plugin's "twenty-five deterministic checks" claim is left standing on that reading. Say the word if you'd rather it read twenty-six and I'll renumber.

Verification

  • plugins/skill-quality/scripts/check-skill.test.sh — full suite passes, including four new assertions.
  • Boundary: 1024 is silent (the cap is >1024, not >=), 1025 warns.
  • Discriminating case: desc(1100) + joiner + wtu(40) = 1143. Check 2 passes the entry; only check 2b catches the field breach. Without this case a single cap could satisfy both assertions, so it is the one that proves the layers are independent.
  • Locale regression: a 600-codepoint / 1200-byte multibyte fixture run under LC_ALL=C must stay silent. Verified as a negative control — reverting to the byte-counting form fails that assertion and only that one.
  • Real target: CHECK_SKILL_SKILLS_ROOT=plugins/docs-hygiene/skills check-skill.sh compress →
    WARN: description alone is 1030 codepoints (Agent Skills spec field maximum 1024), run still PASS — 0 errors.
  • scripts/check-changelog-parity.sh --check and --check-bump both pass.
  • claude plugin validate plugins/skill-quality passes.
  • markdownlint-cli2 clean on the changed markdown.
  • bash -n clean on both scripts. (shellcheck is not installed in this environment — CI covers it.)

Nineteen skills currently trip the new warning. Trimming them is deliberately not in this PR — check 3 protects quoted trigger phrases, so it is gated work, not a mechanical sweep.

Correction to an earlier revision of this description: it claimed nineteen was a lower bound because the gate reported compress at 1044 where an independent scan said 1030. That gap was the byte-vs-codepoint bug, not a scan error — under the corrected codepoint count the gate reports 1030 and the two agree. Nineteen is the count, not a floor.

Related

Refs #3118 — the spec container this defect was surfaced by. Not closed here; it closes when the detector work ships.

claude added 2 commits August 23, 2026 04:27
…he listing cap

`DESC_CHAR_CAP=1536` is Claude Code's in-context listing truncation for the
assembled entry (`description` + " - " + `when_to_use`). The Agent Skills spec
states a separate, smaller maximum for the `description` field alone: "Must be
non-empty / Maximum 1024 characters / Cannot contain XML tags".

The gate checked only the looser limit, so a description could sit under 1536
combined, breach 1024 on its own, and pass clean. Nineteen skills in this
marketplace do.

Add check 2b for the field maximum, reported separately from check 2.

WARN rather than FAIL, on measured evidence: no local validator enforces the
field maximum. `claude plugin validate --strict` (Claude Code 2.1.241) passes a
1248-char description clean, verified against a throwaway fixture plugin, the
only warning raised being an unrelated missing `author`. The breach is latent
for filesystem and plugin skills and hard only on Skills API upload, so failing
the build would block the fleet over a limit nothing local applies.

Numbered 2b rather than 26 to avoid renumbering checks 3-25, whose identities
are cited by number across this repo and in tracker items.

Tests cover the boundary (1024 legal, 1025 warns) and the discriminating case:
a description over 1024 whose assembled entry stays under 1536, which check 2
passes and only check 2b catches.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188gyMe4eBgtcw6izKzPusM
…c-char-cap

# Conflicts:
#	plugins/skill-quality/.claude-plugin/plugin.json
#	plugins/skill-quality/CHANGELOG.md
@claude

claude Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 55s —— View job


Security Review

  • Gather context (PR diff, description, changed files)
  • Run /review:security-review per instructions
  • Post findings

No security issues found.

This PR adds check 2b to plugins/skill-quality/scripts/check-skill.sh: a second length comparison (DESC_LEN > DESC_FIELD_CAP) against a variable already computed by the existing check 2, reported via the same warn()/note() helpers (plain printf '%s\n', no eval, no format-string injection). The corresponding CHANGELOG and plugin.json version bump are doc/metadata-only, and the new test cases in check-skill.test.sh exercise the same code path with fixture data.

  • No new data flow: DESC_LEN is the same integer already derived from frontmatter for check 2; nothing new is parsed, shelled out, or evaluated.
  • No injection surface: all interpolation is %s string formatting, not command construction or eval.
  • No GitHub Actions, auth/authz, or trust-boundary code is touched.
  • Matches the skip-gate condition for "no security-relevant surface after reading the diff" (plugins/review/skills/security-review/SKILL.md).

@github-actions

github-actions Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Last security-reviewed head: 39daa91ed5e0a4eaad83bcf9cbcbdb6efd31ad7a. On the next push, the relevance gate compares only the commits since this SHA; delete this comment to force a full re-review.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c9a4b37614

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/skill-quality/scripts/check-skill.sh Outdated
…t bytes

Review finding from the Codex reviewer on #3150.

`${#CUR_DESC}` counts UTF-8 bytes, not characters, under a byte-oriented
locale. The Agent Skills spec limit is "Maximum 1024 characters", so a
non-ASCII description false-warns: measured, 600 'é' characters report as
1200 under LC_ALL=C.

Count codepoints via the same UTF-8 -> UTF-32BE iconv form check 22 already
uses (every codepoint becomes exactly 4 bytes, so byte-count/4 is the
codepoint count on any host), with the same UTF-8-locale fallback where iconv
is absent. DESC_LEN stays a byte count for check 2, whose 1536 listing cap is
a separate measure, so check 2 behavior is unchanged.

Regression test runs the checker under LC_ALL=C against a 600-codepoint
multibyte fixture, so it fails on the byte-counting form rather than passing
incidentally on a UTF-8 host. Verified as a negative control: reverting the
fix fails that assertion and only that one.

Side effect worth noting: docs-hygiene:compress now measures 1030 codepoints
where the byte form reported 1044.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0188gyMe4eBgtcw6izKzPusM
@claude

claude Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 1m 0s —— View job


Security Review

  • Gather context (PR diff, description, changed files)
  • Run /review:security-review per instructions
  • Post findings

No security issues found.

Reviewed the diff since the prior security review (commit c9a4b37) through the current head, 39daa91. The new commits (66948ac merge, 39daa91) address the Codex reviewer's locale-counting comment on check 2b:

  • check-skill.sh#L376-L392: description length for check 2b is now measured in Unicode codepoints via iconv -f UTF-8 -t UTF-32BE | wc -c (÷4), matching the pattern already used by check 22, with a ${#CUR_DESC} fallback under LC_ALL=C.UTF-8 when iconv is unavailable.
  • All interpolation is printf '%s' and $(...) command substitution on a value (CUR_DESC) already parsed and used unmodified by the pre-existing check 2 — no new parsing, no eval, no shell/command construction from untrusted input.
  • The corresponding test additions (check-skill.test.sh) exercise the same path with fixture data only; CHANGELOG.md and plugin.json are doc/metadata-only.
  • No GitHub Actions, auth/authz, or trust-boundary code is touched.

This stays within the skip-gate condition for "no security-relevant surface after reading the diff" (plugins/review/skills/security-review/SKILL.md) — the Codex comment was a correctness/locale-handling issue, not a security one, and it's already resolved in this diff.

@kyle-sexton
kyle-sexton enabled auto-merge (squash) August 23, 2026 04:42
@kyle-sexton
kyle-sexton merged commit dc52b19 into main Aug 23, 2026
52 checks passed
@kyle-sexton
kyle-sexton deleted the fix/skill-quality-desc-char-cap branch August 23, 2026 04:55
@claude

claude Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @kyle-sexton's task in 47s —— View job


Tasks

  • Gather context (PR diff, description, changed files)
  • Run /review:code-review per instructions
  • Post findings

Result

Skipped per the /review:code-review skip gate: this PR is merged (its commit — dc52b19, PR title fix(skill-quality): check the description field cap separately from the listing cap (#3150) — is already on main, matching the given HEAD SHA 39daa91). The skip gate's first condition is "PR is closed or not open," which applies here, so per the skill's instructions I'm posting no review content.

For reference, the follow-up Codex review comment on this thread flags a real, worth-tracking issue for a future PR: ${#CUR_DESC} in plugins/skill-quality/scripts/check-skill.sh counts bytes rather than codepoints under a byte-oriented locale (e.g. LC_ALL=C), which could false-warn multilingual descriptions on the new field-cap check. Check 22 in the same script already handles this correctly via iconv UTF-8→UTF-32 conversion — the same technique would fix check 2b if/when addressed. Not actioned here since the PR is already merged; would need a new PR.

@github-actions

Copy link
Copy Markdown
Contributor

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(skill-quality): DESC_CHAR_CAP is set to the listing-truncation value, not the spec field limit

2 participants