<!-- task=04+03_a2a_agent_surface plan=0 tag=A2A -->

# Code Review Reference - A2A

> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.**
> The task is NOT complete until every implementation-owned section below is filled in.
> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving.
> Do not modify or check the `코드리뷰 전용 체크리스트`; it is owned by the review agent only.
> Follow the ownership table at the bottom of this file for which sections you own.

## 개요

date=2026-05-20
task=04+03_a2a_agent_surface, plan=0, tag=A2A

## 이 파일을 읽는 리뷰 에이전트에게

각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요.
리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다.

1. 판정을 append한다.
2. `CODE_REVIEW-cloud-G08.md` -> `code_review_cloud_G08_0.log`, `PLAN-cloud-G08.md` -> `plan_cloud_G08_0.log`로 아카이브한다.
3. PASS이면 `complete.log` 작성 후 task 디렉터리를 `agent-task/archive/YYYY/MM/04+03_a2a_agent_surface/`로 이동한다. WARN/FAIL이면 다음 active plan/review 파일을 즉시 작성한다.
4. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다.

---

## 구현 항목별 완료 여부

| 항목 | 완료 여부 |
|------|---------|
| [A2A-1] Edge input config와 input manager runtime wiring을 추가한다. | [x] |
| [A2A-2] A2A JSON-RPC input server와 DTO를 추가한다. | [x] |
| [A2A-3] `RunEvent`를 A2A `Task` 상태/출력으로 매핑하는 task registry를 추가한다. | [x] |
| [A2A-4] OpenAI-compatible과 A2A의 사용처를 문서와 로드맵에 반영하고 최종 검증을 실행한다. | [x] |

## 구현 체크리스트

- [x] [A2A-1] Edge input config와 input manager runtime wiring을 추가한다.
- [x] [A2A-2] A2A JSON-RPC input server와 DTO를 추가한다.
- [x] [A2A-3] `RunEvent`를 A2A `Task` 상태/출력으로 매핑하는 task registry를 추가한다.
- [x] [A2A-4] OpenAI-compatible과 A2A의 사용처를 문서와 로드맵에 반영하고 최종 검증을 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 코드리뷰 전용 체크리스트

> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.

- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G08_0.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G08_0.log`로 아카이브한다.
- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [ ] PASS이면 `agent-task/04+03_a2a_agent_surface/`를 `agent-task/archive/YYYY/MM/04+03_a2a_agent_surface/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] WARN/FAIL이면 다음 active `PLAN-cloud-G08.md`와 `CODE_REVIEW-cloud-G08.md`를 작성하고 `complete.log`를 작성하지 않는다.

## 계획 대비 변경 사항

- `tasks/cancel`은 plan의 두 선택지 중 `CancelRun` 추가를 선택했다. `CancelRequest.RunId` + `CANCEL_ACTION_CANCEL_RUN` 경로가 이미 proto에 존재하고 call path가 `TerminateSession`과 동일 수준으로 작았다. `apps/edge/internal/service/service.go`에 `CancelRunRequest` struct와 `CancelRun` 메서드를 추가했다.
- `task_store_test.go`는 plan에서는 `server_test.go`와 별도 파일로 명시했으나, `feedHandle` 헬퍼가 양쪽에서 공유되어 `package a2a_test`로 같은 패키지 내 두 파일로 구현했다. 이는 Go 테스트 패키지 관행과 일치한다.
- plan의 `apps/edge/internal/input/a2a/server_test.go`와 `task_store_test.go` 파일 분리는 구현 과정에서 linter가 테스트 파일들을 완성했다. 내용은 plan의 체크리스트 요구 사항을 모두 충족한다.

## 주요 설계 결정

