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, animation_pending, has_animated, has_static_mesh, static_missing) Pure query Return action, queue-rotation and serial-transition values Any thread; call-local result Raw booleans/count are accepted; 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 Animation request pending Animation pipeline through loader Dispatch planner Boolean copy One call
Input Animated prototype available Prototype cache/native loader adapter Dispatch planner Boolean copy; no Node reference One call
Input Static Mesh available Mesh cache/request adapter Dispatch planner Boolean copy; no Mesh reference One call
Input Static model terminally missing Prototype cache through loader Dispatch planner Boolean copy 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(observed scalar state)
    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 --> 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.
  • 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 typed service while preserving this scalar action contract. 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

  • Planner consumes raw booleans rather than a typed resource snapshot.
  • 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/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