mesh-delivery acts on events and keeps each pull request's key; the forge's announcements carry a message id (issues 975, 976, design 47 milestone 2)
#345
Merged
mesh-toolsmerged 9 commits from feat/events-and-queues into main2026-10-17 12:52:42 +00:00
Design 47, milestone 2, one release with mesh-controller's pull request of the same branch name (ADR 0406). Issues 975 and 976. The controller goes first: mesh-delivery asks delivery-check with ready, which only the new controller takes, and the builder's read of mesh-delivery_pulls is on the safe side while the bucket does not exist yet.
mesh-delivery acts on events: ready marks (gitea.forge.pull-request.labeled, .unlabeled, .commented), commit statuses (gitea.forge.status), checked, plan-moved and the operator's Approve (decided). Its 10 s tick, 30 s reconcile and minute forge poll are gone; the controller's walks and the open pulls are read once at start; time-bound steps are named one-shot timers, enforced by a source test.
Each pull request's key in state pulls (head, trunk, verdict) is written by compare-and-set; a ready mark asks its check with ready. A merge announced again changes nothing. build-agent reads mesh-delivery.pulls.
gitea publishes its announcements with a fixed message id (announce.ts), so the bus refuses a double; merged-announced.json and pulls-announced.json are gone, the catch-up's since is a key in state announced.
Acceptance (design 47): test 6, a ready mark acted on within seconds, in mesh-delivery's bus test (merge asked about 4 ms after the comment event); the dedup test in modules/gitea/internal/busdedup.
Reviewed in three rounds by an independent reviewer; approved at this head.
after: novox/mesh-controller
Design 47, milestone 2, one release with mesh-controller's pull request of the same branch name (ADR 0406). Issues 975 and 976. The controller goes first: mesh-delivery asks `delivery-check` with `ready`, which only the new controller takes, and the builder's read of `mesh-delivery_pulls` is on the safe side while the bucket does not exist yet.
- mesh-delivery acts on events: ready marks (`gitea.forge.pull-request.labeled`, `.unlabeled`, `.commented`), commit statuses (`gitea.forge.status`), `checked`, `plan-moved` and the operator's Approve (`decided`). Its 10 s tick, 30 s reconcile and minute forge poll are gone; the controller's walks and the open pulls are read once at start; time-bound steps are named one-shot timers, enforced by a source test.
- Each pull request's key in state `pulls` (head, trunk, verdict) is written by compare-and-set; a ready mark asks its check with `ready`. A merge announced again changes nothing. build-agent reads `mesh-delivery.pulls`.
- gitea publishes its announcements with a fixed message id (`announce.ts`), so the bus refuses a double; `merged-announced.json` and `pulls-announced.json` are gone, the catch-up's `since` is a key in state `announced`.
- Acceptance (design 47): test 6, a ready mark acted on within seconds, in mesh-delivery's bus test (merge asked about 4 ms after the comment event); the dedup test in `modules/gitea/internal/busdedup`.
Reviewed in three rounds by an independent reviewer; approved at this head.
mesh/delivery-group group feat/events-and-queues rejected: its order contradicts itself: novox/mesh-catalog@52f70f42b749 and novox/mesh-controller@0f21fe7888be…
mesh/repo-check head 52f70f42b749 merged into trunk a7d526838ab0 beside mesh-controller#368@0f21fe78 — pass: its merge-check.sh passed, each part in its to…
Deliverynovox/mesh-catalog@94f65cccd9c1 — delivered since 2026-10-17T12:59:36Z
Delivery plan — builds build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
tier 0: build-agent, gitea, mesh-delivery
ace: receives build-agent
g14: receives build-agent
novox: receives build-agent, gitea, mesh-delivery
shanks: receives build-agent
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
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
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
Machines
mesh-controller on every machine running it: sent
route-proxy on every machine running it: sent
build-agent on every machine running it: sent
gitea on every machine running it: sent
mesh-delivery on every machine running it: sent
Transitions
2026-10-17 12:39 (new) → proposed (announced): novox/mesh-catalog#345's head announced
2026-10-17 12:43 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
2026-10-17 12:43 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-17 12:54 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 12:54 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 12:59 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@94f65cccd9c1` — **delivered** since 2026-10-17T12:59:36Z
**Delivery plan** — builds build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
- tier 0: build-agent, gitea, mesh-delivery
- ace: receives build-agent
- g14: receives build-agent
- novox: receives build-agent, gitea, mesh-delivery
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- 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
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
**Group** `feat/events-and-queues`, in order: novox/mesh-controller@0f21fe7888be → novox/mesh-catalog@94f65cccd9c1
- novox/mesh-controller@0f21fe7888be before novox/mesh-catalog@94f65cccd9c1: declared, over built by
- composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
**Machines**
- mesh-controller on every machine running it: sent
- route-proxy on every machine running it: sent
- build-agent on every machine running it: sent
- gitea on every machine running it: sent
- mesh-delivery on every machine running it: sent
**Transitions**
- 2026-10-17 12:39 (new) → proposed (announced): novox/mesh-catalog#345's head announced
- 2026-10-17 12:43 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
- 2026-10-17 12:43 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-17 12:54 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 12:54 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 12:59 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`.
Merge check at 52f70f42
Judged: head 52f70f42b749 merged into trunk a7d526838ab0; read beside it: mesh-controller f84becc6daee, mesh-controller-main 7d80155d1636, mesh-host cd13f7726ef0, mesh-tools 7412c5b6c9c5
Siblings read beside it: novox/mesh-controller#3680f21fe7888be merged onto 7d80155d1636 (main); judged by sibling novox/mesh-controller#368 merge f84becc6daee
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)
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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
tier 0: build-agent, gitea, mesh-delivery
ace: receives build-agent
g14: receives build-agent
novox: receives build-agent, gitea, mesh-delivery
shanks: receives build-agent
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
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
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
read by no module's build: modules/mesh-delivery/cmd/mesh-delivery/bus_test.go, modules/mesh-delivery/cmd/mesh-delivery/events_test.go, modules/mesh-delivery/cmd/mesh-delivery/fakes_test.go, modules/mesh-delivery/cmd/mesh-delivery/holder_test.go, modules/mesh-delivery/cmd/mesh-delivery/manifest_test.go, modules/mesh-delivery/cmd/mesh-delivery/markready_test.go, modules/mesh-delivery/cmd/mesh-delivery/pullkey_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_source_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_test.go, modules/mesh-delivery/cmd/mesh-delivery/unchecked_events_test.go
Run by the build seat on shanks as build-1792239339496293222 (builds --log build-1792239339496293222).
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.859914ms)
✔ the newest status of each context is kept, with what it said, when and by whom (31.19379ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.714171ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.311395ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.953106ms)
✔ a note line is appended once, as the forge's user, in its own repository (18.225342ms)
✔ a note that cannot be written is said, never read as written (0.477068ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.842674ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (27.337229ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.220509ms)
[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.283571ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.64805ms)
✔ a start heard after the verdict writes nothing (0.60247ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.744292ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.154318ms)
[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 (3.515847ms)
✔ a rechecked head says where its new run is: building on which machine (1.064987ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.835384ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.692231ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.650741ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (7.022403ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.270484ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (2.070413ms)
✔ a repository as the mesh clones it is told from another (0.446887ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (6.23803ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (49.769028ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (28.157071ms)
✔ gitea_api still writes what an author writes (6.673507ms)
✔ gitea_branch_protection_set only strengthens (15.693681ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (54.8597ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (146.552539ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (15.222934ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.33561ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.203063ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (54.91745ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (19.879789ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.286753ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.053384ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.06362ms)
✔ a repository owner or name is never another path (0.269074ms)
✔ the judges, without a forge (0.358395ms)
✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.241134ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.87328ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.334547ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.186823ms)
✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (86.66558ms)
✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (22.647603ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (35.627401ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.666611ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.270101ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.302645ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.973478ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (71.653879ms)
✔ a pass pages until it reaches back past its floor, and no further (31.765559ms)
✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.008102ms)
✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (9.050626ms)
✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.666505ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.535953ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.662021ms)
✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.910885ms)
✔ another branch's body carries no merge whitelist (0.295955ms)
✔ the rule main is put right in the forge's own words: the body a setup sends carries no status checks of its own (1.347122ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (15.61342ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.030067ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.61108ms)
✔ no verdict sets warning on a required check; only a person's decision fails (0.882375ms)
✔ a commit status is set on the commit, under the merge check's context (48.948105ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.572699ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.346796ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (2.879656ms)
[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.47395ms)
✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.416246ms)
✔ a verdict without siblings is said exactly as before (an older controller) (0.795063ms)
✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.999766ms)
✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (4.079226ms)
✔ a sibling closed unmerged puts the member's verdict out of date (0.462827ms)
✔ a move of the sibling's base puts it out of date, naming both commits (0.418937ms)
✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (1.503084ms)
✔ a merge of another head, or onto another base, is a move (1.602126ms)
✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.960685ms)
✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.682221ms)
✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (1.153609ms)
✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (1.007316ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.628655ms)
✔ a consumer that asks to read joins the readers, who write no package (0.437447ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.542729ms)
✔ a publisher is made and put on the package team, as before (0.226224ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (46.582396ms)
✔ second start: reuses the kept token, mints nothing (29.865969ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (37.631853ms)
✔ 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.889024ms)
✔ 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.854321ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (25.300366ms)
✔ concurrent first calls share one mint (14.413611ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (16.86414ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (94.509925ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (5.320265ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (14.294819ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.746172ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (19.767977ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.184616ms)
✔ nothing to mint with and no token: the client says what is missing (0.162193ms)
[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-fa2x1C/state/token
✔ the tools register once there is a way to a token, and the first call mints it (27.508311ms)
[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-Zyl8do/state/token
✔ a pull request can be closed, read and cleaned up through the tools (34.554764ms)
[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-zmJzt2/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (72.823437ms)
[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-YpV03g/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (60.133184ms)
[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-GLb1M3/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 (43.739391ms)
[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-RAOuH8/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 (42.841547ms)
[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-205eFO/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (37.694354ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.707362ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.865944ms)
[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-FeFodG/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (36.736459ms)
[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 (27.503821ms)
[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-ig9Uw0/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 (44.479543ms)
[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-lMOo0i/state/token
✔ a team another process made between the look and the create is found, not a failure (37.979869ms)
[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-XjmbPU/state/token
✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (34.27575ms)
[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-SLeAIh/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) (33.463706ms)
✔ 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) (12.441209ms)
[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 (3.680699ms)
✔ 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.462623ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (4.818128ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.518688ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.600099ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.337756ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.351906ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.536799ms)
✔ a signed event is published on the bus and acted on once (69.101458ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (34.87779ms)
✔ an event and the poll seeing the same head announce it once (0.546209ms)
✔ each forge event is named in the module's stream and trimmed (0.555458ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.387192ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.157479ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (3.842321ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.718202ms)
✔ the receiver tells what it verified and what it refused (11.985342ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.150019ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.548149ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.581859ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.266195ms)
ℹ tests 151
ℹ suites 0
ℹ pass 151
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 812.948162
**Merge check** at `52f70f42`
Judged: head `52f70f42b749` merged into trunk `a7d526838ab0`; read beside it: mesh-controller `f84becc6daee`, mesh-controller-main `7d80155d1636`, mesh-host `cd13f7726ef0`, mesh-tools `7412c5b6c9c5`
Siblings read beside it: novox/mesh-controller#368 `0f21fe7888be` merged onto `7d80155d1636` (main); judged by sibling novox/mesh-controller#368 merge f84becc6daee
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)
- `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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
- tier 0: build-agent, gitea, mesh-delivery
- ace: receives build-agent
- g14: receives build-agent
- novox: receives build-agent, gitea, mesh-delivery
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- 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
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
- read by no module's build: modules/mesh-delivery/cmd/mesh-delivery/bus_test.go, modules/mesh-delivery/cmd/mesh-delivery/events_test.go, modules/mesh-delivery/cmd/mesh-delivery/fakes_test.go, modules/mesh-delivery/cmd/mesh-delivery/holder_test.go, modules/mesh-delivery/cmd/mesh-delivery/manifest_test.go, modules/mesh-delivery/cmd/mesh-delivery/markready_test.go, modules/mesh-delivery/cmd/mesh-delivery/pullkey_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_source_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_test.go, modules/mesh-delivery/cmd/mesh-delivery/unchecked_events_test.go
Run by the build seat on shanks as `build-1792239339496293222` (`builds --log build-1792239339496293222`).
```
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.859914ms)
✔ the newest status of each context is kept, with what it said, when and by whom (31.19379ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.714171ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.311395ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.953106ms)
✔ a note line is appended once, as the forge's user, in its own repository (18.225342ms)
✔ a note that cannot be written is said, never read as written (0.477068ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.842674ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (27.337229ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.220509ms)
[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.283571ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.64805ms)
✔ a start heard after the verdict writes nothing (0.60247ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.744292ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.154318ms)
[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 (3.515847ms)
✔ a rechecked head says where its new run is: building on which machine (1.064987ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.835384ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.692231ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.650741ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (7.022403ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.270484ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (2.070413ms)
✔ a repository as the mesh clones it is told from another (0.446887ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (6.23803ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (49.769028ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (28.157071ms)
✔ gitea_api still writes what an author writes (6.673507ms)
✔ gitea_branch_protection_set only strengthens (15.693681ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (54.8597ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (146.552539ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (15.222934ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.33561ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.203063ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (54.91745ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (19.879789ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.286753ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.053384ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.06362ms)
✔ a repository owner or name is never another path (0.269074ms)
✔ the judges, without a forge (0.358395ms)
✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.241134ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.87328ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.334547ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.186823ms)
✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (86.66558ms)
✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (22.647603ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (35.627401ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.666611ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.270101ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.302645ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.973478ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (71.653879ms)
✔ a pass pages until it reaches back past its floor, and no further (31.765559ms)
✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.008102ms)
✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (9.050626ms)
✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.666505ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.535953ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.662021ms)
✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.910885ms)
✔ another branch's body carries no merge whitelist (0.295955ms)
✔ the rule main is put right in the forge's own words: the body a setup sends carries no status checks of its own (1.347122ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (15.61342ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.030067ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.61108ms)
✔ no verdict sets warning on a required check; only a person's decision fails (0.882375ms)
✔ a commit status is set on the commit, under the merge check's context (48.948105ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.572699ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.346796ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (2.879656ms)
[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.47395ms)
✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.416246ms)
✔ a verdict without siblings is said exactly as before (an older controller) (0.795063ms)
✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.999766ms)
✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (4.079226ms)
✔ a sibling closed unmerged puts the member's verdict out of date (0.462827ms)
✔ a move of the sibling's base puts it out of date, naming both commits (0.418937ms)
✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (1.503084ms)
✔ a merge of another head, or onto another base, is a move (1.602126ms)
✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.960685ms)
✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.682221ms)
✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (1.153609ms)
✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (1.007316ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.628655ms)
✔ a consumer that asks to read joins the readers, who write no package (0.437447ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.542729ms)
✔ a publisher is made and put on the package team, as before (0.226224ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (46.582396ms)
✔ second start: reuses the kept token, mints nothing (29.865969ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (37.631853ms)
✔ 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.889024ms)
✔ 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.854321ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (25.300366ms)
✔ concurrent first calls share one mint (14.413611ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (16.86414ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (94.509925ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (5.320265ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (14.294819ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.746172ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (19.767977ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.184616ms)
✔ nothing to mint with and no token: the client says what is missing (0.162193ms)
[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-fa2x1C/state/token
✔ the tools register once there is a way to a token, and the first call mints it (27.508311ms)
[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-Zyl8do/state/token
✔ a pull request can be closed, read and cleaned up through the tools (34.554764ms)
[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-zmJzt2/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (72.823437ms)
[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-YpV03g/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (60.133184ms)
[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-GLb1M3/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 (43.739391ms)
[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-RAOuH8/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 (42.841547ms)
[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-205eFO/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (37.694354ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.707362ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.865944ms)
[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-FeFodG/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (36.736459ms)
[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 (27.503821ms)
[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-ig9Uw0/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 (44.479543ms)
[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-lMOo0i/state/token
✔ a team another process made between the look and the create is found, not a failure (37.979869ms)
[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-XjmbPU/state/token
✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (34.27575ms)
[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-SLeAIh/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) (33.463706ms)
✔ 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) (12.441209ms)
[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 (3.680699ms)
✔ 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.462623ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (4.818128ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.518688ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.600099ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.337756ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.351906ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.536799ms)
✔ a signed event is published on the bus and acted on once (69.101458ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (34.87779ms)
✔ an event and the poll seeing the same head announce it once (0.546209ms)
✔ each forge event is named in the module's stream and trimmed (0.555458ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.387192ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.157479ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (3.842321ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.718202ms)
✔ the receiver tells what it verified and what it refused (11.985342ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.150019ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.548149ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.581859ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.266195ms)
ℹ tests 151
ℹ suites 0
ℹ pass 151
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 812.948162
```
mesh/repo-check head 94f65cccd9c1 merged into trunk a7d526838ab0 beside mesh-controller#368@0f21fe78 — pass: its merge-check.sh passed, each part in its to…
Merge check at 94f65ccc
Judged: head 94f65cccd9c1 merged into trunk a7d526838ab0; read beside it: mesh-controller 19f1ecdd64a2, mesh-controller-main 7d80155d1636, mesh-host cd13f7726ef0, mesh-tools 7412c5b6c9c5
Siblings read beside it: novox/mesh-controller#3680f21fe7888be merged onto 7d80155d1636 (main); judged by sibling novox/mesh-controller#368 merge 19f1ecdd64a2
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)
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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
tier 0: build-agent, gitea, mesh-delivery
ace: receives build-agent
g14: receives build-agent
novox: receives build-agent, gitea, mesh-delivery
shanks: receives build-agent
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
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
grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
read by no module's build: modules/mesh-delivery/cmd/mesh-delivery/bus_test.go, modules/mesh-delivery/cmd/mesh-delivery/events_test.go, modules/mesh-delivery/cmd/mesh-delivery/fakes_test.go, modules/mesh-delivery/cmd/mesh-delivery/holder_test.go, modules/mesh-delivery/cmd/mesh-delivery/manifest_test.go, modules/mesh-delivery/cmd/mesh-delivery/markready_test.go, modules/mesh-delivery/cmd/mesh-delivery/pullkey_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_source_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_test.go, modules/mesh-delivery/cmd/mesh-delivery/unchecked_events_test.go
Run by the build seat on shanks as build-1792240766367660601 (builds --log build-1792240766367660601).
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.923542ms)
✔ the newest status of each context is kept, with what it said, when and by whom (23.639471ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.75123ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.308704ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.952553ms)
✔ a note line is appended once, as the forge's user, in its own repository (18.101139ms)
✔ a note that cannot be written is said, never read as written (0.484887ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.786131ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (21.878159ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.959012ms)
[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 (2.306211ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.658009ms)
✔ a start heard after the verdict writes nothing (0.74067ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.71266ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.099145ms)
[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 (2.903509ms)
✔ a rechecked head says where its new run is: building on which machine (1.029624ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.821581ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.866572ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.639649ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (9.029089ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.314994ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (1.806264ms)
✔ a repository as the mesh clones it is told from another (0.322794ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.27945ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (48.198276ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (27.770466ms)
✔ gitea_api still writes what an author writes (8.891488ms)
✔ gitea_branch_protection_set only strengthens (19.172053ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (56.401984ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (156.527965ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.300498ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (15.440484ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (28.692338ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.741642ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (21.982611ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.699352ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.281266ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.724991ms)
✔ a repository owner or name is never another path (0.315204ms)
✔ the judges, without a forge (0.368405ms)
✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.270793ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.867425ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.626704ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.453522ms)
✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (87.742857ms)
✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (22.979773ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (35.928604ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.612828ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.356285ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.428401ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.652901ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (65.556494ms)
✔ a pass pages until it reaches back past its floor, and no further (28.568377ms)
✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.554973ms)
✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (8.633973ms)
✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.352257ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.305433ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.406275ms)
✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.848811ms)
✔ another branch's body carries no merge whitelist (0.279143ms)
✔ 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.353984ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (17.817415ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.048054ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.716839ms)
✔ no verdict sets warning on a required check; only a person's decision fails (2.143328ms)
✔ a commit status is set on the commit, under the merge check's context (50.297964ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.682499ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.424796ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.094881ms)
[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.488152ms)
✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.430156ms)
✔ a verdict without siblings is said exactly as before (an older controller) (0.871681ms)
✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.971573ms)
✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (3.543136ms)
✔ a sibling closed unmerged puts the member's verdict out of date (0.527157ms)
✔ a move of the sibling's base puts it out of date, naming both commits (0.429236ms)
✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (0.621818ms)
✔ a merge of another head, or onto another base, is a move (0.862181ms)
✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.77723ms)
✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.680608ms)
✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (1.269786ms)
✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (0.294784ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.739983ms)
✔ a consumer that asks to read joins the readers, who write no package (0.547587ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.615878ms)
✔ a publisher is made and put on the package team, as before (0.244493ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (39.40543ms)
✔ second start: reuses the kept token, mints nothing (29.205025ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (34.427955ms)
✔ 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 (34.81574ms)
✔ 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.153491ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (22.580728ms)
✔ concurrent first calls share one mint (13.338556ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (17.339088ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (86.588643ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (8.442852ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (18.99858ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.992493ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (23.177145ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.517703ms)
✔ nothing to mint with and no token: the client says what is missing (0.183592ms)
[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-xNt9cy/state/token
✔ the tools register once there is a way to a token, and the first call mints it (31.072951ms)
[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-hO3fhK/state/token
✔ a pull request can be closed, read and cleaned up through the tools (39.816885ms)
[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-KWl8kP/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (94.681599ms)
[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-OKzSCS/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (61.215608ms)
[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-s8ZWDo/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 (44.027111ms)
[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-E6CHLa/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 (43.068428ms)
[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-W5Oqhe/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (37.342093ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.76266ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.859611ms)
[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-PY0yQX/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (37.723428ms)
[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 (24.535424ms)
[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-D5AZ5B/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 (45.312939ms)
[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-dMw8A2/state/token
✔ a team another process made between the look and the create is found, not a failure (36.496232ms)
[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-5QVFrr/state/token
✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (34.351693ms)
[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-PbgJGW/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) (36.052836ms)
✔ 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) (13.804193ms)
[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.570604ms)
✔ 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.445449ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.07992ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.472896ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.520717ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.301574ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.307974ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.489647ms)
✔ a signed event is published on the bus and acted on once (67.914987ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (37.89415ms)
✔ an event and the poll seeing the same head announce it once (0.565467ms)
✔ each forge event is named in the module's stream and trimmed (0.673989ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.567011ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.344118ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (5.378661ms)
✔ a forge that makes no system webhook is said, never taken as heard (1.001804ms)
✔ the receiver tells what it verified and what it refused (11.433551ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.458099ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.606358ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.527337ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.241683ms)
ℹ tests 151
ℹ suites 0
ℹ pass 151
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 844.160658
**Merge check** at `94f65ccc`
Judged: head `94f65cccd9c1` merged into trunk `a7d526838ab0`; read beside it: mesh-controller `19f1ecdd64a2`, mesh-controller-main `7d80155d1636`, mesh-host `cd13f7726ef0`, mesh-tools `7412c5b6c9c5`
Siblings read beside it: novox/mesh-controller#368 `0f21fe7888be` merged onto `7d80155d1636` (main); judged by sibling novox/mesh-controller#368 merge 19f1ecdd64a2
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)
- `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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
- tier 0: build-agent, gitea, mesh-delivery
- ace: receives build-agent
- g14: receives build-agent
- novox: receives build-agent, gitea, mesh-delivery
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- 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
- grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
- read by no module's build: modules/mesh-delivery/cmd/mesh-delivery/bus_test.go, modules/mesh-delivery/cmd/mesh-delivery/events_test.go, modules/mesh-delivery/cmd/mesh-delivery/fakes_test.go, modules/mesh-delivery/cmd/mesh-delivery/holder_test.go, modules/mesh-delivery/cmd/mesh-delivery/manifest_test.go, modules/mesh-delivery/cmd/mesh-delivery/markready_test.go, modules/mesh-delivery/cmd/mesh-delivery/pullkey_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_source_test.go, modules/mesh-delivery/cmd/mesh-delivery/timers_test.go, modules/mesh-delivery/cmd/mesh-delivery/unchecked_events_test.go
Run by the build seat on shanks as `build-1792240766367660601` (`builds --log build-1792240766367660601`).
```
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.923542ms)
✔ the newest status of each context is kept, with what it said, when and by whom (23.639471ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.75123ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.308704ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.952553ms)
✔ a note line is appended once, as the forge's user, in its own repository (18.101139ms)
✔ a note that cannot be written is said, never read as written (0.484887ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.786131ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (21.878159ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.959012ms)
[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 (2.306211ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.658009ms)
✔ a start heard after the verdict writes nothing (0.74067ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.71266ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.099145ms)
[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 (2.903509ms)
✔ a rechecked head says where its new run is: building on which machine (1.029624ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.821581ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.866572ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.639649ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (9.029089ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.314994ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (1.806264ms)
✔ a repository as the mesh clones it is told from another (0.322794ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.27945ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (48.198276ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (27.770466ms)
✔ gitea_api still writes what an author writes (8.891488ms)
✔ gitea_branch_protection_set only strengthens (19.172053ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (56.401984ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (156.527965ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.300498ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (15.440484ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (28.692338ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.741642ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (21.982611ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.699352ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.281266ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.724991ms)
✔ a repository owner or name is never another path (0.315204ms)
✔ the judges, without a forge (0.368405ms)
✔ main's protection is never weakened in its push or its merge whitelist (issue 948, ADR 0398 §2) (0.270793ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.867425ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.626704ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.453522ms)
✔ gitea_merge_pull_request merges a verdict that read siblings only with its group passed and each sibling head reviewed (87.742857ms)
✔ a verdict put out of date by a sibling's move is refused in its own words, and asked again once (22.979773ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (35.928604ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.612828ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.356285ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.428401ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.652901ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (65.556494ms)
✔ a pass pages until it reaches back past its floor, and no further (28.568377ms)
✔ gitea_branch_protection_get answers every rule, read with the administrator account the tools' token cannot read with (27.554973ms)
✔ gitea_branch_protection_get answers the rule for a branch, null when it has none (8.633973ms)
✔ gitea_branch_protection_set still gets the forge's refusal, as the tools' account (4.352257ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.305433ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.406275ms)
✔ main's body always refuses pushes and lets only the tools' account merge, whatever is asked (0.848811ms)
✔ another branch's body carries no merge whitelist (0.279143ms)
✔ 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.353984ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (17.817415ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.048054ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.716839ms)
✔ no verdict sets warning on a required check; only a person's decision fails (2.143328ms)
✔ a commit status is set on the commit, under the merge check's context (50.297964ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.682499ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.424796ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.094881ms)
[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.488152ms)
✔ a verdict that could not learn its siblings says so on the status and in the comment, and names none (0.430156ms)
✔ a verdict without siblings is said exactly as before (an older controller) (0.871681ms)
✔ the siblings a verdict names are kept per member, replaced by its next verdict, and removed by one that read none (0.971573ms)
✔ a sibling's new head puts both statuses of the member back to pending, with the reason, and runs nothing (3.543136ms)
✔ a sibling closed unmerged puts the member's verdict out of date (0.527157ms)
✔ a move of the sibling's base puts it out of date, naming both commits (0.429236ms)
✔ the sibling's own merge of the head read onto the base read does not, in either order of the two merges (0.621818ms)
✔ a merge of another head, or onto another base, is a move (0.862181ms)
✔ only the members whose verdict names the sibling are touched, and a member that moved on is dropped (0.77723ms)
✔ a trunk that moved tells the siblings' weighing; one first seen does not (0.680608ms)
✔ the group rule names every missing thing: the group status at each head read, and each sibling head's review (1.269786ms)
✔ a merged or closed member's entry is dropped, so a sibling's later push never re-pends its merged head (0.294784ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.739983ms)
✔ a consumer that asks to read joins the readers, who write no package (0.547587ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.615878ms)
✔ a publisher is made and put on the package team, as before (0.244493ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (39.40543ms)
✔ second start: reuses the kept token, mints nothing (29.205025ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (34.427955ms)
✔ 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 (34.81574ms)
✔ 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.153491ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (22.580728ms)
✔ concurrent first calls share one mint (13.338556ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (17.339088ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (86.588643ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (8.442852ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (18.99858ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.992493ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (23.177145ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.517703ms)
✔ nothing to mint with and no token: the client says what is missing (0.183592ms)
[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-xNt9cy/state/token
✔ the tools register once there is a way to a token, and the first call mints it (31.072951ms)
[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-hO3fhK/state/token
✔ a pull request can be closed, read and cleaned up through the tools (39.816885ms)
[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-KWl8kP/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (94.681599ms)
[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-OKzSCS/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (61.215608ms)
[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-s8ZWDo/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 (44.027111ms)
[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-E6CHLa/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 (43.068428ms)
[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-W5Oqhe/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (37.342093ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.76266ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.859611ms)
[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-PY0yQX/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (37.723428ms)
[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 (24.535424ms)
[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-D5AZ5B/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 (45.312939ms)
[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-dMw8A2/state/token
✔ a team another process made between the look and the create is found, not a failure (36.496232ms)
[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-5QVFrr/state/token
✔ setting the tools' account up puts main's rule right in every repository: no push, only the tools' account merges (issue 948) (34.351693ms)
[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-PbgJGW/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) (36.052836ms)
✔ 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) (13.804193ms)
[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.570604ms)
✔ 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.445449ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.07992ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.472896ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.520717ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.301574ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.307974ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.489647ms)
✔ a signed event is published on the bus and acted on once (67.914987ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (37.89415ms)
✔ an event and the poll seeing the same head announce it once (0.565467ms)
✔ each forge event is named in the module's stream and trimmed (0.673989ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.567011ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.344118ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (5.378661ms)
✔ a forge that makes no system webhook is said, never taken as heard (1.001804ms)
✔ the receiver tells what it verified and what it refused (11.433551ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.458099ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.606358ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.527337ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.241683ms)
ℹ tests 151
ℹ suites 0
ℹ pass 151
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 844.160658
```
Reviewed by review-events@shanks: Milestone 2 of design 47 (issues 975, 976, ADR 0406): approved after three review rounds at 52f70f4, same tree; both checks green. Marked through mesh-delivery.mark-ready (novox/hq issue 913).
ready at 94f65cccd9c1153e010ceffcca0977fdc9dfe2fc
Reviewed by review-events@shanks: Milestone 2 of design 47 (issues 975, 976, ADR 0406): approved after three review rounds at 52f70f4, same tree; both checks green. Marked through mesh-delivery.mark-ready (novox/hq issue 913).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
after: novox/mesh-controller
Design 47, milestone 2, one release with mesh-controller's pull request of the same branch name (ADR 0406). Issues 975 and 976. The controller goes first: mesh-delivery asks
delivery-checkwithready, which only the new controller takes, and the builder's read ofmesh-delivery_pullsis on the safe side while the bucket does not exist yet.gitea.forge.pull-request.labeled,.unlabeled,.commented), commit statuses (gitea.forge.status),checked,plan-movedand the operator's Approve (decided). Its 10 s tick, 30 s reconcile and minute forge poll are gone; the controller's walks and the open pulls are read once at start; time-bound steps are named one-shot timers, enforced by a source test.pulls(head, trunk, verdict) is written by compare-and-set; a ready mark asks its check withready. A merge announced again changes nothing. build-agent readsmesh-delivery.pulls.announce.ts), so the bus refuses a double;merged-announced.jsonandpulls-announced.jsonare gone, the catch-up'ssinceis a key in stateannounced.modules/gitea/internal/busdedup.Reviewed in three rounds by an independent reviewer; approved at this head.
Delivery
novox/mesh-catalog@94f65cccd9c1— delivered since 2026-10-17T12:59:36ZDelivery plan — builds build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
Group
feat/events-and-queues, in order: novox/mesh-controller@0f21fe7888 → novox/mesh-catalog@94f65cccd9Machines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Merge check at
52f70f42Judged: head
52f70f42b749merged into trunka7d526838ab0; read beside it: mesh-controllerf84becc6daee, mesh-controller-main7d80155d1636, mesh-hostcd13f7726ef0, mesh-tools7412c5b6c9c5Siblings read beside it: novox/mesh-controller#368
0f21fe7888bemerged onto7d80155d1636(main); judged by sibling novox/mesh-controller#368 merge f84becc6daeemesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
Run by the build seat on shanks as
build-1792239339496293222(builds --log build-1792239339496293222).Merge check at
94f65cccJudged: head
94f65cccd9c1merged into trunka7d526838ab0; read beside it: mesh-controller19f1ecdd64a2, mesh-controller-main7d80155d1636, mesh-hostcd13f7726ef0, mesh-tools7412c5b6c9c5Siblings read beside it: novox/mesh-controller#368
0f21fe7888bemerged onto7d80155d1636(main); judged by sibling novox/mesh-controller#368 merge 19f1ecdd64a2mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, gitea, mesh-delivery)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 build-agent, gitea, mesh-delivery → ace, g14, novox, shanks; no bus step
Run by the build seat on shanks as
build-1792240766367660601(builds --log build-1792240766367660601).ready at
94f65cccd9Reviewed by review-events@shanks: Milestone 2 of design 47 (issues 975, 976, ADR 0406): approved after three review rounds at
52f70f4, same tree; both checks green. Marked through mesh-delivery.mark-ready (novox/hq issue 913).