The VN scaffold — Script as the projection, summary and deltas as the truth
From the Meridians Wiki · Public · Maintained · human-contract
Status: rough spec (direction). Not an implementation contract; nothing here ships until scheduled.
This document owns the substrate: what the visual novel is made of, what is authoritative, and what is merely a rendering of it. Every other document in this folder depends on the rule stated here, so it is stated first and once.
The rule. A scene's summary and its typed deltas are the source of truth. The Script — frames, cast, slots, props, moments — is a projection of that truth: generated, versioned, regenerable, and never authoritative. The VN reader is a projection of the projection.
Everything the medium wants follows from taking that seriously. Regenerable staging means a style change is a re-render rather than a rewrite; a delta-grounded scaffold means expressions, plate variants, and cutscene placement can be derived rather than guessed; and a projection that never writes back means a reader can explore a branching record without ever endangering it.
Read alongside: delivery (what the modality is), branching (how a fork is produced), production (what the assets cost), reader (the interface).
1. The layer cake
Scene.summary + typed deltas ← canon. What happened, and what moved.
└ BeatPlan ← structure. The scene's spine.
└ prose (ProseVersion) ← the text. The work's voice.
└ ScriptContent (ScriptVersion) ← the scaffold. Blocking, casting, moments.
└ VN reader ← the experience. Transport, saves, POV, presentation.
The conditioned terminal stage receives:
| Input | Role |
|---|---|
<questions> | Learning questions the reader must be able to answer; each is served by one or more frames. |
<closures> | Thread closures derived from settled Causality Threads; settled Threads become visible fact, open Threads remain undecided. |
Script fidelity is to canon + Flags + questions. Prose is the richest material surface, not independent authority. See pentad/app-integration §5.2 for the full generating-ring reading.
Each layer is derived from the ones above and may be regenerated without touching them. Each layer is
versioned where it is stored — Scene.scriptVersions: ScriptVersion[], resolved through branch lineage
and fork time exactly as proseVersions and planVersions are. That is already how the record works, and
it is the property the VN depends on: the same canon can carry many scaffolds.
The inverse move is forbidden and worth naming, because it is the tempting one:
- Staging never invents an event. The composer is instructed to re-stage, not to write.
- Never re-parse a scaffold (or its prose) to recover canonical state. Deltas are declared, not extracted.
- A reader's traversal never writes to any of these layers.
2. What is already shipped
The scaffold generator exists. generateSceneScript (src/lib/engine/ai/script.ts) composes a scene into a
ScriptContent and commits it as a version, and the prompt it uses
(src/lib/engine/prompts/script/compose.ts) is already a stage-director brief rather than a text-to-images
call. Its inputs, in the order it receives them:
| Input | What it contributes |
|---|---|
| Roster | The exact ids that may be staged — POV character, participants, the location, and artifacts drawn from artifactUsages and ownershipDeltas. Invented ids are refused. |
<logic-context> | Point-in-time knowledge, asymmetry, relationships, required events, arrivals, and artifact possession. A later Scene's knowledge cannot enter an earlier scaffold. |
<scene-brief> | sceneContext for the scene with <world-state> stripped — the structural truth: summary, deltas, events, thread/world/relationship shifts. Always included, even before prose exists. |
<beat-plan> | The structural spine; used to find natural frame boundaries. |
<prose> | Optional and additive: the richest surface when present, the text whose exchanges become moments. |
<prose-profile> | The work's voice — register, stance, devices — so staged lines sound like this work. |
<steer> | GM intent, shaping staging without contradicting the prose. |
<knowledge> | Per-lens knowledge and secret nodes admissible at the scene cursor; Script reveals may name only these ids. |
<force-reading> | Descriptive World/System/Fate/Theme/Causality readout at the scene cursor; it is context, never an optimisation target. |
Two consequences matter for everything downstream:
- The delta layer is already an input to staging. The scaffold is not composed from prose alone, so "summary and deltas are the source of truth" is a property the pipeline mostly already has — the gap is that nothing derives staging decisions from specific deltas (§4), it only reads them as context.
- Staging works without prose. The brief alone can carry a scaffold. That is the cheap lane for exploration: a scouted branch can be staged structurally before anyone pays to write it beautifully.
- Interiority follows the active ordered lens.
Scene.povIdsis the ordered set of Character lenses authorised to carry passages in the Scene;Scene.povIdis its opening/default lens. Legacy Scenes that predate the roster derive it from Domain settings and present participants rather than being rewritten. A Beat may retain the active lens, switch to another allowed Character, or usenullfor narrator/choral space; Script moments preserve those changes. Only the active Character may receive athoughtmoment. Multiple holders can therefore share one Scene with comparable dramatic weight without blending minds inside one passage. Missing or invalid model labels inherit the last valid lens, Single mode cannot switch, and a collective Unit lens grants no individual private thought. The sanitizer enforces this on the regenerable Script projection; it never rewrites Scene canon.
The composed output already carries the medium's grammar: frames as stable tableaux with a locationId
backdrop and a mood note, cast placed in five slots with facing and state, props attached to bearers,
and a moment sequence over the eight beat mechanisms plus the stage-native reaction. Three focal scales
are already modelled — individual (one lit actor), group (speaker plus with), and stage
(ensemble under a caption) — and the off-stage roster already has a home in the cast rail.
That inventory is why this is a scaffold spec and not an engine spec. The composer is close to what a VN needs. What is missing is derivation, identity, and presentation.
3. Entity registers on the stage
The medium distinguishes four presences, and the record already distinguishes them by aspect:
| Register | Record | Aspect | Reuse |
|---|---|---|---|
| Character sprite | Character | 3:4 | Capital — generated once per identity, amortised across every branch |
| Location plate | Location | 16:9 | Capital — one plate, many variants (§5) |
| Prop / artifact | Artifact | 1:1 | Capital — attached to a bearer with with, or standing in the space |
| Event CG | (none) | 16:9 | Consumable — one moment, one bill, no reuse |
The first three compose at render time: a sprite is drawn over a plate, never baked into it. This is the single decision that makes the economics work, and it is already how the renderer resolves entity images. Producing composed art per scene multiplies spend by the branching factor for nothing — the figure is the entity.
The fourth has no home in the model yet. A CG is not an entity image; it is a moment-scoped illustration of one composed instant that will never recur. It therefore belongs on the scaffold, not on an entity — a per-moment reference on the frame's sequence — and it is the only asset class whose cost scales with story length rather than cast size. Rationing it is §4.
4. Deriving staging from deltas
This is the part that does not exist and is the highest-value addition, because it converts the delta layer from context the model reads into decisions the pipeline makes. Four derivations, all deterministic, none requiring a model call:
CG placement. Spend a cutscene where an irreversible delta lands — a thread resolution, an ownership transfer, an entity death or entity-resolution delta, a system delta that closes an option. Not where the prose is loudest. This is the same gate the decision spec uses to qualify a fork, applied to the art budget, and it means the illustrated moments of a route are exactly its consequential ones. A route with no irreversible delta gets no CG and should not: nothing happened.
Expression selection. A character's state is already staged per frame, but which face a sprite wears
should follow two bounded inputs: the frame deltas and state that name that character, and—when present—the
Behaviour Reading produced from the branch-and-moment Entity Interpretation Graph binding. The first says what
changed; the second proposes how this particular character might express it through competing tendencies,
inhibitions, and micro-movements. Factory resolves the semantic request to an approved expression inside the
correct appearance set. This is a presentation choice, not a new delta, a diagnosis, or a model's free guess
at mood.
Plate variant selection. Time, weather, and aftermath variants of a location plate are chosen by world deltas that touch that location — a place burned, occupied, emptied, or rebuilt. A plate that never changes across a route where the world changed is the flattest failure available.
Cast rail as information boundary. The rail already shows who is in play versus waiting. Under a POV (§6) it must show who this seat knows to be in play — the rail is a disclosure surface, not a debug view.
The honest caveat: all four require the composer (or a pure post-pass over the composed scaffold) to receive the deltas addressed to specific frames and moments, rather than as scene-level context. That mapping — delta → frame → moment — is the one new structural thing the scaffold layer needs.
5. Regeneration and identity
Because the scaffold is a projection, it can be regenerated. Because assets are capital, they mostly must not be. The two facts collide, and the resolution is a rule per layer:
- Scaffold: freely regenerable. A new staging of the same scene is a new
ScriptVersion. Nothing is invalidated except the reader's cursor into that scene, which re-resolves. - Sprites and plates: stable by default. Regenerating an entity identity reference changes the person or place a reader has already met. Factory creates versioned appearance and performance descendants and reports their dependants; it never replaces every branch at once by accident.
- CGs: bound to their moment. A CG survives only as long as the moment it illustrates. If the scaffold is regenerated and the moment is gone, the CG is orphaned — and since it cost real money, orphaning should be reported, not silent.
- Voice: cached per line version. A re-read never re-synthesises; editing a line invalidates that line.
Identity across regenerations is the unsolved dependency, and it is worth stating bluntly here because three
documents lean on it: the image call currently sends { prompt, aspect_ratio } and no reference image, so a
character's identity survives only as a stored prose description. Reference conditioning is a prerequisite
for a real sprite set, not a refinement of one (production §identity).
6. POV as a projection, not a filter
Single-POV and multi-POV are the same mechanism at different settings, and the mechanism already exists:
Scenario projects the room per seat, and concealed material is absent from the projection rather than
hidden in the UI. Applied to the VN:
- A POV is a seat. A route read under a POV shows the frames, moments, glossary entries, and rail presences that seat could know. The reader is not trusted to forget.
- Single POV binds the work to one Character lens — the classic structure, and the cheapest to keep coherent. Narrator texture may describe observable space but may not become another mind.
- Multi-POV supports both parallel routes and several holders inside one Scene. The Scene stores the
holders in
povIds, whilepovIdkeeps the opening/default lens backward-compatible. Inside a Scene the boundary is the ordered Beat: one lens per passage, an explicit change between passages, and Script moments that preserve the active holder. The selected holders active in a Scene receive meaningful, comparable weight unless the Scene purpose earns an asymmetry; mechanical turn-taking is not the goal. - Perspective movement is independent of the roster.
povMovementgives the author three stable policies:scene-boundpermits one Character lens per Scene;selective(the default, including legacy records) permits an earned Beat-boundary switch when another holder reveals something unavailable;sharedexpects several present holders to receive meaningful, roughly balanced access inside the Scene. Single-character POV always resolves asscene-bound. Narrator texture may remain inside a Scene without becoming permission to enter an unlisted mind. - A POV switch is a reveal and should be treated as content. It earns its place by unlocking perception,
interpretation, interiority, or dramatic irony unavailable to the prior holder. Ordering constraints are
therefore narrative and technical: the sequence preserves exactly whose knowledge governs each passage.
Prose generation receives both the full Scene roster and the normalized Beat sequence; stale labels inherit
the preceding valid lens and an explicit
nullremains narrator/choral rather than snapping back to the opener. - Perspective is established at birth. The creation wizard keeps the default path light, but its Advanced disclosure exposes the same POV approach, movement, and optional identity direction that Domain Settings later edits. Each paradigm owns a best-fit preset: singular-thinker forms begin tight, multi-thinker forms begin plural, and populated narrative forms remain source-led unless the author says otherwise. Premise suggestion reads the selected paradigm and POV preset, so it proposes material that can actually sustain the intended access pattern rather than decorating a generic premise after the fact.
- Identity inference resolves to the registry. During generated Domain birth the model names exact ids from the Character roster it creates; during corpus extraction the whole-work meta pass distinguishes private narrative access from protagonist prominence, dialogue share, or mere presence. Both paths pass through one deterministic resolver: stale ids are removed, observed Scene lenses are supporting evidence, and restricted modes fall back through anchor Characters only when the evidence is incomplete. The persisted setting always carries real Character ids, so later planning, prose, and Script generation enforce the same knowledge boundary.
The rule that keeps this from decaying: a clearer presentation is never a reason to leak. If the scaffold under a seat would be incoherent without concealed material, the answer is a different scaffold for that seat, not a peek.
7. The ledger
The local VN experiment audit 🔒
demonstrates candidate moment/frame manifests and contextual CG sequencing outside the shipped scaffold.
Integration should reuse that evidence while extending Script and Stageplay's permanent seams; it must
not promote the lab compiler into a parallel canonical scaffold or renderer.
| Requirement | State | Where |
|---|---|---|
| Summary + typed deltas as canon | shipped | Scene, WorldExpansion deltas |
| Scaffold generated from brief + plan + prose | shipped | generateSceneScript, SCRIPT_COMPOSE_SYSTEM |
| Deltas present in the staging context | shipped | <scene-brief> (sceneContext, world-state stripped) |
| Point-in-time knowledge + ordered Beat/moment POV boundary | shipped | deriveLogicRules, BeatPlan, compose prompt, Script sanitizer |
| Scaffold versioned with branch isolation | shipped | Scene.scriptVersions: ScriptVersion[] |
| Frames · slots · facing · state · props · moments | shipped | ScriptFrame / ScriptCastMember / ScriptMoment |
| Focal scales (individual · group · stage) + cast rail | shipped | compose prompt + renderer |
| Sprite-over-plate composition at render time | shipped | entity imageUrl by aspect |
| Per-seat projection with concealment | shipped | projectForSeat (Scenario) |
| Delta → frame → moment addressing | build | new; the dependency for every derivation below |
| CG as a moment-scoped asset class | build | new; no home in the model today |
| Derived CG placement at irreversible deltas | build | new (pure) |
| Derived expression / plate-variant selection | build | new (pure) |
| Reference-conditioned entity identity | build | extends the image lane |
| Branch-and-moment latent-model binding in the scaffold package | build | pins the interpretation used for dialogue and expression |
| Factory resolution of semantic expression needs to approved renditions | build | extends the asset library without making assets canonical state |
| POV-projected scaffold for a reading seat | build | extends projectForSeat to the reader |
| Orphaned-CG reporting on regeneration | build | new |
8. Open questions
- When reader-seat projection ships, may one seat traverse several ordered Character lenses, or does a published reading bind each lens sequence to a distinct seat?
- Does delta→moment addressing come from the composer (a model call that returns it) or from a pure pass that matches declared deltas against staged moments? The pure pass is cheaper and auditable; the composer knows more.
- May a scene carry more than one published scaffold at once — one per POV — or is a POV scaffold resolved the way branch versions already are?
- When an entity image is regenerated, is prior art retained per branch (so an old route still looks like itself) or replaced globally?
- Does a CG belong to the scene, the branch, or the route? Reuse across sister branches is either thrift or a lie, depending on whether the moment is genuinely shared.