docs: refresh README for 3-tier architecture

- 3-tier dependency direction diagram + architecture rule
- Module table reorganized by tier (Generic / HmEG-aware / EgBim)
- Milestones: first E2E, raw scenario E2E (#14), 3-tier split, 126 tests
- Gap status (A~H done, Gap I deferred with Player fallback strategy)
- Directory tree updated to reflect post-refactor layout

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-09 11:06:50 +09:00
parent 03fb504eea
commit 9fe053619f
2 changed files with 94 additions and 11 deletions

View File

@@ -12,18 +12,45 @@
[회귀 시점] → 입력 리플레이 → 결과 파일 B → normalize → diff(A, B) [회귀 시점] → 입력 리플레이 → 결과 파일 B → normalize → diff(A, B)
``` ```
## 3계층 아키텍처
본 도구는 **EG-BIM Modeler 외 다양한 WPF 응용**을 대상으로 하고, 사용자 WPF 응용군 대다수가 자체 3D 엔진 **HmEG**를 공유한다. 따라서 코드는 엄격히 3계층으로 분리된다 (의존 방향 단방향, 자세한 규칙은 [CLAUDE.md §8.1](CLAUDE.md)):
```
App-specific (e.g. EgBim) ──→ HmEG-aware ──→ Generic
(특정 앱만) (HmEG 호스팅 앱 공통) (임의 WPF 앱)
```
`Recordingtest.Architecture.Tests``Assembly.GetReferencedAssemblies()` 검사로 빌드 시점에 규칙을 강제한다. 새 SUT를 추가할 때는 App-specific 계층에 플러그인 진입점 + 어댑터만 작성하고 Generic + HmEG-aware 전부 재사용.
## 모듈 구성 ## 모듈 구성
### Generic tier — 임의 WPF 응용
| 모듈 | 책임 | 상태 | | 모듈 | 책임 | 상태 |
|------|------|------| |------|------|------|
| [Recordingtest.Bridge.Abstractions](src/Recordingtest.Bridge.Abstractions/) | `IEngineStateProvider`/`CameraSnapshot`/`SceneSnapshot` 등 SUT-중립 인터페이스 | ✓ |
| [Recordingtest.SutProber](src/Recordingtest.SutProber/) | SUT 정적 probe (plugin/Json/assembly 카탈로그) | PoC pass | | [Recordingtest.SutProber](src/Recordingtest.SutProber/) | SUT 정적 probe (plugin/Json/assembly 카탈로그) | PoC pass |
| [Recordingtest.Recorder](src/Recordingtest.Recorder/) | 입력 캡처 (UIA element path + offset + 키/마우스/포커스) | PoC pass | | [Recordingtest.Recorder](src/Recordingtest.Recorder/) | 입력 캡처 (UIA element path + offset + 키/마우스/포커스) | PoC pass |
| [Recordingtest.Player](src/Recordingtest.Player/) | 시나리오 재생, 비동기 동기화 | PoC pass | | [Recordingtest.Player](src/Recordingtest.Player/) | 시나리오 재생, 비동기 동기화, null-target fallback | PoC pass |
| [Recordingtest.Normalizer](src/Recordingtest.Normalizer/) | 결과 파일 정규화 (timestamp/GUID/path/float/order) | PoC pass | | [Recordingtest.Normalizer](src/Recordingtest.Normalizer/) | 결과 파일 정규화 (timestamp/GUID/path/float/order) | PoC pass |
| [Recordingtest.DiffReporter](src/Recordingtest.DiffReporter/) | approved vs received diff 리포트 | PoC pass | | [Recordingtest.DiffReporter](src/Recordingtest.DiffReporter/) | approved vs received diff 리포트 | PoC pass |
| [Recordingtest.EngineBridge.Client](src/Recordingtest.EngineBridge.Client/) + [Recordingtest.EgPlugin](src/Recordingtest.EgPlugin/) | HmEG 내부 상태 sidecar (MEF plugin masquerade + HttpListener) | v2 pass |
| [Recordingtest.Runner](src/Recordingtest.Runner/) | 5-모듈 E2E 파이프라인 + 실패 triage | PoC pass | | [Recordingtest.Runner](src/Recordingtest.Runner/) | 5-모듈 E2E 파이프라인 + 실패 triage | PoC pass |
### HmEG-aware tier — HmEG 호스팅 앱 공통
| 모듈 | 책임 | 상태 |
|------|------|------|
| [Recordingtest.Hmeg.Bridge](src/Hmeg/Recordingtest.Hmeg.Bridge/) | `HmegDirectStateProvider` — HmEG 공개 API(Space/HmEGViewport/ISelectable)로 상태 읽기. 앱에 람다로 진입점 주입 | v3 wired |
| [Recordingtest.Hmeg.Catalog](src/Hmeg/Recordingtest.Hmeg.Catalog/) | HmEG 정적 분석 카탈로그 | PoC pass |
| [Recordingtest.Hmeg.Bridge.Client](src/Hmeg/Recordingtest.Hmeg.Bridge.Client/) | `/scene` `/camera` `/selection` HTTP 클라이언트 | v2 pass |
### App-specific tier — 현재 SUT: EG-BIM Modeler
| 모듈 | 책임 | 상태 |
|------|------|------|
| [Recordingtest.Sut.EgBim.PluginHost](src/Sut/EgBim/Recordingtest.Sut.EgBim.PluginHost/) | MEF 진입점(`EditorPlugin` 상속), `BridgeHttpServer` 부팅, `HmegDirectStateProvider` 람다에 `RootSpace`/`View`/`AppManager.FileManager.CurrentFile` 주입 | v3 wired |
## 작업 사이클 — Planner / Generator / Evaluator ## 작업 사이클 — Planner / Generator / Evaluator
Anthropic harness design 원칙을 채택. 같은 에이전트가 생성과 평가를 겸하지 않는다. Anthropic harness design 원칙을 채택. 같은 에이전트가 생성과 평가를 겸하지 않는다.
@@ -39,19 +66,51 @@ Anthropic harness design 원칙을 채택. 같은 에이전트가 생성과 평
- **시나리오 포맷**: YAML/JSON (git diff 친화적) - **시나리오 포맷**: YAML/JSON (git diff 친화적)
- **베이스라인**: `*.approved.{ext}` / `*.received.{ext}` - **베이스라인**: `*.approved.{ext}` / `*.received.{ext}`
## 주요 이정표
- **2026-04-08 — 첫 E2E 성공** 🎉 Smoke 2회차에서 수동 테스트 시나리오 → 재생 → SUT에 Box geometry 생성 확인 (box-v5-clean.yaml).
- **2026-04-08 — Raw 시나리오 E2E 성공** 🎉 수동 cleanup 없이 recorder 원본 `box-v6.yaml`이 그대로 재생되어 Box 생성 (이슈 #14). Player에 null-target fallback, SUT foreground 능동 대기, 선두 alt+tab 노이즈 자동 skip, 스텝 간 타이밍 보존 추가.
- **2026-04-09 — 3-tier 분리 완료** — Generic / HmEG-aware / App-specific 물리적 분리, `Recordingtest.Architecture.Tests`가 의존 그래프 강제. 같은 날 engine-bridge v3 EgBim 람다 실 wire-up (코드 쪽 완료, 라이브 검증 대기).
- **126 단위 테스트** green (Recorder/Player/Normalizer/DiffReporter/Runner/Hmeg.* /Sut.EgBim.* /Architecture)
자세한 과정은 [docs/history/](docs/history/), 결정 근거는 [docs/contracts/](docs/contracts/) + [docs/hmeg-api-survey.md](docs/hmeg-api-survey.md) 참고.
## Gap 현황 (주요)
- **Gap A~H** (smoke 1~2회차) — 전부 fix (#11/#12/#13/#14)
- **Gap I (recorder root-cause)** — EG-BIM Modeler의 CommandBox 등 핵심 입력 컨트롤이 AutomationPeer를 노출하지 않아 UIA 외부에서 본질적으로 식별 불가. **deferred**. 현재는 Player null-target fallback(Type→OS 포커스 / Click→raw_coord)이 공식 우회 전략. 근본 해소는 generic WPF DLL injection 또는 SUT-side AutomationPeer 부착 PoC가 선결.
## 디렉터리 ## 디렉터리
``` ```
recordingtest/ recordingtest/
├── src/ # 모듈별 C# 프로젝트 ├── src/
├── scenarios/ # 시나리오 YAML │ ├── Recordingtest.Bridge.Abstractions/ # Generic — 인터페이스
│ ├── Recordingtest.Recorder/ # Generic
│ ├── Recordingtest.Player/ # Generic
│ ├── Recordingtest.Normalizer/ # Generic
│ ├── Recordingtest.DiffReporter/ # Generic
│ ├── Recordingtest.Runner/ # Generic
│ ├── Recordingtest.SutProber/ # Generic
│ ├── Hmeg/
│ │ ├── Recordingtest.Hmeg.Bridge/ # HmEG-aware
│ │ ├── Recordingtest.Hmeg.Catalog/ # HmEG-aware
│ │ ├── Recordingtest.Hmeg.Bridge.Client/ # HmEG-aware
│ │ └── Recordingtest.Hmeg.Catalog.Probe/ # HmEG-aware CLI
│ └── Sut/
│ └── EgBim/
│ └── Recordingtest.Sut.EgBim.PluginHost/ # App-specific
├── tests/ # 같은 계층 구조로 미러링 + Architecture.Tests
├── scenarios/ # 시나리오 YAML (box-v*.yaml)
├── docs/ ├── docs/
│ ├── contracts/ # Sprint Contracts + evaluations │ ├── contracts/ # Sprint Contracts + evaluations
│ ├── history/ # 작업 히스토리 │ ├── history/ # 작업 히스토리
│ ├── sut-catalog/ # sut-prober 산출물 │ ├── sut-catalog/ # sut-prober 산출물
│ ├── engine-catalog/ # HmEG 후보 카탈로그 (정적 분석)
│ ├── hmeg-api-survey.md # HmEG public API 조사 메모
│ └── guides/ # smoke test, deploy 가이드 │ └── guides/ # smoke test, deploy 가이드
├── CLAUDE.md # 에이전트 운영 지침 ├── CLAUDE.md # 에이전트 운영 지침 + §8.1 3-tier 규칙
├── PROGRESS.md # 완료 상태 ├── PROGRESS.md # 완료 상태 (세션 간 공유 메모리)
└── PLAN.md # 우선순위 큐 └── PLAN.md # 우선순위 큐
``` ```

View File

@@ -0,0 +1,24 @@
# 2026-04-09 — README 갱신 + 누적 커밋 push
**이슈**: 없음 (세션 마무리 문서 작업)
**소요 시간**: ~5분
**Context 사용량**: input ~15k / output ~3k tokens (Opus 4.6)
## 작업
- `README.md`를 3-tier 아키텍처 반영해 전면 갱신
- 3계층(Generic / HmEG-aware / App-specific) 의존 방향 다이어그램 + 강제 규칙
- 모듈 표를 계층별로 재구성 (12개 csproj, 이동 후 경로)
- 주요 이정표 섹션: 2026-04-08 첫 E2E, Raw 시나리오 E2E, 2026-04-09 3-tier 분리, 126 tests
- Gap 현황 (A~H 완료, Gap I deferred + Player fallback 전략)
- 디렉터리 트리를 실제 3-tier 구조로 갱신
- 누적 5개 커밋을 origin으로 push:
- `70bf570` player: raw scenario replay without manual cleanup (#14)
- `98d8014` player: active foreground wait
- `a771352` recorder: focus poller PoC for Gap I-1 (deferred)
- `f6b6e74` 3-tier split (step 1) + engine-bridge v3 scaffold
- `03fb504` BREAKING: 3-tier split step 2 + engine-bridge v3 EgBim lambdas wired
## 관련
- `README.md`