render: extract WMO render resource finalizer

This commit is contained in:
2026-07-18 14:03:27 +04:00
parent fb7c9f174e
commit a86f8f2212
12 changed files with 751 additions and 56 deletions
@@ -0,0 +1,219 @@
# WMO Render Resource Finalizer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-WMO-RENDER-RESOURCE-FINALIZER-001` |
| Owners | Lightweight WMO terminal status polling, Resource validation and cache/missing publication |
| Last verified | Worktree `work/sindo-main-codex/m03-wmo-render-resource-finalizer`, 2026-07-18 |
| Profiles/capabilities | Profile-independent lightweight WMO render-cache finalization |
## Purpose
Finalize pending lightweight WMO `.res` cache requests outside
`StreamingWorldLoader`: poll terminal status, retrieve a loaded Resource, enforce
exact script/current-format validation and publish either the exact Resource or
the historical missing outcome.
## Non-goals
- Select normalized WMO/cache paths or start threaded requests.
- Finalize cached WMO PackedScenes or live WMOBuilder prototypes.
- Own cache lifetime, placements, build queues, scheduler permits or Nodes.
- Change `WMOStreamingResource.FORMAT_VERSION`, fallback or visible behavior.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] -->|compose and tick| Finalizer[WmoRenderResourceFinalizer]
Cache[WmoRenderResourceCacheState] -->|pending snapshot| Finalizer
Finalizer --> ResourceLoader
Finalizer -->|validated Resource or missing| Cache
Cache --> Build[WMO render build queue]
```
The service may depend on `ResourceLoader`, the injected expected Script and the
render Resource cache-state API. File selection, FileAccess, SceneTree, WMOBuilder,
placement, scheduler and application layers remain outside it.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `poll_terminal_requests(cache_state)` | Command/query | Poll all pending paths once and publish terminal outcomes | Renderer main thread; stateless between calls | Null state returns zero; non-terminal retained |
| `is_current_render_resource(resource)` | Query | Enforce exact injected Script and minimum format | Renderer main thread; stateless | Null/wrong/stale false |
| `load_threaded_get_status(path)` | Boundary query | Read opaque threaded status | Renderer main thread; injectable in tests | ResourceLoader semantics |
| `load_threaded_get(path)` | Boundary query | Retrieve terminal Resource | Renderer main thread; injectable in tests | Null accepted as failed outcome |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Detached normalized-path to `.res` path Dictionary | Render cache state | Finalizer polling | Caller-owned copy | One poll pass |
| Input | Opaque ResourceLoader status | ResourceLoader | Finalizer | Integer value | One path poll |
| Input | Terminal Resource | ResourceLoader | Validator | Borrowed reference | One completion |
| Input | Expected Script/minimum format | Loader composition | Validator | Borrowed/value | Finalizer lifetime |
| Output | Exact validated Resource | Finalizer | Render cache state | Cache adopts strong reference | Until full clear |
| Output | Missing transition | Finalizer | Render cache state | Path-only state | Until transient clear |
## Data flow
```mermaid
flowchart TD
Snapshot[Detached pending snapshot] --> Next[Next path in insertion order]
Next --> Poll[Poll ResourceLoader status]
Poll --> Terminal{Loaded or failed?}
Terminal -->|no| Retain[Retain pending]
Terminal -->|failed| Missing[Complete as missing]
Terminal -->|loaded| Get[Get terminal Resource]
Get --> Validate{Exact script and format current?}
Validate -->|yes| Adopt[Complete with exact Resource]
Validate -->|no| Missing
Retain --> Next
Adopt --> Next
Missing --> Next
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Polling
Polling --> Pending: non-terminal status
Polling --> Missing: failed/null/wrong/stale
Polling --> Cached: exact script and current format
Pending --> Polling: later tick
Missing --> [*]
Cached --> [*]
```
The finalizer retains no per-path state. The sibling cache state owns all
Pending/Cached/Missing lifetime and reset transitions.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Finalizer as WmoRenderResourceFinalizer
participant Cache as WmoRenderResourceCacheState
participant RL as ResourceLoader
Loader->>Finalizer: poll_terminal_requests(Cache)
Finalizer->>Cache: request_paths_snapshot()
loop insertion-ordered paths
Finalizer->>RL: load_threaded_get_status(cache path)
alt non-terminal
Finalizer->>Finalizer: retain request
else failed
Finalizer->>Cache: complete_request_as_missing(path)
else loaded
Finalizer->>RL: load_threaded_get(cache path)
Finalizer->>Finalizer: exact Script and format validation
Finalizer->>Cache: complete with Resource or missing
end
end
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Finalizer[WmoRenderResourceFinalizer]
Loader --> Cache[WmoRenderResourceCacheState]
Finalizer --> Cache
Finalizer --> ResourceLoader
Finalizer --> Script[WMOStreamingResource Script contract]
Finalizer -. no dependency .-> FileAccess
Finalizer -. no dependency .-> WMOBuilder
Finalizer -. no dependency .-> Node
Finalizer -. no dependency .-> Scheduler
```
## Ownership, threading and resources
- Renderer main thread serializes polling and cache-state publication.
- Finalizer owns no Resource, request, Node, RID or file lifetime.
- Cache state adopts accepted exact Resource references until its full clear.
- Loader owns request admission, fallback/build orchestration and shutdown order.
- Detached snapshots allow terminal cache-state mutation during iteration.
## Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Null cache state | Guard | Return zero | Contract verifier | Correct composition |
| Non-terminal request | Status | Retain pending | Pending count | Poll next tick |
| Failed request | Terminal status | Complete missing | Negative cache | Transient reset/rebuilt cache |
| Null/wrong-script Resource | Validation | Complete missing | Synthetic fixture | Rebuild cache and reset |
| Stale format | Format below injected minimum | Complete missing | Synthetic fixture | Rebuild cache and reset |
| Shutdown | Loader lifecycle | Drain then cache clear | Shutdown verifier | New loader starts absent |
## Configuration and capabilities
The loader injects the exact `WMOStreamingResource` Script and its
`FORMAT_VERSION`. The service introduces no setting, profile, permit or cache
format. WMO cache paths and build budgets remain loader-owned.
## Persistence, cache and migration
The module serializes nothing and changes no cache format. It only enforces the
existing script identity and current-or-newer format rule before runtime cache
adoption, so no migration or rebake is introduced.
## Diagnostics and observability
- `poll_terminal_requests` returns terminal completion count for tests/future metrics.
- Existing cache pending count and renderer `wmobuild` metrics are unchanged.
- The service emits no logs; normalized relative path remains the correlation key.
## Verification
- `verify_wmo_render_resource_finalizer.gd`: null/non-terminal, insertion order,
failed/load boundary, null/wrong/stale rejection, current/newer identity,
source ownership and 1,000 terminal polls under one second.
- Cache-state, WMO queue/planner/registry/resolver, shutdown, facade,
internal-access and baseline regressions protect adjacent behavior.
- Fidelity evidence is exact orchestration extraction; no asset-backed visual or
original-client parity claim is made.
## Extension points
- A legal current/stale `.res` fixture can extend validation evidence unchanged.
- PackedScene finalization remains separate because it instantiates a probe Node
and uses WMOBuilder metadata rather than script/format Resource fields.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Lightweight WMO terminal polling | Implemented extraction | Status/order/source/timing verifier | Asset-backed traversal pending |
| Script/format validation | Implemented extraction | Wrong/stale/current/newer fixtures | Legal serialized fixture pending |
| Resource/missing publication | Implemented extraction | Exact identity/negative-state fixtures | Asset-backed lifetime/leak run pending |
| WMO materialization | Loader-owned | Existing build regressions | Further safe extraction pending |
## Known gaps and risks
- ResourceLoader status/get remains synchronous main-thread boundary polling.
- No proprietary WMO corpus, serialized corrupt-version fixture, leak run,
traversal p95/p99 or paired original-client capture is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/wmo/wmo_render_resource_finalizer.gd` | Terminal polling, validation and publication |
| `src/render/wmo/wmo_render_resource_cache_state.gd` | Resource/missing/request ownership and resets |
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, request admission, fallback and build orchestration |
| `src/resources/wmo_streaming_resource.gd` | Serialized cache contract and format version |
| `src/tools/verify_wmo_render_resource_finalizer.gd` | Terminal-I/O/validation/source/timing regression |
## Related decisions and references
- [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md)
- [`wmo-render-build-queue.md`](wmo-render-build-queue.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)