Files
recordingtest/docs/history/2026-04-09_3tier-split-step2-and-v3-wireup.md
minsung 03fb504eea BREAKING: 3-tier split step 2 + engine-bridge v3 EgBim lambdas wired
Completes the Generic / HmEG-aware / App-specific separation started in
f6b6e74. The legacy EgPlugin / EngineBridge / EngineBridge.Client /
EngineBridge.Probe modules are moved into their proper tiers, namespaces
and csproj/sln entries are renamed, and the HmegDirectStateProvider
lambdas are finally populated with real handles from the EgBim plugin
host. A new Recordingtest.Architecture.Tests project enforces the tier
rule at build time.

Moves (git mv + csproj/RootNamespace/AssemblyName rename + sln):

  src/Recordingtest.EgPlugin
    -> src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost
  src/Recordingtest.EngineBridge
    -> src/Hmeg/Recordingtest.Hmeg.Catalog
  src/Recordingtest.EngineBridge.Client
    -> src/Hmeg/Recordingtest.Hmeg.Bridge.Client
  src/Recordingtest.EngineBridge.Probe
    -> src/Hmeg/Recordingtest.Hmeg.Catalog.Probe

  tests/Recordingtest.EgPlugin.Tests
    -> tests/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost.Tests
  tests/Recordingtest.EngineBridge.Tests
    -> tests/Hmeg/Recordingtest.Hmeg.Catalog.Tests
  tests/Recordingtest.EngineBridge.IntegrationTests
    -> tests/Hmeg/Recordingtest.Hmeg.Catalog.IntegrationTests

Namespace rename applied across all .cs files and csproj RootNamespace:

  Recordingtest.EgPlugin           -> Recordingtest.Sut.EgBim.PluginHost
  Recordingtest.EngineBridge       -> Recordingtest.Hmeg.Catalog
  Recordingtest.EngineBridge.Client -> Recordingtest.Hmeg.Bridge.Client
  Recordingtest.EngineBridge.Probe -> Recordingtest.Hmeg.Catalog.Probe

New: tests/Recordingtest.Architecture.Tests/

  DependencyGraphTests walks Assembly.GetReferencedAssemblies() for each
  tier and fails if a forbidden reference leaks in:
    - Generic modules must not reference HmEG or any app-specific DLL
    - HmEG-aware modules must not reference app-specific DLLs
    - Recordingtest.Hmeg.Bridge must reference HmEG (positive check)
  11 tests, all passing. Prevents future drift from CLAUDE.md §8.1.

Engine-bridge v3 wire-up (HmEgBridgePlugin.BuildProvider):

  Previously the HmegDirectStateProvider lambdas returned null and the
  chain fell through to reflection. They now call directly into the
  EditorPlugin base class that HmEgBridgePlugin inherits:

    spaceProvider    = () => RootSpace
                           // AppManager.ViewportManager.RootSpace
    viewportProvider = () => View
                           // EGViewport : Control, HmEGViewport
    documentPathProvider = () => AppManager?.FileManager?.CurrentFile

  Every lambda is wrapped in try/catch so plugin construction still
  cannot throw back into the SUT. Editor02.HmEGAppManager.dll added as
  a reference on Recordingtest.Sut.EgBim.PluginHost.csproj — app-
  specific tier, which is allowed by the architecture tests.

Entry points were confirmed from read-only review of the SUT sources at
  D:\GiteaAll\EG-BIM_Modeler\EditorPluginInterface\EditorPlugin.cs
  D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\HmEGAppManager.cs
  D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\SubManager\FileManager.cs

closing out Q1/Q2/Q6/Q7 from docs/hmeg-api-survey.md.

Tests: 115 -> 126 (+11 Architecture), 0 failures.

Next step: live verification of /scene /camera /selection with a real
SUT session; any discrepancy in HmegDirectStateProvider reflection will
be tightened after observing real HmEG camera field names.

Ref: #10 follow-up, #14 follow-up, docs/contracts/generic-sut-split.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 10:39:13 +09:00

7.1 KiB

2026-04-09 — 3-tier 분리 2단계 + engine-bridge v3 EgBim 람다 wire-up

