From db16de1a81ffd17a756e6f8ac6520d343682785b Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Thu, 6 Aug 2026 17:41:38 -0500 Subject: [PATCH] fix(sync-skills): guard the pre-existing prune against an empty prefix (#1087) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit prune_stale_links_in_target compared "$resolved" == "$canonical_real/"* while length-checking only $resolved. If $canonical_real were ever empty the pattern collapses to == "/"* and matches every absolute path. The failure is precisely inverted, which is what makes it worth fixing rather than noting: is_mosaic_skill_name already `continue`s for names that ARE current mosaic skills, so an empty prefix would delete exactly the FOREIGN symlinks in every target directory and preserve the mosaic ones. On this host that is 4 base installs, including codex's own .system entry. Reported by mos-claude as #1087 after I introduced the same guard in the new legacy-cleanup path in the previous commit and walked past this instance thirty lines away. Same defect class, same file, one function apart. $canonical_real is populated by readlink -f after a mkdir -p, so an empty value requires readlink to fail — unlikely, but the consequence is deleting operator symlinks across every harness, which is not a risk worth carrying for one test. --- .../mosaic/framework/tools/_scripts/mosaic-sync-skills | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills b/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills index 0a9d02f9..e57aad17 100755 --- a/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills +++ b/packages/mosaic/framework/tools/_scripts/mosaic-sync-skills @@ -262,7 +262,12 @@ prune_stale_links_in_target() { # -m resolves lexical dangling targets too. If resolution fails, ownership # is unproven and the link must be preserved. resolved="$(readlink -m "$link_path" 2>/dev/null || true)" - if [[ -n "$resolved" && "$resolved" == "$canonical_real/"* ]]; then + # $canonical_real must be length-checked BEFORE use as a prefix: if it were + # ever empty, "$resolved" == "$canonical_real/"* collapses to == "/"* and + # matches every absolute path. Combined with the is_mosaic_skill_name skip + # above, that inverts the function precisely — it would delete exactly the + # FOREIGN symlinks and keep the mosaic ones. (#1087, reported by mos-claude.) + if [[ -n "$resolved" && -n "$canonical_real" && "$resolved" == "$canonical_real/"* ]]; then rm -f "$link_path" echo "[mosaic-skills] Removed stale retired skill link: $link_path" fi