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
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;
titleandpurposeare present;- at least one
textordialogueitem 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
| Field | Required | Accepted value | Meaning |
|---|---|---|---|
title | yes | text | Reader/author-facing passage title |
purpose | yes | purpose identifier | Narrative function of the Block |
summary | no | text | Concise author-facing synopsis |
prose_target | no | positive integer | Reallocatable local drafting budget |
setup | no | marker ID list | Introduces narrative promises |
requires | no | marker ID list | Expects earlier setup evidence |
resolves | no | marker ID list | Pays off earlier setup evidence |
tags | no | text or list | Authoring 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:
| Value | Typical use |
|---|---|
setup | Establish a character, situation, question, or promise |
exposition | Deliver context the reader needs |
development | Advance an established thread |
transition | Move between places, times, or phases |
turn | Change the direction or meaning of events |
climax | Resolve the story's central pressure through decisive action |
resolution | Show the outcome after the central conflict |
payoff | Deliver 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: {
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: { 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>;.
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.
| Field | Accepted value | Meaning |
|---|---|---|
mode | speech, thought, whisper, shout | Delivery category |
position | left, center, right | Reader presentation position |
emotion | identifier | Asset-facing emotional state |
expression | identifier | Asset-facing facial expression |
pose | identifier | Asset-facing body pose |
stage_direction | text | Separate 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:
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;
}setupintroduces a marker.requiressays this Block expects that marker to have been introduced.resolvessays 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:
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:
| Terminator | Shape | Where it can be used |
|---|---|---|
| Automatic continuation | next -> target; | Linear and CYOA; same story or Module |
| Ending | end; | Linear and CYOA; ends the whole story |
| Player choice | choice { option ... } | CYOA; same story or Module |
| Enter Pool | enter pool_id -> resume; | CYOA main story only |
| Complete Module | complete (...); | 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:
next -> answer;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:
entry -> block -> block -> ... -> one endingFull 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
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.
