Skip to content

Lattice reference · 2 of 4

Blocks, content, and linear flow

A Block is the smallest passage that flow can target. It combines authoring metadata, visible content, optional continuity annotations, and exactly one flow terminator.

Block declaration

text
block opening (
  title: "The warning",
  purpose: setup,
  summary: { Mara finds the signal source. },
  setup: [signal_arc],
  tags: [opening]
) {
  text: { A light wakes beneath the garden. };
  next -> answer;
}

Every Block must satisfy all of these rules:

  • its ID is unique across every imported file;
  • title and purpose are present;
  • at least one text or dialogue item is visible;
  • content items appear before the terminator;
  • exactly one terminator is present and allowed by the chosen story type and context;
  • every referenced target exists and stays inside the main story or current Module.

Block metadata

FieldRequiredAccepted valueMeaning
titleyestextReader/author-facing passage title
purposeyespurpose identifierNarrative function of the Block
summarynotextConcise author-facing synopsis
prose_targetnopositive integerReallocatable local drafting budget
setupnomarker ID listIntroduces narrative promises
requiresnomarker ID listExpects earlier setup evidence
resolvesnomarker ID listPays off earlier setup evidence
tagsnotext or listAuthoring classification

prose_target requires a complete Story prose goal. It guides progressive drafting but does not independently make the Block valid or invalid.

The accepted purpose values are:

ValueTypical use
setupEstablish a character, situation, question, or promise
expositionDeliver context the reader needs
developmentAdvance an established thread
transitionMove between places, times, or phases
turnChange the direction or meaning of events
climaxResolve the story's central pressure through decisive action
resolutionShow the outcome after the central conflict
payoffDeliver a promised reveal, consequence, or emotional return

These values support validation and authoring tools; they do not create flow.

Narration

Narration uses text, a colon, a prose value, and a semicolon:

text
text: {
  The station clock kept perfect time.
  Mara watched the second hand cross midnight.
};

A Block may contain multiple narration and Dialogue items in reading order.

text
text: { The rails begin to sing. };
dialogue ch_mara: { That train is a day early. };
text: { No one on the platform turns to answer. };

text is visible content. Publish validation rejects a Block that contains only annotations or flow. Outline validation temporarily permits it so the final Block graph, summaries, budgets, and continuity relationships can be inspected before prose is drafted. Preview and export never substitute an author-facing summary for missing reader text.

Dialogue

The declaration shape is dialogue <character-id> (<metadata>): <prose>;.

text
dialogue ch_mara (
  mode: whisper,
  emotion: cautious,
  expression: focused,
  pose: listening,
  position: left,
  stage_direction: { She leans toward the soil. }
): { Not again. };

The speaker must reference a declared character. All Dialogue metadata is optional.

FieldAccepted valueMeaning
modespeech, thought, whisper, shoutDelivery category
positionleft, center, rightReader presentation position
emotionidentifierAsset-facing emotional state
expressionidentifierAsset-facing facial expression
poseidentifierAsset-facing body pose
stage_directiontextSeparate action or staging note

Presentation fields are retained for Preview/export, but they never alter Block flow, Guards, or runtime State. Missing asset identifiers do not create story branches.

Lightweight continuity markers

Block metadata can describe setup and payoff without adding runtime logic:

text
block warning (
  title: "The warning",
  purpose: setup,
  setup: [signal_arc]
) {
  text: { The key pulses whenever Mara faces north. };
  next -> crossing;
}

block crossing (
  title: "Crossing",
  purpose: development,
  requires: [signal_arc]
) {
  text: { She trusts the pulse and steps into the fog. };
  next -> answer;
}

block answer (
  title: "The answer",
  purpose: payoff,
  resolves: [signal_arc]
) {
  text: { The pulse spells her own name. };
  end;
}
  • setup introduces a marker.
  • requires says this Block expects that marker to have been introduced.
  • resolves says this Block pays the marker off.

Marker IDs are authoring facts, not declared State. They cannot make a Module eligible, satisfy a Pool exit, or remember an ordinary player choice.

Structured continuity events

Place structured annotations among narration and Dialogue:

text
foreshadowing fs_signal: event {
  subject: ch_mara,
  object: it_key,
  predicate: notices,
  time: "before dawn"
};

reveals rv_signal resolves fs_signal: event {
  subject: ch_mara,
  object: it_key,
  predicate: understands,
  time: "midnight"
};

A foreshadowing declaration has its own ID. A reveals declaration has its own ID and names the foreshadowing it resolves.

An event requires subject and predicate; object, place, and time are optional. Event values may be identifiers, strings, numbers, or booleans. These annotations describe narrative evidence only. Even when their tuple resembles a CYOA State, they never activate or remove that State.

Flow terminators

Every Block ends with exactly one of these forms:

TerminatorShapeWhere it can be used
Automatic continuationnext -> target;Linear and CYOA; same story or Module
Endingend;Linear and CYOA; ends the whole story
Player choicechoice { option ... }CYOA; same story or Module
Enter Poolenter pool_id -> resume;CYOA main story only
Complete Modulecomplete (...);CYOA Module only

There is no implicit fall-through. The next Block in the file or Scene does not run unless the terminator targets it.

Linear story rules

Linear story permits only next and end:

text
next -> answer;
text
end;

It rejects choice, enter, complete, predicate, state, state group, pool, module, and story.initial_state even if those declarations parse correctly.

The Story entry must begin one finite chain:

text
entry -> block -> block -> ... -> one ending

Full validation rejects:

  • an incoming edge to the entry;
  • a non-entry Block without exactly one incoming edge;
  • forks or merges;
  • an unreachable/disconnected Block;
  • a cycle;
  • no ending or more than one ending.

Complete Linear example

text
story main (
  title: "Signal Garden",
  language: "en",
  format: novel,
  entry: opening,
  beat_order: [opening, resolution]
);

entity character ch_mara (name: "Mara", role: protagonist);
entity location loc_garden (name: "Signal Garden");

scene garden (
  title: "The garden",
  beat: opening,
  pov: ch_mara,
  location: loc_garden
) {
  block opening (
    title: "The warning",
    purpose: setup,
    setup: [signal_arc]
  ) {
    text: { A light wakes beneath the garden. };
    dialogue ch_mara (
      mode: whisper,
      emotion: cautious
    ): { Not again. };
    next -> answer;
  }

  block answer (
    title: "The answer",
    purpose: resolution,
    resolves: [signal_arc]
  ) {
    text: { She follows the light home. };
    end;
  }
}

Declaration order makes this source easy to scan, but only entry, next, and end define the reading sequence. Adding a Block without linking it into the chain creates an unreachable Block.

Beat order is not Block flow

story.beat_order supplies an expected order for Scene beat metadata. It can diagnose missing, unknown, or out-of-order beats in supported validation paths, but it never connects Blocks. Preserve both the intended beat sequence and explicit Block targets when splitting a Linear story across files.

Next: CYOA choices, State, Pools, and Modules.