#!/usr/bin/env python3 """Receiver-side child process for the snapshot_link smoke test. Reads CLI args, brings up a SnapshotPeer with a registered recv buffer, writes endpoint metadata to a control file, then loops: wait for size signal, verify recv buffer, write ack. Status events are printed as single-line JSON to stdout for parent to parse. """ from __future__ import annotations import argparse import ctypes import hashlib import json import sys import time from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src")) def _pattern_byte(i: int, seed: int) -> int: return (i * 2654435761 + seed) & 0xFF def _fill_pattern(buf, length: int, seed: int) -> None: tile_size = 4096 tile = bytes(_pattern_byte(i, seed) for i in range(tile_size)) tile_arr = (ctypes.c_ubyte * tile_size).from_buffer_copy(tile) n_full = length // tile_size rem = length - n_full * tile_size base = ctypes.addressof(buf) src_addr = ctypes.addressof(tile_arr) for k in range(n_full): ctypes.memmove(base + k * tile_size, src_addr, tile_size) if rem: ctypes.memmove(base + n_full * tile_size, src_addr, rem) def _emit(d: dict) -> None: print(json.dumps(d), flush=True) def main(): ap = argparse.ArgumentParser() ap.add_argument("--host", required=True) ap.add_argument("--port", type=int, required=True) ap.add_argument("--ib", required=True) ap.add_argument("--max-bytes", type=int, required=True) ap.add_argument("--control-path", required=True) ap.add_argument("--sizes", required=True, help="comma-separated bytes") args = ap.parse_args() sizes = [int(s) for s in args.sizes.split(",")] from agentic_pd_hybrid.snapshot_link import SnapshotPeer try: peer = SnapshotPeer( host=args.host, port=args.port, ib_device=args.ib, receive_capacity_bytes=args.max_bytes, ) except Exception as e: import traceback _emit({"event": "init-failed", "error": repr(e), "tb": traceback.format_exc()}) sys.exit(2) endpoint = peer.endpoint Path(args.control_path).write_text(json.dumps({ "session_id": endpoint.session_id, "base_ptr": endpoint.base_ptr, "capacity_bytes": endpoint.capacity_bytes, "ready": True, })) _emit({"event": "endpoint-ready", "session_id": endpoint.session_id, "base_ptr": endpoint.base_ptr, "capacity": endpoint.capacity_bytes}) cp = Path(args.control_path) for size in sizes: if size > args.max_bytes: continue signal_path = cp.with_suffix(f".do{size}") ack_path = cp.with_suffix(f".ack{size}") deadline = time.time() + 120.0 while time.time() < deadline: if signal_path.exists(): break time.sleep(0.05) else: _emit({"event": "no-signal-timeout", "size": size}) continue try: seed = int(signal_path.read_text().strip()) except Exception as e: _emit({"event": "signal-parse-error", "size": size, "err": repr(e)}) continue expected_arr = (ctypes.c_ubyte * size)() _fill_pattern(expected_arr, size, seed) expected_hash = hashlib.sha256(bytes(expected_arr)).hexdigest() recv_bytes = peer.read_bytes(0, size) recv_hash = hashlib.sha256(recv_bytes).hexdigest() ok = recv_hash == expected_hash _emit({ "event": "verify", "size": size, "ok": ok, "expected_sha": expected_hash[:16], "got_sha": recv_hash[:16], "first8_recv": recv_bytes[:8].hex(), "last8_recv": recv_bytes[-8:].hex(), }) ack_path.write_text("done") peer.close() _emit({"event": "receiver-done"}) if __name__ == "__main__": main()