"""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"
{e(k)}
{e(v)}
" for k, v in setting["stat_labels"].items())}
{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 ""}
{e(d["text"])} from {", ".join(scene_ref(s) for s in d["from"])}
')
w('
Option
Source
Effects
')
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'
{e(o["label"])} {e(o["result"])}
{src}
{"".join(effect(f) for f in o["effects"])}
')
w('
')
w('
Every scene
')
for si, s in enumerate(stories):
ids = [i for i, c in enumerate(chunks) if c['story'] == si]
w(f'