# WMO Placement Registry ## Metadata | Field | Value | |---|---| | Status | Implemented extraction | | Target/work package | M03 / `M03-RND-WMO-PLACEMENT-REGISTRY-001` | | Owners | WMO placement-key to tile/global reference-set ownership | | Last verified | Worktree `work/sindo-main-codex/m03-wmo-placement-registry`, 2026-07-17 | | Profiles/capabilities | Existing ADT/WDT WMO placement paths; profile-independent | ## Purpose Own the reference sets that keep one resolved WMO placement active across one or more ADT tiles, or across the global WDT placement lifetime. This separates value-only placement ownership from scene and render-resource ownership. ## Non-goals - Resolve WMO paths, placement identity or world transforms. - Own Nodes, Resources, RIDs, roots, jobs, queues, tasks or caches. - Load, instantiate, build or free WMO content. - Change duplicate order, budgets, retries or global-WMO behavior. - Claim original-client WMO visual, portal, room or material parity. ## Context and boundaries ```mermaid flowchart LR Parsed[ADT/WDT placement] --> Resolver[WmoPlacementResolver] Resolver --> Key[Placement key] Tile[Tile/global reference key] --> Loader[StreamingWorldLoader] Key --> Loader Loader --> Registry[WmoPlacementRegistry] Registry --> Decision[Existing or final-release decision] Decision --> Loader Loader --> NodeMap[Loader key-to-Node map] Loader --> Jobs[Loader jobs/queues/resources] ``` The registry depends only on `RefCounted`, `Dictionary`, `Array` and `String`. SceneTree, Node, Resource, RID, files, loaders, threads, locks, network, gameplay and editor UI are forbidden dependencies. ## Public API | Symbol | Kind | Purpose | Thread/lifetime | Errors | |---|---|---|---|---| | `add_reference(placement_key, reference_key)` | Command/query | Idempotently add a reference; return true only when creating the placement key | Renderer main thread; map session | Empty key returns false and changes nothing | | `release_reference(placement_key, reference_key)` | Command/query | Remove an owned reference; return true only on final removal | Renderer main thread; map session | Empty, unknown or non-owner input returns false | | `contains(placement_key)` | Query | Test whether any reference keeps a placement active | Renderer main thread; map session | Empty key returns false | | `active_count()` | Query | Count active placement keys, not references | Renderer main thread; map session | None | | `diagnostic_snapshot()` | Query | Return detached, placement/reference-sorted records | Renderer main thread; caller-owned result | None | | `clear()` | Command | Remove all ownership records idempotently | Renderer main thread; reset/shutdown | None | ## Inputs and outputs | Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime | |---|---|---|---|---|---| | Input | Resolved `uid:*`, `tile:*:*` or `global` placement key | `WmoPlacementResolver` / loader global adapter | Registry | Copied String dictionary key | Until final release/clear | | Input | ADT tile key or `__global__` reference key | Loader | Registry | Copied String set key | Until release/clear | | Output | First-placement Boolean | Registry | Loader creation path | Value | One call | | Output | Final-release Boolean | Registry | Loader cancellation/free path | Value | One call | | Output | Active count and sorted diagnostic records | Registry | Renderer diagnostics/verifier | Detached values | Caller lifetime | ## Data flow ```mermaid flowchart TD Add[add placement + reference] --> Valid{Both non-empty?} Valid -->|no| Reject[false; unchanged] Valid -->|yes| Existing{Placement exists?} Existing -->|no| Create[Create reference set; true] Existing -->|yes| Retain[Idempotently add reference; false] Release[release placement + reference] --> Owner{Known owner reference?} Owner -->|no| Reject Owner -->|yes| Erase[Erase reference] Erase --> Empty{Set empty?} Empty -->|no| Retained[false; placement remains] Empty -->|yes| Final[Erase placement; true] ``` ## Lifecycle and state The states for one placement are `Absent`, `Referenced` and back to `Absent`. The first valid add enters `Referenced`; further tile/global adds remain there. Only release of the final owned reference returns to `Absent`. Map reset and shutdown call `clear`, which is safe from either state. ```mermaid stateDiagram-v2 [*] --> Absent Absent --> Referenced: first valid add / true Referenced --> Referenced: idempotent/shared add / false Referenced --> Referenced: non-owner/non-final release / false Referenced --> Absent: final owned release / true Referenced --> Absent: clear ``` ## Main sequence ```mermaid sequenceDiagram participant Loader as StreamingWorldLoader participant Registry as WmoPlacementRegistry participant Nodes as Loader Node map Loader->>Registry: contains(placement key) alt existing placement Loader->>Registry: add_reference(key, tile) else new placement built successfully Loader->>Nodes: store key-to-Node Loader->>Registry: add_reference(key, tile/global) end Loader->>Registry: release_reference(key, tile) alt final reference Registry-->>Loader: true Loader->>Loader: cancel render build Loader->>Nodes: queue-free and erase Node else shared/unknown reference Registry-->>Loader: false end ``` ## Ownership, threading and resources - The registry owns only placement-key Dictionaries and reference-key sets. - The loader owns the parallel placement-key-to-Node map and every Node action. - The loader also owns WMO roots, tile state, caches, requests, jobs, queues, retries, cancellation and render-resource lifetime. - Current calls are serialized on the renderer main thread. The registry has no lock and must not be mutated concurrently. ## Errors, cancellation and recovery | Failure | Detection | Behavior | Diagnostic | Recovery | |---|---|---|---|---| | Empty placement/reference key | API guard | Reject without mutation | Contract verifier | Repair caller input | | Duplicate add | Existing set member | Idempotent retain | Sorted snapshot | No recovery required | | Unknown/non-owner release | Missing placement/reference | Ignore without mutation | Contract verifier | Release from actual owner or reset | | Tile build cancellation | Loader-owned condition | Loader cancels job, then releases recorded refs | Existing queue metrics | Retry when tile becomes wanted | | Final release | Empty set after erase | Return true and erase registry record | Active count | Loader cancels/free Node exactly once | | Map reset/shutdown | Loader lifecycle | Loader frees Nodes/jobs and clears registry | Shutdown regression | Next map begins empty | ## Configuration and capabilities There is no setting, profile branch or capacity knob. WMO tile radius, cache limits, render budgets and visibility settings remain loader-owned. ## Persistence, cache and migration No data is serialized and no cache/schema/version changes. Placement and reference keys exist only for the current renderer map session. ## Diagnostics and observability `active_count` backs existing WMO instance metrics. `diagnostic_snapshot` returns deterministic detached records for tests and future read-only diagnostics; mutating the returned arrays cannot alter registry state. ## Verification - `verify_wmo_placement_registry.gd`: invalid/first/idempotent/shared/non-owner/ final/global/clear behavior, deterministic detached diagnostics, source ownership boundaries and bounded 100-by-256 shared-reference cycles. - WMO resolver, material, runtime-cache shutdown and the renderer regression suite verify the unchanged loader adapters and surrounding lifecycle. - Fidelity evidence is exact preservation of the repository's prior shared-ref and final-release semantics. Asset-backed 3.3.5a comparison is still pending. ## Extension points - WMO build/resource state can be extracted separately without moving Nodes into this registry. - A future diagnostic adapter may expose the detached snapshot through the facade without exposing mutable loader internals. ## Capability status | Capability | Status | Evidence | Gap/next step | |---|---|---|---| | WMO placement reference ownership | Implemented extraction | Contract/source/timing verifier | Asset-backed cross-tile corpus pending | | WMO build/resource scheduling | Remains in loader | Existing runtime regressions | Separate state/cancellation package | | WMO visual/portal/material fidelity | Partial | Renderer baseline/material checks | Original-client paired evidence pending | ## Known gaps and risks - Correctness assumes the loader records each successful placement reference in the tile `wmo_refs` array before unregister. - Old caches without positive UID retain resolver-defined per-tile identity. - No proprietary WMO corpus or original-client paired capture is in this package. ## Source map | Path | Responsibility | |---|---| | `src/render/wmo/wmo_placement_registry.gd` | Placement-key/reference-set ownership | | `src/render/wmo/wmo_placement_resolver.gd` | Stable path, identity and transform rules | | `src/scenes/streaming/streaming_world_loader.gd` | Node map, instances, resources, jobs, queues and lifecycle reactions | | `src/tools/verify_wmo_placement_registry.gd` | Contract, boundary and timing regression | ## Related decisions and references - [`wmo-placement-resolver.md`](wmo-placement-resolver.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)