# M2 Build Dispatch Planner ## Metadata | Field | Value | |---|---| | Status | Implemented extraction | | Target/work package | M03 / `M03-RND-M2-BUILD-DISPATCH-PLANNER-001` | | Owners | Pure M2 resource-state to build-action decision | | Last verified | Worktree `work/sindo-main-codex/m03-m2-build-dispatch-planner`, 2026-07-18 | | Profiles/capabilities | Existing static MultiMesh and animated-instance build paths | ## Purpose Select one M2 build action after the loader observes animation/static resource state. The planner makes wait priority, materializer selection and the historical missing-model serial transition explicit without owning resources or engine work. ## Non-goals - Locate, request, load, cache or validate M2 resources. - Size batches, own build jobs/queues or consume renderer permits. - Create, attach or free Nodes, Meshes, MultiMeshes or animated instances. - Change transforms, ordering, render settings, profiles or visible output. - Add spatial-cell batching or M2 format/fidelity behavior. ## Context and boundaries ```mermaid flowchart LR Loader[StreamingWorldLoader resource observations] --> Planner[M2BuildDispatchPlanner] Planner --> Action[Detached action and transition plan] Action --> Loader Loader --> Queue[M2BuildQueue rotate/progress] Loader --> Animated[M2AnimatedInstanceMaterializer] Loader --> Static[M2StaticBatchMaterializer] Loader --> Budget[RenderBudgetScheduler] ``` The planner depends only on `RefCounted`, scalar values, `StringName` constants and a fresh Dictionary. ResourceLoader, caches, Nodes, Meshes, RenderingServer, SceneTree, files, workers, mutexes, gameplay, network and Editor APIs are forbidden. ## Public API | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `ACTION_WAIT_FOR_ANIMATION` | Constant | Pending animation request blocks all other actions | Immutable | None | | `ACTION_MATERIALIZE_ANIMATED` | Constant | Build the selected slice from animated prototype | Immutable | None | | `ACTION_WAIT_FOR_STATIC_MESH` | Constant | Static resource is unresolved and queue must rotate | Immutable | None | | `ACTION_MATERIALIZE_STATIC` | Constant | Build the selected slice from prepared static Mesh | Immutable | None | | `ACTION_ADVANCE_WITHOUT_MATERIALIZATION` | Constant | Empty or terminally missing slice advances without a Node | Immutable | None | | `plan_step(batch_count, resource_snapshot)` | Pure query | Return action, queue-rotation and serial-transition values | Any thread; call-local result | Null snapshot waits for static Mesh; non-positive count advances | ## Inputs and outputs | Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime | |---|---|---|---|---|---| | Input | Planned batch count | `M2BuildBatchPlanner` through loader | Dispatch planner | Integer copy | One call | | Input | Typed animated/static availability, pending and missing observations | Loader / `M2BuildResourceSnapshot` | Dispatch planner | Borrowed snapshot; engine references are not read | One call | | Output | Action | Dispatch planner | Loader materializer/wait branch | Immutable StringName | One operation | | Output | `rotate_queue` | Dispatch planner | Loader queue adapter | Detached boolean | One operation | | Output | `increment_batch_serial` | Dispatch planner | Loader progress adapter | Detached boolean | One operation | The output Dictionary is newly allocated and caller-owned. The planner retains no state, resource or engine-object reference. ## Data flow ```mermaid flowchart TD Input[Observed state] --> AnimationPending{Animation request pending?} AnimationPending -->|yes| WaitAnimation[Wait animation; rotate] AnimationPending -->|no| Positive{Batch count positive?} Positive -->|no| AdvanceEmpty[Advance; keep serial] Positive -->|yes| Animated{Animated prototype?} Animated -->|yes| BuildAnimated[Materialize animated; increment serial] Animated -->|no| StaticReady{Static Mesh ready?} StaticReady -->|yes| BuildStatic[Materialize static; increment serial] StaticReady -->|no| Missing{Model terminally missing?} Missing -->|yes| AdvanceMissing[Advance without Node; increment serial] Missing -->|no| WaitStatic[Wait static Mesh; rotate] ``` ## Lifecycle/state ```mermaid stateDiagram-v2 [*] --> Observe Observe --> Waiting: animation/static unresolved Observe --> Materializing: animated/static resource ready Observe --> Advancing: empty or terminally missing batch Waiting --> [*]: detached wait plan Materializing --> [*]: detached materialization plan Advancing --> [*]: detached advance plan ``` The planner itself is stateless. Queue rotation, cursor adoption, cancellation, map reset and shutdown remain external lifecycle transitions. ## Main sequence ```mermaid sequenceDiagram participant L as StreamingWorldLoader participant B as M2BuildBatchPlanner participant D as M2BuildDispatchPlanner participant Q as M2BuildQueue participant M as M2 materializer participant S as RenderBudgetScheduler L->>L: observe animation request/prototype alt animation not pending L->>B: plan_batch L->>L: lookup/request static Mesh when required end L->>D: plan_step(batch count, resource snapshot) D-->>L: action, rotate flag, serial flag alt wait action L->>Q: rotate_front else materialize action L->>M: materialize selected slice L->>Q: adopt progress and serial else advance action L->>Q: adopt progress and optional serial end L->>S: consume one M2_BUILD permit ``` ## Dependency diagram ```mermaid flowchart TB Loader[StreamingWorldLoader] --> Dispatch[M2BuildDispatchPlanner] Loader --> Batch[M2BuildBatchPlanner] Loader --> Queue[M2BuildQueue] Loader --> Caches[M2 cache/pipeline state] Loader --> Materializers[M2 materializers] Dispatch --> Snapshot[M2BuildResourceSnapshot accessors] Dispatch --> Values[Integers + booleans + StringName] Dispatch -. no dependency .-> Engine[Node / Mesh / ResourceLoader] Dispatch -. no dependency .-> State[Queue / cache / scheduler state] ``` ## Ownership, threading and resources - Planner owns call-local comparisons and the returned Dictionary only. - `M2BuildResourceSnapshot` owns the typed call-local observation contract; dispatch reads availability/pending/missing accessors but no engine references. - Loader owns resource lookup/request order, action execution and permit use. - `M2BuildQueue` owns pending jobs, FIFO keys and progress cursors. - Materializers own main-thread scene construction under loader-owned roots. - Pure calls are thread-safe; the current loader adapter calls on main thread. ## Errors, cancellation and recovery | Failure/state | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Animation request pending | Boolean observation | Wait wins even if other flags are true | Priority fixture | Retry when queue returns front | | Non-positive batch | Count comparison | Advance without serial increment | Zero/negative fixtures | Next group/cursor operation | | Static Mesh unresolved | No Mesh and not missing | Rotate without serial increment | Retry fixture | Loader request/cache completes | | Static model missing | Terminal missing flag | Advance without Node and increment serial | Missing fixture | Later group continues | | Contradictory static ready/missing | Both true | Ready Mesh wins | Priority fixture | Loader normally prevents contradiction | | Tile cancellation/root invalid | Outside planner | Loader cancels before dispatch | Queue/shutdown regressions | Eligible tile may requeue | ## Configuration and capabilities The planner adds no setting. Existing animated/static batch limits, animation enable flag, visibility/shadow settings and `M2_BUILD` permits remain external. ## Persistence, cache and migration No state is serialized. No ADT/M2 cache format or version changes; no rebake or migration is required. ## Diagnostics and observability The planner emits no logs. Its verifier reports the action matrix, source boundaries and bounded 20,000-call timing. Existing loader queue/hitch metrics remain unchanged. ## Verification - `verify_m2_build_dispatch_planner.gd` covers wait priority, zero/negative counts, animated/static priority, unresolved retry, terminal missing, detached results, loader/dependency boundaries and bounded timing. - Queue, batch planner, materializers, caches, shutdown, facade, internal-access and checkpoint tests protect adjacent behavior. - Fidelity evidence is exact branch/transition extraction only. No private asset or original-client visual comparison is claimed. ## Extension points A later package may move resource observation/request execution behind a service that produces `M2BuildResourceSnapshot`. Generic callbacks, signals and a shared state-machine framework remain intentionally excluded. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | M2 build action selection | Implemented extraction | Priority/matrix/source/timing verifier | Asset-backed traversal pending | | Resource observation and requests | Loader-owned | Cache/pipeline regressions | Typed orchestration extraction pending | | Queue/cursor state | Implemented separately | M2 build queue verifier | Asset-backed traversal pending | | Materialization | Implemented separately | Static/animated materializer verifiers | GPU/p95/p99 evidence pending | ## Known gaps and risks - Loader still performs synchronous/native prototype attempts and static request I/O. - Synthetic timing does not measure ResourceLoader, Node or GPU work. - Private traversal, leak, visual and p95/p99 evidence remains unavailable. ## Source map | Path | Responsibility | |---|---| | `src/render/m2/m2_build_dispatch_planner.gd` | Pure action priority and transition plan | | `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step resource observation contract | | `src/scenes/streaming/streaming_world_loader.gd` | Resource observation, action execution, permits and engine lifetime | | `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor plan | | `src/render/m2/m2_build_queue.gd` | Pending jobs, FIFO and cursor ownership | | `src/tools/verify_m2_build_dispatch_planner.gd` | Matrix, boundary and timing regression | ## Related decisions and references - [`m2-build-batch-planner.md`](m2-build-batch-planner.md) - [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md) - [`m2-build-queue.md`](m2-build-queue.md) - [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md) - [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.md) - [`world-renderer.md`](world-renderer.md) - [`../../RENDER.md`](../../RENDER.md) - [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)