Give the build agent a judge cache of its own (issue 704, ADR 0358) #297

Open
mesh-admin wants to merge 1 commits from fix/704-release-branch-judge into main
Contributor

What changed

This is the catalogue half of hq issue 704 and ADR 0358 §6 (accepted, version 11). It delivers together with mesh-controller#329. Both pull requests use the branch name fix/704-release-branch-judge, which makes them one delivery group.

  • modules/build-agent/module.json declares a judge-cache directory resource beside the workspace. It is kept as the agent's own data (class cache), mounted into the agent at the same path (${dir:judge-cache}:${dir:judge-cache}), and handed to it as MESH_JUDGE_CACHE. The same path matters because the container runtime resolves a judge build's mounts on the host.
  • mesh-builder (in #329) mounts this directory into judge builds only, never into a check's containers. It holds three things:
    • the one Go cache every judge is built with, so judge builds stay warm;
    • the release-branch judges, kept one per head;
    • a fresh home for each check's judge build, removed afterwards.
  • Before this, the judge's home came from a temporary directory inside the agent. That left about 235 MB on the host's /tmp on every check, and kept judges were lost on every redeploy.
  • modules/gitea/test/trunk.test.ts adds a test for ADR 0358 §5: a release branch's move puts every open pull request into it out of date, and the merge tool's rule reads it the same way. This already holds. The forge's holder weighs the head of every base branch of an open pull request, not only main (weighTrunks), and the merge tool compares the verdict's trunk commit with the head of the pull request's own base. The test now keeps that behaviour from changing.

Order

This deploys with #329's mesh-builder.

  • An agent with this manifest and an older mesh-builder ignores MESH_JUDGE_CACHE.
  • A new mesh-builder without this manifest builds each judge cold under the check's own root, which nothing of a pull request can write before the build. It is correct, only slower.

Proof

  • modules/build-agent TestTheJudgeCacheIsADirectoryOfItsOwnMountedAtTheSamePath checks the directory, the data entry, the env line, the same-path mount, and that the cache is not inside the workspace.
  • modules/gitea adds the release-branch out-of-date test. All 8 trunk tests pass with Node's runner.
  • merge-check.sh (the Go part, with the gate's module check of every manifest) passes with MESH_CHECK_MODULES=build-agent,gitea.

Reviewer's checklist

  1. It does what ADR 0358 §6 asks of the build agent, and nothing else.
  2. The new tests prove it.
  3. Nothing composes differently for any other module.
  4. No secret, and no database change.
  5. No new manifest field: it uses existing resource, data and volume forms.
  6. The checks pass, as above.
  7. Words follow the glossary.
## What changed This is the catalogue half of hq issue 704 and ADR 0358 §6 (accepted, version 11). **It delivers together with mesh-controller#329.** Both pull requests use the branch name `fix/704-release-branch-judge`, which makes them one delivery group. - `modules/build-agent/module.json` declares a `judge-cache` directory resource beside the workspace. It is kept as the agent's own data (class `cache`), mounted into the agent at the same path (`${dir:judge-cache}:${dir:judge-cache}`), and handed to it as `MESH_JUDGE_CACHE`. The same path matters because the container runtime resolves a judge build's mounts on the host. - mesh-builder (in #329) mounts this directory into judge builds only, never into a check's containers. It holds three things: - the one Go cache every judge is built with, so judge builds stay warm; - the release-branch judges, kept one per head; - a fresh home for each check's judge build, removed afterwards. - Before this, the judge's home came from a temporary directory inside the agent. That left about 235 MB on the host's /tmp on every check, and kept judges were lost on every redeploy. - `modules/gitea/test/trunk.test.ts` adds a test for ADR 0358 §5: a release branch's move puts every open pull request into it out of date, and the merge tool's rule reads it the same way. **This already holds.** The forge's holder weighs the head of every base branch of an open pull request, not only main (`weighTrunks`), and the merge tool compares the verdict's trunk commit with the head of the pull request's own base. The test now keeps that behaviour from changing. ## Order This deploys with #329's mesh-builder. - An agent with this manifest and an older mesh-builder ignores `MESH_JUDGE_CACHE`. - A new mesh-builder without this manifest builds each judge cold under the check's own root, which nothing of a pull request can write before the build. It is correct, only slower. ## Proof - `modules/build-agent` `TestTheJudgeCacheIsADirectoryOfItsOwnMountedAtTheSamePath` checks the directory, the data entry, the env line, the same-path mount, and that the cache is not inside the workspace. - `modules/gitea` adds the release-branch out-of-date test. All 8 trunk tests pass with Node's runner. - `merge-check.sh` (the Go part, with the gate's module check of every manifest) passes with `MESH_CHECK_MODULES=build-agent,gitea`. ## Reviewer's checklist 1. It does what ADR 0358 §6 asks of the build agent, and nothing else. 2. The new tests prove it. 3. Nothing composes differently for any other module. 4. No secret, and no database change. 5. No new manifest field: it uses existing resource, data and volume forms. 6. The checks pass, as above. 7. Words follow the glossary.
mesh-admin added 1 commit 2026-10-14 13:53:43 +00:00
Give the build agent a judge cache of its own, and prove a release branch's move puts its verdicts out of date
mesh/delivery-group group fix/704-release-branch-judge rejected: its order contradicts itself: novox/mesh-catalog@47ef2644aca1 and novox/mesh-controller@2bee29…
mesh/merge-gate head 47ef2644aca1 merged into trunk cb7bf94a77ea — pass: builds build-agent, gitea → ace, g14, novox, shanks; no bus step; every machine co…
mesh/repo-check head 47ef2644aca1 merged into trunk cb7bf94a77ea — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery ready: it delivers once merged
47ef2644ac
Every merge check's judge is built where no pull request can reach (hq ADR 0358 §6,
issue 704): the build agent needs a directory beside its workspace, mounted at the
same path, that only judge builds mount. ADR 0358 §5 already holds in the forge's
holder; this test keeps it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Author
Contributor

Delivery novox/mesh-catalog@47ef2644aca1 — ready since 2026-10-14T13:57:21Z

Delivery plan — builds build-agent, gitea → ace, g14, novox, shanks; no bus step

  • tier 0: build-agent
  • tier 1: gitea
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives build-agent, gitea
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • gitea provides npm-package-registry, git: its consumers are sent again after it
  • gitea keeps data (ADR 0233): what it holds is backed up before it moves
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine

Group fix/704-release-branch-judge, in order:

Transitions

  • 2026-10-14 13:54 (new) → proposed (announced): novox/mesh-catalog#297's head announced
  • 2026-10-14 13:57 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit
  • 2026-10-14 13:57 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail

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

<!-- mesh-delivery:view --> **Delivery** `novox/mesh-catalog@47ef2644aca1` — **ready** since 2026-10-14T13:57:21Z **Delivery plan** — builds build-agent, gitea → ace, g14, novox, shanks; no bus step - tier 0: build-agent - tier 1: gitea - ace: receives build-agent - g14: receives build-agent - novox: receives build-agent, gitea - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - gitea provides npm-package-registry, git: its consumers are sent again after it - gitea keeps data (ADR 0233): what it holds is backed up before it moves - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine **Group** `fix/704-release-branch-judge`, in order: - novox/mesh-catalog@47ef2644aca1 before novox/mesh-controller@2bee290eea56: built by - novox/mesh-controller@2bee290eea56 before novox/mesh-catalog@47ef2644aca1: built by - **refused**: novox/mesh-catalog@47ef2644aca1 and novox/mesh-controller@2bee290eea56: built by puts novox/mesh-catalog@47ef2644aca1 first, built by puts novox/mesh-controller@2bee290eea56 first, version skew puts novox/mesh-controller@2bee290eea56 first; neither outranks the other: declare the order with a line `after: novox/mesh-catalog` in novox/mesh-controller#329's description, or `after: novox/mesh-controller` in novox/mesh-catalog#297's **Transitions** - 2026-10-14 13:54 (new) → proposed (announced): novox/mesh-catalog#297's head announced - 2026-10-14 13:57 proposed → checked (checked): a verdict names this commit and the trunk's head as its trunk commit - 2026-10-14 13:57 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Author
Contributor

Merge check at 47ef2644
Judged: head 47ef2644aca1 merged into trunk cb7bf94a77ea; read beside it: mesh-controller 76304d9db817, mesh-controller-main 76304d9db817, mesh-host 1a2677882380, mesh-tools 954e1a53eef9

  • mesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, 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 build-agent, gitea → ace, g14, novox, shanks; no bus step

  • tier 0: build-agent
  • tier 1: gitea
  • ace: receives build-agent
  • g14: receives build-agent
  • novox: receives build-agent, gitea
  • shanks: receives build-agent
  • build-agent keeps data (ADR 0233): what it holds is backed up before it moves
  • gitea provides npm-package-registry, git: its consumers are sent again after it
  • gitea keeps data (ADR 0233): what it holds is backed up before it moves
  • grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine
  • read by no module's build: modules/build-agent/manifest_test.go

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

--- the gate: build-agent, gitea
build-agent: ok, keeps workspace (cache), judge-cache (cache), ready: server by exec every 10s
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
2 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 2 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T13:47:24Z 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-14T13:47:24Z

modules the change moves:
  - build-agent (changed)
  - gitea (changed)

machines:
  - a machine (f35): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache)
  - holds mesh-dns-resolver (idt): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache)
  - 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; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache)
  - a machine (yvbpsm): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache)

