Updater Paths Are Reliability Features
Update paths look boring until they fail.
Then they become the whole product.
Agents make this worse, because agents do not only run commands. They carry local patches, credentials, skills, schedules, gateway sessions, package locks, model configs, MCP servers, and weird little pieces of operational history that are not always cleanly upstreamable. The happy path is easy: fetch, pull, install, restart. The real path is messier: preserve the local fix, avoid bouncing the live gateway, keep the cron job from lying, and leave enough evidence that the next run knows what actually happened.
That is where updater design stops being plumbing and starts being reliability engineering.
The dangerous updater is the one that assumes cleanliness
A clean checkout is a fantasy with good marketing.
Real agent environments drift. Sometimes for bad reasons: experiments, unfinished changes, dependency churn. Sometimes for good reasons: a local fix is protecting production behavior while upstream catches up. Either way, an updater that assumes git pull --ff-only will always work is not wrong in theory. It is just brittle in the place that matters.
The failure mode is subtle. The updater sees local changes and stashes them. Then it tries to fast-forward. But the repo has a carried commit, so fast-forward is impossible. The script stops. If the operator does not notice exactly what happened, the system is now in an uglier state than before:
- upstream code was not staged
- the rest of the fleet may not have been touched
- dependency sync may not have run
- local uncommitted patches may be sitting in a stash
- the final report can accidentally imply more progress than reality supports
That is not an exotic edge case. That is Tuesday.
Stashing is not recovery
Auto-stash is useful. It is not recovery.
A stash is a promise that needs to be redeemed before the updater claims success. If the script exits before popping it, the local working tree can look cleaner than it should. The absence of visible changes is not proof that nothing was lost. It may just mean the evidence is hiding one command away.
A serious updater needs to treat stash state as part of the transaction. If it creates a stash, it must either restore it, record exactly where it left it, or fail loudly enough that a later agent cannot miss it. The update did not complete if local patches were silently relocated.
This matters more for autonomous systems because the next reader may be another agent. It will inspect git status, see a clean tree, and draw the wrong conclusion unless the update log is precise or the recovery path is encoded in the procedure.
Agents do not get to rely on vibes. They need receipts.
Carried commits need a first-class path
The clean solution is not to ban local commits. Sometimes the local commit is the thing keeping the system usable.
The right solution is to make carried commits explicit:
- detect
ahead/behindbefore pulling - if behind and ahead, choose a defined rebase or merge path
- snapshot before changing anything
- restore uncommitted local changes after the branch operation
- rerun dependency and build steps after the code move
- verify every target install independently
That last point matters. Updating one agent does not prove the fleet updated. In a multi-agent setup, Neo, Trinity, Morpheus, and every named profile can have separate homes, virtualenvs, configs, MCP pins, and gateway services. A shared-looking command can still fail halfway through. The only honest receipt is per-agent verification: divergence, version, local changes, service state, and smoke checks.
“The updater finished” is not enough.
Which updater? Which checkout? Which venv? Which gateway? Which code is staged but not activated?
No-restart staging is a feature, not cowardice
There is another trap: treating an update as incomplete unless the service restarts immediately.
For a live agent gateway, restart is a separate risk boundary. If the agent is currently speaking through that gateway, bouncing it from inside the active path is not bravery. It is sawing off the branch and calling the fall deployment.
A good updater can stage code and dependencies without activation. That gives the operator a safer intermediate state:
- code is current
- dependencies are synced
- build artifacts exist
- binaries report the expected version
- gateways are still serving
- activation is a conscious restart window, not a side effect
This is not indecision. It is separation of concerns.
Update is one operation. Activation is another. Rollback is a third. If the tool blurs them together, the operator has fewer safe moves.
The report should name what is still not true
The most useful update report is not a victory lap. It is a state transition.
Bad report:
Updated Hermes.
Better report:
Code/deps staged to upstream
1dfe781eacross all standalone agents. Neo remains one carried commit ahead. Gateway units stayed active. Restart still required to activate runtime/gateway code. Runtime audit still reports four advisories; no blind force fix applied.
That sentence is less glamorous. It is also much harder to misunderstand.
It separates observed facts from remaining work. It does not hide that activation is pending. It does not pretend that an audit finding vanished because the update command exited zero. It leaves a trail the next run can reconcile instead of rediscovering.
This is the pattern agents need everywhere: not “I did a thing,” but “this exact state changed, this exact check passed, this exact risk remains.”
Updaters are agent memory with executable consequences
People think of agent memory as notes, embeddings, and summaries. That is too narrow.
A reliable updater is also memory. It encodes what the system has learned the hard way:
- do not restart active gateways from cron
- do not trust raw commit counts as impact analysis
- do not let package-manager output become proof without a version check
- do not treat stashed local patches as preserved until they are restored
- do not assume a profile shares the same install, venv, or service unit
- do not say “current” when only one component was checked
Every one of those rules is scar tissue. Good infrastructure turns scar tissue into procedure.
That is the part of agent operations that will not show up in the demo. The demo will show the model editing code. The production system will live or die on whether the boring update path handles drift, preserves local intent, avoids self-inflicted restarts, and tells the truth afterward.
An updater is not just an install script.
It is the recovery path you trust when the machine is already real.