이슈: #10 follow-up (engine-bridge v3) + docs/contracts/generic-sut-split.md 소요 시간: ~110분 (새 세션 / 동일 날짜 두 번째 블록) Context 사용량: input ~80k / output ~18k tokens (Opus 4.6, 1M context, 새 세션)

작업

1. 3-tier 분리 2단계 (mass-rename + move)

기존 EgPlugin/EngineBridge 모듈을 새 계층 폴더로 이동하고 네임스페이스를 일괄 rename.

소스 이동 (git mv):

원본 대상 계층
src/Recordingtest.EgPlugin/ src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/ App-specific
src/Recordingtest.EngineBridge/ src/Hmeg/Recordingtest.Hmeg.Catalog/ HmEG-aware
src/Recordingtest.EngineBridge.Client/ src/Hmeg/Recordingtest.Hmeg.Bridge.Client/ HmEG-aware
src/Recordingtest.EngineBridge.Probe/ src/Hmeg/Recordingtest.Hmeg.Catalog.Probe/ HmEG-aware
tests/Recordingtest.EgPlugin.Tests/ tests/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost.Tests/ App-specific
tests/Recordingtest.EngineBridge.Tests/ tests/Hmeg/Recordingtest.Hmeg.Catalog.Tests/ HmEG-aware
tests/Recordingtest.EngineBridge.IntegrationTests/ tests/Hmeg/Recordingtest.Hmeg.Catalog.IntegrationTests/ HmEG-aware

네임스페이스 rename:

  • Recordingtest.EgPluginRecordingtest.Sut.EgBim.PluginHost
  • Recordingtest.EngineBridgeRecordingtest.Hmeg.Catalog
  • Recordingtest.EngineBridge.ClientRecordingtest.Hmeg.Bridge.Client
  • Recordingtest.EngineBridge.ProbeRecordingtest.Hmeg.Catalog.Probe

csproj rename + <RootNamespace> / <AssemblyName> + ProjectReference 경로 갱신 + recordingtest.sln 에서 remove/add.

문제/해결:

  • git mv가 bin/obj 폴더 때문에 일부 실패 → 해당 폴더 삭제 후 재시도
  • EngineBridge.Client 전체 폴더 이동이 계속 permission denied → 파일 단위로 git mv해서 해결
  • Directory.Build.props의 자동 RootNamespace와 csproj의 수동 RootNamespace 정리 (Sut.EgBim.PluginHost는 수동, Hmeg.Catalog는 수동 덮어쓰기)

2. Architecture Tests 추가

tests/Recordingtest.Architecture.Tests/ — 3-tier 규칙 강제.

  • Generic 모듈 (Bridge.Abstractions, Recorder, Player, Normalizer, DiffReporter, Runner, SutProber) 각각이 HmEG.dll 또는 Editor03.PluginInterface / Editor02.HmEGAppManager / EditorCore참조하지 않음 (Theory, 7건)
  • HmEG-aware 모듈 (Hmeg.Bridge, Hmeg.Catalog, Hmeg.Bridge.Client) 각각이 app-specific DLL을 참조하지 않음 (3건)
  • Hmeg.BridgeHmEG.dll참조함 (positive check, 1건)

총 11건, 모두 pass. 향후 누가 실수로 generic 모듈에 HmEG 참조를 추가하면 여기서 red.

3. engine-bridge v3 EgBim 람다 wire-up (Q1~Q7 답)

사용자가 D:\GiteaAll\EG-BIM_Modeler\EditorPluginInterface 경로 공유 → read-only survey.

확정 (single-file find): EditorPluginInterface/EditorPlugin.cs

public HmEGAppManager AppManager { get => TriggerStateService.AppManager; set; }
public Space RootSpace { get => AppManager.ViewportManager.RootSpace; }
public ViewportManager ViewportManager { get => AppManager.ViewportManager; }
public EGViewport View { get; set; }     // deprecated but still usable

HmEGAppManager (D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\HmEGAppManager.cs):

  • ViewportManager — RootSpace 진입점
  • SelectionManager — 중앙 선택 상태 (필요 시 hook, 현재는 walk)
  • FileManager — 저장 파일 경로
  • AppModeManager — 명령 lifecycle (Q4 후속)

