Files
recordingtest/CLAUDE.md
minsung f6b6e7449e 3-tier split (step 1) + engine-bridge v3 scaffold + HmegDirectStateProvider
Lays down the Generic / HmEG-aware / App-specific separation that lets us
target other HmEG-hosting WPF applications later, and lands the v3 engine
state provider on top of it.

Architecture rule (CLAUDE.md §8.1, new): every module belongs to exactly one
of three tiers — Generic / HmEG-aware / App-specific (e.g. EgBim). Dependency
direction is strictly App-specific → HmEG-aware → Generic. Generic must not
reference HmEG.dll; HmEG-aware must not reference any per-app assembly.

This commit is the first incremental step:

  + src/Recordingtest.Bridge.Abstractions/  (Generic, new csproj)
      IEngineStateProvider, CameraSnapshot, SceneSnapshot,
      NullEngineStateProvider — extracted from EgPlugin so the generic core
      owns the contract. Zero SUT references.

  + src/Hmeg/Recordingtest.Hmeg.Bridge/      (HmEG-aware, new csproj)
      HmegDirectStateProvider — IEngineStateProvider implemented against
      the HmEG public API (Space, HmEGViewport, ISelectable, ModelBase.Uid).
      Decoupled from any specific host app via Func<Space?>/Func<HmEGViewport?>
      lambdas; the EgBim plugin host supplies them. Reusable for any other
      WPF application that hosts HmEG.

      Selection traversal walks Space.Children and collects ModelBase.Uid
      for nodes whose ISelectable.IsSelected is true. We deliberately type
      nodes as object + late-bound Uid lookup to avoid pulling MemoryPack
      into the dependency graph.

  + tests/Hmeg/Recordingtest.Hmeg.Bridge.Tests/
      5 unit tests covering null lambdas, throwing lambdas, document path
      provider, and constructor null arg validation.

  + src/Recordingtest.EgPlugin/ChainedEngineStateProvider.cs
      Wraps two providers; falls back from Hmeg.Direct to the existing
      Reflection accessor when the primary returns empty/default. Lets us
      land the new wire-up before the EgBim adapter Q1~Q7 lookups are
      filled in. 7 new tests.

  + src/Recordingtest.EgPlugin/IAppManagerAccessor.cs
      Reflection accessor abstraction (preserved as the v3 fallback). Looks
      up Editor.AppManager.AppManager via well-known Instance/Current
      property names. Unit-testable through a fake.

  ~ src/Recordingtest.EgPlugin/IEngineStateProvider.cs
      Type definitions removed (now in Bridge.Abstractions); only the
      reflection-based provider remains. ReflectionEngineStateProvider
      delegates everything to IAppManagerAccessor.

  ~ src/Recordingtest.EgPlugin/HmEgBridgePlugin.cs
      BuildProvider() picks ChainedEngineStateProvider(Hmeg.Direct,
      Reflection). The HmEG-aware lambdas are stubs (return null) until the
      next step wires the EgBim adapter; the chain falls through to the
      reflection path so behaviour matches v2 for now.

  + docs/contracts/engine-bridge-v3.md       — Sprint Contract
  + docs/contracts/generic-sut-split.md      — Sprint Contract for the
      remaining mass-rename / folder move (step 2, deferred).
  + docs/hmeg-api-survey.md                  — Read-only survey of the HmEG
      public API (Space, ModelBase, HmEGViewport, IHmCamera, IPlugin) used
      to design HmegDirectStateProvider. Open Q1~Q7 listed.

Tests: 94 → 115 passing, 0 failing. The new HmEG-aware test project copies
HmEG.dll next to its output (Private=true) since it runs out-of-process.

Step 2 (deferred to next session): mass-rename
  src/Recordingtest.EgPlugin → src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost + .Adapter
  src/Recordingtest.EngineBridge → src/Hmeg/Recordingtest.Hmeg.Catalog
  src/Recordingtest.EngineBridge.Client → split (Generic + Hmeg)
plus Recordingtest.Architecture.Tests to enforce the §8.1 dependency rule.

Ref: #10 follow-up, #14 follow-up.

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

15 KiB

CLAUDE.md — recordingtest

