### P2 — SQL 실패가 재시도 불가로 분류된다

- **사실.** `INBOX_RESERVE_FAILED`·`INBOX_QUERY_FAILED`·`INBOX_PURGE_FAILED` 셋 다 `MessagingConfigurationException`이고, 그 예외의 카테고리는 `CONFIGURATION`, `retryable = false`다.
- **근거.** `JdbcInboxRepository.java:77-80, 134-137, 165-168, 179-182`. `MessagingConfigurationException.java`의 `CATEGORY` 상수.
- **왜 문제인가.** `SQLException`의 원인 대부분은 구성 오류가 아니라 **일시적 인프라**다 — 연결 끊김, 데드락, 락 타임아웃, 커넥션 풀 고갈. `FailureCategory`는 "the stable classification a retry engine, DLQ router, and dashboard all agree on"이고 `retryable = false`는 재시도 엔진이 즉시 파킹한다는 뜻이다. 같은 leaf의 `INBOX_ACTION_FAILED`는 `TRANSIENT_INFRASTRUCTURE`/`retryable = true`로 정확히 분류된다 — 같은 파일 안에서 기준이 갈린다.
- **확인 방법.** 네 catch 블록과 `MessagingConfigurationException`의 카테고리 대조.
- **후보.** SQL 실패를 `MessageBrokerUnavailableException`류(또는 `TRANSIENT_INFRASTRUCTURE` 카테고리를 갖는 예외)로 바꾸고, 진짜 구성 오류(테이블 없음 등)만 `CONFIGURATION`으로 남긴다.
- **다음 단계.** **CASE 후보.** 재시도 정책이 실제로 갈리는 지점이다.

### P3 — 세 갈래 판정이 포트의 `boolean`에서 두 갈래로 접힌다

- **사실.** `InboxResult`가 세 값과 `isSafeToSettle()`을 갖는데 production은 `APPLIED`만 만든다. `InboxRepository.reserve`가 `boolean`을 반환하므로 `ALREADY_APPLIED`와 `CLAIMED_ELSEWHERE`가 같은 `false`로 들어온다. `TransactionalInboxHandler`는 그 경우 `HandleResult.success()`를 반환한다 — 정산한다.
- **근거.** `evidence/raw/294` 범위 밖이나 §12.3(a)의 검색 결과. `InboxResult` javadoc.
- **왜 문제인가.** `InboxResult` javadoc이 세 값이 필요한 이유로 정확히 그 정산을 든다 — "would settle a message whose effect is still only half-written by another instance". **다만 그 상황이 PostgreSQL에서 실제로 발생 가능한지 확인하지 않았다**(§16). `ON CONFLICT DO NOTHING`이 미커밋 충돌에 대해 대기한다면 `CLAIMED_ELSEWHERE`는 도달 불가능한 상태이고 enum이 과설계인 것이며, 즉시 0을 반환한다면 이것은 실제 결함이다.
- **확인 방법.** 두 커넥션에서 같은 (message, consumer)를 예약하고 한쪽을 커밋하지 않은 채 다른 쪽의 `executeUpdate()` 반환을 관측한다 — `InboxPostgresIT`에 추가 가능하다.
