fix(skills): finish the skill-relative path contract below SKILL.md (#474 finding 1) - #482
Merged
clark-cant merged 1 commit intoSep 3, 2026
Conversation
Refs #474 (finding 1 follow-up to #476, which rewrote design/SKILL.md and added a contract that grepped only */SKILL.md; the same defect one level down survived). - 29 home-rooted paths (~/.claude/skills/design/scripts/...) in design/references/{cip,icon,logo}-design.md -> scripts/... (27 at review time, two more added by #470); the printed hint in design/scripts/cip/generate.py now derives the absolute path from __file__ - 19 project-rooted invocations (.claude/skills/<skill>/scripts/...) in brand/, slides/ and design/ references -> scripts/... (own skill) or ../<skill>/scripts/... (sibling sub-skill; sub-skills are installed side by side in every layout) - brand/scripts/sync-brand-to-tokens.cjs resolved its sibling script from process.cwd(), silently skipping CSS regeneration under plugin and --global installs; now resolved from __dirname, with a warning when the sibling skill is missing; regression test asserts the regeneration - brand/scripts/extract-colors.cjs: tool-neutral hint instead of a project-rooted path into a skill this plugin does not ship - "Script Paths" section in the five sub-skills that invoke scripts: script path from the skill directory, working directory at the project root - new test_skill_script_paths.py (src, mirrored to both scripts/tests copies): every python/node/bash invocation in every shipped skill markdown must be skill-relative and name a file that ships - check-asset-sync.yml: contract covers every file under both skill trees and home-/project-/variable-rooted forms; ${CLAUDE_PLUGIN_ROOT} allowed only in the plugin-only core SKILL.md; LC_ALL=C + -I for the tracked .coverage binary; grep errors fail instead of passing; push filter includes the workflow and sync-assets.mjs - CLI copy regenerated via sync-assets.mjs Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
notbucki
force-pushed
the
fix/skill-paths-references-and-contract
branch
from
September 3, 2026 05:56
a92bc18 to
44bd797
Compare
clark-cant
approved these changes
Sep 3, 2026
clark-cant
left a comment
Contributor
There was a problem hiding this comment.
Approved: focused follow-up for #474 finding 1. The change removes install-specific invocation anchors across shipped skill documentation, validates the shipped paths, preserves asset synchronization, and the required checks are green. Findings 2 and 3 remain tracked in #474.nnPosted by github-maintain cron
This was referenced Sep 4, 2026
Open
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.
What does this PR change?
Finishes the path contract started in #476: the same install-specific paths one level below
SKILL.md(references/, two scripts), a "Script Paths" statement per sub-skill, a positive test that every shipped markdown invocation resolves, a regression test for the code path that silently skipped, and a CI contract that covers both skill trees and every rooted form.Why?
Refs #474 — finding 1 follow-up; findings 2 and 3 stay open there. #476 rewrote
design/SKILL.mdand its contract grepped only*/SKILL.md, so 27 home-rooted paths indesign/references/(29 after #470 merged), 19 project-rooted invocations acrossbrand/,slides/anddesign/references and two code paths inbrand/scripts/kept the defect (see the review in #474).Matches the 2026-09-03 re-triage in #474: one install-context-safe mechanism (skill-relative paths built from the skill directory), applied to SKILL.md and references in both shipped copies, a test covering every shipped markdown invocation,
check:assetsretained; workflow-reference and count fixes left out. The working directory stays at the project root rather than becoming the skill directory, because the scripts read and write project files (docs/brand-guidelines.md,assets/design-tokens.json, argument paths) fromprocess.cwd()— the contract documents where the path comes from.Details
~/.claude/skills/design/scripts/...): 29 indesign/references/{cip,icon,logo}-design.md(27 at review time plus the two--provider muapilines feat(design): add opt-in MuAPI logo provider #470 added) →scripts/...; the printed hint indesign/scripts/cip/generate.pynow prints the absolute path derived from__file__, so it is correct from any cwd..claude/skills/<skill>/scripts/...) inbrand/references/,slides/references/anddesign/references/→scripts/...for a skill's own scripts,../<skill>/scripts/...for a sibling sub-skill (design → brand / design-system, slides → design-system). The sub-skills are installed side by side in every layout — plugin cache, andcopySubSkillsincli/src/utils/template.tsfor CLI installs — so the sibling form resolves wherever the skill itself does.design/references/slides-*.mdare byte-identical duplicates ofslides/references/*.md; both copies are patched in lockstep.brand/scripts/:sync-brand-to-tokens.cjsresolvedgenerate-tokens.cjsfromprocess.cwd(), so under a plugin or--globalinstall the CSS regeneration was silently skipped (existsSyncfalse); it now resolves from__dirnameand warns when the sibling is missing instead of skipping silently.extract-colors.cjsprinted a concretegemini_batch_process.pyinvocation under a project-rooted path intoai-multimodal, a skill this plugin does not ship; replaced by a one-line hint that names ai-multimodal only as an optional example. That line borders finding 2 — drop it if you would rather handle it there.src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py(mirrored to bothscripts/testscopies bysync-assets.mjs, run by thetests.ymlpytest job and bytest:python): extracts everypython|python3|node|bash <path>invocation from every markdown file under.claude/skills/andcli/assets/skills/(183 today) and asserts each is skill-relative (scripts/<file>,../<skill>/scripts/<file>, or the core skill's${CLAUDE_PLUGIN_ROOT}/...resolved against the repo root) and names a file that ships. On the base08b2e54it reports 96 problems (48 per copy; 92 on58c220f); on this branch 0.test_sync_brand_to_tokens.pynow asserts that the CSS regeneration ran — fails on the base (the step was skipped), passes on this branch.check-asset-sync.yml(step renamed "Path contract - no install-specific skill paths"): every file under.claude/skills/andcli/assets/skills/; home-rooted, project-rooted and variable-rooted forms ($HOME/...,${PWD}/...);${CLAUDE_PLUGIN_ROOT}/...allowed only in the plugin-only.claude/skills/ui-ux-pro-max/SKILL.md: a third check pins the token to that file (and to the checker that names it), and the positive test accepts the form only there — sub-skills also ship through the CLI, where the variable does not exist. Patterns require a path into a named skill, so prose that does not name a skill afterskills/(e.g. the install-context comments indesign-system/scripts/fetch-background.py) is not a hit; prose that does is flagged like code.LC_ALL=C+-Iskipui-styling/scripts/.coverage, a tracked SQLite file whose recorded absolute paths would otherwise match, while an offending line with a stray non-UTF-8 byte is still printed rather than suppressed. A grep error (rc 2, e.g. an unreadable file) fails the step instead of passing it; a missing skill tree is caught by an explicit check before grep runs. The push filter now includes the workflow itself andsync-assets.mjs. Not covered, documented in the step: backslash-separated Windows spellings and the platform-root-relativeskills/<skill>/...form — the positive test above catches any invocation of those shapes, since neither resolves.cli/scripts/sync-assets.mjs;--checkpasses.Verification
08b2e54(unpatched): 60 home-rooted + 42 project-rooted hits across both copies, exit 1 (56 + 42 on58c220f, the state reviewed in design skill: 2 plugin-install defects (hard-coded ~/.claude paths, unbundled claudekit references) + style-count drift in manifests #474). On this branch: 0 + 0, exit 0. Decoys:cd ~/.claude/skills/design && ...,${HOME}/.claude/skills/brand/...,${CLAUDE_PLUGIN_ROOT}/.claude/skills/brand/...,$(pwd)/.claude/skills/design,/home/me/.claude/skills/design/...are flagged;"${CLAUDE_PLUGIN_ROOT}/.claude/skills/ui-ux-pro-max/scripts/search.py", the bare variable form, and prose such as~/.claude/skills/, or as a pluginare not; a NUL-containing binary is skipped, a non-UTF-8 text file with a forbidden path is reported, a missing directory exits 1.python3 -m pytest .claude/skills(thetests.ymljob): 231 passed / 7936 subtests on this branch; the new positive test fails on the base with 96 problems, andtest_sync_brand_to_tokens.pywith the new assertion fails on the base's script on exactly that assertion.node --checkon both.cjs,py_compileongenerate.pyand the tests; thegenerate.pyhint prints the absolute logo script path from an unrelated cwd;sync-brand-to-tokens.cjsregeneratesassets/design-tokens.cssfrom an unrelated cwd and warns when the sibling skill is absent.Interaction with open PRs
08b2e54) while this PR was being prepared; rebased onto it. Its two new--provider muapilines inlogo-design.mdused the home-rooted form and are rewritten here as well — on the base, the contract and the positive test flag exactly those..coverage—-Ithen stays useful for local runs, where__pycache__/*.pycrecord absolute source paths).Out of scope
.claude/skills/ui-ux-pro-max/scripts/search.py— the CLI project-install convention; plugin users get the core skill's hand-authored${CLAUDE_PLUGIN_ROOT}form, and CLI installs getscriptPathrendered per install mode bytemplate.ts— both by design.design-system/scripts/generate-slide.pyderives the project root asPath(__file__).resolve().parents[4], which only holds for a project install — separate fix (reuse_find_project_root()fromfetch-background.py).Checklist
src/ui-ux-pro-max/(source of truth), not directly in.claude/or.factory/— the new test lives insrc/ui-ux-pro-max/scripts/tests/and is mirrored bysync-assets.mjs; the sub-skill edits are in.claude/skills/<skill>/, which is their source of truth (mirrored tocli/assets/skills/)npm run sync:assets && npm run check:assetsincli/src/ui-ux-pro-max/scripts/tests/test_skill_script_paths.py,.claude/skills/brand/scripts/tests/test_sync_brand_to_tokens.py)mainResearched and implemented with Claude Code (an AI coding agent); a human reviewed the changes and approved the submission.
🤖 Generated with Claude Code