이 파일은 Claude Code가 본 저장소에서 작업할 때 항상 읽는 프로젝트 운영 지침이다.

0. 세션 시작 시 필독 (모든 에이전트)

여러 에이전트가 작업을 분담하므로, 세션을 시작하는 모든 에이전트는 가장 먼저 다음 두 파일을 읽는다:

  1. PROGRESS.md — 지금까지 무엇이 끝났는가. 모듈별 진행 상태, 최근 완료 작업, 현재 차단 이슈.
  2. PLAN.md — 앞으로 무엇을 해야 하는가. 모듈별 To-Do, 담당 에이전트, 우선순위, 의존 관계.

읽고 나서 자신이 맡을 작업을 PLAN.md에서 고르고, 시작 시 PROGRESS.md에 "in progress"로 표시한다. 작업이 끝나면:

  • PROGRESS.md 의 해당 항목을 "done"으로 옮기고 날짜·결과·산출물 경로 기록
  • PLAN.md 의 완료 항목 제거 또는 다음 단계로 갱신
  • docs/history/YYYY-MM-DD_{작업명}.md 히스토리 파일 작성 (소요 시간·Context 사용량 필수)

원칙: PROGRESS.md와 PLAN.md는 에이전트 간 공유 메모리다. 자기 머릿속에만 두지 말고 반드시 파일에 반영해야 다음 에이전트가 이어받을 수 있다. 충돌 시 최신 커밋 기준으로 머지하고, 모호하면 사용자에게 질문한다.

0.1 작업 사이클 — Planner → Generator → Evaluator

Anthropic의 "Harness Design for Long-Running Agent Applications" 설계 원칙을 채택한다. 핵심: 생성자와 평가자를 같은 에이전트가 겸하지 않는다. 자기 작업을 과대평가하는 편향을 피하기 위해서다.

모든 비자명한(non-trivial) 모듈/기능 작업은 다음 3단계를 거친다:

  1. Planner (/contract <name>) — 사용자의 요청을 Sprint Contract로 변환.

    • 산출물: docs/contracts/<name>.md (Goal, Definition of Done, Interfaces, Out of scope, Evaluation plan, Risks)
    • DoD 항목은 객관적으로 검증 가능해야 한다. "잘 동작한다"는 금지.
    • PLAN.md에 해당 항목 추가.
  2. Generator — Sprint Contract를 계약으로 삼고 실제 구현. 일반 세션 또는 전용 구현 에이전트가 수행.

    • 계약을 읽고 DoD 항목만 충족시키는 데 집중.
    • 스코프 이탈 금지. 범위 변경이 필요하면 planner를 다시 호출.
  3. Evaluator (/evaluate <name>) — 독립된 evaluator 서브에이전트가 계약 기준으로 채점.

    • 산출물: docs/contracts/<name>.evaluation.md (verdict + evidence table)
    • fail이면 PROGRESS.md에 done으로 옮기지 않는다. Generator가 재작업.
    • pass여야만 호출자가 PROGRESS.md를 갱신한다.

컨텍스트 위생 (context hygiene)

  • 긴 작업 중 컨텍스트가 차면 요약(compaction)하지 말고 파일에 상태를 쏟고 새 세션으로 리셋한다. PROGRESS.md/PLAN.md/Sprint Contract가 그 인계 창구다.
  • Stop hook이 핸드오프 누락을 경고한다. 무시하지 말 것.
  • 모델/하네스가 진화하므로 .claude/ 비계는 주기적으로 감사·축소한다 (PLAN.md에 "scaffolding review" 상시 항목 유지).

1. 프로젝트 정체성

recordingtest 는 사내 WPF 3D 편집 응용(자체 개발, Rhino3D 유사)에 대한 사용자 입력 회귀 테스트 자동화 도구다. 도구 자체이지 SUT가 아니다.

SUT(System Under Test) 개요

  • WPF 데스크톱 애플리케이션, Main Window 1개 Rhino3D 유사 UI
  • 자체 3D 엔진 HmEG (Helix toolkit 유사)
  • MEF 기반 plug-in 아키텍처 — 기능별로 독립 C# 프로젝트
  • 결과물: 자체 포맷의 모델(.hmeg)/프로젝트 저장 파일

