Episode-to-Syuzhet Rendering¶
Status: Active design reference Scope: The rendering phase that transforms an invoked episode and its current namespace into narrative, dialog, fragments, presentation hints, and downstream content requirements.
Implementation status: Phase 10.1 provides the text-only floor:
TextRenderSession renders bounded recursive Jinja text against a
PhaseCtx namespace and carries ephemeral discourse state. It is not yet a
general content request or JOURNAL adapter. Phase 11 adds the text-specific
tangl.story.presentation.render_text_as(...) invocation seam: it selects a
str source through story_dispatch, exposes that resolver to recursive
text templates, and ships replaceable presence defaults. It remains separate
from fragment and media products. Phase 12 adds one credential-owned
document_description adapter: an identity document resolves its bound
subject through its packet manager and recursively projects that presence,
without interpreting credential validity or rendering a packet.
Phase 13 adds a credential-owned inspection_description adapter that
recursively composes a packet’s ordered identity and ordinary documents, with
explicit child bindings and no JOURNAL, media, or disposition interpretation.
Phase 14 makes credentials the first vertical JOURNAL consumer: game-owned
arrival and packet templates invoke those text adapters through the live phase
context, then emit attributed ContentFragment values before the existing
candidate, packet-zone, and document-piece surfaces. The templates disclose
only ordinary appearance and visible documents; findings and disposition remain
on their committed interaction paths. Phase 15 converges packet prose and
targetable credential pieces on the packet manager’s ordered components and
the same document_description adapter. Scenario-authored complete document
text remains an explicit replacement, while compatibility-only visible items
such as baggage remain separate pieces. Phase 16 adds one immutable, typed
issuer-attestation observation to the same recursive projection: profile-owned
neutral wording becomes both document prose and a structured visible_parts
entry on the corresponding piece. A complete authored document replacement owns
its content and does not append generated parts; media composition remains
deferred. Phase 17 adds a second concrete validity observation and a narrow
union of those two heterogeneous leaves. The packet/document projection now
proves ordered text and structured interactive evidence are the same transient
presentation product; media integration remains a separate next discussion.
Purpose¶
The rendering phase transforms the currently invoked episode—its block, story state, entities, mechanics, and relationships—into a realized syuzhet: narrative, dialog, visual descriptions, journal fragments, media requirements, and other presentation products.
The engine does not require one universal renderer. It supplies the bounded namespace, stable referents, lifecycle timing, dispatch, and provenance that specialized content adapters need.
episode + phase-assembled namespace + presentation request
-> content adapter
-> typed content product
-> journal or another downstream adapter
The rendering namespace¶
Before rendering, the phase pipeline assembles the namespace valid for this step. It may contain:
the current block, scene, encounter, and selected action;
graph-owned actors, objects, locations, and relationships;
mechanic state such as outfits, credential packets, vehicles, or markings;
world-, scenario-, block-type-, and block-instance content contributions;
narrator knowledge, discourse focus, locale, and ephemeral render-session state;
scoped catalogs, templates, and adapter hooks.
The namespace is bounded by the current story and world authorities. A renderer must not search process-global state or reach into unrelated worlds.
Content requests and adapters¶
A request identifies a target and presentation role:
target: person
aspect: presence_description
target: packet
aspect: inspection_description
target: event
aspect: narrative
target: speaker
aspect: dialog_line
Different adapters should retain typed results rather than converge on an untyped universal result:
render_text(...) -> str
render_fragments(...) -> list[BaseFragment]
adapt_media_spec(...) -> MediaSpec
resolve_media(...) -> MediaRIT
An author-facing render_as(...) may provide pleasant common vocabulary, but it
delegates to the appropriate typed adapter.
Adapters share namespace access, referents, recursion, and provenance. They do not need to share a template language, traversal algorithm, intermediate representation, or output type.
Rejinja as a humane narrative default¶
Rejinja is a simple textual implementation of this pattern. It recursively evaluates returned templates in the same rendering environment until the content bottoms out.
A room description can invoke its atmosphere, space, and occupants. Each occupant can select a known or anonymous nominal and delegate appearance to their look, outfit, expression, and visible markings.
{{ room.feel() }}
{% for person in room.people %}
{{ person | render_as("presence_description") }}
{% endfor %}
The text adapter may maintain a mutable DiscourseContext within the namespace
for:
the most recently mentioned subject;
bare pronoun resolution;
narrator familiarity;
discourse focus;
tense, point of view, and locale;
state carried between consecutively rendered segments.
That is renderer-session state, not mechanic state. Persistent narrator knowledge can be promoted explicitly when required.
Rejinja is a friendly authoring tool, not the blessed representation for all content. A different runtime may use another template engine or no template engine at all. Portability should be defined by behavioral fixtures, not exact Jinja internals.
Other valid adapters¶
The same rendering contract supports fundamentally different implementations:
A graph visitor selects content nodes and assembles ordered syuzhet fragments.
An NLP adapter operates on a parsed document tree, replacing referents and changing point of view, tense, or conjugation before relinearizing prose.
A media adapter assembles a structured
MediaSpec.A voice adapter selects recorded lines or produces a TTS specification.
An animation adapter emits a timeline, scene graph, or backend-specific plan.
Outputs can feed later adapters:
story state
-> visual description
-> image specification
-> selected/generated RIT
-> MediaFragment
dialog intent
-> realized line
-> voice-line requirement
-> recording or TTS result
-> audio fragment
Media adapters should independently visit the same semantic objects; they should not parse flattened narrative prose to reconstruct them.
Mechanics versus presentation¶
Mechanics supply semantic truth:
facts and traits;
durable identities and references;
component relationships;
visible or discoverable state;
optional authored content hooks.
Adapters decide presentation:
grammar and syntax;
familiarity and nominal selection;
point of view and pronouns;
visibility and suppression;
templates, styles, and output formats.
For credentials, subject UUIDs determine holder identity. A narrative renderer
may describe the bearer and visible photograph, but it must not infer or reveal
wrong holder, invalid, or the expected disposition. Those remain mechanic
interpretations exposed only through the appropriate interaction.
Credentials as a recursive-rendering proof¶
The entire encounter can begin with two small block fragments:
A {{ person | render_as("presence_description") }} steps up to the window.
They lay their documents out for inspection:
{{ packet | render_as("inspection_description") }}
The adapter recursively follows:
packet
-> documents
-> visible document parts
-> portrait
-> referenced bearer
-> look
-> hair
-> outfit
-> expression
-> visible markings
Authors can replace the whole encounter text, the packet description, one document, one document part, or one appearance component without changing the block structure or mechanics.
Static and dynamic materialization¶
Content availability is an independent axis:
Class |
Build or authoring work |
Runtime work |
|---|---|---|
Static |
Finished artifact already exists |
Return it |
Static-selected |
Several finished alternatives exist |
Select one |
Generated-static-selected |
Procedural space is populated during compilation or curation |
Select one |
Generated-dynamic |
Content depends on this playthrough’s live namespace |
Reuse or create it |
A finite dynamic space can be converted into a selectable space by enumerating its parameter cross-product:
gender x glasses x hair x skin x reference face
-> synthetic namespaces
-> adapted portrait specifications
-> generated and reviewed artifacts
-> tagged RIT inventory
A world can substitute another compatible inventory—ink-punk, paper-doll, illustrated, photographic—to reskin the entire experience. Catalog compatibility depends on stable semantic selector dimensions, not filenames or prompt text.
The caller still asks only for the appropriate RIT. The resolver decides whether to return a direct artifact, select an existing one, reuse a non-stale cached result, invoke a forge, or return a pending or fallback result.
Near-term implementation implication¶
The next slice should remain narrative-focused:
Establish the smallest typed content-request and adapter-invocation seam using
PhaseCtxand existing dispatch.Exercise literal, templated, and recursively composed narrative.
Prove one structural journal/syuzhet adapter alongside Rejinja so Jinja does not become the universal contract.
Render a simple presence manager, then an assembled
HasLook.Render one credential document, then a multi-document packet.
Preserve source and entity attribution in final journal fragments.
Defer actual media selection, generation, SVG or raster concerns, and forge overhaul.
The north star is a flexible episode-to-syuzhet transformation space. Credentials and presence are demanding examples of it, not owners of bespoke presentation systems.