fix(skills): sort content_hash by relative posix string, not Path objects (#53404) - #53448
fix(skills): sort content_hash by relative posix string, not Path objects (#53404)#53448Tranquil-Flow wants to merge 1 commit into
Conversation
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Elegant fix for a platform-dependent hashing bug. Sorting Path objects differs from sorting relative-posix strings (directory separators sort differently), which broke symmetry between content_hash (on disk) and bundle_content_hash (in memory). The fix collects (relative_posix, path) pairs and sorts by the string key. The test layout (lib/helper.py vs lib-helper.py) provably triggers the divergence.
Reviewed by Hermes Agent
|
Thanks for the focused regression and diagnosis. The ordering bug remains on current main: Problems
Suggested changes
Automated hermes-sweeper review. |
1a1cf96 to
6bd71c5
Compare
|
Independent verification against current
Result: 239 passed in 2.78s on macOS arm64 / Python 3.11.15. This independently reproduces the issue we observed in a live Hermes install and validates the fix on the latest upstream base. Supporting this existing PR rather than opening a duplicate. |
|
I reproduced this on current Verified implementation commit: Hyperion5088@84f39d8 Changes:
Verification:
Supporting this canonical PR rather than opening another duplicate. The commit is available to cherry-pick if useful. |
6bd71c5 to
a074126
Compare
|
Confirming this on macOS. Worth flagging because the sibling PRs (#71252, #62313, #62519, #78082) all frame the divergence as a Windows path-separator problem — it reproduces on a plain macOS install with no Windows and no case collision involved. Environment: Hermes v0.20.0 (2026.8.3), macOS 26.1 (APFS), Python 3.11.15. Scanning the skill trees on a live install, 2 of 25 directories in Both are pure shared-prefix shapes. The separator Agreement between
The first shape is the same |
What
content_hash()(on-disk skill integrity hash) sortedPathobjects whilebundle_content_hash()(in-memory bundle hash) sorts the relative-posix string keys. For layouts where these orderings disagree, the two functions produced different digests for identical content, breaking the symmetry both docstrings require and corrupting skill integrity tracking / update detection.Root cause
sorted(skill_path.rglob("*"))sortsPathobjects, which compare across path parts (tuple comparison). The value actually hashed is the relative-posix string (parts joined with/). The directory separator/sorts differently inside a flat string than across tuple parts, so for a layout like:lib/helper.py(in a subdirectory)lib-helper.py(top level)…
Pathsort putslib/helper.pyfirst (('lib',)<('lib-helper.py',)), but posix-string sort putslib-helper.pyfirst ('-'(45) < '/'(47)). The bytes are fed into the SHA-256 in a different order → different digest.Fix
Collect
(relative_posix, path)pairs first, then sort by the string key that is fed into the hash — matchingbundle_content_hash, which already sorts its string keys correctly.How verified
test_content_hash_symmetric_when_path_and_posix_order_divergeuses a layout that provably triggers the Path-vs-posix divergence (asserts the layouts disagree before comparing hashes), then assertscontent_hash(skill_dir) == bundle_content_hash(bundle).main(sha256:ede3ce2c…≠sha256:a6e11ca3…).bundle_content_hashsymmetry tests still pass; fulltest_skills_guard.py+test_skills_hub.pysuite: 234 passed.Closes #53404.
Auto-published by Moonsong via Path B automated pipeline.