Skip to content

Files & Fragment Headers

Fragments live in .frag files, parsed and validated at build time. Prefix a filename with _ (e.g. _draft.frag) to exclude it from the build while you work on it.

One fragment per file, and the file is named after the fragment’s id: the fragment below belongs in childhood_stray_dog.frag. npm run validate rejects a shipped file that holds two fragments or whose name does not match. Drafts are exempt, so an underscored file is a fine scratchpad for as many half-written scenes as you like.

=== childhood_stray_dog
phase: childhood
title: The Stray Dog
tags: animals, empathy
requires: VIT >= 30 && !has_pet

Every fragment starts with === fragment_id. Ids are snake_case and must be unique across all files. Header fields follow, ended by the first blank line. An unknown header field is a build error - a typo like phse: cannot slip through.

FieldRequiredNotes
phase:yeschildhood · youth · early-adulthood · middle-life · maturity · late-life
title:yesShown to the player above the scene
tags:recommendedComma-separated. The first tag drives selection diversity - the game avoids three same-tagged fragments in a row. No tags is a build warning.
requires:noCondition for the fragment to be eligible at all. Repeatable - multiple lines AND together.

There is no author field for you to fill in. author: exists in shipped fragments, but it is written when a submission is accepted, from the handle you registered under, and a submitted file containing one is refused. A name you type is a claim; a name taken from your registration is a fact, and authorship is not something that should rest on a claim.

  • # ... - a full-line comment, ignored by the parser. Use for TODOs and notes to other authors.
  • # ... at the end of a line - a trailing comment, on structural lines only: choices, ->, @, === and anything starting with *. It needs two or more spaces before the hash. Prose never carries one, because a hash there is content.
  • \* - alone on a line, opens a block comment; a *\ alone on a line closes it.
  • --- - an optional visual separator, also ignored. Some authors place one between setup prose and choices.

Two spaces rather than one, so that > Ask about the #1 hit keeps its hash. Nobody aligns a comment with a single space, and everybody writes a hash mid-sentence with one.

A block comment hides everything between the fences from the parser, so a commented-out choice does not count as a choice and a commented-out *endif closes nothing. It works anywhere: above the first fragment, between fragments, inside a header, inside a choice body, inside an *if block.

\*
Kevin's whole arc needs rethinking. Parking the old version
here until youth has more fragments to hang it on.
*\

Each fence needs its own line: \* a short note *\ is refused rather than printed, and a *\ with nothing above it is refused too. Leaving a block open is a build error that names the line it opened on. A prose line starting with * still escapes the usual way, as \*text - only a bare \* reads as a fence.

Indentation is purely cosmetic, everywhere. Nesting comes from markers (> vs >>, *if*endif, @), never from spacing. Indent however reads best to you.

If a prose line genuinely needs to start with a keyword (*if, *endif, *set: …), escape it with a leading backslash:

\if only you had known.