Interactive Fiction Intensive Workbook

Interactive Fiction Intensive Workbook

1. The day

You’ve got one story and one day: 10:00–16:00 Pacific, one hour off for lunch. Five hours of studio. The blocks below are your spine — each one ends at a checkpoint, and the checkpoints are exactly what the recovery points in Section 6 restore. Block ends and you’re not at the checkpoint yet? That’s not a sign to catch up alone — go paste the recovery file and get back in the room.

10:00
Open (20m)
We play a published Spaces Between story together, as a room, before anyone writes a word.
Checkpoint: By four, yours is on this shelf
10:20
The Nine Rooms (70m)
Paste the skeleton. Work the seven missions. AI helps with the map and the decorating. Secret Tunnel ritual and break around the midpoint.
Checkpoint: Your dungeon is yours, and it remembers something→ restored by 01-nine-rooms-complete.ink
11:30
Two ways a story dies (20m)
Combinatorial explosion versus on rails — two different ways to wreck a branching story, and one fix for both: state.
Checkpoint: Everything after depends on this
11:50
Lunch (60m)
Optional: go play each other’s dungeons.
12:50
Many diamonds (70m)
Restructure as chained diamonds. Write the trunk first, season it with conditionals after. Uses the Ink Shape Catalog and the Shape Composition Guide.
Checkpoint: Three diamonds chained; the trunk reads differently by state→ restored by 02-diamonds.ink
14:00
AI as co-author (45m)
Second Secret Tunnel ritual and break, and this time it teaches you the Ink -> knot -> tunnel. Then: where AI is genuinely good, and where it is not.
Checkpoint: Three times longer, and the shape survived
14:45
Review & publish (60m)
Fill in the metadata, tag your moods, submit for review as you finish rather than all at once, publish, and go play what your neighbor built.
Checkpoint: On the shelf, and someone played it→ restored by 03-publish-ready.ink
15:45
Close (15m)
The shelf, what to build next, where the community is.
Secret Tunnel

Twice today we stop everything and play the Secret Tunnel song from Avatar. Sokka would want you to. First time, mid-morning: it’s a narrative idea — a hidden connection between rooms. Second time, mid-afternoon: same song, grown-up joke — the Ink -> knot -> construct, a tunnel that comes back. Same ritual. It just grew up.

2. The Nine Rooms

Paste this in. It runs the moment you paste it, so you’ve got a working game within minutes. It also loops forever — every room eventually leads back to another room, and the ending loops right back to the start. That’s by design. Don’t fix the loop. Decorating it is the point: the structure is given to you free, and your creativity goes on top of it.

Ink
Welcome to The Nine Rooms!
-> room_1

=== room_1
You are in the first room. There is a door to the north.
+ Go north
    -> room_2
+ Sit Here
    -> room_1

=== room_2
You are in the Second Room.
There is a door to the south.
There is a door to the east.
There is a door to the west.
There is a door to the north.
+ Go north
    -> room_3
+ Go south
    -> room_1
+ Go east
    -> room_4
+ Go west
    -> room_5

=== room_3
You have entered the third room.
There is a door to the south.
There is a door to the north.
+ Go north
    -> room_6
+ Go south
    -> room_2

=== room_4
You have entered the fourth room.
There is a door to the west.
+ Go west
    -> room_2
+ Stay Here
    -> room_4

=== room_5
You have entered the fifth room.
There is a door to the east.
+ Go east
    -> room_2
+ Stay Here
    -> room_5

=== room_6
You have entered the sixth room.
There is a door to the south.
There is a door to the north.
There is a door to the west.
There is a door to the east.
+ Go north
    -> room_9
+ Go south
    -> room_3
+ Go east
    -> room_8
+ Go west
    -> room_7

=== room_7
You have entered the seventh room.
There is a door to the east.
+ Go east
    -> room_6
+ Stay Here
    -> room_7

=== room_8
You have entered the eighth room.
There is a door to the west.
+ Go west
    -> room_6
+ Stay Here
    -> room_8

=== room_9
This is the Final Room. There is no escape from here.
Make your choice:
+ Win the Game
    -> end_game
+ Stay Here
    -> room_9

=== end_game
You have won the game! Or have you?
-> room_1

Here are your seven missions, in order. Work them on your own dungeon — it does not have to look anything like the rooms shown below.

