ADR 0266: an agent can merge its own pull request, and the restart condition's quiet counts on the controller's clock
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

The record said a merge needs a person; the forge requires no approval and
the merge tool merges once the checks pass, so nothing enforces the
operator's yes. What still remains now says so, and that the restart fix
counts from when the controller first saw the search pending. Item 10 and
routes A and B describe the follow-ups of the review of 2026-10-09.
This commit is contained in:
jochen
2026-10-09 12:27:13 +02:00
parent d7999e6fc9
commit cb907d5810
@@ -156,10 +156,9 @@ the C locale. It finds a way to root when the account:
and pseudo filesystems excepted). The engine searches for these in the background, at most hourly, and
lets one search run for at most 15 minutes. Until the first search after the engine starts has finished,
the verdict is *not judged yet*. *Corrected 2026-10-09: this said the search gets two minutes; the engine's
bound on a search is 15 minutes (mesh-host pull request 61). Its `find` and the package queries still carry
a two-minute limit of their own inside that bound, so as written a search longer than two minutes ends
incomplete before the 15 minutes are reached; which of the two limits holds is for that pull request to
settle;*
bound on a search is 15 minutes, and it governs the whole search: mesh-host pull request 61 dropped a
two-minute limit that sat inside it. The package manager is asked about every path found in one call, and a
walk `find` ended with an error is incomplete unless every error was a file that vanished while it walked;*
11. lives on a machine whose kernel leaves hard or symbolic links to other accounts' files unprotected
(`fs.protected_hardlinks` or `fs.protected_symlinks` is 0).
@@ -325,33 +324,46 @@ found around it and are closed on the mesh's main line:
or forked the catalogue or the node-engine, and committed a `sudo` module granting itself a rule without a
password, had the next push make it root on every node. `rebuild`, `replay --register` and `assign` with
`build` reached the same take-in. Closed by mesh-controller pull request 174, once merged after pull request
164: an outcome registers a module only from the repository the catalogue already builds it from, a new
module only from a repository the catalogue already builds another from (a merge adding one), and anything
else only when the build was asked at the controller's terminal; through a verb, a build of a repository
the catalogue builds nothing from is not asked at all, and a failed gate puts back only a build of the
module's own repository.
164: through any verb, an outcome registers a module only from the repository the catalogue already builds
it from — the same repository by the forge's own id, so one deleted and made again under its name is
refused — and a new module only from a repository the catalogue already builds another from (a merge adding
one); and only from the mesh's forge, where that repository's trunk refuses direct pushes, requires a status
and lets no administrator merge past one. Anything else only when the build was asked at the controller's
terminal, for that repository and path; the serving controller marks itself and everything it starts, so
none of it reads as the terminal. Through a verb, a build of a repository the catalogue builds nothing from
is not asked at all, and a failed gate puts back only a build of the module's own repository.
- **The forge's tools could change the forge's protection** (found in review, 2026-10-09). `gitea_api` sent
any call with a token minted for the forge's administrator, `write:admin` included: an agent could drop a
branch's protection or its required checks, set the merge check's status, write to the default branch,
delete and recreate a repository the mesh builds from, or merge its own pull request past the checks; and
`gitea_branch_protection_set` could weaken a rule. Closed by mesh-catalog pull request 153: `gitea_api`
reads anything but the forge's administration and writes only what an author writes, protection is only
made stronger, a merge needs every required status a success and merges only the head that was checked, a
guarded repository is not deleted, and the token is minted without `write:admin`. A merge after the
operator's yes, of a pull request whose checks passed, works as before. The token still belongs to the
forge's administrator; a non-admin account for the tools is a follow-up.
- **Every node-engine restart raises `agent-can-become-root`.** Until the engine's first search for setuid
programs finishes (§3 item 10), the verdict is *not judged yet*, and the self-check (§4) raises the urgent
condition on every restart. Proposed in mesh-controller pull request 175: while that first search is within
its bound (15 minutes, and one statement more), the condition is not raised; the account is still not
confined, `node show` still says not judged, and the condition is raised as before once the search fails,
runs out its bound or finds a way to root.
reads anything but the forge's administration and writes only what an author writes, never a pull
request's base; protection is only made stronger, a rule named for a branch included; a merge goes only into
a branch whose protection requires the checks, needs every required status a success, and merges only the
head that was checked; a tool deletes only a repository of its own user that no rule guards and that had no
commit in 30 days, named in full; and the token is minted without `write:admin`. The token still belongs to
the forge's administrator; a non-admin account for the tools is a follow-up.
- **An agent can merge its own pull request.** Every mesh repository's trunk requires its checks and no
approval (0 required approvals), and the forge's merge tool merges as the forge's administrator once the
checks pass. Nothing on the forge checks that the operator said yes: the operator's approval in chat is a
habit of the session, not a rule anything enforces. A merged module is what the next push sends, so an agent
that writes a change the checks pass can deliver it without a person. Whether to require an approval on the
forge is the operator's decision, not yet taken.
- **Every node-engine restart raises `agent-can-become-root`**, and the fix proposed for it must not open a
quiet of its own. Until the engine's first search for setuid programs finishes (§3 item 10), the verdict is
*not judged yet*, and the self-check (§4) raises the urgent condition on every restart. Mesh-controller pull
request 175 (merged after 174) keeps the condition quiet while that search is within its bound, counted from
when the controller first saw it waiting, kept in its store and forgotten at the next complete verdict —
never from the engine's own time, which starts again at every restart, so an agent that restarts the engine
in a loop does not keep it quiet. The account is still not confined, `node show` still says not judged, and
the condition is raised at once when the search fails or finds a way to root. Until that pull request is
merged, every restart raises the urgent condition.
- **The self-check judges this record's account only.** ADR 0259's own root probe (mesh-controller pull
request 154) asks the same question through the `sudo` module; it must call this record's judgement, one
function, rather than keep a second answer.
The agent account holds a forge key of its own, registered by the operator, so its pushes are its own. It
works on branches and pull requests, and a merge needs a person anyway.
works on branches and pull requests; what it may merge is said under *What still remains*.
## Consequences