Ghostlight/docs/architecture/ink-branching-scenes.md
2026-05-04 13:33:18 +01:00

498 lines
23 KiB
Markdown

# Ink Branching Scenes
Ink is the branching scene format.
Ghostlight should not invent a parallel dialogue-tree language unless Ink
fails a concrete requirement. Ink already gives us readable interactive fiction,
choices, knots, stitches, variables, compiled JSON, and a format writers
can inspect without needing to wear a compiler helmet indoors.
Ghostlight's job is the machinery around Ink:
- build actor-local awareness from agent state and scene state
- project plausible speech and non-speech choices
- save generation receipts when a model proposes branches
- annotate why those choices make sense
- preserve listener interpretation as separate from actor intent
- record consequence surfaces for later state mutation
- keep fuzzy social updates reviewed until there is training data
- compile Ink into game-consumable JSON
Ink owns the playable branch structure. Ghostlight owns the psychological,
cultural, memory, relationship, and consequence rationale.
## Runtime Shape
The artifact flow is:
```text
agent state + scene state + lore grounding
-> local awareness
-> projected branch choices for the acting character
-> selected branch as observable action
-> event resolution from selected observable action
-> participant appraisals and reviewed state mutation
-> next actor local awareness from updated state
-> next actor action
-> Ink scene
-> compiled Ink JSON
-> reviewed training annotations
-> selected branch consequences
-> state, memory, belief, and relationship updates
```
The Ink file should be readable as Aetheria content on its own. The sidecar
training annotation should explain which Ghostlight state produced each branch
and which consequences are automatic, reviewed, or deferred.
Each character turn must be generated from that character's local awareness.
The protagonist-choice generator should not receive the responder's private
state. The responder generator should not receive the protagonist's private
intent except as an observable action, spoken text, gesture, object use,
silence, or other perceivable cue. A listener is allowed to misunderstand.
Participant appraisal is symmetrical. If an action hurts, threatens, reassures,
humiliates, obligates, or overloads anyone present, that change is consolidated
for the affected character before the next actor is selected. The next actor may
reply, walk away, comply, escalate, or end the interaction. A branch artifact can
still display this as `choice -> reaction`, but Ghostlight should model it as
turns over updated state, not as a mandatory paired response.
One-shot generation of both sides is allowed only as bootstrap scaffolding, and
must be marked as such in the capture review. It is not the target runtime
shape.
The current target path is source-grounded coordinator state, projected local
context, reviewed character turns, branch compiler materialization, and IF
artifact review.
## Ink Comments
Use Ink comments for lightweight traceability in writer-facing drafts:
- `ghostlight.scene`
- `ghostlight.fixture`
- `ghostlight.branch`
- `ghostlight.action`
- `ghostlight.intent`
- `ghostlight.npc_response`
- `ghostlight.consequence`
- `ghostlight.training_hook`
- `aetheria.flashpoint`
Use `//`, not Ink tags (`#`), for this metadata in drafts. Inky exposes tags in
preview, which makes Ghostlight state leak into the readable scene. The
sidecar remains the machine-readable receipt; comments are only local handles
for humans and simple tools.
## Sidecar Annotations
Each Ink scene should have a `.training.json` sidecar with:
- source fixture reference
- protagonist and NPC ids
- actor-local awareness summary
- projection controls
- branch ids and Ink knot paths
- action type and actor intent
- state basis
- reviewed consequences
- training hooks
- mutation policy
This sidecar is intentionally not a new branching format. It is a receipt. Ink
remains the scene tree; the sidecar says why the tree grew that way and which
state changes a reviewer accepted.
Visual planning should live beside the sidecar, not inside it. Use a separate
`.visual.json` artifact for illustrated replay data: style cue, visual scene
anchors, image prompts, character refs, prompt assembly rules, and visual
modifiers. The training annotation may reference that file with
`visual_plan_ref`, but it should remain focused on branch semantics and
mutation surfaces.
## Mutation Policy
Ink variables can record local playthrough outcomes. They should not silently
become canonical Ghostlight state.
Automatic:
- Ink compilation
- branch id and knot validation
- local variables for the current playthrough
- mechanical comment handles and training hook checks
Manual reviewed until trained:
- relationship deltas
- belief updates
- memory writes
- listener misread classification
- activation changes
- promotion from branch outcome into durable Aetheria lore
The current accepted-as-draft scaffold lives in
`examples/ink/pallas-species-strikes.branch-and-fold.v0.ink`.
## Cross-Scene Consequence Carryover
Ghostlight branching scenes should not behave like disconnected dialogue samples.
Interactive fiction needs consequences that survive the scene boundary. A branch
choice should write durable effects into later context: relationship stance,
perceived motives, resource costs, authority flags, object custody, active
constraints, unresolved hooks, and available future options.
For training data, every material branch should preserve:
- the player/NPC-visible choice or action
- the immediate responder output
- accepted world-state, resource, memory, and relationship deltas
- branch flags that later scenes must read
- deferred consequences that should surface later rather than immediately
- scene text showing how the consequence changes the feel of the next beat
The coordinator owns this continuity for now. Later, the trainable coordinator
model should learn to carry these consequences forward without flattening them
into cosmetic callbacks. If a choice does not change later affordances, trust,
resources, risk, or interpretation, it is probably not a meaningful branch.
A complete interactive fixture needs an ordinary-world opening, consequential
branches before the crisis peak, and a resolution that visibly reflects earlier
choices. A single tense exchange can prove a seam; it is not enough to prove the
story machine.
## Branch And Fold
Do not model every choice as an indefinitely isolated authored subtree. Strict
worldline splitting creates a combinatoric writing burden after a handful of
choices, which interactive fiction authors have been discovering the painful
way since approximately the first time someone let a player open a second door.
Ghostlight should branch locally and fold structurally:
```text
choice
-> consequence packet
-> normalized state deltas
-> shared later scene reads compact variables
-> conditional options, callbacks, imagery, and relationship texture
-> major route split only when convergence would be false
```
Most branches should converge onto shared later scenes. The consequences should
survive as compact variables, bands, and flags rather than bespoke scenes for
every exact history.
Good consequence state looks like this:
- `sella_trust_maer: wary`
- `ledger_debt_level: high`
- `clinic_staff_exhaustion: true`
- `veyr_evidence_disclosed: partial`
- `isdra_reads_maer_as: procedurally_dangerous`
- `route_authority_pressure: escalated`
Those variables should alter later scenes by changing available options,
relationship posture, timing pressure, resource costs, callback prose, visual
state modifiers, and participant appraisals. They should not usually require a
fully separate scene route.
Use coarse bands instead of fake precision. `blocked`, `wary`, `conditional`,
`cooperative`, and `personally_invested` are more useful than pretending the
story can meaningfully maintain 37 decimals of trust. The same applies to heat
debt, legal exposure, institutional pressure, staff exhaustion, reputation, and
social suspicion.
Major route splits are allowed when a consequence changes the playable world so
much that folding would lie. Examples:
- a character dies, leaves, defects, or becomes unavailable
- a location is destroyed, locked, opened, or politically inaccessible
- a faction becomes hostile, allied, exposed, or legally empowered
- a key object is destroyed, stolen, transferred, or transformed
- the protagonist loses or gains institutional authority
- a quest route, rescue path, market, technology, or branch-local future opens
or closes entirely
The coordinator owns the fold decision for now. Every material branch should
record whether it folds into an existing later scene, opens a conditional
variant inside that scene, or creates a justified route split. If a route split
is created, the artifact should name the reason and the expected future writing
debt. Trees are cheap to grow and expensive to prune. The machine should not
act surprised by botany.
## Branch Compiler
The Branch Compiler is the organ that turns Ghostlight scene intent into a
playable Ink artifact. It is not the character agent, the responder, the
appraiser, or the final reviewer. It assembles branch structure from already
available scene state, projected actor affordances, proposed actions, accepted
consequence packets, and coordinator constraints.
The Branch Compiler receives:
- fixture brief and scene spine
- source-grounded lore digest
- current world, scene, resource, relationship, and branch state
- actor-local branch candidates, including speech and non-speech actions
- responder outputs or coordinator-authored placeholder reactions
- consequence packets from reviewed turns
- branch-and-fold plan
- visual continuity requirements
- schema and Ink formatting constraints
The Branch Compiler emits:
- readable `.ink` scene content
- Ink variables, knots, stitches, choices, gathers, and conditionals
- a `.training.json` sidecar tying each branch to state basis, action intent,
consequence surfaces, and later callbacks
- compiler notes for fold decisions, route-split decisions, and known review
risks
- image prompt anchors and branch/state modification handles when the fixture is
meant for illustrated output
The Branch Compiler may propose structure. It must not invent hidden character
truth, private motivations, or durable social mutations as if compilation made
them real. Character behavior comes from projected local context and responder
or coordinator-authored action proposals. Social interpretation and memory
changes come from appraiser/mutator review. The compiler's job is to make the
branching artifact playable, legible, and traceable.
The compiler should prefer compact state variables, bands, and flags over
runaway branch trees. It should create major route splits only when the
coordinator or reviewed consequence packet says folding would falsify the
world. When it folds branches, it must preserve the variables and callbacks that
make the fold honest.
The Branch Compiler output is draft until the IF Artifact Reviewer accepts it.
That separation matters. A compiler can make a scene valid Ink while still
producing cosmetic choices, fake folds, unused variables, or endings that ignore
the whole moral machinery. Valid syntax is not wisdom. It is barely hygiene.
## IF Artifact Review
Branching fixtures need an independent review pass before acceptance. The
coordinator should not be the only thing grading its own Ink, sidecar, and
branch logic. That is how a scene ends up proudly tracking variables that do
nothing except sit there in a tiny uniform.
The reviewer may start as a prompted specialist sub-agent. Later it should
become evaluator/classifier training data and eventually a trainable review
organ. Its job is not prose polish. Its job is to catch false consequence.
The reviewer receives:
- the Ink file
- the `.training.json` sidecar
- the coordinator artifact
- the intended branch plan, when available
- the declared state variables, flags, bands, resources, risks, and visual
modifiers
- any compiled Ink or validation output available for the fixture
The reviewer emits:
- `accepted`, `needs_revision`, or `rejected`
- severity-ranked findings
- a fake-variable audit
- a choice-consequence audit
- a fold/callback audit
- a state-gated prose audit
- a visual-continuity audit
- major-route-split justification notes
- specific required fixes
Acceptance requires every material choice to change at least one later surface:
- available options
- scene route or justified route split
- relationship, trust, appraisal, or social standing
- resource cost, time pressure, legal exposure, authority pressure, heat burden,
exhaustion, or other risk
- object custody, evidence quality, access, or technology state
- later callback prose implemented as an actual conditional
- ending texture or outcome band
- visual state modifier tied to a real branch flag or state variable
Every declared variable must either be read later or be marked as telemetry-only
in the sidecar. Ominous variables need threshold behavior. If a variable named
`authority_pressure` rises and nobody with authority ever reacts, the artifact
fails review. If `heat_time` counts down and no route, ending, resource cost, or
staff condition changes, the artifact fails review. The name is allowed to be
dramatic only if the machinery has teeth.
Any prose that says "if X happened" must be implemented as an actual conditional
or moved into reviewer/coordinator notes. Writer-facing text should not name a
branch possibility while dodging the state check that proves which version of
history occurred.
Common failure labels:
- `potemkin_state`: a tracked variable never materially changes the artifact
- `cosmetic_choice`: a choice changes wording but not affordance, cost, risk,
appraisal, callback, visual state, or outcome
- `missing_state_read`: a set variable is never read by later Ink, sidecar, or
coordinator consequence logic
- `fake_fold`: branches claim to fold, but the folded scene ignores their state
- `unearned_convergence`: branches converge despite consequences that should
make convergence false
- `unjustified_route_split`: a separate route exists without enough world or
story consequence to justify the writing debt
- `state_named_instead_of_checked`: prose mentions alternate branch states
instead of checking variables
- `visual_callback_missing`: image modifiers or visual prose ignore branch
consequences that should be visible
- `authority_without_consequence`: institutional pressure rises but never bites
- `resource_without_cost`: ledgers, heat, staff time, supplies, or access are
invoked without affecting later choices or outcomes
- `ending_ignores_major_state`: the ending does not respond to major variables
established earlier
Early branch-and-fold review exposed this exact class of failure: variables can
look important while doing nothing. Review must force important variables to
alter gates, callbacks, visuals, appraisals, or outcomes before a fixture enters
the accepted corpus.
## Scene Imagery
Ghostlight scenes are also website content. A complete fixture should emit a
visual plan artifact alongside prose and training receipts so story branches can
be illustrated without a later model guessing what the room looked like.
Interactive fiction replay also needs screen rhythm. A narrative scene may move
through several visual sections: establishing shot, workstation, break table,
object closeup, alarm state, crowd line, supervisor arrival, aftermath. These
should be segmented so an InkJS replay can click through the scene at the pace
the story needs. A pivotal beat may be a single sentence on its own screen when
the point is to let the image and silence do the work.
Each distinct visual section should preserve an art-direction block with:
- global style cue: the fixture-level visual language when the art direction
depends on a specific non-default look
- establishing shot: location, scale, camera distance, and dominant geometry
- light and color: practical light sources, palette, exposure, haze, glare,
reflection, water, vacuum, screen glow, or other atmosphere
- bodies and interfaces: how each relevant body occupies the space, including
nonhuman access, assistive infrastructure, suits, avatars, wet/dry boundaries,
displays, tools, and machinery
- material evidence: objects, damage, records, ledgers, cargo, alarms, clothing,
stains, frost, heat shimmer, or other concrete traces of what has happened
- branch marks: visible changes caused by earlier choices, such as a sealed
hatch, an exhausted staffer, a live debt ledger, missing equipment, guarded
posture, or a route marker left blinking
- base image prompt: one stable prompt for the unbranched scene, suitable for
generating the reusable background or canonical key frame
- branch modification prompts: additive edit prompts for visible consequences,
changed character positions, opened or sealed routes, damaged objects,
lighting changes, emotional posture, crowd pressure, or other scene states
Named characters need stable visual references. An image model does not know
what Lio Vale looks like because the prose named Lio Vale. Each illustrated
fixture should define `visual_character_refs` for recurring named characters:
body type, apparent age range if relevant, face/hair/skin or equivalent visual
identity, clothing and equipment, silhouette, posture language, and
`do_not_change` continuity details. Scene prompts may then reference those
character refs instead of repeating the whole description every time.
Prompts must be imagegen-ready. Do not rely on unexplained Aetheria jargon as
if the image model knows the lore. If the prompt says "wet-service cradle" or
"dry-operation harness," it should also describe the visible thing according to
the faction, location, and labor regime. A Navigator comfort habitat and an AU
industrial yard should not quietly become the same room. If a scene uses a
biodrone limiter interface, cavity seal assembly, or life-support bypass kit,
describe the shape, scale, materials, lights, cables, seals, or displays that
make it drawable.
The same rule applies to environments. "Manifold line," "cavity yard,"
"supervisor glass," and "anchor rail" are not images by themselves. A base
prompt should describe the physical space a general image model can draw:
corridor shape, wall/ceiling/floor geometry, pipe runs, cable bundles, panels,
screens, rails, doors, windows, gauges, warning lights, materials, scale, and
where bodies stand inside that geometry.
Image prompts should be affirmative. Describe the desired geometry, material,
composition, lighting, bodies, tools, and state. Negative prompt constructions
such as "do not draw X," "without X," or "rather than X" tend to feed the image
model the wrong object and then hope it obeys the minus sign. That is weak
magic. Put the target in the prompt, and keep forbidden assumptions in reviewer
notes or validation errors.
For Aetheria Bloom habitats, prompt the engineered object directly: rotating
cavity-shell infrastructure, consolidated asteroid rubble/debris, TCS
substrate, shell plating, ribbed supports, seals, hub/spoke interfaces,
manifolds, and service systems. Source-specific exceptions should be represented
as their own positive visible description.
Every visual section should identify:
- `visual_scene_id`: stable handle for the screen or key frame
- `ink_anchor`: knot, stitch, or comment handle where the visual applies
- `base_image_prompt`: durable scene prompt for the unmodified frame
- `visible_characters`: characters allowed in the frame and their default
position or stance, plus a `visual_ref` when the character is named
- `state_image_modifiers`: additive prompts tied to explicit variables,
thresholds, or route flags
- `branch_image_modifiers`: additive prompts tied to specific branch ids
- `visual_continuity_notes`: facts that must remain stable across edits
Prompt assembly for illustrated replay should combine:
```text
global style cue
+ visual_character_refs for named characters actually visible in this rendered frame
+ visual section base_image_prompt
+ triggered state or branch image modifiers
```
The base image prompt is allowed to focus on the room and staging only if the
assembled prompt includes the relevant character refs. If a tool sends the base
prompt alone to imagegen, the base prompt must carry enough character detail by
itself. The same applies to visual style: if a fixture depends on a specific
look, the assembled prompt must carry that style cue every time. Names are
handles, not faces.
Keep `visual_continuity_notes` out of assembled image prompts. They are tooling
and review constraints for keeping repeated generations coherent. If a
continuity fact needs to reach the image model, rewrite it as affirmative,
concrete visible prompt text in the base prompt, a character ref, or a
branch/state modifier.
The image generator only sees the prompt you send it. It does not remember the
previous scene unless a real image-editing workflow supplies that image as
input.
The coordinator owns this surface for now. The responder should not receive
omniscient visual art direction unless the character can observe it. Character
packets may receive the local visible subset: what this actor can see, hear,
touch, smell, monitor, or infer from their interface.
Visual continuity is part of consequence carryover. If an earlier branch cost
heat, trust, time, equipment, or social standing, the later scene should show
that cost somewhere on the page or in the frame. Otherwise the choice may be
mechanically recorded while still feeling fake, which is the stupid little
trapdoor interactive fiction keeps setting for itself.
When continuity matters, use one durable base visual prompt and branch/state
modification prompts that describe only what changed. The base prompt owns the
room, palette, dominant geometry, key props, bodies, and interface layout. A
modification prompt owns the delta: Sella moves from the dryside console to the
decon sleeve, the route marker flips from amber to white, a wet access hatch
seals, an exhausted staffer appears behind the glass, Maer's ledger panel gains
a live debt line.
Keep these prompts separate from canonical prose:
- `base_image_prompt`: stable scene anchor
- `state_image_modifiers`: reusable prompts for common scene states
- `branch_image_modifiers`: prompts tied to specific branch ids
- `visual_continuity_notes`: non-prompt reminders about what must remain stable
Image edits are allowed to stylize, compose, and beautify. They are not allowed
to silently change canonical facts. If an edit introduces a new visible object,
body affordance, damage mark, or faction symbol that was not in the scene state,
the coordinator must either reject it, mark it as noncanonical illustration
noise, or promote it through the normal reviewed lore/state path. The picture
does not get to smuggle a gun onto the mantel just because it had a strong
personal brand that morning.