Blog ·
The select goes around the sentence
Fourteen sentences called a container a VM. Swapping the word works in English and breaks in seven languages, so the branch has to hold the whole sentence.
By Provibr
A Proxmox container is, in our product, a VPS. The customer orders one, gets a machine with an address and a plan, and every screen that talks about servers talks about it correctly. The type in the database says vm, and that is right: what was sold is a virtual private server.
The difference lives one layer further down, in the words. A container has no BIOS, no virtual disk in the sense a virtual machine has one, and no guest agent. An earlier round noted, as a loose end, that one sentence still said "the VM" on a container page. There were fourteen.
Why the word is the wrong unit
The obvious repair is a placeholder where the noun goes. Something like a select on the resource kind that yields "container" or "VM", dropped into the middle of the sentence.
It works in English. It breaks in German, where the article is not the same for the two words. It breaks the same way in Spanish, French, Italian, Portuguese and Romanian, and it breaks harder in Ukrainian, where the noun in that position has to take a case ending that the substituted word does not have.
You can chase this. Add a select for the article as well, then one for the adjective agreement, then discover that the natural word order changes. What you have built at that point is a small grammar engine driven by a translator who is not allowed to see the sentence.
So the branch holds the entire sentence. One variant for a container, one for everything else, and the translator writes each of them as a sentence in their own language rather than filling a slot in an English one. The generic branch is byte identical to the value it replaced, so nothing that was already correct moved.
The branch is called container rather than lxc, because the backups tab already used exactly that name for exactly that question, and its view model calls the field the same thing. Two spellings of the same distinction inside one catalogue is a small cost each time and a guess every time: the next round has to work out which of the two applies here.
A select whose branches agree is invisible
A test that checks the shape of these keys, that they parse, that both branches exist, that the argument is spelled the same everywhere, will pass on a select whose two branches say precisely the same thing. That is the exact defect being repaired, so a shape check is worth very little here.
The guard therefore renders both branches, in all nine languages, and requires that the container branch of an English sentence no longer contains the word it was written to remove. It also requires that the list itself has not quietly emptied: a guard over an empty list is the cheapest green in software.
Twenty one tests, and each of them was proven by making it fail first. It is the same form as the guard on backup coverage wording, and for the same reason: the interesting failure is not a syntax error, it is a sentence that is grammatically perfect and factually wrong.
The sentence that was never true
One of the fourteen was not a wording problem at all.
The network tab has a line for when no addresses have been observed. It read, in effect: nothing seen yet, the guest agent has to be running inside the VM.
On a container, that sentence is always shown, because the agent does not read addresses for containers at all. The observation function returns an empty list for that guest kind before it asks anything. So the sentence appeared permanently, and while it was there it said two things that were not true: that this might change once something starts, and that the thing which would change it is a guest agent, which a container never gets.
Nobody had lied. The sentence was written for virtual machines, it was correct for them, and it was reached by a code path that had grown a second caller. That is the ordinary way a screen ends up saying something false: not by anyone writing a falsehood, but by a true sentence being shown in a situation it was never about.
The container branch says what is actually the case, which is that addresses inside a container are not read. It offers no hope of the number appearing later, because there is none.
| Old sentence | What was wrong with it on a container | |
|---|---|---|
| Promise | Nothing seen yet | It will not change; the code never looks |
| Cause | The guest agent must be running | Containers do not have one |
| Fix | Start the guest agent | There is nothing to start |
Two ways the measurement went green for the wrong reason
The browser check for all of this was written first and was wrong twice, in ways worth naming because both produce a passing result.
The first: the check read the visible text of the page and looked for the word "container". It found it. It was in the name of the test plan, zz-k7-containerplan, seeded by the check itself, while the sentence three lines below still said "the VM". A test that seeds a fixture and then searches the whole page for a word the fixture contains is measuring its own setup. The repair is a hook on the element that actually carries the sentence, so the assertion can only be satisfied by the thing it is about.
The second was a real defect that the first one had hidden. The kind that drives the select was derived from an order-form variable that carries a condition about placement, whether the plan has somewhere to run. A container plan without a placement therefore came out as "not a container", and the page said "the VM" on a container order. The kind now comes from the plan's own compute kind plus what the customer chose, neither of which knows anything about placement.
That is the more general shape: a value that happens to correlate with what you want is not the value you want. It agrees until the day it does not, and on that day nothing fails, it just says the other thing.
When two constants share a name
A smaller thing from the same round, in a different guard entirely, because it is a good illustration of how a static check can lose its footing.
We have a guard that resolves the argument shape of shared translation constants by name across the whole project, so that a key used with a shared object is still covered. Two different components each defined a constant called where, holding different things. The guard, resolving by name, could no longer say which one any given call site meant, and it gave up on all of them, including calls in a third file that had nothing to do with either.
Renaming one of them fixed it. The lesson is not about naming: it is that a guard that resolves symbols by name across files has an ambiguity mode, and its ambiguity mode is silence. The check does not report "two candidates", it reports nothing about those call sites at all, and the count of covered keys goes down by an amount nobody is watching.
What was left alone, on purpose
Three things stayed as they were, and writing down why is part of the work.
One sentence tells a customer that reinstalling destroys everything on the machine's disks. On a container that is not a word problem: reinstalling a container preserves, erases or excludes per mount point, and the honest replacement is a statement about which mount points fall into which group. That is a screen, not a translation, and it belongs to the round that builds it.
A hint on the ordering form calls a field the hostname of the virtual machine, in a context where the thing being named is a hosting account. That one is a content decision rather than a container question, and pretending otherwise would have smuggled a copywriting change into a wording fix.
And the licence counter keeps saying VM, because that is what it counts. The unit a customer is billed for is not the unit a customer looks at, and quietly renaming it would make an invoice line stop matching a number on a page.
What we would write down
- Put the branch around the sentence, not around the noun. Word substitution works in English and fails on articles and cases everywhere else.
- Reuse the branch name that already exists. Two spellings of one distinction in one catalogue is a guess for every future round.
- Render both branches in the test. A select whose branches agree passes every check that only looks at shape.
- Assert against a hook on the element, not against the whole page. A fixture name containing the word you are looking for is a green light for nothing.
- A correlated value is not the value. Deriving a resource kind from something that carries a placement condition works until a plan has no placement.
- A guard that resolves by name goes quiet when a name is ambiguous. It does not report the ambiguity, it reports nothing.
- A sentence can be false without anyone writing a falsehood. True for one case, reached by a second caller, is the usual route.
For clarity about what this is: the fourteen keys are live in all nine languages and guarded. Seven of those languages have only been through the rendering test rather than a second reader, and none of this was exercised against a real Proxmox host in this round.
From the platform work on Provibr. What actually differs between the two guest kinds is in VM or container on Proxmox, and why reinstalling a container is a replace rather than a repair is in reinstalling a container is a replace.