fix(issue-31): a claude purge removes every path the session owns, reports what is left, and waits for the process; the stop docs say a purge removes the row #59

Merged
Godopu merged 26 commits from fix/issue-31 into main 2026-10-17 16:25:21 +00:00
2 changed files with 38 additions and 20 deletions
Showing only changes of commit 8a4f651bdf - Show all commits

No files matched your search

+37 -19
View File
@@ -5,7 +5,21 @@ from lib_py.verify_session import workspace_key
def _config_dir(ctx: DiscoveryContext) -> str: def _config_dir(ctx: DiscoveryContext) -> str:
return os.path.join(ctx.home_dir, ".claude") """claude's config root: CLAUDE_CONFIG_DIR moves all of it (sessions/,
file-history/, session-env/, jobs/, projects/)."""
return os.environ.get("CLAUDE_CONFIG_DIR") or os.path.join(ctx.home_dir, ".claude")
def _projects_dirs(ctx: DiscoveryContext) -> list:
"""ctx.claude_dir (CLAUDE_PROJECT_DIR, else <home>/.claude/projects) and
the config root's own projects/, each once."""
dirs, seen = [], set()
for d in (ctx.claude_dir, os.path.join(_config_dir(ctx), "projects")):
real = os.path.realpath(d)
if real not in seen:
seen.add(real)
dirs.append(d)
return dirs
def _tmp_roots() -> list: def _tmp_roots() -> list:
@@ -211,10 +225,12 @@ class ClaudeAgentAdapter(BaseAgentAdapter):
if not _plausible_id(uuid): if not _plausible_id(uuid):
return [] return []
cfg = _config_dir(ctx) cfg = _config_dir(ctx)
jsonl = self.artifact_path(uuid, ctx) paths = []
paths = [p for p in (jsonl, jsonl[:-len(".jsonl")], for pdir in _projects_dirs(ctx):
os.path.join(cfg, "file-history", uuid), base = os.path.join(pdir, ctx.ws_key, uuid)
os.path.join(cfg, "session-env", uuid)) if os.path.lexists(p)] paths += [p for p in (base + ".jsonl", base) if os.path.lexists(p)]
paths += [p for p in (os.path.join(cfg, "file-history", uuid),
os.path.join(cfg, "session-env", uuid)) if os.path.lexists(p)]
job = self._owned_job(uuid, ctx) job = self._owned_job(uuid, ctx)
if job: if job:
paths.append(job) paths.append(job)
@@ -264,20 +280,22 @@ class ClaudeAgentAdapter(BaseAgentAdapter):
if not _plausible_id(uuid): if not _plausible_id(uuid):
return [] return []
cfg = _config_dir(ctx) cfg = _config_dir(ctx)
found = set(scan_names(cfg, uuid, skip=(os.path.join(cfg, "projects"), ctx.claude_dir))) pdirs = _projects_dirs(ctx)
try: found = set(scan_names(cfg, uuid, skip=pdirs))
keys = sorted(os.listdir(ctx.claude_dir)) for pdir in pdirs:
except OSError: try:
keys = [] keys = sorted(os.listdir(pdir))
for key in keys: except OSError:
kdir = os.path.join(ctx.claude_dir, key) keys = []
if uuid in key: for key in keys:
found.add(kdir) kdir = os.path.join(pdir, key)
elif os.path.isdir(kdir): if uuid in key:
try: found.add(kdir)
found.update(os.path.join(kdir, n) for n in os.listdir(kdir) if uuid in n) elif os.path.isdir(kdir):
except OSError: try:
pass found.update(os.path.join(kdir, n) for n in os.listdir(kdir) if uuid in n)
except OSError:
pass
job = self._owned_job(uuid, ctx) job = self._owned_job(uuid, ctx)
if job: if job:
found.add(job) found.add(job)
+1 -1
View File
@@ -18,7 +18,7 @@ import tempfile
# The pane-inherited variables conftest._isolate_agent_homes drops for every # The pane-inherited variables conftest._isolate_agent_homes drops for every
# test, so HOME (which the tests set) governs, as it does in a plain shell. # test, so HOME (which the tests set) governs, as it does in a plain shell.
DROPPED_VARS = ("HOME_DIR", "CLAUDE_PROJECT_DIR", "CODEX_HOME", "LOCAL_BIN") DROPPED_VARS = ("HOME_DIR", "CLAUDE_PROJECT_DIR", "CLAUDE_CONFIG_DIR", "CODEX_HOME", "LOCAL_BIN")
# Read by the code under test but left alone, on purpose: HOME is where the # Read by the code under test but left alone, on purpose: HOME is where the
# real herdr tests and the shim bootstrap find the server socket, and it is # real herdr tests and the shim bootstrap find the server socket, and it is
# what the suite's own tests set for an isolated home; XDG_CONFIG_HOME and # what the suite's own tests set for an isolated home; XDG_CONFIG_HOME and