<!-- task=m-milestone-work-item-creation-sync/13+12_slot_terminal_state plan=0 tag=API -->

# Plan - API

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

구현의 마지막 단계는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 내용과 검증 출력으로 채우는 것이다. 사용자 결정이나 외부 secret이 필요하면 직접 묻지 말고 review stub의 `사용자 리뷰 요청`에 기록한다. finalization은 code-review 전용이다.

## 배경

`live-cycle-gate`의 `slot-terminal-state`는 authoring/projection terminal outcome 이후 reserved slot이 다음 smoke를 막지 않도록 정책 상태로 회수되는지 확인한다. 현재 저장소에는 `UpdateWorkspaceSlotState`가 있지만 scheduler/projection terminal outcome과 연결되어 있지 않다. 이 작업은 12번 task의 finalizer 결과를 받아 slot을 `available`, `dirty`, `error` 중 하나로 전환한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 프롬프트는 금지되며 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:
  - `slot-terminal-state`: authoring 또는 projection이 terminal outcome에 도달하면 reserved workspace slot이 정책에 따라 `available`, `dirty`, `error` 중 하나로 전환된다.
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/workspace-ops-smoke.md`
- `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md`
- `services/core/internal/projectsync/config.go`
- `services/core/internal/projectsync/checkout.go`
- `services/core/internal/storage/workspace_slots.go`
- `services/core/internal/storage/workspace_slots_test.go`
- `services/core/queries/workspace_slots.sql`
- `services/core/migrations/00005_create_workspace_slots.sql`
- `services/core/internal/workitempipeline/service.go`
- `services/core/internal/scheduler/jobs.go`
- `services/core/internal/scheduler/roadmap_sync_jobs.go`
- `services/core/internal/workflow/lifecycle.go`

### 테스트 환경 규칙

- `test_env=local`, `agent-test/local/rules.md` present/read.
- Matched profiles: `core-smoke`, `workspace-ops-smoke`.
- Required commands: `cd services/core && go test ./...`, `git diff --check`; focused package tests are allowed before final verification.

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

- Storage tests verify state update query shape.
- No existing test verifies terminal authoring/projection outcome chooses and persists a slot state.

### 심볼 참조

- Removed/renamed symbols: none.
- New references must be checked with `rg --sort path "UpdateWorkspaceSlotState|SlotStateAvailable|SlotStateDirty|SlotStateError|checkout.slot_id" services/core/internal`.

### 분할 판단

- Split policy evaluated. This task depends on active sibling `12+11_authoring_develop_gate`; predecessor 12 is currently missing `complete.log`, so implementation must wait until task 12 PASS or be run by runtime after predecessor completion.
- Slot terminal state is separate from authoring gate because it touches storage/projectsync state policy and failure categorization.

### 범위 결정 근거

- Do not add new slot states beyond existing DB check constraint.
- Do not reset slots on generic task completion.
- Do not perform filesystem cleanup; only persist policy state.

### 빌드 등급

- `cloud-G06`: cross-boundary but bounded to slot state policy, storage API, scheduler/projection finalization hooks.

## 구현 체크리스트

- [ ] checkout metadata에서 `slot_id`를 읽는 helper를 추가하고 missing slot id는 slot update를 skip하되 task completion/failure를 막지 않도록 기록한다.
- [ ] success projection은 reserved slot을 `available`로, dirty/conflict/push failure는 `dirty` 또는 `error`로 전환하는 정책 helper를 추가한다.
- [ ] TaskWorker failure path와 RoadmapCreationSync finalizer path가 같은 slot release policy를 사용하도록 연결한다.
- [ ] `cd services/core && go test -count=1 ./internal/projectsync ./internal/storage ./internal/scheduler ./internal/workflow`를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] Add slot terminal policy helper

문제: [services/core/internal/storage/workspace_slots.go](/config/workspace/nomadcode/services/core/internal/storage/workspace_slots.go:45)에 state update API는 있으나 terminal outcome 정책이 없다.

해결 방법:

Before:

```go
// services/core/internal/storage/workspace_slots.go:45
func (s *Store) UpdateWorkspaceSlotState(ctx context.Context, args db.UpdateWorkspaceSlotStateParams) (db.WorkspaceSlot, error)
```

After:

```go
// Shape only.
func SlotStateForAuthoringOutcome(decision authoring.Decision) projectsync.SlotState {
	switch decision.FailureCategory {
	case "dirty_workspace", "conflict", "push_failed":
		return projectsync.SlotStateDirty
	case "":
		return projectsync.SlotStateAvailable
	default:
		return projectsync.SlotStateError
	}
}
```

수정 파일 및 체크리스트:

- [ ] `services/core/internal/authoring/result.go` 또는 새 narrow helper 위치에 slot outcome mapping을 둔다.
- [ ] `services/core/internal/authoring/result_test.go`: succeeded/dirty/conflict/push_failed/bridge_failed mapping을 검증한다.

테스트 작성: table test 추가.

중간 검증: `cd services/core && go test -count=1 ./internal/authoring ./internal/projectsync`.

### [API-2] Apply slot release on terminal paths

문제: [services/core/internal/scheduler/jobs.go](/config/workspace/nomadcode/services/core/internal/scheduler/jobs.go:385) failure metadata는 authoring state만 기록하고, slot state는 바꾸지 않는다.

해결 방법:

Before:

```go
// services/core/internal/scheduler/jobs.go:403
failInput.ExtraMetadata = map[string]any{
	workflow.MetadataKeyAuthoringRunState: "failed",
}
```

After:

```go
// Shape only.
if updater := w.SlotFinalizer; updater != nil {
	_ = updater.UpdateWorkspaceSlotState(ctx, slotID, targetState)
}
```

수정 파일 및 체크리스트:

- [ ] `services/core/internal/scheduler/jobs.go`: authoring failure path에 optional slot finalizer를 연결한다.
- [ ] `services/core/internal/scheduler/roadmap_sync_jobs.go`: projection success finalizer에 available transition을 연결한다.
- [ ] `services/core/cmd/server/main.go`: storage store를 finalizer로 wiring한다.
- [ ] `services/core/internal/scheduler/jobs_test.go`: failure category별 slot update 호출을 검증한다.
- [ ] `services/core/internal/scheduler/river_test.go`: worker wiring을 확인한다.

테스트 작성: failure path와 projected success path 모두 regression test 작성.

중간 검증: `cd services/core && go test -count=1 ./internal/scheduler ./internal/storage`.

## 의존 관계 및 구현 순서

이 subtask는 `12+11_authoring_develop_gate`의 `complete.log`가 필요하다. runtime dependency는 디렉터리명 `13+12_slot_terminal_state`가 source of truth다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/authoring/result.go` | API-1 |
| `services/core/internal/authoring/result_test.go` | API-1 |
| `services/core/internal/scheduler/jobs.go` | API-2 |
| `services/core/internal/scheduler/jobs_test.go` | API-2 |
| `services/core/internal/scheduler/roadmap_sync_jobs.go` | API-2 |
| `services/core/internal/scheduler/river.go` | API-2 |
| `services/core/internal/scheduler/river_test.go` | API-2 |
| `services/core/cmd/server/main.go` | API-2 |

## 최종 검증

- `cd services/core && go test -count=1 ./internal/projectsync ./internal/storage ./internal/authoring ./internal/scheduler ./internal/workflow`
- `cd services/core && go test ./...`
- `cd services/core && go vet ./...`
- `git diff --check`

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