Fix folder watcher matching, notifications, and BLM venv isolation
- Remove status filter from folder watcher so tasks in any open status (including "internal review") are matched by keyword - Add retry logic for stuck processing/blocked/unmatched KV states - Fix notification ordering (newest first, limit 50) and date parsing - Use BLM's own .venv Python instead of uv run for subprocess calls - Document external tools venv convention in CLAUDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>cora-start
parent
3b4a8e47be
commit
62186d8dec
|
|
@ -109,6 +109,7 @@ uv add --group test <package>
|
|||
- **Notifications**: All scheduler events go through `NotificationBus.push()`, never directly to a UI
|
||||
- **Tests**: Use `respx` to mock httpx calls, `tmp_db` fixture for isolated SQLite instances
|
||||
- **ClickUp attachments**: `ClickUpClient.upload_attachment()` uses module-level `httpx.post()` (not the shared client) for multipart uploads
|
||||
- **External tools**: Subprocess calls to external projects (e.g. Big-Link-Man) must use the tool's own `.venv` Python, never `uv run` from CheddahBot's environment. Resolve the venv python at `{tool_dir}/.venv/Scripts/python.exe` (Windows) or `{tool_dir}/.venv/bin/python` (Linux/Mac). Fail if no venv exists.
|
||||
|
||||
## Multi-Agent Configuration
|
||||
|
||||
|
|
|
|||
|
|
@ -345,8 +345,11 @@ async def get_notifications():
|
|||
if not _db:
|
||||
return {"notifications": []}
|
||||
|
||||
notes = _db.get_notifications_after(0, limit=100)
|
||||
return {"notifications": notes}
|
||||
rows = _db._conn.execute(
|
||||
"SELECT id, message, category, created_at FROM notifications"
|
||||
" ORDER BY id DESC LIMIT 50"
|
||||
).fetchall()
|
||||
return {"notifications": [dict(r) for r in rows]}
|
||||
|
||||
|
||||
@router.get("/system/kv-states")
|
||||
|
|
|
|||
|
|
@ -488,13 +488,16 @@ class Scheduler:
|
|||
filename = xlsx_path.name
|
||||
kv_key = f"linkbuilding:watched:{filename}"
|
||||
|
||||
# Skip already processed files
|
||||
# Skip completed/failed; retry "processing" (killed run) and "blocked" (missing field)
|
||||
existing = self.db.kv_get(kv_key)
|
||||
if existing:
|
||||
try:
|
||||
state = json.loads(existing)
|
||||
if state.get("status") in ("completed", "processing", "failed"):
|
||||
if state.get("status") in ("completed", "failed"):
|
||||
continue
|
||||
if state.get("status") in ("processing", "blocked", "unmatched"):
|
||||
log.info("Retrying '%s' state for %s", state["status"], filename)
|
||||
self.db.kv_delete(kv_key)
|
||||
except json.JSONDecodeError:
|
||||
continue
|
||||
|
||||
|
|
@ -672,7 +675,7 @@ class Scheduler:
|
|||
return None
|
||||
|
||||
try:
|
||||
tasks = client.get_tasks_from_space(space_id, statuses=["to do"])
|
||||
tasks = client.get_tasks_from_overall_lists(space_id)
|
||||
except Exception as e:
|
||||
log.warning("ClickUp query failed in _match_xlsx_to_clickup: %s", e)
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -38,7 +38,16 @@ def _run_blm_command(
|
|||
|
||||
Always injects -u/-p from BLM_USERNAME/BLM_PASSWORD env vars.
|
||||
"""
|
||||
cmd = ["uv", "run", "python", "main.py", *args]
|
||||
# Use BLM's own venv Python so its dependencies are available
|
||||
venv_python = Path(blm_dir) / ".venv" / "Scripts" / "python.exe"
|
||||
if not venv_python.exists():
|
||||
# Fallback for Linux/Mac
|
||||
venv_python = Path(blm_dir) / ".venv" / "bin" / "python"
|
||||
if not venv_python.exists():
|
||||
raise FileNotFoundError(
|
||||
f"No .venv found in {blm_dir}. External tools must have their own venv."
|
||||
)
|
||||
cmd = [str(venv_python), "main.py", *args]
|
||||
|
||||
# Inject credentials from env vars
|
||||
username = os.getenv("BLM_USERNAME", "")
|
||||
|
|
|
|||
|
|
@ -888,7 +888,7 @@ async function loadNotifications() {
|
|||
|
||||
let html = '<ul class="log-list">';
|
||||
notes.slice(0, 100).forEach(n => {
|
||||
const time = n.created_at ? new Date(n.created_at * 1000).toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit' }) : '';
|
||||
const time = n.created_at ? new Date(n.created_at).toLocaleString('en-US', { month:'short', day:'numeric', hour:'2-digit', minute:'2-digit' }) : '';
|
||||
const level = (n.level || 'info').toLowerCase();
|
||||
const typeCls = level === 'error' ? 'error' : level === 'warning' ? 'error' : 'heartbeat';
|
||||
html += `<li class="log-item">
|
||||
|
|
|
|||
Loading…
Reference in New Issue