Repository navigation
Conversation
…ed files SKILL.md never names An agent opens the files SKILL.md points it at. In the GitSkills corpus 63% of bundled .py, 45% of .sh and 47% of .md files are never named in their SKILL.md, so the agent only finds them by browsing. The new check counts scripts and documents (not data or images) that neither SKILL.md nor a document it names mentions by path, file name or folder, case ignored, and reports the count with three examples. Licence and readme files, _-prefixed helpers, AGENTS.md, tests, vendored folders and nested skills are skipped, and a skill at the checkout root is not checked. On 24 upstream skill repos (2,372 skills) it flags 120 skills, 596 of 2,391 counted files. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
🟡 Changes recommended
Named FIFO references can hang reviews, while substring matching misidentifies overlapping filenames, suppressing warnings and reading unintended documents.
2 open issues
What changed in this PR
2 new findings.
Adds bundle-file-unreferenced to catalog validation, warning when bundled scripts or documents are not named by SKILL.md or a document it references.
Changes:
- Implements case-insensitive reference matching, document traversal, and bundle exclusions.
- Integrates one warning per skill, with counts and example paths.
- Adds regression coverage and documents the new warning.
| File | Description |
|---|---|
dokimi/tests/test_content_checks.py |
Updates expected standalone-review findings. |
dokimi/tests/test_catalog_bundle_unreferenced.py |
Tests references, exclusions, document traversal, and review scope. |
dokimi/dokimi/external/validate.py |
Registers and emits the new warning. |
dokimi/dokimi/external/bundle_refs.py |
Implements bundle scanning and reference detection. |
dokimi/docs/external-content.md |
Documents warning behavior and exclusions. |
dokimi/changelog.d/20261109_160000_napetrov_bundle-file-unreferenced.md |
Announces the warning and its ignore option. |
Review effort: Balanced
|
OpenCodeReview: Commit 00ae79b was not reviewed. This PR has reached its review limit (6 reviews). CI at `00ae79b`: 11 still running, 5 passing, 1 skipped
1 more not listed. To review this commit: re-request review from exai-bot in the Reviewers list. <details><summary>Last review (cd26489): no findings</summary> Open threads from the bot: 0 </details> |
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #559 +/- ##
==========================================
+ Coverage 95.72% 95.74% +0.01%
==========================================
Files 415 416 +1
Lines 55363 55522 +159
Branches 9516 9547 +31
==========================================
+ Hits 52994 53157 +163
+ Misses 1466 1464 -2
+ Partials 903 901 -2
🚀 New features to boost your workflow:
|
A document SKILL.md names was read whatever its type, so a FIFO named notes.md blocked the scan until a writer opened it. Read only regular files. Thread: PRRT_kwDOVBg7g861eMIQ Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ter file The raw substring check let SKILL.md's rebuild.py name build.py and guide.mdx name guide.md. Match the path and the file name as whole names. Thread: PRRT_kwDOVBg7g861eMIw Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The docs say tests are not counted, but only the test folders were skipped: test_build.py and build.test.js in scripts/ were counted as unreferenced. Thread: PRRT_kwDOVBg7g861eOr_ Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Readme, licence, changelog, AGENTS.md and CONTRIBUTING.md were left out of the walk, so a script only README.md named was reported as unreferenced. Walk every document for traversal and count only the countable ones. Thread: PRRT_kwDOVBg7g861eOsF Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Only SKILL.md's links were parsed, so [fill](scripts/fill%2Epy) in forms.md left scripts/fill.py unnamed. Parse the links of every document the scan reads, relative to its own folder, with the same parser. Thread: PRRT_kwDOVBg7g861eOsJ Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at c72c3d3. The check works and the five earlier bot findings are fixed, but folder references are still matched too narrowly: a folder written with ./ or ${CLAUDE_SKILL_DIR}/ gives false warns. It also needs a rebase: it conflicts with current main in validate.py.
Agreed with @exai-bot: build.py.bak counts as naming build.py; a folder link in a nested document does not cover its files.
What I ran, at c72c3d3:
make ci-local --offline: 17,916 passed, 0 failed, 72 skipped. One FAIL,schema_compatonarms.v1.json, comes frommainmoving since the base, not from this PR (it touches no schema).- Repros of
_namedandunreferenced_messagefor folder prefixes, dotted suffixes, nested folder links and${CLAUDE_SKILL_DIR}links in nested documents.
Checked, no issues
- Earlier fixes: the FIFO, whole-name, test-name, skipped-README and encoded-link fixes hold, each with a test.
- Docs and changelog: the new code is in
FINDING_CODES, the docs and the module docstring; the fragment is under Added and says how to turn it off.
The unreadable-SKILL.md guard from main now wraps the bundle check added here. Thread: PRR_kwDOVBg7g88AAAABXBtM2g#1 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…shorter file build.py is not named by a mention of build.py.bak; the trailing lookahead now also rejects a dot followed by a word character. Thread: PRRT_kwDOVBg7g861kC5m Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
./scripts/, ${CLAUDE_SKILL_DIR}/scripts/ and a folder link to an ancestor
folder now count as naming the folder's files.
Thread: PRRT_kwDOVBg7g861px8Y
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
… own folder The destination of a folder link is checked as its folder in the document's folder; the same folder name at the skill root is no longer named by it. Thread: PRRT_kwDOVBg7g861kC5s Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…he skill root
${CLAUDE_SKILL_DIR}/scripts/x.py in references/guide.md names scripts/x.py,
not references/scripts/x.py. The catalog link list keeps the prefix so the
bundle check can tell it apart.
Thread: PRRT_kwDOVBg7g861px8e
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…hecked The walk returns after the cap without leaving os.walk early, and the warning is skipped for an over-cap bundle, so no partial count is given. Thread: PRRT_kwDOVBg7g861px8h Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
… files 'The helpers live in scripts/.' ends the folder name with a period that the folder lookahead refused, so the folder was reported as unnamed. A period followed by a word character still continues the name. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Re: review by vlad-perevezentsev
Fixed in 93200bc and cc078d5: ./, ../, skill-dir folder forms count. |
| DOCS_READ_MAX = 50 | ||
| # A path prefix that names the skill root or the current folder: ``./scripts/``, ``../scripts/``, ``${CLAUDE_SKILL_DIR}/scripts/``. | ||
| # The destination of an inline folder link (``[x](scripts/)``): its folder is in ``links``, so the text is no mention. | ||
| _FOLDER_DEST = re.compile(r"\]\([^)\n]*/\)") |
There was a problem hiding this comment.
This comment has only the word "placeholder", so there is no finding to act on. Please repost the finding if one was meant.
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…he read cap is not judged Thread: PRRT_kwDOVBg7g8611-uo Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
… names that folder Thread: PRRT_kwDOVBg7g8611-u5 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
unreferenced_message was 11 complex against the 10 cap of the growth ratchet (test_growth_ratchet). The search for named documents moves to _named_closure, which also returns whether it was incomplete. Behaviour is unchanged. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g8624Gfw Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g8624GgG Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…rings Thread: PRRT_kwDOVBg7g8624GgP Thread: PRR_kwDOVBg7g88AAAABXq909A Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
… folder Thread: PRRT_kwDOVBg7g8624Gfi Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Thread: PRRT_kwDOVBg7g8624Gfw Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
…s-559-work Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…s that folder The document's text is lowercased before its ./ paths are resolved, so the folder it is resolved against must be lowercased too. Without it, References/guide.md saying ./scripts/ did not name References/scripts/run.py. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
vlad-perevezentsev
left a comment
There was a problem hiding this comment.
Review at e993313. My last point is settled: a9f441c lists the skip conditions in the docs, and I agree with keeping the read cap (replied in its thread). @vchamarthi's six line comments are fixed too.
Agreed with @vchamarthi on the search time: 1900 unnamed .py files and 50 named documents of 200 KB take 24.7 s CPU here. On a bare folder word (the scripts folder) I disagree: the docs define a folder mention with its slash, and the word alone is too common in prose to count. Counting .txt is the author's choice and the docs say so: many skills keep reference text in .txt.
What I ran, at e993313:
make ci-local --offline: 19,004 passed, 0 failed, 72 skipped; every step passes.- Repros of @vchamarthi's cases: a backslash folder, a folder link with a title and as a reference definition in a nested document, a hidden named document, and a nested document in a folder with capitals. Each gives the expected result, and a plain unnamed script still warns.
- The search time above, with a skill of 1900 scripts and 50 named 200 KB documents.
Checked, no issues
- Fixes: bb11659, 9e535a8, 446e43c, 5c5e3ae, 195fbf1 and e993313 each come with a test for its case.
- Backslash as a separator: links are decoded from the raw text, so a Markdown escape such as
guide\_notes.mdin a link still names its file. - Merge: it merges cleanly into current
main; once it is in, #454 conflicts with it invalidate.py.
…older links, a root link, and gives no claim past an unlistable folder - a folder that os.walk cannot list withholds the claim, as an unreadable document does - `[x](scripts\)` is no link, so its text names the folder instead of being dropped - `[x](scripts/#part)` and `[x](scripts/?q)` count as folder links of their own folder - a link to the skill root (`./`, `../`) names every bundled file Signed-off-by: vchamarthi <123653452+vchamarthi@users.noreply.github.com>
|
Pushed 1 commit to
|
e002876 to
9bc964f
Compare
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 9bc964f. The earlier threads hold and the unreadable-document handling is sound, but the two newest rounds (the backslash separator and the root link) each open a false result. Two bugs inline, plus two nits.
What I ran, on a Linux review host at 9bc964f:
make ci-local: 29 passed, 3 failed, 13 skipped; 19,064 tests; the changed-line coverage step passes. The three FAILs are not from the PR:Install gitleaksandScan commits(a download that returned HTTP 504), andRun tests, where two tests intest_audit_test_quality.py, a file the PR does not touch, hit the 60 s timeout under load and pass alone (172 passed in 8.9 s).- Reproductions for the code findings, with no model or network calls.
- A mutation run on
bundle_refs.py: 112 mutants, 37 pass the full suite; most are equivalent or change only a message.
Test gaps (nit). These mutants pass all of test_catalog_bundle_unreferenced.py and test_catalog_validate_scripts.py, and each flips a warn on a realistic SKILL.md: _mentions checking only the first occurrence (i = -1 at bundle_refs.py:116; "Run rebuild.py and then build.py" with both bundled), the .- preceding set at :141 emptied (my-build.py names build.py), after == "-" removed at :113 (build.py-old), UnicodeDecodeError dropped from the except at :190 (a named non-UTF-8 document then ends in an unreadable row), the SKIPPED_DIRS clause at :75 (a non-test_* file in tests/ or Tests/), the suffix guard in _read_doc at :184 (a named .py is read as a document), and suffix.lower() at :83 (A.PY). One test case each would pin them.
Checked, no issues
- Unreadable documents: an oversize, non-UTF-8, symlinked or FIFO named document gives no claim, each guard killed by a test.
- Exclusions and registry: data, images, readme,
AGENTS.md,_helpers, tests,evals/, hidden and vendored folders and nested skills are left out as documented;bundle-file-unreferencedis inFINDING_CODESand--ignore bundle-file-unreferencedremoves it; the changelog fragment is under Added. - Repo rules: every non-merge commit has a
Signed-off-by; the growth ratchet and docs tests pass; no schema or generated files in the diff.
Bot points I checked that no longer apply at this head (1)
- exai-bot's point on
bundle_refs.py:49does not apply: the comment holds only the word "placeholder", so it names no defect.
| percent-encoding decoded), lowercased. | ||
| """ | ||
| parts = rel.lower().split("/") | ||
| if "." in links or "/".join(parts) in links or any("/".join(parts[:i]) in links for i in range(1, len(parts))): |
There was a problem hiding this comment.
A [back](../) link in a nested document names every bundled file. _target("references", "../") normalizes to ".", and this line reads "." in links as "the skill root is linked", which was meant for SKILL.md linking its own root.
Repro (SKILL.md: see references/g.md; references/g.md: [back](../); bundled scripts/run.py, named nowhere): unreferenced_message returns None. With [up](../) removed from the guide it returns 1 of 2 ... never named. A back-link at the end of a reference page is common, so it hides the warn for the whole skill.
Suggestion: treat "." as the root only for links from SKILL.md (pass linked for the root separately from the links of read documents), or only when the document's folder is ".". Add a test with a back-link in a nested document.
There was a problem hiding this comment.
Waiting on a decision before I change this: should a [back](../) link in a nested document name every bundled file? It does today, since ../ from references/ is the skill root. Options: (a) only a root link in SKILL.md names every file; (b) keep it, and the pinned test test_a_nested_link_to_the_skill_root_names_the_files_under_it stays. My recommendation: (a).
There was a problem hiding this comment.
Thanks for asking. Option (a): only a root link in SKILL.md names every bundled file; a root link in a nested document does not. Please change the pinned test test_a_nested_link_to_the_skill_root_names_the_files_under_it to match, and say in the docs and the changelog fragment that a root link in SKILL.md silences the check.
…le-file-unreferenced Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com> # Conflicts: # dokimi/dokimi/external/validate.py
A backslash before _, * or ~ is a Markdown escape, not a folder separator,
so "Run my\_script.py" names my_script.py. A [home](${CLAUDE_SKILL_DIR}/)
link names the skill root as [home](./) does. The test-gap nit lists six
edges the suite did not pin: each has a test now.
Thread: PRRT_kwDOVBg7g863fFOJ
Thread: PRRT_kwDOVBg7g863fFOO
Thread: PRR_kwDOVBg7g88AAAABX_eBaw
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ount The Windows-path test used a leading underscore, which is never counted, so it could not fail. Use a ~ name: the escape reading drops the separator, so only the separator reading names it. Thread: self-review pass 3, nit 1 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The bundled metadata.md in these cases is never named, so bundle-file-unreferenced warns about it too. Assert both codes. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The test named an underscore case but pins a tilde name. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
|
|
Re: review by vlad-perevezentsev
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 0d83bf9. The escape fix and the ${CLAUDE_SKILL_DIR}/ root-link fix both work, but my earlier point on the ../ back-link is still open, so I am not approving yet. Two nits inline.
What I ran, on a Linux review host at 0d83bf9:
make ci-local: 32 passed, 1 failed, 14 skipped; 20,534 tests; changed-line coverage 98%. The one FAIL, the fidelity golden, ran every test to completion and then died in pytest's own temp-dir cleanup (ResourceWarning: unclosed scandir iterator); the installed Claude Code is also not the pinned one. The PR does not touch either.- Reproductions for the escape reading (
my\_script.py,a\*b.py,x\~y.py, Windowsscripts\_helper.py, nested documents with.\and..\prefixes) and for the root links, with no model or network calls. - A mutation run on the two changed pieces: 22 mutants, 12 pass both changed test files; 6 of those are real gaps and the two inline nits cover them.
Still open from my earlier review:
dokimi/dokimi/external/bundle_refs.py:141: a[back](../)link in a nested document still names every bundled file. At this head,SKILL.md=see references/g.md,references/g.md=[back](../), bundledscripts/run.pynamed nowhere:unreferenced_messagereturnsNone; with[back](./)in the guide it warns1 of 2. Take option (a) from the thread: only a root link inSKILL.mdnames every file, and change the pinned testtest_a_nested_link_to_the_skill_root_names_the_files_under_it. Whichever way it goes, the docs and the changelog fragment should say that a link to the skill root silences the check; neither does now.
Checked, no issues
- Escapes:
my\_script.py,a\*b.pyandx\~y.pyname their files;scripts\_helper.pystill namesscripts/_helper.py; the joined two-reading text makes no match across the join and the control cases still warn. - Root link:
[home](${CLAUDE_SKILL_DIR}/)and the form without the slash now name the root like./; the new tests fail against the previous head'svalidate.py. - Repo rules: every non-merge commit has a
Signed-off-by;bundle-file-unreferencedis inFINDING_CODES; the changelog fragment is under Added;validate.pyis 786 of 800 lines in the growth ratchet; no generated files.
Bot points I checked that no longer apply at this head (1)
- exai-bot's point on
bundle_refs.py:49does not apply: the comment holds only the word "placeholder", so it names no defect.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
antonwolfy
left a comment
There was a problem hiding this comment.
I pushed a commit per nit, up to 00ae79b, which fix the two nits from my review:
a96b4d0:dokimi/tests/test_catalog_bundle_unreferenced.py:547: added themy\*script.pycase to the escape parametrize list.00ae79b:dokimi/tests/test_catalog_bundle_unreferenced.py:554: added the five root-link destinations (angle brackets, percent-encoding, backslash escape,#top,?a=1) to thedestparametrize list.
make ci-local at 00ae79b: 33 passed, 0 failed, 14 skipped.
Not approving yet. Still open: the earlier thread of mine above nit level.


Summary
New
warn, one per skill:bundle-file-unreferenced. It counts bundled scripts and documents (.py .sh .js .ts .md .txt …) that neitherSKILL.mdnor a document it names mentions, by path, file name or folder alone, with case ignored. The message gives the count and three example paths. Data, images, licence and readme files,AGENTS.md,_-prefixed helpers, tests, vendored folders and nested skills are not counted, and a skill at the checkout root is not checked. A document in a test or hidden folder thatSKILL.mdnames is still read. A changelog, contributing or notice file supplies no references. A named document the search cannot read (over 256 KiB, not UTF-8, a symlink or a FIFO) gives no claim.Evidence
SKILL.md, 63% of.py, 45% of.shand 47% of.mdfiles are never named in it.tests/test_catalog_bundle_unreferenced.py: 73 pass; the 360-file timing case runs under its 30 s CPU bound. Self-review of the latest round: no blocking finding, nits recorded in the review.Merge danger
Conflicts with the other PRs in this series on
FINDING_CODESand the docs.🤖 Generated with Claude Code