Mission 1

Decorate the rooms

Decorate the rooms of the dungeon! Add descriptions of what is in the rooms — and they don’t have to stay rooms. It doesn’t have to be a dungeon, it can be outside, in a labyrinth, anywhere. It doesn’t have to be places, it can be choices. It doesn’t have to be choices, it can be events. It doesn’t have to be choices made by the protagonist at all.

Underneath, you’re just working with knots and diverts. A knot is a named section of your story, marked with === — think of it like a chapter or a scene. A divert sends the reader there with an arrow, ->. That’s all it means: go to. Once you’ve got that, a room is just a named place in the graph, and it can stand for anything you want it to.

Ink
=== room_1
The first room smells of cold stone and old rain.
{lamps > 0: Your lamp throws a shaky circle of light.}
+ [Go north] -> room_2
+ {lamps == 0} [Take the lamp from its bracket]
    ~ lamps = lamps + 1
    It is warm, which it should not be.
    -> room_1
+ [Sit here a while] -> room_1
Mission 2

A secret tunnel — and a disappearing one

Add at least one secret tunnel, through the mountains, secret secret secret secret tunnel. Don’t let the cave-in get you down, Sokka: add at least one disappearing secret tunnel.

A choice marked with * can only be picked once — come back to this section and that option is gone. A choice marked with + sticks around even after the reader returns, which is great for things they might want to do repeatedly, like ‘look around’ or ‘wait.’ This room gets to the same disappearing effect a different way: a sticky + choice guarded by {tunnel_open}, switched off with ~ tunnel_open = false the moment it’s used. Same outcome as a plain *, more control over exactly when it vanishes.

Ink
=== room_4
A dead end, except for the draught coming from behind the tapestry.
+ [Go west] -> room_2
+ {tunnel_open} [Take the secret tunnel]
    ~ tunnel_open = false
    You crawl through. Behind you, the passage folds shut like a mouth.
    -> room_7
+ [Stay here] -> room_4
Mission 3

A secret item that vanishes when taken

Add at least one secret item that disappears after you pick it up. Is it a magic item? Does it unlock a new area? It could.

Sometimes you need the story to remember something — did the player pick up the key? You track that with a variable: declare it with VAR up top, change it with ~, which just means ‘do something behind the scenes’ — the reader never sees that line. Here, taking the key is a once-only pickup that also flips has_key, so the room and the story both remember it happened.

Ink
=== room_5
Fifth room. Empty, apart from the key on the floor.
+ {not has_key} [Take the key]
    ~ has_key = true
    -> room_5
+ [Go east] -> room_2
Mission 4

An irreversible choice

Add at least one irreversible choice! You don’t need any new syntax for this — just a ~ assignment guarded so the choice that fires it can never fire again. Turning the key sets ~ door_sealed = true, and the choice that turned it required not door_sealed to appear at all. So the instant it fires, its own condition goes false, and the option is gone for the rest of the story. Once turned, it doesn’t turn back.

Ink
=== room_9
The final room. {has_key: The lock is waiting for what you are carrying.|There is a lock, and you have nothing to put in it.}
+ {has_key and not door_sealed} [Turn the key]
    ~ door_sealed = true
    It turns once. It will not turn back.
    -> end_game
+ [Stay here] -> room_9
+ [Go back south] -> room_6
Mission 5

A number that counts up or down

Keep track of at least one number that counts up or down. Declare it with VAR, change it with ~ — that’s the whole trick. This dungeon tracks four counters at the top of the file, and the lamp count is the one that climbs. Watch it happen in Mission 1’s room_1 above: ~ lamps = lamps + 1.

The four VARs this dungeon tracks:

Ink
VAR lamps = 0
VAR tunnel_open = true
VAR has_key = false
VAR door_sealed = false
Mission 6

A loop or cycle

At least one loop or cycle should be present in the story. The simplest loop needs no gather and no label — just a divert back to where you started. The undecorated skeleton already has one: the Sit Here choice diverts straight back to room_1. A knot can divert to itself, and that’s a loop. Want one that gathers several choices back to one shared line first? See Gathers and labels in the Snippet Library.

From the plain skeleton, before decoration:

Ink
=== room_1
You are in the first room. There is a door to the north.
+ Go north
    -> room_2
