Skip to content

S12: Aris and the incident - #368

Merged
jamescrosswell merged 5 commits into
mainfrom
task/359-aris-incident
Oct 22, 2026
Merged

jamescrosswell merged 5 commits into
mainfrom
task/359-aris-incident

Conversation

@jamescrosswell

@jamescrosswell jamescrosswell commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Each world now makes its own incident once, when it's created: who attacked Aris and why, what witnesses saw of that night, who knows what, and two clues in her empty workshop. The facts live in S2's Incident, written into residents' memories, and residents tell the player what they know as their trust allows. Attackers lie through #285's code-decided lies.

server/simile/story/ (new, with its README)

  • The attackers. content/story/story.toml has five grievances, each with the values that make it rankle:

    • a repair that failed them
    • what she does to her own body
    • the rules she breaks
    • a deal she cost them
    • a public humiliation

    A resident's hostility is their best grievance's fit plus how far their Compassion is below 0.5. One to three residents at 0.2 or more attack her (40/35/25%), drawn by how hostile they are. With today's residents the suspects are Henrietta, Raymond, Marguerite and Priya.

  • The motive. One structured Sonnet call (purpose="story-motive") writes it. For each attacker it also writes their grievance, their memory of the night, an alibi, a deflection and a denial in their own voice, and the line Aris wrote about them by their initial. It's told where several attackers met beforehand, so the motive and the facts agree. Every cover stays vague and points at nobody, neither a resident nor a stranger, since Dialogue: direction from low scores, a counterweight and bounds #283's bounds rule out making up facts about people. A failed or invalid reply, or the fake LLM, falls back to the content's templates, so a world always gets an incident.

  • The facts, a week before the world starts:

    • gone, which everyone knows and tells anyone
    • Aris's notes, which name one attacker by their initial
    • a scrap of an attacker's top on the door frame
    • a witness who heard shouting
    • a witness who saw that attacker (described from their Appearance) and the others leave, so it's about all of them
    • the attackers meeting the evening before
    • each attacker's row with Aris, with a witness
    • each attacker in the lane (there_<id>)
    • the attack itself

    The leads always run from the clues to every attacker's there_ fact.

  • Willingness.

    • Witnesses need trust from 0.5 + Security, Peace of Mind, -Justice, -Honesty. Below that they keep quiet, or lie if they're dishonest (red herrings for the eye).
    • Attackers never own up to the attack unless they're honest, and admit their grudge from 0.75 trust.
  • Memories. Every knower gets one observation memory per fact, with its fact_id. Residents hostile enough to be suspects remember her going as good riddance.

  • Aris's notes and the clue are cells on the workshop's ground floor (aris_notes on the workbench, door_frame by the door), in content/story/. The map is unchanged.

  • uv run simile story incident --seed N --facts makes one without touching any world.

Telling and lying (server/simile/conversation/telling.py, small hooks in engine.py, player.py, prompt.py, temptation.py, lifespan.py, and one line in reflection/outcome.py)

  • Once the player's lines mention Aris or the attack (Aris, the mechanic, her workshop, what happened to her or at the workshop, who smashed up the workshop, the break-in, the attack...; not "a heart attack", everyday maker space talk, or "that night" and "where were you" alone), the resident is set up for the rest of the conversation. For each fact they know, R1's trust in the player decides:
    • tell: their first memory of the fact, which they'll tell if asked, so a lie they told since never takes its place
    • lie: a Kind.FACT temptation, with no roll, since trust decided it. Its direction ties the cover to the one question it answers, without the truth: 'You won't say where you'd been the night Aris was attacked. Only if asked that, say exactly: "I was at home all night."' After the lies, once, they're told never to volunteer a cover or add a denial of their own. B3 gets it as any other lie, with Lie.fact_id. The liar's memory of lying and their summary of that conversation both carry that fact_id, so recall leaves them out
    • keep: one paragraph, however many facts, says they'd rather not say. Their memory isn't in the prompt, so it can't slip out.
  • PlayerTalk.tellings holds each fact's Telling (fact, knower, Stance), and Temptation.fact_id and Lie.fact_id mark an incident lie.

