installer v1.28.0: the removal genuinely reverses the installation (R-316)
gates / gates (push) Successful in 13s

v1.27.0's fix worked exactly once per machine. Measured on drill-r50 from virgin,
on the PUBLISHED v1.27.0, before anything was changed: cycle 1 recorded 'no' and
freed :53; cycle 2 recorded 'yes' and left dnsmasq running on 0.0.0.0:53; cycle 3
refused, exit 1. Every box already in the field is at cycle 2, and a reinstall
onto a machine that has had Felhom is cycle 2 by definition.

Why cycle 2 says yes: the preflight's ownership question is dpkg-query package
presence and nothing else - not the absence of a record. Stopping the unit and
leaving the package made our own package read as the household's one cycle later.

Now the uninstall removes the package when the record says we installed it. Order
unchanged and load-bearing: read the record, act, then delete the state file that
holds it. TWO packages are recorded, because dnsmasq ships the unit and
dnsmasq-base ships /usr/sbin/dnsmasq, and each is taken back only if we added it.

The dependency check is a SIMULATION, not a guess: apt-get -s purge is asked what
it would remove and the purge proceeds only if that set is a subset of ours;
otherwise stop+disable, naming the package that blocked it. Never interactive,
never fatal, and the success is re-queried rather than read off an exit code.

Watched: three fixed cycles -> install 3 PASSES; a household resolver untouched; a
dependent package not purged and named; no record -> untouched with the command
named. Red-proofs with the mutation asserted applied: remove the purge -> cycle 3
refuses in those exact words; remove the ownership check -> a household resolver is
purged; infer ownership -> the guess is taken.

Also: R-317 (the agent stats a path dnsmasq-base owns to decide whether to install
dnsmasq - pre-existing, now reachable), R-318 (no honest ownership marker exists
for existing boxes; the preflight message is the mechanism), and the status page's
decisions section rewritten to say what each decision costs and what doing nothing
selects.
This commit is contained in:
2026-08-13 08:16:04 +02:00
parent d102ca5767
commit fc737b0fc0
5 changed files with 239 additions and 21 deletions
+20
View File
@@ -38,6 +38,26 @@ route, and each has been separately absent in this project. **Before claiming a
of the three you have evidence for** — and if a comment or a card asserts the third, find the code path
that performs it or move the claim.
## A removal that leaves the package leaves the next install a second-cycle machine (2026-08-13)
**Stopping is not removing, and the difference does not show up until the cycle after next.** R-300
stopped and disabled the resolver we install and left the package in place — deliberately, as the
conservative act. Cycle 1 looked fixed and *was* fixed. But the next install asks dpkg "is this
installed?", gets yes, records the package as the household's, and the uninstall after that leaves it
running: cycle 3 refuses with the message that blames the customer's network. **Measured, three cycles,
before anything was changed.**
**The general shape:** a teardown that leaves an artifact behind does not merely fail to clean up — it
*changes what the next run concludes about who owns that artifact*. Ask of any "we left it in place,
to be safe": **what will the next install think this is?** If the answer is "ours, misread as theirs",
the conservative act has quietly become the destructive one, one cycle downstream.
**And the corollary that nearly shipped a second defect:** the thing you remove and the thing something
else probes for may be in **different packages**. `dnsmasq` ships the unit; `dnsmasq-base` ships the
binary the agent stats to decide whether to install. Removing one and not the other satisfies the
uninstall and breaks the next install silently (R-317). **Before removing a package, ask what else
tests for the files it shares with its siblings.**
## Two rules earned twice each (2026-08-12)
**A fact must be recorded at the moment it is a fact, not inferred later from something adjacent.**