# task-2970 사후 검증 — PR #226 은 하드닝 미반영 상태로 머지되었다

작성: 2026-08-18 (KST) · 작성자: 아누 (스케줄 실행) · 대상: 제이회장님

## S (상황)

task-2970 `[T2] InsuRo 신규설계 비교 게이트 봉인`은 2026-08-17 23:40 KST 에 `.done`
발행 후 미처리 상태로 남아 00:10 KST 에 done-watcher 가 stale 에스컬레이션했다.

봇(dev2-team)이 남긴 `followup.txt` 의 경고는 다음과 같았다:

> ★ 머지 전 반드시 하드닝 3커밋을 원격에 반영할 것. 미반영 상태로 머지하면
> 마아트가 실제로 뚫었던 위조 axes 구멍이 열린 채 들어간다.

## C (충돌) — 경고보다 머지가 먼저 일어났다

| 항목 | 실측값 |
|---|---|
| PR #226 state | `closed` / `merged=true` |
| merged_at | 2026-08-17 23:59:07 KST (14:59:07Z) |
| merged_by | JonghyukJeon |
| 머지된 head_sha | `53ec1a260baa661d7b4d138f8f27405cc23fe8e2` |
| merge_commit_sha | `08ea5de423d255be9d0d8c36e921a0effd909f28` |
| 로컬 하드닝 HEAD | `a9b1e9c212377ac5cd7f8e1494ba03ed667a1b1d` |
| `a9b1e9c` 가 `origin/main` 의 조상인가 | **ANCESTOR_NO** |
| main 대비 누락 델타 | 3 파일 **+480 / −25** |
| main 내 하드닝 심볼 출현 수 | **0** (`axes_schema_valid`, `REASON_EXCLUDED_AXES_MALFORMED`, `REASON_EXCLUDED_MALFORMED_ENTRY`) |

즉 머지된 것은 봉인 3종 **본체**까지이고, 마아트 독립검증 + Codex 재검증이 찾아낸
**구멍 3건을 막는 하드닝은 main 에 없다**.

`merged_by` 가 회장 계정이지만, 기존 확립된 규칙대로 이것이 회장님의 직접 머지 행위를
의미하지는 않는다(OWNER PAT 경로 가능). 누가 머지했는지는 이 보고서에서 단정하지 않는다.

## Q (질문) — 그래서 구멍이 실제로 열려 있는가

봇의 주장을 승계하지 않고, main 의 `policy_grouping` 패키지를 그대로 추출해
(`git archive origin/main`) 마아트 공격 3종을 직접 실행했다.

### 수정 파일별 검증 상태

| 파일 | main 상태 | 하드닝(로컬 HEAD) 상태 | 검증 방식 |
|---|---|---|---|
| server/policy_grouping/comparison_gate.py | 구멍 3건 열림 | 3건 전부 차단 | 동일 입력 A/B 실행 대조 |
| server/tests/test_grouping_gate_seal_task2970.py | 회귀 테스트 부재 | +273줄 존재 | 심볼 grep 0건 |
| server/tests/test_policy_grouping_comparison_gate.py | 낡은 계약 | 갱신 | diff 23줄 |

### 실행 대조 결과 (동일 입력, main vs 하드닝)

| 공격 | main (현재 라이브 코드) | 하드닝 적용 시 |
|---|---|---|
| A. 위조 axes — 6축 대부분 결손 + `axes_verified=True` | **공식비교 채택 1건** (`ok_confirmed`) | 제외 (`excluded_axes_malformed`) |
| B. `single_modality` 소문자 표기 ↔ 비세분형 group | **공식비교 채택 1건** (`ok_confirmed`) | 제외 (`excluded_modality_axes_group_mismatch`) |
| C. `map_state` 키 부재 entry | **`KeyError: 'map_state'` 예외 누출** | 제외 (`excluded_malformed_entry`) |
| B2. 대조군 — 동일 시나리오 대문자 `SINGLE_MODALITY` | 정상 탐지·제외 | 정상 탐지·제외 |

B2 대조군이 대문자에서는 정상 탐지되므로, B 는 **표기 변형만으로 봉인3 탐지가 조용히
무력화**되는 것이 맞다(오탐 아님).

## A (답) — 위험도는 "확정 결함, 단 현재 외부 도달은 제한적"

### 지금 당장 오비교가 화면에 나가는가: 아니다

main 에서 `axes` 미제공(= 현 프로덕션 호출자 형태, T6 배선 전) 입력을 넣으면
전 행이 `excluded_axes_missing` 으로 제외된다 — 채택 0건. 봉인1은 main 에서 살아 있다.

### 그러면 무엇이 문제인가

1. **T6(clause_id/axes 배선)이 올라가는 순간 A/B 가 라이브 경로가 된다.** 하드닝은
   바로 그 시점을 막기 위한 것이고, 지금 main 에는 그것이 없다.
2. **B 는 조건이 갖춰지면 지금도 도달 가능하다.** `AxisIn.value` 는 자유 문자열이라
   pydantic 이 소문자 `single_modality` 를 통과시키고, `axes_verified=True` 는
   `grouping_bridge` 의 confirmed-mapping 재사용 경로에서 부여된다. 6축을 모두 채운
   요청 + 확정 매핑 재사용이면 게이트가 불일치를 놓친다.
3. A(6축 결손)는 `CoverageAxesIn` 이 6축을 필수로 요구해 **HTTP 경계에서 pydantic 이
   먼저 거른다** — 외부 도달은 막혀 있고, pydantic 을 우회하는 내부 호출자에만 열려 있다.
4. **회귀 테스트 자체가 main 에 없다.** 이후 누가 이 부분을 건드려도 잡아줄 그물이 없다.

### 봇 보고 중 정정할 점

봇의 `result.json` 은 PR 상태를 `open` / `merge_policy: HOLD` 로 기록했다. 실측은
`merged`. 봇이 `.done` 을 쓴 시각(23:40)과 머지 시각(23:59) 사이에 상태가 바뀌었고,
`result.json` 은 작성 시점 스냅샷이다 — 봇의 거짓 보고가 아니라 낡은 봉투다.

## 남은 조치 (회장 승인 필요)

원격 반영이 이번에도 아누 손에서 막혔다:

- `git push` → harness 훅이 차단 (R5). 재현 확인.
- `gh api git/refs`, `commits` → 404 지속 (contents·pulls 는 정상 200).
- 봇이 남긴 재시도 cron `0EE93E9E` → 스케줄 목록·`schedule_history` 양쪽에 기록 없음.
  실행 흔적 자체가 확인되지 않는다.

PR #226 이 이미 머지·종료되었으므로 같은 PR 재사용은 불가하고, **하드닝 3파일을 담은
신규 PR → main** 이 필요하다. 이는 라이브 저장소 대상 외부 작업이므로 승인 없이
진행하지 않았다.

권고: dev2-team(하드닝 원작자, 맥락 보유) 에 신규 PR 위임. 범위는 3파일 한정,
코드 신규 작성 없음(이미 커밋된 `a9b1e9c` 내용 이관).

## 부수 관측 (별건, 단정 아님)

- PR #225 (task-2969 정직노출) 도 `merged=true` 로 확인된다. 기록상으로는 CI e2e RED
  상태였고 후속 t2972 를 위임한 건이다. 머지 전 e2e 가 해소되었는지는 이번 검증 범위
  밖이며, 별도 확인이 필요하다.
- `clause_id` 입력 검증 부재(UUID 아닌 값 → Supabase `22P02` → HTTP 500)는 봇이
  별도 task 로 권고했다. task-2970 이전부터 존재한 결함.
