<!-- task=m-milestone-work-item-creation-sync/06_workspace_provision plan=0 tag=API -->

# Workspace Provision Guard Plan - API

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

`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수 최종 단계다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, 검증 출력을 review stub에 기록한 뒤 active 파일을 남겨 리뷰를 요청한다. 최종 archive, `complete.log`, 로그 rename은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 묻거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다.

## 배경

`workspace-provision` Task는 `branches/main`, `branches/develop`, `slots/000`이 실제 checkout으로 준비되지 않았을 때 임의 cwd로 대체하지 않는 명시 오류 경계를 요구한다. 현재 `projectsync.BuildProvisionPlan`은 경로만 계산하고, checkout 존재 여부는 확인하지 않는다. 이 계획은 pipeline 연결 전 단계로, 재사용 가능한 filesystem readiness guard와 회귀 테스트를 추가한다.

## 사용자 리뷰 요청 흐름

구현 중 blocker는 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 직접 사용자 프롬프트는 금지되며, code-review가 요청 타당성을 검증하고 필요한 경우 실제 `USER_REVIEW.md` 작성을 소유한다.

## 분석 결과

### 읽은 파일

- `agent-ops/skills/common/router.md`
- `agent-ops/skills/common/plan/SKILL.md`
- `agent-ops/skills/common/update-roadmap/SKILL.md`
- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md`
- `agent-test/local/rules.md`
- `agent-test/local/core-smoke.md`
- `agent-test/local/workspace-ops-smoke.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-task/archive/2026/06/m-milestone-work-item-creation-sync/01_workspace_topology/complete.log`
- `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/02+01_workspace_slot_store/complete.log`
- `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/03+01,02_project_binding_checkout/complete.log`
- `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/03+02_develop_match/complete.log`
- `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/04+03_plane_todo_projection/complete.log`
- `services/core/internal/projectsync/config.go`
- `services/core/internal/projectsync/checkout.go`
- `services/core/internal/projectsync/config_test.go`
- `services/core/internal/projectsync/checkout_test.go`
- `services/core/internal/workitempipeline/service.go`
- `services/core/internal/workitempipeline/service_test.go`
- `services/core/internal/http/handlers.go`
- `services/core/internal/http/handlers_test.go`
- `services/core/internal/storage/project_sync_settings.go`
- `services/core/internal/storage/workspace_slots.go`
- `services/core/internal/storage/store.go`
- `services/core/internal/storage/project_sync_store_test.go`
- `services/core/internal/storage/workspace_slots_test.go`
- `services/core/internal/db/models.go`
- `services/core/internal/db/project_sync_settings.sql.go`
- `services/core/internal/db/workspace_slots.sql.go`
- `services/core/queries/project_sync_settings.sql`
- `services/core/queries/workspace_slots.sql`
- `services/core/migrations/00004_create_project_sync_settings.sql`
- `services/core/migrations/00005_create_workspace_slots.sql`
- `services/core/cmd/server/main.go`

### 테스트 환경 규칙

- `test_env=local`.
- `agent-test/local/rules.md`를 읽었다. `services/core/**` 변경이므로 `agent-test/local/core-smoke.md`를 적용한다.
- `agent-roadmap/**`, `agent-task/**` 변경이 함께 있으므로 `agent-test/local/workspace-ops-smoke.md`도 적용한다.
- core 필수 검증은 `cd services/core && go test ./...`; focused 변경은 `go test -count=1`로 실제 재실행한다. roadmap/task 문서 검증은 `git diff --check`다.
- 규칙/프로필은 구조적으로 비어 있지 않았고 `<확인 필요>` 값은 기준 출력 예시뿐이다.

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

- `BuildProvisionPlan` 경로 계산은 `checkout_test.go`가 검증한다.
- 실제 checkout path가 존재하는지, 각 checkout에 `.git` file/dir이 있는지 검증하는 테스트는 없다. 이 계획에서 `provision_test.go`를 추가한다.
- missing branch/slot checkout을 명시 오류로 반환하고 cwd fallback을 사용하지 않는 회귀 테스트가 없다. 이 계획에서 temp dir 기반 negative test를 추가한다.

### 심볼 참조

- 이름 변경/삭제 없음.
- 새 symbol 후보: `ErrWorkspaceProvisionNotReady`, `WorkspaceProvisioner`, `FilesystemWorkspaceProvisioner`. 현재 call site 없음.

### 분할 판단

- split decision policy를 먼저 평가했다.
- 공유 task group: `m-milestone-work-item-creation-sync`.
- sibling 계획:
  - `06_workspace_provision`: filesystem checkout readiness guard. 독립.
  - `07+06_workspace_provision_pipeline`: pipeline/HTTP/storage 연결. `06_workspace_provision` PASS 후 실행.
- 이 계획은 `workspace-provision` 완료를 단독 주장하지 않으므로 `Roadmap Targets`를 쓰지 않는다. 완료 체크는 dependent `07+06_workspace_provision_pipeline` PASS가 담당한다.

