<!-- task=m-gito-protosocket-consumer-wire-readiness/03+01_failure_ops plan=1 tag=REVIEW_REFACTOR -->

# Plan - REVIEW_REFACTOR

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수입니다. 구현 후 검증을 실행하고 실제 stdout/stderr를 붙인 뒤 active 파일을 그대로 두고 리뷰 준비를 보고하세요. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용입니다.

구현 중 사용자만 결정할 수 있는 범위 변경, 사용자 소유 외부 환경/secret, 또는 scope conflict가 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 근거와 재개 조건을 채우고 멈추세요. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않으며, `USER_REVIEW.md`, archive log, `complete.log`를 만들지 않습니다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰가 아닙니다.

## 배경

1차 리뷰는 runner reconnect/shutdown 구현과 대부분의 검증이 재현됨을 확인했지만, 필수 범위였던 malformed binary frame 회귀 테스트가 실제 binary decode failure를 만들지 못해 FAIL 처리되었습니다. 이번 후속은 해당 검증 신뢰를 회복하는 데만 집중합니다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-protosocket-consumer-wire-readiness.md`
- Task ids:
  - `failure-ops`: reconnect, not-ready revision, duplicate event, malformed frame 처리와 로그 기준 정리
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-gito-protosocket-consumer-wire-readiness/03+01_failure_ops/plan_cloud_G06_0.log`
- Archived review: `agent-task/m-gito-protosocket-consumer-wire-readiness/03+01_failure_ops/code_review_cloud_G06_0.log`
- Verdict: FAIL
- Required summary:
  - `services/core/internal/gitosync/runner_test.go:792`의 `serverClient.Send((*structpb.Struct)(nil))`는 실제 malformed binary frame 또는 invalid protobuf payload를 검증하지 못한다. write/send error도 확인하지 않아, 테스트가 정상 이벤트 수신만 확인하는 형태로 약해졌다.
  - `services/core/internal/gitosync/runner.go:73`의 listener는 proto-socket이 이미 `*structpb.Struct`로 파싱한 메시지만 받으므로 invalid `PacketBase`/invalid binary payload 경계는 별도 테스트로 고정해야 한다.
- Suggested/Nit summary: 없음.
- Affected files:
  - `services/core/internal/gitosync/runner_test.go`
  - `services/core/internal/gitosync/runner.go` only if the stronger test exposes behavior gap
- Verification evidence from review:
  - `git diff --check`: PASS, no output.
  - `cd services/core && go test ./internal/gitosync -run 'TestRunner(StopsCleanly|Reconnects|DropsMalformed)' -count=1`: PASS.
  - `cd services/core && go test ./internal/gitoevents ./internal/gitosync -count=1`: PASS.
  - `cd services/core && go test ./...`: PASS, package output contained cached entries.
  - `cd services/core && go test -race ./internal/gitosync -run 'TestRunner(ReconnectsAfterUnexpectedDisconnect|DropsMalformedBinaryFrameWithoutEnqueue)' -count=1`: PASS.
- Roadmap carryover: `failure-ops` remains the completion target.
- Narrow reread allowed:
  - `agent-task/m-gito-protosocket-consumer-wire-readiness/03+01_failure_ops/plan_cloud_G06_0.log`
  - `agent-task/m-gito-protosocket-consumer-wire-readiness/03+01_failure_ops/code_review_cloud_G06_0.log`

## 범위 결정 근거

1차 구현의 reconnect, context cancel, duplicate/not-ready, full test evidence는 재현되었습니다. 후속 범위는 malformed binary/protobuf frame 검증 신뢰 회복과 그 테스트가 드러내는 최소 구현 보정으로 제한합니다.

## 빌드 등급

`cloud-G06`을 유지합니다. 비동기 proto-socket lifecycle 검증이 포함되지만, follow-up은 한 테스트/transport 경계로 제한됩니다.

## 구현 체크리스트

- [ ] archived FAIL finding과 현재 `runner.go`/`runner_test.go` 동작을 확인한다. archive는 위 Snapshot에 명시된 두 파일만 좁게 읽는다.
- [ ] `TestRunnerDropsMalformedBinaryFrameWithoutEnqueue`를 실제 malformed binary/protobuf frame 주입 테스트로 강화한다. `serverClient.Send((*structpb.Struct)(nil))`와 무시된 send error에 의존하지 말고 raw websocket write 또는 `PacketBase`/invalid struct payload를 명시적으로 사용한다.
- [ ] malformed 입력 직후 handler/enqueuer가 호출되지 않고, 이후 정상 `branch.updated` 이벤트가 정확히 1회 처리되는 assertion을 둔다. malformed packet이 transport disconnect로 분류된다면 `ReconnectingRunner` 경로로 재연결 후 정상 이벤트를 검증한다.
- [ ] 강화한 테스트가 실패하면 runner/transport 구현을 최소 수정해 malformed binary/protobuf 입력이 payload/secret 없는 safe drop 또는 reconnect로 수렴하게 한다.
- [ ] `git diff --check`, `cd services/core && go test ./internal/gitosync -run 'TestRunnerDropsMalformedBinaryFrameWithoutEnqueue' -count=1`, `cd services/core && go test ./internal/gitoevents ./internal/gitosync -count=1`, `cd services/core && go test ./...`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 구현 항목

### [REVIEW_REFACTOR-1] Restore malformed binary frame verification trust

문제: 1차 테스트는 `serverClient.Send((*structpb.Struct)(nil))` 결과를 확인하지 않고, 실제 invalid websocket binary frame 또는 invalid protobuf payload가 consumer side-effect 없이 처리되는지 고정하지 않습니다.

해결 방법: 테스트 서버에서 malformed 입력을 deterministic하게 주입합니다. raw websocket binary frame을 쓰는 경우 runner를 `NewReconnectingRunner`로 실행해 disconnect/reconnect 후 정상 이벤트 1회 수신을 검증합니다. invalid `PacketBase` payload를 쓰는 경우 write 결과를 확인하고 정상 이벤트 전 handler 호출이 없음을 검증합니다. 구현이 이 강화 테스트를 통과하지 못하면 transport error classification만 최소 수정합니다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/gitosync/runner_test.go`의 malformed frame 테스트를 강화한다.
- [ ] 필요 시 `services/core/internal/gitosync/runner.go`의 malformed frame/drop/reconnect classification을 최소 보정한다.

테스트 작성: 필수.

중간 검증:

```bash
cd services/core && go test ./internal/gitosync -run 'TestRunnerDropsMalformedBinaryFrameWithoutEnqueue' -count=1
```

## 수정 파일 요약

| 파일 | 항목 |
| --- | --- |
| `services/core/internal/gitosync/runner_test.go` | REVIEW_REFACTOR-1 |
| `services/core/internal/gitosync/runner.go` | REVIEW_REFACTOR-1 if stronger regression exposes a behavior gap |

## 최종 검증

```bash
git diff --check
cd services/core && go test ./internal/gitosync -run 'TestRunnerDropsMalformedBinaryFrameWithoutEnqueue' -count=1
cd services/core && go test ./internal/gitoevents ./internal/gitosync -count=1
cd services/core && go test ./...
```

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