# Streaming Target Planner

## Metadata

| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | `M03-RND-STREAMING-PLANNER-001` |
| Owners | Renderer workstream / M03 integrator |
| Last verified | `e887c3b`, 2026-07-15 |
| Profiles/capabilities | Runtime and Editor preview ADT target selection; profile values supplied by caller |

## Purpose

Calculate which available ADT tiles are wanted and retained for one typed focus
without accessing the SceneTree, worker tasks, renderer queues, caches or GPU
resources. Runtime planning preserves prewarm, hysteresis and boundary-prefetch
behavior; editor planning preserves the square preview radius.

## Non-goals

- Mutate load/finalize/eviction queues.
- Prioritize queued tile paths by world distance.
- Calculate chunk or tile LOD levels after a tile is loaded.
- Own the available WDT tile catalog or cache paths.
- Create, cancel or await jobs.
- Implement M2, WMO, liquid or terrain attachment.

## Context and boundaries

```mermaid
flowchart LR
    Focus[GodotWorldPosition] --> Planner[StreamingTargetPlanner]
    Policy[StreamingTargetPolicy] --> Planner
    Catalog[Available tile keys from WDT/directory] --> Planner
    Planner --> Plan[StreamingTargetPlan]
    Plan --> Streamer[StreamingWorldLoader queue/state apply]
    Streamer --> Queue[Tile load queue]
```

Allowed dependencies:

- typed coordinate values and `CoordinateMapper`;
- immutable planner policy/result contracts;
- read-only available-tile key membership.

Forbidden dependencies:

- `Node`, SceneTree and viewport/camera discovery;
- `WorkerThreadPool`, `ResourceLoader` and filesystem access;
- `RenderingServer`, meshes, materials, nodes and RIDs;
- gameplay, network, UI, editor UI or server adapters;
- streamer queues, tile states and cache dictionaries.

## Public API

| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `StreamingTargetPlanner.plan_runtime` | Pure method | Builds wanted/retained sets with edge/corner prefetch | Any thread if caller does not mutate catalog concurrently; call-scoped | Empty catalog produces empty sets |
| `StreamingTargetPlanner.plan_editor` | Pure method | Builds identical editor wanted/retained square sets | Same as runtime planning | Negative radius clamps to zero |
| `StreamingTargetPolicy` | Immutable value | Captures radius margins and boundary threshold for one plan | Caller-owned, shareable | Values retain scene input; effective margins/threshold are clamped |
| `visible_tile_radius` | Pure query | Resolves chunk-radius versus tile-radius visibility | Any thread/value lifetime | No failure |
| `load_tile_radius` | Pure query | Adds non-negative prewarm margin | Any thread/value lifetime | No failure |
| `retained_tile_radius` | Pure query | Adds non-negative hysteresis margin | Any thread/value lifetime | No failure |
| `StreamingTargetPlan` | Immutable value | Holds focus tile and read-only wanted/retained key sets | Caller-owned plan lifetime | No failure |
| `wanted_tile_keys` | Read-only query | Returns immutable wanted set | Plan lifetime | Mutation is rejected by Godot read-only Dictionary |
| `retained_tile_keys` | Read-only query | Returns immutable retained set | Plan lifetime | Mutation is rejected by Godot read-only Dictionary |

## Inputs and outputs

| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | `GodotWorldPosition` | `WorldRenderFacade`/streamer focus adapter | Planner | Immutable caller value | One plan |
| Input | Available `<tile_x>_<tile_y>` keys | `StreamingWorldLoader` WDT/directory catalog | Planner membership checks | Loader-owned, read-only during call | Map session |
| Input | `StreamingTargetPolicy` | Streamer configuration snapshot | Planner | Immutable caller value | One or more plans |
| Input | Editor preview radius | Editor scene configuration | Planner | Scalar copy | One plan |
| Output | `StreamingTargetPlan.focus_tile` | Planner/`CoordinateMapper` | Streamer diagnostics | Immutable typed coordinate | Plan lifetime |
| Output | Wanted tile-key set | Planner | Streamer queue/state apply | Plan-owned read-only Dictionary | Plan lifetime |
| Output | Retained tile-key set | Planner | Streamer hysteresis/state apply | Plan-owned read-only Dictionary | Plan lifetime |

