Compare commits

..
1 Commits
Author SHA1 Message Date
mesh-adminandClaude Opus 5.5 47ef2644ac 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 out of date: judged against trunk cb7bf94a77ea, the trunk is now e1c8965ddbf1; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk cb7bf94a77ea, the trunk is now e1c8965ddbf1; judged again on a recheck or when it is merged
mesh/delivery checking
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>
2026-10-14 13:48:30 +00:00
4 changed files with 93 additions and 5 deletions
+54
View File
@@ -115,3 +115,57 @@ func TestTheGenesisDocumentIsDeclaredAndIssuedOutside(t *testing.T) {
}
t.Fatal("the build agent has no environment file")
}
// **The judge cache is the agent's own, beside its workspace and never in it** (novox/hq ADR 0358 §6, issue 704): a
// directory resource of its own, kept as the agent's data, mounted into the agent at the same path as on the host —
// the container runtime resolves a judge build's mounts on the host — and handed to the agent as MESH_JUDGE_CACHE.
func TestTheJudgeCacheIsADirectoryOfItsOwnMountedAtTheSamePath(t *testing.T) {
raw, err := os.ReadFile("module.json")
if err != nil {
t.Fatal(err)
}
var m struct {
Data struct {
Own []struct {
ID string `json:"id"`
Path string `json:"path"`
Class string `json:"class"`
} `json:"own"`
} `json:"data"`
Resources []struct {
ID string `json:"id"`
Type string `json:"type"`
Content string `json:"content"`
Volumes []string `json:"volumes"`
} `json:"resources"`
}
if err := json.Unmarshal(raw, &m); err != nil {
t.Fatal(err)
}
dir, data, env, mounted := false, false, false, false
for _, d := range m.Data.Own {
if d.ID == "judge-cache" && d.Path == "${dir:judge-cache}" && d.Class == "cache" {
data = true
}
}
for _, r := range m.Resources {
switch {
case r.ID == "judge-cache" && r.Type == "directory":
dir = true
case r.ID == "agent-env":
env = strings.Contains(r.Content, "MESH_JUDGE_CACHE=${dir:judge-cache}\n")
case r.ID == "server":
for _, v := range r.Volumes {
if v == "${dir:judge-cache}:${dir:judge-cache}" {
mounted = true
}
if strings.HasPrefix(v, "${dir:workspace}") && strings.Contains(v, "judge-cache") {
t.Errorf("the judge cache is mounted inside the workspace: %s", v)
}
}
}
}
if !dir || !data || !env || !mounted {
t.Fatalf("the judge cache: directory %v, data %v, env %v, mounted at the same path %v", dir, data, env, mounted)
}
}
+13 -1
View File
@@ -39,6 +39,12 @@
"path": "${dir:workspace}",
"class": "cache",
"why": "a build's working copy, cloned again for every build"
},
{
"id": "judge-cache",
"path": "${dir:judge-cache}",
"class": "cache",
"why": "the Go cache every merge check's judge is built with and the release-branch judges kept per head; mounted into judge builds only, never into a check's containers (novox/hq ADR 0358 §6)"
}
]
},
@@ -54,12 +60,17 @@
"type": "directory",
"mode": "0700"
},
{
"id": "judge-cache",
"type": "directory",
"mode": "0700"
},
{
"id": "agent-env",
"type": "file",
"path": "${dir:mesh-state}/build-agent.env",
"mode": "0600",
"content": "MESH_BROKER_FILE=/run/mesh/broker\nMESH_NODE=${machine:name}\nMESH_REGISTRY=${bound:artifact-store:at}:${bound:artifact-store:port}\nMESH_PACKAGE_BINDING=/run/mesh/package-registry.json\nMESH_NPM_TOKEN_FILE=/run/mesh/package-registry.secret\nMESH_GENESIS_PACKAGE_REGISTRY=/run/mesh/genesis-package-registry.json\nMESH_WORKSPACE=${dir:workspace}\n"
"content": "MESH_BROKER_FILE=/run/mesh/broker\nMESH_NODE=${machine:name}\nMESH_REGISTRY=${bound:artifact-store:at}:${bound:artifact-store:port}\nMESH_PACKAGE_BINDING=/run/mesh/package-registry.json\nMESH_NPM_TOKEN_FILE=/run/mesh/package-registry.secret\nMESH_GENESIS_PACKAGE_REGISTRY=/run/mesh/genesis-package-registry.json\nMESH_WORKSPACE=${dir:workspace}\nMESH_JUDGE_CACHE=${dir:judge-cache}\n"
},
{
"id": "server",
@@ -72,6 +83,7 @@
"volumes": [
"${dir:mesh-state}:/run/mesh:ro",
"${dir:workspace}:${dir:workspace}",
"${dir:judge-cache}:${dir:judge-cache}",
"/var/run/docker.sock:/var/run/docker.sock"
],
"restart-on": [
+26
View File
@@ -173,3 +173,29 @@ test("a verdict that landed out of date keeps its account on the pull request",
const bare = lateVerdictComment({ commit: HEAD, trunk: TRUNK.slice(0, 12), verdict: "pass" }, null, words);
assert.match(bare, /head dfb1415aaaaa merged into trunk a2c2c35bbbbb, pass/);
});
test("a release branch's move puts every open pull request into it out of date, as a trunk's does (ADR 0358 §5)", async () => {
// novox/hq ADR 0358 §5: for a pull request into a release branch, the release branch takes the trunk's place in ADR
// 0323 — the check merges the head onto the release branch's head and names it as the trunk commit, so the release
// branch's move puts its open verdicts out of date, and leaves those into main alone.
const release = "feat/57-config-and-provisioning";
const heads: Record<string, string> = { main: TRUNK, [release]: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" };
const pulls = [{ number: 315, base: release, head: "3".repeat(40) }, { number: 1, base: "main", head: "1".repeat(40) }];
const { forge, statuses } = aForge(pulls, heads);
const gates = new GateWriter(forge);
const trunks: Record<string, string> = {};
await weighTrunks(forge, gates, trunks, "novox", "mesh-controller");
heads[release] = MOVED;
assert.equal(await weighTrunks(forge, gates, trunks, "novox", "mesh-controller"), true);
const s = statuses.get("3".repeat(40))!;
assert.equal(s["mesh/merge-gate"].state, "pending");
assert.match(s["mesh/merge-gate"].description!, /judged against trunk eeeeeeeeeeee, the trunk is now d28ca95ccccc/);
assert.equal(s["mesh/repo-check"].state, "pending");
assert.equal(statuses.get("1".repeat(40))!["mesh/merge-gate"].state, "success", "a pull request into main is left");
assert.equal(trunks[`novox/mesh-controller:${release}`], MOVED);
// The merge tool's rule reads the same: a verdict judged against the release branch's old head is not current.
assert.equal(verdictAtTrunk({ "mesh/merge-gate": { state: "success",
description: "head x merged into trunk eeeeeeeeeeee — pass" } }, MOVED).current, false);
});
-4
View File
@@ -5,10 +5,6 @@
"policy": "record",
"why": "people's mail: any change to how its containers are declared recreates them together in one send, and the mail is down for everyone until they are up again — a person chooses the moment (hq ADR 0242, issue 295)"
},
"unhealthy": {
"severity": "urgent",
"why": "while mailu is broken, mail is refused or stuck for everyone with a mailbox here (hq ADR 0355, issue 687)"
},
"capabilities": [
"container-runtime"
],