From 30c905dcb0f8b272123ac85fbac8eb82fe2cd169 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Sun, 19 Jul 2026 02:14:32 +0000 Subject: [PATCH] naros: fix agent Dockerfile jq continuation; add corpus overhead harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The manifest-stamping RUN's multi-line jq filter lacked Dockerfile line continuations (newlines inside shell quotes do not continue an instruction), so the first real docker build died with 'unknown instruction: |'. Join the filter with backslash continuations — the parser strips them, jq receives one line. shell/corpus/overhead.py is the M3 <3% wall-clock gate harness: alternating bash/nash corpus rounds (observation spooling on for nash), median totals. Co-Authored-By: Claude Fable 5 --- corpus/overhead.py | 132 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 corpus/overhead.py diff --git a/corpus/overhead.py b/corpus/overhead.py new file mode 100644 index 0000000..8c9bbc9 --- /dev/null +++ b/corpus/overhead.py @@ -0,0 +1,132 @@ +#!/usr/bin/env python3 +"""Corpus wall-clock overhead harness for nash (docs/NASH.md §11, M2/M3 gate: <3%). + +Replays the corpus under bash and nash in alternating rounds and compares total +wall-clock per shell. Only the shell subprocess is timed (fixture seeding and +filesystem snapshots are outside the clock). Rounds alternate shell order so +cache/thermal drift cancels; the reported figure uses the median round total. + +Usage: overhead.py [--nash PATH] [--bash PATH] [--corpus PATH] [--rounds N] + [--observe-spool DIR] [--gate PCT] [--json PATH] + +--observe-spool enables nash observation (spool transport) so the measured +configuration is the deployed one; the env is set identically for bash, where +it is inert. +""" + +import argparse +import json +import os +import shutil +import statistics +import subprocess +import sys +import tempfile +import time + +from replay import FIXTURES, TIMEOUT_S, seed + + +def run_timed(shell, cmd, extra_env): + parent = tempfile.mkdtemp(prefix="nash-overhead-") + workdir = os.path.join(parent, "workspace") + os.makedirs(workdir) + seed(workdir) + env = { + "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + "HOME": workdir, + "LC_ALL": "C", + "LANG": "C", + "TERM": "dumb", + "SHELL": shell, + } + if extra_env: + env.update(extra_env) + start = time.monotonic() + try: + subprocess.run( + [shell, "-c", cmd], + cwd=workdir, + env=env, + capture_output=True, + timeout=TIMEOUT_S, + ) + except subprocess.TimeoutExpired: + pass + elapsed = time.monotonic() - start + shutil.rmtree(parent, ignore_errors=True) + return elapsed + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("--nash", default=os.environ.get("NASH_BIN", "nash")) + ap.add_argument("--bash", default="/bin/bash") + ap.add_argument( + "--corpus", + default=os.path.join(os.path.dirname(os.path.abspath(__file__)), "corpus.jsonl"), + ) + ap.add_argument("--rounds", type=int, default=5) + ap.add_argument("--observe-spool", help="enable nash observation, spooling to this dir") + ap.add_argument("--gate", type=float, default=3.0, help="max overhead percent") + ap.add_argument("--json", help="also write results to this path") + args = ap.parse_args() + + observe_env = None + if args.observe_spool: + os.makedirs(args.observe_spool, exist_ok=True) + observe_env = { + "NUCLEIC_SHELL_SPOOL": args.observe_spool, + "NUCLEIC_SESSION_ID": "corpus-overhead", + } + + with open(args.corpus) as f: + cmds = [json.loads(line)["cmd"] for line in f if line.strip()] + + # Warm-up: one untimed pass per shell (page cache, binary load). + for shell in (args.bash, args.nash): + for cmd in cmds[:10]: + run_timed(shell, cmd, observe_env) + + totals = {"bash": [], "nash": []} + for round_no in range(args.rounds): + order = [("bash", args.bash), ("nash", args.nash)] + if round_no % 2: + order.reverse() + for name, shell in order: + total = sum(run_timed(shell, cmd, observe_env) for cmd in cmds) + totals[name].append(total) + print(f"round {round_no + 1} {name}: {total:.3f}s", flush=True) + + bash_med = statistics.median(totals["bash"]) + nash_med = statistics.median(totals["nash"]) + overhead = 100.0 * (nash_med / bash_med - 1.0) + + print(f"\ncorpus: {len(cmds)} cmds x {args.rounds} rounds") + print(f"bash median: {bash_med:.3f}s nash median: {nash_med:.3f}s") + print(f"overhead: {overhead:+.2f}% (gate: <{args.gate:.1f}%)") + + if args.json: + with open(args.json, "w") as f: + json.dump( + { + "cmds": len(cmds), + "rounds": args.rounds, + "totals": totals, + "bash_median_s": bash_med, + "nash_median_s": nash_med, + "overhead_percent": overhead, + "gate_percent": args.gate, + "observed": bool(args.observe_spool), + }, + f, + indent=1, + ) + + gate = overhead < args.gate + print(f"M3 overhead gate (<{args.gate:.1f}%): {'PASS' if gate else 'FAIL'}") + return 0 if gate else 1 + + +if __name__ == "__main__": + sys.exit(main())