Files

18 KiB
Raw Permalink Blame History

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-28: chatroom epics A–E are merged (section 8); the pivot came 2026-10-27 from CHATROOM_SPEC.md, GROKBOT_NOTES.md and JEV_STRATEGIES.md. The repo now has a GitHub remote (jacisjake/so-many-crows) and lives by the epic flow: issues/PRs in project board 1, one PR per task with falsifiable red→green tests.

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). Remote: git@github.com:jacisjake/so-many-crows.git (main is protected by convention, not settings: land via PR merge). 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, and (chatroom) a 3-nudge loop guard per expectation — after three nudges with no new content from the crow, the harness stops and summarizes to the owner (CHATROOM_SPEC.md section 7).

6. Verified state (2026-10-28)

  • All 113 tests pass with -race.
  • Chatroom epics A–E merged (issues #3–#45 closed): nest loop with Jev-first kind tagging; dispatch modes solo/conspiracy/split with first-claim arbitration, invited lenses, divergence nudges, @Elder synthesis and trust-gated speaking; Quill (assistant: status from nest history, relay, ask_owner fail-safe decisions); Boss (spins registry crows under MaxSpunCrows, creates nests); Sentry watchdog (above-band Jev noul verdicts flag + quarantine; reinstate/retire escalate through ask_owner; Sentry earns triage/watch trust). New roles decided and shipped: Quill (assistant crow), Boss (Chief of Staff). Live wiring so far is proven with mock/scripted brains and fake Jev servers only.
  • 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.

Done since the pivot (epics A–E, 2026-10-28): spec blockers resolved; nest conversation loop (spec step 1); dispatch (modes, arbitration, conspiracy, divergence, Elder, trust gates, guardrails — including the Flock.reserve fix, saturated flocks now honor context); assistant crow Quill (step 2); Boss + Sentry watchdog (step 4's orchestration halves).

Next, in order:

  1. The expectation ledger plus nudges (spec step 3, CHATROOM_SPEC.md section 7): extraction into a ledger, the first two checks (promise kept, single owner), Jev questions per JEV_STRATEGIES.md; the 3-loop guard already exists — this gives it something to watch. The "log-only experiment" (kind + who-responds, no acting) can run beside nest traffic to collect calibration data.
  2. Live-model chatroom session: drive Quill relay → conspiracy → nudge → Boss spin-up → Sentry flag → reinstate end to end with real models before demoing to Smokey (mock-only today).
  3. Opt-in debate mode (spec step 5, section 8) — first tasks of an Epic F.
  4. Visible budgets in the UI (spec step 6, section 9). UI rules apply: screenshots at ~400px and desktop.
  5. 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 (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; GCP. GitHub happened: remote jacisjake/so-many-crows, commit email must stay 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.