Improve skill guidance on text generated by LLM models - #304
Merged
Conversation
A skill that splits its detail into references/ could not name any of it. tree_paths() prunes .claude, so every backticked references/x.md read as a stale pointer to a file sitting beside the skill that wrote it. Resolution now tries the file's own directory and the skill root before the tree, which also covers one skill citing another's SKILL.md. Each candidate has to be a file: resolve() matches against tree_paths(), which lists files only, and a bare existence test would let a directory named like a reference satisfy a pointer here and nowhere else. Three narrower gaps went with it. A path inside a fenced block carries no backticks and was invisible, so a checklist could name a deleted script; a fenced path is recognized when it carries a directory component, which keeps build/elfuse and ./binary out of it. Fences count at any indentation, since a fence inside a numbered step sits at that step's content column and a column-0 anchor skipped those blocks whole. The make-command pattern loses that anchor too, because finding the block is not enough while the command inside it still has to start at column 0. An inline span holding a slash is a path rather than a skill name, so cmd/elfuse-container no longer reads as a skill that went missing. And the references/ files are checked themselves, since a citation rots there exactly as fast. Each class has a self-test case, watched failing with its own fix reverted: sibling resolution with the skill root removed from the search, the indented-fence cases with either anchor restored, and the directory case with the existence test back.
Splitting mk/analysis.mk into verify.mk, lint.mk, and format.mk left check-skill-refs.py naming the old makefile twice, in the docstring and above the target check it describes. Both are prose rather than a typeset reference, so the script cannot report them, which is why they outlived the rename the same script exists to catch. The skills themselves were repointed when the split landed.
henrybear327
force-pushed
the
skills/reconcile-and-gate
branch
from
August 16, 2026 21:40
9cb1020 to
4948c6e
Compare
The check existed and nothing called it, which is how the verify skill came to name a makefile that had been split two commits earlier. It sits beside check-syscall-coverage, the other gate that reads a file the compiler never sees. The target runs the checker twice, self-test first. Every case in that self-test is a class of stale reference the script once shipped past, and behind a flag the assertions run only when somebody remembers to type it. The extra run costs 0.65s, against a target that builds and runs the test matrix. The module docstring gains what the mode does, since a reader meeting it in the build has no reason to know the flag reads no skill file at all. self_test() narrows to the one thing that account leaves out, where its cases came from, and the case shape stays where it belongs, above the list it describes. A clone without .claude/skills exits 0 with a note, so this does not make the build depend on files a clone may not carry. The self-test arm holds there too: it builds its fixtures in a temporary directory and reads the skills directory not at all, watched passing with SKILL_DIR pointed at a name that does not exist.
Three statements described one contributor's checkout as though every clone had it. The working docs were named individually and one of them was declared to win wherever it and this file disagree, which resolves to nothing on a clean checkout, where docs/ and these skills are all a contributor gets. claudedocs/ was described as ignored through .git/info/exclude, so a reader following it lands an untracked directory in git status and reads that as correct. Working docs are now described as a per-clone habit nobody else touches, the conventions defer to none of them, and Layout says where a report goes and which of the two exclusion mechanisms is the per-clone one.
The ASCII rule exempted markdown wholesale, so the character most likely to mark prose as machine-written was permitted everywhere a contributor actually writes prose: docs/ and these skill files are markdown. It has been rejected on sight (PR#209). The rule now names every surface, the stand-in, and the codepoint spelling that keeps the grep for the character from matching the sentence forbidding it. The em dash is only the sharpest tell in a wider register: inflation words, empty pivots, coined vocabulary, trailing -ing glosses, negative parallelism, rule-of-three padding, signposting, effort claims, narrating the change rather than the thing, and invisible-character artifacts date prose the same way. The catalogue is one line per class and the only place a class is stated, so a per-surface section names its own instance and nothing else. The invisible class gets its own scan, because legitimate Unicode lives in docs/. The one instance the tree carried goes with it: the make help banner has printed an em dash since the initial import, and a colon is what the gloss wanted.
Comments, docs/, commit bodies, and PR threads are the surfaces a contributor writes prose on, and the rules for them arrived as two commits that each rediscovered the same classes. The history ban was written twice, once for comments and once for docs/; the ban on coined nouns was written twice, once as "coined metaphors" and once as "invented vocabulary"; figurative accounting was stated only under commit messages although it binds a comment the same way. Two spellings of one rule drift apart, and a reader cannot tell whether the difference is deliberate. The classes now live in Style, which already claimed every surface, and each section carries only its own instance: the doorbell that does not ring for comments, the rejected reply shapes and their citations for pull requests, the diagram rule for commit messages. The checkability list under commit messages loses two of its four entries that way, and the two that remain read as a sentence, which is the shape the register rules ask for. Four classes no section covered come in with them, each one met in this tree: signposting and prompt echo, a closing sentence that grades the change instead of stating a fact, an effort claim standing in for a result, and flattery in a review reply. They are drawn from the sloptrim catalogue, https://github.com/seyedehsanhadi/sloptrim, which the skill does not cite, because a skill has to stand on its own in a fresh clone. Two of that catalogue's classes are false alarms here and stay out. Title Case headings are house style in docs/, and its sentence-length and paragraph-rhythm statistics say nothing about a body wrapped at 72 columns.
henrybear327
force-pushed
the
skills/reconcile-and-gate
branch
from
August 16, 2026 21:56
4948c6e to
87549e0
Compare
The comments in check-skill-refs.py told most reasons twice: a decision argued in a constant's comment and again at its call site, an example spelled out after the rule it illustrates, and a module docstring narrating how the sidecar.c reference was found on top of naming it. Every fact survives. What goes is the second telling, the asides addressed to the reader, and the discovery narrative that belongs in a commit message rather than in a file somebody opens to change the code. 229 lines of comment and docstring become 169. The register rules this branch states for comments and docs bind a script under scripts/ the same way. Verified: the docstring-stripped ASTs of the two revisions are equal, so nothing outside comments moved, and the same comparison reports a difference when a comparison operator in check_paths() is mutated.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Improve the guidance for comment, skill, and documentation writing.
Summary by cubic
Gates skill references in the build and tightens prose rules across the repo. Old: skills could cite stale files and markdown allowed em dashes; new:
check-skill-refsvalidates typeset paths/targets/sections inSKILL.mdandreferences/*.md(including indented fenced blocks), and conventions ban U+2014 and the broader machine-written register across comments, docs, commits, and PRs.scripts/check-skill-refs.py: resolves
references/*.mdbeside the file and skill root before the tree, recognizes paths andmakecommands inside fenced blocks at any indentation (directory+extension required for paths), treats inline backticked spans with slashes as paths (not skills), rejectsfd.c/h, usesis_file()to avoid directory false-positives, repoints targets tomk/verify.mk, checksreferences/*.mdthemselves, extends self-tests (synthetic skills, sibling refs, indented-fence cases), and trims duplicate comments/docstrings without changing behavior.Build:
mk/tests.mkaddscheck-skill-refstomake checkand runs--self-testfirst; clones without.claude/skillsexit 0 with a note.mk/help.mkreplaces the banner em dash with a colon.Conventions:
.claude/skills/elfuse-conventions/SKILL.mdcentralizes style for all prose surfaces; bans U+2014 and machine-written register classes; clarifies per-clone working docs and exclusions.Required action: run
make checkand fix anycheck-skill-refsfindings. Stop using U+2014 and other banned register in comments, commit messages, docs, PR bodies, and reviews.Written for commit 045b186. Summary will update on new commits.