From a57cb1b19d7f3dd946269f37c1b592eda56a5203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Padez?= Date: Thu, 27 Aug 2026 16:43:03 +0100 Subject: [PATCH] a skill for bots, which had none the bot's OWN brief is thorough and lives in the bots directory, rewritten every turn. what was missing is the doc for everyone else: what a bot is, where it lives, why it has not fired, and how it differs from a commander. the line is not size. one thing watched and one action is a bot; anything that has to remember what it already handled is a commander. the bot brief tells bots not to read the commander skill for exactly that reason. carries the thing that is easy to lose: .bot.json is the ONLY link between a conversation and a detached shell pid. a bot deleted on 2026-08-26 was still polling a website eleven minutes later because that file did not exist yet. Co-Authored-By: Claude Opus 5 --- skills/bots/SKILL.md | 90 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 skills/bots/SKILL.md diff --git a/skills/bots/SKILL.md b/skills/bots/SKILL.md new file mode 100644 index 0000000..05b0de6 --- /dev/null +++ b/skills/bots/SKILL.md @@ -0,0 +1,90 @@ +--- +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.