Side effects:

- none;
- no input mutation;
- no logging, I/O, task submission, cache access, SceneTree or GPU work.

## Data flow

```mermaid
flowchart TD
    Position[GodotWorldPosition] --> Tile[CoordinateMapper.godot_to_adt_tile]
    Position --> Local[CoordinateMapper.godot_to_adt_tile_local]
    Policy[StreamingTargetPolicy] --> Radii[load and retained radii]
    Policy --> Threshold[clamped boundary threshold]
    Tile --> Centers[base prefetch center]
    Local --> Centers
    Threshold --> Centers
    Centers --> Wanted[available keys within load radius]
    Centers --> Retained[available keys within retained radius]
    Catalog[Available tile keys] --> Wanted
    Catalog --> Retained
    Wanted --> Plan[StreamingTargetPlan]
    Retained --> Plan
    Tile --> Plan
```

## Lifecycle/state

The planner is stateless. A call creates local center/set dictionaries, freezes
the result dictionaries in `StreamingTargetPlan`, returns the plan and retains
no reference to inputs or output. There is no retry, cancellation or shutdown
state machine.

## Main sequence

```mermaid
sequenceDiagram
    participant Stream as StreamingWorldLoader
    participant Planner as StreamingTargetPlanner
    participant Mapper as CoordinateMapper
    participant Plan as StreamingTargetPlan
    Stream->>Planner: plan_runtime(catalog, typed focus, policy)
    Planner->>Mapper: focus tile and tile-local position
    Mapper-->>Planner: typed coordinates
    Planner->>Planner: prefetch centers and available key sets
    Planner->>Plan: freeze focus/wanted/retained result
    Plan-->>Stream: immutable target plan
    Stream->>Stream: apply queues, LOD and detail state
```

## Ownership, threading and resources

- `StreamingWorldLoader` owns the available catalog and must not mutate it during a planning call.
- `StreamingTargetPolicy` and typed focus remain caller-owned immutable values.
- The planner owns temporary center/set values only until plan construction.
- `StreamingTargetPlan` owns its read-only result dictionaries.
- The planner creates no Node, Resource, RID, thread, mutex or task.
- Current runtime calls occur on the main thread; scene-free contracts permit a
  future worker call only after catalog publication/ownership is explicit.

## Errors, cancellation and recovery

| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty/unavailable catalog | No membership matches | Valid plan with empty sets | Streamer retains existing missing-data diagnostics | Load a valid WDT/directory catalog and replan |
| Focus outside catalog/grid | Typed coordinate does not match keys | Valid focus tile with filtered empty/partial sets | Caller diagnostic if required | Supply valid focus/map or catalog |
| Negative prewarm/retain margin | Policy effective-radius query | Margin clamps to zero | Contract test | Correct configuration if negative value was unintended |
| Boundary threshold outside range | Policy query | Clamp to `0.0..0.49` | Contract test | Correct configuration or accept safe clamp |
| Plan no longer current | New focus/config/catalog revision | Caller discards ephemeral plan | Streamer refresh diagnostics | Replan from current inputs |

Cancellation is not applicable: planning is synchronous, bounded and owns no
external work. A returned stale plan is simply not retained by the planner.

## Configuration and capabilities

| Setting/capability | Default source | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| `lod2_radius_chunks` | Streamer scene/profile | Renderer | Yes at composition/debug boundary | Converts chunk coverage to minimum ADT radius |
| `lod2_tile_radius` | Streamer scene/profile | Renderer | Yes | Minimum visible ADT radius |
| `warm_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends chunk-derived visible radius |
| `prewarm_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends wanted/load radius |
| `retain_tile_margin` | Streamer scene/profile | Renderer | Yes | Extends retained hysteresis radius |
| `boundary_prefetch_threshold` | Streamer scene/profile | Renderer | Yes | Adds adjacent/corner prefetch centers near tile boundaries |
| Editor preview radius | Editor scene | Authoring preview | Yes | Square wanted/retained preview set |

