chore: archive completed subtask files and update server_test.go

This commit is contained in:
toki 2026-06-13 23:33:39 +09:00
parent d49daf9646
commit af639537ec
4 changed files with 160 additions and 30 deletions

View file

@ -42,41 +42,42 @@ task=m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape, plan=0
| 항목 | 완료 여부 |
|------|---------|
| [NOMAD_METADATA-1] NomadCode OpenAI Responses client가 `WorkspaceMetadata.Path`를 flat `metadata.workspace` string으로 보내도록 바꾸고 tests를 갱신한다. | [ ] |
| [NOMAD_METADATA-2] IOP consumer fixture가 old object shape를 거부하고 flat task/source/workspace contract를 유지함을 테스트한다. | [ ] |
| [NOMAD_METADATA-1] NomadCode OpenAI Responses client가 `WorkspaceMetadata.Path`를 flat `metadata.workspace` string으로 보내도록 바꾸고 tests를 갱신한다. | [x] |
| [NOMAD_METADATA-2] IOP consumer fixture가 old object shape를 거부하고 flat task/source/workspace contract를 유지함을 테스트한다. | [x] |
## 구현 체크리스트
- [ ] [NOMAD_METADATA-1] NomadCode OpenAI Responses client가 `WorkspaceMetadata.Path`를 flat `metadata.workspace` string으로 보내도록 바꾸고 tests를 갱신한다.
- [ ] [NOMAD_METADATA-2] IOP consumer fixture가 old object shape를 거부하고 flat task/source/workspace contract를 유지함을 테스트한다.
- [ ] `cd ../nomadcode/services/core && go test -count=1 ./...`와 IOP Edge tests를 실행한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
- [x] [NOMAD_METADATA-1] NomadCode OpenAI Responses client가 `WorkspaceMetadata.Path`를 flat `metadata.workspace` string으로 보내도록 바꾸고 tests를 갱신한다.
- [x] [NOMAD_METADATA-2] IOP consumer fixture가 old object shape를 거부하고 flat task/source/workspace contract를 유지함을 테스트한다.
- [x] `cd ../nomadcode/services/core && go test -count=1 ./...`와 IOP Edge tests를 실행한다.
- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다.
## 코드리뷰 전용 체크리스트
> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다.
> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다.
- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [ ] active `PLAN-*-G??.md`를 `plan_cloud_G07_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/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/`를 `agent-task/archive/YYYY/MM/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [ ] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [ ] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-openai-workspace-agent-execution-contract/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다.
- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다.
- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_cloud_G07_N.log`로 아카이브한다.
- [x] active `PLAN-*-G??.md`를 `plan_cloud_G07_M.log`로 아카이브한다.
- [x] `.gitignore`의 Agent-Ops 관리 block이 `agent-task/**/*.md`와 `agent-task/**/*.log`를 unignore하고 `agent-roadmap/current.md`를 ignore하는지 확인한다.
- [x] PASS이면 `agent-ops/skills/common/code-review/templates/complete-log-template.md` 기준으로 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다.
- [x] PASS이면 active task 디렉터리 `agent-task/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/`를 `agent-task/archive/YYYY/MM/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다.
- [x] PASS이고 task group이 `m-<milestone-slug>`이면 런타임이 읽을 완료 이벤트 메타데이터를 보고하고, roadmap 수정이나 `update-roadmap` 직접 호출을 하지 않는다.
- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/m-openai-workspace-agent-execution-contract/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다.
- [ ] WARN/FAIL이고 user-review gate가 트리거되지 않았으면 다음 active `PLAN-cloud-G07.md`와 `CODE_REVIEW-cloud-G07.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로 이동한다.
## 계획 대비 변경 사항
_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._
없음. 계획한 바와 동일하게 모든 항목을 정상 구현 완료하였습니다.
## 주요 설계 결정
_구현 에이전트가 주요 설계 결정 사항을 기록한다._
- NomadCode OpenAI responses client의 `buildRequestMetadata`가 `WorkspaceMetadata` 객체 전체 대신, `Path` 필드만 flat string으로 `workspace` 키에 담도록 수정하였습니다.
- IOP Edge `responsesMetadata` 모델 구조상 flat string을 수신하도록 선언되어 있어, 객체 형태의 workspace metadata가 전달될 시 JSON 에러와 함께 400 Bad Request("invalid metadata format")로 거부됨을 확인하고, 이를 명시적으로 보장하는 테스트 케이스를 `server_test.go`에 추가하여 contract drift를 원천 방지하였습니다.
## 사용자 리뷰 요청
@ -99,37 +100,55 @@ _기본값은 `없음`이다. 구현 중 사용자 결정, 사용자 소유 외
## 검증 결과
_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._
필수 규칙:
- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다.
- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다.
- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다.
- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다.
- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다.
각 중간 검증과 최종 검증의 모든 명령어 및 결과 출력은 다음과 같습니다.
### NOMAD_METADATA-1 중간 검증
```bash
$ cd ../nomadcode/services/core && go test -count=1 ./internal/adapters/openai ./internal/authoring ./internal/scheduler
(output)
ok github.com/nomadcode/nomadcode-core/internal/adapters/openai 0.006s
ok github.com/nomadcode/nomadcode-core/internal/authoring 0.003s
ok github.com/nomadcode/nomadcode-core/internal/scheduler 2.013s
```
### NOMAD_METADATA-2 중간 검증
```bash
$ go test -count=1 ./apps/edge/internal/openai ./apps/edge/cmd/edge
(output)
ok iop/apps/edge/internal/openai 1.510s
ok iop/apps/edge/cmd/edge 0.029s
```
### 최종 검증
```bash
$ cd ../nomadcode/services/core && go test -count=1 ./...
(output)
ok github.com/nomadcode/nomadcode-core/cmd/plane-smoke 0.006s
? github.com/nomadcode/nomadcode-core/cmd/server [no test files]
ok github.com/nomadcode/nomadcode-core/internal/adapters/a2a 0.008s
ok github.com/nomadcode/nomadcode-core/internal/adapters/jira 0.007s
ok github.com/nomadcode/nomadcode-core/internal/adapters/mattermost 0.008s
ok github.com/nomadcode/nomadcode-core/internal/adapters/openai 0.006s
ok github.com/nomadcode/nomadcode-core/internal/adapters/plane 0.009s
? github.com/nomadcode/nomadcode-core/internal/agent [no test files]
ok github.com/nomadcode/nomadcode-core/internal/authoring 0.009s
ok github.com/nomadcode/nomadcode-core/internal/config 0.003s
? github.com/nomadcode/nomadcode-core/internal/db [no test files]
ok github.com/nomadcode/nomadcode-core/internal/http 0.007s
? github.com/nomadcode/nomadcode-core/internal/model [no test files]
ok github.com/nomadcode/nomadcode-core/internal/notification 0.004s
ok github.com/nomadcode/nomadcode-core/internal/projectsync 0.003s
ok github.com/nomadcode/nomadcode-core/internal/protosocket 0.011s
ok github.com/nomadcode/nomadcode-core/internal/roadmapsync 0.004s
ok github.com/nomadcode/nomadcode-core/internal/scheduler 2.010s
ok github.com/nomadcode/nomadcode-core/internal/storage 0.006s
ok github.com/nomadcode/nomadcode-core/internal/workflow 0.004s
ok github.com/nomadcode/nomadcode-core/internal/workitem 0.004s
ok github.com/nomadcode/nomadcode-core/internal/workitempipeline 0.004s
$ go test -count=1 ./apps/edge/internal/openai ./apps/edge/cmd/edge
(output)
ok iop/apps/edge/internal/openai 1.509s
ok iop/apps/edge/cmd/edge 0.032s
$ git diff --check
(output)
(성공 - 아무 출력 없음)
```
---
@ -152,3 +171,27 @@ $ git diff --check
| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Implementing agent must not modify. |
| 검증 결과 | Implementing agent | Fill command output only; command changes require `계획 대비 변경 사항`. |
| 코드리뷰 결과 | Review agent appends | Not included in stub. |
## 코드리뷰 결과
- 종합 판정: PASS
- 차원별 평가:
- correctness: Pass
- completeness: Pass
- test coverage: Pass
- API contract: Pass
- code quality: Pass
- plan deviation: Pass
- verification trust: Pass
- 발견된 문제: 없음
- 다음 단계: PASS이므로 active plan/review를 log로 아카이브하고 `complete.log`를 작성한 뒤 task directory를 `agent-task/archive/2026/06/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/`로 이동한다. `m-openai-workspace-agent-execution-contract` completion metadata를 보고하며 roadmap 수정은 수행하지 않는다.
### 리뷰 검증 메모
- NomadCode producer는 `WorkspaceMetadata.Path`만 `metadata.workspace` flat string으로 직렬화하고 `task_id`/`source` flat metadata를 보존한다.
- IOP consumer는 `responsesMetadata.Workspace string` decode 경계에서 object workspace shape를 `invalid metadata format` 400으로 거부한다.
- 재실행 검증:
- `cd ../nomadcode/services/core && go test -count=1 ./internal/adapters/openai ./internal/authoring ./internal/scheduler` PASS
- `cd ../nomadcode/services/core && go test -count=1 ./...` PASS
- `go test -count=1 ./apps/edge/internal/openai ./apps/edge/cmd/edge` PASS
- `git diff --check` PASS

