Orchestrate smoke 2차 gap fix evaluation + close #12
- 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) <noreply@anthropic.com>
This commit is contained in:
49
docs/contracts/smoke2-gap-fix.evaluation.md
Normal file
49
docs/contracts/smoke2-gap-fix.evaluation.md
Normal file
@@ -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.
|
||||
Reference in New Issue
Block a user