Replacing a program other products call

Replacing a program other products call

Every stado invocation records, once per caller and command, which executable started it and which command it ran, under ~/.stado/callers/. Nobody writes that list: it is what the installed programs did.

Before stado product install or stado product update places a program, and before stado release local install replaces one, Stado reads the records whose callee is the program being replaced. It keeps those whose caller is another product's recorded installed path and has not changed since it was recorded, and asks the incoming program for each recorded command with <command> --help. A command the incoming program does not answer refuses the installation:

<incoming> does not answer commands installed products still run, so it would break them; update these products to a revision that no longer runs them, then install again: oko cli (installed at ~/.stado/bin/oko, source revision <revision>) runs `~/.stado/bin/stado secrets get`

Update the named product first, then install again. A record whose caller has since been replaced or removed is deleted when it is read, and a caller that is no product's installed path (a shell, a script) is never a reason to refuse. A caller that cannot be read is written to standard error as stado: the program that ran this command was not recorded: <error> and the command still runs.

A record this account cannot read was not written by this account's programs, so it says nothing about what they run: it is named on standard error as caller record <path> cannot be read by this account (<error>); it was not written by this account's programs and is left out of the check, kept, and the installation goes on. A Stado run under another effective user than the owner of HOME (sudo stado …) records nothing, so it leaves no such record behind.

For Stado's resident unit, stado product install stado --surface cli uses the prepared candidate to reconcile the existing native unit from the candidate's catalog before replacing the executable. It checks the installed unit's actual argv with stado quality units --candidate <prepared-stado> --program stado --host <registry-host>. A parser refusal names the host, unit file, argv, candidate and error, and leaves the executable untouched. stado quality parse -- <argv> checks the same parser without running that command. Once files are placed, the real resident process must serve its declared port through stado service serving stado --host <registry-host> --wait --json; an activation failure restores the exact recorded product backup, restarts its unit and reports the observed predecessor state. Stado Desktop → Products keeps the install refusal and status receipt; Services → Managed services shows the unit's last exit and stderr evidence.

A resident unit that serves none of its declared ports when the installation begins (stado service serving stado --host <registry-host> --json answers anything but serving for a row, refuses, or prints no rows, which it does when the registry it reads through is unreachable; the reason then says its serving state could not be read and quotes its last stderr line) is not reconciled before placement: the candidate's service ensure would restart it onto the executable still installed, the one that does not serve, and an installation of a release that fixes exactly that hang failed there every time. The installation says stado: com.wisent.stado on <host> serves none of its declared ports (<rows>); it is rendered through the installed release once that is placed, places the files, then renders the unit through the placed executable (rendered com.wisent.stado on <host> through <installed>) and waits for it to serve as above. The real journey tests/install/resident/resident_unit.mjs stops the unit's process with SIGSTOP before an installation and requires a new process on the placed release that serves.

From the migration on, the unit carries the candidate's command line, so an installation that stops before it commits (the candidate's own unit check or the receipt write refuses) renders the unit again through the executable still installed, which the unit goes on starting. The refusal then ends with the unit was rendered again through <installed>, which is still installed, or, when that render fails too, with rendering it again through <installed> failed too, so the unit may carry options that executable refuses: <error>. stado product rollback stado --surface cli does the same after it restores the predecessor's files: the restored executable renders the unit from its own catalog, and the rolled-back receipt records resident_unit with the host and the executable that rendered it (stado product status stado --surface cli --json). A render the restored executable refuses fails the rollback with the files of stado were rolled back, but its resident unit was not rendered again through <program>.

A unit no installation wrote (one an older Stado rendered, or one edited by hand) can still start the installed executable with an option it refuses. When the argument vector Stado refuses is exactly the one its resident unit file starts (the per-user or system launchd plist, or the systemd unit, under the catalog's label), the refusing executable starts stado service ensure stado --host <this host> as its own process-group leader before it exits, so the unit is rendered from that executable's catalog and restarted without any command from outside. Its stderr, which the unit's log keeps, then says <label> (<unit file>) starts <executable> with an argument vector this build refuses; ... (pid <pid>) renders it again from this executable's own catalog and restarts it, and writes its answer to ~/.stado/logs/resident-converge.log; a converge that cannot start says why on the same line. The host is read from this machine's own registry copy, never from the authority the dead unit serves. The converge's pid, kernel birth second (null when it had already ended by the time it was recorded) and the command line it was started for are kept in ~/.stado/logs/resident-converge.json, so init's next start of the unit says the converge started earlier (pid <pid>) is still rendering it instead of starting a second one while that process runs; a pid another process now holds does not count, and a record that cannot be read is said and ignored. Any other refused command line is answered by the parser alone. The host process's health pass starts the same converge when the registry declares serve roles the resident unit's own process does not run, and starts none for a line a converge already ran for (the beacon's roles block says which).

On macOS, takeover and resident handover require exact membership in a complete native services table, not a matching domain metadata value or a running-PID guess. A registered job may have PID zero; unreadable or unsupported evidence refuses judgment. See Native scope membership. Linux release handover likewise requires an exact loaded manager-roster entry; enablement is not membership, and failed native reads keep their scope and cause. Reader convergence also requires complete native inventory; a missing completion or unreadable baseline read remains an installation failure, not an empty set of readers. Products retains the ordinary failure receipt.

Source: this website