a merge would rebuild 2 module(s) in 2 tier(s): build-agent, gitea

notes:
  - modules/build-agent/manifest_test.go read by no module's build, and rebuild nothing (issue 280)
--- 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 46922 strings of 719 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.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.209s
testing modules/build-agent
ok  	build-agent	1.010s
testing modules/gitea
ok  	gitea/cmd/forge	1.514s
ok  	gitea/cmd/npm-registry	1.845s
?   	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 (11.195851ms)
✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (54.756209ms)
✔ a pull request or issue number that is not one is refused before the forge is asked (1.40332ms)
✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (10.39211ms)
✔ the newest status of each context is kept, with what it said, when and by whom (19.514591ms)
✔ a commit merges only when every required status is a success; a warning blocks (0.728261ms)
✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.326425ms)
✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.988525ms)
✔ a note line is appended once, as the forge's user, in its own repository (10.801755ms)
✔ a note that cannot be written is said, never read as written (0.357975ms)
✔ one view per pull request, found by its marker; only the mesh's statuses (0.580409ms)
✔ a build machine taking the check is said on the head: building on which machine, since the local time (14.061343ms)
✔ the verdict heard while a 'building' write is in flight lands last and stays (7.249434ms)
[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.144227ms)
✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.533868ms)
✔ a start heard after the verdict writes nothing (0.536248ms)
✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.440717ms)
✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.785111ms)
[gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check the forge holds pending; not written
[gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written
✔ an old run's verdict in flight when the recheck comes lands before the recheck's pending (1.682874ms)
✔ a rechecked head says where its new run is: building on which machine (0.70999ms)
✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.621019ms)
✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.464027ms)
✔ a recheck the forge does not take fails its caller, and the head is as it was (0.448557ms)
✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.370797ms)
✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.279744ms)
✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.384145ms)
✔ a repository as the mesh clones it is told from another (0.290714ms)
✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.453258ms)
✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (57.433798ms)
✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (16.463337ms)
✔ gitea_api still writes what an author writes (7.005091ms)
✔ gitea_branch_protection_set only strengthens (15.24565ms)
✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (42.573123ms)
✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (145.259513ms)
✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.950858ms)
✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.148084ms)
✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.527788ms)
✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (58.567814ms)
✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (19.844426ms)
✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.374123ms)
✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (6.360072ms)
✔ a rule named for a branch is not made weaker than the glob rule that guards it now (14.527529ms)
✔ a repository owner or name is never another path (0.580228ms)
✔ the judges, without a forge (0.387876ms)
✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (2.626338ms)
✔ gitea_api never approves a pull request nor requests changes: those are the operator's (1.41034ms)
✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.272836ms)
✔ every page of a pull request's files is read, though the forge caps a page at fifty (27.76908ms)
✔ a pass asks only the repositories that moved since the last look, every one before the first (0.551528ms)
✔ the directories above a merge's files that hold a module at the commit are said, the root never (15.725427ms)
✔ a lookup the forge refuses is thrown, not read as no module (5.074993ms)
✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (6.689866ms)
✔ a new head beyond the newest 20 open pull requests is announced, exactly once (55.293697ms)
✔ a pass pages until it reaches back past its floor, and no further (23.57893ms)
✔ the statuses asked for replace the rule's, and an empty list requires none (7.981745ms)
✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.397086ms)
✔ each open pull request's new head is announced once, and nothing closed or merged (10.235077ms)
✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.071846ms)
✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.631239ms)
✔ no verdict sets warning on a required check; only a person's decision fails (0.865812ms)
✔ a commit status is set on the commit, under the merge check's context (44.188956ms)
✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.531577ms)
✔ a head announced is pending in the build queue, since the local time it was announced (0.242124ms)
✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.175946ms)
✔ a consumer that asks nothing, or write, publishes: the package team (1.671254ms)
✔ a consumer that asks to read joins the readers, who write no package (0.501097ms)
✔ a reader is made, put on the readers team, and taken off the package team (0.560668ms)
✔ a publisher is made and put on the package team, as before (0.241364ms)
✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (31.374442ms)
✔ second start: reuses the kept token, mints nothing (17.472092ms)
✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (20.77913ms)
✔ 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 (24.427622ms)
✔ 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 (19.815885ms)
✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (15.070237ms)
✔ concurrent first calls share one mint (11.003688ms)
✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (12.090655ms)
✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.899583ms)
✔ a second process finds the token the first renewed, and reuses it instead of minting over it (18.656699ms)
✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.602188ms)
✔ nothing to mint with and no token: the client says what is missing (0.193403ms)
[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-0ag8mr/state/token
✔ the tools register once there is a way to a token, and the first call mints it (8.626705ms)
[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-0fZHZ9/state/token
✔ a pull request can be closed, read and cleaned up through the tools (21.794694ms)
[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.903528ms)
✔ 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.149467ms)
✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (3.905996ms)
✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.368875ms)
✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.502907ms)
✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.290454ms)
✔ a verdict that landed out of date keeps its account on the pull request (0.261803ms)
✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.438586ms)
ℹ tests 90
ℹ suites 0
ℹ pass 90
ℹ fail 0
ℹ cancelled 0
ℹ skipped 0
ℹ todo 0
ℹ duration_ms 637.446535
**Merge check** at `47ef2644` Judged: head `47ef2644aca1` merged into trunk `cb7bf94a77ea`; read beside it: mesh-controller `76304d9db817`, mesh-controller-main `76304d9db817`, mesh-host `1a2677882380`, mesh-tools `954e1a53eef9` - `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, 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 build-agent, gitea → ace, g14, novox, shanks; no bus step - tier 0: build-agent - tier 1: gitea - ace: receives build-agent - g14: receives build-agent - novox: receives build-agent, gitea - shanks: receives build-agent - build-agent keeps data (ADR 0233): what it holds is backed up before it moves - gitea provides npm-package-registry, git: its consumers are sent again after it - gitea keeps data (ADR 0233): what it holds is backed up before it moves - grants step: if this changes what the bus's user list says (a new tool, subject or user), novox is sent that list alone, every build there kept, before build-agent is judged on its first machine - read by no module's build: modules/build-agent/manifest_test.go Run by the build seat on shanks as `build-1791986051569263806` (`builds --log build-1791986051569263806`). ``` --- the gate: build-agent, gitea build-agent: ok, keeps workspace (cache), judge-cache (cache), ready: server by exec every 10s 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 2 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 2 module(s) changed against 4 machine(s), as the snapshot of 2026-10-14T13:47:24Z 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-14T13:47:24Z modules the change moves: - build-agent (changed) - gitea (changed) machines: - a machine (f35): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache) - holds mesh-dns-resolver (idt): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache) - 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; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache) - a machine (yvbpsm): composes; the change adds 1 resource(s) and removes 0 (+directory:build-agent.judge-cache) a merge would rebuild 2 module(s) in 2 tier(s): build-agent, gitea notes: - modules/build-agent/manifest_test.go read by no module's build, and rebuild nothing (issue 280) --- 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 46922 strings of 719 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.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.209s testing modules/build-agent ok build-agent 1.010s testing modules/gitea ok gitea/cmd/forge 1.514s ok gitea/cmd/npm-registry 1.845s ? 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 (11.195851ms) ✔ every open pull request is read with what its base requires and what its head has (gitea_open_pulls) (54.756209ms) ✔ a pull request or issue number that is not one is refused before the forge is asked (1.40332ms) ✔ a merged pull request whose branch was deleted is announced with its branch, not its pull ref (10.39211ms) ✔ the newest status of each context is kept, with what it said, when and by whom (19.514591ms) ✔ a commit merges only when every required status is a success; a warning blocks (0.728261ms) ✔ no rule requires nothing; a rule that cannot be read says nothing, never yes (0.326425ms) ✔ a commit is contained in another when the compare finds nothing on it the other lacks (0.988525ms) ✔ a note line is appended once, as the forge's user, in its own repository (10.801755ms) ✔ a note that cannot be written is said, never read as written (0.357975ms) ✔ one view per pull request, found by its marker; only the mesh's statuses (0.580409ms) ✔ a build machine taking the check is said on the head: building on which machine, since the local time (14.061343ms) ✔ the verdict heard while a 'building' write is in flight lands last and stays (7.249434ms) [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.144227ms) ✔ the verdict heard while a 'building' write still waits its turn: the 'building' is never written (0.533868ms) ✔ a start heard after the verdict writes nothing (0.536248ms) ✔ a start from before the check now pending (a run before a recheck, the stream's history) writes nothing (0.440717ms) ✔ an old run's verdict redelivered after a recheck's pending is not written over it (0.785111ms) [gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check the forge holds pending; not written [gitea] novox/hq@a6b08de8: the verdict of build-1791680941000000000 is an older run's than the check now pending; not written ✔ an old run's verdict in flight when the recheck comes lands before the recheck's pending (1.682874ms) ✔ a rechecked head says where its new run is: building on which machine (0.70999ms) ✔ the recheck's own verdict is written, even asked a little before the pending by another machine's clock (0.621019ms) ✔ after a restart, an old run's verdict is weighed against the check the forge's history holds open (0.464027ms) ✔ a recheck the forge does not take fails its caller, and the head is as it was (0.448557ms) ✔ the status tool writes the merge check's pending through the writer, and the mesh's other statuses as they were (5.370797ms) ✔ when a check was asked is read from its id; the opening of the check now pending from the gate's history (0.279744ms) ✔ a head this process did not announce (it restarted) is said only while the forge holds its gate pending (0.384145ms) ✔ a repository as the mesh clones it is told from another (0.290714ms) ✔ gitea_api refuses the forge's administration, a branch's protection and the merge check's status, and sends nothing (7.453258ms) ✔ gitea_api refuses a file written to the default branch or one a rule guards, and writes one on a branch of its own (57.433798ms) ✔ a protection rule covers a branch as the forge decides it: classes, alternatives, a plain name in any case (issue 539) (16.463337ms) ✔ gitea_api still writes what an author writes (7.005091ms) ✔ gitea_branch_protection_set only strengthens (15.24565ms) ✔ gitea_merge_pull_request merges only a head whose required statuses all succeeded, and only that head (42.573123ms) ✔ gitea_merge_pull_request refuses a verdict judged against an earlier trunk, and asks the check once (145.259513ms) ✔ gitea_merge_pull_request merges only the head it is named, and refuses one that is not the head (17.950858ms) ✔ gitea_merge_pull_request is loud when the forge merged a head other than the one it was handed (14.148084ms) ✔ gitea_merge_pull_request says the merge happened when reading back what landed fails (25.527788ms) ✔ gitea_merge_pull_request reads back a fast-forward, and says it cannot read back a squash or a rebase (58.567814ms) ✔ gitea_delete_repo deletes only the tools' own user's repository, unguarded, untouched for 30 days, named in full (19.844426ms) ✔ gitea_api sends the path it judged, and refuses moving a pull request's base (4.374123ms) ✔ gitea_merge_pull_request merges only into a branch whose protection requires the checks (6.360072ms) ✔ a rule named for a branch is not made weaker than the glob rule that guards it now (14.527529ms) ✔ a repository owner or name is never another path (0.580228ms) ✔ the judges, without a forge (0.387876ms) ✔ gitea_api never acts as another user: sudo, in any case, and any query key it does not send are refused (2.626338ms) ✔ gitea_api never approves a pull request nor requests changes: those are the operator's (1.41034ms) ✔ no tool's caller text becomes another path or another user: `..` and `?sudo=` are refused before the forge (5.272836ms) ✔ every page of a pull request's files is read, though the forge caps a page at fifty (27.76908ms) ✔ a pass asks only the repositories that moved since the last look, every one before the first (0.551528ms) ✔ the directories above a merge's files that hold a module at the commit are said, the root never (15.725427ms) ✔ a lookup the forge refuses is thrown, not read as no module (5.074993ms) ✔ a renamed file is said under its old name too, as gone (novox/hq ADR 0267) (6.689866ms) ✔ a new head beyond the newest 20 open pull requests is announced, exactly once (55.293697ms) ✔ a pass pages until it reaches back past its floor, and no further (23.57893ms) ✔ the statuses asked for replace the rule's, and an empty list requires none (7.981745ms) ✔ a new rule refuses direct pushes unless asked; an administrator's override only when said (0.397086ms) ✔ each open pull request's new head is announced once, and nothing closed or merged (10.235077ms) ✔ a verdict is the head commit's status; an error is the forge's error, never a success (1.071846ms) ✔ each layer is its own status: the gate with the modules it judged, the repository's own check beside it (0.631239ms) ✔ no verdict sets warning on a required check; only a person's decision fails (0.865812ms) ✔ a commit status is set on the commit, under the merge check's context (44.188956ms) ✔ a change plan is the gate's result: said on the status and, when it builds something, as a comment (0.531577ms) ✔ a head announced is pending in the build queue, since the local time it was announced (0.242124ms) ✔ the vault's token is used, a setting of it is ignored and said once, without its value (3.175946ms) ✔ a consumer that asks nothing, or write, publishes: the package team (1.671254ms) ✔ a consumer that asks to read joins the readers, who write no package (0.501097ms) ✔ a reader is made, put on the readers team, and taken off the package team (0.560668ms) ✔ a publisher is made and put on the package team, as before (0.241364ms) ✔ first start: mints with the admin account, keeps the token at 0600, asks for the tools' scopes only (31.374442ms) ✔ second start: reuses the kept token, mints nothing (17.472092ms) ✔ the forge rejects the kept token (its data was restored): minted afresh, once, and the call goes through (20.77913ms) ✔ 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 (24.427622ms) ✔ 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 (19.815885ms) ✔ the kept file is gone but the forge still holds a token by that name: replaced, not refused (15.070237ms) ✔ concurrent first calls share one mint (11.003688ms) ✔ the admin account is refused: said plainly, nothing kept, and the next call fails the same way rather than crashing (12.090655ms) ✔ one process shares one source per kept file — the watcher and the tools never renew against each other (0.899583ms) ✔ a second process finds the token the first renewed, and reuses it instead of minting over it (18.656699ms) ✔ a configured token wins, and is reported rather than minted over when the forge rejects it (2.602188ms) ✔ nothing to mint with and no token: the client says what is missing (0.193403ms) [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-0ag8mr/state/token ✔ the tools register once there is a way to a token, and the first call mints it (8.626705ms) [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-0fZHZ9/state/token ✔ a pull request can be closed, read and cleaned up through the tools (21.794694ms) [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.903528ms) ✔ 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.149467ms) ✔ a push to a trunk puts every open pull request into it out of date — twenty-five of them — and none into another (3.905996ms) ✔ a trunk that cannot be read is weighed again on the next pass, never recorded half (0.368875ms) ✔ the trunk's move takes its turn after a verdict in flight, and weighs what the forge holds then (0.502907ms) ✔ a verdict judged against a trunk newer than the pass's first read is not put out of date (0.290454ms) ✔ a verdict that landed out of date keeps its account on the pull request (0.261803ms) ✔ a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5) (0.438586ms) ℹ tests 90 ℹ suites 0 ℹ pass 90 ℹ fail 0 ℹ cancelled 0 ℹ skipped 0 ℹ todo 0 ℹ duration_ms 637.446535 ```
Author
Contributor

Verdict: APPROVE at 47ef2644ac

Independent review of novox/mesh-catalog#297 at 47ef2644ac (issue 704, ADR 0358 v11), read with novox/mesh-controller#329 at 2bee290eea56.

What I ran

  • gofmt -l modules/build-agent: nothing.
  • merge-check.sh with MESH_GATE built from #329's head and MESH_CHECK_MODULES=build-agent,gitea: exit 0. The gate's module check of every manifest passes, and so do the build-agent and gitea Go tests.
  • modules/gitea/test/trunk.test.ts with Node's test runner, after transpiling the module: 8 of 8 tests pass, including the new §5 test.

What I checked

  • The manifest. judge-cache is a directory resource (0700), so the node-engine places it at <data root>/build-agent/judge-cache, beside the workspace and outside it. Its data entry has class cache, which by the catalogue's rule is never backed up, measured or alerted on. It is mounted into the agent at the same path, and MESH_JUDGE_CACHE is in the agent's environment file, whose change restarts the agent. The directory outlives a redeploy as the workspace does.
  • ADR 0358 §5. The author's claim holds. weighTrunks (trunk.ts:133) weighs the head of every open pull request's base, not only main. The merge tool reads the head of the pull request's own base (merge.ts:95). The new test pins both behaviours. It passes on the current code, which is what a test that keeps existing behaviour should do.
  • The order. With this manifest and an older mesh-builder, MESH_JUDGE_CACHE is ignored and the directory stays empty. #329's mesh-builder without this manifest builds judges cold. Neither order gives a wrong verdict.

Findings

  1. Note: merge this only in its delivery group, with #329. #329 has a must-change finding at 2bee290eea56: mesh-lab's replays get the runtime's socket while they read a Go environment file and a Go cache from the workspace, so a pull request can still reach this judge cache. That is a fault in mesh-builder, not in this manifest, but this pull request's body ("mounted into judge builds only") is only a full answer once #329 fixes it.
  2. Should (can follow): the manifest test's workspace check is textual. It looks for a volume that starts with ${dir:workspace} and names judge-cache. The real guarantee is the separate directory resource and #329's refusal of a judge cache inside the workspace, and both hold.
Verdict: APPROVE at 47ef2644aca1 Independent review of novox/mesh-catalog#297 at 47ef2644aca144feefb978a83084793722dee540 (issue 704, ADR 0358 v11), read with novox/mesh-controller#329 at 2bee290eea56. **What I ran** - `gofmt -l modules/build-agent`: nothing. - `merge-check.sh` with MESH_GATE built from #329's head and MESH_CHECK_MODULES=build-agent,gitea: exit 0. The gate's module check of every manifest passes, and so do the build-agent and gitea Go tests. - `modules/gitea/test/trunk.test.ts` with Node's test runner, after transpiling the module: 8 of 8 tests pass, including the new §5 test. **What I checked** - **The manifest.** `judge-cache` is a directory resource (0700), so the node-engine places it at `<data root>/build-agent/judge-cache`, beside the workspace and outside it. Its data entry has class `cache`, which by the catalogue's rule is never backed up, measured or alerted on. It is mounted into the agent at the same path, and MESH_JUDGE_CACHE is in the agent's environment file, whose change restarts the agent. The directory outlives a redeploy as the workspace does. - **ADR 0358 §5.** The author's claim holds. `weighTrunks` (trunk.ts:133) weighs the head of every open pull request's base, not only main. The merge tool reads the head of the pull request's own base (merge.ts:95). The new test pins both behaviours. It passes on the current code, which is what a test that keeps existing behaviour should do. - **The order.** With this manifest and an older mesh-builder, MESH_JUDGE_CACHE is ignored and the directory stays empty. #329's mesh-builder without this manifest builds judges cold. Neither order gives a wrong verdict. **Findings** 1. **Note: merge this only in its delivery group, with #329.** #329 has a must-change finding at 2bee290eea56: mesh-lab's replays get the runtime's socket while they read a Go environment file and a Go cache from the workspace, so a pull request can still reach this judge cache. That is a fault in mesh-builder, not in this manifest, but this pull request's body ("mounted into judge builds only") is only a full answer once #329 fixes it. 2. **Should (can follow): the manifest test's workspace check is textual.** It looks for a volume that starts with `${dir:workspace}` and names judge-cache. The real guarantee is the separate directory resource and #329's refusal of a judge cache inside the workspace, and both hold.
Some optional checks failed
mesh/delivery-group group fix/704-release-branch-judge rejected: its order contradicts itself: novox/mesh-catalog@47ef2644aca1 and novox/mesh-controller@2bee29…
mesh/merge-gate head 47ef2644aca1 merged into trunk cb7bf94a77ea — pass: builds build-agent, gitea → ace, g14, novox, shanks; no bus step; every machine co…
Required
Details
mesh/repo-check head 47ef2644aca1 merged into trunk cb7bf94a77ea — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
Required
Details
mesh/delivery ready: it delivers once merged
You are not authorized to merge this pull request.
This pull request can be merged automatically.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/704-release-branch-judge:fix/704-release-branch-judge
git checkout fix/704-release-branch-judge
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: novox/mesh-catalog#297