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:
44
docs/contracts/diff-reporter.md
Normal file
44
docs/contracts/diff-reporter.md
Normal 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 하위 호환성
|
||||
43
docs/contracts/normalizer.md
Normal file
43
docs/contracts/normalizer.md
Normal 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
44
docs/contracts/player.md
Normal 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" 신호 필요
|
||||
- 시작 상태 초기화 (임시 프로젝트 파일 삭제) 미비 시 누적 오염
|
||||
- 재생 중 모달 대화상자 출현 시 대응 정책 필요
|
||||
47
docs/contracts/recorder.md
Normal file
47
docs/contracts/recorder.md
Normal 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 블록 가능 → 별도 스레드 + 큐 필수
|
||||
48
docs/contracts/sut-prober.md
Normal file
48
docs/contracts/sut-prober.md
Normal 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 `\`) 정규화 정책 — 출력에서 `/`로 통일 권장.
|
||||
Reference in New Issue
Block a user