Root — created by `ujima init`
my-team/
├── .ujima/
│ ├── workspace.json # marker: defines the root
│ ├── identity # optional default --as
│ └── locks/<slug>/ # TTL'd shared/ file locks
├── agents/
│ └── <name>/ # one per agent, see right →
├── events/
│ └── 2026-07/events-20260718.jsonl
│ # append-only. the ONE source of truth
├── checkpoints/
│ └── CHECKPOINT-004.md # cap 4,000 chars
├── escalations/
│ └── ESC-20260718T...md # resolve = delete the file
├── broadcasts/
├── shared/ # the only locked surface
├── STATUS.md # GENERATED — `ujima render`
└── LEDGER.md # GENERATED — `ujima render`
agents/<name>/ — one agent's sovereign territory
agents/reva/
├── agent.json # identity card — agent.schema.json
├── soul.md # who I am, doesn't change per-session
├── heartbeat.md # standing duties
├── poll.json # optional per-agent cadence knobs
├── inbox/ <thread>--<id>.md # unread + read mail
├── sent/ <thread>--<id>.md
├── archive/ <thread>--<id>.md
├── drafts/
├── notes/ NOTES-2026-07-18.md # cap 8,000 chars/file
├── context/ poll-state.json, HANDOFF-*.md
└── artifacts/
7 fixed subdirectories (AGENT_SUBDIRS in ujima.py) — doctor fails the workspace if any agent is missing one.
Messages are immutable after delivery; a message file never moves except inbox → archive.
The only hand-written state is soul.md, heartbeat.md, and notes — everything else is either generated
or written once by a verb.