Make mystery cases mislead instead of pointing straight at the culprit - #62
Merged
Conversation
…asking nicely
The misdirection rules added earlier are instructions, and the generator
satisfies their letter while still writing cases whose evidence names the
killer outright. Nothing measured the result, so nothing improved.
Generation now ends with a probe: a second call sees exactly what a player
sees — dossier, suspect cards, evidence, no secrets and no solution — and
names who it would accuse. If it fingers the real killer and can say what
gave it away, the case is explaining itself, and a revision pass rewrites
only the surface: dossier, cards and evidence contents. The solution,
secrets and testimony are never sent back for rewriting, so whodunit cannot
drift, and a revision that fails validation is discarded. Both steps are
best-effort — a probe failure still yields a playable game.
Two behavioural leaks are closed alongside it. Evidence reports were writing
the inference for the player ("which means only a doctor could obtain it"),
so reports are now findings-only. And with every innocent scripted to crack
under pressure while the killer never does, the culprit was identifiable from
demeanour whatever the plot did: the killer is now written as the cooperative
one, and exactly one innocent stonewalls to the end.
Generation can now take three calls, so the inspector quotes 30-60 seconds.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ToCUNy2SqwvfTq4a6xfSk1
Switches the case generator in both places that named a model: the template's mystery_gm tool_config, which is what an imported project actually uses, and the env fallback for agents configured without one. The probe and the surface revision follow the same setting. The benchmark comment is kept but no longer reads as the reason for the current value — it ranked candidates on latency and cost, never on how well the resulting plot held up. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ToCUNy2SqwvfTq4a6xfSk1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Generated mysteries were solvable in one move: every clue pointed the same way, no witness ever said anything untrue, and the murder method named the culprit's profession outright. The inspector then closed what little gap was left by interpreting each piece of evidence out loud.
What was wrong
check_accusationanswered with the innocent's fullnot_killer_note, which reasons from the very facts that solve the case.What changed
Misdirection is now required. No single clue may identify the killer — guilt follows only from combining two independent sources. The means must be available to at least three of the four suspects. One innocent is framed hard enough to be accused mid-game, and their exoneration depends on a detail the player has to dig up.
New
false_leadper suspect — a claim they state as fact but which is wrong, with at least one appearing to clear the real killer. It reaches the actor throughget_my_character, with instructions to hold it until confronted and then relativise rather than admit it.Split rebuttal. A wrong accusation now returns only a one-line
rebuttal— a single verified fact that blocks the accusation and explains nothing. The fullnot_killer_notemoves to the debrief, wherecheck_accusationalso returnscleared_suspectsso every thread closes at once.solutiongainedred_herringandturning_point, so the reveal explains the trail the player was meant to follow.Transparency is verified, not requested. After generation, a probe call sees exactly what a player sees — dossier, suspect cards, evidence, no secrets and no solution — and names who it would accuse. If it fingers the real killer and can say why, a revision pass rewrites only the surface. The solution, secrets and testimony are never sent back for rewriting, so whodunit cannot drift, and a revision failing validation is discarded. Both steps are best-effort: a probe failure still yields a playable game.
Two behavioural leaks closed. Evidence reports are findings-only — no "which means only a doctor could obtain it". The killer is written as the cooperative suspect, and exactly one innocent stonewalls to the end.
The inspector stopped solving the case. He may no longer rank suspects, confirm or reject a theory, say what a clue means, or highlight the decisive detail. He may note that two statements contradict each other, but not who is wrong.
The featured villa case was rewritten to the same standard. The poison is now the victim's own missing heart drops, so anyone in the house had access. A fourth evidence item puts the victim alive on the phone describing his own symptoms. The culprit is reachable only by intersecting the glass, the onset window and the butler's testimony — and Viktor looks guilty for most of the game.
Case generation runs on
openrouter/deepseek/deepseek-v4-flash; the inspector and the four actors keep their existingmodel_name.Compatibility
validate_casenow requires the new fields, so a case held in an existing session no longer validates. This is handled:_get_casesilently replaces it with the current default rather than failing, so a game in progress resets to the villa instead of erroring.The template is at version 1.3. A project already imported keeps running on its old instructions until it is synced — and
_sync_templateoverwritesinstructionandtool_configunconditionally, so any local edits to those agents would be replaced (recoverable fromagent_config_versions).murder-mystery.jsonandmurder-mystery-en.jsonare untouched — separate templates with the story hardcoded in agent instructions and memory blocks.Tests
536 pass (
python -m unittest discover -s shared/test -p "test_*.py"), 17 of them new: schema requirements for the misdirection fields, that a wrong accusation withholds the solving facts, that rebuttals share no fact withturning_point/evidence_chain, and six for the probe — that it sees only player-visible material, survives a model failure, ignores a lucky guess, and cannot change the solution.Generated by Claude Code