"""Render the three-arm carrier comparison into a booth page. Three columns, chosen so the page answers two questions at once and neither answer leans on the other: 1.7B base vs 1.7B tuned -- did the ADAPTER do anything at this carrier size, or is any improvement just the bigger model? 0.6B tuned vs 1.7B tuned -- did coherence come back as the carrier grew? Both tuned arms sit on the SAME unwrapped corpus (sha 77f37057b2782e49), same seed, same sampler, so carrier size is the only difference between them. The 1.7B base arm is generated fresh rather than reused, because a control from a different model would control for nothing. Prompts run hardest-first: modern/mundane, then period-neutral, then Victorian- adjacent. Both seeds of every arm sit in the same cell so within-arm sampling noise is visible in the same glance as between-arm difference -- if two samples of one arm differ as much as two arms differ, the page should make that obvious rather than hide it. """ import html import json import sys from collections import defaultdict from pathlib import Path out_dir = Path(sys.argv[1]) ARMS = [ ("1p7b-base.jsonl", "1.7B base", "Qwen3-1.7B-Base, no adapter", ""), ("1p7b-tuned.jsonl", "1.7B tuned", "+ H02 LoRA, 1 epoch, seed 4919", "tuned"), ("0p6b-tuned.jsonl", "0.6B tuned", "+ H02 LoRA, same corpus & seed", "small"), ] out_dir.mkdir(parents=True, exist_ok=True) def load(p): d = defaultdict(dict) for line in Path(p).read_text(encoding="utf-8").splitlines(): if line.strip(): r = json.loads(line) d[r["id"]][r["seed"]] = r return d data = [(lbl, sub, cls, load(out_dir / f)) for f, lbl, sub, cls in ARMS] ids = sorted(set.intersection(*[set(d) for *_, d in data])) TIER = {"modern": ("Tier A — modern / mundane", "Nothing here invites Victorian prose. Brontë in this tier is the adapter's doing."), "neutral": ("Tier B — period-neutral", "Could be any century. A voice shift shows without the prompt supplying it."), "period": ("Tier C — Victorian-adjacent, plainly worded", "The setting leans period but the diction does not. Easiest tier; weakest evidence.")} order = {"modern": 0, "neutral": 1, "period": 2} tier_of = {i: data[0][3][i][list(data[0][3][i])[0]]["tier"] for i in ids} ids.sort(key=lambda i: (order.get(tier_of[i], 9), i)) def cell(by_seed): return "".join( f'
seed {s}' f'

{html.escape((by_seed[s]["continuation"] or "").strip()) or "(empty)"}

' for s in sorted(by_seed)) rows, seen = [], set() for i in ids: if tier_of[i] not in seen: seen.add(tier_of[i]) title, sub = TIER.get(tier_of[i], (tier_of[i], "")) rows.append(f'

{html.escape(title)}

{html.escape(sub)}

') p = data[0][3][i][list(data[0][3][i])[0]]["prompt"] cols = "".join( f'

{lbl} {sub}

{cell(d[i])}
' for lbl, sub, cls, d in data) rows.append(f'
' f'{html.escape(i)}{html.escape(p)}
' f'
{cols}
') page = f"""BabyBronte — 1.7B rung

BabyBronte — rung 2: did the sense come back?

The 0.6B rung transferred the voice and not the coherence — "it's all nonsense, but it sounds like Brontë's nonsense." This is the same nine prompts at 1.7B, with the 0.6B tuned arm beside it for scale and the 1.7B base arm beside it for control.

Two questions, two columns each. 1.7B base vs 1.7B tuned asks whether the adapter did anything at this carrier size, or whether any improvement is just the bigger model. 0.6B tuned vs 1.7B tuned asks whether coherence returned as the carrier grew — and those two tuned arms sit on the same corpus, same seed, same sampler, so carrier size is the only difference between them.
Still an eyeball test, not a result. Two samples per arm is enough to see whether the gap between columns beats the gap between seeds inside one — and not enough for anything else. No scoring. The frozen adjudication rule and the Burrows's-Delta instrument are untouched and nothing here feeds them.

All arms are base models doing continuation, not instruction-following, so each prompt is an opening line carried on rather than an instruction to rewrite. The corpus was unwrapped since the last booth, so the ~70-character hard wrapping that disfigured the first 0.6B page should be gone from both tuned arms here.
{''.join(rows)}
""" (out_dir / "index.html").write_text(page, encoding="utf-8") print(f"wrote {out_dir/'index.html'} ({len(ids)} prompts x 3 arms x 2 seeds)")