Fix review findings and add static-analyzer agent

Review-fix batch (owner-confirmed) on the multi-agent harness:

- board_lock: detach holder stdio so a captured `hold` cannot hang on the
  daemon's inherited pipe; probe locks by holder-pid liveness instead of a
  momentary flock, which could spuriously fail a concurrent acquirer
  (storm-tested: 1 winner in 10, 0/15 acquire failures under probe storm)
- hil_test: locked board renders a visible board-locked fail row so the
  report matches the exit code; stale marker cleared on a real re-run
- pr-babysit: autoPush now opt-in (default dry run); resolve recipe
  paginates reviewThreads; post-push resolve gets issue-comment fallback
- validate: size stage honors non-default base via --base-branch; pvs
  stage delegated to the new agent
- new static-analyzer agent (sonnet): PVS-Studio SAST+MISRA for one
  board, structured findings gated on files changed vs base

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rn1AN5DsTdFhRwhugfgKZi
This commit is contained in:
hathach
2026-07-10 23:28:09 +07:00
parent e3dd9245ef
commit 0557655afb
7 changed files with 124 additions and 35 deletions

View File

@ -49,17 +49,22 @@ def read_info(board: str):
def is_locked(board: str) -> bool:
"""True if some live process currently holds the flock."""
path = lock_path(board)
if not os.path.exists(path):
"""True if the recorded holder process is still alive.
Deliberately never touches the flock: even a momentary probe lock would
make a concurrent acquirer's LOCK_NB attempt fail spuriously. The flock
taken by acquirers themselves stays the only authority."""
info = read_info(board)
pid = info.get('pid') if isinstance(info, dict) else None
if not isinstance(pid, int) or pid <= 0:
return False
with open(path) as f:
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
fcntl.flock(f, fcntl.LOCK_UN)
return False
except OSError:
return True
try:
os.kill(pid, 0)
except ProcessLookupError:
return False
except PermissionError:
return True # alive but owned by another user (e.g. the CI runner)
return True
def cmd_hold(boards, reason):
@ -91,6 +96,13 @@ def cmd_hold(boards, reason):
os._exit(0)
# holder (grandchild): acquire all flocks, signal the parent, sleep until killed
os.close(r_fd)
# Detach stdio: a `hold` whose output is captured must see EOF when the
# front-end exits — the immortal holder must not keep that pipe open.
devnull = os.open(os.devnull, os.O_RDWR)
for std_fd in (0, 1, 2):
os.dup2(devnull, std_fd)
if devnull > 2:
os.close(devnull)
try:
handles = []
for b in boards:

View File

@ -1704,7 +1704,9 @@ def test_board(board: Board) -> tuple[str, int, list[str], list]:
_lock_fh = acquire_board_lock(name)
except RuntimeError as e:
log_line(f'{name:25} {STATUS_FAILED}: {e}')
return name, 1, [], []
# visible report row so the ❌ matches the exit code; failed-tests stays
# empty so a re-run repeats the whole board (no bogus -bt test filter)
return name, 1, [], [(name, {'board-locked': 'fail'})]
try:
# default to all tests
test_list = []
@ -1831,7 +1833,11 @@ def accumulate_report(mret: list, report_dir: Path, fresh: bool) -> str:
pass # corrupt/old sidecar: start fresh
# merge this run: current cells override prior for boards/tests that ran
for _, _, _, rows in mret:
for name, _, _, rows in mret:
if rows and not any('board-locked' in cells for _, cells in rows):
# board ran for real this time: clear a stale lock-failure cell
# (its row is keyed by board name; test rows may be variant names)
acc.get(name, {}).pop('board-locked', None)
for row_label, cells in rows:
acc.setdefault(row_label, {}).update(cells)