First, find out what is true
data.origininbrain state—referenceandtagare the copy this brain was installed from.nullmeans it was never pulled: it was born here, and “remote” is only whatevervitruvio.tomlor the logged-in account derives.data.ancestrylists the snapshots this brain has been, most recent first.data.tags— whether the tag you are thinking of exists at all. A tag nobody pushed yet is not a divergence, it is a first push.data.is_noopanddata.impactinplan-pull.is_noop: truemeans the registry holds exactly what is installed.impact.blocksis how many blocks were committed here since the last pull — readimpact.certaintyfirst, because a plan can beapproximate, andunknowncomes withblocks: null.
tree reports ours, theirs, the ancestor they parted at, and per module how many blocks were
added_by_us, added_by_them and removed. That table is the whole picture.
The four situations
Behind is the only case where
pull is right, and it is right because impact says there is nothing to
lose. If impact.blocks is greater than zero, or certainty is unknown, you are not behind — you are diverged
and have not fetched yet.
After every successful dist pull, run brain verify and auth status on that same brain. If auth status
reports a trust root, the installed brain is governed: run auth attribution too, and treat a head that is not
authorized, or an audit that is not complete and fully_vouched, as a possible authorship breach to review
before calling the brain authenticated. It is not proof of corruption — a merge may legitimately introduce an actor
the head’s signer does not vouch for — but it is not something to wave through either.
Ahead needs no ceremony. A push that is a fast-forward is refused by nothing. If it comes back 8, the registry
moved between your plan-pull and your push; that is the fourth row now, not a retry.
Diverged: keep both sides
data.reconciliation.why says which:
THEIRS is data.reconciliation.theirs, which is also data.digest. The fetch does not remember it for you.
No strategy declared
The three strategies land byte-for-byte the same composition and differ only in whose name stays on the incoming work —merge keeps their snapshots and signatures, rebase and squash reissue the work under yours. That is an
attribution decision, and vitruvio refuses to make it for you:
merge is the only strategy under which a contributor’s snapshots still cover something; rebase invalidates any
root they already published. Do not add reconcile = ... to vitruvio.toml just to make the fetch go through; if
you want a standing choice for next time, declare the one you actually mean.
Not clean
The fetch reported and stopped, because opening a reconciliation blocks every write until it is resolved. Open it deliberately, with the strategy you chose:halted: true is a question, not a failure. What each verdict means and which decision it admits is
the subject of Joining two histories. Two rules matter here: a rejected
block is never admitted, and if withdrawn names blocks, they are your work leaving — look at how many, and from
which modules, before accepting.
vitruvio reconcile abort abandons an open reconciliation and writes nothing. It always works.
What never fixes a divergence
dist push --force. It overwrites the registry with your history and discards theirs. There is no undo and no command that recovers what a force-push replaced. The flag exists for a version that is genuinely to be thrown away, and “I want the push to go through” is not that.dist pullto escape exit 8. A pull adopts the published composition; every block committed here since the last pull leaves it. The blobs remain and the old snapshot is retained, but no command restores it. Pull is the behind answer and only the behind answer.- Retrying the push. Exit 8 is not a transient failure and comes back identical every time. The registry did not hiccup; it refused.
- Editing
vitruvio.tomlto declare a strategy, flippublish = false, or point at a different reference so the refusal goes away. Each of those is a statement you make, not a workaround.
Things that look like a divergence and are not
In a project with several brains
Each named brain has its own remote and its own answer. Runvitruvio project show and go through them one at a
time with --brain NAME; a brain marked publish = false can only ever be behind, and one with nothing committed
is skipped by dist push --all rather than failed. A project is not diverged because one brain is: name the brain.