<!-- task=m-agent-readable-repository-refactor/02+01_openai_responses_workspace plan=1 tag=REVIEW_TEST -->

# OpenAI Responses/workspace 테스트 분리 범위 복구

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

> 잘못 옮긴 테스트를 복원한 뒤 실제 책임 그룹을 다시 분리한다. 구현 후 검증을 실행하고 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션에 실제 메모와 stdout/stderr를 채운 뒤 active 파일을 유지한 채 리뷰 준비 완료를 보고한다. 종결 판정, log rename, `complete.log`, archive 이동은 code-review 전용이다. 선택된 Milestone의 미해결 `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 review stub의 `사용자 리뷰 요청`에 정확한 연결 근거를 기록하고 code-review를 위해 중단한다. 구현 중 사용자에게 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하거나 `USER_REVIEW.md`/`complete.log`를 만들거나 archive를 수행하지 않는다. 환경·secret·service 차단, 일반 범위 조정, 후속 에이전트가 보완할 수 있는 증거 공백은 사용자 리뷰 사유가 아니다.

## 배경

첫 구현은 predecessor 완료 후에도 이전 라인 범위를 그대로 적용해, 계획한 Responses/workspace/cancel/route 테스트 대신 후속 provider-pool/tool/log 테스트 26개를 옮겼다. 테스트 본문과 package 행동은 보존됐지만 파일명이 실제 책임을 설명하지 못하고 `[TEST-1]`의 대상은 아직 `server_test.go`에 남아 있다. 함수 의미 앵커를 기준으로 범위를 바로잡는다.

## 사용자 리뷰 요청 흐름

사용자 리뷰 요청은 선택된 Milestone의 구현 잠금 결정만 active review stub의 `사용자 리뷰 요청` 섹션에 기록하며, `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` 형식을 따른다. 구현 중 직접 사용자 prompt는 금지하고, 요청 타당성 검증과 실제 `USER_REVIEW.md` 작성은 code-review가 소유한다.

## Archive Evidence Snapshot

- Prior task: `agent-task/m-agent-readable-repository-refactor/02+01_openai_responses_workspace`
- Archived plan: `agent-task/m-agent-readable-repository-refactor/02+01_openai_responses_workspace/plan_local_G07_0.log`
- Archived review: `agent-task/m-agent-readable-repository-refactor/02+01_openai_responses_workspace/code_review_local_G07_0.log`
- Verdict: `FAIL`
- Findings: Required 2, Suggested 0, Nit 0. 계획 대상은 `server_test.go:25~1310`에 남았고, 후속 provider-pool/tool/log 테스트 26개가 세 파일로 잘못 이동됐다.
- Affected files: `apps/edge/internal/openai/server_test.go`, `apps/edge/internal/openai/responses_handler_test.go`, `apps/edge/internal/openai/workspace_metadata_test.go`, `apps/edge/internal/openai/cancellation_routes_test.go`
- Verification evidence: 옮겨진 26개 `Test*` 본문 해시는 HEAD 원본과 모두 일치했고, `gofmt -d` 출력은 없었으며, `GOCACHE=/tmp/iop-go-build-cache go test -count=1 ./apps/edge/internal/openai`는 `ok`(6.721s), top-level Test는 226개였다.
- Narrow reread allowed when exact prior wording is needed: 위 archived plan/review 두 파일만 읽는다. `agent-task/archive/**`를 광범위하게 탐색하지 않는다.

## 분석 결과

### 읽은 파일

- `agent-roadmap/phase/automation-runtime-bridge/milestones/agent-readable-repository-refactor.md`
- `agent-test/local/rules.md`
- `agent-test/local/edge-smoke.md`
- `agent-test/local/testing-smoke.md`
- `agent-spec/index.md`
- `agent-spec/input/openai-compatible-surface.md`
- `agent-contract/index.md`
- `agent-contract/outer/openai-compatible-api.md`
- `agent-task/archive/2026/07/m-agent-readable-repository-refactor/01_openai_auth_chat/complete.log`
- `agent-task/m-agent-readable-repository-refactor/02+01_openai_responses_workspace/plan_local_G07_0.log`
- `agent-task/m-agent-readable-repository-refactor/02+01_openai_responses_workspace/code_review_local_G07_0.log`
- `apps/edge/internal/openai/server_test.go`
- `apps/edge/internal/openai/server_test_support_test.go`
- `apps/edge/internal/openai/responses_handler_test.go`
- `apps/edge/internal/openai/workspace_metadata_test.go`
- `apps/edge/internal/openai/cancellation_routes_test.go`

### SDD 기준

선택 Milestone은 `SDD: 불필요`다. 외부 API/proto/config schema와 runtime 책임 경계를 바꾸지 않고 같은 package의 테스트 선언 위치만 바꾸는 동작 보존형 리팩터링이라는 기록 사유를 적용한다.

### 테스트 환경 규칙

`test_env=local`이다. `agent-test/local/rules.md`와 매칭 profile `agent-test/local/edge-smoke.md`를 읽었고 변경 package의 fresh `go test`를 필수 검증으로 적용한다. `agent-test/local/testing-smoke.md`도 확인했지만 Makefile/script/검증 체계 변경이 아니므로 추가 smoke는 적용하지 않는다. 캐시 결과는 허용하지 않고 `-count=1`을 사용하며 Test 226개 보존을 별도로 검증한다.

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

운영 행동 변경은 없다. 현재 잘못 옮겨진 26개 테스트의 본문 해시가 HEAD와 일치하고 package test와 226개 목록이 통과했다. 후속 구현에서는 테스트 추가 대신 기존 함수명·assertion·package 경계 보존으로 누락을 검출한다.

### 심볼 참조

운영 심볼의 rename/remove는 없다. `Test*`, `workspaceBoundCfg`, `fakeRunResultWithTimeout` 선언 위치만 바꾸고 이름과 본문은 유지한다.

### 분할 판단

분할 정책을 다시 평가했다. 이 후속은 기존 split subtask `02+01_openai_responses_workspace`의 동일 네 파일에서 잘못된 이동을 복원한 뒤 올바른 이동을 연속 적용해야 하므로 추가 split은 중간 상태만 늘린다. predecessor index `01`은 `agent-task/archive/2026/07/m-agent-readable-repository-refactor/01_openai_auth_chat/complete.log`로 충족됐다.

### 범위 결정 근거

`server_test.go:25~1310`의 현재 함수를 의미 앵커로 분류한다. 잘못 선행 이동된 provider-pool selection/tool-validation/assembled-log 26개는 우선 `server_test.go`로 복원하고, provider tunnel/pool 후반부와 생산 소스, spec/contract 문서는 변경하지 않는다. `TestChatCompletionsRejectsUnsupportedFields`, think-control 검증처럼 세 파일 책임에 명확히 속하지 않는 테스트는 억지로 옮기지 않고 `server_test.go`에 남긴다.

### 빌드 등급

`local-G07`: 동작 판단 없이 함수 선언을 재배치하는 bounded 작업이지만, 1,300줄 이상의 현재 diff를 복원하고 40개 이상의 의미 경계를 누락 없이 유지해야 한다.

## 구현 체크리스트

- [ ] [REVIEW_TEST-1] 세 신규 파일에 잘못 옮겨진 provider-pool/tool/log `Test*` 26개를 본문 변경 없이 `server_test.go`로 복원한다.
- [ ] [REVIEW_TEST-2] 실제 Responses handler, metadata/workspace, cancel/timeout/route 테스트를 의미 앵커 기준으로 세 책임 파일에 옮기고 fresh package test와 top-level Test 226개를 보존한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.

### [REVIEW_TEST-1] 후속 범위 선행 이동 복원

- 문제: `responses_handler_test.go:16~256`, `workspace_metadata_test.go:16~686`, `cancellation_routes_test.go:14~419`의 26개 테스트는 파일명의 책임이 아니며 원래 `server_test.go:3209~4531`에 있던 후속 provider-pool/tool/log 범위다.
- 해결 방법:

~~~text
Before
responses_handler_test.go     Responses metadata estimate + assembled chat logs 6개
workspace_metadata_test.go    provider-pool selection/tunnel 10개
cancellation_routes_test.go   legacy fallback + text tool validation 10개

After
server_test.go                위 26개를 HEAD 원본 순서/본문로 복원
세 책임 파일          REVIEW_TEST-2 대상만 다시 배치
~~~

- 수정 파일 및 체크리스트:
  - [ ] `apps/edge/internal/openai/server_test.go`: 현재 세 파일의 26개 `Test*`를 이름·본문·assertion 변경 없이 원래 순서로 복원한다.
  - [ ] `apps/edge/internal/openai/responses_handler_test.go`: 현재 6개 선언을 복원 대상에서 제거한다.
  - [ ] `apps/edge/internal/openai/workspace_metadata_test.go`: 현재 10개 선언을 복원 대상에서 제거한다.
  - [ ] `apps/edge/internal/openai/cancellation_routes_test.go`: 현재 10개 선언을 복원 대상에서 제거한다.
- 테스트 작성: 추가하지 않는다. 잘못 옮겨진 테스트 본문이 이미 HEAD와 hash-identical이므로 위치만 복원한다.
- 중간 검증:

~~~bash
gofmt -w apps/edge/internal/openai/server_test.go apps/edge/internal/openai/responses_handler_test.go apps/edge/internal/openai/workspace_metadata_test.go apps/edge/internal/openai/cancellation_routes_test.go
! git diff -- apps/edge/internal/openai/server_test.go | rg '^[-+].*func Test(ChatCompletionsProviderPoolOllamaSelectionUsesNormalizedRun|ChatCompletionsRetriesUnknownTextToolCallBeforeResponse|ResponsesMetadataIncludesTypedEstimateAndClassification|ChatCompletionsAssembledLogs)'
~~~

기대 결과: 포맷 성공, 잘못 옮겨진 대표 함수 선언이 `server_test.go` diff에서 추가/삭제로 남지 않음.

### [REVIEW_TEST-2] 실제 Responses/workspace/cancel/route 책임 분리

- 문제: `server_test.go:25~1310`의 Responses shape, generic metadata/workspace, cancel/timeout, route catalog 테스트가 아직 거대 파일에 남아 `[TEST-1]`이 완료되지 않았다.
- 해결 방법:

~~~text
responses_handler_test.go
  TestResponsesDispatchesNonStreamingRequest / RejectsUnsupportedRequests
  ConfiguredTarget / response shape / sentinel / usage / strict output

workspace_metadata_test.go
  Responses + Chat generic metadata/workspace validation
  workspaceBoundCfg + workspace-required route tests + distinct run failures

cancellation_routes_test.go
  collectRunResult timeout/closed-stream + CancelRun tests
  models/Chat/Responses route catalog tests
~~~

- 수정 파일 및 체크리스트:
  - [ ] `apps/edge/internal/openai/responses_handler_test.go`: normalized Responses request/shape/strict output 테스트만 옮긴다.
  - [ ] `apps/edge/internal/openai/workspace_metadata_test.go`: Responses/Chat metadata 검증, `workspaceBoundCfg`, workspace-required route 테스트, workspace 실패 분류를 옮긴다.
  - [ ] `apps/edge/internal/openai/cancellation_routes_test.go`: `collectRunResult` timeout/closed stream, cancel 전파, model/route catalog 테스트와 필요 helper를 옮긴다.
  - [ ] `apps/edge/internal/openai/server_test.go`: 위 선언만 제거하고 provider-pool/tunnel/tool 후반부와 세 책임에 명확히 속하지 않는 Chat validation 테스트를 유지한다.
- 테스트 작성: 추가하지 않는다. 기존 top-level Test 이름과 assertion을 보존하고 fresh package test와 Test 개수로 누락을 검출한다.
- 중간 검증:

~~~bash
rg --sort path -n '^func TestResponsesDispatchesNonStreamingRequest' apps/edge/internal/openai/responses_handler_test.go
rg --sort path -n '^func Test(ChatCompletionsMetadataContractAndWorkspace|ResponsesWorkspaceRequiredRouteMissingWorkspace400)' apps/edge/internal/openai/workspace_metadata_test.go
rg --sort path -n '^func Test(CollectRunResultTimesOut|ChatCompletionContextCancelSendsCancelRun|ModelsExposesCatalogRouteModels|ResponsesRouteCatalogDispatchesRoute)' apps/edge/internal/openai/cancellation_routes_test.go
! rg -n '^func Test(ResponsesDispatchesNonStreamingRequest|ChatCompletionsMetadataContractAndWorkspace|CollectRunResultTimesOut|ModelsExposesCatalogRouteModels)' apps/edge/internal/openai/server_test.go
~~~

기대 결과: 대표 의미 앵커가 각 책임 파일에 존재하고 `server_test.go`에서는 제거됨.

## 의존 관계 및 구현 순서

1. `agent-task/archive/2026/07/m-agent-readable-repository-refactor/01_openai_auth_chat/complete.log`가 predecessor index `01`을 충족한다.
2. `[REVIEW_TEST-1]`로 후속 범위 26개를 복원한 뒤 `[REVIEW_TEST-2]`로 실제 대상을 옮긴다.
3. 디렉터리 이름에 없는 추가 predecessor를 만들지 않는다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `apps/edge/internal/openai/server_test.go` | REVIEW_TEST-1, REVIEW_TEST-2 |
| `apps/edge/internal/openai/responses_handler_test.go` | REVIEW_TEST-1, REVIEW_TEST-2 |
| `apps/edge/internal/openai/workspace_metadata_test.go` | REVIEW_TEST-1, REVIEW_TEST-2 |
| `apps/edge/internal/openai/cancellation_routes_test.go` | REVIEW_TEST-1, REVIEW_TEST-2 |

## 최종 검증

~~~bash
gofmt -w apps/edge/internal/openai/server_test.go apps/edge/internal/openai/responses_handler_test.go apps/edge/internal/openai/workspace_metadata_test.go apps/edge/internal/openai/cancellation_routes_test.go
test -z "$(gofmt -l apps/edge/internal/openai/server_test.go apps/edge/internal/openai/responses_handler_test.go apps/edge/internal/openai/workspace_metadata_test.go apps/edge/internal/openai/cancellation_routes_test.go)"
GOCACHE=/tmp/iop-go-build-cache go test -count=1 ./apps/edge/internal/openai
test "$(GOCACHE=/tmp/iop-go-build-cache go test -list '^Test' ./apps/edge/internal/openai | sed '/^ok[[:space:]]/d' | wc -l)" -eq 226
rg --sort path -n '^func TestResponsesDispatchesNonStreamingRequest' apps/edge/internal/openai/responses_handler_test.go
rg --sort path -n '^func Test(ChatCompletionsMetadataContractAndWorkspace|ResponsesWorkspaceRequiredRouteMissingWorkspace400)' apps/edge/internal/openai/workspace_metadata_test.go
rg --sort path -n '^func Test(CollectRunResultTimesOut|ChatCompletionContextCancelSendsCancelRun|ModelsExposesCatalogRouteModels|ResponsesRouteCatalogDispatchesRoute)' apps/edge/internal/openai/cancellation_routes_test.go
! rg -n '^func Test(ResponsesDispatchesNonStreamingRequest|ChatCompletionsMetadataContractAndWorkspace|CollectRunResultTimesOut|ModelsExposesCatalogRouteModels)' apps/edge/internal/openai/server_test.go
! git diff -- apps/edge/internal/openai/server_test.go | rg '^[-+].*func Test(ChatCompletionsProviderPoolOllamaSelectionUsesNormalizedRun|ChatCompletionsRetriesUnknownTextToolCallBeforeResponse|ResponsesMetadataIncludesTypedEstimateAndClassification|ChatCompletionsAssembledLogs)'
~~~

기대 결과: 포맷 정상, fresh package test PASS, top-level Test 226개, 실제 책임 앵커는 세 파일에 존재, 잘못 선행 이동된 대표 선언은 `server_test.go` diff에 없음.

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