The planner does not select `Blizzlike335` or `Enhanced`; it applies the values
already selected by renderer composition.

## Persistence, cache and migration

- Planner contracts are runtime-only and are not serialized.
- Existing tile keys and cache paths are unchanged.
- No cache/resource/schema version bump or migration is required.

## Diagnostics and observability

- Logs remain emitted by `StreamingWorldLoader` after applying a plan.
- `StreamingTargetPlan` exposes focus/wanted/retained data for deterministic tests.
- Dedicated verifier reports case count, iteration count, total time and average plan time.
- No correlation ID is required for synchronous ephemeral plans.

## Verification

- Unit/contract: `src/tools/verify_streaming_target_planner.gd` covers center
  radii, edge/corner cross product, catalog filtering, clamps, editor behavior,
  input/result ownership and scene-free dependencies.
- Integration: streamer source gate confirms both runtime/editor delegation and
  removal of `_predictive_focus_tiles` from the monolith.
- Fidelity evidence: expected sets encode the pre-extraction formulas; renderer
  checkpoint dry-run must retain all seven checkpoint plans.
- Performance budget: 250 synthetic High-like corner plans average at most
  `4.0 ms` each on the headless regression runner; measured result is recorded
  in test output.
- Manual diagnostics: unchanged `StreamingWorld` focus/wanted/queued log.

## Extension points

- Direction/velocity/frustum priority can extend policy/input only with fixtures
  and a compatibility-safe default reproducing the current position-only plan.
- Multi-focus planning may union additional typed centers without giving the
  planner ownership of queues or consumers.
- Queue ordering remains a separate scheduler/application responsibility.

## Capability status

| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Runtime wanted/retained sets | Implemented | Center/corner/catalog fixtures | Add paired traversal performance evidence |
| Boundary prefetch centers | Implemented | Edge/corner cross-product fixture | Direction/velocity prediction not implemented |
| Editor preview set | Implemented | Negative/radius-one fixtures | Editor viewport integration remains existing adapter |
| Scene-free deterministic execution | Implemented | Dependency/source gate and 250-iteration run | Worker execution is unnecessary today |
| Queue priority/LOD | Not in module | Preserved in streamer | Separate extraction packages |

## Known gaps and risks

- Tile-key sets preserve the streamer's internal string representation; a typed
  catalog migration is deferred until it can remove, rather than duplicate, that source of truth.
- Planner time scales with the union of square radii/prefetch centers. The
  current bounded regression covers High-like corner input but not pathological custom radii.
- Exact p95/p99 frame impact still requires renderer baseline runs with local assets.
- Direction, velocity and camera frustum do not influence target selection yet.

## Source map

| Path | Responsibility |
|---|---|
| `src/render/streaming/streaming_target_planner.gd` | Pure runtime/editor set calculation and boundary centers |
| `src/render/streaming/streaming_target_policy.gd` | Immutable radius/threshold snapshot and effective clamps |
| `src/render/streaming/streaming_target_plan.gd` | Immutable typed focus and read-only result sets |
| `src/scenes/streaming/streaming_world_loader.gd` | Planner composition and plan application to queues/state |
| `src/tools/verify_streaming_target_planner.gd` | Asset-free behavior/dependency/performance regression |

## Related decisions and references

- [`world-renderer.md`](world-renderer.md)
- [`../../targets/03-renderer-facade.md`](../../targets/03-renderer-facade.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
- [`../GODOT_BEST_PRACTICES.md`](../GODOT_BEST_PRACTICES.md)
- [`../CODING_STANDARD.md`](../CODING_STANDARD.md)
