Skip to content
Getting started

Getting started

This page assumes the binary is on your PATH. If it is not, Install is one command.

mvac init .

Run it in the directory that will become the brain — the repo you develop the whole ecosystem from:

$ mvac init .
init: git init — the brain is git-native
init: wrote .multivac/config.yml — declare your repos under repos:
init: wrote AGENTS.md — the door; your agent reads it first
init: wrote .multivac/invariants.md — the law table, zero rows
init: wrote .multivac/ritual.md — candidates, all commented; uncomment what your team owes each other
init: hooks in .multivac/hooks (core.hooksPath) — verify runs on commit

init: done — the brain is scaffolded and empty. Session zero fills it:
init:   before step 0, declare every repo this brain governs under `repos:` in .multivac/config.yml — once committed, the config changes only inside a change
init:   0. commit what was just written: git add -- .multivac AGENTS.md && git commit -m "multivac init"
init:   1. load the multivac skill in your agent — it carries both protocols
init:   2. `multivac repos sync` — clones every declared repo and installs its declared tools
init:   3. discovery, for code that exists — `multivac seed` inventories it, then draft proposed claims from it
init:      interview, for code that does not — the law comes from a human, claim by claim ← this repo holds none
init:   4. a human enacts each row in .multivac/invariants.md, then `multivac doors` and `multivac verify`

Step 3 is the branch Session zero turns on. init prints both and marks the one that fits this directory: on a first run, any file git does not ignore means discovery and an empty repo means the interview; a kept config decides by whether it declares the brain as one of its repos. A new brain for code that lives in other repos is empty, and still wants discovery. Declare repos: before the first commit: after it, a changed config needs an open change.

Exactly these files, nothing else:

AGENTS.md                    the door — first thing any agent reads
.multivac/invariants.md      the law table, zero rows
.multivac/changes/           one file per ecosystem change (a `.gitkeep` for now)
.multivac/config.yml         the registry: repos, doors, adapters
.multivac/ritual.md          the closing ceremony, candidate lines all commented out
.multivac/projected.yml      the version this brain was brought to; only `doors --adopt` moves it
.multivac/hooks/pre-commit   runs `mvac verify` on every commit
.multivac/hooks/pre-push     same, on push
.multivac/hooks/pre-merge-commit  same, on a local merge
.multivac/.gitignore         ignores .multivac/cache/ and .multivac/worktrees/

AGENTS.md is the one file multivac writes at the root, because that is where harnesses read it; everything else it owns lives under .multivac/, out of the way of your own content. git init runs only when the directory is not already a repo. core.hooksPath is pointed at .multivac/hooks/, so the hooks are versioned and travel with the clone; multivac doors arms them in each new clone. The exception is a repo whose core.hooksPath already names a directory of its own, or that has .husky/ and no core.hooksPath: the shims go into that directory where the hook name is free, and core.hooksPath stays as it was. A hook already there that does not run multivac is left untouched, and init prints the line to append to it.

On a repo that already has opinions, init checks before it writes: a .gitignore that would swallow the brain gets explicit negations appended (and the report says so), and an existing hook set-up — .git/hooks/, husky, lefthook, the pre-commit framework — is chained or installed alongside, never silently replaced. See Hooks for the strategies.

Re-running init is safe: an existing .multivac/config.yml is kept, an existing AGENTS.md is never clobbered — multivac only rewrites its managed block, between <!-- multivac:begin --> and <!-- multivac:end -->. The rest of the file is yours.

init also detects what is already in the directory — CLAUDE.md, .cursor/, openspec/, .specify/ — and writes commented proposals into config.yml (# doors: [agents, claude, cursor], # sdd: opsx). Flags enact instead of proposing:

mvac init . --provider claude,cursor --sdd opsx

Flags are configuration, not one-shot magic: they land in .multivac/config.yml. Adopting Cursor in three months is one line in that file plus mvac doors — not re-running init. The default is doors: [agents]: AGENTS.md alone, already read by Codex, opencode and Cursor. Claude Code reads CLAUDE.md and not AGENTS.md, which is the whole reason the claude door is a symlink — --provider claude is what adds it.

What the empty brain says

AGENTS.md after init — the whole file is one managed block:

<!-- multivac:begin -->
## multivac — brain door

This repo is the brain: the source of law and change for its ecosystem.

- Law lives in `.multivac/invariants.md`. Cite rows by ID; a rule quoted without its ID does not bind.
- Every ecosystem decision enters as a change: see `.multivac/changes/` and run `multivac change`.
- The ritual — the closing ceremony no tool can check — is `.multivac/ritual.md`; `change close` prints it, you walk it.
- Check the law against the code before acting: `multivac verify`.

brain empty — load the multivac skill to fill it.
<!-- multivac:end -->

With --sdd, the door also carries that tool’s flow, and init installs it in the brain — or, finding the tool’s binary on neither PATH nor the brain’s node_modules/.bin, refuses with exit 1 before writing anything. A brain whose directory already holds any file git does not ignore is also a code repo, so its new config gets brain: .; a new brain for code that lives in other repos is empty, and declares those repos instead. The SDD stays in the brain: it is where every change’s specs are written, and no code repo installs it. For spec-kit, the install also writes three skeleton templates where spec-kit looks first, and says so:

sdd speckit: scaffolded — brain:.specify is there now; its steps are runnable; skeleton: .specify/templates/overrides/{spec,plan,tasks}-template.md

.multivac/invariants.md is the law table with its format and zero rows:

# Invariants

| ID | statement | authority | state | date | source |
| --- | --- | --- | --- | --- | --- |

And verify is already honest about the emptiness:

$ mvac verify
0 claims · 0 anchored
  read      brain: working tree on main — the brain's own repo, the commit this run gates

  enact     not answered — nothing staged, so no commit is being composed; … reads the index against HEAD

0 blocking broken · exit 0

You start at zero and the tool is already useful. Coverage is counted, never pretended.

Next

Declare your repos in .multivac/config.yml, before the first commit. Once the config is committed, changing it needs an open change:

doors: [agents]
brain_url: git@example.com:acme/brain.git   # what every consumer mounts
repos:
  api: ../acme-api          # bare string = shorthand for { path: ... }
  payments:
    path: ../payments
    url: git@example.com:acme/payments.git   # cloned by `repos sync` if missing

The key (api) is the registry name anchors use — never the directory name.

Then bring every repo in line, and give each one its door:

mvac repos sync   # clone what is missing, mount the brain, install the brain's SDD
mvac repos check  # offline: is each repo the declared clone, with its tools and documents set up
mvac doors        # write each repo's door and hooks

repos sync mounts the brain at .brain in each repo multivac may write to and leaves it staged; commit it in each of them. A repo declared managed: false, and a shallow clone, are read-only and get no mount. brain_url has to be the address everyone clones the brain from — init suggests your origin, but that may be an ssh alias only your machine knows. Run the same commands again whenever you declare another repo: repos sync clones the new one, fetches the repos already on disk, and mounts the brain in each writable repo that still lacks it.

One repo? Say so

When the brain IS the code repo — the usual shape for a single project — declare it with the reserved brain key:

doors: [agents]
# brain==code: this repo is both the brain and the code it governs.
repos:
  brain: .

mvac init writes that brain: . entry, with a few comment lines around it, when it writes the config of a repo that already holds any file git does not ignore. Anchors then target brain:<glob>, mvac change branches right here, and doctor stops looking for a brain mount there is no reason to have — nothing is submoduled into itself. Sibling repos are more keys alongside it whenever the project grows into an ecosystem.

Then fill the brain: Session zero.