atlas-iac/services/hermes/scripts/cli_lane_records.py

357 lines
11 KiB
Python

#!/usr/bin/env python3
"""Bounded inode-pinned reads and validation for Hermes terminal records."""
from __future__ import annotations
import json
import os
import stat
from pathlib import Path
from typing import Any
import cli_lane_goal
from cli_lane_config import (
MAX_TERMINAL_RECORD_BYTES,
QUARANTINE_HASH_BYTES,
RESULT_SCHEMA,
Route,
TerminalIdentity,
TerminalRecoverySnapshot,
TerminalSnapshot,
canonical_run_id,
utc_now,
)
from cli_lane_files import (
_candidate_path,
_terminal_identity,
_terminal_path,
atomic_json,
)
def _read_bounded(descriptor: int, limit: int) -> bytes:
"""Read at most ``limit`` bytes from a regular file descriptor."""
chunks: list[bytes] = []
remaining = max(0, limit)
while remaining:
chunk = os.read(descriptor, min(64 * 1024, remaining))
if not chunk:
break
chunks.append(chunk)
remaining -= len(chunk)
return b"".join(chunks)
def _open_small_json_snapshot(path: Path) -> TerminalSnapshot | None:
"""Open and pin one bounded, singly-linked JSON document."""
directory_flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
directory_flags |= getattr(os, "O_NOFOLLOW", 0)
file_flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)
directory = None
descriptor = None
try:
directory = os.open(path.parent, directory_flags)
descriptor = os.open(path.name, file_flags, dir_fd=directory)
opened = os.fstat(descriptor)
if (
not stat.S_ISREG(opened.st_mode)
or opened.st_nlink != 1
or opened.st_size > MAX_TERMINAL_RECORD_BYTES
):
return None
payload = _read_bounded(descriptor, MAX_TERMINAL_RECORD_BYTES + 1)
if len(payload) > MAX_TERMINAL_RECORD_BYTES:
return None
current = os.stat(
path.name,
dir_fd=directory,
follow_symlinks=False,
)
if (
current.st_dev != opened.st_dev
or current.st_ino != opened.st_ino
or current.st_size != opened.st_size
or len(payload) != opened.st_size
):
return None
value = json.loads(payload.decode("utf-8"))
if not isinstance(value, dict):
return None
snapshot = TerminalSnapshot(value, opened, descriptor, directory)
descriptor = None
directory = None
return snapshot
except (OSError, UnicodeError, json.JSONDecodeError):
return None
finally:
if descriptor is not None:
os.close(descriptor)
if directory is not None:
os.close(directory)
def _open_terminal_recovery_snapshot(path: Path) -> TerminalRecoverySnapshot | None:
"""Pin the entry recovery inspected, even when its payload is malformed."""
directory_flags = os.O_RDONLY | getattr(os, "O_DIRECTORY", 0)
directory_flags |= getattr(os, "O_NOFOLLOW", 0)
file_flags = os.O_RDONLY | getattr(os, "O_NOFOLLOW", 0)
directory = None
descriptor = None
try:
directory = os.open(path.parent, directory_flags)
observed = os.stat(
path.name,
dir_fd=directory,
follow_symlinks=False,
)
if not stat.S_ISREG(observed.st_mode):
snapshot = TerminalRecoverySnapshot(
None,
observed,
None,
directory,
b"",
"non-regular",
)
directory = None
return snapshot
try:
descriptor = os.open(path.name, file_flags, dir_fd=directory)
except OSError:
snapshot = TerminalRecoverySnapshot(
None,
observed,
None,
directory,
b"",
"open-failed",
)
directory = None
return snapshot
opened = os.fstat(descriptor)
if opened.st_dev != observed.st_dev or opened.st_ino != observed.st_ino:
os.close(descriptor)
descriptor = None
snapshot = TerminalRecoverySnapshot(
None,
observed,
None,
directory,
b"",
"identity-changed-during-open",
)
directory = None
return snapshot
observed = opened
if observed.st_nlink != 1:
snapshot = TerminalRecoverySnapshot(
None,
observed,
descriptor,
directory,
b"",
"hardlinked",
)
descriptor = None
directory = None
return snapshot
if observed.st_size > MAX_TERMINAL_RECORD_BYTES:
prefix = _read_bounded(descriptor, QUARANTINE_HASH_BYTES)
snapshot = TerminalRecoverySnapshot(
None,
observed,
descriptor,
directory,
prefix,
"oversized",
)
descriptor = None
directory = None
return snapshot
payload = _read_bounded(descriptor, MAX_TERMINAL_RECORD_BYTES + 1)
after_read = os.fstat(descriptor)
unchanged = (
after_read.st_dev == observed.st_dev
and after_read.st_ino == observed.st_ino
and after_read.st_size == observed.st_size
and after_read.st_mtime_ns == observed.st_mtime_ns
and after_read.st_ctime_ns == observed.st_ctime_ns
and len(payload) == observed.st_size
)
document = None
invalid_reason = "unstable-payload"
if unchanged:
try:
parsed = json.loads(payload.decode("utf-8"))
if isinstance(parsed, dict):
document = parsed
invalid_reason = None
else:
invalid_reason = "non-object-payload"
except (UnicodeError, json.JSONDecodeError):
invalid_reason = "malformed-payload"
snapshot = TerminalRecoverySnapshot(
document,
after_read,
descriptor,
directory,
payload[:QUARANTINE_HASH_BYTES],
invalid_reason,
)
descriptor = None
directory = None
return snapshot
except OSError:
return None
finally:
if descriptor is not None:
os.close(descriptor)
if directory is not None:
os.close(directory)
def _open_terminal_snapshot(
path: Path,
identity: TerminalIdentity,
) -> TerminalSnapshot | None:
"""Open a terminal journal only after its lexical identity is verified."""
if _terminal_identity(path) != identity:
return None
return _open_small_json_snapshot(path)
def _load_terminal_json(path: Path, identity: TerminalIdentity) -> dict[str, Any]:
"""Read one small, singly-linked journal through its validated directory."""
snapshot = _open_terminal_snapshot(path, identity)
if snapshot is None:
return {}
try:
return snapshot.document
finally:
snapshot.close()
def _load_small_json(path: Path) -> dict[str, Any]:
"""Read one bounded immutable evidence document without following links."""
snapshot = _open_small_json_snapshot(path)
if snapshot is None:
return {}
try:
return snapshot.document
finally:
snapshot.close()
def _persist_candidate(
state: dict[str, Any],
state_file: Path,
structured: dict[str, Any],
*,
route: Route,
returncode: int,
goal_turn: int,
) -> Path:
"""Persist every structured response before any judge can supersede it."""
sequence = max(0, int(state.get("candidate_sequence", 0) or 0)) + 1
path = _candidate_path(state_file, state.get("run_id"), sequence)
while path.exists():
sequence += 1
path = _candidate_path(state_file, state.get("run_id"), sequence)
atomic_json(
path,
{
"board": state.get("board"),
"task_id": state.get("task_id"),
"expected_run_id": state.get("run_id"),
"goal_turn": goal_turn,
"provider": route.provider,
"model": route.model,
"effort": route.effort,
"returncode": returncode,
"structured": structured,
"recorded_at": utc_now(),
},
)
state["candidate_sequence"] = sequence
state["last_candidate_file"] = str(path)
atomic_json(state_file, state)
return path
def _write_terminal_record(
state_file: Path,
*,
board: str,
task_id: str,
run_id: Any,
structured: dict[str, Any],
summary: str,
metadata: dict[str, Any],
) -> tuple[Path, dict[str, Any]]:
"""Journal an accepted result before attempting the Kanban transaction."""
if type(run_id) is not int or canonical_run_id(run_id) is None:
raise ValueError("terminal run ID must be a positive SQLite int64")
path = _terminal_path(state_file, run_id)
record = {
"board": board,
"task_id": task_id,
"expected_run_id": run_id,
"result": json.dumps(structured, sort_keys=True),
"summary": summary,
"metadata": metadata,
"kanban_state": "pending",
"recorded_at": utc_now(),
}
atomic_json(path, record)
return path, record
def _terminal_record_valid(
record: dict[str, Any],
identity: TerminalIdentity | None = None,
) -> bool:
"""Reject malformed or non-terminal replay journals without side effects."""
if not isinstance(record, dict):
return False
required_strings = ("board", "task_id", "result", "summary")
if not all(
isinstance(record.get(key), str) and record[key]
for key in required_strings
):
return False
expected_run_id = record.get("expected_run_id")
if (
type(expected_run_id) is not int
or canonical_run_id(expected_run_id) is None
):
return False
if not isinstance(record.get("metadata"), dict):
return False
try:
structured = json.loads(record["result"])
except (TypeError, json.JSONDecodeError):
return False
if not isinstance(structured, dict):
return False
required = set(RESULT_SCHEMA["required"])
if set(structured) != required:
return False
if (
type(structured["status"]) is not str
or structured["status"] not in cli_lane_goal.RESULT_STATUSES
or type(structured["summary"]) is not str
or not structured["summary"].strip()
):
return False
for key in ("changed_files", "tests_run", "artifacts", "findings", "blockers"):
value = structured[key]
if type(value) is not list or any(type(item) is not str for item in value):
return False
valid = (
structured["status"] == "completed"
and structured["blockers"] == []
and cli_lane_goal.unfinished_result_reason(structured) is None
and record["summary"] == structured["summary"]
and record.get("kanban_state") in {"pending", "prepared", "committed"}
)
if not valid or identity is None:
return valid
return (
record.get("board") == identity.board
and record.get("task_id") == identity.task_id
and record.get("expected_run_id") == identity.run_id
and record.get("kanban_state") == identity.state
)