--- name: bots title: Bots description: >- Use when the owner asks about a bot — what one is, how to start one, why one has not fired, how to stop one, or why it is a bot rather than a commander. Also read it before setting one up FOR them. Carries where a bot lives, the record that is the only thing able to stop it, and the two ways one dies quietly. version: 1 --- # A bot is one thing watched and one thing done "Notify me when x.com is down." "Tell the todo guy when NYT has a new headline." That sentence is the whole specification, and **the first message arms it** — a bot that interviews the owner first has already failed at the thing that makes it a bot. ## Bot or commander — the line, and it is not size - **A bot watches ONE thing and reacts.** A shell loop that exits when a condition is true. No ledger, no procedures, no dispatch policy. Arming is `run_in_background: true` on a script and nothing else. - **A commander runs a JOB.** It classifies, it decides, it spawns agents, it keeps `JOB.md` and a record of what it has already handled, and it is set up in a conversation before it is armed. If the answer to "what does it do when it fires" is one action, it is a bot. If the answer needs a paragraph, or the thing has to remember what it already did, it is a commander. Reach for [[commander]] there — and note that the bot brief tells bots explicitly NOT to read it, because a bot that starts building a dispatch loop has stopped being a bot. ## Where everything is ``` $DATA_PATH//bots/ ├── CLAUDE.md the brief every bot runs under — PLATFORM-OWNED, rewritten every turn ├── .sh the watch itself, one per bot ├── .state its baseline, so a restart resumes instead of re-firing └── .bot.json the record: script, state, pids, what it watches ``` Every bot shares that one directory — `getBotsDir(email)` — which is why the record is named for the session id rather than filed under it. The chat list for them is `GET /api/chat/sessions?context=bots`. The context IS the address: no `cwd` needed, because there is only one bots directory per owner. ## `.bot.json` is the only thing that can stop a bot A watch is a detached shell process. Nothing else on the machine knows it belongs to a bot — not the transcript, not the session list, and not the process table, which sees an anonymous `bash`. The record is the only link between the conversation and the pid. That cost was paid before the file existed: a bot deleted on 2026-08-26 was still polling a website eleven minutes later, with nothing in the product able to find it. So when a bot re-arms, it rewrites the record with the new pid, in the same turn. A record that is stale is worse than absent — it names a pid that may now belong to something else entirely. ## Why a bot has not fired — check in this order 1. **Is the process alive?** Read the record, then `ps -p `. A watch dies with a reboot and nothing re-arms it automatically; the bot re-arms itself on its next turn, which needs a message. 2. **Has it fired already and not re-armed?** The exit IS the signal. A bot that fired, reported, and never re-armed looks identical to one that is watching quietly. 3. **Is the baseline wrong?** A "has it changed" watch that wrote its state before exiting will not fire on the same change twice. That is correct, and it is also the usual reason a test does not reproduce. 4. **Is it watching the wrong thing?** `watches` in the record is the bot's own description of its job. Compare it to what the owner actually asked for before debugging any further down. ## The two ways a bot dies quietly - **A reboot.** The loop is a plain background process; nothing supervises it. The record survives, which is what lets the next turn re-arm from it — so a bot that has been silent since a restart is not broken, it is unstarted. - **A fired-and-forgotten watch.** It exited, said its line, and the turn ended without re-arming. Act, re-arm, record — in one turn — is the rule in the brief precisely because forgetting the third step is invisible until the thing never fires again. ## Setting one up for the owner Say the sentence and let the bot arm itself. The brief in that directory is thorough and current — it is rewritten on every turn from `src/servers/api/bots/brief.ts`, so it cannot drift from the platform the way a copy here would. **Do not paste the mechanism into the conversation.** If you find yourself explaining the script shape, the record, or the naming rules to a bot, it has not read its own brief and the fix is to point at it. ## Naming A bot's automatic title is the owner's first sentence, truncated, typos included. It is a prompt, not a name. Two to four words, the subject and what is watched about it — `newweb uptime`, `NYT front page`, `gitea disk space`. Not "Website Monitor Bot": every bot is a monitor, so the word earns nothing in a list of them.