+ Sit Here
    -> room_1
Mission 7

At least one ending — preferably several

There should be at least one ending! But preferably, multiple. -> END is a special built-in divert — it means the story is over. Every path must end, one way or another: either divert to another knot, or use -> END.

The undecorated skeleton doesn’t actually end — its end_game loops back to room_1 forever. You have won the game! Or have you? That’s this mission’s whole job: give it a real ending.

Skeleton — no real ending yet:

Ink
=== end_game
You have won the game! Or have you?
-> room_1

Decorated — a real ending:

Ink
=== end_game
You step out carrying {lamps} {lamps == 1:lamp|lamps} and everything you decided.
You have won the game! Or have you?
-> END

3. Snippet library

Every construct you need today, paste-ready. This is the part you keep after the day is over.

Structure

Knots

A knot is just a named section of your story. Mark it with ===. Think of knots like chapters or scenes.

Ink
=== tavern ===
You walk into a dimly lit tavern.
The bartender looks up.

=== market ===
The market is bustling with merchants and shoppers.

Diverts

A divert sends the reader to a knot. It looks like an arrow, ->. Think of -> as ‘go to.’ That’s all it means.

Ink
=== tavern ===
You walk into a dimly lit tavern.
* Talk to the bartender.
    -> bartender_chat
* Leave the tavern.
    -> market

=== bartender_chat ===
"What can I get you?" the bartender asks.

=== market ===
You step outside into the bright sun.

Stitches

If knots are chapters, stitches are sections within a chapter. Mark them with =. Divert to one with -> knot_name.stitch_name from outside, or just -> stitch_name from within the same knot.

Ink
=== tavern ===

= entrance
You push open the heavy door.
-> bar_area

= bar_area
The bartender nods at you.
Choices

* vs + — once-only vs sticky

A choice marked with * can only be picked once. Come back to this section and that option is gone. A choice marked with + sticks around even if the reader returns — great for things they might want to do repeatedly, like ‘look around’ or ‘wait.’

Ink
* Pick up the shiny coin.
    You pocket the coin. // gone if you come back

+ Look around the room.
    The room is cold and empty. // stays available

Bracketed choice text

Text inside [square brackets] appears only in the choice button, not in the story text after selection. Text outside brackets appears in both. Read Hello[.] there. as: the choice shows “Hello.”, and the result reads “Hello there.”

Ink
* [Approach the bar] You swagger up to the counter.
* [Find a dark corner] You slide into a booth near the back.
* [Turn and leave] The cold night air hits your face again.
Flow

Gathers and labels

When multiple choices should lead to the same next beat, use a gather (-). It catches all three paths and continues with the same text — no need to build a separate knot just for one shared line. Want to loop back to a point instead? Give it a label with (options) and divert to it with -> options.

Ink
"What do you think happened?" Sal asks.

* "Sabotage." Sal nods slowly.
* "Accident." Sal looks skeptical.
* "I don't know yet." Sal shrugs.

- "Either way," she says, "we need to check the reactor logs."
-> reactor_logs

// looping back to a label:
- (options)
* [Wait in silence] -> options
- She watches you carefully.
-> next_scene

Tunnels — the 14:15 construct

A tunnel is a divert that remembers where it came from. Both paths below experience the same tunnel, but each continues to a different destination — you write it once and use it twice. -> knot -> means go there, then come back here; ->-> at the end of the tunnel means return to the caller. The tunnel has no idea who called it — that’s what makes it reusable.

Ink
=== explore_forest ===
The path forks at an ancient oak.
* [Take the left path]
  -> mysterious_sound -> // tunnel: go there, then come back
  The left path continues deeper into the woods.
  -> forest_clearing

* [Take the right path]
  -> mysterious_sound -> // same tunnel, different context
  The right path opens to a meadow.
  -> meadow

=== mysterious_sound ===
You hear something rustling in the undergrowth.
* [Investigate]
  It's a fox. It stares at you, then vanishes.
* [Ignore it]
  You keep walking. The sound fades.
- ->-> // return to the caller
State

VAR and ~

Sometimes you need the story to remember something — did the player pick up the key? Are they friendly or rude? Declare a variable at the top of the file with VAR, and change it anywhere with ~, which means ‘do something behind the scenes’ — the reader never sees that line.

