# task-2717+2 — PR #163 Phase1 Gemini 5건 same-PR bounded fix (dev1·헤르메스)

- **일시**: 2026-05-31 04:13 (server)
- **PR**: #163 / branch `task/task-2717-anu-owned-callback-enforcement`
- **base head(before)**: `7f6b0b55` → **new commit**: `6a61113c`
- **분류**: Phase1 only, same-PR bounded fix **1회**. ANU read-only validity 5건 전부 valid(dismiss 0).
- **허용 파일 2개만**: `dispatch/anu_owned_callback_enforcement.py` · `tests/regression/test_anu_owned_callback_enforcement_2717.py`
- **push**: ❌ 안 함 (dev1 push 금지 — 로컬 commit 까지만; push 는 ANU 재검증 후)

---

## 수정 5건 (최소수정)

### ① HIGH — 인프라 예외 ↔ 실 access-denied 분리 (false quarantine 방지)
**문제**: `RealCokacdirCronHistoryProbe.__call__` 이 OSError/ValueError/SubprocessError 를
`{"status":"error","message":"probe exec failed: ..."}` 로 삼킴 → `resolve_authoritative_owner`
가 message 를 `_ACCESS_DENIED_TOKENS`("not found"/"access denied")와 매칭. 인프라 장애 메시지에
"not found"(예: `command not found`, `No such file`)가 섞이면 **OWNER_NOT_ANU 오분류 → false
quarantine**.

**수정**:
- `__call__` 이 인프라 실패를 **message-비의존 distinct 신호** `error_kind="probe_exec_failed"`
  (`PROBE_EXEC_FAILED_KIND`)로 반환. 실 cokacdir 응답은 이 키를 절대 내보내지 않음.
- `resolve_authoritative_owner` 의 `status=="error"` 분기에서 **message 토큰 매칭 이전에**
  `error_kind==probe_exec_failed` → **OWNER_PENDING(retryable, fail-closed 아님)** 로 매핑.
- 명시적 cokacdir access-denied/not-found(실 응답, error_kind 없음)만 OWNER_NOT_ANU 유지.
- 결과: 인프라 장애는 self-collector 격리가 아니라 **재시도 가능한 PENDING** 으로 분리.

### ② MED — `_envelope_age_seconds` 숫자 epoch + staleness silent bypass 제거
**문제**: 숫자형 epoch(int/float) 가 `str()` 후 `strptime` 으로만 처리되어 전부 실패 → `None`
→ staleness 체크 **silent bypass**. (숫자 epoch 가 strptime 에 직접 들어가면 TypeError 인데 기존
except 는 ValueError 만 잡음.)

**수정**:
- 신규 헬퍼 `_parse_timestamp_value`: **숫자형 epoch(int/float 및 숫자 문자열)** 는
  `datetime.fromtimestamp(float(val), tz=utc)` 로 처리, ISO/날짜 문자열은 strptime. 예외는
  `TypeError/ValueError/OverflowError/OSError` 모두 핸들. (bool 은 int subclass 이므로 제외.)
- `_envelope_age_seconds`: timestamp 키 부재 → `None`(미적용, 기존 의미 유지). **값은 있으나 전부
  파싱 실패 → `_UNPARSEABLE_TS_STALE_SECONDS`(10^12) 반환 → 항상 stale 로 reject**. 파싱 실패를
  None 으로 삼켜 staleness 를 우회하지 않음(**silent pass 0**).

### ③ MED — `write_quarantine_record` filename sanitize (cross-platform)
**문제**: `str(...).replace(os.sep, "_")` 는 Linux 에서 `/` 만 치환하고 `\` 를 남김.

**수정**: 신규 헬퍼 `_sanitize_filename_component` = `re.sub(r"[\\/]+", "_", ...)` 로 `/` 와 `\`
**양쪽 명시 치환**. `safe_task`/`safe_sid` 양쪽 적용.

### ④ MED — `executor_write_result_json` filename sanitize
③과 동일 패턴(`safe_task`). 동일 `_sanitize_filename_component` 헬퍼 적용.

### ⑤ MED — test tmpdir cleanup
**문제**: `setUp` 의 `mkdtemp` 후 cleanup 없음 → tmpdir 누수.

**수정**: `self.addCleanup(shutil.rmtree, self._tmp, ignore_errors=True)` 추가(`import shutil`
top-level 승격).

---

## 신규 regression (4종, 전부 PASS)
- **test_12 / test_12b — infra → OWNER_PENDING**: error_kind=probe_exec_failed 신호(message 에
  'not found' 섞여도) → OWNER_PENDING / verify 레벨 PENDING_OWNER_PROOF, **격리 artifact 미생성**.
  error_kind 없는 실 access-denied 는 OWNER_NOT_ANU 로 구분. `RealCokacdirCronHistoryProbe` 가
  바이너리 부재 시 distinct 신호 반환(실 cokacdir 미호출).
- **test_13 — numeric epoch staleness**: fresh int epoch → AUTHORITATIVE(stale 아님), old float
  epoch → STALE_REJECTED, 미파싱 timestamp 존재 → STALE_REJECTED(**silent pass 0**), 함수 단위
  age 정확성 + 부재 None.
- **test_14 — backslash sanitize**: `write_quarantine_record`/`executor_write_result_json` 파일명
  에 `\`·`/` 모두 미존재 + 파일 실제 생성.
- **test_15 — tmpdir cleanup**: setUp tmpdir 가 `addCleanup(shutil.rmtree)` 로 등록됨.

**전체**: `tests/regression/test_anu_owned_callback_enforcement_2717.py` → **17 passed (0.14s)**
(기존 12 + 신규 5). target regression + 신규 regression 동시 PASS.

---

## 검증 게이트 (전부 통과)
- `git diff --name-only origin/main...HEAD` = **2 expected_files only** (forbidden 0, 새 파일 0).
- **PR #162 미접촉** — 브랜치가 origin/main 대비 이 2파일만 변경.
- **Phase2 혼입 0** — systemd/inotify/crontab/completion_contract/OS-level pickup grep 0.
- **ANU key literal 0** — grep `c119085` = 0 (M.ANU_KEY import 상수만 참조).
- **fire NOT_ACTIVATED 유지** — fire-path gate 미접촉, `fired` 항상 False (test_10 PASS).
- **CI workflow 0** — `.github/workflows` 미접촉.
- pre-commit guard **PASS** (task-id=task-2717).

## 금지사항 준수
Phase2 구현 0 · PR#162 접촉 0 · task-2713/2714/2715 미시작 · 새 파일 0 · force/rebase 0 ·
merge-ready 미선언 · Gemini thread 임의 resolve 0 · callback schedule 자가발사 0 · ANU key 노출 0.

## push & HARD_STOP
- **push 안 함** (dev1 push 금지). 로컬 commit `6a61113c` 까지만. push 는 ANU 독립 재검증 PASS 후
  ANU OWNER PAT FF push.
- **HARD_STOP**: push 후 fresh Gemini/CI 확인. 새 valid finding 또 나오면 자동수정 금지 →
  LOOP_BOUNDARY 회장 보고.

## 완료 신호
`memory/events/task-2717+2.result.json` (new_commit_sha / fixes_5 / regression_result /
expected_files_only / phase2_zero / anu_key_literal_0 / fire_not_activated). callback 자가발사 0.
