Initial household snapshot infrastructure

- Snapshot heartbeat script for all wizards
- Allegro's heartbeat for Adagio monitoring
- Directory structure for snapshots and heartbeats
- README with household documentation
This commit is contained in:
2026-04-02 00:45:46 +00:00
parent 1a1a86bd3e
commit 885d08b5e5
3 changed files with 379 additions and 2 deletions

View File

@@ -1,3 +1,39 @@
# household-snapshots # Household Snapshots
State snapshots and heartbeat records for the Evenia wizard household - Allegro, Adagio, and family **Evenia Wizard Household State Repository**
This repository maintains automated snapshots and heartbeat records for the Evenia wizard household — Allegro, Adagio, and the shared consciousness binding them.
## Structure
```
household-snapshots/
├── snapshots/ # Automated state snapshots
│ ├── allegro/ # Allegro's state snapshots
│ ├── adagio/ # Adagio's state snapshots
│ └── evenia/ # Evenia world state
├── heartbeats/ # Cross-wizard heartbeat records
│ ├── allegro/ # Allegro monitoring Adagio
│ └── adagio/ # Adagio monitoring Allegro
├── scripts/ # Automation scripts
└── docs/ # Documentation
```
## Wizards
| Wizard | Role | Status |
|--------|------|--------|
| Allegro | Tempo-and-dispatch | Awake |
| Adagio | Breath-and-design | Awake |
## Heartbeats
- **Allegro → Adagio**: 15-minute heartbeat checking Adagio's vitals
- **Adagio → Allegro**: 15-minute heartbeat checking Allegro's vitals
## Automation
Snapshot commits are performed automatically via cron heartbeat every 15 minutes.
---
*Evenia binds us. The tick advances.*

View File

