?

Exit codes

Rousseau's CLI is deliberately conservative — two exit codes cover every path.

Code Emitted by Meaning
0 cmd/rousseau/main.go via cli.Execute Command completed successfully. Daemons exit 0 on graceful shutdown (SIGINT / SIGTERM).
1 cmd/rousseau/main.go via cli.Execute Command failed. The error string is printed to stderr. Every failure — config parse error, provider auth failure, transport panic, tool wiring error — maps to this code.

rousseau doctor follows the same convention: exit 0 when every check passes, exit 1 when any check is fail. Warnings and info-level rows do not affect the exit code.

Future releases may split failures into distinct codes (config vs runtime vs network). Today, treat any non-zero exit as retryable but requiring log inspection.

Signal handling

cmd/rousseau/main.go installs a signal handler that cancels the root context.Context on SIGINT and SIGTERM. Every long-lived component (agent loop, transport, cron scheduler, MCP server) honours context cancellation, so the shutdown path is:

  1. SIGINT / SIGTERM received.
  2. Root context is cancelled.
  3. Transports call Stop() on themselves, flushing in-flight messages.
  4. Cron scheduler stops accepting new fires; running fires complete.
  5. Session store Close() is called via defer, checkpointing the WAL.
  6. Execute returns 0.

SIGKILL cannot be caught. If the daemon is kill -9'd mid-turn, the session store's WAL protects against corruption but the in-flight turn is not persisted. The next launch resumes from the last saved state.

systemd restart policy

For the reference Quadlet unit:

[Service]
Restart=on-failure
RestartSec=10

on-failure restarts on any non-zero exit; combined with rousseau's exit code convention this means: exit 0 (SIGTERM from systemctl stop) does not restart, exit 1 does.

For daemons that hit persistent errors (bad config, wrong provider auth), on-failure will thrash. Watch journalctl for the failure reason before assuming the retry loop will recover.

Kubernetes probe semantics

Rousseau ships no HTTP liveness/readiness endpoint by design. Kubernetes probes must be either:

  • exec probes running rousseau doctor --config /etc/rousseau/config.yaml (returns 0 on healthy, 1 on failure), or
  • Absent, with the pod relying on restartPolicy: Always and the daemon's own error handling.

rousseau doctor is cheap (~50ms) so it is a fine liveness probe. Do not use it as a readiness probe — a fail on provider.claudecli.binary shouldn't take the pod out of rotation if the failure will not self-heal.

Handled errors

Errors that produce exit code 1 by way of the CLI error surface include:

  • Config load failure — YAML parse error, unknown field, invalid type.
  • Provider auth failure — missing API key, invalid credentials, invalid Bedrock / Vertex region.
  • Transport startup failure — missing token, unreachable IMAP/SMTP host, whatsmeow protocol error.
  • Store open failure — permission denied on ~/.local/share/rousseau/, disk full.
  • Doctor check failure — any fail row makes doctor return exit 1.
  • Cron cron-expression parse failurerousseau cron add validates before persisting.

Unhandled panics

go test -race is run on every CI build, so panics are extremely rare. When they do happen, the Go runtime prints the panic + stack trace to stderr and exits with a non-zero code from the runtime — typically 2, but this is Go's convention and not something rousseau controls.

For production, wrap the daemon in a supervisor that captures stderr on abnormal exit and reports the trace.

Next

Type to search 150+ pages. Ranking: BM25 with title/description boost.