### 범위 결정 근거

- 이 계획은 git clone/fetch/checkout 실행기를 만들지 않는다. 현재 codebase에 git process abstraction이 없고, 이 slice의 목표는 prepared checkout을 확인해 silent fallback을 막는 것이다.
- project setting 저장 API/UI가 현재 범위에 없으므로 setting-save hook은 구현하지 않는다. first-use 연결은 후속 `07+06_workspace_provision_pipeline`에서 처리한다.
- SQL/migration/query 변경은 하지 않는다.

### 빌드 등급

- `local-G06`: 단일 package 중심의 deterministic filesystem guard와 temp dir 테스트로 검증 가능하다.

## 구현 체크리스트

- [ ] `projectsync`에 prepared checkout 검증용 provisioner interface, filesystem 구현, sentinel error를 추가한다.
- [ ] `projectsync` temp dir 회귀 테스트로 main/develop/default slot checkout 준비 성공과 missing checkout/.git 실패를 검증한다.
- [ ] focused/final 검증을 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [API-1] Filesystem Workspace Readiness Guard

- `문제`: [services/core/internal/projectsync/checkout.go](/config/workspace/nomadcode/services/core/internal/projectsync/checkout.go:3)는 provision path만 계산한다. [services/core/internal/projectsync/checkout.go](/config/workspace/nomadcode/services/core/internal/projectsync/checkout.go:44)는 실제 clone/fetch/checkout 실행이 scope 밖이라고 기록하지만, prepared checkout 확인 API도 없다.

Before (`services/core/internal/projectsync/checkout.go:3`):

```go
// ProvisionPlan describes the complete workspace topology that must be prepared
// for a project before any slot checkout or sync operation can run. Building it
// never executes a git process; it only resolves paths from a normalized Config.
type ProvisionPlan struct {
```

Before (`services/core/internal/projectsync/checkout.go:44`):

```go
// CheckoutPlan is the policy object describing where a single workspace slot
// should be checked out for a project sync target. It is derived purely from a
// normalized Config and a reserved WorkspaceSlot; building it never runs a git
// process. Actual clone/fetch/checkout execution is out of this package's scope.
type CheckoutPlan struct {
```

- `해결 방법`: 새 파일 `services/core/internal/projectsync/provision.go`를 추가한다. `ErrWorkspaceProvisionNotReady`, `WorkspaceProvisioner`, `FilesystemWorkspaceProvisioner`를 정의하고 `EnsureProvisioned(ctx, plan)`이 `ProjectWorkspaceRoot`는 directory로, `MainBranchPath`, `DevelopBranchPath`, `DefaultSlotPath`는 directory plus `.git` file/dir로 확인하게 한다. 실패는 `%w`로 sentinel을 wrap하고 path를 포함한다. `ctx.Err()`가 있으면 즉시 반환한다.

After sketch:

```go
package projectsync

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path/filepath"
)

var ErrWorkspaceProvisionNotReady = errors.New("workspace provision is not ready")

type WorkspaceProvisioner interface {
	EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error
}

type FilesystemWorkspaceProvisioner struct{}

func (FilesystemWorkspaceProvisioner) EnsureProvisioned(ctx context.Context, plan ProvisionPlan) error {
	if err := ctx.Err(); err != nil {
		return err
	}
	if err := ensureDirectory(plan.ProjectWorkspaceRoot); err != nil {
		return err
	}
	for _, path := range []string{plan.MainBranchPath, plan.DevelopBranchPath, plan.DefaultSlotPath} {
		if err := ensureGitCheckout(path); err != nil {
			return err
		}
	}
	return nil
}
```

- `수정 파일 및 체크리스트`:
  - [ ] `services/core/internal/projectsync/provision.go`: provisioner interface, filesystem implementation, sentinel error.
  - [ ] `services/core/internal/projectsync/provision_test.go`: success, missing checkout, missing `.git`, context cancellation tests.
- `테스트 작성`: 새 `services/core/internal/projectsync/provision_test.go`를 작성한다. 테스트 이름은 `TestFilesystemWorkspaceProvisionerAcceptsPreparedCheckouts`, `TestFilesystemWorkspaceProvisionerRejectsMissingCheckout`, `TestFilesystemWorkspaceProvisionerRejectsPathWithoutGitMetadata`, `TestFilesystemWorkspaceProvisionerHonorsCanceledContext`로 둔다.
- `중간 검증`: `cd services/core && go test -count=1 ./internal/projectsync`가 PASS해야 한다.

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `services/core/internal/projectsync/provision.go` | API-1 |
| `services/core/internal/projectsync/provision_test.go` | API-1 |

## 최종 검증

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

예상: PASS. fresh execution required.

```bash
cd services/core && go test ./...
```

예상: PASS. broad baseline은 cache 출력 허용.

```bash
git diff --check
```

예상: 출력 없음.

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