<!-- task=m-iop-agent-run-bridge/05_production_worker_bridge plan=0 tag=REVIEW_API -->

# PLAN - REVIEW_API Production Worker Bridge

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 구현의 마지막 필수 단계다. 구현과 검증을 끝낸 뒤 실제 변경 내용, 계획 대비 차이, 주요 결정, stdout/stderr를 review stub에 기록하고 active 파일을 유지한 채 리뷰 준비를 보고한다. 사용자 전용 결정, 사용자 소유 외부 환경/secret/service 준비, 또는 범위 충돌로 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 채우고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 말고, 후속 에이전트가 재실행/증거 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청으로 보지 않는다.

## 배경

`IOP Agent Run Bridge` 완료 리뷰 중 archived subtask PASS와 별개로 production entrypoint와 runtime data-flow가 아직 실제 bridge로 닫히지 않은 것을 확인했다. worker binary는 Postgres store와 revision scanner를 만들지만 runner에 picker/input loader/invoker를 nil로 전달해 queued `agent_run`을 처리하지 못한다. generic queued picker를 그대로 연결하면 비-agent operation이 running으로 갇힐 수 있고, revision scan은 agent run이 이미 알고 있는 before/after revision을 전달하지 않아 cursor 없는 첫 push 이벤트를 놓칠 수 있다. 이 계획은 milestone을 닫기 위한 production-safe runtime 연결과 회귀 테스트를 추가한다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 전용 결정은 active `CODE_REVIEW-*-G??.md`의 `사용자 리뷰 요청` 섹션에 `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식으로 기록한다. 구현 에이전트는 chat으로 직접 묻지 않고 active 파일을 유지한 채 code-review가 `USER_REVIEW.md` 작성 필요성을 판정하게 한다.

## Archive Evidence Snapshot

- Prior milestone evidence:
  - `agent-task/archive/2026/06/m-iop-agent-run-bridge/01_iop_invocation_contract/complete.log`: PASS; transport-independent input/invocation contract and fake backend evidence.
  - `agent-task/archive/2026/06/m-iop-agent-run-bridge/02+01_git_result_convergence/complete.log`: PASS; Git evidence/result convergence.
  - `agent-task/archive/2026/06/m-iop-agent-run-bridge/03_commit_push_policy/complete.log`: PASS; dirty guard and commit/push policy.
  - `agent-task/archive/2026/06/m-iop-agent-run-bridge/04+03_revision_scan_event/complete.log`: PASS; revision scan event tests, `cd services/core && go test ./...`, contract keyword scan, `git diff --check`.
- Current closure review finding: milestone cannot close until production worker wiring, agent_run-only picking, durable input loading, and cursor-free agent_run revision scan behavior are implemented and verified.
- Additional archive reread is not required by default. If exact prior evidence is needed, read only the four `complete.log` paths listed above.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-shell-iop-execution/milestones/iop-agent-run-bridge.md`
- Task ids:
  - `production-worker`: production worker가 durable agent_run input source에서 실행 입력을 로드하고, agent_run만 안전하게 pick해 IOP adapter/result path를 실제 runtime 의존성으로 연결한다.
  - `revision-scan-seed`: cursor 없는 agent_run push도 known before/after revision으로 `branch.updated` 이벤트를 emit/retry한다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-ops/rules/project/rules.md`
- `agent-ops/rules/private/rules.md`
- `agent-ops/rules/common/rules-roadmap.md`
- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/code-review/SKILL.md`
- `agent-ops/skills/common/update-roadmap/SKILL.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/contracts-smoke.md`
- `agent-test/local/workspace-ops-smoke.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/contracts/rules.md`
- `agent-ops/rules/project/domain/workspace-ops/rules.md`
- `agent-contract/index.md`
- `agent-roadmap/phase/agent-shell-iop-execution/PHASE.md`
- `agent-roadmap/current.md`
- `agent-roadmap/phase/agent-shell-iop-execution/milestones/iop-agent-run-bridge.md`
- `services/core/cmd/worker/main.go`
- `services/core/internal/config/config.go`
- `services/core/internal/core/model.go`
- `services/core/internal/worker/runner.go`
- `services/core/internal/worker/runner_test.go`
- `services/core/internal/controlplane/runtime.go`
- `services/core/internal/controlplane/runtime_test.go`
- `services/core/internal/storage/storage.go`
- `services/core/internal/storage/postgres.go`
- `services/core/internal/storage/postgres_test.go`
- `services/core/migrations/00001_initial.sql`
- `packages/contracts/notes/gito-control-plane.md`

