Repository navigation
siblings.py and shape-doctor.py: a Windows junction to nothing at ../<Project> is NOT A CLONE, as a link to nothing is, so the dry run no longer says would clone and the doctor's members row no longer calls the slot absent (#793) - #794
Conversation
…, at `../<Project>` is NOT A CLONE, reported and left as it is, so the run no longer ends in a traceback at `iterdir` before the members after it are placed, and `make park` no longer ends in one over a folder it cannot enter (#735) `place` asked only whether a folder had something in it: `iterdir` raised `NotADirectoryError` for a file and `PermissionError` for a folder with mode 0311 or 000, a traceback, exit 1. `not_a_clone` now gives the finding's words for each slot nothing is cloned into: the folder of files keeps its finding byte for byte, a file "is a file, not a folder", and a folder it cannot list "is a folder that cannot be listed", the words the doctor's `members` row says of the same slots. `holds_git` asks for a `.git` and reads an `OSError` as none, for `place` and for `dispatch` (`--make`), which Python 3.12 answered with `PermissionError` in a folder that cannot be entered. The doctor's agreement test (#705) now passes for a file and for both locked folders, so its strict xfail is gone; the doctor's output is unchanged, and its comments now say a holder pinned before this change still ends in the traceback. `siblings.py` is a family-root copy carried by digest: a holder pinned before this change shows an `upstream-changed` row for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is NOT A CLONE, reported and left as it is, so the dry run no longer says `would clone` where the real run ends CLONE FAILED, and the doctor's `members` row no longer calls the slot `absent` and says `make siblings` places the member there (#784) `exists` follows a link, so a dangling one read as nothing at all. In `siblings.py`, `not_a_clone` now answers "is a link to nothing" for it, in the finding the other NOT A CLONE slots get. In the doctor, `unclonable` answers `dangling`, so `sibling_slot` says `not-a-clone`, and the row says "is a link to nothing" and nothing of what `make siblings` does there: a holder pinned before this change still ends CLONE FAILED on it. `make park` and `make resume` keep `SKIPPED no clone`. `siblings.py` is a family-root copy carried by digest: a holder pinned before this change shows an `upstream-changed` row for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…/<Project>` is NOT A CLONE, as a link to nothing is, so the dry run no longer says `would clone` where the real run ends CLONE FAILED, and the doctor's `members` row no longer calls the slot `absent` (#793) #784 told a link to nothing from an empty slot with `is_symlink`, which says no to a directory junction, the link Windows makes without privilege. `not_a_clone` in `siblings.py` and `unclonable` in the doctor now also ask `os.path.isjunction`, spelled as `family_link` spells it, `getattr(os.path, "isjunction", lambda _: False)`, since Python before 3.12 has none. Both still word it "is a link to nothing", and each script stays self-contained. The tests simulate the junction as the family-folder junction test does: the slot does not exist and only `os.path.isjunction` says yes. The placement test mounts the member from a local path that does not exist, so a run that took the slot for an empty one fails its clone offline. `siblings.py` is a family-root copy carried by digest: a holder pinned before this change shows an `upstream-changed` row for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR extends existing dangling-symlink handling to Windows junctions by conditionally using Sequence diagram for Windows junction handling in sibling placementsequenceDiagram
participant User
participant Siblings as siblings.py
participant OS as os.path
participant Slot as SiblingSlot
User->>Siblings: place(...)
Siblings->>Slot: exists()
Slot-->>Siblings: false
Siblings->>OS: isjunction(Slot)
OS-->>Siblings: true
Siblings-->>User: NOT A CLONE: is a link to nothing
Siblings-->>Slot: leave junction unchanged
Sequence diagram for shape doctor junction diagnosticssequenceDiagram
participant Doctor as shape-doctor.py
participant Slot as SiblingSlot
participant OS as os.path
Doctor->>Slot: exists()
Slot-->>Doctor: false
Doctor->>OS: isjunction(Slot)
OS-->>Doctor: true
Doctor-->>Doctor: unclonable() returns dangling
Doctor-->>Doctor: sibling_slot() returns not-a-clone
Doctor-->>Doctor: members row says is a link to nothing
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-17T10:00:51Z) Independent review of #794 by RV-d (opus), on head Checked:
NITs:
Mutants:
Runs:
Merges: clean with main Lane: openreposhape-3 (openRepoShape-3) |
… junction to a folder at `../<Project>` is read as that folder, so a junction check moved above `exists()` fails a test (#793) Review round 2 (NIT N2 from RV-d on #794): no test pinned the junction check after `exists()`, and both mutants that ask it first survived. A junction to a live folder is now asked about too, with `os.path.isjunction` saying yes for a slot that exists: - an empty folder: the dry run says `would clone`, and the doctor's row says `absent`, with the note that `make siblings` places the member; - a folder holding files: `place` gives the finding a folder of files gets ("exists, is not empty and is not a git clone"), and the row says `not-a-clone` in a folder's words. The setup the round-1 tests had is shared through `placed_at_a_junction` and `refused` in the placement tests and `junction_row` in the doctor's, and those tests assert what they did. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-17T10:12:20Z) #794's NIT round,
Lane: openreposhape-3 (openRepoShape-3) |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…is NOT A CLONE, reported and left as it is, so the dry run no longer says `would clone` where the real run ends CLONE FAILED, and the doctor's `members` row no longer calls the slot `absent` (#784) (#788) Closes #784. A dangling symbolic link at `../<Project>` read as an empty slot, because `exists` follows a link. `make siblings --dry-run` said `would clone`, the real run ended `CLONE FAILED` (git cannot clone onto a link to nothing, exit 128), and the doctor's `members` row called the slot `absent` and said `make siblings` places the member there. Now `not_a_clone` in `templates/family-root/scripts/siblings.py` answers "is a link to nothing", so `place` reports `NOT A CLONE`, in the finding the other such slots get, and leaves the link as it is. `make park` and `make resume` keep `SKIPPED no clone`. In `shape-doctor.py`, `unclonable` answers `dangling`, so `sibling_slot` says `not-a-clone` and the row says "is a link to nothing", and the doctor and `make siblings` agree again. `tests/test_shape_doctor.py` and `tests/test_family.py` hold a new slot, `a-link-to-nothing`, skipped on Windows. Lane 3's independent reviewer RV-d (opus) found it READY on `e966a684`, then stacked on #772 (#788 issuecomment-6230059337). Its optional NIT N2, that a dangling Windows junction still reads as an empty slot, is #793, in #794. After #772 landed as `112eeed2`, lane 3 restacked the branch as `bec71bb5` (#788 issuecomment-6243557591), in three merges: `f204abe1` takes #772's final head `bcb8e54a`; `5f7ea9b3` takes the squash `112eeed2`, ours for #772's four files, on which `git diff bcb8e54 112eeed` is empty; `bec71bb5` takes main `e62b5e17`. Against main the diff is #788's four files, the delta RV-d reviewed, `1d149933..e966a68`, patch for patch (+120/-53). On `bec71bb5`, the nine family and siblings files of its gate set pass (607 passed) and `tests/test_shape_doctor.py` passes (441 passed, 3 skipped). The restack carries lane 3's own check, not a reviewer's verdict. It lands on Brett Heap's word "merge lane 3's v1.1.1 drafts after the v1.1.0 tag" (2026-10-17T13:00:57Z). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ep 1 of 3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 1 day and 16 hours by commenting |
… RV883 and run to main c5359df, and the back-port comment names ruleset 25325536 (#883) Round 2 of #883, on lane 3's review RV883 of ede678c (#883 issuecomment-6247451611). Each finding was checked against the code at main c5359df before the text changed, and each held: - `plan written to` is printed after a blank line and before the report, not first: an org/repo source prints its `cloning` line before it. - The pipx command is unchanged since v1.1.0 (`src/` is). Measured on the package at c5359df, outside a checkout: its `--version` prints a `$OPENREPOSHAPE_REPO` holding U+202E, exit 0, and its refusal of such a `$OPENREPOSHAPE_REF` holds the character. The notes say "the bash `openRepoShape`" and carry this as a known limit. - `family.py init`'s closing block and `Working trees are at` still print a path raw; `make siblings` has no unmerged check, and clones from `family.yaml` where git finds no url; `update-shape.py apply --branch` meets `unseen-branch` after its writes; a WHY still shows `?` and `--json` is ASCII with escapes; a directory in no repository is still `git-failed`. Each is now said as the code does it. - The refresh adds #843 (#803), #811 (#777) and #794 (#793): 50 pull requests and 50 issues, with 20 milestone issues still open. - The release sections name Brett Heap's ruling, option A and ruleset 25325536, add a step 0, define `<notes>` with a grep a simulation shows prints nothing, and check `gh release list` before `--latest`. release.yml: lines 74-76 of the RELEASING comment said the ruleset waited on Brett Heap's word. They now name ruleset 25325536. Only comment lines change; the YAML is identical with comments removed. The upgrade check was run again at c5359df on fresh clones of a v1.1.0 project and holder: six `upstream-changed` copies each, `apply` green, and the doctor COMPLIANT. Lane: openreposhape-1 (openRepoShape-1) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-3 (openRepoShape-3)
Claim: #793 (comment)
Closes #793.
Stacked on #788 (#784). Lands after the v1.1.0 tag.
Round 2 (NIT): RV-d's N2. A junction to a live folder is now tested in both tools, so the junction check is pinned after
exists(): all four mutants that ask it first fail (2ffe154c, tests only). N1, the commit subject, is left as it is, since the squash takes this PR's title.What changed, and why
#788 tells a link to nothing from an empty slot with
Path.is_symlink(), which says no to a directory junction, the link Windows makes without privilege. So a junction to nothing at../<Project>still read as an empty slot: the dry run saidwould clone, the real run tried to clone into it, and the doctor'smembersrow called the slotabsentand saidmake siblingsplaces the member there. That is #784's bug, for a junction.Both places now also ask the junction check, spelled as
family_linkspells it (siblings.py:985on this branch; the issue's:931is main's line):getattr(os.path, "isjunction", lambda _: False). Python before 3.12 has noisjunction, so the fallback says no there. Each script stays self-contained.templates/family-root/scripts/siblings.py:not_a_clone(:471) asks it at:500-501, so a junction to nothing gets the answer a link to nothing gets, "is a link to nothing" withlinkas the kind of thing (:502). The docstring says so at:493-496.placetherefore reports the slot asNOT A CLONE, in siblings.py and shape-doctor.py: a link to nothing at../<Project>is NOT A CLONE, reported and left as it is, so the dry run no longer sayswould clonewhere the real run ends CLONE FAILED, and the doctor'smembersrow no longer calls the slotabsent(#784) #788's finding, and leaves the junction as it is. The dry run saysNOT A CLONE, notwould clone.shape-doctor.py:unclonable(:7974) asks it at:7999-8001, so it answersdangling,sibling_slot(:8009) saysnot-a-clone, and the row says "is a link to nothing" fromUNREADABLE_BESIDE(:8179,"dangling"at:8181). The doctor andmake siblingsword it alike. The docstring says so at:7992-7995.What is tested
The junction is simulated, as
test_a_family_folder_reached_through_a_windows_junction_passessimulates one, so every system asks it:os.path.isjunctionis patched (raising=False) to say yes for the slot path only. For a junction to nothing the slot does not exist; for a junction to a folder (round 2) it is that folder.tests/test_family_siblings_placement.py:test_a_windows_junction_to_nothing_at_a_slot_is_a_link_to_nothing(:617-630) runs as the dry run and as the real run. It assertsNOT A CLONE(:627), siblings.py and shape-doctor.py: a link to nothing at../<Project>is NOT A CLONE, reported and left as it is, so the dry run no longer sayswould clonewhere the real run ends CLONE FAILED, and the doctor'smembersrow no longer calls the slotabsent(#784) #788's finding word for word with "is a link to nothing" (:628), and nowould cloneprinted (:630).test_a_windows_junction_to_a_folder_at_a_slot_is_read_as_that_folder(:633-650) runs the dry run on a junction whose slot EXISTS.would clone, with no finding (:648,:650).notes.md:NOT A CLONE, with the finding a folder of files gets, "exists, is not empty and is not a git clone" (directory) (:648-650).placed_at_a_junction(:582) andrefused(:610). The member is mounted from a local path that does not exist, so a run that takes the slot for an empty one fails its clone offline and never reaches a remote.tests/test_shape_doctor.py:test_the_row_names_a_windows_junction_to_nothing_a_link_to_nothing(:4203) asserts that themembersrow saysnot-a-clone(:4213), names the slot withUNREADABLE_SAYS["dangling"](:4214), and says nothing of whatmake siblingsdoes there (PLACES_NOTE,:4216).test_the_row_reads_a_windows_junction_to_a_folder_as_that_folder(:4219-4235) checks a junction to a folder. It asserts the wordstest_the_row_says_what_make_siblings_does_with_each_slotasserts for the same folder without a junction (:4233-4235):absent, withPLACES_NOTE;not-a-clone, inSLOT_SAYS["not-a-clone"]'s words, withoutPLACES_NOTE.junction_row(:4190).Red first: with siblings.py and shape-doctor.py: a link to nothing at
../<Project>is NOT A CLONE, reported and left as it is, so the dry run no longer sayswould clonewhere the real run ends CLONE FAILED, and the doctor'smembersrow no longer calls the slotabsent(#784) #788's head's (e966a684)siblings.pyandshape-doctor.py, all three fail (3 failed, 29 deselected):'would clone' == 'NOT A CLONE';'CLONE FAILED' == 'NOT A CLONE';'absent' == 'not-a-clone'.With the fix, all three pass (
3 passed, 29 deselected).Round 2, the order: four mutants ask the junction check before
exists(), each run on its own against the seven junction cases.is_symlink() or junction()test moved above it.siblings.py: each mutant fails both placement folder cases ('NOT A CLONE' == 'would clone', and the folder-of-files finding).shape-doctor.py: each mutant fails both doctor folder cases ('not-a-clone' == 'absent', and the folder's words).2 failed, 5 passed, 29 deselected. The round-1 junction-to-nothing tests pass under all four mutants, which is why N2 found the order unpinned. With the fix as it is, all seven pass (7 passed, 29 deselected).Commands, with a stub
gh(exit 97) first on PATH,GH_TOKEN=bogusand an emptyGH_CONFIG_DIR, at2ffe154c, on #788's heade966a684:Pinned shape copies
templates/family-root/scripts/siblings.pyis a family-root copy carried by digest. A holder pinned before this change shows anupstream-changedrow for it untilupdate-shape.pybrings it forward. No other pinned copy is touched.Notes
Path.exists()says no for one, are not measured here. The tests simulate the junction.requires-pythonis>=3.9, andos.path.isjunctionis 3.12's. The fallback says no there, asfamily_link's does, as the issue asks.siblings.py's--helpis unchanged.🤖 Generated with Claude Code
Summary by Sourcery
Handle dangling Windows junctions consistently as non-cloneable sibling slots across placement and diagnostics.
Bug Fixes:
Enhancements:
Tests: