Observed-target planning
September 18, 2026 · View on GitHub
Session(planning=True) enables an experimental, persistent task choice before
the ordinary unit-action choice. Planning proposes a task and observed target;
it executes no input and supplies no path. planning=False is the default and
retains the baseline policy.
| Behavior | Baseline | Planning enabled |
|---|---|---|
| Selected unit | Jev chooses one current legal command | Jev first chooses a task if the unit has no active plan, then independently chooses one current legal command |
| Later observations | Recent orders and fresh player-observable state | The same observations, plus that unit's active Jev-selected task |
| Command candidates | Existing canonical unit actions | The same canonical unit actions; the plan does not force a direction or filter alternatives |
| Task review | No persistent task | Observed completion, invalidation or bounded expiry; a new task is requested when that unit is next selected |
| Evidence | Command choices and input receipts | Separate planning choices/status events, plus unchanged command choices and input receipts |
python -m civ2.run uses the baseline by default. Add --planning to enable
observed-target planning and --port 3921 for a separately prepared runtime.
Once that runtime has loaded the matching original initial save, use:
python3 -m civ2.run --planning --port 3921 \
--initial-save .runtime/setup/initial.sav --directory runs/planning-attempt \
--env-file /path/to/private/env
The same options are available through Python:
from civ2.engine import Game
from civ2.run import run_steps
from civ2.session import Session
session = Session(
"runs/planning-attempt", ".runtime/setup/initial.sav",
game=Game(port=3921), # A separately prepared runtime for this attempt.
env_file="/path/to/private/env", planning=True,
)
outcome = None
try:
outcome = run_steps(session, max_decisions=10000)
finally:
session.finish(reason=(outcome or {}).get("reason", "Controller stopped; inspect evidence."))
Use separate runtime ports and evidence directories for parallel attempts. A comparison should record the original initial-save hash, settings, model and budgets; matching settings alone do not establish identical starting worlds. No complete-game win or strategic advantage from planning has been verified.
Decision and evidence contract
The session stores each planning request and raw response under a unique
decision ID, records model_plan, then records plan_status at creation and
subsequent native checkpoints. Planning IDs never enter pending command/effect
batches. The later unit_action request receives state.persistent_plan and
independently binds its chosen command to the fresh save and selected actor.
It may detour, wait or choose another legal action. Even a Hold task does not
automatically dispatch Skip.
The dashboard displays the real task_choice vector as an objective choice,
with stage="planning", executes_input=false and no command receipt. The next
action has its own real distribution. Do not multiply these independent stage
probabilities or present either as a win prediction. Session.ledger() and the
stopped-session event distinguish started model calls, returned planning
decisions, returned command decisions and active plans.
Every unit order remains independently model-selected. Saving, opening already selected native menus and acknowledging verified informational dialogs retain their explicit mechanical handlers; a plan authorizes none of these inputs.
Pure planning helpers
Use request_for(state, rules, recent_actions=None, limit=64) to obtain a Jev
request and canonical candidates. The sole task_choice selects a task and
target. Pass that actual selected candidate to
make_plan(candidate, state, rules, limit=64, max_turns=8), then include the active
plan in a later unit-action request. The session performs this integration;
the helpers themselves do not call Jev or the game.
Candidates cover known frontiers, observed Plains/Grassland settlement proposals, rule-compatible roads/irrigation/mines, friendly-city defense for military land units, currently visible units belonging to declared enemies or barbarians, approaching remembered foreign cities with military land units, observed-city trade delivery and current owned-wonder assistance for Caravan/Freight units, and Hold for one turn. Caravan objectives do not guess supply, demand, revenue or shield contribution; every movement and original arrival choice remains independent. Approaching a remembered city reveals no hidden owner or garrison and authorizes no attack: the task ends nearby so Jev reassesses current diplomacy and visible threats through its ordinary choices. Original-game legality and reachability remain authoritative; these are known-compatible proposals, not guaranteed legal or safe destinations. City-center improvements are omitted. No unknown terrain, foreign orders or private rival state is used.
task_candidates(state, rules, limit) returns (candidates, summary). The limit
is 2–255. Hold is reserved; other categories rotate, each ordered by geometric
distance and coordinates/ID. The summary reports omitted counts by task. This
bounded, local ordering can omit worthwhile distant targets; it is not an
optimal-site ranking or a complete map search. A singleton never gets a fake
Choice distribution.
After each fresh native checkpoint, call
advance_plan(plan, before, after, action=None, rules=None). Supply the exact
canonical unit action when this actor moved; unexpected movement invalidates.
The tracker accepts only one matching observed actor, and conservatively
invalidates on removed roster slots/possible ID compaction. It does not remap
unit IDs. New slots may be added, but an indistinguishable stacked actor causes
invalidation. Save observations cannot prove the absence of an otherwise
indistinguishable death/replacement; plans provide context, never input binding.
The ordinary action still binds the fresh save and selected actor independently.
Completion means an observed result: a new owned city at the target, a reported improvement, newly observed terrain beside a frontier, or an elapsed one-turn hold. Arrival alone does not found a city. Defender tasks persist until review; an enemy leaving visibility invalidates its target without implying destruction. Plans expire after the helper's configured 1–20-turn interval (default 8) or 32 checkpoint observations. The session uses these defaults. Expiration never selects an order; the next selection of that unit permits a new Jev task choice.
Long-game limits
- The 32-observation bound counts every native checkpoint, including other units' orders and empire menu reviews. A large empire can therefore review tasks before eight turns elapse. Each newly selected task adds a model call.
- A removed roster slot can mean ID compaction, so it invalidates other active plans conservatively. Identical stacked actors, unexplained movement and changed actor fingerprints also cause review rather than guessed identity.
- Task targets are capped at 64 in the session and ordered locally. Reachable distant goals may be omitted, while included goals may have no useful route. There is no route solver or automatic progress toward the target.
- Plans are per unit. The action request includes that unit's task, not a shared assignment ledger for every other unit. Multiple Settlers can choose competing sites, and all ordinary action alternatives remain available.
run_steps(max_decisions=...)counts both stages, not executed orders. Its outer-loop check can be crossed by one command decision after a planning decision. The separate API request cap still applies. Compare command counts and token usage as well as total model calls.- Plans persist within the live
Sessionand are recorded for audit; no automatic plan restoration after a process restart is implemented.
Tests cover offline choice contracts, stage accounting and conservative continuity. They do not establish that the resulting strategy can win.
Destination arithmetic
The unit-action request includes the geometric distance from its current location and every offered movement destination to its existing Jev-selected target. This helps expose back-and-forth movement without requiring the model to decode the doubled-x coordinates. Distances respect horizontal world wrapping. The harness does not sort commands by distance, supply a route, or force progress: terrain and tactical circumstances can justify a detour. Hold plans and actors whose current fingerprint differs from the plan receive no distance context.
For ground units, it also reports the fewest adjacent edges through observed land to the target. This makes known-water detours visible. Unknown tiles are excluded; a missing connection is not a claim that a route is impossible. The calculation ignores roads, movement costs, units, zones of control, treaties and transport. All original actions remain available to Jev.
In development attempt 010, one scout oscillated between (52,4) and (53,5)
while its target (50,6) required a six-edge known-land detour. The scout left
that loop at decisions 148 and 154, before the land-distance context first
appeared at decision 168. Subsequent travel therefore does not establish that
the new context caused the improvement; its previous plan also changed.
Category and target choices
The optional task-category-target-v1 mode asks Jev to choose a task category
before choosing its destination. This prevents a singleton Hold option from
competing directly with dozens of separate settlement or worker targets. Both
stages use actual model responses, preserve the original candidates and execute
no game input. See the contracts, retry behavior and audit checks.