2. 핵심 전략 — Golden-file 회귀

수동 테스트 입력을 레코딩 → 리플레이 → 결과 저장 파일을 베이스라인과 diff.

ApprovalTests 패턴과 동형. SUT 코드 변경 협조를 최소화하기 위한 의도적 선택.

[수동 테스트] → 입력 레코드 + 결과 파일 A (baseline)
[회귀 시점]   → 입력 리플레이 → 결과 파일 B → normalize → diff(A, B)

우선순위:

  • 1순위: recorder, player, 정규화(normalizer), diff-reporter
  • 2순위: engine-bridge (엔진 상태 sidecar JSON 덤프)
  • 후순위: viewport-verifier (픽셀/이미지 비교) — golden-file로 못 잡는 케이스 보강용

3. 아키텍처 구성요소(예정)

모듈 책임
sut-prober 대상 앱 실행, UIA 트리·MEF plugin 목록 덤프
recorder 입력 캡처(키/마우스/포커스) + element path + offset 동시 저장
player 시나리오 재생, 비동기 작업 동기화
normalizer 저장 파일 정규화(타임스탬프/GUID/경로/부동소수점/순서)
diff-reporter baseline vs received diff, 갈라진 지점 시각화
engine-bridge HmEG 내부 상태(카메라/선택/씬그래프) sidecar JSON 노출
test-runner 시나리오 묶음 실행, 리포트
viewport-verifier (후순위) 3D 스크린샷 SSIM 비교

각 모듈은 독립 PoC → 통합 순서로 진행한다.

4. 기술 스택 가이드

  • 언어: C# / .NET (SUT와 동일 생태계, in-process probe 가능)
  • UI 자동화: FlaUI 1순위 (UIA 기반, .NET 네이티브). WinAppDriver/Appium은 fallback.
  • 저수준 입력: Win32 SetWindowsHookEx (low-level mouse/keyboard) — element 매칭과 hybrid
  • 시나리오 포맷: JSON 또는 YAML (git diff 친화적). 바이너리 금지.
  • 베이스라인 파일: *.approved.{ext} / *.received.{ext} 명명 규칙
  • 이미지/큰 베이스라인: Git LFS

5. 반드시 지킬 설계 원칙

  1. 결정성(Determinism) 우선 — 비결정적 요소(시각·랜덤·경로·GUID·부동소수점·컬렉션 순서)는 정규화 파이프라인을 통과해야 한다. 새 필드 추가 시 정규화 규칙 동시 등록.
  2. Element-aware 입력 캡처 — 좌표만 저장 금지. 항상 UIA element path + 상대 offset을 같이 기록해 해상도/DPI/창크기 변화에 견디게 한다.
  3. 타이밍 동기화 — 고정 sleep 금지. UIA 이벤트, property 변경, plugin 로드 완료 신호를 대기한다.
  4. Dispatcher marshaling — SUT 내부 probe/hook 코드는 반드시 WPF UI thread 위에서 동작.
  5. 체크포인트 — 한 시나리오 안에서 여러 번 저장→비교 가능해야 한다(이분 탐색용).
  6. 실패 아티팩트 풀세트 — 실패 시 스크린샷, UIA 트리 덤프, 엔진 상태 sidecar, 입력 로그, diff를 한 폴더에 동시 저장.
  7. SUT 침습 최소화 — AutomationPeer/probe 부착이 필요하면 별도 어셈블리로 격리하고 SUT 팀과 합의 후 진행.
  8. 민감정보 마스킹 — 레코딩에 비밀번호/토큰 포함 금지.

6. 환경 제약

  • 세션 0 불가: WPF는 대화형 데스크톱 세션 필요 → CI에서 헤드리스 불가, RDP/대화형 agent 필요
  • DPI/멀티모니터 정규화: 테스트 머신은 고정 DPI 권장
  • GPU 의존: 3D 렌더 결과는 드라이버 영향 → 픽셀 비교는 항상 톨러런스 + 마스킹

7. 작업 흐름 규칙

히스토리 기록 (필수)

