Interaction Vocabulary¶
Status: Directional contract for fragment-era interactivity Authority: Story owns interaction meaning; service transports fragments; clients render affordances and gracefully degrade unsupported widgets. Current render-contract authority:
STORYTANGL_WIDGET_VOCAB.md, with repo-current status tracked inWIDGET_CONTRACT_RECONCILIATION.md.
StoryTangl is not trying to turn every client into a general game engine. The engine and handlers own legality, state transitions, scoring, and hidden state. The client-facing vocabulary should instead describe what kind of player action is being requested, what state is legible, and how a renderer may collect a payload.
This note remains useful as interaction-design rationale. The unified widget
vocabulary supersedes it for portable fragment widgets, ProjectedState
section values, tier tags, and conformance fixture planning.
The current choice fragment is the minimal form of this contract. The broader
vocabulary below describes the features we are building toward as fixtures and
widgets grow beyond plain story choices.
Design Split¶
Do not collapse these four vocabularies:
Layer |
Question |
StoryTangl owner |
|---|---|---|
Rules model |
What is legal and what changes state? |
|
Interaction model |
What action shape is requested? |
journal fragments and choice |
Presentation profile |
What can this client render well? |
web, CLI, Ren’Py, future clients |
Narrative projection |
What does this mean in story terms? |
prose, dialog, outcome routing |
The renderer consumes interaction requests. It does not infer legal moves from rules and it does not execute rules locally.
Client Capability Floor¶
The gateway API is the only capability every client can be assumed to have:
the service-facing create/update/resolve-choice and info-read operations
described in docs/src/design/service/FRAGMENT_STREAM_CONTRACT.md. Every
interaction shape must therefore degrade to text, numbered choices, and simple
prompts.
The CLI floor is:
render prose and visible state as readable text
render choices in order, including locked choices and reasons
collect
text,quantity, and visiblepieceselections through promptssubmit the same
choice_idplus payload that a rich widget submitsoptionally submit raw command text to a reserved interpretation choice
Web, Ren’Py, Godot, or tabletop-like clients may render richer controls, but those controls are affordances over the same visible fragments. They are not a client-side rules runtime.
Core Terms¶
Surface¶
A surface is a renderable interaction area: a choice list, hand, board, document packet, piece pool, shop, map, status panel, or resource economy.
In the fragment stream, surfaces are usually represented by group fragments
plus member fragments. group_type="scene" is the current top-level shell;
future groups such as zone, hand, board, or packet should follow the
same id-reference pattern.
Entity¶
An entity is a renderable or targetable object: card, piece, document, die, credential, clue, actor, location, board cell, generator, or inventory item.
At the client boundary, entities should appear as fragments or group members
with small capability fields rather than as deep class hierarchies. A card-like
piece might carry kind, display_state, zone_ref, labels, and hints. The
handler still owns the live graph object.
Zone¶
A zone is a container or locus for entities: deck, discard, hand, field, bag, wallet, credential packet, shop, queue, board cell, or inventory.
Zones matter because many interactions are target-constrained:
accepts:
kind: pieces
min: 1
max: 1
constraints:
target_zone_ref: f-zone-player-hand
Decision legibility requires the referenced zone to be renderable in the current shell when the choice is open.
Affordance¶
An affordance is the user-facing action offer: select, inspect, reveal, play, discard, buy, spend, take, put, arrange, confirm, cancel, pass, stand, hit, deny, arrest, or ask.
Today, ChoiceFragment is the concrete affordance carrier. text, available,
blockers, accepts, and ui_hints describe how the client can present and
collect the action.
Move¶
A move is the handler-facing committed action. It is what resolve_choice or a
future interaction endpoint receives after the renderer collects any payload.
Keep the distinction sharp:
Affordance: “Show a card from your hand.”
Payload:
{piece_ids: ["rust-map-card"]}Move: handler-valid committed action derived from choice id plus payload
Procedure¶
A procedure is a structured interaction cycle: single choice, best-of-N contest, push-your-luck, inspection loop, drafting, bidding, trick, combat round, shop transaction, resource tick, puzzle attempt, or timed challenge.
Procedures are handler/runtime concepts. Clients should see their state through fragments: status, zones, pieces, choices, blockers, outcomes, and events.
Resource And Transaction¶
Resources are typed quantities: coin, health, time, action points, suspicion, influence, generator output, piece reserves, or production inputs.
Resource-loop interactions should expose ledgers and transactions as renderable state rather than burying them in prose. The handler owns arithmetic; the client shows current quantities, available purchases, unavailable reasons, and audit events.
Payload Contract¶
choice.accepts describes the payload shape requested by an open affordance.
It should stay explicit enough that a CLI can ask for the same value without a
special widget library.
Near-term accepted shapes:
|
Payload |
CLI rendering |
|---|---|---|
|
|
numbered choice |
|
|
line prompt with optional validators |
|
|
integer prompt with min/max and reason text |
|
|
numbered entries from a visible target zone |
|
|
command prompt submitted to interpretation edge |
Later, compose can combine those simple parts:
accepts:
kind: compose
parts:
- role: amount
accepts: {kind: quantity, min: 1, max: 7, unit: coin}
- role: target
accepts:
kind: pieces
min: 1
max: 1
constraints: {target_zone_ref: z-room}
The corresponding payload should remain explicit:
{
parts: {
amount: {quantity: 2},
target: {piece_ids: ["guard"]}
}
}
The client may enforce simple visible validators to avoid bad submissions, but backend validation is authoritative.
Command Resolution¶
Classic IF-style command input is a second affordance over the visible action surface, not a requirement that every client embed a language parser.
The preferred model is backend-authoritative:
The runtime emits ordinary visible choices for the turn.
If raw command input is authorized, the runtime also emits a reserved choice such as
edge_id="interpret_command"withaccepts.kind="raw_command".A client may render that choice as a command bar or as a CLI prompt.
A capable client may use advisory grammar hints for autocomplete, preview, and piece highlighting.
On submit, the backend resolves or rejects the command and returns a normal
RuntimeEnvelope.
Grammar hints are optional and must be treated as denormalized convenience metadata derived from the visible turn surface. They must not contain hidden verbs, nouns, aliases, or targets.
The first web-client hint shape is deliberately advisory:
metadata:
grammar:
examples: ["take lamp", "open door"]
verbs: ["take", "open"]
nouns: ["lamp", "door"]
Clients can use this for placeholders or autocomplete. A CLI or minimal client
can ignore it and submit raw text to interpret_command.
When a raw command does not advance the story, the backend should return a
renderable feedback fragment. A future interpretation fragment can cover:
ambiguousunknown_verbunknown_nounblockedimpossiblevalidation_failed
A client without a dedicated interpretation renderer can show the message as ordinary content.
Outcome¶
Outcome is broader than win/lose/draw. A client may need to render success, failure, partial success, mixed result, continue, abort, route chosen, or terminal state. Current game enums remain useful, but fragment-era clients need result fragments or user events that explain what became true.
Record¶
RoundRecord is the mechanics-specific form of a more general interaction
ledger. Future interaction records should preserve request id, selected move,
public before/after state, result, and notes so replay, diagnostics, generated
prose, and tests can share the same audit trail.
Interaction Classes¶
These classes describe effect and rendering expectations:
Class |
Meaning |
|---|---|
observe |
Reveal or present information without changing world state. |
inspect |
Examine a target; may reveal hidden fields or consume an action. |
select |
Choose one or more alternatives without necessarily committing. |
commit |
Irreversible or state-changing decision. |
manipulate |
Move, take, spend, place, arrange, buy, or discard. |
confirm |
Acknowledge a result or advance a phase. |
query |
Ask for explanation, hint, rule, or status. |
Plain story choices are commit or confirm affordances with no additional
payload. Card, piece, credential, and resource interactions add target and
constraint metadata.
Visibility¶
Hidden information needs a first-class presentation contract. Do not encode all non-public information as simply absent.
Useful visibility states include:
private to handler
known to narrator
visible to player
visible to owner only
inferable or hinted
revealed after commit
intentionally misrepresented
remembered in history but no longer on the surface
The fragment stream should expose only what the client may render. Narrative composition may know more than the renderer, but that knowledge must not leak through interaction payloads.
Graceful Degradation¶
Every rich interaction should degrade to choices plus readable state.
Examples:
A web card UI may render hand zones and selectable pieces.
A CLI may render the same zone as a numbered list and collect a piece id.
A CLI command prompt may submit raw text to
interpret_commandwithout local grammar support.A Ren’Py view may show a menu plus a small status panel.
A client without zone support may show the unknown group fallback but should still let ordinary choices work.
ui_hints can request icons, hotkeys, widget families, or layout preferences,
but hints are advisory. accepts and blockers are the portable contract.
Mechanic Coverage Targets¶
The vocabulary should cover the current mechanics survey without creating a client-side rules runtime:
Classic sandbox IF: visible location state, exits, inventory, blocked actions, command text, and backend interpretation feedback.
Rock-paper-scissors: simultaneous or staged commit, opponent tell, dominance relation, round result, best-of-N scoring.
Blackjack/21/22: deck, hand zones, hidden dealer state, hit/stand, threshold and bust result.
Nim and piece games: shared pools, take/put quantity constraints, shrinking legal moves, terminal policy.
Bag RPS and bidding: commit a typed quantity bundle from a reserve.
Credentials/Papers-Please-like scenes: inspect documents, reveal mismatches, request search, pass/deny/arrest disposition.
Incremental/resource loops: wallets, generators, costs, production ticks, and transaction history.
These are fixture and widget targets, not a mandate to add parallel game logic to the client.
Near-Term Implementation Targets¶
The next web/client work should stay small and contract-driven:
Keep
zoneandpiecewidgets small and generic; do not add game-specific board logic to the client.Add
ChoiceInputViewforpick,text,quantity, andpieces.Add canonical fixtures for a quantity interaction and a small sandbox-like turn with visible room/inventory zones.
Keep raw command input as a reserved
raw_commandchoice that submits{text}to the backend; grammar hints are optional presentation metadata.Add
interpretationrendering after the backend shape exists; fallback to content is acceptable before then.Add
composeafter the simple payload widgets are stable.Add browser E2E only after payload widgets and command feedback settle enough that tests will not cement an interim UI shape.
Non-Goals¶
No universal rules DSL at the client boundary.
No client-side game handler or scoring runtime.
No new fragment hierarchy separate from
BaseFragmentandtangl.journal.fragments.No hard dependency on one UI framework’s widget taxonomy.
No requirement that every client render every rich surface.