View file

@ -0,0 +1,43 @@
# Complete - m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape
## 완료 일시
2026-06-13
## 요약
NomadCode metadata workspace wire shape를 flat string contract로 정렬하고 IOP consumer rejection fixture를 보강한 작업을 1회 리뷰에서 PASS로 종료했다.
## 루프 이력
| Plan | Review | Verdict | 메모 |
|------|--------|---------|------|
| `plan_cloud_G07_0.log` | `code_review_cloud_G07_0.log` | PASS | NomadCode producer와 IOP consumer fixture가 계획 범위와 OpenAI-compatible metadata contract에 부합함을 확인했다. |
## 구현/정리 내용
- NomadCode OpenAI Responses client가 `WorkspaceMetadata.Path`를 `metadata.workspace` flat string으로 보내도록 정렬했다.
- NomadCode OpenAI client test와 model comment를 flat workspace wire contract에 맞췄다.
- IOP Edge OpenAI-compatible tests에 object workspace metadata rejection coverage를 추가했다.
## 최종 검증
- `cd ../nomadcode/services/core && go test -count=1 ./internal/adapters/openai ./internal/authoring ./internal/scheduler` - PASS; targeted producer/authoring/scheduler tests passed.
- `cd ../nomadcode/services/core && go test -count=1 ./...` - PASS; all NomadCode core packages passed.
- `go test -count=1 ./apps/edge/internal/openai ./apps/edge/cmd/edge` - PASS; IOP OpenAI-compatible and edge command tests passed.
- `git diff --check` - PASS; no whitespace errors.
## Roadmap Completion
- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/openai-workspace-agent-execution-contract.md`
- Completed task ids:
- `nomad-metadata-shape`: PASS; evidence=`agent-task/archive/2026/06/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/plan_cloud_G07_0.log`, `agent-task/archive/2026/06/m-openai-workspace-agent-execution-contract/07_nomad_metadata_shape/code_review_cloud_G07_0.log`; verification=`cd ../nomadcode/services/core && go test -count=1 ./...`, `go test -count=1 ./apps/edge/internal/openai ./apps/edge/cmd/edge`, `git diff --check`
- Not completed task ids: 없음
## 잔여 Nit
- 없음
## 후속 작업
- 없음

View file

@ -634,6 +634,28 @@ func TestChatCompletionsRejectsCLIMetadata(t *testing.T) {
}
}
func TestChatCompletionsRejectsObjectWorkspaceMetadata(t *testing.T) {
srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil)
req := httptest.NewRequest(http.MethodPost, "/v1/chat/completions", strings.NewReader(`{
"model":"m",
"messages":[{"role":"user","content":"hi"}],
"metadata":{
"workspace": {
"path": "/home/user/workspace",
"source_branch": "develop"
}
}
}`))
w := httptest.NewRecorder()
srv.routes().ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("got %d want 400, body=%s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "invalid metadata format") {
t.Fatalf("expected 'invalid metadata format' error, got %s", w.Body.String())
}
}
func TestResponsesAcceptsFlatNomadCodeMetadataAliases(t *testing.T) {
fake := &fakeRunService{events: make(chan *iop.RunEvent, 2)}
fake.events <- &iop.RunEvent{Type: "delta", Delta: "ok"}
@ -752,6 +774,28 @@ func TestResponsesRejectsCLIMetadata(t *testing.T) {
}
}
func TestResponsesRejectsObjectWorkspaceMetadata(t *testing.T) {
srv := NewServer(config.EdgeOpenAIConf{Adapter: "ollama", Target: "llama"}, &fakeRunService{}, nil)
req := httptest.NewRequest(http.MethodPost, "/v1/responses", strings.NewReader(`{
"model":"m",
"input":"hi",
"metadata":{
"workspace": {
"path": "/home/user/workspace",
"source_branch": "develop"
}
}
}`))
w := httptest.NewRecorder()
srv.routes().ServeHTTP(w, req)
if w.Code != http.StatusBadRequest {
t.Fatalf("got %d want 400, body=%s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "invalid metadata format") {
t.Fatalf("expected 'invalid metadata format' error, got %s", w.Body.String())
}
}
func TestResponsesReturnsNomadCodeCompatibleShape(t *testing.T) {
fake := &fakeRunService{events: make(chan *iop.RunEvent, 3)}
fake.events <- &iop.RunEvent{Type: "delta", Delta: "answer"}