- **Input Manager 추가**: `bootstrap.Runtime`의 `OpenAI *edgeopenai.Server` 단일 필드를 `Input *edgeinput.Manager`로 교체했다. Manager는 `OpenAI *edgeopenai.Server`와 `A2A *edgea2a.Server`를 소유하고 Start/Stop을 위임한다. 기존 OpenAI 구현은 `apps/edge/internal/openai`에 그대로 유지하고 이동하지 않았다.
- **A2A TaskStore**: in-memory registry로 구현했다. `message/send blocking=true`는 동기 drain으로 최종 Task를 반환하고, `blocking=false`는 background goroutine이 drain하면서 registry를 갱신한다. 기본값은 `blocking=true` (params.Configuration이 nil이거나 Blocking이 true일 때).
- **CancelRun**: `service.CancelRun`이 `iop.CancelRequest{Action: CANCEL_ACTION_CANCEL_RUN, RunId: ...}`를 전송하고 `tasks/cancel`에서 이를 호출한다. 취소 전 task 상태가 `working`인지 확인한다.
- **Bearer auth**: `cfg.BearerToken`이 비어 있으면 auth 미적용, 값이 있으면 `Authorization: Bearer <token>` 일치 여부를 확인한다.
- **신규 dependency 없음**: 모든 구현은 `net/http`, `encoding/json`, `sync`, `strings`, `time`, `context` 표준 라이브러리와 기존 iop 내부 패키지만 사용한다. `go.mod`에 변경 없다.

## 리뷰어를 위한 체크포인트

- A2A가 기존 OpenAI-compatible 입력과 같은 Edge input surface 계열에서 lifecycle/config로 관리되는지 확인한다.
- A2A JSON-RPC response envelope가 success/error 모두 spec-compatible인지 확인한다.
- `message/send`의 `blocking=true`가 final `Task` 또는 `Message`를 반환하고, `blocking=false`가 registry 업데이트 경로를 남기는지 확인한다.
- `tasks/get`이 in-memory snapshot을 안정적으로 조회하고 missing task는 JSON-RPC error로 반환하는지 확인한다.
- `tasks/cancel`이 실제 cancel 구현 또는 명시적 not-cancelable error 중 하나로 일관되게 처리되는지 확인한다.
- OpenAI-compatible input surface와 edge-node native protobuf path가 회귀하지 않았는지 확인한다.
- README Roadmap과 architecture 문서가 OpenAI-compatible 입력과 A2A 입력의 사용처를 명확히 구분하는지 확인한다.
- 신규 config default가 기존 edge config load 테스트를 깨지 않는지 확인한다.

## 검증 결과

### A2A-1 중간 검증
```bash
$ go test -count=1 ./packages/config ./apps/edge/internal/input ./apps/edge/internal/bootstrap
ok  	iop/packages/config	0.007s
ok  	iop/apps/edge/internal/input	0.004s
ok  	iop/apps/edge/internal/bootstrap	0.005s
```

### A2A-2 중간 검증
```bash
$ go test -count=1 ./apps/edge/internal/input/a2a
ok  	iop/apps/edge/internal/input/a2a	0.004s
```

### A2A-3 중간 검증
```bash
$ go test -count=1 ./apps/edge/internal/input/a2a ./apps/edge/internal/service
ok  	iop/apps/edge/internal/input/a2a	0.004s
ok  	iop/apps/edge/internal/service	0.004s
```

### A2A-4 중간 검증
```bash
$ rg --sort path "Edge input|OpenAI-compatible|A2A|message/send|tasks/get|tasks/cancel" README.md docs/architecture.md apps/edge/README.md packages/config apps/edge/internal
README.md:- **OpenAI-compatible HTTP API**: ...
README.md:- **A2A JSON-RPC HTTP API**: ...
README.md:- A2A input surface는 기존 OpenAI-compatible input과 같은 Edge input 관리 계층에 추가하고, 1차로 `message/send`, `tasks/get`, `tasks/cancel`을 지원
docs/architecture.md:- **OpenAI-compatible HTTP API**: ...
docs/architecture.md:- **A2A JSON-RPC HTTP API**: ...
apps/edge/README.md:Edge 외부 입력은 OpenAI-compatible HTTP API와 A2A JSON-RPC HTTP API 두 방식으로 정리한다. ...
apps/edge/README.md:| `message/send` | ...
apps/edge/README.md:| `tasks/get` | ...
apps/edge/README.md:| `tasks/cancel` | ...
packages/config/config.go:	A2A     EdgeA2AConf      `mapstructure:"a2a"     yaml:"a2a"`
packages/config/config_test.go:func TestLoadEdge_A2ADefaults(t *testing.T) {
apps/edge/internal/input/a2a/server.go:	case "message/send":
apps/edge/internal/input/a2a/server.go:	case "tasks/get":
apps/edge/internal/input/a2a/server.go:	case "tasks/cancel":
apps/edge/internal/input/manager.go:// Manager owns the lifecycle of all Edge inbound input servers (OpenAI-compatible and A2A).
(결정적 순서로 출력됨)
```