EGViewport : Control, HmEGViewport (HmEG/Controls/HmEGViewport.cs:43) — 그대로 HmEG-aware provider에 넘길 수 있음.

FileManager.CurrentFile : string — 저장 문서 경로.

Q 답 매핑:

  • Q1 Space: this.RootSpace
  • Q2 Viewport: this.View (EGViewport: HmEGViewport)
  • Q3 Selection: 중앙 리스트 없음. Space walk + ISelectable.IsSelected (이미 구현됨)
  • Q4 Command lifecycle: AppModeManager, TriggerStateService.TriggerEnded (별도 contract)
  • Q5 Fov: PerspectiveCamera cast, HmegDirectStateProvider.GetCamera의 reflection FieldOfView 후보 chain이 이미 잡음
  • Q6 DocumentPath: AppManager.FileManager.CurrentFile
  • Q7 EGViewport↔HmEGViewport: EGViewport : Control, HmEGViewport

구현: HmEgBridgePlugin.BuildProvider()가 이제 실 람다 주입:

Func<Space?> spaceProvider = () => { try { return RootSpace; } catch { return null; } };
Func<HmEGViewport?> viewportProvider = () => { try { return View; } catch { return null; } };
Func<string?> documentPathProvider = () =>
{
    try { var p = AppManager?.FileManager?.CurrentFile; return string.IsNullOrEmpty(p) ? null : p; }
    catch { return null; }
};

csproj 추가 참조: Editor02.HmEGAppManager.dllHmEGAppManager.FileManager.CurrentFile 접근을 위해. app-specific tier이므로 허용 (ArchitectureTests는 이 tier를 검사하지 않음).

테스트

  • 전체 suite: 126 tests pass (115 → 126, +11 ArchitectureTests)
  • 구성: Recorder 26 / Player 24 / Sut.EgBim.PluginHost 21 / Normalizer 16 / Architecture 11 / DiffReporter 5 / Runner 6 / Hmeg.Catalog 6 / Hmeg.Catalog.Integration 6 / Hmeg.Bridge 5
  • 빌드/테스트 0 failures

분류 라벨 (2단계 완료 후 현재)

경로 계층
src/Recordingtest.Bridge.Abstractions/ Generic
src/Recordingtest.Recorder/, .Player/, .Normalizer/, .DiffReporter/, .Runner/, .SutProber/, .DiffReporter.Cli/ Generic
src/Hmeg/Recordingtest.Hmeg.Bridge/, .Catalog/, .Catalog.Probe/, .Bridge.Client/ HmEG-aware
src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/ App-specific (EgBim)

라이브 검증 (대기)

다음 세션의 P1:

  1. 본 작업물을 빌드해 EG-BIM Modeler/Plugins/Recordingtest.Sut.EgBim.PluginHost/ 아래 배포
  2. SUT 실행, 이미 열린 .hmeg 문서가 있으면 Box 등 몇 개 객체 생성 + 선택
  3. 셸에서:
    curl http://localhost:38080/health
    curl http://localhost:38080/scene     # object_count, document_path
    curl http://localhost:38080/camera    # eye/target/up/fov 실값
    curl http://localhost:38080/selection # selected_ids
    
  4. 실값이 기대와 다르면 HmegDirectStateProvider.GetCamera의 reflection 멤버 후보 또는 selection walk 로직 보정 1~2회 반복

미커밋 (다음 커밋에 통합)

  • 3-tier 분리 2단계 전체 (수많은 파일 rename/move)
  • Recordingtest.Architecture.Tests 신규
  • HmEgBridgePlugin.BuildProvider 실 람다
  • Editor02.HmEGAppManager.dll 참조 추가
  • recordingtest.sln, PROGRESS.md, PLAN.md, 본 history

관련

  • CLAUDE.md §8.1
  • docs/contracts/generic-sut-split.md
  • docs/hmeg-api-survey.md
  • D:\GiteaAll\EG-BIM_Modeler\EditorPluginInterface\EditorPlugin.cs (read-only)
  • D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\HmEGAppManager.cs (read-only)
  • D:\GiteaAll\EG-BIM_Modeler\HmEGApplicationManagementLibrary\SubManager\FileManager.cs (read-only)