<!-- task=m-milestone-work-item-creation-sync/08_agent_run_result_gate plan=0 tag=AGENT_BRIDGE -->

# Plan - AGENT_BRIDGE

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

`CODE_REVIEW-cloud-G07.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우는 것이 필수다. 구현 후 검증을 실행하고 active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 사용자 결정, 사용자 소유 외부 환경, 범위 충돌로 막히면 review stub의 `사용자 리뷰 요청`에 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 명령 재실행이나 산출물 수집으로 닫을 수 있는 증거 공백은 사용자 리뷰 요청이 아니다. 최종 archive, `complete.log`, roadmap 반영은 code-review/runtime 책임이다.

## 배경

`agent-bridge`의 라우팅, 최소 상태, provider identity, 실행 잠금 기준은 현재 Core 코드와 테스트로 확인된다. 남은 문제는 scheduler가 IOP bridge 응답 성공만으로 `authoring_run_state=succeeded`를 기록하는 반면, 마일스톤의 성공 기준은 `develop` branch에 Milestone 변경이 commit/push되고 이후 sync layer가 provider identity로 매칭하는 것이다. 이 계획은 bridge 응답 성공과 develop 반영 성공을 분리해 `[agent-run-result]`의 완료 기준을 코드와 테스트로 닫는다.

## 사용자 리뷰 요청 흐름

구현 중 사용자 결정이나 사용자 소유 외부 환경/secret이 필요하면 active `CODE_REVIEW-cloud-G07.md`의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 프롬프트는 금지되며, code-review가 요청의 타당성을 판단하고 필요할 때만 `USER_REVIEW.md`를 작성한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md`
- Task ids:
  - `agent-run-result`: Workspace agent authoring run의 성공/실패 판정 기준
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-roadmap/current.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/PHASE.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md`
- `agent-ops/rules/project/domain/core/rules.md`
- `agent-ops/rules/project/domain/workspace-ops/rules.md`
- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/workspace-ops-smoke.md`
- `services/core/internal/scheduler/jobs.go`
- `services/core/internal/scheduler/jobs_test.go`
- `services/core/internal/authoring/request.go`
- `services/core/internal/authoring/request_test.go`
- `services/core/internal/model/model.go`
- `services/core/internal/adapters/openai/client.go`
- `services/core/internal/adapters/openai/client_test.go`
- `services/core/internal/workflow/model.go`
- `services/core/internal/workflow/lifecycle.go`
- `services/core/internal/roadmapsync/identity.go`
- `services/core/internal/roadmapsync/identity_test.go`
- `services/core/internal/roadmapsync/develop_match.go`
- `services/core/internal/roadmapsync/develop_match_test.go`
- `services/core/internal/projectsync/config.go`
- `services/core/internal/projectsync/checkout.go`
- `services/core/internal/projectsync/provision.go`
- `services/core/internal/workitempipeline/service.go`
- `services/core/internal/config/config.go`
- `services/core/cmd/server/main.go`

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`를 읽었다.
- core 변경 기준으로 `agent-test/local/core-smoke.md`를 읽었다. 적용 명령은 `cd services/core && go test ./...`, 보조로 `cd services/core && go vet ./...`다.
- roadmap/task artifact 변경 기준으로 `agent-test/local/workspace-ops-smoke.md`를 읽었다. 적용 명령은 `git diff --check`다.
- `<확인 필요>` 값은 실행 환경 설명 예시에만 있고, 이번 계획의 명령 선택을 막지 않는다.

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

- Plane-origin authoring task가 IOP OpenAI-compatible model bridge로 라우팅되는 동작은 `services/core/internal/scheduler/jobs_test.go`와 `services/core/internal/authoring/request_test.go`가 다룬다.
- `metadata.workspace`가 `/v1/responses` request에 flat string으로 들어가는 동작은 `services/core/internal/adapters/openai/client_test.go`가 다룬다.
- provider/work item identity와 pushed develop Milestone match 기준은 `services/core/internal/roadmapsync/*_test.go`가 다룬다.
- model bridge 응답 성공만으로 authoring 성공을 확정하지 않는 회귀 테스트가 없다. 이번 구현에서 추가해야 한다.
- exit code, push 실패, dirty workspace, conflict 신호를 authoring failure reason으로 분류하는 단위 테스트가 없다. 이번 구현에서 추가해야 한다.

### 심볼 참조

- renamed/removed symbol: none.

### 분할 판단

분할 정책을 먼저 평가했다. 이 작업은 milestone-linked shared task group `m-milestone-work-item-creation-sync` 아래의 단일 subtask `08_agent_run_result_gate`로 둔다. 수정 범위는 Core authoring/scheduler result gate와 해당 테스트로 한정되고, API foundation과 광범위 call-site rollout이 분리되지 않는다. git/process 실행 자체를 여기서 새로 구현하지 않고 post-authoring 결과 판정 계약과 scheduler metadata 경계를 닫는 작업이므로 별도 선행 subtask가 필요하지 않다. 디렉터리명에 `+` dependency가 없으므로 predecessor `complete.log` 확인은 필요 없다.

### 범위 결정 근거

- Plane `Todo -> In Progress` 실행 시작, 하위 티켓 생성, User Review/Done/Cancelled projection은 마일스톤 범위 제외라 건드리지 않는다.
- 실제 IOP Edge 원격 smoke나 real git push는 사용자/환경 의존성이 크므로 단위 테스트 대상이 아니다.
- `roadmapsync.MatchDevelopMilestone`의 develop scan matching 규칙은 이미 존재하므로, 이번 작업은 그 결과를 authoring run 성공 판정에 반영할 수 있는 Core-side 경계로 제한한다.
- DB migration과 SQLC 생성은 새 persisted column이 필요할 때만 확장한다. 가능하면 기존 task metadata에 authoring result/failure metadata를 남긴다.

### 빌드 등급

- build=`cloud-G07`, review=`cloud-G07`: scheduler 상태 전이와 git/develop push 성공 의미를 분리하는 작업이며, 실패 분류와 검증 신뢰도가 중요하다.

## 구현 체크리스트

- [ ] [AGENT_BRIDGE-1] bridge 응답 성공과 develop 반영 성공을 분리하는 authoring result gate를 구현하고, `agent-run-result`의 성공/실패 판정 테스트를 추가한다.
- [ ] 중간 검증 `cd services/core && go test -count=1 ./internal/authoring ./internal/scheduler ./internal/roadmapsync`를 실행한다.
- [ ] 최종 검증 `cd services/core && go test ./...`, `cd services/core && go vet ./...`, `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [AGENT_BRIDGE-1] Authoring Result Gate

### 문제

`services/core/internal/scheduler/jobs.go:140-157`은 model bridge 호출이 성공하면 바로 `authoring_run_state=succeeded`를 result payload에 넣는다.

```go
140			result := map[string]any{
141				"message":                  generated.Text,
142				"mode":                     "authoring_run",
143				"model":                    generated.Model,
144				"response_id":              generated.ID,
145				"authoring_run_state":      "succeeded",
146				"authoring_run_updated_at": time.Now().UTC().Format(time.RFC3339),
147				"usage": map[string]int{
148					"input_tokens":  generated.Usage.InputTokens,
149					"output_tokens": generated.Usage.OutputTokens,
150					"total_tokens":  generated.Usage.TotalTokens,
151				},
152			}
153			raw, err := json.Marshal(result)
154			if err != nil {
155				return nil, "", err
156			}
157			return raw, "authoring run completed", nil
```

마일스톤 기준에서는 authoring 성공이 `develop` branch Milestone 변경 commit/push이며, `roadmapsync.MatchDevelopMilestone`은 이미 pushed develop revision과 provider identity match만 projection-ready로 본다.

```go
44	func MatchDevelopMilestone(in MatchInput) ProjectionCandidate {
45		revision := in.Scan.Revision.Normalize()
52		if !revision.OnDevelop() {
55		if revision.Revision == "" {
58		if !changedFilesContainMilestone(in.Scan.ChangedFiles, expected.RoadmapMilestonePath) {
62		scanned, ok := scannedIdentityForPath(in.Scan.ScannedMilestones, expected.RoadmapMilestonePath)
68		if !expected.Matches(scanned) {
72		return ProjectionCandidate{
75			Ready:    true,
76			Reason:   reasonReady,
77		}
```

### 해결 방법

1. `services/core/internal/authoring/`에 result decision helper를 추가한다. helper는 bridge response success, develop match ready, exit code/push failure/dirty/conflict 같은 failure signal을 입력으로 받아 기존 최소 상태인 `in_progress`, `succeeded`, `failed` 중 하나와 metadata reason을 반환한다.
2. scheduler는 `Model.Generate` 성공만으로 `succeeded`를 기록하지 않는다. develop match evidence가 아직 없으면 `authoring_run_state`를 유지하거나 `in_progress`로 두고, response id/model/usage 같은 bridge evidence만 별도 metadata에 남긴다.
3. sync layer가 pushed develop match를 확인한 뒤 사용할 수 있도록 helper의 ready path는 `succeeded`, failure path는 `failed`와 failure category를 반환하게 한다. 기존 `roadmapsync.MatchDevelopMilestone`은 수정하지 않고 호출 가능한 결정 입력으로 활용한다.
4. 기존 tests 중 `TestRunTaskAuthoringSuccessRecordsState`는 model response만으로 success를 기대하지 않도록 바꾼다. 새 tests는 develop-ready signal에서만 `succeeded`, push failure/dirty/conflict/exit code signal에서 `failed`가 되는지 확인한다.

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

- [ ] `services/core/internal/authoring/result.go`를 추가하거나 동등한 authoring result helper를 만든다.
- [ ] `services/core/internal/authoring/result_test.go`를 추가해 ready/failure/in-progress 분류를 검증한다.
- [ ] `services/core/internal/scheduler/jobs.go`에서 authoring model response result payload가 success를 과잉 주장하지 않게 조정한다.
- [ ] `services/core/internal/scheduler/jobs_test.go`의 authoring success 관련 기대값을 새 경계에 맞게 갱신하고 회귀 테스트를 추가한다.
- [ ] 필요 시 `services/core/internal/workflow/model.go`에 authoring failure category metadata key를 추가하되, 새 task 상태 값은 만들지 않는다.

### 테스트 작성

테스트를 작성한다.

- `services/core/internal/authoring/result_test.go`: develop match ready signal만 `succeeded`가 되는지 검증한다.
- `services/core/internal/authoring/result_test.go`: exit code, push failure, dirty workspace, conflict signal이 `failed`와 구체 reason/category로 분류되는지 검증한다.
- `services/core/internal/scheduler/jobs_test.go`: model bridge 응답 성공만으로 `authoring_run_state=succeeded`가 기록되지 않는지 검증한다.
- 기존 `roadmapsync` tests는 유지하고, 새 helper가 필요로 하는 ready candidate fixture만 재사용한다.

### 중간 검증

```bash
cd services/core && go test -count=1 ./internal/authoring ./internal/scheduler ./internal/roadmapsync
```

예상 결과: 세 패키지 테스트가 실패 없이 종료한다. fresh 실행이 중요하므로 `-count=1`을 사용한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/authoring/result.go` | AGENT_BRIDGE-1 |
| `services/core/internal/authoring/result_test.go` | AGENT_BRIDGE-1 |
| `services/core/internal/scheduler/jobs.go` | AGENT_BRIDGE-1 |
| `services/core/internal/scheduler/jobs_test.go` | AGENT_BRIDGE-1 |
| `services/core/internal/workflow/model.go` | AGENT_BRIDGE-1 |

## 최종 검증

```bash
cd services/core && go test ./...
cd services/core && go vet ./...
git diff --check
```

예상 결과: 모든 명령이 실패 없이 종료한다. `go test ./...`는 전체 회귀 확인이므로 캐시 출력이 허용된다. whitespace 검증은 repo root에서 실행한다.

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