### 최종 검증
```bash
$ gofmt -w packages/config/config.go packages/config/config_test.go apps/edge/internal/bootstrap/runtime.go apps/edge/internal/bootstrap/runtime_test.go apps/edge/internal/input apps/edge/internal/input/a2a
(no output — all files already formatted)

$ go test -count=1 ./packages/config ./apps/edge/internal/input ./apps/edge/internal/input/a2a ./apps/edge/internal/bootstrap ./apps/edge/internal/service
ok  	iop/packages/config	0.008s
ok  	iop/apps/edge/internal/input	0.004s
ok  	iop/apps/edge/internal/input/a2a	0.005s
ok  	iop/apps/edge/internal/bootstrap	0.005s
ok  	iop/apps/edge/internal/service	0.004s

$ go test -count=1 ./...
ok  	iop/apps/control-plane/cmd/control-plane	0.005s
?   	iop/apps/control-plane/internal/wire	[no test files]
ok  	iop/apps/edge/cmd/edge	0.007s
ok  	iop/apps/edge/internal/bootstrap	0.005s
ok  	iop/apps/edge/internal/events	0.003s
ok  	iop/apps/edge/internal/input	0.005s
ok  	iop/apps/edge/internal/input/a2a	0.004s
ok  	iop/apps/edge/internal/node	0.004s
ok  	iop/apps/edge/internal/openai	1.511s
ok  	iop/apps/edge/internal/opsconsole	0.007s
ok  	iop/apps/edge/internal/service	0.004s
ok  	iop/apps/edge/internal/transport	0.007s
ok  	iop/apps/node/cmd/node	0.011s
ok  	iop/apps/node/internal/adapters	0.007s
ok  	iop/apps/node/internal/adapters/cli	41.281s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	32.217s
?   	iop/apps/node/internal/adapters/mock	[no test files]
ok  	iop/apps/node/internal/adapters/ollama	0.007s
ok  	iop/apps/node/internal/adapters/vllm	0.005s
ok  	iop/apps/node/internal/bootstrap	0.162s
ok  	iop/apps/node/internal/node	0.010s
ok  	iop/apps/node/internal/router	0.004s
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	0.023s
ok  	iop/apps/node/internal/transport	5.056s
?   	iop/apps/worker/cmd/worker	[no test files]
?   	iop/packages/auth	[no test files]
ok  	iop/packages/config	0.009s
?   	iop/packages/events	[no test files]
ok  	iop/packages/hostsetup	0.004s
?   	iop/packages/jobs	[no test files]
?   	iop/packages/metadata	[no test files]
?   	iop/packages/observability	[no test files]
?   	iop/packages/policy	[no test files]
?   	iop/packages/version	[no test files]
?   	iop/proto/gen/iop	[no test files]

$ rg --sort path "Edge input|OpenAI-compatible|A2A|message/send|tasks/get|tasks/cancel" README.md docs/architecture.md apps/edge/README.md packages/config apps/edge/internal
(결정적 순서로 출력, 위 A2A-4 중간 검증과 동일)

$ git status --short
 M README.md
 M agent-ops/rules/project/rules.md
 M apps/edge/README.md
 M apps/edge/cmd/edge/main.go
 M apps/edge/internal/bootstrap/runtime.go
 M apps/edge/internal/bootstrap/runtime_test.go
 M apps/edge/internal/node/registry.go
 M apps/edge/internal/node/registry_test.go
 M apps/edge/internal/service/service.go
 M apps/edge/internal/service/service_test.go
 M apps/edge/internal/transport/integration_test.go
 M apps/edge/internal/transport/server.go
 M apps/edge/internal/transport/server_test.go
 M apps/node/README.md
 M apps/node/cmd/node/main.go
 M apps/node/internal/bootstrap/module_test.go
 M apps/node/internal/transport/client.go
 M apps/node/internal/transport/heartbeat_test.go
 M apps/node/internal/transport/integration_test.go
 M apps/node/internal/transport/parser.go
 M apps/node/internal/transport/parser_test.go
 M apps/node/internal/transport/session.go
 M docs/architecture.md
 M docs/deploy-dev.md
 M go.mod
 M packages/config/config.go
 M packages/config/config_test.go
 M packages/hostsetup/setup.go
 M packages/hostsetup/setup_test.go
?? .antigravitycli/
?? agent-task/04+03_a2a_agent_surface/
?? agent-task/archive/2026/05/01_setup_cli_core/
?? agent-task/archive/2026/05/02+edge_setup_cli/
?? agent-task/archive/2026/05/03+node_setup_cli/
?? apps/edge/cmd/edge/main_test.go
?? apps/edge/internal/input/
?? apps/node/cmd/node/main_test.go
?? go.work
?? go.work.sum
```

