diff --git a/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md b/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md index 39dfac8..2a27800 100644 --- a/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md +++ b/agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md @@ -55,7 +55,7 @@ Plane `Backlog + AGENT assignee` 상위 티켓 하나가 NomadCode project sync - [x] [cycle-authoring-run] Plane 티켓 제목/본문과 provider work item identity를 입력으로 `slots/` workspace에서 1회 authoring run을 실행한다. 검증: agent가 slot 안에서 Milestone 파일을 작성/갱신하고 `develop`에 commit/push한다. - [x] [cycle-develop-sync] sync layer가 `branches/develop` 또는 remote `develop`에서 pushed Milestone을 감지하고 원래 Plane 티켓 identity와 매칭한다. 검증: slot local 변경이나 push 실패 상태만으로 Plane `Todo` projection을 수행하지 않는다. - [x] [cycle-plane-todo] 원본 Plane 본문을 `사용자 요청:` 댓글로 보존하고, `develop`의 Milestone 내용으로 Plane 본문/제목을 갱신한 뒤 `Todo`로 이동한다. 검증: 사용자에게 Plane Todo에서 `develop` 반영 결과를 검토할 수 있는 상태가 된다. -- [ ] [cycle-idempotency] 같은 Plane 티켓 또는 같은 Milestone path 재처리 시 중복 생성 없이 남은 단계만 재시도한다. 검증: identity map, actor guard, provider revision, roadmap revision 기준으로 부분 실패를 복구한다. +- [x] [cycle-idempotency] 같은 Plane 티켓 또는 같은 Milestone path 재처리 시 중복 생성 없이 남은 단계만 재시도한다. 검증: identity map, actor guard, provider revision, roadmap revision 기준으로 부분 실패를 복구한다. ### Epic: [project-config] Project sync configuration @@ -74,10 +74,10 @@ Plane project 단위로 Milestone sync에 필요한 기본 설정을 저장하 Project sync workspace를 고정 branch workspace와 agent 작업 slot으로 분리해 source-of-truth scan, 테스트 동기화, release 기준, 병렬 authoring 실행이 서로 섞이지 않도록 한다. -- [ ] [branch-workspaces] repo별 project workspace root 아래 `branches/main`과 `branches/develop`을 항상 유지하는 기준을 정의한다. 검증: 두 workspace는 예약 가능한 agent 작업 slot이 아니며, `develop`은 roadmap/test sync 기준, `main`은 release/promote 기준으로만 사용된다. -- [ ] [slot-layout] agent authoring 작업공간을 `slots/000`, `slots/001`, `slots/002`처럼 `slots/` 하위 3자리 index로 계산하도록 경로 규칙을 갱신한다. 검증: `workspace_base_path=/home/user/workspace`, `repo_dir_name=nomadcode`이면 `develop` 기준 workspace는 `/home/user/workspace/nomadcode/branches/develop`, 기본 작업 slot은 `/home/user/workspace/nomadcode/slots/000`이다. -- [ ] [slot-source-branch] 새 agent slot은 source-of-truth branch인 `develop` 기준으로 checkout/branch 준비를 하되, Plane `Todo` projection은 slot local file이 아니라 `branches/develop` 또는 remote `develop` 반영 여부를 기준으로 판단한다. 검증: slot dirty 상태나 미push 변경만으로 Plane 본문 치환/`Todo` 이동을 하지 않는다. -- [ ] [branch-sync-state] branch workspace와 slot workspace의 상태 모델을 분리한다. 검증: branch workspace는 `synced`, `stale`, `error` 같은 sync 상태 후보를 갖고, slot workspace는 기존 `available`, `in_use`, `dirty`, `error` 같은 배정 상태를 유지한다. +- [x] [branch-workspaces] repo별 project workspace root 아래 `branches/main`과 `branches/develop`을 항상 유지하는 기준을 정의한다. 검증: 두 workspace는 예약 가능한 agent 작업 slot이 아니며, `develop`은 roadmap/test sync 기준, `main`은 release/promote 기준으로만 사용된다. +- [x] [slot-layout] agent authoring 작업공간을 `slots/000`, `slots/001`, `slots/002`처럼 `slots/` 하위 3자리 index로 계산하도록 경로 규칙을 갱신한다. 검증: `workspace_base_path=/home/user/workspace`, `repo_dir_name=nomadcode`이면 `develop` 기준 workspace는 `/home/user/workspace/nomadcode/branches/develop`, 기본 작업 slot은 `/home/user/workspace/nomadcode/slots/000`이다. +- [x] [slot-source-branch] 새 agent slot은 source-of-truth branch인 `develop` 기준으로 checkout/branch 준비를 하되, Plane `Todo` projection은 slot local file이 아니라 `branches/develop` 또는 remote `develop` 반영 여부를 기준으로 판단한다. 검증: slot dirty 상태나 미push 변경만으로 Plane 본문 치환/`Todo` 이동을 하지 않는다. +- [x] [branch-sync-state] branch workspace와 slot workspace의 상태 모델을 분리한다. 검증: branch workspace는 `synced`, `stale`, `error` 같은 sync 상태 후보를 갖고, slot workspace는 기존 `available`, `in_use`, `dirty`, `error` 같은 배정 상태를 유지한다. - [ ] [workspace-provision] project sync 설정 저장 또는 첫 사용 시 `branches/main`, `branches/develop`, 최소 `slots/000`의 checkout 준비 기준을 남긴다. 검증: missing workspace를 조용히 임의 cwd로 대체하지 않고 준비 실패를 명시 오류로 반환한다. ### Epic: [agent-bridge] Workspace agent IOP bridge @@ -146,9 +146,11 @@ Plane-origin 생성 경로를 `develop` agent-roadmap 기준의 `Todo` 검토 - Checkout 기준: 안정성을 우선해 branch workspace와 각 slot은 같은 git remote를 기준으로 독립 checkout을 갖는 작업공간으로 둔다. 초기 workspace 생성 때 clone 비용이 들지만 이후 branch sync와 agent 작업은 각 checkout 안에서 분리되므로 운영 리스크를 낮춘다. - 후속 UI/UX: project settings 화면에서 Plane project target, git remote, workspace 설정을 확인하고 수정하는 UX가 필요하다. - 완료 근거(2026-06-07): `[project-config]`는 `services/core/internal/projectsync/`, `services/core/migrations/00004_create_project_sync_settings.sql`, `services/core/migrations/00005_create_workspace_slots.sql`, `services/core/queries/project_sync_settings.sql`, `services/core/queries/workspace_slots.sql`, `packages/contracts/notes/flutter-core-api-candidates.md`의 모델/저장/slot/UI 후보 경계와 테스트로 확인했다. 검증: `cd services/core && go test ./...` PASS. -- 현황 동기화(2026-06-13): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/*/complete.log`의 `Roadmap Completion` 근거로 `cycle-workspace-topology`, `cycle-iop-http-ready`, `cycle-authoring-run`, `cycle-develop-sync`, `cycle-plane-todo`를 완료 처리했다. 현재 active 작업은 `agent-task/m-milestone-work-item-creation-sync/05+03,04_idempotency_retry`(`cycle-idempotency`)이며, 이 Task는 완료 evidence가 없어 미완료로 유지한다. -- 중지 지점: project sync 설정, workspace topology, IOP Edge HTTP authoring run 경계, pushed Milestone identity match, Plane 원문 댓글 보존/본문·제목 갱신/`Todo` 이동 projection은 완료 근거가 있다. 다음 실행 단위는 identity map 기반 idempotency/retry다. -- 루프 목표: Plane `Backlog + AGENT assignee` -> `slots/` workspace 예약/준비 -> IOP Edge HTTP로 workspace agent authoring run 실행 -> agent가 Milestone 파일 작성/갱신 후 `develop` push -> sync layer가 `branches/develop` 또는 remote `develop`에서 push 완료/roadmap scan을 감지 -> Plane 원문 댓글 보존/본문 치환/제목 변경/`Todo` 이동 -> 사용자가 확인 후 `In Progress`로 이동하는 지점까지다. 현재 최상위 사이클에서 남은 실행 단위는 `cycle-idempotency`이며, `In Progress` 이후 실행은 후속 잠금 범위다. +- 현황 동기화(2026-06-13): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/*/complete.log`의 `Roadmap Completion` 근거로 `cycle-workspace-topology`, `cycle-iop-http-ready`, `cycle-authoring-run`, `cycle-develop-sync`, `cycle-plane-todo`를 완료 처리했다. +- 현황 동기화(2026-06-14): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/05+03,04_idempotency_retry/complete.log`의 `Roadmap Completion` 근거로 `cycle-idempotency`를 완료 처리했다. 검증 근거는 identity map의 원자적 conflict 처리, strict retry identity key, `cd services/core && go test -count=1 ./internal/storage ./internal/roadmapsync ./internal/adapters/plane`, `cd services/core && go test ./...`, `git diff --check` PASS다. `./bin/sqlc`는 기존 `queries/workspace_slots.sql:19:11` ambiguity로 차단되어 완료 로그에 별도 기록되어 있다. +- Workspace topology 세부 동기화(2026-06-14): `agent-task/archive/2026/06/m-milestone-work-item-creation-sync/01_workspace_topology/complete.log`, `02+01_workspace_slot_store/complete.log`, `03+01,02_project_binding_checkout/complete.log`, `03+02_develop_match/complete.log`, `04+03_plane_todo_projection/complete.log`와 현재 `projectsync`/`workitempipeline` 테스트 근거로 `[branch-workspaces]`, `[slot-layout]`, `[slot-source-branch]`, `[branch-sync-state]`를 완료 처리했다. `[workspace-provision]`은 실제 first-use checkout 준비 검증과 기본 slot 등록, missing workspace 명시 오류 연결이 남아 `agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/` 및 `agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/` 계획으로 분리했다. +- 중지 지점: project sync 설정, workspace topology, IOP Edge HTTP authoring run 경계, pushed Milestone identity match, Plane 원문 댓글 보존/본문·제목 갱신/`Todo` 이동 projection, identity map 기반 idempotency/retry는 완료 근거가 있다. +- 루프 목표: Plane `Backlog + AGENT assignee` -> `slots/` workspace 예약/준비 -> IOP Edge HTTP로 workspace agent authoring run 실행 -> agent가 Milestone 파일 작성/갱신 후 `develop` push -> sync layer가 `branches/develop` 또는 remote `develop`에서 push 완료/roadmap scan을 감지 -> Plane 원문 댓글 보존/본문 치환/제목 변경/`Todo` 이동 -> 사용자가 확인 후 `In Progress`로 이동하는 지점까지다. 최상위 `plane-nomad-iop-cycle` 실행 단위는 완료 근거가 있으며, `In Progress` 이후 실행은 후속 잠금 범위다. - 후속 잠금: `Todo -> In Progress` 이후 실행 lifecycle은 사용자가 명시적으로 잠금을 해제할 때까지 진행하지 않는다. - Plane work item 경계: 이 마일스톤에서 말하는 Plane Milestone은 Plane native milestone object가 아니라 1상위 티켓 = 1 agent-roadmap Milestone으로 보는 work item mapping이다. - 확인 필요: 없음 diff --git a/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/CODE_REVIEW-local-G06.md b/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/CODE_REVIEW-local-G06.md new file mode 100644 index 0000000..c79e4bb --- /dev/null +++ b/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/CODE_REVIEW-local-G06.md @@ -0,0 +1,128 @@ + + +# Code Review Reference - API + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves. +> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date=2026-06-14 +task=m-milestone-work-item-creation-sync/06_workspace_provision, plan=0, tag=API + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-{review_lane}-GNN.md` → `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [API-1] Filesystem workspace readiness guard | [ ] | + +## 구현 체크리스트 + +- [ ] `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의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. +- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `FilesystemWorkspaceProvisioner`가 required checkout path를 cwd나 상대 fallback 없이 `ProvisionPlan`에서만 읽는지 확인한다. +- missing checkout 또는 missing `.git` 실패가 `ErrWorkspaceProvisionNotReady`로 판별 가능한지 확인한다. +- temp dir 테스트가 main/develop/default slot 세 path를 모두 검증하는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### API-1 중간 검증 +``` +$ cd services/core && go test -count=1 ./internal/projectsync +(output) +``` + +### 최종 검증 +``` +$ cd services/core && go test -count=1 ./internal/projectsync +(output) +``` + +``` +$ cd services/core && go test ./... +(output) +``` + +``` +$ git diff --check +(output) +``` + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** +> If anything is blank, go back and fill it in before saving this file. +> Leave review-agent-only sections unchanged. diff --git a/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/PLAN-local-G06.md b/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/PLAN-local-G06.md new file mode 100644 index 0000000..25bf3b2 --- /dev/null +++ b/agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/PLAN-local-G06.md @@ -0,0 +1,198 @@ + + +# 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`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/CODE_REVIEW-cloud-G07.md b/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/CODE_REVIEW-cloud-G07.md new file mode 100644 index 0000000..8cc8635 --- /dev/null +++ b/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/CODE_REVIEW-cloud-G07.md @@ -0,0 +1,156 @@ + + +# Code Review Reference - API + +> **[IMPLEMENTING AGENT — READ FIRST] Filling in this file is the mandatory final step of implementation.** +> The task is NOT complete until every implementation-owned section below is filled in. +> Complete the `구현 체크리스트`; the final checklist item is mandatory before saving. +> Fill implementation-owned sections, then stop with active files in place and report ready for review. +> If implementation is blocked by a user-only decision, user-owned external environment prerequisite, or scope conflict, fill `사용자 리뷰 요청` with evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Evidence gaps that a follow-up agent can close by rerunning commands or collecting artifacts are normal follow-up issues, not user-review blockers by themselves. +> Do not ask the user directly, present choices in chat, or call `request_user_input` during implementation; record the needed decision in `사용자 리뷰 요청` and stop for code-review. +> Finalization (`코드리뷰 결과`, log rename, `complete.log`, archive moves, `코드리뷰 전용 체크리스트`) is review-agent-only, even after compaction/resume. +> Follow the ownership table at the bottom of this file for which sections you own. + +## 개요 + +date=2026-06-14 +task=m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline, plan=0, tag=API + +## Roadmap Targets + +- Milestone: `agent-roadmap/phase/agent-ops-mcp-control-plane/milestones/milestone-work-item-creation-sync.md` +- Task ids: + - `workspace-provision`: project sync 설정 저장 또는 첫 사용 시 `branches/main`, `branches/develop`, 최소 `slots/000`의 checkout 준비 기준을 남긴다. +- Completion mode: check-on-pass + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-{review_lane}-GNN.md` → `code_review_{review_lane}_GNN_N.log`, `PLAN-{build_lane}-GNN.md` → `plan_{build_lane}_GNN_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 사용자 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [API-1] Pipeline provision step before slot reservation | [ ] | +| [API-2] HTTP binder provision and default slot registration | [ ] | +| [API-3] Provision error HTTP mapping | [ ] | + +## 구현 체크리스트 + +- [ ] `workitempipeline.ProjectBinder`에 project workspace provision step을 추가하고 `CreateTaskFromWorkItem`이 slot 예약/provider fetch 전에 호출하도록 한다. +- [ ] HTTP project sync binder가 provisioner를 실행하고 기본 `slots/000` row를 기존 `UpsertWorkspaceSlot`로 등록하도록 연결한다. +- [ ] provision failure를 HTTP service error로 명시 매핑하고 pipeline/HTTP 회귀 테스트를 추가한다. +- [ ] focused/final 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_M.log`로 아카이브한다. +- [ ] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다. +- [ ] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS이면 active task 디렉터리 `agent-task/{task_name}/`를 `agent-task/archive/YYYY/MM/{task_name}/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [ ] PASS이고 task group이 `m-`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/{task_group}/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. +- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-{build_lane}-GNN.md`와 `CODE_REVIEW-{review_lane}-GNN.md`를 작성하고 `complete.log`를 작성하지 않는다. +- [ ] USER_REVIEW이면 `agent-ops/skills/common/code-review/templates/user-review-template.md` 기준으로 `USER_REVIEW.md`를 작성하고 active `PLAN-*.md`, `CODE_REVIEW-*.md`, `complete.log`를 남기지 않는다. +- [ ] USER_REVIEW가 사용자 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 아래 항목을 실제 내용으로 교체하고, 구현을 중단한 뒤 active 파일을 그대로 둔 채 리뷰를 요청한다. 구현 에이전트는 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 증거 공백만으로는 사용자 리뷰 요청을 작성하지 않는다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- `CreateTaskFromWorkItem`이 provision failure에서 slot reserve/provider fetch/task creation을 실행하지 않는지 확인한다. +- HTTP binder가 default `slots/000` path를 `BuildProvisionPlan`에서 얻고 기존 `UpsertWorkspaceSlot`을 사용하는지 확인한다. +- provision error가 500이 아니라 409로 매핑되고 raw local path를 response에 노출하지 않는지 확인한다. +- `Roadmap Targets`의 `workspace-provision`은 이 계획이 PASS일 때만 check-on-pass 대상이다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### API-1 중간 검증 +``` +$ cd services/core && go test -count=1 ./internal/workitempipeline +(output) +``` + +### API-2 중간 검증 +``` +$ cd services/core && go test -count=1 ./internal/http +(output) +``` + +### API-3 중간 검증 +``` +$ cd services/core && go test -count=1 ./internal/http +(output) +``` + +### 최종 검증 +``` +$ cd services/core && go test -count=1 ./internal/projectsync ./internal/workitempipeline ./internal/http +(output) +``` + +``` +$ cd services/core && go test ./... +(output) +``` + +``` +$ cd services/core && go vet ./... +(output) +``` + +``` +$ git diff --check +(output) +``` + +--- + +> **[IMPLEMENTING AGENT — BEFORE SAVING] Have you filled in every implementation-owned section: completion table, implementation checklist, changes from plan, design decisions, and verification output?** +> If anything is blank, go back and fill it in before saving this file. +> Leave review-agent-only sections unchanged. diff --git a/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/PLAN-cloud-G07.md b/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/PLAN-cloud-G07.md new file mode 100644 index 0000000..32f945d --- /dev/null +++ b/agent-task/m-milestone-work-item-creation-sync/07+06_workspace_provision_pipeline/PLAN-cloud-G07.md @@ -0,0 +1,304 @@ + + +# Workspace Provision Pipeline Plan - API + +## 이 파일을 읽는 구현 에이전트에게 + +`CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채우는 것이 필수 최종 단계다. 구현 후 검증을 실행하고 실제 변경 내용, 설계 결정, 검증 출력을 review stub에 기록한 뒤 active 파일을 남겨 리뷰를 요청한다. 최종 archive, `complete.log`, 로그 rename은 code-review 전용이다. 사용자 결정, 사용자 소유 외부 환경/secret/서비스 준비, 또는 계획 범위 변경 없이는 안전하게 진행할 수 없으면 review stub의 `사용자 리뷰 요청` 섹션에 정확한 근거를 기록하고 멈춘다. 구현 중 사용자에게 직접 묻거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 후속 에이전트가 명령 재실행이나 산출물 수집으로 해소할 수 있는 검증 공백은 사용자 리뷰 요청이 아니다. + +## 배경 + +`workspace-provision`의 남은 부분은 first-use 경로에서 `branches/main`, `branches/develop`, 최소 `slots/000`이 준비되어 있는지 확인하고, 기본 slot을 DB에 등록한 뒤에만 slot 예약과 provider fetch를 진행하는 것이다. 현재 pipeline은 project sync config와 slot 예약은 연결하지만, prepared checkout 확인과 default slot 등록을 같은 runtime boundary에 묶지 않는다. 이 계획은 `06_workspace_provision`의 provisioner가 PASS된 뒤 pipeline/HTTP/storage adapter에 연결해 roadmap Task 완료를 목표로 한다. + +## 사용자 리뷰 요청 흐름 + +구현 중 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: + - `workspace-provision`: project sync 설정 저장 또는 첫 사용 시 `branches/main`, `branches/develop`, 최소 `slots/000`의 checkout 준비 기준을 남긴다. +- Completion mode: check-on-pass + +## 분석 결과 + +### 읽은 파일 + +- `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`다. +- 규칙/프로필은 구조적으로 비어 있지 않았고 `<확인 필요>` 값은 기준 출력 예시뿐이다. + +### 테스트 커버리지 공백 + +- work item pipeline은 project binding, invalid config, no slot, checkout metadata를 검증하지만 provision failure before reservation/provider fetch를 검증하지 않는다. +- HTTP binder는 resolve/reserve error propagation을 검증하지만 default slot upsert와 provisioner failure propagation을 검증하지 않는다. +- HTTP service error mapping은 project sync missing/no slot을 검증하지만 `ErrWorkspaceProvisionNotReady` mapping이 없다. + +### 심볼 참조 + +- 이름 변경/삭제 없음. +- `ProjectBinder` interface가 확장되면 모든 구현체를 갱신해야 한다. +- 참조 위치: + - `services/core/internal/workitempipeline/service.go`: `ProjectBinder` 정의와 `CreateTaskFromWorkItem` call site. + - `services/core/internal/workitempipeline/service_test.go`: `fakeBinder`, `mapBinder`. + - `services/core/internal/http/handlers.go`: `projectSyncBinder`. + - `services/core/internal/http/handlers_test.go`: `fakeProjectSyncStore`, binder tests. + +### 분할 판단 + +- split decision policy를 먼저 평가했다. +- 공유 task group: `m-milestone-work-item-creation-sync`. +- sibling 계획: + - `06_workspace_provision`: provisioner foundation. + - `07+06_workspace_provision_pipeline`: pipeline/HTTP/storage rollout. +- predecessor index `06`: active sibling `agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/complete.log`가 아직 없다. 이 계획은 해당 complete.log가 생긴 뒤 구현해야 한다. + +### 범위 결정 근거 + +- 이 계획은 git clone/fetch/checkout 실행기를 만들지 않는다. prepared checkout 확인은 `06_workspace_provision`의 provisioner가 담당한다. +- project setting 저장 API/UI hook은 현재 codebase에 없으므로 first-use path만 연결한다. setting-save hook은 후속 settings UI/API slice에서 같은 provisioner를 호출하면 된다. +- SQL/migration/query 변경은 하지 않는다. 기본 slot 등록은 기존 `UpsertWorkspaceSlot` query를 사용한다. +- Plane projection, develop match, idempotency/retry 흐름은 이미 별도 완료 로그가 있으므로 수정하지 않는다. + +### 빌드 등급 + +- `cloud-G07`: internal interface 확장, storage side effect, HTTP error mapping, runtime ordering이 결합된 cross-package 변경이다. + +## 의존 관계 및 구현 순서 + +- `07+06_workspace_provision_pipeline`은 `06_workspace_provision`에만 의존한다. +- 구현 시작 전 `agent-task/m-milestone-work-item-creation-sync/06_workspace_provision/complete.log` 또는 matching archive complete.log가 있어야 한다. +- 이 계획 파일 작성 시점에는 predecessor complete.log가 없다. + +## 구현 체크리스트 + +- [ ] `workitempipeline.ProjectBinder`에 project workspace provision step을 추가하고 `CreateTaskFromWorkItem`이 slot 예약/provider fetch 전에 호출하도록 한다. +- [ ] HTTP project sync binder가 provisioner를 실행하고 기본 `slots/000` row를 기존 `UpsertWorkspaceSlot`로 등록하도록 연결한다. +- [ ] provision failure를 HTTP service error로 명시 매핑하고 pipeline/HTTP 회귀 테스트를 추가한다. +- [ ] focused/final 검증을 실행한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### [API-1] Pipeline Provision Step Before Slot Reservation + +- `문제`: [services/core/internal/workitempipeline/service.go](/config/workspace/nomadcode/services/core/internal/workitempipeline/service.go:38)의 `ProjectBinder`는 resolve/reserve만 제공한다. [services/core/internal/workitempipeline/service.go](/config/workspace/nomadcode/services/core/internal/workitempipeline/service.go:77)는 config normalize 후 곧바로 slot을 예약하므로 prepared checkout 실패를 예약 전에 막을 hook이 없다. + +Before (`services/core/internal/workitempipeline/service.go:38`): + +```go +// ProjectBinder resolves the active project sync binding for a work item's +// provider/project target and reserves an independent workspace slot for it. +// Implementations must key the binding strictly on the normalized +// provider/tenant/project so tickets from the same project share one config and +// never mix with another project's config, and must reserve one available slot +// atomically so concurrent requests never receive the same slot. +type ProjectBinder interface { + ResolveProjectBinding(ctx context.Context, ref workitem.Ref) (ProjectBinding, error) + ReserveWorkspaceSlot(ctx context.Context, projectSyncSettingID int64) (projectsync.WorkspaceSlot, error) +} +``` + +Before (`services/core/internal/workitempipeline/service.go:88`): + +```go + config, err := binding.Config.Normalize() + if err != nil { + return storage.Task{}, err + } + slot, err := s.binder.ReserveWorkspaceSlot(ctx, binding.SettingID) +``` + +- `해결 방법`: `ProjectBinder`에 `EnsureProjectWorkspace(ctx context.Context, binding ProjectBinding) error`를 추가한다. `CreateTaskFromWorkItem`은 config normalize 뒤, `ReserveWorkspaceSlot` 전, provider fetch 전 이 hook을 호출한다. provision error면 slot 예약, provider fetch, task creation을 모두 수행하지 않는다. + +After sketch: + +```go +type ProjectBinder interface { + ResolveProjectBinding(ctx context.Context, ref workitem.Ref) (ProjectBinding, error) + EnsureProjectWorkspace(ctx context.Context, binding ProjectBinding) error + ReserveWorkspaceSlot(ctx context.Context, projectSyncSettingID int64) (projectsync.WorkspaceSlot, error) +} +``` + +```go + config, err := binding.Config.Normalize() + if err != nil { + return storage.Task{}, err + } + if err := s.binder.EnsureProjectWorkspace(ctx, binding); err != nil { + return storage.Task{}, err + } + slot, err := s.binder.ReserveWorkspaceSlot(ctx, binding.SettingID) +``` + +- `수정 파일 및 체크리스트`: + - [ ] `services/core/internal/workitempipeline/service.go`: interface 확장, ordering 변경, comment 갱신. + - [ ] `services/core/internal/workitempipeline/service_test.go`: fake/map binder 구현 갱신, provision failure regression 추가. +- `테스트 작성`: `TestCreateTaskFromWorkItemBlocksWhenWorkspaceProvisionFails`를 추가한다. assertion: provision error가 반환되고 `reserveCalled=false`, `reader.fetchCalled=false`, `creator.calledWith=nil`. +- `중간 검증`: `cd services/core && go test -count=1 ./internal/workitempipeline`가 PASS해야 한다. + +### [API-2] HTTP Binder Provision And Default Slot Registration + +- `문제`: [services/core/internal/http/handlers.go](/config/workspace/nomadcode/services/core/internal/http/handlers.go:59)의 `ProjectSyncStore`는 active setting lookup과 reserve만 갖고 있다. [services/core/internal/http/handlers.go](/config/workspace/nomadcode/services/core/internal/http/handlers.go:77)의 binder는 provision plan을 실행하지 않고 default slot도 first-use에서 등록하지 않는다. + +Before (`services/core/internal/http/handlers.go:59`): + +```go +// ProjectSyncStore is the narrow storage dependency the project sync binder needs +// to look up the active setting for a provider/project target and to reserve an +// available workspace slot for it. +type ProjectSyncStore interface { + GetActiveProjectSyncSettingByTarget(ctx context.Context, provider, tenant, project string) (db.ProjectSyncSetting, error) + ReserveWorkspaceSlot(ctx context.Context, projectSyncSettingID int64) (db.WorkspaceSlot, error) +} +``` + +Before (`services/core/internal/http/handlers.go:77`): + +```go +func NewProjectBinder(store ProjectSyncStore) workitempipeline.ProjectBinder { + if store == nil { + return nil + } + return projectSyncBinder{store: store} +} +``` + +- `해결 방법`: `ProjectSyncStore`에 `UpsertWorkspaceSlot(ctx, db.UpsertWorkspaceSlotParams)`를 추가한다. `projectSyncBinder`에 `projectsync.WorkspaceProvisioner`를 주입하고 `NewProjectBinder(store)`는 filesystem provisioner를 기본값으로 쓴다. 테스트용으로 `NewProjectBinderWithProvisioner(store, provisioner)`를 추가한다. `EnsureProjectWorkspace`는 config normalize, `BuildProvisionPlan`, `provisioner.EnsureProvisioned`, `ToUpsertWorkspaceSlotParams(settingID, DefaultSlotIndex, plan.DefaultSlotPath)`, `store.UpsertWorkspaceSlot` 순서로 실행한다. + +After sketch: + +```go +type ProjectSyncStore interface { + GetActiveProjectSyncSettingByTarget(ctx context.Context, provider, tenant, project string) (db.ProjectSyncSetting, error) + UpsertWorkspaceSlot(ctx context.Context, args db.UpsertWorkspaceSlotParams) (db.WorkspaceSlot, error) + ReserveWorkspaceSlot(ctx context.Context, projectSyncSettingID int64) (db.WorkspaceSlot, error) +} +``` + +```go +func NewProjectBinder(store ProjectSyncStore) workitempipeline.ProjectBinder { + return NewProjectBinderWithProvisioner(store, projectsync.FilesystemWorkspaceProvisioner{}) +} +``` + +- `수정 파일 및 체크리스트`: + - [ ] `services/core/internal/http/handlers.go`: store interface, binder fields/constructors, `EnsureProjectWorkspace` 구현. + - [ ] `services/core/internal/http/handlers_test.go`: fake store/provisioner 갱신, default slot upsert/provision error tests. +- `테스트 작성`: `TestNewProjectBinderEnsuresWorkspaceAndRegistersDefaultSlot`와 `TestNewProjectBinderPropagatesProvisionErrorWithoutUpsert`를 추가한다. assertion: default slot path는 `BuildProvisionPlan`의 `DefaultSlotPath`, provision error면 upsert/reserve가 호출되지 않는다. +- `중간 검증`: `cd services/core && go test -count=1 ./internal/http`가 PASS해야 한다. + +### [API-3] Provision Error HTTP Mapping + +- `문제`: [services/core/internal/http/handlers.go](/config/workspace/nomadcode/services/core/internal/http/handlers.go:329)의 service error mapping은 project sync missing/no slot만 구분한다. workspace checkout 준비 실패는 default 500으로 떨어질 수 있다. + +Before (`services/core/internal/http/handlers.go:329`): + +```go +func (h *Handler) writeServiceError(w stdhttp.ResponseWriter, err error) { + switch { + case errors.Is(err, workflow.ErrInvalidTaskInput): + writeError(w, stdhttp.StatusBadRequest, err.Error()) + case errors.Is(err, workflow.ErrTaskCannotBeEnqueued): + writeError(w, stdhttp.StatusConflict, err.Error()) + case errors.Is(err, workitempipeline.ErrProjectSyncNotConfigured): + writeError(w, stdhttp.StatusServiceUnavailable, "project sync resolver is not configured") + case errors.Is(err, storage.ErrProjectSyncNotFound): + writeError(w, stdhttp.StatusUnprocessableEntity, "no active project sync setting for the work item's project") + case errors.Is(err, storage.ErrNoAvailableWorkspaceSlot): + writeError(w, stdhttp.StatusConflict, "no available workspace slot") + case errors.Is(err, pgx.ErrNoRows): +``` + +- `해결 방법`: `projectsync.ErrWorkspaceProvisionNotReady`를 `409 Conflict`로 매핑하고, response message는 raw path leak을 피하면서 원인을 알 수 있게 `"workspace provision is not ready"`로 둔다. + +After sketch: + +```go + case errors.Is(err, projectsync.ErrWorkspaceProvisionNotReady): + writeError(w, stdhttp.StatusConflict, "workspace provision is not ready") +``` + +- `수정 파일 및 체크리스트`: + - [ ] `services/core/internal/http/handlers.go`: error mapping 추가. + - [ ] `services/core/internal/http/handlers_test.go`: create work item route가 provision error에 409를 반환하는 test 추가. +- `테스트 작성`: `TestCreateWorkItemTaskReturns409WhenWorkspaceProvisionNotReady`를 추가한다. +- `중간 검증`: `cd services/core && go test -count=1 ./internal/http`가 PASS해야 한다. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `services/core/internal/workitempipeline/service.go` | API-1 | +| `services/core/internal/workitempipeline/service_test.go` | API-1 | +| `services/core/internal/http/handlers.go` | API-2, API-3 | +| `services/core/internal/http/handlers_test.go` | API-2, API-3 | + +## 최종 검증 + +```bash +cd services/core && go test -count=1 ./internal/projectsync ./internal/workitempipeline ./internal/http +``` + +예상: PASS. predecessor package와 rollout packages fresh execution required. + +```bash +cd services/core && go test ./... +``` + +예상: PASS. broad baseline은 cache 출력 허용. + +```bash +cd services/core && go vet ./... +``` + +예상: PASS. + +```bash +git diff --check +``` + +예상: 출력 없음. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다.