모든 작업 완료 시 docs/history/YYYY-MM-DD_{작업명}.md 작성. 필수 항목:

  • 소요 시간
  • Context 사용량
  • 관련 이슈 (#N)

누락 시 저장이 차단된다.

저장소

Claude 작업 원칙

  • 세션 시작 시 PROGRESS.md / PLAN.md 먼저 읽기 (§0 참조)
  • 코드 변경 전 관련 파일 read 필수
  • 테스트 자동화 도구 자체의 회귀를 위해 본 저장소 코드도 단위 테스트 보유 권장
  • 의존성 추가는 사전에 사용자 확인
  • 메모리 시스템(~/.claude/projects/.../memory/)에 프로젝트 진행 상태/전략 결정 보존
  • 작업 종료 시 PROGRESS.md / PLAN.md 업데이트 + 히스토리 파일 작성 (3종 세트)

8. 디렉터리 구조 (예정 — 셋업 시 확정)

recordingtest/
├── src/
│   ├── Recordingtest.Recorder/
│   ├── Recordingtest.Player/
│   ├── Recordingtest.Normalizer/
│   ├── Recordingtest.DiffReporter/
│   ├── Recordingtest.EngineBridge/
│   ├── Recordingtest.SutProber/
│   └── Recordingtest.Runner/
├── tests/
├── scenarios/                # 시나리오 JSON/YAML
├── baselines/                # *.approved.* (LFS 후보)
├── docs/
│   ├── history/              # 작업 히스토리
│   ├── contracts/            # Sprint Contracts + evaluations
│   └── sut-catalog/          # sut-explorer 산출물
├── PROGRESS.md
├── PLAN.md
└── CLAUDE.md

9. 비목표 (Out of Scope)

  • SUT 자체의 기능 변경/버그 수정
  • 일반 웹/모바일 자동화
  • 부하·성능 테스트
  • 단위 테스트 프레임워크 대체

8.1 코드 계층 분리 (의무 규칙)

본 도구는 EG-BIM Modeler 외에도 다양한 WPF 응용을 회귀 자동화 대상으로 삼는다. 또한 사용자 WPF 응용군의 대다수가 자체 3D 엔진 HmEG를 공유한다. 따라서 코드는 다음 3개 계층 중 하나에 명시적으로 속한다.

계층 의미 의존 가능 폴더/네이밍
Generic 임의 WPF 데스크톱 응용에 동작 .NET BCL, FlaUI/UIA3, Win32, YamlDotNet 등 SUT-중립 라이브러리만 src/ 직속, 네임스페이스 Recordingtest.*
HmEG-aware HmEG 엔진을 호스팅하는 임의 WPF 응용에 동작 (앱은 고정 안 됨) Generic + HmEG.dll src/Hmeg/ 하위, 네임스페이스 Recordingtest.Hmeg.*
App-specific 특정 응용(예: EG-BIM Modeler)에만 동작 Generic + HmEG-aware + 해당 앱 어셈블리 (Editor03.PluginInterface.dll 등) src/Sut/<AppName>/ 하위, 네임스페이스 Recordingtest.Sut.<AppName>.*

의존 방향 (단방향)

App-specific  ──→  HmEG-aware  ──→  Generic
   (e.g. EgBim)        (e.g. HmegBridge)        (Recorder/Player/...)

역참조 금지: Generic은 HmEG-aware/App-specific을 모름. HmEG-aware는 App-specific을 모름.

강제 사항

  1. Generic 코드는 어떤 SUT 어셈블리도 참조하지 않는다. HmEG.dll도 안 됨.
  2. HmEG-aware 코드는 HmEG.dll만 참조한다. 특정 앱(Editor03.PluginInterface.dll 등)은 참조 금지.
  3. App-specific 코드만이 자기 앱의 어셈블리를 참조한다.
  4. 확장 지점은 항상 한 계층 아래에 인터페이스로 둔다. 예:
    • IEngineStateProvider (Generic) ← HmegEngineStateProvider (HmEG-aware) ← EgBimAppManagerAdapter (App-specific 진입점만 제공)
    • ITargetResolver (Generic) ← HmegSceneNodeTargetResolver (HmEG-aware) ← ...
  5. 시나리오 포맷, 베이스라인 포맷, 정규화 규칙은 Generic. HmEG/앱 특화 정규화 규칙이 필요하면 그 계층에서 plugin 식으로 등록한다.
  6. 새 SUT를 추가할 때 HmEG-aware 코드는 재사용한다. 새로 작성하지 말 것. 앱마다 다른 부분만 App-specific에 둔다 (보통: 플러그인 호스트 진입점 + 명령 lifecycle 이벤트 어댑터).

폴더 레이아웃 (목표 — 본 규칙 추가 후 점진 마이그레이션)

src/
├── Recordingtest.Recorder/              # Generic
├── Recordingtest.Player/                # Generic
├── Recordingtest.Normalizer/            # Generic
├── Recordingtest.DiffReporter/          # Generic
├── Recordingtest.Runner/                # Generic
├── Recordingtest.SutProber/             # Generic
├── Recordingtest.Bridge.Abstractions/   # Generic — IEngineStateProvider, ITargetResolver, ...
├── Recordingtest.Bridge.Client/         # Generic — HTTP 클라이언트
├── Hmeg/
│   ├── Recordingtest.Hmeg.Bridge/       # HmEG-aware — Space/Camera/Selection을 HmEG 공개 API로 읽기
│   ├── Recordingtest.Hmeg.TargetResolver/  # HmEG-aware — 씬 노드 hit-test/포커스 식별
│   └── Recordingtest.Hmeg.Catalog/      # HmEG-aware — 정적 분석/카탈로그 (현 EngineBridge 일부)
└── Sut/
    └── EgBim/                           # EG-BIM Modeler 전용
        ├── Recordingtest.Sut.EgBim.PluginHost/   # MEF entry, EditorPlugin base 상속
        └── Recordingtest.Sut.EgBim.Adapter/      # AppManager 진입점 / command lifecycle 어댑터
tests/
├── Recordingtest.*.Tests/                       # Generic
├── Hmeg/Recordingtest.Hmeg.*.Tests/             # HmEG-aware
└── Sut/EgBim/Recordingtest.Sut.EgBim.*.Tests/

새 SUT(예: 다른 HmEG 호스트 앱)를 추가할 때:

src/Sut/<NewApp>/
    Recordingtest.Sut.<NewApp>.PluginHost/
    Recordingtest.Sut.<NewApp>.Adapter/

이 두 개만 새로 만들면 되고 Generic + HmEG-aware는 그대로 재사용된다.

현재 모듈 분류 (2026-04-09 시점, 마이그레이션 전)

모듈 분류 비고
Recordingtest.Recorder Generic OK
Recordingtest.Player Generic OK
Recordingtest.Normalizer Generic OK
Recordingtest.DiffReporter Generic OK
Recordingtest.Runner Generic OK
Recordingtest.SutProber Generic OK
Recordingtest.EgPlugin 혼합 — 분할 필요 본체는 EG-BIM 전용(MEF entry), reflection accessor는 HmEG 비의존 generic, HmEG state는 HmEG-aware. 3개로 split.
Recordingtest.EngineBridge HmEG-aware HmEG 카탈로그/CandidateFinder. rename → Recordingtest.Hmeg.Catalog
Recordingtest.EngineBridge.Client 혼합 — 분할 필요 HTTP 호출부 → Generic, HmEgHttpSnapshot → HmEG-aware
IEngineStateProvider (현 EgPlugin 안) Generic Recordingtest.Bridge.Abstractions 로 추출
ReflectionAppManagerAccessor (현 EgPlugin 안) App-specific EG-BIM Modeler의 Editor.AppManager.AppManager를 찾는 코드. EgBim 어댑터로 이동. CI fallback 용도로만 유지.
향후 HmegDirectStateProvider HmEG-aware HmEG.dll 공개 API 직접 호출. 모든 HmEG 호스트 앱에서 재사용

Sprint Contract 의무

새 SUT를 추가하거나 기존 모듈을 generic↔SUT 사이에서 옮기는 모든 작업은 /contract <name> 으로 Sprint Contract를 먼저 작성한다. DoD에 "분류 라벨", "의존 그래프 검증", "네임스페이스 규칙" 항목 필수.

10. 결정 로그 위치

주요 기술 결정과 그 근거는 docs/history/와 Claude 메모리(project_recordingtest_*)에 분산 저장된다. 새 결정 시 반드시 둘 다 갱신한다.