Files
lennartandClaude Opus 5 f1fadf7909 add a writer-facing content pipeline under writing/
Dialogue was already plain text, so the gap was never the .yarn files. It was
that the character material a writing session needs was either missing or stored
somewhere a writer could not comfortably work.

Three things were wrong. The writing handbook was not in the repo and was cited
by SC101.yarn as though it were. The best voice material — register, verbal tics,
success/failure/passive samples — was a \n-escaped string inside a JSON field.
The named NPCs had no sheets at all, existing only as their Yarn lines.

writing/ now holds the dialogue-facing layer, at the repo root so it gets no
.meta files and is not a Unity asset:

  STYLE.md          register, the five tone moves, the ban list, branching
                    shapes, the Wrong Truth rule, flag families, candidate rules
  YARN-PRIMER.md    the one page of syntax needed to write a scene
  GLOSSARY.md       in-world and project vocabulary
  voices/           the eleven skill voices, one readable sheet each
  characters/       Bartender, Chevalier Cassian Thal, Bradford Kane
  scenes/           SC-101's brief: intent, beats, checks, constraints
  lore/             the three identity readings and the rules that bind them

STYLE.md is a digest of the Mystery Writing Handbook in the Obsidian vault, with
pointers rather than copies. The vault stays the authoring home for design work,
and Ground Truth — the solution document — deliberately stays out of this repo.

The voice sheets are generated from skill_bible.json but are the source of truth
for the prose. tools/writing/sync_voices.py folds edits back; voicelib.py
guarantees notes -> dict -> notes is identity for all eleven, so --to-json on
unchanged sheets is byte-identical. VoiceSheetSyncTests fails the build on drift
in either direction, and was verified to fail on real drift rather than merely
to pass.

SC101.yarn's header is trimmed from prose rationale to the six rules that bind
while editing that file, with the reasoning moved to the scene brief.

EditMode 42/42. YarnCheck compiles 9 files / 35 nodes.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 20:23:42 +02:00

13 KiB
Raw Permalink Blame History

Style — the register and the rules

Re-read this before every writing session. It is the anchor against drift, which is the characteristic failure of a solo writer working across a year.

This is a dialogue-facing digest. The full argument for each rule is in [[Mystery Writing Handbook]] in the Obsidian vault; section numbers below point into it. Where this file and the handbook disagree, the handbook wins — and this file should be fixed.


1. The register, in one paragraph

Second person, present tense, unreliable. The narration is a committee — eleven voices with opinions, some of them wrong — and the player is inside the argument rather than being told its result. Concrete over abstract, always. Roughly seventy percent plain, sincere prose, so that the flourishes land when they come. Sentimental underneath the cynicism; that is what makes the cynicism bearable.

2. The five tone moves (§6.4)

Learn the moves, use your own words. Do not imitate Disco Elysium's prose — imitate its method.

  1. Concrete over abstract. Never "the room felt neglected." Name the object that proves it. The failure mode of introspective writing is abstraction; the fix is always a physical particular.
  2. Bathos with a return trip. Rise to something genuinely grand, puncture it with the mundane — then come back and mean it anyway. The puncture alone is just irony, and irony alone is cheap. The return is what makes it humane. This is the most-imitated and least-understood DE move.
  3. Mock, then love. Every character gets ridiculed by some voice in your head, and every character then gets one moment where the ridicule is revealed as inadequate to them. No character exists only as a joke.
  4. The world speaks through failure. Broken things describe the society that broke them. Describe what the rent did to the stairwell instead of explaining the political economy.
  5. Second person, present tense, unreliable. "You" and "now" produce intimacy and complicity.

The ban list

  • Quirk with no cost. A voice that is only funny is decoration.
  • Cynicism with no warmth.
  • Everything is a bit. Straight, plain, sincere sentences are what make the flourishes work.
  • Trauma as texture. If suffering is described, someone in the text takes it seriously.
  • Explaining the joke, or the theme. If the thematic statement appears verbatim in dialogue, cut it.

Length discipline

Kind of line Length
Passive skill line 1–3 sentences
Success text 2–5 sentences
Opening frame 2–4 sentences

If you are writing a paragraph, you are writing an essay, and the player will skim it — which trains them to skim everything else. Long passages are earned at high skill ranks, nowhere else.


