The Job
Knowing what you believe is half a loop. The other half is knowing where to look next, and most agents answer it by vibes. Moves answer it off the graph. Each one names a specific belief, gap, or contradiction and estimates how much acting on it would sharpen the agent's picture of its world, scored against where the agent is trying to go.
A move is judgment pointed somewhere: the engine reading the graph and naming where attention pays off most. The top move is the one that buys the most certainty per step toward the goal, so a loop that reads it never has to guess at its own next step.
In the world-model frame this is the planning slice. Clarity tells the agent whether it has enough to act; moves tell it what to do when it doesn't.
What a Move Is
A move is a recommended action the engine surfaces from the current belief state. It carries an action type, a finer subtype, a target, a reason in plain language, an expected value, and an executor.
1{
2 action: 'gather_evidence',
3 subType: 'research',
4 target: 'Missing APAC market analysis',
5 reason: 'High-impact gap with 3 downstream dependencies',
6 value: 0.72,
7 executor: 'agent',
8}- action. The kind of move:
clarify,resolve_uncertainty,gather_evidence,compare_paths. - target. The belief, gap, or contradiction the move addresses. Every move anchors to one, so it is always replayable back to the claim it would inform.
- reason. Why it matters, in natural language.
- value. The uncertainty the move would drain if it resolves, on a 0-1 scale. Higher means a bigger shift in the picture.
- executor. Who should act:
agent,user, orboth.
The action is the routing key. It is a small, closed set, which is what makes the loop below a clean switch. Finer dispatch lives on the subType: validate_assumption, resolve_contradiction, quantify_risk, design_test, research, synthesize, reframe. So a gather_evidence move with subType: 'research' tells the loop both the class of action to take and the exact shape of it. Route on action; branch on subType when you need the detail.
| Action | When it surfaces |
|---|---|
gather_evidence | A gap or thinly supported belief needs investigation |
clarify | A contradiction sits between two beliefs |
resolve_uncertainty | A load-bearing belief is short on evidence |
compare_paths | Several valid interpretations need a decision framework |
Ranking Is by Impact, Not Count
The engine does not surface the most moves. It surfaces the most consequential one first. A gap with three downstream dependencies outranks an isolated gap. A contradiction between two load-bearing beliefs (the ones the rest of the strategy rests on) outranks a contradiction between peripheral claims that nothing leans on. The cheap isolated fix loses to the structural one.
That ordering is the value-of-information calculation. Four signals feed it:
1┌────────────────────────────────────────────────────────────────┐
2│ WHAT FEEDS THE RANKING │
3│ │
4│ 1. EXPECTED RESOLUTION - how much uncertainty drains if │
5│ the move resolves │
6│ 2. DEPENDENCY WEIGHT - how many beliefs lean on the │
7│ target │
8│ 3. LOAD-BEARING - whether the target holds up │
9│ everything above it │
10│ 4. CLARITY LIFT - which move would raise clarity │
11│ the most │
12│ │
13│ Scored against intent: the top move is the one most in │
14│ the way of the goal. │
15│ │
16└────────────────────────────────────────────────────────────────┘Because moves are scored against the active goal, the ranking is goal-conditioned. The same belief state pursuing a different intent surfaces different top moves. The engine is not asking "what is most uncertain," it is asking "what uncertainty is most in the way of where I am trying to go."
Reading Moves
The top move rides back on the turn result, so most loops never need a separate ranking call.
1// Before the agent acts
2const context = await beliefs.before(userMessage)
3console.log(context.moves[0]) // the single best next action this turn
4
5// After the agent acts
6const delta = await beliefs.after(result.text)
7console.log(delta.moves) // updated recommendations
8
9// Full world state, on demand
10const world = await beliefs.read()
11console.log(world.moves) // all current movesWhen you want an explicit ranked list, ask for one:
1const ranked = await beliefs.moves.rank({ topN: 5 })Routing on Moves
Switch on action to steer the loop. Because the action set is small and closed, the branch stays exhaustive:
1const delta = await beliefs.after(result.text)
2const next = delta.moves[0]
3
4if (!next) {
5 // Nothing worth chasing. Clarity is high enough to finalize.
6 await finalize(delta)
7} else if (next.action === 'gather_evidence') {
8 await gatherEvidence(next.target) // next.subType narrows it: 'research', 'design_test', ...
9} else if (next.action === 'clarify') {
10 await resolveContradiction(next.target)
11} else if (next.action === 'resolve_uncertainty') {
12 await deepDive(next.target)
13} else if (next.action === 'compare_paths') {
14 await presentTradeoffs(next.target)
15}The Executor Keeps the Agent in Its Lane
Not every move belongs to the agent. The executor field says who should act on it.
| Executor | Meaning |
|---|---|
agent | The agent can handle this on its own |
user | Needs human input or judgment |
both | The agent can start; the user has to weigh in |
A user move surfaces when the engine reads the target as a value judgment or strategic call the agent should not make alone. The point is to route to a human at that boundary rather than guess past it. The engine marks where the agent's authority runs out so the loop does not quietly cross a line it was never given.
Moves Versus Affordances
Keep two surfaces distinct. Actions are affordances: the declared set of verbs the agent can enact in this world, each carrying a safety class. Moves are the engine's epistemic next steps, the reasoning about where attention pays off most. A move often points at the gap an affordance would close, yet the two answer different questions: what am I allowed to do here, versus where is it worth looking. Read the move to find the open question, read the affordances to find the legal way to close it.
Forecasting a Move Forward
A move's value scores the immediate gain. As act-to-outcome history accumulates, the engine can also project that value a few steps forward.
1const forecast = await beliefs.moves.forecast(action)
2const projection = await beliefs.forecast.predict(actions)moves.forecast(action) projects one move's value across the next few steps; forecast.predict(actions) does it across a set. Both are live, and both report low confidence on a fresh workspace: the projection only earns trust once there is enough history linking actions to the outcomes they produced. On day one you get the immediate ranking and an honest "not enough history yet" on the look-ahead. The forecast sharpens as the agent acts and the engine watches what each action actually moved.
World
The full state: beliefs, edges, clarity, and moves together.
Beliefs
The claims and posteriors a move is scored against.
Intent
Goals and gaps. The destination moves are ranked toward.
Clarity
Whether there is enough on the table to act yet.
Actions
The declared affordance surface, distinct from epistemic moves.
Worldview
The bounded operating model moves are one slice of.
