From 6f896fb701c5f9478e1449105edd77d0c4e7246d Mon Sep 17 00:00:00 2001 From: Zwitschi Date: Tue, 10 Nov 2026 13:46:10 +0100 Subject: [PATCH] docs(memory): the seven findings from the tools move and the e2e chain Records what cost time today, with the evidence: - the four traps of the folder move: sibling imports (sys.path needs both the tools root AND the script's own folder), shell scripts resolving REPO relative to themselves, stale result files passing as success, and Unreal hanging rather than failing when a second editor is alive - plus the pkill-matches-its-own-command-line gotcha; - measured proof that LevelEditorSubsystem.new_level() cannot replace a map in one session (create -> delete -> create returns False, and delete_asset leaves the .umap), so emptying actors is the workable step; and that a falling actor count (600 -> 409) was the fix that audits clean; - two-model judging: the reconciliation rule, the 404 that retired claude-3.5-haiku, and the observation that this judge is consistently pessimistic on our content so the model disagreement is the signal; - local ComfyUI: it is a Stability Matrix install nothing here starts, the documented graphs are UI format that /prompt rejects, albedo-only output, no seamless sampler in 0.44, and per-model licence rules; - hero facade: detail was modelled inside the wall, the hero's materials are not the kit's, Unreal keeps a material on re-import, materials must be saved themselves, and project_dir() is not the repo root; - the render chain is build -> import -> render, and e2e_asset_check.py is the command that runs it all. --- MEMORY.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/MEMORY.md b/MEMORY.md index 77f1063..714fa2a 100644 --- a/MEMORY.md +++ b/MEMORY.md @@ -607,3 +607,57 @@ Road_Hollywood_07 158 (was 366) - **Layout now** — shared, one level down and flat so it is discovered: `~/.pi/agent/skills/<93 packs>/`, `~/.pi/agent/skills/references/`, `~/.pi/agent/skills/LICENSE`. Project-specific and versioned: `.agents/skills/blender-skills/blender-director/`, whose `../references/` links were rewritten to `~/.pi/agent/skills/references/…` because it no longer sits beside them. The 94 `~/.agents/skills` symlinks are gone. - **Verified** by walking the three discovery roots (`~/.pi/agent/skills`, `~/.agents/skills`, project `.agents/skills`, dot-dirs skipped), parsing every frontmatter: **97 skills loaded, 0 malformed, 0 collisions**. Backups of the old trees: `/tmp/agent-skills-backup.tgz`, `/tmp/pi-blender-skills-dupe` (now consumed). - Rule: a skill exists in **one** place. Two roots with the same `name` is a warning on every start, and the loser is invisible — check the startup list, not the file tree. + +## 2026-11-10 — Tools restructured, and the four traps it set + +The sixty scripts had grown into one flat directory; they are now grouped by what they talk to (and therefore by sandbox): `Tools/unreal/` (commandlets, both map builders, `ue_common.py`, `ue_placement.py`), `Tools/blender/` (builders, exporters, FBX preset, MPFB pipeline, `blender_screenshots.py`), `Tools/review/` (render/judge/compare/e2e/`visual_targets.json`), `Tools/gen/` (ComfyUI driver, procedural textures, reference fetching), `Tools/input/` (OS input injection). Root keeps `_common.py`, `check-lfs.sh`, `addon_compat_probe.py`, `sibling_asset_audit.py`, `tests/`. + +Four things broke, each with the same shape — a path that used to resolve and now does not: + +- **Sibling imports.** `import ue_placement` / `import ue_common` failed inside every Unreal commandlet (`ModuleNotFoundError`), because the rewrite put only the tools root on `sys.path` while those modules are the script's _siblings_. Both paths are inserted: `sys.path.insert(0, dirname(_HERE))` then `sys.path.insert(0, _HERE)`. +- **Shell scripts resolve `REPO` relative to themselves.** `Tools/review/render_screenshots.sh` computed the root as one level up, so every commandlet path became `Tools/Tools/...` and the capture step died in **0 s**. Any script whose own depth changes needs its `REPO="$(cd "$(dirname ...)/.." && pwd)"` re-checked. +- **Stale result files pass as success.** The e2e driver accepted `/tmp/gigline/demo-map.txt` left by an earlier run, so a **10-second "demo build"** reported PASS without building. Result files must be unlinked before a step runs, and child output must stream to a per-step log file — a captured pipe hides fifteen minutes of a Unreal build and deadlocks if the engine outlives it. +- **Two editors hang, they do not fail.** Unreal does not refuse a second instance on the same project; it waits. The driver now refuses to start while any `UnrealEditor-Cmd` is alive. `pkill -9 -f ` in the same shell also matches the shell's own command line — kill in a separate invocation, or by PID. + +## 2026-11-10 — `LevelEditorSubsystem.new_level()` cannot replace a map in one session + +Measured on a throwaway path: + +``` +new_level fresh -> True +delete_asset -> True +disk file still present -> True (delete_asset leaves the .umap) +new_level again -> False +``` + +So "delete the map and recreate it" does not work in a commandlet: the package stays in memory and `new_level` refuses the path even after removing the file from disk. What works — and is what the builders actually need — is load, destroy **every** actor, save, and verify the count is 0. That is what `Tools/unreal/ue_recreate_maps.py` does now. + +The related lesson: a prefix-based clear (`clear(("Demo_", "PlayerStart", ...))`) only removes what the builder knows about. Stale actors from experiments survive every rebuild — which is how a stale road mesh and a duplicated vehicle actor were both saved back into the map. Clearing the whole level turned the old **600-actor** map into a **409-actor** one that audits clean (`0 failure(s)`, no duplicate labels): 72 site meshes, 310 facade-kit pieces, six frontage buildings, lights, cameras, player start. **A falling actor count was the fix, not a regression.** + +## 2026-11-10 — Every image is judged by two models; the disagreement is the signal + +`Tools/review/judge_screenshot.py` runs `openai/gpt-4o-mini` and `qwen/qwen3.7-flash` by default and reconciles them: agreement carries the verdict with the mean confidence, disagreement reports `uncertain` and keeps both reasons rather than letting the more confident model win. `--model` forces a single judgement. + +- The second model had to be replaced once already: `anthropic/claude-3.5-haiku` returned **404, no longer served by any provider**. Take the model list from `https://openrouter.ai/api/v1/models` rather than trusting a remembered id. +- On this content the judge is **consistently pessimistic**: it reports `0/11` across review shots the owner has approved, and it has been wrong in both directions (missed a real float, disputed correct frames). Treat it as an advisory pre-check, and treat a _model disagreement_ as the thing worth reading. +- `Tools/gen/comfyui_generate.py` applies the same rule to generation: two checkpoints by default, same prompt and seed, scored by mean absolute Laplacian (catches blurry/flat output), winner takes `--out`, loser is kept beside it. The score ranks candidates; only the judge understands content. + +## 2026-11-10 — Local ComfyUI, and the workflows worth using + +- The server at `http://127.0.0.1:8188` is a **Stability Matrix** install and nothing in this repository starts it. If a tool cannot connect, start Stability Matrix, launch ComfyUI, re-run. The install documents 141 GB of models in `/home/zwitschi/StabilityMatrix/Data/README.md`. +- The documented texture graphs are ComfyUI **UI format**, which `/prompt` cannot accept; a tool must rebuild the equivalent API graph. `Tools/gen/comfyui_generate.py texture` does that with their exact settings: `albedobase` (30 steps, CFG 6, dpmpp_2m/karras), `krea2` (8 steps, CFG 1, euler/simple, `ConditioningZeroOut`, texture-fix VAE), `qwen21` (25 steps, CFG 1, `TextEncodeQwenImage21`, texture-fix VAE). +- Two limits the graphs carry and a tool must not hide: they produce **albedo only** (normals/roughness come from a material tool downstream), and ComfyUI 0.44 core has **no seamless sampler**, so "tileable" in a prompt is a hint — offset the image 50 % and check the seam. +- Licences differ per workflow and matter before shipping: AlbedoBase allows commercial use with credit, Krea-2 has a community licence, **Qwen-Image 2.1 is research-only**. `THIRD_PARTY_NOTICES.md` carries the row. + +## 2026-11-10 — Hero facade: what actually blocked it + +- **Detail modelled inside the wall.** Every applied part sat behind the mass's front face (`Hero_Banner_1` at Y −1.02 against a wall at −0.50), so nothing rendered but the columns and cornice. `seat_on_front()` now states the plane once (`FRONT_Y`) and seats each part at `FRONT_Y + offset`; it reports every part it moves. +- **The hero's materials are not the kit's.** `M_FacadeStone` in `/Game/Hollywood/Site` belongs to the facade _kit_; the hero uses `/Game/Hollywood/ElCapitan/M_Facade_*`, created by the FBX import. Warming the Site material changed nothing for three rebuilds. +- **Unreal keeps a material asset on re-import**, so a colour change in the Blender builder never arrived. Deleting the materials before the import broke the mesh's slots (one colour, no banners, no windows). What works: import normally, then **author one plain material per slot** with the reference colour and assign it. +- **Save the material itself.** Saving only the mesh left the new materials unsaved; after a reload the slots pointed at nothing and the whole elevation fell back to one default colour. +- `unreal.Paths.project_dir()` is `Game/GigLine`, **not** the repository root — textures under `Art/` must be resolved from the script's own location. +- Still open: the generated ashlar albedo is imported and the material contains `TextureSampleParameter2D` → `Multiply` → Base Color with UV tiling, but the render still shows the flat tint. Next probe is the compiled Base Color pin, not the node list. + +## 2026-11-10 — Render-to-import chain, in one order + +A visual change needs **build → import → render**, never just a render: `render_screenshots.sh` renders the imported Unreal assets. `Tools/review/e2e_asset_check.py` runs the whole thing (Blender renders → empty maps → both builds → captures → two-model judge → README) and is the command to use instead of remembering four.