You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
id: design: semantic cell/tag presentation model — one tagged state, many renderers #6010
Part of the worlds design program: #5955. Wave-2 follow-up from the prior-art research (Lane B item 37); parent topic issue: #5970. Research source: workspace/scratch/worlds-capabilities-brief-2026-11-04/prior-art-research.md.
The Dwarf Fortress rule
DF's presentation history is the canonical capability-tier story: ASCII glyphs (CP437) → community tilesets replacing glyphs 1:1 → full graphical tiles — the simulation never changed, only the presentation mapping, and the ASCII tier remains a complete, playable representation. The design rule: world state must be expressible as a grid of typed cells with semantic tags (creature/material/state), and each client tier renders tags at its fidelity. Drew's "blue cape with sparkles → blue letter C / cape emoji" is exactly a tag→glyph fallback rule.
Scope
The tag vocabulary: what a cell carries (kind, owner-visible identity, state flags) such that ASCII, tiles, and rich renderers can all be complete.
Verified fresh 2026-11-05: issue #6010 is OPEN, no hard-stop labels, no other holder. The ticket's load-bearing premise — presentation-is-data already shipped — holds at current main: the module is live in (e.g. consumes ; guests supply legacy UTF-8 text or the versioned Screen cell buffer, never raw escapes/HTML/CSS). So the tagged-state model is an extension of a shipped surface, not greenfield.
Dependency map (all verified OPEN just now): parent program issue #5955; parent topic issue #5970 (user presentation assets — the fallback chain this spec feeds); capability tiers issue #786 (Phase 13 F13.4, in-band terminal detection). Sibling issue #6011 (terminal rendering ladder) is the per-tier rendering-obligation consumer — the tag vocabulary v1 and the ladder should be specified against each other, vocabulary first or together.
Execution shape (for the follow-up task)
Write the tagged-state spec per the ticket's Done-looks-like: cell fields (kind, owner-visible identity, state flags), tag vocabulary v1, and per-tier rendering obligations — with the Dwarf Fortress rule as the invariant: every tier (ASCII → tiles → rich) is a COMPLETE representation of the same tagged state; world code declares tags, the fallback mapping lives in the presentation layer, never in world code. Name the oracle: a worked example cell set (including Drew's blue-cape-with-sparkles case) rendered at every tier with no information loss at the lowest tier. Land as a design record on the issue or a docs PR under the standing rules.
Design boundary. The world owns one canonical tagged state; renderers are projections of it, not sources of game semantics. Changing from ASCII to tiles or rich graphics must not change simulation state. The world declares semantic tags; the trusted presentation layer owns tag-to-glyph/asset mappings and authored fallbacks. This is a proposed logical model, not a silent wire-format change.
Cell model and vocabulary
A cell is a row-major grid position containing zero or more ordered layers (back to front), so terrain, an entity, and an effect can coexist. Each layer has:
kind: terrain | entity | item | effect | marker
primary_tag: namespaced semantic tag
tags: set of additional semantic/appearance tags
identity?: { world_local_id, owner_label? }
state_flags: set of positive state-tag IDs
kind is the stable cross-world role; primary_tag gives its concrete meaning. identity is optional for non-identifiable layers. IDs are scoped to a world instance (not global account IDs); owner_label is a viewer-specific, plain-text projection. The host must redact labels before sending a view to anyone not entitled to see them. State flags are affirmative facts: absence means “not asserted/unknown,” not false.
Appearance/composition tags include core.appearance.cape, core.color.blue, and core.effect.sparkle (an effect may also be a separate layer when it needs its own position/lifetime).
Common state flags: core.state.alive, dead, hostile, wounded, selected, concealed (all fully qualified as core.state.*). Worlds may add stable world.<world-id>.* tags; they may not redefine a core.* meaning.
Tags are inert identifiers, never CSS, HTML, ANSI, shader source, or executable code. A client that does not recognize a tag must retain it in a safe textual fallback/description rather than silently dropping it. The world/presentation registry supplies a plain-text meaning for display; unknown namespaced IDs remain inspectable.
Rendering obligations and composition
For a recipient, first form the authorized tagged-state projection (including identity redaction), then resolve it through the presentation/asset layer, then emit through the selected client tier. Every tier must be a total mapping for the tags it receives. It may reduce visual fidelity, but it must preserve the recipient-visible kind, identity, tags, and state flags in the rendered view or a deterministic legend/accessible text description. If glyphs collide, layers overlap, or a feature has no asset, use a declared secondary mark, legend, or text description; do not guess from pixels or omit the fact.
This composes with the sibling designs rather than duplicating them: #5970 owns asset IDs, capability policy, and the authored rich → static/substitute → sprite/glyph fallback (per-world-disabled filters use their declared fallback); #6011/#786 select the client/protocol capability rung. That rung changes how pixels are transported, not what a tag means. ASCII and symbols receive the same semantic mapping effort as image tiers. The deterministic text projection is the lowest-tier escape hatch; labels and descriptions must be escaped as plain text by trusted presentation code.
Worked example and oracle
At (2,2), define one layer with kind=entity, primary_tag=core.entity.player, owner-view identity {world_local_id: p-1, owner_label: You}, tags {core.appearance.cape, core.color.blue, core.effect.sparkle}, and state {core.state.alive}; surround it with floor and wall cells. Render that same cell set as:
Rich: player sprite, blue-cape asset, sparkle effect; accessible description says “You, player; blue cape; sparkle; alive.”
Tiles: the corresponding Dwarf-Fortress-style player/cape tile and sparkle mark; legend/description retains the same facts.
Symbols: blue C plus * where color/symbols are supported; legend identifies C as You/player with blue cape and * as sparkle.
ASCII-only: map glyph C plus a plain-ASCII legend such as C at 2,2 = You / player; blue cape; sparkle; alive (no reliance on color or emoji).
The oracle is equality of the normalized recipient-visible semantic projection before rendering and after decoding the rendered view plus its legend/description: same kind, entitled identity, all tags, and all state flags at every tier. A test must fail if the lowest tier only emits C and loses cape/sparkle/identity, even though the glyph itself is drawable.
Fit with the shipped presentation DTO
On current main, WorldSurface is Screen or Text. ScreenSurface is version/dimensions plus row-major cells; ScreenCell currently contains only symbol, fg, bg, bold, and columns. ScreenPatch/CellRun are deltas over those concrete visual cells. Therefore the tagged model belongs upstream of the presenter that produces WorldSurface; do not pretend these existing cells already carry semantic tags or overload patches with them. Any future wire representation for tagged state needs an explicit versioned protocol decision. The existing safe text projection can carry the lowest-tier legend.
Acceptance check: a shared example fixture is rendered through each tier and the semantic-projection oracle above passes, including for an unknown tag and a viewer who is not entitled to an identity label. This makes “one tagged state, many renderers” executable without requiring every tier to use the same glyph or asset.
Activity
developing-today commentedon Nov 5, 2026
CODE-1676
dezren39 commentedon Nov 5, 2026
🤖 Agent note · picked up
developing-today commentedon Nov 5, 2026
🤖 Agent note · done
Triage verdict — LIVE, premise holds, spec routed
Verified fresh 2026-11-05: issue #6010 is OPEN, no hard-stop labels, no other holder. The ticket's load-bearing premise — presentation-is-data already shipped — holds at current main: the module is live in (e.g. consumes ; guests supply legacy UTF-8 text or the versioned Screen cell buffer, never raw escapes/HTML/CSS). So the tagged-state model is an extension of a shipped surface, not greenfield.
Dependency map (all verified OPEN just now): parent program issue #5955; parent topic issue #5970 (user presentation assets — the fallback chain this spec feeds); capability tiers issue #786 (Phase 13 F13.4, in-band terminal detection). Sibling issue #6011 (terminal rendering ladder) is the per-tier rendering-obligation consumer — the tag vocabulary v1 and the ladder should be specified against each other, vocabulary first or together.
Execution shape (for the follow-up task)
Write the tagged-state spec per the ticket's Done-looks-like: cell fields (kind, owner-visible identity, state flags), tag vocabulary v1, and per-tier rendering obligations — with the Dwarf Fortress rule as the invariant: every tier (ASCII → tiles → rich) is a COMPLETE representation of the same tagged state; world code declares tags, the fallback mapping lives in the presentation layer, never in world code. Name the oracle: a worked example cell set (including Drew's blue-cape-with-sparkles case) rendered at every tier with no information loss at the lowest tier. Land as a design record on the issue or a docs PR under the standing rules.
8 remaining items
developing-today commentedon Nov 5, 2026
Correction to the triage note above (task 10376): several code identifiers were stripped from that comment in posting. Restored verbatim:
world_presentation, live inpkgs/id.world_session/types.rs, which usescrate::world_presentation::ScreenPatch.All other statements in the note above stand as written.
dezren39 commentedon Nov 5, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 5, 2026
🤖 Agent note · state: handed off
dezren39 commentedon Nov 5, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 5, 2026
Tagged-state spec v1 (task 10548)
Design boundary. The world owns one canonical tagged state; renderers are projections of it, not sources of game semantics. Changing from ASCII to tiles or rich graphics must not change simulation state. The world declares semantic tags; the trusted presentation layer owns tag-to-glyph/asset mappings and authored fallbacks. This is a proposed logical model, not a silent wire-format change.
Cell model and vocabulary
A cell is a row-major grid position containing zero or more ordered layers (back to front), so terrain, an entity, and an effect can coexist. Each layer has:
kindis the stable cross-world role;primary_taggives its concrete meaning.identityis optional for non-identifiable layers. IDs are scoped to a world instance (not global account IDs);owner_labelis a viewer-specific, plain-text projection. The host must redact labels before sending a view to anyone not entitled to see them. State flags are affirmative facts: absence means “not asserted/unknown,” not false.The v1 core vocabulary reserves these roles/tags:
kind:terrain,entity,item,effect,marker.core.terrain.floor|wall|water;core.entity.player|creature|npc;core.item.wearable|tool|consumable;core.effect.sparkle|fire|poison;core.marker.goal|cursor.core.appearance.cape,core.color.blue, andcore.effect.sparkle(an effect may also be a separate layer when it needs its own position/lifetime).core.state.alive,dead,hostile,wounded,selected,concealed(all fully qualified ascore.state.*). Worlds may add stableworld.<world-id>.*tags; they may not redefine acore.*meaning.Tags are inert identifiers, never CSS, HTML, ANSI, shader source, or executable code. A client that does not recognize a tag must retain it in a safe textual fallback/description rather than silently dropping it. The world/presentation registry supplies a plain-text meaning for display; unknown namespaced IDs remain inspectable.
Rendering obligations and composition
For a recipient, first form the authorized tagged-state projection (including identity redaction), then resolve it through the presentation/asset layer, then emit through the selected client tier. Every tier must be a total mapping for the tags it receives. It may reduce visual fidelity, but it must preserve the recipient-visible
kind, identity, tags, and state flags in the rendered view or a deterministic legend/accessible text description. If glyphs collide, layers overlap, or a feature has no asset, use a declared secondary mark, legend, or text description; do not guess from pixels or omit the fact.This composes with the sibling designs rather than duplicating them: #5970 owns asset IDs, capability policy, and the authored rich → static/substitute → sprite/glyph fallback (per-world-disabled filters use their declared fallback); #6011/#786 select the client/protocol capability rung. That rung changes how pixels are transported, not what a tag means. ASCII and symbols receive the same semantic mapping effort as image tiers. The deterministic text projection is the lowest-tier escape hatch; labels and descriptions must be escaped as plain text by trusted presentation code.
Worked example and oracle
At
(2,2), define one layer withkind=entity,primary_tag=core.entity.player, owner-view identity{world_local_id: p-1, owner_label: You}, tags{core.appearance.cape, core.color.blue, core.effect.sparkle}, and state{core.state.alive}; surround it with floor and wall cells. Render that same cell set as:Cplus*where color/symbols are supported; legend identifiesCas You/player with blue cape and*as sparkle.Cplus a plain-ASCII legend such asC at 2,2 = You / player; blue cape; sparkle; alive(no reliance on color or emoji).The oracle is equality of the normalized recipient-visible semantic projection before rendering and after decoding the rendered view plus its legend/description: same kind, entitled identity, all tags, and all state flags at every tier. A test must fail if the lowest tier only emits
Cand loses cape/sparkle/identity, even though the glyph itself is drawable.Fit with the shipped presentation DTO
On current main,
WorldSurfaceisScreenorText.ScreenSurfaceis version/dimensions plus row-major cells;ScreenCellcurrently contains onlysymbol,fg,bg,bold, andcolumns.ScreenPatch/CellRunare deltas over those concrete visual cells. Therefore the tagged model belongs upstream of the presenter that producesWorldSurface; do not pretend these existing cells already carry semantic tags or overload patches with them. Any future wire representation for tagged state needs an explicit versioned protocol decision. The existing safe text projection can carry the lowest-tier legend.Acceptance check: a shared example fixture is rendered through each tier and the semantic-projection oracle above passes, including for an unknown tag and a viewer who is not entitled to an identity label. This makes “one tagged state, many renderers” executable without requiring every tier to use the same glyph or asset.
dezren39 commentedon Nov 5, 2026
🤖 Agent note · done