DMF_Crawler/docs/design/07-tdd-red-system.md
Yun Chan 56a6e2da93 chore: 저장소 구조 정리 및 문서화, 첫 커밋
- src/dist 산출물 분리 원칙 정리(.gitignore, .gitattributes)
- 루트 및 주요 폴더(config/scripts/prompts/tests/src, 런타임 폴더 5종)에
  안내용 README.md 추가
- CHANGELOG.md, LICENSE, docs/ops/05-release-and-versioning.md 추가
- docs/README.md 문서 지도 갱신
2026-09-04 09:25:44 +09:00

433 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# DMF Crawler TDD · E2E RED · 디자인 감사 운영 체계
> 이 문서는 DMF Crawler 의 **TDD/RED 운영 SSOT** 다.
> 이론 근거는 `docs/research/11-tdd-red-and-design-audit-theory.md` 를 따른다.
**상태**: v1 확정
**적용 시점**: 이 문서가 생성된 뒤의 모든 코드·GUI·리포트·에이전트 작업
**핵심 명령**: **테스트 없이 새 코드를 쓰지 않는다. RED 실패를 먼저 본다.**
---
## 1. 법칙
### 1.1 신성한 루프
모든 구현 작업은 다음 순서를 따른다.
1. **RED** — 실패하는 자동화 테스트를 먼저 만든다.
2. **RED 확인** — 그 테스트가 실제로 실패하며, 실패 이유가 기대한 요구 위반인지 확인한다.
3. **GREEN** — 통과에 필요한 최소 구현만 한다.
4. **REFACTOR** — 전체 관련 테스트가 Green 인 상태에서만 구조·중복·이름·디자인을 정리한다.
5. **REGRESSION** — 영향 테스트 + 전체 smoke 를 돌리고 결과를 기록한다.
### 1.2 금지
- 테스트가 없는 `src/` 기능 구현 금지.
- 실패를 보지 않은 테스트를 “RED” 라고 부르기 금지.
- 테스트를 약하게 바꿔 Green 만들기 금지.
- `pytest.skip`, `xfail`, 느슨한 `except Exception: pass`, `assert True` 류로 실패 숨기기 금지.
- 실패 중에 리팩터링 금지.
- 의미 없는 테스트를 무한히 추가하기 금지.
- 디자인 감사를 “눈으로 대충 봄”으로 대체 금지.
### 1.3 예외
다음은 테스트 선행이 어려울 수 있다. 그래도 문서화가 필요하다.
| 예외 | 허용 조건 | 대체 증거 |
|---|---|---|
| 문서만 수정 | 코드 동작이 변하지 않음 | 링크/목차/SSOT 일관성 검사 또는 변경 요약 |
| 설치 스크립트/작업 스케줄러 | 실제 Windows 권한이 필요 | dry-run/PowerShell parser/ASCII/BOM 검사 + 수동 프로브 로그 |
| 실제 공공 사이트 접근 | 사용자 승인 필요 | fixture/mock 서버 E2E + 수동 실행 체크리스트 |
| 순수 스타일 문구 | 자동화 어려움 | 디자인 RED 체크리스트와 스크린샷/수기 판정 기록 |
예외를 남길 때는 “왜 자동화 못 했는가 / 어떤 수동 증거로 대체했는가 / 나중에 자동화할 조건”을 적는다.
---
## 2. RED 계층 구조
### R0 — 계약/부팅 RED
목표: 프로젝트가 import/CLI/패키지 수준에서 깨지지 않음을 보장한다.
필수 검사:
```powershell
.\.venv\Scripts\python.exe -m compileall -q src tests
.\.venv\Scripts\python.exe - <<'PY'
import importlib, pathlib
for p in pathlib.Path('src/dmf_crawler').rglob('*.py'):
if p.name == '__init__.py':
continue
importlib.import_module('.'.join(p.with_suffix('').relative_to('src').parts))
PY
.\.venv\Scripts\python.exe -m dmf_crawler --help
.\.venv\Scripts\python.exe -m dmf_crawler doctor --json
```
RED 예시:
- `cli.py` 가 없으면 `python -m dmf_crawler --help` 실패.
- `storage/repo.py` 가 없으면 `pipeline.run_once()` 런타임 실패.
- `.ps1` 에 BOM 이 없으면 PowerShell 5.1 한글 깨짐 위험.
### R1 — 도메인 단위 RED
대상:
- `normalize.py`
- `diff.py`
- `integrity.py`
- `source_mfds.py` 파서
- `agy/extract.py` JSON 추출
원칙:
- 네트워크 없음.
- DB 없음.
- clock/random 고정.
- fixture 로 실패 재현.
대표 RED:
| 위험 | 테스트 |
|---|---|
| 등록번호 파서 회귀 | 표준/신물질/허여서/깨진 입력 10종 |
| openpyxl 함정 재발 | nedrug xlsx 는 sheet XML 직접 파싱 요구 문서/테스트 |
| API 오류 봉투 2종 | JSON 정상 봉투 + XML/JSON Gateway 오류 봉투 |
| 조용한 빈 파일/빈 응답 | 200 OK + 헤더만/0건은 무결성 게이트 차단 |
| 발급일자 변경 신호 | permit_date 변경 시 CHANGED |
| 취하 오탐 | totalCount 급감/전건 취하율 임계 초과 시 diff 폐기 |
### R2 — 저장소/통합 RED
대상:
- `storage/db.py`
- `storage/repo.py`
- migrations
- `pipeline.py`
필수 RED:
- 새 DB 에 migrations 0001~0003 적용.
- 중복 적용 시 checksum 검증 후 무변화.
- `start_run` → checkpoint → snapshot → events → report data 조회.
- 같은 날짜 SUCCESS 중복 방지.
- 기준선 첫 실행은 이벤트 0건.
- 스냅샷 저장 전 무결성 실패면 DB 오염 없음.
### R3 — 파이프라인 E2E RED
목표: 사람이 실제로 쓰는 흐름을 fixture 로 끝까지 검증한다.
시나리오:
1. **첫 설치/키 없음**
- `doctor` 가 API 키 없음 WARN 을 낸다. 인증키는 선택 기능이다.
- `source.mode=auto` 는 API 키가 없으면 AGY headful Chrome CCBAC03 xlsx 수집을 시도한다.
- `source.mode=api` 에서 키가 없거나 headful 수집이 실패하면 BLOCKED 가 아니라 마지막 성공 자료 PARTIAL 리포트로 폴백한다.
2. **첫 수집 기준선**
- fixture API 3건 → DB snapshot 3건.
- diff 이벤트 0건.
- 대시보드 배너 “기준선 수립일”.
3. **둘째 날 변경**
- 신규 1 / 변경 1 / 취하 1 fixture.
- events 3건, report changes 3행.
4. **무결성 차단**
- 200 OK 이지만 0건.
- diff/persist 스킵.
- 마지막 성공 자료로 PARTIAL 리포트.
5. **리포트 파일 잠김**
- 대상 xlsx 잠김 또는 `~$` 파일 존재.
- `_HHMMSS` 폴백 이름 사용.
- 최신본 갱신 실패는 WARN 으로 기록.
6. **AI 실패 독립성**
- agy timeout/schema fail.
- 리포트는 생성되고 AI 없음 배너만 표시.
### R4 — xlsx 디자인 감사 RED
DMF Crawler 의 가장 중요한 사용자 화면은 Excel 리포트다. xlsx 는 zip/XML 이므로 stdlib 로 검사한다.
필수 감사:
| 항목 | RED 조건 |
|---|---|
| 파일 구조 | `[Content_Types].xml`, `xl/workbook.xml`, worksheets, styles 존재 |
| 시트 순서 | 대시보드가 첫 시트, 메타가 마지막, 워치리스트는 조건부 |
| 시트명 | `00_대시보드` 등 SSOT 명칭과 1:1 |
| 탭 색 | `report/theme.py``TAB_COLORS` 반영 |
| 대시보드 viewport | zoom 90, 행/열 폭 합이 1366×768 무스크롤 목표 초과 금지 |
| 틀 고정 | 변경분/전체현황/집계 시트에 freeze panes |
| 자동필터/표 | 데이터 표가 ListObject 또는 autofilter 를 가짐 |
| 내부 링크 | 대시보드 내비, 변경분→전체현황 링크 존재 |
| 조건부서식 | 신규/변경/취하/워치/게이트 실패 서식 존재 |
| 대비 | 팔레트 상태 배경/글자 contrast 최소 AA, 핵심 상태 AAA 목표 |
| 색 단독 금지 | 신규/변경/취하가 라벨+기호+색으로 표현 |
| 긴 텍스트 | 한글 긴 성분명/제조소/소재지 fixture 가 열폭 255 같은 폭주를 만들지 않음 |
| 빈 상태 | 변경 0건, 워치리스트 없음, AI 없음이 깨진 표/빈 차트로 보이지 않음 |
| 출처 | 식약처/공공데이터포털 출처 문구가 메타/푸터에 존재 |
### R5 — GUI 디자인·UX 감사 RED
대상: `tkinter` 온보딩/복구 GUI.
자동화 전략:
- 실제 창을 띄우되 가능하면 withdraw/offscreen 으로 둔다.
- 위젯 트리(`winfo_children`)를 검사한다.
- `Entry` 에 실제 텍스트를 넣고 버튼 command 를 호출한다.
- 텍스트 길이를 늘려 레이아웃 clipping 을 검사한다.
- geometry propagation 이후 `winfo_reqwidth/height` 와 screen/컨테이너 크기를 비교한다.
필수 감사 항목:
| 범주 | 실패 조건 |
|---|---|
| 끔찍한 중앙 정렬 | 폼 라벨/입력/버튼이 의미 없이 전체 중앙에 뭉쳐 스캔 불가 |
| 시각적 치우침 | 주요 column/버튼 그룹의 x/y 정렬선이 크게 어긋남 |
| 폰트 | 본문 10pt 미만, 대비 낮은 muted text, 제목/본문 hierarchy 없음 |
| overflow | 긴 한글/영문/API 키/경로가 컨테이너 밖으로 나가거나 잘림 |
| input label | 모든 입력에 visible label 또는 설명 텍스트 없음 |
| input padding | 입력 내부 텍스트와 경계/버튼 간격이 너무 좁음 |
| 실제 입력 | API 키/웹훅/워치리스트 텍스트를 넣어도 깨지지 않음 |
| 버튼 클릭 | [키 입력], [다시 검사], [지금 실행], [로그 열기] command 가 연결됨 |
| 복합 시나리오 | 키 없음→입력→검증 실패→오류 표시→재입력 흐름이 끊기지 않음 |
| 아이콘/텍스트 | 아이콘만 있는 버튼에 텍스트/툴팁 없음, vertical align 어긋남 |
| focus | Tab 순서가 논리적이고 기본 포커스가 위험 버튼에 가지 않음 |
| 모달 | 닫기/나중에/복구 행동이 명확하고 ESC/창닫기 처리됨 |
| 자연스러운 한국어 안내 | 온보딩 상단 안내가 `무엇:/왜:/어떻게:/다음:` 같은 AI식 라벨을 노출하지 않고, 현재 막힌 항목과 누를 행동을 1~3개의 자연스러운 문장으로 설명 |
### R6 — 브라우저 수집 E2E RED
브라우저 수집은 수집용 AGY가 visible/headful Chrome 을 조작하는 경로다. 요약용 AGY와 혼동하지 않는다.
- [ ] 전용 Chrome 프로필 Preferences 에 다운로드 경로가 고정된다.
- [ ] CDP 포트 소유권 sentinel 을 확인하지 못하면 중단한다.
- [x] `.tmp`/`.crdownload` 가 사라진 뒤 최종 xlsx 만 집는다. (`test_headful_browser_collect_rejects_incomplete_downloads`)
- [x] 다운로드 파일이 최소 크기/zip 구조/행 수 임계 검증을 통과한다. (`test_headful_browser_collect_validates_download_and_returns_raw_records`)
- [x] CCBAC03 웹 xlsx 헤더 alias(`신청인`, `제조국가`, `최초등록일자`, `최종변경일자`)를 RawRecord 표준 필드로 매핑한다. (`test_headful_browser_accepts_public_ccbac03_xlsx_headers`)
- [x] 로그인 상태가 아니면 “세션 만료”로 감지하고 비밀번호를 agy prompt 로 보내지 않는다. (`test_headful_browser_prompt_contract_forbids_headless_http_and_secrets`)
- [x] `mcp`/`execute_url` permission auto-deny 를 빈 JSON 오류로 뭉개지 않고 원인별로 보고한다. (`test_headful_browser_collect_reports_mcp_permission_denial_from_stderr`, `test_headful_browser_collect_reports_execute_url_permission_denial_from_stderr`)
- [x] AGY 권한은 `mcp(chrome-devtools/*)`, `execute_url(nedrug.mfds.go.kr)`만 좁게 추가한다. `mcp(*)`, `execute_url(*)`, `--dangerously-skip-permissions` 금지. (`test_headful_mcp_allow_rule_is_scoped_and_does_not_use_dangerous_skip`)
- [x] 프롬프트는 ambiguous 버튼 클릭 대신 visible Chrome 에서 `/pbp/CCBAC03/getExcel` 을 열고 wrong export 를 guard 한다. (`test_headful_browser_prompt_blocks_wrong_review_result_export`)
- [x] `source.mode=auto` + API 키 없음은 AGY headful browser 로 라우팅한다. (`test_source_auto_without_api_key_routes_to_headful_browser`)
- [x] `source.mode=browser` 는 API 키가 있어도 AGY headful browser 를 강제한다. (`test_source_browser_mode_routes_to_headful_even_when_api_key_exists`)
- [x] 실제 nedrug 접근은 기본 테스트가 아니라 사용 승인 수동 smoke 로 분리한다. 2026-09-03 smoke `run_20260903_130332`: fetch/normalize/persist/report SUCCESS, 9,816건.
### R7 — 에이전트 지침/정책 RED
`AGENTS.md`, `CLAUDE.md`, 이 문서가 서로 어긋나면 agent 가 나쁜 습관으로 돌아간다.
필수 검사 후보:
- `AGENTS.md` 가 이 문서를 링크한다.
- “No code without RED” 규칙이 존재한다.
- “테스트 약화 금지”가 존재한다.
- “디자인 감사 RED” 체크리스트가 존재한다.
- “실패를 숨기지 말 것”이 존재한다.
- `CLAUDE.md` 는 중복 SSOT 가 아니라 `AGENTS.md`/이 문서 포인터다.
---
## 3. RED 작성 품질 기준
새 RED 는 아래 8문항을 통과해야 한다.
1. 어떤 사용자/운영 위험을 막는가?
2. 어느 요구사항/설계 문서를 근거로 하는가?
3. 실패했을 때 메시지가 원인을 좁혀 주는가?
4. 현재 코드에서 실제로 실패하는가?
5. 실패 이유가 기대한 이유인가?
6. network/clock/random/shared DB 에 의존하지 않는가?
7. 같은 위험을 이미 더 강한 테스트가 덮고 있지 않은가?
8. Green 후에도 리팩터링을 방해하지 않는 행동 중심 테스트인가?
통과하지 못하면 RED 가 아니라 메모다.
---
## 4. RED 통폐합·삭제 기준
TDD 는 계속 RED 를 늘리는 행위가 아니다. 테스트 스위트도 제품이다.
### 4.1 통합해야 하는 경우
- 같은 fixture 로 같은 실패를 여러 테스트가 반복한다.
- 단위 테스트와 E2E 가 같은 단순 분기만 검사한다.
- 실패하면 원인이 항상 같은 내부 구현 세부다.
처리:
- 가장 사용자 의미가 강한 테스트 하나를 남긴다.
- 나머지는 helper/fixture 로 합친다.
- 커밋/작업 로그에 “어떤 위험이 어디로 이동했는지” 기록한다.
### 4.2 삭제해야 하는 경우
- 구현 세부를 강제해 좋은 리팩터링을 막는다.
- flaky 해서 신뢰를 깎는다.
- 요구사항이 사라졌다.
- 더 강한 상위 테스트가 같은 결함을 잡는다.
삭제 금지:
- 단지 Green 을 만들기 어렵다는 이유.
- 실행 시간이 길다는 이유만으로 삭제. 먼저 계층 이동/fixture 축소/impact run 을 시도한다.
### 4.3 완화해야 하는 경우
디자인 임계값(예: 폭/대비/간격)은 완화 가능하지만, 다음이 있어야 한다.
- 실패 스크린샷 또는 xlsx XML 증거.
- 사용자 유즈케이스에서 문제가 되지 않는 이유.
- 완화 후에도 잡히는 결함 목록.
---
## 5. 작업 절차 — 에이전트 필수 프로토콜
### 5.1 시작 전
1. `docs/HANDOFF.md` 를 읽는다.
2. 이 문서와 관련 설계 문서를 읽는다.
3. 변경할 파일 목록을 정한다.
4. 영향 테스트 목록을 쓴다.
5. 아직 테스트가 없으면 먼저 RED 를 작성한다.
### 5.2 구현 중
1. RED 를 실행해 실패 로그를 확인한다.
2. 최소 구현으로 Green 을 만든다.
3. 관련 테스트를 다시 실행한다.
4. Green 상태에서만 리팩터링한다.
5. 전체 smoke 를 돌린다.
### 5.3 보고
보고에는 최소 다음을 포함한다.
- 추가/수정한 RED.
- RED 실패가 기대한 이유였는지.
- Green 을 위해 바꾼 파일.
- 실행한 명령과 결과.
- 남은 실패/미검증 항목.
- 디자인 감사 결과(해당 시).
---
## 6. 영향 테스트 지도 v1
| 변경 파일 | 먼저 돌릴 테스트 | 그 다음 |
|---|---|---|
| `models.py` | 전체 import, dataclass contract tests | 전체 pytest |
| `normalize.py` | `tests/test_normalize.py` | diff/integrity/report data |
| `diff.py` | diff unit tests | pipeline scenario |
| `integrity.py` | integrity gate tests | pipeline stale fallback |
| `source_mfds.py` | API fixture parser tests | fetch E2E mock |
| `storage/db.py` | migration tests | repo/pipeline/report data |
| `storage/repo.py` | repo roundtrip tests | pipeline/report-only |
| `pipeline.py` | pipeline fixture E2E | alerts/watchdog/report |
| `agy/*` | polluted JSON/schema/budget tests | pipeline AI failure independence |
| `report/data.py` | SQL fixture report data tests | xlsx design audit |
| `report/*sheets*` | xlsx XML/visual audit tests | report-only smoke |
| `gui/*` | tkinter structure/input/button tests | onboard smoke |
| `notify/*`, `alerts.py` | template 4요소/DB state tests | notify-pump once |
| `scripts/*.ps1` | BOM/parser tests | 수동 Windows registration probe |
| `AGENTS.md`, `CLAUDE.md` | agent policy contract tests | docs link check |
---
## 7. 현재 프로젝트에 즉시 필요한 RED backlog
이전 구현 워크플로우가 중간에 끊겨 있으므로, 아래 RED 부터 만든다.
### 7.1 R0 계약 RED
- [x] `test_all_modules_import` — 현재 이름: `test_all_expected_runtime_entry_modules_import` (`tests/test_project_contracts.py`)
- [x] `test_cli_help_exits_zero` (`tests/test_project_contracts.py`)
- [x] `test_doctor_json_never_crashes_without_key` (`tests/test_project_contracts.py`)
- [x] `test_powershell_scripts_have_bom` — 현재 이름: `test_powershell_scripts_have_utf8_bom` (`tests/test_project_contracts.py`)
- [x] `test_cmd_files_are_ascii_only` (`tests/test_project_contracts.py`)
### 7.2 R2 저장소 RED
- [x] `test_migrations_apply_to_empty_db` — 현재 이름: `test_migrations_apply_to_empty_db_and_are_checksum_idempotent` (`tests/test_storage_repo.py`)
- [x] `test_repo_baseline_roundtrip` — 현재 이름: `test_repo_baseline_roundtrip_creates_snapshot_without_events` (`tests/test_storage_repo.py`)
- [x] `test_repo_changed_withdrawn_events_roundtrip` — 현재 이름: `test_repo_changed_new_withdrawn_events_roundtrip` (`tests/test_storage_repo.py`)
- [x] `test_same_day_success_is_idempotent` — 현재 이름: `test_same_day_success_is_enforced_by_database_guard` (`tests/test_storage_repo.py`)
### 7.3 R3 파이프라인 RED
- [x] `test_run_without_service_key_uses_existing_baseline_instead_of_blocking`
- [ ] `test_first_success_run_creates_baseline_report`
- [ ] `test_empty_success_body_blocks_diff_and_preserves_previous_snapshot`
- [ ] `test_agy_failure_does_not_block_report`
### 7.4 R4 xlsx 디자인 RED
- [x] `test_report_sheet_order_and_names``tests/test_report_e2e_design.py` 의 seeded DB → 실제 xlsx XML 감사가 검사한다.
- [ ] `test_dashboard_has_no_scroll_viewport_budget`
- [x] `test_report_has_freeze_panes_autofilter_internal_links``tests/test_report_e2e_design.py`
- [x] `test_report_status_styles_are_not_color_only``tests/test_report_e2e_design.py` 의 상태 라벨/기호/조건부서식 감사가 검사한다.
- [ ] `test_report_palette_contrast_ratios`
- [x] `test_long_korean_values_do_not_create_extreme_column_widths``tests/test_report_e2e_design.py`
### 7.5 R5 GUI 디자인 RED
- [ ] `test_onboard_all_inputs_have_visible_labels`
- [ ] `test_onboard_can_type_api_key_and_trigger_save_action`
- [ ] `test_recover_message_explains_problem_and_action_in_natural_korean`
- [ ] `test_main_actions_have_specific_button_labels`
- [ ] `test_long_korean_error_text_does_not_exceed_window_budget`
- [ ] `test_tab_order_reaches_primary_actions_before_secondary_actions`
- [x] `test_onboarding_footer_buttons_render_visible_text_after_refresh` — 사용자 스크린샷 `C:\Users\encep\AppData\Local\Temp\pi-clipboard-5df73a57-a813-41be-b025-819e48bd73c2.png` 회귀. 하단 버튼이 빈 버튼처럼 보이면 실패해야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_onboarding_footer_buttons_keep_text_when_required_items_exist` — 필수/권장 조치가 있는 상태에서도 footer 버튼 텍스트가 짜부라지거나 사라지면 실패해야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_onboarding_row_action_buttons_are_not_clipped_inside_scroll_view` — 행별 액션 버튼이 스크롤 영역/창 하단에 잘리면 실패해야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_onboarding_new_grouped_layout_keeps_primary_actions_visible` — 새 그룹 레이아웃에서 주요 버튼이 아예 안 보이면 실패해야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_scroll_frame_mousewheel_scrolls_when_pointer_is_over_content` — scroll canvas 안의 실제 텍스트/카드 위에서 휠을 굴려도 스크롤되어야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_onboarding_status_panel_names_blockers_instead_of_generic_yellow_info_box` — 상단 안내문은 `무엇:/왜:/어떻게:/다음:` 같은 AI식 라벨이 아니라 자연스러운 한국어 문장이어야 한다. (`tests/test_ux_regressions.py`)
- [x] `test_agy_google_login_is_optional_when_ai_disabled_for_api_mode` — 공식 API만 쓰고 AI 요약이 꺼져 있으면 AGY/Google 로그인을 요구하지 않는다. (`tests/test_ux_regressions.py`)
- [x] `test_agy_is_prompted_for_headful_collection_when_auto_has_no_api_key` — auto+API 키 없음이면 AI 요약이 꺼져 있어도 최신 수집용 headful AGY 준비를 안내한다. (`tests/test_ux_regressions.py`)
- [x] `test_agy_is_prompted_for_forced_browser_mode_even_with_api_key` — browser 모드는 API 키가 있어도 수집용 headful AGY 준비를 안내한다. (`tests/test_ux_regressions.py`)
### 7.6 R6 브라우저 수집 RED
- [x] `test_headful_browser_prompt_contract_forbids_headless_http_and_secrets`
- [x] `test_headful_browser_collect_validates_download_and_returns_raw_records`
- [x] `test_headful_browser_collect_rejects_incomplete_downloads`
- [x] `test_headful_browser_collect_reports_mcp_permission_denial_from_stderr`
- [x] `test_headful_browser_collect_reports_execute_url_permission_denial_from_stderr`
- [x] `test_headful_mcp_allow_rule_is_scoped_and_does_not_use_dangerous_skip`
- [x] `test_headful_browser_prompt_blocks_wrong_review_result_export`
- [x] `test_headful_browser_accepts_public_ccbac03_xlsx_headers`
- [x] `test_source_auto_without_api_key_routes_to_headful_browser`
- [x] `test_source_browser_mode_routes_to_headful_even_when_api_key_exists`
- [ ] 전용 Chrome profile Preferences / CDP 포트 sentinel / 스케줄러 headful smoke RED
---
## 8. 이 문서와 다른 문서의 관계
| 문서 | 관계 |
|---|---|
| `docs/00-REQUIREMENTS.md` | 사용자 요구 SSOT. 이 문서는 테스트 운영 방법 SSOT. |
| `docs/design/01-architecture.md` | 아키텍처 SSOT. 이 문서는 검증 게이트를 추가한다. |
| `docs/design/03-xlsx-report-spec.md` | xlsx 디자인 명세 SSOT. R4 RED 는 이 문서를 실행 가능하게 만든다. |
| `docs/design/04-onboarding-wizard.md` | GUI 명세 SSOT. R5 RED 는 이 문서를 실행 가능하게 만든다. |
| `docs/research/11-tdd-red-and-design-audit-theory.md` | 이론/근거 아카이브. |
| `AGENTS.md` | AI agent 실무 지침. 이 문서를 압축해 적용한다. |
| `CLAUDE.md` | Claude Code 진입 포인터. 중복 정본이 아니다. |
---
## 9. 미해결 항목
- [x] `tests/test_project_contracts.py` 를 만들어 R0 계약 RED 를 실제 코드화한다.
- [x] xlsx XML 감사 RED 를 실제 코드화한다. 현재 파일: `tests/test_report_e2e_design.py`.
- [x] tkinter 구조/입력/버튼 감사 RED 를 실제 코드화한다. 현재 파일: `tests/test_gui_design_contracts.py`, `tests/test_ux_regressions.py`.
- [x] 브라우저 수집 경로 확정 후 R6 RED 를 코드화한다. 현재 파일: `tests/test_agy_headful_collection.py`.
- [ ] 영향 테스트 지도를 자동 생성/검증할지 결정한다(TDAD 방식의 단순 텍스트 맵부터 시작).