import json, sys

spans = [json.loads(line) for line in open(sys.argv[1]) if line.startswith('{')]
by = {span['spanId']: span for span in spans}

def ancestor(span, name):
    while span:
        if span['name'] == name:
            return span
        span = by.get(span.get('parentSpanId'))

sync_name = 'ThreadPullRequestServiceV2.synchronize'
refresh_name = 'PullRequestService.refreshAfterTurn'
t0 = min(int(span['startTimeUnixNano']) for span in spans)
markers = [int(span['startTimeUnixNano']) for span in spans if span['name'] == refresh_name]
assert len(markers) == 1, f'Expected one completed-turn refresh marker, got {len(markers)}'
turn_end = markers[0]
for span in sorted(spans, key=lambda span: int(span['startTimeUnixNano'])):
    name = span['name']
    operation = span.get('attributes', {}).get('github.operation', '')
    if name in (sync_name, refresh_name) or (name == 'GitHubGraphQlAuth.execute' and operation == 'listPullRequestsByHead'):
        sync = ancestor(span, sync_name)
        print(f"t={(int(span['startTimeUnixNano']) - t0) / 1e9:7.3f}s {name:42} {operation:24} parent_sync={sync['spanId'][:6] if sync else '-'}")
lookups = [span for span in spans if span['name'] == 'GitHubGraphQlAuth.execute'
           and span.get('attributes', {}).get('github.operation') == 'listPullRequestsByHead'
           and ancestor(span, sync_name) and int(span['startTimeUnixNano']) > turn_end]
syncs = [span for span in spans if span['name'] == sync_name]
print(f'listPullRequestsByHead under {sync_name} after the turn ended: {len(lookups)}')
print(f'{sync_name} passes in single-thread capture: {len(syncs)}')
print('Turn-end boundary: first PullRequestService.refreshAfterTurn span start (after terminal events commit).')
print('Scope: fresh home, one synthetic thread; count exact GitHub operation under synchronize; no controlled queue gate.')
