# So Many Crows A flock of AI agents. Hand it a todo list; a Sentry crow triages each item, crows fly, and trust is earned over time. Version one: command-line, text-only crows. No web, files, or APIs until each has guardrails. ## Run ``` go build -o crows . ./crows run todo.txt # offline mock brain (no key needed) ANTHROPIC_API_KEY=... ./crows run todo.txt # real crows (Claude) ./crows trust # trust ledger ./crows log -n 20 # flight log ``` Optional: `CROWS_MODEL` overrides the model. Results land in `todo.crows.md`; state lives in `.crows/`. ## Web server ``` cd ui && npm install && npm run build && cd .. # builds the UI into ./web ./crows serve # localhost:8080; API + Server-Sent Events (SSE); Ctrl-C to stop ``` The server runs the same pipeline as `crows run`, but items fly in parallel and approvals come over HTTP. `EVENTS.md` is the contract for events and the API: ``` curl -XPOST localhost:8080/api/lists -d '{"text":"- [ ] What is a crow","requester":"me"}' curl -N "localhost:8080/api/events?list_id=" # replays the list's events so far, then streams curl -XPOST localhost:8080/api/verdicts -d '{"item_id":"-1","target":"triage","approve":true}' curl localhost:8080/api/trust ``` - Item IDs are `-`, numbered from 1 in list order. - Approvals left unanswered for 10 minutes count as rejected (`verdict: timeout`). `-approval-timeout` changes this for testing. - A flooding requester gets 429 with `message` and `cooldown_s`; any list of theirs that is still flying also gets `requester.pranked`. - UI development: run `crows serve`, then `cd ui && npm run dev` (Vite proxies `/api`). The crow art is `art/crow.svg`; the UI's `CrowView` mirrors `RIVE_CONTRACT.md`, so a Rive-backed view replaces the SVG stand-in later. - POST bodies are capped at 64 KB. Static files are served from `./web` when that folder exists. ## Nests A nest is a room where a team of crows works together. Create one with its core crows; jobs in that nest use the core first and borrow from the rest of the flock when they need more (a murder of 3 in a 2-crow nest borrows one). The Sentry and Elder serve every nest. ``` curl localhost:8080/api/nests # {"roster": [...], "nests": [...]} curl -XPOST localhost:8080/api/nests -d '{"name":"Launch copy","crows":["Pepper","Inky"]}' # 201, nest_id "launch-copy" curl -XPOST localhost:8080/api/lists -d '{"text":"- [ ] Should we launch on a Tuesday","nest_id":"launch-copy"}' ./crows run todo.txt -nest launch-copy ``` - The default nest is `todo`, with an empty core: crows come from the whole flock and are never "borrowed". - `crow.flying` carries `borrowed: true` for a crow from outside the nest's core. - Names are 1–40 characters; `nest_id` is the name as a slug and must be unique (409 on a clash). 1–8 roster crows, no repeats, at most 20 nests. Nests are saved in `.crows/nests.json`. ## How an item flies 1. **Sentry** classifies it: `solo` (one crow), `split` (2–4 subtasks in parallel), or `murder` (3 crows answer independently). 2. Split and murder results go to the **Elder**, who merges them and flags disagreement. 3. You approve or reject, depending on trust. ## Trust ladder | Level | Triage | Output | |----------|-------------------|--------------------------------| | Stranger | you approve | you approve | | Regular | you approve | auto-accepted, marked unreviewed | | Trusted | runs on its own | runs on its own | - Promotion: 10 clean approvals in a row (the flock as a whole needs 20). - Demotion: one rejection drops a level. - A crow's level blends its own record with the flock's record for that task type, so new crows start on the flock's reputation. - The Sentry earns triage trust separately, per task type. - The least-trusted crow on a job sets the approval bar. ## Guardrails (`guardrails.go`) | Guardrail | Default | |---|---| | Crows in flight | 5 | | Murder size | 3 | | Items per list | 20 | | Subtasks per split (depth 1, never re-split) | 4 | | Tokens per item / per list | 20,000 / 100,000 | | Crow timeout | 60s | | Requests per minute per requester | 5 | | Cooldown | 30s, doubling per strike, max 1h | Fail-safe: any answer other than `y`, including no input, is a no. ## Abuse Flooding the flock earns a mild prank message and a cooldown. Strikes fade one per clean day. Nothing destructive, nothing public.