2026-04-07 smoke test 1회차 (docs/history/2026-04-07_smoke-1회차-결과.md) 결과, recorder까지는 정상 동작하나 player 재생 시 Box 명령이 재현되지 않음. 4개의 구조적 gap 발견.
해결할 Gap
Gap A (치명) — player UiaPlayerHost.ResolveElement AutomationId-only 매칭
현재 코드 (src/Recordingtest.Player/UiaPlayerHost.cs:135):
privatestaticstringExtractAutomationId(stringuiaPath){// Look for [@AutomationId='...'] in the LAST segment.varmarker="@AutomationId='";varidx=uiaPath.LastIndexOf(marker,StringComparison.Ordinal);...}
→ MetroWindow[@AutomationId='root']/ItemsControl 같이 마지막 segment에 AutomationId가 없는 path는 빈 문자열로 FindFirstDescendant를 호출 → 첫 descendant 아무거나 잡음 → 엉뚱한 element bounds로 offset 적용 → 잘못된 좌표 클릭.
수정 요구:
uia_path를 /로 분리해 ancestor chain 전체를 따라 descend
각 segment에서 ClassName, AutomationId, Name 모두 활용해 매칭
최종 element를 못 찾으면 명확히 throw (현재 false positive)
Gap B (치명) — recorder type 스텝 target 미설정
DragCollapser.FlushType()이 typeRes가 null이면 target을 빈 채로 emit. typeRes는 key event resolver skip 정책 때문에 항상 null.
수정 요구:
가장 최근 focus_change RawEvent의 FocusedElementPath를 type 스텝의 target에 자동 상속
Or: 가장 최근 mouse_down 이벤트의 resolved element 재사용
또는 두 전략 fallback: focus_change 우선 → mouse_down → null
이렇게 하면 player가 type 스텝을 skip하지 않고 정확한 element에 키 입력 보낼 수 있음.
Gap C (부수) — recorder가 SUT 외 창 이벤트 캡처
Win32 hook은 시스템 전역. 사용자 alt+tab 시 다른 창 path 섞임. 옵션:
--filter-window-handle <hwnd> 인자로 attach된 SUT의 hwnd를 받아 hook proc에서 WindowFromPoint 검사
Or: collapser가 SUT 메인 윈도우 path가 아닌 step을 drop
Gap D (부수) — yaml 한글 PowerShell 출력 시 인코딩 깨짐
Get-Content로 볼 때 한글이 cp949로 보임. 파일 자체는 UTF-8일 가능성. 검증 필요. ScenarioWriter가 BOM을 쓰는지 확인.
사이클
Generator (4개 gap 동시 작업, 단위 테스트 + integration regression test 포함)
## 배경
2026-04-07 smoke test 1회차 (`docs/history/2026-04-07_smoke-1회차-결과.md`) 결과, recorder까지는 정상 동작하나 player 재생 시 Box 명령이 재현되지 않음. 4개의 구조적 gap 발견.
## 해결할 Gap
### Gap A (치명) — player `UiaPlayerHost.ResolveElement` AutomationId-only 매칭
현재 코드 (`src/Recordingtest.Player/UiaPlayerHost.cs:135`):
```csharp
private static string ExtractAutomationId(string uiaPath)
{
// Look for [@AutomationId='...'] in the LAST segment.
var marker = "@AutomationId='";
var idx = uiaPath.LastIndexOf(marker, StringComparison.Ordinal);
...
}
```
→ `MetroWindow[@AutomationId='root']/ItemsControl` 같이 마지막 segment에 AutomationId가 없는 path는 빈 문자열로 `FindFirstDescendant`를 호출 → 첫 descendant 아무거나 잡음 → 엉뚱한 element bounds로 offset 적용 → 잘못된 좌표 클릭.
**수정 요구**:
- `uia_path`를 `/`로 분리해 ancestor chain 전체를 따라 descend
- 각 segment에서 `ClassName`, `AutomationId`, `Name` 모두 활용해 매칭
- 최종 element를 못 찾으면 명확히 throw (현재 false positive)
### Gap B (치명) — recorder type 스텝 target 미설정
`DragCollapser.FlushType()`이 typeRes가 null이면 target을 빈 채로 emit. typeRes는 key event resolver skip 정책 때문에 항상 null.
**수정 요구**:
- 가장 최근 `focus_change` RawEvent의 `FocusedElementPath`를 type 스텝의 target에 자동 상속
- Or: 가장 최근 mouse_down 이벤트의 resolved element 재사용
- 또는 두 전략 fallback: focus_change 우선 → mouse_down → null
이렇게 하면 player가 type 스텝을 skip하지 않고 정확한 element에 키 입력 보낼 수 있음.
### Gap C (부수) — recorder가 SUT 외 창 이벤트 캡처
Win32 hook은 시스템 전역. 사용자 alt+tab 시 다른 창 path 섞임. 옵션:
- `--filter-window-handle <hwnd>` 인자로 attach된 SUT의 hwnd를 받아 hook proc에서 `WindowFromPoint` 검사
- Or: collapser가 SUT 메인 윈도우 path가 아닌 step을 drop
### Gap D (부수) — yaml 한글 PowerShell 출력 시 인코딩 깨짐
`Get-Content`로 볼 때 한글이 cp949로 보임. 파일 자체는 UTF-8일 가능성. 검증 필요. ScenarioWriter가 BOM을 쓰는지 확인.
## 사이클
1. Generator (4개 gap 동시 작업, 단위 테스트 + integration regression test 포함)
2. Evaluator
3. Smoke 2회차
## 비용 추정
~200~300k tokens
Related: #2, #6, #7, #11
SutProcessWindowFilter + WindowFilter.cs. 마우스 WindowFromPoint, 키는 GetForegroundWindow. Program.cs가 app.ProcessId로 wire
D — UTF-8 BOM-less
ScenarioWriter가 new UTF8Encoding(false) 명시. round-trip 테스트가 BOM 부재 + 한글 보존 확인
Evaluator DoD
항목
결과
Build
pass (0 warn / 0 err)
Tests
pass 71/71 (60 → 71)
Gap A 실구현
pass (신규 3 파일 + rewire)
Gap B 실구현
pass (상태 + fallback chain)
Gap C 실구현
pass (WindowFromPoint + GetForegroundWindow)
Gap D 실구현
pass (BOM 부재 assertion)
Regression trap
pass (새 타입은 compile trap, 나머지는 behavioral assertion)
PlayerEngine Thread.Sleep
0
SUT 폴더 쓰기
없음
남은 honest gap (non-blocking)
UiaPathResolver bounded descendant fallback은 휴리스틱, smoke 2차에서 튜닝 필요
Win32 키 필터는 GetForegroundWindow only — 빠른 alt+tab 전환 시 race 가능성
focus_change 이벤트는 filter 우회 (UIA 콜백이 이미 SUT-scoped)
UiaPlayerHost는 GetMainWindow를 root로 사용 — 멀티윈도우 SUT 미지원
라이브 SUT 미실행 (샌드박스 제약) — 실환경 검증은 smoke 2회차에서
비용
단계
Tokens
Generator
~92k
Evaluator
~58k
Orchestrator 분담
~15k
합계
~165k
다음 단계
Smoke 2회차 — 사용자 환경에서 recorder (PID attach) → 짧은 Box 시나리오 → player 재생. 이번엔 click이 정확한 element를 잡고, type 스텝이 focus_change 상속으로 target 채워지고, 외부 창 이벤트가 필터되어 깔끔한 yaml이 나와야 함.
closing.
## Smoke 2차 gap fix — Verdict: **pass** ✅
### Generator (`8784fec`)
4개 Gap 모두 구조적 fix:
| Gap | 구현 |
|-----|------|
| A — player full-path resolver | `UiaPathParser.cs` + `IUiaPathResolver.cs` + `UiaPathResolver` 신규. ancestor chain 따라 descend, id→name→class 우선순위, 누락 중간 wrapper 대비 bounded descendant fallback (depth 4). `UiaPlayerHost.ResolveElement` rewire, miss 시 null 반환 |
| B — recorder type target inheritance | `DragCollapser`에 `lastFocusPath`/`lastMousePath` 상태 추가. `FlushType` fallback chain: `typeRes ?? focus ?? mouse` |
| C — window filter | `SutProcessWindowFilter` + `WindowFilter.cs`. 마우스 `WindowFromPoint`, 키는 `GetForegroundWindow`. `Program.cs`가 `app.ProcessId`로 wire |
| D — UTF-8 BOM-less | `ScenarioWriter`가 `new UTF8Encoding(false)` 명시. round-trip 테스트가 BOM 부재 + 한글 보존 확인 |
### Evaluator DoD
| 항목 | 결과 |
|------|------|
| Build | pass (0 warn / 0 err) |
| Tests | pass **71/71** (60 → 71) |
| Gap A 실구현 | pass (신규 3 파일 + rewire) |
| Gap B 실구현 | pass (상태 + fallback chain) |
| Gap C 실구현 | pass (WindowFromPoint + GetForegroundWindow) |
| Gap D 실구현 | pass (BOM 부재 assertion) |
| Regression trap | pass (새 타입은 compile trap, 나머지는 behavioral assertion) |
| PlayerEngine Thread.Sleep | 0 |
| SUT 폴더 쓰기 | 없음 |
### 남은 honest gap (non-blocking)
- `UiaPathResolver` bounded descendant fallback은 휴리스틱, smoke 2차에서 튜닝 필요
- Win32 키 필터는 `GetForegroundWindow` only — 빠른 alt+tab 전환 시 race 가능성
- `focus_change` 이벤트는 filter 우회 (UIA 콜백이 이미 SUT-scoped)
- `UiaPlayerHost`는 `GetMainWindow`를 root로 사용 — 멀티윈도우 SUT 미지원
- 라이브 SUT 미실행 (샌드박스 제약) — 실환경 검증은 smoke 2회차에서
### 비용
| 단계 | Tokens |
|------|--------|
| Generator | ~92k |
| Evaluator | ~58k |
| Orchestrator 분담 | ~15k |
| **합계** | **~165k** |
### 다음 단계
**Smoke 2회차** — 사용자 환경에서 recorder (PID attach) → 짧은 Box 시나리오 → player 재생. 이번엔 click이 정확한 element를 잡고, type 스텝이 focus_change 상속으로 target 채워지고, 외부 창 이벤트가 필터되어 깔끔한 yaml이 나와야 함.
closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
배경
2026-04-07 smoke test 1회차 (
docs/history/2026-04-07_smoke-1회차-결과.md) 결과, recorder까지는 정상 동작하나 player 재생 시 Box 명령이 재현되지 않음. 4개의 구조적 gap 발견.해결할 Gap
Gap A (치명) — player
UiaPlayerHost.ResolveElementAutomationId-only 매칭현재 코드 (
src/Recordingtest.Player/UiaPlayerHost.cs:135):→
MetroWindow[@AutomationId='root']/ItemsControl같이 마지막 segment에 AutomationId가 없는 path는 빈 문자열로FindFirstDescendant를 호출 → 첫 descendant 아무거나 잡음 → 엉뚱한 element bounds로 offset 적용 → 잘못된 좌표 클릭.수정 요구:
uia_path를/로 분리해 ancestor chain 전체를 따라 descendClassName,AutomationId,Name모두 활용해 매칭Gap B (치명) — recorder type 스텝 target 미설정
DragCollapser.FlushType()이 typeRes가 null이면 target을 빈 채로 emit. typeRes는 key event resolver skip 정책 때문에 항상 null.수정 요구:
focus_changeRawEvent의FocusedElementPath를 type 스텝의 target에 자동 상속이렇게 하면 player가 type 스텝을 skip하지 않고 정확한 element에 키 입력 보낼 수 있음.
Gap C (부수) — recorder가 SUT 외 창 이벤트 캡처
Win32 hook은 시스템 전역. 사용자 alt+tab 시 다른 창 path 섞임. 옵션:
--filter-window-handle <hwnd>인자로 attach된 SUT의 hwnd를 받아 hook proc에서WindowFromPoint검사Gap D (부수) — yaml 한글 PowerShell 출력 시 인코딩 깨짐
Get-Content로 볼 때 한글이 cp949로 보임. 파일 자체는 UTF-8일 가능성. 검증 필요. ScenarioWriter가 BOM을 쓰는지 확인.사이클
비용 추정
200300k tokensRelated: #2, #6, #7, #11
Smoke 2차 gap fix — Verdict: pass ✅
Generator (
8784fec)4개 Gap 모두 구조적 fix:
UiaPathParser.cs+IUiaPathResolver.cs+UiaPathResolver신규. ancestor chain 따라 descend, id→name→class 우선순위, 누락 중간 wrapper 대비 bounded descendant fallback (depth 4).UiaPlayerHost.ResolveElementrewire, miss 시 null 반환DragCollapser에lastFocusPath/lastMousePath상태 추가.FlushTypefallback chain:typeRes ?? focus ?? mouseSutProcessWindowFilter+WindowFilter.cs. 마우스WindowFromPoint, 키는GetForegroundWindow.Program.cs가app.ProcessId로 wireScenarioWriter가new UTF8Encoding(false)명시. round-trip 테스트가 BOM 부재 + 한글 보존 확인Evaluator DoD
남은 honest gap (non-blocking)
UiaPathResolverbounded descendant fallback은 휴리스틱, smoke 2차에서 튜닝 필요GetForegroundWindowonly — 빠른 alt+tab 전환 시 race 가능성focus_change이벤트는 filter 우회 (UIA 콜백이 이미 SUT-scoped)UiaPlayerHost는GetMainWindow를 root로 사용 — 멀티윈도우 SUT 미지원비용
다음 단계
Smoke 2회차 — 사용자 환경에서 recorder (PID attach) → 짧은 Box 시나리오 → player 재생. 이번엔 click이 정확한 element를 잡고, type 스텝이 focus_change 상속으로 target 채워지고, 외부 창 이벤트가 필터되어 깔끔한 yaml이 나와야 함.
closing.