Files
recordingtest/docs/history/2026-04-08_이슈14-raw-시나리오-e2e-성공.md
minsung 70bf5703b3 player: raw scenario replay without manual cleanup (#14)
First time box-v6.yaml (raw recorder output, 676 lines) replays end-to-end
and actually creates a Box in the SUT — no AI post-editing of target paths
or offsets required. This is the counterpart to #13's recorder-side fixes:
the player now absorbs the remaining record→replay gaps instead of demanding
a hand-cleaned scenario.

Changes (all in Recordingtest.Player):

- PlayerEngine: null-target fallbacks
  - Type with null target → host.Type() against current focus
  - Click with null target + raw_coord → click at screen-absolute raw_coord
  - Other null targets still skipped
- PlayerEngine: strip leading alt+tab hotkey steps (recording-startup noise
  that fights the player's own foreground switch)
- PlayerEngine: preserve recorded inter-step timing, clamped 150ms–3s,
  routed through new IPlayerHost.Delay so the engine itself stays Sleep-free
  (keeps the existing "no fixed sleep" DoD test passing)
- PlayerEngine: per-step console log for live debugging
- UiaPlayerHost: BringSutToForeground() — SetForeground + Focus + 600ms
  settle, called from Program.cs before engine.Run
- Step model: add RawCoord (int[]) and Ts (long?) fields, auto-mapped from
  YAML raw_coord / ts keys

Tests updated:
- PlayerEngine_NullTarget_SkipsWithoutCalling → _Fallback_Issue14
  (verifies the new Click-with-raw_coord and Type behavior)
- FakePlayerHost (both player.tests and runner.tests) implement Delay

Live smoke: box-v6.yaml raw replay produced the expected Box geometry on
the 2nd attempt; 1st attempt dropped the initial "BOX" keystrokes, tracked
as a follow-up (foreground settle is still threshold-sensitive at 600ms).

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

4.0 KiB

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_SkipsWithoutCallingPlayerEngine_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