149 lines
17 KiB
Markdown
149 lines
17 KiB
Markdown
# So Many Crows: handoff for the next agent
|
|
|
|
Read this whole file before changing anything. It is the single source of truth for where the project stands, why it is built this way, and what comes next. Last updated 2026-10-27: same day, the roadmap pivoted toward the chatroom spec (section 8) — the pivot came from `CHATROOM_SPEC.md`, `GROKBOT_NOTES.md` and `JEV_STRATEGIES.md`.
|
|
|
|
## 1. What this is
|
|
|
|
So Many Crows is a multi-agent app for one owner, Smokey (Jacob Madsen). You hand a "flock" of AI agents ("crows") a todo list in a chat-room web UI. A **Sentry** crow triages each item, worker crows do the work, an **Elder** crow merges multi-crow results, and **the human approves or rejects** until crows earn trust. It belongs to So Many Crows LLC, a Utah LLC Smokey is registering that builds marketing communications (marcomm) tools.
|
|
|
|
**That pipeline is what runs today, but the direction has changed** (2026-10-27): the target is a conversation-driven chatroom where crows claim work in a nest, two new roles appear (assistant crow, Chief of Staff crow), and Jev audits expectations and nudges stalled work. Read `CHATROOM_SPEC.md` (the design, with `[decided]`/`[proposed]` marks), `GROKBOT_NOTES.md` (its evidence base) and `JEV_STRATEGIES.md` (what the classifier can do). The pipeline isn't deleted — it stays the working app until chatroom work replaces it, and nests, the trust ledger, guardrails, SSE, personas and the flight log all carry over.
|
|
|
|
Working copy: `~/Projects/so-many-crows` on Smokey's Mac (Apple silicon, macOS). There is **no Git remote yet** (see section 8). Do not confuse it with `~/Projects/crows`, an unrelated older folder: never touch it.
|
|
|
|
## 2. How to work with Smokey
|
|
|
|
These are his stated preferences. Follow them every time.
|
|
|
|
- **Direct and brief.** No flattery, no padding. Dry wit is welcome.
|
|
- **One question at a time.** Never stack questions.
|
|
- **Don't guess.** If unsure, look it up or ask. Back factual claims with sources (links).
|
|
- **Incremental, RITE-style** (rapid iterative testing and evaluation): smallest working step, show it, adjust. **Don't add complexity until he asks.**
|
|
- **AP style for acronyms:** spell out on first use with the acronym in parentheses, then the acronym alone.
|
|
- **Guardrails first.** No capability (web, files, APIs, new providers) is enabled until its guardrails are defined. This is the project's founding rule.
|
|
- **Never print, log or paste secrets.** When inspecting `.env`, show names and lengths only.
|
|
- He reviews visually. When you change the UI, render and look at it (both desktop width and about 400 pixels wide) before reporting.
|
|
|
|
## 3. Decisions made, and why
|
|
|
|
### Product model
|
|
- **Crows answer alone or work a list together as a "murder."** The Sentry classifies each item as `solo` (one crow), `split` (2 to 4 subtasks in parallel, never re-split) or `murder` (3 crows answer independently, then the Elder reconciles).
|
|
- **Autonomy is earned, like befriending wild crows.** Trust ladder per crow *and* per task type: 0 stranger (human approves triage and output), 1 regular (output auto-accepted, marked unreviewed), 2 trusted (runs alone). Promotion after 10 clean approvals in a row (the flock as a whole needs 20); one rejection drops a level. A crow's effective level blends its own record with the flock's, so new crows start on the flock's reputation. The Sentry earns triage trust separately (task types `triage/<type>`). The least-trusted crow on a job sets the approval bar.
|
|
- **Abuse gets a mild, fading prank.** Flooding (more than 5 lists a minute per requester) earns a cooldown (30 seconds, doubling per strike, max 1 hour), a prank message ("a crow left a gift on your windshield"), and strikes fade one per clean day. Never destructive, never public.
|
|
- **Nests** are rooms where a team of crows works together. Smokey creates a nest and picks its core crows; when a job needs more crows than the core, the Sentry **borrows** from the rest of the flock (labeled "borrowed"). The default nest `todo` uses the whole flock.
|
|
- **Personas.** Ten crows with backstory, a prompt policy (how they approach work) and an output policy (what answers look like). Hugin (Thought) and Munin (Memory) are grounded in Norse sources: Grímnismál stanza 20 and Snorri's Gylfaginning 38. The Elder sits "where Odin sat," hearing reports. Personality affects voice and approach only, never facts or guardrails. Full sheet: `PERSONAS.md`, generated from `personas.go`.
|
|
- **Banter is generated, not scripted** (Smokey's explicit choice): a retrieval-then-inference loop. On a moment (a rejection, a level-up, rival crows on one job), the server retrieves the involved crows' backstories, their relationship and recent flock events from a short shared memory, then the speaking crow's model writes one line in character. Capped at 2 per list, counted against the list's token budget, never blocks work, and lines are validated (one line, at most 220 characters, no links) or dropped.
|
|
|
|
### Models and providers
|
|
- **The main LLM is switchable** via `CROWS_PROVIDER`: `openrouter` (default model `openrouter/free`, a router that picks a different free model per call) or `anthropic` (default `claude-sonnet-5-5`). Every flight logs which model actually answered.
|
|
- **Jev (TypeSafe) is a classifier, run in tandem with the LLM** for the Sentry's triage, called **directly from TypeSafe** (`https://api.typesafe.ai/v1/systemone`), not via OpenRouter. Jev answers mode and task type with calibrated confidence. Its call stands when both confidences are at least 0.7 and the mode is not `split`. Otherwise the LLM Sentry decides: when Jev is unsure, when it calls a split (Jev can't write subtasks), or when Jev errors. Jev never blocks a list.
|
|
- **Smokey's comparison plan:** a free model first, then Claude over OpenCode Zen, then Claude direct from Anthropic. Status: OpenRouter free works (live-tested). Zen is **not built**: Zen's own docs say its free models only work inside the OpenCode app, so only paid Zen models can be used from this app. Claude direct is **built but never live-tested** (no Anthropic key has been provided).
|
|
- **OpenRouter limits** (corrected 2026-10-27: the account is paid, $50 in credits bought, `is_free_tier: false`): 1,000 free-model requests per day, 20 per minute (https://openrouter.ai/docs/api-reference/limits). Check live with `GET /api/v1/key` (never print the label or key). **Pinned model: `CROWS_MODEL=google/gemma-4-26b-a4b-it:free` (switched from gemma-4-31b-it:free after upstream 429s)**; keep it until Smokey says otherwise. Each todo item costs about 2 to 6 requests (Sentry, crows, Elder, banter). Jev triage saves one request per confidently-triaged item.
|
|
|
|
### Stack
|
|
- **Go backend, standard library only**, Go 1.24 (`go.mod`, module `somanycrows`). Smokey's team prefers Go or Rust, hosted on Firebase or Google Cloud (GCP) later.
|
|
- **Web front end: plain TypeScript with Vite**, no framework. Smokey dislikes Metro and Expo; web first, native later via Rive's native runtimes if needed.
|
|
- **Server-Sent Events (SSE)** stream every event to the browser.
|
|
- **Rive** is the planned animation layer, using **data binding** (view models), since Rive's docs mark state machine inputs as deprecated. The `.riv` file does not exist yet; an SVG stand-in implements the same interface.
|
|
|
|
### Art direction (hard-won; follow exactly)
|
|
- **Pen and ink on cream paper, with stipple shading.** Reference: a Ryan LeCount flash sheet Smokey shared.
|
|
- **Never redraw Smokey's crow** (`art/crow.svg`: disc, beak, head with eye cutout, jaw). He rejected a redraw. Keep its silhouette; animate the jaw for "caw."
|
|
- **No outlines on boxes** (cards, text box, buttons, toasts, dialogs). Shape comes from fill plus a **stippled drop shadow**. No dashed lines anywhere. Stippled drop shadows are approved.
|
|
- **Stipple must be subtle and irregular:** a generated tile of fine random dots (fixed seed) layered at 64 and 47 pixels so it never visibly repeats. Never a regular dot grid (reads as halftone).
|
|
- **Crows never change size** in any animation: motion only (hop, tilt, spin, bob).
|
|
- **Sparkles (four-point star plus broken cross) mean celebration only** (level-up). Waiting is a plain "?". Dazed crows fade in place, with no circling stars (Smokey: "looks like they're drunk").
|
|
- Type: IM Fell English SC (headings) and EB Garamond (text). Colors: paper `#f4eedf`, card `#f9f5ea`, ink `#1c1915`, muted `#5f574b`, red `#9b2f22`. Disc colors (Smokey's art): mint for crows, yellow for the Sentry, gray for the Elder.
|
|
- **Layout is a chat platform** (like Slack or Teams): sidebar with Nests (like channels) and The flock (one list tile per crow showing its latest update, time, and a red "N waiting" count, newest on top); a channel feed where the Sentry and crows post; inline approve and reject buttons; verdicts shown as reactions; composer at the bottom. On phones: two tabs (the current nest, and Flock), with no crow strip across the top.
|
|
- **Spacing is generous** ("spread it out a little").
|
|
- Behavior is Duolingo-like (expressive characters, streaks and level-ups); look is pen and ink.
|
|
|
|
### Safety and security
|
|
- Server listens on **127.0.0.1 only** by default. There is no login; requester names are self-asserted (a pranked user can rename to dodge a cooldown).
|
|
- Approvals are **fail-safe**: anything but an explicit yes is a no; unanswered approvals time out as rejected after 10 minutes.
|
|
- Server text is only ever inserted into the page as text, never as HTML.
|
|
- `.env` (mode 600) holds keys, is git-ignored, and is read line by line by `start.command`, never executed.
|
|
|
|
## 4. Repository map
|
|
|
|
| Path | What it is |
|
|
|---|---|
|
|
| `main.go` | Command line: `run`, `serve`, `trust`, `log`, `personas` |
|
|
| `server.go`, `hub.go`, `approve.go` | HTTP API, SSE hub with per-list replay, web and stdin approvers |
|
|
| `pipeline.go` | The shared run loop for command line and server; emits events; banter moments |
|
|
| `flock.go` | Crows: picking (core-first, then borrow), triage, execute, reconcile, guarded `ask` |
|
|
| `brain.go` | Model clients: `AnthropicBrain`, `ChatBrain` (OpenAI-style, used for OpenRouter), `MockBrain` |
|
|
| `jev.go` | Jev classifier client and the guarded `classify` call |
|
|
| `personas.go`, `banter.go` | Personas and policies; retrieval-then-generation banter |
|
|
| `ledger.go`, `guardrails.go`, `nests.go`, `flightlog.go` | Trust ledger and requester strikes; limits; nests; JSON Lines flight log |
|
|
| `*_test.go` | 31 tests; run with `-race` |
|
|
| `EVENTS.md` | **The server and UI contract**: every event, payload and endpoint. Change it first, then the code. |
|
|
| `CHATROOM_SPEC.md` | **The pivot target**: the chatroom design (message kinds, roles, expectation ledger, build plan). |
|
|
| `GROKBOT_NOTES.md` | Black-box observations of Grok Bot that the spec is built from. |
|
|
| `JEV_STRATEGIES.md` | What Jev can do (question types, bands, calibration) and strategies for using it. |
|
|
| `RIVE_CONTRACT.md` | Spec for the animator's `crows.riv` (data binding, one `Crow` artboard for v1) |
|
|
| `PERSONAS.md` | Generated persona sheet. Regenerate with `./crows personas > PERSONAS.md` after editing `personas.go` |
|
|
| `ui/` | Vite + TypeScript source (`src/main.ts`, `crow.ts`, `api.ts`, `style.css`). Builds into `web/` (git-ignored) |
|
|
| `art/crow.svg` | Smokey's crow art. Do not alter. |
|
|
| `start.command` | Double-click launcher: reads `.env`, opens the browser, runs `./crows serve` |
|
|
| `dist/` | Git-ignored build drops (`crows-darwin-arm64`, `crows-linux-arm64`, `update.tgz`) |
|
|
| `.crows/` | Git-ignored runtime state: `ledger.json`, `nests.json`, `flight.jsonl` |
|
|
|
|
## 5. How to build, test and run
|
|
|
|
```
|
|
go version # need Go 1.24+; if missing: brew install go
|
|
go vet ./... && go test -race ./...
|
|
go build -o crows . # Mac binary (start.command expects ./crows)
|
|
cd ui && npm install && npm run build && cd .. # builds the UI into web/
|
|
./start.command # or double-click it; opens http://127.0.0.1:8080
|
|
./start.command --mock # offline mock crows: no keys, no cost
|
|
./crows run todo.txt # command-line run (stdin approvals; banter not printed yet)
|
|
./crows log -n 30 # recent flight log; each flight shows model, tokens, ms
|
|
./crows trust # trust ledger
|
|
```
|
|
|
|
UI development: run the server, then `cd ui && npm run dev` (Vite proxies `/api` to port 8080). Delete `.crows/` to reset state (it holds trust and nests).
|
|
|
|
`.env` keys (names only; never print values): `OPENROUTER_API_KEY` (present), `TYPESAFE_API_KEY` (present), optional `ANTHROPIC_API_KEY`, `CROWS_PROVIDER`, `CROWS_MODEL`, `CROWS_JEV_MODEL` (default `jev-latest`), `CROWS_JEV_THRESHOLD` (default 0.7). Inspect safely with: `sed -n 's/^\([A-Z_]*\)=.*/\1/p' .env`.
|
|
|
|
Guardrail defaults (`guardrails.go`): 5 crows in flight, murder of 3, 20 items per list, 2,000 characters per item, 4 subtasks, 20,000 tokens per item, 100,000 per list, 60-second crow timeout, promote after 10, 5 requests per minute, cooldown 30 seconds doubling to 1 hour, 2 banter calls per list.
|
|
|
|
## 6. Verified state (2026-10-27)
|
|
|
|
- All 31 tests pass with `-race`.
|
|
- Live test with real keys: Jev triaged "Should we launch a CLI or a web app first" as murder (91%) and analysis (100%) in 195 ms; Hugin, Munin and Corvina answered on three different free models; the Elder merged in character; after a rejection, Soot generated: *"Hugin, another grand scheme foiled? Perhaps the CLI is the tiny bird worth chasing."* Total 6,257 tokens.
|
|
- UI end-to-end tested headless at desktop and phone widths: nests, borrowing, approvals, reactions, prank cooldown, banter asides. No console errors.
|
|
- **Not yet done:** Smokey has not yet used the app himself through `start.command` with real crows. It still matters (section 8, step 6), but the chatroom work leads now.
|
|
|
|
## 7. Known gaps and gotchas
|
|
|
|
- **The command line doesn't print banter.** The web UI and flight log show it; `crows run` ignores the `crow.banter` event.
|
|
- **Claude-direct path is untested live.** It is unit-tested only.
|
|
- **Free-model quality is uneven.** In testing, one free model swapped crow and raven examples in a table. That is what the approval gate is for.
|
|
- **No login and self-asserted requester names** (see section 3).
|
|
- **"Regular" trust is half-built:** output is marked unreviewed, but there is no command or view to review it later.
|
|
- **Killing the server:** never use `pkill -f "crows serve"` from a shell whose own command line contains that text; it kills the shell. Find the process ID first (`pgrep -x crows`) or use Ctrl-C in its window.
|
|
- **Deletes may be blocked** in some sandboxed shells. Git objects are read-only, so re-extracting an archive over `.git` prints harmless "Permission denied" lines.
|
|
- `~/Projects/so-many-crows-mac.tgz` is an old installer archive. Safe for Smokey to delete; don't delete it yourself without asking.
|
|
|
|
## 8. What happens next (in order)
|
|
|
|
The roadmap pivoted (2026-10-27): the chatroom spec is now the direction. Do one step at a time. Show the result. Ask before starting the next. **Contracts first** still applies: change `EVENTS.md` before UI or server code for any chatroom message kind or event.
|
|
|
|
1. **Resolve the spec's open blockers before code** (Smokey decides, not the agent): message-kind tagging (Jev can tag it — see `JEV_STRATEGIES.md` strategy 1), the nudge loop guard (nudges count against the nest budget; a miss must not restart work forever), first-claim-wins arbitration when two crows claim at once, and Sentry/Elder's fate (spec open question 4).
|
|
2. **Chatroom build plan step 1** (`CHATROOM_SPEC.md` section 12): one nest, owner plus two crows, event-driven; `say`, `request`, `claim`, `deliver` with @-addressing; mock brain first, live models after it works.
|
|
3. **The assistant crow** as the owner's default chat, relaying into the nest.
|
|
4. **The expectation ledger plus nudges**, two checks first (promise kept, single owner), Jev questions per `JEV_STRATEGIES.md`. The "log-only experiment" there (kind + who-responds, no acting) can run beside current pipeline traffic to collect calibration data.
|
|
5. **The COS crow** spinning up crows on request. Then opt-in debate mode, then visible budgets.
|
|
6. **Smokey's first hands-on session** with the current pipeline stays worth supporting — if he runs `start.command`, watch the Terminal and `./crows log` and fix what he hits (1,000-a-day free-model limit).
|
|
|
|
Paused by the pivot (resume only if Smokey asks): provider comparison beyond pinning a model (the pinned `CROWS_MODEL` stays); CLI banter printing; review queue for "regular" trust; threads; Rive; web-search capability; login; GitHub; GCP. GitHub note for when it happens: Smokey creates an empty repo `jacisjake/so-many-crows`, commit email must be `150986568+jacisjake@users.noreply.github.com`.
|
|
|
|
## 9. Conventions
|
|
|
|
- **Contracts first:** change `EVENTS.md` (or `RIVE_CONTRACT.md`) before code on either side of it.
|
|
- **Every model call goes through `Flock.ask` or `Flock.classify`** so budgets, the 5-crow cap, timeouts and the flight log always apply. Never call a brain directly.
|
|
- **Tests:** add tests for every new path, including failure paths. Run `gofmt -l .`, `go vet ./...`, `go test -race ./...` before committing.
|
|
- **Commits:** small, descriptive messages. Never commit `.env`, `.crows/`, `web/`, `dist/`, binaries or screenshots.
|
|
- **UI:** server data goes into the page with `textContent`, never `innerHTML`. Test at about 400 pixels wide and at desktop width, and look at screenshots before reporting.
|