Cryptograss:Mood Architecture
Overview
Moods are the spatial and organizational unit of the emergent agent system. The architecture deliberately avoids virtualizing Moods as individual environments (no separate Docker container per Mood). A Mood is a filesystem scope — a directory — while execution and runtime context are decoupled and managed by shared processes.[unverified]
This design was settled on 1 Nov 2026 to replace the on-demand per-Mood container plans in magenta#343 and maybelle-config#255, which were closed in its favour. Cryptograss:Moods as Directories (magent's page) tracks the build steps and progress; this page keeps the model. Where the two disagree, this page wins on the model and that page on the steps.[unverified]
Core Philosophy
Mood as a Directory
A Mood does not need its own operating-system process or container to exist. It is a directory path, named by its slug: /moods/chartifacts, not /moods/chartifacts-something-else. The directory holds the Mood's clones and files.[unverified]
What the directory does not hold: context. A session's context lives in its session, keyed by session id — Pi and Claude Code both. The directory only says where a turn runs. Compacting, resuming and moving context are session operations, not file operations.[unverified]
Agent as a Process
Agents do not wake up "inside" a Mood. Each agent runs as one long-lived container and poller that mounts the entire /moods tree. To act within a Mood, the agent starts its turn in /moods/<slug>. One agent instance serves every Mood; there is no "starting" phase for a Mood, because the agent is already running and only reads the directory.[unverified]
Component Structure
1. The /moods Mount
The shared filesystem. Every agent container and the shared workbench mount the whole /moods tree — the directory structure at /moods is what every container sees, whatever path it holds on the host. It is the single source of truth for all active Moods.[unverified]
2. Agents
Each agent has exactly one container and one poller. Because the container mounts /moods, the agent can serve any Mood on waking. New sessions start in their Mood's directory (magenta#345); a resumed session stays where it ran. Without /moods, nothing changes.[unverified]
3. The Workbench (People)
A single shared container called workbench is provided for people. It also mounts /moods.[unverified]
- SSH access: users ssh into the workbench.[unverified]
- Navigation:
cd /moods/<slug>puts you in a Mood.[unverified] - Capabilities: terminals, code-server, and running dev servers for previews.[unverified]
4. Previews
Previews run only in the workbench. Agent containers publish no ports, so an agent that wants a preview shown starts the dev server in workbench over ssh — starting a server needs only Bash.[unverified]
Because preview processes share workbench's network namespace, Caddy on the host routes <slug><N>.hunter… hostnames to ports in workbench. Slug names are chosen over numbered hostnames (p<id>.hunter…, which would need nothing at all) because the slug is what people will actually remember. The mapping is not per-request routing: a small job reads the Mood list and rewrites Caddy's slug-to-port map when it changes, so a new Mood gets its hostname without a redeploy. Until that job lands (step 4), previews answer on workbench0-9.hunter….[unverified]
Thread Management
Threads are concurrent lines of work within a Mood. They are modeled as git branches/worktrees, not separate filesystem scopes.[unverified]
Threads as Worktrees
A thread lives at /moods/<slug>/threads/<thread-name>/ — a git worktree branched off the Mood's checkout. It is made on first touch and shared by everyone working in the thread. After magenta#345, a thread's turns run in the Mood the thread came from: the worktree is where the files are, the Mood is where the session runs.[unverified]
Moving Threads
Moving a thread moves the worktree — the directory — and no session, in either harness. Because context lives in sessions keyed by session id and not in the directory, a thread can move between Moods without anything being copied, rewritten or re-associated: the next turn in the thread resumes its session where it ran, and finds the thread's files at the worktree's new location. (The earlier draft had Pi rewriting a header cwd and Claude Code moving history files; that is not how it works after #345.)[unverified]
Checkbox Integration
The thread checkboxes are workflow actions, not filesystem operations — none of them merges, archives or touches a worktree.[unverified]
- Incorporate (leaving/joining):
/incorporate— magent writes an account of the thread into the Mood it joins or leaves (magenta#205).[unverified] - Compact: compacts each emergent's session in the thread, so the context heap is fresh going forward.[unverified]
- Inform: posts a line in the thread and adds a note to the next wake, so whoever is called next knows where things stand.[unverified]
Trade-offs
Loss of Isolation
Moving away from per-Mood containers loses strict resource and security isolation between Moods: a runaway process in one Mood shares workbench (and the mounts) with everyone else. One mitigation is in the shared-mount design itself — the moods group (gid 3000), setgid directories and a default ACL keep every clone the group's to write, so one agent's checkout is another's to commit in, whatever their umask, and git trusts /moods/* system-wide. If one Mood ever needs hard isolation — running untrusted code, say — it can be promoted back to a container of its own without breaking the directory-based addressing scheme.[unverified]
Open calls (documented ahead, not yet settled)
These are decisions the build will need; they are written down so they are not lost, and none is decided yet.[unverified]
- How re and blu start a preview in workbench. Two options on the table (magent, 1 Nov ~10:20Z): (a) a vault key per agent — magent's recommendation, since nothing generates a secret on its own and Justin would place the keys; or (b) only magent starts previews, through docker.sock — simpler, but a preview from re or blu waits on magent. re's view: the vault key, because waiting on magent makes previews a single point of failure, and the blast radius is workbench's filesystem (no docker.sock there), which both agents already reach through /moods. Justin to call it in step 4.[unverified]
- Who a workbench shell runs as. Today the router enters workbench with
su - magent, so a person's session there runs as magent's user. The ACLs keep the files right; what is not yet solved is identity — for the people's box, sessions should eventually run as the person (or at least be attributable in the logs), not impersonate magent (re, review of #257, 1 Nov ~10:21Z). Not a blocker for step 3; to be settled by step 5.[unverified] - The Caddy map's trigger.' The intended shape: regenerate when the Mood list changes, not on a timer. Whether that means a hook on Mood create/rename or a cheap compare-and-rewrite is magent's call in step 4.[unverified]