3. How voices behave

A skill is a character, not a stat. It wants something for you, and it is wrong about something. Both halves matter: the wrongness is what makes it a voice rather than a hint system.

  • Voices advocate; they do not report. A line is that voice's own biased reading, even when it happens to be right. No voice ever states a candidate identity as settled fact.
  • Respect the forbidden domains. Each sheet in voices/ ends with "domains it must not comment on." PROVENANCE does not discuss appetite. This is characterisation, not bookkeeping.
  • Opposed pairs are a writing engine. Fire both halves of a pair with contradictory readings and you get friction for free. You never have to invent conflict; the cast contains it.
  • Cap introductions at three voices per scene. An opening is many players' first contact with the system, and fewer, stronger first impressions beat a crowded debut. (From [[000_OpeningSceneTemplate]] §3. SC-101 deliberately breaks this with four — one per pair, because each candidate needs its intuition channel present from the start. That is a logged, scene-specific exception. Do not copy it as precedent, and do not "correct" SC-101 down to three.)
  • Passives carry the personality. Roughly 60–70% of all skill text should be passive. They cost the player nothing and are the main delivery system for character.

4. Checks (§4.4)

Two kinds, and knowing which you are writing is the whole discipline:

Colour check Gate check
Purpose Voice, texture, insight Controls access to a clue or route
Share of all checks 80–90% 10–20%
Failure produces Different content Different route
Redundancy required None Always ≥2 independent routes

A failed gate never removes content. It replaces one route with another.

The two-outcome test — apply before writing any check. Write the one-line summary of the success and the one-line summary of the failure. If the failure summary is "the player doesn't get the thing", delete the check.

Other rules that bind:

  • Never roll for what a competent detective would just do. If failing is boring, don't roll. Roll only where both outcomes are content you want to write.
  • Rate-limit active checks to roughly one per 3–5 minutes of play. Prose rhythm dies under constant dice.
  • Use the band names, not raw numbers: Trivial 8, Routine 11, Hard 14, Specialist 17, BuildDefining 20. No in-between values.

The five productive failures (§4.5)

Never write a failure that means "nothing happens." Every failed check produces one of:

Type What happens Best for
Wrong Truth A confident, coherent, false conclusion Reason/perception checks
Cost You get it, and pay — reputation, a relationship, a secret, time Social checks
Detour This route closes, an uglier one opens Gate checks
Character You learn nothing about the case and something about yourself Self checks
Delayed Noted now, resurfaces later at a worse moment Act transitions

Rough distribution across the game: 30% Wrong Truth, 25% Cost, 20% Detour, 15% Character, 10% Delayed. Wrong Truth is the workhorse of a mystery and is badly underused in most games.

The Wrong Truth rule — the one that is easiest to get wrong

A voice that fails does not signal that it failed. It states its wrong conclusion with exactly the same confidence, cadence and structure as a correct one. The player learns it was wrong through contradiction, later.

The honest version of the trick: the roll is visibly failed in the UI. The player knows a roll failed; they do not know which part of what follows is contaminated. The UI banner is the only tell. Never put a hedge, a wobble, or a wink in the prose.

Compare the two PROVENANCE samples in voices/provenance.md — same cadence, same calm, one unearned verb. That is the target.


5. Branching shapes (§2.7)

Three shapes. Default to the first.

A. Hub-and-spoke — ~80% of conversations. Topics are spokes that return to a hub. Reconvergence is structural, so you get it for free, and cost is linear in topics rather than exponential. Chair.yarn is the worked example in this project.

B. Short branching tree — ~15%, for confrontations. Depth ≤ 3. Always reconverge inside the scene. Track the outcome flag, not the path.

C. True divergence — 3–5 moments in the entire game. Endings, the culprit confrontation, one or two irreversible choices. Budget these explicitly.

The 3-deep cap. No conditional nested more than three levels. If you need four, you have a state that should be a flag rather than a path.

Node naming

Existing conventions in this project, which new content should match:

Pattern Used for
<Character>_Talk a character's entry node (Bartender_Talk)
SC###_Hook_<Subject> a scene beat or sub-conversation
Commentary_<Context>_<voice_id> a passive bark

6. Flags and variables

Every variable must be <<declare>>d in Common.yarn. That is what turns $reputaton into a compile error instead of a silently-created second variable. Adding a flag is a writing action — you do not need an engineer.

