<!-- task=m-milestone-work-item-creation-sync/01_project_sync_store plan=1 tag=API -->

# Implementation Plan - API

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

구현 완료는 active `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운 뒤에만 성립한다. 검증을 실행하고, 실제 변경/출력을 기록하고, active 파일은 그대로 둔 채 리뷰 준비를 보고한다. 종료 처리, log rename, `complete.log`, archive 이동은 code-review 스킬 전용이다. 구현 중 사용자만 결정할 수 있는 범위 변경, 외부 환경/secret 준비, scope 충돌이 있으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 적고 멈춘다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`를 만들지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다.

## 배경

첫 번째 Epic의 순수 모델/경로 계산은 `services/core/internal/projectsync`에 작게 추가됐다. 하지만 Project sync 설정은 아직 DB에 저장되지 않으므로 Plane project별 active 설정 조회가 불가능하다. 이 plan은 `project-store` Task만 완료 대상으로 삼아 persistence 경계를 먼저 만든다.

## 사용자 리뷰 요청 흐름

구현 중 차단은 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 해당 섹션은 `agent-ops/skills/common/_templates/implementation-user-review-request-section.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:
  - `project-store`: Project sync 설정을 Core DB에 저장하는 persistence 경계를 정한다.
- 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/plan/SKILL.md`
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
- `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`
- `.gitignore`
- `services/core/internal/projectsync/config.go`
- `services/core/internal/projectsync/config_test.go`
- `services/core/internal/storage/store.go`
- `services/core/internal/db/db.go`
- `services/core/internal/db/models.go`
- `services/core/queries/tasks.sql`
- `services/core/migrations/00001_create_tasks.sql`
- `services/core/migrations/00002_add_task_external_refs.sql`
- `services/core/migrations/00003_add_task_metadata.sql`
- `services/core/sqlc.yaml`

### 테스트 환경 규칙

- 선택한 `test_env`: `local`.
- `agent-test/local/rules.md`를 읽었다. core 변경은 `agent-test/local/core-smoke.md` 기준으로 최소 `cd services/core && go test ./...`가 필수다.
- workspace-ops는 plan/review artifact 작성에만 닿으므로 `agent-test/local/workspace-ops-smoke.md` 기준 `git diff --check`를 최종 확인에 포함한다.
- `<확인 필요>` 값은 core-smoke의 기준 출력 예시에만 있으며, 이 plan의 명령에는 미해결 환경값이 없다.

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

- Project sync 설정 DB schema/query/store는 신규 동작이라 기존 테스트가 없다. 구현 시 정상 저장/조회, provider project별 active unique, 설정 없음 오류를 테스트로 추가한다.
- SQLC 생성 결과는 unit test와 `go test -count=1 ./...`로 컴파일 검증한다.

### 심볼 참조

- none. 기존 symbol rename/remove는 없다.

### 분할 판단

- split decision policy를 평가했다. 공유 task group은 `agent-task/m-milestone-work-item-creation-sync/`다.
- `01_project_sync_store`: 독립 선행 작업. Project sync 설정 DB 저장소를 만든다.
- `02+01_workspace_slot_store`: `01`의 project sync 설정 identity가 있어야 slot table과 예약이 안정적이다. predecessor `01`: missing active/archive `complete.log`.
- `03+01,02_project_binding_checkout`: 설정 조회와 slot 예약이 모두 있어야 Plane-origin binding과 checkout policy를 연결한다. predecessor `01`, `02`: missing active/archive `complete.log`.

### 범위 결정 근거

- `workspace-slot-state`의 원자 예약, `project-bind`, `workspace-checkout`은 이 plan에서 제외한다. 각각 후속 split plan이 맡는다.
- HTTP endpoint, Plane adapter API, 실제 git checkout 실행은 만들지 않는다. 이 plan은 DB persistence boundary만 다룬다.
- 이미 추가된 `projectsync.Config` 모델은 변경하되 필드 의미가 바뀌지 않게 유지한다.

### 빌드 등급

- `local-G06`: DB migration, SQLC 생성, storage boundary가 포함되지만 범위가 core 내부 persistence로 한정되고 deterministic Go test로 검증 가능하다.

## 구현 체크리스트

