from pathlib import Path
import json

root=Path('/tmp/issue-lead-26950-r1/evidence-capture')
out=root/'deliverables'
uploads=json.loads((root/'upload-map.json').read_text())
data=json.loads((out/'comparison-manifest.json').read_text())
def md(name): return uploads[name]['markdown']
def link(name,label=None): return f'[{label or name}]({uploads[name]["url"]})'
before='[6d450a4284](https://git.cafe/pingdotgg/t3code/commit/6d450a4284)'
after='[5c1c8b0004](https://git.cafe/pingdotgg/t3code/commit/5c1c8b0004ee52542418bfc9f22c3a6f07f57b56)'
titles={
 'F1':'Warm baseline survives partial-word host misses — real app',
 'F2':'Host overlap, body-only hit, two supplements and detail selection — harness capture',
 'F3':'True zero matches and Clear search recovery — real app',
 'F4':'Authored involvement and local qualifier exclusions — harness capture',
 'F5':'Typed checks/status veto, then qualifier removal — harness capture',
 'F6':'Checks menu preserves eligible baseline supplement — harness capture',
}
notes={
 'F1':'Both text-free live baselines contained the same 99 identities (host ordering varied). The matching subset was [#26895](https://github.com/pingdotgg/t3code/pull/26895), [#26908](https://github.com/pingdotgg/t3code/pull/26908), [#26934](https://github.com/pingdotgg/t3code/pull/26934), and [#24729](https://github.com/pingdotgg/t3code/pull/24729). Completed GitHub answers for `toa`, `toas`, and `Curs` returned zero on both sides. Before settles empty; after retains one Toast row and three Cursor rows, grouped Authored 1 / Others 2 for Cursor. The recording includes extending `toa` to `toas`; still checkpoints show `toa` and `Curs`.',
 'F2':'Host order H1 (900001 Toast primary), H2 (900002 Notifications, matched in description) stays first. H1 appears once. After appends B3 (900003 Toast fallback) then B4 (900004 Toaster), with Others count 4; before shows only H1/H2, count 2. The same driver attempts to select B3 on both revisions; before cannot select an absent row. After opens the correct B3 detail and closes its tab back to the same four results. All data is synthetic; no public PR is modified.',
 'F3':'The sentinel `zz26950nomatchzz` has no local or host matches. Both revisions show the genuine empty state and Clear search restores the same baseline/group counts. Both settled checkpoints are pixel-identical at zero threshold.',
 'F4':'Start in All, switch to Authored, then type `toa author:bilal draft:false review:approved label:bug -label:wip`. The completed qualified host answer is empty. Only synthetic eligible row 900011 remains after; before is empty. No involvement group header is rendered in Authored. Negative controls: 900012 other author (excluded by the harness server’s authored response); 900013 draft; 900014 review required; 900015 missing bug label; 900016 has excluded wip label. The last four are present in the authored baseline and excluded by the real client parser/filtering. Full seeds are in the manifests.',
 'F5':'Synthetic baseline contains failing-check Toast row 900021. Both `toa checks:passing` and alias `toa status:success` parse to passing checks and remain empty after completed host replies on both revisions. Removing the typed qualifier restores 900021 only after the fix. Both qualified controls are pixel-identical at zero threshold.',
 'F6':'Select Filters → Checks → Passing, then type bare `toa`. The recorded text-free baseline RPC has `filters.checks=passing`, contains passing Toast row 900031 plus a nonmatch, and excludes failing control 900021. The completed text answer is empty. After keeps 900031 with Others count 1; before becomes empty. This is a menu checks filter, with no typed checks/status qualifier. The original after video preserves a long automation pause after the menu selection; host baseline completion occurs at ~5s and typing starts at ~153s.',
}
lines=['## Evidence','',
 '**Additional pixel difference outside the intended list contents:** F4 Diff0 includes four pixels around the Sort control (maximum channel deltas 5, 1, 2, and 1). This persisted after retaking the pair; DOM, bounds and computed color match. DiffT excludes them. The evidence does not claim that every unrelated pixel is identical.','',
 'Captured **6/6 matrix rows**, with **12 before/after recordings**, **10 full-surface still pairs**, **20 diffs**, and six comparison sheets. **Unverified rows: none.** F1/F3 use the real app with live public GitHub data. F2/F4/F5/F6 are visibly labelled page harness captures for controlled forge responses.','',
 f'Before {before}; after {after}. Both revisions ran from their own checkouts with separate fresh server state and `T3CODE_DEV_SEED=0`. No production files, public PRs, or live T3 userdata were changed.','',
 '**Environment for every capture:** Linux x86_64; T3 preview headless Chrome 156.0.0.0; no device emulation; viewport **1440×900 CSS px**; **DPR 2**; default **16px / 100%** font scaling; dark theme; scroll top; default merge-readiness sort; browser Date frozen at **2026-10-29T13:30:00Z**. PNG snapshots contain the whole surface downsampled to **1280×800**; original MP4s are **2880×1800**. Sheets scale the full images without cropping. The matrix explicitly collapses desktop (same web page), mobile, alternate width and light theme.','',
 '**Settling and limits:** relevant successful RPC completion was observed directly; fonts were awaited; each still passed a pairwise zero-pixel screenshot comparison at least 500ms apart. Images/diffs and recording filmstrips were opened for inspection. The preview API offers neither reduced-motion emulation nor a global network-idle primitive; reduced motion remained false. Recordings retain transitions. See the rebuilding notes for these capture-tool limits.','',
 '**Diff definition:** Diff0 highlights any nonzero RGB-channel change in full-intensity magenta; DiffT highlights a maximum channel difference **>16/255**. Context is quarter brightness. Both compare original uncropped PNGs. Diff0 and DiffT derive from the before/after SHAs printed below.','',
 '**What else changed:**','',
 '- F1: matching rows, row metadata, Authored/Others headers/counts replace the erroneous empty illustration, message and retry/clear controls.',
 '- F2: Others count changes 2→4 and B3/B4 rows appear; H1/H2 and the matched-in-description indicator remain. Selecting the newly reachable B3 opens its detail panel and closing returns to four rows. The after-only returned snapshot also clears the prior Refresh-button hover shading (803 pixels, maximum channel delta 9); the main before/after list pair has matching hover state.',
 '- F3: neither settled checkpoint changes at zero threshold.',
 '- F4: the eligible authored row and its metadata replace the empty state; the four low-level Sort-control pixels are disclosed above.',
 '- F5: neither checks/status control changes at zero threshold; removing the qualifier replaces the empty state with the failing-check Toast row and Others 1.',
 '- F6: passing Toast row and Others 1 replace the empty state. No other changed pixels occur outside the listed result/empty-state areas in F1/F2/F5/F6.','',
 'Comparison sheets: '+', '.join(link(f'{f}-sheet.png',f) for f in data)+'.','',
 '**Reproduction and scripts**','',
 *[md(n) for n in ['reproduce.md','seed.sh','start-servers.py','instrument.js','harness.js','driver.js','package-evidence.py','upload-evidence.py','build-index.py','fixture-matrix.md']],
 '',link('comparison-manifest.json','Combined capture, RPC and pixel comparison manifest'),'',
 'The timestamps below are seconds from the monotonic sample immediately before each recording starts; screencast latency can shift video offsets by about one second. Every query uses actual individual keystrokes, spaced by 60ms plus tool overhead. The uploaded manifests preserve exact action/RPC times, inputs and identities without credentials.','']
