"""A reading page for the hand-written Wharton reference: groups, their draft events, and every scene. cd probe/wharton && python3 claude_page.py > claude/REFERENCE.html Self-contained HTML, no scripts beyond a filter box. Reads chunks.json, stories.json and claude/*.json. No inference. """ import collections, glob, html, json, pathlib L = lambda p: json.loads(pathlib.Path(p).read_text(encoding='utf-8')) chunks, stories = L('chunks.json'), L('stories.json') rows = {r['chunk']: r for f in sorted(glob.glob('claude/batches/s*.json')) for r in L(f)} groups, events, setting = L('claude/groups.json')['groups'], L('claude/events.json')['events'], L('claude/setting.json') ev = {e['group']: e for e in events} member_of = {m: g['id'] for g in groups for m in g['members']} e = html.escape TIGHT = {2: 'one predicament', 1: 'broad'} def effect(x): p = x['path'].replace('relationships.', '').replace('skills.', '') if x.get('op') == 'set': return f'{e(p)} ✓' if x.get('op') == 'push': return f'notable +' return f'{e(p)} {"▲" if x["dir"] == "+" else "▼"}' def scene_ref(i): return f'{i}' out = [] w = out.append nst = len(stories); nsc = len(chunks); cov = len(member_of) w(f''' Wharton Reference Pack

The Descent of Man and Other Stories: reference pack

Edith Wharton, 1904 (Project Gutenberg #4519). Every scene was read and summarised by Claude, and the groups and draft events were written by hand, as a comparison target for the story-to-pack tool and its use of local inference. It is a reference to compare against, not an answer key: another careful reader would group some of these differently.

{nst} stories · {nsc} scenes (same ~320-word cut as the O. Henry and Jacobs probes) · {len(groups)} recurring groups · {cov} scenes ({cov/nsc:.0%}) in a group · {len(events)} draft events · {sum(len(x["options"]) for x in events)} options
A group needs ≥3 of the 10 stories and no story above 50% of its members (O. Henry: ≥4 of 97, ≤40%). Effects are a path and a direction only: DESIGN.md gives numbers to the search, not the model.

Setting

Suggested player role: {e(setting["player_role_suggestion"])}

{"".join(f"" for k, v in setting["stat_labels"].items())}
{e(k)}{e(v)}

{e(setting["why_these_labels"])}

Roles: {" · ".join(f'{e(r["token"])}{e(r["role"])}' for r in setting["roles"])}

Stages: {" · ".join(f'{e(s["title"])}: {e(s["standing_rule"])}' for s in setting["stages"])}

Groups and their events

''') for g in groups: m = g['members'] st = collections.Counter(chunks[i]['story'] for i in m) x = ev[g['id']] w(f'

{e(g["name"])}

{e(g["predicament"])}

') w(f'

{TIGHT[g["tightness"]]}{len(m)} scenes' f'{len(st)} storieslargest story {max(st.values())/len(m):.0%}' f'stage: {e(x["stage"])}{"one-off" if not x["recurs"] else ""}

') w('
Member scenes
') for i in m: w(f'') w('
{scene_ref(i)}{e(chunks[i]["title"])}{e(rows[i]["predicament"])}
{e(rows[i]["summary"])}

Draft event

') for d in x['descriptions']: w(f'
{e(d["text"])} from {", ".join(scene_ref(s) for s in d["from"])}
') w('
') for o in x['options']: src = o['source'] if src.startswith('scene'): s = int(src.split()[1]); src = f'scene {scene_ref(s)}' if o.get('source_in_group') is False: src += f' (outside group, {o["offset_from_nearest_member"]:+d} from a member)' w(f'') w('
OptionSourceEffects
{e(o["label"])}
{e(o["result"])}
{src}{"".join(effect(f) for f in o["effects"])}
') w('

Every scene

') for si, s in enumerate(stories): ids = [i for i, c in enumerate(chunks) if c['story'] == si] w(f'

{si}. {e(s["title"])} {s["words"]:,} words, {len(ids)} scenes

') for i in ids: gid = member_of.get(i) w(f'' f'') w('
{i}{e(rows[i]["summary"])}
{e(rows[i]["predicament"])}
{f"""{e(gid)}""" if gid else "-"}
') w('''
''') print('\n'.join(out))