Set up AI dev environment for recordingtest (#2)

- CLAUDE.md with collaboration rules and Planner/Generator/Evaluator cycle
- .claude/ agents, commands, skills, hooks per Claude Code conventions
- Sprint Contracts for sut-prober, normalizer, recorder, player, diff-reporter
- SUT catalog (EG-BIM Modeler, 187 plugins) and .gitignore excluding SUT tree
- PROGRESS.md / PLAN.md as shared agent handoff state
- Solution scaffold targeting sut-prober PoC

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
minsung
2026-04-07 13:57:20 +09:00
parent a48a8a2d1d
commit 7ffbb1f757
47 changed files with 1886 additions and 11 deletions

View File

@@ -0,0 +1,44 @@
# Sprint Contract — diff-reporter
**Owner:** Generator
**Depends on:** normalizer (정규화된 입력)
**Issue:** #2
## Goal
`*.approved``*.received` 쌍을 받아 의미 있는 diff를 생성하고, 실패 지점을 시각화한다. `diff-triager` 서브에이전트가 읽을 수 있는 구조화 출력도 제공.
## Definition of Done
- [ ] `Recordingtest.DiffReporter` 라이브러리 + CLI
- [ ] 입력: `--approved <path> --received <path> --out <dir>`
- [ ] JSON/텍스트 파일은 라인·객체 단위 의미 diff, 바이너리는 hex 요약 diff
- [ ] 출력: `diff.json`(구조화), `diff.md`(사람용), `diff.html`(옵션, side-by-side)
- [ ] `diff.json` 스키마: `{ file, hunks[], summary: { added, removed, changed } }`
- [ ] 동일 파일 비교 시 "identical"로 단정, exit code 0
- [ ] 차이 존재 시 exit code 1 + 요약을 stdout 1줄
- [ ] diff-triager 에이전트가 `diff.json`만 읽어 bucket 분류 가능 (통합 테스트 1개)
## Interfaces
- **Inputs:** 두 파일 경로
- **Outputs:** `diff.json`, `diff.md`, (옵션) `diff.html`
- **Side effects:** 없음
## Out of scope
- 정규화 자체 (normalizer)
- triage 분류 로직 (diff-triager 에이전트)
- 자동 approve (`/approve` 커맨드)
## Evaluation plan
1. 동일 파일 2개 → identical 판정
2. 1필드만 바뀐 JSON → `diff.json.hunks.length == 1`
3. 바이너리 파일 diff → hex summary 출력
4. diff-triager에 통합: normalization gap 케이스에서 bucket=`normalization_gap` 분류 확인
## Risks
- 큰 파일(수십 MB 모델)에서 성능 — 스트리밍 diff 필요
- JSON 스키마 변경 시 triager 하위 호환성

View File

@@ -0,0 +1,43 @@
# Sprint Contract — normalizer
**Owner:** Generator
**Depends on:** sut-prober (Json 카탈로그)
**Issue:** #2
## Goal
SUT 저장 파일(Json 설정 + 모델 파일)의 비결정적 필드를 정규화해 golden-file 비교를 결정적으로 만든다. 규칙은 **프로파일 단위로 버전 관리**되고 단위 테스트를 동반한다.
## Definition of Done
- [ ] `Recordingtest.Normalizer` 라이브러리가 `Normalize(input, profile)` API 제공
- [ ] 기본 프로파일 `default` 포함 규칙 최소 5개: 타임스탬프 마스킹, GUID 치환, 절대 경로 → `<REPO>`/`<USER>`, 부동소수점 epsilon(1e-6), JSON 객체 키 정렬
- [ ] 프로파일은 `profiles/*.yaml`로 선언, 코드 변경 없이 규칙 추가 가능
- [ ] 각 규칙마다 `tests/Normalizer.Tests/` 아래 before/after 샘플 1쌍 이상
- [ ] 동일 입력으로 두 번 정규화해도 같은 바이트 출력 (idempotent)
- [ ] 정규화 로그(sidecar) `normalization.log` 생성 — 어떤 규칙이 몇 번 적용됐는지 기록
- [ ] `json-configs.json` 카탈로그의 "suspected fields" 전수를 default 프로파일로 덮는다
## Interfaces / contracts
- **Inputs:** 파일 경로 + 프로파일 이름
- **Outputs:** 정규화된 바이트 스트림 + sidecar 로그
- **Side effects:** sidecar 로그 쓰기만
## Out of scope
- 바이너리 `.hme`/`.egm` 파서 (별도 contract)
- SUT 내부 호출
- 정규화 규칙 자동 학습
## Evaluation plan
1. `dotnet test tests/Normalizer.Tests` 전부 pass
2. `json-configs.json`의 모든 suspected field가 default 프로파일 규칙에 커버됨 (매핑 테이블 검증)
3. 동일 입력 2회 정규화 바이트 diff 없음
4. 샘플 `Settings.json` 정규화 전/후 비교에서 타임스탬프·경로가 마스킹됨
## Risks
- epsilon 선택이 SUT 실제 정밀도와 충돌 가능 → 구성 가능해야 함.
- 컬렉션 순서 정렬이 SUT 의미를 바꿀 수 있음 → 정렬 대상은 명시적 화이트리스트.

44
docs/contracts/player.md Normal file
View File

@@ -0,0 +1,44 @@
# Sprint Contract — player
**Owner:** Generator
**Depends on:** recorder 스키마 확정
**Issue:** #2
## Goal
recorder가 생성한 시나리오 yaml을 SUT에 재생해 결과 저장 파일을 생성한다. 타이밍은 고정 sleep 금지, UIA/이벤트 기반 동기화.
## Definition of Done
- [ ] `Recordingtest.Player` 콘솔이 `--scenario <path> [--output-dir <path>]` 받음
- [ ] 각 step 재생 전 `wait_for`(uia 이벤트/엘리먼트 property) 대기 구현
- [ ] `target.uia_path`로 엘리먼트 재탐색 후 `offset_norm`으로 좌표 계산해 입력 전달
- [ ] 재생 중 예외 발생 시 시나리오 스텝 index + 엘리먼트 탐색 실패 이유를 아티팩트로 기록
- [ ] 체크포인트 step에서 현재 저장 파일을 `<output-dir>/checkpoint-<n>.*`로 복사
- [ ] 재생 완료 시 exit code 0, 실패 시 non-zero + 아티팩트 경로 출력
- [ ] 동일 시나리오 10회 재생 시 9회 이상 성공 (flaky 허용 상한)
## Interfaces
- **Inputs:** 시나리오 yaml, 아티팩트 출력 디렉터리
- **Outputs:** checkpoint 저장 파일, 실패 아티팩트(스크린샷, UIA 덤프, 로그)
- **Side effects:** SUT 프로세스 실행/종료
## Out of scope
- 정규화 (normalizer의 몫)
- diff (diff-reporter의 몫)
- 리포트 집계 (test-runner의 몫)
## Evaluation plan
1. recorder로 만든 간단 시나리오(Box 생성 저장)를 10회 재생 → 9회 이상 완료 파일 생성
2. 의도적으로 잘못된 `uia_path`를 넣어 실패 → 아티팩트에 탐색 실패 이유 기록 확인
3. 체크포인트 2개 시나리오 재생 → 각 checkpoint 파일 존재 확인
4. 고정 sleep 호출 grep으로 0건 확인
## Risks
- plugin 로드 지연 → 첫 step 전 "plugin ready" 신호 필요
- 시작 상태 초기화 (임시 프로젝트 파일 삭제) 미비 시 누적 오염
- 재생 중 모달 대화상자 출현 시 대응 정책 필요

View File

@@ -0,0 +1,47 @@
# Sprint Contract — recorder
**Owner:** Generator
**Depends on:** FlaUI 승인, sut-prober(UIA 힌트용 카탈로그)
**Issue:** #2
## Goal
사용자가 EG-BIM Modeler에서 수동 테스트하는 동안 입력(키·마우스·창 이벤트)을 캡처해 element-aware 시나리오 파일을 생성한다.
## Definition of Done
- [ ] `Recordingtest.Recorder` 콘솔 실행 시 SUT에 attach하고 입력 캡처 시작
- [ ] 캡처되는 이벤트: 키 다운/업, 마우스 클릭/드래그/휠, 포커스 변경
- [ ] 각 이벤트는 `{ ts, kind, uia_path, offset_norm, raw_coord, value }` 형식으로 기록
- [ ] 3D 뷰포트(SharpDX surface) 클릭 시 uia_path는 호스팅 엘리먼트, `offset_norm``[0..1]` 정규화 좌표
- [ ] 출력: `scenarios/<name>.yaml` 스키마 준수 (`scenario-author` 규격 일치)
- [ ] 비밀번호/토큰 마스킹 규칙 1개 이상 (예: focus된 element가 `PasswordBox`이면 `value``<MASKED>`)
- [ ] 캡처 중 SUT 성능에 눈에 띄는 영향 없음 (60 FPS 유지; 경고만)
- [ ] 캡처 종료 시 요약(이벤트 수, 소요 시간, uia_path 미결 건수)
## Interfaces
- **Inputs:** `--output scenarios/<name>.yaml`, `--attach <pid or window title>`
- **Outputs:** 시나리오 yaml
- **Side effects:** 전역 Win32 hook 등록/해제
## Out of scope
- 리플레이 (player의 몫)
- 3D 엔진 상태 캡처 (engine-bridge 사용 예정)
- 자동 이름 추정 / 자연어 변환 (scenario-author)
## Evaluation plan
1. SUT를 수동 실행 후 recorder attach → Box 명령 버튼 클릭 → Box 생성 드래그 → 저장
2. 출력 yaml 파싱 + 스키마 검증
3. 클릭 이벤트의 `uia_path``Button[@Name*='Box']` 형태 포함 확인
4. 드래그 이벤트의 `offset_norm``[0..1]` 범위 확인
5. `PasswordBox`에 더미 입력 → `<MASKED>` 기록 확인
6. 종료 후 hook 해제 확인 (재실행 시 double-hook 안 남음)
## Risks
- IME(한글) 조합 키 이벤트 손실 → 클립보드 우회 필요 가능
- 해상도/DPI 변경 시 `raw_coord`는 유효성 상실 → `offset_norm` 우선 사용
- Win32 hook은 UI thread 블록 가능 → 별도 스레드 + 큐 필수

View File

@@ -0,0 +1,48 @@
# Sprint Contract — sut-prober
**Owner:** Generator (일반 세션)
**Depends on:** 없음
**Issue:** #2
## Goal
EG-BIM Modeler SUT의 **정적** 구조(플러그인·설정·어셈블리)를 덤프해 `docs/sut-catalog/`에 베이스라인 가능한 카탈로그를 생성한다. SUT는 실행하지 않는다.
## Definition of Done
- [ ] `dotnet run --project src/Recordingtest.SutProber` 실행으로 카탈로그 3종이 생성된다: `plugins.json`, `json-configs.json`, `assemblies.json`
- [ ] `plugins.json``EG-BIM Modeler/Plugins/Eg*Plugin/` 187개(±) 전부를 담고, 각 항목은 `{ name, path, dlls[], size_bytes }` 형식
- [ ] `json-configs.json``EG-BIM Modeler/Json/*.json` 각 파일별 `{ name, top_level_keys[], suspected_nondeterministic_fields[] }`
- [ ] `assemblies.json``HmEG*.dll`, `Editor*.dll`, `HmGeometry*.dll``{ name, size, has_pdb }`
- [ ] 출력은 **정렬되어 있어** 두 번 실행해도 동일 (decimal/텍스트 diff 없음)
- [ ] `EG-BIM Modeler/` 폴더에 대한 쓰기 접근이 없다 (코드 리뷰로 확인)
- [ ] 경로는 repo root 상대 경로로 기록(절대 경로 금지)
## Interfaces / contracts
- **Inputs:** `EG-BIM Modeler/` (read-only), CLI 인자 `--sut <path>` (기본 `EG-BIM Modeler`)
- **Outputs:** `docs/sut-catalog/plugins.json`, `docs/sut-catalog/json-configs.json`, `docs/sut-catalog/assemblies.json`
- **Side effects:** 없음 (로그 제외)
## Out of scope
- SUT 실행 / 리플렉션 / 동적 분석
- PDB 파싱 (engine-bridge의 몫)
- UIA 트리 덤프 (recorder 또는 별도 PoC의 몫)
## Evaluation plan
evaluator는 다음을 수행:
1. `dotnet build` 성공
2. `dotnet run --project src/Recordingtest.SutProber -- --sut "EG-BIM Modeler"` 실행, exit code 0 확인
3. 세 카탈로그 파일 존재 확인 + JSON 파싱 유효성
4. `plugins.json` 엔트리 수 ≥ 180
5. 두 번째 실행 후 세 파일 `git diff` 결과가 비어있음을 확인 (결정성)
6. `grep -r "EG-BIM Modeler" src/Recordingtest.SutProber/` 결과에 `File.Write|File.Delete|File.Create` 호출 없음 확인
## Risks / open questions
- .NET 버전 합의 필요 (`net8.0` 제안). SUT와 다를 수 있으나 prober는 독립 프로세스이므로 무관.
- 플러그인 폴더 구조가 일정한지 샘플 확인 필요.
- 경로 구분자(Windows `\`) 정규화 정책 — 출력에서 `/`로 통일 권장.