refactor: DRY PRESENCE_FILE — single source of truth in workshop_state (#381)
All checks were successful
Tests / lint (pull_request) Successful in 3s
Tests / test (pull_request) Successful in 1m25s

This commit is contained in:
2026-03-18 22:28:46 -04:00
parent b7573432cc
commit 51c27c6974
4 changed files with 17 additions and 18 deletions

View File

@@ -49,6 +49,7 @@ from dashboard.routes.tools import router as tools_router
from dashboard.routes.voice import router as voice_router
from dashboard.routes.work_orders import router as work_orders_router
from dashboard.routes.world import router as world_router
from timmy.workshop_state import PRESENCE_FILE
class _ColorFormatter(logging.Formatter):
@@ -190,7 +191,6 @@ async def _loop_qa_scheduler() -> None:
await asyncio.sleep(interval)
_PRESENCE_FILE = Path.home() / ".timmy" / "presence.json"
_PRESENCE_POLL_SECONDS = 30
_PRESENCE_INITIAL_DELAY = 3
@@ -219,11 +219,11 @@ async def _presence_watcher() -> None:
while True:
try:
if _PRESENCE_FILE.exists():
mtime = _PRESENCE_FILE.stat().st_mtime
if PRESENCE_FILE.exists():
mtime = PRESENCE_FILE.stat().st_mtime
if mtime != last_mtime:
last_mtime = mtime
raw = await asyncio.to_thread(_PRESENCE_FILE.read_text)
raw = await asyncio.to_thread(PRESENCE_FILE.read_text)
state = json.loads(raw)
await ws_mgr.broadcast("timmy_state", state)
else:

View File

@@ -14,29 +14,28 @@ import json
import logging
import time
from datetime import UTC, datetime
from pathlib import Path
from fastapi import APIRouter
from fastapi.responses import JSONResponse
from timmy.workshop_state import PRESENCE_FILE
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/world", tags=["world"])
_PRESENCE_FILE = Path.home() / ".timmy" / "presence.json"
_STALE_THRESHOLD = 90 # seconds — file older than this triggers live rebuild
def _read_presence_file() -> dict | None:
"""Read presence.json if it exists and is fresh enough."""
try:
if not _PRESENCE_FILE.exists():
if not PRESENCE_FILE.exists():
return None
age = time.time() - _PRESENCE_FILE.stat().st_mtime
age = time.time() - PRESENCE_FILE.stat().st_mtime
if age > _STALE_THRESHOLD:
logger.debug("presence.json is stale (%.0fs old)", age)
return None
return json.loads(_PRESENCE_FILE.read_text())
return json.loads(PRESENCE_FILE.read_text())
except (OSError, json.JSONDecodeError) as exc:
logger.warning("Failed to read presence.json: %s", exc)
return None

View File

@@ -55,7 +55,7 @@ def test_build_world_state_defaults():
def test_read_presence_file_missing(tmp_path):
with patch("dashboard.routes.world._PRESENCE_FILE", tmp_path / "nope.json"):
with patch("dashboard.routes.world.PRESENCE_FILE", tmp_path / "nope.json"):
assert _read_presence_file() is None
@@ -67,14 +67,14 @@ def test_read_presence_file_stale(tmp_path):
import os
os.utime(f, (stale_time, stale_time))
with patch("dashboard.routes.world._PRESENCE_FILE", f):
with patch("dashboard.routes.world.PRESENCE_FILE", f):
assert _read_presence_file() is None
def test_read_presence_file_fresh(tmp_path):
f = tmp_path / "presence.json"
f.write_text(json.dumps({"version": 1, "mood": "focused"}))
with patch("dashboard.routes.world._PRESENCE_FILE", f):
with patch("dashboard.routes.world.PRESENCE_FILE", f):
result = _read_presence_file()
assert result is not None
assert result["version"] == 1
@@ -83,7 +83,7 @@ def test_read_presence_file_fresh(tmp_path):
def test_read_presence_file_bad_json(tmp_path):
f = tmp_path / "presence.json"
f.write_text("not json {{{")
with patch("dashboard.routes.world._PRESENCE_FILE", f):
with patch("dashboard.routes.world.PRESENCE_FILE", f):
assert _read_presence_file() is None
@@ -120,7 +120,7 @@ def test_world_state_endpoint_with_file(client, tmp_path):
}
)
)
with patch("dashboard.routes.world._PRESENCE_FILE", f):
with patch("dashboard.routes.world.PRESENCE_FILE", f):
resp = client.get("/api/world/state")
assert resp.status_code == 200
@@ -133,7 +133,7 @@ def test_world_state_endpoint_with_file(client, tmp_path):
def test_world_state_endpoint_fallback(client, tmp_path):
"""Endpoint falls back to live state when file missing."""
with (
patch("dashboard.routes.world._PRESENCE_FILE", tmp_path / "nope.json"),
patch("dashboard.routes.world.PRESENCE_FILE", tmp_path / "nope.json"),
patch("timmy.workshop_state.get_state_dict") as mock_get,
):
mock_get.return_value = {
@@ -154,7 +154,7 @@ def test_world_state_endpoint_fallback(client, tmp_path):
def test_world_state_endpoint_full_fallback(client, tmp_path):
"""Endpoint returns safe defaults when everything fails."""
with (
patch("dashboard.routes.world._PRESENCE_FILE", tmp_path / "nope.json"),
patch("dashboard.routes.world.PRESENCE_FILE", tmp_path / "nope.json"),
patch(
"timmy.workshop_state.get_state_dict",
side_effect=RuntimeError("boom"),

View File

@@ -18,7 +18,7 @@ def _patches(mock_ws, presence_file):
from contextlib import ExitStack
stack = ExitStack()
stack.enter_context(patch("dashboard.app._PRESENCE_FILE", presence_file))
stack.enter_context(patch("dashboard.app.PRESENCE_FILE", presence_file))
stack.enter_context(patch("infrastructure.ws_manager.handler.ws_manager", mock_ws))
for key, val in _FAST.items():
stack.enter_context(patch(key, val))