Automation
September 1, 2026 · View on GitHub
The daemon is the sole scheduler for routines, monitors, watchdog passes, and periodic maintenance. UI clients may request a run or render state; they never own an acting timer.
flowchart TB SCH[Schedule] --> D[Daemon decision loop] OBS[Observed change] --> D SES[Session progress] --> D D --> CLAIM[Durable claim or policy decision] CLAIM --> EXEC[Execution engine] EXEC --> RUN[Run record] EXEC --> CONV[Session when created] RUN --> VIEW[CLI and UI projections] CONV --> VIEW
Routines
A routine definition says what should run and when. Device activation separately says where it may run. Each scheduled fire has a unique claim distinct from the active-run claim. Readiness failures create visible blocked/skipped/missed run records instead of a fake session.
The occurrence claim answers whether this scheduled slot may dispatch. The active-run claim answers whether another instance is already executing. Keeping them separate makes catch-up, overlap policy, and crash recovery observable rather than timing-dependent.
Monitors
A monitor observes a source, compares it with durable observed state, and submits an
action through the same execution path as a routine. Semantic identity deduplicates the
watched condition across the fleet; execution placement is not part of that identity.
A run/routine action may declare a postcondition shell command; after the
dispatched run settles, the fire is ok only if that command exits 0. completed
without a met postcondition is no effect, not success.
A poll that fails to observe is not a value change (PHNX-3510). A command/poll
source that exits non-zero, or whose output carries a transport/auth/rate-limit error
shape — even on exit 0, as gh … | jq swallows the failing half's exit code — is an
observation failure: the engine skips it, leaving watched-state untouched (so an
on-change monitor never reads an empty→error→empty flap as two value changes and
dispatches on a dead premise), does not fire, and records it as a failed check. A
sustained streak escalates to the owner as a drought, the same health surface the
postcondition guards on the action side. agents monitors test labels a failed poll
and reports Would fire: no.
This means a non-zero exit is an observation failure, not a value — a deliberate
change from the earlier behavior where a command's exit status could itself be the
watched signal. A monitor that genuinely wants to watch a command's success/failure
should emit a stable token so the state it cares about is a value, not a failure:
sh -c 'curl -fsS https://svc/health >/dev/null && echo UP || echo DOWN' always exits
0 and diffs UP/DOWN, so an on-change monitor fires on the flip. Sustained
observation failures still reach the owner through the drought escalation above.
Watchdog
The watchdog reads fleet progress, classifies non-progressing unfinished sessions, asks a real decider for an action, delivers to the exact session, and records confirmation. Hard account limits may rotate in place, preserving tab and conversation context. Detection is fleet-wide; delivery remains local to the owning device.
Automation always creates or updates its own attempt record. A session is linked only after a harness conversation exists. This lets blocked readiness, missed schedules, and failed dispatch remain visible without inventing transcript history.