### 테스트 환경 규칙

- test_env: local.
- `agent-test/local/rules.md`를 읽었고, matched profile로 `core-smoke.md`, `contracts-smoke.md`, `workspace-ops-smoke.md`를 읽었다.
- 적용 명령:
  - core: `cd services/core && go test ./...`
  - focused fresh Go tests: `cd services/core && go test -count=1 ./internal/worker ./internal/controlplane ./internal/storage`
  - contracts keyword scan: `rg --sort path -n "proto-socket|REST|gRPC|ChangeRequest|Operation|Event|agent_shell|agent-shell|iop" packages/contracts README.md docs`
  - workspace/roadmap keyword scan: `rg --sort path -n "Gito|agent-shell|agent_shell|iop|gito-shell|control plane" README.md docs packages/contracts agent-roadmap -g '!agent-roadmap/archive/**'`
  - diff hygiene: `git diff --check`
- `<확인 필요>` 값은 발견하지 못했다. Go test cache output은 whole-package smoke에서는 허용하되, 변경 패키지 회귀는 `-count=1`로 fresh 실행한다.

### 테스트 커버리지 공백

- Production worker wiring: `services/core/cmd/worker/main.go:49-62` 경로는 현재 nil dependency 구성이며 테스트가 없다. config/store/adapter wiring 단위 또는 integration-style test가 필요하다.
- Agent-run-only picking: `services/core/internal/storage/postgres.go:624-648` generic picker는 type filter가 없다. 기존 duplicate picker test는 generic behavior만 보므로 `agent_run` 전용 picker 회귀 테스트가 필요하다.
- Durable agent run input: `services/core/internal/core/model.go:32-42`와 `services/core/migrations/00001_initial.sql:26-36`에는 operation input payload source가 없다. loader가 실제 input을 읽는 normal/boundary 테스트가 필요하다.
- Cursor-free agent run revision scan: 기존 `runtime_test.go`는 cursor seeded path 중심이고, `runtime.go:369-379`는 no cursor이면 after를 저장하고 이벤트 없이 반환한다. known before/after가 있는 agent run 전용 회귀 테스트가 필요하다.

### 심볼 참조

- 변경 예정 symbols:
  - `storage.OperationStore.PickQueuedOperation`: 기존 generic method는 유지한다. 새 typed method 또는 adapter를 추가할 경우 `services/core/internal/storage/postgres.go`, `services/core/internal/storage/postgres_test.go`, `services/core/internal/worker/runner.go`, `services/core/cmd/worker/main.go` call site를 모두 갱신한다.
  - `worker.RevisionScanRequest`: `services/core/internal/worker/runner.go`, `services/core/internal/worker/runner_test.go`, `services/core/internal/controlplane/runtime.go`, `services/core/internal/controlplane/runtime_test.go` call site를 갱신한다.
  - durable input structs/store를 새로 추가하면 `services/core/internal/core/model.go`, `services/core/internal/storage/storage.go`, `services/core/internal/storage/postgres.go`, migration, worker loader tests를 함께 갱신한다.

### 분할 판단

split policy를 검토했다. storage picker, operation input, worker adapter, revision scan이 서로 다른 파일을 건드리지만 하나의 production bridge closure path를 이룬다. 따로 split하면 worker wiring이 input source 없이 merge되거나 revision scan request field가 producer 없이 merge되는 중간 상태가 생겨 milestone 종료 판단이 다시 흐려진다. 따라서 단일 cloud-G07 계획으로 묶고, 구현 항목을 순서화해 partial bridge를 피한다.

### 범위 결정 근거

