74 lines
3.4 KiB
Markdown
74 lines
3.4 KiB
Markdown
# semprini-maintainer — Claude Instructions
|
|
|
|
## What this is
|
|
|
|
A standalone, always-on **autonomous maintenance agent** for the semprini.me
|
|
container stack, deployed as a systemd service (`semprini-maintainer`). It keeps
|
|
the stack healthy and current and talks to the operator over Matrix
|
|
(chat.semprini.me).
|
|
|
|
It is the *unattended driver* for the canonical **stack-support agent**, which
|
|
lives in the separate **semprini-core** repo. This project does not reimplement
|
|
that logic — it gathers state and invokes the stack-support agent headless via
|
|
Claude Code, then relays/acts on the agent's JSON result.
|
|
|
|
## Relationship to other projects
|
|
|
|
- **semprini-core** (`../semprini-core`) — the Enterprise Landing Zone this agent
|
|
maintains. At runtime the daemon reads, from the path in `config.json`
|
|
`repo_dir`:
|
|
- `agents/stack-support.agent.md` (injected as system prompt)
|
|
- `docs/stack-support-runbook.md`
|
|
- `core_stack/compose*.yml`
|
|
Keep the stack-support agent definition canonical **in semprini-core** — do not
|
|
fork a copy here.
|
|
- **Other projects** (e.g. `../semprini-blog`) that register Uptime Kuma monitors
|
|
are also maintained; their repos are exposed to the agent via `extra_dirs`.
|
|
|
|
## How it works (one screen)
|
|
|
|
- **Scope** = whatever **Uptime Kuma** tracks at check time, re-read every cycle
|
|
(never hard-coded). Kuma's heartbeat is the health signal — the daemon does not
|
|
re-probe. Kuma's root-owned `kuma.db` is read via a throwaway `keinos/sqlite3`
|
|
container mounting the `uptime-kuma-data` volume read-only.
|
|
- **Health loop** (5 min): down services confirmed over `down_confirmations`
|
|
cycles → invoke stack-support agent to remediate → report to Matrix. Also
|
|
reads Kuma's `important=1` heartbeat transitions since the last cycle (tracked
|
|
by heartbeat id) to catch services that blipped DOWN→UP on their own, and
|
|
hands those self-recovered windows to the agent for a log post-mortem.
|
|
- **Upgrade loop** (7 days): resolve containers behind tracked services → agent
|
|
researches + applies safe upgrades (test, pin, rollback); escalates
|
|
major/breaking ones to Matrix.
|
|
- **Matrix**: bot `@maintainer-bot:semprini.me` DMs `@paul:semprini.me`. Escalations
|
|
are numbered questions; the reply is fed back to the agent to carry out.
|
|
- **Autonomy** (`config.json` → `autonomy`): `full` (default) | `fix-only` |
|
|
`notify`.
|
|
|
|
## Conventions / guardrails
|
|
|
|
- Runs as host user **`paul`** (in `docker` group), **not root** — Claude Code
|
|
refuses `--permission-mode bypassPermissions` under root.
|
|
- `maintainer.py` is **stdlib-only** — keep it dependency-free so it deploys
|
|
without a venv.
|
|
- Secrets live in `config.json` and `.bot-secrets` — **never commit** (gitignored).
|
|
- Prefer editing existing files over adding new ones.
|
|
|
|
## Files
|
|
|
|
| File | Purpose |
|
|
|---|---|
|
|
| `maintainer.py` | The daemon (stdlib only). |
|
|
| `config.example.json` | Template — copy to `config.json`. |
|
|
| `register-matrix-bot.sh` | Creates `@maintainer-bot` account + DM room; writes token to config. |
|
|
| `install.sh` | Syntax-check, install unit, enable + start. |
|
|
| `semprini-maintainer.service` | systemd unit (runs as `paul`). |
|
|
| `docs/architecture.md` | Architecture decision record. |
|
|
|
|
## Test without the service
|
|
|
|
```bash
|
|
python3 maintainer.py --config config.json --once health
|
|
python3 maintainer.py --config config.json --once upgrade
|
|
python3 maintainer.py --config config.json --once replies
|
|
```
|