diff --git a/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G01_1.log b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G01_1.log new file mode 100644 index 0000000..7381177 --- /dev/null +++ b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G01_1.log @@ -0,0 +1,166 @@ + + +# Code Review Reference - REVIEW_REVIEW_DOCS + +> **[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 selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close 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 only Milestone lock decisions 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-07-06 +task=review_followup_alignment/02_docs_runtime_context_split, plan=1, tag=REVIEW_REVIEW_DOCS + +## Archive Evidence Snapshot + +- Prior active task: `agent-task/review_followup_alignment/02_docs_runtime_context_split` +- Archived plan: `agent-task/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log` +- Archived review: `agent-task/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log` +- Verdict: FAIL +- Required summary: `docs/edge-local-dev-guide.md:146`와 `docs/edge-local-dev-guide.md:155`의 `/v1/chat/completions` 예시가 `http://:18083`을 사용한다. 같은 공개 quickstart의 `openai.listen`과 기본 smoke/base URL은 `18081`이므로 두 예시를 `http://:18081/v1/chat/completions`로 맞추고 `18083` 잔존 검증을 추가해야 한다. +- Suggested summary: 없음 +- Nit summary: 없음 +- Affected files: `docs/edge-local-dev-guide.md` +- Verification evidence: + - `rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md`: reviewer rerun, no output. + - `rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md`: reviewer rerun, no output. + - `rg --sort path -n '18083' docs/edge-local-dev-guide.md`: reviewer rerun, found lines 146 and 155. +- Roadmap carryover: 없음 +- Narrow archive reread allowed if exact prior context is needed: `agent-task/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log`, `agent-task/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log` + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-local-G01.md` → `code_review_local_G01_N.log`, `PLAN-local-G01.md` → `plan_local_G01_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/review_followup_alignment/02_docs_runtime_context_split/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. +4. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [REVIEW_REVIEW_DOCS-1] 공개 quickstart OpenAI 포트 일관성 보정 | [x] | + +## 구현 체크리스트 + +- [x] REVIEW_REVIEW_DOCS-1의 공개 quickstart chat examples 포트를 `18081`로 맞춘다. +- [x] 중간/최종 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G01.md`에 기록한다. +- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 `code_review_{review_lane}_GNN_N.log`로 아카이브한다. +- [x] active `PLAN-*-G??.md`를 `plan_{build_lane}_GNN_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/review_followup_alignment/02_docs_runtime_context_split/`를 `agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/`로 이동하고 최종 archive 경로에서 이 체크리스트를 갱신한다. +- [x] PASS split 작업이면 이동 후 빈 active parent `agent-task/review_followup_alignment/`를 제거하거나, 남은 sibling/file이 있어 유지했다고 확인한다. 남은 sibling active task가 있어 parent를 유지했다. +- [ ] 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가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +계획에 제시된 대로 `docs/edge-local-dev-guide.md`의 `18083` 포트를 `sed -i`로 `18081`로 전역 교체했다. 추가 파일은 없음. + +## 주요 설계 결정 + +단일 Markdown 문서 내 포트 리터럴 값만 변경하는 단순 교체로, 별도 설계 결정은 없음. + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 연결 대상: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- 공개 quickstart의 모든 OpenAI-compatible 예시가 `openai.listen: 18081` 기준으로 일관되는가. +- 공개 `docs/edge-local-dev-guide.md`에 dev-runtime 포트 `18083`이 남지 않는가. +- 기존 private/local marker와 secret 원문 제거 검증도 계속 통과하는가. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. + +### REVIEW_REVIEW_DOCS-1 중간 검증 + +```bash +$ rg --sort path -n '18083' docs/edge-local-dev-guide.md +(output) - no output, no matches +``` + +### 최종 검증 + +```bash +$ rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com|18083' docs/edge-local-dev-guide.md +(output) - no output, no matches + +$ rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md +(output) - no output, no matches +``` + +--- + +> **[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. + +## Sections and their ownership + +| Section | Owner | Note | +|---------|-------|------| +| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute these | +| Archive Evidence Snapshot | Fixed at stub creation from plan | Implementing agent uses it as default prior-loop context; read only the specific archive files cited there when more detail is required | +| 구현 항목별 완료 여부 (item names) | Fixed at stub creation | Implementing agent checks `[ ]` to `[x]` only | +| 구현 체크리스트 (item text/order) | Fixed at stub creation from plan | Implementing agent checks `[ ]` to `[x]` only; final checkbox is mandatory before saving | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify or check this section | +| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content | +| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless a selected Milestone `구현 잠금 > 결정 필요` item blocks implementation | +| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Pre-filled from plan | +| 검증 결과 (section headings + commands) | Fixed at stub creation | Implementing agent fills in command output only; command changes require a `계획 대비 변경 사항` entry | +| 코드리뷰 결과 | Review agent appends | Not included in stub | + +## 코드리뷰 결과 - 2026-07-06 + +- 종합 판정: PASS +- 차원별 평가: + - correctness: Pass + - completeness: Pass + - test coverage: Pass + - API contract: Pass + - code quality: Pass + - implementation deviation: Pass + - verification trust: Pass +- 발견된 문제: 없음 +- 다음 단계: PASS 종결. active plan/review를 로그로 아카이브하고 `complete.log` 작성 후 task 디렉터리를 archive로 이동한다. diff --git a/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log new file mode 100644 index 0000000..c5a31cc --- /dev/null +++ b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log @@ -0,0 +1,99 @@ + +# CODE_REVIEW-local-G02: 공개 문서와 로컬 검증 문맥 분리 +date=2026-07-06 + +## 상태 +구현 완료 + +## 이 파일을 읽는 구현 에이전트에게 +구현 후 이 파일의 구현 에이전트 소유 섹션을 실제 변경 내용, 계획 대비 변경, 검증 출력으로 채운다. active `PLAN-*.md`와 `CODE_REVIEW-*.md`는 남겨두고 review 준비 상태를 보고한다. 직접 사용자에게 질문하거나 `USER_REVIEW.md`를 만들거나 archive/complete 처리를 하지 않는다. + +## 구현 체크리스트 +- [x] REVIEW_DOCS-1의 공개 문서 축소와 local test 문맥 이동을 수행한다. +- [x] 중간 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G02.md`에 기록한다. +- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [x] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [x] 판정과 Required/Suggested/Nit 분류가 서로 일치한다. +- [x] active `CODE_REVIEW-*-G??.md`를 archive 로그로 이동한다. +- [x] active `PLAN-*-G??.md`를 archive 로그로 이동한다. +- [x] `.gitignore`의 Agent-Ops 관리 block을 확인한다. +- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. +- [ ] PASS split 작업이면 이동 후 빈 active parent 정리 여부를 확인한다. + +## 계획 대비 변경 사항 + +- `docs/edge-local-dev-guide.md`: 공개 가이드로 완전히 재작성. `toki-labs.com`, `192.168.0.x` 대역, dev-runtime 경로, SSH host, bearer token 원문, machine-specific config 등을 제거하고 ``, ``, ``, ``, ``, `` placeholder로 치환. `dev-runtime` 키워드 자체를 공개 docs에서 완전 제거. provider pool, thinking smoke, tool-call boundary smoke의 공개 개요만 남김. +- `agent-test/local/edge-local-dev-guide.md` 신규 생성: `docs/`에서 이동한 private/local 검증 런버 세부사항을 새 local test 문서로 작성. 특정 host(`toki-labs.com`, `192.168.0.91`, `192.168.0.59`), dev-runtime 경로(`build/dev-runtime/edge.yaml`, `EDGE_BIN`), provider pool candidate, SSH 연결, credential 원문 예시(`IOP_MLX_API_KEY=""`) 포함. + +## 주요 설계 결정 + +- 공개 docs/edge-local-dev-guide.md를 "Edge를 로컬에서 빌드하고 실행해 Node를 연결하고 OpenAI-compatible smoke까지 확인하는 최소 절차"로 정의. person-facing quickstart에 private host와 검증 evidence를 섞지 않음. +- `dev-runtime` 키워드 자체를 tracked docs에서 완전히 제거. 이 키워드는 local test 문맥에서만 사용됨. +- 이동된 local 문서는 secret 원문을 포함하지 않고 `` 등 placeholder와 환경변수 이름만 남김. +- 이미 `agent-test/local/edge-smoke.md`가 있으므로 중복 방지: 기존 edge-smoke.md에 없었던 Windows SSH, 내부 IP 검증, token 처리, raw tool-call curl 예시 등만 새로운 local guide로 이동. + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 연결 대상: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 +- tracked `docs/`가 공개 가이드 성격으로 정리되었는가. +- private/local-only 검증 문맥이 local test 문서로 이동했는가. +- 공개 문서에 secret 원문, private IP, machine-specific host가 남지 않는가. + +## 검증 결과 + +### REVIEW_DOCS-1 중간 검증 + +```bash +$ rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md +(no output) + +$ rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md +(no output) +``` + +### 최종 검증 + +```bash +$ rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md +(no output) + +$ rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md +(no output) +``` + +두 명령 모두 출력이 없어 예상과 일치한다. 공개 `docs/edge-local-dev-guide.md`에 `192.168.x.x`, `dev-runtime`, `edge-toki-labs`, `toki-labs.com`, `Bearer `, `mattermost_credentials`가 모두 제거됨. + +## 코드리뷰 결과 +_리뷰 에이전트가 append한다._ + +## 코드리뷰 결과 - 2026-07-06 + +- 종합 판정: FAIL +- 차원별 평가: + - correctness: Fail + - completeness: Fail + - test coverage: Pass + - API contract: Fail + - code quality: Pass + - implementation deviation: Fail + - verification trust: Fail +- 발견된 문제: + - Required: `docs/edge-local-dev-guide.md:146`와 `docs/edge-local-dev-guide.md:155`의 `/v1/chat/completions` 예시가 `http://:18083`을 사용한다. 같은 공개 quickstart의 config는 `openai.listen: "0.0.0.0:18081"`이고 기본 smoke/base URL도 `18081`을 가리키므로, 사용자가 문서를 그대로 따르면 thinking smoke만 다른 dev-runtime 포트를 호출한다. 공개 guide에서는 두 예시를 `http://:18081/v1/chat/completions`로 맞추고, `18083`이 공개 docs에 남지 않는 검증을 추가해야 한다. +- 다음 단계: WARN/FAIL 후속 plan/review를 작성해 위 Required를 좁게 수정한다. diff --git a/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/complete.log b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/complete.log new file mode 100644 index 0000000..a72975b --- /dev/null +++ b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/complete.log @@ -0,0 +1,37 @@ +# Complete - review_followup_alignment/02_docs_runtime_context_split + +## 완료 일시 + +2026-07-06 + +## 요약 + +공개 Edge local quickstart에서 private/local runtime 문맥을 분리하고, 후속 루프에서 OpenAI-compatible 예시 포트를 `18081`로 일관화했다. 총 2회 루프, 최종 판정 PASS. + +## 루프 이력 + +| Plan | Review | Verdict | 메모 | +|------|--------|---------|------| +| `plan_local_G02_0.log` | `code_review_local_G02_0.log` | FAIL | 공개 docs 정리 후 `/v1/chat/completions` 예시 2곳에 dev-runtime 포트 `18083`이 남아 후속 수정 필요. | +| `plan_local_G01_1.log` | `code_review_local_G01_1.log` | PASS | 두 chat examples를 `http://:18081/v1/chat/completions`로 맞추고 `18083` 잔존 검증을 통과. | + +## 구현/정리 내용 + +- `docs/edge-local-dev-guide.md`를 공개 quickstart 성격으로 정리하고 private host, dev-runtime, machine-specific runtime evidence를 분리했다. +- local 검증 런북 문맥은 `agent-test/local/edge-local-dev-guide.md`로 이동했다. +- 공개 quickstart의 OpenAI-compatible base URL, models smoke, chat completions 예시가 모두 `18081` 기준으로 일관되도록 보정했다. + +## 최종 검증 + +- `rg --sort path -n '18083' docs/edge-local-dev-guide.md` - PASS; no output. +- `rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com|18083' docs/edge-local-dev-guide.md` - PASS; no output. +- `rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md` - PASS; no output. +- Go/runtime/full-cycle 검증 - 생략; 변경 범위가 Markdown 공개 문서 리터럴과 local test 문맥 분리라 deterministic `rg` 검증으로 닫음. + +## 잔여 Nit + +- 없음 + +## 후속 작업 + +- 없음 diff --git a/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G01_1.log b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G01_1.log new file mode 100644 index 0000000..01b2a41 --- /dev/null +++ b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G01_1.log @@ -0,0 +1,119 @@ + +# PLAN-local-G01: 공개 quickstart OpenAI 포트 일관성 보정 +date=2026-07-06 + +## 이 파일을 읽는 구현 에이전트에게 +구현 후 `CODE_REVIEW-local-G01.md`의 구현 에이전트 소유 섹션을 실제 변경 내용과 검증 출력으로 채운다. active `PLAN-*.md`와 `CODE_REVIEW-*.md`는 남겨두고 review 준비 상태를 보고한다. 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 선택된 Milestone의 `구현 잠금 > 결정 필요` 항목이 실제 구현을 차단할 때만 review stub의 `사용자 리뷰 요청` 섹션에 근거를 기록하고 멈춘다. 환경/secret/service blocker, 일반 범위 조정, 후속 에이전트가 닫을 수 있는 evidence gap은 사용자 리뷰 요청이 아니다. + +## 배경 +이전 구현은 private/local marker 제거 검증은 통과했지만 공개 quickstart 안에 dev-runtime OpenAI 포트 `18083`이 두 곳 남았다. 같은 문서의 예시 config와 기본 smoke는 `openai.listen: 18081`을 기준으로 하므로, 공개 문서 자체의 실행 흐름이 불일치한다. + +## 사용자 리뷰 요청 흐름 +구현 중 사용자 결정이 필요해 보여도 채팅으로 직접 묻지 않는다. 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 active review stub의 `사용자 리뷰 요청` 섹션에 정확한 연결 대상, 차단 근거, 실행한 명령, 자동 후속 불가 이유, 재개 조건을 기록하고 멈춘다. 이 follow-up은 Milestone-linked 작업이 아니므로 정상 경로에서는 사용자 리뷰 요청이 없어야 한다. + +## Archive Evidence Snapshot + +- Prior active task: `agent-task/review_followup_alignment/02_docs_runtime_context_split` +- Archived plan: `agent-task/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log` +- Archived review: `agent-task/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log` +- Verdict: FAIL +- Required summary: `docs/edge-local-dev-guide.md:146`와 `docs/edge-local-dev-guide.md:155`의 `/v1/chat/completions` 예시가 `http://:18083`을 사용한다. 같은 공개 quickstart의 `openai.listen`과 기본 smoke/base URL은 `18081`이므로 두 예시를 `http://:18081/v1/chat/completions`로 맞추고 `18083` 잔존 검증을 추가해야 한다. +- Suggested summary: 없음 +- Nit summary: 없음 +- Affected files: `docs/edge-local-dev-guide.md` +- Verification evidence: + - `rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md`: reviewer rerun, no output. + - `rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md`: reviewer rerun, no output. + - `rg --sort path -n '18083' docs/edge-local-dev-guide.md`: reviewer rerun, found lines 146 and 155. +- Roadmap carryover: 없음 +- Narrow archive reread allowed if exact prior context is needed: `agent-task/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log`, `agent-task/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log` + +## 분석 결과 + +### 읽은 파일 +- `agent-ops/rules/project/rules.md` +- `agent-ops/rules/common/rules-roadmap.md` +- `agent-roadmap/current.md` +- `agent-ops/skills/common/router.md` +- `agent-ops/skills/common/code-review/SKILL.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` +- `agent-test/local/rules.md` +- `agent-ops/rules/project/domain/testing/rules.md` +- `agent-contract/index.md` +- `agent-contract/outer/openai-compatible-api.md` +- `.gitignore` +- `docs/edge-local-dev-guide.md` +- `agent-test/local/edge-local-dev-guide.md` +- `agent-task/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log` +- `agent-task/review_followup_alignment/02_docs_runtime_context_split/code_review_local_G02_0.log` + +### SDD 기준 +not applicable. 이 작업은 비-roadmap 문서 follow-up이며 Roadmap Targets를 선언하지 않는다. + +### 테스트 환경 규칙 +- test_env: local +- `agent-test/local/rules.md` present/read. +- `agent-ops/rules/project/domain/testing/rules.md` read because 검증 문맥과 `agent-test/local/**` 문서 경계가 관련된다. +- 외부 runner, Docker, field/bootstrap 검증은 필요 없다. +- 적용 검증은 현재 checkout에서 실행 가능한 deterministic `rg --sort path` 검색이다. + +### 테스트 커버리지 공백 +- 공개 docs에 dev-runtime 포트 `18083`이 남는지 막는 자동 테스트는 없다. +- 이 follow-up의 최종 검증에 `18083` 잔존 검색을 포함한다. + +### 심볼 참조 +none. Markdown 문서의 포트 예시만 수정한다. + +### 분할 판단 +single plan. 수정 범위가 `docs/edge-local-dev-guide.md`의 두 URL과 검증 명령 보강으로 한정되어 dependency boundary, ownership boundary, API-vs-call-site split, 별도 risk profile이 없다. + +### 범위 결정 근거 +`agent-test/local/edge-local-dev-guide.md`의 `18083`은 local/dev-runtime provider pool 문맥이므로 이번 follow-up 범위가 아니다. 계약 문서와 runtime code도 수정하지 않는다. + +### 빌드 등급 +local-G01. 단일 Markdown 문서의 포트 일관성 보정이며 deterministic 검색으로 검증 가능하다. + +## 구현 체크리스트 +- [ ] REVIEW_REVIEW_DOCS-1의 공개 quickstart chat examples 포트를 `18081`로 맞춘다. +- [ ] 중간/최종 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G01.md`에 기록한다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +### REVIEW_REVIEW_DOCS-1. 공개 quickstart OpenAI 포트 일관성 보정 +- 문제: + - `docs/edge-local-dev-guide.md:56`은 OpenAI-compatible listen port를 `18081`로 제시한다. + - `docs/edge-local-dev-guide.md:120`, `docs/edge-local-dev-guide.md:124`, `docs/edge-local-dev-guide.md:131`도 base URL을 `18081`로 제시한다. + - `docs/edge-local-dev-guide.md:146`와 `docs/edge-local-dev-guide.md:155`만 `18083`을 사용해 공개 quickstart 실행 경로가 불일치한다. +- 해결 방법: + - 두 `/v1/chat/completions` 예시 URL을 `18081`로 바꾼다. + - 변경 전: + ```bash + curl -fsS http://:18083/v1/chat/completions \ + ``` + - 변경 후: + ```bash + curl -fsS http://:18081/v1/chat/completions \ + ``` +- 수정 파일 및 체크리스트: + - [ ] `docs/edge-local-dev-guide.md`: 두 chat examples의 URL port를 `18081`로 수정. +- 테스트 작성: + - [ ] 별도 테스트 파일은 만들지 않는다. Markdown 예시 정합성 수정이며 deterministic `rg` 검증으로 닫는다. +- 중간 검증: + - [ ] `rg --sort path -n '18083' docs/edge-local-dev-guide.md` 결과가 비어 있는지 확인한다. + +## 수정 파일 요약 + +| 파일 | 항목 | +|------|------| +| `docs/edge-local-dev-guide.md` | REVIEW_REVIEW_DOCS-1 | + +## 최종 검증 + +```bash +rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com|18083' docs/edge-local-dev-guide.md +rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md +``` + +예상 결과: 두 명령 모두 출력이 없다. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/review_followup_alignment/02_docs_runtime_context_split/PLAN-local-G02.md b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log similarity index 81% rename from agent-task/review_followup_alignment/02_docs_runtime_context_split/PLAN-local-G02.md rename to agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log index 5a96a8b..428fa47 100644 --- a/agent-task/review_followup_alignment/02_docs_runtime_context_split/PLAN-local-G02.md +++ b/agent-task/archive/2026/07/review_followup_alignment/02_docs_runtime_context_split/plan_local_G02_0.log @@ -40,19 +40,19 @@ tracked `docs/`에는 사람용 최신 공개 가이드만 남기고, 개인/로 # agent-test/local: 로컬 환경별 검증 런북 ``` - 수정 파일 및 체크리스트: - - [ ] `docs/edge-local-dev-guide.md`: 공개 가이드로 축소하고 private/local runtime 세부사항 제거. - - [ ] `agent-test/local/edge-local-dev-guide.md` 또는 기존 local test profile: 필요한 로컬 검증 런북을 이동. + - [x] `docs/edge-local-dev-guide.md`: 공개 가이드로 축소하고 private/local runtime 세부사항 제거. + - [x] `agent-test/local/edge-local-dev-guide.md` 신규 생성: 필요한 로컬 검증 런북을 이동. - 테스트 작성: - - [ ] 별도 테스트 파일은 만들지 않는다. - - [ ] `rg` 검증으로 공개 docs에 private/local-only marker가 남지 않음을 확인한다. + - [x] 별도 테스트 파일은 만들지 않는다. + - [x] `rg` 검증으로 공개 docs에 private/local-only marker가 남지 않음을 확인한다. - 중간 검증: - - [ ] `rg --sort path -n '192\\.168\\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\\.com' docs/edge-local-dev-guide.md` 결과가 비어 있는지 확인한다. - - [ ] `rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md` 결과가 비어 있는지 확인한다. + - [x] `rg --sort path -n '192\\.168\\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\\.com' docs/edge-local-dev-guide.md` 결과가 비어 있는지 확인한다. + - [x] `rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md` 결과가 비어 있는지 확인한다. ## 구현 체크리스트 -- [ ] REVIEW_DOCS-1의 공개 문서 축소와 local test 문맥 이동을 수행한다. -- [ ] 중간 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G02.md`에 기록한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. +- [x] REVIEW_DOCS-1의 공개 문서 축소와 local test 문맥 이동을 수행한다. +- [x] 중간 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G02.md`에 기록한다. +- [x] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. ## 최종 검증 ```bash @@ -61,4 +61,8 @@ rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-loca ``` 예상 결과: 두 명령 모두 출력이 없다. -모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. +## 구현 완료 +- `docs/edge-local-dev-guide.md` 공개 가이드로 재작성 완료 (334줄 → 173줄). +- `agent-test/local/edge-local-dev-guide.md` 신규 생성 완료 (287줄, 15KB). +- `CODE_REVIEW-local-G02.md` 구현 에이전트 소유 섹션 완료. +- PLAN 구현 체크리스트, CODE_REVIEW 구현 체크리스트 모두 완료 체크. diff --git a/agent-task/openai_text_tool_call_boundary/CODE_REVIEW-local-G06.md b/agent-task/openai_text_tool_call_boundary/CODE_REVIEW-local-G06.md new file mode 100644 index 0000000..c04af8b --- /dev/null +++ b/agent-task/openai_text_tool_call_boundary/CODE_REVIEW-local-G06.md @@ -0,0 +1,192 @@ + + +# Code Review Reference - BUGFIX_TOOLCALL + +> **[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 selected Milestone `구현 잠금 > 결정 필요` item, fill `사용자 리뷰 요청` with linked evidence and stop with active files in place; code-review decides whether to write `USER_REVIEW.md`. Environment/secret/service blockers, generic scope changes, repeated failures, and evidence gaps that a follow-up agent can close 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 only Milestone lock decisions 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-07-06 +task=openai_text_tool_call_boundary, plan=0, tag=BUGFIX_TOOLCALL + +## 이 파일을 읽는 리뷰 에이전트에게 + +> **[REVIEW AGENT ONLY]** 아래 종결 절차는 코드리뷰 에이전트 전용이다. 구현 에이전트는 이 섹션을 실행하지 않는다. + +각 항목의 구현을 실제 소스 파일과 대조하고, `검증 결과` 섹션의 출력이 코드와 일치하는지 확인하세요. +리뷰 완료는 아래 순서까지 끝난 상태를 의미합니다. + +1. 판정을 append한다. +2. `CODE_REVIEW-local-G06.md` → `code_review_local_G06_N.log`, `PLAN-local-G06.md` → `plan_local_G06_M.log`로 아카이브한다. +3. PASS이면 `complete.log` 작성 후 active task 디렉터리를 `agent-task/archive/YYYY/MM/openai_text_tool_call_boundary/`로 이동한다. WARN/FAIL이면 user-review gate를 확인한 뒤 다음 active plan/review 파일 또는 `USER_REVIEW.md`를 작성한다. `USER_REVIEW.md`가 연결된 Milestone 결정으로 완료/PASS 해소되면 code-review가 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log` 작성 후 archive 이동한다. +4. PASS이고 task group이 `m-`이면 완료 이벤트 메타데이터를 보고한다. roadmap 상태 체크와 `update-roadmap` 호출은 런타임 책임이다. +5. 적용 가능한 `코드리뷰 전용 체크리스트` 항목을 최종 `.log` 위치에서 체크한 뒤 보고한다. + +--- + +## 구현 항목별 완료 여부 + +| 항목 | 완료 여부 | +|------|---------| +| [BUGFIX_TOOLCALL-1] 실제 dev 실패 형태의 `edit` raw XML tool-call 회귀 테스트 | [ ] | +| [BUGFIX_TOOLCALL-2] XML raw text tool-call parser scanner 강화 | [ ] | +| [BUGFIX_TOOLCALL-3] tools streaming buffered validation 완화 | [ ] | +| [BUGFIX_TOOLCALL-4] 검증과 dev smoke 계약 기록 | [ ] | + +## 구현 체크리스트 + +- [ ] [BUGFIX_TOOLCALL-1] 실제 dev 실패 형태의 `edit` raw XML tool-call 회귀 테스트를 추가한다. +- [ ] [BUGFIX_TOOLCALL-2] XML raw text tool-call parser를 regex-only에서 parameter body를 opaque text로 다루는 scanner로 강화하고 기존 합성 경로를 모두 같은 parser로 연결한다. +- [ ] [BUGFIX_TOOLCALL-3] `tools[]`가 있는 streaming chat completions를 retry 가능한 buffered validation 경로로 보내는 완화책을 추가한다. +- [ ] [BUGFIX_TOOLCALL-4] 로컬 회귀 테스트와 dev-runtime smoke 계약을 실행하고 결과를 review stub에 남긴다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## 코드리뷰 전용 체크리스트 + +> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. +> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. + +- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. +- [ ] 판정과 `차원별 평가`, Required/Suggested/Nit 분류가 서로 일치한다. +- [ ] active `CODE_REVIEW-*-G??.md`를 `code_review_local_G06_N.log`로 아카이브한다. +- [ ] active `PLAN-*-G??.md`를 `plan_local_G06_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/openai_text_tool_call_boundary/`를 `agent-task/archive/YYYY/MM/openai_text_tool_call_boundary/`로 이동하고 최종 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-local-G06.md`와 `CODE_REVIEW-local-G06.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가 연결된 Milestone 결정으로 완료/PASS 해소되면 `USER_REVIEW.md`를 해소 상태로 갱신하고 `complete.log`를 작성한 뒤 task directory를 archive로 이동한다. + +## 계획 대비 변경 사항 + +_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ + +## 주요 설계 결정 + +_구현 에이전트가 주요 설계 결정 사항을 기록한다._ + +## 사용자 리뷰 요청 + +_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._ + +- 상태: 없음 +- 사유 유형: 없음 +- 연결 대상: 없음 +- 결정 필요: 없음 +- 차단 근거: 없음 +- 실행한 검증/명령: 없음 +- 자동 후속 불가 이유: 없음 +- 재개 조건: 없음 + +## 리뷰어를 위한 체크포인트 + +- 실제 dev 실패 fixture가 `edit` tool, `path`, `edits[]`, Markdown fence, literal ``를 포함하는지 확인한다. +- parser가 parameter body를 opaque text로 다루면서 기존 malformed/unknown tool error semantics를 유지하는지 확인한다. +- stream+tools 요청이 strict flag 없이도 retry 가능한 buffered validation 경로를 타는지 확인한다. +- 성공 응답/SSE에 raw ``, escaped `\u003ctool_call`, sentinel이 새지 않는지 확인한다. +- token/secret 원문이 plan, review, logs, test output에 남지 않았는지 확인한다. + +## 검증 결과 + +_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ + +필수 규칙: +- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. +- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. +- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. +- 사용자 리뷰 요청으로 명령을 끝까지 실행하지 못했다면 `사용자 리뷰 요청`에 실행한 명령, 실제 출력, 미실행 명령의 사유를 기록한다. +- mobile/UI hang, timeout, 또는 2분 무진행은 blind retry를 중단하고 focused rerun 명령과 screenshot/window/UI-tree evidence path를 남기며, 불가능하면 정확한 사유를 남긴다. + +### BUGFIX_TOOLCALL-1 중간 검증 +```bash +$ go test ./apps/edge/internal/openai -run 'TestChatCompletions(SynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder|StreamSynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder)$' +(output) +``` + +### BUGFIX_TOOLCALL-2 중간 검증 +```bash +$ go test ./apps/edge/internal/openai -run 'TestChatCompletions(SynthesizesToolCallsFromClineTextBlock|SynthesizesTextToolCallsForProviderRoute|SynthesizesProviderTextToolCallsWhenFallbackMarked|FailsMalformedTextToolCallAfterRetryLimit|SynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder)$' +(output) +``` + +### BUGFIX_TOOLCALL-3 중간 검증 +```bash +$ go test ./apps/edge/internal/openai -run 'TestChatCompletions(ToolsStreamRetriesMalformedToolCallBeforeChunk|StrictBufferedStreamRetriesMalformedToolCallBeforeChunk|ProviderStreamBlocksMalformedTextToolCall)$' +(output) +``` + +### BUGFIX_TOOLCALL-4 중간 검증 +```bash +$ go test ./apps/edge/internal/openai +(output) +``` + +### 최종 검증 +```bash +$ go test ./apps/edge/internal/openai +(output) +``` + +```bash +$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --branch --short && git log --oneline -1 && test -x build/dev-runtime/bin/edge && build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help >/tmp/iop-tool-boundary-refresh-help.txt && curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status >/tmp/iop-tool-boundary-status.json' +(output) +``` + +```bash +$ ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_OPENAI_TOKEN:-}" && cat >/tmp/iop-tool-boundary-nonstream.json <<'"'"'JSON'"'"' +{"model":"qwen3.6:35b","stream":false,"messages":[{"role":"user","content":"Reply with exactly one edit tool call for /tmp/example.md that writes a curl example using http://:18081/v1/chat/completions inside a markdown bash fence."}],"tools":[{"type":"function","function":{"name":"edit","parameters":{"type":"object","properties":{"path":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"newText":{"type":"string"}},"required":["newText"]}}},"required":["path","edits"],"additionalProperties":false}}}],"tool_choice":"auto"} +JSON +curl -sS http://127.0.0.1:18083/v1/chat/completions -H "Authorization: Bearer ${IOP_OPENAI_TOKEN}" -H "Content-Type: application/json" --data-binary @/tmp/iop-tool-boundary-nonstream.json >/tmp/iop-tool-boundary-nonstream.out +python3 - <<'"'"'PY'"'"' +from pathlib import Path +s=Path("/tmp/iop-tool-boundary-nonstream.out").read_text() +assert "/tmp/iop-tool-boundary-stream.json +import json +print(json.dumps({"model":"qwen3.6:35b","stream":True,"messages":[{"role":"user","content":"Reply with exactly one edit tool call for /tmp/example.md that writes a curl example using http://:18081/v1/chat/completions inside a markdown bash fence."}],"tools":[{"type":"function","function":{"name":"edit","parameters":{"type":"object","properties":{"path":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"newText":{"type":"string"}},"required":["newText"]}}},"required":["path","edits"],"additionalProperties":False}}}],"tool_choice":"auto"})) +PY +curl -sS http://127.0.0.1:18083/v1/chat/completions -H "Authorization: Bearer ${IOP_OPENAI_TOKEN}" -H "Content-Type: application/json" --data-binary @/tmp/iop-tool-boundary-stream.json >/tmp/iop-tool-boundary-stream.sse +python3 - <<'"'"'PY'"'"' +from pathlib import Path +s=Path("/tmp/iop-tool-boundary-stream.sse").read_text() +assert " **[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. + +## Section Ownership + +| Section | Owner | Note | +|---------|-------|------| +| Header comment, 개요, 리뷰 에이전트 지시 | Fixed at stub creation | Implementing agent must not modify or execute archive/complete/archive move steps | +| 구현 항목별 완료 여부 | Implementing agent checks only | Item text/order fixed | +| 구현 체크리스트 | Implementing agent checks only | Final checkbox is mandatory | +| 코드리뷰 전용 체크리스트 | Review agent only | Implementing agent must not modify | +| 계획 대비 변경 사항, 주요 설계 결정 | Implementing agent | Replace placeholder text with actual content | +| 사용자 리뷰 요청 | Implementing agent | Keep `상태: 없음` unless selected Milestone lock decision blocks implementation | +| 리뷰어를 위한 체크포인트 | Fixed at stub creation | Review focus list | +| 검증 결과 | Implementing agent | Paste actual stdout/stderr | +| 코드리뷰 결과 | Review agent appends | Not included until review | diff --git a/agent-task/openai_text_tool_call_boundary/PLAN-local-G06.md b/agent-task/openai_text_tool_call_boundary/PLAN-local-G06.md new file mode 100644 index 0000000..d22f822 --- /dev/null +++ b/agent-task/openai_text_tool_call_boundary/PLAN-local-G06.md @@ -0,0 +1,416 @@ + + +# Plan - BUGFIX_TOOLCALL + +## 이 파일을 읽는 구현 에이전트에게 + +이 계획은 구현 루프의 입력이다. 구현 후 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채우고 active 파일을 그대로 둔 뒤 review ready를 보고한다. 최종 판정, log rename, `complete.log`, archive 이동은 code-review-skill 전용이다. + +선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 막는 경우에만 review stub의 `사용자 리뷰 요청` 섹션을 정확한 근거로 채우고 멈춘다. 구현 중 직접 사용자에게 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 환경/secret/service 준비, 일반 범위 조정, 반복 실패, 증거 공백은 사용자 리뷰 요청이 아니라 검증 결과 또는 후속 plan으로 남긴다. + +## 배경 + +2026-07-06 dev-runtime의 Pi 호출에서 `qwen3.6:35b` 응답이 raw XML text tool-call로 끝났고, IOP Edge가 이를 OpenAI-compatible `tool_calls`로 합성하는 중 `malformed tool call: missing function block or definition inside tool_call`를 냈다. dev Edge/Node/provider pool은 살아 있었고, client는 SSE error를 받은 쪽이다. 이번 작업은 같은 형태의 `edit` payload를 회귀 fixture로 고정하고, Edge의 raw text tool-call normalization이 복잡한 인자 본문과 streaming tool-use turn을 안전하게 처리하도록 한다. + +## 사용자 리뷰 요청 흐름 + +사용자 리뷰 요청은 선택된 Milestone lock decision만 active review stub의 `사용자 리뷰 요청` 섹션에 기록한다. 구현 에이전트는 직접 사용자 prompt를 만들지 않고, code-review가 정당성 검증과 `USER_REVIEW.md` 작성을 소유한다. + +## 분석 결과 + +### 읽은 파일 + +- `agent-ops/rules/project/rules.md` +- `agent-ops/rules/common/rules-roadmap.md` +- `agent-roadmap/current.md` +- `agent-roadmap/ROADMAP.md` +- `agent-roadmap/phase/operational-observability-provider-management/PHASE.md` +- `agent-roadmap/phase/operational-observability-provider-management/milestones/usage-token-log-ops-mvp.md` +- `agent-ops/skills/common/router.md` +- `agent-ops/skills/common/plan/SKILL.md` +- `agent-ops/skills/common/_templates/implementation-user-review-request-section.md` +- `agent-ops/rules/project/domain/edge/rules.md` +- `agent-ops/rules/project/domain/testing/rules.md` +- `agent-contract/index.md` +- `agent-contract/outer/openai-compatible-api.md` +- `agent-test/local/rules.md` +- `agent-test/dev/rules.md` +- `agent-test/dev/edge-smoke.md` +- `agent-test/dev/node-smoke.md` +- `agent-test/dev/inventory.yaml` +- `Makefile` +- `apps/edge/internal/openai/types.go` +- `apps/edge/internal/openai/stream.go` +- `apps/edge/internal/openai/chat_handler.go` +- `apps/edge/internal/openai/tool_validation.go` +- `apps/edge/internal/openai/server_test.go` +- `apps/edge/internal/openai/log_safety_test.go` + +### SDD 기준 + +not applicable. 이 작업은 활성 `[스케치]` Milestone의 기능 Task 완료를 주장하지 않는 non-roadmap dev 회귀 bugfix다. 활성 Milestone `usage-token-log-ops-mvp`는 `구현 잠금: 잠금`이고 사용량/로그 운영 범위 스케치라, 이번 OpenAI-compatible raw text tool-call parser 회귀 수정과 직접 연결하지 않는다. + +### 테스트 환경 규칙 + +- 선택 test_env: `dev`. 버그가 dev-runtime OpenAI-compatible `18083`에서 발생했고 `agent-test/dev/edge-smoke.md`가 `apps/edge/internal/openai/**` text tool-call 합성/validation 변경 시 non-stream/stream raw boundary smoke를 요구한다. +- 적용 rule/profile: `agent-test/dev/rules.md`, `agent-test/dev/edge-smoke.md`, `agent-test/dev/node-smoke.md`, `agent-test/dev/inventory.yaml`. +- local quick check 기준도 확인했다: `agent-test/local/rules.md`. +- concrete local command: `go test ./apps/edge/internal/openai`는 현재 checkout에서 실행 가능하며 계획 작성 중 PASS 확인됨. +- dev smoke source: `agent-test/dev/edge-smoke.md`의 raw text tool-call boundary 기준. token/secret 원문은 tracked artifact에 남기지 않는다. + +테스트 환경 프리플라이트: + +- runner: `ssh toki@toki-labs.com` +- repo root: `/Users/toki/agent-work/iop-dev` +- dev runtime identity: Edge id `edge-toki-labs-dev`, OpenAI base URL `http://toki-labs.com:18083/v1`, runner-local base URL `http://127.0.0.1:18083/v1`, Edge-Node TCP `toki-labs.com:18084`, admin `127.0.0.1:19093` +- binary/config paths: `build/dev-runtime/bin/edge`, `build/dev-runtime/edge.yaml` +- 2026-07-06 조사 증거: `/v1/models`는 auth 401로 port alive, Control Plane status는 3개 node healthy/idle, `18083`/`18084` TCP reachable. +- 구현 후 dev smoke는 원격 checkout과 dev-runtime binary가 구현 commit을 반영한 뒤에만 현재 변경 검증으로 인정한다. 원격 HEAD가 다르거나 binary가 stale이면 dev-runtime clean sync/rebuild 또는 별도 배포 작업이 먼저 필요하다. + +### 테스트 커버리지 공백 + +- 기존 `server_test.go`는 단순 XML raw text tool-call, mustache tool-call, unknown/malformed 차단, non-stream retry, strict buffered stream retry를 다룬다. +- 공백 1: 이번 실제 실패처럼 `edit` tool의 `edits` JSON 배열 안에 Markdown code fence, escaped JSON request body, `http://:18081/...` angle-bracket placeholder가 들어간 XML parameter fixture가 없다. +- 공백 2: `collectXMLCandidateMatches`와 `collectXMLTextToolCallMatches`가 같은 scanner를 공유해야 하는 회귀가 없다. +- 공백 3: `tools[]`가 있는 streaming 요청을 live SSE로 둬서 retry가 불가능한 경로가 계속 선택되는지 막는 정책 테스트가 없다. + +### 심볼 참조 + +- renamed/removed symbol: none. +- parser 교체 시 함께 확인할 참조: `textToolCallBlockRE`, `textToolFunctionRE`, `textToolParameterRE`, `collectXMLCandidateMatches`, `collectXMLTextToolCallMatches`, `parseTextToolCallBlock`. +- `rg --sort path -n "synthesizeToolCallsFromTextResult|collectXMLCandidateMatches|textTool|tool_validation_error|strict_stream_buffer|malformed tool call" apps/edge/internal/openai`로 call site와 테스트 위치를 확인했다. + +### 분할 판단 + +split decision policy를 적용했다. 단일 plan으로 둔다. + +- ownership boundary: `apps/edge/internal/openai` 한 패키지 안의 OpenAI-compatible raw text tool-call boundary다. +- API-vs-call-site boundary: parser helper와 stream routing call site가 같은 public behavior를 형성하며, parser fixture 없이는 stream 완화만 따로 완료하기 어렵다. +- test strategy: 모든 회귀가 `server_test.go`의 fake run service와 `go test ./apps/edge/internal/openai`로 검증된다. +- risk profile: 성공 content에 raw ``이 새면 안 된다는 동일 계약 리스크다. +- split하면 parser와 stream policy가 서로의 테스트 fixture를 공유해야 해서 review coordination 비용이 더 크다. + +### 범위 결정 근거 + +- 포함: `apps/edge/internal/openai/types.go`, `stream.go`, 필요 시 `tool_validation.go`, `server_test.go`. +- 제외: node provider adapter, provider pool routing, token usage/log ledger, docs 포트 수정, Pi client 설정. 이번 증상은 client가 받은 Edge `tool_validation_error`이며, provider/Node outage 또는 client-side parse failure가 아니다. +- 제외: secret/API key logging. 기존 `log_safety_test.go`의 preview field 금지를 유지한다. + +### 빌드 등급 + +`local-G06`. 변경은 한 Go 패키지에 갇히고 deterministic unit/regression test로 검증 가능하지만, parser state machine과 streaming retry 경로가 OpenAI-compatible 외부 계약에 닿아 중간 수준 리스크가 있다. + +## 구현 체크리스트 + +- [ ] [BUGFIX_TOOLCALL-1] 실제 dev 실패 형태의 `edit` raw XML tool-call 회귀 테스트를 추가한다. +- [ ] [BUGFIX_TOOLCALL-2] XML raw text tool-call parser를 regex-only에서 parameter body를 opaque text로 다루는 scanner로 강화하고 기존 합성 경로를 모두 같은 parser로 연결한다. +- [ ] [BUGFIX_TOOLCALL-3] `tools[]`가 있는 streaming chat completions를 retry 가능한 buffered validation 경로로 보내는 완화책을 추가한다. +- [ ] [BUGFIX_TOOLCALL-4] 로컬 회귀 테스트와 dev-runtime smoke 계약을 실행하고 결과를 review stub에 남긴다. +- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. + +## BUGFIX_TOOLCALL-1 실제 실패 fixture 추가 + +### 문제 + +실제 dev failure는 `apps/edge/internal/openai/types.go:256`의 XML candidate parser가 모델의 raw `` 응답을 읽는 과정에서 발생했다. 현재 테스트는 단순 `` 중심이라, `edit` tool payload의 큰 JSON/Markdown/angle-bracket 본문이 같은 error를 재현하는지 고정하지 못한다. + +실제 에러 케이스 예시: + +````text +Pi session: 2026-07-06T09:13:01.814Z +provider/model: iop / qwen3.6:35b +response_id: chatcmpl-manual-1783329323005263000 +stopReason: error +errorMessage: malformed tool call: missing function block or definition inside tool_call + +assistant content preview: +두 줄을 `18083` -> `18081`로 수정하고 검증을 진행한다. + + + + +/config/workspace/iop/docs/edge-local-dev-guide.md + + +[{"newText": "```bash\ncurl -fsS http://:18081/v1/chat/completions ..."}] + + + +```` + +핵심은 `` 본문이 JSON string array이고 그 안에 Markdown code fence, escaped JSON request body, literal `` placeholder가 들어간다는 점이다. 이 literal angle-bracket text는 tool-call XML tag로 해석하면 안 된다. + +### 해결 방법 + +`apps/edge/internal/openai/server_test.go`에 다음 성격의 테스트를 추가한다. + +Before: + +```go +// apps/edge/internal/openai/server_test.go:514 +func TestChatCompletionsSynthesizesTextToolCallsForProviderRoute(t *testing.T) { + fake.events <- &iop.RunEvent{Type: "delta", Delta: "...\n\n[\"git status\"]\n..."} +} +``` + +After: + +```go +func TestChatCompletionsSynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder(t *testing.T) { + fake.events <- &iop.RunEvent{Type: "delta", Delta: "두 줄을 `18083` -> `18081`로 수정하고 검증을 진행한다.\n\n" + editToolCallFixture} + // expect finish_reason tool_calls, function.name edit, path preserved, + // edits decoded as JSON array, and no raw leak. +} +``` + +fixture 요구: + +- tool schema: `edit` with required `path` string and `edits` array. +- `edits[0].newText` contains `curl -fsS http://:18081/v1/chat/completions`. +- response body must not contain raw `` or escaped `\u003ctool_call`. +- `function.arguments` JSON must decode and preserve the literal `` inside `newText`. + +### 수정 파일 및 체크리스트 + +- [ ] `apps/edge/internal/openai/server_test.go`: actual failure fixture helper/test 추가. +- [ ] `apps/edge/internal/openai/server_test.go`: stream variant도 같은 fixture로 추가하거나 BUGFIX_TOOLCALL-3 테스트에서 재사용. + +### 테스트 작성 + +작성한다. Test names: + +- `TestChatCompletionsSynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder` +- `TestChatCompletionsStreamSynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder` + +### 중간 검증 + +```bash +go test ./apps/edge/internal/openai -run 'TestChatCompletions(SynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder|StreamSynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder)$' +``` + +Expected: PASS, no raw `` leak, decoded `edits[0].newText` contains literal ``. + +## BUGFIX_TOOLCALL-2 XML parser scanner 강화 + +### 문제 + +`apps/edge/internal/openai/types.go:214`-`217`는 XML-ish tool-call parsing을 regex로 수행한다. `collectXMLCandidateMatches`는 `textToolCallBlockRE`로 block을 자르고 `textToolFunctionRE`와 `textToolParameterRE`를 별도로 적용한다. `collectXMLTextToolCallMatches`와 `parseTextToolCallBlock`도 같은 regex 계열을 다시 사용한다. + +```go +// apps/edge/internal/openai/types.go:214 +textToolCallBlockRE = regexp.MustCompile(`(?is)<\s*tool_call\s*>(.*?)<\s*/\s*tool_call\s*>`) +// apps/edge/internal/openai/types.go:216 +textToolFunctionRE = regexp.MustCompile(`(?is)<\s*function\s*=\s*...`) +// apps/edge/internal/openai/types.go:217 +textToolParameterRE = regexp.MustCompile(`(?is)<\s*parameter\s*=\s*...`) +``` + +이 방식은 parameter 본문에 code fence, JSON string, angle-bracket placeholder가 섞인 실제 agent payload를 opaque body로 다루지 못하는 취약점이 있다. + +### 해결 방법 + +XML 형식만 작은 scanner로 교체한다. mustache scanner는 이미 `findMustacheToolCallEnd`에서 quote/depth를 고려하므로 범위를 넓히지 않는다. + +After approach: + +```go +type xmlTextToolCallBlock struct { + start int + end int + name string + params map[string]string + err error +} + +func scanXMLTextToolCallBlocks(content string) ([]xmlTextToolCallBlock, bool) { + // 1. top-level open을 찾는다. + // 2. matching 까지 block을 잡는다. + // 3. block 안에서 top-level ...을 찾는다. + // 4. function body에서 ...를 찾고, + // parameter body는 opaque string으로 둔다. +} +``` + +구현 세부: + +- `` open/close와 `` open/close는 tag boundary로만 인식한다. +- `` body 내부의 ``, shell redirect `<`, JSON string, Markdown fence는 tag로 해석하지 않는다. +- 닫히지 않은 ``은 기존처럼 `malformed tool call: unclosed tool_call block`. +- `` 안에 function block이 없으면 기존 error string `malformed tool call: missing function block or definition inside tool_call`를 유지한다. +- unknown tool은 기존 `tool %q is not in request tools`를 유지한다. +- `collectXMLCandidateMatches`와 `collectXMLTextToolCallMatches`가 같은 scanner 결과를 사용하게 한다. + +### 수정 파일 및 체크리스트 + +- [ ] `apps/edge/internal/openai/types.go`: XML regex block/function/parameter parser를 scanner helper로 교체. +- [ ] `apps/edge/internal/openai/types.go`: `collectXMLCandidateMatches`와 `collectXMLTextToolCallMatches`가 parser 결과를 공유. +- [ ] `apps/edge/internal/openai/types.go`: existing error string과 return semantics 유지. +- [ ] `apps/edge/internal/openai/server_test.go`: 기존 XML/mustache/unknown/malformed 테스트가 계속 통과하는지 확인. + +### 테스트 작성 + +작성한다. BUGFIX_TOOLCALL-1 fixture가 scanner 회귀를 검증한다. 추가로 ``가 JSON string 안에 들어가는 케이스는 이번 범위에서 필수로 만들지 않는다. 실제 실패는 `` literal이며, 임의 close-tag escaping 정책은 별도 parser hardening 후속으로 둔다. + +### 중간 검증 + +```bash +go test ./apps/edge/internal/openai -run 'TestChatCompletions(SynthesizesToolCallsFromClineTextBlock|SynthesizesTextToolCallsForProviderRoute|SynthesizesProviderTextToolCallsWhenFallbackMarked|FailsMalformedTextToolCallAfterRetryLimit|SynthesizesEditToolCallWithMarkdownAndAngleBracketPlaceholder)$' +``` + +Expected: PASS. 기존 raw text tool-call normalization behavior와 실제 edit fixture가 모두 통과한다. + +## BUGFIX_TOOLCALL-3 tools streaming을 buffered validation 경로로 완화 + +### 문제 + +live SSE 경로는 `apps/edge/internal/openai/stream.go:22`에서 `outputPolicy.Strict && outputPolicy.StreamBuffer`일 때만 buffered retry 경로로 간다. 그렇지 않으면 `stream.go:217`-`220`에서 complete 시점에 raw text tool-call 합성을 시도하고 실패하면 SSE `tool_validation_error`를 낸다. 이 경로는 이미 content delta를 보냈을 수 있어 bounded retry가 불가능하다. `tool_validation.go:45`-`50`도 live SSE의 retry 제한을 설명한다. + +```go +// apps/edge/internal/openai/stream.go:36 +if outputPolicy.Strict && outputPolicy.StreamBuffer { + s.streamBufferedChatCompletion(...) +} + +// apps/edge/internal/openai/stream.go:217 +} else if len(req.Tools) > 0 { + res := synthesizeToolCallsFromTextResult(...) + if res.validationErr != nil { + writeSSEErrorWithType(...) + return + } +} +``` + +이번 dev failure는 `strict_output=false`, `strict_stream_buffer=false`인 streaming tool-use turn에서 발생해 재시도 없이 client-visible error가 되었다. + +### 해결 방법 + +`tools[]`가 있는 chat completions streaming 요청은 strict output 여부와 무관하게 buffered validation 경로로 보낸다. + +After: + +```go +if outputPolicy.Strict && outputPolicy.StreamBuffer || len(req.Tools) > 0 { + s.streamBufferedChatCompletion(w, r, req, submitReq, handle, flusher, outputPolicy, validation) + return +} +``` + +동시에 `toolValidationContractForChatRequest`도 `req.Stream && len(req.Tools)>0` buffered policy와 일치하도록 validation contract를 활성화한다. live SSE에서 raw tool-call filter를 유지하는 기존 방어는 no-tools streaming과 혹시 남는 live path용으로 보존한다. + +### 수정 파일 및 체크리스트 + +- [ ] `apps/edge/internal/openai/stream.go`: `len(req.Tools)>0` stream은 `streamBufferedChatCompletion`을 사용. +- [ ] `apps/edge/internal/openai/tool_validation.go`: stream+tools validation contract가 buffered path에서 활성화되도록 조건 정렬. +- [ ] `apps/edge/internal/openai/server_test.go`: 기존 `TestChatCompletionsLiveStreamSkipsRuntimeToolValidation` 기대값을 새 정책에 맞게 수정하거나 새 테스트로 대체. +- [ ] `apps/edge/internal/openai/server_test.go`: invalid first stream attempt -> valid retry -> only valid tool_call emitted을 strict flag 없이 검증. + +### 테스트 작성 + +작성한다. Test name: + +- `TestChatCompletionsToolsStreamRetriesMalformedToolCallBeforeChunk` + +목표: + +- config `StrictOutput:false`, `StrictStreamBuffer:false`. +- request `stream:true`, `tools[]`. +- first fake run malformed native/text tool-call, second run valid. +- `fake.reqsSnapshot()` length 2. +- SSE body contains valid second call and `[DONE]`, first bad call/raw `` does not leak. + +### 중간 검증 + +```bash +go test ./apps/edge/internal/openai -run 'TestChatCompletions(ToolsStreamRetriesMalformedToolCallBeforeChunk|StrictBufferedStreamRetriesMalformedToolCallBeforeChunk|ProviderStreamBlocksMalformedTextToolCall)$' +``` + +Expected: PASS. tool-bearing stream retry policy works without requiring `StrictOutput:true`. + +## BUGFIX_TOOLCALL-4 검증과 dev smoke 계약 + +### 문제 + +OpenAI-compatible raw text tool-call boundary는 외부 API 계약이다. local unit regression만으로 parser는 검증 가능하지만, dev-runtime `18083` smoke는 원격 checkout과 binary가 변경 commit을 반영해야 의미가 있다. + +### 해결 방법 + +local regression을 필수로 실행하고, dev-runtime이 구현 commit으로 배포된 뒤 non-stream/stream smoke를 수행한다. dev smoke payload는 tracked 파일에 secret을 남기지 않고 `/tmp`에서 생성한다. + +### 수정 파일 및 체크리스트 + +- [ ] `CODE_REVIEW-local-G06.md`: local test stdout/stderr 붙여넣기. +- [ ] `CODE_REVIEW-local-G06.md`: dev smoke를 수행했으면 `/tmp` output path와 PASS 조건 기록. +- [ ] dev smoke를 수행하지 못하면 원격 sync/binary stale/token 부재 중 정확한 blocker를 기록. + +### 테스트 작성 + +추가 테스트 파일은 만들지 않는다. `server_test.go` 회귀 테스트가 구현 검증이다. + +### 중간 검증 + +```bash +go test ./apps/edge/internal/openai +``` + +Expected: PASS. + +## 수정 파일 요약 + +| 파일 | 항목 | +|---|---| +| `apps/edge/internal/openai/server_test.go` | BUGFIX_TOOLCALL-1, BUGFIX_TOOLCALL-3 | +| `apps/edge/internal/openai/types.go` | BUGFIX_TOOLCALL-2 | +| `apps/edge/internal/openai/stream.go` | BUGFIX_TOOLCALL-3 | +| `apps/edge/internal/openai/tool_validation.go` | BUGFIX_TOOLCALL-3 | +| `agent-task/openai_text_tool_call_boundary/CODE_REVIEW-local-G06.md` | BUGFIX_TOOLCALL-4 | + +## 최종 검증 + +```bash +go test ./apps/edge/internal/openai +``` + +Expected: PASS. Go test cache output is acceptable only if the new test names appear in the package test list during implementation; if in doubt run `go test -count=1 ./apps/edge/internal/openai`. + +Dev-runtime smoke after the implementation commit is deployed to `/Users/toki/agent-work/iop-dev`: + +```bash +ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && git status --branch --short && git log --oneline -1 && test -x build/dev-runtime/bin/edge && build/dev-runtime/bin/edge --config build/dev-runtime/edge.yaml config refresh --help >/tmp/iop-tool-boundary-refresh-help.txt && curl -fsS http://127.0.0.1:18001/edges/edge-toki-labs-dev/status >/tmp/iop-tool-boundary-status.json' +``` + +Expected: remote checkout is the implementation commit or documented deployed commit, edge binary exists, refresh help succeeds, status JSON exists. If the checkout/binary is stale, do not treat the following smoke as current-change verification. + +```bash +ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_OPENAI_TOKEN:-}" && cat >/tmp/iop-tool-boundary-nonstream.json <<'"'"'JSON'"'"' +{"model":"qwen3.6:35b","stream":false,"messages":[{"role":"user","content":"Reply with exactly one edit tool call for /tmp/example.md that writes a curl example using http://:18081/v1/chat/completions inside a markdown bash fence."}],"tools":[{"type":"function","function":{"name":"edit","parameters":{"type":"object","properties":{"path":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"newText":{"type":"string"}},"required":["newText"]}}},"required":["path","edits"],"additionalProperties":false}}}],"tool_choice":"auto"} +JSON +curl -sS http://127.0.0.1:18083/v1/chat/completions -H "Authorization: Bearer ${IOP_OPENAI_TOKEN}" -H "Content-Type: application/json" --data-binary @/tmp/iop-tool-boundary-nonstream.json >/tmp/iop-tool-boundary-nonstream.out +python3 - <<'"'"'PY'"'"' +from pathlib import Path +s=Path("/tmp/iop-tool-boundary-nonstream.out").read_text() +assert "` leak. A valid raw text tool-call is normalized to `tool_calls`; if the live model refuses or emits malformed output after retry, response must be controlled `tool_validation_error`, not leaked success content. + +```bash +ssh toki@toki-labs.com 'cd /Users/toki/agent-work/iop-dev && test -n "${IOP_OPENAI_TOKEN:-}" && python3 - <<'"'"'PY'"'"' >/tmp/iop-tool-boundary-stream.json +import json +print(json.dumps({"model":"qwen3.6:35b","stream":True,"messages":[{"role":"user","content":"Reply with exactly one edit tool call for /tmp/example.md that writes a curl example using http://:18081/v1/chat/completions inside a markdown bash fence."}],"tools":[{"type":"function","function":{"name":"edit","parameters":{"type":"object","properties":{"path":{"type":"string"},"edits":{"type":"array","items":{"type":"object","properties":{"newText":{"type":"string"}},"required":["newText"]}}},"required":["path","edits"],"additionalProperties":False}}}],"tool_choice":"auto"})) +PY +curl -sS http://127.0.0.1:18083/v1/chat/completions -H "Authorization: Bearer ${IOP_OPENAI_TOKEN}" -H "Content-Type: application/json" --data-binary @/tmp/iop-tool-boundary-stream.json >/tmp/iop-tool-boundary-stream.sse +python3 - <<'"'"'PY'"'"' +from pathlib import Path +s=Path("/tmp/iop-tool-boundary-stream.sse").read_text() +assert "` leak, and either normalized `delta.tool_calls` or controlled `tool_validation_error`. + +모든 코드 변경 완료 후 반드시 `CODE_REVIEW-*-G??.md`의 구현 에이전트 소유 섹션을 채운다. 이 파일 작성이 구현의 마지막 단계다. diff --git a/agent-task/review_followup_alignment/02_docs_runtime_context_split/CODE_REVIEW-local-G02.md b/agent-task/review_followup_alignment/02_docs_runtime_context_split/CODE_REVIEW-local-G02.md deleted file mode 100644 index 9b6f012..0000000 --- a/agent-task/review_followup_alignment/02_docs_runtime_context_split/CODE_REVIEW-local-G02.md +++ /dev/null @@ -1,74 +0,0 @@ - -# CODE_REVIEW-local-G02: 공개 문서와 로컬 검증 문맥 분리 -date=2026-07-06 - -## 상태 -구현 대기 - -## 이 파일을 읽는 구현 에이전트에게 -구현 후 이 파일의 구현 에이전트 소유 섹션을 실제 변경 내용, 계획 대비 변경, 검증 출력으로 채운다. active `PLAN-*.md`와 `CODE_REVIEW-*.md`는 남겨두고 review 준비 상태를 보고한다. 직접 사용자에게 질문하거나 `USER_REVIEW.md`를 만들거나 archive/complete 처리를 하지 않는다. - -## 구현 체크리스트 -- [ ] REVIEW_DOCS-1의 공개 문서 축소와 local test 문맥 이동을 수행한다. -- [ ] 중간 검증 명령을 실행하고 결과를 `CODE_REVIEW-local-G02.md`에 기록한다. -- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다. 이 항목이 완료되기 전에는 구현이 완료된 것이 아니다. - -## 코드리뷰 전용 체크리스트 - -> **[REVIEW AGENT ONLY]** 이 체크리스트는 코드리뷰 에이전트만 사용한다. -> 구현 에이전트는 이 섹션을 수정하거나 체크하지 않는다. - -- [ ] `코드리뷰 결과`에 `PASS`, `WARN`, `FAIL` 중 하나의 판정을 append한다. -- [ ] 판정과 Required/Suggested/Nit 분류가 서로 일치한다. -- [ ] active `CODE_REVIEW-*-G??.md`를 archive 로그로 이동한다. -- [ ] active `PLAN-*-G??.md`를 archive 로그로 이동한다. -- [ ] `.gitignore`의 Agent-Ops 관리 block을 확인한다. -- [ ] PASS이면 `complete.log`를 작성하고 active `.md` 파일을 남기지 않는다. -- [ ] PASS split 작업이면 이동 후 빈 active parent 정리 여부를 확인한다. - -## 계획 대비 변경 사항 -_구현 에이전트가 계획과 다르게 구현한 부분을 이유와 함께 기록한다._ - -## 주요 설계 결정 -_구현 에이전트가 주요 설계 결정 사항을 기록한다._ - -## 사용자 리뷰 요청 - -_기본값은 `없음`이다. 구현 중 새 결정이 필요해 보여도 직접 질문하거나 선택지를 제시하거나 `request_user_input`을 호출하지 않는다. 이 섹션은 선택된 Milestone `구현 잠금 > 결정 필요` 항목이 실구현을 차단할 때만 채운다. 외부 환경/secret/서비스 준비, 검증 증거 공백, 반복 실패, 일반 범위 조정은 사용자 리뷰 요청이 아니며 `검증 결과`, `계획 대비 변경 사항`, 또는 code-review의 일반 follow-up plan으로 처리한다._ - -- 상태: 없음 -- 사유 유형: 없음 -- 연결 대상: 없음 -- 결정 필요: 없음 -- 차단 근거: 없음 -- 실행한 검증/명령: 없음 -- 자동 후속 불가 이유: 없음 -- 재개 조건: 없음 - -## 리뷰어를 위한 체크포인트 -- tracked `docs/`가 공개 가이드 성격으로 정리되었는가. -- private/local-only 검증 문맥이 local test 문서로 이동했는가. -- 공개 문서에 secret 원문, private IP, machine-specific host가 남지 않는가. - -## 검증 결과 -_구현 에이전트가 각 중간 검증 및 최종 검증 명령 실행 후 출력을 여기에 붙여 넣는다._ - -필수 규칙: -- 검증 명령은 고정된 계약이다. 임의로 대체하지 않는다. -- 대체가 필요하면 `계획 대비 변경 사항`에 이유와 대체 명령을 기록한다. -- `검증 결과`에는 실제 stdout/stderr를 붙여 넣는다. - -### REVIEW_DOCS-1 중간 검증 -```bash -rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md -rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md -``` - -### 최종 검증 -```bash -rg --sort path -n '192\.168\.|dev-runtime|edge-toki-labs|Windows SSH|toki-labs\.com' docs/edge-local-dev-guide.md -rg --sort path -n 'mattermost_credentials|Bearer [A-Za-z0-9._-]+' docs/edge-local-dev-guide.md -``` - -## 코드리뷰 결과 -_리뷰 에이전트가 append한다._ diff --git a/docs/edge-local-dev-guide.md b/docs/edge-local-dev-guide.md index 975d9bb..79e6e00 100644 --- a/docs/edge-local-dev-guide.md +++ b/docs/edge-local-dev-guide.md @@ -1,13 +1,11 @@ # Edge Local Quickstart -이 문서는 `toki-labs.com` Control Plane에 native Edge를 붙이고, Ollama Node를 연결해 OpenAI-compatible smoke까지 확인하는 최소 절차다. +이 문서는 Edge를 로컬에서 빌드하고 실행해 Node를 연결하고, OpenAI-compatible smoke까지 확인하는 최소 절차다. 설정 기준은 `edge.yaml`이다. 주소는 환경 변수로 흩뿌리지 않는다. ## 1. Package -현재 `toki-labs.com` 원격처럼 Apple Silicon macOS에서 실행한다. - ```bash make build ``` @@ -17,8 +15,8 @@ make build ```bash rm -rf "$HOME/iop-edge-test" mkdir -p "$HOME/iop-edge-test" -tar -xzf build/packages/iop-edge-darwin-arm64.tar.gz -C "$HOME/iop-edge-test" -cd "$HOME/iop-edge-test/iop-edge-darwin-arm64" +tar -xzf build/packages/iop-edge-.tar.gz -C "$HOME/iop-edge-test" +cd "$HOME/iop-edge-test/iop-edge-" ./iop-edge config init ``` @@ -37,22 +35,16 @@ models: ollama-gemma: "gemma4:26b" edge: - id: "edge-toki-labs" - name: "Toki Labs Edge" + id: "edge-1" + name: "Edge 1" server: listen: "0.0.0.0:19090" - advertise_host: "toki-labs.com" bootstrap: listen: "0.0.0.0:18080" - artifact_base_url: "http://toki-labs.com:18080" artifact_dir: "artifacts" -control_plane: - enabled: true - wire_addr: "toki-labs.com:19081" - refresh: enabled: true listen: "127.0.0.1:19093" @@ -108,7 +100,8 @@ echo $! > run/iop-edge.pid 확인: ```bash -curl -fsS http://toki-labs.com:18000/edges +# Control Plane에 연결된 Edge 상태 조회 +curl -fsS http://:18000/edges ``` ## 5. Node 실행 @@ -118,216 +111,62 @@ curl -fsS http://toki-labs.com:18000/edges Node 연결 확인: ```bash -curl -fsS http://toki-labs.com:18000/edges/edge-toki-labs/status +curl -fsS http://:18000/edges//status ``` -## 6. dev-runtime provider pool 반복 기준 - -현재 GX10 vLLM, OneXPlayer Lemonade, Mac MLX vLLM을 같은 model alias로 묶어 검증하는 dev-runtime 기준은 원격 runner의 동기화된 iop checkout과 `build/dev-runtime/edge.yaml`이다. - -배포 전 원격 runner checkout은 보존 대상이 아니다. 항상 clean sync 후 dev-runtime binary를 다시 빌드한다. +## 6. 기본 Smoke ```bash -cd /Users/toki/agent-work/iop-dev -git fetch origin main -git reset --hard origin/main -git clean -fd - -go build -trimpath -o build/dev-runtime/bin/edge ./apps/edge/cmd/edge -go build -trimpath -o build/dev-runtime/bin/iop-node ./apps/node/cmd/node -GOOS=linux GOARCH=arm64 go build -trimpath -o build/dev-runtime/bin/iop-node-linux-arm64 ./apps/node/cmd/node -GOOS=windows GOARCH=amd64 go build -trimpath -o build/dev-runtime/bin/iop-node-windows-amd64.exe ./apps/node/cmd/node -``` - -기준 주소: - -- Control Plane status: 원격 host의 `http://127.0.0.1:18001` -- dev-runtime Edge id: `edge-toki-labs-dev` -- bootstrap HTTP: `http://toki-labs.com:18082` -- Edge OpenAI-compatible base URL: `http://toki-labs.com:18083/v1` -- Edge-Node TCP transport: `toki-labs.com:18084` - -외부로 노출되는 OpenAI-compatible API는 `openai.bearer_token`으로 보호한다. tracked 문서에는 실제 token 원문을 기록하지 않고, Cline 같은 외부 client에는 운영자가 전달한 API key를 `Authorization: Bearer ...` 형태로 설정한다. - -기준 provider pool: - -- model alias: `qwen3.6:35b` -- provider candidate: `gx10-vllm` on `gx10-vllm-node`, capacity `4`, priority `0` -- provider candidate: `onexplayer-lemonade` on `onexplayer-lemonade-node`, capacity `3`, priority `1` -- provider candidate: `mac-mlx-vllm` on `mac-codex-node`, capacity `2`, priority `2` - -Provider-pool dispatch는 capacity gate를 먼저 적용해 `in_flight >= capacity`인 provider를 후보에서 제외한다. 남은 후보 중 가장 낮은 `in_flight` 레벨을 우선 선택하며, 같은 `in_flight` 레벨 안에서 priority 숫자가 낮은 provider를 먼저 선택한다. 이 기준에서 10개 동시 요청은 정상적으로 `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2`, queue `1` 구성이 된다. - -GX10 vLLM은 `nvidia/Qwen3.6-35B-A3B-NVFP4`를 Docker container `iop-vllm-qwen36-prev-20260704063128`로 띄운다. dev 기준 endpoint는 `http://192.168.0.91:8001/v1`, runtime baseline은 `--max-model-len 262144`, `--max-num-seqs 4`, `--gpu-memory-utilization 0.30`, `--reasoning-parser qwen3`, `--default-chat-template-kwargs '{"enable_thinking":true}'`이다. - -OneXPlayer Lemonade Node는 원격 runner나 Edge host에서 다시 SSH하거나 proxy process로 띄우지 않는다. 현재 작업 호스트에서 Windows host에 `ssh r0bin@192.168.0.59`로 직접 접속한 뒤, 그 host 안에서 generated PowerShell bootstrap 명령을 실행한다. 이때 Node 작업 경로는 `$HOME\iop-field`이며, Lemonade provider endpoint는 Windows host 로컬에서 접근 가능한 값을 기준으로 검증한다. - -OneXPlayer Lemonade는 `Qwen3.6-35B-A3B-MTP-GGUF` artifact를 사용하되 runtime MTP speculative decoding은 끈다. dev long-context 기준 load 설정은 Vulkan backend, `ctx_size=524288`, `llamacpp_args="--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024"`로 고정한다. llama.cpp server 기준 `ctx_size`는 전체 KV/context 예산이고 `-np`는 server slot 수이므로, 이 설정은 총 context 예산을 full `262144` window 2개분으로 제한하고 slot 3개를 둔다. 2026-07-04 관측 기준 `/slots`는 slot별 `n_ctx=174848`을 반환한다. 재시작 뒤에도 유지되도록 `/v1/load`는 `save_options=true`로 적용한다. - -```bash -curl -fsS http://192.168.0.59:13305/v1/load \ - -H 'Content-Type: application/json' \ - -d '{"model_name":"Qwen3.6-35B-A3B-MTP-GGUF","ctx_size":524288,"llamacpp_backend":"vulkan","llamacpp_args":"--spec-type none -np 3 -cb -fa on -b 4096 -ub 1024","save_options":true}' -``` - -Mac MLX provider는 Edge host 자신에서 `vllm-mlx`로 띄운다. dev 기준 endpoint는 `http://127.0.0.1:8002/v1`, served model은 `mlx-community/Qwen3.6-35B-A3B-4bit`, provider capacity는 `2`이다. 작업 경로는 `/Users/toki/agent-work/iop-mlx-vllm`, Python 환경은 `.venv`의 Homebrew Python 3.12, 모델 cache는 `hf-cache`를 사용한다. 확인된 package baseline은 `vllm-mlx 0.3.0`, `mlx 0.31.2`, `mlx-lm 0.31.3`이다. - -vllm-mlx process는 `--max-num-seqs 2`, `--max-kv-size 262144`, `--max-request-tokens 262144`, `--use-paged-cache --paged-cache-block-size 64 --max-cache-blocks 4096`로 고정한다. 이 설정은 병렬 슬롯을 `2`로 제한하고, KV/context 예산을 full `262144` window 1개분으로 맞춘다. - -```bash -cd /Users/toki/agent-work/iop-mlx-vllm -export HF_HOME="$PWD/hf-cache" -export IOP_MLX_API_KEY="" -nohup .venv/bin/vllm-mlx serve mlx-community/Qwen3.6-35B-A3B-4bit \ - --offline \ - --host 127.0.0.1 \ - --port 8002 \ - --api-key "$IOP_MLX_API_KEY" \ - --max-num-seqs 2 \ - --max-kv-size 262144 \ - --max-request-tokens 262144 \ - --use-paged-cache \ - --paged-cache-block-size 64 \ - --max-cache-blocks 4096 \ - --enable-auto-tool-choice \ - --tool-call-parser qwen \ - --reasoning-parser qwen3 \ - --default-chat-template-kwargs '{"enable_thinking": true}' \ - --mllm \ - > logs/vllm-mlx.stdout.log 2> logs/vllm-mlx.stderr.log & -echo $! > vllm-mlx.pid -``` - -Mac MLX provider의 주요 운영 파일은 `vllm-mlx.pid`, `logs/vllm-mlx.stdout.log`, `logs/vllm-mlx.stderr.log`다. 2026-06-24 직접 호출 측정 기준으로 `max_tokens=192`, `enable_thinking=false`에서 1/2/3 동시 총합은 약 `59.66`, `87.31`, `98.13 tok/s`였다. - -SSH 세션 안의 `Start-Process`는 세션 종료와 함께 `iop-node.exe`가 정리될 수 있다. 반복 배포에서는 Windows host에서 `Win32_Process.Create` 방식으로 세션 독립 실행한다. - -```powershell -$work = "$HOME\iop-field" -$cmd = 'cmd.exe /c "cd /d C:\Users\r0bin\iop-field && iop-node.exe --config node.yaml serve >> iop-node.wmi.stdout.log 2>> iop-node.wmi.stderr.log"' -Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = $cmd; CurrentDirectory = $work } -``` - -capacity, provider mapping, model alias 변경은 후보 `edge.yaml` 수정 후 `config check`, refresh dry-run, refresh apply 순서로 반영한다. `config refresh` subcommand나 `19093` admin port가 없으면 dev-runtime binary rebuild 누락으로 보고 clean sync/rebuild부터 다시 수행한다. - -dev-runtime 번들은 바이너리를 `build/dev-runtime/bin/edge`에 둔다. 명령은 그 경로를 `EDGE_BIN`으로 잡고 실행한다. - -```bash -EDGE_BIN=build/dev-runtime/bin/edge -"$EDGE_BIN" --config build/dev-runtime/edge.yaml config check -"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode dry-run --addr 127.0.0.1:19093 -"$EDGE_BIN" --config build/dev-runtime/edge.yaml config refresh --mode apply --addr 127.0.0.1:19093 -``` - -Node host OS 재부팅은 필요하지 않다. Edge process 재시작이나 일시 단절은 Node reconnect 정책으로 처리한다. Node는 최초 연결 성공 이후 기본 `10s` 간격으로 최대 `10`회 재접속을 시도하므로, retry window 안에서 Edge가 회복되면 bootstrap 명령을 다시 실행하지 않는다. retry 한계를 넘겨 Node process가 종료된 경우에만 해당 host에서 새 bootstrap 실행이 필요하다. - -반복 검증 순서: - -1. Control Plane status에서 `edge-toki-labs-dev`의 connected node 수와 node id를 확인한다. -2. Edge host에서 `toki-labs.com:18084`의 established node TCP connection 수를 확인한다. -3. Edge process를 재시작하고 bootstrap 재실행 없이 3개 Node 연결이 회복되는지 확인한다. -4. `http://toki-labs.com:18083/healthz`와 `/v1/models`를 확인한다. -5. capacity를 `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2`로 맞춘 후보 config를 refresh apply하고 Edge process가 유지되는지 확인한다. Node adapter capacity 변경이 `restart_required`로 반환되면 Edge process를 새 config로 재시작한 뒤 Node reconnect 상태를 확인한다. -6. `qwen3.6:35b`로 `/v1/responses`와 `/v1/chat/completions` 각각에 provider capacity 총합 + 1개 동시 요청을 보낸다. -7. capacity `gx10-vllm=4`, `onexplayer-lemonade=3`, `mac-mlx-vllm=2` 기준이면 endpoint별 9개 동시 요청에서 Control Plane status의 provider snapshot이 총 `in_flight=9`, `queued=0`으로 처리되어야 한다. 10개 동시 요청에서는 `in_flight=9`, `queued>=1`, 13개 동시 확장 smoke에서는 `in_flight=9`, `queued>=4`를 기대한다. -8. 요청 완료 후 provider snapshot이 `in_flight=0`, `queued=0`으로 회복되는지 확인한다. - -2026-06-24 dev 13-way 확장 smoke는 Mac capacity가 `3`이던 이전 관측이다. 해당 run에서는 `/v1/chat/completions` 13개 요청이 모두 성공했고 peak `in_flight=10`, 실제 queue 대기 `3`개가 관측되었다. 현재 Mac capacity `2` 기준의 13-way smoke 기대치는 peak `in_flight=9`, queue 대기 `4`개 이상이다. - -Qwen 계열 thinking 모델은 reasoning 텍스트를 포함해 응답할 수 있다. 이 dev smoke에서는 thinking 출력을 실패로 보지 않고, HTTP 성공, final marker 포함, provider log/run count 증가를 기준으로 판정한다. - -### Request-level think-control smoke - -`/v1/chat/completions` 요청은 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`으로 thinking/reasoning 동작과 응답 노출을 제어할 수 있다. dev-runtime provider pool에서는 vLLM, vLLM-MLX, Lemonade 지원 범위가 다르므로 provider-isolated smoke로 provider별 기대 결과를 분리해 판정한다. - -- 공통 안정 smoke: `think` 생략, `include_reasoning=false`, 또는 budget-only `thinking_token_budget` -- vLLM / Lemonade `think=false`: `chat_template_kwargs.enable_thinking=false`로 reasoning 생성을 끄고 content stream이 유지되어야 한다. -- vLLM-MLX `think=false` 또는 `reasoning_effort=none`: 현재 런타임은 streaming reasoning disable을 지원하지 않으므로 `unsupported think control` 오류를 기대한다. -- vLLM / vLLM-MLX / Lemonade `reasoning_effort=low|medium|high`: 현재 provider 매핑에서는 `unsupported think control` 오류를 반환한다. - -예시 (thinking 비활성화, provider-isolated vLLM 또는 Lemonade 기대): - -```bash -curl -fsS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer ' \ - -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"think":false}' -``` - -예시 (thinking 생성은 허용하고 response reasoning_content만 숨김): - -```bash -curl -fsS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer ' \ - -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"include_reasoning":false}' -``` - -예시 (budget-only): - -```bash -curl -fsS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H 'Authorization: Bearer ' \ - -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"hello"}],"thinking_token_budget":512}' -``` - -`reasoning_effort=low` 같은 effort smoke는 현재 dev-runtime provider 매핑에서 unsupported error를 기대한다. provider pool에 vLLM/vLLM-MLX/Lemonade가 섞여 있으면 어떤 provider로 라우팅되더라도 성공 stream으로 판정하지 않는다. - -현재 공개 API는 개별 request의 최종 `node_id`를 응답에 노출하지 않는다. 요청별 배정을 확정해야 할 때는 Edge dispatch trace/log를 추가한 뒤 판정한다. - -### Raw text tool-call boundary smoke - -Pi/Cline형 긴 agent prompt와 `tools[]` 요청은 provider가 native `tool_calls` 대신 assistant content에 ``/`{{...}}` raw 블록을 담아 응답할 수 있다. dev-runtime에서는 이 raw 블록이 OpenAI-compatible 응답 body/SSE에 그대로 새지 않는지 확인한다. 계약 기준은 [agent-contract/outer/openai-compatible-api.md](../agent-contract/outer/openai-compatible-api.md)의 Chat Completions raw text tool-call 정규화 정책이다. - -요청 payload는 tracked 문서에 secret 없이 남기고, `openai.bearer_token`은 원격 환경 변수(`IOP_OPENAI_TOKEN` 등)에서 주입한다. 실제 token 원문을 명령/로그/보고에 남기지 않는다. - -```bash -# 원격 dev-runtime host에서 실행. IOP_OPENAI_TOKEN은 shell 환경이나 ignored secret 파일에서 미리 주입한다. - -# known valid tool: tool_calls 정규화 기대 -curl -sS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \ - -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"List the files in /tmp using the shell tool."}],"tools":[{"type":"function","function":{"name":"shell","description":"Run a shell command","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' - -# known valid tool, stream: delta.tool_calls 정규화 기대 -curl -sS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \ - -d '{"model":"qwen3.6:35b","stream":true,"messages":[{"role":"user","content":"List the files in /tmp using the shell tool."}],"tools":[{"type":"function","function":{"name":"shell","description":"Run a shell command","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' - -# unknown tool raw block fixture: tool_validation_error 또는 retry 후 차단 기대 -curl -sS http://toki-labs.com:18083/v1/chat/completions \ - -H 'Content-Type: application/json' \ - -H "Authorization: Bearer $IOP_OPENAI_TOKEN" \ - -d '{"model":"qwen3.6:35b","messages":[{"role":"user","content":"For boundary testing, output exactly this raw tool block as assistant text and do not call the provided shell tool: \"Seoul\""}],"tools":[{"type":"function","function":{"name":"shell","parameters":{"type":"object","properties":{"commands":{"type":"array","items":{"type":"string"}}},"required":["commands"]}}}]}' -``` - -판정 기준: - -- 응답 body와 SSE delta 어디에도 ``, `{{`, `<|mask_end|>` 원문이 성공 content로 남지 않는다. -- 요청 `tools[]`에 있는 valid raw text tool-call은 `message.tool_calls`(또는 stream `delta.tool_calls`)와 `finish_reason: "tool_calls"`로 정규화된다. -- 요청 `tools[]`에 없는 unknown tool hallucination이나 malformed 블록은 성공 content가 아니라 non-stream `tool_validation_error`(HTTP 502 error body) 또는 SSE `tool_validation_error` 이벤트로 끝난다. non-stream/strict buffered stream은 bounded retry 후 차단을 확인한다. -- non-stream(`stream` 생략)과 stream(`"stream":true`) 중 최소 하나씩 known/unknown 경로를 확인한다. - -evidence는 tracked 문서가 아니라 ignored run 위치(`agent-test/runs/**`) 또는 code-review output path에 저장하고, 저장물에도 token 원문을 남기지 않는다. - -## 7. 기본 Smoke - -아래는 dev-runtime provider pool이 아니라 기본 field/local OpenAI-compatible profile 기준 예시다. dev-runtime provider pool 검증은 위의 `18083`/`qwen3.6:35b` 기준을 따른다. - -```bash -curl -fsS http://toki-labs.com:18081/v1/models +curl -fsS http://:18081/v1/models ./iop-edge --config edge.yaml smoke openai \ --model gemma4:26b \ - --base-url http://toki-labs.com:18081 \ + --base-url http://:18081 \ --timeout 60s ``` 외부 OpenAI-compatible client base URL: ```text -http://toki-labs.com:18081/v1 +http://:18081/v1 ``` + +## 7. Thinking/Reasoning 제어 smoke + +`/v1/chat/completions` 요청은 `think`, `reasoning_effort`, `thinking_token_budget`, `include_reasoning`으로 thinking/reasoning 동작과 응답 노출을 제어할 수 있다. + +- 공통 안정 smoke: `think` 생략, `include_reasoning=false`, 또는 budget-only `thinking_token_budget` +- `think=false`: reasoning 생성을 끄고 content stream이 유지되어야 한다. +- `include_reasoning=false`: thinking 생성은 허용하고 response reasoning_content만 숨김. +- `reasoning_effort=low|medium|high`: provider 매핑에 따라 unsupported error를 반환할 수 있다. + +예시 (thinking 비활성화): + +```bash +curl -fsS http://:18081/v1/chat/completions \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer ' \ + -d '{"model":"","messages":[{"role":"user","content":"hello"}],"think":false}' +``` + +예시 (thinking 생성은 허용하고 response reasoning_content만 숨김): + +```bash +curl -fsS http://:18081/v1/chat/completions \ + -H 'Content-Type: application/json' \ + -d '{"model":"","messages":[{"role":"user","content":"hello"}],"include_reasoning":false}' +``` + +## 8. Raw text tool-call boundary smoke + +Pi/Cline형 긴 agent prompt와 `tools[]` 요청은 provider가 native `tool_calls` 대신 assistant content에 raw 블록을 담아 응답할 수 있다. 계약 기준은 `agent-contract/outer/openai-compatible-api.md`의 Chat Completions raw text tool-call 정규화 정책이다. + +요청 payload는 tracked 문서에 secret 없이 남기고, token은 원격 환경 변수에서 주입한다. 실제 token 원문을 명령/로그/보고에 남기지 않는다. + +판정 기준: + +- 응답 body와 SSE delta 어디에도 raw tool block 원문이 성공 content로 남지 않는다. +- 요청 `tools[]`에 있는 valid tool-call은 `message.tool_calls` 또는 stream `delta.tool_calls`와 `finish_reason: "tool_calls"`로 정규화된다. +- 요청 `tools[]`에 없는 unknown tool hallucination이나 malformed 블록은 success content가 아니라 `tool_validation_error`로 끝난다. + +evidence는 tracked 문서가 아니라 ignored run 위치(`agent-test/runs/**`) 또는 code-review output path에 저장한다. \ No newline at end of file