<!-- task=m-provider-adapter-foundation/02+01_postgres_provider_store plan=1 tag=REVIEW_API -->

# Plan - REVIEW_API

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

이 계획은 직전 코드리뷰 FAIL의 후속 루프다. 구현이 끝나도 task는 완료가 아니다. 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용, 검증 명령, stdout/stderr로 채우고 active 파일을 그대로 둔 뒤 리뷰 준비를 보고한다. 선택된 SDD 결정 또는 선택된 Milestone `구현 잠금 > 결정 필요` 항목 때문에 막히면 review stub의 `사용자 리뷰 요청` 섹션에 연결 대상과 근거를 채우고 멈춘다. 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경/secret/service 차단, 일반 범위 변경, 검증 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과나 후속 계획으로 기록한다.

## 배경

직전 루프는 `provider-store` durable PostgreSQL 구현을 완료하지 못했다. `PgStore.ProviderConfigs()`가 `nil`을 반환하고, `provider_configs` migration과 PostgreSQL CRUD/integration test가 없다. 이 루프는 기존 boundary 계약을 실제 PgStore 구현으로 연결하고, migration/test/evidence를 채운다.

## 사용자 리뷰 요청 흐름

구현 중 직접 사용자 프롬프트는 금지된다. 선택된 SDD 결정 또는 Milestone `구현 잠금 > 결정 필요` 항목만 active review stub의 `사용자 리뷰 요청` 섹션에 기록하며, code-review가 검증 후 실제 `USER_REVIEW.md` 작성을 담당한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/provider-change-request-integration/milestones/provider-adapter-foundation.md`
- Task ids:
  - `provider-store`: provider config를 저장하고 조회하는 store boundary를 만든다.
- Completion mode: check-on-pass

## Archive Evidence Snapshot

- Archived plan: `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/plan_local_G05_0.log`
- Archived review: `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/code_review_local_G05_0.log`
- Verdict: FAIL
- Required summary:
  - `services/core/internal/storage/postgres.go:79`의 `PgStore.ProviderConfigs()`가 `nil`을 반환하고 PgStore field/constructor wiring 및 `pgProviderConfigStore` CRUD 구현이 없다.
  - `services/core/migrations/00001_initial.sql:77` 근처에 `provider_configs` table이 없고 Down block drop 및 migration assertion도 없다.
  - `code_review_local_G05_0.log`의 구현 항목/체크리스트/검증 출력이 placeholder 상태라 구현 완료 증거를 신뢰할 수 없다.
- Affected files:
  - `services/core/migrations/00001_initial.sql`
  - `services/core/internal/storage/storage_test.go`
  - `services/core/internal/storage/postgres.go`
  - `services/core/internal/storage/postgres_test.go`
- Verification evidence:
  - `cd services/core && go test -count=1 ./internal/storage` was rerun during review and exited 0: `ok git.toki-labs.com/toki/gito/services/core/internal/storage 0.005s`.
  - `cd services/core && go test ./...` was rerun during review and exited 0 across core packages, mostly cached.
  - These passes do not prove this task complete because provider config persistence is not implemented or tested.
- Dependency evidence:
  - Predecessor complete log exists at `agent-task/archive/2026/06/m-provider-adapter-foundation/01_provider_store_boundary/complete.log`.
- Roadmap/spec carryover:
  - Keep the Roadmap Targets above.
  - No Spec Targets were present.
- Allowed archive reread:
  - If exact prior wording is needed, read only `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/plan_local_G05_0.log`, `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/code_review_local_G05_0.log`, and `agent-task/archive/2026/06/m-provider-adapter-foundation/01_provider_store_boundary/complete.log`.

## 구현 체크리스트

- [ ] `agent-task/archive/2026/06/m-provider-adapter-foundation/01_provider_store_boundary/complete.log` predecessor evidence를 확인한다.
- [ ] `provider_configs` migration table, Down drop, migration assertion을 추가한다.
- [ ] `PgStore`에 provider config store field/constructor/accessor wiring을 추가한다.
- [ ] `pgProviderConfigStore`의 `SaveProviderConfig`, `GetProviderConfig`, `ListProviderConfigs`와 validation/scan helper를 구현한다.
- [ ] PostgreSQL integration test로 insert/update/get/list/not-found/invalid input 및 raw credential 미저장을 검증한다.
- [ ] `cd services/core && go test -count=1 ./internal/storage` 및 `cd services/core && go test ./...`를 실행하고 실제 stdout/stderr를 기록한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

## 의존 관계 및 구현 순서

이 디렉터리 이름의 `02+01_...`가 runtime dependency source다. predecessor evidence는 이미 archive에 있으므로, 구현 시작 전에 위 `complete.log` 경로만 좁게 확인한다. 추가 의존성은 없다.

### [REVIEW_API-1] provider_configs migration과 assertion 추가

문제: [00001_initial.sql](/config/workspace/gito/services/core/migrations/00001_initial.sql:77)는 `provider_deliveries` 이후에도 `provider_configs` table을 만들지 않는다.

해결 방법: `provider_deliveries` 근처에 `provider_configs` table을 추가한다. 최소 columns는 `id TEXT PRIMARY KEY`, `endpoint TEXT NOT NULL CHECK (btrim(endpoint) <> '')`, `credential_ref TEXT`, `capabilities BIGINT NOT NULL CHECK (capabilities >= 0)`, `created_at`, `updated_at`이다. Down block에는 `DROP TABLE IF EXISTS provider_configs;`를 추가한다.

수정 파일 및 체크리스트:

- [ ] `services/core/migrations/00001_initial.sql`: table과 Down drop 추가.
- [ ] `services/core/internal/storage/storage_test.go`: `TestInitialMigrationSupportsStoreBoundary` snippets에 table/columns/checks 추가.

테스트 결정: migration snippet test는 DB env 없이도 실행되어야 한다. 전체 storage test는 `cd services/core && go test -count=1 ./internal/storage`로 확인한다.

중간 검증:

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

### [REVIEW_API-2] PgStore provider config CRUD 구현

문제: [postgres.go](/config/workspace/gito/services/core/internal/storage/postgres.go:79)의 `ProviderConfigs()`가 `nil`을 반환한다.

해결 방법: `provider` package를 import하고 `PgStore`에 `providerConfigs *pgProviderConfigStore` field를 추가한다. `NewPgStore`에서 초기화한 뒤 `ProviderConfigs()`가 해당 store를 반환한다. `provider.Config.Normalized()`를 사용해 ID/endpoint/credential_ref를 정규화하고, empty id/endpoint 또는 zero capabilities는 `ErrInvalidProviderConfig`로 거부한다. Capabilities는 current bitmask를 `int64`로 저장/조회한다. Raw credential value는 저장하지 않는다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/storage/postgres.go`: provider import, PgStore field, constructor init, accessor 추가.
- [ ] `services/core/internal/storage/postgres.go`: `pgProviderConfigStore.SaveProviderConfig` upsert 구현.
- [ ] `services/core/internal/storage/postgres.go`: `pgProviderConfigStore.GetProviderConfig` not-found bool 구현.
- [ ] `services/core/internal/storage/postgres.go`: `pgProviderConfigStore.ListProviderConfigs` deterministic id order 구현.
- [ ] `services/core/internal/storage/postgres.go`: normalize/scan helper와 error wrapping 추가.

