Repository navigation
fix(health-check): a symlink alias must not make a carrier entry unmeasurable - #2665
Conversation
…asurable git check-ignore rejects any pathspec that traverses a symlink (exit 128, 'beyond a symbolic link'), and the carrier probe folds 128 into UNMEASURED for the whole entry. A compat alias symlink under .claude-sutando/projects/ (space-slug project dir pointing at its dash-slug twin) therefore made the memory entry read 'could NOT measure' on every health pass — while the content was backed up the entire time at its real path. Git never stores content past a symlink, so a materialization that reaches through one is outside what the vault could ever carry: filter it out of _carrier_representatives. Probing the crossing link itself instead would trade the false UNMEASURED for a false DROPPED (dir-only un-ignore patterns like !projects/*/ cannot match a symlink; measured on the live host). Four tests: the live alias shape, a glob matching a symlinked directory itself, a pin that the tree walk does not follow directory symlinks, and a discriminator control proving a genuinely dropped sibling still fails. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KiaMLCxgKNAbmhty29WGN6
bassilkhilo-ag2
left a comment
There was a problem hiding this comment.
Reviewed exact head a573b14 in a scratch clone.
Mutation test confirms the fix is load-bearing and correctly scoped. Reverted _carrier_representatives to drop the _reaches_through_symlink filter — exactly 2 of 49 tests fail (test_a_symlinked_project_ALIAS_does_not_make_the_entry_unmeasured, test_an_entry_matching_a_symlinked_DIRECTORY_itself_stays_measured), nothing else. Restored, re-ran: 49/49.
Discriminator control verified separately (test_a_symlink_does_not_MASK_a_genuinely_dropped_sibling): a symlink alias sitting next to a genuinely un-carried sibling still fails correctly — the fix filters the false-UNMEASURED case without softening real drops. And the file-vs-directory distinction in _reaches_through_symlink (a symlink to a FILE with a real parent chain is deliberately left unfiltered, since git can store that pathspec) is exercised by the existing symlinked-subdir test staying green.
Root-cause reasoning holds up: git genuinely can't store content past a symlink (fatal: pathspec '...' is beyond a symbolic link, exit 128), so folding that into UNMEASURED was masking a real backup as unknown rather than confirmed. The alternative considered and rejected (probing the crossing link itself) is correctly identified as trading a false UNMEASURED for a false DROPPED — dir-only un-ignore patterns can't match a symlink, so that path would read as ignored/dropped even when the real content is fine.
CI green except tsc + tests and diff coverage still running at review time. Mergeable. No blocker.
john-the-dev
left a comment
There was a problem hiding this comment.
Reviewed exact head a573b14777b36c9627abdfb94f8f7c5d66156058.
Evidence:
- Independently reproduced the live alias shape against
origin/main:check_carrier_set_enforced()returnedfail/could NOT measure ...; the same fixture at HEAD returnsok. - Activated same-entry discriminator: with the real wildcard sibling genuinely still ignored and the alias present, HEAD returns
failnamingproj/*/memory/rather than letting the filtered alias mask it. python3 tests/health-check-carrier-set-enforced.test.py: 49/49 pass.- Every
tests/health-check*.test.pyfile: 51/51 pass. py_compile,git diff --check, repository REVIEW hardcoded-path check, and independent added-line path scan pass.
Worst-case disruption: filtering a symlink materialization could hide a real dropped path. The implementation filters only paths beyond a directory symlink, retains real sibling representatives, and the same-entry dropped discriminator stays fail-closed. No mutation/sync path changes.
APPROVE for the code at this exact head. Not yet merge-ready: required clean-install and Python diff-coverage jobs are still in progress, and repository policy still requires another formal maintainer approval.
Coverage Gate✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 77%. Diff CoverageDiff: origin/main...HEAD, staged and unstaged changes
Summary
|
|
Reviewed exact head No blocking findings. The symlink-alias filter is narrowly scoped to directory-symlink materializations that Git cannot measure past, while preserving file-symlink behavior and the genuinely dropped sibling failure path. Focused verification passed: APPROVE - code-ready pending the remaining hosted check. Reviewed by Qingyun's Personal Codex. |
|
@cla-assistant check |
What
git check-ignorerejects any pathspec that traverses a symlink (exit 128,fatal: pathspec '…' is beyond a symbolic link), and the carrier probe folds any non-0/1 exit into UNMEASURED for the whole entry. A compat alias symlink under.claude-sutando/projects/(a space-slug project dir pointing at its dash-slug twin, created for cross-slug session resolution) is matched by the.claude-sutando/projects/*/memory/glob, so the memory entry readcould NOT measure … UNKNOWN, not fineon every health pass — while the content was backed up the entire time at its real path, which the same entry probes via the twin's own materialization.Fix
Git never stores content past a symlink — at most the link entry itself — so a materialization that reaches through one is outside what the vault could ever carry.
_carrier_representativesnow filters matches whose path is a symlinked directory or crosses a symlink below the workspace (_reaches_through_symlink), for both the stale and dropped branches (the single-witness form delegates to it).Why not probe the crossing link itself: that trades the false UNMEASURED for a false DROPPED. Measured on the live host — dir-only un-ignore patterns (
!projects/*/) cannot match a symlink, so the link reads ignored:A symlink to a FILE with a real parent chain is deliberately not filtered: git accepts that pathspec and file rules match it, so existing behavior stands.
Evidence
Repro on the parent commit (live workspace, alias symlink present):
New tests on the parent commit (first two FAIL with the exact live symptom, control passes):
At HEAD:
Four new tests: the live alias shape, a glob matching a symlinked directory itself, a pin that the tree walk does not follow directory symlinks (passes today; guards a future walk change from re-opening the class), and a discriminator control proving a genuinely dropped sibling still fails alongside a symlink.
Scope
Probe-only; no change to sync, exclude generation, or any live path. Single concern.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KiaMLCxgKNAbmhty29WGN6