A template for an orchestration control plane and persistent knowledge base for a fleet of projects driven by Solo and coding agents. This is a metaproject: from here, an agent coordinates work across every other project in your fleet and records what happened so it can be recalled months or years later.
It ships with all of the workflows and none of the data: the playbooks, skills, registry contract, and operating brief, with example (empty) registries in place of any real project or client records. Fork it, wire it to your own fleet, and start logging.
Keep your working copy private. The moment you begin recording real work, your brain accumulates client data, discovery notes, and decision history. That belongs in a private repo. This public template is safe to share precisely because it contains no such data — yours will.
The fleet lives under a single root directory (this template assumes ~/Herd/, the
default for Laravel Herd; change it to wherever your
checkouts live). brain itself is one project in that fleet.
- brain's own Solo data stores (scratchpads / todos / KV scoped to the
brainSolo project) = the orchestrator's live working memory — in-flight coordination. - This directory on disk = the durable archive and knowledge base. Plain files, version-controllable, outlives any Solo project record. Everything that must survive lives here, never only in Solo's database.
Solo deletes all project-scoped scratchpads/todos/KV when a project is deleted, and
project_id changes on re-add. So Solo state is treated as disposable; disk is the
source of truth.
- Record early and often. Storage is cheap; forgotten context is expensive. When real work or a real decision happens in any project, write it down here. It is brain's job to remember what the user will forget.
- History goes back to the beginning. Logs are append-only. You should be able to answer questions like "9 months ago, why did we decide not to use approach X on project Y?" — so capture decisions with their rationale, not just outcomes.
- Client filtering must be complete and accurate. Some projects are client work.
Client-scoped recall and client-facing proposals depend on an authoritative
mapping (see
clients.json). Never guess which project belongs to whom. - Disk first, then Solo. For lifecycle operations, do the Solo-side work, confirm it, then update the disk records last — so files never claim a state that did not happen.
- Never reference projects by raw ID. IDs change on re-add. Resolve by name via
projects.jsonat action time. - Commit on every meaningful change, straight to
main, and push. Lifecycle events, logged work, decision records, and convention changes each get their own commit and an immediategit push. Single user, single machine — keep the backup current with no batching or branching ceremony.
Your working copy of this repo is backed up to a private git repo so the knowledge
base survives loss of the local machine. Everything written under brain/ is therefore
destined for that remote. The rules that follow are non-negotiable:
- Never write secrets to the filesystem. No tokens, API keys, passwords, private keys, connection strings, or credentials — ever. Read them from a secret store (e.g. the OS keychain) at the moment of use. There is no legitimate reason to write them down.
- Redact on export. When a park exports a project's scratchpads/todos/KV, scan the
content for secret-like values first and replace them with
[REDACTED:<reason>]before writing the snapshot. Note any redactions in the park log. A secret must never reach disk, even in an archive. - Client data is private. Discovery, notes, and proposals for client projects live in this repo. Keep your repo private; do not surface client data across clients.
.gitignoreis a backstop, not the policy. It excludes common secret file patterns as a safety net, but rule 1 is the real control.
brain/
├── README.md # this file
├── CLAUDE.md # operating brief auto-loaded by agents working in brain
├── AGENTS.md # -> CLAUDE.md (symlink, for harnesses that read AGENTS.md)
├── projects.json # fleet registry — SOURCE OF TRUTH for every project
├── clients.json # authoritative project -> client mapping
├── playbooks/
│ ├── park.md # park procedure (export Solo state -> disk, delete project)
│ ├── restore.md # restore procedure (re-add project, recreate Solo state)
│ ├── work-on.md # session start: load a project + its `needs` graph
│ ├── new-project.md # clone a git URL into the fleet root and register it
│ ├── coordinate-feature.md # orchestrator role: spin up & oversee a coordinator agent
│ ├── cleanup.md # tidy Solo clutter (worktrees, agents, scratchpads, todos)
│ └── remind-me.md # "remind me to X" -> a brain to-do (defaults to tomorrow)
├── skills/
│ ├── multi-agent-build/ # canonical method source; symlink into ~/.claude/skills
│ └── laravel-cloud-deploy/ # method for provisioning Laravel Cloud safely
├── templates/
│ └── workflow.md # the .solo/workflow.md profile template projects copy
├── ideas/ # experiment/spike notes — especially the ones that FAILED
└── projects/
└── <name>/ # permanent per-project home (exists active OR parked)
├── log.md # append-only, dated work journal (warm history)
├── decisions.md # decision records: what we chose, what we rejected, WHY
└── snapshots/
└── <YYYY-MM-DD>/ # Solo-state export written on each park (cold history)
├── manifest.json
├── scratchpads/*.md
├── todos.json
├── kv.json
└── prompt-templates.json
projects/<name>/ directories are created lazily — the first time you log work,
record a decision, or park the project. Not every project has one.
Single source of truth. Every Solo project is always present here (including
brain itself), so nothing ever has to ask "is this project defined?". Keep it
truthful at every lifecycle event:
| Event | Update |
|---|---|
| Create | Add entry: status: active, current_id, id_history: [id], client (default internal) |
| Park | status -> parked, remove current_id, set parked_at (ID stays in id_history) |
| Restore | status -> active, set current_id to the NEW id, append it to id_history, set restored_at |
| Rename / move | Update the key and/or path |
| Permanent delete | status -> deleted (tombstone — do NOT remove the key, so historical IDs still resolve) |
No-null convention: a field is either present with a real value or omitted —
never store null. So parked_at/restored_at/current_id are absent until the
event that sets them occurs. client is never null; it defaults to your internal org.
needs is always present as an array (empty by default).
status meaning: active = currently loaded in Solo (has current_id);
parked = not currently loaded. A parked entry with an empty id_history and no
parked_at has simply never been loaded (e.g. a dependency folder you track but only
mount on demand). parked_at is present only when the project was parked via the park
playbook (i.e. it had Solo state to archive).
path and repo: path is the project folder relative to the fleet root (almost
always just the folder name) — keeping it relative makes renames and host moves clean.
Resolve the absolute path as ~/Herd/<path> wherever a tool needs it (e.g.
create_project). repo is the canonical clone URL, kept for provenance and client
re-derivation. Note the registry key (project name) can differ from both the folder
(path) and the repo name.
phase (separate from status): a project's lifecycle stage —
pre-launch | launched | maintenance. Set per-project as you engage with it (not guessed
in bulk); absent until set. It biases the default feature-build mode (pre-launch → autonomous).
Because IDs are globally unique and monotonic, the union of all id_history arrays
gives a reverse id -> project lookup, so any stale ID reference in old archived
content still resolves.
brain is in the registry but is never a park target (policy, not a schema flag).
Each projects.json entry has a needs array: the names of other projects it depends
on. Directional — A.needs = ["B"] means "A depends on B". Empty by default; populated
as dependencies are revealed during work. Dependencies may point at projects that are
not normally loaded (e.g. shared packages mounted only on demand).
Loading (session start). When the user says "let's work on X", ensure X AND its
full transitive needs graph are loaded in Solo (load any that are parked), then
report what was loaded. See playbooks/work-on.md.
Parking. When asked to park P, after the active-agent gate, resolve dependencies
and ASK — never assume:
- Dependencies of P (
P.needs) that are currently active may have been loaded only to support P — ask whether to park them too. - Dependents of P (other active projects whose
needsinclude P) would lose their dependency — warn the user and ask how to proceed before parking P.
clients.json is the client registry: each client has a display_name, an internal
flag, and the source_control orgs (host + org) that identify its repos. The default
client is your own internal org. Project→client membership is the client field on
each projects.json entry — the single source of truth; derive client→projects by
scanning it.
Inferring the client for a new project from its repo:
- Read the remote URL (handle both
git@host:org/...andhttps://host/org/...). - Parse
host+org; match case-insensitively againstclients.jsonsource_control. - On a match, assign that client. On no match, ask the user — do not guess.
- A project with no remote defaults to your internal client; flag it to the user.
Do not assume github.com — matching is host-aware (a GitLab client is possible).
Projects are checked out under the fleet root. See playbooks/new-project.md for the full
procedure. One rule that is easy to get wrong if you serve local sites over a wildcard
domain:
If you expose each project at
{folder}.example.comwith a single-level wildcard certificate, a folder name containing a.would produce a multi-level subdomain the cert does not cover. So replace every.in the repo name with_for the local folder name (outofthe.us->outofthe_us). The original URL is kept in the entry'srepofield; client is inferred from the repo org (ask if unknown). If you don't serve sites this way, you can drop this rule and use the repo name verbatim.
How to build substantial, plan-driven features (and occasionally greenfield MVPs from a PRD). Three layers, by audience:
- The method — the
multi-agent-buildskill (canonical source inbrain/skills/, symlinked into~/.claude/skills/so every project's agents inherit it). A coordinator agent decomposes the plan and runs a per-PR loop: implement → hard gate → independent quality review + acceptance judge (decorrelated harnesses) → coordinator arbitration → ship, with a 3-attempt bail. - Project specifics — each project's
.solo/workflow.md(template inbrain/templates/) carries the gate command, dependency install, harness map, merge method, and plan location. Agents read it directly from the repo. - Orchestration —
playbooks/coordinate-feature.mdis the orchestrator's role: load the project, ensure the profile exists, confirm the mode, spawn the coordinator, hand off the brief, monitor via idle timers, relay only when the human is tagged in, and harvest the result into the project log.
Two modes (chosen at spawn; confirm with the user, defaulting per phase):
- A — autonomous (default): the coordinator drives the whole feature, merging each PR when CI is green. Most common.
- B — review-each-PR: the coordinator opens each PR and stops; the user merges, then it continues.
Control default: unless the user says they're taking over, the orchestrator owns the agents it spawns and acts on the user's behalf (e.g. merge on green CI where that's the policy). Bugs/maintenance are out of scope here.
Solo accretes clutter that buries signal. playbooks/cleanup.md (typically run at the start of the
day) tidies the active projects: prunes stale worktrees, closes finished agents, archives old completed
scratchpads, and clears old completed todos — leaving a "needs your eyeball" digest of open todos,
active scratchpads, and running agents. It is reversible-first and proposes before destroying:
worktree prune is automatic; everything destructive is presented for approval; worktrees with
uncommitted or unpushed work (and open todos) are never auto-touched; old completed todos are harvested
into the project log.md before deletion (Solo has no todo archive). A worktree is removable only when
its work is fully in main and the tree is clean — see cleanup.md for the full decision matrix.
Protect tag: scratchpads tagged keep, reference, or plan are never archived by cleanup,
regardless of age — use these to pin standing docs (build plans, PRDs).
projects/<name>/decisions.md entries should each capture: the date, the decision,
the options considered, and the reasoning — especially why the rejected option was
rejected. This is the format optimized for the long-horizon "why did we…" question.
When volume justifies it, this file-based store can be backed by a database with embeddings for semantic recall. The on-disk markdown/JSON remains the durable source; embeddings are a derived index.