Decision 0073
0073. What one machine learned about its body enters through a door of its own
Architecture decision record 0073: 0073. What one machine learned about its body enters through a door of its own
Status: accepted. Builds the hardware-quirk half of ADR-0014, which said “hardware quirks discovered on one machine become proposed hardware modules for all” and left the mechanism unstated.
Context
Phase 6 has three sources of proposals, and until today only two of them existed. The agent
editing its own mutable crates works, and a person filing a change by hand works; both go through
Queue::file, which takes whatever is changed in the working tree, checks every path against the
partition, and refuses if any of them is core.
The third source is the one that makes this a species rather than a fleet: a machine that settled
on a working configuration for its own hardware offering that configuration to the next machine of
the same model. A hardware module belongs in nix/hardware/.
nix/ is core. Deliberately so, and for a reason that gets sharper rather than weaker here: a
.nix file is arbitrary Nix, and there is no such thing as a hardware module that is only a
hardware module. One that said users.users.root.openssh.authorizedKeys would be an escalation
with an entirely plausible name and a filename nobody would look at twice.
So the loop as ADR-0014 described it cannot be built with the door that exists, and the obvious
repair — take nix/hardware/ out of core — is the repair that gives the agent a .nix file it
can write whatever it likes into.
Decision
The partition is not widened. A second door is cut, with a fixed shape.
Queue::offer_body does not read a working tree at all. It takes a proposal composed by
body::propose out of two things the agent does not author:
system.identity— what firmware says this machine is, read bytacid-factsfrom DMI;- the
tacid.*lines of this machine’s ownbody/hardware.nix, which are settings the owner and the agent already arrived at through the ordinary transaction path, with every gate that implies.
Everything else about the module — its path, its name, its header, its rationale — is composed by code. There is no argument to the method. The agent cannot influence the content beyond what it could already put in its own genome, in the open, one approval at a time.
The queue then checks the shape it was handed rather than trusting it: exactly one file, under
nix/hardware/, ending in .nix, with no directory of its own. A composer is code and code
changes; the check is at the door, where it stays true if the composer stops being.
So the exception is not “this directory is writable”. It is this one composer may write this one shape of file, in this one place.
Three narrower rules fall out.
A machine whose firmware will not say what it is offers nothing. qemu, Default string,
To Be Filled By O.E.M. and their neighbours are refused by name. A module keyed on one of them
would match every machine and fit none, which is worse than no module.
system.stateVersion does not travel. It is a fact about the birth of one machine; carried
into a shared module it is wrong everywhere it lands, and wrong in the quiet way that surfaces
years later. It is named in the proposal’s rationale instead, so a reviewer sees that it was
considered and left behind rather than forgotten.
A body proposal records no base revision. Every other proposal is a diff against the checkout it was written in and means nothing without it. This one creates a file that does not exist yet, so it applies against any revision — and because the identifier is a hash of the content, two machines of one model file the same identifier even when their checkouts stand at different revisions. A maintainer sees one module proposed twice rather than two modules proposed once, and a second machine agreeing is evidence instead of noise.
The verb is tacid propose body, and the method is propose.body. It is governed by
self.modify — the same capability as source.propose, and by nothing weaker, because what it
files is still a branch and a commit in this machine’s copy of Tacid’s source.
Consequences
- Asking twice is the same offer. The identifier is a hash of the content, so a machine that
has not changed composes an identical proposal every time it is asked, and the queue hands back
the one it already holds. Without that word for it the second asking would have failed on
git checkout -b— a true sentence about a branch, which a person would read as the offer being refused. - Nothing is pushed, here as everywhere in this crate. The module lives on a local branch until an owner signs it, and the owner who signs it is not the owner of the machine that wrote it.
nix/hardware/gains files whose author is a machine.git logsays so: the commit carries the agent’s identity, and the owner’s signature is a separate act on a separate line.- The composer reads the genome rather than recomposing the body from facts. A setting the owner changed by hand is exactly the kind of thing worth passing on, and a module rebuilt from what the installer would have chosen loses precisely those.
- A module that is filed is not a module that is right. Nothing here evaluates it on a second machine of the same model, because there is no second machine — that is the hardware lab of ADR-0023 and Phase 7. What this builds is the path from one machine’s experience to a maintainer’s queue; the evidence that it generalises is a person’s job until there is a lab.
Alternatives rejected
Move nix/hardware/ out of core. The repair that fits in one line of partition.rs, and it
hands the agent a writable .nix file. Every argument for the partition is an argument against
this.
Have the agent write the module into the working tree and file it with Queue::file. The same
hole, reached by a longer route: file would have to stop refusing nix/, and then it stops
refusing it for every proposal, not only this one.
Compose the module from facts alone, ignoring the genome. Safer-sounding and worse: it drops
the settings a person actually tuned, which are the only ones worth another machine’s attention.
The genome’s tacid.* lines are already the product of gated changes, so reading them adds no
authority the agent did not have.
Let the machine push the branch to a shared repository. See ADR-0014. A species that evolved by whatever the most confident model believed on a Tuesday is not a species with a maintainer.