fix: isolate the adversary review-stack lock for parallel tests and non-POSIX platforms

The advisory flock in run_checks.py uses one system-wide lock path
(~/.cache/coyote/review-stack.lock). Concurrent runs of the timeout/deadline
tests contend on it: the losers sleep in the bounded retry loop, the total
deadline (computed before lock acquisition) expires, and commands record
'skipped: deadline' instead of the expected timeout/kill behavior.

- run_checks.py: ADVERSARY_STACK_LOCK_PATH env override relocates the lock
  file (isolation seam, documented alongside the other env seams); every
  other lock semantic (default path, bounded wait, proceed-unlocked warning,
  fd held for process lifetime, skip when no commands) is unchanged.
- run_checks.py: guard the POSIX-only fcntl import — windows-latest CI has
  python3 and runs these tests, so the unconditional import crashed the
  script there; on non-POSIX the lock is now skipped with a stderr WARN,
  matching the script's documented non-POSIX limitations.
- tests: the shared adversary_script_command helper points every spawned
  script at its own throwaway lock path, isolating the run_checks tests from
  each other and from any real review running on the same machine.
- tests: adversary_run_checks_stack_lock_path_is_env_overridable pins the
  override (lock file appears at the overridden path across two sequential
  runs; no lock file is created when no commands are declared).
This commit is contained in:
Dark-Alex-17 committed 2026-09-17 10:49:35 -06:00
1 parent bfa058130a
commit 5bd1174ddd
2 files changed
+88 -4

No files matched your search

+16 -4
View File
@@ -31,7 +31,9 @@ in-script degradation would never fire. Each command gets
min(PER_COMMAND_TIMEOUT_SECS, remaining budget); commands the deadline
leaves no budget for are recorded as {"cmd", "skipped": "deadline"} entries,
so the in-script handling stays authoritative. The env var
ADVERSARY_RUN_CHECKS_DEADLINE_SECS overrides the deadline (test seam).
ADVERSARY_RUN_CHECKS_DEADLINE_SECS overrides the deadline (test seam), and
ADVERSARY_STACK_LOCK_PATH overrides the review-stack lock file location
(isolation seam — see acquire_stack_lock).
Process hygiene: commands run through the shell. On POSIX each command gets
its own session (start_new_session) and a timeout SIGKILLs the whole process
@@ -51,14 +53,20 @@ GRAPH_STATE_FILE, so an inherited live state file would silently win.
import json
import os
import signal
import fcntl
import subprocess
import sys
import time
try:
import fcntl
except ImportError: # non-POSIX (Windows): advisory flock unavailable
fcntl = None
PER_COMMAND_TIMEOUT_SECS = 900
TOTAL_DEADLINE_SECS = float(os.environ.get("ADVERSARY_RUN_CHECKS_DEADLINE_SECS") or 3300)
STACK_LOCK_PATH = os.path.expanduser("~/.cache/coyote/review-stack.lock")
STACK_LOCK_PATH = os.environ.get("ADVERSARY_STACK_LOCK_PATH") or os.path.expanduser(
"~/.cache/coyote/review-stack.lock"
)
STACK_LOCK_WAIT_SECS = float(os.environ.get("ADVERSARY_STACK_LOCK_WAIT_SECS") or 1200)
@@ -67,7 +75,11 @@ def acquire_stack_lock():
well-known path; the probe is instructed to flock the same file around
stack boot/teardown). Bounded wait so a hogged lock can never turn into a
false red verification: on timeout we proceed WITHOUT the lock and note it
on stderr. The fd is held for the process lifetime (released on exit)."""
on stderr. The fd is held for the process lifetime (released on exit).
On non-POSIX platforms (no fcntl) the lock is skipped entirely."""
if fcntl is None:
sys.stderr.write("WARN: review-stack lock unsupported on this platform; running unlocked\n")
return None
try:
os.makedirs(os.path.dirname(STACK_LOCK_PATH), exist_ok=True)
fd = os.open(STACK_LOCK_PATH, os.O_CREAT | os.O_RDWR)