The change
The ecosystem change: planned in the brain, executed across every repo the feature touches, closing back into the brain. The verb the other three jobs serve.
multivac change new "points expire"
multivac change plan <slug> # which repos, in what order, which invariants it touches
multivac change apply <slug> # branch per repo from its default branch, edits, commits
multivac change land <slug> # MRs respecting the declared order
multivac change close <slug> # updates the brain and verifies the declared claimsFour declared fields
A change declares, before anything is touched:
Which repos it touches. Registry keys. A repo that doesn’t exist yet is legal — greenfield
applycreates it.Landing order — ordered stages. Each stage is a list of repo keys; repos in the same stage land in parallel; a stage lands only after every earlier stage:
landing_order: - [api] # web and worker consume what api serves - [web, worker]The order is law for
land; declare only the ordering that is a real constraint — an empty list is one parallel stage.Which invariants it touches, under the rule: an invariant is never relaxed in code — it is changed in the law first, dated, in the same change.
Which rows it makes true — its claims, each a row’s ID, with their anchors. The row states the rule; the claim cites it and never restates it. This is the contract
closeverifies. Draft the anchors now, while the promise is fresh — after merge nobody remembers.
The change file
A change is a file in the brain: .multivac/changes/<slug>.md, carrying the four
declared fields plus per-repo status
(planned / branched / committed / mr / landed). That file is the state the
five subcommands read and write, across days and machines. On close it is
archived, never deleted.
Done when its anchors resolve
A change is not done when it merges. It is done when its anchors resolve.
Because the claims were declared up front, close doesn’t ask whether
someone updated the docs — it re-runs verify scoped to the declared
claims and refuses to archive until:
- every claim in field 4 resolves ok or moved on its new anchors,
- every claim cites a row this change adds, touches or retires, and that row states its rule,
- every declared repo is recorded landed,
- no claim is anchored only in the change file, which
closearchives, and no row the change adds is left unclaimed while anchored or already in the law, - the SDD’s own artifacts exist, where one is declared.
That scope is deliberate and it is narrower than it sounds: close evaluates
the claim IDs the change declared, not the rows under touches or
retires, and it never runs an unscoped verify. An amended row that nobody
listed as a claim is not re-checked here: verify reports it on each commit
where the hooks are armed. A leg of it gates only when it is broken or vacuous in
a mode that blocks (by default absent, count and each), or in any mode
under --strict, as CI runs it, and the legs of a proposed or drift row are
only reported. An anchor line that does not parse, or names a repo key the
config does not declare, is refused on any row. Declare the rows you amend as
claims if you want close to be the one that answers.
Where sdd_auto is on and an SDD governs a declared repo — the brain itself,
when it is declared as one — the code comes through the change as well. In the
brain’s own checkout and in a change’s worktrees, a commit or a merge of code
outside the branch of an open change that declares the repo is refused, and so
is a merge request whose commits do so, when the pipeline runs
verify --strict --range <base>..<head> --branch <name>. A consumer checkout
reads a mounted brain that can lag the change, so there a branch that is no
open change is only reported and the --range run in CI decides, while a
branch whose open change does not declare the repo is refused in any run. A change is how code reaches a
repo, not a description written next to it.
Updating the documentation stops being discipline and becomes mechanism. Nothing new is invented: the change declares before what today gets checked after, when anyone remembers.
The ritual
Closing a change is a ceremony, and only half of it is mechanical. multivac executes that half — every declared repo landed, every declared claim resolves and cites a row of the law that states its rule. The other half is the team’s: who reviews what, who gets told, what ships before what when the reason is not technical. No tool can invent those, and none can check them.
So the team writes them, one line each, in .multivac/ritual.md next to the
law — and close prints their lines as a checklist once the gate has passed
and the change is archived:
$ mvac change close points-expire
…
ritual (.multivac/ritual.md) — multivac cannot check these; walk them with the user:
- [ ] tell support before the flag flips
- [ ] the public site ships before the backendWhat close prints before the ritual — the claims, the archive and the commit
to make — is in the commands reference.
Printed, not verified. Nothing gates on the ritual and nothing checks it;
headings and comments are left out, so an empty or absent ritual prints nothing
at all. init scaffolds the file with an explanation and candidate lines, all
commented out, and the brain door names it.
It gets its own file rather than a section of the law because the law is
parsed — verify reads its anchors, plan reads its state cells — and the
ritual is prose the tool only ever prints.
Planned — a change that has not started
A roadmap is the list of things an ecosystem intends to do, and the brain
already keeps that list. Every change file is born open, which means a branch
is implied and an invariant id is reserved. So without one more state, the only
way to write an intention down is to commit to starting it today — and the
alternative people reach for is a second list in a wiki or a tracker, which
drifts from the first within a week. Whichever list the tool does not read becomes fiction.
planned sits in front of the lifecycle:
planned → open → archivedIt is the same file, in the same directory, with the same schema, plus a
horizon of now, next or later. That is the entire ordering model: no
dates, no estimates, no rank, no dependencies between items. Starting the work
is change new <slug> on a slug that is already planned — the file is
promoted, not replaced, so the prose written when the idea was young
survives into the change that implements it. One document, one history.
Three properties make the state safe to use rather than decorative:
- It reserves nothing. An id allocated for work that may never happen is a
hole in the law table no later change can fill. Reservation stays at
change new, the moment the work actually begins. - It blocks nothing.
verify --strictrefuses a change only once it is finished and still unclosed: every claim resolving, every declared repo landed. A planned change has neither, so no roadmap entry can hold a release, however long the roadmap stays non-empty. - It is not a gate.
change newon a slug nobody planned works exactly as it always did. Requiring a feature to appear on the roadmap first is unverifiable intent — the same category as the ritual, which is why the ritual is printed and never checked. A gate everyone learns to skip at three in the morning teaches people to work around the tool.
Every later step — plan, apply, land, close — refuses a change that has
not started, and names change new as the step that comes first. Where an SDD
is declared, plan, apply and close run its gate before that check, so
their refusal can be for a missing artifact instead; close --abandon runs no
gate, so a planned change always gets the not-started refusal there.
See roadmap
for the command, and Running changes
for the flow.
The ritual arrives with candidates
init used to write the ritual as a bare comment, and facing a blank page most
people write nothing — so the closing step printed nothing forever. It now
carries candidates drawn from what you declared, every one commented out.
An unadopted ceremony is not a ceremony. Uncomment what your team actually owes each other and delete the rest; nothing is asserted on your behalf, and a commented line never prints. A fresh brain still prints nothing at close, which is exactly what it did before.
Only things no check could decide are seeded: putting a checked thing in the ritual would move it onto a poster.
The ritual is authored — written once by you, never overwritten by any
command. That is the opposite of
flow.md,
which is derived and rewritten whole on every projection.
The subcommands
- plan resolves the declaration against reality: which declared repos
are present, what the order implies, what the change touches. A declared
repo missing locally that has a
urlgets cloned here — an explicit operation that needs it, the same contract asgit submodule update. - apply branches each repo from its default branch, resolved in that
order: where
origin/HEADpoints, then this machine’sinit.defaultBranch, thenmain, thenmaster— never a fixed name; if none exists it branches from the checked-out HEAD and says so. A repo that doesn’t exist is cloned from itsurl, or created with its consumer door when it has none. The edits and commits are yours, on those branches. A declared SDD adapter’s apply step is printed here for you to run (--no-sddskips the printing, the tool’s own init in the brain and this step’s gate for one run; later steps need it again). - land reports the MR order the stages dictate: what is ready to push
now, what is blocked behind an earlier stage. It opens nothing — multivac
has no forge integration — and
--landed <repo>is you recording a merge. - close is the gate above. On success the change file is archived; the rows the change promised are already in the law, enacted by the human.
Decisions made mid-change become claims at close: the agent proposes the row, the human enacts. This is the organic birth path — the main one at steady state.
Greenfield
A change whose repos don’t exist yet: apply creates them — git init,
the consumer door, a first commit — so the first agent session in each starts
at a door that points it to the law; the brain itself is mounted by
multivac repos sync. The brain precedes the code. No second machinery:
apply knows how to create, not only edit.