테스트 결정: PostgreSQL integration test가 이 item의 핵심 검증이다. `GITO_TEST_DATABASE_URL`이 없어서 skip되면 실제 skip output과 남은 DB coverage risk를 review stub에 기록한다.

중간 검증:

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

### [REVIEW_API-3] 통합 테스트와 리뷰 증거 완성

문제: 직전 review stub은 구현 항목/체크리스트가 모두 미완료이고 검증 결과가 `(output)` placeholder였다.

해결 방법: `services/core/internal/storage/postgres_test.go`에 `TestPostgresProviderConfigStoreSavesGetsListsAndValidates`를 추가한다. assertions는 first save insert, second save update, get updated config, list ordered by id, missing get `ok=false`, blank id/endpoint 또는 zero capabilities가 `ErrInvalidProviderConfig`, raw credential value 미사용을 포함한다. 구현 후 active review stub의 구현 항목별 완료 여부, 구현 체크리스트, 계획 대비 변경 사항, 주요 설계 결정, 검증 결과를 실제 내용으로 채운다.

수정 파일 및 체크리스트:

- [ ] `services/core/internal/storage/postgres_test.go`: provider config store integration test 추가.
- [ ] `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/CODE_REVIEW-local-G05.md`: 구현 에이전트 소유 섹션과 실제 검증 출력 작성.

테스트 결정: 최종 검증은 plan의 고정 명령을 그대로 실행한다.

최종 검증:

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

## 수정 파일 요약

| 파일 | 항목 |
| --- | --- |
| `services/core/migrations/00001_initial.sql` | REVIEW_API-1 |
| `services/core/internal/storage/storage_test.go` | REVIEW_API-1 |
| `services/core/internal/storage/postgres.go` | REVIEW_API-2 |
| `services/core/internal/storage/postgres_test.go` | REVIEW_API-3 |
| `agent-task/m-provider-adapter-foundation/02+01_postgres_provider_store/CODE_REVIEW-local-G05.md` | REVIEW_API-3 |

## 최종 검증

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

기대 결과: 두 명령 모두 exit 0. PostgreSQL integration test가 DB env 부재로 skip되면 실제 출력과 남은 위험을 review stub에 남긴다.