for fixture,item in data.items():
    lines += [f'### {fixture} — {titles[fixture]}','',notes[fixture],'',md(f'{fixture}-sheet.png'),'']
    lines += ['**Scripted steps and timestamps (before | after), above the capture pair:**','', '| Step | Before (s) | After (s) |','| --- | ---: | ---: |']
    actions={side:item['manifests'][side]['final']['actions'] for side in ('before','after')}
    labels=list(dict.fromkeys(a['label'] for side in ('before','after') for a in actions[side]))
    for label in labels:
        times=[]
        for side in ('before','after'):
            d=item['manifests'][side];found=next((a for a in actions[side] if a['label']==label),None)
            times.append(f'{(found["t"]-d["final"]["recordOffset"])/1000:.1f}' if found else 'unavailable')
        lines.append('| '+label+' | '+' | '.join(times)+' |')
    lines += ['', '**Completed query replies:**','', '| Side | Time (s) | Query / filters | Answer count |','| --- | ---: | --- | ---: |']
    for side in ('before','after'):
        d=item['manifests'][side];off=d['final']['recordOffset']
        for h in d['host']:
            event=next((e for e in d['final']['log'] if e.get('event')=='response' and abs(e['t']-h['t'])<.01),None)
            inp=event['input'] if event else {}
            desc='`'+inp.get('query','')+'`'+(' / `'+json.dumps(inp['filters'],separators=(',',':'))+'`' if inp.get('filters') else '')
            lines.append(f'| {side} | {(h["t"]-off)/1000:.1f} | {desc} | {h["count"]} |')
    lines += ['',md(f'{fixture}-before.mp4'),md(f'{fixture}-after.mp4'),'',link(f'{fixture}-before-manifest.json','Before RPC/action manifest')+' · '+link(f'{fixture}-after-manifest.json','After RPC/action manifest'),'',
        '| Checkpoint — web 1440×900, dark | Before | After | Diff0 | DiffT (>16/255) |', '| --- | --- | --- | --- | --- |']
    for pair in item['pairs']:
        name=pair['label']
        lines.append(f'| {name} | '+md(f'{fixture}-before-{name}.png')+'<br>'+before+' | '+md(f'{fixture}-after-{name}.png')+'<br>'+after+' | '+md(f'{fixture}-{name}-Diff0.png')+f'<br>{before} → {after}<br>{pair["Diff0"]["pixels"]} pixels | '+md(f'{fixture}-{name}-DiffT.png')+f'<br>{before} → {after}<br>{pair["DiffT"]["pixels"]} pixels |')
    if fixture=='F2':
        lines += ['', '**After-only selection checkpoints** (B3 is absent before, so these are supplemental illustrations rather than a fabricated comparison pair):','',md('F2-after-detail.png'),'',md('F2-after-returned.png')]
    lines += ['',link(f'{fixture}-before-filmstrip.png','Before recording inspection sheet')+' · '+link(f'{fixture}-after-filmstrip.png','After recording inspection sheet'),'']
lines += ['Captured by gpt-6.1-sol in the Codex harness using the T3 preview browser tools. Media were uploaded using ghdrop.','']
text='\n'.join(lines)
(root/'evidence.md').write_text(text)
(out/'index.md').write_text(text)
print('Wrote evidence.md and index.md:',len(text),'characters')
