From 146328f4e313872a93ec80928b0939d063c96384 Mon Sep 17 00:00:00 2001 From: waiwai <511158080@qq.com> Date: Wed, 15 Jul 2026 07:29:18 +0800 Subject: [PATCH] fix(skills_guard): sort paths as strings to match bundle_content_hash ordering _content_digest() sorted Path objects via sorted(skill_path.rglob('*')), which uses POSIX component-wise comparison. bundle_content_hash() sorts string keys (sorted(bundle.files)). When a skill directory contains a file and a subdirectory sharing a name prefix (e.g. styles.md and styles/x.md), the two sort orders differ, causing the same skill content to produce different digests on each side. Fix by adding a key=lambda that converts each Path to its relative path string before sorting, so _content_digest stays symmetric with bundle_content_hash. Tag the docstring with a comment explaining why. Fixes: hash mismatch between content_hash() and bundle_content_hash() for skills with files whose names are prefixes of subdirectory names. --- tools/skills_guard.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/skills_guard.py b/tools/skills_guard.py index 47f200ab880b..9e198643282a 100644 --- a/tools/skills_guard.py +++ b/tools/skills_guard.py @@ -692,7 +692,15 @@ def _content_digest(skill_path: Path) -> str: """Canonical SHA-256 over relative paths and exact file bytes.""" h = hashlib.sha256() if skill_path.is_dir(): - for file_path in sorted(skill_path.rglob("*")): + # Sort by relative path string to stay symmetric with + # tools.skills_hub.bundle_content_hash (which sorts string keys). + # Sorting Path objects uses component-wise POSIX comparison and can + # produce a different order than string comparison when a file and a + # directory share a name prefix (e.g. styles.md vs styles/x.md). + for file_path in sorted( + skill_path.rglob("*"), + key=lambda p: p.relative_to(skill_path).as_posix(), + ): if file_path.is_file(): rel = file_path.relative_to(skill_path).as_posix() h.update(rel.encode("utf-8") + b"\x00")