From 6a8bd6e26c4db89f08f7a054f178251c7928a426 Mon Sep 17 00:00:00 2001 From: hack-cli-tests Date: Wed, 30 Sep 2026 12:52:40 -0400 Subject: [PATCH] test: serialize the frontend acceptance stand-in's shared state Stand-in invocations ran concurrently (a foreground `up` while the driver polls `ps`) and each did an unlocked read-modify-write of state.json. A `ps` that loaded the state before the second `up` saved its foreground token then saved its stale copy over it, so `up` saw its token gone and exited 0 before readiness; lost call records failed the acceptance test as well. That failed 26 of 40 local runs, and Runtime state models on CI. Each invocation now holds an exclusive flock across its read-modify-write and releases it only before its long waits (the foreground loop and the ps-hang fault). 40 of 40 local runs pass. --- tests/python/test_native_frontend_acceptance.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/python/test_native_frontend_acceptance.py b/tests/python/test_native_frontend_acceptance.py index 5b1bf3710..09329f82d 100644 --- a/tests/python/test_native_frontend_acceptance.py +++ b/tests/python/test_native_frontend_acceptance.py @@ -22,11 +22,16 @@ # One stand-in serves as both hack-native and hack-v5 (by file name) over a shared state file. # `up` refuses unless a prepared base is required, then stays in the foreground until `down`. FAKE = r'''#!PYTHON -import json, os, pathlib, secrets, sys, time +import fcntl, json, os, pathlib, secrets, sys, time here = pathlib.Path(__file__).parent name = pathlib.Path(__file__).name path = here / "state.json" fault = os.environ.get("FAKE_FAULT", "") +# Invocations run concurrently (a foreground `up` while the driver polls `ps`). Each holds this +# exclusive lock across its read-modify-write of the shared state, so no invocation saves a stale +# copy over another's update; it is released only before a long wait. +lock = open(here / "state.lock", "a") +fcntl.flock(lock, fcntl.LOCK_EX) def load(): @@ -86,6 +91,7 @@ def reply(body, code=0): up_env={k: v for k, v in os.environ.items() if k.startswith("HACK_NATIVE_")}) state.setdefault("run", "r" + secrets.token_hex(8)) save(state) + fcntl.flock(lock, fcntl.LOCK_UN) deadline = time.time() + 30 while time.time() < deadline: time.sleep(0.02) @@ -94,6 +100,7 @@ def reply(body, code=0): sys.exit(3) if argv[0] == "ps": if fault == "ps-hang": + fcntl.flock(lock, fcntl.LOCK_UN) time.sleep(10) if state.get("foreground"): reply({"status": "observed", "phase": "ready-observed", "backend": "native", "run": state["run"],