Files
open-wc/docs/modules/m2-build-dispatch-planner.md
T

11 KiB

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

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

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

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

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

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