Ink
VAR has_key = false
VAR friendliness = 0

* Pick up the key.
    ~ has_key = true
    You grab the rusty key.

Conditionals

Now that you can remember things, you can check them with conditions. Read { has_key } as ‘if has_key is true.’ Gate a whole block, or gate a single choice.

Ink
{ has_key:
    You unlock the door and step through.
- else:
    The door is locked. You need a key.
}

* { has_key } Unlock the door.
    The door creaks open.
* Try to force it open.
    It won't budge.
Variety on repeat visits

Sequences — {&} {!} {~}

{a|b|c} is the default, stopping: it plays each option in turn and sticks on the last. {!...} is once-only: it stops after the last, then goes silent. {&...} is a cycle: it loops. {~...} is a shuffle: random every time. If the player visits a scene more than once, you don’t want identical text — these are how you keep it fresh.

Ink
The clock ticks. {&loudly|softly|loudly|softly}
She sighs. {!again|for the third time|one last time}
You notice {~a crack in the wall|dust on the shelf|a photograph}.
The Spaces Between tags

# mood: tags

Put one on its own line before the content it colors: wonder, calm, sadness, danger.

Ink
# mood:wonder
# mood:calm
# mood:sadness
# mood:danger

# live: tags

# live:characterId lights up the character voice indicator in the reader while that character is speaking.

Ink
# live:ferryman

Bundle metadata

A published story is a JSON bundle underneath: your compiled Ink, a characters map keyed by the ids your # live: tags reference, and a metadata block. This is exactly what the Workshop’s metadata drawer is filling in for you — worth knowing what it’s building.

JSON
{
  "inkJson": { /* compiled ink JSON */ },
  "characters": {
    "narrator": {
      "name": "Display Name",
      "personality": "description",
      "voice": "speech style",
      "knowledge": "background info"
    }
  },
  "metadata": {
    "title": "Story Title",
    "author": "In-World Author Name",
    "authorSpecies": "Species Name",
    "fylgja": "creature-sprite-id",
    "fylgjaDescription": "description",
    "themes": ["tag1", "tag2"],
    "wordCount": 850
  }
}

4. Diamonds

The two ways a branching story dies

Every branching story fails one of two ways, and knowing which one you’re drifting toward is half the craft.

Failure 1: Combinatorial explosion

You write a choice with 3 options, each leads to a new scene with 3 options, each leads to… and suddenly you’re writing 27 scenes for a 3-choice story. You burn out at scene 12. The player only ever sees 3 of them. It happens when you treat every choice as a fork that needs its own downstream content.

Failure 2: On rails

The player makes choices, but the story barrels toward the same scenes regardless. Choices feel cosmetic. Players disengage. It happens when you over-correct for explosion and converge everything immediately, with no consequence carried forward.

The fix for both is the same thing: state. Don’t branch the text — branch the variables, then let those variables color a shared path.

Quick structural heuristics

  1. Decide your shape first. Diamond? Branch-and-bottleneck? Quality-based? Sketch it before you write a word of prose — see the Ink Shape Catalog.
  2. Pick your tracked state. What 3–7 variables actually matter? Go past ~7 and you’ll lose track; go too few and choices stop feeling consequential.
  3. Plan your bottlenecks before your branches. Know where the river reconverges before you let it split.
  4. Aim for diamonds ~2–4 beats long. Branch, let the player feel the difference, converge. Repeat.
  5. Make divergence cheap and convergence rich. Short branches, long shared trunks, colored by state.

Write the trunk first, with conditional seasoning

Write the shared path first, with {flag: ...} inserts seasoned in as you go. Then write the branch variants. Add voice and flavor choices liberally once the trunk is solid. That’s the pattern behind the crossing knot in Recovery Point 02 below: one trunk paragraph, seasoned by {trust > 0: ...|...}, never duplicated into two copies of the scene.

Chained-diamond template

Copy this, then replace the comments. It’s scaffolding, not a story — three diamonds chained end to end, trunk-first. Want to see it fully written out, with real prose? That’s Recovery Point 02.

Ink
VAR state_a = false

-> diamond_one

// -- Diamond one: branch, let the player experience the difference, converge --
=== diamond_one ===
// 2-3 choices that differ meaningfully -- divergence is cheap here
* [Choice A]
    ~ state_a = true
    -> diamond_one_converge
