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
No files matched your search
@@ -1,4 +1,4 @@
|
||||
import os, re, json, glob, math, stat, time, shutil, subprocess
|
||||
import os, re, json, glob, math, stat, time, shutil, calendar, subprocess
|
||||
from typing import Optional, Any
|
||||
from lib_py.agents.base import BaseAgentAdapter, DiscoveryContext, conversation_in_use, scan_names, _plausible_id
|
||||
from lib_py.verify_session import workspace_key
|
||||
@@ -84,10 +84,11 @@ def _session_file_live(data: dict, pid) -> bool:
|
||||
or not math.isfinite(started) or started <= 0:
|
||||
return True
|
||||
started = started / 1000.0
|
||||
# lstart in UTC: a local time is ambiguous in the hour a DST change repeats.
|
||||
try:
|
||||
out = subprocess.run(["ps", "-p", str(pid), "-o", "lstart="], capture_output=True, text=True,
|
||||
timeout=5, env=dict(os.environ, LC_ALL="C")).stdout
|
||||
lstart = time.mktime(time.strptime(" ".join(out.split()), "%a %b %d %H:%M:%S %Y"))
|
||||
timeout=5, env=dict(os.environ, LC_ALL="C", TZ="UTC0")).stdout
|
||||
lstart = calendar.timegm(time.strptime(" ".join(out.split()), "%a %b %d %H:%M:%S %Y"))
|
||||
except Exception:
|
||||
return True
|
||||
# startedAt is written 1.7-3.2 s after the process starts; 2 s covers
|
||||
|
||||
@@ -447,6 +447,30 @@ def test_session_file_liveness_fails_safe(monkeypatch, live):
|
||||
assert claude_mod._session_file_live({"startedAt": hour_ago}, live.pid) is True, "ps unavailable: live"
|
||||
|
||||
|
||||
def test_liveness_reads_lstart_as_utc(tmp_path, monkeypatch, live):
|
||||
"""2026-11-01 01:30 happens twice in EST5EDT. A process that started at
|
||||
the second one (06:30 UTC) shows lstart 01:30 to a local-time ps; read as
|
||||
the first (05:30 UTC) it predates a session file written at 05:45. ps
|
||||
runs with TZ=UTC0 instead, which BSD ps and procps both honour."""
|
||||
import calendar
|
||||
from lib_py.agents.adapters import claude as claude_mod
|
||||
fake = tmp_path / "fakebin"
|
||||
fake.mkdir()
|
||||
(fake / "ps").write_text('#!/bin/sh\nif [ "$TZ" = UTC0 ]; then echo "Sun Nov 1 06:30:00 2026"; '
|
||||
'else echo "Sun Nov 1 01:30:00 2026"; fi\n')
|
||||
(fake / "ps").chmod(0o755)
|
||||
utc = lambda hhmmss: calendar.timegm(time.strptime(f"2026-11-01 {hhmmss}", "%Y-%m-%d %H:%M:%S")) * 1000 # noqa: E731
|
||||
try:
|
||||
with monkeypatch.context() as m:
|
||||
m.setenv("PATH", f"{fake}:{os.environ['PATH']}")
|
||||
m.setenv("TZ", "EST5EDT,M3.2.0,M11.1.0")
|
||||
time.tzset()
|
||||
assert claude_mod._session_file_live({"startedAt": utc("05:45:00")}, live.pid) is False, "reused pid"
|
||||
assert claude_mod._session_file_live({"startedAt": utc("06:30:02")}, live.pid) is True
|
||||
finally:
|
||||
time.tzset()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("started", ["NaN", float("nan"), float("inf"), float("-inf"), "inf", "1760000000000",
|
||||
"soon", True, False, None, [1], "<missing>"])
|
||||
def test_an_unusable_started_at_means_live(live, started):
|
||||
|
||||
Reference in new issue
Block a user