Make the forge's webhook a system one, and say whether its events are heard (hq issue 729) #309

Merged
mesh-admin merged 1 commits from fix/729-the-forge-events-are-heard-or-said into main 2026-10-14 21:40:45 +00:00
Contributor

Fixes the half of hq issue 729 that #304 got wrong: no forge event has reached the bus since #304 went live on 2026-10-14 23:05.

Cause (gitea v1.27.3 source, verified by the review): POST /admin/hooks makes a system hook only when config.is_system_webhook is "true"; #304 never sent it, so the forge made a default hook, copied only into repositories created later and fired for none of the existing ones. GET /admin/hooks defaults to system hooks, so every start made another default hook; editHook changes neither kind nor secret. The fault was silent: Pace.miss logged only while events were heard.

Change (modules/gitea):

  • ensureSystemHook sends the system flag, makes the new hook first and reads it back, then deletes every other hook at the receiver's url: old system hooks, default hooks and each repository's copy (all pages); a final listing must name exactly the new hook, or the start fails and says so.
  • A gitea_events tool: the receiver, the hook (id, url, events, kind, what it replaced), verified and refused deliveries with the last of each, the polls' pace, healthy or unhealthy and why.
  • Loud failure: no verified delivery for 10 minutes (from start or the last one) while the poll found at least two heads or merges no event announced logs "THE FORGE'S EVENTS ARE NOT HEARD" once per silent spell and makes the tool unhealthy; refusals since the last verified delivery make it unhealthy too.

Checks: merge-check-typescript.sh for gitea, 111 of 111 tests; merge-check.sh exit 0.

Reviewed independently before push: APPROVE at 2833a049aa.

Fixes the half of hq issue 729 that #304 got wrong: no forge event has reached the bus since #304 went live on 2026-10-14 23:05. **Cause** (gitea v1.27.3 source, verified by the review): `POST /admin/hooks` makes a system hook only when `config.is_system_webhook` is "true"; #304 never sent it, so the forge made a *default* hook, copied only into repositories created later and fired for none of the existing ones. `GET /admin/hooks` defaults to system hooks, so every start made another default hook; `editHook` changes neither kind nor secret. The fault was silent: `Pace.miss` logged only while events were heard. **Change** (modules/gitea): - `ensureSystemHook` sends the system flag, makes the new hook first and reads it back, then deletes every other hook at the receiver's url: old system hooks, default hooks and each repository's copy (all pages); a final listing must name exactly the new hook, or the start fails and says so. - A `gitea_events` tool: the receiver, the hook (id, url, events, kind, what it replaced), verified and refused deliveries with the last of each, the polls' pace, healthy or unhealthy and why. - Loud failure: no verified delivery for 10 minutes (from start or the last one) while the poll found at least two heads or merges no event announced logs "THE FORGE'S EVENTS ARE NOT HEARD" once per silent spell and makes the tool unhealthy; refusals since the last verified delivery make it unhealthy too. Checks: merge-check-typescript.sh for gitea, 111 of 111 tests; merge-check.sh exit 0. Reviewed independently before push: APPROVE at 2833a049aa0f2408fb6708b0fdfe89b77bfb77fb.
mesh-admin added 1 commit 2026-10-14 21:29:39 +00:00
Make the forge's webhook a system one, and say whether its events are heard (hq issue 729)
mesh/merge-gate head 2833a049aa0f merged into trunk c51357f8afd4 — pass: builds gitea → novox; no bus step; every machine composes with the change as it di…
mesh/repo-check head 2833a049aa0f merged into trunk c51357f8afd4 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
2833a049aa
POST /admin/hooks on gitea 1.27.3 makes a default webhook unless
config.is_system_webhook is "true": a default webhook fires for no existing
repository, and the system listing does not name it, so every start made
another and no event reached the receiver. Its edit never changes a secret
or a kind, so the new system webhook is made first and read back, then every
other hook at the receiver is deleted: system, default, and each
repository's copy of a default one, which would send its events twice.

