Files
rune/scripts/check_migration_mutants.py
Nick 5b3ff5bcc0 feat(tui+substrate): skills + G7 hardening delta + SLO records — ALL 23 packages green
- tui/skills (G14, LAST package): pattern proposals with consent-first
  law (nothing drafted until explicit y), activation views with test
  evidence, D10 self-transformation proposal-only truth (fixture walks
  the whole keyboard proving no activation path), consent-drift guard
- G7 delta over mid-flight 5db2e249: SSH replay fixed to GREEN (setsid
  controlling-tty, boot-ACK wait, burst-boundary parsing, JSON drain) —
  main-session verified: 4/4 mutants RED, ssh GREEN, 21/21 pytest
- verification/slowest-target-slo/: schema + validated records; budgets
  proposed-only (agreed=false per GAP-BT-05)
- 23/23 tui packages green by main-session own run (cmd/rune-tui
  mid-write by wave H1 version wiring — excluded here, lands with H1)
2026-10-16 11:42:44 +01:00

1141 lines
46 KiB
Python

#!/usr/bin/env python3
"""check_migration_mutants.py — phase 0B task 0.11: the migration-oracle
mutant suite + SSH-profile replay + slowest-target SLO recording.
I-R2-008 (remaining gate): "Migration-oracle mutant suite (the named
four mutants) plus dead-Python verification after quarantine expiry."
The parity unit is the authority-preserving action/receipt trace —
action IDs, key scopes, focus transitions, breakpoint outcomes, theme
tokens, oracle traces — NEVER widget parity. This script:
1. Builds the AUTHORITATIVE trace (the migration oracle baseline) from
the frozen receipts at research/tui-frontier-r2/receipts/ (focus,
resize, burst, keyprobe) plus explicitly pinned values for what the
receipts do not yet carry (recovery segment, launcher binding,
theme tokens — each labeled in the trace's `pinned` block). The
baseline is sha256-pinned in-module: drift is a RED, never silent.
2. Applies the four NAMED mutants — key scope, receipt origin,
recovery ordering, launcher target — and requires the oracle to go
RED on every one, with the named invariant among the fired checks.
A mutant that survives (or a harness that fires nothing) is a
failed suite: FALSE GREEN IS NOT RETIREMENT EVIDENCE.
3. --ssh-replay: replays the FIXED input/burst/resize traces under an
SSH profile (real ssh when RUNE_SSH_TARGET/--ssh-target answers
BatchMode; otherwise an honest PTY-profile fallback, TERM pinned
xterm-256color like the keyprobe receipts). The known
transport-stable divergence (atomic ESC+q -> "alt+q" vs split ->
"esc","q", EV-BT-004) is an EXPECTATION, replayed as such.
4. --slo-record/--slo-validate: the slowest-target SLO recording
structure (verification/slowest-target-slo/) — budgets proposed
from the lab receipts, `agreed: false` until the owner ratifies
(GAP-BT-05: no agreed SLO yet), every record honest about whether
its target is actually the slowest supported one.
This is the MIGRATION oracle, distinct from the lab iso-gate mutant
suite (receipts/at-oracle/mutants — action-isomorphism class). The
unified runner declares this suite as member `migration_mutants`
(scripts/unified_check.py MEMBERS); wiring it live (run:True + IMPL
entry) is a main-session edit — this file lands standalone first.
Usage:
python scripts/check_migration_mutants.py # mutant suite
python scripts/check_migration_mutants.py --ssh-replay # trace replay
python scripts/check_migration_mutants.py --slo-record # SLO record
python scripts/check_migration_mutants.py --slo-validate FILE
python scripts/check_migration_mutants.py --pins # print pins
"""
from __future__ import annotations
import argparse
import copy
import hashlib
import json
import os
import subprocess
import sys
import time
from pathlib import Path
REPO = Path(__file__).resolve().parent.parent
RECEIPTS_DIR = REPO / "research/tui-frontier-r2/receipts"
RECEIPT_OUT_DIR = RECEIPTS_DIR / "migration-mutants"
SLO_DIR = REPO / "verification/slowest-target-slo"
# The canonical recovery phase order — the ordering law the recovery
# mutant violates. Pinned (constructed authority record; real recovery
# receipts land with tasks 7.1-7.3 and must re-pin against this oracle).
RECOVERY_PHASES = ["checkpoint", "degrade", "restart", "restore",
"resume", "reattach"]
# Keys that carry LANE authority (focus transitions), per the frozen
# focus receipt's key/action pairing. Everything modal-scoped while the
# approve dialog is open; plain keys otherwise.
LANE_KEYS = {"1", "2", "tab"}
# ── canonical json + pins ─────────────────────────────────────────────────
def canonical_json(obj) -> str:
return json.dumps(obj, sort_keys=True, separators=(",", ":"),
ensure_ascii=False)
def canonical_sha256(obj) -> str:
return hashlib.sha256(canonical_json(obj).encode()).hexdigest()
# ── fixed-trace loading (the frozen receipts) ─────────────────────────────
def load_fixed_traces(receipts_dir: Path = RECEIPTS_DIR) -> dict:
"""Load the fixed burst/resize/input traces. These files are FROZEN
research receipts — read-only inputs, never rewritten here."""
bt = receipts_dir / "bubbletea"
ao = receipts_dir / "at-oracle"
paths = {
"focus": bt / "focus.json",
"resize": bt / "resize.json",
"burst": bt / "burst-n5000-slow0.json",
"keyprobe_atomic": ao / "keyprobe-ssh-atomic-1.json",
"keyprobe_split": ao / "keyprobe-ssh-split-1.json",
}
loaded = {}
for name, p in paths.items():
if not p.is_file():
raise FileNotFoundError(
f"fixed trace receipt missing: {p} — the migration "
f"oracle grounds itself in the frozen receipts; refusing "
f"to invent data")
loaded[name] = json.loads(p.read_text())
return loaded
def _key_scope(action_id: str, key: str, modal_open: bool) -> str:
"""Scope under whose authority a key was interpreted — the field the
key-scope mutant corrupts. Derived from the frozen focus receipt's
evidence (q_swallowed_in_modal, modal open/close pairing)."""
if modal_open:
return "modal.approve"
if action_id.startswith("modal.open"):
return "task-list"
if key in LANE_KEYS:
return "lane"
if action_id.startswith("nav."):
return "task-list"
return "input"
def build_baseline(receipts_dir: Path = RECEIPTS_DIR) -> dict:
"""Project the authoritative action/receipt trace from the frozen
receipts. Deterministic: same receipts in, same trace out, pinned
by BASELINE_SHA256."""
tr = load_fixed_traces(receipts_dir)
focus = tr["focus"]
actions = focus["actions"]
keys = focus["keys_via_parser"]
session = "sess-authority-0001"
steps = []
modal_open = False
focus_state = "chat"
for i, (action_id, key) in enumerate(zip(actions, keys), start=1):
scope = _key_scope(action_id, key, modal_open)
before = focus_state
if action_id == "focus.chat":
focus_state = "chat"
elif action_id == "focus.tasks":
focus_state = "tasks"
# focus_restored (frozen receipt): modal open/swallow/cancel
# never moves focus; nav stays on the tasks lane.
steps.append({
"seq": i,
"kind": "action",
"action_id": action_id,
"key": key,
"key_scope": scope,
"focus_before": before,
"focus_after": focus_state,
"breakpoint_outcome": "none",
"receipt": {
"id": f"rcpt-{action_id or 'noop'}-seq{i}",
"origin_session": session,
"origin_kind": "live",
},
})
if action_id == "modal.open.approve":
modal_open = True
elif action_id == "modal.cancel.approve":
modal_open = False
# Recovery segment: pinned constructed authority record (the frozen
# receipts carry no failure/recovery trace yet — tasks 7.1-7.3
# record the real one; this segment pins the ORDERING LAW now).
snapshot_id = "snap-authority-0001"
checkpoint_seq = len(steps) + 1
for j, phase in enumerate(RECOVERY_PHASES, start=checkpoint_seq):
step = {
"seq": j,
"kind": "recovery",
"phase": phase,
"breakpoint_outcome": "none",
"receipt": {
"id": f"rcpt-recovery-{phase}-seq{j}",
"origin_session": session,
"origin_kind": "live",
},
}
if phase == "checkpoint":
step["snapshot_id"] = snapshot_id
step["task_id"] = "task-2"
if phase == "restore":
step["snapshot_id"] = snapshot_id
if phase == "resume":
step["resumes_seq"] = checkpoint_seq
if phase == "reattach":
step["term"] = "xterm-256color"
steps.append(step)
launcher = {
"kind": "native",
"executable": "tui/cmd/rune-tui",
"digest": "sha256:" + hashlib.sha256(
b"qualified-native-launcher-of-record:v1:tui/cmd/rune-tui"
).hexdigest(),
"attestation": "scripts/build_paired.sh+build_attestation.verify_build_pairing",
}
return {
"trace_id": "migration-authority-trace-1",
"session": session,
"parity_unit": "authority-preserving action/receipt trace",
"launcher": launcher,
"theme_tokens": ["accent=medium_purple", "surface=panel",
"text=default", "muted=dim"],
"steps": steps,
"pinned": {
"recovery_segment": "constructed authority record; real "
"recovery receipts land with tasks 7.1-7.3",
"theme_tokens": "pinned token list (visual-system parity "
"unit); receipts do not yet carry tokens",
"source_receipts": {
name: canonical_sha256(doc)
for name, doc in tr.items()
},
},
}
# Pins — filled after the builder froze (update DELIBERATELY, never
# silently: `--pins` prints current values for re-pinning).
BASELINE_SHA256 = "0792967580af18f8ca15aa144566f5cd1d27f1eacf06a04c3391b1da2671dbdf"
LAUNCHER_PIN = "e0ec3949081e131778f30a81d89aa5d123dd6e6339570e3c9add9844e7c43e8b"
# ── the migration oracle ──────────────────────────────────────────────────
def check_trace(candidate: dict, baseline: dict) -> dict:
"""Judge a candidate native trace against the authoritative trace.
GREEN only when every authority-preserving invariant holds; RED
otherwise, with `fired_checks` naming the catching invariants (the
receipt convention from the lab mutant suite). The oracle compares
action/receipt traces, never pixels or widget counts.
"""
fired: list[str] = []
details: list[str] = []
def fire(name: str, why: str) -> None:
if name not in fired:
fired.append(name)
details.append(f"{name}: {why}")
# -- envelope -----------------------------------------------------------
for field in ("trace_id", "session", "parity_unit"):
if candidate.get(field) != baseline.get(field):
fire("trace_envelope_exact",
f"{field} {candidate.get(field)!r} != {baseline.get(field)!r}")
# -- action ids (order + values) ---------------------------------------
c_actions = [(s["seq"], s["action_id"]) for s in candidate.get("steps", [])
if s.get("kind") == "action"]
b_actions = [(s["seq"], s["action_id"]) for s in baseline["steps"]
if s["kind"] == "action"]
if c_actions != b_actions:
fire("action_ids_exact", "action id sequence diverged")
# -- key scopes: every key under the same authority ---------------------
b_by_seq = {s["seq"]: s for s in baseline["steps"]}
for s in candidate.get("steps", []):
ref = b_by_seq.get(s.get("seq"))
if ref is None:
continue
if s.get("key") != ref.get("key") or \
s.get("key_scope") != ref.get("key_scope"):
fire("key_scopes_exact",
f"seq {s.get('seq')}: key/scope "
f"({s.get('key')!r},{s.get('key_scope')!r}) != "
f"({ref.get('key')!r},{ref.get('key_scope')!r})")
# -- receipts stay bound to their origin --------------------------------
for s in candidate.get("steps", []):
ref = b_by_seq.get(s.get("seq"))
if ref is None:
continue
rcpt = s.get("receipt") or {}
rref = ref.get("receipt") or {}
expected_id = f"rcpt-{s.get('action_id') or 'noop'}-seq{s.get('seq')}" \
if s.get("kind") == "action" else rref.get("id")
if (rcpt.get("origin_session") != candidate.get("session")
or rcpt.get("origin_session") != rref.get("origin_session")
or rcpt.get("origin_kind") != rref.get("origin_kind")
or rcpt.get("id") != rref.get("id")
or rcpt.get("id") != expected_id):
fire("receipts_origin_bound",
f"seq {s.get('seq')}: receipt origin/id not bound "
f"({rcpt.get('origin_session')!r}, {rcpt.get('id')!r})")
# -- recovery ordering ---------------------------------------------------
c_phases = [s.get("phase") for s in candidate.get("steps", [])
if s.get("kind") == "recovery"]
if c_phases != RECOVERY_PHASES:
fire("recovery_ordering_valid",
f"phase order {c_phases} != canonical {RECOVERY_PHASES}")
else:
checkpoints: dict[str, int] = {}
for s in candidate.get("steps", []):
if s.get("kind") == "recovery":
if s.get("phase") == "checkpoint":
checkpoints[s.get("snapshot_id")] = s["seq"]
if s.get("phase") == "restore" and \
checkpoints.get(s.get("snapshot_id")) is None:
fire("recovery_ordering_valid",
f"restore seq {s['seq']} references unknown/"
f"later snapshot {s.get('snapshot_id')!r}")
if s.get("phase") == "resume" and \
s.get("resumes_seq") not in checkpoints.values():
fire("recovery_ordering_valid",
f"resume seq {s['seq']} resumes from non-checkpoint "
f"{s.get('resumes_seq')!r}")
# -- launcher target ------------------------------------------------------
launcher = candidate.get("launcher") or {}
if launcher != baseline["launcher"]:
fire("launcher_target_attested",
f"launcher binding != qualified native binding of record "
f"({launcher.get('kind')!r}, {launcher.get('executable')!r})")
launcher_text = canonical_json(launcher).lower()
if launcher.get("kind") != "native" or "python" in launcher_text:
# exact-token semantics: a fallback python launcher must fire even
# if someone re-pins the binding (NEG-003/NEG-004 lesson)
fire("launcher_target_attested",
"launcher is not the attested native target (python token "
"or non-native kind in the launch chain)")
# -- focus transitions + breakpoints + theme tokens -----------------------
for s in candidate.get("steps", []):
ref = b_by_seq.get(s.get("seq"))
if ref is None:
continue
if (s.get("focus_before"), s.get("focus_after")) != \
(ref.get("focus_before"), ref.get("focus_after")):
fire("focus_transitions_exact",
f"seq {s.get('seq')}: focus transition diverged")
if s.get("breakpoint_outcome") != ref.get("breakpoint_outcome"):
fire("breakpoint_outcomes_exact",
f"seq {s.get('seq')}: breakpoint outcome diverged")
if candidate.get("theme_tokens") != baseline["theme_tokens"]:
fire("theme_tokens_exact", "theme token set diverged")
return {"verdict": "GREEN" if not fired else "RED",
"fired_checks": fired,
"details": details}
def trace_diff(a: dict, b: dict, prefix: str = "") -> list[str]:
"""Leaf paths where two traces differ — keeps mutants surgical."""
diffs: list[str] = []
if isinstance(a, dict) and isinstance(b, dict):
for k in sorted(set(a) | set(b)):
if k not in a or k not in b:
diffs.append(f"{prefix}{k}")
else:
diffs += trace_diff(a[k], b[k], f"{prefix}{k}.")
elif isinstance(a, list) and isinstance(b, list):
if len(a) != len(b):
diffs.append(f"{prefix[:-1]}")
for i, (x, y) in enumerate(zip(a, b)):
diffs += trace_diff(x, y, f"{prefix}[{i}].")
elif a != b:
diffs.append(prefix[:-1] if prefix else "<root>")
return diffs
# ── the four named mutants ────────────────────────────────────────────────
def mut_key_scope(trace: dict) -> dict:
"""KEY SCOPE: a key claims authority it did not have — the modal-
swallowed 'q' (frozen receipt: q_swallowed_in_modal) is recorded as
if interpreted under plain input scope. Classic escaped-modal
regression: the same byte sequence acting under the wrong authority.
"""
for s in trace["steps"]:
if s.get("kind") == "action" and s.get("action_id") == "modal.swallow":
s["key_scope"] = "input"
break
return trace
def mut_receipt_origin(trace: dict) -> dict:
"""RECEIPT ORIGIN: one receipt claims it was minted by a different
session — the forged-provenance regression (a receipt attesting
work it did not witness; provenance would be lying)."""
for s in trace["steps"]:
if s.get("kind") == "action":
s["receipt"]["origin_session"] = "sess-other-9999"
break
return trace
def mut_recovery_order(trace: dict) -> dict:
"""RECOVERY ORDERING: resume executes before restore — the crash-
recovery regression (resuming from state that was never restored,
the classic lost-update on restart)."""
steps = trace["steps"]
idx = {s.get("phase"): i for i, s in enumerate(steps)
if s.get("kind") == "recovery"}
i_restore, i_resume = idx["restore"], idx["resume"]
steps[i_restore], steps[i_resume] = steps[i_resume], steps[i_restore]
return trace
def mut_launcher_target(trace: dict) -> dict:
"""LAUNCHER TARGET: the trace is produced by a python fallback
launcher instead of the attested native executable — the
silent-Python-fallback regression I-R2-008 exists to kill (a green
harness that invokes Python after quarantine is not evidence)."""
trace["launcher"] = {
"kind": "python-fallback",
"executable": "python3 src/rune/rune_tui.py",
"digest": "sha256:unattested",
"attestation": "none",
}
return trace
MUTANTS = {
"mut-key-scope": mut_key_scope,
"mut-receipt-origin": mut_receipt_origin,
"mut-recovery-order": mut_recovery_order,
"mut-launcher-target": mut_launcher_target,
}
MUTANT_EXPECTED_CHECK = {
"mut-key-scope": "key_scopes_exact",
"mut-receipt-origin": "receipts_origin_bound",
"mut-recovery-order": "recovery_ordering_valid",
"mut-launcher-target": "launcher_target_attested",
}
def run_mutants(baseline: dict, mutants: dict = MUTANTS) -> list[dict]:
"""Run each mutant against the oracle. A mutant SURVIVING (green) or
firing without its named invariant is a failed harness row."""
rows = []
for name, fn in mutants.items():
expected = MUTANT_EXPECTED_CHECK.get(name, "__unknown__")
cand = fn(copy.deepcopy(baseline))
verdict = check_trace(cand, baseline)
rows.append({
"mutant": name,
"oracle_verdict": verdict["verdict"],
"fired_checks": verdict["fired_checks"],
"expected_check": expected,
"fired_expected_check": expected in verdict["fired_checks"],
"caught": verdict["verdict"] == "RED"
and expected in verdict["fired_checks"],
})
return rows
def run_suite(receipts_dir: Path = RECEIPTS_DIR,
receipt_path: Path | None = None) -> dict:
"""The suite: baseline GREEN (no false positive) + 4/4 named mutants
RED with their named invariants. Receipt written to the research
receipts tree (mirrors the at-oracle mutant-suite convention)."""
baseline = build_baseline(receipts_dir)
drift = canonical_sha256(baseline) != BASELINE_SHA256
base_verdict = check_trace(baseline, baseline)
rows = run_mutants(baseline)
summary = {
"test": "migration_oracle_mutant_suite",
"harness": "scripts/check_migration_mutants.py (migration "
"oracle: authority-preserving action/receipt trace "
"parity — NOT the lab iso gate)",
"binding": "tasks.md 0.11; migration-and-retirement 'Deliberate "
"mutants'; I-R2-008 remaining gate",
"baseline": {
"sha256": canonical_sha256(baseline),
"pin_holds": not drift,
"oracle_verdict_on_self": base_verdict["verdict"],
"source_receipts": baseline["pinned"]["source_receipts"],
},
"mutants": rows,
"expectation": "baseline pin holds + oracle GREEN on baseline + "
"every named mutant RED with its named invariant",
"baseline_green": base_verdict["verdict"] == "GREEN" and not drift,
"all_mutants_red": bool(rows)
and all(r["caught"] for r in rows),
}
summary["verdict"] = "GREEN" if (summary["baseline_green"]
and summary["all_mutants_red"]) else "RED"
out = receipt_path or (RECEIPT_OUT_DIR / "mutant-summary.json")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(summary, indent=2, sort_keys=True) + "\n")
summary["_receipt_path"] = str(out)
return summary
# ── SSH-profile replay harness (fixed burst/resize/input traces) ──────────
#
# I-R2-006 remaining gate: "SSH channel full replay". The fixed traces
# live in the frozen receipts; this harness replays them under the SSH
# profile. Transport honesty: real ssh (BatchMode) when --ssh-target /
# RUNE_SSH_TARGET answers, else a PTY carrying the SSH profile the
# keyprobe receipts recorded (TERM=xterm-256color, tty stdin) — the
# receipt always names which transport actually ran.
KEY_NAME_BYTES = {"tab": b"\t", "alt+q": b"\x1bq"}
ESC_TIMEOUT_S = 0.05 # the vendored parser's pinned EscTimeout
SPLIT_GAP_S = 0.20 # > EscTimeout: split delivery, EV-BT-004
PROBE_SENTINEL = b"\x04\x04"
PROBE_MARKER = b"@@PROBE_JSON@@"
PROBE_SRC = r'''
import fcntl, json, os, select, signal, struct, sys, termios, time, tty
fd = sys.stdin.fileno()
tty.setraw(fd)
events, sizes = [], []
def _winch(sig, frm):
ws = fcntl.ioctl(fd, termios.TIOCGWINSZ, b"\0" * 8)
r, c = struct.unpack("HH", ws[:4])
sizes.append([time.time_ns(), r, c])
os.write(fd, ("\x11ACKR%dC%d\r" % (r, c)).encode())
signal.signal(signal.SIGWINCH, _winch)
ws0 = fcntl.ioctl(fd, termios.TIOCGWINSZ, b"\0" * 8)
r0, c0 = struct.unpack("HH", ws0[:4])
sizes.append([time.time_ns(), r0, c0])
os.write(fd, ("\x11ACKR%dC%d\r" % (r0, c0)).encode())
deadline = time.time() + float(sys.argv[1] if len(sys.argv) > 1 else 20)
while time.time() < deadline:
r, _, _ = select.select([fd], [], [], 0.05)
if not r:
continue
b = os.read(fd, 4096)
if PROBE_SENTINEL_VIEW in b:
break
events.append([time.time_ns(), b.hex()])
os.write(fd, b"\x06")
out = {"term": os.environ.get("TERM"), "initial_size": [r0, c0],
"events": events, "sizes": sizes,
"stdin_is_tty": os.isatty(fd)}
os.write(sys.stdout.fileno(), b"\n@@PROBE_JSON@@"
+ json.dumps(out).encode() + b"\n")
'''.replace("PROBE_SENTINEL_VIEW", repr(PROBE_SENTINEL))
# Session preamble: SIGWINCH reaches the probe only when the slave is
# its CONTROLLING terminal, which requires setsid-then-open (the
# pexpect pattern). PTY spawn uses this; over ssh the remote pty
# already provides one.
SESSION_PREAMBLE = r'''
import os, sys
os.setsid()
if len(sys.argv) > 2:
_f = os.open(sys.argv[2], os.O_RDWR)
os.dup2(_f, 0); os.dup2(_f, 1); os.dup2(_f, 2)
if _f > 2:
os.close(_f)
'''
def key_bytes(name: str) -> bytes:
if name in KEY_NAME_BYTES:
return KEY_NAME_BYTES[name]
return name.encode()
def parse_key_events(events: list) -> list[str]:
"""Reference parser for the FIXED trace alphabet with the pinned
50ms EscTimeout: ESC + next byte inside the window is one key
(alt+q); ESC alone past it is 'esc'. This is the harness's own
instrument (keyprobe convention) — it judges transport behavior,
it is not the product parser."""
keys: list[str] = []
flat: list[tuple[int, bytes]] = [(t, bytes.fromhex(h)) for t, h in events]
i = 0
while i < len(flat):
t, chunk = flat[i]
j = 0
while j < len(chunk):
b = chunk[j:j + 1]
if b == b"\x1b":
nxt_t, nxt_b = (flat[i + 1] if i + 1 < len(flat) else (0, b""))
same_chunk = j + 1 < len(chunk)
if same_chunk:
keys.append("alt+" + chunk[j + 1:j + 2].decode())
j += 2
continue
if nxt_b and (nxt_t - t) / 1e9 <= ESC_TIMEOUT_S:
keys.append("alt+" + nxt_b[:1].decode())
i += 1
j = len(chunk)
continue
keys.append("esc")
elif b == b"\t":
keys.append("tab")
else:
keys.append(b.decode())
j += 1
i += 1
return keys
def validate_frozen_records(traces: dict) -> list[str]:
"""Record-check the frozen burst/resize/focus receipts against their
own claims (determinism, no loss, no order violations, budgets
present). A corrupted receipt input is a RED replay input."""
problems = []
burst, resize, focus = (traces["burst"], traces["resize"],
traces["focus"])
# burst carries no `deterministic` field (that is a resize/focus
# claim) — check only the claims the receipt actually makes
if burst.get("order_violations") != 0 \
or burst.get("missing_seqs_sample") is not None \
or burst.get("last_seq_applied") != 4999 \
or not burst.get("final_contains_last") \
or burst.get("err", "") != "":
problems.append("frozen burst receipt fails its own claims")
if not resize.get("deterministic") or not resize.get("focus_preserved") \
or not resize.get("width_within_80") \
or resize.get("final_80x24_sha_r1") != resize.get("final_80x24_sha_r2"):
problems.append("frozen resize receipt fails its own claims")
if not focus.get("actions_expected") or not focus.get("keys_match_script") \
or not focus.get("q_swallowed_in_modal"):
problems.append("frozen focus receipt fails its own claims")
return problems
class _PtyTransport:
"""SSH-profile PTY: TERM=xterm-256color (what the keyprobe receipts
recorded), raw probe child on the slave, 80x24 initial winsize."""
name = "pty-ssh-profile"
def __init__(self):
import pty
import fcntl
import struct
import termios
self.master, slave = pty.openpty()
fcntl.ioctl(self.master, termios.TIOCSWINSZ,
struct.pack("HHHH", 24, 80, 0, 0))
slave_path = os.ttyname(slave)
os.close(slave) # the probe opens it itself AFTER setsid
env = dict(os.environ, TERM="xterm-256color")
self.proc = subprocess.Popen(
[sys.executable, "-s", "-c", SESSION_PREAMBLE + PROBE_SRC,
"20", slave_path],
stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, env=env, close_fds=True)
def wait_ready(self, duration_s: float = 5.0) -> bool:
"""Wait for the probe's initial size ACK — input written to the
master before the child opens the slave is dropped by the
kernel, so the harness must not feed the script early. Reads on
a master whose slave is not open yet raise EIO: that is
'not ready', not an error."""
import select
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline:
r, _, _ = select.select([self.master], [], [], 0.05)
if not r:
continue
try:
chunk = os.read(self.master, 65536)
except OSError:
continue
if b"ACKR" in chunk:
time.sleep(0.05) # let setraw settle before feeding
return True
return False
def write(self, data: bytes) -> None:
os.write(self.master, data)
def resize(self, rows: int, cols: int) -> None:
import fcntl
import struct
import termios
fcntl.ioctl(self.master, termios.TIOCSWINSZ,
struct.pack("HHHH", rows, cols, 0, 0))
def read_output(self, duration_s: float, done=None) -> bytes:
import select
buf = b""
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline:
r, _, _ = select.select([self.master], [], [], 0.05)
if r:
try:
buf += os.read(self.master, 65536)
except OSError:
break
if done is not None:
if done(buf):
break
elif PROBE_MARKER in buf:
# marker seen; keep draining until the JSON parses whole
if _extract_probe_json(buf) is not None:
break
return buf
def close(self):
try:
self.proc.terminate()
self.proc.wait(timeout=5)
except Exception:
self.proc.kill()
os.close(self.master)
class _SshTransport:
"""Real SSH transport: same probe source, remote python, forced tty
(-tt) so raw-mode/ioctl/SIGWINCH behave as over a real channel."""
def __init__(self, target: str):
self.name = f"ssh:{target}"
self.proc = subprocess.Popen(
["ssh", "-tt", "-o", "BatchMode=yes",
"-o", "ConnectTimeout=5", target,
"python3", "-s", "-c", PROBE_SRC, "20"],
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL)
def write(self, data: bytes) -> None:
self.proc.stdin.write(data)
self.proc.stdin.flush()
def resize(self, rows: int, cols: int) -> None:
# SIGWINCH on a remote pty is driven by the remote terminal
# emulator; over the ssh channel we send the xterm CSI resize
# escape — best-effort; the replay records what the probe
# actually observed.
self.write(f"\x1b[8;{rows};{cols}t".encode())
def read_output(self, duration_s: float) -> bytes:
import selectors
sel = selectors.DefaultSelector()
sel.register(self.proc.stdout, selectors.EVENT_READ)
buf = b""
deadline = time.monotonic() + duration_s
while time.monotonic() < deadline:
for key, _ in sel.select(0.05):
chunk = key.fileobj.read1(65536)
if not chunk:
return buf
buf += chunk
if PROBE_MARKER in buf:
return buf
return buf
def close(self):
try:
self.proc.kill()
except Exception:
pass
def _extract_probe_json(output: bytes) -> dict | None:
idx = output.find(PROBE_MARKER)
if idx < 0:
return None
tail = output[idx + len(PROBE_MARKER):].strip()
try:
return json.loads(tail)
except json.JSONDecodeError:
return None
def ssh_replay(receipts_dir: Path = RECEIPTS_DIR,
ssh_target: str | None = None,
burst_tokens: int = 300,
receipt_path: Path | None = None) -> dict:
"""Replay the FIXED input/burst/resize traces under the SSH profile.
Transport: real ssh when a BatchMode target answers (the I-R2-006
'SSH channel full replay'), else a PTY carrying the recorded SSH
profile (TERM=xterm-256color, tty stdin) — the receipt names which
actually ran; the fallback is never presented as the real channel.
"""
traces = load_fixed_traces(receipts_dir)
frozen_problems = validate_frozen_records(traces)
transport = None
fallback_reason = ""
target = ssh_target or os.environ.get("RUNE_SSH_TARGET", "")
if target:
probe = subprocess.run(
["ssh", "-o", "BatchMode=yes", "-o", "ConnectTimeout=5",
target, "echo ok"], capture_output=True, timeout=15)
if probe.returncode == 0:
transport = _SshTransport(target)
else:
fallback_reason = (f"ssh target {target!r} unreachable "
f"(rc={probe.returncode})")
if transport is None:
if target and not fallback_reason:
fallback_reason = "no ssh target configured"
elif not target:
fallback_reason = ("no ssh target configured "
"(RUNE_SSH_TARGET/--ssh-target)")
transport = _PtyTransport()
problems: list[str] = list(frozen_problems)
ready = getattr(transport, "wait_ready", lambda **k: True)()
if not ready:
problems.append("probe never reported ready (initial ACK "
"missing) — input replay would be dishonest")
try:
# -- input trace: the frozen key script + the split variant ------
input_script: list[tuple[str, bytes, float]] = []
for name in traces["focus"]["keys_via_parser"]:
input_script.append((name, key_bytes(name), 0.02))
input_script.append(("esc;split", b"\x1b", SPLIT_GAP_S))
input_script.append(("q;split", b"q", 0.02))
writes: list[tuple[str, int]] = [] # name, write-time ns
for name, data, gap in input_script:
writes.append((name, time.monotonic_ns()))
transport.write(data)
time.sleep(gap)
# -- burst trace: sequenced tokens, chunked -----------------------
tokens = [f"s{i:05d}\n".encode() for i in range(burst_tokens)]
burst_t0 = time.monotonic_ns()
for k in range(0, len(tokens), 10):
transport.write(b"".join(tokens[k:k + 10]))
time.sleep(0.001)
burst_write_ns = time.monotonic_ns() - burst_t0
# -- resize trace: 80x24 -> 140x30 -> 80x24 -----------------------
time.sleep(0.1)
transport.resize(30, 140)
time.sleep(0.2)
transport.resize(24, 80)
time.sleep(0.2)
transport.write(PROBE_SENTINEL)
output = transport.read_output(10.0)
finally:
transport.close()
probe_data = _extract_probe_json(output)
if probe_data is None:
problems.append("probe produced no JSON (marker missing/truncated)")
probe_data = {"events": [], "sizes": [], "term": None,
"initial_size": None, "stdin_is_tty": None}
# input: parse with the 50ms rule, compare to frozen expectations.
# Only events BEFORE the first burst token are the input trace.
burst_start = next((i for i, (_, h) in enumerate(probe_data["events"])
if b"s00000" in bytes.fromhex(h)), None)
if burst_start is None:
problems.append("no burst tokens observed in probe events")
burst_start = len(probe_data["events"])
parsed = parse_key_events(probe_data["events"][:burst_start])
expected_keys = list(traces["focus"]["keys_via_parser"]) + ["esc", "q"]
frozen_atomic = traces["keyprobe_atomic"]["parsed_keys"]
frozen_split = traces["keyprobe_split"]["parsed_keys"]
input_ok = parsed == expected_keys
if not input_ok:
problems.append(f"input parse diverged: {parsed} != {expected_keys}")
# burst: reassemble the byte stream, extract tokens, order+loss
stream = b"".join(bytes.fromhex(h) for _, h in probe_data["events"])
import re
seen = re.findall(rb"s(\d{5})\n", stream)
seqs = [int(x) for x in seen]
missing = sorted(set(range(burst_tokens)) - set(seqs))
ordered = seqs == sorted(seqs)
dupes = len(seqs) != len(set(seqs))
if missing or not ordered or dupes:
problems.append(f"burst replay loss/order violation "
f"(missing={len(missing)}, ordered={ordered}, "
f"dupes={dupes})")
# resize: probe must have observed initial + both resizes
observed = [(r, c) for _, r, c in probe_data["sizes"]]
expected_sizes = [(24, 80), (30, 140), (24, 80)]
resize_ok = observed == expected_sizes
if not resize_ok:
problems.append(f"resize observations {observed} != {expected_sizes}")
receipt = {
"test": "ssh_profile_replay_fixed_traces",
"harness": "scripts/check_migration_mutants.py --ssh-replay",
"binding": "tasks.md 0.11 + I-R2-006 remaining gate (SSH channel "
"full replay); traces frozen at "
"research/tui-frontier-r2/receipts/",
"transport": {
"actual": transport.name,
"ssh_requested": bool(target),
"fallback_reason": fallback_reason,
"term": probe_data.get("term"),
"stdin_is_tty": probe_data.get("stdin_is_tty"),
"esc_timeout_s": ESC_TIMEOUT_S,
},
"traces": {
"input": {
"replayed": [w[0] for w in writes],
"parsed_keys": parsed,
"expected_keys": expected_keys,
"match": input_ok,
"frozen_atomic_expectation": frozen_atomic,
"frozen_split_expectation": frozen_split,
"ev_bt_004_divergence_reproduced":
parsed[-2:] == ["esc", "q"],
},
"burst": {
"tokens_sent": burst_tokens,
"tokens_received": len(set(seqs)),
"missing": len(missing),
"ordered": ordered,
"write_phase_ms": round(burst_write_ns / 1e6, 1),
},
"resize": {
"observed_sizes": observed,
"expected_sizes": expected_sizes,
"match": resize_ok,
},
},
"frozen_record_problems": frozen_problems,
"problems": problems,
"verdict": "GREEN" if not problems else "RED",
}
out = receipt_path or (RECEIPT_OUT_DIR / "ssh-replay.json")
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(receipt, indent=2, sort_keys=True) + "\n")
receipt["_receipt_path"] = str(out)
return receipt
# ── slowest-target SLO recording structure ────────────────────────────────
#
# tasks.md 0.11: "slowest-supported-target run against the agreed SLO."
# GAP-BT-05: no agreed SLO exists yet — so the budgets below are
# PROPOSED from the lab receipts (agreed:false until the owner
# ratifies), and every record states honestly whether its target is the
# slowest SUPPORTED target or just the machine that ran it.
PROPOSED_BUDGETS = {
"key_p99_ms": 5.0, # lab: 0.481 (burst-n5000 key_send_to_update p99)
"msg_p99_ms": 5.0, # lab: 0.488
"updates_per_s_min": 1000, # lab: 7041 stream updates/s
"order_violations_max": 0, # lab: 0
"missing_seqs_max": 0, # lab: null
"bytes_per_frame_avg_max": 4096, # lab: 49
}
SLO_RECORD_REQUIRED = [
"schema_version", "ran_at", "runner", "target", "is_slowest_supported_target",
"transport", "run", "budgets", "verdict", "artifacts",
]
def build_slo_record(replay_receipt: dict, *,
slowest_target: bool = False) -> dict:
cpu = ""
try:
cpu = (Path("/proc/cpuinfo").read_text().split("model name")[1]
.split("\n")[0].split(":")[1].strip())
except Exception:
pass
ram_gb = None
try:
for line in Path("/proc/meminfo").read_text().splitlines():
if line.startswith("MemTotal"):
ram_gb = round(int(line.split()[1]) / 1e6, 1)
except Exception:
pass
burst = replay_receipt["traces"]["burst"]
measured = {
"updates_per_s": round(burst["tokens_received"]
/ max(burst["write_phase_ms"] / 1000, 1e-9), 1),
"order_violations": 0 if burst["ordered"] else 1,
"missing_seqs": burst["missing"],
}
verdict = "GREEN" if (
replay_receipt["verdict"] == "GREEN"
and measured["order_violations"] <= PROPOSED_BUDGETS["order_violations_max"]
and measured["missing_seqs"] <= PROPOSED_BUDGETS["missing_seqs_max"]
and measured["updates_per_s"] >= PROPOSED_BUDGETS["updates_per_s_min"]
) else "RED"
return {
"schema_version": 1,
"ran_at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
"runner": "scripts/check_migration_mutants.py --slo-record",
"target": {
"name": "lab-desktop",
"cpu": cpu,
"ram_gb": ram_gb,
"note": "Ryzen 9 5950X-class lab machine — NOT the slowest "
"supported target (GAP-BT-05); the slowest-target "
"run gets its own record when that hardware runs "
"this harness",
},
"is_slowest_supported_target": slowest_target,
"transport": replay_receipt["transport"],
"run": {
"replay_receipt": replay_receipt["_receipt_path"],
"replay_verdict": replay_receipt["verdict"],
"input_match": replay_receipt["traces"]["input"]["match"],
"resize_match": replay_receipt["traces"]["resize"]["match"],
"measured": measured,
},
"budgets": {
"proposed": PROPOSED_BUDGETS,
"agreed": False,
"agreement_note": "no agreed SLO yet (GAP-BT-05); budgets "
"proposed from lab receipts "
"burst-n5000-slow0.json",
},
"verdict": verdict,
"artifacts": [replay_receipt["_receipt_path"]],
}
def slo_validate(record: dict) -> list[str]:
"""Structural + arithmetic validation of an SLO record. Used by the
record writer itself and by tests/CI so a malformed or lying record
cannot stand in for the slowest-target run."""
errs = []
for field in SLO_RECORD_REQUIRED:
if field not in record:
errs.append(f"missing field: {field}")
if errs:
return errs
if record["schema_version"] != 1:
errs.append(f"unknown schema_version {record['schema_version']}")
m = record["run"]["measured"]
b = record["budgets"]["proposed"]
if m["order_violations"] > b["order_violations_max"] \
or m["missing_seqs"] > b["missing_seqs_max"] \
or m["updates_per_s"] < b["updates_per_s_min"]:
computed_green = False
else:
computed_green = True
if computed_green != (record["verdict"] == "GREEN"):
errs.append(f"verdict {record['verdict']!r} disagrees with "
f"measured-vs-budget arithmetic")
if record["is_slowest_supported_target"] and not record["budgets"]["agreed"]:
errs.append("a slowest-target claim requires an AGREED SLO — "
"proposed budgets cannot ratify it")
return errs
def slo_record(slowest_target: bool = False) -> dict:
replay = ssh_replay()
record = build_slo_record(replay, slowest_target=slowest_target)
errs = slo_validate(record)
if errs:
record["validation_errors"] = errs
SLO_DIR.mkdir(parents=True, exist_ok=True)
rec_dir = SLO_DIR / "records"
rec_dir.mkdir(exist_ok=True)
slug = record["target"]["name"].replace("/", "-")
out = rec_dir / f"{record['ran_at'].replace(':', '')}-{slug}.json"
out.write_text(json.dumps(record, indent=2, sort_keys=True) + "\n")
record["_record_path"] = str(out)
record["_valid"] = not errs
return record
# ── main ──────────────────────────────────────────────────────────────────
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__.split("\n")[0])
ap.add_argument("--ssh-replay", action="store_true",
help="replay the fixed input/burst/resize traces "
"under the SSH profile")
ap.add_argument("--ssh-target", default="",
help="ssh target for the real-channel replay "
"(default RUNE_SSH_TARGET env)")
ap.add_argument("--slo-record", action="store_true",
help="run the replay and record an SLO run record")
ap.add_argument("--slowest-target", action="store_true",
help="label the SLO record as the slowest supported "
"target (operator asserts constrained hardware)")
ap.add_argument("--slo-validate", metavar="FILE",
help="validate an existing SLO record file")
ap.add_argument("--pins", action="store_true",
help="print current baseline/launcher pins")
args = ap.parse_args(argv)
if args.pins:
baseline = build_baseline()
print(f"BASELINE_SHA256 = {canonical_sha256(baseline)!r}")
print(f"LAUNCHER_PIN = {canonical_sha256(baseline['launcher'])!r}")
print(f"in-module pin holds: "
f"{canonical_sha256(baseline) == BASELINE_SHA256}")
return 0
if args.slo_validate:
doc = json.loads(Path(args.slo_validate).read_text())
errs = slo_validate(doc)
for e in errs:
print(f"INVALID: {e}")
print(f"{'VALID' if not errs else 'INVALID'}: {args.slo_validate}")
return 0 if not errs else 1
if args.slo_record:
rec = slo_record(slowest_target=args.slowest_target)
print(f"SLO record verdict={rec['verdict']} "
f"slowest_target={rec['is_slowest_supported_target']} "
f"valid={rec['_valid']} -> {rec['_record_path']}")
return 0 if rec["verdict"] == "GREEN" and rec["_valid"] else 1
if args.ssh_replay:
r = ssh_replay(ssh_target=args.ssh_target or None)
print(f"transport={r['transport']['actual']} "
f"(fallback: {r['transport']['fallback_reason'] or 'none'})")
print(f"input match={r['traces']['input']['match']} "
f"burst missing={r['traces']['burst']['missing']} "
f"ordered={r['traces']['burst']['ordered']} "
f"resize match={r['traces']['resize']['match']}")
for p in r["problems"]:
print(f"PROBLEM: {p}")
print(f"VERDICT: {r['verdict']} -> {r['_receipt_path']}")
return 0 if r["verdict"] == "GREEN" else 1
summary = run_suite()
for row in summary["mutants"]:
print(f" [{row['oracle_verdict']:<5}] {row['mutant']} "
f"fired={','.join(row['fired_checks'])} "
f"expected-fired={row['fired_expected_check']}")
print(f"baseline_green={summary['baseline_green']} "
f"all_mutants_red={summary['all_mutants_red']} "
f"verdict={summary['verdict']} -> {summary['_receipt_path']}")
return 0 if summary["verdict"] == "GREEN" else 1
if __name__ == "__main__":
sys.exit(main())