* [Choice B]
    -> diamond_one_converge

=== diamond_one_converge ===
// The trunk. One passage, seasoned by state_a instead of duplicated.
{state_a: text colored by the choice they made|text for the choice they didn't make}
-> diamond_two

// -- Diamond two: repeat the shape --
=== diamond_two ===
* [Choice C] -> diamond_two_converge
* [Choice D] -> diamond_two_converge

=== diamond_two_converge ===
-> diamond_three

// -- Diamond three: chain as many as the story needs --
=== diamond_three ===
// same shape again: branch, differ, converge
* [Choice E] -> diamond_three_converge
* [Choice F] -> diamond_three_converge

=== diamond_three_converge ===
-> END

5. AI prompt pack

The rule for today

AI is excellent at filling a shape and bad at choosing one. Good: generating the map, decorating rooms, tonal variation, procedural text, expanding a diamond to size, generating the flavour text that makes convergence rich. Bad: choosing the shape, designing the state model, deciding what the story means, knowing which choice should be irreversible. Think of AI as the intern drafting scaffolding — you make the creative decisions.

A tension worth naming

Older material sometimes asks AI to help pick which variables to track. Today’s rule is sharper: AI fills, it doesn’t choose. Decide your state first — even a scribbled list of 3–7 things that matter — then ask AI to help you use it.

Map your rooms

This map won’t update when you edit yours — it’s just static. But, Any AI can help you make one! Paste your current .ink file first.

Prompt
Draw a mermaid flowchart of the knots in this Ink story and how they connect -- which choice from which room leads to which knot. Use the room names as labels, and mark any tunnel or loop clearly.

Decorate a room

Prompt
Write 2-3 sentences of atmospheric description for this scene in my interactive fiction:
[describe the scene]

Keep it second person ("you"), present tense, concise.
This is for a text-based game -- every word should earn its place.

Tonal variation

Prompt
Take this room description: [paste]

Rewrite it so it feels [sinister / joyful / eerie / cozy] instead -- same events, same choices, different mood. Don't add or remove anything the player can do, just recolor how it reads.

Season a trunk (diamonds)

For after you’ve written the trunk yourself — this fills the seasoning, it doesn’t design the branch.

Prompt
Here's my diamond's trunk knot:
[paste]

It currently reads the same regardless of {state}. Add 1-2 sentences of conditional seasoning --
{state: ...|...} -- that colors the trunk without duplicating it into two full copies.

Character sheet

Prompt
Create a character sheet for an interactive fiction NPC:

Name: [name]
Role in story: [what they do -- shopkeeper, rival, guide, etc.]
Setting: [your story's setting]

Include:
- 2-3 core motivations (what drives them)
- 2-3 fears (what they avoid or dread)
- Speech pattern (short sentences? flowery? sarcastic? formal?)
- 3 example dialogue lines that capture their voice
- One thing they would NEVER say (defines their boundaries)

Dialogue, in their voice

AI gives you options. Your job is editorial. Don’t use everything it generates — pick the best lines, cut the rest. You’re the director; AI is the actors at the table read.

Prompt
Here is my character sheet:
[PASTE CHARACTER SHEET]

Write dialogue for this character in the following situations.
For each situation, give me 2-3 versions so I can pick the best one.
Write in ink interactive fiction format (second person, present tense for narration).

Situations:
1. The player meets this character for the first time
2. The player asks this character for help
3. The player does something this character disapproves of

Keep dialogue SHORT (1-2 sentences per line).
Match the speech pattern from the character sheet exactly.
Include action beats between dialogue lines.

Stress-test your branches

Prompt
Here's my Ink script with variables and conditions:
[PASTE YOUR FULL SCRIPT]

Playtest this for me. Try these paths:
1. The "completionist" -- find everything, talk to everyone
2. The "speedrunner" -- take the most direct route to an ending
3. The "contrarian" -- choose the least obvious option every time

For each path, tell me:
- Does the story make sense from start to finish?
- Are there any dead ends (no choices and no -> END)?
- Are there moments where a variable SHOULD matter but doesn't?
- Any dialogue that contradicts what happened earlier on this path?
The AI workflow

Design the system first — decide what state to track and how it should affect the story. Prompt AI for structure — let it generate the Ink scaffolding. Playtest immediately — run it in the Workshop, find what breaks. Edit for voice — AI generates the structure, you bring the soul. Iterate with AI — ‘this choice feels flat, give me three alternatives that raise the stakes.’

6. Recovery points

A working .ink file for the end of every major block. Lost the thread? Paste the previous checkpoint over your Manuscript in the Workshop app and rejoin at the next mission — that’s the whole point of an uncapped room with one facilitator. Nobody has to fall behind past a recovery point.

01-nine-rooms-complete.ink

Restores: Your dungeon is yours, and it remembers something — the end of The Nine Rooms block. Paste this in and rejoin at Two ways a story dies.

All seven missions, worked: a lamp that counts up, a secret tunnel that closes behind you, a key that vanishes when taken, a door that seals forever once turned, a real ending.

Ink
VAR lamps = 0
VAR tunnel_open = true
VAR has_key = false
VAR door_sealed = false

Welcome to The Nine Rooms!
-> room_1

=== room_1
The first room smells of cold stone and old rain.
{lamps > 0: Your lamp throws a shaky circle of light.}
+ [Go north] -> room_2
+ {lamps == 0} [Take the lamp from its bracket]
    ~ lamps = lamps + 1
    It is warm, which it should not be.
    -> room_1
+ [Sit here a while] -> room_1

=== room_2
Four doors. The room seems to be counting them for you.
+ [Go north] -> room_3
+ [Go south] -> room_1
+ [Go east] -> room_4
+ [Go west] -> room_5

=== room_3
Something has been dragged across this floor, recently.
+ [Go north] -> room_6
+ [Go south] -> room_2

=== room_4
A dead end, except for the draught coming from behind the tapestry.
+ [Go west] -> room_2
+ {tunnel_open} [Take the secret tunnel]
    ~ tunnel_open = false
    You crawl through. Behind you, the passage folds shut like a mouth.
    -> room_7
+ [Stay here] -> room_4

=== room_5
Fifth room. Empty, apart from the key on the floor.
+ {not has_key} [Take the key]
    ~ has_key = true
    -> room_5
+ [Go east] -> room_2

=== room_6
The sixth room is a crossroads and it knows it.
+ [Go north] -> room_9
+ [Go south] -> room_3
+ [Go east] -> room_8
+ [Go west] -> room_7

=== room_7
The seventh room is where the tunnel comes out, whether or not you used it.
+ [Go east] -> room_6
+ [Stay here] -> room_7

=== room_8
The eighth room. Someone has scratched a tally into the wall.
+ [Go west] -> room_6
+ [Stay here] -> room_8

=== room_9
The final room. {has_key: The lock is waiting for what you are carrying.|There is a lock, and you have nothing to put in it.}
+ {has_key and not door_sealed} [Turn the key]
    ~ door_sealed = true
    It turns once. It will not turn back.
    -> end_game
+ [Stay here] -> room_9
+ [Go back south] -> room_6

=== end_game
You step out carrying {lamps} {lamps == 1:lamp|lamps} and everything you decided.
You have won the game! Or have you?
-> END
02-diamonds.ink

Restores: Three diamonds chained; the trunk reads differently by state — the end of Many diamonds. Paste this in and rejoin at AI as co-author.

Two full diamonds and a seasoned trunk (the block asks for three — build the third from here using the chained-diamond template in Section 4). The crossing knot is the trunk-with-seasoning pattern in action: one passage, colored by trust, never duplicated.

Ink
VAR trust = 0
VAR knows_truth = false
VAR took_the_coat = false

-> arrival

// ── Diamond one ─────────────────────────────────────────────────────────
=== arrival
The ferryman does not look up when you board.
+ ["It's cold." ] -> arrival_warm
+ [Say nothing.] -> arrival_cold
= arrival_warm
    ~ trust = trust + 1
    "It is," he agrees, and something in him unlocks a notch.
    -> crossing
= arrival_cold
    ~ trust = trust - 1
    The silence sets, like something cooling.
    -> crossing

// ── Trunk, seasoned by state ────────────────────────────────────────────
=== crossing
The water goes by underneath, {trust > 0: companionably|like an argument you are losing}.
{trust > 0: He offers you his coat.|He does not offer you his coat.}
+ {trust > 0} [Take the coat]
    ~ took_the_coat = true
    -> midpoint
+ [Watch the far bank] -> midpoint

// ── Diamond two ─────────────────────────────────────────────────────────
=== midpoint
Halfway, he says: "You know what's on the other side."
+ ["I do."]
    ~ knows_truth = true
    -> midpoint_join
+ ["Tell me."]
    ~ trust = trust + 1
    He tells you. You wish he hadn't.
    ~ knows_truth = true
    -> midpoint_join
+ [Pretend not to hear] -> midpoint_join
= midpoint_join
    The far bank resolves out of the dark.
    -> landing

=== landing
{knows_truth:
    You step off knowing exactly what you are walking into.
- else:
    You step off into something you have refused to name.
}
{took_the_coat: The coat is still around your shoulders. You will have to give it back, eventually.}
{trust > 1: He watches you go, and you feel it.|He is already pushing off.}
-> END
03-publish-ready.ink

Restores: The state a story needs walking into Review & publish. Carries the # mood: and # live: tags a review-ready story needs.

Short, deliberately — this is what done enough to submit looks like: a # mood: tag on every beat, a # live: tag on the speaking character, and every path reaching -> END.

Ink
VAR trust = 0

# mood:calm
-> arrival

=== arrival
# live:ferryman
The ferryman does not look up when you board.
+ ["It's cold."]
    ~ trust = trust + 1
    "It is," he agrees.
    -> crossing
+ [Say nothing.] -> crossing

=== crossing
# mood:wonder
The water goes by underneath, {trust > 0: companionably|like an argument you are losing}.
+ [Watch the far bank] -> landing

=== landing
# mood:sadness
# live:ferryman
"Go on, then," he says. "It doesn't get easier from here."
-> END

7. Review checklist

Before you submit

Here’s what you can check yourself, before you ever hit publish:

  • Make sure every path reaches -> END or loops back to a valid choice. No dead ends — a knot with no choices and no divert leaves your reader stranded.
  • Give at least one variable a moment where an earlier choice visibly changes the story later on. That’s the whole point of tracking it.
  • Read your characters’ lines out loud. Does it still sound like them, start to finish?
  • Check your # mood: tags match the scene, not just habit — # mood:danger on a calm beat reads as a bug.
  • Read it in Preview, not just in the Manuscript editor. Typos hide in the editor and jump right out in Preview.
  • Read your choice transitions aloud. Prose should flow, not clunk.
  • Play through at least two different paths yourself. Do both make narrative sense?
ErrorFix
not found: knot_nameCheck spelling — knot names must match exactly.
Story stops unexpectedlyYou’re missing a -> END or a divert to the next knot.
Choice doesn’t appearMake sure it starts with * or + and is indented under the right knot.

What editorial review checks

Review is LLM-backed and it hard-gates publish: your story’s review status has to come back passed before it can go live. That’s a real gate, not a formality — build the self-check habits above and most stories clear it the first time.

The mature rating, stated plainly

Put this many people in a room writing interactive fiction, and somebody will write something dark. That’s a certainty, not a risk. A mature rating does not reject your story — it routes to manual review. Your facilitator holds that manual-approve path and has used it before. Intense, complicated, dark work still ships. Just not always by four o’clock.

8. Next steps

Threads — not today, but real

Real stories don’t follow a single track. Subplots simmer in the background. Characters pursue their own agendas. News arrives from elsewhere. Ink’s thread system lets you weave multiple storylines together — presenting choices from different narrative strands in a single moment.

A thread (<- knot_name) pulls content from a parallel storyline into the knot you’re in, as if it were written inline, and returns with ->->, same as a tunnel. It just doesn’t fit in a single studio day alongside the Nine Rooms, diamonds, AI authoring, and publishing — and it’s the least useful construct on day one anyway. Learn it next, once today’s shapes feel comfortable.

Further down the same road: Ink’s LIST system (named, ordered state — relationships, inventory, world progression, beyond a plain VAR) and functions ({function_name()}, reusable logic that generates text from that state). Both build directly on what you did today; neither is needed to finish today.

The rest of the toolkit

Four companion tools, already live — not duplicated here, worth bookmarking:

By four, yours is on this shelf. Go build the next one.