The tools' own account. The gitea module creates mesh-tools, a plain account, using the admin login. It adds the account to the team mesh-tools in the novox organisation. The team can write code, issues and pull requests in every repository of the organisation. It cannot administer a repository or create one.
The token. The tools' token is minted for mesh-tools. The admin account's old token with that name is deleted only after the new token is kept, so the tools always have a working token. A token kept from before for the admin account is replaced at the next start.
Who does what. The admin login is still used to set the forge up: minting tokens, the account and team, the system webhook and the package provisioner. The Go forge bundle (gitea_commit and the reads) now sends the tools' token instead of the admin login. It never sends a token that was kept for another account.
Protection rules. The forge shows protection rules only to a repository administrator. So these now read a branch's protection from the branch itself:
the merge tool
the list of open pull requests
the delivery view
a gitea_api file write
the repository check's warning facts
gitea_commit
New branches. For a branch that does not exist yet, the forge itself refuses a commit that a protection rule would block.
ready-authors. The default is now mesh-tools mesh-admin jschoubben. Pull requests that mesh-admin marked ready before this change still count as ready.
Not in this change
novox/hq issue 759 covers the npm-registry bundle, which still logs in as the admin account. Its retention job reads every repository's lockfiles.
What the tools can no longer do
The forge now refuses these to the tools:
gitea_branch_protection_set
gitea_branch_protection_get without a branch
gitea_delete_repo
creating a repository in the organisation
Private repositories outside novox are no longer visible to the tools.
Checks run locally
merge-check.sh is green, run with a mesh-controller built from main.
merge-check-typescript.sh with MESH_CHECK_MODULES=gitea passes 122 of 122 tests.
The new tests fail on the code before the fix.
Proven only after delivery
The token is minted for mesh-tools, and the account and team exist on the forge.
Merges, commit statuses, comments and pull requests work under that token.
The forge refuses a protection edit made with that token.
The admin account's old mesh-tools token is gone.
gitea_commit from the Go bundle works with the kept token.
Fixes novox/hq issue 469: ADR 0365 (the forge's identities), clause 2.
## What it does
- **The tools' own account.** The gitea module creates `mesh-tools`, a plain account, using the admin login. It adds the account to the team `mesh-tools` in the `novox` organisation. The team can write code, issues and pull requests in every repository of the organisation. It cannot administer a repository or create one.
- **The token.** The tools' token is minted for `mesh-tools`. The admin account's old token with that name is deleted only after the new token is kept, so the tools always have a working token. A token kept from before for the admin account is replaced at the next start.
- **Who does what.** The admin login is still used to set the forge up: minting tokens, the account and team, the system webhook and the package provisioner. The Go `forge` bundle (gitea_commit and the reads) now sends the tools' token instead of the admin login. It never sends a token that was kept for another account.
- **Protection rules.** The forge shows protection rules only to a repository administrator. So these now read a branch's protection from the branch itself:
- the merge tool
- the list of open pull requests
- the delivery view
- a `gitea_api` file write
- the repository check's warning facts
- `gitea_commit`
- **New branches.** For a branch that does not exist yet, the forge itself refuses a commit that a protection rule would block.
- **ready-authors.** The default is now `mesh-tools mesh-admin jschoubben`. Pull requests that mesh-admin marked ready before this change still count as ready.
## Not in this change
- novox/hq issue 759 covers the npm-registry bundle, which still logs in as the admin account. Its retention job reads every repository's lockfiles.
## What the tools can no longer do
The forge now refuses these to the tools:
- `gitea_branch_protection_set`
- `gitea_branch_protection_get` without a branch
- `gitea_delete_repo`
- creating a repository in the organisation
Private repositories outside `novox` are no longer visible to the tools.
## Checks run locally
- `merge-check.sh` is green, run with a mesh-controller built from main.
- `merge-check-typescript.sh` with `MESH_CHECK_MODULES=gitea` passes 122 of 122 tests.
- The new tests fail on the code before the fix.
## Proven only after delivery
- The token is minted for `mesh-tools`, and the account and team exist on the forge.
- Merges, commit statuses, comments and pull requests work under that token.
- The forge refuses a protection edit made with that token.
- The admin account's old `mesh-tools` token is gone.
- `gitea_commit` from the Go bundle works with the kept token.
mesh/merge-gate head 48f0662fdea2 merged into trunk 5ce2d07c7f47 — pass: builds gitea → novox; no bus step; every machine composes with the change as it di…
mesh/repo-check head 48f0662fdea2 merged into trunk 5ce2d07c7f47 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
The tools' token reached everything the administrator may do, so their boundary rested on their own guards.
The gitea module now makes a plain account, mesh-tools, with the admin login, gives it write on code, issues
and pull requests of every novox repository through one team, and mints the tools' token for it; the admin's
old token goes only after the new one is kept. The Go forge bundle sends that token, never the admin login.
The forge shows protection rules only to a repository administrator, so every check reads a branch's
protection from the branch. mesh-admin stays a ready author, so marks made before the move keep counting.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deliverynovox/mesh-catalog@48f0662fdea2 — delivered since 2026-10-14T22:54:44Z
Delivery plan — builds gitea → novox; no bus step
tier 0: gitea
novox: receives gitea
gitea provides npm-package-registry, git: its consumers are sent again after it
gitea keeps data (ADR 0233): what it holds is backed up before it moves
Machines
gitea on every machine running it: sent
Transitions
2026-10-14 22:28 (new) → proposed (announced): novox/mesh-catalog#311's head announced
2026-10-14 22:31 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
2026-10-14 22:31 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-14 22:35 ready → proposed (trunk moved): out of date: judged against trunk 7913b56d66, the trunk is now 5ce2d07c7f47; judged again on a recheck or when it is merged
2026-10-14 22:49 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
2026-10-14 22:49 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-14 22:51 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-14 22:51 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
2026-10-14 22:54 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@48f0662fdea2` — **delivered** since 2026-10-14T22:54:44Z
**Delivery plan** — builds gitea → novox; no bus step
- tier 0: gitea
- novox: receives gitea
- gitea provides npm-package-registry, git: its consumers are sent again after it
- gitea keeps data (ADR 0233): what it holds is backed up before it moves
**Machines**
- gitea on every machine running it: sent
**Transitions**
- 2026-10-14 22:28 (new) → proposed (announced): novox/mesh-catalog#311's head announced
- 2026-10-14 22:31 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
- 2026-10-14 22:31 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-14 22:35 ready → proposed (trunk moved): out of date: judged against trunk 7913b56d66f7, the trunk is now 5ce2d07c7f47; judged again on a recheck or when it is merged
- 2026-10-14 22:49 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
- 2026-10-14 22:49 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-14 22:51 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-14 22:51 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path — not a failed walk that carried a later commit for it, after which it waits for a walk of its own commit
- 2026-10-14 22:54 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`.
The tree of 48f0662f (parent 7913b56d) is identical to 8a9f48fd, which I reviewed locally: both trees are 29467b41. git diff 8a9f48fd 48f0662f is empty.
Checks I ran on the reviewed tree:
merge-check.sh passed. MESH_GATE was a mesh-controller built from origin/main 3ed08c34.
merge-check-typescript.sh with MESH_CHECK_MODULES=gitea: 122 of 122 tests pass.
gofmt, go vet and go test on modules/gitea are clean.
With each fix's non-test code reverted, its new tests fail.
Findings from the first review, and how each is fixed:
A gitea_api file write still read the protection rules list, which the forge refuses the tools' account. Fixed: it now reads the protection the forge applies to the target branch. The fake forge in the tests now refuses the list to an account that is not an administrator, and 2 tests fail without the fix.
The repository check's facts (repoCheckFacts) still read the rules list. Fixed: they moved to pulls.ts and read the base branch's applied protection. Proven by putting the old read back behind the new export: the test then fails on its assertion, not only at import.
The npm-registry bundle still logs in as the administrator. Split to issue 759.
The administrator's old token was deleted before the new token was minted. Fixed: the new token is minted and kept first, then the old one is deleted, and a failed delete is retried at the next start. 2 tests fail without the fix.
The account and team setup duplicated GiteaAdmin's code. Fixed: it now uses GiteaAdmin's ensureUser (plain), ensureTeamWith and addUserToTeam, which also handle a team made by another process at the same moment. Its test fails without the fix.
Pull requests marked ready by mesh-admin before the move would stop counting. Fixed: the readyAuthors fallback is now mesh-tools plus the admin account, and the ready-authors default is mesh-tools mesh-admin jschoubben. Each change has a test that fails without it.
Nit, not blocking: the test "the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator" now checks that the administrator is included, so its name says the opposite of what it checks.
Verdict: APPROVE at 48f0662fdea295ac81609eb3bda01a8655e535aa
The tree of 48f0662f (parent 7913b56d) is identical to 8a9f48fd, which I reviewed locally: both trees are 29467b41. `git diff 8a9f48fd 48f0662f` is empty.
Checks I ran on the reviewed tree:
- merge-check.sh passed. MESH_GATE was a mesh-controller built from origin/main 3ed08c34.
- merge-check-typescript.sh with MESH_CHECK_MODULES=gitea: 122 of 122 tests pass.
- gofmt, go vet and go test on modules/gitea are clean.
- With each fix's non-test code reverted, its new tests fail.
Findings from the first review, and how each is fixed:
1. A gitea_api file write still read the protection rules list, which the forge refuses the tools' account. Fixed: it now reads the protection the forge applies to the target branch. The fake forge in the tests now refuses the list to an account that is not an administrator, and 2 tests fail without the fix.
2. The repository check's facts (repoCheckFacts) still read the rules list. Fixed: they moved to pulls.ts and read the base branch's applied protection. Proven by putting the old read back behind the new export: the test then fails on its assertion, not only at import.
3. The npm-registry bundle still logs in as the administrator. Split to issue 759.
4. The administrator's old token was deleted before the new token was minted. Fixed: the new token is minted and kept first, then the old one is deleted, and a failed delete is retried at the next start. 2 tests fail without the fix.
5. The account and team setup duplicated GiteaAdmin's code. Fixed: it now uses GiteaAdmin's ensureUser (plain), ensureTeamWith and addUserToTeam, which also handle a team made by another process at the same moment. Its test fails without the fix.
6. Pull requests marked ready by mesh-admin before the move would stop counting. Fixed: the readyAuthors fallback is now mesh-tools plus the admin account, and the ready-authors default is `mesh-tools mesh-admin jschoubben`. Each change has a test that fails without it.
Nit, not blocking: the test "the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator" now checks that the administrator is included, so its name says the opposite of what it checks.
Merge check at 48f0662f
Judged: head 48f0662fdea2 merged into trunk 7913b56d66f7; read beside it: mesh-controller 3ed08c3464e3, mesh-controller-main 3ed08c3464e3, mesh-host 1a2677882380, mesh-tools 5d70eb3e35a9
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)
mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
Delivery plan — builds gitea → novox; no bus step
tier 0: gitea
novox: receives gitea
gitea provides npm-package-registry, git: its consumers are sent again after it
gitea keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as build-1792016934549860413 (builds --log build-1792016934549860413).
- gitea (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): gitea
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 47534 strings of 727 files
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.014s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.012s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 62, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.239s
testing modules/gitea
ok gitea/cmd/forge 1.507s
ok gitea/cmd/npm-registry 1.822s
? gitea/internal/forgehttp [no test files]
--- its merge-check-typescript.sh (typescript toolchain)
type-checking modules/gitea (TypeScript)
testing modules/gitea (TypeScript)
✔ a pull request opened after the margin is looked at, by its own update time (13.413491ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (56.268363ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.394732ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (12.064959ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (19.904672ms)
✔ a ready comment by an account not listed is ignored, and said (0.416397ms)
✔ a ready comment older than the latest push, or than the label, never counts (46.296717ms)
[gitea] f cannot be read (gone); only mesh-tools, mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-tools, mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (16.734003ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.820353ms)
✔ the newest status of each context is kept, with what it said, when and by whom (22.596084ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.881514ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.330155ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (1.168459ms)
✔ a note line is appended once, as the forge's user, in its own repository (9.294536ms)
✔ a note that cannot be written is said, never read as written (0.411747ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.719641ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (22.831869ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.599603ms)
[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.028366ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.66428ms)
✔ a start heard after the verdict writes nothing (0.454127ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.488698ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.070957ms)
[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.197395ms)
✔ a rechecked head says where its new run is: building on which machine (0.966015ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.771182ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.688401ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.64568ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.488556ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.281325ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (1.775148ms)
✔ a repository as the mesh clones it is told from another (0.437037ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.383525ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (47.778601ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (26.202482ms)
✔ gitea_api still writes what an author writes (10.115848ms)
✔ gitea_branch_protection_set only strengthens (16.652862ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (48.432791ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (152.038327ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (18.609032ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (13.250708ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.767685ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.139024ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (20.34148ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.459084ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.054071ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.211968ms)
✔ a repository owner or name is never another path (0.283305ms)
✔ the judges, without a forge (0.447447ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.817569ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.401158ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (3.997243ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (28.470297ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.501918ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (11.228987ms)
✔ a lookup the forge refuses is thrown, not read as no module (4.271416ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (6.695846ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (56.73936ms)
✔ a pass pages until it reaches back past its floor, and no further (27.959859ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.685422ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.432167ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (18.152975ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.126368ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.816883ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.149458ms)
✔ a commit status is set on the commit, under the merge check's context (45.326622ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.6424ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.412037ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.038178ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.674127ms)
✔ a consumer that asks to read joins the readers, who write no package (0.483987ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.519798ms)
✔ a publisher is made and put on the package team, as before (0.212244ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (40.216322ms)
✔ second start: reuses the kept token, mints nothing (26.306672ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (27.790826ms)
✔ 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 (30.283076ms)
✔ 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 (26.11984ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (24.744239ms)
✔ concurrent first calls share one mint (13.31571ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (16.168963ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (87.358831ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (7.793272ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (16.429008ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.821593ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (22.128498ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.626642ms)
✔ nothing to mint with and no token: the client says what is missing (0.201494ms)
[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-u1bBez/state/token
✔ the tools register once there is a way to a token, and the first call mints it (27.732125ms)
[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-ANcyU0/state/token
✔ a pull request can be closed, read and cleaned up through the tools (38.935721ms)
[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-DT48Tx/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (52.402953ms)
[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-OSdGDJ/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (43.130827ms)
[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-1E8cJC/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 (38.181679ms)
[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-qnQlrO/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 (33.681239ms)
[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-MuGm8G/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (28.892734ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.806593ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.951105ms)
[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-ZXta8L/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (28.214413ms)
[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 (15.90549ms)
[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-FJjNPa/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 (33.11865ms)
[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-j4khXK/state/token
✔ a team another process made between the look and the create is found, not a failure (27.036445ms)
[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.100143ms)
✔ 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.697817ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.858458ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.425417ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.513708ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.270264ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.246813ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.477908ms)
✔ a signed event is published on the bus and acted on once (63.840342ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (28.486147ms)
✔ an event and the poll seeing the same head announce it once (0.455487ms)
✔ each forge event is named in the module's stream and trimmed (0.573589ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.27008ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.223399ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.302698ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.705681ms)
✔ the receiver tells what it verified and what it refused (7.919125ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.406571ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.566939ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.473457ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.245454ms)
ℹ tests 122
ℹ suites 0
ℹ pass 122
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 675.650078
**Merge check** at `48f0662f`
Judged: head `48f0662fdea2` merged into trunk `7913b56d66f7`; read beside it: mesh-controller `3ed08c3464e3`, mesh-controller-main `3ed08c3464e3`, mesh-host `1a2677882380`, mesh-tools `5d70eb3e35a9`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
**Delivery plan** — builds gitea → novox; no bus step
- tier 0: gitea
- novox: receives gitea
- gitea provides npm-package-registry, git: its consumers are sent again after it
- gitea keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as `build-1792016934549860413` (`builds --log build-1792016934549860413`).
```
- gitea (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): gitea
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 47534 strings of 727 files
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.014s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.012s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 62, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.239s
testing modules/gitea
ok gitea/cmd/forge 1.507s
ok gitea/cmd/npm-registry 1.822s
? gitea/internal/forgehttp [no test files]
--- its merge-check-typescript.sh (typescript toolchain)
type-checking modules/gitea (TypeScript)
testing modules/gitea (TypeScript)
✔ a pull request opened after the margin is looked at, by its own update time (13.413491ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (56.268363ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.394732ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (12.064959ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (19.904672ms)
✔ a ready comment by an account not listed is ignored, and said (0.416397ms)
✔ a ready comment older than the latest push, or than the label, never counts (46.296717ms)
[gitea] f cannot be read (gone); only mesh-tools, mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-tools, mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (16.734003ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.820353ms)
✔ the newest status of each context is kept, with what it said, when and by whom (22.596084ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.881514ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.330155ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (1.168459ms)
✔ a note line is appended once, as the forge's user, in its own repository (9.294536ms)
✔ a note that cannot be written is said, never read as written (0.411747ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.719641ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (22.831869ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (6.599603ms)
[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.028366ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.66428ms)
✔ a start heard after the verdict writes nothing (0.454127ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.488698ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.070957ms)
[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.197395ms)
✔ a rechecked head says where its new run is: building on which machine (0.966015ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.771182ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.688401ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.64568ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.488556ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.281325ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (1.775148ms)
✔ a repository as the mesh clones it is told from another (0.437037ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (5.383525ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (47.778601ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (26.202482ms)
✔ gitea_api still writes what an author writes (10.115848ms)
✔ gitea_branch_protection_set only strengthens (16.652862ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (48.432791ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (152.038327ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (18.609032ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (13.250708ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.767685ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.139024ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (20.34148ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.459084ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.054071ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.211968ms)
✔ a repository owner or name is never another path (0.283305ms)
✔ the judges, without a forge (0.447447ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.817569ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.401158ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (3.997243ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (28.470297ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.501918ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (11.228987ms)
✔ a lookup the forge refuses is thrown, not read as no module (4.271416ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (6.695846ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (56.73936ms)
✔ a pass pages until it reaches back past its floor, and no further (27.959859ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.685422ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.432167ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (18.152975ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.126368ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.816883ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.149458ms)
✔ a commit status is set on the commit, under the merge check's context (45.326622ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.6424ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.412037ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.038178ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.674127ms)
✔ a consumer that asks to read joins the readers, who write no package (0.483987ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.519798ms)
✔ a publisher is made and put on the package team, as before (0.212244ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (40.216322ms)
✔ second start: reuses the kept token, mints nothing (26.306672ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (27.790826ms)
✔ 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 (30.283076ms)
✔ 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 (26.11984ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (24.744239ms)
✔ concurrent first calls share one mint (13.31571ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (16.168963ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (87.358831ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (7.793272ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (16.429008ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.821593ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (22.128498ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.626642ms)
✔ nothing to mint with and no token: the client says what is missing (0.201494ms)
[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-u1bBez/state/token
✔ the tools register once there is a way to a token, and the first call mints it (27.732125ms)
[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-ANcyU0/state/token
✔ a pull request can be closed, read and cleaned up through the tools (38.935721ms)
[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-DT48Tx/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (52.402953ms)
[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-OSdGDJ/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (43.130827ms)
[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-1E8cJC/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 (38.181679ms)
[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-qnQlrO/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 (33.681239ms)
[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-MuGm8G/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (28.892734ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.806593ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.951105ms)
[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-ZXta8L/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (28.214413ms)
[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 (15.90549ms)
[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-FJjNPa/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 (33.11865ms)
[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-j4khXK/state/token
✔ a team another process made between the look and the create is found, not a failure (27.036445ms)
[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.100143ms)
✔ 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.697817ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.858458ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.425417ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.513708ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.270264ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.246813ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.477908ms)
✔ a signed event is published on the bus and acted on once (63.840342ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (28.486147ms)
✔ an event and the poll seeing the same head announce it once (0.455487ms)
✔ each forge event is named in the module's stream and trimmed (0.573589ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.27008ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.223399ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.302698ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.705681ms)
✔ the receiver tells what it verified and what it refused (7.919125ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.406571ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.566939ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.473457ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.245454ms)
ℹ tests 122
ℹ suites 0
ℹ pass 122
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 675.650078
```
Merge check at 48f0662f
Judged: head 48f0662fdea2 merged into trunk 5ce2d07c7f47; read beside it: mesh-controller 4cc6b6c566a5, mesh-controller-main 4cc6b6c566a5, mesh-host 198fd34ec497, mesh-tools 5d70eb3e35a9
mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)
mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
Delivery plan — builds gitea → novox; no bus step
tier 0: gitea
novox: receives gitea
gitea provides npm-package-registry, git: its consumers are sent again after it
gitea keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as build-1792018006881611906 (builds --log build-1792018006881611906).
- gitea (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): gitea
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 47568 strings of 727 files
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.013s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.013s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 62, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.211s
testing modules/gitea
ok gitea/cmd/forge 1.512s
ok gitea/cmd/npm-registry 1.807s
? gitea/internal/forgehttp [no test files]
--- its merge-check-typescript.sh (typescript toolchain)
type-checking modules/gitea (TypeScript)
testing modules/gitea (TypeScript)
✔ a pull request opened after the margin is looked at, by its own update time (13.85387ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (61.507732ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.567844ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (14.241637ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (20.253587ms)
✔ a ready comment by an account not listed is ignored, and said (0.535688ms)
✔ a ready comment older than the latest push, or than the label, never counts (49.909297ms)
[gitea] f cannot be read (gone); only mesh-tools, mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-tools, mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (17.896222ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.790812ms)
✔ the newest status of each context is kept, with what it said, when and by whom (21.136631ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.720701ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.318095ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.994595ms)
✔ a note line is appended once, as the forge's user, in its own repository (19.14079ms)
✔ a note that cannot be written is said, never read as written (0.523108ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.789112ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (18.895956ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (9.589435ms)
[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.139687ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.69651ms)
✔ a start heard after the verdict writes nothing (0.518488ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.692001ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.981175ms)
[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.57538ms)
✔ a rechecked head says where its new run is: building on which machine (0.931884ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.752951ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.64722ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.889693ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.608865ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.297994ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.457107ms)
✔ a repository as the mesh clones it is told from another (0.321025ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.95925ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (51.144526ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (26.451121ms)
✔ gitea_api still writes what an author writes (8.608571ms)
✔ gitea_branch_protection_set only strengthens (16.707363ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (50.976903ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (159.149923ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.22363ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (12.922497ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (26.37261ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (56.155751ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (20.217258ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.97292ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.091587ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.742998ms)
✔ a repository owner or name is never another path (0.381076ms)
✔ the judges, without a forge (0.353325ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (2.514338ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (1.212779ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (4.788973ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (27.08278ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.457277ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (13.758309ms)
✔ a lookup the forge refuses is thrown, not read as no module (4.453547ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (5.589475ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (53.51601ms)
✔ a pass pages until it reaches back past its floor, and no further (25.848223ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (10.935716ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.413286ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (11.452604ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.147167ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.751802ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.088847ms)
✔ a commit status is set on the commit, under the merge check's context (44.052377ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.621759ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.392596ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (2.923375ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.645035ms)
✔ a consumer that asks to read joins the readers, who write no package (0.474007ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.593089ms)
✔ a publisher is made and put on the package team, as before (0.251584ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (35.065341ms)
✔ second start: reuses the kept token, mints nothing (27.077761ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (27.624679ms)
✔ 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 (30.088427ms)
✔ 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 (28.098385ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (25.71981ms)
✔ concurrent first calls share one mint (13.102009ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (15.987122ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (98.737197ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (7.957321ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (15.738788ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.788942ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (21.156571ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.233015ms)
✔ nothing to mint with and no token: the client says what is missing (0.161642ms)
[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-uJrF2c/state/token
✔ the tools register once there is a way to a token, and the first call mints it (24.058095ms)
[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-VfVGWh/state/token
✔ a pull request can be closed, read and cleaned up through the tools (34.828758ms)
[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-ARcNzQ/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (52.576007ms)
[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-WypBAe/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (45.076584ms)
[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-ZU9o1o/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 (35.868054ms)
[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-Au1Ob5/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 (32.160628ms)
[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-gc0TLR/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (25.473936ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.70592ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.880174ms)
[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-CTvqNM/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (27.346245ms)
[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 (12.885335ms)
[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-3I6PIi/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 (34.551664ms)
[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-MrmqSB/state/token
✔ a team another process made between the look and the create is found, not a failure (27.970014ms)
[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.377245ms)
✔ 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.463622ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.273315ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (1.436111ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.66436ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.356735ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.346465ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (1.414742ms)
✔ a signed event is published on the bus and acted on once (59.512092ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (30.599264ms)
✔ an event and the poll seeing the same head announce it once (0.488828ms)
✔ each forge event is named in the module's stream and trimmed (0.716511ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.562443ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.30561ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.689321ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.867103ms)
✔ the receiver tells what it verified and what it refused (10.174084ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.432212ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.569689ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.412466ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.309325ms)
ℹ tests 122
ℹ suites 0
ℹ pass 122
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 672.06137
**Merge check** at `48f0662f`
Judged: head `48f0662fdea2` merged into trunk `5ce2d07c7f47`; read beside it: mesh-controller `4cc6b6c566a5`, mesh-controller-main `4cc6b6c566a5`, mesh-host `198fd34ec497`, mesh-tools `5d70eb3e35a9`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
**Delivery plan** — builds gitea → novox; no bus step
- tier 0: gitea
- novox: receives gitea
- gitea provides npm-package-registry, git: its consumers are sent again after it
- gitea keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as `build-1792018006881611906` (`builds --log build-1792018006881611906`).
```
- gitea (changed)
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 1 module(s) in 1 tier(s): gitea
--- its merge-check.sh (go toolchain)
ok git.novox.be/novox/mesh-catalog/checks/words 0.003s
words: 12 retired words and 70 daemons' lines, none in the 47568 strings of 727 files
ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.013s
agent-home: no archive and no file written into under the agent's home
ok git.novox.be/novox/mesh-catalog/checks/secrets 0.013s
secrets: no setting can replace a vault secret in a mergeable file (1 known, nested)
ok git.novox.be/novox/mesh-catalog/checks/paths 0.006s
paths: no module names another's state on its machine (3 known, listed to go)
long-running resources without health: 62, as kept
ok git.novox.be/novox/mesh-catalog/checks/deps 0.211s
testing modules/gitea
ok gitea/cmd/forge 1.512s
ok gitea/cmd/npm-registry 1.807s
? gitea/internal/forgehttp [no test files]
--- its merge-check-typescript.sh (typescript toolchain)
type-checking modules/gitea (TypeScript)
testing modules/gitea (TypeScript)
✔ a pull request opened after the margin is looked at, by its own update time (13.85387ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (61.507732ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.567844ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (14.241637ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (20.253587ms)
✔ a ready comment by an account not listed is ignored, and said (0.535688ms)
✔ a ready comment older than the latest push, or than the label, never counts (49.909297ms)
[gitea] f cannot be read (gone); only mesh-tools, mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-tools, mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (17.896222ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.790812ms)
✔ the newest status of each context is kept, with what it said, when and by whom (21.136631ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.720701ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.318095ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.994595ms)
✔ a note line is appended once, as the forge's user, in its own repository (19.14079ms)
✔ a note that cannot be written is said, never read as written (0.523108ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.789112ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (18.895956ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (9.589435ms)
[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.139687ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.69651ms)
✔ a start heard after the verdict writes nothing (0.518488ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.692001ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.981175ms)
[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.57538ms)
✔ a rechecked head says where its new run is: building on which machine (0.931884ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.752951ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.64722ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.889693ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.608865ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.297994ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.457107ms)
✔ a repository as the mesh clones it is told from another (0.321025ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.95925ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (51.144526ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (26.451121ms)
✔ gitea_api still writes what an author writes (8.608571ms)
✔ gitea_branch_protection_set only strengthens (16.707363ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (50.976903ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (159.149923ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.22363ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (12.922497ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (26.37261ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (56.155751ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (20.217258ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (3.97292ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (7.091587ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (13.742998ms)
✔ a repository owner or name is never another path (0.381076ms)
✔ the judges, without a forge (0.353325ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (2.514338ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (1.212779ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (4.788973ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (27.08278ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.457277ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (13.758309ms)
✔ a lookup the forge refuses is thrown, not read as no module (4.453547ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (5.589475ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (53.51601ms)
✔ a pass pages until it reaches back past its floor, and no further (25.848223ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (10.935716ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.413286ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (11.452604ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.147167ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.751802ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.088847ms)
✔ a commit status is set on the commit, under the merge check's context (44.052377ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.621759ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.392596ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (2.923375ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.645035ms)
✔ a consumer that asks to read joins the readers, who write no package (0.474007ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.593089ms)
✔ a publisher is made and put on the package team, as before (0.251584ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (35.065341ms)
✔ second start: reuses the kept token, mints nothing (27.077761ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (27.624679ms)
✔ 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 (30.088427ms)
✔ 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 (28.098385ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (25.71981ms)
✔ concurrent first calls share one mint (13.102009ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (15.987122ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (98.737197ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (7.957321ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (15.738788ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.788942ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (21.156571ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.233015ms)
✔ nothing to mint with and no token: the client says what is missing (0.161642ms)
[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-uJrF2c/state/token
✔ the tools register once there is a way to a token, and the first call mints it (24.058095ms)
[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-VfVGWh/state/token
✔ a pull request can be closed, read and cleaned up through the tools (34.828758ms)
[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-ARcNzQ/state/token
✔ the tools' token is minted for an account of their own, which is no administrator and writes through its team (52.576007ms)
[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-WypBAe/state/token
✔ with the tools' token, the forge writes a status and refuses a protection edit — the forge itself, not a guard (45.076584ms)
[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-ZU9o1o/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 (35.868054ms)
[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-Au1Ob5/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 (32.160628ms)
[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-gc0TLR/state/token
✔ a tools' account someone made an administrator is made a plain account again at the next mint (25.473936ms)
✔ the tools never act as the administrator: MESH_GITEA_TOOLS_USER naming it is refused (0.70592ms)
✔ the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator (0.880174ms)
[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-CTvqNM/state/token
✔ a repository check said as a warning on a head without merge-check.sh reads the base's protection from the branch (27.346245ms)
[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 (12.885335ms)
[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-3I6PIi/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 (34.551664ms)
[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-MrmqSB/state/token
✔ a team another process made between the look and the create is found, not a failure (27.970014ms)
[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.377245ms)
✔ 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.463622ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (6.273315ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (1.436111ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.66436ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.356735ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.346465ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (1.414742ms)
✔ a signed event is published on the bus and acted on once (59.512092ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (30.599264ms)
✔ an event and the poll seeing the same head announce it once (0.488828ms)
✔ each forge event is named in the module's stream and trimmed (0.716511ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.562443ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.30561ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (4.689321ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.867103ms)
✔ the receiver tells what it verified and what it refused (10.174084ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.432212ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.569689ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.412466ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.309325ms)
ℹ tests 122
ℹ suites 0
ℹ pass 122
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 672.06137
```
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.
Fixes novox/hq issue 469: ADR 0365 (the forge's identities), clause 2.
What it does
mesh-tools, a plain account, using the admin login. It adds the account to the teammesh-toolsin thenovoxorganisation. The team can write code, issues and pull requests in every repository of the organisation. It cannot administer a repository or create one.mesh-tools. The admin account's old token with that name is deleted only after the new token is kept, so the tools always have a working token. A token kept from before for the admin account is replaced at the next start.forgebundle (gitea_commit and the reads) now sends the tools' token instead of the admin login. It never sends a token that was kept for another account.gitea_apifile writegitea_commitmesh-tools mesh-admin jschoubben. Pull requests that mesh-admin marked ready before this change still count as ready.Not in this change
What the tools can no longer do
The forge now refuses these to the tools:
gitea_branch_protection_setgitea_branch_protection_getwithout a branchgitea_delete_repoPrivate repositories outside
novoxare no longer visible to the tools.Checks run locally
merge-check.shis green, run with a mesh-controller built from main.merge-check-typescript.shwithMESH_CHECK_MODULES=giteapasses 122 of 122 tests.Proven only after delivery
mesh-tools, and the account and team exist on the forge.mesh-toolstoken is gone.gitea_commitfrom the Go bundle works with the kept token.Delivery
novox/mesh-catalog@48f0662fdea2— delivered since 2026-10-14T22:54:44ZDelivery plan — builds gitea → novox; no bus step
Machines
Transitions
7913b56d66, the trunk is now 5ce2d07c7f47; judged again on a recheck or when it is mergedThe commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Verdict: APPROVE at
48f0662fdeThe tree of
48f0662f(parent7913b56d) is identical to 8a9f48fd, which I reviewed locally: both trees are29467b41.git diff 8a9f48fd 48f0662fis empty.Checks I ran on the reviewed tree:
Findings from the first review, and how each is fixed:
mesh-tools mesh-admin jschoubben. Each change has a test that fails without it.Nit, not blocking: the test "the manifest hands the tools their account, and the merge-ready authors name it rather than the administrator" now checks that the administrator is included, so its name says the opposite of what it checks.
Merge check at
48f0662fJudged: head
48f0662fdea2merged into trunk7913b56d66f7; read beside it: mesh-controller3ed08c3464e3, mesh-controller-main3ed08c3464e3, mesh-host1a2677882380, mesh-tools5d70eb3e35a9mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)Delivery plan — builds gitea → novox; no bus step
Run by the build seat on shanks as
build-1792016934549860413(builds --log build-1792016934549860413).Merge check at
48f0662fJudged: head
48f0662fdea2merged into trunk5ce2d07c7f47; read beside it: mesh-controller4cc6b6c566a5, mesh-controller-main4cc6b6c566a5, mesh-host198fd34ec497, mesh-tools5d70eb3e35a9mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: gitea)mesh/repo-check: PASS — its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)Delivery plan — builds gitea → novox; no bus step
Run by the build seat on shanks as
build-1792018006881611906(builds --log build-1792018006881611906).