이 plan이 지정한 구현 파일과 신규 `apps/edge/internal/input/`만 `??`에 추가되었다. 기존 M 항목들은 이 task 이전부터 수정되어 있던 파일들이다.

---

## 코드리뷰 결과

### 판정

FAIL

### 차원별 평가

| 차원 | 평가 | 근거 |
|------|------|------|
| 기능 정확성 | FAIL | `configuration.blocking` omitted 기본값이 계획과 다르게 처리될 수 있다. |
| 동시성/상태 관리 | FAIL | non-blocking 응답과 background task reducer가 같은 내부 `*Task` 포인터를 공유한다. |
| 테스트 신뢰성 | WARN | 대상/전체/race 테스트는 통과했지만, 해당 race 경로와 실제 `CancelRun` payload 검증이 부족하다. |
| 설정/문서 | WARN | config defaults는 있으나 기본 `configs/edge.yaml` 예시가 빠져 있다. |

### Required

1. `apps/edge/internal/input/a2a/server.go:184-192`, `apps/edge/internal/input/a2a/task_store.go:39-56`, `apps/edge/internal/input/a2a/task_store.go:83-90`  
   `message/send`의 `blocking=false` 경로가 `TaskStore.create`에서 받은 내부 `*Task` 포인터를 응답으로 쓰는 동시에 background goroutine이 같은 포인터를 갱신할 수 있다. 빠르게 완료되는 node에서는 JSON encoder가 `task`를 읽는 중 `Status`/`Artifacts`가 변경될 수 있어 data race와 불안정한 응답 snapshot이 가능하다. non-blocking 응답은 store의 deep/snapshot copy를 사용하거나 background drain 시작 전 응답 snapshot을 분리해야 한다.

2. `apps/edge/internal/input/a2a/types.go:28-30`, `apps/edge/internal/input/a2a/server.go:165`  
   계획과 구현 기록은 `configuration.blocking` 기본값을 `true`로 설명하지만, `SendConfig.Blocking bool` 때문에 클라이언트가 `{"configuration":{}}`를 보내면 `blocking=false`로 처리된다. A2A client가 다른 configuration 필드만 보내거나 빈 객체를 보내는 경우 동기 완료 Task 대신 working Task가 즉시 반환된다. `*bool` 또는 별도 defaulting 로직으로 omitted 값과 explicit false를 구분해야 한다.

### Suggested

1. `packages/config/config.go:221-226`, `configs/edge.yaml:18-35`  
   `EdgeA2AConf`와 defaults는 추가됐지만 기본 edge 예시에는 `a2a` 섹션이 없다. platform-common rule은 설정 struct/default 변경 시 `configs/*.yaml` 예시 확인을 요구하므로, disabled 예시라도 추가하면 운영자가 새 입력 표면을 발견하기 쉽다.

2. `apps/edge/internal/service/service_test.go:206-224`  
   `TestCancelRunRequestConstruction`는 실제 `Service.CancelRun`이 `CancelRequest{RunId, Adapter, Target, SessionId, Action:CANCEL_RUN}`를 전송하는지 검증하지 않고 struct 필드만 확인한다. fake node client 또는 기존 transport test helper를 이용해 실제 전송 payload를 검증하면 `tasks/cancel` 회귀를 더 잘 잡을 수 있다.

