Compare commits

...

268 Commits

Author SHA1 Message Date
sindoring 70729bb341 targets: accept native M2 animation resource observer 2026-07-18 10:57:18 +04:00
sindoring d37c799850 merge: native M2 animation resource observer 2026-07-18 10:56:08 +04:00
sindoring c24c3f159c coordination: hand off native M2 animation resource observer 2026-07-18 10:56:04 +04:00
sindoring 1cb0101a73 render: extract native M2 animation resource observer 2026-07-18 10:55:47 +04:00
sindoring d22a9cd743 coordination: claim native M2 animation resource observer 2026-07-18 10:42:13 +04:00
sindoring 6f385ed261 targets: accept cached M2 animation resource observer 2026-07-18 10:24:30 +04:00
sindoring f79e064d25 merge: cached M2 animation resource observer 2026-07-18 10:23:29 +04:00
sindoring 6a23c1b996 coordination: hand off cached M2 animation resource observer 2026-07-18 10:23:22 +04:00
sindoring df87619220 render: extract cached M2 animation resource observer 2026-07-18 10:23:03 +04:00
sindoring 3519f183bb coordination: claim cached M2 animation resource observer 2026-07-18 10:10:19 +04:00
sindoring 5ebf4de2ff targets: accept static M2 build resource observer 2026-07-18 03:04:01 +04:00
sindoring a043c79654 merge: static M2 build resource observer 2026-07-18 03:02:06 +04:00
sindoring 194b64d030 coordination: hand off static M2 build resource observer 2026-07-18 03:02:02 +04:00
sindoring 7cb3e3412f render: extract static M2 build resource observer 2026-07-18 03:01:43 +04:00
sindoring f062ea91cd coordination: claim static M2 build resource observer 2026-07-18 02:51:46 +04:00
sindoring 032a256e70 targets: accept M2 build resource snapshot 2026-07-18 02:48:54 +04:00
sindoring 4354834c50 merge: M2 build resource snapshot 2026-07-18 02:47:35 +04:00
sindoring 41b3b63215 coordination: hand off M2 build resource snapshot 2026-07-18 02:47:26 +04:00
sindoring ec1b90f1e4 render: add M2 build resource snapshot 2026-07-18 02:47:10 +04:00
sindoring 0decd10e09 coordination: claim M2 build resource snapshot 2026-07-18 02:38:46 +04:00
sindoring b113db01cd targets: accept M2 build dispatch planner 2026-07-18 02:35:49 +04:00
sindoring 99a90ddfb3 merge: M2 build dispatch planner 2026-07-18 02:34:36 +04:00
sindoring ed71b36aec coordination: hand off M2 build dispatch planner 2026-07-18 02:34:31 +04:00
sindoring 17f5cc0faa render: extract M2 build dispatch planner 2026-07-18 02:34:18 +04:00
sindoring 1fb566f9bc coordination: claim M2 build dispatch planner 2026-07-18 02:27:16 +04:00
sindoring c74b90a8ea targets: accept M2 build queue 2026-07-18 02:25:14 +04:00
sindoring 1dc013e5c2 merge: M2 build queue 2026-07-18 02:24:00 +04:00
sindoring 8938350a59 coordination: hand off M2 build queue 2026-07-18 02:23:53 +04:00
sindoring 4a8338f2f0 render: extract M2 build queue 2026-07-18 02:23:40 +04:00
sindoring 3ee9e77422 coordination: claim M2 build queue 2026-07-18 02:13:15 +04:00
sindoring 81e33a9bc4 targets: accept static M2 batch materializer 2026-07-18 02:02:10 +04:00
sindoring 5e82daacb9 merge: static M2 batch materializer 2026-07-18 02:01:15 +04:00
sindoring 8339189907 coordination: hand off static M2 batch materializer 2026-07-18 02:01:09 +04:00
sindoring 252927d90c render: extract static M2 batch materializer 2026-07-18 02:00:55 +04:00
sindoring 0fd052923d coordination: claim static M2 batch materializer 2026-07-18 01:54:26 +04:00
sindoring 86814deca1 targets: accept animated M2 materializer 2026-07-18 01:50:05 +04:00
sindoring c251985256 merge: animated M2 instance materializer 2026-07-18 01:49:17 +04:00
sindoring 899a9ae012 coordination: hand off animated M2 materializer 2026-07-18 01:49:08 +04:00
sindoring 456f3e2334 render: extract animated M2 materializer 2026-07-18 01:48:55 +04:00
sindoring 9ab4c0762d coordination: claim animated M2 materializer 2026-07-18 01:39:44 +04:00
sindoring 5b2d9f23fe docs(M03): accept M2 animation playback 2026-07-18 01:27:33 +04:00
sindoring c0fc191bcd merge M03 M2 animation playback 2026-07-18 01:26:18 +04:00
sindoring 15c24692f0 coord(M03): hand off M2 animation playback 2026-07-18 01:26:10 +04:00
sindoring 9b73571dcb refactor(M03): extract M2 animation playback 2026-07-18 01:25:54 +04:00
sindoring 5be36376c2 coord(M03): claim M2 animation playback 2026-07-18 01:19:13 +04:00
sindoring c0a7bfbd8d docs(M03): accept M2 animated scene finalizer 2026-07-18 01:16:19 +04:00
sindoring e7a7c67f6d merge M03 M2 animated scene finalizer 2026-07-18 01:14:40 +04:00
sindoring bc2bd06abc coord(M03): hand off M2 animated scene finalizer 2026-07-18 01:14:32 +04:00
sindoring 1b450dc580 refactor(M03): extract M2 animated scene finalizer 2026-07-18 01:14:17 +04:00
sindoring 03ba129a52 coord(M03): claim M2 animated scene finalizer 2026-07-18 01:07:09 +04:00
sindoring c20bc62ac0 docs(M03): accept M2 animation load pipeline 2026-07-17 18:25:20 +04:00
sindoring 3dccd3e3ee merge M03 M2 animation load pipeline 2026-07-17 18:24:16 +04:00
sindoring 672e0ce2a5 coord(M03): hand off M2 animation load pipeline 2026-07-17 18:24:10 +04:00
sindoring b8e0f11ed5 refactor(M03): extract M2 animation load pipeline state 2026-07-17 18:23:55 +04:00
sindoring 2695afcdba coord(M03): claim M2 animation load pipeline 2026-07-17 18:16:59 +04:00
sindoring 20a64b5dfc docs(M03): accept M2 prototype cache state 2026-07-17 17:07:27 +04:00
sindoring 1de50f98ce merge M03 M2 prototype cache state 2026-07-17 17:00:59 +04:00
sindoring 5e828aaf01 docs(M03): hand off M2 prototype cache state 2026-07-17 16:29:28 +04:00
sindoring cd664eb4cb refactor(M03): extract M2 prototype cache state 2026-07-17 16:29:11 +04:00
sindoring 87bf067493 coord(M03): claim M2 prototype cache state 2026-07-17 16:18:29 +04:00
sindoring ca77d32be1 docs(M03): accept raw M2 model repository 2026-07-17 15:54:59 +04:00
sindoring 7cd07dda4f merge M03 raw M2 model repository 2026-07-17 15:53:54 +04:00
sindoring 8605b43b1f docs(M03): hand off raw M2 model repository 2026-07-17 15:53:39 +04:00
sindoring b608a81766 refactor(M03): extract raw M2 model repository 2026-07-17 15:53:20 +04:00
sindoring e4bb10cee7 coord(M03): claim raw M2 model repository
Work-Package: M03-RND-M2-RAW-MODEL-REPOSITORY-001
Agent: sindo-main-codex
Tests: coordination gate passed before claim
Fidelity: native-boundary extraction only; parser methods and paths unchanged
2026-07-17 15:44:15 +04:00
sindoring 1d94eace68 docs(M03): accept M2 runtime mesh finalizer
Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001
Agent: sindo-main-codex
Tests: post-merge finalizer, classifier, extractor, cache, pipeline, materials, shutdown, facade, internal-access, manifest, documentation and coordination gates pass
Fidelity: preserves refresh version 2 and rebuild/fallback transitions; asset-backed visual parity remains unclaimed
2026-07-17 15:03:48 +04:00
sindoring 1fafddecb6 merge M03 M2 runtime mesh finalizer 2026-07-17 15:02:28 +04:00
sindoring 0007a69769 docs(M03): hand off M2 runtime mesh finalizer
Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001
Agent: sindo-main-codex
Tests: 37 headless regressions, checkpoint dry-run, documentation and coordination gates pass
Fidelity: refresh/rebuild/fallback extraction only; asset-backed visual parity remains unclaimed
2026-07-17 15:02:16 +04:00
sindoring ece7724a28 refactor(M03): extract M2 runtime mesh finalizer
Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001
Agent: sindo-main-codex
Tests: 37 headless renderer/coordinate contracts pass; checkpoint dry-run 7/7; documentation and coordination gates pass
Fidelity: preserves refresh version 2, classifier, rebuild and fallback transitions; no visual parity claim
2026-07-17 15:01:52 +04:00
sindoring 951bd54fff coord(M03): claim M2 runtime mesh finalizer
Work-Package: M03-RND-M2-RUNTIME-MESH-FINALIZER-001
Agent: sindo-main-codex
Tests: coordination gate passed before claim
Fidelity: extraction-only package; refresh version and visible behavior unchanged
2026-07-17 14:54:16 +04:00
sindoring dd8cfbf79c docs(M03): accept M2 mesh resource extractor
Work-Package: M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001
Agent: sindo-main-codex
Tests: post-merge extractor, cache, pipeline, classifier, materials, shutdown, facade, internal-access, manifest, documentation and coordination gates pass
Fidelity: preserves first-Mesh preorder and temporary PackedScene cleanup; asset-backed visual parity remains unclaimed
2026-07-17 13:04:17 +04:00
sindoring fa85985ec5 merge M03 M2 mesh resource extractor 2026-07-17 13:03:11 +04:00
sindoring f68e971cce docs(M03): hand off M2 mesh resource extractor
Work-Package: M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001
Agent: sindo-main-codex
Tests: 36 headless regressions, checkpoint dry-run, documentation and coordination gates pass
Fidelity: traversal/lifetime extraction only; asset-backed visual parity remains unclaimed
2026-07-17 13:02:58 +04:00
sindoring 7b0e1eac5f refactor(M03): extract M2 mesh resource traversal
Work-Package: M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001
Agent: sindo-main-codex
Tests: 36 headless renderer/coordinate contracts pass; checkpoint dry-run 7/7; documentation and coordination gates pass
Fidelity: preserves first-Mesh preorder and temporary PackedScene cleanup; no visual parity claim
2026-07-17 13:02:40 +04:00
sindoring 6607100686 coord(M03): claim M2 mesh resource extractor
Work-Package: M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001
Agent: sindo-main-codex
Tests: coordination gate passed before claim
Fidelity: extraction-only package; traversal and visible behavior unchanged
2026-07-17 12:57:38 +04:00
sindoring 5e2afbc400 docs(M03): accept M2 mesh resource cache state
Work-Package: M03-RND-M2-MESH-RESOURCE-CACHE-001
Agent: sindo-main-codex
Tests: post-merge cache, pipeline, classifier, materials, shutdown, facade, internal-access, manifest, documentation and coordination gates pass
Fidelity: preserves Mesh identity/replacement/final-shutdown lifetime; asset-backed visual parity remains unclaimed
2026-07-17 12:12:19 +04:00
sindoring 30945615b9 merge M03 M2 mesh resource cache state 2026-07-17 12:11:09 +04:00
sindoring c5ecaba0ab docs(M03): hand off M2 mesh resource cache state
Work-Package: M03-RND-M2-MESH-RESOURCE-CACHE-001
Agent: sindo-main-codex
Tests: 35 headless regressions, checkpoint dry-run, documentation and coordination gates pass
Fidelity: cache ownership/lifetime extraction only; asset-backed visual parity remains unclaimed
2026-07-17 12:10:53 +04:00
sindoring e990a6503d refactor(M03): extract M2 mesh resource cache state
Work-Package: M03-RND-M2-MESH-RESOURCE-CACHE-001
Agent: sindo-main-codex
Tests: 35 headless renderer/coordinate contracts pass; checkpoint dry-run 7/7; documentation and coordination gates pass
Fidelity: preserves exact Mesh references, replacement semantics and final-shutdown lifetime; no visual parity claim
2026-07-17 12:10:37 +04:00
sindoring e2bb501695 coord(M03): claim M2 mesh resource cache state
Work-Package: M03-RND-M2-MESH-RESOURCE-CACHE-001
Agent: sindo-main-codex
Tests: coordination gate passed before claim
Fidelity: extraction-only package; visible behavior unchanged
2026-07-17 12:04:34 +04:00
sindoring ef782d1f2b docs(M03): accept M2 mesh load pipeline state
Work-Package: M03-RND-M2-MESH-LOAD-PIPELINE-001
Agent: sindo-main-codex
Tests: post-merge pipeline, classifier, materials, shutdown, facade, internal-access, manifest, documentation and coordination gates pass
Fidelity: preserves request polling/completion order, statuses, metrics and shutdown drain; asset-backed visual parity remains unclaimed
2026-07-17 11:09:20 +04:00
sindoring 83b3618c50 merge M03 M2 mesh load pipeline state 2026-07-17 10:24:50 +04:00
sindoring ccbb27a9fd docs(M03): hand off M2 mesh load pipeline state
Work-Package: M03-RND-M2-MESH-LOAD-PIPELINE-001
Agent: sindo-main-codex
Tests: 34 headless regressions, documentation/coordination gates and checkpoint dry-run pass
Fidelity: lifecycle extraction only; asset-backed visual parity remains unclaimed
2026-07-17 10:24:41 +04:00
sindoring 82df7a555d refactor(M03): extract M2 mesh load pipeline state
Work-Package: M03-RND-M2-MESH-LOAD-PIPELINE-001
Agent: sindo-main-codex
Tests: 34 headless contract regressions pass; documentation and coordination gates pass; checkpoint dry-run 7/7
Fidelity: preserves request insertion polling order, terminal completion FIFO/statuses, metrics and clear/shutdown behavior; no visual parity claim
2026-07-17 10:23:52 +04:00
sindoring 5f6287905c coord(M03): claim M2 mesh load pipeline state 2026-07-17 10:15:17 +04:00
sindoring 8ec13d6018 docs(M03): accept M2 runtime rebuild classifier 2026-07-17 10:02:55 +04:00
sindoring 38f831ea2f merge M03 M2 runtime rebuild classifier 2026-07-17 10:01:54 +04:00
sindoring 0544cf22df docs(M03): hand off M2 runtime rebuild classifier 2026-07-17 10:01:47 +04:00
sindoring 19ca8f48e8 refactor(M03): extract M2 runtime rebuild classifier 2026-07-17 10:01:29 +04:00
sindoring 324e64dd6e coord(M03): claim M2 runtime rebuild classifier 2026-07-17 09:56:16 +04:00
sindoring 90b1dac56e docs(M03): accept ADT water scene finalizer 2026-07-17 09:50:33 +04:00
sindoring 874fd0f3f2 merge M03 ADT water scene finalizer 2026-07-17 09:49:30 +04:00
sindoring 9cb8bdb137 docs(M03): hand off ADT water scene finalizer 2026-07-17 09:49:18 +04:00
sindoring 235be3a36c refactor(M03): extract ADT water scene finalizer 2026-07-17 09:48:56 +04:00
sindoring 4f8fc3b3b8 coord(M03): claim ADT water scene finalizer 2026-07-17 09:43:25 +04:00
sindoring e1b2886657 docs(M03): accept ADT water load pipeline state 2026-07-17 09:41:05 +04:00
sindoring f4707750b7 merge M03 ADT water load pipeline state 2026-07-17 09:39:33 +04:00
sindoring 78adb54cfe docs(M03): hand off ADT water load pipeline state 2026-07-17 09:39:24 +04:00
sindoring b7c036f14d refactor(M03): extract ADT water load pipeline state 2026-07-17 09:38:24 +04:00
sindoring 7973945582 coord(M03): claim ADT water load pipeline state 2026-07-17 09:32:25 +04:00
sindoring 4387c66c0a docs(M03): accept WMO scene resource cache state 2026-07-17 01:20:29 +04:00
sindoring ada0fd9467 merge M03 WMO scene resource cache state 2026-07-17 01:19:40 +04:00
sindoring ff4f320412 docs(M03): hand off WMO scene resource cache state 2026-07-17 01:19:36 +04:00
sindoring c83929428d refactor(M03): extract WMO scene resource cache state 2026-07-17 01:19:19 +04:00
sindoring 71fa093118 coord(M03): claim WMO scene resource cache state 2026-07-17 01:14:11 +04:00
sindoring e690756301 docs(M03): accept WMO render resource cache state 2026-07-17 01:12:30 +04:00
sindoring b4955d6162 merge M03 WMO render resource cache state 2026-07-17 01:11:32 +04:00
sindoring b4b94b3cbb docs(M03): hand off WMO render resource cache state 2026-07-17 01:11:27 +04:00
sindoring d39f591e50 refactor(M03): extract WMO render resource cache state 2026-07-17 01:11:11 +04:00
sindoring 4314f961f6 coord(M03): claim WMO render resource cache state 2026-07-17 01:04:15 +04:00
sindoring c9547041da docs(M03): accept WMO render build queue 2026-07-17 01:00:40 +04:00
sindoring 60603e18c8 merge M03 WMO render build queue cold-load fix 2026-07-17 00:59:50 +04:00
sindoring 3411156f8f docs(M03): refresh WMO queue handoff 2026-07-17 00:59:44 +04:00
sindoring e576ae2cad fix(M03): make WMO build queue cold-load safe 2026-07-17 00:59:11 +04:00
sindoring a878e7c4e7 merge M03 WMO render build queue 2026-07-17 00:57:20 +04:00
sindoring ab40a76b62 docs(M03): hand off WMO render build queue 2026-07-17 00:57:11 +04:00
sindoring e1bb105fff refactor(M03): extract WMO render build queue 2026-07-17 00:56:55 +04:00
sindoring 040b635ec6 coord(M03): claim WMO render build queue 2026-07-17 00:51:33 +04:00
sindoring 1e740aaa65 docs(M03): accept WMO render build planner 2026-07-17 00:49:17 +04:00
sindoring c06aed568b merge M03 WMO render build planner 2026-07-17 00:48:26 +04:00
sindoring 01bad4ca26 docs(M03): hand off WMO render build planner 2026-07-17 00:48:13 +04:00
sindoring 3a6b1d306d refactor(M03): extract WMO render build planner 2026-07-17 00:47:54 +04:00
sindoring e420a8724f coord(M03): claim WMO render build planner 2026-07-17 00:42:19 +04:00
sindoring 1e73e597f8 docs(M03): accept WMO placement registry 2026-07-17 00:34:25 +04:00
sindoring f9e5a4f2e0 merge M03 WMO placement registry 2026-07-17 00:33:34 +04:00
sindoring 16053b46ca docs(M03): hand off WMO placement registry 2026-07-17 00:33:23 +04:00
sindoring c5900368d4 refactor(M03): extract WMO placement registry 2026-07-17 00:33:09 +04:00
sindoring 79bc4e6ffc coord(M03): claim WMO placement registry 2026-07-17 00:26:32 +04:00
sindoring 5aa40d6afb docs(M03): accept WMO placement resolver 2026-07-17 00:24:37 +04:00
sindoring d2cb52ff6b merge M03 WMO placement resolver 2026-07-17 00:23:29 +04:00
sindoring c2e0696fe0 docs(M03): hand off WMO placement resolver 2026-07-17 00:23:18 +04:00
sindoring 05e216a953 refactor(M03): extract WMO placement resolver 2026-07-17 00:22:53 +04:00
sindoring f01302fd44 coord(M03): claim WMO placement resolver 2026-07-17 00:16:39 +04:00
sindoring 4f40c779b9 docs(M03): accept M2 build batch planner 2026-07-17 00:14:06 +04:00
sindoring 3f847171f8 merge M03 M2 build batch planner 2026-07-17 00:13:11 +04:00
sindoring af9d077905 docs(M03): hand off M2 build batch planner 2026-07-17 00:13:01 +04:00
sindoring be6db7f7e0 refactor(M03): extract M2 build batch planner 2026-07-17 00:12:42 +04:00
sindoring a562d126b9 coord(M03): claim M2 build batch planner 2026-07-17 00:08:54 +04:00
sindoring 9d74d996d5 docs(M03): accept M2 placement grouper 2026-07-17 00:07:11 +04:00
sindoring f88bf9714e merge M03 M2 placement grouper 2026-07-17 00:06:19 +04:00
sindoring 7587d3bd3c docs(M03): hand off M2 placement grouper 2026-07-17 00:06:10 +04:00
sindoring 9df32d38ef refactor(M03): extract M2 placement grouper 2026-07-17 00:05:49 +04:00
sindoring 9c9020e60f coord(M03): claim M2 placement grouper 2026-07-16 23:57:35 +04:00
sindoring c34e538dcf docs(M03): accept M2 placement transform resolver 2026-07-16 23:54:41 +04:00
sindoring 396be5e69d merge M03 M2 placement transform resolver 2026-07-16 23:52:34 +04:00
sindoring 1792938567 docs(M03): hand off M2 placement transform package 2026-07-16 23:52:20 +04:00
sindoring 0a09d7bc1e refactor(M03): extract M2 placement transform resolver 2026-07-16 23:51:59 +04:00
sindoring 91f0ba8433 coord(M03): claim M2 placement transform resolver 2026-07-16 23:38:29 +04:00
sindoring 1b6c46870d docs(M03): accept M2 unique placement registry 2026-07-16 10:02:14 +04:00
sindoring 2342430439 merge M03 M2 unique placement registry 2026-07-16 10:01:34 +04:00
sindoring c41e8886a6 docs(coordination): hand off M2 unique registry 2026-07-16 10:01:23 +04:00
sindoring acb3a809bb refactor(M03): extract M2 unique placement registry 2026-07-16 10:01:09 +04:00
sindoring c13c993645 coord(M03): claim M2 unique placement registry 2026-07-16 09:54:44 +04:00
sindoring 57ae1b8f16 docs(M03): accept terrain chunk queue planner 2026-07-16 09:52:59 +04:00
sindoring 24aef13801 merge M03 terrain chunk queue planner 2026-07-16 09:52:22 +04:00
sindoring 4b90dbc1c1 docs(coordination): hand off terrain chunk queue planner 2026-07-16 09:52:18 +04:00
sindoring f800e11cd2 refactor(M03): extract terrain chunk queue planner 2026-07-16 09:52:01 +04:00
sindoring 0d2c820ea7 coord(M03): claim terrain chunk queue planner 2026-07-16 09:47:11 +04:00
sindoring b568768866 docs(M03): accept terrain chunk LOD planner 2026-07-16 09:45:09 +04:00
sindoring f36fabb8e1 merge M03 terrain chunk LOD planner 2026-07-16 09:44:31 +04:00
sindoring 9e69a8a95b docs(coordination): hand off terrain LOD planner 2026-07-16 09:44:27 +04:00
sindoring eb94cfc646 refactor(M03): extract terrain chunk LOD planner 2026-07-16 09:44:11 +04:00
sindoring aa21aafdfa coord(M03): claim terrain chunk LOD planner 2026-07-16 09:37:45 +04:00
sindoring ea08b8ef51 docs(M03): accept terrain mesh cache integration 2026-07-16 09:30:59 +04:00
sindoring 630a0c131d merge(M03): integrate terrain mesh cache service 2026-07-16 09:30:02 +04:00
sindoring 10815be766 coord(M03): hand off terrain mesh cache service 2026-07-16 09:29:55 +04:00
sindoring 97480e06bb refactor(M03): extract terrain quality mesh cache 2026-07-16 09:29:38 +04:00
sindoring 6617dfe74e coord(M03): claim terrain mesh cache service 2026-07-16 09:25:43 +04:00
sindoring 61bad09a9c docs(M03): accept entity presentation integration 2026-07-16 01:12:55 +04:00
sindoring 606770c525 merge(M03): integrate entity presentation facade 2026-07-16 01:12:21 +04:00
sindoring b73b2e74a2 coord(M03): hand off entity presentation facade 2026-07-16 01:12:17 +04:00
sindoring 1807c3a363 feat(M03): add entity presentation facade 2026-07-16 01:11:58 +04:00
sindoring 3c5880f190 coord(M03): extend entity presentation fixture claim 2026-07-16 01:05:21 +04:00
sindoring a7b2f9475b coord(M03): claim entity presentation facade 2026-07-16 01:04:32 +04:00
sindoring a85e416cb8 docs(M03): accept environment snapshot integration 2026-07-16 01:01:25 +04:00
sindoring c69abd634f merge(M03): integrate environment snapshot facade 2026-07-16 01:00:41 +04:00
sindoring cd23a62be8 coord(M03): hand off environment snapshot facade 2026-07-16 01:00:36 +04:00
sindoring 2ee647a220 feat(M03): add environment snapshot facade 2026-07-16 01:00:19 +04:00
sindoring 60f1e363c0 coord(M03): claim facade environment snapshot 2026-07-16 00:55:17 +04:00
sindoring cb103e1fde docs(M03): accept facade ground query integration 2026-07-16 00:52:46 +04:00
sindoring d6e5b530ef merge(M03): integrate ground query cold-start fix 2026-07-16 00:52:08 +04:00
sindoring 8c46065ada coord(M03): update ground query handoff 2026-07-16 00:52:04 +04:00
sindoring 9408d887ba fix(M03): support cold rendered ground sample startup 2026-07-16 00:51:52 +04:00
sindoring 80cb08422b merge(M03): integrate facade ground query 2026-07-16 00:49:53 +04:00
sindoring 0773de5977 coord(M03): hand off facade ground query 2026-07-16 00:49:47 +04:00
sindoring b697a896e8 rnd(M03): add rendered ground query facade 2026-07-16 00:49:28 +04:00
sindoring 6117e5282e coord(M03): claim facade ground query 2026-07-16 00:40:46 +04:00
sindoring 53d84ccc8a docs(M03): accept streamer internal access gate 2026-07-16 00:38:58 +04:00
sindoring 7e35de7f61 merge(M03): integrate streamer internal access gate 2026-07-16 00:37:29 +04:00
sindoring ba9f89691d coord(M03): hand off streamer access gate 2026-07-16 00:37:15 +04:00
sindoring b58bf2caa6 test(M03): enforce streamer internal access boundary 2026-07-16 00:37:00 +04:00
sindoring 52ea639d64 coord(M03): claim streamer internal access gate 2026-07-16 00:33:32 +04:00
sindoring a023d76eea docs(M03): accept render budget scheduler integration 2026-07-16 00:32:11 +04:00
sindoring 7ece2abea2 merge(M03): integrate render budget scheduler 2026-07-16 00:31:30 +04:00
sindoring 10c5d4cc12 coord(M03): hand off render budget scheduler 2026-07-16 00:31:21 +04:00
sindoring e52f703da5 rnd(M03): extract render budget scheduler 2026-07-16 00:31:01 +04:00
sindoring 91f0724ce2 coord(M03): claim render budget scheduler 2026-07-16 00:22:16 +04:00
sindoring 650d584ff0 docs(M03): accept streaming planner integration
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: post-merge planner/facade/focus/coordinate/manifest/shutdown and repository gates pass
Fidelity: target selection formulas and renderer behavior preserved
2026-07-16 00:11:57 +04:00
sindoring 643850c8ee Merge planner cold-start fix
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: stale-class-cache headless verifier passes
Fidelity: no planning behavior change
2026-07-16 00:11:20 +04:00
sindoring ad8fc41ace fix(M03): support cold planner startup
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: stale-class-cache headless planner verifier passes
Fidelity: type-resolution-only fix; planning behavior unchanged
2026-07-16 00:11:07 +04:00
sindoring d0f74f840b Merge work package M03-RND-STREAMING-PLANNER-001
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: planner/facade/focus/coordinate/manifest/shutdown contracts, dry-run and repository gates
Fidelity: preserves target sets, boundary prefetch, queue priority, caches and visible renderer behavior
2026-07-16 00:08:32 +04:00
sindoring 5b10288ff7 docs(M03): publish streaming planner handoff
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: planner and repository gates pass
Fidelity: no renderer policy or visual change
2026-07-16 00:08:24 +04:00
sindoring e887c3bad5 rnd(M03): extract streaming target planner
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: planner/facade/focus/coordinate/manifest/shutdown contracts, seven-checkpoint dry-run, repository gates
Fidelity: preserves wanted/retained radii, boundary prefetch, editor preview, queue priority and cache/render behavior
2026-07-16 00:08:02 +04:00
sindoring 0c24f96ae1 rnd(M03): claim streaming target planner
Work-Package: M03-RND-STREAMING-PLANNER-001
Agent: sindo-main-codex
Tests: not run (coordination-only commit)
Fidelity: no runtime change
2026-07-15 23:58:57 +04:00
sindoring ad0532925a qar(M03): exclude reference projects from Godot imports
Work-Package: M03-QAR-REFERENCE-IMPORTS-001
Agent: sindo-main-codex
Tests: headless Godot rescan; nested reference repositories remain clean
Fidelity: reference-only tooling change; runtime and renderer behavior unchanged
2026-07-15 23:56:15 +04:00
sindoring 9c3f7018fb docs(M03): accept renderer facade integration
Work-Package: M03-RND-FACADE-001
Agent: sindo-main-codex
Tests: post-merge verification follows
Fidelity: no runtime change
2026-07-15 23:49:59 +04:00
sindoring a100cede46 Merge work package M03-RND-FACADE-001
Work-Package: M03-RND-FACADE-001
Agent: sindo-main-codex
Tests: facade/focus/coordinate/manifest contracts, dry-run, coordination and documentation gates
Fidelity: no renderer target, budget, cache or visual behavior change
2026-07-15 23:49:49 +04:00
sindoring 2466fd67d3 docs(M03): publish renderer facade handoff
Work-Package: M03-RND-FACADE-001
Agent: sindo-main-codex
Tests: facade/focus contracts and repository gates pass
Fidelity: no visual or streaming-policy change
2026-07-15 23:49:21 +04:00
sindoring 3d528e3bbf rnd(M03): introduce renderer facade seam
Work-Package: M03-RND-FACADE-001
Agent: sindo-main-codex
Tests: facade/focus/coordinate/manifest contracts, renderer dry-run, coordination and documentation gates
Fidelity: focus targets, budgets, queues, caches and visible renderer behavior remain unchanged
2026-07-15 23:48:53 +04:00
sindoring a629bedccf rnd(M03): claim renderer facade seam
Work-Package: M03-RND-FACADE-001
Agent: sindo-main-codex
Tests: not run (coordination-only commit)
Fidelity: no runtime change
2026-07-15 23:43:50 +04:00
sindoring 4f566b1957 Merge pull request 'qar(M02): close player decomposition milestone' (#28) from work/sindo-main-codex/m02-integrator-closeout into master
Reviewed-on: #28
2026-07-15 22:38:42 +03:00
sindoring d9c847c557 docs(M02): hand off milestone closeout
Work-Package: M02-QAR-INTEGRATOR-CLOSEOUT-001
Agent: sindo-main-codex
Tests: records complete post-merge and renderer verification
Fidelity: records direct, reference-only and open-gap classifications
2026-07-15 23:35:22 +04:00
sindoring 1bf5b526be qar(M02): close player decomposition milestone
Work-Package: M02-QAR-INTEGRATOR-CLOSEOUT-001
Agent: sindo-main-codex
Tests: all M02 contracts, renderer dry-run, documentation and coordination gates passed
Fidelity: direct binding evidence and reference-only timing/routing are separated; remaining locomotion/camera gaps stay explicit
2026-07-15 23:35:00 +04:00
sindoring 9275fbe04f reference, scenes 2026-07-15 23:32:50 +04:00
sindoring eb122d52a9 Merge pull request 'gmp(M02): add 3.3.5 input profile' (#27) from work/sindo-main-codex/m02-input-profile-335 into master
Reviewed-on: #27
2026-07-14 23:20:22 +03:00
sindoring e819eeb35b docs(M02): hand off 3.3.5 input profile
Work-Package: M02-GMP-INPUT-PROFILE-335-001
Agent: sindo-main-codex
Tests: records complete package and renderer verification
Fidelity: records private-source hashes, public reference revisions and remaining gaps
2026-07-15 00:18:03 +04:00
sindoring 8776a6b362 gmp(M02): add 3.3.5 input profile
Work-Package: M02-GMP-INPUT-PROFILE-335-001
Agent: sindo-main-codex
Tests: player input/movement/capability/terrain/camera/presentation, coordinate/streaming, docs/coordination and renderer dry-run passed
Fidelity: build-12340 binding hashes and selected defaults pinned; TrinityCore turn rate and WoWee mouse routing are compatibility references with explicit limits
2026-07-15 00:17:34 +04:00
sindoring c3094c9413 Merge pull request 'gmp(M02): gate debug movement capabilities' (#26) from work/sindo-main-codex/m02-sandbox-capabilities into master
Reviewed-on: #26
2026-07-14 22:58:54 +03:00
sindoring 7d5d13f702 docs(M02): publish movement capability handoff
Work-Package: M02-GMP-SANDBOX-CAPABILITIES-001

Agent: sindo-main-codex
2026-07-14 23:56:00 +04:00
sindoring 742c415885 gmp(M02): gate debug movement capabilities
Work-Package: M02-GMP-SANDBOX-CAPABILITIES-001

Agent: sindo-main-codex

Tests: capability/input/movement/terrain/camera/presentation; coordinate/streaming/docs/coordination; renderer dry-run 7/7

Fidelity: proves debug sprint/free-flight exclusion in Blizzlike335; no positive movement parity claim
2026-07-14 23:55:27 +04:00
sindoring 8c71e46872 Merge pull request 'rnd(M02): extract character presenters' (#25) from work/sindo-main-codex/m02-character-presentation into master
Reviewed-on: #25
2026-07-14 22:44:11 +03:00
sindoring ad820699bc docs(M02): publish character presentation handoff
Work-Package: M02-RND-CHARACTER-PRESENTATION-001

Agent: sindo-main-codex
2026-07-14 23:42:42 +04:00
sindoring 97bb53f6cd rnd(M02): extract character presenters
Work-Package: M02-RND-CHARACTER-PRESENTATION-001

Agent: sindo-main-codex

Tests: presentation/input/movement/terrain/camera regressions; coordinate/streaming/docs/coordination gates; renderer dry-run 7/7

Fidelity: preserves current sandbox model/outfit and Stand/Idle/Run/Walk behavior; build-12340 parity remains unverified
2026-07-14 23:42:12 +04:00
sindoring 68e6f60d90 Merge pull request 'rnd(M02): extract third-person camera rig' (#24) from work/sindo-main-codex/m02-camera-rig into master
Reviewed-on: #24
2026-07-14 22:30:33 +03:00
sindoring 02dec23c2a docs(M02): publish camera rig handoff
Work-Package: M02-RND-CAMERA-001
2026-07-14 23:27:41 +04:00
sindoring 8c1cf9be36 rnd(M02): extract third-person camera rig
Work-Package: M02-RND-CAMERA-001
2026-07-14 23:27:03 +04:00
sindoring f5bb64e6c7 Merge pull request 'gmp(M02): extract player terrain query' (#23) from work/sindo-main-codex/m02-terrain-query into master
Reviewed-on: #23
2026-07-14 22:17:12 +03:00
sindoring 0ce3cae208 docs(M02): publish terrain query handoff
Work-Package: M02-GMP-TERRAIN-QUERY-001
Agent: sindo-main-codex
Tests: coordination and documentation gates; diff check
Fidelity: documents inherited height/snap regression and remaining collision evidence gap
2026-07-14 23:11:23 +04:00
sindoring a45d521567 gmp(M02): extract player terrain query
Work-Package: M02-GMP-TERRAIN-QUERY-001
Agent: sindo-main-codex
Tests: terrain query, player input and movement regressions; asset-free scene; renderer dry-run; coordinate, streaming, documentation and coordination gates
Fidelity: preserves existing ADT outer-grid interpolation and player ground snap; build-12340 collision parity remains unverified
2026-07-14 23:10:55 +04:00
sindoring 5206c42cf0 Merge pull request 'gmp(M02): extract local player movement' (#22) from work/sindo-main-codex/m02-local-movement into master
Reviewed-on: #22
2026-07-14 22:01:21 +03:00
sindoring d2d6df3655 docs(M02): publish local movement handoff
Work-Package: M02-GMP-MOVEMENT-001
Agent: sindo-main-codex
Tests: coordination and documentation gates; diff check
Fidelity: documents preserved sandbox behavior and remaining build-12340 evidence gap
2026-07-14 22:59:59 +04:00
sindoring 435e1c95d2 gmp(M02): extract local player movement
Work-Package: M02-GMP-MOVEMENT-001
Agent: sindo-main-codex
Tests: local movement and player input regressions; asset-free scene; renderer dry-run; coordinate, streaming, documentation and coordination gates
Fidelity: preserves current sandbox speed, acceleration, sprint and camera-relative flight behavior; build-12340 parity remains unverified
2026-07-14 22:59:33 +04:00
sindoring 14dead194b Merge pull request 'gmp(M02): add player input intent seam' (#21) from work/sindo-main-codex/m02-player-input into master
Reviewed-on: #21
2026-07-14 21:52:26 +03:00
sindoring e794764269 docs(M02): publish player input handoff
Work-Package: M02-GMP-INPUT-001
Agent: sindo-main-codex
Tests: coordination and documentation gates; diff check
Fidelity: documents regression evidence and unverified build-12340 semantics
2026-07-14 22:45:14 +04:00
sindoring 6bd2e84048 gmp(M02): add player input intent seam
Work-Package: M02-GMP-INPUT-001
Agent: sindo-main-codex
Tests: verify_player_input; renderer baseline dry-run; coordinate, streaming, documentation and coordination gates
Fidelity: preserves current sandbox bindings and movement/camera behavior; exact build-12340 semantics remain unverified
2026-07-14 22:44:44 +04:00
sindoring 62eece991c Merge pull request 'docs(M01): claim milestone closeout' (#20) from work/sindo-main-codex/m01-integrator-closeout into master
Reviewed-on: #20
2026-07-14 12:55:09 +03:00
sindoring fc81059dfa docs(M01): mark milestone closeout ready
Work-Package: M01-QAR-INTEGRATOR-CLOSEOUT-001

Agent: sindo-main-codex
2026-07-14 12:14:39 +04:00
sindoring dd412e6d0d test(M01): close coordinates and seams milestone
Work-Package: M01-QAR-INTEGRATOR-CLOSEOUT-001

Agent: sindo-main-codex

Tests: all M01 domain gates, unified renderer dry-run, coordination and documentation gates

Fidelity: five build-12340 points, raw ADT and mapped AzerothCore spawn evidence; no 1:1 claim
2026-07-14 12:13:43 +04:00
sindoring 07f08044f4 docs(M01): claim milestone closeout
Work-Package: M01-QAR-INTEGRATOR-CLOSEOUT-001

Agent: sindo-main-codex
2026-07-14 12:01:49 +04:00
sindoring c8e99f28d6 Merge pull request 'chore(M01): remove local spawn capture logs' (#19) from work/sindo-main-codex/m01-server-spawn-renderer into master
Reviewed-on: #19
2026-07-14 10:58:37 +03:00
sindoring 48cc976ba0 docs(M01): mark server spawn renderer ready
Work-Package: M01-QAR-SERVER-SPAWN-RENDERER-001

Agent: sindo-main-codex

Tests: server spawn GUI cold/warm, unified renderer dry-run, domain and documentation gates
2026-07-14 11:56:39 +04:00
sindoring 318e6ae757 chore(M01): remove local spawn capture logs
Work-Package: M01-QAR-SERVER-SPAWN-RENDERER-001

Agent: sindo-main-codex

Tests: renderer dry-run, coordinate/domain gates, GUI cold/warm inspection
2026-07-14 11:55:05 +04:00
sindoring 747fae8e2e Merge pull request 'координаты' (#18) from work/sindo-main-codex/m01-server-spawn-renderer into master
Reviewed-on: #18
2026-07-14 10:48:53 +03:00
sindoring 24ddc99a7b координаты 2026-07-14 11:48:14 +04:00
sindoring 52b6fa2ebb Merge pull request 'docs(M01): claim server spawn renderer evidence' (#17) from work/sindo-main-codex/m01-server-spawn-renderer into master
Reviewed-on: #17
2026-07-14 10:42:14 +03:00
sindoring 3933dbfc47 docs(M01): claim server spawn renderer evidence
Work-Package: M01-QAR-SERVER-SPAWN-RENDERER-001

Agent: sindo-main-codex
2026-07-13 17:41:02 +04:00
sindoring a07a487368 docs(M01): mark coordinate boundary gate ready
Work-Package: M01-FND-COORD-BOUNDARY-GATE-001

Agent: sindo-main-codex
2026-07-13 16:40:07 +04:00
sindoring 47dec6c32c feat(M01): enforce coordinate conversion boundaries
Work-Package: M01-FND-COORD-BOUNDARY-GATE-001

Agent: sindo-main-codex
2026-07-13 16:37:29 +04:00
sindoring c8e62acf4c docs(M01): claim coordinate boundary gate
Work-Package: M01-FND-COORD-BOUNDARY-GATE-001

Agent: sindo-main-codex
2026-07-13 16:15:18 +04:00
sindoring 7c64e8d60a docs(M01): mark domain identities ready
Work-Package: M01-FND-DOMAIN-IDENTITIES-001

Agent: sindo-main-codex
2026-07-13 16:11:35 +04:00
sindoring c1dc07c705 feat(M01): add typed domain identities
Work-Package: M01-FND-DOMAIN-IDENTITIES-001

Agent: sindo-main-codex
2026-07-13 16:11:00 +04:00
sindoring d68786355c docs(M01): claim domain identity contracts
Work-Package: M01-FND-DOMAIN-IDENTITIES-001

Agent: sindo-main-codex
2026-07-13 16:05:55 +04:00
sindoring 77cd17a5bc docs(M01): mark streaming focus ready
Work-Package: M01-RND-STREAMING-FOCUS-001

Agent: sindo-main-codex
2026-07-13 15:45:32 +04:00
sindoring 7815385b3b feat(M01): add explicit streaming focus
Work-Package: M01-RND-STREAMING-FOCUS-001

Agent: sindo-main-codex
2026-07-13 15:41:16 +04:00
sindoring fbef131bcb docs(M01): claim streaming focus contract
Work-Package: M01-RND-STREAMING-FOCUS-001

Agent: sindo-main-codex
2026-07-13 15:35:45 +04:00
sindoring 52e1b2ba67 docs(M01): mark coordinate fixtures ready
Work-Package: M01-QAR-COORD-FIXTURES-001

Agent: sindo-main-codex
2026-07-13 14:28:03 +04:00
sindoring ce4f7ec0e2 test(M01): add coordinate golden fixtures
Work-Package: M01-QAR-COORD-FIXTURES-001

Agent: sindo-main-codex
2026-07-13 14:27:38 +04:00
sindoring 758b375e7e docs(M01): claim coordinate golden fixtures
Work-Package: M01-QAR-COORD-FIXTURES-001
Agent: sindo-main-codex
Tests: not applicable (claim only)
Fidelity: pinned AzerothCore spawn, raw ADT metadata and build 12340 observations scoped
2026-07-13 14:23:23 +04:00
sindoring b55bfca2a7 docs(M01): mark ADT tile axis fix ready
Work-Package: M01-FND-COORD-TILE-AXIS-002
Agent: sindo-main-codex
Tests: coordination and documentation gates passed
Fidelity: handoff records raw Azeroth_31_49 MCNK evidence
2026-07-13 14:13:10 +04:00
sindoring a7072e3d14 fix(M01): correct ADT filename coordinate axes
Work-Package: M01-FND-COORD-TILE-AXIS-002
Agent: sindo-main-codex
Tests: coordinate mapper, M00 calibration, baseline manifest, coordination and documentation gates passed
Fidelity: Azeroth_31_49 raw MCNK origins and OpenRealm confirm filename X from world Y and filename Y from world X
2026-07-13 14:12:51 +04:00
sindoring f9c467b71f docs(M01): claim ADT tile axis correction
Work-Package: M01-FND-COORD-TILE-AXIS-002
Agent: sindo-main-codex
Tests: not applicable (claim only)
Fidelity: raw Azeroth_31_49 ADT and OpenRealm independently expose swapped filename axes
2026-07-13 14:09:20 +04:00
sindoring 1c0dfc761d docs(M01): mark coordinate contract handoff ready
Work-Package: M01-FND-COORDS-001
Agent: sindo-main-codex
Tests: coordination and documentation gates passed
Fidelity: handoff records five build 12340 golden coordinate points
2026-07-13 13:30:18 +04:00
sindoring f45695c26a fnd(M01): add canonical coordinate mapper
Work-Package: M01-FND-COORDS-001
Agent: sindo-main-codex
Tests: coordinate mapper, M00 coordinate calibration, baseline manifest, coordination and documentation gates passed
Fidelity: five build 12340 camera points map within 0.002 yard; no visual parity claim
2026-07-13 13:29:59 +04:00
sindoring c2be8ae95e docs(M01): claim canonical coordinate contract
Work-Package: M01-FND-COORDS-001
Agent: sindo-main-codex
Tests: not applicable (claim only)
Fidelity: scope references merged M00 build 12340 coordinate evidence
2026-07-13 13:22:02 +04:00
sindoring 8636427234 Merge pull request 'test(M00): close renderer baseline milestone' (#16) from work/sindo-main-codex/m00-integrator-closeout into master
Reviewed-on: #16
2026-07-13 12:17:51 +03:00
sindoring 5627180fd5 docs(M00): mark closeout handoff ready
Work-Package: M00-QAR-INTEGRATOR-CLOSEOUT-001
Agent: sindo-main-codex
Tests: coordination and documentation gates
Fidelity: metadata-only handoff; no compatibility claim change
2026-07-13 13:17:06 +04:00
sindoring ba81098797 test(M00): close renderer baseline milestone
Work-Package: M00-QAR-INTEGRATOR-CLOSEOUT-001
Agent: sindo-main-codex
Tests: post-merge render baseline dry-run; comparator self-test; coordination and documentation gates
Fidelity: records seven build-12340 reference categories and measured gaps without claiming parity
2026-07-13 13:16:52 +04:00
sindoring f19c0c45a7 Merge pull request 'fix(M00): release GUI capture render resources' (#15) from work/sindo-main-codex/m00-gui-rid-shutdown into master
Reviewed-on: #15
2026-07-13 12:12:33 +03:00
409 changed files with 36560 additions and 2090 deletions
+4
View File
@@ -35,3 +35,7 @@
# CMake build директория
/src/native/build/
# Local root-level diagnostic capture logs
/*.stdout.log
/*.stderr.log
+557 -4
View File
@@ -25,7 +25,32 @@ Paired run 2026-07-11 подтвердил крупный coordinate/placement g
## Основные файлы
- `src/scenes/streaming/streaming_world_loader.gd` - главный runtime streamer мира.
- `src/render/world_render_facade.gd` - стабильная граница runtime/tools для focus и metrics.
- `src/scenes/streaming/streaming_world_loader.gd` - внутренний runtime streamer мира.
- `src/render/wmo/wmo_placement_resolver.gd` - value-only WMO path, identity and transform rules.
- `src/render/wmo/wmo_placement_registry.gd` - WMO placement-key to tile/global reference sets; Nodes and render jobs remain in the streamer.
- `src/render/wmo/wmo_render_build_step_planner.gd` - mesh-first lightweight WMO group operation and cursor planning without Nodes or Resources.
- `src/render/wmo/wmo_render_build_queue.gd` / `wmo_render_build_job.gd` - typed pending group jobs, FIFO placement keys and strong root/resource references without engine destruction.
- `src/render/wmo/wmo_render_resource_cache_state.gd` - validated lightweight WMO render Resources, negative cache and pending cache paths without ResourceLoader I/O.
- `src/render/wmo/wmo_scene_resource_cache_state.gd` - validated cached-WMO PackedScenes, negative cache and pending `.tscn` paths without file/I/O/Node ownership.
- `src/render/liquid/adt_water_load_pipeline_state.gd` - ADT water pending FIFO/dedupe, active task IDs and worker-safe parsed-result mailbox without parser or Node ownership.
- `src/render/liquid/adt_water_scene_finalizer.gd` - stateless main-thread ADT water build/attach and optional persisted Editor ownership through the existing ADTBuilder.
- `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` - memoized billboard/UV-rotation decision for stale cached M2 runtime mesh refresh.
- `src/render/m2/m2_animated_scene_finalizer.gd` - animated PackedScene candidate ownership, historical material repair and AnimationPlayer validation.
- `src/render/m2/m2_animation_playback_controller.gd` - deterministic per-instance phase, default animation selection and native/imported playback mutation.
- `src/render/m2/m2_animated_instance_materializer.gd` - main-thread animated instance duplication, render settings, playback startup and non-empty batch attachment.
- `src/render/m2/m2_static_batch_materializer.gd` - main-thread static M2 MultiMesh construction, render settings and attachment.
- `src/render/m2/m2_build_queue.gd` / `m2_build_job.gd` - typed pending M2 jobs, FIFO/stale tile keys, grouped-transform references and progress cursors without engine destruction.
- `src/render/m2/m2_build_dispatch_planner.gd` - pure animation/static wait, materialization and missing-model advance decision.
- `src/render/m2/m2_build_resource_snapshot.gd` - typed per-step animated/static resource observations without engine destruction.
- `src/render/m2/m2_static_build_resource_observer.gd` - static Mesh cache lookup, threaded request selection and missing transition.
- `src/render/m2/m2_cached_animation_resource_observer.gd` - cached animated GLB eligibility, threaded request admission and snapshot production.
- `src/render/m2/m2_animation_load_pipeline_state.gd` - animated M2 threaded-load request records and completion-order finalize FIFO without I/O or Node ownership.
- `src/render/m2/m2_mesh_load_pipeline_state.gd` - static M2 threaded-load request records, terminal statuses and completion-order finalize FIFO without I/O or Mesh ownership.
- `src/render/m2/m2_mesh_resource_cache_state.gd` - normalized-path prepared static M2 Mesh references with final-shutdown lifetime.
- `src/render/m2/m2_mesh_resource_extractor.gd` - first-Mesh selection from direct/PackedScene/Node inputs with temporary instance cleanup.
- `src/render/m2/m2_runtime_mesh_finalizer.gd` - refresh version, classifier lifetime, M2Builder rebuild and original-Mesh fallback.
- `src/render/m2/m2_prototype_cache_state.gd` - shutdown-lifetime static/animated prototype Nodes and missing/static-only path outcomes.
- `src/scenes/streaming/eastern_kingdoms_streaming.tscn` - текущая сцена для проверки Azeroth/Eastern Kingdoms.
- `addons/mpq_extractor/loaders/adt_builder.gd` - сборка ADT terrain, control splat материалов и MH2O liquids.
- `addons/mpq_extractor/loaders/m2_builder.gd` - сборка статического M2 mesh/material.
@@ -304,7 +329,7 @@ Native M2 animation first pass for composite doodads:
- `M2NativeAnimator` evaluates the selected Stand sequence and applies WoW-style bone matrices:
`T(pivot + translation) * R * S * T(-pivot) * parent`.
- Vertex influences are resolved through `.skin` local bone indices and the M2 `boneCombos` palette, matching the original section/batch renderer model used by WoW/whoa.
- `StreamingWorldLoader` routes only `gryphonroost` through this native path; simple critters still use the existing GLB allowlist and most M2 world props stay on static MultiMesh.
- `M2NativeAnimationResourceObserver` routes only `gryphonroost` through this native path; simple critters still use the existing GLB allowlist and most M2 world props stay on static MultiMesh.
- Current implementation rebuilds the animated `ArrayMesh` on CPU. This is correct enough for the problematic rare doodads and gives us the same data layout that can later move to shader/GPU skinning.
Причина: композитные doodad вроде `GryphonRoost` снова ломают визуал при GLB-анимации. До M2-native renderer все world placement M2 должны оставаться статическими. GLB-анимация оставлена только как вручную включаемый debug experiment через allowlist.
@@ -393,6 +418,12 @@ Known limits:
- `WMOBuilder` строит group meshes;
- runtime registry дедуплицирует WMO по `unique_id`;
- WMO на границе ADT не дублируются;
- `WmoPlacementRegistry` owns only placement reference sets; the streamer keeps
the parallel key-to-Node map and performs final-release cancellation/free;
- `WmoRenderBuildStepPlanner` selects one mesh or MultiMesh group and its next
cursors; the streamer still materializes Nodes and consumes the frame permit;
- `WmoRenderBuildQueue` owns pending typed jobs/FIFO keys and releases references
on cancel/reset; the streamer still validates and frees engine objects;
- большие WMO не должны инстанцироваться как тяжелые `.tscn` во время движения;
- добавлен lightweight render-cache path для WMO groups;
- `wmo_render_group_ops_per_tick` ограничивает подключение WMO groups по кадрам;
@@ -788,7 +819,12 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
- Billboard metadata is resolved per drawn vertex through the skin section bone palette and vertex bone indices. This avoids treating an entire mixed surface as billboard just because the palette contains a billboard bone.
- `M2Builder` stores billboard pivot/mode in `Mesh.ARRAY_CUSTOM0` as RGBA float data. `CUSTOM0.xyz` is the Godot-space bone pivot; `CUSTOM0.w` is the billboard mode: spherical, lock X, lock Y, or lock Z.
- `WowM2Material` applies camera-facing billboarding in the vertex shader for marked vertices. This works with grouped ADT M2 `MultiMesh` batches because the shader uses each instance transform.
- Runtime M2 cache refresh now has `M2_MATERIAL_REFRESH_VERSION`. Old cached `.tscn/.glb` meshes are replaced in memory from raw `.m2` only when the raw model actually needs custom billboard attributes or UV-rotation data. Do not rebuild every stale cached M2 during streaming: creating fresh materials for ordinary props can exhaust the D3D12 resource descriptor heap.
- Runtime M2 cache refresh uses material refresh version `2` (now
`M2RuntimeMeshFinalizer.MATERIAL_REFRESH_VERSION`). Old cached `.tscn/.glb`
meshes are replaced in memory from raw `.m2` only when the raw model actually
needs custom billboard attributes or UV-rotation data. Do not rebuild every
stale cached M2 during streaming: creating fresh materials for ordinary props
can exhaust the D3D12 resource descriptor heap.
- Verified with `ElwynnGrass1.m2`: native data exposes a billboard batch, the built mesh contains `CUSTOM0`, and the M2 shader compiles with the billboard uniform enabled.
- Remaining parity work: many Elwynn tree canopy M2s do not carry billboard bone flags, so their flat-looking leaves are a separate material/geometry parity issue, not this bone-billboard path.
@@ -798,7 +834,8 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
- Texture transform dictionaries now expose static UV rotation matrices and approximate Z-rotation speeds, in addition to existing translation, translation speed and scale.
- `WowM2Material` applies UV rotation and scale around WoW's texture center `(0.5, 0.5)`, then applies translation/scroll. This is closer to the client path shown by `CM2Model::AnimateTextureTransformsMT`.
- All four M2 texture stages receive UV rotation and rotation-speed uniforms, matching the existing four-stage texture binding path.
- `M2_MATERIAL_REFRESH_VERSION` and `M2Builder.MATERIAL_FORMAT_VERSION` are bumped to `2`, so cached M2 meshes are rebuilt in memory when raw `.m2` data is available.
- Runtime M2 material refresh and `M2Builder.MATERIAL_FORMAT_VERSION` are `2`,
so stale cached M2 meshes can be rebuilt in memory when raw `.m2` data is available.
- Verified with `Spells/ArcaneForceShield_Blue.m2`: native data exposes a non-identity UV rotation, the built material receives the stage rotation uniform, and the shader compiles.
## 2026-07-08 Renderer Material Regression Smoke
@@ -830,6 +867,522 @@ $exe = Join-Path $env:TEMP 'godot-4.6.1-openwc\Godot_v4.6.1-stable_win64.exe'
- A focused 8-second GUI checkpoint and a seven-checkpoint single-pass GUI run finish without Node, renderer resource or RID leak diagnostics. A timing-dependent anonymous zero-reference `RefCounted` ObjectDB warning can remain at engine teardown; it is tracked separately and is not suppressed.
- This is shutdown-only ownership behavior and does not alter checkpoint rendering, cache formats or the `Blizzlike335` compatibility profile.
## 2026-07-13 Explicit Streaming Focus
- `StreamingWorldLoader` consumes an immutable `StreamingFocus` containing a
typed `GodotWorldPosition`; runtime streaming no longer discovers the active
`Camera3D` from the viewport or child nodes.
- Eastern Kingdoms and Kalimdor runtime scenes explicitly use the player as the
streaming source. Checkpoint capture and terrain/occluder probes explicitly
use their dedicated camera through the same public refresh path.
- The editor viewport camera remains behind a dedicated editor-only adapter.
`camera_path` is retained solely for optional automatic overview positioning.
- Streaming radii, LOD decisions, cache formats and renderer quality profiles
are unchanged by this migration.
## 2026-07-13 Coordinate Conversion Boundary Gate
- Sky/area lookup, streaming tile ownership, player spawn/terrain sampling and
terrain diagnostics now cross world-coordinate spaces through typed
`CoordinateMapper` methods.
- Renderer distance, LOD, ray and model-local M2/WMO calculations remain in
their existing Godot/local spaces; they are not axis conversions.
- The repository gate rejects new manual world-center, WoW/Godot and
Godot-position-to-ADT formulas. The unified renderer baseline runs this gate.
- Native ADT/WDT parsers keep one shared world conversion in
`wow_chunk_reader.h`; the independent M00 calibration formula remains a test oracle.
- Coordinate contract version 2, renderer radii, cache formats and visual
quality profiles are unchanged.
## 2026-07-13 Golden Server Spawn Renderer Evidence
- The pinned AzerothCore Human Warrior start at server position
`(-8949.95, -132.493, 83.5312)` maps through `CoordinateMapper` to Godot
`(17199.159666667, 83.5312, 26016.616666667)`, ADT `(32,48)` and MCNK `(3,12)`.
- `server_spawn_render_manifest.json` drives the ordinary Eastern Kingdoms
checkpoint capture pipeline at that location; no live database or production
spawn entity is introduced.
- A renderer-native emissive marker starts at the exact mapped origin and keeps
depth testing enabled so GUI evidence exposes its relationship to terrain.
- `verify_server_spawn_renderer.gd` rejects drift between the pinned fixture,
manifest target/player coordinates and expected ADT ownership. The unified
renderer baseline runs this contract check before capture.
- This closes a coordinate/renderer integration criterion only. It does not
claim TrinityCore row equivalence or visual parity with the original client.
## 2026-07-15 WorldRenderFacade First Seam
- Runtime streaming scenes, checkpoint capture and renderer probes now submit
typed focus through `WorldRenderFacade`; those callers no longer call the
monolithic streamer's focus methods or configure its focus source directly.
- Baseline capture reads queue/cache/activity counts through
`renderer_metrics_snapshot()`. The facade returns a deep copy, so diagnostics
cannot mutate renderer-owned state.
- `StreamingWorldLoader` remains the internal implementation and still owns all
queues, tasks, caches, nodes and RIDs. The facade adds no parallel state or
replacement scheduler.
- This package does not alter targets, budgets, cache versions, placement,
visibility, materials or animation. Environment, entity visuals and ground
query remain explicit follow-up facade contracts in M03.
## 2026-07-15 StreamingTargetPlanner Extraction
- Runtime and editor wanted/retained ADT set calculation now lives in the
scene-free `StreamingTargetPlanner`.
- `StreamingTargetPolicy` preserves existing chunk/tile radii, warm/prewarm/
retain margins and the clamped boundary-prefetch threshold.
- `StreamingTargetPlan` freezes its focus/wanted/retained result; the streamer
still owns available paths, load-queue distance sorting, loaded-tile LOD/detail
state, async work, caches, nodes and RIDs.
- Asset-free fixtures cover center, edge/corner prefetch, catalog filtering,
clamp and editor behavior plus bounded High-like iteration timing.
- Cache versions, quality profiles, queue priority and visible rendering rules
are unchanged by the extraction.
## 2026-07-16 RenderBudgetScheduler Extraction
- The scene-free `RenderBudgetScheduler` now owns the per-frame counters for 16
named renderer operation lanes. It owns no queue, task, cache, Node or RID.
- `StreamingWorldLoader` snapshots its existing exported limits once per process
frame and asks for a boolean permit immediately before each historically
budgeted operation.
- Chunk removals and creations intentionally share one `chunk_geometry` lane;
the existing removal-first drain order and combined limit are preserved.
- Loader teardown cancels permit issuance before waiting for asynchronous work.
Existing worker/result staleness checks and resource cleanup remain unchanged.
- Asset-free contracts cover exact bounds, independent/shared lanes, frame reset,
invalid inputs, terminal cancellation, detached diagnostics and hot-path timing.
- Defaults, quality presets, queue order, cache versions and visible rendering
rules are unchanged. Asset-backed p95/p99 comparison remains required.
## 2026-07-16 Streamer Internal Access Gate
- `verify_renderer_internal_access.gd` derives private queue, task, cache and
tile-state field names from `StreamingWorldLoader` declarations and rejects
external member/reflection access in gameplay and Godot `EditorPlugin` package sources.
- The gate follows the implementation inventory instead of duplicating a manual
list, so a newly named private renderer field is covered automatically when it
matches those ownership categories.
- Scene composition may still reference the loader script and the facade's
implementation path; those are composition details, not mutable queue access.
- Renderer diagnostic probes are added explicitly as their facade contracts land.
- This is a source-only boundary check and changes no renderer behavior or fidelity.
## 2026-07-16 Rendered Ground Query Facade
- `WorldRenderFacade.sample_ground_height()` accepts `GodotWorldPosition` and
returns renderer-owned immutable `RenderedGroundSample` from already loaded
quality/tile-LOD render meshes, avoiding a render-to-gameplay dependency.
- Its factories load the predeclared Script path instead of relying on a warm
global-class cache; a dedicated cold-start verifier covers this bootstrap path.
- `renderer_ground_query_snapshot()` preserves the terrain probe's tile readiness,
queue position, mesh bounds and nearby seam-fallback diagnostics as a detached
Dictionary; callers receive no queue, tile-state, Mesh or Node reference.
- `probe_render_terrain_height.gd` now uses this facade snapshot and no longer reads
`_tile_states`, `_tile_load_queue`, `_tile_loading_tasks` or `_available_tiles`.
- Gameplay continues using the independently composed M02 `TerrainQuery`/
`AdtTerrainQuery`. Loaded render meshes are diagnostic and are not authoritative
movement collision.
- The ray height, 3x3 tile search and `2/5/10/20/40`-unit nearby fallback are
preserved. No terrain geometry, cache, streaming or visual behavior changed.
## 2026-07-16 Terrain Chunk LOD Planner Extraction
- `TerrainChunkLodPlanner` now owns the pure mapping from populated parsed ADT
chunk indices to desired terrain LOD 0/1/2.
- `TerrainChunkLodPolicy` snapshots the existing enabled flag, three raw chunk
radii and chunk size once per streaming-target application.
- Horizontal XZ distance to the chunk center, inclusive squared thresholds,
sparse-index omission and historical negative-radius squaring are unchanged.
- `StreamingWorldLoader` still owns tile/chunk state, queues, operation budgets,
creation/removal, Mesh/Node/RID finalization and tile-LOD fallback.
- Cache formats, quality presets and visible behavior are unchanged. The
scene-free contract is formula evidence only; asset-backed p95/p99 and visual
acceptance remain required.
## 2026-07-16 Terrain Chunk Geometry Queue Planner Extraction
- `TerrainChunkGeometryQueuePlanner` now compares current and desired chunk state
and returns fresh chunk create/remove request arrays.
- LOD mismatches and absent chunks retain the existing request shape; creates
remain sorted by horizontal distance from chunk origin to streaming focus.
- Desired tile LOD still removes every current chunk, while sparse parsed chunk
records retain the existing tile-origin distance fallback.
- `StreamingWorldLoader` still owns queue storage/drains, removal-first execution,
the shared `chunk_geometry` budget lane and every Mesh/Node/RID side effect.
- Cache formats, quality profiles and visual rules are unchanged. Asset-backed
p95/p99 and visual acceptance remain required.
## 2026-07-16 M2 Unique Placement Registry Extraction
- `M2UniquePlacementRegistry` now owns positive ADT `MDDF.uniqueId` reservations
across streamed tiles using the existing `uid:<decimal>` keys.
- Missing/zero/negative IDs remain untracked; duplicates inside one reservation
call are suppressed and another tile records one skipped key until owner release.
- Owner release still lets the loader find eligible waiting tiles and enqueue the
existing detail retry path. First ownership remains tile-processing-order based.
- `StreamingWorldLoader` still owns tile state, retry queues, grouping/build tasks,
M2 caches, MultiMesh/Node/Mesh/material/RID creation and all operation budgets.
- M2/ADT cache formats, transforms, profiles and visible rules are unchanged.
Asset-backed cross-boundary p95/p99 and visual acceptance remain required.
## 2026-07-16 M2 Placement Transform Resolver Extraction
- `M2PlacementTransformResolver` now owns the pure ADT M2 basis and local
origin-compensation formulas used by worker grouping, placeholders and direct
instance creation.
- Regular models retain `Basis.from_euler`; `ElwynnCliffRock01/02` retain their
narrow shell correction; `NewWaterfall` and `ElwynnTallWaterfall01` retain
calibrated world yaw, local fall-axis twist and tall-sheet anchor compensation.
- Path separator/case/MDX handling, constants and caller-specific final scale
clamps are unchanged. The resolver owns no Node, task, cache or RID.
- Cache formats, placement positions and visible rules are unchanged by the
extraction. Asset-backed visual recheck and general placement parity remain gaps.
## 2026-07-16 M2 Placement Grouper Extraction
- `M2PlacementGrouper` now owns pure worker-side validation, historical path
normalization and ordered tile-local transform grouping of ADT M2 placements.
- Invalid records, backslash replacement, lowercase `.mdx/.mdl` conversion,
first-seen ordering and the `0.0001` scale clamp are unchanged.
- The loader retains tasks, mutex/result queues, stale-result checks, tile/build
state, caches and all Node/MultiMesh/Mesh/material/RID finalization.
- Cache formats, batching, profiles and visible rules are unchanged. Asset-backed
traversal p95/p99, visual recheck and spatial-cell grouping remain pending.
## 2026-07-17 M2 Build Batch Planner Extraction
- `M2BuildBatchPlanner` now owns pure static/animated batch-limit selection,
remaining-count capping and next-offset/group-completion calculation.
- Existing animated/static limits still clamp to at least one; resource readiness,
queue rotation, serial progression and `M2_BUILD` permit consumption are unchanged.
- `M2BuildQueue` now retains typed pending jobs, FIFO/stale keys and cursors.
The loader retains tile cancellation, resource readiness/retries and all
Node/MultiMesh/Mesh/material/RID finalization.
- Cache formats, quality profiles, batching output and visible rules are unchanged.
Asset-backed p95/p99 and spatial-cell batching evidence remain pending.
## 2026-07-18 M2 Build Dispatch Planner Extraction
- `M2BuildDispatchPlanner` now owns the pure action priority between pending
animation, animated materialization, pending/ready static Mesh and terminally
missing-model advancement.
- Pending animation still wins before batch planning. Unresolved static Meshes
still rotate and consume one `M2_BUILD` permit without advancing progress.
- Empty batches advance without serial change; animated, static and terminally
missing positive batches retain the historical serial increment.
- Resource lookup/request order, queue/cursor adoption, materialization, permits,
Node lifetime, cache formats, profiles and visible rules remain unchanged.
## 2026-07-18 M2 Build Resource Snapshot Extraction
- `M2BuildResourceSnapshot` now carries one build-step normalized path, optional
animated prototype, pending-animation state, optional static Mesh and terminal
missing-model state through typed accessors.
- Animation observation is captured first. Static observation is adopted only
for a positive non-animated batch after animation is no longer pending.
- The dispatch planner consumes the snapshot; the loader borrows the selected
prototype/Mesh for the existing materializer calls.
- ResourceLoader/cache requests, permit/cursor transitions, engine lifetime,
cache formats, profiles and visible rules remain unchanged.
## 2026-07-18 M2 Static Build Resource Observer Extraction
- `M2StaticBuildResourceObserver` now owns prepared-Mesh lookup, existing-request
detection, `.tscn`-before-`.glb` candidate selection, pivot-prefix GLB rejection,
threaded request admission and terminal missing transition for build jobs.
- The observer fills `M2BuildResourceSnapshot`; the loader retains animation
observation, finalize drains, materialization, permits and engine lifetime.
- Candidate order, request errors, cache formats, profiles and visuals are unchanged.
## 2026-07-17 M2 Runtime Mesh Rebuild Classifier Extraction
- `M2RuntimeMeshRebuildClassifier` now owns the memoized decision used when a
stale cached M2 mesh may need an in-memory rebuild from raw data.
- Existing triggers are unchanged: any billboard flag/positive billboard vertex
count, or a referenced non-identity UV rotation/absolute rotation speed above
`0.000001` across at most four texture stages.
- Invalid batch variants and combo/transform indices remain skipped. The first
decision for a normalized path remains cached until the same two historical
map/shutdown clear sites.
- `StreamingWorldLoader` still requests raw M2 data and owns ResourceLoader,
returned-Mesh adoption and finalization budgets. Refresh-version checks,
M2Builder rebuild and fallback now belong to `M2RuntimeMeshFinalizer`.
- No cache version, shader, material, geometry or visible rule changed. Synthetic
predicate timing is not asset-backed descriptor-pressure/p95/p99 evidence.
## 2026-07-17 M2 Mesh Load Pipeline State Extraction
- `M2MeshLoadPipelineState` now owns static M2 successful threaded-load request
records and the FIFO populated when ResourceLoader polling reaches loaded or
failed terminal status.
- Pending polling snapshots preserve Dictionary insertion order; terminal FIFO
order follows completion transition order. Existing `m2_mesh` metrics still
count pending plus finalize records at all three sites.
- Shutdown still drains every pending ResourceLoader path before state clear;
map reset and shutdown retain the same two clear sites.
- `StreamingWorldLoader` still selects `.tscn`/`.glb` paths, excludes animated
pivot-prefix GLBs, performs all ResourceLoader calls, consumes
`M2_MESH_FINALIZE`, extracts/prepares Meshes and updates Mesh/missing caches.
- Cache formats, material refresh, profiles and visible output are unchanged.
Synthetic state timing is not asset-backed I/O, leak or p95/p99 evidence.
## 2026-07-17 M2 Animation Load Pipeline State Extraction
- `M2AnimationLoadPipelineState` now owns animated M2 successful threaded-load
request records and the FIFO populated at loaded/failed terminal transitions.
- Pending snapshots preserve insertion order, while finalization follows completion
order. All three historical `m2_animation` metrics still count both stages.
- Shutdown still drains pending ResourceLoader paths before clear; map reset and
shutdown preserve the same two clear sites.
- At this extraction stage, `StreamingWorldLoader` retained GLB eligibility/cache selection, every
ResourceLoader call, `M2_ANIMATION_FINALIZE` permits, scene instantiation,
material repair and prototype/static-fallback decisions.
- Cache formats, animation behavior and visible output are unchanged. Synthetic
timing is not asset-backed I/O, leak, animation-fidelity or p95/p99 evidence.
## 2026-07-18 M2 Cached Animation Resource Observer Extraction
- `M2CachedAnimationResourceObserver` now owns cached animated-prototype lookup,
allow/deny matching, historical `.glb` candidate selection, GLB animation/
primitive/schema safety validation, request admission and static-only fallback.
- The observer produces `M2BuildResourceSnapshot` with the exact borrowed cached
prototype or pending state and retains/frees no engine object.
- Native GryphonRoost raw-data build/debug logging belongs to the sibling native
observer; terminal polling, finalization, permits and SceneTree mutation remain
loader-owned.
- Defaults, path order, accepted empty/`pivot_prefix_v1` schemas, cache format,
metrics and visible behavior are unchanged. Generated GLB metadata fixtures
are not private asset, leak, p95/p99 or original-client animation evidence.
## 2026-07-18 M2 Native Animation Resource Observer Extraction
- `M2NativeAnimationResourceObserver` now owns the exact case-insensitive
`gryphonroost` candidate rule, animated prototype/static-only cache checks,
synchronous raw animated read, native builder call, adoption and success log.
- The native attempt remains first. Empty raw data/surfaces and null/childless
builds retain the same static-only fallback; accepted Nodes remain owned by
`M2PrototypeCacheState` until final shutdown.
- `StreamingWorldLoader` retains observer order, typed snapshot construction,
cached-GLB fallback, terminal ResourceLoader polling/finalize, permits,
materialization and SceneTree lifetime.
- The historical unfreed childless builder result is deliberately preserved and
documented as a leak risk. No parser, cache, profile or visible rule changed;
synthetic fixtures are not asset-backed animation or performance evidence.
## 2026-07-18 M2 Animated Scene Finalizer Extraction
- `M2AnimatedSceneFinalizer` now owns terminal animated PackedScene candidate
instantiation, depth-first Mesh/AnimationPlayer traversal, static-prototype
material override mapping and rejected detached-root destruction.
- Material repair retains source/target depth-first ordering, source-index clamp,
per-surface override priority and first discovered material fallback.
- A candidate still requires at least one AnimationPlayer. Accepted exact Node3D
and player count return to the loader; invalid candidates are freed, including
the previously unhandled non-Node3D imported-root case.
- `StreamingWorldLoader` retains ResourceLoader I/O/statuses, finalize permits,
material-prototype selection/loading, prototype adoption/static fallback,
animation playback policy and diagnostic logging.
- Cache formats, profiles and visible rules are unchanged. Synthetic scene and
material fixtures are not asset-backed animation-fidelity or p95/p99 evidence.
## 2026-07-18 M2 Animation Playback Controller Extraction
- `M2AnimationPlaybackController` now owns the stable path/index phase formula,
ordinary/fish/bird animation-name priority, substring/first-name fallbacks and
AnimationPlayer linear-loop/play/seek mutation.
- Exact-script native animators retain depth-first pairing, the same five copied
fields, optional `prepare_runtime`, phase assignment and debug-state sampling.
- Native debug records are detached and sampled only when `debug_streaming` is
enabled; `StreamingWorldLoader` retains path normalization and exact log text.
- Loader still duplicates/transforms/attaches instances, applies visibility,
shadows and Editor ownership, owns build cursors/permits and obtains player inventory.
- Cache formats, playback priorities, phase behavior and visible rules are unchanged.
Synthetic timing is not asset-backed animation fidelity or p95/p99 evidence.
## 2026-07-17 M2 Mesh Resource Cache State Extraction
- `M2MeshResourceCacheState` now owns normalized-path references to prepared
static M2 Mesh resources. Lookup returns the exact retained reference and a
later store for one path keeps the historical replacement behavior.
- The cache retains map-refresh lifetime and clears only at the existing final
runtime-cache shutdown site after asynchronous work is drained.
- `StreamingWorldLoader` still owns normalization, `.tscn`/`.glb` selection,
ResourceLoader I/O, Mesh extraction, material refresh/rebuild, cache adoption
decisions, permits and MultiMesh materialization. Prototype/negative state is
now a sibling cache service.
- Cache formats, refresh metadata, profiles and visible output are unchanged.
Synthetic cache timing is not asset-backed memory/leak or p95/p99 evidence.
## 2026-07-17 M2 Mesh Resource Extractor
- `M2MeshResourceExtractor` now selects the first Mesh from direct Mesh
Resources, temporary PackedScene instances and existing prototype/rebuild
Node subtrees using the prior depth-first child order.
- PackedScene extraction still instantiates exactly once and synchronously frees
the temporary root before returning the borrowed Mesh reference.
- `StreamingWorldLoader` still owns ResourceLoader I/O and raw M2 consumers,
material refresh/rebuild, cache/missing adoption, permits and MultiMesh builds.
- Cache formats, traversal rules, profiles and visible output are unchanged.
Synthetic traversal timing is not asset-backed leak or p95/p99 evidence.
## 2026-07-17 M2 Runtime Mesh Finalizer
- `M2RuntimeMeshFinalizer` now owns material refresh version `2`, stale-Mesh
rebuild classification, M2Builder rebuild and original-Mesh fallback.
- Current Meshes still skip raw `.m2` loading. `M2RawModelRepository` now owns
FileAccess/ClassDB M2Loader I/O and supplies raw data through the loader only
when the finalizer reports a stale Mesh; both historical clear sites persist.
- Billboard/UV-rotation predicates, rebuild extraction, metadata key and failure
fallback are unchanged. Cache adoption decisions, permits and MultiMesh
materialization remain loader-owned; negative outcomes belong to prototype state.
- Synthetic triangle rebuild/fallback timing is not asset-backed material,
descriptor-pressure/leak or p95/p99 evidence.
## 2026-07-17 M2 Raw Model Repository
- `M2RawModelRepository` now owns the repeated extracted-file and optional
native `M2Loader` boundary for static `load_m2` and animated
`load_m2_animated` raw Dictionaries.
- `StreamingWorldLoader` delegates stale-Mesh refresh and static reads directly;
`M2NativeAnimationResourceObserver` delegates native animated reads. The loader
retains normalization, `.tscn/.glb` fallback order, permits and Node/Mesh use;
prototype/negative state is isolated in `M2PrototypeCacheState`.
- The repository retains no path, native object or parsed data. Empty paths,
absent files/classes/methods and invalid results produce the same empty-value
fallback contract; path join/globalization and native method names are exact.
- No cache format, parser, material, geometry, animation or visible rule changed.
Synthetic missing-input timing is not asset-backed I/O/p95/p99 or fidelity evidence.
## 2026-07-17 M2 Prototype Cache State
- `M2PrototypeCacheState` now owns the exact static and animated prototype Node
references plus missing-model and static-animation negative path outcomes.
- Sync static, native animated, threaded GLB animation, Mesh finalize and
material-prototype paths delegate lookup/adoption/negative transitions to the
service. First valid prototype remains canonical for each normalized path.
- Map refresh still retains all four states. Final shutdown still occurs after
asynchronous drain and now calls one idempotent release operation that frees
detached prototypes, queues defensive in-tree Nodes and clears path outcomes.
- `StreamingWorldLoader` retains normalization, ResourceLoader/native I/O,
builders, animation policy, fallback order, permits and materialization.
- No cache format, visible rule or profile changed. Synthetic ownership/timing
evidence is not an asset-backed traversal/leak/p95/p99 or fidelity claim.
## 2026-07-17 WMO Placement Resolver Extraction
- `WmoPlacementResolver` now owns lowercase/slash cache-key normalization,
positive MODF `uid:<decimal>` identity with the legacy tile/index fallback and
world `Transform3D` composition.
- Lightweight render roots, cached scenes and live prototypes now share the same
position/Euler/unclamped-scale formula.
- The loader retains registry refs, jobs/queues, cache/load state, resource
fallback, cancellation and all Node/Mesh/MultiMesh/material/RID ownership.
- Cache formats, placement values, profiles and visible rules are unchanged.
Asset-backed placement/p95/p99 and general WMO parity remain pending.
## 2026-07-17 WMO Render Resource Cache State Extraction
- `WmoRenderResourceCacheState` now owns validated lightweight-WMO render
Resources, negative entries and normalized-path to pending-cache-path records.
- `StreamingWorldLoader` still constructs cache paths, calls `ResourceLoader`,
polls requests and validates `WMOStreamingResource` script identity plus
`FORMAT_VERSION` before completing cache state.
- Map reset and orderly request draining clear pending/negative state while
retaining accepted Resources; final runtime cache release clears all state.
- Missing render-cache files still are not negatively cached, preserving retry
and cached-scene/live fallback behavior. Formats, profiles and visuals are unchanged.
- Asset-backed corrupt-cache, traversal/leak p95/p99 and paired fidelity evidence
remain pending.
## 2026-07-17 WMO Scene Resource Cache State Extraction
- `WmoSceneResourceCacheState` now owns validated cached-WMO PackedScenes,
negative entries and normalized-path to pending-`.tscn` records.
- `StreamingWorldLoader` still checks file existence and
`wmo_max_runtime_scene_mb`, calls `ResourceLoader`, instantiates a validation
probe, checks WMOBuilder cache metadata and frees the probe before adoption.
- Missing files, oversize scenes, request errors, load failures and stale scenes
retain their prior negative-cache and live-prototype fallback behavior.
- Map reset clears pending/negative state while retaining accepted scenes; final
shutdown releases all scene state. Formats, profiles and visuals are unchanged.
- Asset-backed oversize/stale fixtures, traversal/leak p95/p99 and paired fidelity
evidence remain pending.
## 2026-07-17 ADT Water Load Pipeline State Extraction
- `AdtWaterLoadPipelineState` now owns ADT water pending FIFO/deduplication,
active WorkerThreadPool task IDs and the mutex-protected parsed-result mailbox.
- `StreamingWorldLoader` still selects concurrency, starts/waits tasks, invokes
`ADTLoader`, consumes `WATER_FINALIZE` permits, rejects stale tile/path/root
results and owns `water_loaded` timing. Main-thread Water subtree construction
and attachment are delegated separately as described below.
- Tile release still cancels only pending water requests; active work completes
and its result is discarded when tile ownership is stale. Shutdown waits task
IDs before clearing state; reset does not interrupt workers.
- Pending-plus-active `water` metrics, parsing, formats, materials, profiles and
visible behavior are unchanged.
- Asset-backed MH2O/MCLQ fidelity, long-traversal leak/p95/p99 and paired capture
evidence remain pending.
## 2026-07-17 ADT Water Scene Finalizer Extraction
- `AdtWaterSceneFinalizer` now owns the two main-thread operations that turn
validated parsed ADT data into a Water subtree: the existing
`ADTBuilder.build_tile_water_scene` call and attachment to the tile root.
- Both raw tile finalization and delayed cached-tile water results use the same
service. Optional persisted Editor ownership is assigned recursively only
when the loader supplies its existing edited-scene owner.
- The service is stateless and retains no Node, Mesh, material or RID. The tile
SceneTree subtree remains the lifetime owner; dry data still causes ADTBuilder
to free its temporary empty Water root.
- Parsing, task/result state, concurrency, stale validation, `WATER_FINALIZE`
permits, `enable_water` and `water_loaded` timing remain loader-owned.
- Geometry, materials, coordinates, cache formats, profiles and visible output
are unchanged. Synthetic geometry/ownership evidence is not an MH2O/MCLQ or
original-client parity claim; asset-backed traversal/p95/p99 remains pending.
## 2026-07-16 World Environment Snapshot Facade
- `WorldEnvironmentSnapshot` carries one immutable finite time-of-day value,
normalized to `[0, 24)` hours; invalid non-finite input is explicit.
- `WorldRenderFacade.apply_environment_snapshot()` delegates the snapshot to the
scene-owned `WowSkyController`. The facade owns no `Environment`, sun, skybox,
shader global or DBC sampling state.
- Checkpoint capture now submits its manifest time through the facade instead of
locating `WowSkyController` and mutating `use_system_time`, `time_speed` and
`fixed_time_hours` directly.
- Applying a snapshot performs the same three fixed-clock assignments as the old
capture path. Light/Area/Skybox DBC lookup, interpolation, fog, sun and shader
behavior remain unchanged; weather and indoor environment state are follow-up work.
## 2026-07-16 World Entity Presentation Facade
- `EntityPresentationSnapshot` contract version 1 combines session `EntityId`,
typed Godot world position, visual PackedScene path, finite yaw/scale and visibility.
- `WorldRenderFacade.present_entity()` and `remove_entity()` delegate to an explicit
scene-owned `WorldEntityPresenter`; both runtime scenes compose that service.
- The presenter owns only visual subtrees. Same-path snapshots update transform/
visibility, changed paths replace after successful instantiation, and removal
detaches plus queues the owned root for deletion.
- The local player, authoritative state, GUID mapping and asset/display selection
remain outside this service. Runtime emits no entity commands yet, so baseline
output is unchanged.
- PackedScene loading is currently synchronous on create/replace and is a documented
prototype limitation, not an accepted network-scale frame-critical path.
## 2026-07-16 Terrain Quality Mesh Cache Extraction
- `TerrainQualityMeshCache` now owns full-quality terrain revisit Mesh references,
tile-key LRU order and source admission outside `StreamingWorldLoader`.
- The historical rules are unchanged: successful restore touches newest; store
trims oldest to `terrain_quality_mesh_cache_limit`; zero/negative capacity keeps
no entries; control-splat/splat sources are never retained in this revisit cache.
- The loader keeps three narrow adapters: restore into tile state, store after a
full-quality result and clear during existing world reset/shutdown.
- ADT parsing, quality tasks/results, tile state, cache format versions, material/
Node/RID finalization, budgets and visible terrain behavior remain loader-owned.
## Practical Rule For Future Work
If something improves quality but creates visible hitch, it is not done. Move it to bake/cache/background work, split finalization over frames, or prewarm it before the player can see it.
@@ -1,6 +1,7 @@
# M00-QAR-ANIMATION-SKY-EVIDENCE-001 — Animation and sky evidence
<!-- OPENWC_CLAIM:M00-QAR-ANIMATION-SKY-EVIDENCE-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-ANIMATION-SKY-EVIDENCE-001:d1df794 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-CAMERA-FRAMING-REFINEMENT-001 — Goldshire pitch/FOV refinement
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-FRAMING-REFINEMENT-001:3172f41 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-CAMERA-OCCLUDERS-001 — Camera occluder diagnostic
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-OCCLUDERS-001:sindo-main-codex:2026-07-13 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-OCCLUDERS-001:9d34a47 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-CAMERA-POSE-EVIDENCE-001 — Goldshire camera pose evidence
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-POSE-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-EVIDENCE-001:eca76c5 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-CAMERA-POSE-SWEEP-001 — Empirical camera pose sweep
<!-- OPENWC_CLAIM:M00-QAR-CAMERA-POSE-SWEEP-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAMERA-POSE-SWEEP-001:657a1d8 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-CAPTURE-SHUTDOWN-001 — Capture shutdown drain
<!-- OPENWC_CLAIM:M00-QAR-CAPTURE-SHUTDOWN-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-CAPTURE-SHUTDOWN-001:8d4641a -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-COORD-CALIBRATION-001 — Renderer coordinate calibration
<!-- OPENWC_CLAIM:M00-QAR-COORD-CALIBRATION-001:sindo-main-codex:2026-07-13 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-COORD-CALIBRATION-001:dfc1031 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-FINAL-PAIRED-EVIDENCE-001 — Final paired evidence audit
<!-- OPENWC_CLAIM:M00-QAR-FINAL-PAIRED-EVIDENCE-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FINAL-PAIRED-EVIDENCE-001:239cb24 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-FOV-SWEEP-001 — Empirical camera FOV sweep
<!-- OPENWC_CLAIM:M00-QAR-FOV-SWEEP-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-FOV-SWEEP-001:f548507 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-GUI-RID-SHUTDOWN-001 — GUI capture RID shutdown
<!-- OPENWC_CLAIM:M00-QAR-GUI-RID-SHUTDOWN-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-GUI-RID-SHUTDOWN-001:f19c0c4 -->
## Ownership
@@ -0,0 +1,79 @@
# M00-QAR-INTEGRATOR-CLOSEOUT-001 — M00 milestone closeout
<!-- OPENWC_CLAIM:M00-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_HANDOFF:READY:M00-QAR-INTEGRATOR-CLOSEOUT-001:ba81098 -->
## Ownership
- Target: M00
- Program: QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m00-integrator-closeout`
- Lease expires UTC: 2026-07-15
- Integrator: sindo-main-codex, assigned by the user on 2026-07-13
## Outcome
Audit merged M00 work packages, refresh final Evidence, close the milestone and activate M01.
## Non-goals
- Change renderer behavior or compatibility claims.
- Start M01 implementation.
- Commit proprietary references or generated capture output.
## Paths
- Exclusive: M00/M01 target status and final M00 Evidence
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, completed M00 claims
- Generated/ignored: `user://` post-merge reports
## Contracts and data
- Public API/events: unchanged
- Schema/format version: unchanged
- Migration/compatibility: none
- Consumers: milestone integrator and M01 work packages
## Dependencies
- Requires: merged M00 PRs #1 through #15 and final paired evidence
- Blocks: M01 activation
- External state: private reference images remain outside Git; their hashes and metrics are already recorded
## Verification
- Commands: post-merge render baseline dry-run, coordination gate, documentation gate
- Fixtures: seven-checkpoint non-proprietary manifest and recorded private-reference hashes/metrics
- Fidelity evidence: closeout preserves the explicit non-parity classification in `docs/M00_FINAL_PAIRED_EVIDENCE.md`
- Performance budget: no runtime change
## Documentation deliverables
- Inline public API docs: unchanged
- Module specification: unchanged
- Data-flow diagram: unchanged
- Sequence/state/dependency diagrams: unchanged
- Source map/status updates: target index, roadmap current state and M00 Evidence
## Simplicity and naming
- Important names introduced: none
- Simplest considered solution: status and Evidence-only integrator patch
- Rejected complexity/abstractions: new closeout tooling for a one-time milestone transition
- Unavoidable complexity and justification: none
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: verified all M00 PRs and merge commits, refreshed final Evidence, closed M00, activated M01 and passed transition gates
- Next: review and merge the closeout PR before starting an M01 implementation claim
- Blocked by:
## Handoff
- Commit: `ba81098`
- Results: exactly one ACTIVE target (M01); M00 DONE marker accepted; post-merge baseline, comparator self-test, coordination and documentation gates passed
- Remaining risks: documented renderer fidelity gaps and D3D12 fallback continue beyond M00
- Documentation updated: target status/index, development-roadmap current state and final M00 Evidence
@@ -1,6 +1,7 @@
# M00-QAR-LANDMARK-REGION-DIFF-001 — Landmark-region visual diff
<!-- OPENWC_CLAIM:M00-QAR-LANDMARK-REGION-DIFF-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-LANDMARK-REGION-DIFF-001:3d90c6d -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-TERRAIN-HEIGHT-001 — Terrain height diagnostic
<!-- OPENWC_CLAIM:M00-QAR-TERRAIN-HEIGHT-001:sindo-main-codex:2026-07-13 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TERRAIN-HEIGHT-001:f8538ba -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-TILE-OWNERSHIP-001 — Waterfall terrain tile ownership
<!-- OPENWC_CLAIM:M00-QAR-TILE-OWNERSHIP-001:sindo-main-codex:2026-07-13 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-TILE-OWNERSHIP-001:2306200 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-VISUAL-DIFF-001 — Renderer checkpoint visual diff
<!-- OPENWC_CLAIM:M00-QAR-VISUAL-DIFF-001:sindo-main-codex:2026-07-13 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-VISUAL-DIFF-001:19df5b7 -->
## Ownership
@@ -1,6 +1,7 @@
# M00-QAR-WMO-READINESS-001 — Goldshire WMO readiness
<!-- OPENWC_CLAIM:M00-QAR-WMO-READINESS-001:sindo-main-codex:2026-07-14 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M00-QAR-WMO-READINESS-001:5092995 -->
## Ownership
@@ -0,0 +1,131 @@
# M01-FND-COORD-BOUNDARY-GATE-001 — Coordinate conversion boundary gate
<!-- OPENWC_CLAIM:M01-FND-COORD-BOUNDARY-GATE-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORD-BOUNDARY-GATE-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: FND/QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-coordinate-boundary-gate`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Route remaining GDScript world/tile axis conversions through `CoordinateMapper`
and add a repository-wide gate that rejects new manual world-center, axis-swap
or world-to-ADT tile formulas outside explicit contract/native-oracle boundaries.
## Non-goals
- Rewriting model-local M2/WMO vertex or placement orientation transforms.
- Moving renderer distance/LOD geometry out of Godot space.
- Replacing the native parser boundary with GDScript calls from worker threads.
- Changing coordinate contract semantics, renderer radii or cache formats.
## Paths
- Exclusive: `src/tools/verify_coordinate_conversion_boundaries.gd`
- Shared/hotspots: `src/domain/coordinates/coordinate_mapper.gd`, coordinate
tests/docs/ADR, streamer/player/sky/terrain-probe consumers,
`docs/CODING_STANDARD.md`, this claim
- Generated/ignored: Godot import metadata, extracted assets and renderer caches
## Contracts and data
- Additive mapper methods: Godot position to ADT tile/local and ADT tile/local
to Godot position
- Native exception: one formula definition in `src/native/src/wow_chunk_reader.h`;
ADT/WDT loaders may call it but may not define copies
- Oracle exception: `verify_render_coordinate_calibration.gd` retains one
independent M00 formula to detect common-mode mapper regressions
- Model-local transforms are outside the world-coordinate gate
## Dependencies
- Requires: coordinate contract v2 and M01 seams through `7c64e8d`
- Blocks: M01 coordinate-centralization exit criterion and milestone evidence audit
- External state: no dependency or proprietary input
## Verification
- Commands: conversion-boundary gate, mapper/golden fixtures, streaming focus,
identities, renderer calibration/manifest and repository gates
- Fixtures: Godot/ADT tile center, local offsets and exact boundaries
- Fidelity evidence: existing build-12340 points and raw ADT/server fixture remain unchanged
- Performance budget: mapper allocations occur only at adapter/update boundaries;
renderer distance/LOD math remains in Godot scalars
## Documentation deliverables
- Inline public API docs: new mapper methods
- Module specification/ADR: boundary APIs, allowed exceptions and gate behavior
- Coding standard: mandatory review rule for cross-space conversions
- Data-flow diagram: existing coordinate module diagram updated for direct adapters
- State/sequence diagrams: unchanged; pure synchronous mapping
## Simplicity and naming
- Important names introduced: `godot_to_adt_tile`, `godot_to_adt_tile_local`,
`adt_tile_local_to_godot`, `verify_coordinate_conversion_boundaries`
- Simplest considered solution: extend the existing mapper and scan source text
for narrow high-signal formula signatures
- Rejected complexity/abstractions: generic coordinate algebra framework,
native-to-GDScript calls on parser workers and a broad numeric-literal ban
- Unavoidable complexity and justification: one native boundary and one
independent test oracle must remain explicit to avoid worker coupling and common-mode tests
- Measured optimization evidence: existing renderer distance/LOD scalar math is unchanged
## Status
- State: ready-for-review
- Done: mapper extensions, consumer migration, repository gate, review rule,
integrated renderer gate, regression fixtures and documentation
- Next: integrator review, merge and M01 completion-evidence audit
- Blocked by:
## Handoff
- Commit: `47dec6c`
- Branch: `work/sindo-main-codex/m01-coordinate-boundary-gate`
- Outcome: all audited GDScript world/tile conversions in sky, streaming,
player spawn/ground sampling and terrain probes route through typed
`CoordinateMapper` boundaries. A repository scan rejects new manual
world-center, legacy axis-conversion, world-to-tile, tile-local and
tile-center formulas.
- Contract/schema: additive `godot_to_adt_tile`,
`godot_to_adt_tile_local` and `adt_tile_local_to_godot` APIs; no persisted
format, cache version or renderer radius changes.
- Verification: CoordinateConversionBoundaries PASS (`83` files, one native
boundary, one independent oracle, four consumers, six classifier guards);
CoordinateMapper PASS (`5` golden points, `8` tile boundaries, `2` raw MCNK,
`5` yaw cases); renderer calibration PASS (`5` points, maximum error
`0.000015`); renderer manifest PASS (`7/7/7`); unified render baseline dry-run,
headless project load, runtime cache shutdown, coordination, documentation
and whitespace gates passed.
- GUI regression: final ADT-boundary capture compared `2/2`, missing `0`,
failed `0` (cold mean `0.0002324`, changed ratio `0.0013741`; warm mean
`0.0000226`, changed ratio `0.0001285`). Earlier full capture produced all
`14` images; `11/14` met the strict changed-pixel threshold. The three
threshold-only differences retained low mean error (`<= 0.00622`) and were
visually classified as cold M2 readiness and liquid animation phase, with
unchanged camera, geometry alignment, area and streaming focus.
- Fidelity: existing build-12340 calibrated points, raw ADT fixture and server
spawn fixture remain unchanged. Native parser workers retain exactly one
formula in `wow_chunk_reader.h`; the calibration verifier retains exactly one
independent formula so mapper and oracle cannot share a common-mode defect.
- Rebuild: none. Local ignored extracted assets/native binary/cache junctions
were used for runtime checks and are not part of the commit.
- Remaining risks: source scanning intentionally recognizes narrow high-signal
formula signatures rather than parsing arbitrary algebra; native formula
parity still relies on calibration/raw fixtures. Model-local M2/WMO basis
transforms and Godot-space LOD distance math are intentionally outside scope.
- Recommended merge order: after `7c64e8d`; this package closes the remaining
M01 manual axis-conversion checklist item, but only the milestone integrator
may update target Evidence/status or set `OPENWC_TARGET_DONE`.
- Documentation: coding review rule, coordinate-mapping module API/input-output/
data-flow/capability/risk/source-map sections, ADR 0001 and `RENDER.md` were
updated. Existing pure synchronous data-flow diagram remains authoritative;
no state/sequence diagram changed.
@@ -0,0 +1,104 @@
# M01-FND-COORD-TILE-AXIS-002 — ADT filename axis correction
<!-- OPENWC_CLAIM:M01-FND-COORD-TILE-AXIS-002:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORD-TILE-AXIS-002:c8e99f2 -->
## Ownership
- Target: M01
- Program: FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-coordinate-tile-axis-fix`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Correct the additive coordinate contract so `AdtTileCoordinate(tile_x, tile_y)`
matches actual `Map_tileX_tileY.adt` filenames and MCNK `indexX/indexY` axes.
## Non-goals
- Migrating renderer/native consumers.
- Changing canonical, server, Godot or ADT placement position arithmetic.
- Adding server-spawn fixtures or `StreamingFocus`.
- Committing proprietary ADT bytes.
## Paths
- Exclusive: `src/domain/coordinates/coordinate_mapper.gd`,
`src/domain/coordinates/adt_chunk_coordinate.gd`,
`src/tools/verify_coordinate_mapper.gd`
- Shared/hotspots: `docs/adr/0001-canonical-world-coordinates.md`,
`docs/modules/coordinate-mapping.md`, this claim
- Generated/ignored: local extracted ADT and Godot import metadata
## Contracts and data
- Public API/events: method names unchanged; corrected tile/chunk axis semantics
- Schema/format version: coordinate contract version increments from 1 to 2
- Migration/compatibility: no released consumer exists; contract was merged but
not yet consumed, so correction precedes consumer rollout
- Consumers: future renderer, network, gameplay and server-data adapters
## Dependencies
- Requires: merged `M01-FND-COORDS-001` at `1c0dfc7`
- Blocks: coordinate golden fixtures and all coordinate consumers
- External state: private `Azeroth_31_49.adt` used read-only for evidence
## Verification
- Commands: coordinate mapper probe, M00 calibration, manifest, documentation
and coordination gates
- Fixtures: build 12340 Elwynn point must resolve to `Azeroth_31_49.adt`;
MCNK `(0,0)` and `(15,15)` establish chunk-axis direction
- Fidelity evidence: local ADT SHA-256
`18185B5FAC9566D9C57A9C19ABD75F108370D1795639B6077167C0EFD3672FAE`;
OpenRealm independently indexes filename X from world Y and filename Y from world X
- Performance budget: unchanged constant-time scalar arithmetic
## Documentation deliverables
- Inline public API docs: corrected tile/chunk direction comments
- Module specification: contract version, formulas, capability evidence and risk
- Data-flow diagram: corrected filename/chunk axes
- ADR: corrected decision and evidence
- Sequence/state diagrams: not applicable; pure synchronous mapper
## Simplicity and naming
- Important names introduced: none
- Simplest considered solution: swap the two filename formulas and corresponding
local/chunk ownership calculations
- Rejected complexity/abstractions: compatibility flag for a contract with no consumers
- Unavoidable complexity and justification: ADT filename axes differ from client X/Y order
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: raw-corpus diagnosis, filename/local/chunk axis correction, contract
version 2 documentation and regression fixtures
- Next: integrator review and merge before coordinate consumer work
- Blocked by:
## Handoff
- Commit: `a7072e3`
- Branch: `work/sindo-main-codex/m01-coordinate-tile-axis-fix`
- Outcome: `tile_x/chunk_x` now grow east from canonical Y and
`tile_y/chunk_y` grow south from canonical X; public method names unchanged
- Contract/schema: coordinate contract version 2; no persisted format, cache or migration
- Verification: coordinate mapper PASS (5 build-12340 points, 8 boundaries,
2 recorded raw MCNK origins, 5 yaw cases); M00 calibration PASS with maximum
error `0.000015`; renderer manifest, coordination, documentation, dependency
and whitespace checks passed
- Fidelity: private `Azeroth_31_49.adt` hash and raw MCNK origins recorded in
ADR 0001; OpenRealm independently uses the same filename-axis ordering
- Rebuild: none; private/generated prerequisites remain outside Git
- Remaining risks: server spawn fixture, native placement conversion and
MDDF/MODF Euler semantics remain subsequent M01 packages
- Recommended merge order: merge this correction before golden fixture or consumer migration
- Documentation: ADR 0001 and coordinate-mapping module data flow, contract
version, capability evidence and known risks updated
+111
View File
@@ -0,0 +1,111 @@
# M01-FND-COORDS-001 — Canonical coordinate contracts
<!-- OPENWC_CLAIM:M01-FND-COORDS-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-COORDS-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-coordinate-mapper`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Introduce scene-free value objects for canonical WoW, server, ADT placement,
ADT tile/chunk/local and Godot world coordinate spaces, plus the single pure
`CoordinateMapper` contract for position and world-yaw conversions.
## Non-goals
- Migrating `StreamingWorldLoader`, native parsers, sky, player or renderer code.
- Defining model-specific MDDF/MODF Euler rotation corrections.
- Introducing `StreamingFocus`, typed IDs or a test framework dependency.
- Claiming visual parity from arithmetic round trips.
## Paths
- Exclusive: `src/domain/coordinates/**`, `src/tools/verify_coordinate_mapper.gd`,
`docs/modules/coordinate-mapping.md`, `docs/adr/0001-canonical-world-coordinates.md`
- Shared/hotspots: `docs/modules/README.md`, this claim
- Generated/ignored: Godot import metadata and local test output
## Contracts and data
- Public API/events: immutable coordinate value objects and pure mapper methods
- Schema/format version: no persisted format; contract version 1 in module docs
- Migration/compatibility: additive contract; existing renderer conversions remain
unchanged until a later consumer migration package
- Consumers: future renderer, network, gameplay and server-data adapters
- Rollout order: contract and fixtures first, then native/parser and renderer consumers
## Dependencies
- Requires: merged M00 build 12340 coordinate calibration evidence
- Blocks: M01 renderer coordinate migration and `StreamingFocus`
- External state: no proprietary inputs; reference formulas are source-only evidence
## Verification
- Commands: headless coordinate contract probe, headless project load,
coordination and documentation gates
- Fixtures: five build 12340 camera points, ADT map center/edge/boundary points,
server/ADT/Godot round trips and yaw normalization cases
- Fidelity evidence: M00 observed build 12340 positions plus independent
OpenRealm/WoWee and TrinityCore grid formulas
- Performance budget: constant-time scalar arithmetic with no allocation beyond result values
## Documentation deliverables
- Inline public API docs: every public value object and mapper method
- Module specification: purpose, public API, inputs/outputs, ownership, errors and source map
- Data-flow diagram: server/ADT/canonical/Godot mappings
- Dependency diagram: canonical contract consumers and forbidden scene dependency
- Sequence/state diagrams: not applicable; mapper is synchronous and stateless
- ADR: coordinate axes, units, tile indexing and compatibility rollout
## Simplicity and naming
- Important names introduced: `CanonicalWowWorldPosition`, `ServerWorldPosition`,
`AdtPlacementPosition`, `AdtTileCoordinate`, `AdtChunkCoordinate`,
`AdtTileLocalPosition`, `GodotWorldPosition`, `WorldYaw`, `CoordinateMapper`
- Simplest considered solution: scalar immutable value objects and one pure mapper
- Rejected complexity/abstractions: generic vector-space framework, matrices,
dependency injection and a third-party test framework
- Unavoidable complexity and justification: distinct source spaces prevent silent axis swaps
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: repository/reference audit, ADR, immutable value objects, pure mapper,
build 12340/boundary/yaw fixtures, module specification and repository gates
- Next: integrator review and merge before renderer/native consumer migration
- Blocked by:
## Handoff
- Commit: `f45695c`
- Branch: `work/sindo-main-codex/m01-coordinate-mapper`
- Outcome: additive contract for canonical/server/ADT/Godot positions, ADT
tile/chunk/local ownership and world-facing yaw; no existing consumer changed
- Public contracts: contract version 1 documented in
`docs/modules/coordinate-mapping.md`; no persisted schema or cache change
- Verification: coordinate mapper PASS (5 golden points, 8 tile boundaries,
5 yaw cases); M00 coordinate calibration PASS (5 points, maximum error
`0.000015`); renderer manifest, coordination, documentation, dependency and
whitespace checks passed; headless project load completed with expected
missing proprietary-data diagnostics in the isolated worktree
- Fidelity: observed build 12340 positions match within `0.002` yard; formulas
cross-checked against TrinityCore, OpenRealm and WoWee; visual parity is not claimed
- Migration/rebuild: none for this additive package; consumers migrate only
after merge and must preserve renderer checkpoints
- Remaining risks: native loaders and renderer still use manual conversions;
MDDF/MODF Euler mapping is intentionally unresolved; server-spawn integration
and finite-value adapter validation remain later M01 packages
- Recommended merge order: this contract first, coordinate fixtures/server-spawn
audit second, renderer/native consumers third, manual-conversion search gate last
- Documentation: ADR 0001; coordinate-mapping module spec with data-flow and
dependency diagrams; inline API documentation on all public symbols
@@ -0,0 +1,112 @@
# M01-FND-DOMAIN-IDENTITIES-001 — Stable domain identity namespaces
<!-- OPENWC_CLAIM:M01-FND-DOMAIN-IDENTITIES-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-FND-DOMAIN-IDENTITIES-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-domain-identities`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Add scene-free, immutable identity values for stable authored content,
session-local entities, opaque WoW wire GUIDs and namespaced server entries so
callers cannot silently interchange their numeric/string representations.
## Non-goals
- Allocating Content IDs, Entity IDs or server entries.
- Decoding WoW GUID high types before the build-12340 protocol package.
- Defining map/spell/item/quest/display/sound IDs in this package.
- Migrating gameplay, network or Content Project consumers that do not exist yet.
## Paths
- Exclusive: `src/domain/identity/`, `src/tools/verify_domain_identities.gd`,
`docs/modules/domain-identities.md`, `docs/adr/0002-domain-identity-namespaces.md`
- Shared/hotspots: `docs/ARCHITECTURE.md`, this claim
- Generated/ignored: Godot import metadata
## Contracts and data
- `ContentId`: canonical lowercase UUID text persisted with schema-owned content
- `EntityId`: positive session-local integer, never a persisted content/server identity
- `WowGuid`: opaque high/low unsigned 32-bit words preserving wire bits
- `ServerEntryId`: positive integer plus non-empty adapter/table namespace
- Migration: additive types only; no existing persisted data changes
## Dependencies
- Requires: M01 coordinate/focus seams through `77cd17a`
- Blocks: Content Project schemas, server adapters, network entity mapping and gameplay registry
- External state: no dependency or proprietary input
## Verification
- Commands: identity contract/boundary verifier, dependency search,
coordination/documentation gates and headless project load
- Fixtures: valid/invalid UUIDs, session IDs, full unsigned GUID words and
duplicate numeric server entries in different namespaces
- Fidelity evidence: GUID words remain opaque and lossless until build-12340
packet fixtures establish semantic decomposition
- Performance budget: constant-time scalar/string validation outside frame loops
## Documentation deliverables
- Inline public API docs: every value and validation/equality method
- Module specification: persistence, ownership, namespace mapping and recovery
- Data-flow diagram: authored/runtime/wire/server identities into adapters
- State/sequence diagrams: lifecycle diagram for content/session/wire/server identity
- ADR: namespace separation and UUID persistence decision
## Simplicity and naming
- Important names introduced: `ContentId`, `EntityId`, `WowGuid`, `ServerEntryId`
- Simplest considered solution: four independent values with explicit fields
- Rejected complexity/abstractions: generic ID base class, registry service,
UUID generator and speculative GUID high-type enum
- Unavoidable complexity and justification: server entry namespace and split GUID
words prevent collisions and signed 64-bit loss
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: four identity values, boundary verifier, ADR, module specification,
architecture mapping and repository gates
- Next: integrator review and merge before the manual axis-conversion gate
- Blocked by:
## Handoff
- Commit: `c1dc07c`
- Branch: `work/sindo-main-codex/m01-domain-identities`
- Outcome: authored content, runtime entities, WoW wire GUIDs and namespaced
server entries now have separate immutable scene-free value objects
- Contract/schema: identity contract version 1; `ContentId` uses canonical
lowercase UUID text. No existing schema/cache is migrated; containing future
schemas still require their own version and duplicate/reference policy
- Verification: DomainIdentities PASS (`5` ContentId, `5` EntityId, `6` WowGuid,
`5` ServerEntryId cases); StreamingFocus, coordinate golden fixtures and
CoordinateMapper PASS; M00 calibration PASS with maximum error `0.000015`;
renderer manifest PASS (`7` checkpoints); headless project load exit `0`;
coordination, documentation, naming/dependency and whitespace checks passed
- Fidelity: `WowGuid` preserves two complete unsigned 32-bit wire words and
explicitly does not claim build-12340 high-type semantics without packet
fixtures; zero GUID remains an uninterpreted observable sentinel
- Rebuild: none; no dependency or proprietary input. Clean-worktree project
load reports expected missing ignored extracted assets/character resources
- Remaining risks: no ID allocator, mapping registry or server namespace
vocabulary exists yet; UUID shape does not enforce generator version/variant;
GDScript callers must use domain `equals` or canonical keys rather than
reference `==`
- Recommended merge order: after StreamingFocus at `77cd17a`, before the M01
manual axis-conversion rule/search gate
- Documentation: ADR 0002, domain-identities module API/input-output/data-flow/
lifecycle/persistence/recovery/capability/source-map sections, module registry
and architecture Domain mapping added or updated
@@ -0,0 +1,109 @@
# M01-QAR-COORD-FIXTURES-001 — Coordinate golden fixtures
<!-- OPENWC_CLAIM:M01-QAR-COORD-FIXTURES-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-QAR-COORD-FIXTURES-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-coordinate-fixtures`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Add a versioned, non-proprietary fixture and headless validator joining one
pinned AzerothCore player spawn, raw ADT/MCNK metadata and the five M00
original-client coordinate observations through coordinate contract version 2.
## Non-goals
- Committing ADT files, SQL dumps or original-client screenshots.
- Claiming TrinityCore spawn equivalence without a pinned populated TDB source.
- Migrating renderer/native consumers or rendering a live server spawn.
- Changing coordinate formulas, IDs or `StreamingFocus`.
## Paths
- Exclusive: `src/tests/fixtures/coordinate_golden_points.json`,
`src/tools/verify_coordinate_golden_fixtures.gd`
- Shared/hotspots: `docs/modules/coordinate-mapping.md`, this claim
- Generated/ignored: extracted ADT data, server database downloads and Godot metadata
## Contracts and data
- Public API/events: no new production API; fixture schema version 1
- Coordinate contract: version 2, unchanged
- Migration/compatibility: additive test data; fixture consumers reject unknown schema versions
- Consumers: M01 coordinate mapper, future native/renderer/server adapter migrations
## Dependencies
- Requires: merged coordinate contract version 2 at `b55bfca`
- Blocks: coordinate consumer migration and golden server-spawn renderer integration
- External state: source evidence reduced to pinned revision, numeric rows and SHA-256 hashes
## Verification
- Commands: fixture validator, coordinate mapper probe, M00 calibration,
manifest, coordination and documentation gates
- Fixtures: AzerothCore Human Warrior start; raw `Azeroth_32_48.adt` MCNK
`(3,12)`; five build 12340 camera points
- Fidelity evidence: exact source provenance and mapper outputs with bounded tolerances
- Performance budget: negligible headless JSON and scalar arithmetic
## Documentation deliverables
- Inline public API docs: fixture-validator header and field validation functions
- Module specification: fixture schema, provenance, verification and source map
- Data-flow diagram: server/raw/client fixtures into mapper assertions
- State/sequence/dependency diagrams: unchanged; synchronous stateless validation
## Simplicity and naming
- Important names introduced: `coordinate_golden_points`,
`verify_coordinate_golden_fixtures`
- Simplest considered solution: one JSON fixture and one existing-style headless script
- Rejected complexity/abstractions: test framework dependency, SQL parser, ADT bytes in Git
- Unavoidable complexity and justification: three independent source categories need explicit provenance
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: pinned source research, schema 1 fixture, headless validator, module
documentation and all package gates
- Next: integrator review and merge before coordinate consumer migration
- Blocked by:
## Handoff
- Commit: `ce4f7ec`
- Branch: `work/sindo-main-codex/m01-coordinate-fixtures`
- Outcome: one versioned fixture now joins a pinned AzerothCore Human Warrior
spawn, private raw MCNK metadata and five original-client build 12340 points
through coordinate contract version 2
- Contract/schema: additive test fixture schema 1; production coordinate API and
contract version 2 are unchanged; proprietary bytes remain outside Git
- Verification: golden fixture PASS (`1` server spawn, `1` raw ADT chunk, `5`
original-client points); CoordinateMapper PASS (`5` golden points, `8` tile
boundaries, `2` raw MCNK cases, `5` yaw cases); M00 calibration PASS with
maximum error `0.000015`; renderer manifest PASS (`7` checkpoints); coordination,
documentation, dependency and whitespace checks passed
- Fidelity: AzerothCore source pinned at revision
`68fcf0098b16388093989627f37be530fc91f07d`; private
`Azeroth_32_48.adt` SHA-256
`D66E27FD82B461AE38B78562952AED428E619777A3F3E35DD8FF97444644770A`;
server and raw data independently resolve tile `(32,48)` and chunk `(3,12)`
- Rebuild: no proprietary input is required to rerun the committed fixture;
local ignored renderer resources are still required by the inherited M00
manifest probe
- Remaining risks: TrinityCore equivalence needs a pinned populated TDB snapshot;
live server adapter, renderer integration and MDDF/MODF Euler semantics remain
subsequent M01 work
- Recommended merge order: after coordinate contract version 2 (`b55bfca`) and
before live coordinate consumer migration
- Documentation: coordinate-mapping module inputs/outputs, persistence, data-flow
diagram, verification, capability matrix, risks and source map updated
@@ -0,0 +1,101 @@
# M01-QAR-INTEGRATOR-CLOSEOUT-001 — M01 milestone closeout
<!-- OPENWC_CLAIM:M01-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-16 -->
<!-- OPENWC_HANDOFF:READY:M01-QAR-INTEGRATOR-CLOSEOUT-001:dd412e6 -->
## Ownership
- Target: M01
- Program: QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-integrator-closeout`
- Lease expires UTC: 2026-07-16
- Integrator: sindo-main-codex, continuing the user-directed milestone workflow on 2026-07-14
## Outcome
Audit every merged M01 work package on current `master`, record final evidence,
close M01 and activate M02 only if all target verification and documentation
criteria pass.
## Non-goals
- Change coordinate, identity, streaming or renderer behavior.
- Start M02 implementation.
- Add new compatibility or visual-parity claims.
- Commit proprietary assets, generated UIDs, caches or capture output.
## Paths
- Exclusive: M01/M02 target status, M01 final Evidence and M01 claim acceptance markers
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, this claim
- Generated/ignored: local Godot imports, renderer corpus and `user://` reports
## Contracts and data
- Public APIs, schemas, cache formats and migrations: unchanged
- Coordinate contract remains version 2
- Fixture schema remains version 1
- Consumers: milestone sequence and M02 work packages
## Dependencies
- Requires: all seven M01 READY commits integrated through master `c8e99f2`
- Blocks: M02 activation
- External state: server-spawn GUI hashes and original-client numeric evidence are recorded; proprietary files remain outside Git
## Verification
- Commands: all M01 domain/fixture/boundary/focus/identity/spawn gates,
unified renderer dry-run, coordination/documentation checks and diff hygiene
- Fidelity evidence: five build-12340 points, raw ADT metadata, pinned
AzerothCore spawn and cache-complete GUI marker capture
- Performance: no runtime change; preserve recorded renderer cold/warm evidence
## Documentation deliverables
- M01 final target Evidence and checklist
- M01 claim integration-acceptance markers
- Target index and roadmap transition to M02
- API/module specifications and diagrams are audited, not changed unless a gap is found
## Simplicity and naming
- Important names introduced: none beyond the closeout work-package ID
- Simplest approach: evidence/status-only integrator patch
- Rejected complexity: new one-off closeout framework or behavioral changes
- Unavoidable complexity: none
## Status
- State: ready-for-review
- Done: accepted seven M01 packages, reran post-merge gates, recorded complete
target Evidence, closed M01 and activated M02
- Next: merge closeout commit before starting an M02 implementation claim
- Blocked by:
## Handoff
- Commit: `dd412e6`
- Branch: `work/sindo-main-codex/m01-integrator-closeout`
- Outcome: M01 is `DONE` with all seven checklist items and complete Evidence;
M02 is the only `ACTIVE` target and `targets/README.md` points to it.
- Integration audit: all seven M01 READY commits are ancestors of master
`c8e99f2`; their claims now carry `OPENWC_INTEGRATION:ACCEPTED` markers.
- Verification: unified renderer dry-run passed project load, material/dedupe/
shutdown gates, manifest `7/7/7`, calibration maximum error `0.000015`,
coordinate boundary (`84` files) and server-spawn renderer `(32,48)/(3,12)`;
coordinate mapper, golden fixtures, StreamingFocus and domain identity gates
passed. Coordination and documentation gates passed; coordination reports
only five already-expired M00 claim warnings.
- Fidelity: closeout preserves five build-12340 coordinate observations, raw ADT
evidence, pinned AzerothCore spawn mapping and cache-complete Northshire GUI
evidence without claiming original-client visual parity or TrinityCore row
equivalence.
- Rebuild/migration: none. Coordinate contract remains version 2; fixture schema
remains version 1; renderer cache formats are unchanged.
- Remaining risks: recorded in M01 Evidence—TrinityCore populated spawn,
placement Euler parity, native parser boundary and broader renderer parity.
- Documentation: M01 target Evidence/checklist, target index and roadmap current
state updated. Existing coordinate, identity and renderer module specs/API/
diagrams were audited and passed the documentation gate.
@@ -0,0 +1,135 @@
# M01-QAR-SERVER-SPAWN-RENDERER-001 — Golden server spawn renderer evidence
<!-- OPENWC_CLAIM:M01-QAR-SERVER-SPAWN-RENDERER-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-QAR-SERVER-SPAWN-RENDERER-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: QAR/RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-server-spawn-renderer`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Route the pinned AzerothCore Human Warrior spawn fixture through
`CoordinateMapper` into the live Eastern Kingdoms renderer and produce
repeatable headless contract checks plus GUI screenshot evidence at the mapped
ADT tile/chunk.
## Non-goals
- Connecting to a live AzerothCore or TrinityCore database.
- Claiming TrinityCore spawn equivalence without a pinned populated TDB row.
- Changing coordinate formulas, streaming radii, terrain, player movement or
production character presentation.
- Adding proprietary assets or screenshots to Git.
## Paths
- Exclusive: `src/tools/server_spawn_render_manifest.json`,
`src/tools/verify_server_spawn_renderer.gd`
- Shared/hotspots: `src/tools/capture_render_checkpoints.gd`, renderer runner,
coordinate/world-renderer module documentation, `RENDER.md`, this claim
- Generated/ignored: Godot UIDs/import metadata and `user://` PNG/JSON evidence
## Contracts and data
- Fixture schema and coordinate contract version remain unchanged.
- Additive diagnostic checkpoint metadata may request a renderer-native marker;
existing M00 checkpoints remain byte-for-byte compatible.
- The spawn remains sourced from pinned AzerothCore revision
`68fcf0098b16388093989627f37be530fc91f07d`.
## Dependencies
- Requires: merged M01 packages through `a07a487`
- Blocks: M01 golden server-spawn renderer criterion and milestone closeout
- External state: GUI capture requires the existing local extracted/cached
renderer corpus; headless contract verification remains non-proprietary
## Verification
- Commands: dedicated spawn verifier, coordinate fixture/mapper/boundary gates,
renderer baseline dry-run, GUI checkpoint capture and human inspection
- Fixture: AzerothCore race `1`, class `1`, map `0`, server position
`(-8949.95, -132.493, 83.5312)`
- Expected renderer result: Godot position
`(17199.159666667, 83.5312, 26016.616666667)`, ADT `(32,48)`, MCNK `(3,12)`
- Fidelity classification: server/coordinate integration evidence only; it does
not establish full original-client visual parity
## Documentation deliverables
- Inline API/tool documentation for new verifier and marker metadata
- Coordinate-mapping module capability/evidence update
- World-renderer checkpoint input/output/source-map update when marker support is added
- Existing synchronous coordinate data-flow diagram updated only if flow changes
## Simplicity and naming
- Important names: `server_spawn_render_manifest`,
`verify_server_spawn_renderer`, `diagnostic_spawn_marker`
- Simplest approach: reuse the existing checkpoint capture pipeline with one
dedicated manifest and a renderer-native diagnostic marker
- Rejected complexity: live DB adapter, new scene-test framework and production
spawn entity implementation before the network/gameplay milestones
- Performance: one diagnostic checkpoint outside production runtime
## Status
- State: ready-for-review
- Done: fixture-to-renderer manifest/verifier, renderer-native marker, unified
baseline integration, cold/warm GUI evidence, documentation and log cleanup
- Next: integrator review, merge cleanup/handoff and M01 closeout audit
- Blocked by:
## Handoff
- Commits: implementation `24ddc99`; required cleanup `318e6ae`
- Branch: `work/sindo-main-codex/m01-server-spawn-renderer`
- Outcome: the pinned AzerothCore Human Warrior start is routed through
`CoordinateMapper` into the ordinary Eastern Kingdoms capture scene. A
depth-tested magenta marker begins at the exact mapped spawn origin.
- Contract/schema: additive diagnostic manifest schema 1 and marker metadata;
coordinate contract version 2, production renderer APIs, cache formats and
`Blizzlike335` behavior are unchanged.
- Verification: `SERVER_SPAWN_RENDERER PASS spawn=1 checkpoint=1 tile=32_48
chunk=3_12 profile=Blizzlike335`; unified renderer dry-run passed project,
materials, M2 dedupe, shutdown, `7/7/7` manifest, five-point calibration,
coordinate-boundary and server-spawn steps; coordinate golden fixtures,
mapper, StreamingFocus, domain identities, documentation and whitespace gates
passed. Coordination passed with five expired M00-claim warnings only.
- GUI evidence: two `1280x900` cold/warm PNGs captured under
`user://server_spawn_renderer_m01_gui_run2`; focus tile `(32,48)`, terrain
quality `100/100`, missing cache `0`, queues drained, report results `2`.
Human inspection confirms the marker is on rendered terrain beside the road
at Northshire Abbey rather than below terrain or in an adjacent tile. PNG
hashes: cold `c08c43bcb75e5b3277105afb0d4e5a76caae9db91a7db64488770ffa03e32589`,
warm `1b75237a090ea163836372a8e39e778c909773f4508063adc3f70a3e12a85a62`.
- Fidelity: source remains AzerothCore revision
`68fcf0098b16388093989627f37be530fc91f07d`; server
`(-8949.95,-132.493,83.5312)` maps to Godot
`(17199.159666667,83.5312,26016.616666667)`, ADT `(32,48)`, MCNK `(3,12)`.
This proves coordinate/renderer integration, not original-client visual parity
or TrinityCore populated-row equivalence.
- Runtime notes: D3D12 descriptor-heap initialization fell back to Vulkan;
optional ignored character/ambient GLB imports were unavailable. Neither
affected terrain/marker evidence. The known anonymous ObjectDB teardown
warning remained; no Node, renderer resource or RID leak diagnostic occurred
in the cache-complete run.
- Repository hygiene: four accidentally committed local GUI logs are removed by
`318e6ae`; root `*.stdout.log`/`*.stderr.log` files are now ignored. Generated
PNG/JSON evidence and proprietary corpus remain outside Git.
- Remaining risks: the diagnostic marker is not a production network entity;
live server adapters belong to M06/M08. Exact original-client character spawn
presentation and TrinityCore database equivalence remain unverified.
- Documentation: `RENDER.md`, renderer-baseline procedure, coordinate-mapping
and world-renderer module API/input-output/data-flow/capability/source-map
sections updated. No lifecycle/state diagram changed because the verifier and
mapper path are synchronous; capture reuses the documented renderer lifecycle.
- Recommended merge order: `318e6ae` after master merge `747fae8`. Then the M01
milestone integrator may accept all M01 claims, update target Evidence/checks
and evaluate `OPENWC_TARGET_DONE`.
@@ -0,0 +1,120 @@
# M01-RND-STREAMING-FOCUS-001 — Explicit streaming focus contract
<!-- OPENWC_CLAIM:M01-RND-STREAMING-FOCUS-001:sindo-main-codex:2026-07-15 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M01-RND-STREAMING-FOCUS-001:c8e99f2 -->
## Ownership
- Target: M01
- Program: RND/FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m01-streaming-focus`
- Lease expires UTC: 2026-07-15
- Integrator: M01 milestone integrator
## Outcome
Introduce a scene-free `StreamingFocus` value and make runtime/test streaming
consume it through an explicit source or public setter instead of discovering
the active `Camera3D` from a viewport.
## Non-goals
- Extracting `WorldRenderFacade` or `StreamingTargetPlanner` from the monolithic loader.
- Adding direction, velocity, frustum or multi-focus planning.
- Decomposing the player controller before M02.
- Changing terrain, M2, WMO, liquid, cache or LOD formulas.
## Paths
- Exclusive: `src/domain/streaming/`, `src/tools/verify_streaming_focus.gd`
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/scenes/streaming/eastern_kingdoms_streaming.tscn`,
`src/scenes/streaming/kalimdor_streaming.tscn`, renderer diagnostic tools,
`docs/modules/world-renderer.md`, this claim
- Generated/ignored: local extracted assets, renderer caches and Godot import metadata
## Contracts and data
- Public API: immutable `StreamingFocus`; loader `set_streaming_focus` and
`refresh_streaming_focus`
- Source adapter: explicit `streaming_focus_source_path` may reference any `Node3D`
- Migration: runtime scenes use player focus; capture/probe tools explicitly use
their dedicated camera as the source
- Compatibility: streaming radii/LOD/cache behavior and coordinate contract v2 unchanged
## Dependencies
- Requires: coordinate contract version 2 and golden fixtures at `52e1b2b`
- Blocks: M01 renderer/test focus exit criterion and later renderer facade extraction
- External state: no new dependency or proprietary input
## Verification
- Commands: streaming focus contract/integration verifier, renderer baseline
manifest/calibration, headless scene smoke, coordination/documentation gates
- Fixtures: Node3D player-style source and explicit capture-style setter
- Fidelity evidence: existing seven renderer checkpoints and build-12340 coordinate
calibration must remain unchanged
- Performance budget: one scalar focus snapshot per configured streaming interval;
no new per-tile or per-chunk work
## Documentation deliverables
- Inline public API docs: focus value and loader public methods
- Module specification: inputs/outputs, ownership, lifecycle, failure/recovery and source map
- Data-flow diagram: player/editor/capture source to focus contract to loader
- State/sequence diagram: focus sampling and refresh threshold sequence
## Simplicity and naming
- Important names introduced: `StreamingFocus`, `streaming_focus_source_path`,
`set_streaming_focus`, `refresh_streaming_focus`
- Simplest considered solution: one immutable value plus one explicit scene adapter path
- Rejected complexity/abstractions: provider interface hierarchy, service locator,
multi-focus registry and predictive motion fields
- Unavoidable complexity and justification: editor viewport remains a separate
adapter because its camera is owned by `EditorInterface`
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: contract, runtime/editor/capture migration, headless regression,
module diagrams, renderer documentation and cold/warm GUI capture
- Next: integrator review and merge before typed identity work
- Blocked by:
## Handoff
- Commit: `7815385`
- Branch: `work/sindo-main-codex/m01-streaming-focus`
- Outcome: runtime streaming consumes immutable `StreamingFocus`; Eastern
Kingdoms and Kalimdor scenes explicitly follow the player, while capture and
terrain/occluder probes explicitly follow their dedicated camera
- Contract/API: new scene-free `StreamingFocus(GodotWorldPosition)` plus loader
`set_streaming_focus`, `refresh_streaming_focus` and
`streaming_focus_source_path`; `camera_path` remains only for optional overview positioning
- Verification: StreamingFocus PASS (`1` contract, `2` runtime scenes, `3`
capture tools); coordinate golden fixtures and CoordinateMapper PASS; M00
calibration PASS with maximum error `0.000015`; manifest PASS (`7`
checkpoints); runtime-cache shutdown PASS; headless runtime scene exit `0`;
coordination, documentation, dependency and whitespace checks passed
- Renderer checkpoint: standard 8-second GUI capture completed `14` images
(`7` cold + `7` warm) and wrote schema-1 `report.json`; focus tiles changed
through the public contract; no Node/resource/RID leak diagnostic appeared
- Fidelity: checkpoint camera positions, coordinate contract v2, streaming radii,
LOD formulas, cache formats and quality profiles are unchanged; this is
regression evidence, not a new `1:1` parity claim
- Rebuild: none; GUI verification used local ignored extracted assets/cache
junctions. Missing worktree `.godot/imported` copies for several incidental M2
GLBs produced existing cache-load fallback diagnostics only
- Remaining risks: the monolithic loader still owns planning/jobs/presentation;
direction, velocity, frustum and multiple foci remain M03 planner work. The
known timing-dependent zero-reference `ObjectDB` shutdown warning occurred,
without Node/resource/RID leak diagnostics
- Recommended merge order: after coordinate fixtures at `52e1b2b`, before typed
identity and renderer-facade extraction
- Documentation: `world-renderer.md` inputs/outputs, public API, lifecycle,
data-flow and sequence diagrams, ownership, recovery, verification,
capability matrix, risks and source map updated; `RENDER.md` updated
+113
View File
@@ -0,0 +1,113 @@
# M02-GMP-INPUT-001 — Player input intent seam
<!-- OPENWC_CLAIM:M02-GMP-INPUT-001:sindo-main-codex:2026-07-16 -->
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-001:6bd2e84 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-001:14dead1 -->
## Ownership
- Target: M02
- Program: GMP/FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-player-input`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Replace direct keyboard polling in the runtime player with remappable Input Map
actions and an immutable `PlayerInputSource → MoveIntent` boundary while
preserving the current render-sandbox movement behavior.
## Non-goals
- Extract local movement, terrain query, camera rig or character presenters.
- Claim exact build-12340 movement/keybinding parity before original-client fixtures.
- Change movement speeds, camera geometry, terrain sampling or renderer behavior.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/domain/input/`, `src/gameplay/input/`,
`src/tests/scenes/player_input_regression.tscn`,
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`, this claim
- Shared/hotspots: `project.godot`,
`src/scenes/player/third_person_wow_controller.gd`, `docs/modules/README.md`
- Generated/ignored: Godot import metadata, `.uid` files and local renderer corpus
## Contracts and data
- Public API: immutable `MoveIntent`; `PlayerInputSource.sample_move_intent`
- Configuration: additive `openwc_player_*` Input Map actions
- Persistence/schema/cache: unchanged
- Consumers: current sandbox player controller; later local movement controller
## Dependencies
- Requires: merged M01 coordinate/streaming-focus contracts on master `62eece9`
- Blocks: later M02 movement-controller extraction
- External state: none; tests use synthetic Input Map events
## Verification
- Commands: dedicated player-input verifier, headless project load, coordinate
boundary gate, coordination/documentation gates and diff hygiene
- Fixtures: synthetic action strengths and configured default bindings
- Fidelity evidence: current W/S/A/D, Shift, E/Q and Space sandbox behavior remains
mapped through actions; exact 3.3.5a semantics remain unverified
- Performance budget: one immutable intent allocation per physics tick; no I/O
## Documentation deliverables
- Inline API documentation for `MoveIntent`, `PlayerInputActions` and
`PlayerInputSource`
- Player-input module specification with inputs/outputs, data-flow and sequence diagrams
- Module registry/source map and honest capability status
## Simplicity and naming
- Important names: `MoveIntent`, `PlayerInputSource`, `PlayerInputActions`
- Simplest approach: immutable value, one input adapter and one controller consumer
- Rejected complexity: input provider hierarchy, event bus and generic command framework
- Unavoidable complexity: none
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: twelve remappable actions, immutable `MoveIntent`, stateless
`PlayerInputSource`, controller migration, asset-free regression scene,
dedicated verifier and module specification
- Next: M02 integrator reviews and merges before local movement extraction
- Blocked by:
## Handoff
- Commit: `6bd2e84`
- Branch: `work/sindo-main-codex/m02-player-input`
- Outcome: the runtime player no longer polls physical keyboard keys. Movement
action strengths cross an immutable `MoveIntent` seam; camera and immediate
sandbox-flight events use the same remappable action catalog.
- Public contract: additive `MoveIntent`, `PlayerInputActions` and
`PlayerInputSource`; no schema, cache, coordinate or renderer contract change.
- Verification: `PLAYER_INPUT PASS actions=12 intent_cases=6 controller=1
regression_scene=1`; asset-free scene smoke exited `0`; coordinate boundary
(`88` files) and StreamingFocus gates passed; unified renderer dry-run passed
all eight pre-capture gates and produced seven dry-run checkpoint results;
coordination/documentation/diff gates passed.
- Fidelity: W/S/A/D, Shift, E/Q, Space, right mouse, wheel and Escape remain the
default sandbox bindings. Regression drives forward movement, camera zoom and
immediate flight toggle through the real controller. This does not establish
exact build-12340 default bindings, turn/strafe policy or gameplay movement parity.
- Local verification inputs: the isolated worktree reused the existing ignored
native DLL and generated ADT resource scripts. Proprietary extracted data and
the character GLB were absent, so the renderer dry-run logged the expected
missing-corpus diagnostics while all contract steps returned success.
- Rebuild/migration: none. Action names are additive; persisted user-binding
migration does not exist yet and is documented as a gap.
- Remaining risks: production profile gating for sprint/free flight, exact
3.3.5a bindings, local movement/terrain/camera/presentation extraction and
broader movement/camera state tests remain M02 work.
- Documentation: inline API comments plus `docs/modules/player-input.md` with
inputs/outputs, data-flow and sequence diagrams, ownership, recovery,
capability status, known gaps and source map.
@@ -0,0 +1,136 @@
# M02-GMP-INPUT-PROFILE-335-001 — WoW 3.3.5a input profile
<!-- OPENWC_CLAIM:M02-GMP-INPUT-PROFILE-335-001:sindo-main-codex:2026-07-17 -->
## Ownership
- Target: M02
- Program: GMP/QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-input-profile-335`
- Lease expires UTC: 2026-07-17
- Integrator: M02 milestone integrator
## Outcome
Add a profile-aware, remappable `Blizzlike335` input path whose default movement
bindings match the extracted build-12340 binding table while preserving the
existing `RenderSandbox` controls. Add deterministic turn intent and keyboard
turn consumption at the server-contract default turn rate.
## Non-goals
- Implement jump/fall/swim, network prediction or server reconciliation.
- Claim complete movement or camera parity from binding and turn-rate evidence.
- Persist user keybindings or build keybinding UI.
- Commit proprietary extracted client files.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/tests/fixtures/player_input_profile_335.json`, this claim
- Shared/hotspots: `project.godot`, `src/domain/input/move_intent.gd`,
`src/gameplay/input/player_input_actions.gd`,
`src/gameplay/input/player_input_source.gd`,
`src/gameplay/movement/local_player_movement_controller.gd`,
`src/scenes/player/third_person_wow_controller.gd`,
`src/scenes/player/third_person_camera_rig.gd`, player/movement verifiers
and regression scene, `docs/modules/player-input.md`,
`docs/modules/local-player-movement.md`, `docs/modules/third-person-camera.md`
- Generated/ignored: local `.godot`, native DLL and proprietary extracted corpus
## Contracts and data
- `MoveIntent` gains an immutable, clamped signed turn axis.
- `PlayerInputSource` selects explicit sandbox or compatibility action sets at
construction and routes compatibility A/D to strafe while camera rotation is held.
- `LocalPlayerMovementController` converts turn intent into a deterministic yaw
delta without reading engine input or owning a scene transform.
- A sanitized JSON fixture records selected binding facts, source hashes and the
pinned TrinityCore 3.3.5 server-contract commit.
## Dependencies
- Requires: merged sandbox capability package on master `c3094c9`.
- Blocks: final M02 integration audit and milestone closeout.
- External state: local extracted build-12340 binding files are evidence inputs
only; committed tests are reproducible without them.
## Verification
- Fixture schema/provenance and selected default-binding regression.
- Pure profile composition, A/D turn, Q/E strafe and camera-held A/D strafe cases.
- Deterministic turn-rate and controller integration regression.
- Existing M02 and repository gates remain required.
## Documentation deliverables
- Updated input/movement public API, profile matrix, data flow, sequence,
failure/recovery, fidelity provenance and source maps.
## Simplicity and naming
- Important names: `turn_axis`, `BLIZZLIKE_STRAFE_LEFT`, `TURN_LEFT`.
- Simplest approach: one profile selection at composition plus one scalar intent.
- Rejected complexity: input-context framework, command bus and persisted binding store.
- Unavoidable complexity: compatibility A/D behavior depends on camera-rotate state.
- Measured optimization evidence: not applicable.
## Status
- State: ready-for-review
- Done: added distinct remappable profiles, signed turn intent/controller path,
camera-yaw synchronization, sanitized provenance fixture, regression coverage
and updated module specifications
- Next: milestone integrator reviews and merges `8776a6b`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M02-GMP-INPUT-PROFILE-335-001:8776a6b -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-INPUT-PROFILE-335-001:eb122d5 -->
## Handoff
- Branch/commit: `work/sindo-main-codex/m02-input-profile-335` at `8776a6b`.
- Outcome: `RenderSandbox` retains A/D strafe and debug E/Q/Space/Shift behavior;
`Blizzlike335` now samples W/S and Up/Down forward/back, Q/E strafe, A/D and
Left/Right turn, with A/D routed to strafe while right-mouse rotation is active.
- Public contracts: `MoveIntent.turn_axis`; profile-selecting
`PlayerInputSource.new(profile_id)`; pure compatibility composition;
`LocalPlayerMovementController.calculate_yaw_delta_radians(...)`; camera yaw
synchronization after keyboard turn. Unknown input profiles fail closed to
`Blizzlike335`.
- Project configuration: four additive compatibility actions; existing action
names and sandbox mappings remain valid. W/S also gain the original Up/Down
secondary defaults.
- Verification: player input `actions=16 intent_cases=9 profiles=3 fixture=1
controller=1 regression_scene=3`; movement `cases=15 state_transitions=2`;
capabilities `profiles=3 sprint=2 flight=2 scenes=2`; terrain
`contract=4 interpolation=1 cache=1 failures=2`; camera
`state_cases=12 policy_cases=3`; presentation `appearance_cases=9
animation_cases=10`; coordinate boundaries `files=101`; StreamingFocus
`runtime_scenes=2 capture_tools=3`.
- Renderer: unified dry-run passed project/material/dedupe/cache/manifest/
calibration/coordinate/server-spawn gates and planned all seven checkpoints;
coordinate gate reported `files=103`. Missing proprietary GLB/DBC/ADT inputs
produced the expected diagnostics only.
- Repository gates: documentation `module_specs=8`; coordination
`fallback_claims=31`, with the same five unrelated expired M00 warnings.
- Fidelity: local build-12340 `DefaultBindings.wtf` SHA-256
`35FFA0E4E2356A13A0633A848F9E57ABE7DF9D8A16C2245E15D33013AD2F56A5`
and `Bindings.xml` SHA-256
`2E01276AFB7462F1417710F13B4BF1A456341F7F28CC8AE0D6D9C7442D7F6793`
were rechecked and selected facts sanitized into the committed fixture.
TrinityCore revision `2853a621d6af91a803787a2b8a509f4ce3e0300d` supplies the
3.141594 rad/s player server-contract turn rate. WoWee revision
`626243e937fb93965fa583a6507ed5a1aa7dda4b` supports right-mouse A/D routing.
- Fidelity limits: server turn rate and public reference routing are not direct
original-client timing/behavior proof. Jump, autorun and left-button
camera/select defaults are recorded but not implemented. This package does
not claim complete input, movement or camera parity.
- Migration/cache: no persisted settings or cache schema. Future saved bindings
must migrate stable action names; current project users receive additive actions.
- Documentation: updated input, movement and camera APIs, inputs/outputs, data
flow, sequence, ownership, failure/recovery, configuration, fidelity and source maps.
- Recommended merge order: merge after sandbox capabilities (already on master),
then run the final M02 integration/evidence audit without marking the target
done solely from this package.
+118
View File
@@ -0,0 +1,118 @@
# M02-GMP-MOVEMENT-001 — Local movement state/controller
<!-- OPENWC_CLAIM:M02-GMP-MOVEMENT-001:sindo-main-codex:2026-07-16 -->
<!-- OPENWC_HANDOFF:READY:M02-GMP-MOVEMENT-001:435e1c9 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-MOVEMENT-001:5206c42 -->
## Ownership
- Target: M02
- Program: GMP
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-local-movement`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Move local velocity, sandbox flight state, acceleration and directional speed
calculation from `ThirdPersonWowController` into a scene-free,
independently-testable local movement controller without changing current
sandbox motion.
## Non-goals
- Extract terrain queries, camera rig, character presentation or animation.
- Add jump/fall/swim, server prediction or reconciliation.
- Change current movement speeds, acceleration, camera-relative flight or ground snap.
- Gate debug movement by build profile; that remains a separate M02 package.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/gameplay/movement/`,
`src/tools/verify_local_player_movement.gd`,
`docs/modules/local-player-movement.md`, this claim
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`,
`docs/modules/README.md`
- Generated/ignored: local `.godot`, native DLL, generated ADT resources and renderer corpus
## Contracts and data
- Public API: stateful `LocalPlayerMovementController`
- Inputs: immutable `MoveIntent`, local Godot movement basis and physics delta seconds
- Outputs: local Godot displacement and read-only velocity/flight state
- Schema/cache/coordinate contracts: unchanged
- Consumer: current sandbox player scene; later gameplay composition root
## Dependencies
- Requires: merged input seam on master `14dead1`
- Blocks: terrain query and camera/presentation extraction from the player hotspot
- External state: none; unit tests use synthetic intents and bases
## Verification
- Commands: dedicated pure movement verifier, player-input regression, asset-free
scene smoke, coordinate/StreamingFocus gates, renderer dry-run and repository gates
- Fixtures: identity/rotated/pitched bases, acceleration steps, sprint and flight transitions
- Fidelity evidence: exact current 7.0/4.5/4.5 speeds, 28.0 acceleration,
6x sandbox sprint and camera-relative free flight are regression-locked only
- Performance budget: one small state update per physics tick; no allocation, I/O or scene lookup
## Documentation deliverables
- Inline public API docs for movement state/configuration and update methods
- Local movement module specification with data-flow, state and sequence diagrams
- Updated player-input consumer diagram/status and module registry
## Simplicity and naming
- Important name: `LocalPlayerMovementController`
- Simplest approach: one stateful RefCounted owning velocity and flight toggle
- Rejected complexity: generic locomotion framework, ECS component hierarchy and event bus
- Unavoidable complexity: movement basis remains an explicit local-space input
so the pure controller does not discover player/camera Nodes
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: scene-free movement state/controller, player scene migration, twelve
numeric cases, two flight transitions, integrated regression and module documentation
- Next: M02 integrator reviews and merges before TerrainQuery extraction
- Blocked by:
## Handoff
- Commit: `435e1c9`
- Branch: `work/sindo-main-codex/m02-local-movement`
- Outcome: `ThirdPersonWowController` no longer owns velocity, flight state,
acceleration or directional speed calculation. It selects an explicit
Godot-world basis, applies returned displacement, and retains terrain and
presentation adapters for later M02 packages.
- Public contract: additive `LocalPlayerMovementController` with read-only
Godot-world velocity/flight state, `toggle_sandbox_flight` and `advance`.
Input, coordinate, cache and renderer contracts remain unchanged.
- Verification: `LOCAL_PLAYER_MOVEMENT PASS cases=12 state_transitions=2
scene_boundary=1`; `PLAYER_INPUT PASS actions=12 intent_cases=6 controller=1
regression_scene=1`; asset-free scene smoke exited `0`; coordinate boundary
and StreamingFocus passed; renderer dry-run passed all eight pre-capture gates
and seven checkpoint plans; coordination/documentation/diff gates passed.
- Fidelity: exact existing defaults are regression-locked—forward `7.0`,
backward/strafe `4.5`, acceleration `28.0`, sandbox sprint `6x`, vertical
flight `7.0`, velocity reset on toggle and camera-relative pitched flight.
This preserves the sandbox baseline but is not original-client evidence.
- Local verification inputs: the isolated worktree reused the existing ignored
native DLL and generated ADT resource scripts. Proprietary extracted data and
character GLB were absent, so renderer dry-run logged expected missing-corpus
diagnostics while every contract step returned success.
- Rebuild/migration: none; transient movement state is not persisted.
- Remaining risks: exported speed changes after `_ready` do not reconfigure the
controller; exact build-12340 movement semantics, terrain/collision state,
jump/fall/swim, server prediction and debug profile gating remain open.
- Documentation: inline public API plus `docs/modules/local-player-movement.md`
with inputs/outputs, data-flow, state and sequence diagrams, ownership,
recovery, capability status, risks and source map; player-input consumer
diagrams/status were updated.
@@ -0,0 +1,127 @@
# M02-GMP-SANDBOX-CAPABILITIES-001 — Debug movement capability gate
<!-- OPENWC_CLAIM:M02-GMP-SANDBOX-CAPABILITIES-001:sindo-main-codex:2026-07-16 -->
## Ownership
- Target: M02
- Program: GMP/FND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-sandbox-capabilities`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Introduce an immutable local-player movement capability contract and require it
at the movement boundary so debug sprint and free flight execute only in the
explicit `RenderSandbox` profile and are rejected by `Blizzlike335`.
## Non-goals
- Claim that current movement values or `Blizzlike335` locomotion are 1:1.
- Build a global application/profile framework before the application shell exists.
- Remove debug Input Map actions or change their current sandbox bindings.
- Implement jump, fall, swim, collision, server authority or reconciliation.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/gameplay/movement/player_movement_capabilities.gd`,
`src/tools/verify_player_movement_capabilities.gd`, this claim
- Shared/hotspots: `src/gameplay/movement/local_player_movement_controller.gd`,
`src/scenes/player/third_person_wow_controller.gd`, movement/input verifiers,
player regression scene, `docs/modules/local-player-movement.md`,
`docs/modules/player-input.md`
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
## Contracts and data
- `PlayerMovementCapabilities` owns immutable profile ID plus explicit debug
sprint/flight booleans and safe named factories.
- `LocalPlayerMovementController` requires the capability value; missing input
falls back to `Blizzlike335`, never sandbox.
- Player scene maps its exported profile selection once at the composition boundary.
- Input actions and `MoveIntent` remain unchanged; rejected debug requests have no effect.
## Dependencies
- Requires: merged character presentation package on master `8c71e46`.
- Blocks: final M02 integration audit and milestone closeout.
- External state: none; verification is asset-free.
## Verification
- Pure profile factory/default and immutability regression.
- Sprint accepted in RenderSandbox and ignored in Blizzlike335.
- Flight toggle/vertical basis accepted in RenderSandbox and rejected in Blizzlike335.
- Real regression scene verifies both profile compositions.
- Existing input/movement/terrain/camera/presentation and renderer gates remain required.
- Fidelity evidence: negative proof that debug capabilities cannot affect the
compatibility profile; no positive movement parity claim.
## Documentation deliverables
- Inline public API and profile semantics on the capability/controller boundary.
- Updated movement and input specs: API, inputs/outputs, data flow, lifecycle,
failure/recovery, capability matrix and source map.
## Simplicity and naming
- Important name: `PlayerMovementCapabilities`.
- Simplest approach: one immutable value injected into the existing controller.
- Rejected complexity: autoload singleton, generic feature-flag service and global profile registry.
- Unavoidable complexity: one profile-to-capability mapping at scene composition.
- Measured optimization evidence: not applicable.
## Status
- State: ready-for-review
- Done: added immutable capability contract, gated sprint/free-flight, covered
pure and real-scene profiles and updated movement/input specifications
- Next: milestone integrator reviews and merges `742c415`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M02-GMP-SANDBOX-CAPABILITIES-001:742c415 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-SANDBOX-CAPABILITIES-001:c3094c9 -->
## Handoff
- Branch/commit: `work/sindo-main-codex/m02-sandbox-capabilities` at `742c415`.
- Outcome: debug sprint and free flight now require explicit
`PlayerMovementCapabilities.render_sandbox()`; `Blizzlike335`, unknown and
null capability inputs reject both effects.
- Public contract: immutable profile ID plus `allows_debug_sprint` and
`allows_debug_free_flight`; named factories map configuration once at the
composition boundary. The movement controller exposes its effective profile ID.
- Runtime composition: `ThirdPersonWowController.movement_profile_id` defaults
to `RenderSandbox`, preserving renderer-scene behavior. Selecting
`Blizzlike335` keeps ordinary movement while ignoring Shift/Space/E/Q effects.
- Verification: capability `profiles=3 sprint=2 flight=2 scenes=2 boundary=1`,
movement `cases=12 state_transitions=2`, input `actions=12 intent_cases=6`,
terrain `contract=4 interpolation=1 cache=1 failures=2`, camera
`state_cases=12 policy_cases=3`, presentation
`appearance_cases=9 animation_cases=10`, coordinate boundaries
`files=101 consumers=5`, and StreamingFocus `runtime_scenes=2 capture_tools=3` passed.
- Renderer: dry-run passed project/material/dedupe/cache/manifest/calibration/
coordinate/server-spawn gates and planned all seven checkpoints; the combined
run reported coordinate `files=103`. Missing proprietary GLB/DBC/ADT inputs
produced the expected diagnostics only.
- Repository gates: documentation `module_specs=8`, coordination
`fallback_claims=30`; five unrelated expired M00 claim warnings remain.
- Fidelity: this package provides negative evidence that sandbox-only debug
movement cannot alter the `Blizzlike335` profile. It does not prove ordinary
movement, bindings, flight or timing match build 12340.
- Migration: direct `LocalPlayerMovementController.new(...)` callers that need
debug behavior must now pass `render_sandbox()`; omitted/null capability is
intentionally compatibility-safe. Existing repository scenes require no edit.
- Cache/persistence: no schema, cache, binding or stored-profile migration.
- Local ignored inputs: native extension DLL, UID cache and generated ADT
resource scripts supported renderer verification and are not committed.
- Risks: runtime renderer scenes still default to `RenderSandbox`; the future
application shell must explicitly choose its profile. `Blizzlike335` currently
guarantees only debug exclusion, not complete movement fidelity.
- Recommended merge order: merge after presentation (already on master), then
perform the final M02 integration/evidence audit before any target closeout.
- Documentation: updated movement/input API, inputs/outputs, data flow, state,
sequence, ownership, failure/recovery, capabilities, verification and source maps.
@@ -0,0 +1,119 @@
# M02-GMP-TERRAIN-QUERY-001 — Player terrain query boundary
<!-- OPENWC_CLAIM:M02-GMP-TERRAIN-QUERY-001:sindo-main-codex:2026-07-16 -->
<!-- OPENWC_HANDOFF:READY:M02-GMP-TERRAIN-QUERY-001:a45d521 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-GMP-TERRAIN-QUERY-001:f5bb64e -->
## Ownership
- Target: M02
- Program: GMP/RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-terrain-query`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Introduce a typed `TerrainQuery` boundary and move ADT loading, caching, chunk
selection and height interpolation out of `ThirdPersonWowController` without
changing current spawn/ground-snap behavior.
## Non-goals
- Change ADT topology, coordinate formulas, terrain mesh or renderer caches.
- Add physics collision, slopes, water, jump/fall/swim or server movement.
- Reuse the streaming renderer cache before its public terrain-query contract exists.
- Extract camera or character presentation.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/gameplay/terrain/`, `src/tools/verify_terrain_query.gd`,
`docs/modules/terrain-query.md`, this claim
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
`src/tools/verify_player_input.gd`,
`src/tools/verify_coordinate_conversion_boundaries.gd`,
`docs/modules/local-player-movement.md`,
`docs/modules/README.md`
- Generated/ignored: local `.godot`, native DLL, generated ADT resources and renderer corpus
## Contracts and data
- Public API: `TerrainQuery.sample_ground_height(GodotWorldPosition)` returning
immutable `TerrainGroundSample`
- Production adapter: `AdtTerrainQuery` with owned per-tile parse cache
- Test/data-source seam: optional ADT tile loader callable at construction
- Coordinate contract version, ADT format, renderer cache and persistence: unchanged
- Consumers: current player scene; future movement/collision policies
## Dependencies
- Requires: merged movement controller on master `5206c42`
- Blocks: camera/presentation extraction from the player hotspot
- External state: production uses existing native `ADTLoader`; synthetic tests need no proprietary data
## Verification
- Commands: synthetic height/cache/failure verifier, player input/movement
regressions, asset-free scene, coordinate/StreamingFocus gates, renderer dry-run
and repository gates
- Fixtures: synthetic 145-height chunk payload and injected flat terrain query
- Fidelity evidence: preserves existing outer-grid bilinear interpolation,
spawn ground offset and per-tick snap formula only
- Performance budget: one cached tile lookup and local interpolation per ground query;
no new I/O after first tile load
## Documentation deliverables
- Inline public API docs for query, sample and ADT adapter
- Terrain-query module spec with inputs/outputs, data-flow, cache lifecycle and sequence diagrams
- Updated movement/coordinate consumer boundaries, source maps and module registry
## Simplicity and naming
- Important names: `TerrainQuery`, `TerrainGroundSample`, `AdtTerrainQuery`
- Simplest approach: one narrow query contract, one typed result and one existing-format adapter
- Rejected complexity: general collision framework, terrain service locator and renderer-cache coupling
- Unavoidable complexity: ADT 145-height layout and chunk lookup stay isolated in adapter
- Measured optimization evidence: existing per-tile cache retained; no new optimization
## Status
- State: ready-for-review
- Done: typed query/sample contract, cached ADT adapter, player migration,
synthetic interpolation/cache/failure tests, injected scene regression and documentation
- Next: M02 integrator reviews and merges before camera-rig extraction
- Blocked by:
## Handoff
- Commit: `a45d521`
- Branch: `work/sindo-main-codex/m02-terrain-query`
- Outcome: `ThirdPersonWowController` no longer loads ADTs, caches tile
dictionaries, selects chunks or interpolates height grids. It consumes a
replaceable typed `TerrainQuery` and retains only existing offset/snap policy.
- Public contracts: additive immutable `TerrainGroundSample`, replaceable
`TerrainQuery`, cached `AdtTerrainQuery` and player `set_terrain_query`.
Coordinate contract version, ADT/native format and renderer caches are unchanged.
- Verification: `TERRAIN_QUERY PASS contract=4 interpolation=1 cache=1
failures=2 player_injection=1`; player-input and local-movement regressions
passed; asset-free scene exited `0`; coordinate boundary passed with five
required consumers and StreamingFocus passed; renderer dry-run passed eight
pre-capture gates and seven checkpoint plans; coordination/documentation/diff gates passed.
- Fidelity: synthetic fractional-grid fixture locks the inherited 145-height
outer-grid bilinear result within `0.002` Godot units; injected scene locks
spawn offset `0.05` and physics snap. This is sandbox-regression evidence,
not build-12340 triangle/hole/collision parity.
- Local verification inputs: isolated worktree reused the existing ignored
native DLL and generated ADT resource scripts. Proprietary extracted data and
character GLB were absent, so renderer dry-run logged expected missing-corpus
diagnostics while all contract steps returned success.
- Rebuild/migration: none; caches are transient and existing formats remain unchanged.
- Remaining risks: first ADT parse remains synchronous as before; unavailable
tiles cache until query replacement; player still owns snap policy; exact
ADT triangle choice, holes, slopes, liquids and collision remain open.
- Documentation: inline public APIs plus `docs/modules/terrain-query.md` with
inputs/outputs, data-flow, cache state and cross-boundary sequence diagrams,
ownership, failure/recovery, capability status and source map. Coordinate and
movement consumer specifications were updated.
@@ -0,0 +1,111 @@
# M02-QAR-INTEGRATOR-CLOSEOUT-001 — M02 milestone closeout
<!-- OPENWC_CLAIM:M02-QAR-INTEGRATOR-CLOSEOUT-001:sindo-main-codex:2026-07-17 -->
## Ownership
- Target: M02
- Program: QAR
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-integrator-closeout`
- Lease expires UTC: 2026-07-17
- Integrator: sindo-main-codex, continuing the user-directed milestone workflow on 2026-07-15
## Outcome
Audit every merged M02 package on current master, record final structural and
fidelity evidence, close M02 and activate M03 only when all target and repository
gates pass.
## Non-goals
- Change player, movement, terrain, camera, presentation or renderer behavior.
- Claim complete WoW 3.3.5a locomotion/camera parity.
- Implement recorded jump/fall/swim/fly, collision or timing gaps.
- Commit proprietary assets, generated UIDs, caches or user scene changes.
## Paths
- Exclusive: M02/M03 target status, M02 final Evidence, all M02 claim acceptance markers
- Shared/hotspots: `targets/README.md`, `targets/DEVELOPMENT_ROADMAP.md`, this claim
- Generated/ignored: local Godot imports, native DLL, renderer corpus and `user://` reports
## Contracts and data
- Runtime APIs, Input Map actions, fixture schemas and cache formats: unchanged
- Consumers: milestone sequence and future M03 renderer-facade packages
- Fidelity classification distinguishes direct build-12340 binding evidence,
server/reference compatibility evidence and explicitly unverified gaps
## Dependencies
- Requires: all seven M02 READY packages integrated through master `eb122d5`
- Blocks: M03 activation
- External state: private original-client files are hash/provenance inputs only
## Verification
- Commands: all M02 input/movement/capability/terrain/camera/presentation tests,
coordinate/streaming boundaries, unified renderer dry-run, documentation,
coordination and diff hygiene
- Fidelity evidence: build-12340 default binding hashes and selected facts;
pinned TrinityCore turn rate; public reference routing; explicit remaining gaps
- Performance: no runtime change; renderer baseline remains the performance guard
## Documentation deliverables
- M02 final target Evidence/checklist and limitation statement
- M02 claim integration-acceptance markers
- Target index and roadmap transition to M03
- Existing API/module specifications and diagrams audited without behavioral edits
## Simplicity and naming
- Important names introduced: none beyond the closeout work-package ID
- Simplest approach: evidence/status-only integrator patch
- Rejected complexity: behavioral fixes or a new closeout framework
- Unavoidable complexity: explicit evidence classification prevents false 1:1 claims
## Status
- State: ready-for-review
- Done: accepted seven M02 packages, reran post-merge gates, recorded complete
target Evidence, closed M02 and activated M03
- Next: merge closeout commit `1bf5b52` before starting an M03 implementation claim
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M02-QAR-INTEGRATOR-CLOSEOUT-001:1bf5b52 -->
## Handoff
- Commit: `1bf5b52`.
- Branch: `work/sindo-main-codex/m02-integrator-closeout`.
- Outcome: M02 is `DONE` with all eight structural checklist items and complete
classified Evidence; M03 is the only `ACTIVE` target and the target index/
roadmap point to renderer-facade extraction.
- Integration audit: all seven READY implementation commits are ancestors of
merged master `eb122d5`; their claims now carry the corresponding
`OPENWC_INTEGRATION:ACCEPTED` merge markers.
- Verification: input `16/9/3/1/3`, movement `15` cases and `2` transitions,
capability `3` profiles and `2` scenes, terrain `4/1/1/2`, camera `12/3`,
presentation `9/10/3`, coordinate boundaries `103` files and StreamingFocus
`2` runtime scenes/`3` capture tools all passed. Unified renderer dry-run
passed project/material/dedupe/shutdown gates, manifest `7/7/7`, calibration
maximum error `0.000015`, server-spawn renderer and seven checkpoint plans.
- Repository gates: documentation passed with `8` module specs and `7` required
files. Coordination passed with one active target; `13` expired M00 claim
warnings are historical and unrelated to M02.
- Fidelity: direct build-12340 binding hashes/facts are preserved in the
sanitized fixture. TrinityCore turn rate and WoWee mouse routing remain
explicitly reference-only. Translation speeds/camera values are sandbox
regressions, and jump/fall/swim/fly parity remains unimplemented/unverified.
Debug sprint/free flight is negatively proven absent from `Blizzlike335`.
- Remaining risks: target Evidence carries exact follow-up scope for M09/M10/M12:
movement/camera timing, jump/fall/swim/fly, autorun/LMB semantics, active
collision, terrain/liquid policy, persisted bindings, server authority and
prediction/reconciliation. No complete `1:1` claim is made.
- Runtime/cache/migration: no behavioral change, dependency, cache schema or
persisted settings migration in closeout.
- Documentation: M02 target Evidence/checklist, seven acceptance markers,
target index and roadmap state updated. Existing input, movement, terrain,
camera and presentation API/module specs and diagrams passed the documentation gate.
+119
View File
@@ -0,0 +1,119 @@
# M02-RND-CAMERA-001 — Third-person camera rig boundary
<!-- OPENWC_CLAIM:M02-RND-CAMERA-001:sindo-main-codex:2026-07-16 -->
## Ownership
- Target: M02
- Program: RND/GMP
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-camera-rig`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Move mouse capture, orbit yaw/pitch, zoom state and Camera3D transforms from
`ThirdPersonWowController` into a reusable `ThirdPersonCameraRig`, with an
explicit replaceable camera-collision policy that preserves the current
no-collision sandbox baseline.
## Non-goals
- Enable a new collision/raycast behavior before build-12340 comparison.
- Change initial yaw/pitch, zoom limits, sensitivity, FOV, far plane or camera path.
- Implement first-person, follow lag, shoulder offsets or cinematic cameras.
- Change movement, terrain query, character presentation or renderer streaming focus.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/scenes/player/third_person_camera_rig.gd`,
`src/scenes/player/camera_collision_policy.gd`,
`src/scenes/player/no_camera_collision_policy.gd`,
`src/tools/verify_third_person_camera_rig.gd`,
`docs/modules/third-person-camera.md`, this claim
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`,
runtime streaming scenes, player regression scene,
`src/tools/verify_player_input.gd`, `docs/modules/player-input.md`,
`docs/modules/local-player-movement.md`, `docs/modules/README.md`
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
## Contracts and data
- Public API: `ThirdPersonCameraRig.initialize_for_character`,
`handle_camera_input`, `godot_world_flight_movement_basis`, state getters and
`set_collision_policy`
- Policy: `CameraCollisionPolicy.resolve_camera_distance`; default
`NoCameraCollisionPolicy` returns requested distance unchanged
- Scene paths and renderer camera path remain unchanged
- Schema/cache/coordinate contracts: unchanged
- Consumers: player composition, sandbox flight movement and renderer viewport
## Dependencies
- Requires: merged TerrainQuery package on master `f5bb64e`
- Blocks: character appearance/animation extraction from remaining player hotspot
- External state: none; tests use asset-free camera/player scene
## Verification
- Commands: dedicated orbit/zoom/clamp/policy verifier, input/movement/terrain
regressions, asset-free scene, coordinate/StreamingFocus gates, renderer dry-run
and repository gates
- Fixtures: initial state, remappable action events, mouse deltas and fixed-distance policy
- Fidelity evidence: preserve current `-18°` pitch, `8` distance, `1` zoom step,
`2..18` clamp, `0.003` sensitivity and no-collision output
- Performance budget: constant-time state/transform update; no raycast in baseline policy
## Documentation deliverables
- Inline API/export documentation for rig and collision policies
- Camera module spec with inputs/outputs, data-flow, state and sequence diagrams
- Updated input/movement consumer diagrams/source maps and module registry
## Simplicity and naming
- Important names: `ThirdPersonCameraRig`, `CameraCollisionPolicy`,
`NoCameraCollisionPolicy`
- Simplest approach: one Node3D rig on existing CameraPivot and one explicit identity policy
- Rejected complexity: generic camera framework, spring arm migration and hidden physics raycast
- Unavoidable complexity: rig mutates Camera3D/character Node transforms on main thread
- Measured optimization evidence: not applicable
## Status
- State: ready-for-review
- Done: extracted rig and collision policy, migrated all three scene compositions,
added regressions and completed required module documentation
- Next: milestone integrator reviews and merges `8c1cf9b`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M02-RND-CAMERA-001:8c1cf9b -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CAMERA-001:68e6f60 -->
## Handoff
- Outcome: `ThirdPersonCameraRig` now owns capture, orbit, zoom and camera
transforms; `ThirdPersonWowController` only dispatches camera events and
consumes the rig basis for sandbox flight.
- Public contracts: `initialize_for_character`, `handle_camera_input`,
`godot_world_flight_movement_basis`, `set_collision_policy`, read-only state,
and `CameraCollisionPolicy.resolve_camera_distance`.
- Verification: camera `state_cases=12 policy_cases=3 scenes=3`, input
`actions=12 intent_cases=6`, movement `cases=12 state_transitions=2`, terrain
`contract=4 interpolation=1 cache=1 failures=2`, coordinate boundary
`files=98 consumers=5`; documentation, coordination and diff checks passed.
- Renderer dry-run: seven checkpoint plans and report completed; expected
diagnostics only for unavailable proprietary corpus/character GLB.
- Fidelity: preserves the sandbox defaults (`-18 degrees`, distance `8`, zoom
`2..18` step `1`, sensitivity `0.003`, pivot `1.7`, far `50000`) and the prior
no-collision behavior. No build-12340 parity claim is made.
- Local ignored inputs: native extension DLL and generated ADT resource scripts
were used for the renderer dry-run and are not part of the commit.
- Migration: none; `ThirdPersonPlayer/CameraPivot/Camera3D` remains stable.
- Risks: active collision and exact original-client camera semantics remain
unverified; exported initial values are captured at ready; mouse mode is
process-global.
- Documentation: added the camera API/module spec plus data-flow, state and
sequence diagrams; updated player-input and local-movement consumer maps.
@@ -0,0 +1,125 @@
# M02-RND-CHARACTER-PRESENTATION-001 — Character presentation boundary
<!-- OPENWC_CLAIM:M02-RND-CHARACTER-PRESENTATION-001:sindo-main-codex:2026-07-16 -->
## Ownership
- Target: M02
- Program: RND/GMP
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m02-character-presentation`
- Lease expires UTC: 2026-07-16
- Integrator: M02 milestone integrator
## Outcome
Move character GLB composition, geoset/texture/outfit setup and locomotion
animation selection from `ThirdPersonWowController` into independently
replaceable `CharacterAppearancePresenter` and `CharacterAnimationPresenter`
components without changing the current sandbox presentation.
## Non-goals
- Add new model, equipment, animation or build-12340 behavior.
- Change character asset formats, DBC parsing, texture composition or geoset rules.
- Add network-driven appearance/movement snapshots.
- Change input, movement, terrain, camera or renderer streaming behavior.
- Mark M02 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/scenes/character/character_appearance_presenter.gd`,
`src/scenes/character/character_animation_presenter.gd`,
`src/tools/verify_character_presentation.gd`,
`docs/modules/character-presentation.md`, this claim
- Shared/hotspots: `src/scenes/player/third_person_wow_controller.gd`, runtime
streaming scenes, player regression scene, `src/tools/verify_player_input.gd`,
`docs/modules/local-player-movement.md`, `docs/modules/README.md`
- Reused unchanged: geoset controller, texture compositor and outfit resolver
- Generated/ignored: local `.godot`, native DLL, generated resources and renderer corpus
## Contracts and data
- Appearance API loads/replaces one character root under the existing `Visual` node.
- Animation API binds an `AnimationPlayer` from that root and accepts a typed
moving/stationary presentation state.
- Scene paths, model defaults, DBC/cache/coordinate contracts remain unchanged.
- Consumers: player scene composition and renderer viewport only.
## Dependencies
- Requires: merged camera package on master `68e6f60`.
- Blocks: final M02 sandbox-profile gate and target integration audit.
- External state: asset-free tests use a synthetic mesh and AnimationPlayer.
## Verification
- Dedicated appearance composition, replacement, grounding and failure regression.
- Dedicated animation discovery, fallback, looping, blend and state regression.
- Player source-boundary and three-scene wiring checks.
- Existing input/movement/terrain/camera regressions, coordinate gates and renderer dry-run.
- Fidelity evidence: preserve current sandbox model path, scale, yaw, outfit,
Stand/Idle and Run/Walk selection only; no original-client parity claim.
## Documentation deliverables
- Inline public API/export documentation for both presenters.
- Module specification with inputs/outputs, data-flow, lifecycle/state and sequence diagrams.
- Updated movement consumer/source map and module registry.
## Simplicity and naming
- Important names: `CharacterAppearancePresenter`, `CharacterAnimationPresenter`.
- Simplest approach: two small scene components on the existing `Visual` subtree.
- Rejected complexity: generic presenter framework, async asset scheduler and duplicated character systems.
- Unavoidable complexity: scene-tree composition and AnimationPlayer discovery run on the main thread.
- Measured optimization evidence: not applicable.
## Status
- State: ready-for-review
- Done: extracted both presenters, migrated all runtime/regression scenes,
added asset-free regressions and completed required module documentation
- Next: milestone integrator reviews and merges `97bb53f`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M02-RND-CHARACTER-PRESENTATION-001:97bb53f -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M02-RND-CHARACTER-PRESENTATION-001:8c71e46 -->
## Handoff
- Branch/commit: `work/sindo-main-codex/m02-character-presentation` at `97bb53f`.
- Outcome: `CharacterAppearancePresenter` owns model/geoset/compositor/outfit
composition and `CharacterAnimationPresenter` owns AnimationPlayer discovery,
loop preparation and moving/stationary playback. The player is now only the
composition and horizontal-velocity adapter.
- Changed composition: both runtime scenes and the player regression scene keep
`ThirdPersonPlayer/Visual`; they add `Visual/AnimationPresenter`.
- Public contracts: appearance load/read-root/ready signal and animation
bind/present/read-state APIs documented in the module specification.
- Verification: presentation `appearance_cases=9 animation_cases=10 scenes=3`,
input `actions=12 intent_cases=6`, movement `cases=12 state_transitions=2`,
terrain `contract=4 interpolation=1 cache=1 failures=2`, camera
`state_cases=12 policy_cases=3`, coordinate boundaries `files=99 consumers=5`,
and StreamingFocus `runtime_scenes=2 capture_tools=3` passed.
- Renderer: dry-run passed project/material/dedupe/cache/manifest/calibration/
coordinate/server-spawn gates and planned all seven checkpoints. Missing
proprietary HumanMale GLB, DBC and ADT corpus produced expected diagnostics.
- Repository gates: documentation `module_specs=8`, coordination
`fallback_claims=29`; five unrelated expired M00 claim warnings remain.
- Fidelity: current model path, scale `1`, yaw `90 degrees`, class `1`, blend
`0.15 seconds`, starter outfit and Stand/Idle/Run/Walk selection are preserved.
No build-12340 appearance or animation parity claim is made.
- Migration: repository scenes are migrated. External/custom scenes must move
model/class/scale/yaw exports from the player node to the scripted `Visual`
node and add `Visual/AnimationPresenter`. No cache/database rebuild is needed.
- Local ignored inputs: native extension DLL, UID cache and generated ADT
resource scripts supported the dry-run and are not committed.
- Risks: synchronous model load may hitch; first descendant AnimationPlayer wins;
moving state is a sandbox threshold boolean; complete equipment/animation
fidelity remains open.
- Recommended merge order: merge this package after camera (already on master),
then implement the sandbox profile gate and perform the final M02 integration audit.
- Documentation: added character-presentation API, inputs/outputs, data-flow,
lifecycle/state, sequence, ownership/failure and source-map sections; updated
the module registry and movement presentation consumer map.
@@ -0,0 +1,115 @@
# M03-RND-ADT-WATER-LOAD-PIPELINE-001 — ADT water load pipeline state
<!-- OPENWC_CLAIM:M03-RND-ADT-WATER-LOAD-PIPELINE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-adt-water-load-pipeline`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract ADT water pending-request FIFO/deduplication, active worker task IDs and
worker-safe result mailbox from the loader while preserving loader-owned parse,
budget, stale-result and scene finalization behavior.
## Non-goals
- Instantiate `ADTLoader`, start/wait WorkerThreadPool tasks or parse ADT bytes.
- Consume `WATER_FINALIZE` permits or choose concurrency/profile limits.
- Build/attach/free water Nodes, materials, Meshes or RIDs.
- Change tile/path stale checks, empty-data behavior or `water_loaded` timing.
- Change MH2O/MCLQ parsing or claim liquid fidelity/performance parity.
## Paths
- Exclusive: `src/render/liquid/adt_water_load_pipeline_state.gd`,
`src/tools/verify_adt_water_load_pipeline_state.gd`,
`docs/modules/adt-water-load-pipeline-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_render_runtime_cache_shutdown.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Pending requests preserve FIFO and tile-key deduplication.
- Tile release removes all matching pending entries but does not cancel active work.
- Active task IDs are loader-created opaque integers keyed by tile.
- Worker publication and main-thread result pop/clear are mutex protected.
- Result order and Dictionary payload identity remain unchanged.
- Clear releases bookkeeping only; loader waits tasks at orderly shutdown.
- Water parsing, coordinate, material, profile and visual rules are unchanged.
## Dependencies
- Requires: current master `4387c66`; accepted scheduler and renderer lifecycle
- Blocks: further liquid parse/finalization decomposition
- External state: none; verification uses synthetic task IDs and payloads
## Verification
- Commands: dedicated FIFO/dedupe/task/result/cancel/clear/thread/source/timing
verifier; shutdown, water/material and adjacent renderer/streaming/coordinate/
documentation/coordination gates
- Fixtures: synthetic tile/path requests, task IDs and water dictionaries
- Fidelity evidence: exact current queue/task/result transitions preserved
- Performance budget: 100 cycles over 256 requests/results under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, worker sequence and dependency diagrams
- World-renderer, module registry and RENDER status updates
## Simplicity and naming
- Important names introduced: `AdtWaterLoadPipelineState`
- Simplest considered solution: one RefCounted with FIFO/dedupe/task maps and one mutex mailbox
- Rejected complexity/abstractions: generic async scheduler, signals, callbacks,
WorkerThreadPool wrapper, parser/material ownership or multi-lane framework
- Unavoidable complexity and justification: result mailbox crosses worker/main threads
- Measured optimization evidence: bounded synthetic lifecycle loop
## Status
- State: accepted
- Done: pipeline state implemented; loader enqueue/start/publish/drain/release/
reset/shutdown/metrics adapters migrated; required documentation added
- Next: continue liquid parse/finalization extraction or another remaining M03
service boundary
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-ADT-WATER-LOAD-PIPELINE-001:f470775 -->
## Handoff
- Commit: `b7c036f`
- Results: water pipeline PASS (`cases=12`, `iterations=100`,
`elapsed_ms=86.170`; initial direct cold run `94.576ms`); real Thread result
publication, FIFO/dedupe/task/cancel/clear/source contracts PASS; WMO caches/
queue/planner/registry/resolver, materials, shutdown and 18 adjacent M2/terrain/
facade/scheduler/streaming/coordinate regressions PASS; documentation and
coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Remaining risks: clear does not interrupt active WorkerThreadPool work and a
post-reset publication remains stale until drained/cleared, matching prior
behavior; payload Dictionaries require no mutation after publication; parser,
main-thread water materialization and asset-backed MH2O/MCLQ traversal/leak/
p95/p99/original-client evidence remain pending.
- Documentation updated: inline public API; new
`docs/modules/adt-water-load-pipeline-state.md`; module registry, world renderer
and `RENDER.md`; data-flow/state/sequence/dependency diagrams, ownership,
threading, failure/recovery, profiles, performance limit and source map included.
- Integration: merged as `f470775`; post-merge water pipeline (`cases=12`,
`iterations=100`, `elapsed_ms=86.935`), shutdown, renderer materials, facade,
internal-access (`private_symbols=36`), `7/7` manifest, documentation and
coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-ADT-WATER-LOAD-PIPELINE-001:b7c036f -->
@@ -0,0 +1,116 @@
# M03-RND-ADT-WATER-SCENE-FINALIZER-001 — ADT water scene finalizer
<!-- OPENWC_CLAIM:M03-RND-ADT-WATER-SCENE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-adt-water-scene-finalizer`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract main-thread ADT water subtree construction, tile-root attachment and
optional recursive editor ownership from `StreamingWorldLoader` while retaining
the existing `ADTBuilder` geometry/material implementation.
## Non-goals
- Parse or reinterpret MH2O/MCLQ data.
- Own pending requests, WorkerThreadPool tasks or parsed-result delivery.
- Consume `WATER_FINALIZE` permits or validate tile/path/result staleness.
- Mark tile state `water_loaded` or choose whether water is enabled.
- Change liquid geometry, materials, coordinates, cache formats or profiles.
## Paths
- Exclusive: `src/render/liquid/adt_water_scene_finalizer.gd`,
`src/tools/verify_adt_water_scene_finalizer.gd`,
`docs/modules/adt-water-scene-finalizer.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_adt_water_load_pipeline_state.gd`,
`docs/modules/adt-water-load-pipeline-state.md`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Empty water data or invalid tile root produces no Node and no scene mutation.
- Existing `ADTBuilder.build_tile_water_scene` remains the only geometry/material source.
- A non-empty built Water root is attached exactly once to the supplied tile root.
- Optional editor ownership is assigned recursively only when explicitly enabled
and an edited scene root is available.
- The returned Node is borrowed and remains owned by the tile SceneTree subtree.
## Dependencies
- Requires: accepted water pipeline package on current master `e1b2886`
- Blocks: further liquid finalization/material responsibility extraction
- External state: none; verification uses synthetic liquid dictionaries
## Verification
- Commands: dedicated empty/invalid/build/attach/editor-owner/source/timing
verifier; water pipeline, materials, shutdown, facade, internal-access,
manifest, documentation and coordination gates
- Fixtures: synthetic one-cell liquid mask/heights and temporary Node roots
- Fidelity evidence: exact existing ADTBuilder output and two attachment paths preserved
- Performance budget: 100 empty finalization attempts under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, main-thread sequence and dependency diagrams
- Water-pipeline, world-renderer, module registry and RENDER status updates
## Simplicity and naming
- Important names introduced: `AdtWaterSceneFinalizer`
- Simplest considered solution: one RefCounted with one explicit attach operation
- Rejected complexity/abstractions: generic scene finalizer, signals, callbacks,
builder interface, queue ownership or alternate liquid renderer
- Unavoidable complexity and justification: editor-persisted generated subtrees
require recursive owner assignment after SceneTree attachment
- Measured optimization evidence: bounded synthetic no-water loop
## Status
- State: accepted
- Done: stateless finalizer implemented; both loader water paths migrated;
synthetic scene/owner regression and required documentation added
- Next: continue liquid parsing/task execution extraction or another remaining M03 boundary
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-ADT-WATER-SCENE-FINALIZER-001:874fd0f -->
## Handoff
- Commit: `235be3a`
- Results: scene finalizer PASS (`cases=8`, `iterations=100`,
`elapsed_ms=0.042`; initial `0.024ms`); synthetic liquid produced one
four-vertex/six-index surface with tile attachment and recursive owner; water
pipeline (`119.648ms`), WMO/M2/terrain/facade/scheduler/streaming/coordinate,
materials, shutdown, internal-access `36` and 32 headless contract regressions
PASS; documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Remaining risks: main-thread mesh/material construction remains synchronous
behind one existing permit; the Dictionary builder boundary remains untyped;
proprietary MH2O/MCLQ traversal/leak/p95/p99/original-client evidence is absent.
A verifier launched concurrently with the initial cold editor import observed
the pre-existing global-class cache race; serialized editor parse and shutdown
rerun passed, with only expected missing private asset diagnostics.
- Documentation updated: inline public API; new
`docs/modules/adt-water-scene-finalizer.md`; water-pipeline spec, module registry,
world renderer and `RENDER.md`; data-flow/state/sequence/dependency diagrams,
ownership, main-thread effects, failure/recovery, migration and source map included.
- Integration: merged as `874fd0f`; post-merge finalizer (`cases=8`,
`iterations=100`, `elapsed_ms=0.025`), water pipeline (`98.398ms`), shutdown,
renderer materials, facade, internal-access (`private_symbols=36`), `7/7`
manifest, documentation and coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-ADT-WATER-SCENE-FINALIZER-001:235be3a -->
+117
View File
@@ -0,0 +1,117 @@
# M03-RND-FACADE-001 — Renderer facade first seam
<!-- OPENWC_CLAIM:M03-RND-FACADE-001:sindo-main-codex:2026-07-17 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-renderer-facade`
- Lease expires UTC: 2026-07-17
- Integrator: M03 milestone integrator
## Outcome
Introduce `WorldRenderFacade` as the only runtime/tool caller surface for typed
streaming focus updates and read-only renderer metrics, while retaining
`StreamingWorldLoader` as the unchanged internal implementation.
## Non-goals
- Extract the pure target planner or budget scheduler in this package.
- Add environment or entity-presentation commands before their contracts exist.
- Replace the M02 `TerrainQuery` gameplay boundary in this package.
- Change streaming radii, queues, cache formats, visuals or quality presets.
- Mark M03 complete or edit its checklist/Evidence.
## Paths
- Exclusive: `src/render/world_render_facade.gd`,
`src/tools/verify_world_render_facade.gd`, this claim
- Shared/hotspots: both runtime streaming scenes, three renderer capture/probe
tools, `src/tools/verify_streaming_focus.gd`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`
- Generated/ignored: local `.godot`, native build outputs, caches and proprietary
renderer corpus
## Contracts and data
- Public API: `set_streaming_focus`, `refresh_streaming_focus`,
`renderer_metrics_snapshot`
- Scene adapter: explicit `streaming_focus_source_path` owned by the facade
- Internal implementation seam: `streaming_world_loader_path`; callers must not
read streamer queues or call its methods directly
- Schema/cache/coordinate contracts: unchanged
- Consumers: runtime scenes, capture tool and renderer probes
## Dependencies
- Requires: M01 `StreamingFocus` and merged M02 closeout on master `4f566b1`
- Blocks: environment/entity/ground facade contracts and planner extraction
- External state: none; contract verification uses a test double
## Verification
- Commands: dedicated facade verifier, streaming-focus regression, renderer
baseline manifest/dry-run, coordinate boundary and repository gates
- Fixtures: typed focus, source Node3D and read-only metrics test double
- Fidelity evidence: no renderer configuration, planner, queue, placement,
visibility, material, animation or cache behavior changes
- Performance budget: one source position sample per process frame; target
recomputation remains throttled inside the existing streamer
## Documentation deliverables
- Inline public API docs for facade properties and methods
- Updated world-renderer module specification
- Updated data-flow, sequence and dependency diagrams
- Updated source map/status and renderer notes
## Simplicity and naming
- Important names: `WorldRenderFacade`, `renderer_metrics_snapshot`
- Simplest approach: one small Node adapter delegating to the existing streamer
- Rejected complexity: service container, generic command bus, streamer rewrite
or duplicate queue/cache state
- Unavoidable complexity: scene-relative source and implementation paths are
resolved on the main thread with explicit diagnostics
- Measured optimization evidence: not applicable; behavior-preserving seam
## Status
- State: ready-for-review
- Done: introduced the facade, migrated two runtime scenes and three renderer
tools, added regression coverage and updated renderer documentation
- Next: M03 integrator reviews and merges `3d528e3`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-001:3d528e3 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-001:a100ced -->
## Handoff
- Commit: `3d528e3`
- Integration: accepted in master merge `a100ced`
- Results: `WorldRenderFacade` is the runtime/tool surface for typed focus and
detached metrics; the existing streamer remains the sole owner of queues,
caches, tasks, nodes and RIDs.
- Verification: facade `delegation=3 runtime_scenes=2 tools=3`; streaming focus
`contract=1 runtime_scenes=2 capture_tools=3`; coordinate boundary
`files=105 native_boundary=1 oracle=1 consumers=5 classifier_cases=6`;
render manifest `checkpoints=7 coverage=7 caches=7`; seven-checkpoint headless
dry-run; coordination/documentation/diff gates passed.
- Fidelity: no changes to renderer target calculation, budgets, queue order,
cache versions, placement, visibility, materials or animation. The dry-run
retained all seven checkpoint plans. No build-12340 parity claim is added.
- External/local inputs: ignored native DLL and generated ADT resource scripts
were copied from master only to run the worktree smoke. Proprietary extracted
ADT and character GLB data were unavailable, producing the expected dry-run
degraded-data diagnostics.
- Remaining risks: environment, entity visuals and ground query are not yet
facade contracts; streamer exported configuration remains transitional scene
composition; focus-source sampling adds one small main-thread adapter step per
process frame while target refresh remains streamer-throttled.
- Documentation updated: inline facade API, `docs/modules/world-renderer.md`
public API/inputs/outputs/data-flow/sequence/ownership/recovery/status/source
map, and `RENDER.md` file map plus implementation note.
@@ -0,0 +1,130 @@
# M03-RND-FACADE-ENTITY-001 — World entity presentation commands
<!-- OPENWC_CLAIM:M03-RND-FACADE-ENTITY-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-facade-entity`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Add immutable, versioned world-entity presentation input and stable facade
commands for idempotent present/update and removal, backed by a renderer-owned
scene service with explicit entity subtree ownership.
## Non-goals
- Define authoritative entity/gameplay/network state or allocate/map identities.
- Move the local player controller/presenters behind the world-entity service.
- Implement build-12340 display IDs, equipment, animation, effects or visibility rules.
- Add background loading, pooling, MultiMesh conversion or a generic ECS/framework.
- Change streamer queues, caches, world assets, baseline visuals or target status.
## Paths
- Exclusive: `src/render/entities/entity_presentation_snapshot.gd`,
`src/render/entities/world_entity_presenter.gd`,
`src/tools/verify_world_entity_presentation.gd`,
`src/tests/scenes/world_entity_visual_replacement_fixture.tscn`,
`docs/modules/world-entity-presentation.md`, this claim
- Shared/hotspots: `src/render/world_render_facade.gd`, both runtime streaming
scenes, `src/tools/verify_world_render_facade.gd`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Reused unchanged: `EntityId`, `GodotWorldPosition`, existing asset-free
character presentation model fixture
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
proprietary renderer corpus
## Contracts and data
- Contract version: `EntityPresentationSnapshot.CONTRACT_VERSION = 1`
- Snapshot: valid session-local `EntityId`, typed Godot world position, visual
PackedScene path, finite yaw radians, positive finite uniform scale and visibility
- Public API: `WorldRenderFacade.present_entity`, `remove_entity`,
`entity_presentation_snapshot`
- Presenter behavior: present is idempotent by entity key; same visual updates
transform/visibility, changed visual replaces the owned subtree; remove is explicit
- Schema/cache/coordinate contracts: no persisted schema or cache change
## Dependencies
- Requires: integrated M03 environment facade master `a85e416`, M01 identity and
coordinate contracts
- Blocks: completion of the first M03 facade checklist item and later entity renderer extraction
- External state: none; tests use a repository-owned asset-free PackedScene fixture
## Verification
- Commands: cold snapshot contract; presenter lifecycle/ownership regression;
facade delegation and runtime wiring; identity/coordinate/internal-access;
renderer manifest/shutdown/planner/scheduler/focus and repository gates
- Fixtures: invalid identity/position/yaw/scale/path, spawn, idempotent update,
visual replacement, missing asset, removal, detached diagnostic snapshot and teardown
- Fidelity evidence: no runtime commands are produced yet, so existing scene output
and seven baseline plans remain unchanged
- Performance budget: constant-time entity lookup; synchronous resource load only
on first presentation/visual replacement and explicitly excluded from hot-path acceptance
## Documentation deliverables
- Inline public API docs: snapshot, facade commands and presenter lifecycle
- Module specification: new world-entity-presentation specification
- Data-flow diagram: read model through facade into owned visual subtree
- Sequence/state/dependency diagrams: present/update/replace/remove/failure/teardown
- Source map/status updates: module registry, world renderer and `RENDER.md`
## Simplicity and naming
- Important names introduced: `EntityPresentationSnapshot`, `WorldEntityPresenter`,
`present_entity`, `remove_entity`
- Simplest considered solution: one immutable full visual snapshot and one
dictionary-owned Node3D presenter below an explicit scene service
- Rejected complexity/abstractions: ECS, event bus, command hierarchy, factory
registry, pooling, async loader framework or duplicate identity registry
- Unavoidable complexity and justification: renderer must own and deterministically
replace/free visual subtrees while authoritative state remains external
- Measured optimization evidence: no optimization claim; lifecycle is asset-free tested
## Status
- State: ready-for-review
- Done: immutable contract-v1 snapshot, presenter ownership lifecycle, facade
commands, runtime composition, asset-free regressions and module documentation
- Next: M03 integrator reviews and merges `1807c3a`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-ENTITY-001:1807c3a -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-ENTITY-001:606770c -->
## Handoff
- Commit: `1807c3a`
- Results: facade now exposes idempotent `present_entity`, explicit
`remove_entity` and detached diagnostics. Both runtime scenes compose a
`WorldEntityPresenter` that owns one replaceable visual subtree per valid
session `EntityId`; local player and authoritative state remain separate.
- Verification: entity `snapshot_cases=8 lifecycle_cases=14 runtime_scenes=2`;
facade `delegation=9 entity_contract=6`; identities `5/5/6/5`; character
presentation `9/10/3/1`; coordinate boundary `files=119`; internal access
`43/7/9/1`; environment/ground/terrain, manifest `7/7`, shutdown, scheduler
(`20000` iterations), planner (`250` iterations), focus, editor parse, dry-run,
coordination, documentation and diff gates passed.
- Fidelity: runtime produces no world-entity commands yet, so all seven baseline
plans and existing player/world output remain unchanged. No build-12340
display, equipment, animation, effects or visibility parity claim is added.
- External/local inputs: ignored native DLL and generated ADT scripts supported
editor/dry-run only. Missing proprietary ADT/DBC/character corpus produced the
expected degraded diagnostics.
- Remaining risks: PackedScene create/replace loads synchronously and is not an
accepted network-scale hot path; resource paths are adapter inputs rather than
stable content IDs; animation/equipment/effects and scale profiling remain open.
- Documentation updated: new world-entity-presentation API/input-output/data-flow/
lifecycle/sequence/ownership/failure/configuration/migration/diagnostic/source-map
spec; world renderer, module registry and `RENDER.md` updated.
- Integration: accepted in master merge `606770c`; post-merge entity/facade,
character, internal-access, manifest, shutdown, scheduler and planner gates passed.
@@ -0,0 +1,120 @@
# M03-RND-FACADE-ENVIRONMENT-001 — Renderer environment snapshot boundary
<!-- OPENWC_CLAIM:M03-RND-FACADE-ENVIRONMENT-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-facade-environment`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Add an immutable world-environment snapshot accepted by `WorldRenderFacade`,
delegate it to the existing DBC sky controller, and migrate deterministic render
checkpoint time setup away from direct sky-controller property mutation.
## Non-goals
- Change Light/Area/Skybox DBC selection, interpolation, fog, sun or shader rules.
- Add weather, indoor lighting, new clock semantics or build-12340 parity claims.
- Extract the sky service or move its Environment/Node/RID ownership.
- Change runtime scene defaults, cache formats, streaming behavior or target status.
## Paths
- Exclusive: `src/render/environment/world_environment_snapshot.gd`,
`src/tools/verify_world_environment_snapshot.gd`, this claim
- Shared/hotspots: `src/render/world_render_facade.gd`,
`src/scenes/sky/wow_sky_controller.gd`, both runtime streaming scenes,
`src/tools/capture_render_checkpoints.gd`,
`src/tools/verify_world_render_facade.gd`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
proprietary renderer corpus
## Contracts and data
- Public API: immutable `WorldEnvironmentSnapshot` and
`WorldRenderFacade.apply_environment_snapshot`
- Snapshot input: finite time-of-day hours normalized into one 24-hour day
- Adapter: explicit `world_environment_controller_path`; facade/controller retain
no ownership of caller snapshot
- Schema/cache/coordinate contracts and runtime defaults: unchanged
- Consumer: deterministic renderer checkpoint capture; future world-state adapter
## Dependencies
- Requires: integrated M03 facade/ground-query master `cb103e1`
- Blocks: completion of the facade environment checklist slice and later sky extraction
- External state: none for contract tests; visual comparison still needs proprietary corpus
## Verification
- Commands: dedicated cold snapshot contract, facade delegation/wiring regression,
capture source-boundary regression, renderer manifest/dry-run and repository gates
- Fixtures: normalization, non-finite rejection, missing/wrong controller,
exact snapshot delegation and existing runtime scene wiring
- Fidelity evidence: preserve existing fixed-time application and all DBC-derived
visual logic; seven checkpoint plans remain valid
- Performance budget: one immutable value allocation per environment update and
constant-time main-thread delegation
## Documentation deliverables
- Inline public API docs: snapshot, facade adapter and sky-controller application
- Module specification: update existing world-renderer specification
- Data-flow diagram: snapshot producer through facade into DBC sky controller
- Sequence/state/dependency diagrams: application/failure path and ownership notes
- Source map/status updates: world-renderer module, module registry if needed, `RENDER.md`
## Simplicity and naming
- Important names introduced: `WorldEnvironmentSnapshot`, `apply_environment_snapshot`
- Simplest considered solution: one immutable time snapshot over the existing sky implementation
- Rejected complexity/abstractions: generic weather graph, duplicated lighting DTO,
event bus, service container or alternate Environment owner
- Unavoidable complexity and justification: facade resolves a separate scene-owned
sky controller because the streamer does not own outdoor environment rendering
- Measured optimization evidence: not applicable; bounded constant-time adapter
## Status
- State: ready-for-review
- Done: immutable value, facade/controller adapter, checkpoint migration,
regressions and renderer module documentation
- Next: M03 integrator reviews and merges `2ee647a`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-ENVIRONMENT-001:2ee647a -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-ENVIRONMENT-001:c69abd6 -->
## Handoff
- Commit: `2ee647a`
- Results: `WorldEnvironmentSnapshot` normalizes finite time into one day and
exposes non-finite failure explicitly. `WorldRenderFacade` delegates the
immutable value to the existing sky controller; checkpoint capture no longer
locates or mutates that controller directly.
- Verification: snapshot `contract=5`; facade `delegation=6 ground_contract=2
environment_contract=7 runtime_scenes=2 tools=3`; internal access `43/7/9/1`;
terrain, coordinate (`files=116 consumers=5`), manifest (`7/7`), shutdown,
scheduler (`20000` iterations), planner (`250` iterations), focus, editor parse,
dry-run capture, coordination, documentation and diff gates passed.
- Fidelity: applying a snapshot retains the previous three fixed-clock assignments
and leaves DBC profile/area/skybox lookup, interpolation, fog, sun, shader,
cache and streaming rules unchanged. Dry-run retained all seven plans and
reported dusk as `time=19.00`. No build-12340 parity claim is added.
- External/local inputs: ignored native DLL and generated ADT resource scripts
were copied from master for parse/dry-run only. Missing proprietary ADT/DBC/
character data produced expected degraded diagnostics.
- Remaining risks: the snapshot currently covers exact world time only; weather,
indoor state and asset-backed paired zone-transition evidence remain open.
- Documentation updated: inline value/facade/controller API plus world-renderer
API/input-output/data-flow/sequence/ownership/failure/capability/source-map and
`RENDER.md` implementation notes.
- Integration: accepted in master merge `c69abd6`; post-merge snapshot, facade,
internal-access, manifest, shutdown, scheduler, planner and focus gates passed.
@@ -0,0 +1,115 @@
# M03-RND-FACADE-GROUND-QUERY-001 — Rendered ground query facade
<!-- OPENWC_CLAIM:M03-RND-FACADE-GROUND-QUERY-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-facade-ground-query`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Expose typed ground-height sampling and a detached readiness diagnostic through
`WorldRenderFacade`, using already loaded render terrain meshes, and migrate the
terrain-height probe away from direct streamer state/queue access.
## Non-goals
- Replace the M02 gameplay-owned `TerrainQuery`/`AdtTerrainQuery` composition.
- Make rendered mesh sampling authoritative for movement, collision or server state.
- Add slopes, holes, liquid, WMO/M2 collision or async query callbacks.
- Change terrain meshes, streaming policy, queue order, cache formats or visuals.
- Complete environment/entity-visual facade contracts or mark M03 complete.
## Paths
- Exclusive: this claim
- Shared/hotspots: `src/render/world_render_facade.gd`,
`src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/probe_render_terrain_height.gd`,
`src/tools/verify_world_render_facade.gd`,
`src/tools/verify_renderer_internal_access.gd`, `docs/modules/world-renderer.md`,
`docs/modules/terrain-query.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resource scripts,
extracted renderer corpus and probe reports
## Contracts and data
- Input: immutable `GodotWorldPosition` in Godot world units
- Output: renderer-owned immutable `RenderedGroundSample` plus optional detached diagnostic Dictionary
- Sampling source: loaded quality or tile-LOD Mesh and its live tile transform
- M02 ADT query cache, renderer cache versions and coordinate contracts: unchanged
## Dependencies
- Requires: M02 terrain query contract and integrated M03 facade/access gate `53d84cc`
- Blocks: removal of renderer diagnostic internal-access exception and later terrain service extraction
- External state: asset-backed probe is optional; contract tests use a facade double
## Verification
- Commands: facade query/snapshot isolation contract, internal-access gate,
terrain-query regression, focus/coordinate/manifest/shutdown regressions and repository gates
- Fixtures: available/unavailable sample, detached diagnostic, probe source migration
- Fidelity evidence: mesh ray/nearest fallback algorithm moves unchanged from the probe
- Performance budget: on-demand diagnostic query only; no per-frame query added
## Documentation deliverables
- Inline facade/streamer API docs
- World-renderer API/data-flow/sequence/ownership/recovery/source-map updates
- Terrain-query backend status/ownership clarification and renderer implementation note
## Simplicity and naming
- Important names: `sample_ground_height`, `renderer_ground_query_snapshot`
- Simplest approach: one typed facade query over one existing diagnostic read model
- Rejected complexity: duplicate ADT cache, physics service, callback bus, query node,
generic spatial interface or new dependency
- Unavoidable complexity: triangle-mesh ray query retains the existing bounded
nearby fallback for seam/edge diagnostic behavior
## Status
- State: ready-for-review
- Done: added renderer-owned typed sample, facade query/snapshot, migrated terrain
probe, expanded internal-access/coordinate gates and updated module docs
- Next: M03 integrator reviews and merges `b697a89` plus cold-start fix `9408d88`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-FACADE-GROUND-QUERY-001:9408d88 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-FACADE-GROUND-QUERY-001:d6e5b53 -->
## Handoff
- Commits: implementation `b697a89`; cold-start fix `9408d88`
- Results: `WorldRenderFacade` delegates loaded-mesh ground sampling and returns
renderer-owned `RenderedGroundSample`; detached diagnostic snapshots preserve
the probe report while hiding queue/tile/Mesh/Node references. Gameplay remains
on the independent M02 `TerrainQuery` composition.
- Verification: facade `delegation=5 ground_contract=2 runtime_scenes=2 tools=3`;
cold rendered-ground value `contract=3` from an empty Godot class cache;
internal access `private_symbols=43 gameplay=7 editor_sources=9 renderer_tools=1`;
terrain query contracts passed; coordinate boundary `files=113 consumers=5`;
renderer manifest `7/7`; shutdown ownership, scheduler/planner, coordination,
documentation and diff gates passed.
- Fidelity: the probe's 5000-unit ray height, 3x3 tile search, highest-hit choice,
readiness fields and `2/5/10/20/40` nearby fallback moved unchanged. No terrain
geometry, streaming, cache, material, placement or visual rule changed. No
build-12340 parity claim is added.
- External/local inputs: ignored native DLL and generated ADT resource scripts
were copied from master only for editor/streamer parse and then removed.
Proprietary extracted world data was unavailable, producing expected degraded diagnostics.
- Remaining risks: triangle-mesh generation is on-demand/main-thread and diagnostic;
it is unsuitable for per-physics-frame gameplay; exact asset-backed probe output
still depends on local corpus readiness; environment/entity facade contracts remain.
- Documentation: inline facade/loader/value API; world-renderer API/data-flow/
sequence/ownership/recovery/capability/source map; terrain-query authority
clarification and renderer implementation notes updated.
- Integration: implementation accepted in master merge `80cb084`; cold-start fix
accepted in merge `d6e5b53`. Post-merge cold/facade/access/terrain/coordinate/
manifest/shutdown/scheduler/planner/focus and repository gates passed.
@@ -0,0 +1,106 @@
# M03-RND-INTERNAL-ACCESS-GATE-001 — Streamer internal access gate
<!-- OPENWC_CLAIM:M03-RND-INTERNAL-ACCESS-GATE-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-streamer-internal-access-gate`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Add an automated repository boundary gate proving gameplay code and Godot
`EditorPlugin` implementations do not access `StreamingWorldLoader` private
queue, task, cache or tile-state fields.
## Non-goals
- Change renderer behavior, queues, jobs, cache formats or facade contracts.
- Ban scene composition references to the loader script or facade adapter path.
- Migrate renderer-only diagnostic probes that inspect internal readiness/terrain;
those require the later metrics/ground-query facade contract.
- Mark M03 complete.
## Paths
- Exclusive: `src/tools/verify_renderer_internal_access.gd`, this claim
- Shared/hotspots: `docs/modules/world-renderer.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Read-only scan scope: `src/gameplay/**/*.gd`, `src/editor/**/*.gd`, Godot
`EditorPlugin` package scripts under `src/**/*.gd` and `addons/**/*.gd`, and
the streamer field declarations
## Contracts and data
- Input: repository GDScript source and actual private streamer declarations
- Output: deterministic pass/fail diagnostics with consumer path and symbol
- Runtime, scene, cache, coordinate and asset contracts: unchanged
## Dependencies
- Requires: integrated facade and scheduler master `a023d76`
- Blocks: safe service extraction and M03 dependency regression acceptance
- External state: none
## Verification
- Commands: dedicated internal-access gate, facade regression, coordination,
documentation and diff gates
- Fixtures: gameplay source discovery, EditorPlugin discovery and synthetic
forbidden symbol detection
- Fidelity evidence: source-only enforcement; no runtime or visual code changes
- Performance budget: one bounded repository source scan in a headless test process
## Documentation deliverables
- World-renderer boundary/verification/known-gap/source-map updates
- Renderer implementation note documenting diagnostic-tool exception
- No new module spec because this package adds a verifier to the existing renderer module
## Simplicity and naming
- Important name: `verify_renderer_internal_access.gd`
- Simplest approach: derive forbidden fields from the current streamer instead
of maintaining a second hand-written queue list
- Rejected complexity: language plugin, custom linter framework or runtime proxy
- Unavoidable complexity: recursive source discovery expands each actual
`EditorPlugin` root to its package scripts while excluding reference mirrors
## Status
- State: ready-for-review
- Done: derived 43 private renderer symbols, scanned seven gameplay and nine
EditorPlugin-package sources, added synthetic positive/negative fixtures and docs
- Next: M03 integrator reviews and merges `b58bf2c`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-INTERNAL-ACCESS-GATE-001:b58bf2c -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-INTERNAL-ACCESS-GATE-001:7e35de7 -->
## Handoff
- Commit: `b58bf2c`
- Results: repository gate derives private queue/task/cache/state declarations
from the streamer and rejects external member/reflection access in gameplay and
EditorPlugin package sources. Current scope passes with `43/7/9` inventory counts.
- Verification: internal access, facade and budget scheduler headless contracts;
Godot editor scan with local ignored dependencies; coordination, documentation
and diff gates passed.
- Fidelity: source-only enforcement changes no runtime configuration, queue order,
cache version, placement, material, animation or visibility behavior. No
build-12340 parity claim is added.
- External/local inputs: ignored native DLL and two generated ADT resource scripts
were copied from master only for facade/editor smoke and then removed. Proprietary
extracted world data was unavailable, producing expected degraded-data diagnostics.
- Remaining risks: renderer diagnostic terrain probe remains an explicit exception;
whitespace-obfuscated dynamic reflection is outside the lightweight source gate;
future non-EditorPlugin authoring code is already prohibited by repository policy.
- Documentation: world-renderer boundary/verification/capability/known-gap/source
map and `RENDER.md` implementation note updated; no new module was introduced.
- Integration: accepted in master merge `7e35de7`; post-merge access gate
`private_symbols=43 gameplay=7 editor_sources=9`, facade/scheduler/planner and
repository gates passed.
@@ -0,0 +1,104 @@
# M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-animated-instance-materializer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animated-instance-materializer`
## Outcome
Extract main-thread animated M2 instance duplication, render-property application,
playback startup and batch attachment from `StreamingWorldLoader`.
## Non-goals
- Change build-job cursors, budgets, prototype selection or animation eligibility.
- Change animation selection, phase, native animator behavior or diagnostic text.
- Own Editor scene persistence, static MultiMesh materialization or tile cleanup.
- Change visible transforms, visibility ranges, shadows or placement ordering.
## Paths
- Exclusive: `src/render/m2/m2_animated_instance_materializer.gd`,
`src/tools/verify_m2_animated_instance_materializer.gd`,
`docs/modules/m2-animated-instance-materializer.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 animation
playback/finalizer/build/facade/internal-access/manifest verifiers and specs,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- Duplicate flags, names, transforms and input order remain unchanged.
- Visibility end/margin and shadow mode are applied recursively before attachment.
- Playback data copy/start delegates to the accepted playback controller contract.
- The batch is attached only when at least one duplicate succeeds.
- Loader retains diagnostic formatting/path normalization and Editor ownership.
## Dependencies
- Requires: accepted playback package on master `5b2d9f2`
- Blocks: further animated M2 build orchestration extraction
- External state: Godot Node duplication and SceneTree attachment remain main-thread APIs
## Verification
- Commands: dedicated duplication/order/render/playback/empty/source/timing verifier;
M2 animated pipeline/finalizer/playback/prototype/build/shutdown/material/facade/
internal-access/manifest regressions; docs/coordination/dry-run gates
- Fixtures: synthetic prototype trees with geometry, AnimationPlayer and native animator
- Fidelity evidence: exact transforms, flags, render settings, playback delegation and order
- Performance budget: 1,000 synthetic instances under 1 second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent playback/finalizer/build/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2AnimatedInstanceMaterializer`
- Simplest solution: synchronous main-thread RefCounted returning batch diagnostics
- Rejected complexity: signals, async queue, callbacks and generic scene factory
- Unavoidable complexity: imported AnimationPlayer and native animator paths coexist
- Measured optimization evidence: bounded synthetic batch materialization
## Status
- State: ready for integration
- Done: materializer, loader adapter, verifier and required documentation
- Next: integrator merge and post-merge acceptance
- Blocked by:
## Handoff
- Commit: `456f3e2`
- Results: materializer passes 12 duplication/order/render/playback/diagnostic/
source cases and 1,000 instances in 9.267 ms; all 50 autonomous headless
verifiers pass, while the proprietary ADT placement probe is unavailable
without `data/extracted`; checkpoint dry-run passes 7/7; documentation and
coordination gates pass; loader-private inventory remains 30.
- Remaining risks: private asset traversal/visual comparison, descriptor
pressure, leaks and p95/p99 remain unavailable; materialization is still
synchronous main-thread work and no original-client comparison is claimed.
- Documentation updated: inline API; `m2-animated-instance-materializer.md`
with data-flow, lifecycle, sequence and dependency diagrams; playback,
finalizer, build, world-renderer specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:456f3e2 -->
## Integration
- Merge commit: `c251985`
- Post-merge results: materializer `cases=12 instances=1000 elapsed_ms=13.719`;
playback/finalizer, shutdown, facade, internal-access `30`, checkpoint dry-run
`7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001:c251985 -->
@@ -0,0 +1,110 @@
# M03-RND-M2-ANIMATED-SCENE-FINALIZER-001
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-animated-scene-finalizer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animated-scene-finalizer`
## Outcome
Extract animated M2 PackedScene candidate instantiation, material override repair
and AnimationPlayer validation from `StreamingWorldLoader` into an explicit
main-thread finalizer.
## Non-goals
- Call ResourceLoader or interpret threaded-load statuses.
- Choose animation eligibility, cache paths, material prototypes or permits.
- Adopt/cache prototype Nodes or change static/native animation paths.
- Change visible materials, animation selection, cache formats or profiles.
## Paths
- Exclusive: `src/render/m2/m2_animated_scene_finalizer.gd`,
`src/tools/verify_m2_animated_scene_finalizer.gd`,
`docs/modules/m2-animated-scene-finalizer.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
animation pipeline/prototype/material/build/shutdown/facade/internal-access/
manifest verifiers and module specs, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- Only a PackedScene whose instantiated root is Node3D becomes a candidate;
unsupported or invalid roots return null and any created invalid root is freed.
- Material repair preserves depth-first MeshInstance3D order, source-index
clamping, per-surface override priority and first-material fallback.
- Finalization accepts a candidate only when at least one descendant
AnimationPlayer exists; rejected detached candidates are freed synchronously.
- Accepted result transfers the exact Node3D and player count to the loader.
- Loader retains ResourceLoader, material-prototype acquisition, permit,
prototype adoption/static fallback and diagnostic-log decisions.
## Dependencies
- Requires: accepted animation load pipeline package on master `c20bc62`
- Blocks: further animated M2 build/materialization decomposition
- External state: imported GLB/PackedScene shape remains engine-owned input
## Verification
- Commands: dedicated invalid/ownership/traversal/material/finalization/source/
timing verifier; animation pipeline/prototype/shutdown/material/M2 build/facade/
internal-access/manifest and adjacent M2 regressions; docs/coordination/dry-run gates
- Fixtures: synthetic PackedScenes, nested AnimationPlayers and ArrayMesh materials
- Fidelity evidence: exact material mapping and accepted/rejected Node transitions
- Performance budget: 10,000 depth-first candidate queries under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent animation/prototype/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2AnimatedSceneFinalizer`
- Simplest solution: one stateless main-thread RefCounted over engine scene types
- Rejected complexity: callbacks, generic scene pipeline, signals or cache ownership
- Unavoidable complexity: material repair occurs between candidate creation and acceptance
- Measured optimization evidence: bounded synthetic subtree traversal
## Status
- State: accepted
- Done: finalizer, loader adapters, lifecycle/material verifier and required documentation
- Next: continue animated M2 playback/instance materialization extraction
- Blocked by:
## Handoff
- Commit: `1b450dc`
- Results: animated scene finalizer passes 13 invalid/type/lifetime/traversal/
material/transfer/source cases and 10,000 depth-eight queries in 40.126 ms;
all 48 autonomous headless verifiers pass, while the proprietary ADT placement
probe is unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
documentation and coordination gates pass; loader-private inventory remains 30.
- Remaining risks: proprietary GLB traversal, material/animation comparison,
descriptor pressure, leaks and p95/p99 remain unavailable; no paired original-
client visual comparison is claimed. Positional material mapping is unchanged.
- Documentation updated: inline API; `m2-animated-scene-finalizer.md` with
data-flow, lifecycle, sequence and dependency diagrams; animation pipeline,
prototype/world-renderer specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:1b450dc -->
## Integration
- Merge commit: `e7a7c67`
- Post-merge results: finalizer `cases=13 iterations=10000
elapsed_ms=40.314`; animation pipeline, prototype state, shutdown, materials,
facade, internal-access `30`, manifest `7/7`, documentation and coordination passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATED-SCENE-FINALIZER-001:e7a7c67 -->
@@ -0,0 +1,112 @@
# M03-RND-M2-ANIMATION-LOAD-PIPELINE-001 — M2 animation load pipeline state
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-animation-load-pipeline`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract threaded animated-M2 pending request records and completion-order
finalize FIFO from `StreamingWorldLoader` into an explicit state service.
## Non-goals
- Call ResourceLoader, select/validate GLB paths or interpret status values.
- Instantiate/repair/cache animated Nodes or own prototype outcomes.
- Change animation allow/deny/candidate policy, permits or visible behavior.
- Add cancellation, persistence, retries, dependencies or cache-format changes.
## Paths
- Exclusive: `src/render/m2/m2_animation_load_pipeline_state.gd`,
`src/tools/verify_m2_animation_load_pipeline_state.gd`,
`docs/modules/m2-animation-load-pipeline-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
prototype/shutdown/material/build/facade/internal-access/manifest verifiers
and module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
`RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- One non-empty normalized path has at most one pending cache path.
- Pending snapshots preserve Dictionary insertion order and detach records.
- Completing a known request moves one copied record with opaque status into a
completion-order FIFO; unknown/duplicate completion is rejected.
- Defensive discard removes pending state without creating a finalize record.
- Total work remains pending plus finalize count at all three historical metrics.
- Map reset and shutdown retain the same two clear sites; shutdown still drains
ResourceLoader paths before clear.
## Dependencies
- Requires: accepted prototype cache package on master `20a64b5`
- Blocks: animated M2 finalization/materialization service extraction
- External state: ResourceLoader request statuses remain loader-owned
## Verification
- Commands: dedicated validation/dedupe/order/transition/discard/snapshot/clear/
source/timing verifier; prototype/shutdown/material/M2 build/facade/internal-
access/manifest and adjacent M2 regressions; docs/coordination/dry-run gates
- Fixtures: synthetic normalized/cache paths and opaque integer statuses
- Fidelity evidence: request insertion, terminal FIFO, metrics and clear transitions
- Performance budget: 100 cycles over 256 request completions under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent M2/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2AnimationLoadPipelineState`
- Simplest solution: one main-thread RefCounted with pending map and finalize FIFO
- Rejected complexity: generic Resource pipeline, signals, callbacks, typed
ResourceLoader enum wrapper, Node ownership or async abstraction
- Unavoidable complexity: pending insertion order and terminal completion order differ
- Measured optimization evidence: bounded synthetic transition loop
## Status
- State: accepted
- Done: state, loader adapters, lifecycle verifier and required documentation
- Next: continue animated M2 finalization/materialization extraction
- Blocked by:
## Handoff
- Commit: `b8e0f11`
- Results: animation pipeline passes 11 validation/dedupe/order/transition/FIFO/
discard/snapshot/source cases and 100-by-256 cycles in 69.798 ms; all 47
autonomous headless verifiers pass, while the proprietary ADT placement probe
is unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
documentation and coordination gates pass; loader-private inventory decreases
from 31 to 30.
- Remaining risks: successful animated-GLB traversal, descriptor pressure,
leaks, animation fidelity and p95/p99 require proprietary data; no paired
original-client visual comparison is claimed.
- Documentation updated: inline API; `m2-animation-load-pipeline-state.md` with
data-flow, lifecycle, sequence and dependency diagrams; prototype/world-renderer
specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:b8e0f11 -->
## Integration
- Merge commit: `3dccd3e`
- Post-merge results: animation pipeline `cases=11 iterations=100
elapsed_ms=70.309`; prototype state, shutdown, materials, facade,
internal-access `30`, manifest `7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATION-LOAD-PIPELINE-001:3dccd3e -->
@@ -0,0 +1,110 @@
# M03-RND-M2-ANIMATION-PLAYBACK-001
<!-- OPENWC_CLAIM:M03-RND-M2-ANIMATION-PLAYBACK-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-animation-playback`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-animation-playback`
## Outcome
Extract deterministic animated M2 playback selection/phase, AnimationPlayer
mutation and native animator data/start behavior from `StreamingWorldLoader`.
## Non-goals
- Duplicate or attach animated instances/batch roots.
- Own visibility, shadow, Editor owner or build-job cursor rules.
- Load/finalize/cache scenes or change animation eligibility.
- Change candidate priority, phase formula, logging text or visible playback.
## Paths
- Exclusive: `src/render/m2/m2_animation_playback_controller.gd`,
`src/tools/verify_m2_animation_playback_controller.gd`,
`docs/modules/m2-animation-playback-controller.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 animated
finalizer/build/prototype/material/facade/internal-access/manifest verifiers and
specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- Phase remains `abs(hash("path:index")) % 1000 / 1000.0`.
- Exact animation-name priority remains ordinary Stand/Idle/Run/Walk, fish
Run/Walk/Swim/Stand/Idle/Death, birds Run/Walk/Swim/Stand/Idle, then
case-insensitive substring fallback and first-name fallback.
- Every AnimationPlayer animation becomes linear-looping before selected play;
positive-length selection seeks to deterministic phase.
- Native animators match exact script identity in depth-first order, copy the
same five fields, call prepare_runtime when available and set phase.
- Runtime debug state is sampled only when existing debug mode requests it;
loader retains final log formatting/path normalization.
## Dependencies
- Requires: accepted animated scene finalizer package on master `c0a7bfb`
- Blocks: animated M2 instance materializer extraction
- External state: AnimationPlayer and M2NativeAnimator APIs remain engine/scene contracts
## Verification
- Commands: dedicated phase/selection/loop/play/seek/native-copy/start/diagnostic/
source/timing verifier; animated finalizer/pipeline/prototype/build/shutdown/
material/facade/internal-access/manifest regressions; docs/coordination/dry-run gates
- Fixtures: synthetic AnimationPlayers and real M2NativeAnimator nodes
- Fidelity evidence: exact priority, phase and mutation/reference contracts
- Performance budget: 20,000 selection/phase operations under 1 second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent finalizer/build/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2AnimationPlaybackController`
- Simplest solution: stateless main-thread RefCounted over supplied Nodes/players
- Rejected complexity: signals, playback state machine, generic animation framework
- Unavoidable complexity: native and imported AnimationPlayer paths coexist
- Measured optimization evidence: bounded selection/phase loop
## Status
- State: accepted
- Done: playback controller, loader adapters, verifier and required documentation
- Next: extract animated M2 instance/batch materialization
- Blocked by:
## Handoff
- Commit: `9b73571`
- Results: playback controller passes 15 phase/selection/loop/play/seek/native-
copy/start/diagnostic/source cases and 20,000 phase-selection pairs in
23.415 ms; all 49 autonomous headless verifiers pass, while the proprietary
ADT placement probe is unavailable without `data/extracted`; checkpoint
dry-run passes 7/7; documentation and coordination gates pass; loader-private
inventory remains 30.
- Remaining risks: private animation-name/timing/native visual comparison,
descriptor pressure, leaks and p95/p99 remain unavailable; default selection
is still heuristic and no paired original-client comparison is claimed.
- Documentation updated: inline API; `m2-animation-playback-controller.md` with
data-flow, lifecycle, sequence and dependency diagrams; finalizer/build/world-
renderer specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-ANIMATION-PLAYBACK-001:9b73571 -->
## Integration
- Merge commit: `c0fc191`
- Post-merge results: playback `cases=15 iterations=20000 elapsed_ms=23.008`;
animated finalizer/pipeline, prototype, build, shutdown, materials, facade,
internal-access `30`, manifest `7/7`, documentation and coordination passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-ANIMATION-PLAYBACK-001:c0fc191 -->
@@ -0,0 +1,110 @@
# M03-RND-M2-BUILD-BATCH-PLANNER-001 — M2 build batch planner
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-BATCH-PLANNER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-build-batch-planner`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract pure static/animated M2 build batch sizing and cursor progression from
the loader build-job loop into a scene-free planner.
## Non-goals
- Move build-job/queue ownership, tile eligibility or cancellation.
- Move animation/mesh requests, cache lookup, queue rotation or retry behavior.
- Create Nodes, MultiMesh, Mesh, materials or RIDs.
- Change render-budget permit consumption or per-profile batch limits.
- Introduce spatial-cell batching or claim asset-backed performance parity.
## Paths
- Exclusive: `src/render/m2/m2_build_batch_planner.gd`,
`src/tools/verify_m2_build_batch_planner.gd`,
`docs/modules/m2-build-batch-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/m2-placement-grouper.md`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: transform count, current offset, animated/static path selector and raw
animated/static batch limits
- Output: fresh plan Dictionary with effective batch size, batch count, next
offset and group-complete flag
- Selected batch limit clamps to at least one
- Batch count clamps to remaining non-negative transforms
- Group completes when no batch can be consumed or offset plus batch reaches end
- Queue/cache/schema/coordinate/profile values: unchanged
## Dependencies
- Requires: current master `9d74d99`; grouped transform arrays from
`M2PlacementGrouper`
- Blocks: broader M2 build-job state extraction
- External state: none; tests use scalar synthetic counts/limits
## Verification
- Commands: dedicated static/animated/limit/remaining/complete/empty/offset and
bounded timing verifier; loader source boundary; grouper/transform/registry plus
facade/internal-access/manifest/shutdown/documentation/coordination gates
- Fidelity evidence: exact batch/count/cursor formulas moved unchanged
- Performance budget: constant-time scalar/container calculation; no Node,
Resource, RID, task, queue, mutex, cache or file access
## Documentation deliverables
- Inline planner API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
performance, fidelity limitations and source map
- Grouper, renderer/module registry and `RENDER.md` updates
## Simplicity and naming
- Important name: `M2BuildBatchPlanner`
- Simplest solution: one stateless `plan_batch` query with a small detached result
- Rejected complexity: job classes, state machine framework, callbacks, signals,
Resources, strategies or scheduler integration
- Unavoidable complexity: loader retains raw build-job Dictionaries
- Measured optimization evidence: bounded synthetic planning loop
## Status
- State: accepted
- Done: planner, loader adapter, regressions and documentation verified
- Next: M2 queue/resource state extraction and spatial-cell evidence
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-BUILD-BATCH-PLANNER-001:be6db7f -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-BUILD-BATCH-PLANNER-001:3f84717 -->
## Handoff
- Commit: `be6db7f` (`refactor(M03): extract M2 build batch planner`)
- Results: dedicated planner PASS (`cases=8`, `iterations=20000`,
`elapsed_ms=13.464`); grouper, transform resolver, M2 registry/dedupe, terrain
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
editor import parsed with only expected missing private corpus diagnostics;
documentation PASS (`module_specs=18`, `required_files=7`); coordination PASS
with 23 pre-existing expired M00/M01 warnings; checkpoint dry-run planned `7/7`.
- Remaining risks: no private asset-backed p95/p99 or visual comparison; result
and loader job remain Dictionaries; resource/queue transitions and spatial-cell
batching are still loader-owned; negative offset behavior remains permissive.
- Documentation updated: inline public API; new
`docs/modules/m2-build-batch-planner.md`; placement grouper module spec,
renderer module registry, `docs/modules/world-renderer.md` and `RENDER.md`.
- Integration: accepted in master merge `3f84717`; post-merge planner
(`cases=8`, `iterations=20000`, `elapsed_ms=14.532`), grouper, transform
resolver, registry, internal-access, facade, manifest, documentation and
coordination gates passed.
@@ -0,0 +1,97 @@
# M03-RND-M2-BUILD-DISPATCH-PLANNER-001
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-DISPATCH-PLANNER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-build-dispatch-planner`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-dispatch-planner`
## Outcome
Extract the pure M2 build-step dispatch decision from the loader while preserving
animation-wait priority, static-resource retry, missing-model advancement and
exact permit/cursor behavior.
## Non-goals
- Move ResourceLoader I/O, path lookup, cache state or retry ownership.
- Create/free Nodes, Meshes, MultiMeshes or animated instances in the planner.
- Change batch sizing, queue ordering, transforms, visuals or cache versions.
- Add spatial-cell batching or new diagnostics/profile settings.
## Paths
- Exclusive: `src/render/m2/m2_build_dispatch_planner.gd`,
`src/tools/verify_m2_build_dispatch_planner.gd`,
`docs/modules/m2-build-dispatch-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
queue/materializer/cache verifiers and specs, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- Pending animation request wins before empty/animated/static decisions.
- Positive batch with animated prototype selects animated materialization.
- Positive static batch selects prepared Mesh, waits when unresolved, and advances
without materialization only when the model is terminally missing.
- Zero/non-positive batch advances without materialization.
- Planner returns a fresh action plan and owns no state or engine reference.
## Dependencies
- Requires: accepted typed M2 build queue on master `c74b90a`
- Blocks: extracting stateful M2 resource readiness/dispatch orchestration
- External state: cache lookups, requests and engine lifetime remain loader APIs
## Verification
- Commands: dedicated action-priority/matrix/fresh-result/source/timing verifier;
M2 queue/planner/materializer/cache/prototype/shutdown/facade/internal-access/
manifest regressions; documentation, coordination and checkpoint dry-run gates
- Fixtures: scalar availability and terminal-state combinations only
- Fidelity evidence: exact existing branch priority and retry/advance behavior
- Performance budget: 20,000 pure dispatch plans under one second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent M2 build/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name: `M2BuildDispatchPlanner`
- Simplest solution: one stateless RefCounted with explicit action constants
- Rejected complexity: callbacks, signals, cache references and generic state machine
- Unavoidable complexity: animation-pending priority precedes every other action
- Measured optimization evidence: bounded synthetic decision loop
## Status
- State: integration accepted
- Done: implementation, verification, documentation and worktree handoff
- Next: extract typed M2 resource observation/request orchestration
- Blocked by:
## Handoff
- Commit: `17f5cc0faa376c8ac84a7ac72d5b8c0366866980`
- Results: dispatch planner `cases=10 iterations=20000 elapsed_ms=11.933`;
all autonomous headless regressions `53/53`; internal access
`private_symbols=30`; baseline manifest/dry-run `7/7`; documentation
`module_specs=40`; coordination passed with 30 historical expired warnings.
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
resource observation/requests, action execution, permits and engine lifetime
intentionally remain loader-owned.
- Documentation updated: new `m2-build-dispatch-planner.md` with API/I/O,
data-flow, lifecycle, sequence, dependency and ownership diagrams; batch/queue/
world renderer specs, module registry, RENDER and M03 Evidence updated.
- Integration: master merge `99a90ddfb3364d6672198887198602b84dd19e58`;
post-merge dispatch planner `cases=10 iterations=20000 elapsed_ms=12.291`,
twelve adjacent M2/resource/lifetime/facade/internal-access/manifest checks,
documentation and coordination all passed; checkpoint dry-run retained `7/7` plans.
@@ -0,0 +1,98 @@
# M03-RND-M2-BUILD-QUEUE-001
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-QUEUE-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-build-queue`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-queue`
## Outcome
Replace loader-owned raw M2 build-job Dictionary/FIFO fields with typed job and
queue state while preserving cursor, rotation, stale-key and lifetime behavior.
## Non-goals
- Move tile eligibility, resource readiness/retry or scheduler policy.
- Create/free Nodes, Meshes, MultiMeshes or animated instances inside queue state.
- Change grouping, batch planning, materialization, transform order or visuals.
- Change M2 cache versions, diagnostics text or Editor persistence.
## Paths
- Exclusive: `src/render/m2/m2_build_job.gd`,
`src/render/m2/m2_build_queue.gd`,
`src/tools/verify_m2_build_queue.gd`,
`docs/modules/m2-build-queue.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
materializer/cache/shutdown/facade/internal-access verifiers and specs,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- A job retains tile key, root, exact groups Dictionary, insertion-order
`groups.keys()` snapshot, group index, transform offset and batch serial.
- FIFO keeps duplicate/stale keys; front rotation moves exactly one key to tail.
- Job erase does not erase FIFO entries, preserving external-cancel stale cleanup.
- Progress adopts group index, transform offset and serial atomically.
- Queue release never frees engine objects; loader retains finish/cancel cleanup.
## Dependencies
- Requires: accepted static materializer package on master `81e33a9`
- Blocks: extracting remaining M2 build/resource orchestration
- External state: Node validity and SceneTree destruction remain loader APIs
## Verification
- Commands: dedicated invalid/FIFO/duplicate/stale/rotation/cursor/lifetime/
diagnostics/source/timing verifier; M2 planner/materializer/cache/prototype/
shutdown/facade/internal-access/manifest regressions; docs/coordination/dry-run gates
- Fixtures: synthetic Node3D roots and ordered transform-group Dictionaries
- Fidelity evidence: exact existing keyed state, FIFO and cursor transitions
- Performance budget: 100 cycles of 256 enqueue/rotate/erase operations under one second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent build/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important names: `M2BuildJob`, `M2BuildQueue`
- Simplest solution: two small RefCounted types mirroring accepted WMO queue pattern
- Rejected complexity: signals, generic queue base, callbacks and engine destruction
- Unavoidable complexity: keyed jobs and FIFO keys intentionally permit stale entries
- Measured optimization evidence: bounded synthetic lifecycle loop
## Status
- State: integration accepted
- Done: implementation, verification, documentation and worktree handoff
- Next: extract the next unclaimed M2 resource-dispatch seam
- Blocked by:
## Handoff
- Commit: `4a8338f2f0503c2ba31c495e00a696d359518c06`
- Results: cold editor parse `exit=0`; M2 build queue `cases=13 iterations=100
elapsed_ms=66.247`; all autonomous headless regressions `52/52`; internal
access `private_symbols=30`; baseline manifest/dry-run `7/7`; documentation
`module_specs=39`; coordination passed with 30 historical expired warnings.
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
resource readiness/dispatch, permits, materializer choice and root destruction
intentionally remain loader-owned.
- Documentation updated: new `m2-build-queue.md` with API/I/O, data-flow,
lifecycle, sequence, dependency and ownership diagrams; world renderer,
batch planner, module registry, RENDER and M03 Evidence updated.
- Integration: master merge `1dc013e5c2597d5e6f26f44807f5f0a943bf1517`;
post-merge queue `cases=13 iterations=100 elapsed_ms=65.998`, ten adjacent
M2/lifetime/facade/internal-access/manifest checks, documentation and
coordination all passed; checkpoint dry-run retained `7/7` plans.
@@ -0,0 +1,98 @@
# M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001
<!-- OPENWC_CLAIM:M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-build-resource-snapshot`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-build-resource-snapshot`
## Outcome
Replace loose per-step M2 animated/static resource variables with a typed
observation snapshot consumed by the dispatch planner and materializer adapter.
## Non-goals
- Move ResourceLoader I/O, cache lookup/request execution or retry ownership.
- Own or free prototype Nodes or Mesh Resources.
- Change dispatch priority, batch sizing, queue ordering, permits or visuals.
- Change M2 cache paths, formats, allowlists or native animation selection.
## Paths
- Exclusive: `src/render/m2/m2_build_resource_snapshot.gd`,
`src/tools/verify_m2_build_resource_snapshot.gd`,
`docs/modules/m2-build-resource-snapshot.md`, this claim
- Shared/hotspots: `src/render/m2/m2_build_dispatch_planner.gd`, its verifier/spec,
`src/scenes/streaming/streaming_world_loader.gd`, M2 queue/materializer/cache
specs and tests, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
`RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- Snapshot retains normalized path, optional animated prototype, animation
request state, optional static Mesh and terminal missing-model state.
- Static observation is adopted only after animation is not pending, the batch
is positive and no animated prototype exists.
- Accessors borrow exact engine references without transferring/freeing ownership.
- Diagnostics expose only path and scalar availability flags.
## Dependencies
- Requires: accepted M2 dispatch planner on master `b113db0`
- Blocks: extracting M2 resource observation/request service ownership
- External state: prototype/cache/pipeline/resource lifetime remains loader-owned
## Verification
- Commands: dedicated identity/adoption/diagnostic/lifetime/source/timing verifier;
dispatch/queue/batch/materializer/cache/pipeline/shutdown/facade/internal-access/
manifest regressions; documentation, coordination and checkpoint dry-run gates
- Fixtures: synthetic Node3D prototype and ArrayMesh resources
- Fidelity evidence: exact resource presence/pending/missing values and branch order
- Performance budget: 20,000 snapshot construct/adopt/read cycles under one second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent dispatch/queue/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name: `M2BuildResourceSnapshot`
- Simplest solution: one small RefCounted value holder with explicit accessors
- Rejected complexity: generic resource union, signals, callbacks and cache ownership
- Unavoidable complexity: two-phase animation then optional static observation
- Measured optimization evidence: bounded synthetic lifecycle loop
## Status
- State: integration accepted
- Done: implementation, verification, documentation and worktree handoff
- Next: extract the M2 resource observation/request producer service
- Blocked by:
## Handoff
- Commit: `ec1b90f1e44037572e3e5052e6dedc016c29859a`
- Results: resource snapshot `cases=12 iterations=20000 elapsed_ms=42.995`;
dispatch planner remained green; all autonomous headless regressions `54/54`;
internal access `private_symbols=30`; baseline manifest/dry-run `7/7`;
documentation `module_specs=41`; coordination passed with 30 historical warnings.
- Remaining risks: proprietary ADT visual/p95/p99 evidence is unavailable;
resource lookup/request execution, action execution, permits and engine lifetime
intentionally remain loader-owned.
- Documentation updated: new `m2-build-resource-snapshot.md` with API/I/O,
data-flow, lifecycle, sequence, dependency and ownership diagrams; dispatch/
queue/world renderer specs, module registry, RENDER and M03 Evidence updated.
- Integration: master merge `4354834c50ae426640c721041af67d5265e82205`;
post-merge snapshot `cases=12 iterations=20000 elapsed_ms=42.211`, dispatch
planner and twelve adjacent resource/materializer/lifetime/facade/internal-
access/manifest checks, documentation and coordination all passed; checkpoint
dry-run retained `7/7` plans.
@@ -0,0 +1,91 @@
# M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001
<!-- OPENWC_CLAIM:M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-cached-animation-resource-observer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-cached-animation-resource-observer`
## Outcome
Move cached-GLB animated M2 lookup, allow/deny and primitive/schema safety
selection, threaded request admission and static-only transition from the loader
into a service that produces `M2BuildResourceSnapshot`.
## Non-goals
- Move native GryphonRoost raw-data build, debug logging or finalize drains.
- Own/free prototype Nodes or change cache/pipeline ownership.
- Change allow/deny defaults, primitive limits, candidate order or GLB rules.
- Change batching, permits, cache formats, profiles or visible output.
## Paths
- Exclusive: `src/render/m2/m2_cached_animation_resource_observer.gd`,
`src/tools/verify_m2_cached_animation_resource_observer.gd`,
`docs/modules/m2-cached-animation-resource-observer.md`, this claim
- Shared: loader, M2 snapshot/animation pipeline/prototype specs and verifiers,
world renderer, module registry, `RENDER.md` and M03 Evidence
## Contracts and data
- Cached animated prototype wins; static-only and pending states do not request.
- Empty allowlist rejects; denylist wins; matching is stripped/case-insensitive.
- Historical nested/lowercase/basename `.glb` order remains exact.
- Candidate requires animations, primitive limit and accepted empty or
`pivot_prefix_v1` schema; other schemas are rejected.
- Successful/`ERR_BUSY` request is remembered and reported pending; no usable
candidate or request failure marks animation static.
- Service produces a snapshot borrowing the exact prototype and frees nothing.
## Dependencies
- Requires: accepted static observer `5ebf4de`, snapshot and animation pipeline
- Blocks: native animation observation and remaining finalize extraction
## Verification
- Synthetic cache/pipeline/snapshot lifecycle, policy/path/GLB metadata fixtures,
source boundaries and timing; adjacent M2/renderer gates; full headless suite
and checkpoint dry-run.
## Documentation deliverables
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
diagrams; adjacent renderer docs and M03 Evidence.
## Status
- State: accepted
- Done: implementation, integration and post-merge acceptance
- Next: native M2 animation observation extraction
- Blocked by:
## Handoff
- Commit: `df87619` (`render: extract cached M2 animation resource observer`)
- Results: observer PASS (`33` cases, `20,000` iterations, `61.745ms`);
full autonomous suite PASS (`56/56`); renderer checkpoint dry-run PASS (`7/7`);
internal-access PASS (`30` private symbols); documentation PASS (`43` module
specs); coordination PASS with `34` historical warnings.
- Remaining risks: successful asynchronous admission is covered by source and
existing pipeline regressions because an undrained unit request leaks; generated
GLB metadata does not prove Godot import fidelity; private traversal, p95/p99,
leak and paired visual evidence remain unavailable; native observation and
terminal ResourceLoader polling/finalize remain loader-owned.
- Documentation updated: new cached observer API/I/O/data-flow/state/sequence/
dependency/ownership specification; M2 snapshot, batch/dispatch, animation
pipeline/prototype, world-renderer, module registry, `RENDER.md` and M03 Evidence.
## Integration
- Merge: `f79e064` (`merge: cached M2 animation resource observer`)
- Post-merge: observer `33` cases / `20,000` iterations / `61.514ms`;
animation pipeline `11` / `66.049ms`; snapshot `12` / `39.947ms`;
dispatch `10` / `17.915ms`; prototype cache `16` / `35.053ms`; shutdown,
facade, internal-access `30`, manifest `7/7`, documentation `43` and
coordination passed. Checkpoint dry-run completed `7/7`; expected private
Azeroth/character assets remained unavailable.
@@ -0,0 +1,109 @@
# M03-RND-M2-MESH-LOAD-PIPELINE-001 — M2 mesh load pipeline state
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-LOAD-PIPELINE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-mesh-load-pipeline`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract static M2 threaded ResourceLoader request records and their terminal
finalize FIFO from `StreamingWorldLoader`, preserving request order, status
transition, metrics and reset/shutdown behavior.
## Non-goals
- Call ResourceLoader or choose `.tscn`/`.glb` cache paths.
- Own M2 Mesh, scene, missing or material-refresh caches.
- Extract Mesh from Resources or rebuild/adopt runtime Meshes.
- Consume `M2_MESH_FINALIZE` permits.
- Change cache formats, animation loading or visible M2 behavior.
## Paths
- Exclusive: `src/render/m2/m2_mesh_load_pipeline_state.gd`,
`src/tools/verify_m2_mesh_load_pipeline_state.gd`,
`docs/modules/m2-mesh-load-pipeline-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- One normalized path owns at most one pending request.
- Pending records preserve Dictionary insertion order for polling snapshots.
- Terminal completion removes the request and appends its record/status to FIFO.
- Finalize pop remains oldest-first; duplicate/unknown transitions are rejected.
- Total work count remains pending plus finalize records.
- Clear drops bookkeeping only; loader drains ResourceLoader requests first.
## Dependencies
- Requires: accepted classifier package on current master `8ec13d6`
- Blocks: further M2 Mesh cache/finalization service extraction
- External state: none; verification uses synthetic paths and opaque statuses
## Verification
- Commands: dedicated validation/order/transition/FIFO/clear/source/timing
verifier; M2 classifier/builder/grouper/transform/registry, materials, shutdown,
facade, internal-access, manifest, documentation and coordination gates
- Fixtures: synthetic normalized paths, resource paths and integer terminal statuses
- Fidelity evidence: exact request/finalize transitions and metric/reset sites preserved
- Performance budget: 100 cycles over 256 request/completion/pop records under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- World-renderer, module registry and RENDER status updates
## Simplicity and naming
- Important names introduced: `M2MeshLoadPipelineState`
- Simplest considered solution: one RefCounted with request map and finalize FIFO
- Rejected complexity/abstractions: generic ResourceLoader queue, signals,
callbacks, Mesh cache ownership or animation/static pipeline merger
- Unavoidable complexity and justification: polling and budgeted finalization are
distinct lifecycle stages across frames
- Measured optimization evidence: bounded synthetic transition loop
## Status
- State: accepted
- Done: state holder, loader adapters, contract verifier and required documentation
- Next: further M2 Mesh cache/finalizer extraction
- Blocked by:
## Handoff
- Commit: `82df7a5`
- Results: 34 headless contract regressions pass; dedicated pipeline state
verifier passes 100-by-256 transitions in 100.273 ms; documentation and
coordination gates pass; checkpoint dry-run passes 7/7.
- Remaining risks: no private asset traversal, leak/descriptor-pressure or
original-client visual comparison; ResourceLoader I/O and Mesh cache/finalizer
ownership remain in `StreamingWorldLoader` by design.
- Documentation updated: inline API; `m2-mesh-load-pipeline-state.md` with
data-flow, state, sequence and dependency diagrams; module registry,
`world-renderer.md` and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-LOAD-PIPELINE-001:82df7a5 -->
## Integration
- Merge commit: `83b3618`
- Post-merge results: pipeline state `cases=11 iterations=100
elapsed_ms=69.196`; classifier, materials, shutdown, facade, internal-access
`34`, manifest `7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-LOAD-PIPELINE-001:83b3618 -->
@@ -0,0 +1,108 @@
# M03-RND-M2-MESH-RESOURCE-CACHE-001 — M2 mesh resource cache state
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-RESOURCE-CACHE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-mesh-resource-cache`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract the normalized-path to static M2 Mesh resource cache from
`StreamingWorldLoader`, preserving lookup, replacement, shutdown lifetime and
all existing cache consumers.
## Non-goals
- Own the shared M2 missing-path cache or animated/prototype node caches.
- Call ResourceLoader, extract Meshes from Resources or select cache paths.
- Prepare, rebuild or materialize M2 Meshes.
- Change cache formats, material refresh, animation or visible behavior.
## Paths
- Exclusive: `src/render/m2/m2_mesh_resource_cache_state.gd`,
`src/tools/verify_m2_mesh_resource_cache_state.gd`,
`docs/modules/m2-mesh-resource-cache-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_m2_mesh_load_pipeline_state.gd`,
`src/tools/verify_render_runtime_cache_shutdown.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- Non-empty normalized M2 paths map to at most one Mesh resource reference.
- Store replaces the previous reference for the same path.
- Lookup returns the exact retained Mesh reference; unknown/empty paths return null.
- Clear releases all cache references only at the existing final-shutdown site.
- Shared missing/prototype/animated caches remain loader-owned and unchanged.
## Dependencies
- Requires: accepted mesh load pipeline package on master `ef782d1`
- Blocks: isolated M2 Mesh resource finalizer extraction
- External state: none; verification uses synthetic Mesh resources
## Verification
- Commands: dedicated validation/store/replace/lookup/clear/source/timing
verifier; mesh pipeline/classifier/material/shutdown/M2 build/facade/internal-
access/manifest, documentation and coordination gates
- Fixtures: synthetic ArrayMesh resources and normalized paths
- Fidelity evidence: exact cache call-site and shutdown lifetime preservation
- Performance budget: 100 cycles over 256 store/lookup entries under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state and dependency diagrams
- World-renderer, module registry and RENDER status updates
## Simplicity and naming
- Important name introduced: `M2MeshResourceCacheState`
- Simplest solution: one RefCounted with one typed-purpose Dictionary
- Rejected complexity: generic resource cache, missing-state merger, eviction,
signals, callbacks or materialization ownership
- Unavoidable complexity: none beyond retaining engine Resource references
- Measured optimization evidence: bounded synthetic store/lookup loop
## Status
- State: accepted
- Done: cache state, loader/test adapters, contract verifier and required documentation
- Next: isolated M2 Mesh resource finalizer extraction
- Blocked by:
## Handoff
- Commit: `e990a65`
- Results: dedicated cache verifier passes 100-by-256 store/lookups in
38.974 ms; 35 headless renderer/coordinate regressions pass; checkpoint
dry-run passes 7/7; documentation and coordination gates pass.
- Remaining risks: no private M2 traversal, Mesh memory/leak/descriptor-pressure
run or original-client visual comparison; extraction/preparation, shared
missing state and materialization remain loader-owned by design.
- Documentation updated: inline API; `m2-mesh-resource-cache-state.md` with
data-flow, lifecycle, sequence and dependency diagrams; pipeline spec, module
registry, `world-renderer.md` and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-RESOURCE-CACHE-001:e990a65 -->
## Integration
- Merge commit: `3094561`
- Post-merge results: cache state `cases=9 iterations=100
elapsed_ms=38.686`; pipeline, classifier, materials, shutdown, facade,
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-RESOURCE-CACHE-001:3094561 -->
@@ -0,0 +1,109 @@
# M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001 — M2 mesh resource extractor
<!-- OPENWC_CLAIM:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-mesh-resource-extractor`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract first-Mesh selection from loaded M2 Resources and Node subtrees,
including temporary PackedScene instance cleanup, from `StreamingWorldLoader`.
## Non-goals
- Call ResourceLoader or select/request cache paths.
- Refresh/rebuild materials or load raw M2 data.
- Own the Mesh cache, missing state, prototypes or MultiMesh materialization.
- Change traversal order, cache formats, profiles or visible behavior.
## Paths
- Exclusive: `src/render/m2/m2_mesh_resource_extractor.gd`,
`src/tools/verify_m2_mesh_resource_extractor.gd`,
`docs/modules/m2-mesh-resource-extractor.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
M2 pipeline/cache/material/shutdown verifiers, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, M2 module specs, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- A direct Mesh Resource returns the same Mesh reference.
- A PackedScene is instantiated once, searched depth-first in child order and
its temporary root is freed before return.
- A Node subtree search returns the first MeshInstance3D Mesh in historical
depth-first preorder without changing the tree.
- Null, unsupported Resources and subtrees without a Mesh return null.
## Dependencies
- Requires: accepted Mesh resource cache package on master `5e2afbc`
- Blocks: isolated material refresh/rebuild finalizer extraction
- External state: none; verification uses synthetic Mesh/Node/PackedScene fixtures
## Verification
- Commands: dedicated direct/PackedScene/subtree/order/lifetime/source/timing
verifier; M2 pipeline/cache/classifier/material/shutdown/build/facade/internal-
access/manifest, documentation and coordination gates
- Fixtures: ArrayMesh, MeshInstance3D subtrees and synthetic PackedScene
- Fidelity evidence: exact traversal order and temporary-node destruction
- Performance budget: 10,000 synthetic subtree lookups under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- M2 cache/pipeline, world-renderer, registry and RENDER updates
## Simplicity and naming
- Important name introduced: `M2MeshResourceExtractor`
- Simplest solution: one stateless RefCounted with two explicit extraction APIs
- Rejected complexity: callback-driven finalizer, generic scene utility,
cache ownership, signals or visitor framework
- Unavoidable complexity: temporary PackedScene instance must be freed while
its referenced Mesh remains borrowed
- Measured optimization evidence: bounded synthetic depth-first traversal
## Status
- State: accepted
- Done: extractor, three loader/test adapters, contract verifier and required documentation
- Next: isolated M2 Mesh material preparation finalizer extraction
- Blocked by:
## Handoff
- Commit: `7b0e1ea`
- Results: extractor passes direct/PackedScene/subtree/order/lifetime contracts
and 10,000 traversals in 39.243 ms; 36 headless renderer/coordinate
regressions pass; checkpoint dry-run passes 7/7; documentation and
coordination gates pass.
- Remaining risks: synchronous PackedScene instantiation remains behind the
existing permit; no private M2 corrupt-scene/traversal/leak/p95/p99 or
original-client comparison; material preparation remains loader-owned.
- Documentation updated: inline API; `m2-mesh-resource-extractor.md` with
data-flow, lifecycle, sequence and dependency diagrams; cache/pipeline specs,
module registry, `world-renderer.md` and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001:7b0e1ea -->
## Integration
- Merge commit: `fa85985`
- Post-merge results: extractor `cases=10 iterations=10000
elapsed_ms=38.172`; cache, pipeline, classifier, materials, shutdown, facade,
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001:fa85985 -->
@@ -0,0 +1,84 @@
# M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001
<!-- OPENWC_CLAIM:M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-native-animation-resource-observer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-native-animation-resource-observer`
## Outcome
Move native GryphonRoost M2 animation candidate selection, synchronous raw read,
native animated build, prototype/static-only adoption and debug diagnostics from
the loader into a dedicated observer.
## Non-goals
- Change the exact native candidate predicate or add more animated models.
- Move cached-GLB observation, ResourceLoader polling/finalize or playback.
- Own attached instances, consume permits or change queue/batch behavior.
- Change native parsing/building, cache formats, profiles or visible output.
## Paths
- Exclusive: `src/render/m2/m2_native_animation_resource_observer.gd`,
`src/tools/verify_m2_native_animation_resource_observer.gd`,
`docs/modules/m2-native-animation-resource-observer.md`, this claim
- Shared: loader, M2 cached observer/snapshot/raw repository/prototype specs and
verifiers, world renderer, module registry, `RENDER.md` and M03 Evidence
## Contracts and data
- Only case-insensitive paths containing `gryphonroost` are native candidates.
- Cached animated prototype wins; static-only state suppresses repeated reads.
- Empty raw data or animated surfaces marks animation static.
- Builder result must be non-null with at least one child; rejection marks static.
- Successful result is adopted through first-wins prototype cache semantics.
- Existing `M2_NATIVE_ANIM_CACHE` fields and debug gating remain exact.
- Observer attaches/frees no Node and starts no async work.
## Dependencies
- Requires: accepted cached observer `6f385ed`, raw repository and prototype state
- Blocks: remaining ResourceLoader polling/finalize extraction
## Verification
- Synthetic candidate/cache/static/raw/build/adoption/lifetime/logging/source and
timing contracts; adjacent M2/renderer gates; full headless suite and checkpoint
dry-run.
## Documentation deliverables
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
diagrams; adjacent renderer docs and M03 Evidence.
## Status
- State: accepted
- Done: implementation, verification and documentation
- Next: remaining cached ResourceLoader terminal polling/finalize extraction
- Blocked by:
## Handoff
- Commit: `1cb0101`
- Results: observer `33` cases / `20,000` timing iterations; all `57/57`
autonomous headless regressions; facade/internal-access/shutdown/manifest;
documentation `44`; coordination; checkpoint dry-run `7/7`.
- Remaining risks: private asset animation/visual/leak/p95/p99 evidence is absent;
native parse/build stays synchronous; historical childless rejected Node remains
unfreed; cached ResourceLoader terminal polling/finalize stays loader-owned.
- Documentation updated: new native observer API/I/O/data-flow/state/sequence/
dependency/ownership spec; cached observer, snapshot, dispatch, repository,
prototype cache, world renderer, module registry, `RENDER.md` and M03 Evidence.
## Integration
- Merge: `d37c799`
- Post-merge: native/cached observers, repository, cache, snapshot, dispatch,
shutdown, facade, internal-access `30`, manifest `7/7`, documentation `44`,
coordination and checkpoint dry-run `7/7` passed on master.
@@ -0,0 +1,114 @@
# M03-RND-M2-PLACEMENT-GROUPER-001 — M2 placement grouper
<!-- OPENWC_CLAIM:M03-RND-M2-PLACEMENT-GROUPER-001:sindo-main-codex:2026-07-19 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-placement-grouper`
- Lease expires UTC: 2026-07-19
- Integrator: M03 milestone integrator
## Outcome
Extract pure ADT M2 placement validation, path normalization and local-transform
grouping from the worker callback into a scene-free service.
## Non-goals
- Change placement ordering, coordinate conversion, transform formulas or clamps.
- Move WorkerThreadPool tasks, mutex/result queues, tile state or retry behavior.
- Move build jobs, animation/mesh loading, caches, Nodes, MultiMesh or RIDs.
- Generalize grouping policy, change batching or add a dependency.
- Claim build-12340 placement or visual parity beyond preserved formulas.
## Paths
- Exclusive: `src/render/m2/m2_placement_grouper.gd`,
`src/tools/verify_m2_placement_grouper.gd`,
`docs/modules/m2-placement-grouper.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_m2_placement_transform_resolver.gd`,
`docs/modules/m2-placement-transform-resolver.md`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: tile-local origin, MMDX name table and raw ADT M2 placement array
- Output: fresh Dictionary keyed by historically normalized relative M2 path;
each value is a fresh ordered Array of tile-local `Transform3D` values
- Invalid variants, out-of-range name IDs and empty paths are skipped
- Backslashes become slashes; lowercase `.mdx/.mdl` suffixes become `.m2`
- Transform basis/origin delegates to `M2PlacementTransformResolver`; final basis
scale retains the grouping-path clamp `max(scale, 0.0001)`
- Cache/schema/coordinate versions: unchanged
## Dependencies
- Requires: current master `c34e538`, `M2PlacementTransformResolver`
- Blocks: broader M2 worker/build decomposition
- External state: none; tests use synthetic names and placement dictionaries
## Verification
- Commands: dedicated invalid/name/path/group-order/default/transform/scale and
bounded timing verifier; loader source boundary; resolver/registry/dedupe plus
facade/internal-access/manifest/shutdown/documentation/coordination gates
- Fidelity evidence: exact loop rules and transform composition moved unchanged
- Performance budget: linear over placement count with fresh output containers;
no Node, Resource, RID, task, mutex, cache or file access
## Documentation deliverables
- Inline grouper API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
performance, fidelity limitations and source map
- Transform resolver, renderer/module registry and `RENDER.md` updates
## Simplicity and naming
- Important name: `M2PlacementGrouper`
- Simplest solution: one stateless `group_placements` query
- Rejected complexity: group/result classes, strategy interfaces, callbacks,
Resources, policies or generic spatial batching framework
- Unavoidable complexity: raw ADT dictionaries remain at the parser adapter
- Measured optimization evidence: bounded synthetic grouping loop
## Status
- State: accepted
- Done: pure grouper, worker adapter, regressions and documentation verified
- Next: M2 worker/build state extraction and spatial-cell evidence
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PLACEMENT-GROUPER-001:9df32d3 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PLACEMENT-GROUPER-001:f88bf97 -->
## Handoff
- Commit: `9df32d3` (`refactor(M03): extract M2 placement grouper`)
- Results: dedicated grouper PASS (`cases=9`, `iterations=100`,
`placements=256`, `elapsed_ms=67.736`); transform resolver PASS (`cases=9`,
`iterations=10000`, `elapsed_ms=28.571`); M2 registry/dedupe, terrain
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
editor import parsed the project with only expected missing private corpus
diagnostics; documentation PASS (`module_specs=17`, `required_files=7`);
coordination PASS with 23 pre-existing expired M00/M01 warnings; checkpoint
dry-run planned `7/7`.
- Remaining risks: no private asset-backed traversal/visual comparison;
uppercase extension preservation is historical but inconsistent with the
resolver's case-insensitive exception matching; grouping remains model-path
based rather than spatial-cell based; worker/build state and p95/p99 remain.
- Documentation updated: inline public API; new
`docs/modules/m2-placement-grouper.md`; transform resolver module spec,
renderer module registry, `docs/modules/world-renderer.md` and `RENDER.md`.
- Integration: accepted in master merge `f88bf97`; post-merge grouper
(`cases=9`, `iterations=100`, `placements=256`, `elapsed_ms=67.104`),
transform resolver, registry, internal-access, facade, manifest,
documentation and coordination gates passed.
@@ -0,0 +1,107 @@
# M03-RND-M2-PLACEMENT-TRANSFORM-001 — M2 placement transform resolver
<!-- OPENWC_CLAIM:M03-RND-M2-PLACEMENT-TRANSFORM-001:sindo-main-codex:2026-07-19 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-placement-transform`
- Lease expires UTC: 2026-07-19
- Integrator: M03 milestone integrator
## Outcome
Extract pure ADT M2 placement basis/origin-offset calculation, including the
existing waterfall and Elwynn cliff-rock calibrations, into a scene-free service.
## Non-goals
- Change coordinate conversion, placement positions, rotations or scale clamps.
- Generalize model-specific corrections or add new allowlisted models.
- Move grouping/tasks, MultiMesh/Node/Mesh/material/RID creation or caches.
- Change M2 animation/material behavior, cache versions or renderer profiles.
- Claim build-12340 placement parity beyond existing fixtures.
## Paths
- Exclusive: `src/render/m2/m2_placement_transform_resolver.gd`,
`src/tools/verify_m2_placement_transform_resolver.gd`,
`docs/modules/m2-placement-transform-resolver.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: ADT-loader Godot Euler rotation, normalized/original M2 relative path,
and raw placement scale where origin compensation is requested
- Output: `Basis` and local origin-offset `Vector3`; no retained state
- Regular models use `Basis.from_euler`; two cliff rocks retain narrow YZX-style
shell correction; two waterfall sheets retain yaw/twist/anchor compensation
- Scale clamps remain caller-owned for final basis and resolver-owned at `0.0001`
only for historical waterfall anchor compensation
- Cache/schema/coordinate versions: unchanged
## Dependencies
- Requires: current master `1b6c468`
- Blocks: pure M2 grouping extraction and broader M2 service decomposition
- External state: none; tests use synthetic rotations/paths and pinned constants
## Verification
- Commands: dedicated regular/path-normalization/cliffrock/waterfall/twist-anchor/
scale and bounded timing verifier; three loader call-site/source boundaries;
M2 unique/dedupe plus facade/terrain/internal-access/manifest/shutdown gates
- Fidelity evidence: exact formulas/constants and model allowlists moved unchanged
- Performance budget: allocation-free scalar/Basis math per placement aside from
path normalization; no Node, Resource, RID, task, cache or file access
## Documentation deliverables
- Inline resolver API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
performance, fidelity limitations and source map
- World renderer/module registry/`RENDER.md` updates
## Simplicity and naming
- Important name: `M2PlacementTransformResolver`
- Simplest solution: stateless service with basis and origin-offset queries
- Rejected complexity: transform strategy hierarchy, generic correction registry,
Resources, data-driven override format or callback injection
- Unavoidable complexity: four measured model-specific exceptions remain explicit
- Measured optimization evidence: bounded synthetic transform loop
## Status
- State: accepted
- Done: resolver extracted, three call sites migrated, contracts and docs verified
- Next: pure M2 grouping extraction and broader M2 service decomposition
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PLACEMENT-TRANSFORM-001:0a09d7b -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PLACEMENT-TRANSFORM-001:396be5e -->
## Handoff
- Commit: `0a09d7b` (`refactor(M03): extract M2 placement transform resolver`)
- Results: dedicated formula/source/timing contract PASS (`cases=9`,
`iterations=10000`, `elapsed_ms=28.732`); cold editor parse PASS;
documentation gate PASS (`module_specs=16`, `required_files=7`);
coordination gate PASS with 23 pre-existing expired M00/M01 warnings; related
M2 unique/dedupe, terrain cache/LOD/queue, facade, internal-access, manifest,
shutdown, scheduler, streaming focus/planner and coordinate-boundary suites
PASS; renderer checkpoint dry-run PASS `7/7`.
- Remaining risks: no private asset-backed visual recheck in this package;
general ADT M2 placement parity and asset-backed p95/p99 remain pending.
- Documentation updated: inline public API; new
`docs/modules/m2-placement-transform-resolver.md`; renderer module registry,
`docs/modules/world-renderer.md` and `RENDER.md`.
- Integration: accepted in master merge `396be5e`; post-merge resolver
(`cases=9`, `iterations=10000`, `elapsed_ms=27.409`), M2 registry,
internal-access, facade, manifest, documentation and coordination gates passed.
@@ -0,0 +1,111 @@
# M03-RND-M2-PROTOTYPE-CACHE-001 — M2 prototype cache state
<!-- OPENWC_CLAIM:M03-RND-M2-PROTOTYPE-CACHE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-prototype-cache`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract static/animated detached M2 prototype references plus missing-model and
static-animation negative outcomes from `StreamingWorldLoader` into one explicit
shutdown-lifetime cache-state service.
## Non-goals
- Choose `.tscn/.glb` paths, call ResourceLoader/native loaders or build Nodes.
- Own animation/Mesh request queues, Mesh resources, materials or MultiMeshes.
- Change candidate/allowlist/denylist rules, fallback order or visible behavior.
- Add persistence, eviction, async work, dependencies or cache-format changes.
## Paths
- Exclusive: `src/render/m2/m2_prototype_cache_state.gd`,
`src/tools/verify_m2_prototype_cache_state.gd`,
`docs/modules/m2-prototype-cache-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, runtime
cache shutdown/M2 raw/finalizer/extractor/pipeline/material/animation verifiers
and module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
`RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- Normalized non-empty paths key static and animated detached `Node3D` prototypes.
- Missing-model and static-animation outcomes are independent path sets.
- Exact cached Node reference is returned; first adoption for an occupied path wins.
- Shutdown frees valid detached nodes synchronously, queues in-tree nodes for free,
clears all four states and remains idempotent.
- Map refresh retains the state exactly as before; only final shutdown clears it.
## Dependencies
- Requires: accepted raw model repository package on master `ca77d32`
- Blocks: remaining M2 animation request/materialization extraction
- External state: none; verification uses synthetic Node3D instances
## Verification
- Commands: dedicated admission/identity/independence/duplicate/free/source/timing
verifier; runtime shutdown/raw/finalizer/extractor/cache/pipeline/material/
animation/M2 build/facade/internal-access/manifest, docs and coordination gates
- Fixtures: detached and in-tree synthetic Node3D prototypes
- Fidelity evidence: all existing lookup/store/negative/clear caller transitions
- Performance budget: 100 cycles over 256 path-state operations under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent M2/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2PrototypeCacheState`
- Simplest solution: one main-thread RefCounted with two Node maps and two path sets
- Rejected complexity: generic Node cache, signals, callbacks, eviction policy,
weak references, async cleanup or dependency injection
- Unavoidable complexity: detached Nodes require explicit free at final shutdown
- Measured optimization evidence: bounded synthetic cache-cycle loop
## Status
- State: accepted
- Done: cache state, loader/test adapters, lifecycle verifier and required documentation
- Next: continue M2 animation request/materialization extraction
- Blocked by:
## Handoff
- Commit: `cd664eb`
- Results: prototype state passes 16 admission/identity/negative/lifecycle/source
cases and 100-by-256 cycles in 50.266 ms; 46 autonomous headless verifiers
pass, while the proprietary ADT placement probe is unavailable without
`data/extracted`; checkpoint dry-run passes 7/7; documentation and
coordination gates pass; loader-private inventory decreases from 34 to 31.
- Remaining risks: strong prototypes remain unbounded until shutdown; successful
asset traversal, descriptor pressure, leaks and p95/p99 require proprietary
data; no original-client visual comparison is claimed.
- Documentation updated: inline API; `m2-prototype-cache-state.md` with data-flow,
lifecycle, sequence and dependency diagrams; pipeline/Mesh-cache/extractor/raw
repository/world-renderer specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-PROTOTYPE-CACHE-001:cd664eb -->
## Integration
- Merge commit: `1de50f9`
- Post-merge results: prototype cache `cases=16 iterations=100
elapsed_ms=37.978`; shutdown, raw repository, finalizer, extractor, Mesh cache,
pipeline, materials, facade, internal-access `31`, manifest `7/7`,
documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-PROTOTYPE-CACHE-001:1de50f9 -->
@@ -0,0 +1,109 @@
# M03-RND-M2-RAW-MODEL-REPOSITORY-001 — raw M2 model repository
<!-- OPENWC_CLAIM:M03-RND-M2-RAW-MODEL-REPOSITORY-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-raw-model-repository`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract the repeated FileAccess/ClassDB native `M2Loader` boundary for static
and animated raw model Dictionaries from `StreamingWorldLoader`.
## Non-goals
- Cache raw data, Meshes, prototypes, missing paths or animation decisions.
- Build Nodes/Meshes, refresh materials or choose `.tscn/.glb` caches.
- Change static/animated parser methods, path resolution or fallback order.
- Add dependencies, persistence, retries or visible behavior.
## Paths
- Exclusive: `src/render/m2/m2_raw_model_repository.gd`,
`src/tools/verify_m2_raw_model_repository.gd`,
`docs/modules/m2-raw-model-repository.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
finalizer/extractor/cache/pipeline/material/animation/shutdown verifiers and
module specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`,
`RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- Static load calls native `load_m2`; animated load calls `load_m2_animated`.
- Paths remain `globalize_path(extracted_directory.path_join(normalized_path))`.
- Empty path, missing native class/file/instance/method, parse failure or
non-Dictionary result returns an empty Dictionary without retained state.
- Each successful request creates one native loader instance as before.
## Dependencies
- Requires: accepted runtime Mesh finalizer package on master `1d94eac`
- Blocks: M2 prototype/cache state extraction
- External state: native M2Loader registration and local extracted files
## Verification
- Commands: dedicated invalid/missing/class/source/timing verifier; finalizer/
classifier/extractor/cache/pipeline/material/animation/shutdown/M2 build/facade/
internal-access/manifest, documentation and coordination gates
- Fixtures: empty/missing normalized paths plus source contract; proprietary
success corpus remains external
- Fidelity evidence: exact native method names, path expression and three caller transitions
- Performance budget: 10,000 rejected empty-path calls under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Finalizer/extractor/cache/pipeline, world-renderer, registry and RENDER updates
## Simplicity and naming
- Important name introduced: `M2RawModelRepository`
- Simplest solution: one stateless RefCounted with static and animated load methods
- Rejected complexity: generic native repository, caching, dependency injection,
retries, diagnostics framework or async wrapper
- Unavoidable complexity: animated native method availability is a distinct guard
- Measured optimization evidence: bounded rejected-input loop
## Status
- State: accepted
- Done: repository, three loader adapters, contract verifier and required documentation
- Next: continue M2 prototype/cache state extraction
- Blocked by:
## Handoff
- Commit: `b608a81`
- Results: repository passes invalid/missing/stateless/native-boundary contracts
and 10,000 empty-path rejects in 3.275 ms; 45 repository-independent
headless verifiers pass, while the proprietary ADT placement probe is
unavailable without `data/extracted`; checkpoint dry-run passes 7/7;
documentation and coordination gates pass.
- Remaining risks: successful/corrupt native parsing, descriptor pressure,
leaks and p95/p99 remain asset-backed work; native calls are synchronous and
cannot be cancelled mid-parse; no original-client visual comparison is claimed.
- Documentation updated: inline API; `m2-raw-model-repository.md` with data-flow,
lifecycle, sequence and dependency diagrams; finalizer/classifier/world-renderer
specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RAW-MODEL-REPOSITORY-001:b608a81 -->
## Integration
- Merge commit: `7cd07dd`
- Post-merge results: repository `cases=10 iterations=10000
elapsed_ms=3.200`; finalizer, extractor, cache, pipeline, shutdown, facade,
internal-access `34`, manifest `7/7`, documentation and coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RAW-MODEL-REPOSITORY-001:7cd07dd -->
@@ -0,0 +1,110 @@
# M03-RND-M2-RUNTIME-MESH-FINALIZER-001 — M2 runtime mesh finalizer
<!-- OPENWC_CLAIM:M03-RND-M2-RUNTIME-MESH-FINALIZER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-runtime-mesh-finalizer`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract stale cached-Mesh refresh classification, metadata adoption,
M2Builder rebuild and fallback from `StreamingWorldLoader` into one runtime M2
Mesh finalizer while retaining raw-file I/O in the loader.
## Non-goals
- Load raw `.m2` files, call FileAccess/ClassDB M2Loader or choose cache paths.
- Own ResourceLoader requests, Mesh cache/missing state or MultiMesh builds.
- Change material refresh version `2`, classifier predicates or rebuild behavior.
- Change cache formats, profiles or visible output.
## Paths
- Exclusive: `src/render/m2/m2_runtime_mesh_finalizer.gd`,
`src/tools/verify_m2_runtime_mesh_finalizer.gd`,
`docs/modules/m2-runtime-mesh-finalizer.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2
classifier/extractor/cache/pipeline/material/shutdown verifiers and module
specs, `docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated M2 resources, caches and
proprietary renderer corpus
## Contracts and data
- Refresh version remains `2`; already-current Meshes return unchanged without raw data.
- Stale Meshes require caller-supplied raw data only when refresh is necessary.
- Empty/ordinary raw data marks the original Mesh current and returns it.
- Billboard/UV-rotation data attempts M2Builder rebuild and returns the rebuilt
first Mesh; rebuild failure marks and returns the original Mesh.
- Classifier memoization retains both existing clear sites through finalizer clear.
## Dependencies
- Requires: accepted Mesh resource extractor package on master `dd8cfbf`
- Blocks: further M2 raw-data I/O/prototype state extraction
- External state: none; verification uses synthetic Mesh/raw-data fixtures
## Verification
- Commands: dedicated current/stale/ordinary/rebuild/fallback/clear/source/timing
verifier; classifier/extractor/cache/pipeline/material/shutdown/M2 build/facade/
internal-access/manifest, documentation and coordination gates
- Fixtures: ArrayMesh and one synthetic triangle/billboard M2 Dictionary
- Fidelity evidence: exact refresh version, predicate, rebuild and fallback transitions
- Performance budget: 100 cycles over 256 ordinary finalizations under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Classifier/extractor/cache/pipeline, world-renderer, registry and RENDER updates
## Simplicity and naming
- Important name introduced: `M2RuntimeMeshFinalizer`
- Simplest solution: one service composing existing classifier/extractor and M2Builder
- Rejected complexity: callbacks for loader I/O, generic material pipeline,
cache ownership, signals or strategy framework
- Unavoidable complexity: two-phase raw-data request avoids loading raw files for current Meshes
- Measured optimization evidence: bounded synthetic ordinary finalization loop
## Status
- State: accepted
- Done: finalizer, loader/test adapters, transition/rebuild verifier and required documentation
- Next: further M2 raw-data I/O/prototype state extraction
- Blocked by:
## Handoff
- Commit: `ece7724`
- Results: finalizer passes current/stale/ordinary/synthetic-rebuild/fallback/
clear contracts and 100-by-256 finalizations in 53.719 ms; 37 headless
renderer/coordinate regressions pass; checkpoint dry-run passes 7/7;
documentation and coordination gates pass.
- Remaining risks: rebuild remains synchronous behind the existing permit; no
private M2 material/traversal/descriptor-pressure/leak/p95/p99 or original-
client comparison; raw `.m2` I/O remains loader-owned by design.
- Documentation updated: inline API; `m2-runtime-mesh-finalizer.md` with
data-flow, lifecycle, sequence and dependency diagrams; classifier/extractor/
cache/pipeline specs, module registry, `world-renderer.md` and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RUNTIME-MESH-FINALIZER-001:ece7724 -->
## Integration
- Merge commit: `1fafdde`
- Post-merge results: finalizer `cases=11 iterations=100
elapsed_ms=54.349`; classifier, extractor, cache, pipeline, materials,
shutdown, facade, internal-access `34`, manifest `7/7`, documentation and
coordination gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RUNTIME-MESH-FINALIZER-001:1fafdde -->
@@ -0,0 +1,115 @@
# M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001 — M2 runtime rebuild classifier
<!-- OPENWC_CLAIM:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-runtime-rebuild-classifier`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract the memoized decision that stale cached M2 meshes require an in-memory
runtime rebuild because raw batches contain billboard vertices or referenced UV
rotation transforms.
## Non-goals
- Load raw M2 files or cache Resources.
- Build, replace or mutate Meshes, materials, Nodes or RIDs.
- Change `M2_MATERIAL_REFRESH_VERSION` or cache formats.
- Interpret animation, particles, ribbons or general M2 shader fidelity.
- Change path normalization, ResourceLoader transitions or finalize budgets.
## Paths
- Exclusive: `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd`,
`src/tools/verify_m2_runtime_mesh_rebuild_classifier.gd`,
`docs/modules/m2-runtime-mesh-rebuild-classifier.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/M2 resources, caches
and proprietary renderer corpus
## Contracts and data
- Billboard metadata requires rebuild when `has_billboard` is true or
`billboard_vertex_count` is positive in any Dictionary batch.
- UV rotation requires non-empty transforms and combos plus a referenced
non-identity `Vector4` rotation or absolute rotation speed above `0.000001`.
- Only up to four texture stages per batch are inspected; invalid variants and
out-of-range combo/transform indices are skipped.
- The first decision for a normalized path is memoized until clear, matching the
existing loader cache lifetime.
## Dependencies
- Requires: current accepted master `90b1dac`
- Blocks: further M2 mesh request/cache/finalization extraction
- External state: none; verification uses synthetic raw M2 Dictionaries
## Verification
- Commands: dedicated billboard/UV/index/stage/cache/clear/source/timing verifier;
M2 builder/grouper/transform/registry, materials, shutdown, facade,
internal-access, manifest, documentation and coordination gates
- Fixtures: synthetic batches, transform combos and rotation dictionaries
- Fidelity evidence: exact existing predicate thresholds, stage cap and cache lifetime preserved
- Performance budget: 100 classifications over 256 paths under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- World-renderer, module registry and RENDER status updates
## Simplicity and naming
- Important names introduced: `M2RuntimeMeshRebuildClassifier`
- Simplest considered solution: one RefCounted with one memoized query and clear
- Rejected complexity/abstractions: generic predicate cache, strategy interface,
ResourceLoader wrapper, signals or typed rewrite of raw parser Dictionaries
- Unavoidable complexity and justification: four-stage combo indirection is the
existing build-12340 M2 material boundary
- Measured optimization evidence: bounded synthetic classification loop
## Status
- State: accepted
- Done: classifier/cache implemented; loader query and both clear sites migrated;
synthetic predicate/cache verifier and required documentation added
- Next: continue M2 mesh request/cache/finalization extraction
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001:38f831e -->
## Handoff
- Commit: `19ca8f4`
- Results: classifier PASS (`cases=12`, `iterations=100`,
`elapsed_ms=64.585`; initial `35.461ms`); billboard flag/count, invalid
variants/indices, UV rotation/speed/epsilon, four-stage cap, memoization,
detached diagnostics and clear contracts PASS; 33 M2/WMO/liquid/terrain/
facade/scheduler/streaming/coordinate/material/shutdown regressions PASS;
internal-access decreased to `35`; documentation and coordination gates PASS;
checkpoint dry-run PASS `7/7`.
- Remaining risks: raw parser data remains Dictionary-based; the first decision
for a path remains authoritative until clear; classification covers the current
four-stage material implementation only; private asset descriptor-pressure,
traversal, p95/p99 and paired original-client evidence remain absent.
- Documentation updated: inline public API; new
`docs/modules/m2-runtime-mesh-rebuild-classifier.md`; module registry, world
renderer and `RENDER.md`; data-flow/state/sequence/dependency diagrams,
ownership, failure/recovery, thresholds, cache lifetime and source map included.
- Integration: merged as `38f831e`; post-merge classifier (`cases=12`,
`iterations=100`, `elapsed_ms=45.439`), materials, shutdown, M2 batch planner,
facade, internal-access (`private_symbols=35`), `7/7` manifest, documentation
and coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-RUNTIME-REBUILD-CLASSIFIER-001:19ca8f4 -->
@@ -0,0 +1,106 @@
# M03-RND-M2-STATIC-BATCH-MATERIALIZER-001
<!-- OPENWC_CLAIM:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-static-batch-materializer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-static-batch-materializer`
## Outcome
Extract main-thread static M2 `MultiMesh` construction, render-property setup
and batch attachment from `StreamingWorldLoader`.
## Non-goals
- Change M2 grouping, batch sizing, build cursors, budgets or retry behavior.
- Change Mesh loading, refresh, cache ownership or missing-model outcomes.
- Own animated M2 instances, Editor persistence or tile cleanup.
- Introduce spatial cells or change transform order, visibility or shadows.
## Paths
- Exclusive: `src/render/m2/m2_static_batch_materializer.gd`,
`src/tools/verify_m2_static_batch_materializer.gd`,
`docs/modules/m2-static-batch-materializer.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`, M2 build/
Mesh-cache/facade/internal-access/manifest verifiers and specs,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources/caches and proprietary corpus
## Contracts and data
- `MultiMesh.TRANSFORM_3D`, exact Mesh identity, instance count and transform
slice order remain unchanged.
- Batch node name remains model basename plus serial.
- Visibility end/margin and shadow setting remain exact renderer inputs.
- A valid batch attaches once to the supplied M2 parent and returns its node.
- Loader retains Editor-owner assignment, build cursor, budgets and retries.
## Dependencies
- Requires: accepted animated materializer package on master `86814de`
- Blocks: further M2 build-job orchestration extraction
- External state: Godot MultiMesh and SceneTree mutation remain main-thread APIs
## Verification
- Commands: dedicated invalid/name/mesh/count/order/render/attachment/source/timing
verifier; M2 build/cache/prototype/animated/shutdown/material/facade/internal-
access/manifest regressions; docs/coordination/dry-run gates
- Fixtures: synthetic ArrayMesh and ordered Transform3D slices
- Fidelity evidence: exact existing MultiMesh construction and render settings
- Performance budget: materialize 10,000 transforms under one second
## Documentation deliverables
- Inline API docs
- Module spec with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state, sequence and dependency diagrams
- Adjacent build/cache/world-renderer/module-registry/RENDER updates
## Simplicity and naming
- Important name introduced: `M2StaticBatchMaterializer`
- Simplest solution: synchronous main-thread RefCounted returning attached node
- Rejected complexity: signals, async queue, callbacks and generic materializer base
- Unavoidable complexity: MultiMesh is a Godot Resource owned through its node
- Measured optimization evidence: bounded synthetic transform upload
## Status
- State: ready for integration
- Done: materializer, loader adapter, verifier and required documentation
- Next: integrator merge and post-merge acceptance
- Blocked by:
## Handoff
- Commit: `252927d`
- Results: materializer passes 11 invalid/node/Mesh/render/attachment/source
cases and 10,000 transform API writes in 0.552 ms; all 51 autonomous headless
verifiers pass, while the proprietary ADT placement probe is unavailable
without `data/extracted`; checkpoint dry-run passes 7/7; documentation and
coordination gates pass; loader-private inventory remains 30.
- Remaining risks: headless dummy timing excludes GPU upload cost; private asset
traversal/visual comparison, spatial-cell culling, descriptor pressure, leaks
and p95/p99 remain unavailable; no original-client comparison is claimed.
- Documentation updated: inline API; `m2-static-batch-materializer.md` with
data-flow, lifecycle, sequence and dependency diagrams; build/cache/world-
renderer specs, module registry and `RENDER.md`.
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:252927d -->
## Integration
- Merge commit: `5e82daa`
- Post-merge results: static materializer `cases=11 instances=10000
elapsed_ms=1.327`; build/cache/animated materializer, shutdown, facade,
internal-access `30`, checkpoint dry-run `7/7`, documentation and coordination
gates passed.
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-STATIC-BATCH-MATERIALIZER-001:5e82daa -->
@@ -0,0 +1,86 @@
# M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001
<!-- OPENWC_CLAIM:M03-RND-M2-STATIC-BUILD-RESOURCE-OBSERVER-001:sindo-main-codex:2026-07-20 -->
## Owner
- Agent ID: `sindo-main-codex`
- Target: M03 Renderer Facade and Safe Extraction
- Branch: `work/sindo-main-codex/m03-m2-static-build-resource-observer`
- Worktree: `C:\Users\sindo\open-wc-worktrees\m03-m2-static-build-resource-observer`
## Outcome
Move static M2 build cache lookup, threaded request selection and terminal
missing transition from the loader into a service that fills
`M2BuildResourceSnapshot`.
## Non-goals
- Move animation/native prototype observation or finalize drains.
- Own/free Meshes, Nodes or cache entries.
- Change path candidate order, GLB filtering, retry behavior, permits or visuals.
- Change cache formats, profiles or batching.
## Paths
- Exclusive: `src/render/m2/m2_static_build_resource_observer.gd`,
`src/tools/verify_m2_static_build_resource_observer.gd`,
`docs/modules/m2-static-build-resource-observer.md`, this claim
- Shared: loader, M2 snapshot/dispatch/pipeline/cache specs and verifiers,
world renderer, module registry, RENDER and M03 Evidence
## Contracts and data
- Cached Mesh wins; terminal missing returns missing; existing pending request waits.
- Candidate order remains normalized/lowercase nested then basename per extension.
- `.tscn` precedes `.glb`; pivot-prefix GLB is skipped for static build.
- Successful/`ERR_BUSY` request is remembered; no usable candidate marks missing.
- Snapshot adopts exact Mesh/missing result; service never frees engine objects.
## Dependencies
- Requires: accepted resource snapshot `032a256`
- Blocks: remaining animated resource observer extraction
## Verification
- Synthetic cache/pipeline/snapshot lifecycle, path order, source boundaries and timing;
adjacent M2 and renderer gates; full headless suite and checkpoint dry-run.
## Documentation deliverables
- Inline API docs; module API/I/O/ownership; data-flow/state/sequence/dependency
diagrams; adjacent renderer docs and M03 Evidence.
## Status
- State: accepted
- Done: implementation, integration and post-merge acceptance
- Next: remaining animated resource observer extraction
- Blocked by:
## Handoff
- Commit: `7cb3e34` (`render: extract static M2 build resource observer`)
- Results: observer PASS (`11` cases, `20,000` iterations, `64.654ms`);
full autonomous suite PASS (`55/55`); renderer checkpoint dry-run PASS (`7/7`);
internal-access PASS (`30` private symbols); documentation PASS (`42` module
specs); coordination PASS with `30` historical warnings.
- Remaining risks: successful asynchronous admission is covered by source and
existing pipeline regressions because an undrained unit request leaks; private
asset traversal, p95/p99, leak and visual evidence remain unavailable;
animated observation and static finalize polling remain loader-owned.
- Documentation updated: new observer module API/I/O/data-flow/state/sequence/
dependency/ownership specification; M2 snapshot, pipeline/cache, world-renderer,
module registry, `RENDER.md` and M03 Evidence updated.
## Integration
- Merge: `a043c79` (`merge: static M2 build resource observer`)
- Post-merge: observer `11` cases / `20,000` iterations / `64.202ms`;
snapshot `12` / `42.895ms`; dispatch `10` / `18.096ms`; Mesh pipeline
`11` / `73.615ms`; Mesh cache `9` / `40.229ms`; prototype cache `16` /
`35.947ms`; shutdown, facade, internal-access `30`, manifest `7/7`,
documentation `42` and coordination passed. Checkpoint dry-run completed
`7/7`; expected private Azeroth/character assets remained unavailable.
@@ -0,0 +1,105 @@
# M03-RND-M2-UNIQUE-REGISTRY-001 — M2 unique placement registry
<!-- OPENWC_CLAIM:M03-RND-M2-UNIQUE-REGISTRY-001:sindo-main-codex:2026-07-19 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-m2-unique-registry`
- Lease expires UTC: 2026-07-19
- Integrator: M03 milestone integrator
## Outcome
Extract cross-tile positive `MDDF.uniqueId` reservation ownership from the
streamer into a scene-free `M2UniquePlacementRegistry`.
## Non-goals
- Move tile state, candidate requeue, group/build tasks or result queues.
- Move MultiMesh/Node/Mesh/material creation, animation or resource loading.
- Change unique-key format, first-owner semantics or unkeyed placement behavior.
- Change placement transforms, detail radius, cache versions or quality profiles.
- Add dependencies or mark the broad M2 service complete.
## Paths
- Exclusive: `src/render/m2/m2_unique_placement_registry.gd`,
`src/tools/verify_m2_unique_placement_registry.gd`,
`docs/modules/m2-unique-placement-registry.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_m2_unique_dedupe.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: tile key and raw ADT M2 placement dictionaries
- Reserve output: fresh filtered placements, owned unique keys and skipped keys
- Release input/output: tile key plus claimed keys; only current owner releases
- Positive IDs map to `uid:<decimal>`; zero/negative/missing IDs remain untracked
- Registry is session-memory only and clears on renderer world reset
- Queue/cache/schema/coordinate versions: unchanged
## Dependencies
- Requires: current master `57ae1b8`
- Blocks: broader M2 service extraction and dependency/performance acceptance
- External state: none; tests use synthetic placement dictionaries
## Verification
- Commands: dedicated unkeyed/duplicate/cross-tile/release/clear/detached-
diagnostic and bounded timing verifier; loader source boundary; M2 unique-id
smoke plus facade/terrain/internal-access/manifest/shutdown/scheduler gates
- Fidelity evidence: exact positive-ID key format and first-owner/retry behavior
- Performance budget: dictionary membership over placement count; no Node,
Resource, RID, task, file or persistent cache
## Documentation deliverables
- Inline registry API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
failure, recovery, performance and source map
- World renderer/module registry/`RENDER.md` updates
## Simplicity and naming
- Important name: `M2UniquePlacementRegistry`
- Simplest solution: one private key-to-owner dictionary and explicit operations
- Rejected complexity: generic identity registry, signals, callbacks, Resources,
locks or ownership graph
- Unavoidable complexity: raw placement dictionaries remain at ADT adapter boundary
- Measured optimization evidence: bounded synthetic reserve/release verifier
## Status
- State: accepted
- Done: stateful registry, loader adapters, migrated smoke, regressions and docs
- Next: asset-backed boundary traversal and further M2-service extraction later in M03
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-M2-UNIQUE-REGISTRY-001:acb3a80 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-M2-UNIQUE-REGISTRY-001:2342430 -->
## Handoff
- Commit: `acb3a80` (`refactor(M03): extract M2 unique placement registry`)
- Results: dedicated contract PASS (`cases=10`, `iterations=100`, `35.688 ms`);
historical `uid:11785` smoke PASS; terrain/facade/internal-access/manifest/
shutdown/scheduler/streaming/focus/terrain-query/coordinate regressions PASS;
internal inventory remains `42`; editor parse and documentation/coordination/
diff gates PASS; checkpoint dry-run planned `7/7`
- Remaining risks: private Azeroth corpus was absent, so the asset-backed ADT
placement probe could not open the map and is not counted as passed evidence;
first ownership remains processing-order based; M2 grouping/build/tasks/caches/
finalization and asset-backed p95/p99 remain pending
- Documentation updated: `m2-unique-placement-registry.md`, module registry,
`world-renderer.md`, `RENDER.md`, inline API documentation and data-flow,
lifecycle and sequence diagrams
- Integration: accepted in master merge `2342430`; post-merge registry, legacy
smoke, internal-access, facade and manifest gates passed
@@ -0,0 +1,116 @@
# M03-RND-SCHEDULER-001 — Bounded render budget scheduler
<!-- OPENWC_CLAIM:M03-RND-SCHEDULER-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-render-budget-scheduler`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Extract the streamer's per-frame main-thread operation quotas into a scene-free
`RenderBudgetScheduler`, preserving existing queue priority and quota values and
stopping permit issuance after renderer teardown cancellation.
## Non-goals
- Move queue, worker-task, cache, Node, Resource or RID ownership out of the streamer.
- Change queue ordering, concurrency limits, exported defaults or visual behavior.
- Add a generic job framework, priority graph, time-based adaptive budgeting or dependency.
- Change cache formats, renderer profiles or target milestone status.
## Paths
- Exclusive: `src/render/streaming/render_budget_scheduler.gd`,
`src/tools/verify_render_budget_scheduler.gd`,
`docs/modules/render-budget-scheduler.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated cache resources and
proprietary renderer corpus
## Contracts and data
- Input: immutable per-frame map from documented operation lane IDs to
non-negative operation limits
- Output: boolean permits and remaining/consumed diagnostic snapshots
- Lifecycle: `begin_frame` resets permits; `cancel` permanently rejects permits
for the current scheduler instance
- Queue contents, ordering, task concurrency and schema/cache versions: unchanged
- Consumer: `StreamingWorldLoader` main-thread queue/finalize drains
## Dependencies
- Requires: integrated M03 facade and streaming planner on master `650d584`
- Blocks: renderer performance regression gate and per-service extraction
- External state: none; tests use synthetic lane limits and no world assets
## Verification
- Commands: dedicated scheduler verifier, facade/planner/focus/coordinate
regressions, renderer manifest/dry-run and repository gates
- Fixtures: shared lane priority, independent lanes, zero/negative limits,
frame reset, unknown lane and cancellation
- Fidelity evidence: operation defaults and loop order are captured before
migration; all seven renderer checkpoint plans remain valid
- Performance budget: permit checks are constant-time dictionary operations and
create no Node, Resource, worker task, cache entry or RID
## Documentation deliverables
- Inline API docs for scheduler lifecycle, permits and diagnostics
- New module specification with input/output table, data-flow, sequence and
lifecycle diagrams
- Updated world-renderer flow/ownership/source map, module registry and renderer notes
## Simplicity and naming
- Important name: `RenderBudgetScheduler`
- Simplest approach: one small main-thread service with named quota lanes
- Rejected complexity: generic job graph, callbacks, queue ownership, adaptive
timing controller, service container or third-party dependency
- Unavoidable complexity: chunk removal and creation intentionally share one
lane so removal retains priority while consuming the same historical budget
- Measured optimization evidence: no optimization claim; exact bounded extraction
## Status
- State: ready-for-review
- Done: extracted all 16 historical operation quotas, migrated ordered drains,
added terminal teardown cancellation, asset-free verification and module docs
- Next: M03 integrator reviews and merges `e52f703`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-SCHEDULER-001:e52f703 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-SCHEDULER-001:7ece2ab -->
## Handoff
- Commit: `e52f703`
- Results: `RenderBudgetScheduler` owns only per-frame lane counters and terminal
cancellation; `StreamingWorldLoader` retains queue order, task/cache/node/RID
ownership and operation execution. Chunk removal/create share one historical budget.
- Verification: scheduler `cases=6 iterations=20000 elapsed_ms=7.495`; planner
`cases=5 average_ms=1.879`; facade/focus contracts passed; coordinate boundary
`files=111 consumers=6`; renderer manifest `checkpoints=7 coverage=7 caches=7`;
shutdown ownership, documentation, coordination and diff gates passed.
- Fidelity: exported defaults, quality presets, queue order, operation sites,
cache versions and visible rendering rules are unchanged. Seven checkpoint
plans remain valid. No build-12340 parity claim is added.
- External/local inputs: ignored native DLL and two generated ADT resource scripts
were copied from master only for the worktree parse/smoke and then removed.
Proprietary extracted ADT data was unavailable, producing expected degraded-data diagnostics.
- Remaining risks: queue storage length and worker concurrency remain streamer-owned;
cancellation stops permit issuance but does not interrupt in-flight work; exact
p95/p99 impact still requires an asset-backed baseline.
- Documentation: new scheduler module spec with API, input/output, data-flow,
sequence, lifecycle, ownership, cancellation, performance and source-map sections;
world renderer, module registry and renderer implementation notes updated.
- Integration: accepted in master merge `7ece2ab`; post-merge scheduler timing
`cases=6 iterations=20000 elapsed_ms=10.216`, all listed regressions and gates passed.
@@ -0,0 +1,120 @@
# M03-RND-STREAMING-PLANNER-001 — Pure streaming target planner
<!-- OPENWC_CLAIM:M03-RND-STREAMING-PLANNER-001:sindo-main-codex:2026-07-17 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-streaming-target-planner`
- Lease expires UTC: 2026-07-17
- Integrator: M03 milestone integrator
## Outcome
Extract runtime/editor ADT wanted/retained-set calculation and boundary prefetch
selection from `StreamingWorldLoader` into a scene-free, deterministic
`StreamingTargetPlanner` with typed focus input and asset-free contract tests.
## Non-goals
- Change load/retain radii, boundary thresholds, queue priority or LOD policy.
- Move queue mutation, async jobs, cache ownership or GPU finalization.
- Extract `RenderBudgetScheduler` or asset services in this package.
- Change cache formats, renderer profiles or target milestone status.
## Paths
- Exclusive: `src/render/streaming/streaming_target_planner.gd`,
`src/render/streaming/streaming_target_policy.gd`,
`src/render/streaming/streaming_target_plan.gd`,
`src/tools/verify_streaming_target_planner.gd`,
`docs/modules/streaming-target-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_coordinate_conversion_boundaries.gd`,
`docs/modules/world-renderer.md`, `docs/modules/coordinate-mapping.md`,
`docs/modules/README.md`, `RENDER.md`
- Generated/ignored: `.godot`, native DLL, generated cache resources and
proprietary renderer corpus
## Contracts and data
- Input: immutable `GodotWorldPosition`, available-tile key set and immutable
`StreamingTargetPolicy`
- Output: immutable `StreamingTargetPlan` with focus tile plus detached
wanted/retained tile-key sets
- Tile-key representation remains the streamer's existing `<x>_<y>` internal key
- Schema/cache/coordinate versions: unchanged
- Consumers: `StreamingWorldLoader` runtime/editor refresh paths
## Dependencies
- Requires: M01 typed coordinates/focus and integrated facade package `a100ced`
- Blocks: budget scheduler extraction and planner performance regression gate
- External state: none; tests use synthetic tile catalogs
## Verification
- Commands: dedicated planner verifier, facade/focus/coordinate regressions,
renderer manifest/dry-run and repository gates
- Fixtures: center, edge, corner, unavailable-tile, clamped-radius and editor cases
- Fidelity evidence: expected tile sets are captured from current formulas; full
renderer dry-run retains all seven checkpoint plans
- Performance budget: planner work remains bounded by square radii and creates no
Node, Resource, task, cache or RID
## Documentation deliverables
- Inline API docs for planner/policy/plan
- New module specification with input/output table and data-flow diagram
- Updated world-renderer data-flow/sequence/ownership/source map
- Updated module registry and renderer notes
## Simplicity and naming
- Important names: `StreamingTargetPlanner`, `StreamingTargetPolicy`,
`StreamingTargetPlan`
- Simplest approach: one pure calculator plus two immutable value objects
- Rejected complexity: generic graph planner, priority framework, service
container, job ownership or duplicate available-tile catalog
- Unavoidable complexity: boundary-prefetch cross product must preserve corner
behavior and existing string tile keys
- Measured optimization evidence: no new optimization; exact extraction
## Status
- State: ready-for-review
- Done: extracted immutable policy/plan and pure planner, migrated runtime/editor
target refresh, added behavior/dependency/performance coverage and module docs
- Next: integrated in master; next M03 package extracts the budget scheduler
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-STREAMING-PLANNER-001:ad8fc41 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-STREAMING-PLANNER-001:643850c -->
## Handoff
- Commit: `e887c3b`; cold-start fix `ad8fc41`
- Integration: accepted in master merges `d0f74f8` and `643850c`
- Results: runtime/editor wanted and retained ADT sets plus boundary-prefetch
centers are calculated by scene-free `StreamingTargetPlanner`; streamer keeps
queue ordering, LOD/detail state, jobs, caches, nodes and RIDs.
- Verification: planner `cases=5 iterations=250 average_ms=1.765`; facade
`delegation=3 runtime_scenes=2 tools=3`; focus `contract=1 runtime_scenes=2
capture_tools=3`; coordinate boundary `files=109 consumers=6`; renderer
manifest `checkpoints=7 coverage=7 caches=7`; shutdown ownership passed;
documentation, coordination and diff gates passed.
- Fidelity: center/corner/catalog/clamp/editor fixtures preserve the extracted
formulas; seven-checkpoint dry-run retained all plans. No radius, threshold,
load priority, cache version, placement, material, animation or visibility
rule changed. No build-12340 parity claim is added.
- External/local inputs: ignored native DLL and generated ADT resource scripts
were copied from master for worktree smoke only. Proprietary ADT and character
assets were unavailable, so the dry-run emitted expected degraded-data diagnostics.
- Remaining risks: exact p95/p99 impact still needs an asset-backed baseline;
pathological custom radii are not benchmarked; tile-key strings remain the
existing internal catalog representation; queue/LOD application remains monolithic.
- Documentation updated: new planner module spec with API/input/output/data-flow/
sequence/ownership/recovery/performance/source-map sections; world renderer,
coordinate mapping, module registry and renderer implementation notes updated.
@@ -0,0 +1,118 @@
# M03-RND-TERRAIN-CACHE-SERVICE-001 — Terrain quality mesh cache service
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-CACHE-SERVICE-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-terrain-quality-cache`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Extract the full-quality terrain revisit Mesh cache, its LRU ownership and
source-admission rules from `StreamingWorldLoader` into a small scene-free
`TerrainQualityMeshCache`, preserving all call sites and observable behavior.
## Non-goals
- Move ADT parsing, tile state, quality-upgrade tasks/results or SceneTree/RID finalization.
- Change cache capacity defaults, quality profiles, source priority or cache formats.
- Cache control-splat/splat meshes that the current loader deliberately rejects.
- Extract chunk LOD, terrain material building, ground query or other asset services.
- Add dependencies, persistence, background work or mark the terrain service complete.
## Paths
- Exclusive: `src/render/terrain/terrain_quality_mesh_cache.gd`,
`src/tools/verify_terrain_quality_mesh_cache.gd`,
`docs/modules/terrain-quality-mesh-cache.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_render_runtime_cache_shutdown.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
proprietary renderer corpus
## Contracts and data
- Public service API: `store`, `restore`, `clear`, detached `diagnostic_snapshot`
- Entry: tile key, retained Mesh reference and source string
- Admission: non-empty key, non-null Mesh, source other than
`control_splat_cache`, `splat_cache` or `splat`
- LRU: successful restore touches newest; store touches then trims oldest to
`max(0, capacity)`; invalid restored records are removed
- Persistence/cache format: none; session-memory ownership only
## Dependencies
- Requires: integrated facade contracts master `61bad09`
- Blocks: later terrain service extraction and cache/finalization acceptance audit
- External state: none; tests use synthetic ArrayMesh references
## Verification
- Commands: dedicated cache contract/LRU/ownership regression; streamer source
boundary; shutdown/facade/internal-access/manifest/planner/scheduler and repository gates
- Fixtures: admission/rejection, replacement, LRU touch/eviction, zero/negative
capacity, invalid record cleanup, clear and detached diagnostics
- Fidelity evidence: same sources/capacity/call sites; all seven baseline plans unchanged
- Performance budget: dictionary lookup plus bounded single-entry LRU operations;
no Node, task, file, cache serialization or RID creation
## Documentation deliverables
- Inline public API docs: store/restore/clear/diagnostics and ownership
- Module specification: new terrain quality mesh cache specification
- Data-flow diagram: upgrade result through cache to revisit state
- Sequence/state/dependency diagrams: store/restore/evict/reset and failure paths
- Source map/status updates: module registry, world renderer and `RENDER.md`
## Simplicity and naming
- Important names introduced: `TerrainQualityMeshCache`
- Simplest considered solution: one RefCounted with dictionary plus ordered tile-key array
- Rejected complexity/abstractions: generic cache framework, weak references,
callbacks, Resource cache integration, background eviction or persistence
- Unavoidable complexity and justification: Mesh references must survive unloaded
tile state while LRU keeps revisit memory bounded
- Measured optimization evidence: exact ownership extraction, no optimization claim
## Status
- State: ready-for-review
- Done: scene-free cache ownership/LRU extraction, loader adapter migration,
contract regression and module documentation
- Next: M03 integrator reviews and merges `97480e0`
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-CACHE-SERVICE-001:97480e0 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-CACHE-SERVICE-001:630a0c1 -->
## Handoff
- Commit: `97480e0`
- Results: `TerrainQualityMeshCache` now owns retained full-quality Mesh
references, source admission and oldest-to-newest LRU. The loader retains only
restore-to-state, store-after-upgrade and clear lifecycle adapters.
- Verification: cache `contract_cases=15 loader_calls=3`; facade/entity/ground/
terrain regressions; internal access `private_symbols=42 gameplay=7
editor_sources=9 renderer_tools=1`; manifest `7/7`; shutdown; scheduler
`20000` iterations; planner `250` iterations; focus; editor parse; dry-run;
coordination, documentation and diff gates passed.
- Fidelity: `baked_full` revisit retention, three excluded splat sources, LRU
touch/trim, profile capacities and all call sites remain unchanged. All seven
checkpoint plans remain valid. No visible or build-12340 parity claim is added.
- External/local inputs: ignored native DLL and generated ADT scripts supported
parse/dry-run only; missing proprietary ADT/DBC/character data produced expected diagnostics.
- Remaining risks: entry count does not measure Mesh bytes; ordered-array touch
remains linear; asset-backed revisit flash, memory and p95/p99 evidence remain pending;
terrain build/tasks/state/finalization are still monolithic.
- Documentation updated: new terrain cache API/input-output/data-flow/lifecycle/
sequence/ownership/failure/configuration/migration/diagnostic/source-map spec;
world renderer, registry and `RENDER.md` updated.
- Integration: accepted in master merge `630a0c1`; post-merge cache, facade,
internal-access, manifest, shutdown, scheduler and planner gates passed.
@@ -0,0 +1,104 @@
# M03-RND-TERRAIN-CHUNK-QUEUE-001 — Pure terrain chunk geometry queue planner
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-CHUNK-QUEUE-001:sindo-main-codex:2026-07-19 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-terrain-chunk-queue`
- Lease expires UTC: 2026-07-19
- Integrator: M03 milestone integrator
## Outcome
Extract chunk-geometry create/remove desired-operation calculation from the
streamer into a scene-free `TerrainChunkGeometryQueuePlanner`.
## Non-goals
- Move queue storage/drains, render budgets, tile-LOD queues or async tasks.
- Move chunk Mesh/Node/RID creation/removal or terrain state ownership.
- Change removal-first execution, nearest-first create order or LOD selection.
- Change coordinate conversions, radii, cache formats or quality profiles.
- Add dependencies or mark the broad terrain service checklist complete.
## Paths
- Exclusive: `src/render/terrain/terrain_chunk_geometry_queue_planner.gd`,
`src/tools/verify_terrain_chunk_geometry_queue_planner.gd`,
`docs/modules/terrain-chunk-geometry-queue-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: loader-owned tile-state read model plus immutable `GodotWorldPosition`
- Output: fresh create/remove request arrays; planner retains no queue state
- Removal: current chunks absent from desired state, or all current chunks while
tile LOD is desired
- Creation: missing or LOD-mismatched chunks, sorted by origin XZ distance
- Queue/cache/schema/coordinate versions: unchanged
## Dependencies
- Requires: integrated terrain LOD planner master `b568768`
- Blocks: broader terrain service extraction and dependency/performance acceptance
- External state: none; tests use synthetic tile/chunk dictionaries
## Verification
- Commands: dedicated removal/create/mismatch/tile-fallback/sparse/order and
bounded timing verifier; loader source boundary; facade/cache/LOD/internal-access/
manifest/shutdown/scheduler/streaming planner and repository gates
- Fidelity evidence: extracted branch/order/distance rules and loader drain order
- Performance budget: bounded linear planning plus existing create sort; no engine
resource, task, cache or persistent allocation framework
## Documentation deliverables
- Inline planner API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
failure, performance and source map
- World renderer/module registry/`RENDER.md` updates
## Simplicity and naming
- Important name: `TerrainChunkGeometryQueuePlanner`
- Simplest solution: one stateless planner returning two fresh arrays
- Rejected complexity: generic operation graph, queue class, callback strategy,
spatial index or ownership transfer
- Unavoidable complexity: raw dictionaries remain at the loader/ADT adapter boundary
- Measured optimization evidence: bounded synthetic multi-tile verifier
## Status
- State: accepted
- Done: pure planner, loader queue adoption, semantic/timing regressions and docs
- Next: asset-backed performance acceptance and further terrain-service extraction later in M03
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-CHUNK-QUEUE-001:f800e11 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-CHUNK-QUEUE-001:24aef13 -->
## Handoff
- Commit: `f800e11` (`refactor(M03): extract terrain chunk queue planner`)
- Results: dedicated contract PASS (`cases=8`, `iterations=100`, `235.096 ms`);
LOD/cache/facade/internal-access/manifest/shutdown/scheduler/streaming/focus/
ground/terrain-query/coordinate regressions PASS; internal inventory remains
`42`; editor parse has no script errors; documentation/coordination/diff gates
PASS; checkpoint dry-run planned `7/7` entries with expected missing-private-
corpus diagnostics
- Remaining risks: planner adds a second linear tile-state pass; asset-backed
p95/p99 and visual acceptance remain pending; queue drains, terrain state and
Mesh/Node/RID finalization remain loader-owned
- Documentation updated: `terrain-chunk-geometry-queue-planner.md`, module
registry, `world-renderer.md`, `RENDER.md`, inline API documentation and
data-flow/lifecycle/sequence diagrams
- Integration: accepted in master merge `24aef13`; post-merge planner,
internal-access, facade and manifest gates passed
@@ -0,0 +1,103 @@
# M03-RND-TERRAIN-LOD-PLANNER-001 — Pure terrain chunk LOD planner
<!-- OPENWC_CLAIM:M03-RND-TERRAIN-LOD-PLANNER-001:sindo-main-codex:2026-07-18 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-terrain-chunk-lod-planner`
- Lease expires UTC: 2026-07-18
- Integrator: M03 milestone integrator
## Outcome
Extract chunk-level terrain LOD desired-state calculation from the streamer into
a scene-free `TerrainChunkLodPlanner` with immutable policy and typed focus input.
## Non-goals
- Move chunk/tile queues, loaded state, Mesh/Node/RID creation or budget drains.
- Change radii, chunk size, threshold inclusivity, priority sorting or tile LOD policy.
- Normalize/reorder custom radii or reinterpret negative values.
- Move ADT parsing, quality cache/tasks, materials or ground query.
- Add dependencies, persistence, workers or mark terrain extraction complete.
## Paths
- Exclusive: `src/render/terrain/terrain_chunk_lod_policy.gd`,
`src/render/terrain/terrain_chunk_lod_planner.gd`,
`src/tools/verify_terrain_chunk_lod_planner.gd`,
`docs/modules/terrain-chunk-lod-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated resources, caches and
proprietary renderer corpus
## Contracts and data
- Input: ADT chunk adapter records, immutable `GodotWorldPosition`, immutable
enabled/radii/chunk-size policy
- Output: detached Dictionary mapping source chunk index to LOD `0`, `1` or `2`
- Formula: horizontal distance to `origin + half chunk`; inclusive squared thresholds
- Disabled policy and empty/sparse chunks return absent entries exactly as before
- Queue/cache/schema/coordinate versions: unchanged
## Dependencies
- Requires: integrated terrain cache slice master `ea08b8e`
- Blocks: broader terrain service extraction and dependency/performance acceptance
- External state: none; tests use synthetic chunk adapter records
## Verification
- Commands: dedicated center/boundary/outside/sparse/disabled/custom-radius and
bounded timing verifier; loader source boundary; facade/cache/internal-access/
manifest/shutdown/scheduler/streaming planner and repository gates
- Fidelity evidence: extracted formulas and all seven baseline plans remain unchanged
- Performance budget: bounded linear scan over at most the ADT chunk record count;
no Node, Resource, RID, task, cache entry or allocation framework
## Documentation deliverables
- Inline API docs for policy/planner
- New module spec with inputs/outputs, data-flow, lifecycle, sequence, ownership,
failure, performance and source map
- World renderer/module registry/`RENDER.md` updates
## Simplicity and naming
- Important names: `TerrainChunkLodPolicy`, `TerrainChunkLodPlanner`
- Simplest solution: immutable scalar policy plus one pure linear planner
- Rejected complexity: generic spatial index, quadtree, callback strategy,
normalized profile model or queue ownership
- Unavoidable complexity: raw chunk dictionaries remain at the ADT adapter boundary
- Measured optimization evidence: verifier records bounded High-like planning time
## Status
- State: accepted
- Done: immutable policy, pure planner, both loader call sites, regressions and docs
- Next: asset-backed performance acceptance and further terrain-service extraction later in M03
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-TERRAIN-LOD-PLANNER-001:eb94cfc -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-TERRAIN-LOD-PLANNER-001:f36fabb -->
## Handoff
- Commit: `eb94cfc` (`refactor(M03): extract terrain chunk LOD planner`)
- Results: dedicated contract PASS (`cases=7`, `iterations=250`, `12.726 ms`);
cache/facade/internal-access/manifest/shutdown/scheduler/streaming-focus/
streaming-planner/ground/terrain-query/coordinate regressions PASS; editor parse
has no script errors; documentation and coordination gates PASS; checkpoint
dry-run planned `7/7` entries with expected missing-private-corpus diagnostics
- Remaining risks: asset-backed p95/p99 and visual acceptance remain pending;
terrain state, queue, quality-upgrade and finalization ownership remains in loader
- Documentation updated: `terrain-chunk-lod-planner.md`, module registry,
`world-renderer.md`, `RENDER.md`, inline public API documentation and data-flow,
lifecycle and sequence diagrams
- Integration: accepted in master merge `f36fabb`; post-merge planner, facade,
internal-access and manifest gates passed
@@ -0,0 +1,106 @@
# M03-RND-WMO-PLACEMENT-REGISTRY-001 — WMO placement registry
<!-- OPENWC_CLAIM:M03-RND-WMO-PLACEMENT-REGISTRY-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-placement-registry`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract WMO placement-key to tile-reference ownership from the loader while
leaving every Node and renderer-resource lifecycle in the loader.
## Non-goals
- Change placement identity rules or WMO transforms.
- Move Node maps, world roots, tile state, jobs, queues or cancellation.
- Move cache/load requests, material refresh or render-group building.
- Change duplicate ordering, global-WMO lifetime or build budgets.
- Claim asset-backed WMO fidelity or performance acceptance.
## Paths
- Exclusive: `src/render/wmo/wmo_placement_registry.gd`,
`src/tools/verify_wmo_placement_registry.gd`,
`docs/modules/wmo-placement-registry.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/wmo-placement-resolver.md`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- Input: resolved WMO placement key and tile/global reference key
- `add_reference` creates a key/ref set or idempotently retains an existing ref
- `release_reference` erases only the supplied ref and returns true only when the
final ref removes the placement key
- `contains`, `active_count`, detached sorted diagnostics and idempotent `clear`
- Loader retains key→Node mapping and reacts to final release by cancelling the
render build and queue-freeing the corresponding Node
- Cache/schema/coordinate/profile versions: unchanged
## Dependencies
- Requires: current master `5aa40d6`, `WmoPlacementResolver` identity contract
- Blocks: WMO build/resource state decomposition
- External state: none; tests use synthetic placement/tile keys
## Verification
- Commands: dedicated add/shared/idempotent/non-owner/final/global/clear/
diagnostics/source/timing verifier; resolver/material/shutdown plus M2/terrain/
facade/internal-access/manifest/scheduler/streaming/coordinate/docs/coord gates
- Fidelity evidence: exact shared-ref and final-release behavior preserved
- Performance budget: dictionary membership over active WMO keys/refs; no Node,
Resource, RID, task, queue, mutex, cache or file access
## Documentation deliverables
- Inline registry API documentation
- New module spec with inputs/outputs, data-flow, lifecycle/state, ownership,
failure/recovery, performance, fidelity limitations and source map
- Placement resolver, renderer/module registry and `RENDER.md` updates
## Simplicity and naming
- Important name: `WmoPlacementRegistry`
- Simplest solution: one private key-to-reference-set Dictionary
- Rejected complexity: generic ref-count registry, signals, callbacks, Resources,
weak refs or Node ownership inside the service
- Unavoidable complexity: loader keeps a parallel key-to-Node map
- Measured optimization evidence: bounded synthetic add/release loop
## Status
- State: accepted
- Done: registry extracted; global/tile adapters migrated; Node/resource/job
ownership retained in loader; contract, regressions, dry-run and docs passed
- Next: WMO build/resource state extraction and asset-backed placement evidence
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-PLACEMENT-REGISTRY-001:c590036 -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-PLACEMENT-REGISTRY-001:f9e5a4f -->
## Handoff
- Commit: `c590036`
- Results: `verify_wmo_placement_registry.gd` PASS cases=10,
iterations=100, elapsed_ms=53.941; 21 adjacent WMO/M2/terrain/facade/
scheduler/streaming/coordinate regressions PASS; documentation and
coordination gates PASS; render dry-run PASS 7/7.
- Remaining risks: no proprietary asset corpus or original-client paired WMO
capture; dry-run reports expected missing ADT/DBC/character assets; WMO
build/resource state remains loader-owned.
- Documentation updated: `docs/modules/wmo-placement-registry.md`,
`docs/modules/wmo-placement-resolver.md`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`; inline public API comments added.
- Integration: accepted in master merge `f9e5a4f`; post-merge registry
(`cases=10`, `iterations=100`, `elapsed_ms=54.178`), resolver, runtime-cache
shutdown, facade, documentation and coordination gates passed.
@@ -0,0 +1,110 @@
# M03-RND-WMO-PLACEMENT-RESOLVER-001 — WMO placement resolver
<!-- OPENWC_CLAIM:M03-RND-WMO-PLACEMENT-RESOLVER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-placement-resolver`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract pure WMO cache-path normalization, placement identity and world-transform
resolution from the loader into a scene-free service.
## Non-goals
- Change WDT/ADT coordinate conversion or WMO placement values.
- Own WMO registry references, tile lifecycle, jobs, queues or retries.
- Load cached/live WMO resources or validate cache format versions.
- Create Nodes, Mesh/MultiMesh, materials or RIDs.
- Change portal/room/material/occlusion behavior or claim WMO parity.
## Paths
- Exclusive: `src/render/wmo/wmo_placement_resolver.gd`,
`src/tools/verify_wmo_placement_resolver.gd`,
`docs/modules/wmo-placement-resolver.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- `normalize_relative_path`: replace backslashes, lowercase entire cache key
- `resolve_unique_key`: positive `unique_id` becomes `uid:<decimal>`; otherwise
preserve `tile:<tile-key>:<placement-index>` fallback
- `resolve_world_transform`: defaults position/rotation to zero and scale to one;
compose the same Godot Euler basis and un-clamped uniform scale
- Three transform consumers: render-cache root, cached scene and live prototype
- Cache/schema/coordinate/profile versions: unchanged
## Dependencies
- Requires: current master `4f40c77`
- Blocks: WMO registry/build decomposition
- External state: none; tests use synthetic paths and placement dictionaries
## Verification
- Commands: dedicated path/identity/default/rotation/scale/source and bounded
timing verifier; M2/terrain/facade/internal-access/manifest/shutdown/scheduler/
streaming/coordinate/documentation/coordination gates
- Fidelity evidence: exact normalization, identity and property-transform rules
moved unchanged
- Performance budget: constant-time string/value math; no Node, Resource, RID,
task, queue, mutex, cache or file access
## Documentation deliverables
- Inline resolver API documentation
- New module spec with inputs/outputs, data-flow, lifecycle, ownership, failure,
performance, fidelity limitations and source map
- Renderer/module registry and `RENDER.md` updates
## Simplicity and naming
- Important name: `WmoPlacementResolver`
- Simplest solution: one stateless service with three explicit pure queries
- Rejected complexity: placement class hierarchy, generic model resolver,
Resources, strategies, callbacks or registry framework
- Unavoidable complexity: raw placement Dictionary remains parser boundary
- Measured optimization evidence: bounded synthetic resolver loop
## Status
- State: accepted
- Done: resolver, seven loader adapters, regressions and documentation verified
- Next: WMO registry/build extraction and asset-backed placement evidence
- Blocked by:
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-PLACEMENT-RESOLVER-001:05e216a -->
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-PLACEMENT-RESOLVER-001:d2cb52f -->
## Handoff
- Commit: `05e216a` (`refactor(M03): extract WMO placement resolver`)
- Results: dedicated resolver PASS (`cases=9`, `iterations=20000`,
`elapsed_ms=28.332`), including historical Node3D property equivalence;
renderer materials, M2 planner/grouper/transform/registry/dedupe, terrain
cache/LOD/queue, facade, internal-access, manifest, shutdown, scheduler,
streaming planner/focus, terrain-query and coordinate-boundary suites PASS;
editor import parsed with only expected missing private corpus diagnostics;
documentation PASS (`module_specs=19`, `required_files=7`); coordination PASS
with 23 pre-existing expired M00/M01 warnings; checkpoint dry-run planned `7/7`.
- Remaining risks: no private asset-backed WMO placement/visual/p95/p99 evidence;
old caches without positive UID retain per-tile fallback; raw Dictionaries and
WMO registry/build/resource state remain loader-owned; scale stays unclamped.
- Documentation updated: inline public API; new
`docs/modules/wmo-placement-resolver.md`; renderer module registry,
`docs/modules/world-renderer.md` and `RENDER.md`.
- Integration: accepted in master merge `d2cb52f`; post-merge resolver
(`cases=9`, `iterations=20000`, `elapsed_ms=27.919`), renderer materials,
shutdown, internal-access, facade, manifest, documentation and coordination
gates passed.
@@ -0,0 +1,108 @@
# M03-RND-WMO-RENDER-BUILD-PLANNER-001 — WMO render build step planner
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-BUILD-PLANNER-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-render-build-planner`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract the pure one-operation WMO render build cursor decision from the loader
while preserving mesh-before-MultiMesh ordering and one budget permit per group.
## Non-goals
- Move Nodes, Resources, materials, jobs, queues, cancellation or permits.
- Change WMO placement, cache loading, transforms, visibility or shadows.
- Change build ordering, group names, editor ownership or finalization thread.
- Change cache formats, settings, presets or renderer diagnostics.
- Claim asset-backed WMO fidelity or p95/p99 acceptance.
## Paths
- Exclusive: `src/render/wmo/wmo_render_build_step_planner.gd`,
`src/tools/verify_wmo_render_build_step_planner.gd`,
`docs/modules/wmo-render-build-step-planner.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- Input: mesh count/index and MultiMesh count/index
- Output: `mesh`, `multimesh` or `complete` operation, selected index and next
cursor values
- Mesh is selected while its cursor is below count; MultiMesh is selected only
after mesh exhaustion; complete follows both exhausted ranges
- Loader retains cursor adoption, Resource array access, Node creation, material
refresh, ownership, queue cancellation and budget permit consumption
- Cache/schema/coordinate/profile versions: unchanged
## Dependencies
- Requires: current master `1e73e59`, accepted WMO placement registry/resolver
- Blocks: further WMO render build state decomposition
- External state: none; verification uses scalar synthetic fixtures
## Verification
- Commands: dedicated ordering/cursor/empty/raw-edge/source/timing verifier;
WMO registry/resolver/material/shutdown plus M2/terrain/facade/internal-access/
manifest/scheduler/streaming/coordinate/docs/coord gates
- Fidelity evidence: exact mesh-first, one-step cursor behavior preserved
- Performance budget: 20,000 pure plans under 1 second; no Node, Resource, RID,
task, queue, mutex, cache or file access
## Documentation deliverables
- Inline public planner API documentation
- New module spec with inputs/outputs, data flow, sequence, ownership,
failure/recovery, performance/fidelity limits and source map
- Renderer module registry, world renderer and `RENDER.md` updates
## Simplicity and naming
- Important name: `WmoRenderBuildStepPlanner`
- Simplest solution: one stateless function and three operation constants
- Rejected complexity: job class, iterator, generic cursor framework, callbacks,
signals or ownership of engine objects
- Unavoidable complexity: loader still has separate mesh/MultiMesh materializers
- Measured optimization evidence: bounded synthetic planning loop
## Status
- State: accepted
- Done: planner and loader adapter implemented; Node/Resource/material/job/queue/
permit ownership retained in loader; regressions, dry-run and docs passed
- Next: WMO build job/resource ownership or liquid service extraction
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-BUILD-PLANNER-001:c06aed5 -->
## Handoff
- Commit: `3a6b1d3`
- Results: dedicated planner PASS (`cases=9`, `iterations=20000`,
`elapsed_ms=11.914`); WMO registry/resolver/material/shutdown and 18 adjacent
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Remaining risks: no proprietary WMO corpus, original-client paired capture or
traversal p95/p99; dry-run reports expected missing ADT/DBC/character assets;
WMO Resource arrays and untyped build jobs/queues remain loader-owned.
- Documentation updated: inline planner API;
`docs/modules/wmo-render-build-step-planner.md`, module registry,
`docs/modules/world-renderer.md`, `RENDER.md` with data-flow and sequence
diagrams, ownership, failure/recovery and source map.
- Integration: accepted in master merge `c06aed5`; post-merge planner
(`cases=9`, `iterations=20000`, `elapsed_ms=11.059`), WMO registry/resolver,
runtime-cache shutdown, facade, documentation and coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-BUILD-PLANNER-001:3a6b1d3 -->
@@ -0,0 +1,117 @@
# M03-RND-WMO-RENDER-BUILD-QUEUE-001 — WMO render build queue
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-BUILD-QUEUE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-render-build-queue`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract typed lightweight-WMO render build jobs and their FIFO key queue from
the loader while leaving all engine-object destruction and materialization in
the loader.
## Non-goals
- Queue-free Nodes or free Resources/RIDs.
- Move material refresh, Node creation, editor ownership or budget permits.
- Change duplicate enqueue, stale-key cleanup, cancel or clear ordering.
- Change placement, cache loading/versioning, transforms, shadows or visibility.
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
## Paths
- Exclusive: `src/render/wmo/wmo_render_build_job.gd`,
`src/render/wmo/wmo_render_build_queue.gd`,
`src/tools/verify_wmo_render_build_queue.gd`,
`docs/modules/wmo-render-build-queue.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`docs/modules/wmo-render-build-step-planner.md`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- Typed job: placement key, strong Node3D/Resource references and two cursors
- FIFO queue preserves duplicate key append and first-occurrence erase behavior
- Stale front keys remain observable for loader-compatible pop-and-continue
- Cancel releases the keyed job reference and first queued key only
- Clear releases every job/key reference idempotently; never frees engine objects
- Loader retains validity checks, planner invocation, cursor adoption,
materialization, permits, cancellation reactions and Node destruction
- Cache/schema/coordinate/profile versions: unchanged
## Dependencies
- Requires: current master `1e740aa`, accepted WMO build step planner
- Blocks: further WMO cache/load and materialization decomposition
- External state: none; verification uses synthetic Node3D/Resource instances
## Verification
- Commands: dedicated invalid/FIFO/duplicate/stale/cursor/cancel/clear/
diagnostics/source/timing verifier; WMO planner/registry/resolver/material/
shutdown plus M2/terrain/facade/internal-access/manifest/scheduler/streaming/
coordinate/docs/coord gates
- Fidelity evidence: exact current queue/dictionary behavior preserved
- Performance budget: 100 cycles over 256 jobs under 1 second
## Documentation deliverables
- Inline job/queue API documentation
- New module spec with inputs/outputs, data flow, state/sequence, ownership,
threading, failure/recovery, performance/fidelity limits and source map
- Build-step planner, renderer module registry, world renderer and RENDER updates
## Simplicity and naming
- Important names: `WmoRenderBuildJob`, `WmoRenderBuildQueue`
- Simplest solution: one typed record plus Dictionary/Array queue composition
- Rejected complexity: generic job scheduler, signals, callbacks, mutexes,
Resources as job records or engine lifetime callbacks
- Unavoidable complexity: duplicate keys can intentionally leave stale entries
- Measured optimization evidence: bounded synthetic enqueue/cancel/clear loop
## Status
- State: accepted
- Done: typed job/queue implemented; three clear sites, metrics, enqueue, drain and
cancel migrated; engine destruction/materialization retained in loader
- Next: WMO cache/load state or liquid service extraction
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-BUILD-QUEUE-001:60603e1 -->
## Handoff
- Commit: `e576ae2` (implementation `e1bb105` plus cold-load fix)
- Results: queue PASS (`cases=11`, `iterations=100`, `elapsed_ms=59.874`);
WMO planner/registry/resolver/material/shutdown and 18 adjacent M2/terrain/
facade/scheduler/streaming/coordinate regressions PASS; documentation and
coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Cold-load recovery: post-merge smoke exposed an editor class-cache dependency;
typed queue accessors removed the loader/global-class dependency and a fresh
run without `.godot` passed (`cases=11`, `elapsed_ms=57.835`).
- Remaining risks: strong Node/Resource references require every loader cancel/
clear path; duplicate enqueue intentionally preserves stale FIFO keys; no
proprietary WMO corpus, original-client paired capture, leak run or traversal
p95/p99; dry-run reports expected missing ADT/DBC/character assets.
- Documentation updated: inline job/queue API;
`docs/modules/wmo-render-build-queue.md`, build-step planner spec, module
registry, world renderer and `RENDER.md`; data-flow/state/sequence diagrams,
ownership, threading, failure/recovery and source map included.
- Integration: implementation merged as `a878e7c`; its first post-merge smoke
exposed and rejected a cold global-class registration dependency. Fix merged
as `60603e1`; post-fix master queue (`cases=11`, `iterations=100`,
`elapsed_ms=56.454`), planner, registry, resolver, shutdown, facade,
documentation and coordination gates passed without parse diagnostics.
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-BUILD-QUEUE-001:e576ae2 -->
@@ -0,0 +1,115 @@
# M03-RND-WMO-RENDER-RESOURCE-CACHE-001 — WMO render resource cache state
<!-- OPENWC_CLAIM:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-render-resource-cache`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract lightweight-WMO render Resource cache, negative cache and asynchronous
request bookkeeping from the loader while preserving loader-owned I/O and
cache-format validation.
## Non-goals
- Call `ResourceLoader`, construct cache paths or inspect files.
- Validate `WMOStreamingResource` script identity or format version.
- Own cached WMO scenes, live prototypes, placement/build jobs or Nodes/RIDs.
- Change fallback order, retry behavior, quality profiles or visual output.
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
## Paths
- Exclusive: `src/render/wmo/wmo_render_resource_cache_state.gd`,
`src/tools/verify_wmo_render_resource_cache_state.gd`,
`docs/modules/wmo-render-resource-cache-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_render_runtime_cache_shutdown.gd`,
`docs/modules/wmo-render-build-queue.md`, `docs/modules/world-renderer.md`,
`docs/modules/README.md`, `RENDER.md`, `targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- State keys are loader-normalized relative WMO paths copied as `String`.
- Cached values are already validated `Resource` references.
- Pending values are cache paths for loader-owned `ResourceLoader` requests.
- Terminal request removal precedes validated-resource or missing-state adoption.
- Request/missing reset preserves accepted resources; full clear releases all.
- Cache/schema/coordinate/profile versions and validation rules remain unchanged.
## Dependencies
- Requires: current master `c954704`, accepted WMO render build queue
- Blocks: further WMO cached-scene/load and materialization decomposition
- External state: none; verification uses synthetic Resources and paths
## Verification
- Commands: dedicated lifecycle/request/cache/reset/diagnostics/source/timing
verifier; WMO queue/planner/registry/resolver/material/shutdown plus adjacent
renderer/streaming/coordinate/documentation/coordination gates
- Fixtures: synthetic Resource instances and normalized relative/cache paths
- Fidelity evidence: exact current cache/missing/request transitions preserved
- Performance budget: 100 cycles over 256 resource/request keys under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state and loader interaction sequence diagrams
- World-renderer, module registry, WMO queue and RENDER status updates
## Simplicity and naming
- Important names introduced: `WmoRenderResourceCacheState`
- Simplest considered solution: one RefCounted around three Dictionaries
- Rejected complexity/abstractions: generic cache, callbacks, signals, mutexes,
ResourceLoader wrapper, persistence layer or shared WMO/M2 cache hierarchy
- Unavoidable complexity and justification: partial reset must retain validated
Resources while discarding pending/negative state at map reset and shutdown
- Measured optimization evidence: bounded synthetic store/request/reset loop
## Status
- State: accepted
- Done: state holder implemented; loader cache/request/reset/metrics adapters
migrated; shutdown regression and required documentation updated
- Next: cached WMO scene/load state or liquid service extraction
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:b4955d6 -->
## Handoff
- Commit: `d39f591`
- Results: cache-state PASS (`cases=10`, `iterations=100`,
`elapsed_ms=58.727`); direct no-`.godot` cache-state run PASS (`45.361ms`);
WMO queue/planner/registry/resolver/material/shutdown plus 18 adjacent
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Remaining risks: accepted Resources remain strongly referenced until full
shutdown release; cached-scene and ResourceLoader I/O seams remain in loader;
no proprietary corrupt-cache fixture, asset-backed WMO traversal/leak run,
p95/p99 or original-client paired evidence. Full loader cannot be used as a
direct empty-class-cache smoke because pre-existing typed global coordinate/
focus dependencies require editor class registration; the extracted service
itself passed cold and normal editor parse passed.
- Documentation updated: inline public API; new
`docs/modules/wmo-render-resource-cache-state.md`; module registry, WMO queue,
world renderer and `RENDER.md`; data-flow/state/sequence diagrams, ownership,
threading, cache-version boundary, failure/recovery and source map included.
- Integration: merged as `b4955d6`; post-merge cache state (`cases=10`,
`iterations=100`, `elapsed_ms=51.949`), shutdown, WMO queue/resolver, facade,
internal-access (`private_symbols=41`), `7/7` manifest, documentation and
coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-RENDER-RESOURCE-CACHE-001:d39f591 -->
@@ -0,0 +1,117 @@
# M03-RND-WMO-SCENE-RESOURCE-CACHE-001 — WMO cached-scene resource state
<!-- OPENWC_CLAIM:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:sindo-main-codex:2026-07-20 -->
## Ownership
- Target: M03
- Program: RND
- Owner/Agent ID: sindo-main-codex
- Branch: `work/sindo-main-codex/m03-wmo-scene-resource-cache`
- Lease expires UTC: 2026-07-20
- Integrator: M03 milestone integrator
## Outcome
Extract cached-WMO `PackedScene`, negative-cache and asynchronous request state
from the loader while preserving loader-owned file limits, I/O and scene-cache
version validation.
## Non-goals
- Call `ResourceLoader`/`FileAccess`, construct `.tscn` paths or measure files.
- Instantiate scenes or validate WMO builder cache metadata/version.
- Own live WMO prototypes, placement/build jobs, instantiated Nodes or RIDs.
- Change missing/oversize/request-error fallback behavior or debug output.
- Claim asset-backed WMO fidelity, leak acceptance or traversal p95/p99.
## Paths
- Exclusive: `src/render/wmo/wmo_scene_resource_cache_state.gd`,
`src/tools/verify_wmo_scene_resource_cache_state.gd`,
`docs/modules/wmo-scene-resource-cache-state.md`, this claim
- Shared/hotspots: `src/scenes/streaming/streaming_world_loader.gd`,
`src/tools/verify_render_runtime_cache_shutdown.gd`,
`docs/modules/wmo-render-resource-cache-state.md`,
`docs/modules/world-renderer.md`, `docs/modules/README.md`, `RENDER.md`,
`targets/03-renderer-facade.md`
- Generated/ignored: `.godot`, native DLL, generated ADT/WMO resources, caches
and proprietary renderer corpus
## Contracts and data
- State keys are loader-normalized relative WMO paths copied as `String`.
- Cached values are loader-validated `PackedScene` references.
- Pending values are `.tscn` paths for loader-owned threaded requests.
- Missing state accepts absent file, oversize, request-start, load and validation failures.
- Terminal pending removal precedes validated-scene or missing adoption.
- Transient clear retains accepted scenes; full clear releases all state.
- Cache metadata/version, file-size limit, profile and fallback rules are unchanged.
## Dependencies
- Requires: current master `e690756`; accepted WMO render Resource cache state
- Blocks: further WMO I/O/fallback and materialization decomposition
- External state: none; verification uses synthetic PackedScenes and paths
## Verification
- Commands: dedicated lifecycle/request/missing/reset/diagnostics/source/timing
verifier; WMO render-cache/queue/planner/registry/resolver/material/shutdown plus
adjacent renderer/streaming/coordinate/documentation/coordination gates
- Fixtures: synthetic PackedScene instances and normalized relative/cache paths
- Fidelity evidence: exact current cached-scene/missing/request transitions preserved
- Performance budget: 100 cycles over 256 scene/request keys under 1 second
## Documentation deliverables
- Inline public API docs
- Module specification with inputs/outputs, ownership and source map
- Data-flow, lifecycle/state and loader interaction sequence diagrams
- World-renderer, module registry, render-cache and RENDER status updates
## Simplicity and naming
- Important names introduced: `WmoSceneResourceCacheState`
- Simplest considered solution: one RefCounted around three Dictionaries
- Rejected complexity/abstractions: generic cache hierarchy, callbacks, signals,
mutexes, ResourceLoader wrapper, persistence layer or render/scene cache merger
- Unavoidable complexity and justification: missing can be recorded without a
pending request, and partial reset must retain validated PackedScenes
- Measured optimization evidence: bounded synthetic store/request/reset loop
## Status
- State: accepted
- Done: state holder implemented; loader lookup/request/drain/reset/metrics
adapters migrated; shutdown regression and required documentation updated
- Next: WMO I/O/live fallback or liquid service extraction
- Blocked by:
<!-- OPENWC_INTEGRATION:ACCEPTED:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:ada0fd9 -->
## Handoff
- Commit: `c839294`
- Results: scene cache-state PASS (`cases=11`, `iterations=100`,
`elapsed_ms=40.768`; initial direct cold run `43.636ms`); render cache-state,
WMO queue/planner/registry/resolver/material/shutdown plus 18 adjacent
M2/terrain/facade/scheduler/streaming/coordinate regressions PASS;
documentation and coordination gates PASS; checkpoint dry-run PASS `7/7`.
- Remaining risks: accepted PackedScenes remain strongly referenced until full
shutdown release; ResourceLoader/FileAccess I/O, cache-validation probes and
live fallback remain in loader; no proprietary stale/oversize fixture,
asset-backed traversal/leak run, p95/p99 or original-client paired evidence.
The extracted scene state passed cold; full loader cold execution still needs
editor class registration for pre-existing focus/coordinate global classes.
- Documentation updated: inline public API; new
`docs/modules/wmo-scene-resource-cache-state.md`; render-cache spec, module
registry, world renderer and `RENDER.md`; data-flow/state/sequence diagrams,
ownership, threading, file-size/cache-version boundary, failure/recovery and
source map included.
- Integration: merged as `ada0fd9`; post-merge scene state (`cases=11`,
`iterations=100`, `elapsed_ms=40.113`), render state (`43.434ms`), shutdown,
WMO queue/resolver, facade, internal-access (`private_symbols=40`), `7/7`
manifest, documentation and coordination gates passed.
<!-- OPENWC_HANDOFF:READY:M03-RND-WMO-SCENE-RESOURCE-CACHE-001:c839294 -->
+6 -1
View File
@@ -36,7 +36,12 @@ TrinityCore/AzerothCore ◄── Network Adapter ◄── Runtime Client
### Domain
Содержит идентификаторы, value objects, команды и события: `EntityId`, `WowGuid`, `MapPosition`, `SpellId`, `MoveIntent`, `EntityMoved`. Не содержит side effects.
Содержит идентификаторы, value objects, команды и события: stable authored
`ContentId`, session-local `EntityId`, opaque wire `WowGuid`, namespaced
`ServerEntryId`, typed coordinates, `SpellId`, `MoveIntent`, `EntityMoved`.
Эти identity namespaces не конвертируются неявно и не содержат side effects;
их lifecycle и mapping определены в
[`modules/domain-identities.md`](modules/domain-identities.md).
### Gameplay
+19
View File
@@ -237,6 +237,23 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
- Медленный понятный reference implementation полезен как oracle для optimized implementation.
- Сложная оптимизация удаляется или упрощается, если она не даёт измеримого результата.
## Coordinate conversion boundary
- Cross-space world conversions выполняются только через typed API
`CoordinateMapper`; ручные world-center offsets, swaps осей и вычисление ADT
filename indices из Godot position запрещены.
- Scene/render adapter МОЖЕТ создать `Vector3` только после получения
`GodotWorldPosition` или перевести engine vector в typed scalar value на своей границе.
- Единственная native world-position формула находится в
`src/native/src/wow_chunk_reader.h`, поскольку parser workers не вызывают
GDScript. Новая native копия требует отдельного boundary decision и parity fixture.
- `verify_render_coordinate_calibration.gd` остаётся независимым test oracle;
production code не копирует его формулы.
- Model-local M2/WMO vertex basis и placement orientation не являются world
coordinate conversion. Они документируются и тестируются своим format adapter.
- `verify_coordinate_conversion_boundaries.gd` обязателен при изменении
coordinates, native parsers, sky, player, streaming или renderer diagnostics.
## Review checklist
- Можно ли понять назначение сущности по имени?
@@ -245,6 +262,8 @@ TODO/FIXME обязан содержать target/work-package ID или diagnos
- Можно ли убрать abstraction без потери correctness/boundary?
- Есть ли скрытый mutable state или side effect?
- Не появился ли второй источник истины?
- Все ли cross-space world coordinates проходят через `CoordinateMapper` или
документированную native boundary?
- Измерена ли сложная optimization?
- Изолирована и документирована ли неизбежная сложность?
- Соответствуют ли API docs и diagrams фактическому коду?
+19 -1
View File
@@ -10,7 +10,10 @@
.\tools\run_render_baseline.ps1
```
Скрипт последовательно проверяет headless-загрузку проекта, renderer materials, M2 unique-id dedupe, regression manifest и затем снимает checkpoint-ы. Для CI или быстрой проверки контракта без PNG:
Скрипт последовательно проверяет headless-загрузку проекта, renderer materials,
M2 unique-id dedupe, regression manifest, coordinate boundaries и dedicated
server-spawn renderer contract, затем снимает checkpoint-ы. Для CI или быстрой
проверки контракта без PNG:
```powershell
.\tools\run_render_baseline.ps1 -DryRun -WaitSeconds 0.1 -MeasureSeconds 0.1
@@ -18,6 +21,21 @@
Если `godot` отсутствует в `PATH`, скрипт использует локальный executable, указанный в `RENDER.md`. Другой executable задаётся через `-GodotPath`.
Отдельный M01 server-spawn checkpoint использует тот же capture pipeline, но не
расширяет семичастный M00 visual baseline:
```powershell
godot --headless --path . --script res://src/tools/verify_server_spawn_renderer.gd
godot --path . --script res://src/tools/capture_render_checkpoints.gd -- `
--manifest res://src/tools/server_spawn_render_manifest.json `
--output user://server_spawn_renderer
```
Manifest связывает pinned AzerothCore Human Warrior spawn с Godot position,
ADT tile/chunk и renderer-native diagnostic marker. Маркер начинается точно в
spawn origin и проходит обычный depth test, поэтому screenshot показывает его
отношение к terrain; это integration evidence, а не заявление visual parity.
Результат по умолчанию находится в `user://render_baseline`:
- `report.json` — Godot, OS, CPU, GPU/backend, viewport, revision, cache inventory, cache versions, параметры checkpoint-ов и результаты;
@@ -0,0 +1,183 @@
# ADR 0001: Canonical world coordinate contract
- Status: Accepted for M01 contract implementation
- Date: 2026-07-13
- Work packages: `M01-FND-COORDS-001`, corrected by `M01-FND-COORD-TILE-AXIS-002`
- Decision owners: M01 foundation/integration owners
## Context
OpenWC currently performs coordinate conversion in native ADT/WDT loaders,
`StreamingWorldLoader`, sky, player and renderer diagnostic scripts. The same
numbers are frequently carried in `Vector3` or dictionaries, so the coordinate
space is implicit. Existing names also mix ADT filename indices, renderer X/Z
indices and MDDF/MODF placement fields.
M00 recorded five positions observed in WoW 3.3.5a build 12340. They verify the
current client-position to Godot-position arithmetic within `0.002` yard, but
they do not define a cross-layer contract and do not prove camera composition
or visual parity.
Independent references agree on a 64 by 64 ADT grid and an approximately
`533.3333` yard tile size. They do not all use the word “canonical” for the same
axis order. OpenWC therefore needs names tied to observable formats instead of
adopting an internal convention from one reference project.
## Decision
### Canonical position
`CanonicalWowWorldPosition(X, Y, Z)` preserves the numeric X/Y/Z order used by
the WoW client, movement protocol and TrinityCore/AzerothCore world positions.
X and Y are horizontal; Z is height. Units are WoW yards.
`ServerWorldPosition` is a distinct type but converts component-for-component
to canonical. The type boundary prevents a server record from being passed
directly to renderer code and leaves room for adapter validation without a
silent axis swap.
### World extent and ADT indices
The contract uses:
```text
ADT_TILE_SIZE_YARDS = 1600 / 3
ADT_TILES_PER_MAP_AXIS = 64
WOW_WORLD_HALF_EXTENT_YARDS = 32 * ADT_TILE_SIZE_YARDS
tileX = floor(32 - canonical.Y / ADT_TILE_SIZE_YARDS)
tileY = floor(32 - canonical.X / ADT_TILE_SIZE_YARDS)
```
`AdtTileCoordinate(tile_x, tile_y)` always means the indices in
`Map_tileX_tileY.adt`. Results are intentionally not clamped: the exact
south-east extent produces index 64 and remains diagnosable as out of bounds.
Normal valid indices are 0 through 63.
An `AdtTileLocalPosition` is measured east and south from the tile's
north-west corner. Filename `tile_x` and MCNK `chunk_x` grow east; filename
`tile_y` and MCNK `chunk_y` grow south, with valid indices 0 through 15. These
direction names avoid pretending that ADT file axes are Godot X/Z axes.
### ADT placement fields
MDDF/MODF position fields are represented in on-disk X/height/Z order:
```text
adt.X = halfExtent - canonical.X
adt.height = canonical.Z
adt.Z = halfExtent - canonical.Y
```
Model-local MDDF/MODF Euler corrections are not world orientation. They remain
outside this contract until a placement-orientation package has format- and
model-specific fixtures.
### Godot renderer basis
The compatibility renderer keeps one Godot unit equal to one WoW yard and uses
Y-up coordinates from the north-west map extent:
```text
godot.X = halfExtent - canonical.Y # east
godot.Y = canonical.Z # up
godot.Z = halfExtent - canonical.X # south
```
`GodotWorldPosition` contains scalars rather than `Vector3`. A scene/render
adapter may construct `Vector3` only at its boundary.
### World yaw
Server/canonical yaw zero faces increasing WoW X; positive yaw turns toward
increasing WoW Y. In the mapped Godot basis, Godot forward `-Z` and positive
Y-axis yaw produce the same numeric world angle. Mapper conversions therefore
normalize the value to `[-PI, PI)` without adding an offset.
This identity applies to world-facing yaw only. It does not apply to M2/WMO
asset axes or placement Euler triples.
### Contract version 2 correction evidence
Contract version 1 incorrectly calculated filename `tile_x` from canonical X
and `tile_y` from canonical Y. The private WotLK corpus contains the observed
Elwynn point in `Azeroth_31_49.adt`, SHA-256
`18185B5FAC9566D9C57A9C19ABD75F108370D1795639B6077167C0EFD3672FAE`; the
transposed `Azeroth_49_31.adt` does not exist. Raw MCNK headers in that file
show `indexX` decreasing world Y (east) and `indexY` decreasing world X (south).
OpenRealm independently calculates filename X from world Y and filename Y from
world X. Contract version 2 corrects the formulas before any consumer migration.
### Enforced conversion boundaries
GDScript production consumers use `CoordinateMapper` for canonical/Godot and
Godot/ADT tile-local conversion. `StreamingWorldLoader`, player spawn/terrain
sampling, sky/area lookup and terrain diagnostics may construct `Vector3` only
at their scene/tool boundary.
The native ADT/WDT parser path retains one equivalent formula definition in
`src/native/src/wow_chunk_reader.h`; worker parsers cannot call GDScript. The
M00 renderer calibration keeps one independent formula as a test oracle so a
shared mapper defect cannot make both implementation and evidence agree.
Repository verification rejects additional world-center, world/tile or named
WoW/Godot formulas outside those explicit exceptions. Model-local M2/WMO basis
transforms are separate format contracts and are not classified as world-space mapping.
## Alternatives considered
### Make renderer coordinates canonical
Rejected because protocol, database and ADT adapters would silently inherit a
presentation-specific offset and Y-up basis.
### Adopt WoWee's internal canonical axis names unchanged
Rejected because that reference swaps server X/Y into its chosen north/west
ordering. OpenWC preserves observable client/server field names and makes the
render/ADT transformations explicit instead.
### Use `Vector3` plus comments
Rejected because comments cannot prevent server, ADT and Godot values from
being passed across the wrong boundary.
### Generic vector-space or matrix framework
Rejected as unnecessary complexity for fixed affine transforms and integer
grid ownership rules.
## Consequences
- Network and server-data adapters can expose typed positions without knowing
Godot.
- Renderer consumers must explicitly map and then create Godot vectors at their
own boundary.
- ADT filename indices can no longer be confused with renderer X/Z tile names.
- GDScript world/tile consumers use mapper APIs; renderer distance and LOD math
remains ordinary Godot-space geometry.
- Native ADT/WDT loaders share one boundary formula and the M00 calibration
keeps one independent non-production oracle.
- New manual axis conversions are review/test violations enforced by the
repository-wide source gate.
- Persisted schemas are unchanged. Future serialized positions must include an
explicit coordinate-space/version discriminator.
## Verification
- Five M00 build-12340 client camera positions map to the recorded Godot
checkpoints within `0.002` yard.
- Server, ADT placement, Godot and tile-local conversions round-trip.
- Exact center, tile, chunk and world-extent boundaries have explicit tests.
- The observed Elwynn position resolves to the real `Azeroth_31_49.adt` file;
MCNK `(0,0)` to `(15,15)` origins confirm X-east and Y-south chunk growth.
- World-yaw normalization and round-trip cases cover cardinal angles and wrap.
- Direct Godot/ADT tile-local conversions round-trip and production consumers
are checked by `verify_coordinate_conversion_boundaries.gd`.
## References
- [TrinityCore GridDefines](https://github.com/TrinityCore/TrinityCore/blob/master/src/server/game/Grids/GridDefines.h)
- `reference/open-realm/games/world-of-warcraft/renderer/wow/r_wowmap_adt.c`
- `reference/open-realm/games/world-of-warcraft/renderer/wow/r_wowmap_objects.c`
- `reference/WoWee/include/core/coordinates.hpp`
- [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
+122
View File
@@ -0,0 +1,122 @@
# ADR 0002: Separate domain identity namespaces
- Status: Accepted for M01 contract implementation
- Date: 2026-07-13
- Work package: `M01-FND-DOMAIN-IDENTITIES-001`
- Decision owners: M01 foundation/integration owners
## Context
OpenWC must relate authored content, runtime objects, WoW protocol objects and
TrinityCore/AzerothCore rows. All four domains can expose an integer or string,
but their identity lifetimes and collision rules are different. A raw `int`, a
generic `id` field or one universal ID class would allow an entity sequence to
be persisted as content identity, a database entry to be sent as a wire GUID,
or equal entry numbers from different tables to collide.
The Content Project requires stable references before server entries are
allocated. Runtime entities require cheap session-local identity even when no
server object exists. WoW 3.3.5a GUIDs carry opaque 64 wire bits whose semantic
decomposition must be established by protocol fixtures. Server templates use
numeric entries scoped by an adapter/table namespace.
## Decision
### ContentId
`ContentId` stores canonical lowercase UUID text in the `8-4-4-4-12` shape.
It is the only identity in this decision intended for stable authored-content
references and serialization. Construction normalizes whitespace and letter
case; `is_valid()` reports malformed input without crashing an import.
The value validates text shape, not UUID version or variant bits. A future
Content Project allocator chooses the UUID generation policy and owns duplicate
detection. Every persisted schema that contains a `ContentId` still declares
its own schema version and field migration.
### EntityId
`EntityId` stores a positive integer allocated by one runtime session owner.
It is valid only inside that session and must not be persisted as content,
protocol or server identity. Sequence reuse policy belongs to the future entity
registry, not the value object.
### WowGuid
`WowGuid` stores opaque `high_32_bits` and `low_32_bits` integers. Each word is
validated against `0..0xffffffff`, preserving every unsigned wire bit without
combining it into GDScript's signed 64-bit integer. The all-zero value remains
an observable protocol sentinel and receives no gameplay interpretation here.
High-type masks, packed-GUID codecs and object-kind extraction are explicitly
deferred to the build-12340 protocol package and require packet fixtures.
### ServerEntryId
`ServerEntryId` stores a positive numeric entry together with a normalized,
non-empty adapter/table namespace. Equal numbers in different namespaces are
different identities. Mapping a server entry to a `ContentId` is explicit
adapter/snapshot data; neither value derives the other.
### Equality and keys
The types expose domain-named `equals` methods because `RefCounted` `==`
compares object references. Stable keys are available only where their lifetime
allows them: `ContentId.to_key()`, `WowGuid.to_hex_string()` and
`ServerEntryId.to_key()`. `EntityId` exposes only `to_debug_key()` to discourage
persistence.
## Alternatives considered
### One generic `Id<T>` abstraction
Rejected because GDScript has no compile-time generic type distinction and the
shared API would obscure different validation, lifetime and persistence rules.
### Use strings everywhere
Rejected because prefixes are a convention rather than a type boundary and
would still permit accidental cross-layer assignment.
### Store WowGuid in one integer
Rejected because unsigned 64-bit GUIDs can exceed GDScript's positive signed
integer range. Split words preserve the wire representation losslessly.
### Use server entries as ContentId
Rejected because entries are allocated by a particular backend/table, can
collide between namespaces and do not exist while authoring offline content.
### Generate UUIDs inside ContentId
Rejected because randomness/allocation is a side effect requiring duplicate,
fixture and deterministic-build policy. This value object only normalizes and
validates an allocator-provided value.
## Consequences
- Cross-layer contracts must name the identity type they accept.
- Content references remain stable before and after server deployment.
- Runtime entity registries need explicit mappings to wire GUIDs/content IDs.
- Server adapters must supply a namespace and maintain entry-to-content maps.
- Malformed external values remain representable long enough for adapters to
produce diagnostics, but consumers must call the relevant validation method.
- Additional typed IDs such as map, spell, item and quest remain separate
follow-up contracts rather than aliases of these four values.
## Verification
- UUID normalization, shape validation and value equality are fixture-tested.
- Zero/positive session sequences establish `EntityId` validity boundaries.
- Maximum unsigned GUID words round-trip to exactly sixteen hexadecimal digits;
overflow is rejected and zero remains detectable.
- Equal server entry numbers in creature/gameobject namespaces do not compare equal.
- Dependency search verifies that identity values do not inherit Node/Resource
or expose `Vector3`.
## References
- [RFC 9562: Universally Unique IDentifiers](https://www.rfc-editor.org/rfc/rfc9562.html)
- [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
- [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md)
+43
View File
@@ -7,7 +7,50 @@
| Module | Current status | Primary specification |
|---|---|---|
| Foundation/data | Planned/Partial | [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md) |
| Coordinate mapping | Implemented | [`coordinate-mapping.md`](coordinate-mapping.md) |
| Domain identities | Implemented contract | [`domain-identities.md`](domain-identities.md) |
| Player input | Partial | [`player-input.md`](player-input.md) |
| Local player movement | Implemented | [`local-player-movement.md`](local-player-movement.md) |
| Terrain query | Implemented | [`terrain-query.md`](terrain-query.md) |
| Terrain quality Mesh cache | Implemented extraction | [`terrain-quality-mesh-cache.md`](terrain-quality-mesh-cache.md) |
| Terrain chunk LOD planner | Implemented extraction | [`terrain-chunk-lod-planner.md`](terrain-chunk-lod-planner.md) |
| Terrain chunk geometry queue planner | Implemented extraction | [`terrain-chunk-geometry-queue-planner.md`](terrain-chunk-geometry-queue-planner.md) |
| ADT water load pipeline state | Implemented extraction | [`adt-water-load-pipeline-state.md`](adt-water-load-pipeline-state.md) |
| ADT water scene finalizer | Implemented extraction | [`adt-water-scene-finalizer.md`](adt-water-scene-finalizer.md) |
| M2 unique placement registry | Implemented extraction | [`m2-unique-placement-registry.md`](m2-unique-placement-registry.md) |
| M2 placement transform resolver | Implemented | [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md) |
| M2 placement grouper | Implemented extraction | [`m2-placement-grouper.md`](m2-placement-grouper.md) |
| M2 build batch planner | Implemented extraction | [`m2-build-batch-planner.md`](m2-build-batch-planner.md) |
| M2 build dispatch planner | Implemented extraction | [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md) |
| M2 build resource snapshot | Implemented extraction | [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md) |
| M2 static build resource observer | Implemented extraction | [`m2-static-build-resource-observer.md`](m2-static-build-resource-observer.md) |
| M2 cached animation resource observer | Implemented extraction | [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md) |
| M2 native animation resource observer | Implemented extraction | [`m2-native-animation-resource-observer.md`](m2-native-animation-resource-observer.md) |
| M2 build queue | Implemented extraction | [`m2-build-queue.md`](m2-build-queue.md) |
| M2 static batch materializer | Implemented extraction | [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md) |
| M2 runtime mesh rebuild classifier | Implemented extraction | [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md) |
| M2 animated scene finalizer | Implemented extraction | [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md) |
| M2 animation playback controller | Implemented extraction | [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md) |
| M2 animated instance materializer | Implemented extraction | [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.md) |
| M2 animation load pipeline state | Implemented extraction | [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md) |
| M2 mesh load pipeline state | Implemented extraction | [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md) |
| M2 mesh resource cache state | Implemented extraction | [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md) |
| M2 mesh resource extractor | Implemented extraction | [`m2-mesh-resource-extractor.md`](m2-mesh-resource-extractor.md) |
| M2 runtime mesh finalizer | Implemented extraction | [`m2-runtime-mesh-finalizer.md`](m2-runtime-mesh-finalizer.md) |
| M2 raw model repository | Implemented extraction | [`m2-raw-model-repository.md`](m2-raw-model-repository.md) |
| M2 prototype cache state | Implemented extraction | [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md) |
| WMO placement resolver | Implemented extraction | [`wmo-placement-resolver.md`](wmo-placement-resolver.md) |
| WMO placement registry | Implemented extraction | [`wmo-placement-registry.md`](wmo-placement-registry.md) |
| WMO render build step planner | Implemented extraction | [`wmo-render-build-step-planner.md`](wmo-render-build-step-planner.md) |
| WMO render build queue | Implemented extraction | [`wmo-render-build-queue.md`](wmo-render-build-queue.md) |
| WMO render Resource cache state | Implemented extraction | [`wmo-render-resource-cache-state.md`](wmo-render-resource-cache-state.md) |
| WMO scene Resource cache state | Implemented extraction | [`wmo-scene-resource-cache-state.md`](wmo-scene-resource-cache-state.md) |
| Third-person camera | Implemented | [`third-person-camera.md`](third-person-camera.md) |
| Character presentation | Implemented boundary / Partial fidelity | [`character-presentation.md`](character-presentation.md) |
| Renderer | Partial | [`world-renderer.md`](world-renderer.md), [`../../RENDER.md`](../../RENDER.md) |
| World entity presentation | Implemented boundary / Prototype visuals | [`world-entity-presentation.md`](world-entity-presentation.md) |
| Streaming target planner | Implemented | [`streaming-target-planner.md`](streaming-target-planner.md) |
| Render budget scheduler | Implemented | [`render-budget-scheduler.md`](render-budget-scheduler.md) |
| Network/session | Planned | [`../../targets/roadmap/03-network-and-session.md`](../../targets/roadmap/03-network-and-session.md) |
| Gameplay | Prototype/Planned | [`../../targets/roadmap/04-gameplay-systems.md`](../../targets/roadmap/04-gameplay-systems.md) |
| UI/Lua/audio | Prototype/Planned | [`../../targets/roadmap/05-ui-lua-audio.md`](../../targets/roadmap/05-ui-lua-audio.md) |
@@ -0,0 +1,252 @@
# ADT Water Load Pipeline State
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | M03 / `M03-RND-ADT-WATER-LOAD-PIPELINE-001` |
| Owners | ADT water pending FIFO/dedupe, active task IDs and worker-safe result mailbox |
| Last verified | Worktree `work/sindo-main-codex/m03-adt-water-load-pipeline`, 2026-07-17 |
| Profiles/capabilities | ADT MH2O/MCLQ water loading; profile-independent state |
## Purpose
Own the cross-frame and cross-thread bookkeeping for asynchronous ADT water
loads outside `StreamingWorldLoader`: FIFO requests, queued-key deduplication,
opaque active task IDs and parsed-result delivery. The loader retains task
execution, parsing, budgets, stale-result decisions and scene finalization.
## Non-goals
- Start, cancel or wait WorkerThreadPool tasks.
- Instantiate `ADTLoader` or interpret MH2O/MCLQ data.
- Select concurrency limits or consume render permits.
- Create, attach or free Nodes, Meshes, materials or RIDs.
- Change liquid visuals, coordinates, tile acceptance or loaded-state timing.
## Context and boundaries
```mermaid
flowchart LR
Tile[Loaded tile needing water] --> Loader[StreamingWorldLoader]
Loader --> State[AdtWaterLoadPipelineState]
State --> Request[Oldest tile/path request]
Request --> Loader
Loader --> Worker[WorkerThreadPool ADTLoader task]
Worker --> State
State --> Result[Parsed ADT result]
Result --> Loader
Loader --> Budget[WATER_FINALIZE permit]
Loader --> Finalizer[AdtWaterSceneFinalizer]
Finalizer --> Builder[ADTBuilder water scene]
Finalizer --> Scene[Main-thread tile Node attach]
```
Allowed dependencies are RefCounted, Dictionary/Array/String and one Godot
`Mutex` around results. WorkerThreadPool, ADTLoader, scheduler, tile state,
scene-tree/render resources and gameplay/editor/network are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `enqueue_request(tile_key, adt_path)` | Command/query | Append deduplicated FIFO request | Renderer main thread; until dequeue/cancel/clear | Empty/queued/active returns false |
| `take_next_request()` | Command/query | Pop oldest request and release queued marker | Renderer main thread | Empty returns `{}` |
| `cancel_pending_requests(tile_key)` | Command/query | Remove all matching pending entries | Tile release/main thread | Empty/unknown removes zero; active unaffected |
| `remember_active_task(tile_key, task_id)` | Command/query | Retain loader-created opaque task ID | Main thread; until completion/clear | Empty/duplicate returns false |
| `has_active_task` / `active_task_id_for` | Query | Test/borrow active task identity | Main thread | Unknown ID is `-1` |
| `complete_active_task(tile_key)` | Command/query | Remove and return completed task ID | Main thread after wait | Unknown returns `-1` |
| `active_task_ids_snapshot()` | Query | Copy IDs for orderly shutdown waiting | Main thread | Detached Array |
| `publish_result(tile_key, path, data)` | Command/query | Append parsed result under mutex | Worker or main thread | Empty key/path returns false |
| `has_result()` / `pop_result()` | Query/command | Observe/pop oldest result under mutex | Main-thread drain | Empty pop returns `{}` |
| `total_load_count()` | Query | Pending plus active metric | Main thread | Results intentionally excluded |
| `pending_request_count()` / `active_task_count()` | Query | Support loader concurrency loop | Main thread | None |
| `clear()` | Command | Release all bookkeeping and results | Main thread after wait or map reset | Does not interrupt workers |
| `diagnostic_snapshot()` | Query | Return detached scalar requests/tasks/results | Main thread; result mailbox locked | Water data omitted |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Tile key and ADT path | Loader tile state | Pending FIFO | Copied Strings | Until dequeue/cancel/clear |
| Input | Opaque task ID | Loader WorkerThreadPool adapter | Active task map | Integer value | Until wait/completion/clear |
| Input | Tile/path/parsed ADT Dictionary | Worker parser adapter | Result mailbox | Dictionary reference retained | Until pop/clear |
| Output | Oldest request Dictionary | Pipeline state | Loader task-start adapter | Fresh record from FIFO | One start attempt |
| Output | Active task ID snapshot | Pipeline state | Loader shutdown wait | Detached Array | Shutdown loop |
| Output | Oldest result Dictionary | Pipeline state | Loader finalize adapter | Mailbox record transferred | One finalize attempt |
| Output | Scalar diagnostics | Pipeline state | Verifier/future metrics | Detached records | Snapshot lifetime |
Side effects are limited to collection mutation, reference retention and result
mutex locking. Parsing, permits and scene mutation remain outside the module.
## Data flow
```mermaid
flowchart TD
Enqueue[enqueue tile/path] --> Duplicate{Queued or active?}
Duplicate -->|yes| Reject[false; unchanged]
Duplicate -->|no| FIFO[Append FIFO and queued key]
FIFO --> Take[take next; release queued marker]
Take --> Validate[Loader checks tile/path]
Validate --> Start[Loader starts worker and remembers task ID]
Start --> Parse[Worker parses ADT]
Parse --> Publish[Mutex-protected publish result]
Publish --> Permit[Loader obtains WATER_FINALIZE permit]
Permit --> Pop[Pop oldest result]
Pop --> Wait[Loader waits task and completes active ID]
Wait --> Stale{Tile/path/root current?}
Stale -->|no| Discard[Discard result]
Stale -->|yes| Attach[Build and attach water scene]
```
## Lifecycle/state
A tile request progresses through `Absent`, `Queued`, `Active` and
`ResultReady`; the loader wait/completion returns bookkeeping to `Absent` before
validating/attaching the result. Queued tile release cancels only `Queued`.
Active work is not interrupted; a later stale result is drained and discarded.
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Queued: accepted enqueue
Queued --> Absent: pending cancellation or invalid dequeue
Queued --> Active: loader starts task
Active --> ResultReady: worker publishes result
ResultReady --> Absent: loader waits and completes task
Queued --> Absent: clear
Active --> Absent: clear bookkeeping only
ResultReady --> Absent: clear
```
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader main thread
participant State as AdtWaterLoadPipelineState
participant Worker as WorkerThreadPool task
participant Budget as RenderBudgetScheduler
participant Finalizer as AdtWaterSceneFinalizer
Loader->>State: enqueue_request(tile key, ADT path)
Loader->>State: take_next_request()
Loader->>Worker: add_task(parse binding)
Loader->>State: remember_active_task(key, task ID)
Worker->>Worker: ADTLoader.load_adt(path)
Worker->>State: publish_result(key, path, data)
Note over State: result mutex protects publication/pop
Loader->>Budget: try_consume_permit(WATER_FINALIZE)
Loader->>State: pop_result()
Loader->>Worker: wait_for_task_completion(task ID)
Loader->>State: complete_active_task(key)
Loader->>Loader: reject stale tile/path/root
Loader->>Finalizer: attach_water_scene(data, origin, tile root, editor owner)
Loader->>Loader: mark water_loaded
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> State[AdtWaterLoadPipelineState]
Loader --> Scheduler[RenderBudgetScheduler]
Loader --> Worker[WorkerThreadPool / ADTLoader]
Loader --> Finalizer[AdtWaterSceneFinalizer]
Finalizer --> Builder[ADTBuilder / SceneTree]
Worker -->|publish only| State
State -. no dependency .-> Scheduler
State -. no dependency .-> Finalizer
```
## Ownership, threading and resources
- Main thread owns pending and active-task collection mutation.
- Worker threads may call only `publish_result`; result observe/pop/clear also lock.
- Parsed water Dictionary identity is retained until main-thread pop.
- Loader owns WorkerThreadPool task lifetime and must wait before orderly shutdown clear.
- Loader owns tile states and the tile subtree; `AdtWaterSceneFinalizer` performs
the main-thread ADTBuilder call and attachment without retaining Nodes.
- `clear` cannot cancel a worker; post-reset publication becomes a stale result
and is discarded because no active task/tile ownership remains.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty/duplicate request | Enqueue guards | Reject unchanged | Contract verifier | Correct caller state |
| Tile removed while queued | Loader release | Remove matching pending requests | Source/lifecycle verifier | Reload can enqueue later |
| Tile removed while active | Loader stale check after result | Wait task, remove active ID, discard result | Existing metrics | Reload can enqueue later |
| Parser unavailable/empty | Worker publishes empty Dictionary | Loader marks `water_loaded` without Node | Existing behavior | Future tile reload |
| Path changed | Loader compares result path | Discard after task completion | Source regression | Current tile can request again |
| Shutdown | Loader snapshots/waits active IDs | Clear all bookkeeping/results | Shutdown/source verifier | New loader starts empty |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| `enable_water` | Loader setting | Quality/custom | Yes | Gates enqueue/process, unchanged |
| `max_concurrent_water_tasks` | Loader setting | Quality/custom | Yes | Bounds active tasks outside state |
| `water_finalize_ops_per_tick` | Loader setting | Quality/custom | Yes | Bounds result pops via scheduler |
## Persistence, cache and migration
No state is serialized or cached. ADT source/cache formats and liquid material
versions are unchanged; no migration or rebuild is required.
## Diagnostics and observability
- `total_load_count` preserves the prior pending-plus-active `water` metric.
- Snapshot exposes FIFO tile/path, sorted active keys and result tile/path only.
- Parsed liquid payloads, Nodes and task internals are not exposed.
- No logs or correlation IDs are introduced.
## Verification
- `verify_adt_water_load_pipeline_state.gd`: invalid/dedupe/FIFO behavior,
active IDs, payload identity, real Thread publication, pending cancellation,
clear, detached diagnostics, loader boundaries and bounded timing.
- Existing material/shutdown/facade/streaming regressions cover adjacent behavior.
- Fidelity evidence is preservation of current scheduling/finalize transitions;
no MH2O/MCLQ visual or original-client parity claim is made.
- Performance budget: 100 cycles of 256 request/task/result transitions under 1 second.
## Extension points
- ADT parse execution can later move behind a parser/job adapter without changing state.
- Water scene materialization is now delegated to `AdtWaterSceneFinalizer`,
independently of this async bookkeeping service.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| ADT water load pipeline state | Implemented extraction | FIFO/task/thread/source/timing verifier | Asset-backed traversal/leak evidence pending |
| ADT liquid parsing | Existing/loader-owned adapter | Native ADTLoader and dry-run paths | MH2O/MCLQ fidelity fixtures pending |
| Water scene finalization | Implemented extraction | Dedicated synthetic scene/ownership verifier | Asset-backed traversal/hitch evidence pending |
## Known gaps and risks
- `clear` intentionally does not interrupt active WorkerThreadPool work.
- Result payload Dictionaries are retained by reference and must not be mutated after publication.
- Result availability and pop use separate mutex acquisitions; one serialized
main-thread consumer preserves ordering, but the API is not a multi-consumer queue.
- No proprietary liquid corpus, traversal/leak run, p95/p99 or paired capture is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/liquid/adt_water_load_pipeline_state.gd` | Request/task/result bookkeeping and result mutex |
| `src/render/liquid/adt_water_scene_finalizer.gd` | Main-thread ADTBuilder call, attach and optional editor ownership |
| `src/scenes/streaming/streaming_world_loader.gd` | Parse task execution, permits, stale checks and tile loaded timing |
| `addons/mpq_extractor/loaders/adt_builder.gd` | ADT liquid geometry/material construction |
| `src/tools/verify_adt_water_load_pipeline_state.gd` | Lifecycle/thread/boundary/timing regression |
## Related decisions and references
- [`world-renderer.md`](world-renderer.md)
- [`adt-water-scene-finalizer.md`](adt-water-scene-finalizer.md)
- [`render-budget-scheduler.md`](render-budget-scheduler.md)
- [`../../RENDER.md`](../../RENDER.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
+232
View File
@@ -0,0 +1,232 @@
# ADT Water Scene Finalizer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-ADT-WATER-SCENE-FINALIZER-001` |
| Owners | Main-thread ADT water subtree build, attach and optional editor ownership |
| Last verified | Worktree `work/sindo-main-codex/m03-adt-water-scene-finalizer`, 2026-07-17 |
| Profiles/capabilities | Existing ADT MH2O/MCLQ scene output; profile-independent adapter |
## Purpose
Provide one liquid-specific main-thread boundary for converting parsed ADT
water data through the existing `ADTBuilder`, attaching the resulting `Water`
subtree to a caller-owned tile root and assigning optional persisted Editor
ownership. The service is stateless and retains no Node or Resource.
## Non-goals
- Parse or validate MH2O/MCLQ binary structures.
- Replace ADTBuilder geometry, material or coordinate calculations.
- Own water request/task/result state, permits or concurrency.
- Validate tile/path/root freshness beyond receiving a valid root.
- Mark `water_loaded`, enable water or remove water subtrees.
## Context and boundaries
```mermaid
flowchart LR
Parse[Parsed ADT Dictionary] --> Loader[StreamingWorldLoader]
Loader -->|validated data, origin, tile root| Finalizer[AdtWaterSceneFinalizer]
Finalizer --> Builder[ADTBuilder]
Builder --> Water[Water Node3D or null]
Water --> Finalizer
Finalizer -->|main-thread add_child| Tile[Caller-owned tile root]
Loader -->|after return| Loaded[water_loaded timing]
```
Allowed dependencies are `ADTBuilder` and Godot Node/mesh types needed to adopt
its output. WorkerThreadPool, scheduler, tile-state dictionaries, facade,
gameplay, network and editor plugin packages are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `attach_water_scene(water_data, tile_origin_godot_units, tile_root, editor_owner)` | Command/query | Build and attach one existing-format water subtree | Renderer main thread; returned Node is borrowed for tile lifetime | Empty data, invalid root or dry build returns null without attachment |
`editor_owner` is optional. When supplied it must be an ancestor suitable for
Godot `Node.owner`; ownership is recursively assigned to the generated subtree.
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Parsed ADT water-containing Dictionary | ADTLoader/raw tile path | Finalizer/ADTBuilder | Caller retains Dictionary; not mutated | One main-thread call |
| Input | Tile origin in Godot world units | Loader tile state/ADTBuilder | Finalizer | Value copy | One call |
| Input | Valid tile root | StreamingWorldLoader | Finalizer | Caller/SceneTree-owned | Must outlive call and attached subtree |
| Input | Optional edited scene root | Loader Editor policy | Finalizer | SceneTree-owned borrowed Node | One call |
| Output | Attached `Water` Node3D or null | Finalizer | Loader/SceneTree | Tile root owns attached Node | Tile subtree lifetime |
Side effects are limited to ADTBuilder mesh/material creation, `add_child` and
optional recursive `Node.owner` assignment. No cache, filesystem, task or tile
state is mutated.
## Data flow
```mermaid
flowchart TD
Input[water data + Godot origin + tile root] --> Guard{Data and root valid?}
Guard -->|no| None[Return null; unchanged]
Guard -->|yes| Build[ADTBuilder.build_tile_water_scene]
Build --> Dry{Water root exists?}
Dry -->|no| None
Dry -->|yes| Attach[tile root add_child]
Attach --> Owner{Editor owner supplied?}
Owner -->|yes| Assign[Assign owner recursively]
Owner -->|no| Return[Return borrowed root]
Assign --> Return
```
## Lifecycle/state
The service has no retained lifecycle. Each call moves one temporary builder
result through the following call-local states.
```mermaid
stateDiagram-v2
[*] --> Validating
Validating --> Rejected: empty data or invalid root
Validating --> Building: valid input
Building --> Dry: no geometry
Building --> Attached: Water root built
Attached --> EditorOwned: owner supplied
Attached --> Complete: no owner
EditorOwned --> Complete
Rejected --> [*]
Dry --> [*]
Complete --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader main thread
participant Finalizer as AdtWaterSceneFinalizer
participant Builder as ADTBuilder
participant Tile as Tile root
Loader->>Finalizer: attach_water_scene(data, origin, tile, editor owner)
Finalizer->>Builder: build_tile_water_scene(data, origin)
Builder-->>Finalizer: Water root or null
alt Water root exists
Finalizer->>Tile: add_child(Water)
opt editor owner supplied
Finalizer->>Finalizer: assign owner recursively
end
Finalizer-->>Loader: borrowed Water root
else dry/invalid
Finalizer-->>Loader: null
end
Loader->>Loader: preserve existing water_loaded timing
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Finalizer[AdtWaterSceneFinalizer]
Finalizer --> Builder[ADTBuilder]
Builder --> Material[WowLiquidMaterial]
Finalizer --> Scene[Godot SceneTree]
Pipeline[AdtWaterLoadPipelineState] --> Loader
Scheduler[RenderBudgetScheduler] --> Loader
Finalizer -. no dependency .-> Pipeline
Finalizer -. no dependency .-> Scheduler
```
## Ownership, threading and resources
- The method is main-thread only because it creates Mesh/Material/Node objects
and mutates the SceneTree.
- `ADTBuilder` owns temporary construction. It frees an empty `Water` root and
returns null for dry/invalid liquid geometry.
- After `add_child`, the caller's tile subtree owns the returned Water root and
all MeshInstance children; the finalizer retains no references.
- Optional editor ownership affects serialization ownership only and does not
replace SceneTree lifetime ownership.
- Parsed Dictionaries are borrowed and not mutated.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty parsed data | Input guard | Return null, no builder call | Contract verifier | Loader preserves existing loaded timing |
| Missing/freed tile root | Input guard | Return null, no scene mutation | Contract verifier | Stale-result validation normally rejects earlier |
| Dry or malformed liquid arrays | ADTBuilder returns null | No attachment | Builder/shutdown regression | Reload after corrected source |
| Tile/path stale | Loader before call | Finalizer is not invoked | Existing loader behavior | Current tile may enqueue again |
| Shutdown/cancellation | Loader drains/cancels before call | No retained finalizer work | Shutdown verifier | New loader starts stateless |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| `enable_water` | Loader setting | Quality/custom | Yes | Gates both caller paths; not owned here |
| `water_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Budgets async calls before finalizer |
| `editor_persist_generated_nodes` | `false` | Editor only | Yes | Supplies or omits editor owner |
## Persistence, cache and migration
The service writes no files and changes no cache/resource schema. Existing ADT,
terrain cache and liquid material versions remain valid; no rebuild or migration
is required. Persisted Editor ownership follows the loader's existing opt-in.
## Diagnostics and observability
- Logs: none; existing loader/builder diagnostics remain authoritative.
- Metrics: no independent counters; existing water queue/finalize metrics remain.
- Debug views: generated `Water/Liquid_<id>` names remain unchanged.
- Correlation IDs: tile/path correlation stays in loader pipeline state.
## Verification
- `verify_adt_water_scene_finalizer.gd`: empty/invalid/dry guards, one-cell
geometry, attachment, recursive owner, loader delegation and bounded timing.
- `verify_adt_water_load_pipeline_state.gd`: adjacent async ownership and loaded timing.
- Renderer material and shutdown verifiers cover material construction and dry-root cleanup.
- Fidelity evidence is the unchanged ADTBuilder implementation and unchanged two
loader call positions; no new original-client liquid parity claim is made.
- Performance budget: 100 empty calls under one second.
## Extension points
- A future liquid renderer may replace the internal builder only after paired
MH2O/MCLQ fixtures establish equivalent geometry/material behavior.
- Removal/eviction remains ordinary tile subtree ownership and needs no parallel API.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| ADT water scene finalization seam | Implemented extraction | Synthetic geometry/attach/owner/source verifier | Asset-backed traversal and hitch metrics pending |
| ADT liquid geometry/materials | Existing implementation | ADTBuilder/material/checkpoint evidence | MH2O/MCLQ and original-client parity incomplete |
| Async water scheduling | Existing extracted state | Water pipeline verifier | Active worker interruption remains loader-owned |
## Known gaps and risks
- The service intentionally wraps the existing Dictionary-based ADTBuilder boundary.
- It does not verify that `editor_owner` is an ancestor; the loader supplies the
edited scene root under the existing policy.
- Mesh/material creation remains synchronous main-thread work behind the existing permit.
- No proprietary liquid corpus, long traversal leak run, p95/p99 or paired capture is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/liquid/adt_water_scene_finalizer.gd` | Main-thread build, attach and optional recursive editor ownership |
| `addons/mpq_extractor/loaders/adt_builder.gd` | Existing ADT liquid geometry/material creation and dry-root cleanup |
| `src/render/liquid/adt_water_load_pipeline_state.gd` | Pending/task/result state before finalization |
| `src/scenes/streaming/streaming_world_loader.gd` | Permits, stale checks, tile state and finalizer calls |
| `src/tools/verify_adt_water_scene_finalizer.gd` | Synthetic scene/ownership/boundary/timing regression |
## Related decisions and references
- [`adt-water-load-pipeline-state.md`](adt-water-load-pipeline-state.md)
- [`world-renderer.md`](world-renderer.md)
- [`render-budget-scheduler.md`](render-budget-scheduler.md)
- [`../../RENDER.md`](../../RENDER.md)
- [`../../targets/roadmap/02-rendering-and-graphics.md`](../../targets/roadmap/02-rendering-and-graphics.md)
+273
View File
@@ -0,0 +1,273 @@
# Character Presentation
## Metadata
| Field | Value |
|---|---|
| Status | Implemented boundary / Partial fidelity |
| Target/work package | M02 / M02-RND-CHARACTER-PRESENTATION-001 |
| Owners | Character appearance composition and locomotion animation presentation |
| Last verified | `97bb53f`, 2026-07-14 |
| Profiles/capabilities | Current render sandbox character experiment |
## Purpose
Compose the current sandbox character GLB, geosets, skin textures and starter
outfit below the existing `Visual` node, then independently present its
stationary or moving animation. Keep visual asset loading and AnimationPlayer
state out of gameplay movement and the player scene composition root.
## Non-goals
- Define authoritative character identity, equipment or movement state.
- Change the existing GLB, geoset, texture-compositor, DBC or outfit formats.
- Implement full build-12340 animation selection, blending or equipment visuals.
- Load world assets, query terrain, process input or control the camera.
- Introduce asynchronous streaming, background scene mutation or persistence.
## Context and boundaries
```mermaid
flowchart LR
Scene[ThirdPersonWowController composition] --> Appearance[CharacterAppearancePresenter]
Model[Configured PackedScene] --> Appearance
DBC[Extracted DBC/item textures] --> Appearance
Appearance --> Root[CharacterModel root]
Root --> Geosets[Existing geoset controller]
Root --> Compositor[Existing texture compositor]
Appearance -->|ready root| Scene
Scene --> Animation[CharacterAnimationPresenter]
Velocity[Movement read model] -->|moving boolean| Animation
Animation --> Player[AnimationPlayer below root]
```
Allowed dependencies:
- configured Godot `PackedScene` character asset;
- existing `CharacterGeosetController`, `CharacterTextureCompositor` and
`WowCharacterOutfitResolver` presentation components;
- `AnimationPlayer` and animation resources below the composed character root;
- main-thread player scene composition and movement velocity read model.
Forbidden dependencies:
- `MoveIntent`, input devices or movement integration;
- `TerrainQuery`, ADT parsing, coordinate conversion or streaming decisions;
- cameras, network packets, server databases or gameplay reducers;
- direct production database writes or cache-format ownership.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `CharacterAppearancePresenter.load_character_appearance(content_root_override)` | Command | Replaces the owned character root and schedules final grounding/outfit composition | Main thread; may be called again during scene lifetime | Returns `false` for empty/invalid model; warning for invalid resource |
| `CharacterAppearancePresenter.character_root` | Read-only state | Current owned model root or null | Stable until next load | None |
| `character_appearance_ready(character_root)` | Signal | Publishes a fully attached, grounded root | Deferred main-thread completion | Not emitted for invalid/empty model |
| `CharacterAnimationPresenter.bind_character_root(character_root)` | Command | Discovers and prepares the first descendant AnimationPlayer | Main thread; binding lifetime | Returns `false` and clears state when absent |
| `CharacterAnimationPresenter.present_locomotion(is_moving)` | Command | Selects current stationary/moving candidate and starts it once | Main/physics thread | Returns `false` when unavailable, missing or unchanged |
| `active_animation_name` | Read-only state | Selected animation name | Stable until bind/state transition | Empty when unbound |
| `has_animation_player` | Read-only state | Whether the presenter has a valid bound player | Stable until bind | None |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Model resource path, class, scale and yaw | Runtime scene composition | Appearance presenter | Exported configuration | Captured at load |
| Input | Extracted content root | Player scene configuration | Appearance presenter | Copied string | Captured at load |
| Input | Packed character scene | Godot resource loader | Appearance presenter | Shared resource; presenter owns instance | Scene lifetime |
| Output | Composed `CharacterModel` root | Appearance presenter | Scene/animation presenter | Appearance presenter owns node | Until replacement |
| Output | Ready signal | Appearance presenter | Player composition | Synchronous signal after deferred completion | One per successful load |
| Input | Moving/stationary boolean | Player adapter from horizontal velocity | Animation presenter | Value copy | One physics tick |
| Output | AnimationPlayer playback mutation | Animation presenter | Rendered character | Bound root owns player/resources | Until next transition |
Side effects:
- loads and instantiates the configured model on the main thread;
- adds/removes one owned `CharacterModel` subtree;
- composes the existing texture/outfit components and reads their extracted inputs;
- changes model transform for scale, yaw and ground alignment;
- configures locomotion loop modes and starts AnimationPlayer playback;
- does not write files, caches, databases or network state.
## Data flow
```mermaid
flowchart TD
Config[Model/class/scale/yaw/content root] --> Load[Load PackedScene]
Load --> Replace[Replace owned CharacterModel]
Replace --> Attach[Attach model and compositor]
Attach --> Ground[Fit mesh bounds to visual ground]
DBC[Existing outfit resolver] --> Outfit[Apply starter outfit]
Ground --> Ready[Emit ready root]
Outfit --> Ready
Ready --> Bind[Discover AnimationPlayer]
Velocity[Horizontal movement velocity] --> State[Moving or stationary]
State --> Select[Run/Walk or Stand/Idle selection]
Bind --> Select
Select --> Playback[Loop and play with blend]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Empty
Empty --> Loading: valid load command
Loading --> Ready: attach + deferred composition
Loading --> Empty: invalid resource
Ready --> Loading: replacement load
Ready --> Empty: empty model command
state Animation {
[*] --> Unbound
Unbound --> Stationary: bind root with animations
Stationary --> Moving: moving=true
Moving --> Stationary: moving=false
Stationary --> Unbound: bind missing player
Moving --> Unbound: bind missing player
}
```
Appearance replacement removes the old root from the scene immediately and
queues it for deletion. Deferred completion checks root identity, so a stale
completion cannot publish a replaced model.
## Main sequence
```mermaid
sequenceDiagram
participant Scene as ThirdPersonWowController
participant Appearance as CharacterAppearancePresenter
participant Existing as Geoset/Compositor/Outfit
participant Animation as CharacterAnimationPresenter
participant Player as AnimationPlayer
Scene->>Appearance: load_character_appearance(content root)
Appearance->>Appearance: load, instantiate and attach root
Appearance->>Existing: compose textures and starter outfit
Appearance-->>Scene: character_appearance_ready(root)
Scene->>Animation: bind_character_root(root)
Animation->>Player: prepare loops and play Stand/Idle
loop Physics ticks
Scene->>Animation: present_locomotion(is moving)
Animation->>Player: play only on state/name change
end
```
## Ownership, threading and resources
- The runtime scene owns one appearance presenter on `Visual` and one animation
presenter below it.
- The appearance presenter exclusively owns the generated `CharacterModel`
subtree; imported shared resources remain Godot-owned.
- The animation presenter borrows an AnimationPlayer reference until rebind or
subtree replacement; the player scene orders rebind through the ready signal.
- Loading, node mutation, bounds calculation, outfit setup and playback occur
on the main thread. There are no workers, locks, RIDs or cancellation tokens.
- The player controller owns neither model resources nor animation state.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty model path | Load guard | Clears old root and returns `false` | Contract regression | Configure a path and retry |
| Invalid model resource | Failed PackedScene cast | Remains empty | Named warning with path | Correct/extract asset and retry |
| Replaced deferred completion | Root identity check | Stale completion ignored | Replacement regression | No action required |
| Missing DBC/outfit inputs | Existing resolver returns false/empty | Base model remains visible | Existing resolver diagnostics | Restore extracted inputs and reload |
| Missing AnimationPlayer | Recursive discovery returns null | Presenter becomes unbound | Contract return/read state | Bind a root with animations |
| Missing candidate animation | Candidate selection returns empty | Current playback unchanged | Verifier/source diagnostics | Extend verified candidate policy later |
There is no background load cancellation. Replacement is the recovery path and
invalid model state is explicit through the return value and null read model.
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Character model | Human male sandbox GLB | Current sandbox | Before reload | Model scene input |
| Character class ID | `1` | Current sandbox | Before reload | Starter outfit lookup |
| Uniform scale | `1.0` | Current sandbox | Before reload | Character root scale |
| Yaw offset | `90` degrees | Current sandbox | Before reload | Imported model correction |
| Extracted content root | `res://data/extracted` | Local content | Per load override | DBC/item texture input |
| Animation blend | `0.15` seconds | Current sandbox | Before bind/transition | Playback cross-fade/default blend |
| Stationary candidates | `Stand`, then `Idle` | Current sandbox | Fixed | Exact then substring selection |
| Moving candidates | `Run`, then `Walk` | Current sandbox | Fixed | Exact then substring selection |
## Persistence, cache and migration
Presenter state is transient. No saved settings, user profile, database schema
or cache format changes. Existing scene paths remain
`ThirdPersonPlayer/Visual` and add only `Visual/AnimationPresenter`; model and
extracted-data paths retain their defaults. Moving the model exports from the
player node to `Visual` is a scene-authoring ownership change, not persisted
runtime data migration.
## Diagnostics and observability
- Logs: invalid model path warns with the responsible presenter and path.
- Read models: current character root, animation binding and active name.
- Tests: dedicated verifier reports appearance, animation, scene and player boundaries.
- Metrics/correlation IDs: not applicable to this synchronous sandbox presenter.
## Verification
- Unit/contract: synthetic model fixture verifies load, scale, yaw, grounding,
compositor composition, ready signal, replacement and empty-model clearing.
- Animation: synthetic libraries verify nested discovery, exact and substring
candidate selection, loop policy, blend configuration and duplicate suppression.
- Boundary: player source contains no model loading, outfit, bounds or
AnimationPlayer logic; appearance and animation dependencies remain separate.
- Integration: Eastern Kingdoms, Kalimdor and player regression scenes compose
both presenters; existing input/movement/terrain/camera tests remain required.
- Fidelity evidence: only preservation of the current sandbox defaults and
selection rules. No build-12340 appearance or animation parity is claimed.
- Performance: one synchronous model composition per load and constant-time
locomotion calls except on state transition; asset streaming remains future work.
## Extension points
- Replace starter outfit inputs with a versioned appearance/equipment snapshot.
- Replace boolean locomotion input with a typed presentation state after
movement/network contracts define walk, run, jump, fall, swim, mount and emotes.
- Add asynchronous/preloaded model providers behind the appearance command
without moving resource loading back into gameplay.
- Add verified animation IDs/variation policy after build-12340 fixtures exist.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Independent appearance composition | Implemented | Synthetic fixture and three scene wirings | Integrator target update |
| Independent locomotion animation | Implemented | Exact/fallback/loop/blend regression | Add typed presentation state later |
| Current starter outfit | Partial | Existing resolver reused unchanged | Needs extracted DBC fixture and client comparison |
| Current skin/geoset composition | Partial | Existing components reused | Full equipment/customization fidelity incomplete |
| Build-12340 animation semantics | Planned | No original-client fixture | Capture animation IDs/transitions/timing |
| Runtime equipment/network updates | Planned | No snapshot contract | M08/M09/M12 work |
## Known gaps and risks
- Model loading remains synchronous and can hitch when not preloaded.
- Exact appearance, equipment, animation choice, playback speed and blending are unverified.
- The moving input is a sandbox threshold boolean, not authoritative locomotion state.
- The first descendant AnimationPlayer wins; multi-player imported scenes need a
more explicit binding contract if they appear.
- Existing geoset/compositor/outfit implementations retain their own fidelity gaps.
## Source map
| Path | Responsibility |
|---|---|
| `src/scenes/character/character_appearance_presenter.gd` | Model-root ownership, composition, ground fit and outfit setup |
| `src/scenes/character/character_animation_presenter.gd` | AnimationPlayer binding and locomotion playback state |
| `src/scenes/character/character_geoset_controller.gd` | Existing geoset visibility and outfit defaults |
| `src/scenes/character/character_texture_compositor.gd` | Existing skin/equipment texture composition |
| `src/scenes/character/wow_character_outfit_resolver.gd` | Existing DBC starter outfit lookup |
| `src/scenes/player/third_person_wow_controller.gd` | Scene composition and moving-state adapter |
| `src/tests/scenes/character_presentation_model_fixture.tscn` | Asset-free model/grounding fixture |
| `src/tools/verify_character_presentation.gd` | Presenter, scene and dependency regression |
## Related decisions and references
- ADR: none; current behavior and resource formats are preserved.
- Upstream/reference: `targets/02-player-decomposition.md`,
`targets/roadmap/02-rendering-and-graphics.md`,
`targets/roadmap/04-gameplay-systems.md`, `RENDER.md`.
+265
View File
@@ -0,0 +1,265 @@
# Coordinate Mapping
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | `M01-FND-COORDS-001`, `M01-FND-COORD-TILE-AXIS-002`, `M01-QAR-COORD-FIXTURES-001`, `M01-FND-COORD-BOUNDARY-GATE-001`, `M01-QAR-SERVER-SPAWN-RENDERER-001`, `M02-GMP-TERRAIN-QUERY-001` consumer migration |
| Owners | Foundation/domain |
| Last verified | `a45d521`, 2026-07-14 |
| Profiles/capabilities | All profiles; contract version 2 |
## Purpose
Provide scene-free value objects and one pure `CoordinateMapper` for positions,
ADT grid ownership and world-facing yaw. The module makes every source space
explicit before renderer, network, gameplay and authoring consumers migrate.
## Non-goals
- Reading ADT, packet, SQL or scene data.
- Constructing `Vector3`, `Transform3D`, Node or Resource values.
- Correcting M2/WMO local axes or MDDF/MODF Euler rotations.
- Selecting maps, validating terrain height or owning streaming focus.
- Persisting coordinate values in this work package.
## Context and boundaries
```mermaid
flowchart LR
Server[Server / movement X,Y,Z] --> ServerPosition[ServerWorldPosition]
ADT[MDDF/MODF X,height,Z] --> AdtPosition[AdtPlacementPosition]
ServerPosition --> Mapper[CoordinateMapper]
AdtPosition --> Mapper
Mapper --> Canonical[CanonicalWowWorldPosition]
Canonical --> Tile[AdtTileCoordinate + AdtTileLocalPosition]
Canonical --> GodotPosition[GodotWorldPosition]
GodotPosition --> Adapter[Future scene/render adapter]
Adapter --> Vector[Vector3 at boundary]
Scene[Node / Camera3D / scene lookup] -. forbidden .-> Mapper
```
Allowed dependencies:
- GDScript scalar math and `RefCounted` lifetime;
- other value objects in `src/domain/coordinates`;
- callers in network, gameplay, render and server-data adapters.
Forbidden dependencies:
- `Node`, `Resource`, SceneTree, Camera3D and global scene lookup;
- packet buffers, SQL rows, asset parsers and renderer state;
- `Vector3` as a public mapper input or output;
- model-specific placement corrections.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `CanonicalWowWorldPosition` | Immutable value | Client/server X/Y/Z in yards | Any thread; caller-owned reference | No bounds validation |
| `ServerWorldPosition` | Immutable value | Server/wire X/Y/Z in yards | Any thread; caller-owned reference | No adapter validation |
| `AdtPlacementPosition` | Immutable value | MDDF/MODF X/height/Z fields | Any thread; caller-owned reference | No file validation |
| `GodotWorldPosition` | Immutable value | Godot east/up/south scalars | Any thread; caller-owned reference | Does not create `Vector3` |
| `AdtTileCoordinate` | Immutable value | `Map_tileX_tileY.adt` indices | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices |
| `AdtTileLocalPosition` | Immutable value | East/height/south offsets from tile north-west corner | Any thread; caller-owned reference | Values are intentionally unclamped |
| `AdtChunkCoordinate` | Immutable value | MCNK X-east/Y-south indices inside one tile | Any thread; caller-owned reference | `is_within_map_grid()` reports invalid indices |
| `CanonicalWowWorldYaw` | Immutable value | Canonical world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `ServerWorldYaw` | Immutable value | Movement orientation in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `GodotWorldYaw` | Immutable value | Godot Y-axis world yaw in radians | Any thread; caller-owned reference | Constructor does not normalize |
| `CoordinateMapper.server_to_canonical` / inverse | Pure static methods | Preserve server X/Y/Z as canonical | Synchronous; new result value | Null input is a caller contract violation |
| `CoordinateMapper.canonical_to_godot` / inverse | Pure static methods | Apply Y-up renderer basis and map offset | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.godot_to_adt_tile` | Pure static method | Resolve filename indices from typed renderer position | Synchronous; new result value | Result remains unclamped |
| `CoordinateMapper.godot_to_adt_tile_local` | Pure static method | Resolve east/height/south offsets from typed renderer position | Synchronous; new result value | Caller validates ownership/range |
| `CoordinateMapper.adt_tile_local_to_godot` | Pure static method | Compose typed renderer position from tile/local values | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.canonical_to_adt_placement` / inverse | Pure static methods | Convert ADT placement field basis | Synchronous; new result value | Non-finite scalars propagate |
| `CoordinateMapper.canonical_to_adt_tile` | Pure static method | Compute unclamped filename indices | Synchronous; new result value | Out-of-map result remains invalid |
| `CoordinateMapper.canonical_to_adt_tile_local` / inverse | Pure static methods | Split/reconstruct tile-local position | Synchronous; new result value | Caller validates tile ownership |
| `CoordinateMapper.adt_tile_local_to_chunk` | Pure static method | Compute unclamped MCNK indices | Synchronous; new result value | Boundary/out-of-range remains diagnosable |
| World-yaw mapper methods | Pure static methods | Normalize and cross the world-basis boundary | Synchronous; new result value | Non-finite input is unsupported |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | `ServerWorldPosition`, `ServerWorldYaw` | Future protocol/server adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Input | `AdtPlacementPosition` | Future ADT adapter | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Input | `CanonicalWowWorldPosition`, `CanonicalWowWorldYaw` | Gameplay/content/domain | `CoordinateMapper` | Immutable caller value | Any thread, call duration |
| Output | `CanonicalWowWorldPosition` | `CoordinateMapper` | Gameplay/content/adapters | New immutable value | Caller-owned reference |
| Output | `AdtTileCoordinate`, `AdtTileLocalPosition`, `AdtChunkCoordinate` | `CoordinateMapper` | Streaming/content/terrain-query adapters | New immutable values | Caller-owned references |
| Output | `GodotWorldPosition`, `GodotWorldYaw` | `CoordinateMapper` | Future render/scene adapter | New immutable values | Caller-owned references |
| Test input | `coordinate_golden_points.json` schema 1 | Pinned public server row plus private numeric observations | Headless fixture verifier | Versioned repository fixture | Test-process lifetime |
Side effects:
- none;
- no scene mutation, file/cache write, network send, database access or logging.
## Data flow
```mermaid
flowchart LR
ServerFixture[Pinned AzerothCore spawn] --> FixtureVerifier[Golden fixture verifier]
RawADTFixture[Raw ADT numeric metadata] --> FixtureVerifier
ClientFixture[Build 12340 observations] --> FixtureVerifier
FixtureVerifier --> Mapper
ServerFixture --> SpawnManifest[Server spawn render manifest]
SpawnManifest --> SpawnVerifier[Renderer checkpoint verifier]
SpawnVerifier --> Capture[Live renderer capture]
Capture --> Marker[Diagnostic marker at mapped spawn]
Server[ServerWorldPosition X,Y,Z] -->|identity components| Canonical[CanonicalWowWorldPosition]
ADT[AdtPlacementPosition X,height,Z] -->|half extent - horizontal| Canonical
Canonical -->|tileX from Y; tileY from X| Tile[AdtTileCoordinate]
Canonical -->|north-west edge offsets| Local[AdtTileLocalPosition]
Local -->|floor offset / chunk size| Chunk[AdtChunkCoordinate]
Canonical -->|half extent - Y; Z; half extent - X| Godot[GodotWorldPosition]
Godot -->|typed direct adapter| Tile
Godot -->|typed direct adapter| Local
Godot --> TerrainQuery[AdtTerrainQuery consumer]
Godot --> TargetPlanner[StreamingTargetPlanner consumer]
TerrainQuery --> Tile
TerrainQuery --> Local
TerrainQuery --> Chunk
TargetPlanner --> Tile
TargetPlanner --> Local
Tile -->|tile plus local| Godot
ServerYaw[ServerWorldYaw] -->|normalize only| CanonicalYaw[CanonicalWowWorldYaw]
CanonicalYaw -->|same numeric world angle| GodotYaw[GodotWorldYaw]
```
## Lifecycle/state
The module is stateless. Each call reads immutable scalar inputs and returns a
new value object. There is no initialization, shutdown, cancellation or retry
state machine.
## Main sequence
No cross-layer asynchronous sequence occurs inside this module. Producers parse
or validate their external format first, call the mapper synchronously, and
then pass only typed results across the next boundary.
## Ownership, threading and resources
- Value objects own only scalar fields and, for chunks, one immutable tile reference.
- Callers own returned `RefCounted` references.
- Methods do not mutate inputs or shared state and are safe for worker use.
- No Node, RID, file, socket, database or cache resource is acquired.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Tile/chunk outside standard grid | `is_within_map_grid()` | Unclamped indices are preserved | Caller reports source and map context | Reject or apply explicit map policy |
| Tile-local value outside half-open tile | Consumer boundary validation | Value is preserved | Caller reports source coordinate | Recompute ownership or reject malformed input |
| Null value object | Static typing/call failure | Programming contract violation | GDScript error with call site | Fix producer; no fallback value |
| NaN/infinite scalar | Adapter validation | Arithmetic propagates invalid value | Adapter must report source field | Reject malformed external input |
There is no cancellation because every conversion is constant-time scalar math.
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Contract version | 2 | All | No | Corrected filename/chunk axes in ADR 0001 |
| Godot units per WoW yard | 1 | `Blizzlike335` and current tools | No | Preserves current renderer scale |
| ADT grid | 64 tiles/axis, 16 chunks/tile | WotLK 3.3.5a | No | Defines index and boundary math |
## Persistence, cache and migration
Production code adds no persisted format, cache or migration. Test schema 1 in
`coordinate_golden_points.json` persists explicit source provenance, coordinate
contract version 2, tolerances, source-space values and expected outputs. It
contains no proprietary bytes, database dump or original-client image. Existing
dictionaries, native `Vector3` payloads and renderer conversion helpers remain compatible.
Consumer migration is ordered after this contract and must remove old manual
conversions only after its producer/consumer tests pass.
Any future serialized coordinate must store a schema version and explicit space;
raw three-number arrays are not an accepted new persistence contract.
## Diagnostics and observability
- Logs: none in production mapper.
- Metrics: none; conversion cost is constant and outside frame-budget concern.
- Debug views: headless contract probe prints one PASS summary.
- Correlation IDs: supplied by external adapters if an invalid source is reported.
## Verification
- Unit/contract tests: `src/tools/verify_coordinate_mapper.gd`.
- Cross-source golden test: `src/tools/verify_coordinate_golden_fixtures.gd`.
- Boundary enforcement: `src/tools/verify_coordinate_conversion_boundaries.gd`.
- Server-spawn renderer contract: `src/tools/verify_server_spawn_renderer.gd`
validates the dedicated manifest against the same pinned fixture before GUI capture.
- Integration/E2E: sky, player spawn, streamer, terrain probe and the extracted
`AdtTerrainQuery` use mapper boundaries;
`StreamingFocus` provides typed renderer focus, and the pinned AzerothCore
Human Warrior spawn is captured in the Eastern Kingdoms renderer at ADT
`(32,48)`, chunk `(3,12)`.
- Fidelity evidence: five build-12340 camera positions from the M00 manifest map
within `0.002` yard; a pinned AzerothCore human-warrior spawn and one raw MCNK
header observation independently confirm tile `(32,48)` and chunk `(3,12)`;
independent reference formulas are recorded in ADR 0001.
- Performance budgets: constant-time arithmetic; no scene or file access.
- Manual diagnostics: not required for the pure contract; renderer checkpoints
are required when consumers migrate.
## Extension points
- Add typed map IDs around positions without changing axis semantics.
- Add a format-specific placement-orientation adapter with its own fixtures.
- Add serialization only with explicit space/version fields and migrations.
- Add consumer adapters that construct engine-native vectors at their boundary.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Canonical/server/Godot position mapping | Implemented | Headless round-trip, five M00 golden points and migrated GDScript consumers | Network/server adapters remain future consumers |
| ADT placement mapping | Implemented | Synthetic/direct round-trip fixtures | Compare native parsed placements during migration |
| ADT tile/local/chunk ownership | Implemented | Center/extent boundaries, raw `Azeroth_31_49.adt` evidence and direct Godot adapter round-trip | Native parser remains explicit boundary |
| World-facing yaw mapping | Implemented | Cardinal/wrap round-trip fixtures | Placement Euler remains separate |
| Scene-free production contract | Implemented | Source boundary, headless tests and repository-wide conversion gate | Maintain gate with new consumers |
| Cross-source coordinate fixture | Implemented | Pinned AzerothCore spawn, raw ADT MCNK metadata and five build-12340 points pass one headless verifier | Add live adapter/renderer integration |
| TrinityCore populated spawn equivalence | Not verified | The base development schema has no pinned populated row used by this fixture | Pin and verify a TDB snapshot before claiming parity |
| Visual/server-spawn integration | Implemented | Dedicated manifest/verifier plus cold/warm GUI capture of the mapped AzerothCore spawn | This is coordinate/renderer integration evidence, not original-client visual parity |
## Known gaps and risks
- Native ADT/WDT parsing retains one shared boundary formula because worker
parsers cannot call GDScript; the gate prevents additional definitions.
- Contract version 1 briefly transposed ADT filename/chunk axes; version 2 fixes
the defect before any consumer migration.
- MDDF/MODF rotation comments and implementations disagree across existing paths;
this package deliberately does not generalize them into world yaw.
- Finite-number validation belongs to external format adapters and is not yet implemented.
- Arithmetic agreement does not prove terrain, placement, camera direction or visual parity.
- The golden server row is verified against pinned AzerothCore data only; no
TrinityCore equivalence claim is made without a pinned populated TDB snapshot.
## Source map
| Path | Responsibility |
|---|---|
| `src/domain/coordinates/coordinate_mapper.gd` | Constants and all coordinate-axis conversions |
| `src/domain/coordinates/*_position.gd` | Immutable typed position values |
| `src/domain/coordinates/adt_tile_coordinate.gd` | ADT filename tile indices and bounds |
| `src/domain/coordinates/adt_chunk_coordinate.gd` | MCNK indices and bounds |
| `src/domain/coordinates/*_yaw.gd` | Immutable typed world-facing yaw values |
| `src/tools/verify_coordinate_mapper.gd` | Headless fixtures and boundary regressions |
| `src/tests/fixtures/coordinate_golden_points.json` | Versioned cross-source values, provenance and tolerances |
| `src/tools/verify_coordinate_golden_fixtures.gd` | Cross-source fixture schema and mapper validation |
| `src/tools/verify_coordinate_conversion_boundaries.gd` | Repository-wide manual conversion and required-consumer gate |
| `src/gameplay/terrain/adt_terrain_query.gd` | Typed Godot-position consumer for ADT tile/local/chunk lookup |
| `src/render/streaming/streaming_target_planner.gd` | Typed Godot-position consumer for ADT focus tile and boundary-local lookup |
| `src/tools/server_spawn_render_manifest.json` | Pinned server-spawn camera, player, marker and expected ADT ownership |
| `src/tools/verify_server_spawn_renderer.gd` | Fixture-to-renderer manifest contract verifier |
| `docs/adr/0001-canonical-world-coordinates.md` | Normative axes, units, boundaries and rollout decision |
## Related decisions and references
- ADR: [`../adr/0001-canonical-world-coordinates.md`](../adr/0001-canonical-world-coordinates.md)
- M00 evidence: [`../M00_FINAL_PAIRED_EVIDENCE.md`](../M00_FINAL_PAIRED_EVIDENCE.md)
- Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
- Testing rules: [`../TESTING.md`](../TESTING.md)
+232
View File
@@ -0,0 +1,232 @@
# Domain Identities
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | `M01-FND-DOMAIN-IDENTITIES-001` |
| Owners | Foundation/domain |
| Last verified | Worktree `work/sindo-main-codex/m01-domain-identities`, 2026-07-13 |
| Profiles/capabilities | All profiles; identity contract version 1 |
## Purpose
Provide non-interchangeable, scene-free identity values for authored content,
runtime entities, WoW wire objects and namespaced server entries. Each value
states its lifetime and serialization rules instead of exposing a generic `id`.
## Non-goals
- Allocating, reserving or globally registering IDs.
- Parsing packet bytes or server database rows.
- Mapping GUID high types or server entries to gameplay object kinds.
- Defining Content Project, snapshot or replay schemas.
- Replacing future typed map/spell/item/quest/display/sound IDs.
## Context and boundaries
```mermaid
flowchart LR
Authoring[Content Project allocator] --> ContentId[ContentId]
Session[Entity registry] --> EntityId[EntityId]
Wire[Build 12340 packet adapter] --> WowGuid[WowGuid]
Server[Core snapshot adapter] --> ServerEntry[ServerEntryId]
ContentId --> Mapping[Explicit identity mapping records]
EntityId --> Mapping
WowGuid --> Mapping
ServerEntry --> Mapping
Mapping --> Consumers[Gameplay / network / authoring consumers]
Raw[Raw int or string] -. forbidden across boundary .-> Consumers
```
Allowed dependencies:
- GDScript scalar/string operations and `RefCounted` lifetime;
- callers in content, gameplay, network and server adapter layers.
Forbidden dependencies:
- Node, Resource, SceneTree, renderer and UI state;
- packet buffers, SQL connections or database table implementations;
- implicit conversion between identity namespaces;
- random/global allocation inside the value objects.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `ContentId` | Immutable value | Stable authored identity as canonical lowercase UUID text | Any thread; persisted content lifetime | `is_valid()` reports malformed shape |
| `ContentId.equals` | Pure method | Value equality across references | Call duration | Null compares false |
| `ContentId.to_key` | Pure method | Canonical serialization/dictionary key | Stable across schema lifetime | Caller must validate first |
| `EntityId` | Immutable value | Positive session-local runtime sequence | One runtime session | `is_valid()` rejects zero/negative |
| `EntityId.equals` | Pure method | Equality within an explicitly shared session | Call duration | Cross-session comparison is a caller error |
| `EntityId.to_debug_key` | Pure method | Non-persistent diagnostic label | Session/log lifetime | Must not be serialized as content |
| `WowGuid` | Immutable value | Opaque unsigned high/low wire words | Packet/replay/entity mapping lifetime | `has_valid_word_ranges()` reports overflow |
| `WowGuid.is_empty` | Pure method | Detect all-zero wire sentinel | Call duration | Does not assign semantic meaning |
| `WowGuid.to_hex_string` | Pure method | Sixteen-digit diagnostic/replay key | Wire/replay lifetime | Caller validates ranges first |
| `ServerEntryId` | Immutable value | Adapter/table namespace plus positive entry | Snapshot/deployment mapping lifetime | `is_valid()` rejects missing namespace/non-positive entry |
| `ServerEntryId.to_key` | Pure method | Namespaced mapping key | Snapshot/schema lifetime | Caller validates first |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | UUID text | Future Content Project allocator/parser | `ContentId` | Copied normalized string | Build/content lifetime |
| Input | Session sequence | Future entity registry | `EntityId` | Copied integer | One runtime session |
| Input | Two unsigned 32-bit words | Future build-12340 packet codec | `WowGuid` | Copied integers | Packet/replay/entity lifetime |
| Input | Namespace and numeric entry | Future core snapshot adapter | `ServerEntryId` | Copied normalized string/integer | Snapshot/deployment lifetime |
| Output | Validation/equality/key results | Identity values | Adapters/registries/schemas | Returned scalar/string | Call duration |
Side effects:
- none;
- no allocation registry, random generation, file/cache write, packet read,
database query, scene mutation or logging.
## Data flow
```mermaid
flowchart TD
ContentSource[Authored document] -->|UUID text| ContentId
ServerRow[Backend table row] -->|adapter + table + entry| ServerEntryId
Packet[World packet] -->|high/low wire words| WowGuid
Registry[Session entity allocator] -->|positive sequence| EntityId
ServerEntryId --> DeployMap[Explicit deployment mapping]
ContentId --> DeployMap
WowGuid --> RuntimeMap[Explicit runtime mapping]
EntityId --> RuntimeMap
DeployMap --> Snapshot[Versioned snapshot/change set]
RuntimeMap --> WorldState[Session world state]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
state ContentIdentity {
[*] --> Parsed
Parsed --> Persisted: valid ContentId
Persisted --> Referenced
}
state RuntimeIdentity {
[*] --> Allocated
Allocated --> Active
Active --> Released: session owner removes entity
Released --> [*]
}
state ExternalIdentity {
[*] --> Observed
Observed --> Mapped: WowGuid or ServerEntryId
Mapped --> Stale: disconnect or snapshot replacement
Stale --> [*]
}
```
The values themselves are stateless and immutable. Allocation, release,
mapping and stale-state ownership belong to future registries/adapters.
## Main sequence
Identity construction and validation are synchronous. An external adapter reads
its format, constructs the matching value, validates it, then publishes an
explicit mapping or typed domain event. No value object performs I/O or retries.
## Ownership, threading and resources
- Values own only copied strings/integers and are caller-owned `RefCounted` references.
- Public methods do not mutate values or shared state and are worker-safe.
- Content/session/wire/server mapping records are owned by their future layer,
not by these values.
- No Node, RID, file, socket, database, lock or cache resource is acquired.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Malformed ContentId | `is_valid()` | Invalid value remains inspectable | Source field/path supplied by parser | Regenerate or repair authored ID |
| Invalid EntityId sequence | `is_valid()` | Registry must reject publication | Session/correlation context | Fix allocator/session mapping |
| GUID word overflow | `has_valid_word_ranges()` | Codec must reject packet/replay value | Packet offset/opcode context | Reject malformed input; do not truncate |
| Empty GUID | `is_empty()` | Preserved as protocol sentinel | Adapter decides whether field permits empty | Apply opcode/field-specific rule |
| Missing server namespace/entry | `is_valid()` | Snapshot adapter rejects mapping | Backend/table/row context | Correct adapter schema mapping |
| Unknown/stale mapping | Registry/snapshot lookup | No implicit numeric fallback | Typed IDs in diagnostic | Refresh snapshot/session or reject event |
There is no cancellation inside constant-time value operations.
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Identity contract version | 1 | All | No | Four separate namespaces and validation rules |
| ContentId text form | Lowercase `8-4-4-4-12` UUID | Authoring/runtime/tools | No | Stable persisted key |
| GUID decomposition | Opaque two-word storage | `wotlk_335a_build_12340` | No | Prevents unverified high-type assumptions |
## Persistence, cache and migration
- `ContentId.uuid_text` is the only stable persisted identity introduced here.
- Containing schemas must store their own schema version and duplicate/reference policy.
- `EntityId` must never be persisted across sessions.
- `WowGuid` may appear in versioned packet replays as explicit high/low words or
the exact sixteen-digit hex form; a replay schema owns that choice.
- `ServerEntryId` may appear in snapshots only with both namespace and entry.
- This package is additive and migrates no existing data or cache.
## Diagnostics and observability
- Stable diagnostics use `ContentId.to_key`, `WowGuid.to_hex_string` and
`ServerEntryId.to_key`.
- Runtime diagnostics may use `EntityId.to_debug_key` with a session correlation ID.
- Production metrics/logging belong to registries and adapters; values emit none.
## Verification
- Contract test: `src/tools/verify_domain_identities.gd`.
- Fixtures cover UUID normalization/malformed shape, positive/zero session IDs,
maximum/overflow GUID words, zero GUID and cross-table server entry collisions.
- Dependency gate confirms no Node/Resource/Vector3 exposure.
- Fidelity boundary: GUID bit meaning remains unclaimed until build-12340 packet fixtures.
- Performance: constant-time integer checks and a bounded 36-character UUID scan.
## Extension points
- Content Project allocator and duplicate/reference validation.
- Session-owned entity registry and explicit `WowGuid ↔ EntityId` mappings.
- Backend capability adapters that define server entry namespaces.
- Separate typed map/spell/item/quest/display/sound values.
- Fixture-backed build-12340 GUID high-type decoding in the protocol layer.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Stable authored ContentId | Implemented contract | UUID normalization/validation fixtures | Allocator and schema arrive with Content Project |
| Session-local EntityId | Implemented contract | Positive/zero/equality fixtures | Registry lifecycle arrives with gameplay world state |
| Lossless opaque WowGuid | Implemented contract | Full unsigned words and hex fixture | Packet codec/high-type semantics need build-12340 fixtures |
| Namespaced ServerEntryId | Implemented contract | Cross-table collision fixture | Backend namespace capability mapping arrives with server adapters |
| Cross-namespace mappings | Planned | Architecture/ADR boundary | Implement only in owning registries/adapters |
## Known gaps and risks
- No allocator or duplicate registry exists yet.
- GDScript `==` compares references; consumers must use domain `equals` methods
or canonical keys until a registry owns keying.
- UUID shape validation deliberately does not enforce generation version/variant.
- Server namespace vocabulary is adapter-owned and not yet centrally catalogued.
- GUID high-type/object-kind semantics remain unverified rather than guessed.
## Source map
| Path | Responsibility |
|---|---|
| `src/domain/identity/content_id.gd` | Stable canonical UUID content identity |
| `src/domain/identity/entity_id.gd` | Session-local runtime identity |
| `src/domain/identity/wow_guid.gd` | Opaque unsigned WoW wire identity |
| `src/domain/identity/server_entry_id.gd` | Namespaced backend entry identity |
| `src/tools/verify_domain_identities.gd` | Headless value/boundary regression |
| `docs/adr/0002-domain-identity-namespaces.md` | Normative namespace/lifetime decision |
## Related decisions and references
- ADR: [`../adr/0002-domain-identity-namespaces.md`](../adr/0002-domain-identity-namespaces.md)
- Architecture: [`../ARCHITECTURE.md`](../ARCHITECTURE.md)
- Foundation plan: [`../../targets/roadmap/01-foundation-and-data.md`](../../targets/roadmap/01-foundation-and-data.md)
+292
View File
@@ -0,0 +1,292 @@
# Local Player Movement
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | M02 / M02-GMP-MOVEMENT-001, M02-GMP-SANDBOX-CAPABILITIES-001, M02-GMP-INPUT-PROFILE-335-001 and presentation consumers |
| Owners | Gameplay local movement state; scene composition owns the instance |
| Last verified | M02-GMP-INPUT-PROFILE-335-001 worktree, 2026-07-15 |
| Profiles/capabilities | `RenderSandbox` debug movement; `Blizzlike335` selected bindings and keyboard turn with debug sprint/flight rejected |
## Purpose
Own current local velocity, acceleration and sandbox flight state independently
of the player scene. Convert an immutable `MoveIntent` plus an explicit
Godot-world movement basis into deterministic per-tick displacement and convert
signed turn intent into a yaw delta without querying terrain, cameras, input
devices or visual assets.
## Non-goals
- Apply world transforms or own a `CharacterBody3D`.
- Query terrain height, collision, water or slopes.
- Implement jump, fall, swim, server prediction or reconciliation.
- Select the player basis versus camera-pivot basis; the scene adapter owns that decision.
- Claim build-12340 movement fidelity from sandbox regression alone.
## Context and boundaries
```mermaid
flowchart LR
Input[MoveIntent] --> Movement[LocalPlayerMovementController]
Basis[Explicit local movement Basis] --> Movement
Delta[Physics delta seconds] --> Movement
Profile[Scene profile ID] --> Capabilities[PlayerMovementCapabilities]
Capabilities --> Movement
Movement --> Displacement[Godot-world displacement]
Movement --> Yaw[Godot yaw delta radians]
Movement --> Velocity[Read-only velocity state]
Scene[ThirdPersonWowController] --> Basis
Displacement --> Scene
Scene --> Transform[Player world transform]
Scene --> Terrain[TerrainQuery and ground snap adapter]
Velocity --> Presentation[Visual facing and CharacterAnimationPresenter adapter]
```
Allowed dependencies:
- `MoveIntent` from the gameplay input boundary.
- Godot value types `Basis` and `Vector3` for renderer world-space direction math.
- Main-thread scene adapter for basis selection and displacement application.
Forbidden dependencies:
- `Node`, `Resource`, `Input`, `Camera3D`, `ADTLoader` or scene lookup.
- World-coordinate conversion or manual ADT/tile formulas.
- Character assets, animation players, materials, packets or server state.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `PlayerMovementCapabilities.render_sandbox()` | Factory | Creates the only debug sprint/free-flight capability set | Immutable caller value | None |
| `PlayerMovementCapabilities.blizzlike_335()` | Factory | Creates the compatibility-safe set with debug movement disabled | Immutable caller value | Name is intent, not a parity claim |
| `PlayerMovementCapabilities.for_profile_id(...)` | Factory | Maps composition configuration once; unknown IDs fail closed | Immutable caller value | Unknown maps to `Blizzlike335` |
| `LocalPlayerMovementController.new(...)` | Constructor | Sets explicit speeds, acceleration, sprint multiplier and movement capabilities | Created by composition root; retained for player scene lifetime | Null capability defaults to `Blizzlike335` |
| `godot_world_velocity_units_per_second` | Read-only state | Current Godot-world velocity after the last update | Stable until next mutation on owning thread | None |
| `is_flight_enabled` | Read-only state | Current sandbox free-flight mode | Stable until toggle | None |
| `movement_profile_id` | Read-only state | Effective capability profile ID | Controller lifetime | None |
| `toggle_sandbox_flight()` | Command | Toggles flight only when explicitly permitted and clears velocity | Owning physics/input thread | Rejected request returns `false` with state unchanged |
| `advance(move_intent, godot_world_movement_basis, delta_seconds)` | Stateful update | Accelerates toward requested velocity and returns displacement for the tick | Owning physics thread; one call per tick | Negative delta is treated as zero |
| `calculate_yaw_delta_radians(move_intent, delta_seconds)` | Pure query | Converts signed turn input to Godot yaw delta | Owning physics thread | Negative delta is treated as zero |
Constructor units:
| Parameter | Unit/meaning |
|---|---|
| `run_speed_units_per_second` | Forward Godot units per second |
| `backward_speed_units_per_second` | Backward Godot units per second |
| `strafe_speed_units_per_second` | Lateral Godot units per second |
| `flight_vertical_speed_units_per_second` | Sandbox vertical Godot units per second |
| `acceleration_units_per_second_squared` | Velocity change per second |
| `sandbox_sprint_multiplier` | Dimensionless debug multiplier |
| `keyboard_turn_speed_radians_per_second` | Positive keyboard yaw speed; defaults to PI rad/s |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | `MoveIntent` | `PlayerInputSource` | Movement controller | Immutable caller-held value | One physics tick |
| Input | `PlayerMovementCapabilities` | Application/player scene composition | Movement controller | Immutable caller-held value | Controller lifetime |
| Input | Godot-world `Basis` | Player or `ThirdPersonCameraRig` adapter | Movement controller | Value copy | One physics tick |
| Input | Delta seconds | Godot physics loop | Movement controller | Scalar value | One physics tick |
| Input | Flight toggle command | Remappable sandbox action via scene adapter | Movement controller | Synchronous command | Input event dispatch |
| Output | Displacement `Vector3` | Movement controller | Player scene adapter | Value copy | Applied in same tick |
| Output | Yaw delta radians | Movement controller | Player scene adapter | Scalar value | Applied before movement basis selection |
| Output | Velocity `Vector3` | Movement controller | Facing and `CharacterAnimationPresenter` scene adapter | Read-only value copy | Until next update |
| Output | Flight state | Movement controller | Basis/terrain scene adapter | Read-only boolean | Until next toggle |
Side effects:
- Mutates only controller-owned velocity and flight state.
- Does not mutate scene tree, transforms, resources, filesystem, network or cache.
- The scene adapter remains responsible for transform mutation and ground snap.
## Data flow
```mermaid
flowchart LR
Intent[MoveIntent axes] --> Target[Calculate target velocity]
Intent --> Turn[Calculate signed yaw delta]
Turn --> SceneYaw[Scene applies character yaw]
Basis[Player or camera-pivot Basis] --> Directions[Forward/right directions]
Directions --> Target
Config[Speeds and sprint multiplier] --> Target
Profile[RenderSandbox or Blizzlike335] --> Capability[Immutable debug capabilities]
Capability -->|allow or reject sprint| Target
Capability -->|allow or reject flight toggle| Current
Previous[Owned velocity] --> Integrate[move_toward by acceleration × delta]
Target --> Integrate
Integrate --> Current[Updated velocity]
Current --> Step[velocity × delta]
Step --> Displacement[Scene-applied displacement]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> GroundedSandbox
GroundedSandbox --> FlyingSandbox: toggle [debug flight allowed] / clear velocity
GroundedSandbox --> GroundedSandbox: toggle [debug flight rejected]
FlyingSandbox --> GroundedSandbox: toggle / clear velocity
GroundedSandbox --> GroundedSandbox: advance
FlyingSandbox --> FlyingSandbox: advance
```
The names describe the existing sandbox mode only. They are not authoritative
gameplay movement states and do not imply terrain contact or server permission.
## Main sequence
```mermaid
sequenceDiagram
participant Input as PlayerInputSource
participant Scene as ThirdPersonWowController
participant Move as LocalPlayerMovementController
participant Cap as PlayerMovementCapabilities
participant Present as CharacterAnimationPresenter adapter
Input-->>Scene: MoveIntent
Scene->>Cap: map configured profile once
Scene->>Move: construct with immutable capabilities
Scene->>Move: calculate_yaw_delta_radians(intent, delta)
Move-->>Scene: yaw delta
Scene->>Scene: apply yaw and synchronize camera orbit yaw
Scene->>Move: advance(intent, selected basis, delta)
Move-->>Scene: displacement
Scene->>Scene: apply global position and ground snap
Scene->>Move: godot_world_velocity_units_per_second
Scene->>Present: horizontal motion
```
## Ownership, threading and resources
- The player scene creates and exclusively owns one movement controller.
- The composition root creates one immutable capability value; the controller
retains it and exposes only its effective profile ID.
- Velocity and flight state have the same lifetime as that controller.
- All mutations currently occur on the main input/physics thread.
- Read-only value returns are copies; consumers cannot mutate owned state.
- No Nodes, Resources, RIDs, files, jobs or worker threads are created.
- The supplied `Basis` is a Godot-world direction basis, not a typed world-position contract or coordinate conversion.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Negative physics delta | `advance` guard | Returns zero displacement; velocity unchanged | Unit regression | Resume with valid non-negative delta |
| Missing scene basis owner | Scene composition defect | Controller cannot be called correctly | Scene/parser error or boundary test | Restore explicit scene dependency |
| Flight toggle during motion | Command path | Velocity clears before next step | State-transition regression | Continue from zero velocity |
| Flight/sprint request in `Blizzlike335` | Capability check | Request has no effect | Profile regression | Select explicit `RenderSandbox` only for tooling/debug scenes |
| Unknown/null profile capability | Factory/constructor safe default | Uses `Blizzlike335`; debug movement disabled | Capability regression | Correct composition profile ID |
| Missing terrain data | `TerrainGroundSample.is_available == false` | Scene skips existing ground snap | Stable terrain failure code | Replace/recover query backend |
There is no asynchronous cancellation, retry or rollback. Recreating the
controller resets velocity and flight state deterministically.
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Forward speed | `7.0` units/s | Current sandbox | Fixed for controller lifetime | Target forward velocity |
| Backward speed | `4.5` units/s | Current sandbox | Fixed for controller lifetime | Target backward velocity |
| Strafe speed | `4.5` units/s | Current sandbox | Fixed for controller lifetime | Target lateral velocity |
| Acceleration | `28.0` units/s² | Current sandbox | Fixed for controller lifetime | Acceleration and deceleration |
| Flight vertical speed | `7.0` units/s | Sandbox debug | Fixed for controller lifetime | Vertical free-flight target |
| Sprint multiplier | `6.0` | Sandbox debug | Fixed for controller lifetime | Multiplies planar and vertical targets |
| Camera-relative basis | Selected only while flight is enabled | Sandbox debug | Per tick | Preserves existing pitched flight direction |
| Debug sprint permission | Enabled | `RenderSandbox` only | Immutable | Allows sprint request multiplier |
| Debug free-flight permission | Enabled | `RenderSandbox` only | Immutable | Allows toggle and vertical movement |
| Compatibility-safe debug permissions | Disabled | `Blizzlike335` | Immutable | Ignores sprint/toggle requests |
| Keyboard turn rate | `3.141594` rad/s | `Blizzlike335` composition | Fixed for controller lifetime | Server-contract default applied to signed turn intent |
## Persistence, cache and migration
Movement state is transient and not serialized. No schema, cache, migration or
saved settings change is introduced. Future reconnect/replay state requires a
separate versioned movement snapshot contract.
## Diagnostics and observability
- Logs: verifier emits `LOCAL_PLAYER_MOVEMENT PASS` or named invariant failures.
- Metrics: none yet; update is constant-time vector math.
- Debug views: current character facing and animation remain observable scene outputs.
- Correlation IDs: not applicable to synchronous local sandbox state.
## Verification
- Unit/contract: `src/tools/verify_local_player_movement.gd` covers forward,
backward, strafe, rotated basis, acceleration, deceleration, sprint, flight,
pitched camera basis, state reset, signed keyboard turn and invalid delta.
- Capability: `verify_player_movement_capabilities.gd` covers three profile-ID
cases, safe defaults, sprint/flight allow/reject behavior and two real scene compositions.
- Boundary: verifier rejects Node/Input/Camera/ADT dependencies and movement
integration/state remaining in the player scene.
- Scene integration: `verify_player_input.gd` drives the asset-free player
regression scene through forward motion and immediate flight toggle.
- Fidelity evidence: translation defaults remain regression-locked against the
pre-extraction sandbox. The compatibility keyboard turn default is pinned to
TrinityCore 3.3.5 `playerBaseMoveSpeed[MOVE_TURN_RATE]`; that server contract
does not alone prove original-client timing parity.
- Performance budget: constant-time vector math, no per-tick I/O or scene lookup;
`MoveIntent` allocation remains owned by the input package.
## Extension points
- Terrain/collision policy can consume displacement without changing input or velocity ownership.
- A future server-aware predictor may replace this controller behind the scene composition boundary.
- A typed movement snapshot may expose deterministic replay state when M08/M09 require it.
- A future application profile can map to this narrow capability value without
coupling the movement controller to the application shell.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Scene-free local velocity state | Implemented | Pure controller and source boundary regression | Integrate target checklist after review |
| Existing planar acceleration/speeds | Implemented | Exact numeric unit cases and real scene regression | Compare with build 12340 before fidelity claim |
| Compatibility keyboard turn | Implemented | Pinned server-core value, signed pure cases and real-scene profile regression | Direct original-client timing comparison remains required |
| Existing camera-relative free flight | Implemented in `RenderSandbox` | Pitched-basis, profile and toggle/reset cases | Original-client flight semantics remain out of scope |
| Typed sprint/free-flight exclusion | Implemented | Pure and real-scene profile regressions | Application shell must select profile explicitly later |
| Terrain height query | Implemented | Typed `TerrainQuery` and injected player regression | Ground-snap policy remains scene-owned |
| Terrain collision movement policy | Planned | Height-only query does not model collision | Add slopes/holes/collision later |
| Jump/fall/swim | Planned | M02/M09 roadmap | Requires terrain/liquid and server contracts |
| Prediction/reconciliation | Planned | M08/M09 roadmap | Requires movement snapshot/network contract |
## Known gaps and risks
- Translation/acceleration defaults preserve the existing sandbox and are not original-client evidence.
- Configuration is captured at scene `_ready`; runtime mutation of exported speed
properties does not reconfigure an existing controller.
- Ground snap remains in the player scene, but its ADT data access is now behind
a replaceable `TerrainQuery`.
- Runtime renderer scenes intentionally default to `RenderSandbox`; a future
game-client composition must explicitly select `Blizzlike335`.
- `Blizzlike335` currently means only debug movement exclusion and must not be
interpreted as evidence that remaining movement semantics are 1:1.
- The module uses Godot math value types; a future engine-independent gameplay
predictor may require scalar/domain movement contracts.
## Source map
| Path | Responsibility |
|---|---|
| `src/gameplay/movement/local_player_movement_controller.gd` | Owned velocity/flight state and deterministic integration |
| `src/gameplay/movement/player_movement_capabilities.gd` | Immutable profile-to-debug-capability contract and safe defaults |
| `src/gameplay/terrain/terrain_query.gd` | Replaceable ground-height input boundary |
| `src/scenes/player/third_person_camera_rig.gd` | Camera-relative sandbox flight basis producer |
| `src/scenes/character/character_animation_presenter.gd` | Locomotion presentation consumer behind the scene adapter |
| `src/scenes/player/third_person_wow_controller.gd` | Composition, basis selection, displacement/terrain and presentation-state adapters |
| `src/tools/verify_local_player_movement.gd` | Pure numeric, state-transition and dependency regression |
| `src/tools/verify_player_movement_capabilities.gd` | Profile factory, allow/reject and real-scene regression |
| `src/tests/scenes/player_input_regression.tscn` | Asset-free integrated player fixture |
| `src/tools/verify_player_input.gd` | Input-to-movement scene regression |
| `src/tests/fixtures/player_input_profile_335.json` | Turn-rate and selected binding provenance |
## Related decisions and references
- ADR: none; this follows the existing M02 decomposition boundary.
- Upstream/reference: `targets/02-player-decomposition.md`,
`targets/roadmap/04-gameplay-systems.md`, `docs/ARCHITECTURE.md`.
@@ -0,0 +1,231 @@
# M2 Animated Instance Materializer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-ANIMATED-INSTANCE-MATERIALIZER-001` |
| Owners | Animated M2 duplicate/batch construction, render settings, playback startup and attachment |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animated-instance-materializer`, 2026-07-18 |
| Profiles/capabilities | Imported GLB and native experimental animated M2 instances |
## Purpose
Materialize one ordered animated M2 build batch on the main thread. The service
duplicates an accepted prototype, preserves historical names and transforms,
applies visibility/shadow settings recursively, starts native/imported playback,
attaches a non-empty batch and returns detached native diagnostic entries.
## Non-goals
- Plan build batches, advance job cursors or consume render budgets.
- Load, finalize, select or cache animated prototypes.
- Select animations or implement native animator deformation.
- Format logs, normalize paths, assign Editor owners or clean up tile roots.
- Materialize static M2 `MultiMesh` batches.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader adapter] --> Materializer[M2AnimatedInstanceMaterializer]
Prototype[Accepted animated prototype] --> Materializer
Plan[Transforms and batch slice] --> Materializer
Materializer --> Playback[M2AnimationPlaybackController]
Materializer --> Finalizer[M2AnimatedSceneFinalizer traversal]
Materializer --> Batch[Attached animated batch]
Materializer --> Diagnostics[Detached diagnostic entries]
```
Allowed dependencies are Godot Node/Node3D/GeometryInstance3D APIs and the
accepted animated finalizer and playback services. Resource loading, files,
workers, caches, scheduler policy, Editor ownership and `MultiMesh` are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `materialize_batch(parent, path, prototype, transforms, start, count, serial, visibility_end, visibility_margin, cast_shadows, native_script, collect_diagnostics)` | Command/query | Build, start and attach one ordered animated batch | Main thread; borrowed inputs, parent owns result | Invalid/empty input or all failed duplicates returns `{}` |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | M2 parent root | Loader build job | Batch attachment | Borrowed Node3D | Tile/job lifetime |
| Input | Relative path, start/count and serial | Batch planner/loader adapter | Names, phase and slice | Copied scalars | One call |
| Input | Accepted animated prototype | Prototype cache | Duplicate source | Borrowed Node3D | Cache-owned |
| Input | Ordered transforms | Placement grouping/build job | Instance transforms | Borrowed Array | Job lifetime |
| Input | Visibility/shadow settings | Renderer configuration | Recursive render mutation | Copied scalars | One call |
| Input | Native animator Script identity | Loader composition | Playback controller | Borrowed Script | Application lifetime |
| Output | `batch_root` | Materializer | Parent SceneTree and loader Editor-owner adapter | Parent-owned Node3D | Tile lifetime |
| Output | `native_diagnostics` entries | Playback controller/materializer | Loader log adapter | Caller-owned Array/Dictionaries | Debug call |
Side effects are Node duplication, instance naming/transform assignment,
recursive geometry mutation, playback mutation and SceneTree attachment. The
service retains no batch, prototype, transform or diagnostic references.
## Data flow
```mermaid
flowchart TD
Slice[Ordered transform slice] --> Duplicate[Duplicate prototype with signals, groups and scripts]
Duplicate --> Identity[Assign historical name and transform]
Identity --> Render[Apply visibility margin and shadow recursively]
Render --> NativeCopy[Copy native animator runtime fields]
NativeCopy --> AttachInstance[Attach duplicate to detached batch]
AttachInstance --> Players[Inventory AnimationPlayers]
Players --> Start[Start deterministic native/imported playback]
Start --> More{More slice entries?}
More -->|yes| Duplicate
More -->|no, non-empty| AttachBatch[Attach batch to supplied parent]
Start --> Diagnostics[Tag detached states with instance index]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Validating
Validating --> Empty: invalid input
Validating --> Building: valid slice
Building --> Building: next successful duplicate
Building --> Empty: no successful duplicates
Building --> Attached: non-empty batch
Empty --> [*]
Attached --> [*]: parent/tile owner releases subtree
```
The `RefCounted` service is stateless; lifecycle labels describe each batch call.
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant M as M2AnimatedInstanceMaterializer
participant F as M2AnimatedSceneFinalizer
participant P as M2AnimationPlaybackController
participant R as M2 parent root
L->>M: materialize_batch(slice, prototype, render settings)
loop ordered instance
M->>M: duplicate, name, transform, render settings
M->>P: copy native animator fields
M->>F: animation_players_in_subtree(duplicate)
M->>P: start_instance_playback(path, index, players, debug)
end
M->>R: add_child(non-empty batch)
M-->>L: batch root and indexed diagnostics
L->>L: format logs and assign Editor owner
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Materializer[M2AnimatedInstanceMaterializer]
Materializer --> Playback[M2AnimationPlaybackController]
Materializer --> Finalizer[M2AnimatedSceneFinalizer]
Materializer --> Engine[Node3D / GeometryInstance3D / SceneTree APIs]
Loader --> EditorOwner[Editor ownership adapter]
Loader --> BuildJob[Build cursor and budget]
Materializer -. no dependency .-> Cache[Prototype/cache/load state]
Materializer -. no dependency .-> MultiMesh[Static M2 materialization]
```
## Ownership, threading and resources
- Loader/build state borrows the accepted prototype and ordered transform Array.
- Each successful duplicate becomes a child of the detached batch immediately.
- A non-empty batch transfers to the supplied parent; the parent owns its lifetime.
- A batch with no successful duplicates is queued for deletion and no reference returns.
- Playback/finalizer services borrow each duplicate only during synchronous calls.
- Returned diagnostic entries contain the stable instance index and a detached state.
- All Node duplication, mutation and attachment is main-thread-only.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Null parent/prototype | Entry guard | Return `{}` without mutation | Contract verifier | Correct composition/cache state |
| Empty transforms or non-positive count | Entry guard | Return `{}` | Contract verifier | Planner supplies a non-empty slice |
| Duplicate is not Node3D | Cast result | Skip that entry and preserve order of successes | Child-count contract | Repair prototype scene root |
| Every duplicate fails | Empty batch check | Queue empty batch, return `{}` | Empty-result contract | Rebuild/import prototype |
| Shutdown during/after call | Outside service | Call is synchronous; loader owns cancellation and subtree release | Shutdown regression | Loader cancels job/releases parent |
Transform bounds remain a planner/build-job precondition exactly as before the
extraction; this service does not clamp or silently reorder an invalid slice.
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Duplicate flags | Signals + groups + scripts | All | No | Preserves historical instance behavior |
| Visibility end/margin | Loader renderer settings / chunk size | All | Yes, between calls | Applied to every GeometryInstance3D descendant |
| Shadow mode | Loader `m2_cast_shadows` | All | Yes, between calls | Enables/disables descendant shadow casting |
| Native diagnostics | Loader `debug_streaming` | Debug | Yes | Collects indexed state only when requested |
## Persistence, cache and migration
No data is serialized and no cache/schema version changes. Editor owner
assignment remains a loader concern after attachment, so generated-scene
persistence behavior and migration remain unchanged.
## Diagnostics and observability
- The materializer emits no logs, metrics or debug views.
- Native state remains detached and is tagged with the absolute instance index.
- Loader preserves the existing normalized-path `M2_NATIVE_ANIMATOR` log text.
- No correlation ID is introduced; path/index remains the existing identity.
## Verification
- `verify_m2_animated_instance_materializer.gd` covers invalid input, names,
order, transforms, recursive visibility/margin/shadows, playback, native field
references, indexed diagnostics, prototype immutability and ownership boundaries.
- Playback/finalizer/pipeline/build/prototype/shutdown/material/facade/internal-
access regressions protect adjacent behavior.
- Fidelity evidence is exact extraction of existing flags/order/settings/calls;
no original-client visual comparison or proprietary asset result is claimed.
- Performance budget: 1,000 synthetic Node3D instances under one second.
## Extension points
A later facade-owned animated presentation command may supply the same explicit
batch contract. Async SceneTree mutation, callbacks and a generic scene factory
are intentionally excluded until measurements show a need.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Ordered animated instance/batch materialization | Implemented extraction | Synthetic batch contract verifier | Proprietary asset traversal pending |
| Recursive visibility/shadow settings | Implemented extraction | Nested geometry fixture | Visual distance/shadow comparison pending |
| Native/imported playback startup | Delegated to implemented controller | Playback + materializer verifiers | Full WoW animation-state mapping pending |
| Editor persistence | Existing loader-owned | Source boundary and editor helper | Editor integration fixture remains separate |
## Known gaps and risks
- The existing build planner must continue to guarantee valid transform bounds.
- Default animation selection and native deformation retain their documented gaps.
- No private asset, paired original-client, descriptor-pressure, leak or p95/p99 run exists.
- Recursive render mutation remains proportional to duplicate subtree size.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_animated_instance_materializer.gd` | Duplicate, render mutation, playback startup and non-empty batch attachment |
| `src/render/m2/m2_animation_playback_controller.gd` | Phase, selection and native/imported playback mutation |
| `src/render/m2/m2_animated_scene_finalizer.gd` | AnimationPlayer inventory for each duplicate |
| `src/scenes/streaming/streaming_world_loader.gd` | Build adapter, diagnostic formatting and Editor ownership |
| `src/tools/verify_m2_animated_instance_materializer.gd` | Batch/render/playback/boundary/timing regression |
## Related decisions and references
- [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md)
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
- [`m2-build-batch-planner.md`](m2-build-batch-planner.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)
+212
View File
@@ -0,0 +1,212 @@
# M2 Animated Scene Finalizer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-ANIMATED-SCENE-FINALIZER-001` |
| Owners | Detached animated candidate, material repair and player validation |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animated-scene-finalizer`, 2026-07-18 |
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
## Purpose
Finalize one loaded animated M2 scene on the main thread: instantiate a valid
detached Node3D candidate, copy the historical static-prototype material mapping
and accept only candidates containing AnimationPlayer descendants.
## Non-goals
- Call ResourceLoader or interpret threaded-load statuses.
- Choose animation eligibility, cache paths, permits or material prototypes.
- Adopt prototypes, mark static fallback or select/play animations.
- Process native M2 animators or static Mesh rebuilds.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] -->|loaded Resource and material source| Finalizer[M2AnimatedSceneFinalizer]
Finalizer -->|accepted Node3D and player count| Loader
Loader --> Cache[M2PrototypeCacheState]
```
Allowed dependencies are Godot scene/resource/material types. ResourceLoader,
filesystem, workers, scheduler, builders, raw repositories, prototype cache and
other application layers are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `instantiate_candidate(resource)` | Ownership query | Instantiate PackedScene as detached Node3D | Main thread; caller receives valid candidate | Unsupported/invalid null; created wrong-type root freed |
| `repair_materials(animated_root, material_source_root)` | Command | Apply historical material overrides | Main thread; borrowed roots | Null/empty roots no-op |
| `finalize_candidate(candidate_root)` | Ownership command/query | Require AnimationPlayer and transfer root/count | Main thread; detached candidate | Null empty; rejected candidate freed |
| `mesh_instances_in_subtree(root)` | Query | Depth-first MeshInstance3D inventory | Main thread; borrowed subtree | Null returns empty |
| `animation_players_in_subtree(root)` | Query | Depth-first AnimationPlayer inventory | Main thread; borrowed subtree | Null returns empty |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Loaded Resource | Loader I/O adapter | Candidate instantiator | Borrowed Resource | One finalize permit |
| Input | Static material prototype Node3D | Loader cache/build adapter | Material repair | Borrowed Node | One repair call |
| Input | Detached animated candidate | Instantiator | Repair/final validation | Finalizer then caller/release | One attempt |
| Output | Accepted prototype and player count | Finalizer | Loader adoption/log adapter | Exact Node transferred | Shutdown cache lifetime |
| Output | Depth-first engine-node arrays | Traversal | Loader preparation/playback | Borrowed references | One call |
Side effects are PackedScene instantiation, surface override assignment and
synchronous destruction of rejected detached roots.
## Data flow
```mermaid
flowchart TD
Resource[Loaded Resource] --> Packed{PackedScene?}
Packed -->|no| Reject[Return null]
Packed -->|yes| Instantiate[Instantiate]
Instantiate --> Root{Node3D?}
Root -->|no| FreeWrong[Free root and reject]
Root -->|yes| Repair[Repair materials]
Repair --> Players{AnimationPlayer exists?}
Players -->|no| FreeCandidate[Free and reject]
Players -->|yes| Transfer[Transfer exact root and count]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Candidate: instantiate valid scene
Candidate --> Candidate: repair materials
Candidate --> Accepted: player found
Candidate --> Released: no player
Accepted --> [*]: ownership transferred
Released --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant F as M2AnimatedSceneFinalizer
participant C as M2PrototypeCacheState
L->>F: instantiate_candidate(Resource)
F-->>L: detached Node3D or null
L->>L: get static material prototype
L->>F: repair_materials(candidate, source)
L->>F: finalize_candidate(candidate)
alt accepted
F-->>L: exact Node3D and player count
L->>C: adopt animated prototype
else rejected
F-->>L: empty; candidate freed
L->>C: mark animation static
end
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Finalizer[M2AnimatedSceneFinalizer]
Finalizer --> Engine[PackedScene / Node3D / Mesh / Material / AnimationPlayer]
Loader --> Resource[ResourceLoader]
Loader --> Budget[RenderBudgetScheduler]
Loader --> Prototype[M2PrototypeCacheState]
Finalizer -. no dependency .-> Resource
Finalizer -. no dependency .-> Budget
Finalizer -. no dependency .-> Prototype
```
## Ownership, threading and resources
- Every method runs synchronously on the renderer main thread.
- A valid candidate is detached and finalizer-owned until acceptance.
- Acceptance transfers the exact Node3D to the loader/prototype cache path.
- Rejection frees the candidate synchronously, including wrong-type roots.
- Traversal results borrow Nodes; source materials remain Resource-owned.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Null/unsupported Resource | Type guard | Return null | Dedicated verifier | Loader marks static |
| Wrong root type | Instantiated type guard | Free and return null | Node-count regression | Rebuild cache |
| No material source/meshes | Null/empty traversal | Leave materials unchanged | Material fixture | Imported materials remain |
| Missing source surface | Material lookup | First source material fallback | Mapping fixture | Repair static source |
| No AnimationPlayer | Descendant inventory | Free and return empty | Lifetime regression | Loader marks static |
| Shutdown/cancellation | Not owned | No retained state | N/A | Loader drains first |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Finalize permit | `m2_animation_finalize_ops_per_tick = 1` | Existing profiles | Yes | Caller bounds calls |
| Material mapping | Depth-first/index-clamped | All | No | Preserves current appearance |
| Required players | At least one | All | No | Rejects non-animated scenes |
## Persistence, cache and migration
The service retains no state and serializes nothing. GLB/cache formats, material
versions and prototype lifetimes are unchanged; no rebake is required.
## Diagnostics and observability
- Accepted result reports player count for the existing `M2_ANIM_CACHE` log.
- Traversal order is deterministic depth-first preorder.
- The service owns no logging or metrics.
## Verification
- `verify_m2_animated_scene_finalizer.gd` covers invalid Resources, wrong-root
release, exact transfer, player traversal/count, override priority, fallback,
source-index mapping, source boundaries and timing.
- Adjacent animation pipeline/prototype/shutdown/material/build tests protect callers.
- Fidelity evidence is exact mapping/lifecycle extraction; no asset-backed or
original-client visual parity claim is made.
- Performance budget: 10,000 depth-eight player inventories under one second.
## Extension points
Animation playback policy and native animator field copying now belong to
`M2AnimationPlaybackController`; accepted instance/batch materialization is owned
by `M2AnimatedInstanceMaterializer`.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| PackedScene candidate | Implemented extraction | Type/lifetime fixture | Proprietary corrupt corpus pending |
| Material repair | Implemented extraction | Exact-reference mapping | Asset-backed comparison pending |
| Player validation | Implemented extraction | Nested traversal/transfer | Multi-player GLB corpus pending |
## Known gaps and risks
- Material matching remains positional rather than semantic.
- Accepted prototypes remain unbounded until final loader shutdown.
- No private traversal, descriptor-pressure, p95/p99 or paired-client run exists.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_animated_scene_finalizer.gd` | Candidate ownership, traversal, material repair and validation |
| `src/render/m2/m2_animation_playback_controller.gd` | Accepted-instance playback and native animator startup |
| `src/render/m2/m2_animated_instance_materializer.gd` | Per-duplicate player inventory consumer and batch owner |
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending/terminal records before finalization |
| `src/render/m2/m2_prototype_cache_state.gd` | Accepted prototype/static-only outcomes |
| `src/scenes/streaming/streaming_world_loader.gd` | I/O, permits, material source, adoption and logs |
| `src/tools/verify_m2_animated_scene_finalizer.gd` | Scene/material/lifetime/boundary/timing regression |
## Related decisions and references
- [`m2-animation-playback-controller.md`](m2-animation-playback-controller.md)
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.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)
@@ -0,0 +1,225 @@
# M2 Animation Load Pipeline State
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-ANIMATION-LOAD-PIPELINE-001` |
| Owners | Animated M2 threaded request records and terminal finalize FIFO |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-load-pipeline`, 2026-07-17 |
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
## Purpose
Own cross-frame bookkeeping between a successful animated M2 ResourceLoader
request, terminal polling and budgeted main-thread scene finalization. This is
an exact state extraction; cached animation eligibility and request admission
belong to `M2CachedAnimationResourceObserver`, while scene finalization and
retained Node lifecycle belong to `M2AnimatedSceneFinalizer` and
`M2PrototypeCacheState`.
## Non-goals
- Select cache paths, interpret ResourceLoader statuses or perform I/O.
- Decide animated/static fallback or own prototype Nodes.
- Instantiate PackedScenes, repair materials or consume render permits.
- Merge the distinct animated and static-Mesh pipelines.
## Context and boundaries
```mermaid
flowchart LR
Observer[M2CachedAnimationResourceObserver] --> IO[ResourceLoader request]
Observer --> State[M2AnimationLoadPipelineState]
Loader[StreamingWorldLoader] --> IO
Loader --> State
State -->|detached pending records| Loader
Loader -->|opaque terminal status| State
State -->|completion FIFO| Loader
Loader --> Budget[M2_ANIMATION_FINALIZE permit]
Loader --> Prototype[M2PrototypeCacheState]
```
Allowed dependencies are value containers, Strings and opaque integer statuses.
ResourceLoader, workers, scheduler, Node/Resource/Mesh ownership and other
renderer services are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `remember_request(normalized_relative_path, resource_path)` | Command/query | Insert one pending request | Renderer main thread; until transition/clear | Empty/duplicate false |
| `has_request(path)` | Query | Pending-request dedupe | Main thread | Empty/unknown false |
| `request_records_snapshot()` | Query | Detached pending records in insertion order | Main thread; caller-owned | None |
| `complete_request(path, terminal_status)` | Command/query | Move copied record into completion FIFO | Main thread after poll | Unknown false |
| `discard_request(path)` | Command/query | Remove without finalization | Main thread | Unknown false |
| `has_finalize_record()` / `pop_finalize_record()` | Query/command | Drain completion-order FIFO | Main-thread budget drain | Empty pop returns `{}` |
| `total_work_count()` | Query | Pending plus finalize metric | Main thread | None |
| `pending_request_count()` / `finalize_record_count()` | Query | Stage diagnostics | Main thread | None |
| `clear()` | Command | Drop bookkeeping after caller I/O drain/reset | Main thread | Idempotent; no I/O drain |
| `diagnostic_snapshot()` | Query | Detached paths/status records | Main thread | No Resources exposed |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Normalized M2 path and GLB Resource path | Loader request adapter | Pending map | Copied Strings | Until completion/discard/clear |
| Input | Opaque terminal status | Loader polling adapter | Finalize FIFO | Integer value | Until pop/clear |
| Output | Detached pending records | State | Loader poll/shutdown adapter | Caller-owned copies | One pass |
| Output | Oldest completion record | State | Loader finalizer | Transferred Dictionary | One finalize attempt |
| Output | Detached diagnostics | State | Verifier/future metrics | Caller-owned copies | Snapshot lifetime |
Side effects are limited to collection mutation and retaining String/integer values.
## Data flow
```mermaid
flowchart TD
Start[Successful threaded request] --> Remember[Remember request]
Remember --> Poll[Loader polls detached snapshot]
Poll --> Terminal{Loaded or failed?}
Terminal -->|no| Poll
Terminal -->|yes| Complete[Complete with opaque status]
Complete --> FIFO[Finalize FIFO]
FIFO --> Permit{Permit available?}
Permit -->|no| FIFO
Permit -->|yes| Pop[Pop oldest record]
Pop --> Finalize[Loader loads/instantiates or marks static]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Pending: remember
Pending --> TerminalQueued: complete
Pending --> Absent: discard or clear
TerminalQueued --> Absent: pop or clear
```
## Main sequence
```mermaid
sequenceDiagram
participant O as CachedAnimationObserver
participant L as StreamingWorldLoader
participant R as ResourceLoader
participant S as M2AnimationLoadPipelineState
participant P as M2PrototypeCacheState
O->>R: load_threaded_request(GLB)
O->>S: remember_request(path, GLB)
loop frames
L->>S: request_records_snapshot()
L->>R: load_threaded_get_status(GLB)
end
L->>S: complete_request(path, status)
L->>S: pop_finalize_record() after permit
L->>R: load_threaded_get(GLB)
L->>P: adopt animated prototype or mark static
```
## Ownership, threading and resources
- Main thread serializes all mutation.
- State owns only request/finalize Dictionaries with copied paths and statuses.
- Loader drains pending ResourceLoader paths before orderly shutdown clear.
- Loader owns PackedScene instantiation and material repair; prototype state owns
accepted detached Node references and static-only outcomes.
## Dependency diagram
```mermaid
flowchart TB
Observer[M2CachedAnimationResourceObserver] --> State[M2AnimationLoadPipelineState]
Observer --> Resource[ResourceLoader request]
Loader[StreamingWorldLoader] --> State[M2AnimationLoadPipelineState]
Loader --> Resource
Loader --> Budget[RenderBudgetScheduler]
Loader --> Prototype[M2PrototypeCacheState]
State -. no dependency .-> Resource
State -. no dependency .-> Budget
State -. no dependency .-> Prototype
```
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller or request later |
| Request start/cache miss | Cached observer | No insertion; mark static | Observer contract | Cache correction/reload |
| Non-terminal status | Loader | Keep pending | Existing metric | Poll next frame |
| Failed terminal load | Popped status | Loader marks static | Existing behavior | Future map/session reload |
| Empty defensive path | Loader poll | Discard and mark static | Source contract | Correct producer |
| Shutdown | Loader drains pending paths | Clear state | Source/shutdown regressions | New loader starts empty |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| `enable_m2_animated_instances` | `true` | Existing renderer profile | Yes | Enables caller request path |
| `m2_animation_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Bounds caller FIFO drain |
| Animated allow/deny/primitive rules | Existing values | Existing renderer profile | Yes | Observer filters before state insertion |
## Persistence, cache and migration
State is not serialized. Cache formats and prototype lifetimes are unchanged;
no rebake or migration is required.
## Diagnostics and observability
- `total_work_count()` preserves all three historical `m2_animation` metrics.
- Snapshots expose only paths and opaque statuses, never Resources or Nodes.
- Normalized M2 path is the correlation key; existing loader logs are unchanged.
## Verification
- `verify_m2_animation_load_pipeline_state.gd` covers validation, dedupe,
insertion order, completion FIFO, opaque status, discard, detached snapshots,
source boundaries and 100-by-256 timing.
- Adjacent renderer, scheduler, prototype and shutdown regressions cover callers.
- Fidelity evidence is exact state/lifecycle extraction; no visual 3.3.5a parity
or proprietary asset-backed claim is made.
- Performance budget: 25,600 request/complete/pop transitions under one second.
## Extension points
ResourceLoader polling may later move behind a separate adapter without
changing this value-only state contract. Animated-scene finalization is now a
sibling service.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Animated request/finalize state | Implemented extraction | Synthetic contract/source/timing verifier | Asset-backed traversal/leak/p95/p99 pending |
| Cached request admission and GLB selection | Implemented in observer | Policy/GLB/source verifier | Asset-backed traversal pending |
| Terminal ResourceLoader polling | Existing loader-owned | Shutdown/finalizer regressions | I/O adapter extraction optional |
| Animated prototype outcomes | Implemented extraction | Prototype cache verifier | Asset-backed animation fidelity pending |
## Known gaps and risks
- Dictionary records preserve the existing dynamic ResourceLoader boundary.
- `clear()` does not drain I/O; caller ordering remains mandatory.
- No private asset traversal, leak/descriptor-pressure or paired-client run is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending records, completion FIFO and metrics |
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Eligibility, GLB selection and request admission |
| `src/render/m2/m2_animated_scene_finalizer.gd` | Candidate instantiation, material repair and player validation |
| `src/render/m2/m2_prototype_cache_state.gd` | Animated prototype/static-only outcomes |
| `src/scenes/streaming/streaming_world_loader.gd` | Native eligibility/build, polling, permits and adoption |
| `src/tools/verify_m2_animation_load_pipeline_state.gd` | Lifecycle/boundary/timing regression |
## Related decisions and references
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md)
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.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)
@@ -0,0 +1,214 @@
# M2 Animation Playback Controller
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-ANIMATION-PLAYBACK-001` |
| Owners | Per-instance AnimationPlayer/native animator playback mutation |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-animation-playback`, 2026-07-18 |
| Profiles/capabilities | Imported GLB and native experimental animated M2 instances |
## Purpose
Apply deterministic playback to one already duplicated M2 instance: copy native
animator runtime fields, derive a stable phase, prepare/phase native animators,
select an imported animation and configure linear looping/playback/seek.
## Non-goals
- Duplicate, name, transform or attach instances/batch roots.
- Apply visibility, shadows or Editor ownership.
- Load/finalize/cache animated scenes or decide eligibility.
- Change animation priorities, phase formula or native animator implementation.
## Context and boundaries
```mermaid
flowchart LR
Prototype[Animated prototype] --> Materializer[M2AnimatedInstanceMaterializer]
Materializer -->|source and duplicate| Playback[M2AnimationPlaybackController]
Finalizer[M2AnimatedSceneFinalizer player inventory] --> Playback
Playback --> Native[M2NativeAnimator mutation]
Playback --> Imported[AnimationPlayer mutation]
```
Allowed dependencies are supplied Nodes, Script identity, AnimationPlayer and
Animation resources. ResourceLoader, files, workers, caches, scheduler,
MultiMesh, SceneTree attachment and application layers are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `copy_native_animator_data(source_root, target_root, script)` | Command | Copy five runtime fields in depth-first pair order | Main thread; borrowed nodes | Null/script mismatch yields no pairs |
| `start_instance_playback(root, path, index, script, players, collect_diagnostics)` | Command/query | Start native/imported playback and optionally return native state | Main thread; one instance | Null/empty inventories no-op |
| `native_animators_in_subtree(root, script)` | Query | Exact-script depth-first inventory | Main thread; borrowed nodes | Null root/script returns empty |
| `phase_for_instance(path, index)` | Pure query | Stable phase in `[0, 1)` | Any thread; scalar | Historical hash behavior retained |
| `choose_default_animation(player, path)` | Pure engine query | Apply exact ordinary/fish/bird/fallback priority | Main thread; borrowed player | Null/no animations returns empty |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Prototype and duplicate subtrees | M2 animated instance materializer | Native field copy | Borrowed Nodes | One duplicate |
| Input | Exact native animator Script | Loader composition | Native inventory | Borrowed Script | Call-local |
| Input | Relative path and instance index | Build batch adapter | Phase/selection | Copied values | One start |
| Input | AnimationPlayer inventory | Animated scene finalizer traversal | Imported playback | Borrowed players | One start |
| Output | Mutated native/imported playback | Controller | Rendered instance | Nodes retain state/resources | Instance lifetime |
| Output | Detached native diagnostic records | Controller | Loader log adapter | Caller-owned Dictionaries | Debug call |
Side effects are native field assignment, prepare/phase calls, animation loop
mutation, play and seek. The service retains no inputs.
## Data flow
```mermaid
flowchart TD
Identity[Path and index] --> Phase[Stable hash phase]
NativeInventory[Exact-script native inventory] --> Prepare[prepare runtime if available]
Phase --> NativePhase[set native phase]
Players[AnimationPlayers] --> Select[Choose path-specific default]
Select --> Loop[Set every animation LOOP_LINEAR]
Loop --> Play[Play selected name]
Phase --> Seek[Seek positive-length selection]
NativePhase --> Diagnostics{Debug requested?}
Diagnostics -->|yes| Snapshot[Detached runtime state]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Unstarted
Unstarted --> Prepared: copy native data and start
Prepared --> Playing: native phase and/or AnimationPlayer play
Playing --> Playing: repeated deterministic start
Playing --> [*]: instance owner releases subtree
```
The controller itself is stateless; lifecycle labels describe borrowed instance state.
## Main sequence
```mermaid
sequenceDiagram
participant M as M2AnimatedInstanceMaterializer
participant F as M2AnimatedSceneFinalizer
participant P as M2AnimationPlaybackController
participant N as M2NativeAnimator
participant A as AnimationPlayer
M->>P: copy_native_animator_data(prototype, duplicate, script)
M->>F: animation_players_in_subtree(duplicate)
F-->>M: ordered players
M->>P: start_instance_playback(path, index, players, debug)
P->>N: prepare_runtime and set_phase
P->>A: choose, loop, play and seek
P-->>M: optional detached native diagnostics
M-->>M: tag states with instance index
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Materializer[M2AnimatedInstanceMaterializer]
Materializer --> Playback[M2AnimationPlaybackController]
Materializer --> Finalizer[M2AnimatedSceneFinalizer]
Playback --> Engine[Node / Script / AnimationPlayer / Animation]
Materializer --> Batch[SceneTree batch materialization]
Loader --> NativeScript[M2NativeAnimator script]
Playback -. no dependency .-> ResourceLoader
Playback -. no dependency .-> Cache[M2PrototypeCacheState]
Playback -. no dependency .-> Scheduler[RenderBudgetScheduler]
```
## Ownership, threading and resources
- Materializer owns prototype duplication, instance/batch roots and SceneTree attachment.
- Materializer uses finalizer traversal for borrowed AnimationPlayer references.
- Controller borrows Nodes/Script/players only for the synchronous call.
- Native arrays are assigned by reference exactly as before extraction.
- Diagnostic Dictionaries are deep-duplicated before return.
- Main thread performs all engine-object mutation; pure phase math is thread-safe.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Missing roots/script | Inventory guard | No native copy/start | Contract fixture | Correct composition |
| Fewer target animators | Pair-count minimum | Copy available pairs only | Native copy fixture | Rebuild import/prototype |
| No matching name | Ordered fallback | Substring, first name, then empty | Selection fixtures | Add compatible animation |
| Empty animation selection | Empty result | Skip loop/play/seek for that player | Selection fixture | Static/native path continues |
| Zero-length selected animation | Length guard | Play without seek | Playback contract | Imported timing remains engine-owned |
| Cancellation/shutdown | Outside service | No retained state | N/A | Loader owns subtree release |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| Phase buckets | 1000 | All | No | Stable per path/index desynchronization |
| Loop mode | `LOOP_LINEAR` | All | No | Every available imported animation loops |
| Native diagnostics | `debug_streaming` | Debug | Yes | Samples state only when requested |
## Persistence, cache and migration
No state or format is serialized. GLB/native cache formats, prototype lifetime
and material versions are unchanged; no rebake is required.
## Diagnostics and observability
- Optional native states remain detached and contain existing prepared,
processing, mesh, bone, surface and length fields.
- Loader retains `M2_NATIVE_ANIMATOR` log formatting and path normalization.
- Controller emits no logs or metrics.
## Verification
- `verify_m2_animation_playback_controller.gd` covers exact phase, ordinary/
fish/bird priorities, substring/first fallback, loop/play/seek, native exact-
script order, five-field copy, phase, detached diagnostics and boundaries.
- Finalizer/build/prototype/material/shutdown regressions protect adjacent behavior.
- Fidelity evidence is exact policy/mutation extraction; no private asset or
original-client animation comparison is claimed.
- Performance budget: 20,000 phase-and-selection pairs under one second.
## Extension points
Server-driven animation state may later replace default selection through a
separate presentation contract; this fallback controller must remain available
for world doodads and compatibility fixtures.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Imported default selection/playback | Implemented extraction | Synthetic priority/play/seek verifier | Asset-backed animation-name corpus pending |
| Native data/phase startup | Implemented extraction | Exact-reference/native state verifier | Native visual fidelity remains experimental |
| Instance/batch materialization | Implemented extraction | Materializer contract verifier | Asset-backed traversal pending |
## Known gaps and risks
- Hash phase intentionally depends on existing Godot String hashing behavior.
- Default-name heuristics are not a complete WoW animation-state mapping.
- No proprietary traversal, animation timing comparison, p95/p99 or paired-client run exists.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_animation_playback_controller.gd` | Phase, selection, native copy/start and imported playback |
| `src/render/m2/m2_animated_scene_finalizer.gd` | Accepted prototype/player inventory |
| `src/scenes/streaming/m2_native_animator.gd` | Experimental native deformation runtime |
| `src/render/m2/m2_animated_instance_materializer.gd` | Instance duplication/attachment and playback composition |
| `src/scenes/streaming/streaming_world_loader.gd` | Build adapter, diagnostic formatting and Editor ownership |
| `src/tools/verify_m2_animation_playback_controller.gd` | Policy/mutation/boundary/timing regression |
## Related decisions and references
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.md)
- [`m2-build-batch-planner.md`](m2-build-batch-planner.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)
+198
View File
@@ -0,0 +1,198 @@
# M2 Build Batch Planner
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | M03 / `M03-RND-M2-BUILD-BATCH-PLANNER-001` |
| Owners | Pure static/animated M2 batch sizing and group cursor progression |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-batch-planner`, 2026-07-17 |
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
## Purpose
Select the effective per-tick M2 batch limit, cap it to remaining transforms and
plan the next group offset without accessing loader state or renderer resources.
## Non-goals
- Own M2 build jobs, queues, tile state, retries or cancellation.
- Decide whether an animated prototype or static mesh is ready.
- Rotate queues or consume `RenderBudgetScheduler` permits.
- Create or modify Nodes, MultiMesh, Mesh, materials or RIDs.
- Introduce spatial cells or change profile batch limits.
## Context and boundaries
```mermaid
flowchart LR
Groups[M2PlacementGrouper transform arrays] --> Loader[Loader build loop]
Limits[Animated/static batch limits] --> Planner[M2BuildBatchPlanner]
Loader --> Planner
Planner --> Plan[Detached batch plan]
Plan --> Loader
Loader --> Dispatch[M2BuildDispatchPlanner]
Dispatch --> Materialize[Animated or MultiMesh materialization]
Loader --> Budget[RenderBudgetScheduler permit]
```
Allowed dependencies are integer/boolean scalar math and a fresh Dictionary.
Node, SceneTree, RenderingServer, ResourceLoader, WorkerThreadPool, mutexes,
renderer caches, gameplay, network, editor UI and files are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `plan_batch(transform_count, current_offset, has_animated_prototype, animated_batch_limit, static_batch_limit)` | Pure query | Return the selected batch size/count and next group cursor | Any thread; call-local result | Non-positive selected limit clamps to one; empty/past-end range completes |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Transform count | Grouped transform array | Planner remaining-count formula | Scalar copy | One plan |
| Input | Current group offset | Loader build job | Planner cursor formula | Scalar copy | One plan |
| Input | Animated prototype availability | Loader resource selection | Planner limit selector | Scalar copy | One plan |
| Input | Animated/static raw limits | Renderer profile/exports | Planner clamp | Scalar copy | One plan |
| Output | `effective_batch_size` | Planner | Tests/diagnostics | Detached integer | One plan |
| Output | `batch_count` | Planner | Loader materialization adapter | Detached integer | One plan |
| Output | `next_offset` | Planner | Loader build-job cursor | Detached integer | Until next batch |
| Output | `group_complete` | Planner | Loader group-index progression | Detached boolean | One plan |
The output Dictionary is newly allocated and caller-owned. The planner retains
no reference to it or any input.
## Data flow
```mermaid
flowchart TD
Select{Animated prototype?} -->|yes| Animated[animated_batch_limit]
Select -->|no| Static[static_batch_limit]
Animated --> Clamp[max 1]
Static --> Clamp
Clamp --> Remaining[max 0 of transform_count - current_offset]
Remaining --> Count[min effective limit, remaining]
Count --> Complete{count <= 0 or offset + count >= total?}
Complete -->|yes| Done[group_complete true; next_offset 0]
Complete -->|no| Continue[group_complete false; next_offset offset + count]
```
## Lifecycle/state
The planner is stateless. It has no initialization, reset, cancellation,
recovery or shutdown work. Each call returns a complete detached value plan.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader build loop
participant Planner as M2BuildBatchPlanner
participant Materializer as Animated/MultiMesh adapter
participant Scheduler as RenderBudgetScheduler
Loader->>Planner: plan_batch(count, offset, path kind, limits)
Planner-->>Loader: batch_count, next_offset, group_complete
Loader->>Loader: resolve/request prototype or mesh
Loader->>Materializer: materialize selected transform slice
Loader->>Loader: adopt cursor/group completion
Loader->>Scheduler: consume M2_BUILD permit
```
## Ownership, threading and resources
- The planner owns only call-local scalar values and the returned Dictionary.
- `M2BuildQueue` owns typed jobs, FIFO ordering, serial numbers and group/offset
cursors. `M2BuildDispatchPlanner` owns the pure resource-state action decision;
the loader owns tile checks and native-first orchestration, while static and
cached animation observers own their observation/retry phases.
- Materializers own main-thread Node/MultiMesh construction under loader roots.
- The scheduler owns the frame-local `M2_BUILD` counter.
- Pure planning is thread-safe, though the current adapter calls it on main thread.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Zero/negative selected limit | Scalar clamp | Effective size becomes one | Contract fixture | Correct profile value later |
| Empty transform group | Remaining count zero | Count zero; group completes | Contract fixture | Loader advances group |
| Offset at/past end | Remaining count clamp | Count zero; offset resets | Contract fixture | Loader advances group |
| Negative offset | No new validation | Historical arithmetic is preserved | Contract fixture | Loader inputs should remain valid |
| Resource pending/missing | Outside planner | Loader rotates or advances as before | Existing cache/build diagnostics | Resource loader retry/fallback |
| Tile cancelled | Outside planner | Loader cancels job before planning/materialization | Existing lifecycle gates | Eligible tile may requeue |
## Configuration and capabilities
The planner introduces no setting. It consumes existing
`m2_animated_instances_per_tick` and `m2_multimesh_batch_size` values after the
loader determines whether an animated prototype is available.
## Persistence, cache and migration
No persistence, schema or cache format participates. ADT/M2 cache versions,
quality presets and rebuild instructions are unchanged.
## Diagnostics and observability
The planner emits no logs. Its verifier covers deterministic formula/source
boundaries and records bounded 20,000-plan timing. Existing loader metrics retain
queue depth, build activity and hitch observability.
## Verification
- `verify_m2_build_batch_planner.gd`: static/animated selection, invalid-limit
clamp, remaining cap, exact completion, empty/past-end and negative-offset
behavior, loader/dependency source boundaries and bounded timing.
- Grouper, transform resolver, unique registry/dedupe, facade, internal-access,
manifest, shutdown, scheduler, streaming, coordinate, documentation and
coordination regressions remain required.
- Fidelity evidence is formula extraction only. Asset-backed frame pacing and
visual output require the renderer checkpoint/performance workflow.
## Extension points
- Remaining native resource observation may be extracted while retaining the
dispatch and typed build-job/FIFO contracts.
- Spatial-cell batching must use measured culling/performance evidence and must
not silently change this model-path batch cursor.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Static/animated batch cursor planning | Implemented extraction | Contract/source/timing verifier | Asset-backed p95/p99 pending |
| Typed build queue/cursor state | Implemented extraction | M2 build queue lifecycle verifier | Asset-backed traversal pending |
| Resource dispatch decision | Implemented extraction | M2 build dispatch planner verifier | Asset-backed traversal pending |
| Resource observation/requests | Remains in loader | Existing lifecycle regressions | Stateful extraction pending |
| Spatial-cell batching | Planned | Renderer roadmap | Culling evidence/design pending |
## Known gaps and risks
- The planner result remains a Dictionary; `M2BuildQueue` adopts its scalar
cursor values through one explicit progress call.
- Negative offsets are preserved rather than rejected to avoid a hidden behavior change.
- Planning timing does not measure resource loading or materialization.
- Private asset-backed p95/p99 and visual comparison remain unavailable.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_build_batch_planner.gd` | Pure limit/count/cursor planning |
| `src/render/m2/m2_build_dispatch_planner.gd` | Pure observed-state action/transition planning |
| `src/render/m2/m2_build_queue.gd` | Typed pending jobs, FIFO order and cursor ownership |
| `src/scenes/streaming/streaming_world_loader.gd` | Tile checks, resource readiness, progress adoption, materializer adapters and budgets |
| `src/render/m2/m2_static_batch_materializer.gd` | Planned static-slice MultiMesh construction and attachment |
| `src/render/m2/m2_animation_playback_controller.gd` | Animated-instance phase/selection/native/imported playback |
| `src/render/m2/m2_animated_instance_materializer.gd` | Planned animated-slice duplication, playback startup and attachment |
| `src/render/m2/m2_placement_grouper.gd` | Produces grouped transform arrays |
| `src/tools/verify_m2_build_batch_planner.gd` | Formula, source and timing regression |
## Related decisions and references
- [`m2-placement-grouper.md`](m2-placement-grouper.md)
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.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)
+237
View File
@@ -0,0 +1,237 @@
# 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 renderer observers produce 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
```mermaid
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
```mermaid
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
```mermaid
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
```mermaid
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
```mermaid
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 native-first observation order, action execution and permit use;
native/static/cached-animation observers own their resource phases.
- `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
Static, cached animated and native animated observation now live behind sibling
services that produce `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 |
| Static/cached/native animation observation | Implemented separately | Observer/cache/pipeline regressions | Asset-backed traversal 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
- Native prototype attempts remain synchronous through the observer; loader still
owns static request I/O and terminal animated ResourceLoader polling.
- 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/render/m2/m2_cached_animation_resource_observer.gd` | Cached animation observation/request phase |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native candidate/read/build/cache observation |
| `src/scenes/streaming/streaming_world_loader.gd` | Observation order, 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 |
## Related decisions and references
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
- [`m2-build-queue.md`](m2-build-queue.md)
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
+265
View File
@@ -0,0 +1,265 @@
# M2 Build Queue
## Metadata
| Field | Value |
|---|---|
| Status | Implemented |
| Target/work package | M03 / `M03-RND-M2-BUILD-QUEUE-001` |
| Owners | Pending M2 build-job records, FIFO tile keys and build cursors |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-queue`, 2026-07-18 |
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
## Purpose
Own typed pending M2 build state outside `StreamingWorldLoader`: a keyed job
retains its M2 root, grouped transforms, insertion-order group keys and three
progress cursors, while a separate FIFO retains scheduling order and stale keys.
## Non-goals
- Decide tile eligibility, detail radius, resource readiness or retries.
- Consume scheduler permits or plan batch sizes.
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
- Load/cache resources, group placements or mutate tile state.
- Change model order, transform order, render settings or visible output.
## Context and boundaries
```mermaid
flowchart LR
Groups[M2PlacementGrouper result] --> Loader[StreamingWorldLoader adapter]
Loader --> Queue[M2BuildQueue]
Queue --> Job[M2BuildJob]
Queue --> Loader
Loader --> Planner[M2BuildBatchPlanner]
Loader --> Dispatch[M2BuildDispatchPlanner]
Loader --> Static[M2StaticBatchMaterializer]
Loader --> Animated[M2AnimatedInstanceMaterializer]
Loader --> Scheduler[RenderBudgetScheduler]
```
Allowed dependencies are String, Dictionary, Array, RefCounted and a strong
Node3D reference. SceneTree destruction, materializers, Mesh/MultiMesh,
RenderingServer/RIDs, ResourceLoader/files, workers/mutexes, scheduler policy,
gameplay, network and Editor APIs are forbidden.
## Public API
### `M2BuildJob`
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `tile_key()` | Query | Immutable keyed identity | Main thread; job lifetime | None |
| `root()` | Query | Borrow strongly retained M2 root | Main thread; no ownership transfer | May be externally invalidated |
| `groups()` | Query | Borrow exact grouped-transform Dictionary | Main thread; job lifetime | None |
| `group_keys()` | Query | Borrow enqueue-time insertion-order key snapshot | Main thread; job lifetime | None |
| `group_index()` | Query | Current model-path cursor | Main thread | None |
| `transform_offset()` | Query | Current transform cursor within group | Main thread | None |
| `batch_serial()` | Query | Current batch naming serial | Main thread | None |
| `adopt_progress(group_index, offset, serial)` | Command | Atomically replace all progress values | Main thread | Raw integer semantics retained |
| `diagnostic_snapshot()` | Query | Detached scalar/count state | Any serialized caller | Omits engine references |
### `M2BuildQueue`
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `enqueue(tile_key, root, groups)` | Command | Replace keyed job and append FIFO key | Main thread; until erase/clear | Invalid/empty input returns false |
| `has_pending/front_key/pop_front/rotate_front` | Commands/queries | Inspect, drain or rotate FIFO independently of job validity | Main thread | Empty returns false/empty String |
| `has_job/job_for` | Queries | Inspect keyed record, including stale-key distinction | Main thread | Missing returns false/null |
| `root_for/groups_for/group_keys_for` | Queries | Borrow current job inputs | Main thread | Stale returns null/empty collection |
| `group_index_for/transform_offset_for/batch_serial_for` | Queries | Read current progress | Main thread | Stale returns zero |
| `adopt_progress(tile_key, group_index, offset, serial)` | Command | Atomically update current job progress | Main thread | Stale returns false |
| `erase_job(tile_key)` | Command | Remove keyed job but preserve FIFO keys | Main thread | Unknown returns false |
| `job_keys()` | Query | Detached active-key snapshot for loader cleanup | Main thread | Empty returns empty Array |
| `clear()` | Command | Release all job and FIFO references | Main thread | Idempotent; never frees Nodes |
| `pending_count/active_job_count` | Queries | FIFO and keyed-job metrics | Main thread | None |
| `diagnostic_snapshot()` | Query | Detached FIFO and sorted job diagnostics | Main thread | None |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Tile key and M2 root | Loader group-result finalizer | Queue/job | Copied String; strong borrowed Node reference | Until erase/clear |
| Input | Grouped transforms Dictionary | M2PlacementGrouper through loader mailbox | Job | Exact strong reference | Job lifetime |
| Input | `groups.keys()` order | Job constructor | Loader group selection | Job-owned Array snapshot | Job lifetime |
| Input | Next group/offset/serial | Planner/materializer adapter | Job progress | Copied integers | Until next adoption |
| Output | Front/rotated/popped tile key | Queue | Loader scheduling loop | Copied String | One drain step |
| Output | Borrowed root/groups/group keys | Job/queue | Loader readiness/materialization | No ownership transfer | One drain step |
| Output | Detached diagnostics/key snapshots | Queue/job | Tests/metrics/cleanup adapter | Caller-owned collections | Call result |
Side effects are keyed/FIFO collection mutation, strong-reference retention and
cursor mutation. No engine object is created, destroyed or attached.
## Data flow
```mermaid
flowchart TD
Enqueue[Tile key, root and grouped transforms] --> Job[Create or replace M2BuildJob]
Enqueue --> FIFO[Append tile key to FIFO]
FIFO --> Front[Borrow front key]
Front --> Current{Current keyed job exists?}
Current -->|no| Pop[Pop stale key]
Current -->|yes| Loader[Loader eligibility/readiness/materialization]
Loader -->|resource pending| Rotate[Move front key to tail]
Loader -->|operation complete| Progress[Adopt group, offset and serial]
Loader -->|finish/cancel| Erase[Erase job only]
Erase --> Pop
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Queued: enqueue valid job and FIFO key
Queued --> Rotated: resource pending
Rotated --> Queued: returns to FIFO front
Queued --> Progressed: adopt progress
Progressed --> Queued: group remains
Queued --> StaleQueued: erase job before FIFO pop
Progressed --> StaleQueued: finish or cancel
StaleQueued --> Absent: pop stale key
Queued --> Absent: clear
StaleQueued --> Absent: clear
```
Duplicate enqueue replaces the current keyed job and adds another FIFO entry;
later erase can therefore leave multiple stale entries, matching historical raw state.
## Main sequence
```mermaid
sequenceDiagram
participant G as M2 group-result drain
participant Q as M2BuildQueue
participant L as StreamingWorldLoader
participant P as M2BuildBatchPlanner
participant M as M2 materializer
G->>Q: enqueue(tile key, root, groups)
L->>Q: front_key and has_job
Q-->>L: borrowed root/groups/keys and cursors
L->>P: plan selected transform slice
alt resource pending
L->>Q: rotate_front
else materializable
L->>M: materialize selected slice
L->>Q: adopt_progress(next index, offset, serial)
else cancelled or complete
L->>L: free/queue-free root if required
L->>Q: erase_job
L->>Q: pop_front
end
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Queue[M2BuildQueue]
Queue --> Job[M2BuildJob]
Job --> EngineBase[Node3D strong reference]
Loader --> Planner[M2BuildBatchPlanner]
Loader --> Materializers[Static and animated materializers]
Loader --> Scheduler[RenderBudgetScheduler]
Queue -. no dependency .-> SceneMutation[SceneTree destruction/attachment]
Queue -. no dependency .-> Resources[Mesh / MultiMesh / ResourceLoader]
```
## Ownership, threading and resources
- Queue owns keyed job records and FIFO String entries.
- Job retains the exact groups Dictionary, a fresh group-key snapshot and root.
- Root ownership remains with the loader/tile SceneTree; queue release never frees it.
- Loader validates `is_instance_valid`, frees empty/aborted roots and mutates tile state.
- `M2BuildDispatchPlanner` selects wait/materializer/advance actions without
borrowing queue-owned engine references.
- All current operations run on the renderer main thread; no mutex is required.
- Group worker results cross their existing mutex mailbox before enqueue.
## Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Invalid enqueue input | Entry guard | Reject without mutation | Contract fixture | Correct caller composition |
| Duplicate tile key | Keyed assignment | Replace job and append FIFO key | Duplicate fixture | Stale entries drain normally |
| Stale FIFO key | `has_job == false` | Loader pops and continues without permit | Stale fixture | No action required |
| Resource pending | Loader cache state | Rotate one front key and consume existing permit | Rotation fixture/build diagnostics | Retry when key returns front |
| Root externally invalid | Loader validity check | Erase job and pop key | Loader source contract | Tile may regroup/requeue later |
| Tile cancelled | Loader policy | Queue-free root, erase job; FIFO key drains/pops | Shutdown/lifetime regression | Eligible tile may requeue |
| World clear | Loader job-key snapshot | Cancel roots, then clear queue | Shutdown regression | New map starts empty |
## Configuration and capabilities
The queue introduces no settings. Existing static/animated batch limits,
visibility/shadow settings, resource caches and `M2_BUILD` permits remain loader,
planner, materializer and scheduler contracts.
## 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
- `pending_count` preserves the existing FIFO-backed `m2_build` queue metric.
- `active_job_count` distinguishes keyed jobs from stale/duplicate FIFO entries.
- Diagnostic snapshots retain FIFO order and sort job records by tile key.
- Job diagnostics expose cursors/counts but never root/groups references.
- The module emits no logs.
## Verification
- `verify_m2_build_queue.gd` covers invalid input, exact groups/root retention,
FIFO, duplicate replacement, rotation, stale entries, atomic progress, erase/
clear engine lifetime, detached sorted diagnostics and loader boundaries.
- Planner, static/animated materializer, Mesh pipeline/cache/prototype, shutdown,
facade, internal-access and checkpoint regressions protect adjacent behavior.
- Fidelity evidence is exact state-transition extraction; visual rules are not changed.
- Performance budget: 100 cycles of 256 enqueue/rotate/erase operations under one second.
- No original-client or proprietary asset comparison is claimed for bookkeeping state.
## Extension points
Remaining resource readiness and dispatch logic may later move behind explicit
commands once its cancellation semantics are independently fixed. A generic
queue base, signals and callbacks are intentionally excluded.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Typed keyed M2 jobs and FIFO | Implemented extraction | Lifecycle/order/source/timing verifier | Asset-backed traversal pending |
| Cursor/serial ownership | Implemented extraction | Atomic progress fixtures | Typed batch-plan result remains Dictionary |
| Root destruction | Existing loader-owned | Lifetime/source/shutdown regressions | Keep outside state service |
| Resource dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
| Resource observation/requests | Existing loader-owned | Adjacent cache/build tests | Safe extraction remains |
## Known gaps and risks
- Grouped transforms remain untyped Dictionary/Array data.
- Queue permits duplicate and stale FIFO keys intentionally for compatibility.
- Job accessors return borrowed mutable groups/key collections; current loader is the sole consumer.
- Private asset traversal, long-flight p95/p99 and leak evidence remain unavailable.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_build_job.gd` | Strong root/groups references, key snapshot and progress cursors |
| `src/render/m2/m2_build_queue.gd` | Keyed job ownership, FIFO/stale/rotation lifecycle and diagnostics |
| `src/scenes/streaming/streaming_world_loader.gd` | Eligibility, readiness, permits, materialization, tile state and root cleanup |
| `src/render/m2/m2_build_batch_planner.gd` | Batch count and cursor-plan calculation |
| `src/render/m2/m2_build_dispatch_planner.gd` | Resource-state action and transition planning |
| `src/tools/verify_m2_build_queue.gd` | Lifecycle/order/lifetime/boundary/timing regression |
## Related decisions and references
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
- [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md)
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
- [`m2-static-batch-materializer.md`](m2-static-batch-materializer.md)
- [`m2-animated-instance-materializer.md`](m2-animated-instance-materializer.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)
+239
View File
@@ -0,0 +1,239 @@
# M2 Build Resource Snapshot
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-BUILD-RESOURCE-SNAPSHOT-001` |
| Owners | Per-build-step observed M2 resource references and outcome flags |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-build-resource-snapshot`, 2026-07-18 |
| Profiles/capabilities | Existing static MultiMesh and animated-instance build paths |
## Purpose
Carry the resource observations for one M2 build operation as a typed contract:
normalized path, optional animated prototype, pending-animation state, optional
static Mesh and terminal missing-model state. The snapshot never owns engine lifetime.
## Non-goals
- Locate, request, load, validate or cache M2 resources.
- Decide dispatch action, batch count, queue order or permit consumption.
- Create, attach or free Nodes, Meshes, MultiMeshes or animated instances.
- Change animation allowlists, native candidate rules or cache paths/formats.
- Persist observations beyond one build operation.
## Context and boundaries
```mermaid
flowchart LR
Native[M2NativeAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
Cached[M2CachedAnimationResourceObserver] --> Snapshot
Static[M2StaticBuildResourceObserver] --> Snapshot
Snapshot --> Dispatch[M2BuildDispatchPlanner]
Snapshot --> Loader
Loader --> Animated[M2AnimatedInstanceMaterializer]
Loader --> Static[M2StaticBatchMaterializer]
```
The snapshot depends only on `RefCounted`, String, booleans and borrowed Node3D/
Mesh references. ResourceLoader, cache/pipeline state, SceneTree mutation,
RenderingServer/RIDs, files, workers, mutexes, gameplay, network and Editor APIs
are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| Constructor `(normalized_path, animated_prototype, animation_pending)` | Command | Capture first-phase animation observation | Main thread; one build operation | Values retained exactly |
| `normalized_relative_path()` | Query | Return observed normalized path | Snapshot lifetime | None |
| `animated_prototype()` | Query | Borrow exact animated prototype | Main thread; no ownership transfer | May be null |
| `has_animated_prototype()` | Query | Report prototype availability | Any serialized caller | None |
| `animation_request_pending()` | Query | Report pending animation request | Any serialized caller | None |
| `adopt_static_observation(mesh, missing)` | Command | Replace second-phase static observation | Main thread | Values retained exactly, including contradictory inputs |
| `static_mesh()` | Query | Borrow exact prepared Mesh | Main thread; no ownership transfer | May be null |
| `has_static_mesh()` | Query | Report static Mesh availability | Any serialized caller | None |
| `static_model_missing()` | Query | Report terminal missing outcome | Any serialized caller | None |
| `diagnostic_snapshot()` | Query | Return detached path/availability flags | Any serialized caller | Omits engine references |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Normalized relative path | Loader path adapter | Snapshot/diagnostics | Copied String | Snapshot lifetime |
| Input | Optional animated prototype | Native/cached animation observer | Snapshot/materializer adapter | Borrowed exact Node3D reference | One build operation |
| Input | Animation request pending | Cached animation observer | Snapshot/dispatch planner | Boolean copy | One build operation |
| Input | Optional prepared static Mesh | Mesh cache/request path | Snapshot/materializer adapter | Borrowed exact Mesh reference | One build operation |
| Input | Static model terminally missing | Prototype cache state | Snapshot/dispatch planner | Boolean copy | One build operation |
| Output | Availability/pending/missing values | Snapshot | Dispatch planner | Scalar reads | One dispatch plan |
| Output | Borrowed prototype or Mesh | Snapshot | Loader materializer adapter | No ownership transfer | One materialization call |
| Output | Detached diagnostics | Snapshot | Tests/future metrics | Caller-owned Dictionary | Call result |
The snapshot is released after the build-loop operation. Releasing or replacing
it never frees the borrowed Node3D or Mesh.
## Data flow
```mermaid
flowchart TD
Path[Normalized M2 path] --> Construct[Create snapshot]
Animated[Animated prototype or null] --> Construct
Pending[Animation request pending] --> Construct
Construct --> PendingCheck{Animation pending?}
PendingCheck -->|yes| Dispatch[Dispatch planner]
PendingCheck -->|no| Batch[Batch planner]
Batch --> StaticNeeded{Positive static batch?}
StaticNeeded -->|yes| Adopt[Adopt Mesh and missing observation]
StaticNeeded -->|no| Dispatch
Adopt --> Dispatch
Dispatch --> Borrow[Loader borrows selected resource]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> AnimationObserved: construct
AnimationObserved --> AnimationObserved: animation pending or animated path
AnimationObserved --> StaticObserved: adopt static result
StaticObserved --> StaticObserved: replace static result
AnimationObserved --> Released: operation ends
StaticObserved --> Released: operation ends
Released --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant S as M2BuildResourceSnapshot
participant B as M2BuildBatchPlanner
participant D as M2BuildDispatchPlanner
participant M as M2 materializer
L->>L: delegate native animated prototype observation
L->>S: construct native result
opt no native prototype
L->>L: delegate cached animation observer
L->>S: receive cached/pending snapshot
end
alt animation not pending
L->>B: plan batch using snapshot availability
opt positive static batch
L->>L: lookup/request static Mesh
L->>S: adopt_static_observation
end
end
L->>D: plan_step(batch count, snapshot)
D-->>L: action plan
opt materialization action
L->>S: borrow prototype or Mesh
L->>M: materialize selected slice
end
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Snapshot[M2BuildResourceSnapshot]
Dispatch[M2BuildDispatchPlanner] --> Snapshot
Snapshot --> Values[String and booleans]
Snapshot --> Borrowed[Borrowed Node3D and Mesh]
Snapshot -. no dependency .-> IO[ResourceLoader / FileAccess]
Snapshot -. no dependency .-> State[Cache / pipeline / queue / scheduler]
Snapshot -. no dependency .-> Mutation[SceneTree / RenderingServer]
```
## Ownership, threading and resources
- Snapshot owns only copied scalar state and temporary references.
- Prototype/cache services retain resource state; the native observer owns its
phase while loader owns native-first ordering among resource observers.
- Scene roots remain loader/tile-owned; Mesh lifetime follows cache/resource refs.
- Dispatch planner reads only snapshot accessors and never mutates the snapshot.
- Current construction/adoption occurs on renderer main thread.
## Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty path | Exact String retained | No validation or hidden normalization | Contract fixture | Loader already normalizes input |
| Null prototype/Mesh | Availability accessor false | Dispatch uses pending/missing state | Contract fixture | Loader request/cache path retries |
| Mesh and missing both true | Exact adoption | Both retained; dispatch gives ready Mesh priority | Contradiction fixture | Loader normally prevents combination |
| Static observation replaced | Explicit adoption | Latest Mesh/missing values win | Replacement fixture | None required |
| Borrowed Node externally freed | Outside snapshot | Loader validity/lifecycle gates remain authoritative | Shutdown regressions | Tile may cancel/requeue |
| Tile cancellation | Outside snapshot | Ephemeral snapshot releases references | Queue/shutdown regressions | Eligible tile may rebuild |
## Configuration and capabilities
The snapshot adds no setting. Existing animation enable/allow/deny rules, batch
limits, cache paths, render settings and scheduler permits remain external.
## Persistence, cache and migration
No observation is serialized. No ADT/M2 cache version changes and no rebake or
migration are required.
## Diagnostics and observability
`diagnostic_snapshot()` exposes normalized path and four availability/outcome
flags without Node/Mesh references. The module emits no logs. Existing loader
queue/hitch metrics remain unchanged.
## Verification
- `verify_m2_build_resource_snapshot.gd` covers exact path/reference identity,
defaults, static adoption/replacement/clear, contradictory flags, detached
diagnostics, engine lifetime, loader/dispatch boundaries and bounded timing.
- Dispatch, queue, batch, materializer, cache/pipeline, shutdown, facade,
internal-access and checkpoint regressions protect adjacent behavior.
- Fidelity evidence is exact observation transfer only; no private asset or
original-client visual claim is made.
## Extension points
`M2CachedAnimationResourceObserver` produces the cached animated phase and
`M2StaticBuildResourceObserver` produces the static phase. Native animation is
produced by `M2NativeAnimationResourceObserver`. The value stays independent of all
producers and of generic callback frameworks.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Typed per-step M2 resource observation | Implemented extraction | Identity/adoption/lifetime/source/timing verifier | Asset-backed traversal pending |
| Dispatch decision | Implemented separately | Dispatch planner verifier | Asset-backed traversal pending |
| Static lookup/request execution | Implemented separately | Static observer verifier | Asset-backed traversal pending |
| Cached animated lookup/request execution | Implemented separately | Cached observer verifier | Asset-backed traversal pending |
| Native animated lookup/build execution | Implemented observer extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
| Engine lifetime/materialization | Loader/materializer-owned | Lifetime/materializer regressions | GPU/p95/p99 evidence pending |
## Known gaps and risks
- Static observation is mutable during its short two-phase construction.
- Raw Node3D/Mesh references remain necessary for current materializer APIs.
- Native parsing/build remains synchronous; loader still owns terminal polling.
- Synthetic timing does not measure resource, Node or GPU work.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step resource observations and diagnostics |
| `src/render/m2/m2_build_dispatch_planner.gd` | Snapshot-to-action planning |
| `src/render/m2/m2_static_build_resource_observer.gd` | Static snapshot production and requests |
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached animated snapshot production and requests |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native prototype observation producer |
| `src/scenes/streaming/streaming_world_loader.gd` | Observer ordering and materializer borrowing |
| `src/tools/verify_m2_build_resource_snapshot.gd` | Identity/adoption/lifetime/boundary/timing regression |
## Related decisions and references
- [`m2-build-dispatch-planner.md`](m2-build-dispatch-planner.md)
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
- [`m2-build-queue.md`](m2-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)
@@ -0,0 +1,241 @@
# M2 Cached Animation Resource Observer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-CACHED-ANIMATION-RESOURCE-OBSERVER-001` |
| Owners | Cached animated M2 eligibility, request admission and initial snapshot |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-cached-animation-resource-observer`, 2026-07-18 |
| Profiles/capabilities | Existing optional cached-GLB animated M2 path |
## Purpose
Produce the cached-animation phase of `M2BuildResourceSnapshot`: reuse an exact
animated prototype, wait for a pending request, admit the first safe GLB request,
or record a terminal static-only animation outcome without changing behavior.
## Non-goals
- Load/build native GryphonRoost animation or emit its diagnostics.
- Poll/finalize threaded Resources or instantiate/fix imported scenes.
- Own/free prototype Nodes, snapshots, pipeline entries or cache state.
- Materialize instances, consume permits or change animation policy defaults.
- Change GLB layout, cache format, batching, profiles or visible output.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Native[M2NativeAnimationResourceObserver]
Native -->|no prototype| Observer[M2CachedAnimationResourceObserver]
Prototype[M2PrototypeCacheState] --> Observer
Pipeline[M2AnimationLoadPipelineState] --> Observer
Resource[ResourceLoader and GLB JSON] --> Observer
Observer --> Snapshot[M2BuildResourceSnapshot]
Snapshot --> Dispatch[M2BuildDispatchPlanner]
```
## Public API
| Symbol | Kind | Purpose | Failure behavior |
|---|---|---|---|
| `observe(path, cache_dir, max_primitives, allow, deny, prototype_state, pipeline_state, debug)` | Command/query | Produce cached prototype/pending/static-only snapshot | Invalid input returns empty non-pending snapshot |
| `find_eligible_glb_cache_path(...)` | I/O query | Select first existing safe historical candidate | Returns empty String |
| `is_animation_path_allowed(path, allow, deny)` | Pure query | Apply stripped case-insensitive substring policy | Empty path/allowlist rejects; deny wins |
| `cache_resource_paths(cache_dir, path)` | Pure query | Return nested/lowercase/basename GLB candidates | Empty values yield fewer candidates |
| `glb_cache_is_safe_for_runtime_animation(path, max, debug)` | I/O query | Validate animations, primitive limit and schema | Invalid/missing GLB rejects |
| `read_glb_json(path)` | I/O query | Read version-2 GLB JSON chunk | Returns empty Dictionary |
| `glb_primitive_count(gltf)` | Pure query | Count primitives across meshes | Invalid entries contribute zero |
| `glb_animation_schema(gltf)` | Pure query | Read OpenWC schema marker | Missing marker returns empty String |
## Inputs and outputs
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|---|---|---|---|---|
| Input | Normalized M2 path/cache directory | Loader | Observer | Copied Strings; one observation |
| Input | Primitive cap and allow/deny patterns | Loader exports | Observer | Copied/read-only call values |
| Input | Prototype and request states | M2 state services | Observer | Borrowed services; map/session |
| Input | Existing GLB cache file | Offline cache pipeline | Observer | Read-only file access |
| Output | Resource snapshot | Observer | Loader/dispatch/materializer | Caller-owned snapshot; one build step |
| Output | Borrowed exact prototype | Prototype state | Snapshot/materializer | No ownership transfer |
| Output | Threaded request record | Observer | Animation pipeline | Pipeline-owned copied paths |
| Output | Static-only transition | Observer | Prototype state | State-owned copied path |
## Data flow
```mermaid
flowchart TD
Start[Observe normalized path] --> Valid{Valid composition?}
Valid -->|no| Empty[Empty non-pending snapshot]
Valid -->|yes| Cached{Prototype cached?}
Cached -->|yes| Ready[Snapshot with exact prototype]
Cached -->|no| Static{Already static-only?}
Static -->|yes| Empty
Static -->|no| Pending{Request exists?}
Pending -->|yes| Wait[Pending snapshot]
Pending -->|no| Policy{Allow and not deny?}
Policy -->|no| Mark[Mark static-only]
Policy -->|yes| Candidate[Historical GLB candidates]
Candidate --> Safe{Animations, primitive cap, schema safe?}
Safe -->|no usable| Mark
Safe -->|yes| Request[Threaded request]
Request -->|OK or busy| Remember[Remember and return pending snapshot]
Request -->|error| Mark
Mark --> Empty
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Observing
Observing --> Cached
Observing --> Pending
Observing --> Requested
Observing --> StaticOnly
Observing --> Rejected
Cached --> [*]
Pending --> [*]
Requested --> [*]
StaticOnly --> [*]
Rejected --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant O as CachedAnimationObserver
participant C as PrototypeCacheState
participant P as AnimationLoadPipelineState
participant R as ResourceLoader
participant S as M2BuildResourceSnapshot
L->>L: attempt native candidate
alt no native prototype
L->>O: observe(path, cache/policy/state)
O->>C: find animated / static-only
O->>P: has request
opt eligible cache candidate
O->>R: exists + GLB JSON + threaded request
O->>P: remember request
end
O-->>L: snapshot
else native prototype
L->>S: construct native snapshot
end
```
## Dependency diagram
```mermaid
flowchart TB
Observer[M2CachedAnimationResourceObserver] --> Snapshot[M2BuildResourceSnapshot]
Observer --> Prototype[M2PrototypeCacheState]
Observer --> Pipeline[M2AnimationLoadPipelineState]
Observer --> ResourceLoader
Observer --> FileAccess[GLB header and JSON]
Observer -. no dependency .-> Native[M2 native builder/repository]
Observer -. no dependency .-> Finalizer[M2AnimatedSceneFinalizer]
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
Observer -. no dependency .-> SceneTree
```
## Ownership, threading and resources
- Observer retains no service, request, snapshot, Resource or Node reference.
- Prototype state retains accepted Nodes and static-only paths until shutdown.
- Pipeline state retains copied request paths until terminal polling/finalize.
- Snapshot borrows the exact cached prototype; observer never frees it.
- Resource existence/request and synchronous GLB JSON inspection run on the
renderer main thread, matching the previous loader behavior.
- The sibling native observer owns the native build attempt. Loader remains owner
of terminal polling/finalize, permits, materialization and SceneTree mutation.
## Errors, cancellation and recovery
| Failure/state | Behavior | Recovery |
|---|---|---|
| Empty path/dependency | Return empty non-pending snapshot without mutation | Correct composition |
| Cached prototype | Return exact reference immediately | None |
| Existing request | Return pending without duplicate admission | Retry after terminal drain |
| Disallowed/denied path | Mark static-only | New loader session/configuration |
| Missing/invalid/unsafe GLB | Continue candidates, then mark static-only | Repair/rebake cache and restart session |
| Request error | Mark static-only | New loader session/cache repair |
| Tile cancellation | Observer retains nothing | Loader queue remains authoritative |
| Shutdown | Loader drains requests before state clear | New loader starts empty |
## Configuration and capabilities
The service consumes existing `m2_cache_dir`, `m2_animated_max_primitives`,
`m2_animated_allowlist_patterns`, `m2_animated_denylist_patterns` and debug flag.
It adds no setting and preserves runtime mutability and defaults.
## Persistence, cache and migration
No persistence or cache format changes are introduced. Existing `.glb` candidate
layout and OpenWC schema markers are read unchanged; no rebake is required.
## Diagnostics and observability
Unsupported non-empty schemas preserve the historical optional
`M2_ANIM_REJECT` debug line. Snapshot and pipeline diagnostics remain detached;
queue and hitch metrics are unchanged.
## Verification
- Dedicated verifier covers invalid/cached identity/static/pending/missing
lifecycle, allow/deny semantics, exact path order, generated GLB header/JSON,
animation presence, primitive cap, accepted/rejected schemas, source boundaries
and bounded timing.
- Snapshot, dispatch, pipeline, prototype, shutdown, facade, internal-access and
checkpoint regressions protect adjacent behavior.
- Fidelity evidence is exact policy/I/O extraction. No original-client visual or
animation parity claim is made.
## Extension points
Native animation observation can become a sibling service without changing this
cached-GLB contract. Terminal ResourceLoader polling/finalization may later move
behind a dedicated adapter while retaining the pipeline state.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Cached animation observation/request | Implemented extraction | Lifecycle/policy/GLB/source/timing verifier | Asset-backed traversal pending |
| Native animation observation | Implemented sibling extraction | Native observer lifecycle/source verifier | Asset-backed traversal pending |
| Animated finalize/preparation | Loader/finalizer split | Pipeline/finalizer regressions | Polling adapter optional |
## Known gaps and risks
- A successful asynchronous request is not started by the unit fixture because
leaving it undrained leaks process work; source and pipeline tests cover admission.
- Generated GLB metadata fixtures validate policy, not Godot import fidelity.
- Private traversal, leak/descriptor pressure, p95/p99 and paired visuals remain
unavailable without private assets.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_cached_animation_resource_observer.gd` | Cached GLB policy, request and snapshot production |
| `src/render/m2/m2_build_resource_snapshot.gd` | Typed per-step observation value |
| `src/render/m2/m2_animation_load_pipeline_state.gd` | Pending request/finalize state |
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and static-only outcomes |
| `src/render/m2/m2_native_animation_resource_observer.gd` | Native-first candidate/read/build/cache observation |
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, fallback order, finalize and execution |
| `src/tools/verify_m2_cached_animation_resource_observer.gd` | Lifecycle/policy/GLB/boundary/timing regression |
## Related decisions and references
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
- [`m2-native-animation-resource-observer.md`](m2-native-animation-resource-observer.md)
- [`m2-animation-load-pipeline-state.md`](m2-animation-load-pipeline-state.md)
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.md)
- [`m2-animated-scene-finalizer.md`](m2-animated-scene-finalizer.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)
+242
View File
@@ -0,0 +1,242 @@
# M2 Mesh Load Pipeline State
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-MESH-LOAD-PIPELINE-001` |
| Owners | Static M2 threaded request records and terminal finalize FIFO |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-load-pipeline`, 2026-07-17 |
| Profiles/capabilities | Existing static M2 cached `.tscn`/`.glb` load path |
## Purpose
Own cross-frame bookkeeping between successful static M2 ResourceLoader request
start, terminal loaded/failed polling and budgeted main-thread finalization. The
loader retains every I/O and engine-resource operation.
## Non-goals
- Call ResourceLoader or select cache paths/formats.
- Own M2 scene, missing or material-refresh caches, or prepared Mesh references.
- Extract Meshes from PackedScene/GLB Resources.
- Consume finalize permits or rebuild/adopt runtime Meshes.
- Merge static and animated M2 pipelines.
## Context and boundaries
```mermaid
flowchart LR
Need[Static M2 mesh needed] --> Loader[StreamingWorldLoader]
Loader --> IO[ResourceLoader request]
Loader --> State[M2MeshLoadPipelineState]
State --> Poll[Detached pending records]
Poll --> Loader
Loader --> IO
Loader -->|terminal status| State
State --> FIFO[Finalize FIFO]
FIFO --> Loader
Loader --> Budget[M2_MESH_FINALIZE permit]
Loader --> Cache[M2 Mesh resource cache or M2 prototype outcome state]
```
Allowed dependencies are value containers, Strings and opaque integer statuses.
ResourceLoader, filesystem, WorkerThreadPool, Mesh/Resource/Node/RID ownership,
scheduler and other application layers are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `remember_request(normalized_relative_path, resource_path)` | Command/query | Insert one pending request | Renderer main thread; until terminal/discard/clear | Empty/duplicate rejected |
| `has_request(path)` | Query | Test pending dedupe | Main thread | Empty/unknown false |
| `request_records_snapshot()` | Query | Copy pending records in insertion order | Main thread; caller-owned | None |
| `complete_request(path, terminal_status)` | Command/query | Move request to terminal FIFO | Main thread after poll | Unknown rejected |
| `discard_request(path)` | Command/query | Remove request without finalization | Main thread | Unknown false |
| `has_finalize_record()` / `pop_finalize_record()` | Query/command | Observe/pop completion-order FIFO | Main-thread budget drain | Empty pop returns `{}` |
| `total_work_count()` | Query | Preserve pending-plus-finalize metric | Main thread | None |
| `pending_request_count()` / `finalize_record_count()` | Query | Stage diagnostics | Main thread | None |
| `clear()` | Command | Drop bookkeeping after I/O drain/reset | Main thread | Idempotent; does not drain I/O |
| `diagnostic_snapshot()` | Query | Detached request/finalize records | Main thread | No Resources exposed |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Normalized M2 path and cache Resource path | Loader request adapter | Pending map | Copied Strings | Until terminal/discard/clear |
| Input | Opaque terminal ResourceLoader status | Loader poll adapter | Finalize record | Integer value | Until pop/clear |
| Output | Detached pending records | Pipeline state | Loader poll/shutdown adapter | Caller-owned copies | One poll/wait pass |
| Output | Oldest terminal record | Pipeline state | Loader finalizer | Record ownership transferred | One finalize attempt |
| Output | Detached diagnostics | Pipeline state | Verifier/future metrics | Caller-owned copies | Snapshot lifetime |
Side effects are limited to collection mutation and retaining String/integer values.
## Data flow
```mermaid
flowchart TD
Start[Successful ResourceLoader request] --> Remember[remember request]
Remember --> Snapshot[request records snapshot]
Snapshot --> Poll[Loader polls status]
Poll --> Active{In progress?}
Active -->|yes| Snapshot
Active -->|no| Complete[complete request with terminal status]
Complete --> FIFO[Append finalize FIFO]
FIFO --> Permit{Loader permit available?}
Permit -->|no| FIFO
Permit -->|yes| Pop[Pop oldest terminal record]
Pop --> Finalize[Loader gets Resource and delegates first-Mesh extraction]
Finalize --> Prepare[M2RuntimeMeshFinalizer prepares Mesh]
Prepare --> Adopt[Loader adopts Mesh or marks prototype outcome state]
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Pending: remember request
Pending --> Pending: non-terminal poll
Pending --> TerminalQueued: complete request
Pending --> Absent: discard
TerminalQueued --> Absent: pop finalize
Pending --> Absent: clear
TerminalQueued --> Absent: clear
```
One path has at most one pending request. After completion it may be requested
again only if loader cache/missing rules permit it.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Resource as ResourceLoader
participant State as M2MeshLoadPipelineState
participant Budget as RenderBudgetScheduler
Loader->>Resource: load_threaded_request(cache path)
Resource-->>Loader: OK or ERR_BUSY
Loader->>State: remember_request(normalized, cache path)
loop frames
Loader->>State: request_records_snapshot()
Loader->>Resource: load_threaded_get_status(path)
end
Loader->>State: complete_request(normalized, terminal status)
Loader->>Budget: try_consume_permit(M2_MESH_FINALIZE)
Loader->>State: pop_finalize_record()
Loader->>Resource: load_threaded_get(path)
Loader->>Loader: extract/refresh/adopt Mesh or mark missing
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> State[M2MeshLoadPipelineState]
Loader --> Resource[ResourceLoader]
Loader --> Budget[RenderBudgetScheduler]
Loader --> MeshCache[M2 Mesh resource and prototype outcome cache states]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
State -. no dependency .-> Resource
State -. no dependency .-> Budget
State -. no dependency .-> MeshCache
```
## Ownership, threading and resources
- Main thread serializes all state mutation and snapshots.
- State owns only request/finalize Dictionaries containing Strings and status integers.
- Loader owns ResourceLoader request lifetime and drains active paths before shutdown clear.
- `M2MeshResourceCacheState` owns prepared static Mesh references and
`M2RuntimeMeshFinalizer` owns refresh/rebuild/fallback. The loader owns shared
adoption decisions, raw I/O and remaining engine resources; the prototype
cache service owns missing outcomes.
- Snapshot and diagnostic records are detached; caller mutation cannot affect state.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty/duplicate request | State guard | Reject unchanged | Contract verifier | Correct caller/request later |
| Request start failure | Loader return code | No state insert; mark missing | Existing loader behavior | Cache/source correction |
| Non-terminal status | Loader poll | Keep pending | Existing queue metric | Poll next frame |
| Terminal load failure | Status in popped record | Loader marks missing | Existing missing behavior | World/cache reload |
| Empty defensive path | Loader before poll | Discard and mark missing | Source regression | Correct request producer |
| Shutdown | Loader drains pending Resource paths | Clear state | Shutdown verifier | New loader starts empty |
## Configuration and capabilities
| Setting/capability | Default | Profile | Runtime mutable | Effect |
|---|---|---|---|---|
| `m2_mesh_finalize_ops_per_tick` | `1` | Quality/custom | Yes | Bounds terminal FIFO pops outside state |
| Cache extension order | `.tscn`, `.glb` | Existing M2 path | No | Loader selects request path |
| Animated pivot-prefix GLB exclusion | Existing rule | All | No | Loader filters before request |
## Persistence, cache and migration
No pipeline state is serialized. M2 cache formats, material refresh version and
rebuild policy are unchanged; no migration or rebake is required.
## Diagnostics and observability
- `total_work_count` preserves all three historical `m2_mesh` metrics.
- Diagnostic snapshots expose only paths/statuses, never loaded Resources or Meshes.
- Existing loader logs and hitch sections remain unchanged.
- Normalized M2 path is the correlation key.
## Verification
- `verify_m2_mesh_load_pipeline_state.gd`: validation, dedupe, insertion-order
polling, terminal transition, completion FIFO, opaque status, discard, metrics,
detached diagnostics, loader ownership and 100-by-256 timing.
- Classifier/material/shutdown/M2 placement/build regressions cover adjacent behavior.
- Fidelity evidence is exact state/lifecycle extraction; no asset-backed or
original-client visual parity claim is made.
- Performance budget: 25,600 request/complete/pop transitions under one second.
## Extension points
- Mesh/missing cache adoption can move to a separate finalizer without changing this state.
- Animated M2 has distinct candidate/static fallback semantics and remains separate.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Static M2 request/finalize state | Implemented extraction | Contract/source/timing verifier | Asset-backed long traversal pending |
| Static request admission I/O | Implemented in observer | Observer/shutdown regressions | Asset-backed traversal pending |
| Static request polling/finalize I/O | Existing loader-owned | Shutdown/material regressions | Finalize extraction optional |
| Mesh cache | Implemented extraction | Mesh resource cache state verifier | Asset-backed memory/leak run pending |
| First-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
| Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
| Mesh materialization | Existing loader-owned | Renderer/material tests | Further safe extraction pending |
## Known gaps and risks
- Records remain Dictionaries because ResourceLoader polling is a dynamic Godot boundary.
- Clear does not drain ResourceLoader; caller ordering is mandatory.
- Status integers are intentionally opaque inside state.
- No private asset traversal, leak/descriptor-pressure or p95/p99 run is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending records, terminal FIFO and metrics |
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared static Mesh references and final clear |
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
| `src/render/m2/m2_prototype_cache_state.gd` | Missing-model outcome retention |
| `src/scenes/streaming/streaming_world_loader.gd` | Cache path choice, I/O polling, permits and adoption decisions |
| `src/render/m2/m2_runtime_mesh_rebuild_classifier.gd` | Downstream stale Mesh rebuild decision |
| `src/tools/verify_m2_mesh_load_pipeline_state.gd` | Lifecycle/boundary/timing regression |
## Related decisions and references
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.md)
- [`m2-build-batch-planner.md`](m2-build-batch-planner.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)
@@ -0,0 +1,218 @@
# M2 Mesh Resource Cache State
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-MESH-RESOURCE-CACHE-001` |
| Owners | Prepared static M2 Mesh resource references by normalized path |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-resource-cache`, 2026-07-17 |
| Profiles/capabilities | Existing static M2 cached and raw fallback paths |
## Purpose
Own the runtime cache of prepared static M2 `Mesh` references outside
`StreamingWorldLoader`. Dedicated services extract and prepare each Mesh; the
loader remains responsible for finding, loading, adopting and materializing it.
## Non-goals
- Own the shared M2 missing-path, prototype or animated-scene caches.
- Call ResourceLoader/FileAccess or select `.tscn`/`.glb` paths.
- Extract a Mesh from a Resource or Node.
- Refresh materials, rebuild Meshes or create MultiMesh instances.
- Add eviction, persistence, cache versions or quality-profile behavior.
## Context and boundaries
```mermaid
flowchart LR
Need[Static M2 mesh lookup] --> Loader[StreamingWorldLoader]
Loader --> Cache[M2MeshResourceCacheState]
Cache -->|cached Mesh or null| Loader
Loader --> Pipeline[M2MeshLoadPipelineState]
Pipeline --> IO[ResourceLoader]
IO --> Extract[M2MeshResourceExtractor]
Extract --> Prepare[M2RuntimeMeshFinalizer]
Prepare --> Cache
Loader --> Build[M2StaticBatchMaterializer]
```
The cache may retain `Mesh` resources and copied normalized-path Strings. It has
no Node, ResourceLoader, filesystem, worker, scheduler, missing-state, builder,
gameplay, network or editor dependency.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `store_mesh(path, mesh)` | Command/query | Retain or replace one prepared Mesh | Renderer main thread; final shutdown | Empty path/null Mesh rejected |
| `has_mesh(path)` | Query | Test whether a Mesh is retained | Renderer main thread | Empty/unknown false |
| `find_mesh(path)` | Query | Borrow exact retained Mesh reference | Renderer main thread | Empty/unknown null |
| `mesh_count()` | Query | Return retained entry count | Diagnostics/tests | None |
| `normalized_paths_snapshot()` | Query | Copy insertion-order path keys | Diagnostics/tests | Never exposes Mesh references |
| `clear()` | Command | Release every retained Mesh reference | Final shutdown after async drain | Idempotent |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Normalized relative M2 path | Loader normalization adapter | Cache key | Copied String | Cache lifetime |
| Input | Prepared static Mesh | Loader finalizer or synchronous fallback | Cache value | Strong reference retained | Until replacement/clear |
| Output | Borrowed exact Mesh | Cache lookup | Static M2 batch builder | Cache retains reference | One build/materialization pass |
| Output | Detached normalized paths | Cache diagnostics | Verifier/future metrics | Caller-owned Strings | Snapshot lifetime |
Store and clear mutate only the cache Dictionary and Resource reference counts.
## Data flow
```mermaid
flowchart TD
Lookup[Lookup normalized M2 path] --> Cached{Mesh cached?}
Cached -->|yes| Return[Return retained Mesh]
Cached -->|no| Missing{Prototype outcome says missing?}
Missing -->|yes| Null[Return null]
Missing -->|no| Load[Loader request or synchronous fallback]
Load --> Prepare[Extractor and runtime finalizer prepare Mesh]
Prepare --> Valid{Prepared Mesh valid?}
Valid -->|yes| Store[Store or replace cache entry]
Valid -->|no| MarkMissing[Loader updates prototype outcome state]
Store --> Return
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Absent
Absent --> Cached: store valid Mesh
Cached --> Cached: replace Mesh reference
Cached --> Absent: final shutdown clear
```
Map/world refresh retains cached Meshes. Only the existing final runtime-cache
release clears them after asynchronous shutdown drain.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Cache as M2MeshResourceCacheState
participant Pipeline as M2MeshLoadPipelineState
Loader->>Cache: find/has normalized path
alt cache miss
Loader->>Pipeline: request/poll/finalize record
Loader->>Loader: ResourceLoader get; delegate extract + prepare
Loader->>Cache: store_mesh(path, prepared Mesh)
end
Cache-->>Loader: exact retained Mesh
Loader->>Loader: materialize static M2 batch
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Cache[M2MeshResourceCacheState]
Loader --> Pipeline[M2MeshLoadPipelineState]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
Loader --> Builder[M2Builder]
Loader --> Materializer[M2StaticBatchMaterializer]
Cache --> Mesh[Godot Mesh Resource]
Cache -. no dependency .-> Pipeline
Cache -. no dependency .-> Classifier
Cache -. no dependency .-> Builder
```
## Ownership, threading and resources
- Renderer main-thread adapters serialize every cache operation.
- The cache owns normalized-path Strings and strong Mesh references.
- Borrowed Mesh lookups do not transfer ownership or duplicate resources.
- `M2MeshResourceExtractor` owns first-Mesh selection and temporary PackedScene
instances. `M2PrototypeCacheState` owns missing/prototype/animated state; the
static materializer owns MultiMesh construction/attachment; the loader owns
resource adoption and build-job decisions.
- The loader drains asynchronous work before the final cache clear.
## Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Empty path or null Mesh | Cache guard | Reject unchanged | Contract verifier | Correct caller input |
| Unknown lookup | Membership query | Return null | Existing loader path | Request/load or missing decision |
| Replacement | Existing key | Retain new exact reference | Contract verifier | Normal subsequent lookup |
| Load/preparation failure | Loader boundary | Do not store; loader marks missing | Existing renderer behavior | Cache/source correction and reload |
| Final shutdown | Loader lifecycle | Clear releases references | Shutdown/source verifier | New loader starts empty |
## Configuration and capabilities
The module adds no setting, budget or profile branch. Cache path order, finalize
permits and batching remain loader-owned; refresh version `2` belongs to
`M2RuntimeMeshFinalizer`.
## Persistence, cache and migration
The state is runtime-only and serializes nothing. Existing `.tscn`/`.glb`
formats, import caches and material refresh metadata are unchanged; no migration
or rebake is required.
## Diagnostics and observability
`mesh_count` and `normalized_paths_snapshot` expose scalar ownership diagnostics
without Mesh references. Existing renderer metrics/logging are unchanged because
the historical Mesh cache had no queue contribution or log site.
## Verification
- `verify_m2_mesh_resource_cache_state.gd`: invalid input, exact reference,
replacement, lookup, detached path snapshot, idempotent clear, loader source
ownership and 100-by-256 bounded timing.
- Mesh pipeline/classifier/material/shutdown/M2 build/facade regressions cover
adjacent behavior.
- Fidelity evidence is exact cache/lifetime extraction; no original-client
visual-parity or asset-backed performance claim is made.
## Extension points
- `M2RuntimeMeshFinalizer` returns a prepared Mesh for `store_mesh` without
changing cache ownership.
- Eviction requires measured memory pressure and explicit lifetime semantics.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Prepared static M2 Mesh cache | Implemented extraction | Contract/source/timing verifier | Asset-backed memory/leak run pending |
| M2 Mesh request lifecycle | Implemented extraction | Pipeline state verifier | Finalize polling remains loader-owned |
| M2 Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
| M2 Mesh preparation | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
## Known gaps and risks
- Strong Mesh references intentionally remain until final shutdown.
- Missing/prototype/animated lookup state is now a separate sibling service.
- No proprietary M2 traversal, leak/descriptor-pressure, p95/p99 or paired
original-client capture is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh references and final clear |
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh selection and temporary PackedScene lifetime |
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype references and negative lookup outcomes |
| `src/scenes/streaming/streaming_world_loader.gd` | Normalization, raw/resource I/O and materialization |
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
| `src/render/m2/m2_mesh_load_pipeline_state.gd` | Pending request and terminal finalize records |
| `src/tools/verify_m2_mesh_resource_cache_state.gd` | Cache ownership/lifetime/boundary/timing regression |
## Related decisions and references
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.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)
+210
View File
@@ -0,0 +1,210 @@
# M2 Mesh Resource Extractor
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-MESH-RESOURCE-EXTRACTOR-001` |
| Owners | First-Mesh selection and temporary PackedScene instance lifetime |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-mesh-resource-extractor`, 2026-07-17 |
| Profiles/capabilities | Static M2 threaded cache and synchronous raw/prototype fallback paths |
## Purpose
Select the first M2 Mesh from a direct Resource, PackedScene or existing Node
subtree while keeping traversal and temporary-node lifetime outside
`StreamingWorldLoader`.
## Non-goals
- Request/load Resources or choose cache paths.
- Load raw M2 data, refresh materials or rebuild Meshes.
- Own Mesh/prototype/missing caches or pending/finalize queues.
- Create MultiMeshes, attach Nodes or consume render permits.
- Change first-Mesh selection order or visible output.
## Context and boundaries
```mermaid
flowchart LR
Pipeline[M2 load pipeline] --> Loader[StreamingWorldLoader]
Loader --> Resource[Loaded Mesh or PackedScene Resource]
Resource --> Extractor[M2MeshResourceExtractor]
Extractor -->|first Mesh or null| Loader
Loader --> Prepare[M2RuntimeMeshFinalizer]
Prepare --> Cache[M2MeshResourceCacheState]
Prototype[Existing M2 Node subtree] --> Extractor
```
The extractor may inspect `Resource`, `PackedScene`, `Node`, `MeshInstance3D`
and `Mesh` engine types. It has no ResourceLoader, filesystem, worker, cache,
scheduler, builder, gameplay, network or editor dependency.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `extract_first_mesh(resource)` | Query with temporary ownership | Return direct Mesh or first Mesh from one PackedScene instance | Renderer main thread; temporary root freed before return | Null/unsupported/empty returns null |
| `find_first_mesh_in_subtree(root)` | Query | Return first Mesh in historical depth-first preorder | Renderer main thread; caller-owned subtree unchanged | Null/no Mesh returns null |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Direct Mesh Resource | Loader threaded finalizer | Extractor | Borrowed Resource | One call |
| Input | PackedScene Resource | Loader threaded finalizer | Extractor | Borrowed scene; extractor owns temporary instance | Temporary root until return |
| Input | Existing prototype/rebuild Node subtree | Loader builder/fallback | Extractor | Borrowed Node tree | One traversal |
| Output | First Mesh reference | Extractor | Loader preparation/cache adapter | Borrowed exact reference | Cache may retain later |
The only side effect is synchronous creation and immediate `free()` of a
temporary PackedScene instance.
## Data flow
```mermaid
flowchart TD
Input[Resource or Node subtree] --> Direct{Direct Mesh?}
Direct -->|yes| Return[Return exact Mesh]
Direct -->|no| Packed{PackedScene?}
Packed -->|yes| Instantiate[Instantiate temporary root]
Packed -->|no| Traverse[Traverse caller-owned subtree]
Instantiate --> TraverseTemp[Depth-first preorder traversal]
TraverseTemp --> Free[Free temporary root]
Free --> ReturnOrNull[Return first Mesh or null]
Traverse --> ReturnOrNull
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Stateless
Stateless --> TemporaryInstance: extract PackedScene
TemporaryInstance --> Traversing: instantiate succeeded
Traversing --> Stateless: free temporary root before return
Stateless --> Stateless: direct Mesh/subtree/invalid input
```
No state or Resource reference is retained between calls.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as StreamingWorldLoader
participant Extractor as M2MeshResourceExtractor
participant Scene as PackedScene
Loader->>Extractor: extract_first_mesh(loaded Resource)
alt direct Mesh
Extractor-->>Loader: same Mesh reference
else PackedScene
Extractor->>Scene: instantiate()
Scene-->>Extractor: temporary root
Extractor->>Extractor: depth-first first-Mesh search
Extractor->>Extractor: temporary_root.free()
Extractor-->>Loader: Mesh or null
end
Loader->>Loader: prepare and cache Mesh or mark missing
```
## Dependency diagram
```mermaid
flowchart TB
Loader[StreamingWorldLoader] --> Extractor[M2MeshResourceExtractor]
Extractor --> Types[Resource / PackedScene / Node / Mesh]
Loader --> Pipeline[M2MeshLoadPipelineState]
Loader --> Cache[M2MeshResourceCacheState]
Loader --> Finalizer[M2RuntimeMeshFinalizer]
Extractor -. no dependency .-> Pipeline
Extractor -. no dependency .-> Cache
Extractor -. no dependency .-> Classifier
```
## Ownership, threading and resources
- The renderer main thread performs all traversal and PackedScene instantiation.
- Direct and subtree Mesh references are borrowed without duplication.
- A PackedScene temporary root is extractor-owned and synchronously freed.
- Existing prototype/rebuild subtrees remain caller-owned and are never mutated.
- Cache retention remains a cache-state concern; runtime material preparation
belongs to `M2RuntimeMeshFinalizer`.
## Errors, cancellation and recovery
| Failure/state | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Null/unsupported Resource | Type guard | Return null | Contract verifier | Loader marks missing/falls back |
| PackedScene instantiate failure | Null root | Return null | Existing loader behavior | Rebuild cache/source |
| No Mesh in subtree | Traversal exhaustion | Return null | Contract verifier | Loader marks missing/falls back |
| Null MeshInstance Mesh | Null traversal result | Parent continues with later siblings | Order verifier | Normal sibling search |
| Shutdown/cancel | No retained state | No action | Ownership docs | Caller owns surrounding job |
## Configuration and capabilities
The module adds no settings or profile branches. Cache format order, material
refresh version, render permits and batching remain unchanged outside it.
## Persistence, cache and migration
The extractor is stateless and serializes nothing. Existing `.tscn`/`.glb`
formats, cache versions and rebuild metadata are unchanged; no migration or
rebake is required.
## Diagnostics and observability
The module emits no logs and retains no counters. Loader correlation remains the
normalized M2 path. Its verifier measures traversal time and temporary Node count.
## Verification
- `verify_m2_mesh_resource_extractor.gd`: direct/invalid Resources, depth-first
child order, null-Mesh sibling continuation, subtree ownership, PackedScene
exact Mesh identity, temporary Node release, source boundaries and 10,000
bounded traversals.
- M2 pipeline/cache/classifier/material/shutdown/build regressions cover adjacent
behavior. No asset-backed visual parity claim is made.
## Extension points
- `M2RuntimeMeshFinalizer` consumes the Mesh returned here and the loader stores
its result in `M2MeshResourceCacheState`.
- Broader generic scene traversal is intentionally excluded until another real
consumer requires the same exact contract.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Direct/PackedScene first-Mesh extraction | Implemented extraction | Resource/order/lifetime verifier | Asset-backed corrupt-scene fixture pending |
| Prototype/rebuild subtree selection | Implemented extraction | Synthetic preorder verifier | Asset-backed M2 traversal pending |
| Material refresh/rebuild finalization | Implemented extraction | Runtime finalizer transition/rebuild verifier | Asset-backed material comparison pending |
## Known gaps and risks
- PackedScene instantiation remains synchronous main-thread work behind the
existing finalize permit.
- The first Mesh rule intentionally ignores additional MeshInstances.
- No proprietary M2 corpus, corrupt cache, leak/descriptor-pressure, p95/p99 or
paired original-client capture is included.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_mesh_resource_extractor.gd` | First-Mesh traversal and temporary PackedScene lifetime |
| `src/render/m2/m2_prototype_cache_state.gd` | Prototype and missing/static-only lookup state |
| `src/scenes/streaming/streaming_world_loader.gd` | ResourceLoader/raw I/O, cache decisions and materialization |
| `src/render/m2/m2_runtime_mesh_finalizer.gd` | Refresh/rebuild/fallback preparation |
| `src/render/m2/m2_mesh_resource_cache_state.gd` | Prepared Mesh retention |
| `src/tools/verify_m2_mesh_resource_extractor.gd` | Resource/order/lifetime/boundary/timing regression |
## Related decisions and references
- [`m2-mesh-resource-cache-state.md`](m2-mesh-resource-cache-state.md)
- [`m2-mesh-load-pipeline-state.md`](m2-mesh-load-pipeline-state.md)
- [`m2-runtime-mesh-rebuild-classifier.md`](m2-runtime-mesh-rebuild-classifier.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)
@@ -0,0 +1,245 @@
# M2 Native Animation Resource Observer
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-NATIVE-ANIMATION-RESOURCE-OBSERVER-001` |
| Owners | Native animation candidate policy, raw read, build and cache outcome |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-native-animation-resource-observer`, 2026-07-18 |
| Profiles/capabilities | Existing native GryphonRoost animated M2 path |
## Purpose
Resolve the historical native animated M2 path as one synchronous observation:
select a GryphonRoost candidate, reuse cached state, read animated raw data,
build a detached prototype and publish either the prototype or static-only result.
## Non-goals
- Add native animation candidates or change the `gryphonroost` predicate.
- Observe/request cached GLB animation or poll/finalize ResourceLoader work.
- Duplicate, attach, animate or free accepted instances in the SceneTree.
- Consume render permits or change build queue, batching and dispatch policy.
- Change native parsing/building, cache formats, profiles or visible output.
## Context and boundaries
```mermaid
flowchart LR
Loader[StreamingWorldLoader] --> Observer[M2NativeAnimationResourceObserver]
Observer --> Repository[M2RawModelRepository]
Observer --> Builder[M2NativeAnimatedBuilder]
Observer --> Cache[M2PrototypeCacheState]
Observer --> Prototype[Detached animated prototype]
Prototype --> Snapshot[M2BuildResourceSnapshot]
Observer -->|no prototype| Cached[M2CachedAnimationResourceObserver]
```
The observer is the native-first resource boundary. The loader supplies an
already-normalized path and remains responsible for fallback ordering and for
constructing the snapshot consumed by dispatch.
## Public API
| Symbol | Kind | Purpose | Failure behavior |
|---|---|---|---|
| `is_native_animation_candidate(path)` | Pure query | Apply the historical case-insensitive GryphonRoost substring policy | Empty/unmatched path returns false |
| `observe(path, extracted_dir, repository, cache, debug)` | Command/query | Return an exact cached/new native prototype or record static-only fallback | Invalid/non-candidate input returns null without mutation |
| `build_animated_prototype(data, extracted_dir)` | Adapter command | Call the retained native animated builder dependency | Missing builder or invalid result returns null |
The optional constructor dependency exists for deterministic tests; production
uses `M2NativeAnimatedBuilder` without a new abstraction or runtime setting.
## Inputs and outputs
| Direction | Data | Producer | Consumer | Ownership/lifetime |
|---|---|---|---|---|
| Input | Normalized relative M2 path | Loader path adapter | Observer | Copied String; one call |
| Input | Extracted directory | Loader configuration | Repository/builder | Copied String; one call |
| Input | Raw animated Dictionary | Repository | Observer/builder | Call-local value |
| Input | Prototype cache state | Loader composition | Observer | Borrowed service; loader session |
| Output | Cached or adopted prototype | Cache/observer | Loader snapshot | Borrowed exact Node3D reference |
| Output | Static-only transition | Observer | Prototype cache | Copied path; loader session |
| Output | Debug line | Observer | Runtime log | Emitted only when enabled and build succeeds |
## Data flow
```mermaid
flowchart TD
Start[Observe normalized path] --> Candidate{Valid native candidate?}
Candidate -->|no| None[Return null]
Candidate -->|yes| Cached{Cached prototype?}
Cached -->|yes| Return[Return exact prototype]
Cached -->|no| Static{Marked static-only?}
Static -->|yes| None
Static -->|no| Read[Read animated raw data]
Read --> Surfaces{Data and animated surfaces?}
Surfaces -->|no| Mark[Mark static-only]
Surfaces -->|yes| Build[Build detached prototype]
Build --> Children{Prototype has children?}
Children -->|no| Mark
Children -->|yes| Adopt[Adopt first cache prototype]
Adopt --> Log{Debug enabled?}
Log -->|yes| Emit[Emit exact native cache fields]
Log -->|no| Return
Emit --> Return
Mark --> None
```
## Lifecycle/state
```mermaid
stateDiagram-v2
[*] --> Observing
Observing --> Rejected: invalid or non-candidate
Observing --> Cached: positive cache hit
Observing --> StaticOnly: negative cache hit
Observing --> Reading: uncached candidate
Reading --> StaticOnly: no animated surfaces
Reading --> Building: usable raw data
Building --> StaticOnly: null or childless result
Building --> Adopted: valid result
Adopted --> [*]
Cached --> [*]
StaticOnly --> [*]
Rejected --> [*]
```
## Main sequence
```mermaid
sequenceDiagram
participant L as StreamingWorldLoader
participant O as NativeAnimationObserver
participant C as M2PrototypeCacheState
participant R as M2RawModelRepository
participant B as M2NativeAnimatedBuilder
L->>O: observe(path, directory, repository, cache, debug)
O->>C: find animated / is static-only
alt uncached native candidate
O->>R: load_animated_model_data(directory, path)
R-->>O: raw Dictionary
O->>B: build(raw, directory)
alt valid prototype
O->>C: adopt_animated_prototype(path, prototype)
O-->>L: canonical prototype
else unavailable
O->>C: mark_animation_static(path)
O-->>L: null
end
else cached result
O-->>L: prototype or null
end
```
## Dependency diagram
```mermaid
flowchart TB
Observer[M2NativeAnimationResourceObserver] --> Repository[M2RawModelRepository]
Observer --> Builder[M2NativeAnimatedBuilder]
Observer --> Cache[M2PrototypeCacheState]
Loader[StreamingWorldLoader] --> Observer
Observer -. no dependency .-> ResourceLoader
Observer -. no dependency .-> Pipeline[M2AnimationLoadPipelineState]
Observer -. no dependency .-> Scheduler[RenderBudgetScheduler]
Observer -. no dependency .-> SceneTree
```
## Ownership, threading and resources
- Observation and native parsing/building remain synchronous on the renderer
main thread, exactly as before extraction; there is no mid-call cancellation.
- The observer retains no path, raw Dictionary, Node or service reference.
- Prototype cache owns an accepted detached Node until final shutdown; callers
only borrow the canonical reference.
- The historical childless-builder rejection does not free its detached Node.
This deliberately preserved lifetime edge is documented as a remaining leak
risk rather than silently changed during architectural extraction.
- Loader owns snapshots, fallback sequencing, permits, materialization and every
SceneTree mutation.
## Errors, cancellation and recovery
| Failure/state | Behavior | Recovery |
|---|---|---|
| Empty/non-candidate path or missing dependency | Return null without state mutation | Correct composition/path |
| Cached prototype | Return exact canonical Node immediately | None |
| Existing static-only outcome | Return null without repeated I/O | New loader session after source repair |
| Empty raw data or animated surfaces | Mark static-only and return null | Repair source/parser and start new session |
| Null/childless build | Mark static-only and return null | Repair builder/data and start new session |
| Duplicate successful build | Cache releases later candidate and returns first | None |
| Cancellation during native call | Not supported; call completes | Loader controls whether the call starts |
| Shutdown | Observer retains nothing | Cache releases adopted Nodes after drains |
## Configuration and capabilities
The observer consumes the existing extracted directory and `debug_streaming`
flag. Candidate policy remains the case-insensitive substring `gryphonroost`.
No profile, cache, budget or animation setting is added.
## Persistence, cache and migration
No persistence or format changes are introduced. Positive and static-only state
continues to use the shutdown-lifetime prototype cache; no asset rebake is needed.
## Diagnostics and observability
A successful debug-enabled build emits the unchanged `M2_NATIVE_ANIM_CACHE`
record with path, surface count, bone count, animation id, sequence index,
activity score and length. Rejections remain silent as before.
## Verification
- Dedicated synthetic verifier covers candidate policy, invalid dependencies,
cache identity, static suppression, raw/surface/build failures, preserved
childless lifetime, exact repository/builder arguments, adoption, source
ownership and bounded candidate checks.
- Cached observer, raw repository, prototype cache, snapshot, dispatch, shutdown,
facade, internal-access and render-baseline checks protect adjacent behavior.
- Fidelity evidence is exact control-flow and diagnostic extraction. It is not
original-client animation, visual, memory or asset-backed performance evidence.
## Extension points
Terminal cached-GLB polling/finalization can move behind a separate adapter
without changing this native-first contract. Candidate expansion requires its
own fidelity evidence and must not silently change `Blizzlike335` behavior.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Native candidate selection | Implemented extraction | Policy/source/timing verifier | Additional candidates require fidelity work |
| Raw read/build/cache outcome | Implemented extraction | Failure/identity/adoption verifier | Asset-backed traversal pending |
| Terminal cached ResourceLoader polling | Loader-owned | Existing pipeline regressions | Dedicated finalize adapter |
## Known gaps and risks
- Native parsing/building is synchronous and not bounded by a render permit.
- Rejected childless builder Nodes retain the historical unowned lifetime edge.
- Private asset traversal, descriptor/leak pressure, p95/p99 and paired visual/
animation evidence remain unavailable in this package.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_native_animation_resource_observer.gd` | Candidate/cache/raw/build/outcome/logging observation |
| `src/render/m2/m2_raw_model_repository.gd` | Optional native file/parser boundary |
| `src/render/m2/m2_prototype_cache_state.gd` | Positive prototype and static-only ownership |
| `addons/mpq_extractor/loaders/m2_native_animated_builder.gd` | Existing animated prototype construction |
| `src/scenes/streaming/streaming_world_loader.gd` | Composition, native-first fallback order and execution |
| `src/tools/verify_m2_native_animation_resource_observer.gd` | Lifecycle/identity/boundary/timing regression |
## Related decisions and references
- [`m2-cached-animation-resource-observer.md`](m2-cached-animation-resource-observer.md)
- [`m2-build-resource-snapshot.md`](m2-build-resource-snapshot.md)
- [`m2-raw-model-repository.md`](m2-raw-model-repository.md)
- [`m2-prototype-cache-state.md`](m2-prototype-cache-state.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)
+193
View File
@@ -0,0 +1,193 @@
# M2 Placement Grouper
## Metadata
| Field | Value |
|---|---|
| Status | Implemented extraction |
| Target/work package | M03 / `M03-RND-M2-PLACEMENT-GROUPER-001` |
| Owners | Pure ADT M2 placement validation, normalization and transform grouping |
| Last verified | Worktree `work/sindo-main-codex/m03-m2-placement-grouper`, 2026-07-16 |
| Profiles/capabilities | Existing static M2 worker-grouping path |
## Purpose
Convert valid raw ADT M2 placement records into ordered tile-local transforms
grouped by the historically normalized relative model path.
## Non-goals
- Reserve cross-tile unique IDs or choose placement ownership.
- Own WorkerThreadPool tasks, mutexes, result queues or tile lifecycle.
- Load models, choose animated/static rendering or build MultiMesh batches.
- Create Node, Mesh, material, Resource or RID objects.
- Change path case, transform rules, batching or visual fidelity.
## Context and boundaries
```mermaid
flowchart LR
ADT[ADT names + placements] --> Registry[M2UniquePlacementRegistry]
Registry --> Grouper[M2PlacementGrouper]
Grouper --> Resolver[M2PlacementTransformResolver]
Resolver --> Grouper
Grouper --> Groups[Path to ordered Transform3D arrays]
Groups --> Worker[Loader worker result adapter]
Worker --> Build[Loader-owned build queue]
```
Allowed dependencies are value containers, `Vector3`, `Basis`, `Transform3D`
and `M2PlacementTransformResolver`. SceneTree, Node, RenderingServer,
WorkerThreadPool, mutexes, ResourceLoader, files, gameplay, network and editor UI
are forbidden.
## Public API
| Symbol | Kind | Purpose | Thread/lifetime | Errors |
|---|---|---|---|---|
| `group_placements(tile_origin, m2_names, m2_placements)` | Pure query | Return valid placements grouped as ordered tile-local transforms | Worker/main thread; call-local output | Invalid variants/name IDs/empty paths are skipped |
## Inputs and outputs
| Direction | Contract/data | Producer | Consumer | Ownership | Thread/lifetime |
|---|---|---|---|---|---|
| Input | Godot-space tile origin | Loader tile state | Local-position calculation | Value copy | One call |
| Input | ADT MMDX name table | Parsed ADT tile | Path lookup | Read-only PackedStringArray | One call |
| Input | Raw placement dictionaries | Unique registry result | Validation/transforms | Read-only shallow values | One call |
| Output | Relative path to ordered `Transform3D` arrays | Grouper | Loader worker/build adapter | Fresh Dictionary and arrays | One call/build job |
Output retains first-seen group-key order through Godot Dictionary insertion
order and placement order inside each array. Inputs are not retained or returned.
## Data flow
```mermaid
flowchart TD
Call[group_placements] --> Next[Read placement]
Next --> Dict{Dictionary?}
Dict -->|no| Next
Dict -->|yes| Name{Valid name_id?}
Name -->|no| Next
Name -->|yes| Normalize[Backslash to slash; lowercase MDX/MDL to M2]
Normalize --> Empty{Path empty?}
Empty -->|yes| Next
Empty -->|no| Local[world position - tile origin]
Local --> Resolve[Resolve basis and origin offset]
Resolve --> Scale[Apply max scale 0.0001]
Scale --> Append[Append Transform3D to path group]
Append --> Next
Next -->|complete| Return[Return fresh groups]
```
## Lifecycle/state
The grouper retains no state. Every call constructs one stateless transform
resolver and fresh output containers. Reset, cancellation and shutdown require
no grouper action.
## Main sequence
```mermaid
sequenceDiagram
participant Loader as Loader worker callback
participant Grouper as M2PlacementGrouper
participant Resolver as M2PlacementTransformResolver
Loader->>Grouper: group_placements(origin, names, placements)
loop valid placement
Grouper->>Resolver: resolve basis and origin offset
Resolver-->>Grouper: value-only basis and offset
Grouper->>Grouper: append local Transform3D
end
Grouper-->>Loader: fresh path groups
Loader->>Loader: mutex-protected result enqueue
```
## Ownership, threading and resources
- The grouper owns only call-local containers and transforms.
- The loader owns tasks, mutex/result queue, tile/build state, caches, model
loading, Nodes, MultiMesh, Mesh, materials and RIDs.
- Worker use is safe because the grouper and resolver mutate no shared state.
- The caller owns returned containers; mutation cannot affect later calls.
## Errors, cancellation and recovery
| Failure | Detection | Behavior | Diagnostic | Recovery |
|---|---|---|---|---|
| Non-Dictionary placement | Type guard | Skip | Contract fixture | Repair parser input |
| Invalid name ID | Bounds guard | Skip | Contract fixture | Repair name table/placement |
| Empty path | Empty guard | Skip | Contract fixture | Supply valid MMDX entry |
| Missing position/rotation/scale | Defaults | Zero position/rotation and scale 1 | Contract fixture | Expected fallback |
| Zero/negative scale | Historical clamp | Basis scale uses `0.0001` | Contract fixture | Supply valid ADT scale |
| Cancellation after grouping | Loader state check | Discard stale result | Loader regressions | Re-request eligible detail |
Cancellation stays loader-owned because grouping has no task or partial state.
## Configuration and capabilities
No setting or profile branch is introduced. Preserved behavior includes
backslash replacement without lowercasing, case-sensitive lowercase `.mdx/.mdl`
conversion, first-seen ordering, tile-local subtraction and scale clamp `0.0001`.
## Persistence, cache and migration
No persistence or cache format participates. ADT and M2 cache versions are
unchanged, so no rebuild or migration is required.
## Diagnostics and observability
The service emits no logs. Its verifier records contract results, source
boundaries and bounded synthetic 100-by-256 placement timing. Loader metrics
continue to own task/build/cache observability.
## Verification
- `verify_m2_placement_grouper.gd`: invalid input, path behavior, ordering,
defaults, local origin, regular/waterfall transforms, scale clamp, fresh output,
loader/source boundary and timing.
- `verify_m2_placement_transform_resolver.gd`: three transform consumers remain
covered as two direct loader adapters plus the grouper adapter.
- Registry/dedupe, facade, internal-access, manifest, shutdown, documentation and
coordination regressions remain required.
- Fidelity evidence is exact extraction; no new asset-backed parity claim is made.
## Extension points
- `M2BuildBatchPlanner` now owns pure batch sizing/cursor progression; the
remaining build-job state machine stays behind explicit loader budgets.
- Spatial cells require measured culling evidence and are outside this contract.
## Capability status
| Capability | Status | Evidence | Gap/next step |
|---|---|---|---|
| Pure path/transform grouping | Implemented extraction | Contract/source/timing verifier | Asset-backed traversal timing pending |
| Worker/build orchestration | Remains in loader | Existing regressions | Separate stateful extraction required |
| Spatial MultiMesh cells | Partial | Current path batches | Culling-driven design/evidence pending |
## Known gaps and risks
- Raw placement dictionaries remain the ADT parser boundary.
- Uppercase extensions remain unchanged even though transform exception matching
is case-insensitive.
- Grouping is by model path, not spatial culling cell.
- Asset-backed p95/p99 and visual comparison remain unavailable.
## Source map
| Path | Responsibility |
|---|---|
| `src/render/m2/m2_placement_grouper.gd` | Validation, normalization and grouping |
| `src/render/m2/m2_placement_transform_resolver.gd` | Basis and origin rules |
| `src/render/m2/m2_build_batch_planner.gd` | Downstream batch sizing and cursor planning |
| `src/scenes/streaming/streaming_world_loader.gd` | Worker/result/build lifecycle and resources |
| `src/tools/verify_m2_placement_grouper.gd` | Contract, dependency and timing regression |
## Related decisions and references
- [`m2-build-batch-planner.md`](m2-build-batch-planner.md)
- [`m2-placement-transform-resolver.md`](m2-placement-transform-resolver.md)
- [`m2-unique-placement-registry.md`](m2-unique-placement-registry.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)

Some files were not shown because too many files have changed in this diff Show More