Raised by CodeRabbit on PR #1041 (the develop -> main promotion PR carrying #1040/#1042), declined there as out of scope for that fix chain per Pieter's explicit call, tracked here for a deliberate follow-up.
The gap
reject_symlinks() and tree_digest() in scripts/build_dist.py both walk .agents/skills/ (and the generated distribution trees) via Path.rglob("*"). CPython's pathlib glob/rglob implementation catches OSError from the underlying os.scandir() call per directory and treats an unreadable directory as empty, rather than raising. Verified against the Python 3.13 pathlib source and confirmed live with a chmod 0 probe directory during the PR's review.
Consequence: a permission-denied (or otherwise unreadable) nested directory under .agents/skills/<name>/ is silently excluded from both the symlink check and the digest walk, rather than surfacing as an error. This predates the recent --check exit-code work in #1042 (0 clean / 1 stale / 2 real failure): even with that fix, a partial digest from a silently-skipped subdirectory can still produce a misleading verdict (either a false "current" or a false "stale", neither of which is the "2, we could not actually check" the exit-code contract now promises for a genuine execution failure). It also affects regenerate(), which shares the same walk, independent of --check at all.
Possible direction (undecided)
Replace the bare rglob("*") calls with a traversal that surfaces a scan error explicitly, e.g. os.walk(..., onerror=...) or Path.walk(..., on_error=...) (3.12+), raising rather than silently continuing past an unreadable directory. Scope, and whether to touch regenerate() as well as --check, is undecided.
Raised by CodeRabbit on PR #1041 (the develop -> main promotion PR carrying #1040/#1042), declined there as out of scope for that fix chain per Pieter's explicit call, tracked here for a deliberate follow-up.
The gap
reject_symlinks()andtree_digest()inscripts/build_dist.pyboth walk.agents/skills/(and the generated distribution trees) viaPath.rglob("*"). CPython'spathlibglob/rglob implementation catchesOSErrorfrom the underlyingos.scandir()call per directory and treats an unreadable directory as empty, rather than raising. Verified against the Python 3.13 pathlib source and confirmed live with achmod 0probe directory during the PR's review.Consequence: a permission-denied (or otherwise unreadable) nested directory under
.agents/skills/<name>/is silently excluded from both the symlink check and the digest walk, rather than surfacing as an error. This predates the recent--checkexit-code work in #1042 (0 clean / 1 stale / 2 real failure): even with that fix, a partial digest from a silently-skipped subdirectory can still produce a misleading verdict (either a false "current" or a false "stale", neither of which is the "2, we could not actually check" the exit-code contract now promises for a genuine execution failure). It also affectsregenerate(), which shares the same walk, independent of--checkat all.Possible direction (undecided)
Replace the bare
rglob("*")calls with a traversal that surfaces a scan error explicitly, e.g.os.walk(..., onerror=...)orPath.walk(..., on_error=...)(3.12+), raising rather than silently continuing past an unreadable directory. Scope, and whether to touchregenerate()as well as--check, is undecided.