<!-- task=m-gito-protosocket-consumer-wire-readiness/01_wire_transport_decode plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

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

이 계획은 이전 리뷰에서 나온 Required 이슈만 닫는 follow-up입니다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret, 또는 scope conflict 없이는 안전하게 진행할 수 없을 때만 `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션을 채우고 멈추세요. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하지 않습니다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/gito-protosocket-consumer-wire-readiness.md`
- Task ids:
  - `transport-adapter`: JSON text websocket transport를 실제 proto-socket binary/protobuf transport 또는 동등 adapter로 교체
  - `envelope-decode`: binary frame에서 받은 `structpb.Struct` envelope을 기존 `gitoevents.DecodeBranchUpdatedEnvelope` 입력으로 안전하게 변환
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Current archived plan: `agent-task/m-gito-protosocket-consumer-wire-readiness/01_wire_transport_decode/plan_cloud_G07_0.log`
- Current archived review: `agent-task/m-gito-protosocket-consumer-wire-readiness/01_wire_transport_decode/code_review_cloud_G07_0.log`
- Verdict: FAIL
- Required summary:
  - `services/core/internal/gitosync/runner.go:64` and `services/core/internal/gitosync/runner.go:74` silently drop inbound decoded envelopes and disconnect EOF when the buffered channel is full.
  - `services/core/internal/gitosync/runner_test.go:123` verifies transport `Write`/`Read` manually but does not prove `gitoevents.Client.Run` delivers the binary `branch.updated` event to the handler exactly once.
  - `services/core/internal/gitosync/runner_test.go:89` covers invalid packet bytes but not valid-Struct malformed/mismatched envelope side-effect behavior through the real transport/client path.
- Affected files:
  - `services/core/internal/gitosync/runner.go`
  - `services/core/internal/gitosync/runner_test.go`
  - `services/core/internal/gitoevents/events_test.go` if an extra decoder regression is needed
- Verification evidence from prior loop:
  - `git diff --check` passed after review-applied `gofmt`.
  - `cd services/core && go test ./internal/gitosync ./internal/gitoevents ./internal/protosocket` passed.
  - `cd services/core && go test ./...` passed.
  - `cd services/core && go test ./internal/gitosync -run TestRunnerTransportUsesBinaryProtoSocketSubscribeAndEvent -count=1 -v` passed.
- Roadmap carryover: keep the same `transport-adapter` and `envelope-decode` targets; do not mark roadmap tasks directly.
- Allowed archive reread: only the two archived files listed above if exact prior wording is needed.

## 범위 결정 근거

이 follow-up은 active `01_wire_transport_decode`의 실패 원인만 수정합니다. README, compose, roadmap 상태 전환, bridge e2e, reconnect policy, Plane mutation semantics는 범위 밖입니다.

## 구현 체크리스트

- [ ] inbound decoded envelopes and disconnect/error signals are never silently dropped; overflow or close is either backpressured or returned as an explicit `Read` error.
- [ ] a real proto-socket server plus `gitoevents.Client.Run` test proves `event.subscribe` is sent and exactly one on-target binary `branch.updated` event reaches the handler.
- [ ] valid-Struct malformed/mismatched frames, including wrong channel/action and missing payload, are covered through the real transport/client side-effect boundary before a valid event.
- [ ] `gofmt`, `git diff --check`, `cd services/core && go test ./internal/gitosync ./internal/gitoevents ./internal/protosocket`, and `cd services/core && go test ./...` are executed.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_API-1] Make inbound queue overflow and disconnect observable

문제: `protoSocketTransport` currently uses non-blocking sends with `default` in the typed listener and disconnect listener. This can silently lose `branch.updated` wakeups or lose the terminal EOF/error signal.

해결 방법:
- Add a small helper for enqueueing inbound results so decoded envelopes and terminal errors share one policy.
- Prefer backpressure over silent loss, or close/mark the transport with an explicit overflow error that `Read` returns deterministically.
- Ensure `Read(ctx)` cannot block forever after the websocket has disconnected just because an EOF/error notification was dropped.
- Keep the fix local to `services/core/internal/gitosync/runner.go`.

테스트 작성:
- Add a focused test in `services/core/internal/gitosync/runner_test.go` that would fail against the prior lossy `default` path.
- The test should prove either all queued branch events are eventually observable, or overflow is surfaced as a deterministic error rather than silent data loss.
- Include a disconnect-after-buffered-events case if the chosen implementation can otherwise lose EOF.

중간 검증:

```bash
cd services/core && go test ./internal/gitosync -run 'TestProtoSocketTransport.*(Overflow|Disconnect|Drop)' -count=1
```

### [REVIEW_API-2] Prove the production client handler path over binary proto-socket

문제: `TestRunnerTransportUsesBinaryProtoSocketSubscribeAndEvent` drives `protoSocketTransport` directly. It does not prove `gitoevents.Client.Run` sends the subscription and forwards the binary broadcast to the handler exactly once.

해결 방법:
- Use a real `httptest` websocket server with `toki.NewWsClient` and `protosocket.ParserMap()`.
- Build a `gitoevents.Client` with `&protoSocketTransport{url: wsURL}` and a handler channel.
- Assert the server receives `channel=event`, `action=event.subscribe`, `events=["branch.updated"]`, `repo_id`, and `branch`.
- Send one binary `branch.updated` event from the server and assert exactly one handler invocation with the expected repo/branch/revision.
- Cancel the context and assert `Run` exits cleanly.

테스트 작성:
- Suggested name: `TestProtoSocketTransportClientRunDeliversBinaryBranchUpdatedEvent`.
- This may replace or extend `TestRunnerTransportUsesBinaryProtoSocketSubscribeAndEvent`, but the final test must exercise `gitoevents.Client.Run`.

중간 검증:

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

### [REVIEW_API-3] Cover malformed and mismatched Struct frames through the side-effect boundary

문제: The current malformed test sends invalid packet bytes. The plan also required valid `structpb.Struct` envelopes with malformed or mismatched semantic content to be dropped or routed to an error path without handler side effects.

해결 방법:
- Through the same real proto-socket transport/client path, send frames such as wrong channel/action, missing payload, malformed `changed_files`, and optional non-Struct or unknown type behavior if feasible.
- Assert the handler is not called for those frames.
- Then send a valid on-target `branch.updated` event and assert the handler is called once, proving the stream remains usable after ignored semantic frames.

테스트 작성:
- Suggested name: `TestProtoSocketTransportClientRunDropsMalformedAndMismatchedStructs`.
- If a non-Struct packet cannot be observed by this parser map, document it in `계획 대비 변경 사항` and cover the explicit parser/drop behavior with the closest deterministic test.

중간 검증:

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

## 수정 파일 요약

| 파일 | 항목 |
| --- | --- |
| `services/core/internal/gitosync/runner.go` | REVIEW_API-1 |
| `services/core/internal/gitosync/runner_test.go` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |
| `services/core/internal/gitoevents/events_test.go` | REVIEW_API-3 if an extra decoder regression is clearer there |

## 최종 검증

```bash
gofmt -w services/core/internal/gitosync/runner.go services/core/internal/gitosync/runner_test.go services/core/internal/gitoevents/events_test.go
git diff --check
cd services/core && go test ./internal/gitosync ./internal/gitoevents ./internal/protosocket
cd services/core && go test ./...
```
