feat(system): S85: Trigger self-healing live + drone error handler + watchdog v1.1 snapshot + STATUS
Co-Authored-By: @devpulse <devpulse@aipass>
This commit is contained in:
@@ -92,11 +92,11 @@ INBOX="path/to/inbox.json"; while true; do sleep 10; UNREAD=$(python3 -c "import
|
||||
# 4. Wake notification arrives → read mail → process → dispatch next → repeat
|
||||
```
|
||||
|
||||
**Key:** Use `unread_count > 0` (not total_messages — close resets totals). Always `close all` before arming. 10s poll interval. `run_in_background: true` so the completion notification wakes you. On timeout, wake anyway to check if agent crashed — then either restart watchdog or re-dispatch.
|
||||
**Key:** Snapshot unread_count BEFORE arming, then wake when it increases. Don't require empty inbox — works with existing mail. 10s poll interval. `run_in_background: true` so the completion notification wakes you. On timeout, wake anyway to check if agent crashed.
|
||||
|
||||
**Watchdog one-liner (copy-paste ready):**
|
||||
```
|
||||
INBOX="/home/patrick/Projects/AIPass/src/aipass/devpulse/.ai_mail.local/inbox.json"; C=0; while [ $C -lt 60 ]; do sleep 10; C=$((C+1)); UNREAD=$(python3 -c "import json; from pathlib import Path; p=Path('$INBOX'); print(json.loads(p.read_text()).get('unread_count',0) if p.exists() else 0)" 2>/dev/null); if [ "$UNREAD" -gt "0" ]; then echo "WOKE: $UNREAD unread"; exit 0; fi; done; echo "TIMEOUT: 10min no reply — check if agent crashed"; exit 0
|
||||
INBOX="/home/patrick/Projects/AIPass/src/aipass/devpulse/.ai_mail.local/inbox.json"; INITIAL=$(python3 -c "import json; from pathlib import Path; p=Path('$INBOX'); print(json.loads(p.read_text()).get('unread_count',0) if p.exists() else 0)" 2>/dev/null); C=0; while [ $C -lt 60 ]; do sleep 10; C=$((C+1)); CURRENT=$(python3 -c "import json; from pathlib import Path; p=Path('$INBOX'); print(json.loads(p.read_text()).get('unread_count',0) if p.exists() else 0)" 2>/dev/null); if [ "$CURRENT" -gt "$INITIAL" ]; then echo "WOKE: new mail ($INITIAL→$CURRENT)"; exit 0; fi; done; echo "TIMEOUT: 10min no new mail"; exit 0
|
||||
```
|
||||
|
||||
## Memory & Tracking
|
||||
|
||||
@@ -431,6 +431,10 @@ def main() -> int:
|
||||
logger.warning("Registry error during systems listing: %s", exc)
|
||||
err_console.print(f"drone: {exc}")
|
||||
return 1
|
||||
except Exception as exc:
|
||||
logger.error("[drone] Unhandled error in systems: %s", exc)
|
||||
err_console.print(f"drone: unexpected error: {exc}")
|
||||
return 1
|
||||
|
||||
# scan — discover available commands in a branch
|
||||
if command == "scan":
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
"subject": "DPLAN-0039 prax audit work items — test coverage, JSON corruption, stale cleanup",
|
||||
"date_closed": "2026-03-29",
|
||||
"location": "prax"
|
||||
},
|
||||
{
|
||||
"plan_id": "FPLAN-0097",
|
||||
"type": "FPLAN",
|
||||
"subject": "Codex + Gemini log monitoring adapters for prax monitor",
|
||||
"date_closed": "2026-04-10",
|
||||
"location": "prax"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import json
|
||||
from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from aipass.spawn.apps.handlers.file_ops import (
|
||||
SKIP_NAMES,
|
||||
_replace_path_placeholders,
|
||||
@@ -68,6 +70,11 @@ class TestEnsureDirectory:
|
||||
assert target.exists()
|
||||
assert target.is_dir()
|
||||
|
||||
def test_ensure_directory_none_raises_valueerror(self) -> None:
|
||||
"""Passing None raises ValueError with clear message."""
|
||||
with pytest.raises(ValueError, match="ensure_directory received None path"):
|
||||
ensure_directory(None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# copy_template
|
||||
|
||||
Reference in New Issue
Block a user