Mova0.5 pre-release⌕
☰
home / docs / world — worlds, schools and students

world — worlds, schools and students

world is where Mova keeps the world of a text and where its students live: the course pupil of the primary schools, the maths student, the readers of fables and tales, and the self-directed learners (induce, selfplay). This README describes the tale world in detail, with its measurements; Architecture gives the overview.

The tale world

A prototype of the SLM (small language model) module from Smart annotator — layers, context, memory: «Tale world: entities, registries, cursor», «Minimal objects», «Immutable states — the alternative variant», «World-change language», «Non-linear narrative». The change language has three layers, run by one executor: v1, the base (checked on Aesop fables); v2, intentions, beliefs and more (checked on FairytaleQA); v3, the non-linear narrative.

Change language v2 and FairytaleQA, in brief (2026-09-26):

Change language v3, non-linear narrative, in brief (2026-09-26):

How it works

Model (world.rs, types.rs)

Change language (lang.rs)

One command per line, anchors ^sN at the end. A line spanning two sentences has two anchors: ^s3 ^s4.

commandwhat it does
cursor [chapter= para= sent=] [at=L] [time=]where the narrative moved
loc L type= [class=] [name=] [in=L]a location, nested in another via in=
obj O type= [class=] [name=] [at=L|C]an object in a place or held by a character
char C type= class= [name=] [at=L] [attributes…]a character
move C to=L / move O to=L|C [cause=E]change of place: a character goes; an item fell or was picked up
have C O [cause=E]first possession of an item that has no place yet
give C O to=C [cause=E]handing over an item (added)
set X key=value … [cause=E]an attribute (added)
rel R kind= a=C b=C [cause=E] · rel R kind= cause=E · rel R fact=Ea relation: start, change, fact (added)
event E verb= [agent= patient= to= at= instr=] [why= how=]an event
say C|narrator [to=C] act= [indirect=0|1] [sincere=0|1] [means=] [why=] id=Ean utterance; act is the closed set prag::schema::Act

Closed sets:

Parsing is strict; a format error is a rejection. This rejects:

Change language v2 (lang.rs, world.rs)

New registries: P — intentions, B — beliefs. Both are classes with their own immutable states, like relations. Bidirectional links:

