# task-2841 보고서 — PR-E1 reference_id 필터 보안불변식 명시 테스트 2건

## Situation
PR-E1(task-2840, PR #121, 브랜치 `task/task-2840-dev1`)에서 `GET /api/insuro/consultation-history/v1` LIST 의 `reference_id` 옵션 필터를 추가했다. 코드상 CORRECT·per-FA SAFE·malformed FAIL-CLOSED 는 기존 9종 테스트로 확인됐다.

## Complication
Codex 검토 미결: **보안 불변식이 reference_id 필터와 함께 유지되는지 명시하는 테스트 2건 부재**.
1. 교차-FA reference_id → 0행 (타 FA 레코드 노출 0)
2. reference_id 일치 + REQUIRES_REVIEW 제외 (OK_ONLY_VIEW 유지)

## Question
reference_id 필터가 적용된 상태에서도 (a) 서버파생 fa_account_id 스코프가 강제되어 교차-FA 노출이 0인가, (b) OK 전용 뷰가 유지되어 REQUIRES_REVIEW 가 제외되는가 — 를 단순 통과가 아니라 **불변식 관측**으로 증명한다.

## Answer
test-only. `server/tests/test_consultation_history_v1.py` 한 파일만 변경(소스 0). 기존 하네스(`ch_list_env`, `_ListQueryMock`, `_sample_ok_row`) 재사용.

### 변경 내용
- **`_ListQueryMock` opt-in 필터강제 백킹 추가** (기존 9종 무영향 — 기본 `_backing=None` 시 기존과 100% 동일):
  - `set_enforced_backing(rows, view_status_ok_only=False)`: `execute()` 가 기록된 `.eq()` 필터를 backing dataset 에 **실제로 적용**하도록 강제. `view_status_ok_only=True` 이면 OK_ONLY_VIEW 의 `record_status='OK'` 뷰 필터도 선적용.
- **신규 테스트 2건** (섹션 L):
  1. `test_list_reference_id_cross_fa_returns_zero` — FA-B 소유 reference_id(질의값과 일치)를 가진 row 를 backing 에 넣고 조회. mock 이 `.eq("fa_account_id", <서버파생>)` 를 적용해 fa 불일치로 **0건**. reference_id 만이면 통과했을 row 가 0건이 된다는 사실 자체가 fa_account_id .eq 강제의 증거. `OK_ONLY_VIEW` 사용·raw 테이블 미접근 확인.
  2. `test_list_reference_id_excludes_requires_review` — 같은 reference_id 의 OK+REQUIRES_REVIEW 2 row 를 backing 에 넣고 `view_status_ok_only=True`. 응답은 **OK 1건만**(REQUIRES_REVIEW 제외). reference_id/fa_account_id `.eq` 적용 + OK_ONLY_VIEW 사용·raw 미접근 확인.

### 인지 검증 (가짜 통과 배제)
- test 1: backing 1건 투입 → 결과 0건. enforcement 미작동이었다면 1건 반환→assert 실패. PASS ⇒ 필터 실동작 실증.
- test 2: backing 2건 투입 → 결과 1건(row-ok만). view 필터 미작동이었다면 2건→실패. PASS ⇒ 뷰 의미 실동작 실증.
- 기본값 `_backing=None` 으로 기존 65개 회귀 0 확인.

## 테스트 결과
- `python3 -m pytest tests/test_consultation_history_v1.py -q` → **67 passed, 4 warnings** (기존 65 + 신규 2, 회귀 0).
- 신규 2건 개별: `2 passed, 65 deselected`.
- `git diff --stat`: `server/tests/test_consultation_history_v1.py` 1 file changed, +92/-1. routes/·main.py·src/ 변경 0.

## L1 스모크테스트 결과
- 서버 재시작: **해당없음** (test-only, merge_policy:none — 실서버 curl 부적절).
- API 응답 확인: **통과** — pytest 가 FastAPI `TestClient` 로 실제 app 을 in-process 구동, `GET /api/insuro/consultation-history/v1?reference_id=...` 실 HTTP 요청 → 200 + 기대 응답 shape(`{"records":[...],"count":N}`) 실측. (신규 2건이 이 실 라우터 경로를 구동)
- 스크린샷: 해당없음(백엔드 API, 프론트 UI 없음).
- L1 판정: **통과** (실 라우터 구동 + 응답 검증 실측).

## Pyright 진단 참고
`Import "main"/"schemas..." could not be resolved` 경고는 이 파일 전체가 쓰는 `sys.path.insert(server)` 실행 컨텍스트 기반 패턴 탓의 **pre-existing** 경고로, 이번 변경과 무관하며 pytest 는 정상 통과. 신규 코드가 유발한 진단 0.

## 모델 사용 기록
- 아르고스(테스터): **sonnet** — 정밀 스펙 기반 테스트 코드 적용 + pytest/grep/git 검증.
- 팀장(헤르메스, Opus): 설계·라우터/뷰 의미 분석·인지 검증·통합. 직접 코딩 0.

## 머지 판단
- **머지 필요**: No (지시서: **머지 금지**. PR #121 반영·ANU 독립검증 대상)
- **브랜치**: `task/task-2840-dev1` (PR #121)
- **워크트리 경로**: `/home/jay/projects/InsuRo/.worktrees/task-2840-dev1`
- **push**: `afc09e4..29ee990` (기존 브랜치 이어커밋, 새 PR 없음)
- **머지 의견**: test-only·소스 0·67 passed 회귀 0. 두 불변식 테스트가 mock enforcement 로 실제 격리/ok-only 를 관측(단순 통과 아님). 머지는 아누+Codex 독립검증 후 회장 파이프라인 판단.

## 비고
- reference_id 필터 × 보안불변식 test 갭(Codex 미결) 종결. PR-E1 검증 커버리지 보강 완료.
- 다음: 아누+Codex 독립검증 → (승인 시) 머지 → PR-E2(웹앱 두 페이지 V 재배선).
