fix(issue-21): review ffb5055e R1-R3 - re-check the tree after --stop, refuse submodule stashes, roll a partial submodule init back to a usable checkout
Review ffb5055e (planner-reviewer-codex-01): - R1 [P1] worktree_remove.sh: the clean-tree and submodule checks ran only before --stop, so work a stopping session wrote (or committed, or stashed) was deleted by the forced removal. The checks are one function now, run before any change and again right before the removal; any finding then refuses with exit 3 (sessions already stopped, worktree kept), and the removal method follows that latest look. - R2 [P1] lib_py/worktree.py: a submodule stash is reachable from no branch and leaves the status clean, and the forced removal deletes the worktree's own submodule repositories. `git stash list` in every submodule is a separate finding (`stashes`), refused like dirty/unpushed; errors fail closed as before. - R3 [P2] worktree_prepare.sh: when one submodule cloned and a later one failed, the rollback deleted modules/ while the cloned checkout's .git file still pointed into it, and `git status` failed in the worktree. The rollback now undoes only this attempt: each submodule whose git dir did not exist before is deinitialised before its git dir is deleted; submodules initialised earlier are kept; shared submodule.* config values are restored exactly (new keys removed, pre-existing values put back - init and deinit both write .git/config). A final `git status` either confirms a usable checkout or prints what to inspect. Tests W6e (a write during stop keeps the worktree and the file), W6f (a submodule stash refuses), W6g (partial failure on a fresh worktree: usable, no modules/, no new config, then prepare and a plain removal work) and W6h (a failing retry keeps the earlier `sm` init and its config value). The skill docs and the design doc describe the recheck, the stash rule and the scoped rollback. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017R3Eg7MEpSWnm1oB9ZPP6G
This commit is contained in:
1 parent
7988db9adf
commit
bbbbb95ef6
7 files changed
+181
-42
No files matched your search
@@ -124,19 +124,23 @@ def open_jobs(rows: List[dict], dirs: Iterable[str], exclude_ids: Iterable[str]
|
||||
|
||||
def submodule_findings(wt: str) -> Dict[str, object]:
|
||||
"""trace: the worktree git dir holds modules/ (git then refuses a plain
|
||||
`worktree remove`, even after a failed init); dirty / unpushed: what a
|
||||
forced removal would destroy. `HEAD` is part of the unpushed check: a
|
||||
submodule is on a detached HEAD after `submodule update`, so commits made
|
||||
there are on no branch and `--branches --not --remotes` alone misses them."""
|
||||
`worktree remove`, even after a failed init); dirty / unpushed / stashes:
|
||||
what a forced removal would destroy - it deletes the worktree's own
|
||||
submodule repositories. `HEAD` is part of the unpushed check: a submodule
|
||||
is on a detached HEAD after `submodule update`, so commits made there are
|
||||
on no branch and `--branches --not --remotes` alone misses them. Stashes
|
||||
live in refs/stash (and its reflog), reachable from neither."""
|
||||
gd = _git(wt, "rev-parse", "--absolute-git-dir")
|
||||
if gd.returncode != 0 or not gd.stdout.strip():
|
||||
return {"trace": False, "dirty": "", "unpushed": "", "error": gd.stderr.strip() or "rev-parse failed"}
|
||||
return {"trace": False, "dirty": "", "unpushed": "", "stashes": "",
|
||||
"error": gd.stderr.strip() or "rev-parse failed"}
|
||||
res: Dict[str, object] = {"trace": os.path.isdir(os.path.join(gd.stdout.strip(), "modules")),
|
||||
"dirty": "", "unpushed": "", "error": ""}
|
||||
"dirty": "", "unpushed": "", "stashes": "", "error": ""}
|
||||
if not res["trace"]:
|
||||
return res
|
||||
checks = (("dirty", "git status --porcelain"),
|
||||
("unpushed", "git log --oneline HEAD --branches --not --remotes"))
|
||||
("unpushed", "git log --oneline HEAD --branches --not --remotes"),
|
||||
("stashes", "git stash list"))
|
||||
errors = []
|
||||
for key, cmd in checks:
|
||||
r = _git(wt, "submodule", "foreach", "--recursive", "--quiet", cmd)
|
||||
|
||||
Reference in new issue
Block a user