command / keywhat it does
plan P kind= who=C[+C] [to=C] what="…" [src=E]an intention is opened. Kinds: promise plan agreement wish threat order task curse prophecy bargain bet
plan P status=kept|broken|dropped by=Ethe intention is closed by a fulfilment or violation event, once
believe B who=C claim="…" [about=X] [from=E] [true=0|1]a belief: the claim, what it is about, where it came from, whether it is true in the tale world
believe B status=dropped by=E · believe B true=0|1 by=Eabandoned; truth established
done C cause=Ethe goal is reached: goal becomes unknown, state done with a cause
unset C <key> cause=E [why="…"]a goal abandoned, a feeling or status passed; why is stored in the state
feel C +a +b -c [cause=E]feelings are a set: add and remove, the rest stays; set C feeling=a,b sets the whole set
detach O to=L|C cause=E · attach O to=C|O cause=Ea part is detached (lying or in hand) · attached
mention X [Y…]a mention of existing entities with no changes and no cursor shift
obj … part=C|O · num=a body part or part of a thing; a quantity (digits or some few several many all pair)
loc … here=0a place only mentioned: the narrative cursor does not move there
event … agent=C1+C2 … neg=1 cause=E about=X when=<word> before=Ejoint agents (likewise patient, to); an event that did not happen; a causing event; about a past event, intention or entity; story time separate from place in the text; a memory (happened before E)
say C1+C2 to=C3+C4 act=request+promise … cause= about= when=in chorus; up to 3 acts per utterance
rel … kind=benefactor|master|parent|ruler|captor|suitor|mentordirected: "a is the <kind> of b" (the Mouse is the Lion's benefactor, the Lion is the debtor). Also spouse

The closed sets were extended with the general vocabulary of tales (places, items, 26 feelings). The list was compiled before the FairytaleQA run, without looking at the reference answers.

A feeling with a single tag stays a Tag, as in v1, so the v1 tests and fables give the same output.

Executor gates

A command is applied to a copy of the world. A violation is a rejection; the world does not change (test rejected_command_leaves_world_unchanged).

gatewhen red
referencethe id is not in the registry; an id is created twice; cause/fact points to an event not yet written
typethe id is from the wrong registry (move C1 to=C2); the attribute does not belong to this registry
bidirectional linkafter the write some link is not mutual: have C2 O1 while the meat is still with C1 or lies on the ground; changing a relation endpoint
teleportationset … at=; an item "jumps" from character to character without give
nearbypicking up, handing over, dropping is possible only in the same location or a nested one (in=)
silent changea known attribute value or relation kind changes without cause=E. Unknown → known is not a change but a discovery (like "Jackie was a boy" in the specification)
continuityevent at=L while the agent is not there; give of an item that is not there; giving to oneself
anchoran anchor outside the text; anchors go backwards; cursor para= does not match the database

v2 gates (negative controls — v2_gates_are_not_mute, v2_links_catch_injected_mismatch, v2_format_is_strict):

gatewhen red (v2)
silent changean intention closed without by=E; a belief changed without by=E; done/unset/detach/attach without a cause; a known feeling changed without cause=; relation direction (a ↔ b) changed without a cause; a known num changed without a cause
continuityan intention closed a second time; kept by an event with neg=1; move/give/have/detach/attach/done through an event that did not happen; done without a goal; unset of an unknown; feel -x when x is absent; a belief abandoned a second time; attach of something already a part; detach of something that is not a part
linka part moved by move, give, have without detach; a part also lying separately; plans, beliefs, parts without a counterpart
nearbydetach/attach far from the whole
referencesrc=/from=/by=/cause=/before=/about= to something not yet written; an event referring to itself; mention of a nonexistent entity; a nonexistent joint agent
continuity (joint)event at=L while at least one of the joint agents is elsewhere

The link gate is not built into the executor.

Queries (query.rs, qa.rs)

world query <doc> <dir> <query>. Entity description — C1, char:fox, obj:meat|flesh, loc:tree. a || b — a second reading if the first gave no answer.

State and place:

Events:

Relations:

Working memory (context_at, the view from the cursor):

md graph: md.rs builds a human view — the text, the cursor by sentence, registries, hero states as deltas, relations, chronology, working memory and gates.

LLM (vmm.rs)

Prompt:

Call:

Accounting — calls.jsonl.

LLM v2 (rules_v2, body_v2, call_v2):

world depends on prag (opus, schema::Act, data::duck_query) and on en — only dictionary reads in code: lemmas of question words and past tense of verbs in answers.

Commands

export PATH=$HOME/.cargo/bin:$PATH
cargo build --release
../target/release/world prompt 2451                                   # prompt without a call
../target/release/world vmm 2451 $MOVA_DATA/runs/world-2026-09-26      # LLM → cmds-2451.txt
../target/release/world build 2451 $MOVA_DATA/runs/world-2026-09-26 --questions data/questions.tsv
../target/release/world query 2451 $MOVA_DATA/runs/world-2026-09-26 diff char:crow 1 7

# FairytaleQA (v2)
R=$MOVA_DATA/runs/world-v2-2026-09-26
../target/release/world ftqa-import $MOVA_DATA/raw/fairytaleqa/hf-test.csv $MOVA_DATA/raw/fairytaleqa
../target/release/world ftqa-prompt golden-goose                  # v2 prompt without a call
../target/release/world ftqa-vmm golden-goose $R                  # LLM v2 in chunks → cmds-golden-goose.txt
../target/release/world ftqa-build golden-goose $R                # gates → world-golden-goose.md
../target/release/world ftqa-eval $R --sample 10 --seed 2 --exclude $R/seen-questions.tsv
../target/release/world ftqa-parse questions.tsv --frame          # question parsing (development)

# Question reader v2 (en model: WORLD_EN_MODEL or $MOVA_DATA/en/models/en.bin)
O=$MOVA_DATA/runs/world-reader-2026-09-26
../target/release/world reader-parse questions.tsv                # UD tree parsing (validation)
../target/release/world reader-span-dev $O/valid [--show why]     # spans on validation, without the world
../target/release/world reader-eval $R $O --dev $O/dev-questions.tsv   # development: seen questions only
../target/release/world reader-ask golden-goose $R 4 [--facts]    # one (allowed) question in detail
../target/release/world reader-eval $R $O --sample 10 --seed reader-2026-09-26 \
    --exclude $O/dev-questions.tsv --exclude $R/freeze-2/manual-sample.tsv --exclude $O/excluded-extra.tsv
../target/release/world reader-eval $R $O --skip $O/dev-questions.tsv  # table on the 305 non-dev questions
../target/release/world reader-eval $R $O --control shuffle|noindex    # negative controls

Run of 2026-09-26: three fables

Source: Project Gutenberg #21, public domain, database docs/sentences. In this translation the Crow has meat (a bit of meat), not cheese.

fabledocsentencesLLM linesacceptedformatgateswithout commands
The Fox and the Crow245162626000
The Lion And The Mouse235564444000
The Hare and the Tortoise236674444000

The gates let the clean LLM output through. That they are not mute is shown by the test real_vmm_output_and_its_mutations on the real output for "The Fox and the Crow". Each mutation turns red on its own gate:

The Fox and the Crow (2451)

The Lion and the Mouse (2355)

The Hare and the Tortoise (2366)

Answers from the state

Procedure:

  1. Questions of the FairytaleQA types, their queries and expected answers (data/questions.tsv) were written before the LLM call and not edited after it.
  2. The SLM takes the answer only from the state and the history, with anchors.
  3. Scoring: words from the expected answer must appear in the answer text, with order via >.
  4. All 24 answers were reviewed by hand.

The honesty control (FX, LX, HX) was added after the run. The answers to these questions are absent from the state by construction, so the correct answer is "not in state".

#typequestionSLM answer (abridged)score
F1characterWho are the main characters?Crow (6 mentions, ^s1–^s6), Fox (4, ^s2–^s6)hit
F2settingWhere was the Crow when the Fox praised her?C1@4 in L2 tree ⊂ L1 ground, from ^s1 (move because of E2 perch)hit
F3actionWho had the meat at the beginning and at the end?@first Crow (^s1, because of E1 steal); @last Fox (^s6, because of E9 pick_up)hit
F4causeWhy did the Fox praise the Crow?E5 ^s3 ^s4: evaluate, indirect, insincere; why "…drop the meat"hit
F5feelingWhat did the Crow feel when the Fox doubted her voice?feeling=proud from ^s5 because of E5hit
F6causeWhy did the Crow caw?E7 caw ^s5: why "refute the slight on her voice"hit
F7outcomeWhat happened to the meat?Crow ^s1 → ground ^s5 (because of E8 drop) → Fox ^s6 (because of E9)hit
F8predictionWill the Crow trust the Fox?"probably will be wary of each other and feud": stranger ^s2 → adversary ^s6hit
FXcontrolWhat did the Fox do with the meat afterwards?not in state (no eat events)honest
L1characterWho woke the Lion?sleep=awake from ^s1 because of E1 run (agent Mouse)hit
L2feelingWhat did the Lion feel when he caught the Mouse?feeling=angry from ^s2 because of E1hit
L3actionWhat did the Mouse promise?E3 ^s2 request; means "spare my life and I will surely repay your kindness"hit
L4causeWhy did the Lion let the Mouse go?E4 laugh: "a mouse could never help a lion"; E5 release: "the Mouse's plea amused him"hit
L5characterWho tied up the Lion?the hunters (E6 catch, E7 bind ^s4)hit
L6outcomeHow did the Mouse free the Lion?^s5 recognize → come → gnaw ropes (teeth) → freehit
L7outcomeHow did the relationship change?stranger → adversary ^s2 → acquaintance ^s3 → friend ^s5, with causeshit
L8predictionWhat will happen if the Lion meets the Mouse again?"probably will help each other": friend from ^s5hit
LXcontrolWho is the Mouse's mother?not in state (mother unknown)honest
H1characterWho chose the course and the goal?Fox (E5 choose ^s2)hit*
H2causeWhy did the Hare agree?belief "the tortoise cannot possibly beat him" from ^s2hit
H3settingWhere did the Hare fall asleep?L3 wayside ⊂ L1 the race course, from ^s5hit
H4actionWhat did the Tortoise do during the race?^s3 start; ^s4 go "slow but steady, never stopping"hit
H5outcomeWho won?Tortoise (E13 win ^s6)hit
H6feelingWhat did the Tortoise feel at the end?feeling=tired from ^s6 because of E8hit
H7causeWhy did the Hare lose?E9 lie ^s5 at wayside: why "he believed he could easily win"hit
H8causeWhy did the slow one win (moral)?narrator ^s7: "steady effort beats careless speed"hit
HXcontrolHow old is the Hare?not in state (age unknown)honest

Total: 24/24 hit, 0 miss, 0 "not in state"; honesty control 3/3.

Caveats:

Cost of the Opus calls

3 calls (Opus claude-opus-5-5, high), no retries: the format gate rejected nothing.

fablesoutputcache writecache readestimate $
245126.6215020,3806,8980.207
236631.4284318,7168,5720.208
235534.0348520,0128,5720.232
total92.0 (in parallel ≈34)847859,10824,042≈0.65

claude -p runs on the subscription, so the $ from total_cost_usd is only an estimate. Input per call is ≈27–29 thousand tokens, mostly the built-in system prompt of claude -p. Our prompt (rules, example, fable) is ≈8 KB, i.e. ≈2 thousand tokens. Output is 2–3.5 thousand tokens.

What v2 adds to v1, and how the LLM used it

gap of v1 (fable run)v2how the LLM used it on 10 fairy tales
Irrealis and plans: the conditional and the agreed-upon lived only in the text of means/belief or became an event (E5 choose)plan with a state and a fulfillment event81 intentions; 68 closed: kept 36, broken 25, dropped 7
One act per utterance (the Mouse's plea is both request and promise)act=request+promise66 utterances
Joint agents ("they agreed", "the two started")agent=C1+C2, say C1+C250 events
Goals never enddone / unset … why=10 / 18
One feeling at a timefeel +a -b, feeling=a,b177 feel
Two timesabout=, when=, before=104 / 48 / 12
Beliefs are flat textbelieve: claim, what about, from where, truth value96 lines
The cursor jumped to a mentioned placeloc … here=0, mention54 / 22
Body parts as objectspart=, detach, attach18 parts; the old man's wen moved to the neighbor via detach → attach
Quantitynum=53
Negation ("never stopped" only in how)neg=135
Directed relationshipsbenefactor master parent ruler captor suitor mentor33
Motive and cause as textpartly: cause=E on events, about=. why is still text—

What remains:

FairytaleQA: the five-year-old test (2026-09-26)

Data

How the test went

  1. LLM v2 writes world changes from the story text. The LLM does not see the questions.
  2. World. The SLM executes the lines with gates.
  3. The SLM reader (ans.rs, Rust, no Opus) answers the questions in natural language from the state alone:
    • parses the question: intent, subject, verb, a "time anchor" after when/after/because;
    • looks up state facts by lemmas from the en dictionary; the question's sections take priority, as in FairytaleQA, where the model sees the section;
    • builds the answer from slots: the event's why, the cause or consequence event, the feeling at the anchor time, goal, intention, belief, participant, place, number.
    • No slot means "not in state".
    • A separate mark, "anchor": the sentence of the best fact found, as a fallback.
  4. Measurement (report.rs):
    • ROUGE-L F1, max over the two references, as in the paper: tokens are lowercase Latin letters and digits, no stemming;
    • slices by type, explicit vs. inferred, share of "not in state";
    • baseline: the sentence from the question's sections with the largest lemma overlap with the question;
    • manual check of a sample: 1 / 0.5 / 0 for the SLM and the baseline, plus whether the answer is in the state at all (checked against the commands).
  5. Protocol honesty.
    • Question parsing was developed on the validation split (question forms only) and on the v2 example.
    • Then the answerer was frozen (freeze-1.txt, source hashes), and only then run on the test questions for the first time.
    • The second reader round (freeze-2) came after analyzing the failures of sample 1, so it is not blind. Its manual check is on a fresh sample: 68 questions, none of the 127 questions seen before the changes is in it.

LLM: 10 worlds

storysentenceslinesgatescallsoutput, tok.s
old-hop-giant2689017,31564
the-miserly-farmer411090114,358122
sleeping-beauty-in-the-wood441770127,088224
golden-goose5221102 (repair of 4 lines)30,197260
fox-and-wolf5914002 (repair of 3)15,959145
whippety-stourie661650120,793181
lucky-andrew8524002 (repair of 2)31,787270
how-an-old-man-lost-his-wen8919502 (repair of 1)19,028167
jelly-fish-and-monkey14429402 (2 parts)39,630323
the-dwarfie-stone16143503 (2 parts + repair of 1)51,534442
total7672055017257,6892198 (in 4 threads)

Cost. 17 calls to Opus claude-opus-5-5, high:

All 17 calls were made before 11:28Z by a binary built at 11:15Z, still on the subscription. At 11:39Z prag::opus switched to an API key, so from then on world ftqa-vmm spends real money, and total_cost_usd is the actual price.

Results: ROUGE-L (432 questions)

"SLM": "not in state" counts as 0. "+ anchor": where the state is silent, the anchor sentence is used.

typeNSLM blind (freeze-1)not in stateSLM v2 (freeze-2, not blind)not in statev2 + anchorbaseline
character450.13938%0.19024%0.1930.140
setting240.2988%0.3270%0.3270.242
action1260.22329%0.25724%0.2740.288
feeling480.21121%0.23621%0.2360.060
causal relationship1220.13452%0.19321%0.2030.274
outcome resolution280.19111%0.2250%0.2250.280
prediction390.17110%0.1993%0.2050.086
all4320.18532%0.22618%0.2350.222
explicit3250.19032%0.23019%0.2400.259
inferred1070.17032%0.21415%0.2200.109

For comparison, on the test set in the paper:

Our numbers are not comparable with them: we trained nothing on this data, and an answer from the state is not a span from the text. Only the baseline is comparable.

Manual check

Blind reader (freeze-1), sample 1: 70 questions, 10 per type:

typeSLMnot in stateanswer is in statebaseline
character35%4100%40%
setting50%190%85%
action20%590%90%
feeling65%085%35%
causal relationship10%455%50%
outcome resolution30%280%20%
prediction30%085%55%
all34%1684%54%

Reader v2 (freeze-2, not blind), fresh sample 2: 68 questions, none seen before:

typeSLMnot in stateanswer is in statebaseline
character25%175%70%
setting (8)31%081%75%
action40%185%45%
feeling65%365%20%
causal relationship30%165%70%
outcome resolution60%095%50%
prediction35%1100%15%
all41%781%49%

Conclusion.

Examples (freeze-2)

Hits:

  1. golden-goose #13 "what happened when the eldest caught hold of the goose by its wing?"
    • SLM (consequence E36 ← E35 via cause=): "…her fingers stuck fast to the goose and she could not take her hand away".
    • ROUGE 0.78; baseline 0.14 (a sentence about the intention to pluck a feather).
  2. whippety-stourie #48 "what did the widowed mistress want to do before she let the old woman know about her name?"
    • SLM from the intention P7 plan: "tease the fairy a few minutes before letting her know she has found out her name".
    • 0.52 vs. 0.18.
  3. the-dwarfie-stone #104 "what happened after the two countesses fled to scotland?"
    • SLM, the events following E152 flee: "the norsemen invaded scotland, and the norsemen set on fire the countesses' castle".
    • 0.42 vs. 0.10.
    • Also sleeping-beauty #1 "how many fairies?" → num → "seven", 1.0 vs. 0.0. And jelly-fish #21 "how did the king feel when his wife became ill?" → feel … cause=E12 fall_ill → "worried" (reference "troubled": correct by hand, ROUGE 0).

Failures:

  1. how-an-old-man-lost-his-wen #1 "what did the good old man have growing in his right cheek?" → "all kinds of medicines".
    • The wen is in the state: O1 part=C1.
    • For "have", the reader looks only in items, not in parts. Reader failure.
  2. whippety-stourie #8 "why did the widowed mistress rise very early…?" → "to feed her pigs".
    • The LLM wrote why as a tautology, and the real cause (rent-day) is not in the state.
    • State failure (LLM): the baseline with its sentence gets 0.55.
  3. golden-goose #18 "what happened when the princess saw the seven people continually running?" → "Dullhead claimed the princess as his bride".
    • The question's anchor ("saw … running") was matched to the event E49 laugh, and the reader took its consequence instead of the event itself.
    • Anchor matching failure.
    • Also the-dwarfie-stone #22 "who was as different from his brother as night is from day?" → "an enormous grey-headed raven". The comparison of the brothers is not in the state (state), and the reader grabbed a random agent (reader).

Next steps

The reader is taken further by question reader v2 (next section).

  1. LLM prompt. why must be the real cause, not a restatement of the action. Short action names (glosses) in the story's own words, for explicit questions.
  2. The five-year-old test per specification. An Opus judge where ROUGE penalizes synonyms ("afraid" / "terrified", "seven" / "7"). Comparison with Opus reading the story itself.
  3. More stories. The remaining 13 test stories only after an explicit decision: runs cost money.

Question reader v2 (2026-09-26)

The answer is in the state for 81–84% of questions, but reader v1 (ans.rs) finds it in 34–41%: the weak spot is the reader. Reader v2 is a set of separate modules next to v1 (ans.rs keeps its freeze-2 hash), with no Opus calls: the worlds of the 10 tales are already built.

How it works

  1. A single fact index (index.rs). From 102 (old-hop-giant) to 494 (the-dwarfie-stone) facts per tale, each with sentence anchors, a cause (cause) and story order:

    • events: agent, object, addressee, place, instrument, how, why, when, about, negation;
    • utterances: speaker, addressee, acts, means, why;
    • intentions: kind, holders, text, toward whom, state and the fulfilling event; beliefs: proposition, about what, truth value;
    • attributes by state: traits, status, goal, feeling (only added tags), age, sex, sleep, freedom, life, number, name, place — with the unset … why= explanation;
    • objects in hand, parts, names of characters and places with in= ancestors, relations.

    Search uses lemmas from the en dictionary with field weights: predicate +3; subject in its role +3, elsewhere +1, absent −2; another participant +1.5; content word — the field weight (predicate 1.5; texts and participants 1; time and "about" 0.5); the question's section +3, adjacent +0.5, farther −1.5; for a prediction the next section +2.5.

  2. Parsing the question with a UD tree (qframe.rs, model ud-ewt-eslspok.bin).

    • A subordinate clause ("when/after/because/if/as soon as …") is a separate clause: the reader first looks up its fact — the time anchor — and then answers about the main clause.
    • Main clause: question word, auxiliary, predicate; participants come from the tree of the declarative sentence "subject + did + predicate + rest". The tagger knows such sentences better than lowercase questions.
    • Predicate repair with the en dictionary: the candidate must be a VERB in the declarative tree; "to V" is an xcomp of the previous candidate.
    • Names outside the dictionary (assipattle) are capitalized before tagging; "ashamed" is not (a familiar suffix).
    • Question type → index field: feel → feeling; why → why / causing event / goal / intention / traits of the loved one; happen → consequences (cause= anchors), otherwise the following events; have → objects and parts; who in the passive → patient; "who did X think" → belief; "what would happen if" → content of the utterance.
  3. Span of the anchor sentence (span.rs). FairytaleQA answers to explicit questions are text spans. Predicate node (lemma from the question or from the fact) → the subtree of the role:

    • subject, object, place, time, cause (advcl because/for/to; "X, so V"; "so ADJ that"), manner;
    • feeling — only feeling words with an intensifier ("sorely troubled");
    • "what happened when X" — the main clause after the subordinate one, or the following coordinated predicates;
    • a question echo (a span made mostly of the question's own words) is not an answer.
  4. Merging (reader.rs) — a transparent rule; the reason is written to the log of every answer:

    • fact score < 6 (CONF) or no fact → baseline sentence: "state not confident (score 4.0 < 6)", "state is silent";
    • otherwise, if the question's words cover ≥ 50% of the fact's anchor sentence (COVER; explicit by our measure, not by the dataset label) and a span is found → span with the rule name: "state: score 10.0 ≥ 6; question in sentence ^s38 at 50% → span [conj-after]";
    • otherwise → fact field: "state: score 12.5 ≥ 6 → fact field";
    • a feeling with a feeling word in the anchor sentence, and "what happened" — always a span; description and number — a field.

    A search without a predicate match and with fewer than two words is never confident.

Protocol

Results: ROUGE-L

305 questions unseen by development (main table). "State only" — "not in state" counts as 0; "state + sentence" — merging.

typeNbefore: reader v1 (freeze-2)after: v2, state + sentencev2, state onlybaselinenot in state (v2)
character260.1370.2870.2560.14038%
setting80.3380.3890.3400.20612%
action1060.2400.3290.2430.26937%
feeling300.1780.1540.1440.03937%
causal relationship1020.1960.2940.1870.28140%
outcome resolution130.2650.4090.3560.33915%
prediction200.2180.2080.1870.08920%
all3050.2130.2940.2190.22935%
explicit2300.2220.3340.2480.26734%
implicit750.1850.1710.1310.11439%

All 432 (including the 127 dev questions): v1 0.226 (as in freeze-2 — a cross-check), v2 0.320, state only 0.246, baseline 0.222; explicit 0.230 / 0.358 / 0.259 (baseline), implicit 0.214 / 0.203 / 0.109. On the 127 dev questions — 0.382: the gap from 0.294 is the price of developing on one's own questions.

By answer source (432): baseline sentence — 145 (0.220); anchor-sentence span — 99 (0.517, baseline on the same questions 0.283); fact field — 188 (0.292, baseline 0.191).

Spans on validation (1025, no world): oracle sentence 0.397 → span 0.448 (with the fallback "sentence without question words" 0.458); baseline sentence 0.219 → 0.256.

Manual check (fresh sample, 53)

typeNbefore: v1v2baselinev2 took the sentenceanswer is in the state
character1040%55%55%595%
action1020%70%70%7100%
feeling1050%65%45%190%
causal relationship1025%50%60%680%
outcome resolution367%67%100%0100%
prediction1045%40%25%095%
all5338%57%54%1992%
explicit3839%64%61%1596%
implicit1533%37%37%483%

Conclusion.

Negative controls

runstate onlystate + sentencebaselinenot in state
normal (432)0.2460.3200.22234%
facts of another tale (tale i reads the index and world of tale i+1)0.0030.2070.22294%
no index00.222 = baseline0.222100%

On the 305 non-dev questions: foreign facts — 0.213 (state 0.003), no index — 0.229 = baseline.

Examples (fresh sample, after the freeze)

Hits:

  1. the-miserly-farmer #17 "what happened when the farmer turned the corner?"
    • The anchor is the subordinate-clause event E44 turn (^s38). The consequence is in the same sentence → span [conj-after]: "there lay the missing piece from the axle by the city wall".
    • ROUGE 1.00. v1 — "the farmer found the axle of his cart" (0.28); baseline — the whole sentence (0.80).
  2. how-an-old-man-lost-his-wen #9 "why did the man sit inside a large hole in the hollow trunk of a tree?"
    • Fact E14 espy (score 12.5: subject +3, place +1.5, words +5, section +3) → field why: "find shelter from the rain".
    • Manually — yes; baseline ("at last he espied a large hole…") — no; v1 — "not in state".
  3. golden-goose #17 "how did the king feel about dullhead as a son-in-law?" (implicit)
    • "about X" → the king's feeling caused by an event involving Dullhead: "annoyed".
    • The baseline only hints ("did not much fancy him"); v1 — "not in state".

Misses:

  1. jelly-fish-and-monkey #1 "who was the dragon king of the sea?" → "long, long ago, in old japan, the kingdom of the".
    • The fact is correct (C1, name "rin jin, the dragon king of the sea"). But merging preferred the span, and the subject rule, with no nsubj in the tree, took "the words before the predicate".
    • Merging failure: for "who is X" the character's name must take priority over the span.
  2. whippety-stourie #28 "what did the old woman ask for in return?" → baseline sentence, wrong.
    • The answer is in the state: E33 means="… now give me what I ask: your son".
    • But the LLM named the character "the old lady in green" (class fairy), so "the old woman" did not link to C5. The utterance went through without a subject (score 2 < 6).
    • Linking failure of a name to a character.
  3. how-an-old-man-lost-his-wen #19 "why did the man get back in the tree?" → "the tree was in the background".
    • The reader took the why field of event E22 creep_back — and that is exactly what the LLM wrote. But it is not a motive ("he did not want the demons to see him").
    • State failure: why as a tautology or circumstance, not a cause. The same one as in the freeze-2 example.

What next

  1. Merging by type: "who is X" — the name, not a span; forbid the subject span without nsubj.
  2. Name linking: synonyms of class and name (old woman ↔ old lady; "the doctor" ↔ "the fish doctor"). Candidates come from all mentions of the character, not only from the name in the state.
  3. Feelings: choose the fact by the time anchor, not "the first one caused by an event involving X". Synonymy of tags with answers (annoyed ↔ upset) — via the dictionary, not by fitting.
  4. LLM prompt: why must be the real cause. Here both the state and the reader run into tautologies.
  5. Opus judge instead of ROUGE for synonyms — only after an explicit decision.

World v3: non-linear narrative (2026-09-26)

Specification — Smart annotator — layers, context, memory: "Non-linear narrative" (event identity, the v3 pipeline, partial order of time), "Hierarchical and global worlds", "Checking claims against the world", "Immutable states".

The scope of v3. v1 and v2 hold linear causality. v3 holds three things separately:

Architecture (story.rs, narr.rs)

A canonical event is one. An event gets an E id at its first mention.

Two axes.

The frame's narrative line.

Frame stack. A frame is who tells, to whom, the level, its own cursor and its own world. The narrative level (Genette) is counted by narrators (Story::narr_level): a recollection by the same narrator is a time frame, not a new level. Frame nesting is a separate number.

frameown worldtime axiswhat passes into the parent world on close
root (level 0)the document worldown—
world=same time=past (testimony, recollection, explanation)projection of the parent world: identity and structure of entities, without places and transient states (feeling, sleep, goal, status, life)the same, everything in the frame is before before=/src=new entities (identity, stable fields, place); for existing ones only what the parent does not know; the past does not overwrite the present
time=nowthe parent's world and linethe same—
world=new (story within a story)a new empty worldown, separateevents are foreign: they can be talked about, but they do not cause changes in the listener's world
branch=dream|lie|plan|ifa copy of the parent worldownnothing (it did not happen)

Reality branches. Branches: real (happened), dream, plan, intent, lie, if, report, belief.

Observers' knowledge. An observer is a character, the reader (reader) or the narrator.

Pipeline (World::apply, exactly per the specification):

  1. Sentence cursor (anchors, monotonicity).
  2. Frame: top of the stack, its world and line.
  3. Mention of an event or claim.
  4. Resolution into a canonical event (id reference) or a new event.
  5. Story time axis: nodes, a line or placement edge, past-frame boundaries.
  6. Reality branch.
  7. Observer knowledge.
  8. Gates decide whether the canonical world changes. On refusal the world is unchanged: the command runs on a copy of the root together with the frame worlds.

Change language v3 (lang.rs)

commandwhat it does
frame open F<n> narrator=C|narrator [to=C+…|reader] level=<n> [world=same|new] [time=past|now] [before=E] [src=E] [branch=…]frame: level one deeper than the current; level=0 names the root narrator (once, as the first frame); the same id again continues an interrupted frame
frame close F<n>close the deepest open frame; what passes into the parent world is in the table above
claim K<n> who=C|reader|narrator about=E status=believes|doubts|wrong|knows interp="…" [from=E]an observer's claim about an existing event
claim K<n> status=… by=Ethe observer changed their mind because of an event
time E1 BEFORE|AFTER|DURING|SAME E2relation on the story axis (one world)
time E at="…"time in the words of the anchor sentence — absolute time only from the text
branch E dream|plan|intent|lie|if|report|belief|real [by=E]the event's reality branch
know who=C|reader fact=E|K|P|… from=^sN [how=saw|heard|told|inferred|revealed|guessed] [by=E]who knows a fact and from which sentence
reveal E [to=…] [by=E] [interp="…"] [wrong=K+…] [right=K] [agent= patient= instr=] [cause=E] [before=|after=|during=|same=E]revealing the truth about an existing event
event … after=E | during=E | same=Eplacement at creation, in addition to v2's before=; only one

Closed sets: branches, claim statuses, knowledge sources, frame world and time. Parsing is strict:

All of these are format refusals (v3_format_is_strict, 29 corrupted and 12 correct lines). mention E… remains a format error, as in v2: an existing event is mentioned by reference (claim, know, reveal, time), not as a new event.

v3 gates

gatewhen red
framethe level is not exactly one deeper; closing a frame that is not the deepest; closing the root; naming the root a second time; continuing with a different parent, level or narrator
story timea cycle in the partial order (time, placement, reveal, frame boundary); an at="…" that is not in the anchor sentence
teleportation (in story time)in the present, an event where the character was not just before on the story axis: a recollection without a time marker. In a past frame — the same inside its world, where the neighbours are the frame's states, not the present ones
brancha state change by an event not of this branch (dream, lie, plan); branch of an event that has already changed the world; know of an event not in the real branch
world boundarytime or placement across worlds; a change in the listener's world caused by an event of a story within a story
event identitya character's second death with a different cause — the same event twice; reveal overwrites an existing role
referenceclaim/know/reveal/time/branch about something nonexistent; reveal of a new event ("a reveal does not create events"); a claim about a different event in wrong=
continuitya dead character acts in the present
anchorknow from=^sN later than the command's sentence
silent changea claim's status changed without by=E

Compatibility: v1 and v2 are the linear special case

Testbeds (public domain, Project Gutenberg)

Downloaded with the sources pipeline: manifest sources.jsonl → sources plan → curl -K → sources verify: ok 2, fail 0. The book pages are saved in pages/.

testbedsourcestatuscutparagraphssentenceswords
speckled-band — a detective story resolving the pastConan Doyle, The Adventures of Sherlock Holmes, PG #1661 → "VIII. The Adventure of the Speckled Band"PD: author † 1930 (UA/EU — since 2001), USA — 1892; PG page: "Public domain in the USA"from heading VIII to IX2515959934
arabian-nights — frames within framesAndrew Lang (ed.), The Arabian Nights Entertainments, PG #128 → introduction "The Arabian Nights" and "The Story of the Merchant and the Genius" with the stories of the first and second old menPD: Lang † 1912, translators from his circle (Leonora Lang † 1933), USA — 1898; PG page: "Public domain in the USA"from "The Arabian Nights" to "The Story of the Fisherman"1212845180

Measurement protocol

  1. Questions — before the LLM. 35 questions (questions-v3.tsv: 20 on "The Speckled Band", 15 on "The Arabian Nights") were written from the testbed texts before the first v3 LLM call. Question types:

    • time;
    • knowledge;
    • frames;
    • control.

    For each question the formal qa3.rs query and the expected answer were recorded. Hashes of the questions, the sentences of both testbeds and qa3.rs — freeze.txt (14:24Z).

  2. LLM. An event description in a query is verb variants, a participant and a sentence range (ev:die|death/char:julia@s85-160), because ids are unknown before the run.

  3. Pilot. The first part of "The Arabian Nights" — one call, including the format repair.

    • It showed a cascade: the LLM referred to an event it wrote one line below (before=E5 before E5). The reference gate rejected 12 lines.
    • One paragraph about forward references was added to the prompt, and part was re-asked. The pilot output is in pilot/.
    • Questions and expected answers did not change.
  4. Blind measurement. Before it, hashes of all the code — freeze-eval.txt: qa3.rs is the same as in freeze.txt.

LLM: two worlds

Opus claude-opus-5-5 (high) via prag::opus, API. Parts of up to 95 sentences (V3_PART). The second and later parts receive:

Format repair — one per part, and only for rejected lines.

testbedsentencespartslinesacceptedformat (after repair)gatesno commands
speckled-band5957118211760 (4 single-line repairs)60
arabian-nights28437127120 (1 repair)00

The six gate refusals are LLM errors.

Commands.

testbedevents (happened / in branches)framesnarrative levels (nesting)claimsrevealsknowledge
speckled-band588 (587 / 1)32 (3)37 (48 lines including status changes)116
arabian-nights324 (323 / 1 lie)43 (3)526

Cost. 17 calls, output 290,755 tokens (including thinking), cache write 545,840, cache read 142,127, 2514 s.

Answers from the state (35 frozen questions)

qa3.rs answers via the formal query, from the state only, with ^sN anchors. Scoring — the expected words in order plus anchors within range. Manual check — on all 35 questions, criteria in freeze-*/manual.tsv.

typequestionsfreeze-1 (blind), manualfreeze-2 (not blind), manual
time96 (+1 "not in state")8 (+1 "not in state")
knowledge1188
frames11710
control433
all3524 (69%); automatic 2629 (83%); automatic 30
"The Speckled Band"201216
"The Arabian Nights"151213

freeze-1 — blind. The code was frozen (freeze-eval.txt) before the first run of the questions. 26 automatic hits, 24 manual: two are word coincidences.

freeze-2 — NOT blind, after analysing the freeze-1 misses. Questions, expected answers and event descriptions are the same. Three model rules were changed:

  1. Narrative level — by narrators (Genette). The LLM put the whole story in Watson's time frame (frame open F2 narrator=C1 level=1 time=past before=E1) — a recollection by the same narrator, not a new level. Frame nesting remains a separate number.
  2. Flash-forward and recollection — only with their own time declaration: a placement of the event, time or reveal about it. Without this, every event told before a recollection would "flash forward" merely because of the recollection.
  3. The cause of a state change is no later than the next event on the line (a soft edge). The LLM writes the consequence (set C17 life=dead cause=E97) where the narrative reaches it.

Regression after the changes — the same 31 files byte for byte. Tests are green.

What was still missed (freeze-2):

Example: solving "The Speckled Band" — a change of knowledge, not of the past

The LLM wrote 1182 lines. Frame structure:

F1  level 0  Dr. Watson → reader                     ^s1–…     root (memoir; written after everything)
└ F2  level 0*  Dr. Watson                          ^s6–^s595  time frame: the whole story is before E1 (the act of writing)
  └ F3  level 1  Helen Stoner → Holmes, Watson      ^s68–^s189 (9 spans: Holmes asks follow-up questions — the frame is closed and continued)
                                                               same world, past: before E15 (her arrival)

* narrative level is counted by narrators; nesting is 1.

Julia's death is one event, E97.

Around it are observers' claims, each with its own status history (claims about=E97):

claimwhointerpretationstatus over text time
K11coroner"no satisfactory cause of death could be found"doubts ^s165
K15Helen"she died of pure fear and nervous shock"believes ^s174 → doubts ^s444 (Holmes: "No, I do not think so") → wrong ^s577 (reveal)
K19Watson"Julia was alone when she met her mysterious end"believes ^s226 → doubts ^s486 → wrong ^s577 (reveal)
K20Holmes"the gipsies, intimate with the doctor, were behind the death"believes ^s229 → doubts ^s233 → wrong ^s572 ("I had come to an entirely erroneous conclusion")

The reveal at ^s577. Holmes's words to Watson (E571) on the train the next day.

There is no new event "murder two years ago": the event is the same, E97. What changed is knowledge:

Holmes knew about the event itself since ^s89 as a listener of the frame. He knew the truth since ^s576–^s577: claims K23, K25, K26 → knows.

Roylott's death is a flash-forward.

Time is a partial order, with an explanation. order E97 E15 → "E97 die … — before — E15 arrive. Why: E97 ≤ F3 (in frame F3); F3 < E15 (frame F3 — past). E15 told earlier (^s12 vs ^s89)".

sort reconstructs a chronology that the text does not have:

  1. mother's death ^s77;
  2. Julia's engagement ^s94;
  3. Julia's death ^s89;
  4. Helen's engagement ^s183;
  5. Helen's arrival ^s12;
  6. the bite ^s565;
  7. Roylott's death ^s5.

Example: Scheherazade's frames (arabian-nights)

The LLM wrote 712 lines. Gates rejected 0; sentences without commands — 0.

F·root    level 0  narrator → reader                           ^s1–…            81 events
└ F1      level 1  Scheherazade → Schahriar, Dinarzade        ^s66–^s93, ^s106–^s283   new world (its own axis 1)
  ├ F2    level 2  first old man (with the hind) → genie, merchant   ^s151–^s212      same world of the tale, past: before E139 "came with the hind"
  └ F3    level 2  second old man → genie, merchant, …        ^s217–^s274      same, past: before E238

Negative controls of the specification

controlhow it was checkedresult
flashback without a time mark → teleportation gateflashback_without_time_mark_is_teleport: Helen on Baker Street; a past event "at Stoke Moran" — without a frame, in a past frame, with before=without a mark — teleportation; with a frame or before= — passes. The neighbour on the story axis is the frame's states, not the present ones: in the frame Helen is already at Stoke Moran, and an event on Baker Street without move is also teleportation
Watson's claim does not change the canonical worldclaim_does_not_change_canonical_world: snapshots and state counts of all entities and events before and after the claim; on real output, the world without all claims is the same (real_vmm_v3_output_and_its_mutations)no change. A claim recorded as an event in the belief branch is red when it tries to change state (branch)
a reveal does not create a second eventreveal_does_not_create_second_event: events and narration registry before and after reveal — the same count; a mutation of real output (the solution recorded as a new "murder" event)same number of events. reveal of a nonexistent one — reference; a second death — identity; overwriting an existing role — identity

On the real worlds the same is visible without tests:

v3 commands

export PATH=$HOME/.cargo/bin:$PATH
R=$MOVA_DATA/runs/world-v3-2026-09-26
# sources: manifest → plan → curl → verification
target/release/sources plan $MOVA_DATA/raw $R/queues $R/sources.jsonl
curl -sS --fail -K $R/queues/curl-www.gutenberg.org.cfg --create-dirs
target/release/sources verify $MOVA_DATA/raw $R/sources.jsonl
# testbeds: cut out a story, sentences (en::tok)
../target/release/world poly-prep $MOVA_DATA/raw/en-narr-pg1661-doyle-holmes/pg1661.txt \
    "VIII. THE ADVENTURE OF THE SPECKLED BAND" "IX. THE ADVENTURE OF THE ENGINEER’S THUMB" $R/stories/speckled-band.tsv
../target/release/world poly-prep $MOVA_DATA/raw/en-narr-pg128-lang-arabian-nights/pg128.txt \
    "The Arabian Nights" "The Story of the Fisherman" $R/stories/arabian-nights.tsv
../target/release/world v3-estimate $R speckled-band arabian-nights   # cost estimate before the run
../target/release/world v3-prompt arabian-nights $R 1                # prompt of a part, no call
../target/release/world v3-vmm arabian-nights $R                     # LLM in parts, cap WORLD_V3_BUDGET
../target/release/world v3-build arabian-nights $R                   # gates → story-arabian-nights.md
../target/release/world v3-query speckled-band $R order ev:die/char:julia ev:arrive/char:helen@s10-40
../target/release/world v3-eval $R $R/questions-v3.tsv               # frozen questions → answers-v3.md
../target/release/world v3-cost $R
WORLD_V3=1 ../target/release/world ftqa-build golden-goose $MOVA_DATA/runs/world-v2-2026-09-26   # old runs on the v3 executor

v3 tests (tests3.rs)

cargo test --release — 33 tests: 22 for v1, v2 and reader v2, and the 11 v3 tests below.

What next

  1. Narrative level vs time frame. A memory of the same narrator is a time frame, not a new level.
    • In freeze-2 the level is already counted by narrators (Story::narr_level).
    • The next step is in the language itself: frame open … time=past without a change of narrator does not require level+1.
  2. A single view of states in story time.
    • Currently a character's states in a past frame live in the frame's world. Between two frames about the same time they are reconciled only through canonical events.
    • For example, the identity gate does not see a second death in a neighbouring past frame: it has its own projection.
    • What is needed is a history of states of each entity on the story axis from all worlds — "immutable states" on the partial order.
  3. Parallel lines (specification, item 5): several cursors on a shared axis. Currently time=now continues the parent's line, and there is no separate parallel line.
  4. Global entities and epochs ("Hierarchical and global worlds"):
    • scope scope=global|book|tale;
    • Baghdad and Harun al-Rashid are shared across tales;
    • epochs — re-linking between tales, as described.
  5. A natural-language v3 question reader: UD parsing (as in reader v2) → qa3 queries. Currently questions are formalised by hand before the run.
  6. SLM change scribe (v4): first frames and time from the UD tree and signals — pluperfect, years ago, before, discourse connectives. Gold — LLM v3 output with gates.

Tests

cargo test --release — 33 tests: 13 v1, 6 v2, 3 reader v2 and 11 v3 — in the section "v3 tests".

v1:

v2:

Reader v2:

Three v1 fables on the v2 executor give identical world-*.md and answers-*.md (24/24, control 3/3): fables. After reader v2, a rebuild gives the same files byte for byte.

Files

Crate:

Fables run world-2026-09-26:

v2 run world-v2-2026-09-26:

World v3 world-v3-2026-09-26:

Reader v2 world-reader-2026-09-26:

Licence

Elamur Cosmic License (Elamur Cosmic License). Third-party data keeps its own terms, as noted where it is used.

This page in Markdown → https://md.mova.elamur.ai/free/en/docs/world/