210 lines
8.4 KiB
Markdown
210 lines
8.4 KiB
Markdown
# M2 Static Build Resource Observer
|
|
|
|
## Metadata
|
|
|
|
| Field | Value |
|
|
|---|---|
|
|
| Status | Implemented extraction |
|
|
| Target/work package | M03 / `M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001` |
|
|
| Owners | Static build Mesh lookup, request selection and missing transition |
|
|
| Last verified | Worktree `work/sindo-main-codex/m03-m2-static-build-resource-observer`, 2026-07-18 |
|
|
| Profiles/capabilities | Existing static MultiMesh M2 build path |
|
|
|
|
## Purpose
|
|
|
|
Produce the static phase of `M2BuildResourceSnapshot`: reuse a prepared Mesh,
|
|
wait for an existing request, start the first eligible cache request, or record
|
|
a terminal missing model while preserving historical path and GLB rules.
|
|
|
|
## Non-goals
|
|
|
|
- Observe animated/native prototypes or finalize threaded loads.
|
|
- Own/free Meshes, Nodes, cache entries or snapshot lifetime.
|
|
- Materialize instances, plan batches, rotate queues or consume permits.
|
|
- Change cache format, candidate order, profiles or visible output.
|
|
|
|
## Context and boundaries
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
Loader[StreamingWorldLoader] --> Observer[M2StaticBuildResourceObserver]
|
|
MeshCache[M2MeshResourceCacheState] --> Observer
|
|
Missing[M2PrototypeCacheState] --> Observer
|
|
Pipeline[M2MeshLoadPipelineState] --> Observer
|
|
Observer --> Snapshot[M2BuildResourceSnapshot]
|
|
Snapshot --> Dispatch[M2BuildDispatchPlanner]
|
|
```
|
|
|
|
## Public API
|
|
|
|
| Symbol | Kind | Purpose | Errors |
|
|
|---|---|---|---|
|
|
| `observe(snapshot, path, cache_dir, mesh_cache, prototype_cache, pipeline)` | Command/query | Adopt cached/pending/requested/missing static result | Invalid inputs return `rejected` |
|
|
| `cache_resource_paths(cache_dir, path, extensions)` | Pure query | Return unique historical nested/lowercase/basename candidates | Empty values yield fewer/empty candidates |
|
|
| `glb_animation_schema(path)` | Read query | Read OpenWC schema marker from GLB JSON | Invalid/missing GLB returns empty String |
|
|
| `OUTCOME_*` | Constants | Stable diagnostic transition identifiers | None |
|
|
|
|
## Inputs and outputs
|
|
|
|
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|
|
|---|---|---|---|---|
|
|
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
|
|
| Input | Mesh/missing/request states | M2 state services | Observer | Borrowed services; map/session |
|
|
| Input/output | Resource snapshot | Loader | Observer/dispatch/materializer | Borrowed; one build operation |
|
|
| Output | Cached Mesh or missing/pending values | Observer | Snapshot | Exact borrowed Mesh/scalars |
|
|
| Output | Threaded request record | Observer | Mesh pipeline | Pipeline-owned path record |
|
|
| Output | Outcome StringName | Observer | Tests/future diagnostics | Immutable scalar |
|
|
|
|
## Data flow
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
Start[Observe static path] --> Cached{Mesh cached?}
|
|
Cached -->|yes| AdoptMesh[Adopt exact Mesh; cached]
|
|
Cached -->|no| Missing{Already missing?}
|
|
Missing -->|yes| AdoptMissing[Adopt missing]
|
|
Missing -->|no| Pending{Request pending?}
|
|
Pending -->|yes| AdoptPending[Adopt unresolved; pending]
|
|
Pending -->|no| Candidates[Generate tscn then glb candidates]
|
|
Candidates --> Eligible{Exists and not pivot-prefix GLB?}
|
|
Eligible -->|yes| Request[Threaded request]
|
|
Request -->|OK or busy| Remember[Remember request; requested]
|
|
Eligible -->|none/error| MarkMissing[Mark/adopt missing]
|
|
```
|
|
|
|
## Lifecycle/state
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> Observing
|
|
Observing --> Cached
|
|
Observing --> Pending
|
|
Observing --> Requested
|
|
Observing --> Missing
|
|
Observing --> Rejected
|
|
Cached --> [*]
|
|
Pending --> [*]
|
|
Requested --> [*]
|
|
Missing --> [*]
|
|
Rejected --> [*]
|
|
```
|
|
|
|
## Main sequence
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant L as StreamingWorldLoader
|
|
participant O as StaticBuildResourceObserver
|
|
participant C as Mesh/prototype cache state
|
|
participant P as MeshLoadPipelineState
|
|
participant R as ResourceLoader
|
|
participant S as M2BuildResourceSnapshot
|
|
L->>O: observe(snapshot, path, cache_dir, states)
|
|
O->>C: cached/missing lookup
|
|
alt cache hit or missing
|
|
O->>S: adopt static observation
|
|
else pending
|
|
O->>P: has_request
|
|
O->>S: adopt unresolved observation
|
|
else request candidate
|
|
O->>R: exists + load_threaded_request
|
|
O->>P: remember_request
|
|
O->>S: adopt unresolved observation
|
|
end
|
|
```
|
|
|
|
## Dependency diagram
|
|
|
|
```mermaid
|
|
flowchart TB
|
|
Observer[M2StaticBuildResourceObserver] --> ResourceLoader
|
|
Observer --> FileAccess[GLB header/JSON read]
|
|
Observer --> MeshCache[M2MeshResourceCacheState]
|
|
Observer --> PrototypeCache[M2PrototypeCacheState]
|
|
Observer --> Pipeline[M2MeshLoadPipelineState]
|
|
Observer --> Snapshot[M2BuildResourceSnapshot]
|
|
Observer -. no dependency .-> SceneTree
|
|
Observer -. no dependency .-> Materializers
|
|
Observer -. no dependency .-> Scheduler
|
|
```
|
|
|
|
## Ownership, threading and resources
|
|
|
|
- Observer retains no request, cache, snapshot or engine reference.
|
|
- Cache/pipeline services retain their existing state and resource ownership.
|
|
- Snapshot borrows the exact cached Mesh; observer never frees it.
|
|
- Resource existence/request and GLB inspection run on renderer main thread.
|
|
- Finalization and Mesh preparation remain in the loader drain path.
|
|
|
|
## Errors, cancellation and recovery
|
|
|
|
| State | Behavior | Recovery |
|
|
|---|---|---|
|
|
| Invalid dependency/path | Return `rejected`; no mutation | Correct composition |
|
|
| Existing request | Return `pending`; no duplicate request | Retry when queue rotates |
|
|
| Pivot-prefix GLB | Skip as static candidate | Continue next candidate |
|
|
| Request error | Stop historical search and mark missing | Later reset/new session |
|
|
| No candidate | Mark/adopt terminal missing | Cache content becomes available in new session |
|
|
| Tile cancellation | Observer retains nothing | Loader/queue cancellation remains authoritative |
|
|
|
|
## Configuration and capabilities
|
|
|
|
The observer consumes existing `m2_cache_dir`. It adds no profile, cache version,
|
|
batch, visibility, shadow or scheduler setting.
|
|
|
|
## Persistence, cache and migration
|
|
|
|
No new persistence is introduced. Existing `.tscn/.glb` layout, pipeline records
|
|
and missing-cache lifetime are unchanged; no rebake is required.
|
|
|
|
## Diagnostics and observability
|
|
|
|
Returned outcomes distinguish rejected/cached/pending/requested/missing without
|
|
logging. Existing queue and hitch metrics remain unchanged.
|
|
|
|
## Verification
|
|
|
|
- Dedicated verifier covers invalid, cached identity, missing, pending, path
|
|
order/deduplication, absent GLB schema, source ownership and bounded timing.
|
|
- Snapshot/dispatch/pipeline/cache/shutdown/facade/internal-access/checkpoint
|
|
regressions protect adjacent behavior.
|
|
- No original-client visual claim is made; this is bookkeeping/I/O extraction.
|
|
|
|
## Extension points
|
|
|
|
Animated/native observation may become a separate producer of the animation
|
|
phase. Shared cache candidate/GLB inspection can later be reused without adding
|
|
a generic resource framework.
|
|
|
|
## Capability status
|
|
|
|
| Capability | Status | Evidence | Gap |
|
|
|---|---|---|---|
|
|
| Static build lookup/request production | Implemented extraction | Lifecycle/path/source/timing verifier | Asset-backed traversal pending |
|
|
| Static finalize/preparation | Existing loader-owned | Pipeline/finalizer regressions | Later extraction |
|
|
| Animated observation | Existing loader-owned | Animation regressions | Separate producer pending |
|
|
|
|
## Known gaps and risks
|
|
|
|
- Successful request behavior is protected by source plus existing pipeline
|
|
tests; starting an undrained asynchronous request in the unit fixture would leak.
|
|
- GLB schema parser reads synchronously, matching the previous loader behavior.
|
|
- Private traversal, p95/p99, leak and visual evidence remain unavailable.
|
|
|
|
## Source map
|
|
|
|
| Path | Responsibility |
|
|
|---|---|
|
|
| `src/render/m2/m2_static_build_resource_observer.gd` | Static cache/request/missing observation |
|
|
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed observation result |
|
|
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, finalize drain and action execution |
|
|
| `src/tools/verify_m2_static_build_resource_observer.gd` | Lifecycle/path/boundary/timing regression |
|
|
|
|
## Related decisions and references
|
|
|
|
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
|
|
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
|
|
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
|
|
- [`world-renderer.md`](world-renderer.md)
|
|
- [`../../RENDER.md`](../../RENDER.md)
|