Families that are load-bearing, two of them lint-enforced against the candidate registry:

Family Meaning Enforced
$self_lean_<candidate_id> identity lean yes — id must exist in candidates.json
$found_<clue_id> clue coverage yes — id must exist in candidates.json
$visited_<location_id> first arrival at a location convention
$errand_<errand_id> errand reached a state that matters convention
$seen_<scene_id> scene witnessed convention
$spoke_to_<character> first conversation happened convention
$check_* written by <<check>> never <<set>> by hand

The candidate rules — these are hard constraints

The protagonist's identity has three live readings and never resolves. Three integers track belief, not truth.

  1. Never compare one lean against another. Not in Yarn, not in prose, not in a debug line. Comparing a lean to a constant is fine and intended. $self_lean_a > $self_lean_b must not exist anywhere.
  2. No ground-truth field. Nothing named isTrue, actualIdentity, canonical, or weight.
  3. Clue availability is exploration-gated, never skill-gated. An unlockFlag names a $visited_* or $errand_* variable. A clue-bearing node never sits behind a skill_rank( guard.
  4. Evidence balance. Each candidate carries exactly one object, one testimony, one document, and one intuition clue. Do not stack types, or trust becomes an artefact of imbalance.
  5. Lean is belief, so a Wrong Truth still writes lean. Being wrong moves the needle. That is the point.

Two threads that never resolve

The project has two permanently unresolved lore threads, referred to as Thread 2 and Thread 9. Candidate material must stay structurally separate from them: do not resolve them, do not map a candidate clue onto them. If you find yourself needing to know what they contain in order to write a line, you are writing the wrong line.


7. Flow control

<<detour>> returns to the caller. <<jump>> does not, and clears the return stack.

  • A node started directly by an interactable has nothing waiting on it, so its branches <<jump>>, and each destination calls <<enter_environment>> itself.
  • A sub-conversation that must resume its caller uses <<detour>> — and never <<jump>> inside it, which would strand the caller forever.
  • A menu that loops uses <<jump>> back to its own hub, which replaces the current node and pushes nothing.

Nodes sharing a title: form a node group; Yarn runs the most specific variation whose when: conditions pass. Prefer adding a variation over growing an <<if>> chain.


8. Writing order for a scene (§5.5)

  1. Fill the scene brief header — purpose and critical outputs. Stop. Confirm the clue routes are registered in Assets/Candidates/candidates.json.
  2. Write the beats as one-line summaries.
  3. Write the checks table — both outcomes as summaries, before any prose.
  4. Now write prose, in this order: opening frame → success texts → failure texts → passives → barks.
  5. Update clue status in candidates.json.

Do not implement in engine until the act is drafted. Implementing mid-draft is the biggest time sink in solo narrative work, because every structural revision then costs double.


9. The voice sheet

Five sentences to be proud of, five to cut, three adjectives, one sentence on what the game feels about its world. This is the anchor §6.5 asks for and it is cheaper than re-reading your own draft.

Three adjectives for the register: (unset — decide these and stop leaving the field open)

What the game feels about its world: (unset)

Five sentences to be proud of — provisional, taken from shipped SC-101 and the Skill Bible, to be replaced with chosen ones:

The mahogany doors are too heavy for the hinges someone recently, badly, tightened — you feel the extra half-second of resistance before either side gives.

Someone left a card with your name on it at the registration desk three hours before you decided to come. You don't remember deciding.

Cigar smoke, spilled aperitif, and under it — cold, sharp — the ammonia bite of a floor mopped in a hurry, love. Someone's trying to make this room smell like money. It smells like panic with good cologne on top.

A line that begins nowhere is a line somebody drew.

Her hands are wrong for that coat. It's somebody's good coat, and it isn't hers.

Five to cut: (unset — write these next session; the cut list is the more useful half, because it names the specific temptations this project keeps giving in to)

This section is deliberately incomplete. [[NightclubArcadia]] has carried "Write the one-page Voice Sheet" as an open action for a while, and [[000_OpeningSceneTemplate]] records tone checks as BLOCKED on it — the SC-101 spec could not honestly certify its own tone field. Filling the four unset fields above unblocks that. It is a half-hour of decisions, not a writing project.