- IOP 내부 모델 라우팅, CLI/profile/stdin/secret injection은 milestone 범위 제외이므로 구현하지 않는다.
- `../agent-shell` UI/package, provider PR/MR 생성, Plane/Jira lifecycle은 건드리지 않는다.
- 외부 protocol을 proto-socket으로 되돌리지 않는다. existing contract note의 HTTP dispatch/callback 방향만 따른다.
- 사용자 소유 외부 IOP service URL/credential이 필요한 경우 hard-code하지 말고 config와 ref boundary로만 표현한다. 실제 외부 서비스 준비가 필요해 구현자가 자동으로 검증할 수 없으면 review stub의 사용자 리뷰 요청으로 멈춘다.

### 빌드 등급

cloud-G07. Postgres schema/store, worker runtime, external HTTP adapter boundary, revision event semantics가 함께 걸린 production closure 작업이다.

## 구현 체크리스트

- [ ] `production-worker`를 위해 durable agent_run input source와 loader를 추가하고, production worker가 agent_run만 안전하게 pick해 configured IOP adapter/result path를 실행하도록 연결한다. 검증: `cd services/core && go test -count=1 ./internal/worker ./internal/storage` 및 `cd services/core && go test ./...`
- [ ] `revision-scan-seed`를 위해 agent_run known before/after revision을 revision scan에 전달하고 cursor 없는 watched branch도 `branch.updated` 이벤트를 emit/retry하도록 한다. 검증: `cd services/core && go test -count=1 ./internal/controlplane ./internal/worker`
- [ ] contracts/workspace keyword scan과 diff hygiene를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

1. Durable input source를 먼저 추가한다.
2. `agent_run` 전용 picker와 production worker wiring을 연결한다.
3. IOP adapter/result path를 config 기반으로 붙인다.
4. revision scan before/after propagation과 no-cursor event behavior를 보강한다.
5. focused tests, full core smoke, keyword scans, diff hygiene를 실행한다.

### [REVIEW_API-1] Durable agent_run input and production-safe picker

#### 문제

- `services/core/cmd/worker/main.go:49-62`는 Postgres store와 scanner를 만든 뒤 `worker.NewRunner(cfg, logger, nil, nil, scanner)`로 runner를 구성해 production worker가 항상 `worker picker not configured` 경로로 빠진다.
- `services/core/internal/storage/postgres.go:624-648`의 `PickQueuedOperation`은 operation type filter 없이 queued row를 running으로 전이한다. 이를 runner에 그대로 연결하면 `services/core/internal/worker/runner.go:170-174`에서 non-agent operation을 no-op 처리해 running 상태에 갇힐 수 있다.
- `services/core/internal/core/model.go:32-42`와 `services/core/migrations/00001_initial.sql:26-36`에는 `AgentRunInput`을 durable하게 저장/조회할 source가 없다. `services/core/internal/worker/runner.go:221-226`의 loader/invoker interface는 production 구현이 없다.

#### 해결 방법

