# engine-bridge Probe Design (PoC v2 blueprint) Related: issue #9, contract `docs/contracts/engine-bridge.md`. ## Goal Provide recordingtest with a reliable, low-latency read path into HmEG's live runtime state (selected object IDs, camera state, scene summary, render-complete flag) while the SUT (`EG-BIM Modeler`) is running, so golden-file scenarios can checkpoint internal state alongside saved `.hmeg` files. PoC v1 (this sprint) delivers static catalogs and a skeleton API. PoC v2 must actually get into the process. The rest of this document surveys the options and picks one. ## Options ### 1. `AssemblyLoadContext` side-load in the recordingtest process Load `HmEG.dll` into a secondary `AssemblyLoadContext` inside the recordingtest test runner. - Pros: no native code, single process, trivial lifetime. - Cons: **we would be reading our own copy of HmEG, not the SUT's**. No live camera, no live selection. Any singleton the SUT maintains is unreachable. - Verdict: **not viable** for read-live-state; listed for completeness so reviewers don't propose it. Static metadata scanning (what PoC v1 already does) is the legitimate use of this family of APIs. ### 2. CLR profiling API (ICorProfilerCallback) Ship a native profiler DLL registered via `CORECLR_PROFILER*` env vars. The profiler attaches at CLR startup, can `SetILFunctionBody` on HmEG methods, or hand managed code to a bootstrap assembly that installs hooks. - Pros: deepest reach, survives obfuscation, hooks JIT. Can intercept private methods. - Cons: native DLL (C/C++), must match CLR bitness and version, requires SUT restart with env vars set, complex failure modes, AV-sensitive. - Verdict: **fallback** for a zero-cooperation scenario. Too heavy for first attempt. ### 3. In-process managed injector (e.g. `Ezez.CLRInjection`, `SharpMonoInjector` style) `CreateRemoteThread` + `LoadLibrary` on a small native bootstrap that starts a CLR host and runs our managed bridge DLL inside the SUT's AppDomain. Bridge uses ordinary reflection to reach `HmEGAppManager` and exposes state via named pipe or loopback HTTP. - Pros: attach at runtime, no SUT restart if the SUT is already running. Pure managed once bootstrapped. Symmetrical with how profilers like dotTrace attach on demand. - Cons: Windows-only, bitness must match (SUT is x64 WPF), AV alarms, fragile across .NET runtime versions, process isolation privileges required. CoreCLR hosting from a foreign thread is tricky compared to classic Framework. - Verdict: viable but operationally painful. Use only if option 4 is blocked. ### 4. Plugin masquerade via SUT's existing MEF pipeline (RECOMMENDED) The SUT already loads plugins from `EG-BIM Modeler/Plugins/` using MEF (per `CLAUDE.md` §1 and `docs/sut-catalog/plugins.md`). Drop `Recordingtest.EngineBridge.SutPlugin.dll` into that folder. It exports the plugin contract the SUT expects, grabs a reference to `HmEGAppManager` (or whatever the static catalog shows is the canonical accessor), and publishes `IEngineSnapshot` over a localhost endpoint (named pipe `recordingtest-bridge` or HTTP on 127.0.0.1). - Pros: legitimate extension point, no native code, no process injection, no AV noise, CI friendly, single cooperating restart. Honors CLAUDE.md §5.7 ("SUT 침습 최소화 — 별도 어셈블리로 격리"). Integrates with dispatcher marshaling by design — the plugin runs on the WPF UI thread when called back. - Cons: requires SUT restart once to load the plugin; depends on MEF contract remaining stable; small IPC surface to keep deterministic. - Verdict: **chosen path for PoC v2**. ### 5. AutomationPeer (comparison baseline) Add a custom `UIA` AutomationPeer on the 3D viewport that exposes selection via the standard `SelectionPattern` and camera via custom properties. - Pros: standard, debuggable with `inspect.exe`. - Cons: requires SUT source changes; cannot easily expose full scene graph or render-complete without contortions; limited to what UIA patterns can model. - Verdict: parallel track for exposing UI-shaped state. Not a replacement for the bridge. ## Render-complete signal | Approach | Latency (expected) | Notes | |---|---|---| | Poll `IsDirty`-like property at ~16 ms | ~1 frame | simple, robust; costs a reflection call per tick | | Subscribe to a `FrameRendered`/`Invalidated` event | event-driven, ~0 ms | best case; depends on such an event existing (the static catalog will tell us — look for `render` candidates with `MemberKind=Event`) | | WPF `CompositionTarget.Rendering` from inside the plugin | ~1 frame | works even if HmEG has no event; UI-thread bound | Recommendation: try event subscription first via the candidates catalog; fall back to `CompositionTarget.Rendering` inside the MEF plugin because the plugin already runs on the UI thread. ## Recommendation PoC v2: **implement option 4 (plugin masquerade)** with a named-pipe JSON protocol that produces the `IEngineSnapshot` DTOs defined in this library. Option 2 (CLR profiler) is the documented fallback for a future "zero cooperation" scenario. Option 5 (AutomationPeer) is a parallel concern that belongs to `sut-prober` and `recorder`, not to engine-bridge. ## Open questions (tracked, not blocking v1) - Exact MEF contract the SUT expects (see `docs/sut-catalog/plugins.md` — resolved before v2) - Whether HmEG exposes a `FrameRendered`-style event (answer will come from v1 catalog) - Selection identity: object GUID vs. transient index — needs a stable key for golden diffs