<!-- task=m-inbound-queue-ordering/07_python_gateway plan=2 tag=REVIEW_REVIEW_API -->

# Python Worker Gateway Close Race 후속 계획

## 이 파일을 읽는 구현 에이전트에게

이 계획은 `code_review_cloud_G08_1.log`의 Required 이슈만 해결한다. 이전 후속에서 process parse 실패, full queue backpressure, whitespace, 전체 matrix는 통과 근거가 있으므로 불필요하게 다시 구조를 크게 바꾸지 않는다. 구현 후 `CODE_REVIEW-cloud-G08.md`의 구현 에이전트 소유 섹션에 실제 변경 내용과 검증 stdout/stderr를 채운 뒤 active 파일을 그대로 두고 리뷰를 요청한다.

## 배경

`close()`는 gateway task set을 한 번만 snapshot해 기다린다. asyncio gateway path는 outer `_process_and_enqueue` task 안에서 inner `_async_worker` task를 새로 만들기 때문에, close가 outer task만 기다리고 inner task를 놓칠 수 있다. 이 경우 close가 receive task를 취소한 뒤 inner worker가 뒤늦게 inbound queue에 item을 넣어 consumer 없는 queue item이 남는다.

## 사용자 리뷰 요청 흐름

구현 중 user-only blocker가 있으면 active review stub의 `사용자 리뷰 요청` 섹션을 채운다. 이 race는 repo 내부 코드와 테스트로 해결 가능한 문제이므로 사용자 리뷰 요청 대상이 아니다.

## Roadmap Targets

- Milestone: `agent-roadmap/milestones/inbound-queue-ordering.md`
- Task ids:
  - `python-gateway`: Python process/asyncio worker gateway 후보를 적용한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-task/m-inbound-queue-ordering/07_python_gateway/plan_cloud_G08_1.log`
- `agent-task/m-inbound-queue-ordering/07_python_gateway/code_review_cloud_G08_1.log`
- `python/proto_socket/communicator.py`
- `python/test/test_communicator.py`

### 범위 결정 근거

- 수정 범위는 asyncio gateway close lifecycle race와 그 회귀 테스트다.
- process gateway parse 실패 drop, full queue await put, whitespace 정리는 이미 통과했으므로 건드리지 않는다.
- 가능하면 nested inner task를 없애고 `_process_and_enqueue` 자체가 asyncio gateway parse와 dispatch를 끝까지 소유하도록 단순화한다.

### 빌드 등급

- build/review: `cloud-G08`. 이유: asyncio task lifecycle, close/cancel race, test scheduler 제어가 포함된다.

## 구현 체크리스트

- [ ] asyncio gateway path에서 close가 놓칠 수 있는 nested worker task 구조를 제거하거나 close가 새로 추가된 gateway task까지 반복 drain/cancel하도록 수정한다.
- [ ] close 직후 inner worker가 늦게 완료되어도 consumer 없는 `_inbound_queue` item이 남지 않도록 한다.
- [ ] Python test에 asyncio gateway worker completion을 지연시키는 close race 회귀 검증을 추가한다.
- [ ] 중간 검증으로 `cd python && python3 -m pytest -q test/test_communicator.py`를 실행한다.
- [ ] 최종 검증으로 `bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all`을 실행한다.
- [ ] `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_REVIEW_API-1] asyncio gateway close race

#### 문제

close가 `_gateway_tasks` snapshot을 한 번만 기다리는 동안 outer gateway task가 inner `_async_worker` task를 새로 등록할 수 있다. 새 inner task는 close 대기 대상에서 빠지고, receive task 취소 이후 queue에 item을 남길 수 있다.

#### 해결 방법

- 선호: asyncio gateway에서 inner task를 만들지 말고 `_process_and_enqueue` 안에서 `await asyncio.sleep(0)`, parse, `_dispatch_gateway_result`를 직접 수행한다.
- 대안: close에서 `_gateway_tasks`가 안정적으로 빌 때까지 반복적으로 gather/cancel하고, close 시작 이후 새 dispatch가 queue에 들어가지 않도록 상태를 검사한다.

#### 테스트 작성

- `proto_socket.communicator.asyncio.sleep` 또는 더 안전한 local hook/monkeypatch로 gateway worker yield를 지연시킨 뒤, `on_received_data` 직후 `close()`를 호출한다.
- close 후 충분히 기다려도 listener 미호출 상태에서 `_inbound_queue.qsize() == 0`, `_receive_task.done() == True`, gateway task set empty를 검증한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `python/proto_socket/communicator.py` | REVIEW_REVIEW_API-1 |
| `python/test/test_communicator.py` | REVIEW_REVIEW_API-1 |

## 최종 검증

```bash
bash agent-ops/skills/project/run-proto-socket-test-matrix/scripts/run_matrix.sh --all
```

모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.