- 기존 generic `PickQueuedOperation`의 의미는 유지한다.
- `agent_run`만 queued->running으로 전이하는 typed picker/store method를 추가한다. SQL은 기존 CTE 패턴을 재사용하되 `WHERE state=$1 AND type=$2` 조건을 둔다.
- operation input payload를 저장/조회할 최소 durable 모델을 추가한다. 권장안은 별도 `agent_run_inputs` 테이블과 store interface이며, operation row 자체의 기존 create/list semantics를 깨지 않는다.
- production `AgentRunInputLoader`는 operation id로 durable input을 읽고 `worker.AgentRunInput`으로 변환한다. 빈 workspace path, instruction, branch 등 필수 입력은 repo-owned validation error로 실패시킨다.
- production worker main은 Postgres store, typed picker, operation/event store, durable input loader, configured IOP invoker를 모두 갖춘 경우에만 agent_run runner를 구성한다. IOP endpoint config가 비어 있으면 agent_run을 pick하지 않고 명확한 startup log 또는 disabled path로 둔다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/core/model.go`: durable agent_run input 모델을 추가한다.
- [ ] `services/core/migrations/00001_initial.sql`: input table/index 또는 동등한 durable schema를 추가한다.
- [ ] `services/core/internal/storage/storage.go`: typed picker와 agent_run input store interface를 추가한다.
- [ ] `services/core/internal/storage/postgres.go`: typed picker SQL, input create/get implementation, validation을 추가한다.
- [ ] `services/core/internal/storage/postgres_test.go`: generic picker 기존 동작 유지 테스트와 agent_run-only picker/input store tests를 추가한다.
- [ ] `services/core/internal/worker/runner.go`: production loader/typed picker adapter 연결에 필요한 작은 interface 조정을 한다.
- [ ] `services/core/internal/worker/runner_test.go`: non-agent operation이 typed picker에서 선택되지 않는 회귀와 missing input failure를 추가한다.
- [ ] `services/core/internal/config/config.go`: IOP HTTP adapter에 필요한 endpoint/ref config만 추가하고 raw secret은 추가하지 않는다.
- [ ] `services/core/cmd/worker/main.go`: production dependencies를 wiring하고 nil dependency no-op path를 제거하거나 explicit disabled 상태로 만든다.

#### 테스트 작성

- 작성한다.
- `services/core/internal/storage/postgres_test.go`: `TestPostgresOperationStorePickQueuedAgentRunOperationFiltersType`, `TestPostgresAgentRunInputStoreCreateAndGet`.
- `services/core/internal/worker/runner_test.go`: typed picker/wiring adapter 또는 durable input loader failure/success tests.
- 필요 시 `services/core/cmd/worker` wiring은 extractable constructor를 만들어 unit test한다.

#### 중간 검증

```bash
cd services/core && go test -count=1 ./internal/worker ./internal/storage
```

예상: fresh 실행 PASS.

### [REVIEW_API-2] IOP HTTP adapter/result path

#### 문제

- `services/core/internal/worker/runner.go:83-89`는 `AgentRunInputLoader`와 `AgentRunInvoker` interface만 정의하고 production implementation이 없다.
- `packages/contracts/notes/gito-control-plane.md`는 HTTP dispatch/callback boundary를 기록하지만, worker binary는 해당 boundary를 config나 adapter로 연결하지 않는다.

#### 해결 방법

- raw secret 없이 endpoint/ref 중심 config를 추가한다.
- `worker` 또는 별도 internal package에 HTTP `AgentRunInvoker`를 구현한다. 입력/출력 JSON은 existing `AgentRunInput`/`AgentRunResult` contract shape와 호환되게 하고, unsupported state나 invalid response는 sanitized failure로 수렴시킨다.
- callback-only 외부 service를 즉시 검증할 수 없는 경우에도 runner가 operation을 무한 running으로 두지 않도록 synchronous dispatch/result mode 또는 명확한 unsupported config error를 선택한다. 사용자 소유 외부 service 준비 없이는 자동 검증이 불가능한 결정을 만나면 review stub의 `사용자 리뷰 요청`으로 멈춘다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/config/config.go`: endpoint/ref config를 추가한다.
- [ ] `services/core/internal/worker/` 또는 적절한 internal package: HTTP invoker implementation과 tests를 추가한다.
- [ ] `services/core/cmd/worker/main.go`: HTTP invoker를 runner dependencies에 연결한다.
- [ ] `packages/contracts/notes/gito-control-plane.md`: 실제 production adapter shape와 기존 contract note가 어긋나면 최소 문구만 갱신한다.

#### 테스트 작성

- 작성한다.
- `httptest.Server`로 success, failed/cancelled, invalid response, transport error를 검증한다.
- transport error는 raw upstream body를 operation event에 노출하지 않고 stable sanitized message로 수렴하는지 확인한다.

#### 중간 검증

```bash
cd services/core && go test -count=1 ./internal/worker
```

예상: fresh 실행 PASS.

### [REVIEW_API-3] Revision scan seed from agent_run evidence

#### 문제