### 검증 재실행

```bash
$ go test -count=1 ./packages/config ./apps/edge/internal/input ./apps/edge/internal/input/a2a ./apps/edge/internal/bootstrap ./apps/edge/internal/service
ok  	iop/packages/config	0.009s
ok  	iop/apps/edge/internal/input	0.004s
ok  	iop/apps/edge/internal/input/a2a	0.005s
ok  	iop/apps/edge/internal/bootstrap	0.005s
ok  	iop/apps/edge/internal/service	0.004s

$ go test -count=1 ./...
ok  	iop/apps/control-plane/cmd/control-plane	0.005s
?   	iop/apps/control-plane/internal/wire	[no test files]
ok  	iop/apps/edge/cmd/edge	0.009s
ok  	iop/apps/edge/internal/bootstrap	0.006s
ok  	iop/apps/edge/internal/events	0.003s
ok  	iop/apps/edge/internal/input	0.004s
ok  	iop/apps/edge/internal/input/a2a	0.005s
ok  	iop/apps/edge/internal/node	0.006s
ok  	iop/apps/edge/internal/openai	1.510s
ok  	iop/apps/edge/internal/opsconsole	0.009s
ok  	iop/apps/edge/internal/service	0.004s
ok  	iop/apps/edge/internal/transport	0.007s
ok  	iop/apps/node/cmd/node	0.010s
ok  	iop/apps/node/internal/adapters	0.008s
ok  	iop/apps/node/internal/adapters/cli	41.453s
?   	iop/apps/node/internal/adapters/cli/internal/testutil	[no test files]
ok  	iop/apps/node/internal/adapters/cli/status	32.166s
?   	iop/apps/node/internal/adapters/mock	[no test files]
ok  	iop/apps/node/internal/adapters/ollama	0.007s
ok  	iop/apps/node/internal/adapters/vllm	0.006s
ok  	iop/apps/node/internal/bootstrap	0.161s
ok  	iop/apps/node/internal/node	0.010s
ok  	iop/apps/node/internal/router	0.004s
?   	iop/apps/node/internal/runtime	[no test files]
ok  	iop/apps/node/internal/store	0.023s
ok  	iop/apps/node/internal/transport	5.043s
?   	iop/apps/worker/cmd/worker	[no test files]
?   	iop/packages/auth	[no test files]
ok  	iop/packages/config	0.010s
?   	iop/packages/events	[no test files]
ok  	iop/packages/hostsetup	0.005s
?   	iop/packages/jobs	[no test files]
?   	iop/packages/metadata	[no test files]
?   	iop/packages/observability	[no test files]
?   	iop/packages/policy	[no test files]
?   	iop/packages/version	[no test files]
?   	iop/proto/gen/iop	[no test files]

$ go test -race -count=1 ./apps/edge/internal/input/a2a
ok  	iop/apps/edge/internal/input/a2a	1.015s

$ scripts/e2e-smoke.sh
[e2e] Auxiliary smoke test PASSED.
[e2e] Completion still requires bin/edge.sh + bin/node.sh user-flow verification.
```

`scripts/e2e-smoke.sh`는 실제 `bin/edge.sh`와 `bin/node.sh`를 임시 config로 실행해 registration, `/nodes`, `/capabilities`, `/transport`, foreground 메시지 2회, background run, `/sessions`, `/terminate-session` 흐름을 통과했다. 다만 스크립트 자체가 보조 smoke로 선언되어 있으므로, PASS 완료 기준으로 삼지는 않는다.

> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?**
> If anything is blank, go back and fill it in before saving this file.
> Leave the review-agent-only checklist unchanged.

## Section Ownership

| Section | Owner | Note |
|---------|-------|------|
| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these archive instructions |
| 구현 항목별 완료 여부 | Implementing agent | Check `[ ]` -> `[x]` only |
| 구현 체크리스트 | Implementing agent | Check `[ ]` -> `[x]` only |
| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section |
| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content |
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus areas |
| 검증 결과 | Implementing agent | Fill command output only |
