# task-2716 — PR_DIFF_HYGIENE_GUARD (allowlist-aware, 로컬 구현 only)

- **팀**: dev4-team (비슈누 / Vishnu) — v36 harness · finish-task.sh 컨텍스트 보유
- **레벨**: Lv.2~3 (신규 가드 모듈 + finish-task/pre-push 결선 + 테스트)
- **우선순위**: 회장 2026-05-30 확정 1순위. 4규칙 중 이것 1개만. 2713/2714 backlog, 2715 본 task 후 재판단.
- **단일소스**: `memory/specs/system_anu_executor_4rule_codification_spec_260530.md` (Rule 3)
- **★ 이 task 는 로컬 구현 only — PR/push/merge/GitHub write 전면 금지**. 로컬 commit + 검증 + ANU callback 까지만. PR 진입은 회장 별도 결정.

## 목표
runtime code PR diff 에 **미선언 artifact** 가 섞이는 것을 차단하는 **allowlist-aware** 가드. PR #156 / PR #161(evidence md 혼입) 재발 방지. 정상 expected_files 는 false-positive 0 으로 통과.

## 설계 원칙 (회장 verbatim)

### 1. allowlist-aware
- **memory/reports 전체 차단 금지**.
- **expected_files 에 명시된 파일은 통과** (예: 선언된 `memory/reports/task-*.md` → PASS).
- expected_files 에 **없는** 다음은 차단: `memory/events/**`, `memory/artifacts/**`, `*callback-envelope*.json`, `*auth*marker*.json`(auth-path-marker), `*fix-evidence*.md` / `*-evidence.md`, temporary report, cleanup evidence.
- **runtime PR 에 미선언 artifact 가 섞이는 것만 차단**한다.
- allowlist 소스 = 해당 task 의 **선언된 expected_files** (task md 의 expected_files 섹션 또는 CI_WATCH_HANDOFF 의 expected_files). **하드코딩 금지** — 함수 인자로 받아 testable 하게.

### 2. 결선 지점
- **주 결선**: finish-task.sh / pre-push 단계에서 **차단**(contamination 발견 시 non-zero 로 push 거부 또는 finish 차단).
- **보조 결선**: watcher 에서 **read-only detection**(보고만, 차단 아님).
- **CI workflow 변경은 본 task 범위에서 제외**(`.github/workflows/**` 손대지 말 것).

### 3. 결과 분류
- `PASS`: diff = 선언된 expected_files 만(code + 선언된 report).
- `PR_DIFF_CONTAMINATED`: 미선언 artifact 발견 → 차단 + 어떤 파일이 왜 막혔는지 리스트.

## 구현
- 신규 모듈: `scripts/harness/v36/pr_diff_hygiene_guard.py`
  - 핵심 함수: `classify_pr_diff(changed_files: list[str], expected_files: list[str]) -> dict` → `{"status": "PASS"|"PR_DIFF_CONTAMINATED", "blocked": [...], "reasons": {...}}`
  - allowlist 매칭(정확/glob), artifact 패턴 매처, expected_files 예외.
- finish-task.sh pre-push 단계 결선(최소 수정): push 전 `classify_pr_diff` 호출 → CONTAMINATED 시 차단. (runtime-code-only PR 자기적용)
- watcher 보조: read-only detection 함수 재사용.

## 필수 테스트 (회장 지정 10 + 모듈 단위)
1. clean runtime diff(code만) → PASS
2. expected_files 에 명시된 `memory/reports/task-2712.md` → PASS
3. 미선언 `memory/reports/...fix-evidence.md` → BLOCK
4. `memory/events/*.json` → BLOCK
5. `memory/artifacts/**` → BLOCK
6. `*callback-envelope.json` → BLOCK
7. `*auth*marker*.json` (auth-path-marker) → BLOCK
8. allowlist(expected_files)에 포함되면 위 패턴도 예외 PASS
9. **PR #161 artifact contamination fixture**(ea2eb02a diff: 4 code + evidence md) → BLOCK (evidence md 지목)
10. **PR #161 cleanup code-only fixture**(4aa779ec 결과 diff) → PASS

fixture 는 실제 PR #161 파일목록 기반으로 구성(이번 사건 회귀 고정).

## 산출물 원칙 (회장 verbatim)
- **실행 코드 + 테스트 중심**.
- 중간 self-check marker 남발 금지.
- spec v1~vN 복제 금지.
- **final evidence packet 1개만** 허용(`memory/artifacts/task-2716-*/` 또는 report 1개).
- 새 task 확장 금지.

## 금지 (verbatim)
- task-2713/2714/2715 동시 dispatch 금지.
- CI workflow(`.github/workflows/**`) 수정 금지.
- **PR/push/merge/GitHub write 금지** (로컬 commit + 검증 + callback 만).
- task-2712 evidence 변경 금지.
- 기존 merged history 수정 금지.
- artifact cleanup 위해 기존 PR 되돌리기 금지.

## push 정책
- dev bot App token(ghs_) 부재 + 회장 push 금지 → **push 하지 않는다**. 로컬 commit(적절 task 브랜치) + finish-task(project_path 없이 로컬) + ANU callback.

## 완료 시 ANU callback envelope 필수 필드
task_id=task-2716 / module_path / test_result(N/N) / wired_points(finish-task,pre-push,watcher) / ci_workflow_untouched=true / new_commit_sha(로컬) / final_evidence_path / collector_role=ANU / self_key_used=false. 상세는 report 1개에 위임.