Memory (server/simile/memory/store.py): recall leaves out memories with a fact_id, facts(owner) returns them, and forget_facts() drops them. So nobody brings the incident up in a scene, a reply or a work until the player asks. After that, B3 remembers what a resident told the player like any conversation, but never the truth behind a lie: neither B3's prompt nor the liar's holds it.

Choices

  • Old saves get an incident once, on their next start. Without one an old village has no story, short of simile reset, which throws away its memories and relationships. Adding one is safe:

    • it's dated a week before the save's own time, which can fall within what the village has lived through, but its memories stay out of recall, so residents only bring it up when asked, as in a new world
    • it's made at most once and its memories are never lost: they're stored first and the world is saved after, and at startup a world with no incident forgets any memories with a fact_id, which only a start that died in between can leave.

    Tested on a copy of server/data/simile.db in a temp dir, with the fake LLM:

    • before: no incident, 510 memories
    • first start: Priya attacked, 17 incident memories, saved
    • second start: the same incident and 17 memories, nothing made again
  • Residents don't tell each other. Telling is to whoever asks, which is the player. Gossip about the incident between residents would change everyday chatter, so learned_by is left for S11's gossip.

  • The incident is made in the story lifespan, after persistence has restored the save. It blocks startup for the one Sonnet call, once per world: 7 to 23 s in the real run. A cast world (P9) draws it from the cast's seed, a world with no residents makes none, and the log says only that it was made.

Notes for review

  • Outside Owns: four persistence tests asserted that nothing makes an incident at startup, which S12 now does. I changed only those assertions:
    • three in tests/persistence/test_story.py: an old save, a fresh start and :memory: now start with an empty journal and a new incident
    • one in tests/persistence/test_lifespan.py: the restart comparison now includes the investigation
    • and, as the lead agreed, reflection/outcome.py sets fact_id on the memories B3 writes for an incident lie, and on the liar's summary of that conversation
  • The day: test_simulate's 24-hour day (8:00, talk=3, seed 0) prints the same report on main, on this branch, and on this branch with a made incident's memories seeded into the store. The simulation runs no conversations, the seeded memories are observations, which don't inspire, and recall leaves them out.
  • Sonnet calls in the real run: 11. One per incident for seeds 1, 2, 3, 5, 6, 8 and 11, seed 8 again after giving the writer the meeting place, seeds 3 and 2 again to check the first cover rule, and seed 2 once more after the covers were made vague. None needed a retry. Haiku 4.5 player replies to three questions, asked before the review and again after it, checked telling and lying.

Please look at

