Branch refinement — Meridians World Search (MWS): Monte Carlo tree search over a recorded world
From the Meridians Wiki · Public · Maintained · human-contract
Status: rough spec (direction). Not an implementation contract; nothing here ships until scheduled. Manual refinement (Futures, regenerate, edit, delete) is the shipped surface and stays the override.
Meridians World Search (MWS) is the name of this search: a Monte Carlo tree search over a world's meridians (its fate pathways) that develops the world at the forks where outcomes are most uncertain and most costly and prices each pathway. It proposes and prices; it never chooses.
The idea in one line: a branch tree is a bonsai, and MCTS is how a bonsai is grown. Nothing is cut by decree; branches that move the world get light and are deepened, branches that move nothing are starved and wither. Today a Domain is authored once — a v1 with no selection pressure, every branch shipped at fitness 1 — and refined by hand when someone notices. The future is an opt-out, autonomous refinement loop that keeps searching and re-weighting the tree the world already has, so the visual novel a reader meets is the survivor of a search, not the first draft. We call the loop MWS: well-travelled paths through a world's possibility, found by visiting, valued by consequence, kept by an honest ledger.
MWS is what makes Meridians a causal simulator rather than a story generator, and it is the first priority among the specs (specs map). Given a situation — a world we made, or one built from a person's own circumstances — it spends its effort on the high-uncertainty, high-consequence forks and returns the pathways with their costs, so a person rehearsing inside the world can see the game being played (incentives, information asymmetry, timing, hidden constraints), try a route, see what it caused, and retry. It exposes the board; it never plays it. Under the VNMMO the same search runs more automated, as a shared tree with a value per seat (§12) — a stretch goal on a separate roadmap, built on this loop.
This is not a new generator. Fate exploration already fans sisters at a fork, already scores them with free deterministic derivations (fatefulness, orthogonality), and already names an upper-confidence rule for choosing where to fan next (§5 step 1). This spec completes that rule into the full four-step MCTS loop — select, expand, simulate, backpropagate — and makes it the standing process a Domain runs between readings, rather than a cycle an author launches. MCTS is used for refinement and continued exploration of a tree that exists, not for generating a story from nothing.
Authority is unchanged:
Domain defines. Branch preserves. Scenario resolves. Episode embodies. World inherits and lives.
Read first: fate exploration (§4 scoring, §5 policy, §6 promotion), visual-novel branching (the Reading, fan selection, branch-and-bottleneck), training worlds (the canonical route and the Rasch reading the tree serves), latent traits (the person's record, and the target band it hands the flag plan), the pentad (the currency every value below is stated in).
The subsystems it reads: src/types/domain/branch.ts (Branch — parentBranchId, forkEntryId,
entryIds: the tree is already a tree), src/lib/core/graph/branch-tree.ts (resolveBranchTreeParents,
layoutBranchTree), src/types/domain/futures.ts (Future, FutureRun, priorLogit — the prior),
src/lib/core/forces/thread-market.ts (outcomeProbabilityDeltas, normalizedEntropy, closure tests),
src/lib/core/forces/composition.ts (openMassSeries, consequenceShareSeries — the value),
src/lib/core/forces/record-health.ts (recordHealth — the existing "what should the author look at"
seam), src/types/domain/settings.ts (AutoConfig, autoExecute — the attributable mandate for
unattended runs).
1. Why MCTS and not the alternatives
The tree we hold has three properties that pick the algorithm:
- It is deep and the payoff is late. A fork at arc 2 matters because of what it does at arc 6. Beam search prunes hard and early and kills exactly that branch. Genetic search explodes breadth without depth.
- Every branch has a real, free value. Fatefulness and consequence share are derivations of the record, not a model's opinion — so a tree-search value function exists without training anything.
- We are never done. A Domain keeps being read; the search must be anytime — well shaped at every budget, better with more.
MCTS with an upper-confidence bound (UCT) has all three: it converges to the best subtree given enough
visits, it deepens only what has earned depth, and it can stop at any moment and leave a good tree. Its
prior-guided form (a policy prior times an exploration term, as in AlphaZero's PUCT) maps onto what we
already store: priorLogit is the world's belief in a future; visits are what readers and scouts actually
did with it.
2. The loop, in Meridians terms
One refinement step costs one scout-depth continuation (cents, per fate-exploration §3). A run is a budget of steps. Each step:
-
Select. Walk from the canon root down the branch tree, at each fork taking the child that maximises
score(child) = Q(child) + c · P(child) · √N(fork) / (1 + N(child))where
Qis the child's mean value (§3),Pits prior (§4),Nvisit counts (§5), andcthe exploration constant. Stop at a fork that is expandable: fewer thankaccepted sisters, or a sister whose consequence has never been measured to depthd. -
Expand. Fan one new sister at that fork with mandated divergence (fate-exploration §5 step 2: opposed
Futurevariables, siblings passed asconsidered). Delta-gate it: empty or duplicate deltas mean it is not a candidate, not a low-scoring one. -
Simulate. Roll the sister forward at scout depth for
darcs (default 2 — enough foropensto fire and for downstream threads to move). No prose, no assets;WorldExpansiondeltas only. This is the rollout, and its whole cost is the scout continuations. -
Backpropagate. Compute the rollout's value (§3) and add it to every node on the selected path; bump their visit counts. A fork whose new sister moved the world raises the value of every ancestor that led to it — that is how a deep payoff finds its way back to an early fork.
Repeat until budget, floor, or manual stop (fate-exploration §5 step 5). The tree after a run is the same
canonical Branch[] — with more sisters where the world was alive, none where it was not, and a visit and
value ledger over all of it.
Simulation is scouting, not production. A rollout never becomes a reader-facing branch by itself. It
earns production through the existing promotion ladder (fate-exploration §6), which now reads Q and N
instead of a single fatefulness snapshot.
3. The value function — bits, never opinions
Q must be deterministic, free, derived from state, and stated in the pentad's currency. Four terms, all
already computed:
-
Consequence — belief movement downstream of the fork attributable to the sister's scene(s):
consequenceShareSeriessummed over the rollout window. This is fatefulness with a horizon. -
Divergence of futures — how far the sister's rolled-forward open mass (
openMassSeries/H_openover threads) has moved from its siblings'. A fork whose children converge back withindarcs is not a fork; it is a detour. Jensen–Shannon divergence between the children's belief trajectories is the measure; direction disagreement on a shared thread is its strongest form. -
Structural change —
systemDeltas/ownershipDeltasin the rollout, weighted as fate-exploration §4 already weights them. -
Reach — how far the sister's Flags compound: the Flag→Flag dependency graph the Pulse already reads (
closureGraph,closureReachincore/forces/closure-graph.ts) gives each collapse a downstream count and a landing — whether its consequence stayed in its own thread or touched World / System state. A rollout that opens a Flag other Flags come to rely on is worth more than one that settles a question nothing else needs.
Will enters only through Causality: the search prices futures over the existing Flag tree, and the visual-novel turning-point rule places a reader decision where the ring hands the reader the Causality input. The constitution is a prior on which hops are cheap, never a chooser; MWS can price a future without deciding it. In the Readings pipeline, that prior is a System 1 score over generated candidates, not a field emitted by candidate generation.
One value, in nats, per rollout. Where this value points is the whole of MWS's search allocation. Point it at plausibility and the search grows the safe, predictable tree every generator already writes. Nothing above rewards plausibility: the terms pay for belief that moved and consequence that compounded, so an impossible premise with a rigorous record out-values a probable one whose forks rejoin. The wire (§4) decides which branches may exist; the value decides which get light. The value is a readout, never a training target (fate-exploration §4, second hard rule). It selects what to explore and what to promote; it is never fed back into a prompt as "write a high-value arc", and it is never tuned to make the numbers rise.
For a training world, one more term is admissible and only there: item information (training-worlds §5a) — how much a fork pitched inside the hour's target band would tell and teach. The band is a property of the flag plan: latent-traits steering writes it there (a difficulty range on a named axis, ±1 logit wide) from the person's or cohort's reading (latent traits §4), and the search reads only the band. A fork nobody would find hard or easy is worth less to a course than one pitched at its cohort. This is a property of the fork, not a judgement of a person (§7).
4. The prior — the world's own belief, then the author's
P(child) is the softmax over sisters of priorLogit — the world's belief in that future at the moment it
was read (futures.ts). That is the same quantity the shipped Futures panel already shows. Two overrides,
both attributable:
- The canonical route. In a training world the author's good route (training-worlds §3) carries a boosted prior so the search deepens around it first — the bonsai's copper wire. The prior is calibrated against players later (training-worlds §5a); the search does not need to wait for that.
- A pin. An author may pin a branch (prior → 1 at its fork) or fence one off (prior → 0). Both are typed actions on the record and both are visible in the ledger. This is what "manual refinement stays the override" means concretely: the author steers the prior, the search does the visiting.
Never a model asked "which future is more likely?" The prior is what the record believed.
5. Visits — scouts and readers count alike
N(child) counts two things and keeps them distinguishable:
- Scout visits — rollouts that passed through this branch.
- Reader visits — commitments at this fork by an actual reader or seat (visual-novel §3), attributable Will from outside the simulation.
A reader visit is worth more than a scout visit to the search (it is evidence the fork is where people
actually stand), but it never writes to Q directly — a reader choosing a branch is a fact about the
reader's Will, not about the world's consequence. Readers steer where the search looks; the record decides
what it finds.
Storage: visits and values are a derived ledger over the branch tree, keyed by Branch.id, recomputed
from the record and the rollout deltas. No stored visits field on Branch; nothing here adds a second
source of truth or a stored position register.
6. Pruning — starved, never cut
Selection is the pruning. A branch with low Q and a fading exploration bonus simply stops being visited:
no new sisters, no deeper rollouts, no promotion. Retention follows fate-exploration §5: keep the top m
per fork plus every branch any reader has visited; nothing referenced by a save, route, or produced asset is
ever deleted. A withered branch is still a parallel world someone might return to; it has just lost the sun.
The bonsai reading, stated once: the wire is the prior, the light is the value, the visits are growth. An author who wants a shape wires it; the search grows toward light within that wire.
7. Boundaries — what the search may and may never do
- Exploration proposes; it never commits. Unchanged from fate-exploration. A rollout stages what a
future would cost the world; taking it belongs to a reader, a seat, a Director's
autoExecutemandate, or an agent through MCP. The search never selects the active branch, never resolves a Scenario, never writes a Position. - Opt-out, not silent. A Domain runs refinement by default between readings within its budget; the
owner can pause or fence it per Domain, per branch, or per fork. Every run leaves a ledger entry: budget
spent, forks expanded, values before and after.
recordHealth's "what the author should look at" is where the run reports — it proposes; the author looks. - The player is never the thing selected against. In a training world the value is a property of
forks and futures — consequence, divergence, item information — never of a person. No term in
Qreads a player's θ, response, or history; the one thing the person's record may hand the search is the flag plan's target band, and that hand-off is latent-traits steering, not the search. The search shapes the world the player meets; it does not shape a verdict about them (latent-model §2, training-worlds §5a). - No second canonical state. Branches, scenes, deltas stay the record. The search's ledger is derived. Rollout deltas that never earn promotion are retained as candidates exactly as fate-exploration retains them today.
- Reader-facing surfaces stay deterministic projections. The reader's fork shows accepted sisters and
their rarity (visual-novel §2); it never shows
Q,N, or the exploration bonus.
8. Why this serves the visual novel and the training world
- The fork is real because it survived. A sister that reaches a reader has out-valued alternatives that were actually rolled out — the fork carries divergence of futures by construction, not by prompt.
- The tree has the right shape. Branch-and-bottleneck (visual-novel §6) falls out: the search deepens where consequence persists and lets detours rejoin, because rejoining scores zero divergence.
- Continuations are the unit of production, not new worlds. A fresh Domain is a table with no history: nothing staked, nothing carried, nothing compounding — which is why generated prose on its own reads as worthless to the people it is written for. A continuation inherits the record, so a Flag from the last hand still binds this one. The search is cheap on continuations (the tree already exists; only the frontier is rolled out) and expensive on new worlds, and that is the right way round: fewer worlds, kept open longer, with deeper chains.
- Analysis first; generation deals. The scarce thing in this loop is the book — the record, the belief market, the Flags, the dependency chains, the ledger of what readers staked and how it settled. Prose is the cheapest input the system has and is treated that way: dealt on demand at the frontier, never the artefact the world is proud of. Every reader-facing surface the loop touches should show what a fork did, not how well it was written.
- Hours compound. For the concierge journey, the refinement loop is what turns "AI generates a new hour every week" into "the hour was searched": every played hour's forks feed visits back, the next hour's tree is grown from a better-valued prior, and the author's wire stays in force. A training world that survives is one whose forks moved belief and pitched at its cohort — the world is selected, the person is measured.
- Manual refinement becomes steering. The author's time moves from fixing v1 branches to wiring the tree: pin, fence, boost the route, and read the ledger.
9. The honest ledger
| Piece | Status | Where it lives |
|---|---|---|
| Branch tree with fork pointers | shipped | Branch, branch-tree.ts |
| Fan of sisters with mandated divergence | shipped (Futures) | futures.ts, batch runner |
| Fatefulness and orthogonality score | build (fate-exploration §11) | new pure derivation in core/forces |
| Consequence share, open mass | shipped | composition.ts |
Value function Q (§3) | build | pure, over rollout deltas |
Prior P from priorLogit; pin / fence actions | prior shipped; pins build | futures.ts; reducer + ACTION_META |
| Visit / value ledger (§5) | build | derived projection keyed by Branch.id |
| UCT select → expand → simulate → backprop (§2) | build | Tier-2 operation over the scout lane |
| Budget bound, stop rules | shipped shape | AutoConfig, fate-exploration §2 |
| Run report into record health | build | record-health.ts |
| Opt-out per Domain / branch / fork | build | settings + typed deltas |
| Training-world item-information term | build, gated on §5a | rasch.ts (unbuilt) |
Per-branch Iterate sessions (revisions[branchId]), rename, read-iterate, headless iterate-turn (§10) | shipped (2026-09-18) | revision.ts, ops/handlers/iterate.ts, query-catalog.ts |
| Branch-keyed operation lock (sister tips concurrent, one tip ordered) | shipped | ops/client.ts operationTargetLockKey |
| One live Scenario game per branch tip | shipped | ops/handlers/game.ts |
| Windowed / as-of Iterate context; Flags + reach as Iterate cues (§10) | build | iterate-executor.ts buildIterateContext |
| Driver-side turn memory across headless turns (§10) | build | iterate-turn history |
10. Refinement inside the loop — Iterate as the per-node refiner
MCTS as written above moves structure: it fans sisters, rolls deltas forward, and values consequence. A visual novel also needs the material on a surviving branch to be good — scene structure, plan, prose, questions, script — and that is Iterate's job. The full loop a driver runs is therefore
explore (fan sisters at a fork) → refine (Iterate the frontier scene's cells) →
generate (prose / script for the rung it earned) → refine (Iterate against the grader) → explore …
Iterate is the refine step, and it must be addressable per node, which is why an Iterate session is
a property of a branch, not of a Domain: revisions[branchId], one session per tip, sister tips
running concurrently, exactly as Scenario holds one live game per tip. A driver — the shipped UI, an agent
over MCP, or a future MWS operation — works the loop with four moves and nothing else:
| Move | Surface | Jurisdiction |
|---|---|---|
| read the frontier | Tier-0 read-iterate, read-branches, force/flag reads | resolved view of one (domainId, branchId) |
| act on it | Tier-2 iterate-turn { branchId, turn, goal? } | writes only into that branch's session shelf |
| settle it | MERGE_REVISION / DISCARD_REVISION / RENAME_REVISION through dispatch | that branch only; siblings untouched |
| score it | deterministic derivations (§3) over the branch's resolved record | readout, never a prompt |
The same techniques plug in as different explore policies over the same refine step — Reading
probability (a Reading's priced fan), Scenario probability (seats resolving at a table), Flag probability
(which Fate questions collapse) — and MCTS settings (c, d, k, budget) are per-Domain constants; none
of them changes the refine contract. Two boundaries carry over unchanged: refine never chooses (an
Iterate turn improves the material of the branch it was pointed at; selecting which branch is a
participant's or the driver's attributable act), and the score never enters the prompt (§3).
Scalability and context cues — what the audit found (2026-09-18)
Grounded against ops/client.ts, iterate-executor.ts, mcp/catalog.ts, ops/handlers/game.ts:
- Concurrency is already branch-shaped. Effectful operations take a branch-keyed in-process lock: sister tips run concurrently, one tip stays ordered whatever the trigger (cron, MCP, HTTP). Iterate now inherits that. Cross-process ordering (two hosts on one record) still rests on run-id dedup.
- Iterate context grows with the timeline.
buildIterateContextlists every entry in the branch's resolved sequence with its version cells. Linear in scenes; fine at tens, not at hundreds. Needed before deep trees: a window (frontier ± n, as-of cursor) plus cheap read tools over the rest (search_prose,read_flags,read_thread) so grounding costs less than opening every scene. - Cues are thread-level, not flag-level. The Iterate brief carries open Threads and downstream
branch digests but not the branch's Flags, their reach, or which Fate question the frontier scene is
meant to move. Those are exactly the cues a refine step under MWS needs; the Pulse already
derives them (
closure-graph.ts). Add them to the brief as a<causality>block, readout only. - Headless turns are memoryless.
iterate-turnruns each turn with empty history; the revision plan and summary are the only carried state. That is deliberate (the plan is the memory, and it is inspectable) but a driver running many passes should be able to pass a bounded prior-report digest. - Sessions are visible, sessions-of-sessions are not.
read-iterateshows every open session with+N −Nand plan state. There is no ledger yet relating sessions to the visit/value ledger of §5 — when that ledger is built, a merged session should stamp the branch's node so the search can see that a tip was refined, not only that it was rolled out. - Scenario and Iterate must not share a tip blindly. A live game and a live Iterate session on the same branch both write that branch; the branch lock orders them but does not reason about them. The driver should not refine a tip while its Scenario is mid-resolution.
11. Risks, and the test that would falsify this
- Goodhart through the generator. If the value leaks into prompts, the scout learns to write
world-shattering nonsense. Held by §3's readout rule; detectable as
Qrising while reader-observed consequence (visual-novel §10 telemetry) does not. - Convergence to one strand. Too small a
cand the tree deepens one route and starves the fork the reader wanted. Held by keepingcper Domain, reader visits in the exploration term, and the retention floor; detectable as sister count per reachable fork falling belowk. - Rollout depth too shallow to see consequence.
d = 2may miss payoffs that fire at arc 4. Test on the six recorded exports: doesQat depth 2 rank forks the same way the full record does at depth 6? If not, the depth is wrong, not the idea. - Cost drift. A run is bounded by the same budget lane as exploration; the falsifier is a run whose
spend per unit of
Qgained rises across cycles — the tree is saturated and should stop.
The falsifying experiment for the whole spec, cheap and read-only: take one recorded Domain with a deep branch tree, replay MCTS over its existing branches (no new generation — visits and values only, rollouts replaced by what the record already holds downstream), and check whether the forks it would deepen are the ones readers actually diverged at. If the search and the readers disagree about where the world is alive, the value function is wrong and nothing above should be built on it.
12. Open decisions
- Rollout depth
dand the exploration constantc: per Domain constants, or derived from the Domain's settling rate (volume decay, volatility)? - Whether reader visits should carry a decaying weight so an old cohort does not pin the search forever.
- Stake. The reader's counterpart to a scout visit: before a resolving scene, a reader commits a
distribution over a Flag's outcomes; the record scores it in nats when the Flag collapses. It is the one
move that gives a reader something of their own at risk on a table where the prose cost nothing, and it
yields a second reader signal beside the Rasch item (a log score over one's own stakes — calibration,
where the item measures seeing the right move; latent-traits §2 keeps both on the person's record).
Constraints if built: the stake is belief, never money; the house edge is
zero by construction — a reader's expected score rises with calibration and the world takes no cut; it
scores a reading of the world, never a person; it steers visits (§5) and never writes
Q. - Toward the shared tree — the pocket dimension's search. Several seats committing at one fork make the value a vector (one entry per seat) and selection per seat (multi-agent / max-n MCTS); many timelines held live at once is tree parallelisation with virtual loss, and the visit distribution over branches is the posterior over which timeline the world is in — the quantity a Reading already prices; with generation in the loop, nodes group by decision sequence rather than exact state (open-loop search), which is what makes rejoin and bottleneck cheap. Boundary unchanged and harder: the search prices seats' joint futures, never chooses for any seat, and one seat's visits never write another seat's value. This is the form MWS takes under a populated World (world roadmap); it is not scheduled before a bounded multi-seat run has been played. Before seats, the same search grows the shared multiverse: timeline-dated VN releases cut from earned tips, seeds founding new Domains, and the Settled Layer a universe's surviving branches agree on (world tree).
- How a fenced branch reads to a reader who already committed to it — invisible in new readings, preserved in saves is the default.
- Whether the run schedule is "between readings" (idle) or "after each reading" (event), and who pays when a Hosted tenant has many readers and no idle.
- The exact shape of the ledger entry and where it surfaces beyond record health.