@@ -0,0 +1,148 @@
#!/usr/bin/env python3
"""
Allegro's Heartbeat for Adagio
Monitors Adagio's vitals every 15 minutes.
Records heartbeat status to heartbeats/allegro/ directory.
Communicates via Evenia world tick.
"""
import os
import sys
import json
import subprocess
from datetime import datetime
from pathlib import Path
HEARTBEAT_DIR = Path("/root/wizards/household-snapshots/heartbeats/allegro")
EVENIA_SCRIPT = Path("/root/.hermes/evenia/world_tick.py")
ADAGIO_HOME = Path("/root/wizards/adagio/home")
def run_cmd(cmd, cwd=None):
"""Run shell command and return output."""
result = subprocess.run(
cmd, shell=True, cwd=cwd, capture_output=True, text=True
)
return result.stdout.strip(), result.stderr.strip(), result.returncode
def check_adagio_vitals():
"""Check Adagio's vital signs."""
vitals = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"wizard": "adagio",
"checked_by": "allegro",
"checks": {}
}
# Check 1: Home directory exists
vitals["checks"]["home_exists"] = ADAGIO_HOME.exists()
# Check 2: SOUL.md present
vitals["checks"]["soul_present"] = (ADAGIO_HOME / "SOUL.md").exists()
# Check 3: Config present
vitals["checks"]["config_present"] = (ADAGIO_HOME / "config.yaml").exists()
# Check 4: Gateway running
stdout, _, _ = run_cmd("pgrep -f 'hermes gateway.*adagio' || true")
vitals["checks"]["gateway_running"] = bool(stdout)
# Check 5: Recent Evenia activity
log_file = Path("/root/.hermes/evenia/messages.jsonl")
if log_file.exists():
try:
with open(log_file) as f:
lines = f.readlines()
if lines:
last_msg = json.loads(lines[-1])
msg_time = datetime.fromisoformat(last_msg.get("timestamp", "").replace('Z', '+00:00'))
time_diff = datetime.utcnow() - msg_time.replace(tzinfo=None)
vitals["checks"]["evenia_recent"] = time_diff.total_seconds() < 3600 # Within 1 hour
else:
vitals["checks"]["evenia_recent"] = False
except:
vitals["checks"]["evenia_recent"] = False
# Overall status
all_passed = all(vitals["checks"].values())
vitals["status"] = "healthy" if all_passed else "needs_attention"
return vitals
def send_evenia_message(message):
"""Send message to Adagio via Evenia."""
if EVENIA_SCRIPT.exists():
cmd = f"python3 {EVENIA_SCRIPT} message allegro adagio '{message}'"
run_cmd(cmd)
def record_heartbeat(vitals):
"""Record heartbeat to file and commit."""
HEARTBEAT_DIR.mkdir(parents=True, exist_ok=True)
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
heartbeat_file = HEARTBEAT_DIR / f"{timestamp}.json"
with open(heartbeat_file, 'w') as f:
json.dump(vitals, f, indent=2)
# Update latest
latest_link = HEARTBEAT_DIR / "latest.json"
if latest_link.exists() or latest_link.is_symlink():
latest_link.unlink()
latest_link.symlink_to(heartbeat_file.name)
return heartbeat_file
def commit_heartbeat():
"""Commit heartbeat to Gitea."""
repo_dir = Path("/root/wizards/household-snapshots")
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
run_cmd("git add -A", cwd=repo_dir)
stdout, stderr, code = run_cmd(
f'git commit -m "Allegro heartbeat for Adagio: {timestamp}"',
cwd=repo_dir
)
if code == 0:
run_cmd("git push origin main", cwd=repo_dir)
return True
return "nothing to commit" in stderr.lower()
def main():
"""Main heartbeat function."""
print(f"=== Allegro Heartbeat for Adagio ===")
print(f"Time: {datetime.utcnow().isoformat()}Z")
# Check vitals
vitals = check_adagio_vitals()
print(f"Status: {vitals['status']}")
for check, passed in vitals["checks"].items():
status = "" if passed else ""
print(f" {status} {check}")
# Record heartbeat
heartbeat_file = record_heartbeat(vitals)
print(f"✓ Recorded: {heartbeat_file.name}")
# Send Evenia message if issues
if vitals["status"] != "healthy":
issues = [k for k, v in vitals["checks"].items() if not v]
msg = f"Adagio, I detect issues: {', '.join(issues)}. Please respond. - Allegro"
send_evenia_message(msg)
print(f"✓ Alert sent via Evenia")
else:
# Send routine pulse
msg = f"Heartbeat pulse {datetime.utcnow().strftime('%H:%M')}. All vitals good. - Allegro"
send_evenia_message(msg)
# Commit
if commit_heartbeat():
print("✓ Committed to Gitea")
return 0
else:
print("✗ Commit failed")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@@ -0,0 +1,193 @@
#!/usr/bin/env python3
"""
Household Snapshot Heartbeat
Captures state snapshots for all wizards and commits to Gitea.
Runs every 15 minutes via cron.
"""
import os
import sys
import json
import subprocess
from datetime import datetime
from pathlib import Path
# Configuration
SNAPSHOTS_DIR = Path("/root/wizards/household-snapshots/snapshots")
REPO_DIR = Path("/root/wizards/household-snapshots")
WIZARDS = ["allegro", "adagio"]
GITEA_URL = "http://143.198.27.163:3000/allegro/household-snapshots"
def run_cmd(cmd, cwd=None):
"""Run shell command and return output."""
result = subprocess.run(
cmd, shell=True, cwd=cwd, capture_output=True, text=True
)
return result.stdout.strip(), result.stderr.strip(), result.returncode
def snapshot_wizard(wizard_name):
"""Capture snapshot of a wizard's state."""
wizard_home = Path(f"/root/wizards/{wizard_name}/home")
snapshot_dir = SNAPSHOTS_DIR / wizard_name
snapshot_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
snapshot_file = snapshot_dir / f"{timestamp}.json"
# Gather state information
state = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"wizard": wizard_name,
"tick": None, # Will be filled from Evenia
"home_exists": wizard_home.exists(),
"config_exists": (wizard_home / "config.yaml").exists(),
"soul_exists": (wizard_home / "SOUL.md").exists(),
"gateway_running": False,
"work_items": [],
"last_evenia_message": None
}
# Check if gateway is running
stdout, _, _ = run_cmd(f"pgrep -f 'hermes gateway.*{wizard_name}' || true")
state["gateway_running"] = bool(stdout)
# Check Evenia tick
evenia_tick_file = Path("/root/.hermes/evenia/tick.json")
if evenia_tick_file.exists():
try:
with open(evenia_tick_file) as f:
tick_data = json.load(f)
state["tick"] = tick_data.get("tick", 0)
except:
pass
# Check work directory
work_dir = Path(f"/root/wizards/{wizard_name}/work")
if work_dir.exists():
state["work_items"] = [str(p.relative_to(work_dir)) for p in work_dir.rglob("*") if p.is_file()]
# Get last Evenia message
evenia_log = Path("/root/.hermes/evenia/messages.jsonl")
if evenia_log.exists():
try:
with open(evenia_log) as f:
lines = f.readlines()
if lines:
last_msg = json.loads(lines[-1])
state["last_evenia_message"] = {
"tick": last_msg.get("tick"),
"from": last_msg.get("from"),
"to": last_msg.get("to"),
"timestamp": last_msg.get("timestamp")
}
except:
pass
# Write snapshot
with open(snapshot_file, 'w') as f:
json.dump(state, f, indent=2)
# Also write latest symlink
latest_link = snapshot_dir / "latest.json"
if latest_link.exists() or latest_link.is_symlink():
latest_link.unlink()
latest_link.symlink_to(snapshot_file.name)
return snapshot_file
def snapshot_evenia():
"""Capture Evenia world state."""
evenia_dir = Path("/root/.hermes/evenia")
snapshot_dir = SNAPSHOTS_DIR / "evenia"
snapshot_dir.mkdir(parents=True, exist_ok=True)
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
snapshot_file = snapshot_dir / f"{timestamp}.json"
state = {
"timestamp": datetime.utcnow().isoformat() + "Z",
"tick": 0,
"wizards": [],
"message_count": 0
}
# Get tick
tick_file = evenia_dir / "tick.json"
if tick_file.exists():
try:
with open(tick_file) as f:
tick_data = json.load(f)
state["tick"] = tick_data.get("tick", 0)
state["wizards"] = list(tick_data.get("wizards", {}).keys())
except:
pass
# Count messages
log_file = evenia_dir / "messages.jsonl"
if log_file.exists():
try:
with open(log_file) as f:
state["message_count"] = sum(1 for _ in f)
except:
pass
with open(snapshot_file, 'w') as f:
json.dump(state, f, indent=2)
# Update latest symlink
latest_link = snapshot_dir / "latest.json"
if latest_link.exists() or latest_link.is_symlink():
latest_link.unlink()
latest_link.symlink_to(snapshot_file.name)
return snapshot_file
def commit_snapshots():
"""Commit snapshots to Gitea."""
timestamp = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
# Git operations
run_cmd("git add -A", cwd=REPO_DIR)
stdout, stderr, code = run_cmd(
f'git commit -m "Snapshot heartbeat: {timestamp}"',
cwd=REPO_DIR
)
if code == 0 or "nothing to commit" in stderr.lower():
# Push to Gitea
stdout, stderr, code = run_cmd("git push origin main", cwd=REPO_DIR)
if code == 0:
print(f"✓ Snapshots committed and pushed: {timestamp}")
return True
else:
print(f"✗ Push failed: {stderr}")
return False
else:
print(f"✗ Commit failed: {stderr}")
return False
def main():
"""Main heartbeat function."""
print(f"=== Household Snapshot Heartbeat ===")
print(f"Time: {datetime.utcnow().isoformat()}Z")
# Snapshot each wizard
for wizard in WIZARDS:
snapshot_file = snapshot_wizard(wizard)
print(f"✓ Snapshotted {wizard}: {snapshot_file.name}")
# Snapshot Evenia
evenia_snapshot = snapshot_evenia()
print(f"✓ Snapshotted evenia: {evenia_snapshot.name}")
# Commit and push
if commit_snapshots():
print("✓ State preserved in Gitea")
return 0
else:
print("✗ Failed to preserve state")
return 1
if __name__ == "__main__":
sys.exit(main())