- [ ] `project_sync_settings` migration과 SQLC queries를 추가한다. 검증: Plane project별 하나의 active sync 설정을 찾을 수 있고 inactive 설정은 active lookup에서 제외된다.
- [ ] `storage.Store` 또는 좁은 core persistence boundary에 `projectsync.Config` 저장/조회 메서드를 추가한다.
- [ ] Project sync 설정 저장/조회 테스트를 작성한다.
- [ ] `cd services/core && go test -count=1 ./...`와 `git diff --check`를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] Project sync DB schema and SQLC queries

- 문제: 현재 migrations는 `tasks` 계열만 정의한다(`services/core/migrations/00003_add_task_metadata.sql:1`). `services/core/queries/tasks.sql:1`도 task query만 있으므로 project sync 설정을 저장하거나 active 설정을 찾을 수 없다.
- 해결 방법: `services/core/migrations/00004_create_project_sync_settings.sql`을 추가한다. 필드는 provider, tenant, project, git_remote_url, source_branch, workspace_id, workspace_base_path, repo_dir_name, active, created_at, updated_at를 포함한다. `(provider, tenant, project) WHERE active` partial unique index를 둔다. `services/core/queries/project_sync_settings.sql`에 create/upsert, get active by target, deactivate queries를 추가하고 `services/core/bin/sqlc` 또는 프로젝트 표준 SQLC 실행으로 generated db code를 갱신한다.
- 수정 파일 및 체크리스트:
  - [ ] `services/core/migrations/00004_create_project_sync_settings.sql`
  - [ ] `services/core/queries/project_sync_settings.sql`
  - [ ] `services/core/internal/db/*.go` generated SQLC output
- 테스트 작성: SQL shape는 storage test에서 검증한다. 별도 SQL parser test는 작성하지 않는다.
- 중간 검증: `cd services/core && go test -count=1 ./internal/db`가 컴파일 성공해야 한다. package에 test가 없으면 `?` 출력은 허용한다.

### [API-2] Storage boundary for active project sync config

- 문제: `storage.Store`는 task CRUD만 노출한다(`services/core/internal/storage/store.go:24`). Project sync 설정을 `projectsync.Config`로 저장하거나 provider project target으로 조회하는 API가 없다.
- 해결 방법: `storage`에 project sync input/output adapter를 추가한다. DB row와 `projectsync.Config` 변환은 storage 내부에 둔다. 설정 없음은 호출자가 분기 가능한 package-level error로 반환한다.
- 수정 파일 및 체크리스트:
  - [ ] `services/core/internal/storage/store.go` 또는 좁은 새 storage file
  - [ ] `services/core/internal/projectsync/config.go`는 필드 의미를 바꾸지 않고 필요한 helper만 추가
- 테스트 작성: `services/core/internal/storage/project_sync_store_test.go`를 작성한다. pgx/sqlc boundary를 실제 DB 없이 검증하기 어렵다면 변환 helper unit test와 SQLC compile 검증을 작성하고, 실제 DB integration 부재를 review stub에 남긴다.
- 중간 검증: `cd services/core && go test -count=1 ./internal/storage ./internal/projectsync`.

### [API-3] Persistence regression coverage

- 문제: `agent-roadmap/.../milestone-work-item-creation-sync.md:51`의 검증은 active 설정 lookup이 핵심인데, 기존 테스트는 해당 동작을 다루지 않는다.
- 해결 방법: 정상 저장/조회, provider/tenant/project 분리, inactive 제외, 필수 필드 누락 reject를 테스트한다. DB integration이 없으면 storage 변환과 query compile까지를 이번 pass의 검증 범위로 명시하고 후속 DB integration test gap을 남긴다.
- 수정 파일 및 체크리스트:
  - [ ] `services/core/internal/storage/project_sync_store_test.go`
  - [ ] 필요한 경우 `services/core/internal/projectsync/config_test.go`
- 테스트 작성: 위 파일에 작성한다.
- 중간 검증: `cd services/core && go test -count=1 ./internal/storage ./internal/projectsync`.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/migrations/00004_create_project_sync_settings.sql` | API-1 |
| `services/core/queries/project_sync_settings.sql` | API-1 |
| `services/core/internal/db/*.go` | API-1 |
| `services/core/internal/storage/store.go` 또는 `services/core/internal/storage/project_sync_settings.go` | API-2 |
| `services/core/internal/storage/project_sync_store_test.go` | API-3 |
| `services/core/internal/projectsync/config.go` | API-2 |
| `services/core/internal/projectsync/config_test.go` | API-3 |

## 최종 검증

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

기대 결과: 두 명령 모두 exit code 0. Go test cache output은 허용하지 않으므로 `-count=1`을 유지한다.

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