From abd81f939a762f028c158524b7ab23ec8d2c7363 Mon Sep 17 00:00:00 2001 From: Mathias Date: Wed, 26 Aug 2026 07:53:14 +0200 Subject: [PATCH] fix(doctor): report a real harness directory as unmanaged, not as "outside" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found on flamingo during the first Mac install. ~/.config/crush/skills held real directories from a pre-plugin install, and doctor said: FAIL outside: ~/.config/crush/skills resolves to ~/.config/crush/skills, outside the active checkout ~/.local/share/skills/skills Naming a directory as its own resolution target is true and useless. Worse, it pointed at the wrong fix: "outside" means a link into a foreign checkout, which you repair by re-pointing the link. A real directory is repaired by moving it aside and re-running install.sh — install.sh already refuses to delete it, correctly, since it did not create it and cannot know what is in there. Now reported as its own class, with the skill count so it is obvious whether the directory holds a stale copy of the library or something unique that belongs in the repo: FAIL unmanaged: is a real directory, not a link into the checkout — it holds N unmanaged skill(s); move it aside and re-run install.sh The distinction matters because content in that directory reaches exactly one harness and cannot receive new skills — the failure mode is silent divergence, which is the same family as everything else this tool exists to catch. Bump-Type: patch Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m --- bin/doctor | 9 +++++++++ tests/doctor_test.sh | 13 +++++++++++++ 2 files changed, 22 insertions(+) diff --git a/bin/doctor b/bin/doctor index ceab5df..784bdf5 100755 --- a/bin/doctor +++ b/bin/doctor @@ -109,6 +109,15 @@ do continue fi + # A real directory where a link belongs holds content nothing manages: it + # cannot receive new skills and silently shadows the checkout. Distinguished + # from the foreign-checkout case below because the fix differs — move it + # aside and re-run install.sh, rather than re-point a link. + if [ ! -L "$dir" ] && [ -d "$dir" ]; then + add FAIL unmanaged "$dir is a real directory, not a link into the checkout — it holds $(count_skills "$dir") unmanaged skill(s); move it aside and re-run install.sh" + continue + fi + # Resolve to a physical path and confirm it lands inside THIS checkout. # A link that resolves to a different checkout is the dangerous case: it # works, so nothing complains, while serving stale content. diff --git a/tests/doctor_test.sh b/tests/doctor_test.sh index f18eb8d..3265961 100644 --- a/tests/doctor_test.sh +++ b/tests/doctor_test.sh @@ -112,6 +112,19 @@ mkdir -p "$f/home/.claude/skills" ln -sfn "$f/ck/skills/alpha" "$f/home/.claude/skills/alpha" assert_fails "$f" 'legacy' 'leftover per-skill Claude symlinks are reported' +# --- 7b. harness dir is a REAL directory, not a link into the checkout ------- +# Found on flamingo 2026-08-26: ~/.config/crush/skills held real directories +# from a pre-plugin install. The old message read "resolves to +# , outside the active checkout" — naming the directory as its own +# resolution target, which is true but useless. It must say the dir is not a +# link at all, because the fix (move it aside, re-run install.sh) is different +# from the fix for a link into a foreign checkout. +f=$(new_fixture realdir) +mkdir -p "$f/home/.agents" "$f/home/.config/crush/skills/alpha" +ln -sfn "$f/ck/skills" "$f/home/.agents/skills" +printf -- '---\nname: alpha\ndescription: a\n---\n' > "$f/home/.config/crush/skills/alpha/SKILL.md" +assert_fails "$f" 'not a link' 'real directory is reported as unmanaged, not as "outside"' + # --- 7. dangling link inside a harness dir (not the canonical link itself) -- # Exercises the per-entry scan rather than the canonical-link check: this is # the shape the 243 dead links actually had.