The pipeline's embed-and-cluster step is dead, and this commit holds both the evidence for that and the step proposed to replace it. Predicaments. Scenes are re-described as "what the person is up against", with no names, jobs or places, then embedded and clustered (redescribe.py, topic_words.py, topic_share.py). The pilot chose qwen3:14b over 3b by reading both side by side. Two defects the pilot exposed are fixed: split.py missed titles in quotes and a contents subtitle after a dash, so three stories had been merged into their neighbours, and strip_names.py read New York place names as people. The corrected corpus is probe/v2 (97 stories, 839 scenes); carry_summaries.py reuses the 829 unchanged v1 summaries. Topic share fell from 20% to 13% at k=60, short of the pre-registered 10%. Hand references. Three corpora were read scene by scene and written up by hand, under the same prompt rules the local models get, as a baseline to judge them against: O. Henry (probe/v2/claude, 839 scenes, 20 situations), Wharton's Descent of Man (probe/wharton, 262 scenes, 16 groups) and Jacobs's The Lady of the Barge (probe/jacobs, 157 scenes, 19 groups). Each has its own README and a readable page. No inference was used for any of them. Catalogue. probe/catalogue maps every hand group in the three references onto 36 situation entries, with an answer key per corpus and one recurrence rule applied to all three. classify.py assigns a scene one entry or none, leave-one-corpus- out; score.py checks it against the key, with a self-test on random labels. Why clustering is out: hand-written predicaments, embedded and clustered exactly as the model's were, agree with the hand grouping at ARI 0.05 — no better than the 14B text's 0.07. Better rewriting cannot rescue it. Embeddings cannot even shortlist: the hand label is the nearest entry 13% of the time and in the top 8 half the time. The classification runs are not here. The dev and test runs are pre-registered in probe/catalogue/README.md with the bar set beforehand, and are blocked on the inference host, whose GPU has fallen off the PCIe bus three times. The 30-scene partial output in out/ is not a result. Review page. The situation review is now a browser page rather than JSON edited by hand (review_page.py, review_page_logic.cjs with Node tests, format schema v2). It has never been rendered in a real browser. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014BygvsUXV9eU6oHkTCkKZ1
193 lines
14 KiB
Python
193 lines
14 KiB
Python
"""Hand-built reference grouping of the Jacobs predicaments (Claude-written, no local inference).
|
|
|
|
Each scene is in at most one group; scenes that fit no recurring situation stay unassigned.
|
|
Options follow DESIGN.md: 3-5 per group, each marked by where it came from --
|
|
'archetype' a fixed, setting-free option
|
|
'scene:N' what someone in scene N actually did (or refused to do)
|
|
'filled' written to fill a gap the scenes leave
|
|
`moves` names spine paths and a direction only (+ / - / risk). No magnitudes: the model never
|
|
chooses a number. Skill labels proposed for this corpus: nerve, cunning.
|
|
"""
|
|
G = [
|
|
dict(id='G01', name='Someone who knows your past names a price',
|
|
predicament='Someone who knows what you did wants paying to keep quiet, and you have a life to lose.',
|
|
actor='respectable person', wants='silence', counterpart='someone who knows their past',
|
|
members=[43, 67, 68, 81, 82, 83, 87, 90],
|
|
options=[
|
|
('Pay what they ask.', 'scene:82', {'money':'-', 'standing':'+', 'relationships.blackmailer':'+'}),
|
|
('Refuse, and dare them to tell.', 'scene:43', {'nerve':'+', 'standing':'risk'}),
|
|
('Seem to give in, and wait for your moment.', 'scene:87', {'cunning':'+', 'relationships.family':'-'}),
|
|
('Try to take the evidence off them by force.', 'scene:43', {'nerve':'+', 'flags.violent':'set', 'standing':'risk'}),
|
|
('Tell the truth yourself before they can.', 'filled', {'standing':'-', 'relationships.blackmailer':'-'})]),
|
|
dict(id='G02', name='Hiding something from someone close who keeps getting near it',
|
|
predicament='You are hiding something, and someone you care for keeps innocently coming close to it.',
|
|
actor='person with a secret', wants='to keep it hidden', counterpart='someone close to them',
|
|
members=[7, 8, 45, 46, 47, 48, 49, 50, 51, 52],
|
|
options=[
|
|
('Change the subject and steer them away.', 'scene:47', {'cunning':'+', 'relationships.close':'-'}),
|
|
('Keep your face still and say nothing.', 'archetype', {'nerve':'+'}),
|
|
('Promise to deal with it yourself, alone.', 'scene:49', {'nerve':'+', 'flags.secret_kept':'set'}),
|
|
('Tell them the truth.', 'filled', {'relationships.close':'risk', 'standing':'-'})]),
|
|
dict(id='G03', name='Someone innocent is taking the blame for what you did',
|
|
predicament='Suspicion has fallen on someone else for what you did, and you could let it stay there.',
|
|
actor='person at fault', wants='to escape blame', counterpart='an innocent suspect',
|
|
members=[10, 44, 59, 74, 75, 76, 77, 93],
|
|
options=[
|
|
('Own up.', 'scene:10', {'standing':'-', 'relationships.suspect':'+'}),
|
|
('Say nothing and let it pass.', 'archetype', {'relationships.suspect':'-', 'flags.guilty_secret':'set'}),
|
|
('Quietly steer suspicion away from them without confessing.', 'scene:60', {'cunning':'+'}),
|
|
('Point at them outright.', 'scene:75', {'standing':'+', 'relationships.suspect':'-', 'flags.guilty_secret':'set'})]),
|
|
dict(id='G04', name='A rival is after the same person',
|
|
predicament='Someone else wants the person you want, and only one of you can have them.',
|
|
actor='suitor', wants='the person they are courting', counterpart='a rival',
|
|
members=[4, 5, 111, 112, 113, 116, 117, 118, 140, 141, 142],
|
|
options=[
|
|
('Race them to it.', 'scene:111', {'nerve':'+', 'relationships.rival':'-'}),
|
|
('Settle it between you by a game or a bargain.', 'scene:114', {'relationships.rival':'+', 'standing':'risk'}),
|
|
('Put the rival in a bad light.', 'scene:142', {'cunning':'+', 'relationships.rival':'-', 'money':'-'}),
|
|
('Outdo their compliments.', 'scene:140', {'relationships.courted':'+'}),
|
|
('Step aside.', 'archetype', {'relationships.rival':'+', 'relationships.courted':'-'})]),
|
|
dict(id='G05', name='A deal you could tip in your favour',
|
|
predicament='You want something from someone and could quietly tip the deal your way.',
|
|
actor='bargainer', wants='the better end of a deal', counterpart='the other party',
|
|
members=[35, 36, 114, 115, 143],
|
|
options=[
|
|
('Deal straight.', 'archetype', {'standing':'+'}),
|
|
('Hide why you want it and haggle.', 'scene:36', {'cunning':'+', 'money':'-'}),
|
|
('Butter them up first: let them win, fill their glass.', 'scene:143', {'cunning':'+', 'relationships.other':'+'}),
|
|
('Slip in a condition they will not notice.', 'scene:114', {'cunning':'+', 'relationships.other':'risk'})]),
|
|
dict(id='G06', name='Partners who do not trust each other over a shared prize',
|
|
predicament='You and a partner are after the same prize, and neither of you trusts the other.',
|
|
actor='partner', wants='their share, or all of it', counterpart='their partner',
|
|
members=[30, 33, 34, 37, 38, 125],
|
|
options=[
|
|
('Split it fairly and keep your word.', 'archetype', {'relationships.partner':'+', 'money':'+'}),
|
|
('Cut your partner out.', 'scene:37', {'cunning':'+', 'money':'risk', 'relationships.partner':'-'}),
|
|
('Keep your partner where you can see them.', 'scene:38', {'nerve':'+'}),
|
|
('Threaten to tell unless they cut you in.', 'scene:29', {'money':'+', 'relationships.partner':'-'})]),
|
|
dict(id='G07', name='People are after your money',
|
|
predicament='You have money, or people think you do, and everyone around you suddenly wants a piece.',
|
|
actor='person with money', wants='to keep it', counterpart='people courting it',
|
|
members=[27, 28, 32, 138, 139],
|
|
options=[
|
|
('Enjoy the attention while it lasts.', 'scene:138', {'relationships.flatterer':'+', 'money':'risk'}),
|
|
('Tell them plainly what you think they are after.', 'filled', {'nerve':'+', 'relationships.flatterer':'-'}),
|
|
('Make sure nobody gets it.', 'scene:28', {'money':'+', 'standing':'-'}),
|
|
('Deny you have anything.', 'scene:32', {'cunning':'+'})]),
|
|
dict(id='G08', name='A windfall with a catch',
|
|
predicament='Something could change your fortunes overnight, and you are not sure what it will cost.',
|
|
actor='person of small means', wants='a change of fortune', counterpart='whoever offers it',
|
|
members=[16, 17, 18, 20, 22, 135, 136, 137],
|
|
options=[
|
|
('Try it and see.', 'scene:18', {'money':'+', 'flags.tempted_fate':'set'}),
|
|
('Throw it away.', 'scene:17', {'nerve':'+'}),
|
|
('Ask someone who knows before you decide.', 'scene:16', {'relationships.adviser':'+'}),
|
|
('Keep it quiet until you are sure.', 'filled', {'cunning':'+'})]),
|
|
dict(id='G09', name='Your scheme turns on you',
|
|
predicament='A plan you were sure of is coming apart, and the people you meant to fool are watching.',
|
|
actor='schemer', wants='to come out ahead', counterpart='the people they tried to fool',
|
|
members=[12, 39, 40, 54, 78, 79, 88, 91, 144, 145, 146, 147],
|
|
options=[
|
|
('Keep smiling and take your loss.', 'scene:40', {'standing':'-', 'nerve':'+'}),
|
|
('Brazen it out.', 'scene:107', {'nerve':'+', 'standing':'risk'}),
|
|
('Double down on the plan.', 'scene:145', {'cunning':'+', 'money':'risk'}),
|
|
('Own up and ask for mercy.', 'filled', {'standing':'-', 'relationships.fooled':'+'})]),
|
|
dict(id='G10', name='Treated with contempt by those over you',
|
|
predicament='People with power over you treat you with contempt, and you want to turn the tables.',
|
|
actor='person brought low', wants='respect, or revenge', counterpart='those in charge',
|
|
members=[64, 92, 124, 126, 127, 128, 129, 130, 131, 133, 134],
|
|
options=[
|
|
('Swallow it and do as you are told.', 'scene:128', {'relationships.authority':'+', 'standing':'-'}),
|
|
('Insist on who you really are.', 'scene:124', {'standing':'risk', 'relationships.authority':'-'}),
|
|
('Wait until they need you, then name your price.', 'scene:129', {'money':'+', 'cunning':'+'}),
|
|
('Get even quietly.', 'scene:131', {'cunning':'+', 'relationships.authority':'-', 'flags.revenge':'set'})]),
|
|
dict(id='G11', name='Someone unwelcome has settled into your home',
|
|
predicament='Someone you do not want has settled into your home, and you cannot seem to make them go.',
|
|
actor='householder', wants='them gone', counterpart='an unwelcome guest',
|
|
members=[2, 63, 85, 86],
|
|
options=[
|
|
('Tell them to go.', 'filled', {'nerve':'+', 'relationships.guest':'-'}),
|
|
('Put up with it.', 'scene:85', {'relationships.household':'-'}),
|
|
('Arrange for them to disgrace themselves.', 'scene:63', {'cunning':'+', 'relationships.guest':'-'}),
|
|
('Stand up for the people they are hurting.', 'scene:86', {'relationships.household':'+', 'nerve':'+'})]),
|
|
dict(id='G12', name='Something may be waiting in the dark',
|
|
predicament='Something frightening may be close by in the dark, and you must decide whether to face it.',
|
|
actor='frightened person', wants='to be safe', counterpart='whatever is out there',
|
|
members=[19, 25, 26, 73, 96, 97, 99, 151, 152, 153, 154, 155],
|
|
options=[
|
|
('Open the door and look.', 'scene:155', {'nerve':'+', 'flags.faced_fear':'set'}),
|
|
('Bar the door and stay put.', 'scene:97', {'nerve':'-'}),
|
|
('Go on through the dark anyway.', 'scene:151', {'nerve':'+', 'standing':'risk'}),
|
|
('Laugh it off in front of the others.', 'scene:19', {'standing':'+'})]),
|
|
dict(id='G13', name='Pressed to do something you believe is wrong',
|
|
predicament='Someone is pressing you to do something you believe is wrong or dangerous.',
|
|
actor='reluctant person', wants='to do right without losing them', counterpart='the one pressing',
|
|
members=[23, 24, 29, 62, 69],
|
|
options=[
|
|
('Refuse flatly.', 'scene:62', {'nerve':'+', 'relationships.presser':'-'}),
|
|
('Do it, to keep the peace.', 'scene:24', {'relationships.presser':'+', 'flags.did_wrong':'set'}),
|
|
('Go along, for a share.', 'scene:29', {'money':'+', 'flags.did_wrong':'set'}),
|
|
('Agree, then quietly undo it.', 'scene:26', {'cunning':'+', 'relationships.presser':'risk'})]),
|
|
dict(id='G14', name='A story nobody can check',
|
|
predicament='You have a story to tell that nobody can check, and people are ready to believe it.',
|
|
actor='storyteller', wants='to be believed', counterpart='a listening crowd',
|
|
members=[95, 98, 102, 103, 148],
|
|
options=[
|
|
('Tell it big.', 'scene:102', {'standing':'+', 'flags.tall_tale':'set'}),
|
|
('Tell it straight.', 'archetype', {'standing':'+'}),
|
|
('Get someone to vouch for you.', 'scene:103', {'relationships.voucher':'risk'}),
|
|
('Say you have seen nothing worth telling.', 'scene:148', {'nerve':'+'})]),
|
|
dict(id='G15', name='Caught where you should not be',
|
|
predicament='You have been caught where you should not be, and need an explanation right now.',
|
|
actor='person caught out', wants='to get away with it', counterpart='whoever caught them',
|
|
members=[9, 14, 58, 60, 65, 66, 106, 107, 108, 119],
|
|
options=[
|
|
('Brazen it out.', 'scene:107', {'nerve':'+', 'standing':'risk'}),
|
|
('Take the embarrassing explanation you are offered.', 'scene:66', {'standing':'-', 'relationships.catcher':'+'}),
|
|
('Tell the plain truth.', 'scene:119', {'standing':'-', 'relationships.catcher':'+'}),
|
|
('Say nothing and slip away.', 'archetype', {'relationships.catcher':'-'})]),
|
|
dict(id='G16', name='Blamed at home',
|
|
predicament='Someone at home holds you responsible for trouble you think is not your fault.',
|
|
actor='person being blamed', wants='peace at home', counterpart='the one blaming them',
|
|
members=[0, 1, 3, 55, 57],
|
|
options=[
|
|
('Put it on someone else.', 'scene:9', {'relationships.spouse':'+', 'relationships.other':'-'}),
|
|
('Keep your head down until it blows over.', 'archetype', {'relationships.spouse':'-'}),
|
|
('Speak up for yourself.', 'scene:57', {'nerve':'+', 'relationships.spouse':'risk'}),
|
|
('Smooth it over with a peace offering.', 'filled', {'money':'-', 'relationships.spouse':'+'})]),
|
|
dict(id='G17', name='You are sure who did it and cannot prove it',
|
|
predicament='You are sure who wronged you, and you cannot prove it.',
|
|
actor='wronged person', wants='what was taken, or justice', counterpart='the suspect',
|
|
members=[31, 56, 61, 100, 109],
|
|
options=[
|
|
('Search them.', 'scene:31', {'relationships.suspect':'-', 'standing':'risk'}),
|
|
('Accuse them openly.', 'scene:56', {'nerve':'+', 'relationships.suspect':'-'}),
|
|
('Set a trap.', 'scene:61', {'cunning':'+'}),
|
|
('Take it to the authorities.', 'scene:100', {'relationships.authority':'risk'}),
|
|
('Let it drop.', 'archetype', {'money':'-', 'standing':'-'})]),
|
|
dict(id='G18', name='Lost, far from anything you know',
|
|
predicament='You find yourself lost, far from anyone you know, and must get your bearings.',
|
|
actor='stranded person', wants='to get back', counterpart='whoever is nearby',
|
|
members=[122, 123, 149, 150],
|
|
options=[
|
|
('Ask the first person you meet for help.', 'scene:151', {'relationships.stranger':'+'}),
|
|
('Find your companion and make a plan together.', 'scene:123', {'relationships.companion':'+'}),
|
|
('Push on alone.', 'scene:150', {'nerve':'+', 'standing':'risk'}),
|
|
('Demand to be taken back at once.', 'scene:124', {'standing':'risk', 'relationships.authority':'-'})]),
|
|
dict(id='G19', name='Making amends',
|
|
predicament='You have wronged someone, and want them to forgive you.',
|
|
actor='person in the wrong', wants='forgiveness', counterpart='the person they wronged',
|
|
members=[11, 13, 156],
|
|
options=[
|
|
('Apologise plainly.', 'scene:156', {'relationships.wronged':'+'}),
|
|
('Make a grand gesture.', 'scene:11', {'nerve':'+', 'relationships.wronged':'risk'}),
|
|
('Pay for the damage.', 'filled', {'money':'-', 'relationships.wronged':'+'}),
|
|
('Leave it and hope it is forgotten.', 'archetype', {'relationships.wronged':'-'})]),
|
|
]
|
|
|
|
# Protagonist standing by story, for stage assignment (DESIGN.md "Stages"). Per story, not per
|
|
# scene: a coarse first cut, and the one place this grouping uses a judgement not in the text.
|
|
STANDING = {0:'middle', 1:'middle', 2:'low', 3:'high', 4:'middle', 5:'high', 6:'middle',
|
|
7:'low', 8:'high', 9:'low', 10:'low', 11:'middle'}
|
|
# Story 9's professionals are treated as crew for the whole voyage, so they rank low.
|