Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions emrg/server/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,23 +287,45 @@ async def serve(self) -> None:
pass

def _evolution_count(self) -> int:
"""Total completed evolution cycles across scheduler handlers.
"""Total completed evolution cycles across scheduler handlers + disk.

The daemon's own ``self.evolutions`` list is a legacy from the
pre-scheduler BackgroundThread design (#95) and is never appended;
the scheduler's handlers own the real per-cycle logs. Aggregate from
the scheduler, falling back to the legacy list only when the
scheduler is unavailable (e.g. test harnesses mock it away).

The scheduler's in-memory count resets to 0 on daemon restart, while
the ``evolution-*.json`` log files persist — so also count valid log
files on disk and return the max. This keeps the GUI growth card /
evolution toast consistent with the ``recent`` list (which reads the
same files) across restarts instead of showing 0.
"""
in_memory = 0
sched = getattr(self, "_scheduler", None)
if sched is not None:
try:
total = sched.total_evolutions()
if isinstance(total, int):
return total
in_memory = total
except Exception:
pass
return len(self.evolutions)
else:
in_memory = len(self.evolutions)

disk = 0
try:
logs_dir = config_dir() / "logs"
for f in logs_dir.glob("evolution-*.json"):
try:
data = json.loads(f.read_text(encoding="utf-8"))
if data.get("timestamp"):
disk += 1
except (json.JSONDecodeError, OSError):
continue # corrupt/partial write — don't count
except OSError:
pass
return max(in_memory, disk)

async def _handle_client(self, ws) -> None:
"""Handle a single WebSocket client connection.
Expand Down
3 changes: 2 additions & 1 deletion tests/test_ws_e2e.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ async def _test():
frame = await asyncio.wait_for(ws.recv(), timeout=5)
resp = json.loads(frame)
assert resp["type"] == "evolution_summary"
assert resp["count"] == 0 # in-memory evolutions empty in test harness
assert resp["count"] == 2 # valid disk logs counted (corrupt skipped)
# newest first (reverse lexicographic = chronological)
assert [r["timestamp"] for r in resp["recent"]] == [
"2026-08-06T10:00:00", "2026-08-06T09:00:00"]
Expand Down Expand Up @@ -552,6 +552,7 @@ async def _test():
frame = await asyncio.wait_for(ws.recv(), timeout=5)
resp = json.loads(frame)
assert resp["type"] == "evolution_summary"
assert resp["count"] == 25, f"count must include all valid disk logs, got {resp['count']}"
assert len(resp["recent"]) == 20, f"limit must clamp to 20, got {len(resp['recent'])}"
# newest-first: first entry is the highest timestamp
assert resp["recent"][0]["timestamp"] == "2026-08-06T00:24:00"
Expand Down
Loading