Files
Teknium a8ca904922 feat(evals): post-mortem harness — forensics lanes + live A/B + review probes for the #102117 run fixes
evals/postmortem/ turns the one-off audit behind tracking issue #103563 into
something anyone with a Hermes state.db copy (and optionally rotated
agent.log*) can run on their own fan-out:

  forensics/   common.py discovers the run tree (root = most descendants,
               compression-rollover children excluded so cost buckets stay
               disjoint), fits pricing from estimated_cost_usd, and five lanes
               recompute the OBSERVED figures: tokens (buckets, depth/duration
               shares, context reconstruction, excess-cache-write proxy, cap
               replay), logcalls (per-call cache behaviour from agent.log with
               coverage printed first; strict and loose plateau definitions
               reported separately), delegation (timeouts, orphaned children,
               polling hours, batch-join withheld child-hours, truncated
               summaries), tools (hardline blocks, foreground refusals,
               whole-file rewrites), goal_loop (nudges, parked barrier), rework
               (public-surface drop at PR open + post-open commit inventory).
               Every figure is labeled OBSERVED or MODELED.
  live_ab/     the per-PR A/Bs (real code paths, fake providers, temp
               HERMES_HOME), paths from argv.
  review_probes/ the independent /review's probes, credited and adapted; each
               reproduced a round-1 defect and the fixed head must pass it.
  run.py       runs the offline probes against one or two checkouts and prints
               PASS/FAIL side by side (--live adds the ones that spend cents).
  tests/       synthetic-DB smoke test for the lanes and runner.

On the run's DB the lanes reproduce the tracking issue's population exactly
(1,394 sessions, 93,284 calls, $19,302.59; cache_write $11,159.76) and on
main vs an integration checkout of the 13 PRs the runner shows every probe
FAIL -> PASS (two guard-only probes pass on both, noted in run.py).

The trajectories are deliberately not shipped: the DB holds 51,956 home
paths, 5,341 e-mails, private IPs, chat ids and real-shaped credentials in
tool output. The lane reports and recomputed JSON are in a secret gist
linked from #103563.
2026-09-05 09:13:10 -07:00

49 lines
2.8 KiB
Python

"""#103551: remote backend, FIFO, and quadratic-time hazards of the rewrite hint
Independent-review probe (written by the /review subagent for tracking issue #103563, adapted here).
It reproduced a defect in the first version of the PR; the fixed head must pass it. Paths are taken
from the command line / environment, never hard-coded. Usage: see the argument parsing at the top of the file.
"""
import sys, os, pathlib, tempfile, json, time, signal
from unittest.mock import patch
root=pathlib.Path(tempfile.mkdtemp(prefix='hint-probe-'))
sys.path.insert(0, sys.argv[1] if len(sys.argv)>1 else os.getcwd()) # repo root under test
os.environ['HERMES_HOME']=tempfile.mkdtemp(dir=root)
os.environ['TERMINAL_ENV']='local'
from tools import file_tools as f
from tools.file_operations import WriteResult
print('module',f.__file__)
with tempfile.TemporaryDirectory(dir=root) as d:
p=pathlib.Path(d)/'file.txt'
for n in [1000,4000,10000,20000]:
old='same repetitive record\n'*n;p.write_text(old, encoding='utf-8');new=old+'end\n'
start=time.monotonic(); hint=f._whole_file_rewrite_hint('default',str(p),new)
print('repeat',n,len(old),round(time.monotonic()-start,3),bool(hint),flush=True)
old=''.join(f'{i} arbitrary unique data for test\n' for i in range(1500));p.write_text(old, encoding='utf-8')
new=old.replace('750 arbitrary','750 edited')
start=time.monotonic();r=json.loads(f.write_file_tool(str(p),new,task_id='review'))
print('live-write',round(time.monotonic()-start,3),r,p.read_text(encoding='utf-8')==new,flush=True)
class RemoteOps:
env=object()
def write_file(self,path,content):
self.written=(path,content)
return WriteResult(bytes_written=len(content),verified=True)
ops=RemoteOps()
p.write_text(old, encoding='utf-8')
with patch.object(f,'_get_file_ops',return_value=ops):
print('remote_backend_is_host',f._file_ops_uses_host_paths(ops))
r=json.loads(f.write_file_tool(str(p),new,task_id='remote-review'))
print('remote_empty_target_hint_from_host',r.get('hint'), 'host_unchanged',p.read_text(encoding='utf-8')==old,flush=True)
fifo=pathlib.Path(d)/'pipe.txt';os.mkfifo(fifo)
alarms=[]
def alarm(sig,frame):
alarms.append(time.monotonic())
raise TimeoutError('host FIFO read blocked')
signal.signal(signal.SIGALRM,alarm) # windows-footgun: ok — POSIX-only FIFO hazard probe
with patch.object(f,'_get_file_ops',return_value=ops):
start=time.monotonic();signal.alarm(2)
try: print('remote_fifo',f.write_file_tool(str(fifo),'x'*20000,task_id='remote-fifo'), 'seconds',time.monotonic()-start,'read_alarm_fired',bool(alarms))
finally:signal.alarm(0)
with patch.object(f,'_whole_file_rewrite_hint',return_value=None):
start=time.monotonic();print('remote_fifo_base_no_hint',f.write_file_tool(str(fifo),'x'*20000,task_id='remote-fifo'), 'seconds',time.monotonic()-start)