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]>
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
---
|
||||
id: SC###
|
||||
title: <title>
|
||||
act: I
|
||||
status: outlined # outlined | drafted | revised | in-engine | tested
|
||||
yarn_file: Assets/Dialogue/Scenes/SC###.yarn
|
||||
location:
|
||||
reenterable: no
|
||||
---
|
||||
|
||||
# SC-### — <title>
|
||||
|
||||
> One scene, one file, ideally one screen of spec. If this brief does not fit on a page, the
|
||||
> scene is doing too much — split it. That discipline is what lets one writer hold a sixty-scene
|
||||
> game in their head.
|
||||
>
|
||||
> This is the **brief**, not the dialogue. The words live in the `.yarn` file.
|
||||
|
||||
## Purpose
|
||||
|
||||
One line: what has changed by the end.
|
||||
|
||||
## Cast
|
||||
|
||||
Speaking characters, and which voices are prominent. **Cap voice introductions at three.**
|
||||
|
||||
## Entry conditions
|
||||
|
||||
Flags required. "None" is a good answer.
|
||||
|
||||
## Critical outputs
|
||||
|
||||
Things every build must be able to obtain. Each needs ≥2 independent routes — and independent
|
||||
means different trace channels, not two checks in the same conversation.
|
||||
|
||||
| Clue id | Routes |
|
||||
|---|---|
|
||||
| | |
|
||||
|
||||
## Optional outputs
|
||||
|
||||
Missable by design. One route is fine.
|
||||
|
||||
## Shape
|
||||
|
||||
`hub-and-spoke` | `short tree` | `divergent`. Default to the first.
|
||||
|
||||
## Opening frame
|
||||
|
||||
2–4 sentences of establishing prose. Sensory, specific, and including one concrete detail that
|
||||
does not serve the plot.
|
||||
|
||||
## Beats
|
||||
|
||||
3–6. Each beat is one exchange or one discovery.
|
||||
|
||||
1.
|
||||
|
||||
## Checks
|
||||
|
||||
Write **both** outcomes as summaries before writing any prose. If the failure summary is "the
|
||||
player doesn't get the thing", delete the check.
|
||||
|
||||
| # | Voice | Colour/Gate | DC band | Success → | Failure type | Failure → |
|
||||
|---|---|---|---|---|---|---|
|
||||
| | | | | | | |
|
||||
|
||||
## Branch points
|
||||
|
||||
| id | The choice | Reconverges at | Persistent effect |
|
||||
|---|---|---|---|
|
||||
| | | | |
|
||||
|
||||
## Exit states
|
||||
|
||||
Keep to ≤3. More than three means this is a hub, not a scene.
|
||||
|
||||
## Flags
|
||||
|
||||
**Sets:**
|
||||
**Reads:**
|
||||
|
||||
## Constraints specific to this scene
|
||||
|
||||
Anything a later editor must not "fix". Deliberate exceptions to a general rule go here, with
|
||||
the reason, so they survive review.
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
id: SC101
|
||||
title: The Card at the Desk
|
||||
act: I
|
||||
status: in-engine
|
||||
yarn_file: Assets/Dialogue/Scenes/SC101.yarn
|
||||
location: The conference hall — registration desk, the bar, the table
|
||||
reenterable: yes
|
||||
---
|
||||
|
||||
# SC-101 — The Card at the Desk
|
||||
|
||||
The opening. Playable end to end, and it exits into SC-102, which is still a one-line stub.
|
||||
|
||||
An opening scene is not a normal node in the sandbox. It has no entry conditions and almost
|
||||
nothing to gate. Its job is to sell the register in one sitting — the voice-committee narration,
|
||||
the tone, the world's texture of disrepair, and one open question the player carries into Act I.
|
||||
|
||||
## Purpose
|
||||
|
||||
Establish that the protagonist does not know who they are, that other people appear to, and that
|
||||
this was arranged before they decided to come.
|
||||
|
||||
## Cast
|
||||
|
||||
**Speaking:** Narrator, the Bartender, Chevalier Cassian Thal, Bradford Kane (silent — unwritten).
|
||||
|
||||
**Voices introduced:** CONFLUENCE (Reason), FACEWORK (Social), STANDING ORDER (Self),
|
||||
HOUSE POUR (Body). Also appearing: PROVENANCE, AMNESTY, THE FLOAT.
|
||||
|
||||
## Entry conditions
|
||||
|
||||
None.
|
||||
|
||||
## Critical outputs
|
||||
|
||||
| Clue id | Routes |
|
||||
|---|---|
|
||||
| `made_asset_intuition` | CONFLUENCE hook at the desk (`$visited_desk`) |
|
||||
| `journalist_intuition` | FACEWORK hook at the desk (`$visited_desk`) |
|
||||
| `inheritor_intuition` | STANDING ORDER hook at the desk (`$visited_desk`) |
|
||||
|
||||
All three are **single-route**, which is acceptable only because they are intuitions rather than
|
||||
gates: they fire passively, with no roll, so no build can miss them. The object, testimony and
|
||||
document clues for all three candidates are still `Planned` with no `sceneId` — see the registry
|
||||
in `Assets/Candidates/candidates.json`.
|
||||
|
||||
## Optional outputs
|
||||
|
||||
- The Atlantean cross on the place card — `Hard` PROVENANCE, one route, missable by design.
|
||||
- The Cyprus connection — `Hard` PROVENANCE with Chevalier Cassian Thal, then an inquiry.
|
||||
- Mr. Doss, surfaced from either the Bartender or the Chevalier — two independent routes.
|
||||
- The Estate drink, and whether it tastes like the protagonist's own past.
|
||||
|
||||
## Shape
|
||||
|
||||
Hub-and-spoke throughout. The chair menu (`SC101_Hook_Menu_Chair_Interaction`) is the hub and
|
||||
re-evaluates which variation shows after every sub-conversation returns.
|
||||
|
||||
## Opening frame
|
||||
|
||||
> 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. Beyond them: a hall built for
|
||||
> four hundred people politely disagreeing about tariffs, hosting perhaps sixty, none of whom
|
||||
> look like they've read the programme.
|
||||
|
||||
## Beats
|
||||
|
||||
1. Arrival at the desk. A card with the protagonist's name, left three hours before they decided
|
||||
to come. Pure orientation, no plot delivery.
|
||||
2. Four voices introduce themselves, one per pair, each reading the room differently.
|
||||
3. The bar. The Bartender recognises someone the protagonist does not remember being.
|
||||
4. The table. The place card, and a symbol that does not belong in Switzerland.
|
||||
5. Two seated gentlemen. One of them mirrors back whatever the protagonist has been projecting.
|
||||
6. Exit into SC-102 once a reading has been declared.
|
||||
|
||||
## Checks
|
||||
|
||||
| # | Voice | Colour/Gate | DC band | Success → | Failure type | Failure → |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 1 | PROVENANCE | colour | Hard | the cross is identified as not-Swiss, sets the flag | Wrong Truth | "This is probably related to Switzerland." Same calm, no hedge. |
|
||||
| 2 | PROVENANCE | colour | Hard | Basel reads as old money, opens the Cyprus inquiry | Wrong Truth | the inquiry never opens; nothing signals that it existed |
|
||||
|
||||
Both are colour, not gates: failure changes what the protagonist believes, never what they can
|
||||
reach. The desk hooks are **check-free by design** — a roll there would produce a pass/fail state
|
||||
that reads as evidence about identity, which this scene must not deliver.
|
||||
|
||||
## Branch points
|
||||
|
||||
| id | The choice | Reconverges at | Persistent effect |
|
||||
|---|---|---|---|
|
||||
| B1 | Admit the fugue to the Bartender, or claim "the usual" | bar conversation end | `$revealed_fugue_to_bartender`; lean to Made Asset or Inheritor |
|
||||
| B2 | Whether the Estate tastes familiar | drink node end | ±1 Inheritor lean, first glass only |
|
||||
| B3 | Accept or deny the Chevalier's reading | `SC101_Deny_Candidate` loop | `$declared_candidate_to_chevalier_cassian_thal`; lean |
|
||||
|
||||
## Flags
|
||||
|
||||
**Sets:** `$seen_sc_101`, `$visited_desk`, `$found_*_intuition` (×3), `$spoke_to_bartender`,
|
||||
`$revealed_fugue_to_bartender`, `$looking_for_doss`, `$had_the_estate_drink`,
|
||||
`$sat_down_at_table`, `$saw_knights_atlantean_cross_on_place_card`,
|
||||
`$spoke_to_chevalier_cassian_thal`, `$spoke_to_bradford_kane`, `$inquired_about_cyprus`,
|
||||
`$declared_candidate_to_chevalier_cassian_thal`, `$self_lean_*`
|
||||
|
||||
**Reads:** all of the above, plus `$player_name`, `$player_name_full`, `$player_alias`
|
||||
|
||||
## Constraints specific to this scene
|
||||
|
||||
These are deliberate. Do not "fix" them.
|
||||
|
||||
1. **Four voice introductions, against the cap of three.** One per pair — Self, Body, Social,
|
||||
Reason — because each candidate carries one intuition from a different voice-pair, and cutting
|
||||
to three would drop a candidate's intuition channel from its earliest appearance. HOUSE POUR
|
||||
is the non-lean control and the one to cut if the cap is ever enforced strictly. This is a
|
||||
logged exception for SC-101 only; do not copy it as precedent elsewhere.
|
||||
2. **Evidence balance.** Each candidate gets exactly one of each evidence type. Never stack
|
||||
types, or trust becomes an artefact of imbalance rather than of play.
|
||||
3. **Beat order is fixed.** Hooks fire in sequence from `SC101_Desk`: STANDING ORDER → HOUSE POUR
|
||||
→ FACEWORK → CONFLUENCE. Each stays an independently authored node. Never `<<if>>`-guard a
|
||||
hook on another hook having fired, on any `$self_lean_*`, or on `skill_rank(...)`.
|
||||
4. **The passive pattern for clue-bearing hooks.** One voice per hook, from the candidate's
|
||||
attached pair. The line is that character's *argument*, never a report. Exactly one
|
||||
`$self_lean_*` write, with `$found_<clue_id>` set alongside it.
|
||||
5. **The body of the scene stays neutral.** No lean, no candidate attachment outside the hooks.
|
||||
6. **The Chevalier's tie bias.** `top_skill` receives `made_asset` first and returns its first
|
||||
argument on a tie, so an even score reads as Made Asset. Deliberate, not an accident of
|
||||
argument order.
|
||||
|
||||
## Open work
|
||||
|
||||
- `SC101_Hook_Questions_Bartender_Talk` is placeholder text ("Question 1 / Answer 1"). It is a
|
||||
working loop with nothing in it, and the natural home for the missing testimony clues.
|
||||
- Bradford Kane is reachable and silent.
|
||||
- The Atlantean cross is never connected to the knight who is sitting right there.
|
||||
- Tone could not be certified against a voice sheet when this scene was specced, because one did
|
||||
not exist. See `STYLE.md` §9 — four fields there still need deciding.
|
||||
Reference in New Issue
Block a user