From de0ca9876ab97b572a16921658d1e96149648c0e Mon Sep 17 00:00:00 2001 From: minsung Date: Tue, 7 Apr 2026 20:34:57 +0900 Subject: [PATCH] =?UTF-8?q?Orchestrate=20smoke=202=EC=B0=A8=20gap=20fix=20?= =?UTF-8?q?evaluation=20+=20close=20#12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 4 gaps (player resolver, type target, window filter, UTF-8 BOM-less) all pass - 71/71 tests, regression traps verified - Ready for smoke 2회차 live validation Co-Authored-By: Claude Opus 4.6 (1M context) --- PROGRESS.md | 3 +- docs/contracts/smoke2-gap-fix.evaluation.md | 49 +++++++++++++++++++ docs/history/2026-04-07_readme-작성.md | 9 ++++ .../2026-04-07_이슈12-smoke2-fix-evaluator.md | 32 ++++++++++++ ...6-04-07_이슈12-smoke2-fix-orchestration.md | 33 +++++++++++++ 5 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 docs/contracts/smoke2-gap-fix.evaluation.md create mode 100644 docs/history/2026-04-07_readme-작성.md create mode 100644 docs/history/2026-04-07_이슈12-smoke2-fix-evaluator.md create mode 100644 docs/history/2026-04-07_이슈12-smoke2-fix-orchestration.md diff --git a/PROGRESS.md b/PROGRESS.md index 18ecfb4..f07a8c4 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -38,10 +38,11 @@ | 2026-04-07 | Smoke test 1회차 — integration gap 4개 발견 (recorder target null, VK 코드, player enum, null guard) | `scenarios/box-create.yaml` | | 2026-04-07 | Smoke gap fix + Evaluator pass (#11) — STAThread, KeyTranslator, 60 tests, regression trap 검증 | commit `139fbbc` | | 2026-04-07 | Smoke test 1회차 — recorder PID attach + UIA target 정상 (box-v4), player 재생 부분 실패 | `docs/history/2026-04-07_smoke-1회차-결과.md`, scenarios/box-v4*.yaml | +| 2026-04-07 | Smoke 2차 gap fix + Evaluator pass (#12) — full-path resolver, type target inheritance, window filter, UTF-8 BOM-less, 71 tests | commit `8784fec` | ## In progress -- 이슈 #12 — Smoke 1회차 후속 gap fix (player resolver + recorder type target + 부수) +_(없음 — Smoke 2회차 라이브 검증 대기)_ ## In progress diff --git a/docs/contracts/smoke2-gap-fix.evaluation.md b/docs/contracts/smoke2-gap-fix.evaluation.md new file mode 100644 index 0000000..c5fbecd --- /dev/null +++ b/docs/contracts/smoke2-gap-fix.evaluation.md @@ -0,0 +1,49 @@ +# Evaluation — smoke2 gap fix (issue #12) + +- Commit graded: `8784fec` +- Evaluator: independent session (Opus 4.6 [1m]) +- Date: 2026-04-07 +- Note: Issue #12 used a free-form issue body instead of a Sprint Contract + (`docs/contracts/smoke2-gap-fix.md` does not exist). Acceptable per + CLAUDE.md §0.1 for follow-up bug fixes, but recorded here. + +## Verdict: **PASS** + +## Verdict table + +| Item | Required | Observed | Status | +|---|---|---|---| +| Build | 0 warn / 0 err (TWAE) | Clean, 0/0 | pass | +| Tests total | 71 pass / 0 fail / 0 skip | 71 pass / 0 fail / 0 skip (16+10+17+5+5+6+6+6) | pass | +| Gap A — full path resolver | UiaPathParser splits `/`, parses `(Class, AutomationId?, Name?)`; `IUiaTreeNode` adapter; descend chain id→name→class; bounded fallback; UiaPlayerHost wired; null on miss | All present. `UiaPathParser.cs` quote-aware split, attribute parser. `IUiaPathResolver.cs` defines `IUiaTreeNode` + `UiaPathResolver` with `MatchRoot` + `FindChild` + `DescendantsBounded(maxDepth:4)` documented fallback. `Matches` priority AutomationId > Name > ClassName. `UiaPlayerHost.ResolveElement` uses `UiaPathResolver.Resolve(new FlaUiTreeNode(window), uiaPath)` via `Retry.WhileNull`, returns null on miss (engine handles throw). Old `ExtractAutomationId` shortcut removed. | pass | +| Gap B — type target inheritance | `_lastFocusPath` / `_lastMousePath` state; FlushType fallback chain (typeRes → focus → mouse) | `DragCollapser` adds `lastFocusPath` + `lastMousePath` locals (line 46-47); `FlushType` fallback `typeRes ?? lastFocusPath ?? lastMousePath` (line 72); focus_change updates `lastFocusPath` (line 332); mouse_down_l/r updates `lastMousePath` from downRes (line 128, 188). | pass | +| Gap C — window filter | `ShouldKeep`; mouse uses `WindowFromPoint`; key uses `GetForegroundWindow`; wired to SUT pid in Program | `WindowFilter.cs`: `IWindowFilter`, `PassThroughWindowFilter`, `SutProcessWindowFilter` with two pluggable lookups. Mouse path → `processFromPoint`, key path → `processFromForeground`, focus_change always kept, pid==0 permissive. `Program.cs` lines 87-107 wires `SutProcessWindowFilter` to `app.ProcessId` using `WindowFromPoint`+`GetWindowThreadProcessId` and `GetForegroundWindow`. `LowLevelHook` exposes mutable `Filter` and applies it in both Keyboard/Mouse procs. | pass | +| Gap D — UTF-8 BOM-less | Explicit `UTF8Encoding(false)`; round-trip test with Korean strings + no BOM | `ScenarioWriter.WriteToFile` line 43-44: `new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)`. Round-trip test `ScenarioWriter_RoundTrip_PreservesKorean` asserts byte[0..2] != EF BB BF and Korean Name/Description/Path round-trip. | pass | +| Thread.Sleep in PlayerEngine | 0 | 0 (grep) | pass | +| EG-BIM Modeler writes | none | none | pass | + +## Regression-trap analysis + +| New test | Would have failed pre-fix? | Why | +|---|---|---| +| `UiaPathParser_ParsesMultiSegment_WithClassAndId` | yes — `UiaPathParser` did not exist | Compile-trap | +| `UiaPathParser_ParsesNameAttribute` | yes | Compile-trap | +| `UiaPathResolver_Descend_FindsNestedElement` | yes | Compile-trap; also exercises chain descent | +| `UiaPathResolver_LastSegmentWithoutId_UsesClassName` | yes | Validates ClassName fallback in `Matches` | +| `UiaPathResolver_NotFound_ReturnsNull` | yes | Validates null-not-throw contract | +| `SmokeRegression_BoxV4CleanLike_ParsesAndResolves` | yes — explicitly proves the resolver no longer collapses to "first descendant" (Assert.NotSame) | Direct guard against the smoke 1차 bug | +| `DragCollapser_TypeAfterFocusChange_InheritsTarget` | yes — pre-fix `FlushType` only used `typeRes`; with all-null resolver result, `Target` would be null. Test asserts `Target.UiaPath == focusPath`. | Direct guard for Gap B | +| `DragCollapser_TypeAfterMouseDown_FallbackToMouseTarget` | yes — same reason; asserts mouse path inheritance | Direct guard | +| `WindowFilter_ExternalCoord_DropsEvent` | yes — `SutProcessWindowFilter` did not exist | Compile-trap; also asserts drop semantics | +| `WindowFilter_SutCoord_KeepsEvent` | yes | Compile-trap; asserts keep + permissive pid=0 | +| `ScenarioWriter_RoundTrip_PreservesKorean` | yes — pre-fix relied on default overload; the explicit byte-level `EF BB BF` assertion + Korean round-trip would only deterministically pass with the explicit encoder | Direct guard for Gap D | + +All new tests are load-bearing. + +## Notes + +- `docs/guides/smoke-test.md` Gap D tip section confirmed (commit diff shows +18 lines). +- No partial hacks observed; all four gaps are real code-level fixes with + unit coverage and reasonable abstractions (pluggable lookups for the + filter, pure adapter for the path resolver). +- Recommend Generator update PROGRESS.md to mark issue #12 done. diff --git a/docs/history/2026-04-07_readme-작성.md b/docs/history/2026-04-07_readme-작성.md new file mode 100644 index 0000000..2951930 --- /dev/null +++ b/docs/history/2026-04-07_readme-작성.md @@ -0,0 +1,9 @@ +# README 작성 및 push + +- 일시: 2026-04-07 +- 소요 시간: ~3분 +- Context 사용량: ~5% + +## 변경 +- `README.md`를 한 줄 placeholder에서 프로젝트 개요(전략/모듈표/사이클/스택/디렉터리)로 확장 +- commit `3738a0d`, push to origin/main diff --git a/docs/history/2026-04-07_이슈12-smoke2-fix-evaluator.md b/docs/history/2026-04-07_이슈12-smoke2-fix-evaluator.md new file mode 100644 index 0000000..4ac0a47 --- /dev/null +++ b/docs/history/2026-04-07_이슈12-smoke2-fix-evaluator.md @@ -0,0 +1,32 @@ +# 이슈 #12 — smoke 2차 gap fix evaluator + +- 일자: 2026-04-07 +- 역할: Evaluator (독립 세션) +- 관련 이슈: #12 +- 평가 대상 커밋: `8784fec` +- 결과: **PASS** + +## 소요 시간 +약 6분 + +## Context 사용량 +약 55k tokens (Opus 4.6 [1m]) + +## 수행 내역 +1. `dotnet build recordingtest.sln` → 0 warn / 0 err +2. `dotnet test recordingtest.sln --no-build` → 71 pass / 0 fail / 0 skip +3. Gap A: `UiaPathParser.cs`, `IUiaPathResolver.cs`, `UiaPlayerHost.cs` 직접 read — 분리된 파서/리졸버, FlaUI 어댑터, null-on-miss 확인 +4. Gap B: `DragCollapser.cs` — `lastFocusPath`/`lastMousePath` 상태 + `FlushType` fallback 체인 확인 +5. Gap C: `WindowFilter.cs`, `LowLevelHook.cs`, `Program.cs` — `SutProcessWindowFilter` 구현 + 마우스/키 분리 + Program 와이어링 확인 +6. Gap D: `ScenarioWriter.cs` — 명시적 `UTF8Encoding(false)` 확인; 라운드트립 테스트의 BOM 바이트 단언 확인 +7. `PlayerEngine.cs` `Thread.Sleep` grep → 0 +8. `git diff HEAD~1 HEAD -- tests/...RecorderTests.cs` 로 신규 테스트 5개 모두 load-bearing 확인 +9. `docs/contracts/smoke2-gap-fix.md` 부재 확인 — issue body 운영, evaluation 파일에 명시 + +## 산출물 +- `docs/contracts/smoke2-gap-fix.evaluation.md` (verdict + 회귀 트랩 표) +- 본 히스토리 파일 + +## 비고 +- Sprint Contract 파일이 없는 follow-up 흐름이지만 4개 gap이 issue body에 명확히 정의되어 있어 평가 가능했음. +- PROGRESS.md / 코드 수정은 evaluator scope 밖이라 손대지 않음. diff --git a/docs/history/2026-04-07_이슈12-smoke2-fix-orchestration.md b/docs/history/2026-04-07_이슈12-smoke2-fix-orchestration.md new file mode 100644 index 0000000..1b9c2a2 --- /dev/null +++ b/docs/history/2026-04-07_이슈12-smoke2-fix-orchestration.md @@ -0,0 +1,33 @@ +# 2026-04-07 이슈 #12 — Smoke 2차 gap fix 오케스트레이션 + +- **이슈**: #12 (smoke 1회차 후속 4-gap fix) +- **소요 시간**: ~15분 (Generator ~6분 + Evaluator ~2분 + orchestrator 마무리) +- **Context 사용량**: ~400k tokens (orchestrator 누적) + +## 사이클 + +1. Smoke 1회차 발견 4-gap 이슈화 (#12 open) +2. Generator 백그라운드 → commit `8784fec` (60 → 71 tests) +3. Evaluator 백그라운드 → **pass** (4/4 gap pass + regression trap 검증) +4. PROGRESS 갱신, 이슈 close, commit + push + +## 수정 요약 + +- **Gap A**: `UiaPathParser` + `UiaPathResolver` + `IUiaTreeNode` 새 추상화. 기존 last-AutomationId 휴리스틱 제거. ancestor chain 따라 descend하며 id→name→class 우선. +- **Gap B**: `DragCollapser`에 `lastFocusPath`/`lastMousePath` 추가. `FlushType` fallback chain 적용. +- **Gap C**: `SutProcessWindowFilter` + P/Invoke `WindowFromPoint`/`GetForegroundWindow`. `Program.cs`가 attached pid로 wire. +- **Gap D**: `ScenarioWriter`가 UTF-8 BOM-less 명시. 한글 round-trip 테스트 + BOM 부재 assertion. + +## 비용 + +Generator ~92k + Evaluator ~58k + Orchestrator ~15k = **~165k** + +## 다음 단계 + +**Smoke 2회차** — 실제 EG-BIM Modeler에서 짧은 Box 시나리오 재검증. 기대: +- click이 정확한 element 잡음 +- type "BOX"/"10" 실제로 입력됨 +- SUT 외 이벤트 필터됨 +- Box가 화면에 실제로 그려짐 + +사용자 라이브 환경 필요.