One forge user, nox-agent, for every machine's agents' SSH pushes, each machine's key titled by machine (issue 991, ADR 0410) #344

Merged
mesh-tools merged 1 commits from fix/991-one-forge-agent-user into main 2026-10-17 00:51:08 +00:00
Member

Issue 991, ADR 0410 (operator, 2026-10-17: one forge agent user is enough).

  • ForgeUser names nox-agent for every consumer; agent-token keeps its per-node account through NodeAccount (unchanged behaviour).
  • EnsureKeys is scoped to mesh:<node>.: one machine's pass never removes another machine's key; keys titled otherwise are never touched.
  • The earlier agent-<node> users lose the mesh: keys the provider registered (said once, only when keys were removed), just before the key is added to nox-agent, since Gitea refuses one key on two users; they keep their team seat while agent-token mints their token; never deleted.
  • Tests: per-title removal, the move from agent-g14 to nox-agent against a fake forge that refuses a duplicate key, a failing pass leaving the old key, nothing said for a user without mesh keys.

Reviewed (larger model) in 2 rounds, approved at 2dd9797 (tree 444c084); this forge commit carries the same diff.

Issue 991, ADR 0410 (operator, 2026-10-17: one forge agent user is enough). - `ForgeUser` names `nox-agent` for every consumer; agent-token keeps its per-node account through `NodeAccount` (unchanged behaviour). - `EnsureKeys` is scoped to `mesh:<node>.`: one machine's pass never removes another machine's key; keys titled otherwise are never touched. - The earlier `agent-<node>` users lose the `mesh:` keys the provider registered (said once, only when keys were removed), just before the key is added to nox-agent, since Gitea refuses one key on two users; they keep their team seat while agent-token mints their token; never deleted. - Tests: per-title removal, the move from agent-g14 to nox-agent against a fake forge that refuses a duplicate key, a failing pass leaving the old key, nothing said for a user without mesh keys. Reviewed (larger model) in 2 rounds, approved at 2dd9797 (tree 444c084); this forge commit carries the same diff.
mesh-tools added 1 commit 2026-10-17 00:46:59 +00:00
One forge user, nox-agent, for every machine's agents' SSH pushes, each machine's key titled by machine (issue 991, ADR 0410)
mesh/merge-gate head 3a2560dc51fc merged into trunk 82df3bd58913 — pass: builds gitea → novox; no bus step; every machine composes with the change as it di…
mesh/repo-check head 3a2560dc51fc merged into trunk 82df3bd58913 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
3a2560dc51
mesh-tools added the mesh/ready label 2026-10-17 00:47:07 +00:00
Author
Member

ready at 3a2560dc51

Reviewed by lead-57@novox: Reviewer (opus) approved 2dd9797 (tree 444c084) in round 2, the same tree as this forge commit: ADR 0410, one forge user nox-agent, per-machine key titles, old users' keys moved first. Marked through mesh-delivery.mark-ready (novox/hq issue 913).

ready at 3a2560dc51fcd73ff60fdd8c39b390473310007e Reviewed by lead-57@novox: Reviewer (opus) approved 2dd9797 (tree 444c084) in round 2, the same tree as this forge commit: ADR 0410, one forge user nox-agent, per-machine key titles, old users' keys moved first. Marked through mesh-delivery.mark-ready (novox/hq issue 913).
Author
Member

Delivery novox/mesh-catalog@3a2560dc51fc — delivered since 2026-10-17T00:53:51Z

Delivery plan — builds gitea → novox; no bus step

  • tier 0: gitea
  • novox: receives gitea
  • gitea provides npm-package-registry, git, gitea-read, forge-account: its consumers are sent again after it
  • gitea keeps data (ADR 0233): what it holds is backed up before it moves

Machines

  • gitea on every machine running it: sent

Transitions

  • 2026-10-17 00:47 (new) → proposed (announced): novox/mesh-catalog#344's head announced
  • 2026-10-17 00:50 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-17 00:50 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-17 00:51 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
  • 2026-10-17 00:51 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
  • 2026-10-17 00:53 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says

