parity: a host's exclude list also holds on its own CI runner
playhorizon's runner fails remote_fireball and remote_fireworks even alone and
at bare main (3b19c386c: 2 of 2 runs and the retry), while the Mac mini passes
them; every fork PR whose parity lands there stopped on them. The host's
exclude list only kept harnesses off workers sent there, not off the runner's
own jobs: platform_skips now adds the local host's exclude, skipped with its
reason unless named. The two harnesses join playhorizon's list; the Mac
mini's runner and the nightly audit (macOS) keep running them.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,7 +42,7 @@ The game logs into `HORIZON_HOME` and falls back to the player's own game folder
|
||||
|
||||
Harnesses can also run on the Mac mini, the desktop and playhorizon (`tools/parity_hosts.json` describes them; `desktop` and `playhorizon` use their CI runner's environment and vanilla data, and the desktop refuses work, or stops it, while someone uses it: docs/release-flow.md, "The desktop's runner"; its `default` list is empty, so pass `--hosts macmini,playhorizon` or set `PARITY_HOSTS`, after copying `shared-local/` to `~/code/horizon-parity/shared-local` on each host). The runner mirrors the worktree there with rsync (not `.git`, `.local`, `target` or ignored files) into `~/code/horizon-parity/remote/<host><path>`, whose `.local` links to the vanilla data in `~/code/horizon-parity/shared-local`, and starts `tools/parity.py --worker` over ssh. The worker builds the probes (about 80 s the first time) and runs what it is sent under the same tracer, admitting harnesses against its own free memory. One queue, longest first, feeds every machine, remote ones first: the Mac takes up to 14 harnesses, the desktop 8, playhorizon 3 (on CPUs 0-3: its CI runner has 4-7). Each pass comes back with the content hash of every file it depended on, so it is keyed on exactly what ran, and logs come back to `.local/parity-runner/logs/`; state, sharing and `gate.py` work as before. If a host can't be reached, or drops out, its harnesses run here.
|
||||
|
||||
Harnesses a host can't run are listed in the file: `local_only` (`save_roundtrip` runs the game on a virtual screen) and each host's `exclude`. The Mac excludes nothing: vanilla's `Math.sin`, `pow`, `log` and the like don't round the same on an Apple silicon Mac as on Linux (HotSpot uses fdlibm there, Intel's stubs on x86-64), and Rust's float functions round like neither (they call the platform libm), so code that mirrors a vanilla `Math` call uses `horizon_core::jmath`, which computes what HotSpot computes on the host architecture. The ThinkPad no longer runs harnesses (2026-10-26): playhorizon is the Linux host that is always on.
|
||||
Harnesses a host can't run are listed in the file: `local_only` (`save_roundtrip` runs the game on a virtual screen) and each host's `exclude`. A host's `exclude` also holds when `parity.py` runs on that host itself, as its CI runner does: the harness is skipped there with its reason (playhorizon skips `remote_fireball` and `remote_fireworks`, which its CPU-starved CI slice fails even on bare main), unless named on the command line. The Mac mini's runner and the nightly audit still run them. The Mac excludes nothing: vanilla's `Math.sin`, `pow`, `log` and the like don't round the same on an Apple silicon Mac as on Linux (HotSpot uses fdlibm there, Intel's stubs on x86-64), and Rust's float functions round like neither (they call the platform libm), so code that mirrors a vanilla `Math` call uses `horizon_core::jmath`, which computes what HotSpot computes on the host architecture. The ThinkPad no longer runs harnesses (2026-10-26): playhorizon is the Linux host that is always on.
|
||||
|
||||
The file's `solo` list names harnesses that drive game windows (`remote_fireworks`, `server_player_sculk`, `world_reopen`, ...). On a machine where `parity.py` has 4 CPUs or fewer, such as playhorizon's CI runner, each of them runs alone, after the others: there frames are drawn on the CPU, and beside other harnesses a screenshot or a control request took more ticks than the check allows. `PARITY_SOLO=1` or `0` forces this on or off.
|
||||
|
||||
|
||||
@@ -145,6 +145,13 @@ class ParityRunnerTests(unittest.TestCase):
|
||||
with mock.patch.object(parity.sys, "platform", "darwin"):
|
||||
self.assertEqual(parity.platform_skips(config), {"options": "libm"})
|
||||
|
||||
def test_a_hosts_excludes_are_skipped_when_it_runs_parity_itself(self):
|
||||
config = {"hosts": {"playhorizon": {"exclude": {"remote_fireworks": "starved"}}, "macmini": {"exclude": {}}}}
|
||||
with mock.patch.object(parity.sys, "platform", "linux"):
|
||||
self.assertEqual(parity.platform_skips(config, "playhorizon"), {"remote_fireworks": "starved"})
|
||||
self.assertEqual(parity.platform_skips(config, "macmini"), {})
|
||||
self.assertEqual(parity.platform_skips(config, "elsewhere"), {})
|
||||
|
||||
def test_named_only_harnesses_are_listed_from_the_host_config(self):
|
||||
self.assertEqual(parity.named_only({"named_only": {"lan_integration": "slow"}}), {"lan_integration": "slow"})
|
||||
self.assertEqual(parity.named_only({}), {})
|
||||
|
||||
+8
-6
@@ -1454,11 +1454,13 @@ def affected_harnesses(found, records, changed, reach=True, base=None):
|
||||
return affected
|
||||
|
||||
|
||||
def platform_skips(config):
|
||||
"""Harnesses tools/parity_hosts.json marks as giving different results on this platform: {name: reason}."""
|
||||
if sys.platform == "linux":
|
||||
return {}
|
||||
return dict(config.get("linux_only", {}))
|
||||
def platform_skips(config, host=None):
|
||||
"""Harnesses tools/parity_hosts.json marks as giving different results on this platform, and those this host's
|
||||
entry excludes (playhorizon's CI runner runs its own pull request jobs here, not only workers sent to it):
|
||||
{name: reason}."""
|
||||
skips = {} if sys.platform == "linux" else dict(config.get("linux_only", {}))
|
||||
skips.update(config.get("hosts", {}).get(HOST if host is None else host, {}).get("exclude", {}))
|
||||
return skips
|
||||
|
||||
|
||||
def known_flaky(config):
|
||||
@@ -1593,7 +1595,7 @@ def main():
|
||||
chosen = [name for name in found if stale(name, state, hasher, environment)]
|
||||
skips = platform_skips(host_config())
|
||||
for name in [name for name in chosen if base_name(name) in skips and not args.names]:
|
||||
print(f"skipped on {sys.platform}: {name}: {skips[base_name(name)]}", flush=True)
|
||||
print(f"skipped on {HOST} ({sys.platform}): {name}: {skips[base_name(name)]}", flush=True)
|
||||
chosen.remove(name)
|
||||
manual = {} if args.names or args.all else named_only(host_config())
|
||||
for name in [name for name in chosen if base_name(name) in manual]:
|
||||
|
||||
@@ -31,7 +31,9 @@
|
||||
"exclude": {
|
||||
"mob_ai": "the vanilla oracle runs out of its 3 GB heap here (OutOfMemoryError in IO workers; passes on the desktop and the Mac) - 2026-10-26 trial",
|
||||
"lan_integration": "two clients rendered on the CPU (lavapipe): different timing subtests fail on each run (elytra, consume-release, barter, critical-hits) - 2026-10-26 trial",
|
||||
"world_reopen": "failed twice here (no Save and Quit button after reopening; the minecart-count flake), passes on the desktop - 2026-10-26 trial"
|
||||
"world_reopen": "failed twice here (no Save and Quit button after reopening; the minecart-count flake), passes on the desktop - 2026-10-26 trial",
|
||||
"remote_fireball": "fails here even alone, at bare main 3b19c386c (2 of 2 runs and the retry), and passes on the Mac mini: the guest's egg is seen in 2 of 6 samples. CPU-starved: the whole horizon-ci slice (runner on 4-7, workers on 0-3) shares a 700% quota and was throttled in 27% of periods, so lavapipe frames and screenshots outlast the projectile's flight - 2026-10-27",
|
||||
"remote_fireworks": "fails here even alone, at bare main 3b19c386c (2 of 2 runs and the retry), and passes on the Mac mini: the guest's sparks are gone by its trail screenshot (same CPU starvation as remote_fireball; a private Xvfb on CPUs 0-3 did not help) - 2026-10-27"
|
||||
},
|
||||
"note": "a partner's server (Ubuntu, 8 EPYC threads, 30 GB, Detroit; tailnet only), its CI runner has CPUs 4-7, so workers get 0-3 (as tools/remote.sh runs do) and 3 jobs, in the CI runner's environment, slice and Xvfb screen (tools/ci/linux-runner.sh); the always-on Linux host since the ThinkPad stopped running harnesses (2026-10-26)"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user