On this page
Removal: a replacement takes its predecessor with it
Written 2026-09-07, first against wisent-ai/stado at 1647e5ba6 while the
collapse was still seven branches, and updated the same day once it landed on
main as 3570f05a. Every count below names the revision it was read at.
Command surface says what the surface should become:
382 leaf invocations across 52 groups, host alone carrying 93, collapsed into
seven capabilities and nineteen primitives. This page is the other half of that
work — what leaves the product when a capability lands, what has already left,
and what makes any of it stay gone. It exists because the collapse is mostly a
deletion, and the deletion is the step that gets postponed: a superseded
command left beside its replacement is a second source of truth, and the one
nobody calls is the one that rots.
What one capability deletes
A capability is not finished when it works. It is finished when the verbs it
replaces are gone in the same change, and that shape is measurable: against
main, the integration branch is 104 files, 11,319 insertions and 9,809
deletions. Nearly one deletion per insertion is what a collapse looks like when
nothing is kept for later.
Each capability lands as four things, and each one takes its predecessors with it:
| Lands | Where it goes |
|---|---|
| the declaration it reads | product data compiled into the binary, beside stado-rs/data/service-catalog.json |
| one CLI module | dispatched from stado-rs/src/cli/mod.rs |
| one Desktop section | the Hosts and Services screens, so the GUI gains the capability in the same change, not later |
| one test area | a directory of the crate's integration tests — and the one thing this page got wrong, so see below |
| and one removal | the modules, test areas and Desktop paths of the verbs it replaced |
All seven landed on main in 3570f05a. stado-rs/src/cli/coding.rs and
stado-rs/src/cli/precheck_runner.rs are deleted, folded into the workload and
runner capabilities; stado-rs/src/deploy/host_recovery_release.rs is deleted
into declared repair; and the two test areas whose commands no longer exist,
stado-rs/tests/forward_close/main.rs and
stado-rs/tests/recovery_release/main.rs, are removed with them.
The seven new areas that replaced those two are gone as well, deleted in
3cf0670f the day after they were written. They spawned the real binary and
passed, and every one of them stopped at a tempdir and a seeded registry
document: none installed a runner, repaired a service, reclaimed a byte, opened
a route or delivered a release. A capability therefore does NOT land as four
things — it lands as three, and the fourth is owed. What each capability has on
main today is a declaration, a CLI module, a Desktop section, a page, and no
test evidence. Test suite carries what a real one requires and
the disposable host it needs first.
Seven pull requests carry the work, 543 through 549, one capability each,
integrated on one branch. They are not independent: stado-rs/src/cli/mod.rs is
the one file all seven touch, which is why they merge in an order rather than at
once, and why an integration branch exists at all.
Documentation leaves the repository once, and cannot come back
The product repository carries exactly two Markdown files, README.md and
CHANGELOG.md. That is not a convention, it is a test:
stado-rs/tests/documentation/main.rs reads the tree and refuses operator
documentation carried as repository Markdown, and .wisent-release.json
declares that same test as one of the three quality steps — fmt, clippy,
documentation — that the release worker runs before it builds and that every
pull request runs on the change introducing it. This is not theoretical: a test
plan written into the repository as Markdown on 2026-09-07 was refused by that
step with the verdict introduced, after it had already been merged. The page
it should have been is Test suite.
The matching rule on this side is that a page may not name a file that is not
there. src/cli/docs-source-paths.mjs resolves the revision the remote actually
serves, lists that commit's tree, and refuses any page naming a path the tree
lacks — unless the sentence naming it says the path is gone. Both directions are
covered: prose cannot outlive the file it describes, and a file cannot be
declared dead from a stale worktree, which is exactly how one page came to
report a build script as deleted while the repository still served it.
The scripts directory is gone; its code is not
The scripts/ directory was deleted from the product repository in commit
4c7d524f, and scripts/quality_gate.sh went with it. What did not happen is
the part that makes a deletion real. The four programs that directory held now
live inside .github/workflows/version-check.yml, written to a runner temporary
directory by heredoc at run time: the surface reader, the baseline generator,
the version gate and the quality gate. Nothing in the product can call them, no
test area drives them, and reading one means reading a workflow.
So this strand is unfinished, and this page says so rather than counting the
directory's absence as a result. What closes it: release qualification is
product behaviour, so it belongs behind a product command with its own test
area, and .github/workflows/version-check.yml should call that command the way
.github/workflows/deploy.yml already calls the stado CLI. One half is
already right — .wisent-release.json holds the quality steps, and the release
worker and the pull-request gate execute exactly those argv, so there is one
declaration used twice.
What a removal costs, and what nothing charges for
Removing an advertised command is a version event. The version-check job
builds the candidate, reads the command surface out of that binary, derives the
baseline from the bytes the release channel actually serves, and refuses the
change when the declared version does not match the class of the surface
change. A verb cannot be dropped quietly.
The limit of that gate decides what the seven capabilities are actually
defended by, so it belongs on this page. The gate reads the top-level
command list out of one --help. New top-level groups therefore register as an
additive change, while the 93 host subcommands disappearing underneath them
are invisible to it. What defends those removals is the rest of the contract:
one test area per capability driving the real binary, a CLI reference generated
from the binary instead of written by hand, and the two documentation checks
above. Reading a subcommand count off a green version gate would be reading a
check that measures nothing — see Checks that measure
nothing.
The release-quality-gate job runs the declared quality steps against the pull
request and attributes a refusal: introduced when the step passes on the base
and fails here, inherited when the base already fails it, unattributed when
the base cannot be checked out for comparison. Attribution matters during a
cleanup, because a large deletion attracts the blame for whatever was already
broken.
The order, and where it stands
The order is the cost of being wrong, not the size of the change: workload placement first, because an operator reaches for it daily and a wrong host is a silently wasted run; then declared repair, because it runs when something is already broken; then runners, credentials, host space, routing, and host release state.
It landed the same day. The seven branches were superseded by one pull request,
#551, merged as 3570f05a: stado host answered 93 subcommands and now answers
22 primitives, each capability reads a declaration compiled into the binary, and
a page per capability is published beside this one. The seven test areas came
with it, and the host disk, host cleanup, host reclaim, host disk-cleanup
and host build-caches verbs are gone because the space capability took them.
What the collapse dropped, and why this page says so
One removal arrived without its replacement, which is the failure this page
exists to name, found in the change this page describes. The runner family took
repository-add and every runner-groups call with it: on 3570f05a, git grep
for runner-groups, repository-add and reconcile_repository under
stado-rs/src returns nothing. So nothing in the fleet can now declare which
repositories may schedule on a managed runner group, and the runner capability
passes a group name only at registration. The cost is not theoretical:
weles-landing has sixteen documentation runs queued for a runner it is not
permitted to use, and the identity that could grant that permission does not
exist — the vault's three GitHub bearers carry read:org, never admin:org.
A capability that consumes a declaration is the right shape; a capability that consumes a declaration nobody can write is the same second source of truth in a new costume. The repair is a declaration for runner-group membership, read by the capability that registers runners, and it belongs to that capability rather than to a returning verb.
Nothing on this page lowers a command count by itself. The count drops when a capability ships and takes its verbs with it — and it only stays dropped when what replaced it can be declared.
Source: this website