Releases: odiumuniverse/verger
Release list
v0.1.4
verger v0.1.4
Two changes. Neither changes an exit code, a flag or a document.
Runs that deliver many files are faster on macOS
Every file verger wrote paid for two flushes to the disk — the file's own and its directory's. On
macOS the first is a full flush to the device, and a run that delivers a few hundred files spent
most of its time waiting on them.
Most of those files can be rebuilt: a delivered artifact, a rendered document, a staged copy. If
one is lost to a power cut, that looks exactly like a file that was never written, and the next
run writes it again. Those writes now skip their own flush, and the run flushes the directories
it wrote into once, at the end.
What cannot be rebuilt keeps both flushes on every write: verger.toml, the lock, the receipts of
what was delivered and removed, consent decisions, the secrets file, your previous bytes held in
the trash or backed up before an edit, and a key verger writes into your own settings file.
When a run returns, what it wrote is on disk, as before.
checksums.txt verifies against what the release page publishes
The v0.1.2 release listed a man-page archive in checksums.txt that was never uploaded, so
shasum -a 256 -c checksums.txt
ended in verger-man-0.1.2.tar.gz: FAILED open or read beside the downloads. The man pages live
inside each platform archive, which is where Homebrew installs them from, and checksums.txt now
lists exactly the files the release publishes. Downloaded into one directory, the command above
reports OK for each of them.
Note on v0.1.3
The v0.1.3 tag exists but was never released: its release run stopped at the smoke step,
which no longer unpacked the archive it was checking. v0.1.4 is the same code with that step
repaired.
v0.1.2
verger v0.1.2
Twelve behaviour changes. Three of them can change an exit code your script branches on, and
one changes what actually happens when you pass a flag.
A question nobody answered is now exit 5
If a run reached a question it could not put to you — hooks nobody consented to, an
unconfirmed package — the run stopped with exit 3, the same code used for a disagreement
sitting on disk. It is now exit 5, which means "waiting on you".
The next action was always the same in both cases: answer the question, or pass -y. Exit 3
told a script the run had failed for a reason it could not act on.
If your script branches on 3 to mean "needs your answer", change it to 5. Exit 3 now means
only a genuine conflict on disk.
Two of those are wider than they were. 6 used to mean only "that agent is not installed here";
it now also means the agent is installed, was asked, and did not do it. 1 used to be the code
for anything unclassified, which quietly swallowed ordinary failures — it is now only for errors
verger cannot describe, and every error verger raises on purpose has a code of its own.
The full set: 0 ok, 1 something verger does not understand, 2 a bad invocation, 3 a
conflict, 4 a refusal by a rule, 5 consent, 6 a host that could not carry out what was
asked, 7 a file written by a newer verger.
--allow-downgrade now really rolls back
verger update --allow-downgrade tells verger it is allowed to move a package to an older
version than the one installed. That was accepted, shown in the plan as not-skipped, and then
nothing was installed.
It now does what it says. A channel that resolves backwards is installed, and the plan says so.
verger.toml is edited where you wrote it
Your spec file is now changed in place rather than rewritten whole. Your comments, your
section order and your choice of quoting survive a run that changes the file.
One exception, unchanged in spirit from before: when a change cannot be applied as a local
edit, the file is written out in full in verger's canonical form. That has always been the
fallback; it is now rarer. Verger writes plain strings with ", so lines it adds match the
lines you wrote rather than arriving in a different quoting dialect.
verger search and verger import
Two new commands.
verger search <query>
Finds packages by name and tells you which are already installed. It never reaches the
network: it answers from your spec, your lock, your receipts and the local: sources you have,
and lists everything else under skipped instead of quietly leaving it out. A list you can
trust is a list that says what it could not read.
verger import
Writes what your agents already installed into your spec, as [[package]] entries. It writes
the spec and nothing else — no host file is touched and nothing is delivered. Run verger sync
afterwards to make the machine match. Running it twice changes nothing the second time.
Your cooldown setting now does something
cooldown in your spec's defaults (24 hours if you do not set it) was accepted and then
ignored. A run now honours it: verger update defers a package and tells you
<package>: deferred by the 24h0m0s cooldown, next attempt after 2026-10-02T11:04:19Z
A cooldown is about a reconcile bringing something newer, so it applies to update and not
to a plain sync. --force bypasses it.
Hooks on five hosts are skipped, and say so
omp, kilo, opencode, agy and dsh have no hook surface verger can render into. A
package carrying hooks is still installed for them — the run succeeds — and the run now says
how many hooks it skipped and why, for example:
2 hook(s) skipped: omp's extension API has no lifecycle event stream: during a real
authenticated model call, fourteen lifecycle names registered on omp's own EventEmitter
fired NONE, while registration itself is proven to work. A delivered module would load,
register, and wait forever, so no hook can be claimed delivered here
The reasons differ per host, and each names the host's own limit rather than a verger version.
Shortly:
- omp — its extension API has no lifecycle event stream. A module would load, register and
wait for an event that never comes. - kilo — its only plugin mechanism takes the name of a published package and updates
config, so a module built on your machine cannot be registered through it. A module written
to disk is not loaded either: probed on kilo 7.8.1 it produced no output at all, with or
without apackage.jsonbeside it. Its extension bus is therefore unknown, and no delivered
hook can be shown running. - opencode — its event table carries no session-start event, so a delivered module could
only ever fire on a tool call, which needs a model. - agy — its hooks file is keyed by owner with no hooks wrapper, a shape verger does not
write. - dsh — it has no hook document at all; hooks reach it only through a config string inside
a profile preset.
Consenting to hooks does not change this. There is nothing on those hosts for consent to turn
on, so the skip is a fact about the host, not a question.
One skill, one file, several hosts
When the same skill goes to several hosts, it is now written once and shared, instead of being
copied into each host separately. Removing it from one host leaves the others alone: the file stays
for as long as any host still uses it, and goes when the last one stops.
This matters most on remove and on narrowing a spec's host list. Previously each host had its own
copy and the ownership was ambiguous — removing from one host could take a file the others were
still using. Now the ownership is the set of hosts that want it, and one host dropping out is not
a reason for the others to lose the file.
except now applies to every command that writes
A package can say which hosts it must not reach. That exclusion used to be honoured by some
commands and quietly ignored by others, so the same spec meant different things depending on how
you ran it. install, update, sync, adopt and import all honour it now, and they all reach
that decision the same way — one place, one answer.
Nothing else changes about except. If you have not used it, nothing here affects you.
remove takes your keys back out of settings.json
When verger adds a hook to an agent's settings.json, it records what it added. verger remove now
removes those keys again, instead of leaving them behind.
Two things it will not do:
- it does not touch a key you have edited yourself. If you changed the value verger wrote, that key
is yours now and it is left in place; - it does not touch keys verger never wrote. Your own entries in the same file are untouched.
The upshot: after remove, a file verger edited no longer carries verger's leftovers, and you can
tell the difference between "verger put this here" and "you put this here".
remove fails when the host did not remove the plugin — exit 6
When the plugin was installed by the agent itself rather than copied in by verger, verger now
asks the agent whether the plugin is gone. If the agent still lists it, the removal fails,
and the message names the host and repeats what that host's own CLI said:
remove: omp did not remove acme/plugin: plugin 1.0.0
It used to report success. That was wrong: the package was still installed, and the run said
otherwise, so a script reading the exit code was told a removal had happened when it had not.
This turns a previous exit 0 into a failure, and the code is 6. Exit 6 means a host could
not carry out what was asked of it — the agent answered, and the answer was no. It is not the
code verger uses for a bug of its own, so a script branching on exit 1 as "verger crashed" will
not see it here.
Any failed cell now reports 6 for the same reason: a cell that failed is a host that did not do
what it was asked. Exit 1 is now reserved for errors verger did not anticipate, which are the
only ones it can no longer describe.
Removing a natively-installed plugin works in this release; this is what happens when the agent
refuses the uninstall.
A lock from a newer verger stops the run — exit 7
If your verger.lock was written by a newer verger than the one running, the run now stops with
exit 7 and leaves that file exactly as it found it. It does not try to continue on a lock it
cannot read, and it does not overwrite it.
Two commands can carry a forward lock: run verger update on the machine with the newer verger,
or copy the lock back from a machine that has it. The message names the lock and the version that
wrote it.
eject no longer loses your packages
Handing a plugin home over to standalone verger used to be lossy in a confusing way: the packages
moved, but an empty directory was left where the home had been. Every later command then found that
empty directory, treated it as the home, and told you there was nothing installed while your
packages sat in the other place.
After eject there is one plugin home, not two. The emptied directory is gone, nothing of it is
left behind in the vault, your packages are at the standalone home, and a later beadle or verger
command does not bring the empty directory back.
v0.1.1
Full Changelog: https://github.com/odiumuniverse/verger/commits/v0.1.1