# 2026-04-08 — 이슈 #14 Raw 시나리오 E2E 성공 **이슈**: #14 Raw 레코딩 시나리오를 수동 cleanup 없이 재생 가능하게 **소요 시간**: ~90분 **Context 사용량**: ~60k tokens (Opus 4.6) ## 결과 🎉 **`scenarios/box-v6.yaml` 원본(AI 후처리 없음)** → Player 재생 → **SUT에 Box geometry 생성 확인**. 이전 box-v5-clean.yaml E2E 성공은 AI가 focus 이벤트 40+개 제거, target UIA 경로 리타기팅(ItemsControl→CommandBox), 뷰포트 offset 박음질 등 수작업 후편집의 결과였다. 이번 작업으로 **수작업 없이 recorder 원본을 그대로 재생**하는 경로가 처음으로 열렸다. ## 문제 분해 레코더 원본은 다음 4가지 이유로 재생 불가였다: 1. **Type/Click null target** — recorder가 key/mouse 이벤트 발생 시 focused element를 resolve 못 해 `target: null`로 저장. Player는 "#11에서 null skip" 정책이라 아예 실행 안 함. 2. **Player 실행 시 포커스** — `dotnet run` 한 PowerShell이 foreground라 첫 type("BOX")이 PowerShell로 들어감. 3. **선두 alt+tab 노이즈** — 녹화 시작 시 사용자가 에디터→SUT로 전환하려던 alt+tab 2개가 재생 시 SUT를 오히려 off-foreground로 보냄. 4. **스텝 간 타이밍 없음** — 엔진이 즉시 연속 실행 → SUT가 BOX 명령 → 모서리 픽 전환할 틈 없음. ## 구현 (Player 쪽 포스트프로세싱) ### 1. Null-target fallback (PlayerEngine) - `Type + null target` → 현재 포커스로 그대로 `host.Type()` (SUT CommandBox 포커스 가정) - `Click + null target + raw_coord` → screen-absolute 좌표로 직접 `host.Click()` - 기타 null target → 여전히 skip (안전) - `Step.RawCoord: int[]?` 추가, YAML `raw_coord` 자동 매핑 ### 2. SUT foreground 강제 (UiaPlayerHost.BringSutToForeground) - `_app.GetMainWindow().SetForeground() + Focus()` - 600ms settle (150 → 600으로 상향; 초기 char 드롭 관찰 후) - Program.cs가 engine.Run 이전에 1회 호출 ### 3. 선두 alt+tab 자동 skip (PlayerEngine.Run) - 녹화 startup 노이즈 제거. SUT가 이미 foreground인 상태에서 alt+tab 실행은 오히려 유해. ### 4. 스텝 간 타이밍 복원 (PlayerEngine + IPlayerHost.Delay) - `Step.Ts: long?` 추가 - `PlayerEngineOptions.PreserveTiming` (기본 on) - `ts_i - ts_{i-1}` 를 150ms~3s로 클램프해 host.Delay 호출 - **엔진 내부 `Thread.Sleep` 금지 DoD를 유지하기 위해** 딜레이는 `IPlayerHost.Delay`로 위임. `UiaPlayerHost`만 실제 sleep, `FakePlayerHost`는 기록만. - 첫 스텝도 MinStepDelay 받도록 prevTs 시드 ### 5. 스텝 로그 - `[player] step {i} kind={kind} value={value}` — 라이브 디버깅용 ## 테스트 - `PlayerEngine_NullTarget_SkipsWithoutCalling` → `PlayerEngine_NullTarget_Fallback_Issue14`로 교체 - click(null+raw_coord) → clicks[0] 검증, type(null+value) → types[0] 검증 - click(null, no raw_coord), drag(null) → 여전히 skip - 전체 suite green: 94+ tests (Player 24, Runner 6, Recorder 26, Normalizer 16, ...) ## 라이브 검증 (사용자 환경) ``` dotnet run --project src\Recordingtest.Player -- --scenario scenarios\box-v6.yaml --output-dir artifacts\replay-v6-raw --no-launch ``` 첫 시도는 BOX 타이핑이 누락 (`[player] step 2 kind=Type value=BOX` 로그는 찍혔지만 SUT command box에 안 들어감). 두 번째 시도에서 Box geometry 생성 성공. ## 남은 과제 (PLAN.md P1에 등록) - **foreground settle 경계선 문제** — 600ms가 가끔 부족. `SetForegroundWindow` 후 능동 대기(`GetForegroundWindow == sut_hwnd` polling) 또는 첫 type 이전에 Keyboard warm-up 필요. - **recorder Gap I-1** — null_target_steps를 근본적으로 줄이려면 recorder가 key_down 시점에 `Automation.FocusedElement`를 직접 쿼리해서 typeRes를 채워야 함. 현재는 player fallback으로 우회 중. ## 관련 커밋 - (pending) Player null-target fallback + foreground + alt+tab strip + timing preservation