The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-catalog@3a2560dc51fc` — **delivered** since 2026-10-17T00:53:51Z **Delivery plan** — builds gitea → novox; no bus step - tier 0: gitea - novox: receives gitea - gitea provides npm-package-registry, git, gitea-read, forge-account: its consumers are sent again after it - gitea keeps data (ADR 0233): what it holds is backed up before it moves **Machines** - gitea on every machine running it: sent **Transitions** - 2026-10-17 00:47 (new) → proposed (announced): novox/mesh-catalog#344's head announced - 2026-10-17 00:50 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-17 00:50 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-17 00:51 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move - 2026-10-17 00:51 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit - 2026-10-17 00:53 delivering → delivered (done): its walk is done, carrying its own commit: every machine of its deploy plan passed, or was left as its policy says The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Author
Member

Merge check at 3a2560dc
Judged: head 3a2560dc51fc merged into trunk 82df3bd58913; read beside it: mesh-controller b0d489a86c23, mesh-controller-main b0d489a86c23, mesh-host cd13f7726ef0, mesh-tools 7412c5b6c9c5

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)
  • mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)

Delivery plan — builds gitea → novox; no bus step

  • tier 0: gitea
  • novox: receives gitea
  • gitea provides npm-package-registry, git, gitea-read, forge-account: its consumers are sent again after it
  • gitea keeps data (ADR 0233): what it holds is backed up before it moves

Run by the build seat on shanks as build-1792198021380335615 (builds --log build-1792198021380335615).

✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.915753ms)
✔ the newest status of each context is kept, with what it said, when and by whom (19.838528ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.68624ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.290474ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.895933ms)
✔ a note line is appended once, as the forge's user, in its own repository (12.620899ms)
✔ a note that cannot be written is said, never read as written (0.451637ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.566668ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (22.375755ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (7.622954ms)
[gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written
✔ the verdict written while a start's read of the forge is in flight stays the newest status (1.284879ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.67815ms)
✔ a start heard after the verdict writes nothing (0.565779ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.695591ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.880983ms)
[gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check the forge holds pending; not written
[gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written
✔ an old run's verdict in flight when the recheck comes lands before the recheck's pending (1.834457ms)
✔ a rechecked head says where its new run is: building on which machine (0.714281ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.592729ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.454147ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.448167ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (6.710111ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.346316ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.449297ms)
✔ a repository as the mesh clones it is told from another (0.290494ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.32441ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (44.087192ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (21.695546ms)
✔ gitea_api still writes what an author writes (7.680386ms)
✔ gitea_branch_protection_set only strengthens (17.004995ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (49.515144ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (144.246015ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (16.386596ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (12.865323ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.450822ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (56.961425ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (21.31943ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.272054ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (6.980745ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (14.377426ms)
✔ a repository owner or name is never another path (0.255754ms)
✔ the judges, without a forge (0.359415ms)
✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.256544ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.714416ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.362945ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.104987ms)
✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (85.712487ms)
✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (19.96295ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (34.820633ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.592099ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (14.239063ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.243518ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.494162ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (69.599525ms)
✔ a pass pages until it reaches back past its floor, and no further (27.165088ms)
✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.591804ms)
✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (9.381261ms)
✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.578139ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (4.460827ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.355186ms)
✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.798811ms)
✔ another branch's body carries no merge whitelist (0.283905ms)
✔ the rule main is put right in the forge's own words: the body a setup sends carries no status checks of its own (0.370865ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (16.789203ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.421041ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.892543ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.171918ms)
✔ a commit status is set on the commit, under the merge check's context (42.410266ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.593699ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.387716ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (4.383755ms)
[gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 at bbbbbbbbbbbb, it is now at dddddddddddd: out of date, nothing run (novox/hq ADR 0382)
[gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was closed unmerged: out of date, nothing run (novox/hq ADR 0382)
[gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 merged onto cccccccccccc, its main is now eeeeeeeeeeee: out of date, nothing run (novox/hq ADR 0382)
[gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was merged otherwise than its head bbbbbbbbbbbb onto cccccccccccc: out of date, nothing run (novox/hq ADR 0382)
[gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was merged otherwise than its head bbbbbbbbbbbb onto cccccccccccc: out of date, nothing run (novox/hq ADR 0382)
[gitea] novox/mesh-host's main moved from 444444444444 to 333333333333: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323)
✔ both statuses name the siblings the verdict read, keeping the verdict word and the start of its reason within 140 characters (2.349195ms)
✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.423526ms)
✔ a verdict without siblings is said exactly as before (an older controller) (0.891053ms)
✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.993285ms)
✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (2.759102ms)
✔ a sibling closed unmerged puts the member's verdict out of date (0.346705ms)
✔ a move of the sibling's base puts it out of date, naming both commits (0.396006ms)
✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (0.561508ms)
✔ a merge of another head, or onto another base, is a move (0.807043ms)
✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.761981ms)
✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.63547ms)
✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (0.807692ms)
✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (0.187353ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.503753ms)
✔ a consumer that asks to read joins the readers, who write no package (0.431576ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.491517ms)
✔ a publisher is made and put on the package team, as before (0.195823ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (35.261009ms)
✔ second start: reuses the kept token, mints nothing (27.011236ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (30.715701ms)
✔ a token kept with write:admin by an earlier build is minted again without it at the next start, and the forge drops the old one (33.323639ms)
✔ a kept token lacking a scope the tools need: the forge refuses the call for the scope, the token is re-minted with the whole list, and the call goes through (31.472382ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (29.095647ms)
✔ concurrent first calls share one mint (16.422876ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (18.68999ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (56.174483ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (8.178863ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (18.350026ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.042635ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (24.497567ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.835083ms)
✔ nothing to mint with and no token: the client says what is missing (0.260084ms)
[gitea] no tools — no Gitea token, and nothing to mint one with — set MESH_GITEA_TOKEN, or MESH_GITEA_ADMIN_USER, MESH_GITEA_ADMIN_PASSWORD_FILE, MESH_GITEA_STATE_DIR
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-WVe8Gv/state/token
✔ the tools register once there is a way to a token, and the first call mints it (33.450832ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-j5aOKb/state/token
✔ a pull request can be closed, read and cleaned up through the tools (41.920309ms)
[gitea] no token kept — minting one
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-zOqSUj/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (73.91093ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-TMtxks/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (55.099667ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-FS9GCi/state/token
✔ what a base's protection asks is read from the branch, which the tools' account may read, not from the rules' list it may not (48.371416ms)
[gitea] the kept token was minted with other scopes than the tools take now — minting one with them
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-V1YXwA/state/token
[gitea] deleted 1 token(s) named "mesh-tools" the admin account "mesh-admin" held: the tools act as "mesh-tools" now
✔ the administrator's token from before goes: a kept admin token is minted again for the tools' account, and the forge drops the old one (46.351766ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-hXfWCT/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (39.894818ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.862753ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.991835ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-JULfGH/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (42.380597ms)
[gitea] no token kept — minting one
✔ a mint that fails leaves the administrator's old token in place: the tools are never left without one (29.459523ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-0hdLdT/state/token
[gitea] FAILED to delete the token(s) named "mesh-tools" the admin account "mesh-admin" holds — tried again at the next start: Gitea DELETE /users/mesh-admin/tokens/902 (named mesh-tools): 500 {"message":"the forge is not well"}
✔ an old token that cannot be deleted after the mint is said, the new token kept, and deleted at the next start (48.59148ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-iGqYv9/state/token
✔ a team another process made between the look and the create is found, not a failure (44.178463ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-m6IVn4/state/token
✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (39.593944ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-aCFlqa/state/token
✔ a glob rule covering main keeps its statuses when main is given a rule of its own name (issue 948, review round 1) (39.35848ms)
✔ a start with the token kept still puts main's rules right: the module's start asks for it each time (issue 948, review round 1) (14.483558ms)
[gitea] novox/mesh-host's main moved from a2c2c35bbbbb to d28ca95ccccc: 25 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323)
[gitea] novox/mesh-host's main moved from d28ca95ccccc to ffffffffffff: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323)
[gitea] novox/mesh-host: whether a trunk moved could not be weighed, so it is weighed again on the next pass — the forge is away
[gitea] novox/mesh-host's main moved from a2c2c35bbbbb to d28ca95ccccc: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323)
[gitea] novox/mesh-controller's feat/57-config-and-provisioning moved from eeeeeeeeeeee to d28ca95ccccc: 1 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323)
✔ both statuses say the head and the trunk commit the verdict judged, first, whatever the summary's length (2.383616ms)
✔ a trunk's move puts an open head's merge check out of date, naming both commits, unless a check is on its way (1.362721ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (5.98447ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.499238ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.533738ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.355245ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.331315ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (1.328409ms)
✔ a signed event is published on the bus and acted on once (73.201679ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (33.651255ms)
✔ an event and the poll seeing the same head announce it once (0.495248ms)
✔ each forge event is named in the module's stream and trimmed (0.756132ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.503023ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.398541ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.885313ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.736671ms)
✔ the receiver tells what it verified and what it refused (10.104942ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.129607ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.634809ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.336295ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.192853ms)
ℹ tests 147
ℹ suites 0
ℹ pass 147
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 785.121815
**Merge check** at `3a2560dc` Judged: head `3a2560dc51fc` merged into trunk `82df3bd58913`; read beside it: mesh-controller `b0d489a86c23`, mesh-controller-main `b0d489a86c23`, mesh-host `cd13f7726ef0`, mesh-tools `7412c5b6c9c5` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea) - `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript) **Delivery plan** — builds gitea → novox; no bus step - tier 0: gitea - novox: receives gitea - gitea provides npm-package-registry, git, gitea-read, forge-account: its consumers are sent again after it - gitea keeps data (ADR 0233): what it holds is backed up before it moves Run by the build seat on shanks as `build-1792198021380335615` (`builds --log build-1792198021380335615`). ``` ✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.915753ms) ✔ the newest status of each context is kept, with what it said, when and by whom (19.838528ms) ✔ a commit merges only when every required status is a success; a warning blocks (0.68624ms) ✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.290474ms) ✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.895933ms) ✔ a note line is appended once, as the forge's user, in its own repository (12.620899ms) ✔ a note that cannot be written is said, never read as written (0.451637ms) ✔ one view per pull request, found by its marker; only the mesh's statuses (0.566668ms) ✔ a build machine taking the check is said on the head: building on which machine, since the local time (22.375755ms) ✔ the verdict heard while a 'building' write is in flight lands last and stays (7.622954ms) [gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written ✔ the verdict written while a start's read of the forge is in flight stays the newest status (1.284879ms) ✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.67815ms) ✔ a start heard after the verdict writes nothing (0.565779ms) ✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.695591ms) ✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.880983ms) [gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check the forge holds pending; not written [gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written ✔ an old run's verdict in flight when the recheck comes lands before the recheck's pending (1.834457ms) ✔ a rechecked head says where its new run is: building on which machine (0.714281ms) ✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.592729ms) ✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.454147ms) ✔ a recheck the forge does not take fails its caller, and the head is as it was (0.448167ms) ✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (6.710111ms) ✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.346316ms) ✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.449297ms) ✔ a repository as the mesh clones it is told from another (0.290494ms) ✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.32441ms) ✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (44.087192ms) ✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (21.695546ms) ✔ gitea_api still writes what an author writes (7.680386ms) ✔ gitea_branch_protection_set only strengthens (17.004995ms) ✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (49.515144ms) ✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (144.246015ms) ✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (16.386596ms) ✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (12.865323ms) ✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.450822ms) ✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (56.961425ms) ✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (21.31943ms) ✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.272054ms) ✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (6.980745ms) ✔ a rule named for a branch is not made weaker than the glob rule that guards it now (14.377426ms) ✔ a repository owner or name is never another path (0.255754ms) ✔ the judges, without a forge (0.359415ms) ✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.256544ms) ✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.714416ms) ✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.362945ms) ✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.104987ms) ✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (85.712487ms) ✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (19.96295ms) ✔ every page of a pull request's files is read, though the forge caps a page at fifty (34.820633ms) ✔ a pass asks only the repositories that moved since the last look, every one before the first (0.592099ms) ✔ the directories above a merge's files that hold a module at the commit are said, the root never (14.239063ms) ✔ a lookup the forge refuses is thrown, not read as no module (5.243518ms) ✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.494162ms) ✔ a new head beyond the newest 20 open pull requests is announced, exactly once (69.599525ms) ✔ a pass pages until it reaches back past its floor, and no further (27.165088ms) ✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.591804ms) ✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (9.381261ms) ✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.578139ms) ✔ the statuses asked for replace the rule's, and an empty list requires none (4.460827ms) ✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.355186ms) ✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.798811ms) ✔ another branch's body carries no merge whitelist (0.283905ms) ✔ the rule main is put right in the forge's own words: the body a setup sends carries no status checks of its own (0.370865ms) ✔ each open pull request's new head is announced once, and nothing closed or merged (16.789203ms) ✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.421041ms) ✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.892543ms) ✔ no verdict sets warning on a required check; only a person's decision fails (1.171918ms) ✔ a commit status is set on the commit, under the merge check's context (42.410266ms) ✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.593699ms) ✔ a head announced is pending in the build queue, since the local time it was announced (0.387716ms) ✔ the vault's token is used, a setting of it is ignored and said once, without its value (4.383755ms) [gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 at bbbbbbbbbbbb, it is now at dddddddddddd: out of date, nothing run (novox/hq ADR 0382) [gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was closed unmerged: out of date, nothing run (novox/hq ADR 0382) [gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 merged onto cccccccccccc, its main is now eeeeeeeeeeee: out of date, nothing run (novox/hq ADR 0382) [gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was merged otherwise than its head bbbbbbbbbbbb onto cccccccccccc: out of date, nothing run (novox/hq ADR 0382) [gitea] novox/mesh-controller#346: its verdict was judged beside mesh-host#114 was merged otherwise than its head bbbbbbbbbbbb onto cccccccccccc: out of date, nothing run (novox/hq ADR 0382) [gitea] novox/mesh-host's main moved from 444444444444 to 333333333333: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323) ✔ both statuses name the siblings the verdict read, keeping the verdict word and the start of its reason within 140 characters (2.349195ms) ✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.423526ms) ✔ a verdict without siblings is said exactly as before (an older controller) (0.891053ms) ✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.993285ms) ✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (2.759102ms) ✔ a sibling closed unmerged puts the member's verdict out of date (0.346705ms) ✔ a move of the sibling's base puts it out of date, naming both commits (0.396006ms) ✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (0.561508ms) ✔ a merge of another head, or onto another base, is a move (0.807043ms) ✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.761981ms) ✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.63547ms) ✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (0.807692ms) ✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (0.187353ms) ✔ a consumer that asks nothing, or write, publishes: the package team (1.503753ms) ✔ a consumer that asks to read joins the readers, who write no package (0.431576ms) ✔ a reader is made, put on the readers team, and taken off the package team (0.491517ms) ✔ a publisher is made and put on the package team, as before (0.195823ms) ✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (35.261009ms) ✔ second start: reuses the kept token, mints nothing (27.011236ms) ✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (30.715701ms) ✔ a token kept with write:admin by an earlier build is minted again without it at the next start, and the forge drops the old one (33.323639ms) ✔ a kept token lacking a scope the tools need: the forge refuses the call for the scope, the token is re-minted with the whole list, and the call goes through (31.472382ms) ✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (29.095647ms) ✔ concurrent first calls share one mint (16.422876ms) ✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (18.68999ms) ✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (56.174483ms) ✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (8.178863ms) ✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (18.350026ms) ✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.042635ms) ✔ a second process finds the token the first renewed, and reuses it instead of minting over it (24.497567ms) ✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.835083ms) ✔ nothing to mint with and no token: the client says what is missing (0.260084ms) [gitea] no tools — no Gitea token, and nothing to mint one with — set MESH_GITEA_TOKEN, or MESH_GITEA_ADMIN_USER, MESH_GITEA_ADMIN_PASSWORD_FILE, MESH_GITEA_STATE_DIR [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-WVe8Gv/state/token ✔ the tools register once there is a way to a token, and the first call mints it (33.450832ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-j5aOKb/state/token ✔ a pull request can be closed, read and cleaned up through the tools (41.920309ms) [gitea] no token kept — minting one [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-zOqSUj/state/token ✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (73.91093ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-TMtxks/state/token ✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (55.099667ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-FS9GCi/state/token ✔ what a base's protection asks is read from the branch, which the tools' account may read, not from the rules' list it may not (48.371416ms) [gitea] the kept token was minted with other scopes than the tools take now — minting one with them [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-V1YXwA/state/token [gitea] deleted 1 token(s) named "mesh-tools" the admin account "mesh-admin" held: the tools act as "mesh-tools" now ✔ the administrator's token from before goes: a kept admin token is minted again for the tools' account, and the forge drops the old one (46.351766ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-hXfWCT/state/token ✔ a tools' account someone made an administrator is made a plain account again at the next mint (39.894818ms) ✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.862753ms) ✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.991835ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-JULfGH/state/token ✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (42.380597ms) [gitea] no token kept — minting one ✔ a mint that fails leaves the administrator's old token in place: the tools are never left without one (29.459523ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-0hdLdT/state/token [gitea] FAILED to delete the token(s) named "mesh-tools" the admin account "mesh-admin" holds — tried again at the next start: Gitea DELETE /users/mesh-admin/tokens/902 (named mesh-tools): 500 {"message":"the forge is not well"} ✔ an old token that cannot be deleted after the mint is said, the new token kept, and deleted at the next start (48.59148ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-iGqYv9/state/token ✔ a team another process made between the look and the create is found, not a failure (44.178463ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-m6IVn4/state/token ✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (39.593944ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-tools" (write:repository, write:issue, read:user), kept at /tmp/gitea-tools-account-aCFlqa/state/token ✔ a glob rule covering main keeps its statuses when main is given a rule of its own name (issue 948, review round 1) (39.35848ms) ✔ a start with the token kept still puts main's rules right: the module's start asks for it each time (issue 948, review round 1) (14.483558ms) [gitea] novox/mesh-host's main moved from a2c2c35bbbbb to d28ca95ccccc: 25 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323) [gitea] novox/mesh-host's main moved from d28ca95ccccc to ffffffffffff: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323) [gitea] novox/mesh-host: whether a trunk moved could not be weighed, so it is weighed again on the next pass — the forge is away [gitea] novox/mesh-host's main moved from a2c2c35bbbbb to d28ca95ccccc: 0 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323) [gitea] novox/mesh-controller's feat/57-config-and-provisioning moved from eeeeeeeeeeee to d28ca95ccccc: 1 open pull request(s) into it put out of date, nothing run (novox/hq ADR 0323) ✔ both statuses say the head and the trunk commit the verdict judged, first, whatever the summary's length (2.383616ms) ✔ a trunk's move puts an open head's merge check out of date, naming both commits, unless a check is on its way (1.362721ms) ✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (5.98447ms) ✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.499238ms) ✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.533738ms) ✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.355245ms) ✔ a verdict that landed out of date keeps its account on the pull request (0.331315ms) ✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (1.328409ms) ✔ a signed event is published on the bus and acted on once (73.201679ms) ✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (33.651255ms) ✔ an event and the poll seeing the same head announce it once (0.495248ms) ✔ each forge event is named in the module's stream and trimmed (0.756132ms) ✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.503023ms) ✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.398541ms) ✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.885313ms) ✔ a forge that makes no system webhook is said, never taken as heard (0.736671ms) ✔ the receiver tells what it verified and what it refused (10.104942ms) ✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.129607ms) ✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.634809ms) ✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.336295ms) ✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.192853ms) ℹ tests 147 ℹ suites 0 ℹ pass 147 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 785.121815 ```
mesh-tools merged commit a7d526838a into main 2026-10-17 00:51:08 +00:00
mesh-tools deleted branch fix/991-one-forge-agent-user 2026-10-17 00:51:09 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#344