HaltState AI: Two Patterns: Scripts vs Services
HaltState supports two usage patterns depending on your architecture.
Pattern 1: Cron Jobs & Scripts (Exit and Retry)
For scheduled tasks that run periodically, use guard() with idempotency keys:
#!/usr/bin/env python3
"""Nightly maintenance script - runs via cron"""
from haltstate import HaltStateClient, ApprovalPending, ActionDenied
from datetime import date
import sys
client = HaltStateClient(tenant_id="...", api_key="hs_...")
op_key = f"maintenance-{date.today().isoformat()}"
try:
with client.guard(
action="logs.prune",
params={"days": 30},
idempotency_key=op_key
):
prune_old_logs()
print("Maintenance complete")
except ApprovalPending as e:
print(f"Pending approval: {e.approval_id}")
print("Will retry on next cron run")
sys.exit(0) # Clean exit - cron retries later
except ActionDenied as e:
print(f"Denied: {e}")
sys.exit(1)
Cron handles the retry:
# /etc/cron.d/maintenance
0 3 * * * root /opt/scripts/maintenance.py
Pattern 2: Long-Running Services (Callbacks)
For Flask, FastAPI, or daemon processes, use on_approval() callbacks:
from haltstate import HaltStateClient
client = HaltStateClient(tenant_id="...", api_key="hs_...")
def handle_approval(decision):
"""Called automatically when human approves/rejects"""
if decision.approved:
print(f"Approved by {decision.approver}: {decision.action}")
execute_pending_action(decision.request_id)
else:
print(f"Rejected: {decision.reason}")
# Register callback - starts SSE listener in background thread
client.on_approval(handle_approval)
# Your service continues running...
app.run()
Which Pattern to Use?
| Scenario | Pattern | Why |
|---|---|---|
| Cron jobs | Exit & Retry | Natural scheduler retry |
| CI/CD pipelines | Exit & Retry | Pipeline handles retry |
| Web servers | Callbacks | Always running |
| Queue workers | Callbacks | Long-lived process |
| CLI tools | Exit & Retry | User re-runs command |