<!-- task=m-control-plane-separation-migration/05+04_core_cicd_state plan=1 tag=REVIEW_CICD_STATE -->

# Plan - REVIEW_CICD_STATE

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

구현 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 변경 사유, 검증 출력으로 채운다. 구현 중 사용자만 결정할 수 있는 범위 변경이나 외부 환경 준비가 필요하면 리뷰 stub의 `사용자 리뷰 요청` 섹션에 근거를 적고 멈춘다. 직접 사용자에게 질문하거나 `USER_REVIEW.md`, `complete.log`, archive 로그를 만들지 않는다. 명령 재실행으로 해소 가능한 증거 공백은 사용자 리뷰 요청이 아니다.

## 배경

이 후속 계획은 `code_review_local_G06_0.log`의 FAIL 판정에서 나온 Required 이슈만 닫는다. 서버 CI/CD 상태 API의 큰 범위는 유지하고, 저장소 상태 전이 지속성 및 Go 포맷 계약을 보완한다.

## 범위 결정 근거

- 포함: `cicdstate.Store`의 persisted 상태 전이/update API, 이에 대한 unit test, 변경 Go 파일 gofmt.
- 제외: durable storage, scheduling policy, runner execution loop, auth/audit, artifact binary upload, 새 HTTP endpoint 확장. 상태 전이 HTTP endpoint가 꼭 필요하다고 판단되면 현재 계획 대비 변경 사항에 이유와 테스트를 기록한다.

## 구현 체크리스트

- [ ] `cicdstate.Store`에 persisted job/execution state transition/update API를 추가하고 copy 반환 계약을 유지한다.
- [ ] store unit tests가 transition 후 `GetJob`/`GetExecution`/snapshot 재조회로 persisted 상태와 invalid/unknown transition 오류를 검증하도록 보강한다.
- [ ] 변경된 Go 파일에 `gofmt`를 적용하고 `gofmt -l services/core/internal/cicdstate/store.go services/core/internal/cicdstate/store_test.go services/core/internal/httpserver/server.go services/core/internal/httpserver/server_test.go`이 빈 출력임을 확인한다.
- [ ] `cd services/core && go test -count=1 ./...`를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## [REVIEW_CICD_STATE-1] persisted 상태 전이 API

### 문제

`services/core/internal/cicdstate/store.go`의 `GetJob`/`GetExecution`은 copy를 반환한다. 현재 테스트는 이 copy에서 `TransitionTo`를 호출하고 다시 store를 조회하지 않으므로, 실제 저장된 job/execution 상태는 바뀌지 않는다. 이전 계획은 `create/update/append/list/snapshot API`와 상태 전이 검증을 요구했다.

### 해결 방법

`Store`가 lock 안에서 내부 record를 조회하고 상태 전이를 적용하는 update API를 추가한다. 예: `TransitionJob(id, newState)`와 `TransitionExecution(id, newState)`. 전이 검증은 기존 `validTransitions`를 재사용하되, `UpdatedAt`은 `s.now()`를 사용한다. 반환값은 기존 조회 API와 동일하게 copy를 반환하거나 필요 없으면 error만 반환한다.

기존 `Job.TransitionTo`/`Execution.TransitionTo`는 유지하더라도 store update API에서 사용하거나, 혼동을 줄이기 위해 내부 helper로 축소한다. 후속 runner dispatch가 store를 통해 상태를 바꿀 수 있어야 한다.

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

- [ ] `services/core/internal/cicdstate/store.go`: persisted state transition/update API 추가, copy 반환 계약 유지.
- [ ] `services/core/internal/cicdstate/store_test.go`: transition 후 재조회/Snapshot으로 저장 상태 변경 검증.

### 테스트 작성

작성한다. 최소한 아래를 검증한다.

- job: queued -> running -> succeeded 후 `GetJob` 또는 `SnapshotJobs`가 변경된 상태를 반환한다.
- execution: queued -> running -> failed/succeeded 후 `GetExecution` 또는 `SnapshotExecutions`가 변경된 상태를 반환한다.
- terminal state에서 추가 전이는 error이고 저장 상태가 바뀌지 않는다.
- unknown job/execution id 전이는 error이다.

### 중간 검증

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

기대 결과: package tests PASS.

## [REVIEW_CICD_STATE-2] Go 포맷 계약

### 문제

`gofmt -l services/core/internal/cicdstate/store.go services/core/internal/cicdstate/store_test.go services/core/internal/httpserver/server.go services/core/internal/httpserver/server_test.go`가 `store.go`, `store_test.go`, `server.go`를 출력한다. Go 소스는 gofmt 적용 상태로 남겨야 한다.

### 해결 방법

변경된 Go 파일에 `gofmt -w`를 적용한다. 포맷만 바뀌는 파일은 범위를 넓히지 말고, 포맷 검증 명령이 빈 출력인지 기록한다.

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

- [ ] `services/core/internal/cicdstate/store.go`: gofmt 적용.
- [ ] `services/core/internal/cicdstate/store_test.go`: gofmt 적용.
- [ ] `services/core/internal/httpserver/server.go`: gofmt 적용.
- [ ] `services/core/internal/httpserver/server_test.go`: 명령 대상에 포함해 포맷 상태 확인.

### 중간 검증

```bash
gofmt -l services/core/internal/cicdstate/store.go services/core/internal/cicdstate/store_test.go services/core/internal/httpserver/server.go services/core/internal/httpserver/server_test.go
```

기대 결과: 빈 출력.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/cicdstate/store.go` | REVIEW_CICD_STATE-1, REVIEW_CICD_STATE-2 |
| `services/core/internal/cicdstate/store_test.go` | REVIEW_CICD_STATE-1, REVIEW_CICD_STATE-2 |
| `services/core/internal/httpserver/server.go` | REVIEW_CICD_STATE-2 |
| `services/core/internal/httpserver/server_test.go` | REVIEW_CICD_STATE-2 검증 대상 |

## 리뷰어 체크포인트

- `Store` transition API 호출 후 다시 조회해도 상태가 유지된다.
- copy 반환 계약이 깨지지 않는다.
- `gofmt -l ...`이 빈 출력이다.
- 후속 구현이 계획 범위 밖의 HTTP/runner 계약을 불필요하게 확장하지 않는다.

## 최종 검증

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

기대 결과: 모든 Go 테스트 PASS. Go test cache output은 허용하지 않으므로 `-count=1`을 유지한다.

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