#!/usr/bin/env python3 """Render groups.json + records.json as one self-contained page: REFERENCE.html.""" import html, json, pathlib here = pathlib.Path(__file__).parent d = json.loads((here / 'groups.json').read_text()) recs = {r['chunk']: r for r in json.loads((here / 'records.json').read_text())} stories = json.loads((here.parent / 'stories.json').read_text()) e = html.escape SRC = {'archetype': 'archetype', 'filled': 'filled gap'} def src(o): if o['source'].startswith('scene:'): n = o['source'].split(':')[1] return f'from scene {n}' + ('' if o['in_group'] else ' (outside this group)') return SRC[o['source']] def moves(m): return ' '.join(f'{e(k)} {e(v)}' for k, v in m.items()) def scene(c): r = recs[c] return (f'
{c} · {e(stories[r["story"]]["title"])} — {e(r["predicament"])}' f'

{e(r["summary"])}

') out = [] for g in d['groups']: out.append(f'''

{g["id"]} · {e(g["name"])}

{e(g["predicament"])}

{e(g["sentence"])} · {len(g["members"])} scenes from {len(g["stories"])} stories ({e(", ".join(g["story_titles"]))}) · stage {g["stage"]}

Options

    {"".join(f"
  1. {e(o['text'])} {src(o)}
    {moves(o['moves'])}
  2. " for o in g["options"])}

Scenes

{"".join(scene(m["chunk"]) for m in g["members"])}
''') un = ''.join(scene(c) for c in d['unassigned']) page = f''' Bierce Reference Groups

Tales of Soldiers and Civilians: reference groups

Ambrose Bierce, 1891 (Project Gutenberg #13334). 26 stories, 299 scenes cut by the same rule as chunk.py. Summaries, predicaments, groups and options written by Claude by reading every scene: no local inference. A comparison baseline, not a pack. Option moves give a direction only, never a number.

{"".join(out)}

Unassigned ({len(d["unassigned"])})

Scenes that fit no situation recurring in three or more stories.

{un}
''' (here / 'REFERENCE.html').write_text(page, encoding='utf-8') print('wrote', here / 'REFERENCE.html', len(page), 'bytes')