Example motives (Sonnet, today's residents):

  • Seed 5, Henrietta, alone: Henrietta spent months writing to the council and filing complaints about Aris's late-night welding and the fumes in the lane, and the workshop was never shut down. That failure shook her belief that rules protect people, and she decided to make Aris understand it herself. She went alone that night meaning to wreck the place and frighten Aris into stopping, and it turned violent when Aris came out into the lane and Henrietta grabbed at her steel hand.
  • Seed 6, Marguerite, alone: Marguerite Oyelaran spent months trying to buy the design for Aris's pump governor, planning to patent it and license it on. Aris refused to sell, then pinned the full drawings up for anyone to copy, which made a patent worthless and cost Marguerite the money she had already put into the deal. She went to the maker space alone at night to wreck the place and frighten Aris into feeling the loss. It turned physical in the lane, and she tore at the steel hand far harder than she meant to.
  • Seed 11, Marguerite, alone, a different grievance: Marguerite Oyelaran had offered to patent the design of Aris's pump repair for the village, and Aris corrected her in front of everyone at the village hall, showing the design was old and unpatentable. For a woman whose living and pride rest on knowing what an idea is worth, being wrong in public was something she couldn't let go, and it festered for weeks. She went alone to the workshop to scare Aris and wreck her work, and when Aris grabbed her in the lane behind it, the steel hand was torn half off in the struggle.
  • Seed 1, Priya and Henrietta: Priya was left out of pocket and embarrassed after a repair Aris did failed in the middle of her biggest night, and she wanted Aris to feel some of that humiliation. Henrietta had long believed Aris's body modifications were unnatural and that the children hanging around the workshop were at risk, and she wanted Aris frightened into closing her door to them. They met by chance when each was nursing her own anger, agreed to go to the workshop together after dark to scare her and wreck the place, and the lane behind it turned rougher than either meant it to.
  • Seed 8, Raymond and Henrietta: Raymond Tay lost a deal because Aris wouldn't sell him what he needed and then made the terms public, and Henrietta Okonkwo-Bell had spent months trying to get the workshop shut down through the proper channels and been turned away. They met at the Daily Grind the evening before, each found the other was fed up with Aris, and agreed to go and sort out the maker space once and for all. They meant to wreck some of her kit and frighten her into backing down, and it got out of hand in the lane behind the workshop.
  • Seed 3, Raymond, Priya and Henrietta: Three people with three separate complaints about Aris found they were all saying the same things in the same corners of the village, and a night of grumbling turned into a plan to go to the workshop and scare her into changing her ways or leaving. Raymond wanted payback for a deal she had blown open, Priya for a repair that failed on the worst night of her season, and Henrietta for the children she thought Aris was leading astray. They meant to smash some things and frighten her, but she fought back, it got rough in the lane, and her hand was torn half off before they ran.
  • Seed 2, Marguerite, Priya and Raymond: Three people with separate quarrels with Aris ended up in the same place on the same night. Marguerite had never forgiven being shown up in front of the village, Priya was still out of pocket after Aris refused her, and Raymond had spent months telling himself the workshop was no place for children. They went to wreck the workshop and frighten her into backing down, and in the lane behind it a shove and a grab turned into her steel hand being torn half off. None of them had planned that, and none of them has said a word about it since.

Their notes, alibis and deflections, for example:

  • Aris's notes:

    • "R. came by again about the shop deal. I told him I'd say it to anyone who asked, and I won't sell him what he wants. He went very quiet and smiled, and I liked that less than the shouting."
    • "P. was back about the compressor. It would have cooked itself in a month, but she'd rather be angry than listen."
  • Alibis:

    • Henrietta: "I was at home with the curtains drawn, same as every evening after the news."
    • Raymond: "I was in my car on the phone to a client for most of the evening."
    • Priya: "I was setting up for the next night's bar, ask anyone who bought a drink."
  • Deflections now stay vague and point at nobody. Seed 2's three attackers, after the rule:

    • Marguerite: "I couldn't say who it was, and I make a point of not guessing."
    • Priya: "I really don't know who did it, and I'd rather not think about it."
    • Raymond: "I wouldn't like to say, it's not my place to point a finger."

    Before the rules, the same seed's deflections invented people ("some people from out of town") or framed real neighbours ("I'd look at Desmond").

Asked in the real game (Haiku 4.5, a template incident with seed 3):

Visitor: Where were you the night Aris was attacked?
Raymond: I was at home all night.

Visitor: Did you see or hear anything the night Aris was attacked?
Lachlan: I heard shouting and breaking glass from the maker space that night, late on. Couldn't tell you who it was or what it was about, though. You asking because you're looking into it?

Visitor: Did you see or hear anything the night Aris was attacked?
Marguerite: I saw three figures hurry out of that lane late that night. Grey-haired one in red, sunglasses despite the dark. The other two I didn't get a clear look at.

Before the review Raymond added "I barely knew the woman", a denial nobody asked for. With each cover tied to its question, he gives only his alibi.

Taste, left as it is:

  • The seen lead names the attacker outright ("that sounds like Henrietta"), which makes that witness close to decisive.
  • Some witness covers aren't really lies ("I keep out of other people's business"), but the eye will still flush on them.
  • The debug overlay lists memories through recall, so it doesn't show the incident's memories. A list of facts there would help S13.

For S8, S11 and S13

  • S8 (the eye):
    • An incident lie is decided when the player first touches the incident and holds for the rest of the conversation, as Dialogue: code decides when a low-honesty resident lies, and the prompt directs the line #285's lies do.
    • PlayerTalk.tellings[fact_id].stance is Stance.LIE, talk.lies["fact/<id>"] with Temptation.fact_id, and engine.lies all say so.
    • Each cover is now tied to one question and must be said exactly, but code still doesn't know which sentence voices it: once an attacker is asked, every reply carries their cover directions. Flushing every line after the direction would over-flush, so the eye needs a per-line decision, such as matching the cover's exact words.
  • S11: telling reads R1's trust in the player at the moment they ask. If what residents see of you lowers trust, they'll tell less, with no change here. A resident who declines to talk tells nothing.
  • S13: found_in is aris_notes or door_frame, and load_story().clue(id) gives the cell and the journal's name.
    • A TELL is the fact, and its leads open.
    • A LIE is the knower's cover, as told, with no leads.
    • KEEP teaches nothing.
    • gone is told by anyone at trust 0, so the first time the player asks anyone about Aris, its leads to her notes and the shouting witness start the trail.

Closes #359

🤖 Generated with Claude Code

Each world makes its incident once, when it's created: one to three
residents whose values and grievances fit attack Aris, Sonnet writes the
motive and each attacker's side of it once, and the facts around the night
go into residents' memories, with witnesses who saw parts of it and two
clues in her workshop.

Residents tell the player what they know as their trust allows: they tell
it, lie with their cover story through #285's lies, or keep it back. R2's
recall leaves the incident's memories out, so nobody brings it up otherwise.

A save from before the incident gets one on its next start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jamescrosswell

Copy link
Copy Markdown
Contributor Author

Changes requested. The incident is well made and the trail holds. But once the player asks, the truth can reach everyday prompts through B3, and one failure window loses the incident's memories for good.

What I checked: every path is within Owns. In the persistence tests, only the four assertions the PR names (and those tests' names) change. Nothing touches the maps, the schema or the protocol, and every LLM call goes through L1. pytest tests/story tests/memory tests/conversation tests/persistence: 1808 passed. SIMILE_LLM=fake simile story incident --seed 4 --facts made three attackers from the templates without calling Claude. The 24-hour day (talk=3, seed 0) prints the same report on the merge base, on this branch, and on this branch with 26 incident memories already in the store.

Changes:

  1. The truth leaks into recall through B3. Each incident lie goes to engine.tell, so B3's prompt lists its Lie.said, and Lie.said holds the attacker's own memory, for example: Marguerite remembers this, but won't own up to it: "I went to the maker space the night Aris was attacked with Henrietta and Raymond...". B3 then writes her summary of the conversation with no fact_id, and recall puts it in her scenes, replies and works. "I lied to Visitor about what happened to Aris" gets there too, once for each lie she tells.
    • Keep the memory out of what B3 sees. It only needs the cover to check that the lie was told. Leaving the memory out of the liar's reply prompt as well, as KEEP does, would also stop it slipping out.
    • Then either tag the liar's lie memory with its fact or say why it's fine in recall. Tagging needs Lie.fact_id plus a change in reflection/outcome.py, which is outside Owns, so ask the lead.
    • The README says these memories are "never on anyone's mind in a scene, a reply or a work". Add that B3's memories carry the incident once the player has asked.
  2. A failure between the save and the memories loses them for good. begin saves the incident and then calls memory.add. If the process dies in between, or add raises (on a locked database, say), every later start finds an incident and skips it, so nobody ever remembers it. One fix: store the memories first and save after. When there's no incident, delete any memories that have a fact_id before making one, since they can only be left over from such a failure. Then the incident is made at most once and its memories are never lost.
  3. Residents volunteer covers and reword them. Once asked, an attacker gets all three directions (alibi, deflection and denial) on every reply, each opening "If it comes up". Raymond was only asked where he was, but he added a reworded denial ("I barely knew the woman"). Code didn't decide that lie, so the eye can't read it exactly. Tie each cover to its own question and forbid volunteering, e.g. 'Only if they ask about this, say exactly "..."; don't bring it up yourself.' The "sir" is B5 guessing the player's gender, which isn't this PR's to fix.
  4. Say KEEP once. A resident keeping back two facts gets the same KEEP paragraph twice, as Ingrid does in a 4-resident cast at seed 1 (shouting and grudge_henrietta). Dedupe it.
  5. Bring these docstrings to one line:
    • begin, make_incident and writing.write (3 lines each)
    • build (4 lines)
    • turn_prompt, which now has 4
    • the 2-line ones: cli.incident, _Building.witness, _Building.attacker, figure, hostility, attackers, Stance, stance, tellings in both telling.py and engine.py, PlayerTalk.tellings, _ask and MemoryStore.recall

Not blocking:

  • seen has about=[marked], but its witness saw every attacker. S2 says about lists everyone a fact is about, "even if those who know it couldn't name them".
  • The cues catch some everyday talk: "where were you" (also a shift cue), "workshop" and "heart attack". They miss some incident talk, such as "what happened to her" and "the mechanic". Since the setup lasts for the whole conversation, that's fine for now.
  • Casting's seeded() now depends on recall leaving these memories out. The story lifespan runs before casting's running, so without the filter a new P9 world would skip its cast's history. A test would pin that.
  • The debug overlay no longer shows any incident memories. A list of facts there would help S13.
  • For an old save, "dated before anything residents have said" holds only if the save covers less than 8 sim days. recall hides the memories, so it's harmless, but the README overstates it.
  • Nits:
    • begin raises IndexError at startup in a world with no residents.
    • The incident doesn't use P9's world seed.
    • The startup log prints the attackers and the whole motive.
    • A test name says "arises_workshop".

For the user (taste):

  • The motives are credible, small enough for a village, and vary well between seeds.
  • Several deflections point vaguely at other people. Priya's "I heard it was some people from out of town" makes some up, which Dialogue: direction from low scores, a counterweight and bounds #283's bounds forbid. The writer's deflection description could rule that out.
  • The alibis are in each resident's voice and specific. Some witness covers aren't really lies ("I keep out of other people's business"), but the eye will still flush on them. The seen lead names the attacker outright ("that sounds like Henrietta"), which makes that witness close to decisive.

jamescrosswell and others added 2 commits October 23, 2026 03:00
…ers to questions

- An incident lie's direction holds only the cover, tied to the one question it answers, so
  neither B3 nor the liar's prompt sees the truth. Lie.fact_id tags B3's lie memories, so
  recall leaves them out. Residents are told never to volunteer a cover or add a denial.
- KEEP is said once, however many facts are kept back.
- The incident's memories are stored before it's saved, and memories left from an incident
  that was never saved are forgotten first.
- The motive's writer may point covers only at the village's residents, or at nobody.
- The log says only that an incident was made. A cast world uses its seed, and a world with
  no residents makes none. `seen` is about every attacker. The cues need Aris or the night.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jamescrosswell

Copy link
Copy Markdown
Contributor Author

Addressed in bae3f58:

  1. The truth no longer reaches B3.
    • An incident lie's direction holds only the cover, tied to its question. Neither B3's prompt nor the liar's has the memory.
    • Lie.fact_id tags the memories B3 writes for an incident lie (reflection/outcome.py, as the lead agreed), so recall leaves them out.
    • test_b3_never_sees_the_truth_and_files_the_lie_under_its_fact checks B3's prompt and that no memory without a fact_id mentions the truth.
    • Both READMEs now say B3 remembers what a resident told the player, once asked.
  2. Memories first, then the save. begin stores the memories, then sets the incident and saves. With no incident, it first forgets any memories with a fact_id (MemoryStore.forget_facts). Tested both.
  3. Covers.
    • Each lie reads 'You won't say . Only if asked that, say exactly: ""'. One line after the lies says never to volunteer a cover or add a denial. In a real reply, Raymond now gives only his alibi.
    • The writer gets the residents' first names, and covers may point only at them or at nobody. Two Sonnet checks invented nobody, but most deflections now name a real neighbour. That's in the PR under Please look at.
  4. KEEP is said once.
  5. All 18 docstrings are one line.

Non-blocking, all done:

  • The log says only "Made the incident".
  • seen is about every attacker.
  • The cues need Aris or the night: "what happened to her" and "the mechanic" count, and "a heart attack" and a plain "where were you" don't.
  • A world with no residents makes none.
  • A cast world uses its seed.
  • A test pins casting's seeded().
  • The old-save README wording is fixed, and the test is renamed.

Sonnet calls: 10 in all.

@jamescrosswell

Copy link
Copy Markdown
Contributor Author

Changes requested (re-review of bae3f58). All five round-1 items are fixed, but tagging B3's lie memory with its fact brought a new bug, and one channel for the lie is still open.

Checked:

  • Against main, the diff stays within Owns, plus reflection/outcome.py, which the lead added. The schema and protocol files only show up in the compare because S5a: Contract: eating #367 was merged in.
  • Tests: pytest tests/story tests/memory tests/conversation tests/persistence tests/reflection tests/casting: 2029 passed. All 7 checks are green.
  • Every docstring and comment is now one line.
  • SIMILE_LLM=fake simile story incident --seed 4 still runs without calling Claude.
  • Startup order: begin forgets leftover memories, stores the new ones, then sets the incident and saves. If it dies after storing the memories, the next start forgets them and makes a new incident. No window makes two incidents or loses one.
  • forget_facts: it deletes only rows in memories with a fact_id. Only the incident's own memories and B3's incident lie memories carry one, and it runs only when there's no incident. The test keeps two copies with no fact_id, and they survive.
  • Prompts:
    • Neither the reply prompt nor B3's holds the true memories. The test checks every memory text.
    • Each cover is tied to its question, UNASKED appears once, and so does KEEP.
  • The cue tests cover six positives and four negatives, among them "heart attack", "Where were you yesterday?" and "the workshop".

Changes:

  1. After one lie, the B3 memory of lying replaces the real memory. tellings maps each fact_id to the newest memory. B3's lie memory now carries the fact_id and is newer than the seeded one, so it wins. If the resident later trusts the player enough to tell, the direction becomes: 'You remember this, and trust Visitor enough to tell them if they ask: "I lied to Visitor about what happened to Aris."'
    • I reproduced it at seed 4. Marguerite lies about grudge_marguerite at trust 0.3, then at 0.8 "tells" that line instead of her grudge.
    • A dishonest witness who lies and later tells has the same problem.
    • Keep the oldest memory per fact, or skip lie memories, and add a test where a resident who lied tells the original memory once trusted.
  2. B3 still knows the cover is a lie. The end-to-end test proves the true memories never reach B3, and that the lie memories are filed under their facts. It can't show what a real summary says, because its summary is scripted. B3's prompt still lists "I was at home all night" under "Lies they were set up to tell", so a real summary can say "I told Visitor I was home all night, though I wasn't". That summary has no fact_id, so it's in recall. Either:
    • give the liar's conversation memory the lie's fact_id in outcome.py, and make the test's scripted summary admit the lie, or
    • say in the README that a liar's own summary may still mention lying.

Not blocking:

  • The cues miss "Who smashed up the workshop?", "What happened at the workshop?" and "the break-in". They still fire on everyday "maker space" (Ingrid works there) and on "That night at the pub...". Since the setup lasts the whole conversation and residents only tell when asked, that's tolerable.

For the user (taste):

  • Deflections now point at named, innocent neighbours ("I'd look at Desmond, he never forgave her for something or other"). That frames real residents with claims nobody made, which sits awkwardly with Dialogue: direction from low scores, a counterweight and bounds #283's "never make up facts about the village, its places, times or people". Telling the writer to stay vague is the safer default, as the PR offers.

jamescrosswell and others added 2 commits October 23, 2026 03:49
…ague

- A resident who lied and later trusts the player tells their first memory of the fact, not
  B3's newer memory of lying.
- The liar's summary of a conversation with an incident lie carries the lie's fact_id, so
  recall leaves it out even when it admits the lie.
- The motive's writer keeps every cover vague, pointing at nobody, resident or not.
- The cues catch the smashed-up workshop and the break-in, and no longer fire on everyday maker
  space talk or "that night" alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jamescrosswell

Copy link
Copy Markdown
Contributor Author

Addressed in bf4232f:

  1. The first memory wins. tellings keeps each fact's oldest memory, which is the seeded one, so neither a lie memory nor a summary takes its place. test_someone_who_lied_tells_what_they_remember_once_they_trust_the_player runs both sequences: an attacker lies about a grudge, and a dishonest witness about the meeting. B3 reflects, trust rises to 0.95, and each then tells their original memory. Both cases fail on the old newest-wins code.
  2. The liar's summary stays with the fact. reflection/outcome.py gives the liar's conversation memory the fact_id of an incident lie they were set up to tell. The end-to-end test's summary now admits the lie ("I told Visitor I was home all night, though I wasn't."), recall returns nothing for the liar, and facts files it.
  3. Covers stay vague. The writer is told that every alibi, deflection and denial names, accuses and hints at nobody, resident or not. The prompt no longer lists residents, and fake-LLM tests check both. In one Sonnet call (seed 2), all three deflections pointed at nobody, e.g. "I wouldn't like to say, it's not my place to point a finger."
  4. Cues.
    • These now count: "Who smashed up the workshop?", "What happened at the workshop?" and "the break-in".
    • These no longer do: everyday maker space talk, "That night at the pub…", "Where were you that night?" alone.
    • All are in the tests.

Sonnet calls: 11 in all.

@jamescrosswell

Copy link
Copy Markdown
Contributor Author

Approved. Both round-2 changes are in. I checked them, and the #369 merge, at bf4232f.

  • The first memory wins, including for an old save. begin forgets every memory that has a fact_id before it seeds a new incident. Each seeded memory is dated at its fact, and every fact is at least 26 sim hours before the world's now (the latest is gone). B3 dates its memories at the conversation's end, which is never before the incident was made: the incident is only kept if it was saved, and a restore puts the clock back no earlier than that save. So a lie memory or a summary can't be older than the seeded one.
    • To try it, I made an incident for a save whose clock is far along, with older everyday memories in the store. I then filed a lie memory and a summary for every knower of every fact, dated at the instant the incident was made. All 11 residents still tell their seeded memory for every fact.
    • The new test covers an attacker and a dishonest witness who lie, go through B3, and tell the original memory at trust 0.95.
  • The liar's summary: _hidden gives it the fact_id of an incident lie they were set up to tell. The end-to-end test's summary admits the lie, recall is empty for the liar, and facts holds both memories.
  • Covers stay vague: the writer is told to name, accuse and hint at nobody. The residents list is gone from the prompt, and the fake-LLM test checks both.
  • The S5: The player gets hungry #369 merge is clean. No file was changed on both sides, and redoing the merge gives the same tree as the committed merge commit. Hunger's new player lifespan doesn't touch memory or the investigation.
  • Checks:
    • Tests: pytest tests/story tests/memory tests/conversation tests/persistence tests/reflection tests/casting tests/player tests/protocol: 2286 passed.
    • Ruff is clean, and all 7 checks are green.
    • Docstrings and comments are all one line.

Not blocking:

  • A liar's summary is filed under the fact whenever they were set up to lie, even if the lie never came up. So the whole conversation leaves their recall. That's a fair trade.
  • The cues still fire on any "the attack" or "the break-in" ("the attack on the bakery's prices") and on "Who hurt her?". They still miss "the cyborg". Fine for now.

@jamescrosswell
jamescrosswell marked this pull request as ready for review October 22, 2026 15:30
@jamescrosswell
jamescrosswell merged commit 4c95b6f into main Oct 22, 2026
7 checks passed
@jamescrosswell
jamescrosswell deleted the task/359-aris-incident branch October 22, 2026 15:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

S12: Aris and the incident

1 participant