- `services/core/internal/worker/runner.go:64-71`의 `RevisionScanRequest`는 provider/repo/branch/workdir/runner/observed_at만 전달한다.
- worker는 `AgentRunGitEvidence.BeforeRevision`/`AfterRevision`을 알고 있지만 scan request에 싣지 않는다.
- `services/core/internal/controlplane/runtime.go:369-379`는 cursor가 없으면 `after`만 cursor로 저장하고 이벤트 없이 반환한다. agent_run push 직후 watched branch에 cursor가 없으면 `branch.updated` 이벤트를 놓칠 수 있다.
- `services/core/internal/controlplane/runtime.go:433-455`의 `ScanAgentRunRevision`은 generic `ScanBranchRevision`에만 위임해 agent_run evidence를 활용할 수 없다.

#### 해결 방법

- `RevisionScanRequest`에 `BeforeRevision`/`AfterRevision` 또는 동등한 explicit revision pair를 추가한다.
- worker가 commit/push policy 완료 후 revision scan을 호출할 때 collected git evidence의 before/after를 전달한다.
- runtime은 request에 explicit before/after가 있으면 cursor 유무와 관계없이 changed files를 해당 pair로 계산하고 `HandleRevision`을 호출한다. publish failure면 cursor를 advance하지 않는 기존 보장을 유지한다.
- explicit pair가 없으면 기존 `ScanBranchRevision` behavior를 유지한다.

#### 수정 파일 및 체크리스트

- [ ] `services/core/internal/worker/runner.go`: `RevisionScanRequest` 확장 및 call site 갱신.
- [ ] `services/core/internal/worker/runner_test.go`: scanner request가 before/after revision을 포함하는지 검증.
- [ ] `services/core/internal/controlplane/runtime.go`: explicit pair path와 cursor update semantics 추가.
- [ ] `services/core/internal/controlplane/runtime_test.go`: no cursor watched branch에서 agent_run scan이 event를 emit/retry하고 publish failure 시 durable cursor를 advance하지 않는 회귀 테스트 추가.

#### 테스트 작성

- 작성한다.
- `TestRuntimeScanAgentRunRevisionEmitsWithoutExistingCursor`를 추가한다.
- 기존 `TestRuntimeScanAgentRunRevisionPublishFailureDoesNotAdvanceDurableState`가 explicit pair에서도 통과하도록 보강한다.
- `TestRunnerRunOnceAgentRunRevisionScan`에서 request before/after를 assert한다.

#### 중간 검증

```bash
cd services/core && go test -count=1 ./internal/controlplane ./internal/worker
```

예상: fresh 실행 PASS.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/core/model.go` | REVIEW_API-1 |
| `services/core/migrations/00001_initial.sql` | REVIEW_API-1 |
| `services/core/internal/storage/storage.go` | REVIEW_API-1 |
| `services/core/internal/storage/postgres.go` | REVIEW_API-1 |
| `services/core/internal/storage/postgres_test.go` | REVIEW_API-1 |
| `services/core/internal/config/config.go` | REVIEW_API-1, REVIEW_API-2 |
| `services/core/cmd/worker/main.go` | REVIEW_API-1, REVIEW_API-2 |
| `services/core/internal/worker/runner.go` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |
| `services/core/internal/worker/runner_test.go` | REVIEW_API-1, REVIEW_API-2, REVIEW_API-3 |
| `services/core/internal/controlplane/runtime.go` | REVIEW_API-3 |
| `services/core/internal/controlplane/runtime_test.go` | REVIEW_API-3 |
| `packages/contracts/notes/gito-control-plane.md` | REVIEW_API-2 |

## 최종 검증

```bash
cd services/core && go test -count=1 ./internal/worker ./internal/controlplane ./internal/storage
```

```bash
cd services/core && go test ./...
```

```bash
rg --sort path -n "proto-socket|REST|gRPC|ChangeRequest|Operation|Event|agent_shell|agent-shell|iop" packages/contracts README.md docs
```

```bash
rg --sort path -n "Gito|agent-shell|agent_shell|iop|gito-shell|control plane" README.md docs packages/contracts agent-roadmap -g '!agent-roadmap/archive/**'
```

```bash
git diff --check
```

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