Chapter 2
The graph: entities, edges, tags
Underneath everything, a TropeLang story is a graph: a set of nodes (the things in the story), edges (how they relate), and tags (what each thing is like). It is a temporal graph — facts are stamped into beats and scenes, and the store is append-mostly, so the engine can always look back at what was true earlier. The point of the model is that once a story is a graph, "find the tropes" becomes "find the shapes," which a machine can do.
Entities are nodes
A node is declared with a keyword and a name. There are five kinds:
char (a character or actor), set (a place or time),
obj (an object), evt (an event — something that happens), and
arc (a thread or the story itself, which is both a container and a node you can tag).
That's the whole vocabulary of kinds; everything more specific is expressed with tags.
char vale [+Protagonist] [+Grifter]
char renn [+Antagonist] [+Collector]
char dossey [+Ally]
obj ledger
vale -- renn : "the appraisal"
vale > ledger
dossey -- vale : "old partners" Composition over inheritance
In vale [+Grifter], the tag is a component the node carries, alongside
[+Protagonist] — there is no Grifter class that vale is an
instance of. Tags stack freely: a character can be a grifter, a mentor, and grieving at once, with no
class hierarchy to manage. Specialization comes from adding tags, never from subclassing. (Tags do form
an ontology among themselves — one tag can imply others — but that is a relationship between
tags, covered in Chapter 3.)
Edges are relationships
An edge connects two nodes, optionally annotated with a quoted phrase. The operator says what kind of tie it is:
--associates / knows / relates (vale -- renn)>possesses / holds / carries (vale > ledger)->causes / leads to / targets@located at / occurs at><mutual conflict (the one symmetric operator)
Most are directed. Prefixing with ! severs a tie — vale !-- renn breaks the
association, the moment a friendship ends or trust dies. Because the store never truly deletes, that
severance is itself a recorded fact: the engine knows there was a bond, and that it broke.
Time is built in
Facts live inside nested temporal containers — beat (a single action), scene
(a run of beats), act, and arc. Order matters: a setup in beat 1 and its
payoff in beat 2 are different moments, and recognition can reason about that gap — "the hero never did
disarm the bomb."
This is why a .trl file has two distinct phases: the declarations up top
(the cast and props you just met) and the temporal event log below (the
scenes and beats, in reading order). Everything the timeline names already exists above it — a
convention worth its own page in the style guide.
scene the_appraisal {
beat 1 {
char vale [+Grifter]
char renn [+Mark]
vale -- renn : "introductions"
}
beat 2 {
evt the_switch [&Deceives(agent=vale, target=renn)]
}
}
Every name here — Protagonist, Grifter, Collector,
Deceives — comes from a shared vocabulary the engine understands. How that vocabulary works,
and how one tag can pull in a dozen others, is next.