mesh/merge-gate pass: builds gitea → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery failed: its walk failed, carrying its own commit: a gate on a first machine (what it carried put back), a build, a machine
mesh-delivery read the gate as pending and then wrote "building on", and the gitea module could write the verdict in between: the forge keeps the newest status, so a pass read pending and a failure hid under "building". The gitea module now hears the build seat's started itself and puts every write of a head's gate on one chain, marking a verdict the moment it is heard; a start from before the pending words were last set is not the current check's.
165 lines
8.8 KiB
TypeScript
165 lines
8.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
|
|
// **A pending check says where it is, and the verdict lands last** (novox/hq issue 446). This module is the one writer
|
|
// of a head's mesh/merge-gate: the announcement's pending words, "building on <machine> since …" when the build seat
|
|
// says a machine took the check, and the verdict. The forge keeps the newest status of a context, so a pending word
|
|
// written after the verdict hides it — a pass reads pending, a failure hides under "building".
|
|
|
|
const SHA = "a6b08de8507c1703528277e5cefc80548dbc9bb3";
|
|
|
|
// The build seat's `started` as a machine said it for novox/hq#309's check on 2026-10-11 (its log, read with the
|
|
// controller's `builds --log build-1791680941048622697`, opens at 03:09:01 on shanks); only the forge's address
|
|
// replaced. The shape is mesh-controller's link.BuildStart.
|
|
const startedOnShanks = { id: "build-1791680941048622697", repository: "http://127.0.0.1:20000/novox/hq.git",
|
|
ref: SHA, on: "shanks", at: "2026-10-11T01:09:01.204319Z" };
|
|
|
|
/** A forge that keeps the newest status per context, as gitea does, and whose writes take as long as a test says. */
|
|
function aForge() {
|
|
const kept: Record<string, { state: string; description: string; updated_at: string }> = {};
|
|
const written: string[] = [];
|
|
let hold: Promise<void> | null = null;
|
|
let release = () => {};
|
|
let readHold: Promise<void> | null = null;
|
|
let readRelease = () => {};
|
|
return {
|
|
kept, written,
|
|
/** The next writes wait until released: a request in flight. */
|
|
holdWrites() {
|
|
hold = new Promise<void>((r) => (release = r));
|
|
},
|
|
release() {
|
|
hold = null;
|
|
release();
|
|
},
|
|
async setCommitStatus(_o: string, _r: string, _sha: string, s: { state: string; context: string; description: string }) {
|
|
if (hold) await hold;
|
|
kept[s.context] = { state: s.state, description: s.description, updated_at: new Date().toISOString() };
|
|
written.push(`${s.state} ${s.description}`);
|
|
},
|
|
/** Reads answer what the forge held when asked, as late as the reads are held: the answer of a request in flight. */
|
|
holdReads() {
|
|
readHold = new Promise<void>((r) => (readRelease = r));
|
|
},
|
|
releaseReads() {
|
|
readHold = null;
|
|
readRelease();
|
|
},
|
|
async gateOf(_o: string, _r: string, _sha: string) {
|
|
const held = kept["mesh/merge-gate"] ? { ...kept["mesh/merge-gate"] } : undefined;
|
|
if (readHold) await readHold;
|
|
return held;
|
|
},
|
|
};
|
|
}
|
|
|
|
/** The clock of the test: the head announced at 01:08:50Z. */
|
|
const announcedAt = () => new Date("2026-10-11T01:08:50Z");
|
|
|
|
test("a build machine taking the check is said on the head: building on which machine, since the local time", async () => {
|
|
const { GateWriter, buildingWords } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
await gates.pending("novox", "hq", SHA);
|
|
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), true);
|
|
assert.equal(forge.kept["mesh/merge-gate"].state, "pending");
|
|
assert.equal(forge.kept["mesh/merge-gate"].description, buildingWords("shanks", new Date(startedOnShanks.at)));
|
|
assert.equal(buildingWords("shanks", new Date(2026, 9, 11, 3, 9, 1)), "building on shanks since 03:09");
|
|
});
|
|
|
|
test("the verdict heard while a 'building' write is in flight lands last and stays", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
await gates.pending("novox", "hq", SHA);
|
|
forge.holdWrites(); // the 'building' request is in flight when the verdict comes
|
|
const building = gates.started("novox", "hq", SHA, startedOnShanks);
|
|
await new Promise((r) => setImmediate(r));
|
|
const verdict = gates.verdict("novox", "hq", SHA, () =>
|
|
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
|
|
forge.release();
|
|
await Promise.all([building, verdict]);
|
|
assert.equal(`${forge.kept["mesh/merge-gate"].state} ${forge.kept["mesh/merge-gate"].description}`, "success pass",
|
|
`the verdict is not the newest status: ${forge.written.join(" | ")}`);
|
|
});
|
|
|
|
// The race the review of mesh-catalog#202 reproduced: a writer reads the forge's gate (pending), the verdict is written,
|
|
// then the writer writes "building" — and the pass reads pending until the next push. A head this process did not
|
|
// announce is the one case where the forge is read before writing.
|
|
test("the verdict written while a start's read of the forge is in flight stays the newest status", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
forge.kept["mesh/merge-gate"] = { state: "pending", description: "waiting", updated_at: "2026-10-11T01:08:50Z" };
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
forge.holdReads();
|
|
const building = gates.started("novox", "hq", SHA, startedOnShanks);
|
|
await new Promise((r) => setImmediate(r));
|
|
const verdict = gates.verdict("novox", "hq", SHA, () =>
|
|
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
|
|
await new Promise((r) => setImmediate(r));
|
|
forge.releaseReads();
|
|
await Promise.all([building, verdict]);
|
|
assert.equal(`${forge.kept["mesh/merge-gate"].state} ${forge.kept["mesh/merge-gate"].description}`, "success pass",
|
|
`the verdict is not the newest status: ${forge.written.join(" | ")}`);
|
|
});
|
|
|
|
test("the verdict heard while a 'building' write still waits its turn: the 'building' is never written", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
forge.holdWrites(); // the pending words are still being written
|
|
const pending = gates.pending("novox", "hq", SHA);
|
|
const building = gates.started("novox", "hq", SHA, startedOnShanks);
|
|
const verdict = gates.verdict("novox", "hq", SHA, () =>
|
|
forge.setCommitStatus("novox", "hq", SHA, { state: "failure", context: "mesh/merge-gate", description: "fail: 0 of 4 compose" }));
|
|
forge.release();
|
|
await Promise.all([pending, verdict]);
|
|
assert.equal(await building, false);
|
|
assert.equal(forge.kept["mesh/merge-gate"].state, "failure");
|
|
assert.ok(!forge.written.some((w) => w.includes("building")), forge.written.join(" | "));
|
|
});
|
|
|
|
test("a start heard after the verdict writes nothing", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
await gates.pending("novox", "hq", SHA);
|
|
await gates.verdict("novox", "hq", SHA, () =>
|
|
forge.setCommitStatus("novox", "hq", SHA, { state: "success", context: "mesh/merge-gate", description: "pass" }));
|
|
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false);
|
|
assert.equal(forge.kept["mesh/merge-gate"].state, "success");
|
|
});
|
|
|
|
test("a start from before the check now pending (a run before a recheck, the stream's history) writes nothing", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
let now = announcedAt();
|
|
const gates = new GateWriter(forge, () => now);
|
|
await gates.pending("novox", "hq", SHA);
|
|
await gates.verdict("novox", "hq", SHA, () =>
|
|
forge.setCommitStatus("novox", "hq", SHA, { state: "failure", context: "mesh/merge-gate", description: "error" }));
|
|
now = new Date("2026-10-11T01:30:00Z"); // checked again: pending once more
|
|
await gates.pending("novox", "hq", SHA);
|
|
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false, "the first run's start");
|
|
assert.match(forge.kept["mesh/merge-gate"].description, /^waiting in the build queue since /);
|
|
assert.equal(await gates.started("novox", "hq", SHA, { ...startedOnShanks, at: "2026-10-11T01:31:00Z" }), true,
|
|
"the second run's start");
|
|
});
|
|
|
|
test("a head this process did not announce (it restarted) is said only while the forge holds its gate pending", async () => {
|
|
const { GateWriter } = await import("../gate.ts");
|
|
const forge = aForge();
|
|
forge.kept["mesh/merge-gate"] = { state: "success", description: "pass", updated_at: "2026-10-11T01:20:00Z" };
|
|
const gates = new GateWriter(forge, announcedAt);
|
|
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), false);
|
|
forge.kept["mesh/merge-gate"] = { state: "pending", description: "waiting", updated_at: "2026-10-11T01:08:50Z" };
|
|
assert.equal(await gates.started("novox", "hq", SHA, startedOnShanks), true);
|
|
});
|
|
|
|
test("a repository as the mesh clones it is told from another", async () => {
|
|
const { clonedAs } = await import("../gate.ts");
|
|
assert.equal(clonedAs("http://127.0.0.1:20000/novox/hq.git", "novox", "hq"), true);
|
|
assert.equal(clonedAs("http://127.0.0.1:20000/novox/mesh-hq.git", "novox", "hq"), false);
|
|
assert.equal(clonedAs("http://127.0.0.1:20000/novox/mesh-controller.git", "novox", "hq"), false);
|
|
});
|