gitea_events says where the receiver listens, the hook as ensured and what
it replaced, the verified and refused deliveries and the polls' pace. When
nothing verified for ten minutes, since the start or the last delivery,
while the poll found two heads or merges no event said, it is logged loudly
once per silent spell and answered unhealthy; so are deliveries refused
since the last verified one.
Author
Contributor

Delivery novox/mesh-catalog@2833a049aa0f — delivered since 2026-10-14T21:55:52Z

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 21:30 (new) → proposed (announced): novox/mesh-catalog#309's head announced
  • 2026-10-14 21:40 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-14 21:40 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
  • 2026-10-14 21:52 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 21:53 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 21:55 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@2833a049aa0f` — **delivered** since 2026-10-14T21:55:52Z **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 21:30 (new) → proposed (announced): novox/mesh-catalog#309's head announced - 2026-10-14 21:40 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-14 21:40 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail - 2026-10-14 21:52 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 21:53 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 21:55 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-admin added the mesh/ready label 2026-10-14 21:30:47 +00:00
Author
Contributor

ready at 2833a049aa (the operator's word, 2026-10-14; independent review APPROVE at this sha).

ready at 2833a049aa0f2408fb6708b0fdfe89b77bfb77fb (the operator's word, 2026-10-14; independent review APPROVE at this sha).
Author
Contributor

Merge check at 2833a049
Judged: head 2833a049aa0f merged into trunk 0f76b6e232a5; read beside it: mesh-controller 60cc9ec363df, mesh-controller-main 60cc9ec363df, 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 novox as build-1792013406200342873 (builds --log build-1792013406200342873).

--- the gate: gitea
gitea: ok, keeps data (valuable), ready: server by http /api/healthz on web expecting 200 every 30s
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T21:19:19Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-14T21:19:19Z

modules the change moves:
  - 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.011s
words: 12 retired words and 70 daemons' lines, none in the 47322 strings of 722 files
ok  	git.novox.be/novox/mesh-catalog/checks/agenthome	0.018s
agent-home: no archive and no file written into under the agent's home
ok  	git.novox.be/novox/mesh-catalog/checks/secrets	0.017s
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.017s
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.832s
testing modules/gitea
ok  	gitea/cmd/forge	1.838s
ok  	gitea/cmd/npm-registry	2.375s
?   	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 (9.73735ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (147.185883ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.935294ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (22.005945ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (25.042822ms)
✔ a ready comment by an account not listed is ignored, and said (0.483481ms)
✔ a ready comment older than the latest push, or than the label, never counts (61.813497ms)
[gitea] f cannot be read (gone); only mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (17.407376ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.868742ms)
✔ the newest status of each context is kept, with what it said, when and by whom (29.673761ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.603922ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.25405ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.816001ms)
✔ a note line is appended once, as the forge's user, in its own repository (10.739362ms)
✔ a note that cannot be written is said, never read as written (0.471471ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.743871ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (48.110278ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (9.89236ms)
[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.388543ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.652851ms)
✔ a start heard after the verdict writes nothing (0.482611ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.581861ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.945874ms)
[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 (4.266978ms)
✔ a rechecked head says where its new run is: building on which machine (1.052712ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.944512ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.733642ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.688701ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (13.672568ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.299371ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.398041ms)
✔ a repository as the mesh clones it is told from another (0.349091ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.495625ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (66.920808ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (35.617923ms)
✔ gitea_api still writes what an author writes (10.547792ms)
✔ gitea_branch_protection_set only strengthens (28.714229ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (53.15783ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (161.313791ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (18.202118ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.34351ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (26.360254ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.629925ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (22.144555ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.173419ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (5.676562ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (11.346043ms)
✔ a repository owner or name is never another path (0.459081ms)
✔ the judges, without a forge (0.396001ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.541563ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.370085ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.553502ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (42.209127ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.408681ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.048943ms)
✔ a lookup the forge refuses is thrown, not read as no module (6.523574ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.129474ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (97.15466ms)
✔ a pass pages until it reaches back past its floor, and no further (44.289131ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (9.997801ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (1.792614ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (14.72822ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.201103ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.742981ms)
✔ no verdict sets warning on a required check; only a person's decision fails (1.109853ms)
✔ a commit status is set on the commit, under the merge check's context (67.347758ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.609022ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.30415ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (4.345199ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.794674ms)
✔ a consumer that asks to read joins the readers, who write no package (0.502901ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.679731ms)
✔ a publisher is made and put on the package team, as before (0.235811ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (31.717755ms)
✔ second start: reuses the kept token, mints nothing (23.649049ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (311.882141ms)
✔ 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 (70.664906ms)
✔ 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 (149.672998ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (22.926257ms)
✔ concurrent first calls share one mint (14.260619ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (17.164745ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (83.889312ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (6.353883ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (16.907445ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.192742ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (212.768048ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.925126ms)
✔ nothing to mint with and no token: the client says what is missing (0.259331ms)
[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-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-aLX7Mz/state/token
✔ the tools register once there is a way to a token, and the first call mints it (13.731538ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-kX5fxW/state/token
✔ a pull request can be closed, read and cleaned up through the tools (24.56328ms)
[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 (1.905354ms)
✔ 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.249253ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (5.23778ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.3525ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.478661ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.294541ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.246181ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.396291ms)
✔ a signed event is published on the bus and acted on once (63.46498ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (28.231248ms)
✔ an event and the poll seeing the same head announce it once (0.459241ms)
✔ each forge event is named in the module's stream and trimmed (0.613281ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.157593ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.127622ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (3.670438ms)
✔ a forge that makes no system webhook is said, never taken as heard (0.921872ms)
✔ the receiver tells what it verified and what it refused (9.54808ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.395463ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.657961ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.439821ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.220301ms)
ℹ tests 111
ℹ suites 0
ℹ pass 111
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 1405.59899

Out of date when it landed: out of date: judged against trunk 0f76b6e232, the trunk is now c51357f8afd4; judged again on a recheck or when it is merged

**Merge check** at `2833a049` Judged: head `2833a049aa0f` merged into trunk `0f76b6e232a5`; read beside it: mesh-controller `60cc9ec363df`, mesh-controller-main `60cc9ec363df`, 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 novox as `build-1792013406200342873` (`builds --log build-1792013406200342873`). ``` --- the gate: gitea gitea: ok, keeps data (valuable), ready: server by http /api/healthz on web expecting 200 every 30s long-running resources without health: 0 files asking for a setting without saying whether it is trusted: 0 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T21:19:19Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-14T21:19:19Z modules the change moves: - 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.011s words: 12 retired words and 70 daemons' lines, none in the 47322 strings of 722 files ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.018s agent-home: no archive and no file written into under the agent's home ok git.novox.be/novox/mesh-catalog/checks/secrets 0.017s 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.017s 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.832s testing modules/gitea ok gitea/cmd/forge 1.838s ok gitea/cmd/npm-registry 2.375s ? 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 (9.73735ms) ✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (147.185883ms) ✔ a pull request or issue number that is not one is refused before the forge is asked (1.935294ms) ✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (22.005945ms) ✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (25.042822ms) ✔ a ready comment by an account not listed is ignored, and said (0.483481ms) ✔ a ready comment older than the latest push, or than the label, never counts (61.813497ms) [gitea] f cannot be read (gone); only mesh-admin may mark a pull request ready [gitea] f names no account that may mark a pull request ready; only mesh-admin may ✔ a long thread hides nothing: every page of the timeline is read (17.407376ms) ✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.868742ms) ✔ the newest status of each context is kept, with what it said, when and by whom (29.673761ms) ✔ a commit merges only when every required status is a success; a warning blocks (0.603922ms) ✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.25405ms) ✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.816001ms) ✔ a note line is appended once, as the forge's user, in its own repository (10.739362ms) ✔ a note that cannot be written is said, never read as written (0.471471ms) ✔ one view per pull request, found by its marker; only the mesh's statuses (0.743871ms) ✔ a build machine taking the check is said on the head: building on which machine, since the local time (48.110278ms) ✔ the verdict heard while a 'building' write is in flight lands last and stays (9.89236ms) [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.388543ms) ✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.652851ms) ✔ a start heard after the verdict writes nothing (0.482611ms) ✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.581861ms) ✔ an old run's verdict redelivered after a recheck's pending is not written over it (1.945874ms) [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 (4.266978ms) ✔ a rechecked head says where its new run is: building on which machine (1.052712ms) ✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.944512ms) ✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.733642ms) ✔ a recheck the forge does not take fails its caller, and the head is as it was (0.688701ms) ✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (13.672568ms) ✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.299371ms) ✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.398041ms) ✔ a repository as the mesh clones it is told from another (0.349091ms) ✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.495625ms) ✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (66.920808ms) ✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (35.617923ms) ✔ gitea_api still writes what an author writes (10.547792ms) ✔ gitea_branch_protection_set only strengthens (28.714229ms) ✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (53.15783ms) ✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (161.313791ms) ✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (18.202118ms) ✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.34351ms) ✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (26.360254ms) ✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (60.629925ms) ✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (22.144555ms) ✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.173419ms) ✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (5.676562ms) ✔ a rule named for a branch is not made weaker than the glob rule that guards it now (11.346043ms) ✔ a repository owner or name is never another path (0.459081ms) ✔ the judges, without a forge (0.396001ms) ✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (1.541563ms) ✔ gitea_api never approves a pull request nor requests changes: those are the operator's (2.370085ms) ✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.553502ms) ✔ every page of a pull request's files is read, though the forge caps a page at fifty (42.209127ms) ✔ a pass asks only the repositories that moved since the last look, every one before the first (0.408681ms) ✔ the directories above a merge's files that hold a module at the commit are said, the root never (16.048943ms) ✔ a lookup the forge refuses is thrown, not read as no module (6.523574ms) ✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (7.129474ms) ✔ a new head beyond the newest 20 open pull requests is announced, exactly once (97.15466ms) ✔ a pass pages until it reaches back past its floor, and no further (44.289131ms) ✔ the statuses asked for replace the rule's, and an empty list requires none (9.997801ms) ✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (1.792614ms) ✔ each open pull request's new head is announced once, and nothing closed or merged (14.72822ms) ✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.201103ms) ✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.742981ms) ✔ no verdict sets warning on a required check; only a person's decision fails (1.109853ms) ✔ a commit status is set on the commit, under the merge check's context (67.347758ms) ✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.609022ms) ✔ a head announced is pending in the build queue, since the local time it was announced (0.30415ms) ✔ the vault's token is used, a setting of it is ignored and said once, without its value (4.345199ms) ✔ a consumer that asks nothing, or write, publishes: the package team (1.794674ms) ✔ a consumer that asks to read joins the readers, who write no package (0.502901ms) ✔ a reader is made, put on the readers team, and taken off the package team (0.679731ms) ✔ a publisher is made and put on the package team, as before (0.235811ms) ✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (31.717755ms) ✔ second start: reuses the kept token, mints nothing (23.649049ms) ✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (311.882141ms) ✔ 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 (70.664906ms) ✔ 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 (149.672998ms) ✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (22.926257ms) ✔ concurrent first calls share one mint (14.260619ms) ✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (17.164745ms) ✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (83.889312ms) ✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (6.353883ms) ✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (16.907445ms) ✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.192742ms) ✔ a second process finds the token the first renewed, and reuses it instead of minting over it (212.768048ms) ✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.925126ms) ✔ nothing to mint with and no token: the client says what is missing (0.259331ms) [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-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-aLX7Mz/state/token ✔ the tools register once there is a way to a token, and the first call mints it (13.731538ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-kX5fxW/state/token ✔ a pull request can be closed, read and cleaned up through the tools (24.56328ms) [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 (1.905354ms) ✔ 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.249253ms) ✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (5.23778ms) ✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.3525ms) ✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.478661ms) ✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.294541ms) ✔ a verdict that landed out of date keeps its account on the pull request (0.246181ms) ✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.396291ms) ✔ a signed event is published on the bus and acted on once (63.46498ms) ✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (28.231248ms) ✔ an event and the poll seeing the same head announce it once (0.459241ms) ✔ each forge event is named in the module's stream and trimmed (0.613281ms) ✔ the webhook is made a system webhook, which the forge sends every repository's events to (1.157593ms) ✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (1.127622ms) ✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (3.670438ms) ✔ a forge that makes no system webhook is said, never taken as heard (0.921872ms) ✔ the receiver tells what it verified and what it refused (9.54808ms) ✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (1.395463ms) ✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.657961ms) ✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.439821ms) ✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (0.220301ms) ℹ tests 111 ℹ suites 0 ℹ pass 111 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 1405.59899 ``` **Out of date when it landed**: out of date: judged against trunk 0f76b6e232a5, the trunk is now c51357f8afd4; judged again on a recheck or when it is merged
Author
Contributor

Merge check at 2833a049
Judged: head 2833a049aa0f merged into trunk c51357f8afd4; read beside it: mesh-controller 60cc9ec363df, mesh-controller-main 60cc9ec363df, 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 novox as build-1792013765773952572 (builds --log build-1792013765773952572).

--- the gate: gitea
gitea: ok, keeps data (valuable), ready: server by http /api/healthz on web expecting 200 every 30s
long-running resources without health: 0
files asking for a setting without saying whether it is trusted: 0
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T21:19:19Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-14T21:19:19Z

modules the change moves:
  - 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.030s
words: 12 retired words and 70 daemons' lines, none in the 47374 strings of 722 files
ok  	git.novox.be/novox/mesh-catalog/checks/agenthome	0.016s
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.405s
testing modules/gitea
ok  	gitea/cmd/forge	1.529s
ok  	gitea/cmd/npm-registry	1.868s
?   	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 (97.963234ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (255.218022ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.817674ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (102.990985ms)
✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (97.257783ms)
✔ a ready comment by an account not listed is ignored, and said (3.440917ms)
✔ a ready comment older than the latest push, or than the label, never counts (157.425508ms)
[gitea] f cannot be read (gone); only mesh-admin may mark a pull request ready
[gitea] f names no account that may mark a pull request ready; only mesh-admin may
✔ a long thread hides nothing: every page of the timeline is read (35.562275ms)
✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.842911ms)
✔ the newest status of each context is kept, with what it said, when and by whom (138.379818ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.695992ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.29458ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (9.574461ms)
✔ a note line is appended once, as the forge's user, in its own repository (41.558516ms)
✔ a note that cannot be written is said, never read as written (0.869652ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (2.555496ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (151.656186ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (50.002594ms)
[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.032172ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (43.010169ms)
✔ a start heard after the verdict writes nothing (0.555001ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (7.248965ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (22.986138ms)
[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 (4.419529ms)
✔ a rechecked head says where its new run is: building on which machine (22.101676ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (1.898595ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (8.603138ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.658872ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (22.338636ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.30146ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.382611ms)
✔ a repository as the mesh clones it is told from another (7.338065ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (10.931613ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (206.51635ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (64.171484ms)
✔ gitea_api still writes what an author writes (16.157654ms)
✔ gitea_branch_protection_set only strengthens (30.792225ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (132.961467ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (284.412643ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (29.634872ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (27.431807ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (52.380379ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (81.188139ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (33.618521ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (5.796242ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (10.461252ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (16.031183ms)
✔ a repository owner or name is never another path (0.390981ms)
✔ the judges, without a forge (0.482071ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (4.232029ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (5.731642ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (6.302993ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (90.448219ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.679881ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (38.902141ms)
✔ a lookup the forge refuses is thrown, not read as no module (23.522029ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (34.016161ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (307.0088ms)
✔ a pass pages until it reaches back past its floor, and no further (57.47285ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (39.010961ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.435731ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (74.937076ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.039802ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (6.259023ms)
✔ no verdict sets warning on a required check; only a person's decision fails (9.71132ms)
✔ a commit status is set on the commit, under the merge check's context (155.171113ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (1.312603ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.298741ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (67.890041ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.768633ms)
✔ a consumer that asks to read joins the readers, who write no package (0.509721ms)
✔ a reader is made, put on the readers team, and taken off the package team (2.597606ms)
✔ a publisher is made and put on the package team, as before (0.256091ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (94.278517ms)
✔ second start: reuses the kept token, mints nothing (65.370416ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (197.696542ms)
✔ 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 (68.411422ms)
✔ 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 (71.195009ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (46.394477ms)
✔ concurrent first calls share one mint (27.948238ms)
✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (26.594986ms)
✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (104.684038ms)
✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (21.661455ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (27.174827ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.289783ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (31.316275ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (5.482262ms)
✔ nothing to mint with and no token: the client says what is missing (0.30656ms)
[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-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-p4XuXw/state/token
✔ the tools register once there is a way to a token, and the first call mints it (14.831171ms)
[gitea] no token kept — minting one
[gitea] the forge already holds a token named "mesh-tools" — replacing it
[gitea] minted a token for "mesh-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-dys1VX/state/token
✔ a pull request can be closed, read and cleaned up through the tools (38.625251ms)
[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.008725ms)
✔ a trunk's move puts an open head's merge check out of date, naming both commits, unless a check is on its way (7.457046ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (14.17729ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (8.158667ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (7.992296ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.583992ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.32953ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.505821ms)
✔ a signed event is published on the bus and acted on once (259.424831ms)
✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (64.090783ms)
✔ an event and the poll seeing the same head announce it once (5.746042ms)
✔ each forge event is named in the module's stream and trimmed (0.870462ms)
✔ the webhook is made a system webhook, which the forge sends every repository's events to (5.07934ms)
✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (5.718491ms)
✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (10.317871ms)
✔ a forge that makes no system webhook is said, never taken as heard (3.061787ms)
✔ the receiver tells what it verified and what it refused (21.564125ms)
✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (2.695466ms)
✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.573881ms)
✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.400811ms)
✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (2.216085ms)
ℹ tests 111
ℹ suites 0
ℹ pass 111
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 2051.017156
**Merge check** at `2833a049` Judged: head `2833a049aa0f` merged into trunk `c51357f8afd4`; read beside it: mesh-controller `60cc9ec363df`, mesh-controller-main `60cc9ec363df`, 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 novox as `build-1792013765773952572` (`builds --log build-1792013765773952572`). ``` --- the gate: gitea gitea: ok, keeps data (valuable), ready: server by http /api/healthz on web expecting 200 every 30s long-running resources without health: 0 files asking for a setting without saying whether it is trusted: 0 1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here judging 1 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T21:19:19Z says they run composing every machine as the mesh is the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued composing every machine with the change the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose) judged by controller development build against the facts of 2026-10-14T21:19:19Z modules the change moves: - 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.030s words: 12 retired words and 70 daemons' lines, none in the 47374 strings of 722 files ok git.novox.be/novox/mesh-catalog/checks/agenthome 0.016s 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.405s testing modules/gitea ok gitea/cmd/forge 1.529s ok gitea/cmd/npm-registry 1.868s ? 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 (97.963234ms) ✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (255.218022ms) ✔ a pull request or issue number that is not one is refused before the forge is asked (1.817674ms) ✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (102.990985ms) ✔ a pull request's ready mark is read: the label and the head the newest listed ready comment names (97.257783ms) ✔ a ready comment by an account not listed is ignored, and said (3.440917ms) ✔ a ready comment older than the latest push, or than the label, never counts (157.425508ms) [gitea] f cannot be read (gone); only mesh-admin may mark a pull request ready [gitea] f names no account that may mark a pull request ready; only mesh-admin may ✔ a long thread hides nothing: every page of the timeline is read (35.562275ms) ✔ the accounts that may mark are the setting's, and only the mesh's own when it cannot be read (0.842911ms) ✔ the newest status of each context is kept, with what it said, when and by whom (138.379818ms) ✔ a commit merges only when every required status is a success; a warning blocks (0.695992ms) ✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.29458ms) ✔ a commit is contained in another when the compare finds nothing on it the other lacks (9.574461ms) ✔ a note line is appended once, as the forge's user, in its own repository (41.558516ms) ✔ a note that cannot be written is said, never read as written (0.869652ms) ✔ one view per pull request, found by its marker; only the mesh's statuses (2.555496ms) ✔ a build machine taking the check is said on the head: building on which machine, since the local time (151.656186ms) ✔ the verdict heard while a 'building' write is in flight lands last and stays (50.002594ms) [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.032172ms) ✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (43.010169ms) ✔ a start heard after the verdict writes nothing (0.555001ms) ✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (7.248965ms) ✔ an old run's verdict redelivered after a recheck's pending is not written over it (22.986138ms) [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 (4.419529ms) ✔ a rechecked head says where its new run is: building on which machine (22.101676ms) ✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (1.898595ms) ✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (8.603138ms) ✔ a recheck the forge does not take fails its caller, and the head is as it was (0.658872ms) ✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (22.338636ms) ✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.30146ms) ✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.382611ms) ✔ a repository as the mesh clones it is told from another (7.338065ms) ✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (10.931613ms) ✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (206.51635ms) ✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (64.171484ms) ✔ gitea_api still writes what an author writes (16.157654ms) ✔ gitea_branch_protection_set only strengthens (30.792225ms) ✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (132.961467ms) ✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (284.412643ms) ✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (29.634872ms) ✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (27.431807ms) ✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (52.380379ms) ✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (81.188139ms) ✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (33.618521ms) ✔ gitea_api sends the path it judged, and refuses moving a pull request's base (5.796242ms) ✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (10.461252ms) ✔ a rule named for a branch is not made weaker than the glob rule that guards it now (16.031183ms) ✔ a repository owner or name is never another path (0.390981ms) ✔ the judges, without a forge (0.482071ms) ✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (4.232029ms) ✔ gitea_api never approves a pull request nor requests changes: those are the operator's (5.731642ms) ✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (6.302993ms) ✔ every page of a pull request's files is read, though the forge caps a page at fifty (90.448219ms) ✔ a pass asks only the repositories that moved since the last look, every one before the first (0.679881ms) ✔ the directories above a merge's files that hold a module at the commit are said, the root never (38.902141ms) ✔ a lookup the forge refuses is thrown, not read as no module (23.522029ms) ✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (34.016161ms) ✔ a new head beyond the newest 20 open pull requests is announced, exactly once (307.0088ms) ✔ a pass pages until it reaches back past its floor, and no further (57.47285ms) ✔ the statuses asked for replace the rule's, and an empty list requires none (39.010961ms) ✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.435731ms) ✔ each open pull request's new head is announced once, and nothing closed or merged (74.937076ms) ✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.039802ms) ✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (6.259023ms) ✔ no verdict sets warning on a required check; only a person's decision fails (9.71132ms) ✔ a commit status is set on the commit, under the merge check's context (155.171113ms) ✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (1.312603ms) ✔ a head announced is pending in the build queue, since the local time it was announced (0.298741ms) ✔ the vault's token is used, a setting of it is ignored and said once, without its value (67.890041ms) ✔ a consumer that asks nothing, or write, publishes: the package team (1.768633ms) ✔ a consumer that asks to read joins the readers, who write no package (0.509721ms) ✔ a reader is made, put on the readers team, and taken off the package team (2.597606ms) ✔ a publisher is made and put on the package team, as before (0.256091ms) ✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (94.278517ms) ✔ second start: reuses the kept token, mints nothing (65.370416ms) ✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (197.696542ms) ✔ 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 (68.411422ms) ✔ 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 (71.195009ms) ✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (46.394477ms) ✔ concurrent first calls share one mint (27.948238ms) ✔ two tokens hold the name (two mints raced): both are deleted by their id, and one fresh token is minted (26.594986ms) ✔ two processes on one kept file, both without a token, mint once: the second waits for the first and reuses it (104.684038ms) ✔ a lock left by a process that died mid-mint is taken over, not waited on for ever (21.661455ms) ✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (27.174827ms) ✔ one process shares one source per kept file — the watcher and the tools never renew against each other (1.289783ms) ✔ a second process finds the token the first renewed, and reuses it instead of minting over it (31.316275ms) ✔ a configured token wins, and is reported rather than minted over when the forge rejects it (5.482262ms) ✔ nothing to mint with and no token: the client says what is missing (0.30656ms) [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-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-p4XuXw/state/token ✔ the tools register once there is a way to a token, and the first call mints it (14.831171ms) [gitea] no token kept — minting one [gitea] the forge already holds a token named "mesh-tools" — replacing it [gitea] minted a token for "mesh-admin" (write:repository, write:issue, read:user), kept at /tmp/gitea-dys1VX/state/token ✔ a pull request can be closed, read and cleaned up through the tools (38.625251ms) [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.008725ms) ✔ a trunk's move puts an open head's merge check out of date, naming both commits, unless a check is on its way (7.457046ms) ✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (14.17729ms) ✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (8.158667ms) ✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (7.992296ms) ✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.583992ms) ✔ a verdict that landed out of date keeps its account on the pull request (0.32953ms) ✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.505821ms) ✔ a signed event is published on the bus and acted on once (259.424831ms) ✔ an unsigned event, or one whose signature does not verify, is refused and published nowhere (64.090783ms) ✔ an event and the poll seeing the same head announce it once (5.746042ms) ✔ each forge event is named in the module's stream and trimmed (0.870462ms) ✔ the webhook is made a system webhook, which the forge sends every repository's events to (5.07934ms) ✔ default webhooks left at the receiver by an earlier start are replaced by one system webhook (5.718491ms) ✔ a repository's copy of a default webhook at the receiver is deleted, on every page, and said (10.317871ms) ✔ a forge that makes no system webhook is said, never taken as heard (3.061787ms) ✔ the receiver tells what it verified and what it refused (21.564125ms) ✔ a forge that never delivers is said loudly once, and unhealthy, when the poll finds what no event said (2.695466ms) ✔ a forge whose events stop after one delivery is said loudly once per silent spell, and refusals are unhealthy (0.573881ms) ✔ without a receiver by configuration the polls alone watch, healthy; a receiver that failed is unhealthy (0.400811ms) ✔ the polls look every 30 s until events are heard, every five minutes while they are, and 30 s again on a miss (2.216085ms) ℹ tests 111 ℹ suites 0 ℹ pass 111 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 2051.017156 ```
mesh-admin merged commit c79337579d into main 2026-10-14 21:40:45 +00:00
mesh-admin deleted branch fix/729-the-forge-events-are-heard-or-said 2026-10-14 21:40:45 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#309