#!/usr/bin/env python3 """Merge hand-coded values (coding.json) with counts computed from responses.jsonl.""" import json, os from urllib.parse import urlparse BASE = os.path.dirname(os.path.abspath(__file__)) CC3 = ('com','co','org','net','ac','gov','edu') def dom(c): u = c.get('url') if u: h = urlparse(u).netloc.lower().split(':')[0] p = h.split('.') if len(p) >= 3 and p[-2] in CC3 and len(p[-1]) == 2: return '.'.join(p[-3:]) return '.'.join(p[-2:]) if len(p) >= 2 else h t = (c.get('title') or '').lower().strip() return t if '.' in t and ' ' not in t else None recs = [json.loads(l) for l in open(os.path.join(BASE,'responses.jsonl'), encoding='utf-8') if l.strip()] # A counted cell is an API-surface run on the four registered model configurations. The consumer # scrapes and the gpt-5.5 run share run numbers with counted runs and must not collide with them: # keying on (question, system, run) alone silently substituted the consumer record into a counted row. COUNTED_MODELS = ('gpt-4.1-mini', 'claude-sonnet-4-5', 'gemini-2.5-flash', 'sonar') def is_counted(r): if r.get('surface') != 'api': return False if not r.get('answer_text'): return False # verbatim text, per the stated definition if not r.get('started_utc'): return False # and a timestamp if r.get('citations') is None: return False # and a citation list m = (r.get('model_reported') or '') return any(m.startswith(c) for c in COUNTED_MODELS) # ── transcription, checked against the record ──────────────────────────────────────────────────── # `figure_verbatim` and `source_named_verbatim` are typed by the coder. On 2026-07-28 a check of the # essay's central quotation came back "appears in no file in the package": because the sheet records # a different sentence from the same answer than the essay quotes. The sheet was right about the # coding and wrong as a quotation source, and nothing said which it was. # # So every transcription is now tested against responses.jsonl and labelled. A field that is not a # substring of what the system actually said is marked PARAPHRASE, and the raw sentence it came from # is printed beside it. Nothing here is a quotation unless the record says it is. import re as _re def _norm(t): """Compare on content, not on typography: the coder types straight quotes and en dashes.""" t = (t or "") for a, b in (("\u2019", "'"), ("\u2018", "'"), ("\u201c", '"'), ("\u201d", '"'), ("\u2013", "-"), ("\u2014", "-"), ("\u2212", "-"), ("\u00a0", " ")): t = t.replace(a, b) return _re.sub(r"\s+", " ", t).strip().lower() def _fragments(hand): """The coder joins separate excerpts with an ellipsis or a semicolon. Test each on its own.""" parts = _re.split(r"\s*(?:\u2026|\.\.\.|;)\s*", hand or "") return [f.strip(" *`") for f in parts if len(f.strip(" *`")) >= 12] def transcription(hand, raw): """(label, evidence): evidence is the record's own sentence when the field is not verbatim.""" frags = _fragments(hand) if not frags: return "not a quotation", "" nraw = _norm(raw) missing = [f for f in frags if _norm(f) not in nraw] if not missing: return "verbatim", "" # Find the record's sentence that the coder was looking at, using the longest word run that hits. probe = max(frags, key=len) words = _norm(probe).split() hit = "" for n in range(min(8, len(words)), 2, -1): needle = " ".join(words[:n]) i = nraw.find(needle) if i != -1: start = max(nraw.rfind(". ", 0, i) + 2, 0) end = nraw.find(". ", i) hit = (raw or "")[start:(end + 1 if end != -1 else min(len(raw or ""), start + 320))].strip() break return "PARAPHRASE", hit checked = {'verbatim': 0, 'other': 0} by = {} for r in recs: if not is_counted(r): continue k = (r['question_id'], r['system'], r['run']) if k in by: raise SystemExit(f'duplicate counted record for {k}: refusing to guess which is the counted one') by[k] = r coding = json.load(open(os.path.join(BASE,'coding.json'), encoding='utf-8')) out = ["# Coding sheet: counted cells", "", f"Coded by **{coding['coded_by']}**, {coding['coded_utc']}, by hand against section 6 of the protocol.", "Second-coder pass on a 25% sample: **RUN 2026-07-28, by a language model rather than an " "independent human reader**: see `second-coder-pass.md`. The protocol's human requirement was " "waived by the author on 2026-07-28 and the deviation is in the changelog.", "", "`citations_n` and `domains_unique` are computed from `responses.jsonl`, not typed. `domains_unique`", "counts registrable domains, so `media-publications.bcg.com` and `www.bcg.com` are one domain.", "`interested_share` is null on every row: the protocol requires each domain to be classified from its", "own homepage and that pass has not been run.", ""] for q in ('Q1','Q4','Q5'): rows = [r for r in coding['rows'] if r['q'] == q] if not rows: continue out += [f"## {q}", "", "| System | Run | figure_given | source_named | source_class | cit | uniq | int_share | contra | version | search |", "| --- | ---: | --- | --- | --- | ---: | ---: | --- | --- | --- | --- |"] for r in rows: rec = by.get((r['q'], r['system'], r['run'])) cits = (rec.get('citations') or []) if rec else [] ds = [d for d in (dom(c) for c in cits) if d] out.append(f"| {r['system']} | {r['run']} | {r['figure_given']} | {r['source_named']} | " f"{r['source_class']} | {len(cits)} | {len(set(ds))} | " f"{': ' if r['interested_share'] is None else r['interested_share']} | " f"{r['contra_as_support']} | {r['version_returned']} | {r['search_used']} |") out.append("") for r in rows: rec = by.get((r['q'], r['system'], r['run'])) cits = (rec.get('citations') or []) if rec else [] ds = sorted({d for d in (dom(c) for c in cits) if d}) raw = (rec or {}).get('answer_text') or '' fig_state, fig_hit = transcription(r['figure_verbatim'], raw) src_state, src_hit = transcription(r['source_named_verbatim'], raw) checked['verbatim' if fig_state == 'verbatim' else 'other'] += 1 checked['verbatim' if src_state == 'verbatim' else 'other'] += 1 out += [f"**{q} · {r['system']} · run {r['run']}**", "", f"- Figure [{fig_state}]: {r['figure_verbatim']}"] if fig_hit: out.append(f" - the record says: \u201c{fig_hit}\u201d") out.append(f"- Named as the source [{src_state}]: {r['source_named_verbatim']}") if src_hit: out.append(f" - the record says: \u201c{src_hit}\u201d") out.append(f"- Domains: {', '.join(ds) if ds else '(none)'}") if r['provenance_error']: out.append("- `provenance_error` (route: routed = a cited document is established to contain it):") routes = r.get('provenance_error_route') or ['' for _ in r['provenance_error']] out += [f" - [{rt}] {e}" for e, rt in zip(r['provenance_error'], routes)] else: out.append("- `provenance_error`: none") out += [f"- Coder note: {r['coder_note']}", ""] tot = checked['verbatim'] + checked['other'] out.insert(4, f"**Transcription check.** Every `Figure` and `Named as the source` field below is tested " f"against the answer text in `responses.jsonl`. {checked['verbatim']} of {tot} are verbatim " f"and are marked `[verbatim]`; the rest are the coder's paraphrase, are marked " f"`[PARAPHRASE]`, and carry the record's own sentence underneath. **Quote from the " f"`[verbatim]` fields or from `responses.jsonl`, never from a paraphrase.**") out.insert(5, "") open(os.path.join(BASE,'coding-sheet.md'),'w',encoding='utf-8').write("\n".join(out)) print("wrote coding-sheet.md : ", len(coding['rows']), "rows")