diff --git a/agent-contract/outer/anthropic-compatible-api.md b/agent-contract/outer/anthropic-compatible-api.md index 84507176..7a41cd97 100644 --- a/agent-contract/outer/anthropic-compatible-api.md +++ b/agent-contract/outer/anthropic-compatible-api.md @@ -160,7 +160,10 @@ remains one ingress and produces at most one frozen caller terminal. Cleanup may replace a pending success or length candidate with `error/workspace_cleanup` before publication; after publication, negative endpoint acknowledgement changes internal completion only and cannot write a second terminal. This is the implemented S11 -`error-cancel` boundary; external Claude qualification remains deferred to S12. +`error-cancel` boundary. S12 external Claude qualification passed on an approved IOP +Node with SDK retry and session-title generation disabled: one supervised Claude task +produced ingress `0→1`, Gemini → ornith-fast → Gemini stage order, one `end_turn`, and +the exact expected workspace result without exposing private tool protocol. A streaming marked request uses a separate privacy-closed projector for the same coordinator execution. The projector opens exactly one `message_start` envelope and @@ -223,8 +226,10 @@ observing exactly one `/v1/messages` ingress, one caller-safe terminal, and no p This projector is a service-to-endpoint boundary and does not widen the generic Stream Evidence Gate event/filter/recovery contract. Edge startup installs the composite single-request executor driving the active Plan -> Work -> Review stage pipeline with -generic failure behavior on private stage errors; local deterministic evidence is test-covered, -while actual external Claude qualification remains explicitly deferred to S12 (`claude-smoke`). +generic failure behavior on private stage errors; local deterministic evidence is test-covered. +S12 (`claude-smoke`) also qualifies the external Claude path on an approved IOP Node +with exactly one accepted Messages ingress, the expected Plan → Work → Review stage order, +one `end_turn`, and the exact expected workspace result. Ordinary unmarked Messages routing, Chat behavior, and both count-tokens routes remain unchanged. diff --git a/agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md b/agent-roadmap/archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md similarity index 77% rename from agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md rename to agent-roadmap/archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md index 2e132c14..0bd09c3a 100644 --- a/agent-roadmap/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md +++ b/agent-roadmap/archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md @@ -2,8 +2,8 @@ ## 위치 -- Roadmap: [ROADMAP.md](../../../ROADMAP.md) -- Phase: [PHASE.md](../PHASE.md) +- Roadmap: [ROADMAP.md](../../../../ROADMAP.md) +- Phase: [PHASE.md](../../../../phase/knowledge-tool-optimization-extension/PHASE.md) - SDD: [SDD.md](../../../sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md) ## 목표 @@ -13,7 +13,7 @@ Claude가 IOP의 Anthropic-compatible model을 호출할 때 `/v1/messages` POST ## 상태 -[검토중] +[완료] ## 구현 잠금 @@ -91,13 +91,16 @@ Claude가 IOP의 Anthropic-compatible model을 호출할 때 `/v1/messages` POST ## 완료 리뷰 -- 상태: 검토중 +- 상태: 통과 - 요청일: 2026-08-08 +- 종료일: 2026-08-09 - 완료 근거: 동일 Milestone task group의 canonical PASS `complete.log` 25건과 현재 코드·계약·테스트를 Task id별로 집계해 12개 기능 Task와 SDD S01~S12의 구현·검증 연결을 확인했다. - 완료 근거: `error-cancel`은 request/stage budget·provider/tool timeout·malformed/repetition·disconnect가 추가 ingress나 partial success 없이 단일 오류·취소·length terminal로 수렴하는 matrix/race 검증을 통과했다. -- 완료 근거: `claude-smoke`는 실제 Claude `sole-live-18` 한 번으로 ingress `0→1`, Gemini→ornith-fast→Gemini, stage/total timing, Work write·Review read·cleanup, 정확한 42-byte 결과와 단일 `end_turn`을 redacted manifest로 검증했다. -- 검토 항목: 모든 기능 Task와 SDD Acceptance/Evidence 연결이 충족되었으며 남은 구현·검증 항목은 없다. -- 리뷰 코멘트: `[완료]` 전환과 archive는 별도 Milestone 종료 검토에서 처리한다. +- 완료 근거: [`claude-smoke` canonical PASS](../../../../../agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/25+23,24_claude_smoke_qualification/complete.log)는 실제 Claude `sole-live-18` 한 번으로 ingress `0→1`, Gemini→ornith-fast→Gemini, stage/total timing, Work write·Review read·cleanup, 정확한 42-byte 결과와 단일 `end_turn`을 redacted manifest로 검증했다. +- 계약 동기화: [Anthropic-Compatible Messages API](../../../../../agent-contract/outer/anthropic-compatible-api.md)의 S12 보류 표기를 실제 qualification 완료 상태로 갱신했다. +- Spec 동기화: [Edge-Node Provider Execution](../../../../../agent-spec/runtime/edge-node-execution.md)과 [OpenAI-Compatible 입력 표면](../../../../../agent-spec/input/openai-compatible-surface.md)에 S12 complete-log evidence와 현재 qualification 상태를 반영했다. +- 검토 항목: 모든 기능 Task, SDD Acceptance/Evidence, 계약·Spec 동기화가 충족되었으며 남은 구현·검증·결정 차단 항목은 없다. +- 리뷰 코멘트: 종료 검토를 통과해 `[완료]`로 전환하고 2026-08-09 archive로 이동했다. ## 범위 제외 @@ -110,11 +113,11 @@ Claude가 IOP의 Anthropic-compatible model을 호출할 때 `/v1/messages` POST ## 작업 컨텍스트 - 관련 경로: `apps/edge/internal/openai`, `apps/edge/internal/service`, `apps/node/internal/node`, `apps/node/internal/transport`, `packages/go/config`, `packages/go/streamgate`, `proto/iop`, `configs/edge.yaml` -- 구현 기준선: 완료·아카이빙한 [[route-01] IOP 실행 프리셋과 Hot Path](../../../archive/phase/knowledge-tool-optimization-extension/milestones/iop-hot-path-one-shot-execution.md)의 execution preset/config generation, coordinator, endpoint codec, Stream Evidence Gate, authorization/lease, error·cleanup·observability 기반과 현재 Anthropic↔Gemini Chat bridge를 재사용한다. 과도기 caller tool-result smoke는 이 마일스톤의 선행 차단이 아니며, exact single-request E2E는 이 마일스톤이 직접 검증한다. +- 구현 기준선: 완료·아카이빙한 [[route-01] IOP 실행 프리셋과 Hot Path](iop-hot-path-one-shot-execution.md)의 execution preset/config generation, coordinator, endpoint codec, Stream Evidence Gate, authorization/lease, error·cleanup·observability 기반과 현재 Anthropic↔Gemini Chat bridge를 재사용한다. 과도기 caller tool-result smoke는 이 마일스톤의 선행 차단이 아니며, exact single-request E2E는 이 마일스톤이 직접 검증한다. - 표준선: one-shot의 완료 기준은 logical `request_id`가 아니라 실제 Claude→IOP `/v1/messages` POST count 1이다. - 표준선: request-scoped workspace/tool execution은 IOP Edge와 승인된 IOP Node가 소유하며 외부 Claude tool callback에 의존하지 않는다. -- 큐 배치: 완료·아카이빙된 `[route-01]` 다음인 route lane의 `[route-02]` 2번이며 현재 active lane head다. -- 실행 순서와 차단 관계: [전역 마일스톤 실행 순서](../../../priority-queue.md) -- 후속: [Heavy Plan/Review 실행과 검증 MVP](knowledge-tool-validation-optimization.md), [Execution Preset 하이브리드 Mode 라우팅](openai-compatible-hybrid-request-execution-routing.md) -- 추가 비교 검증: 정식 기능과 `[claude-smoke]` 완료 이후 [[bench-02] IOP 원샷 Agent 모델 비교 벤치마크](iop-one-shot-agent-model-comparison.md)에서 수행하며, [[bench-01] Agent 비교 벤치마크 파이프라인 준비](agent-comparison-benchmark-pipeline.md)는 이 Milestone과 병렬로 진행할 수 있다. 이 비교는 현재 Milestone의 완료 Task나 필수 smoke를 대체하지 않는다. +- 큐 배치: 완료·아카이빙된 `[route-01]` 다음인 route lane의 `[route-02]` 2번으로 실행했으며, 완료 archive와 함께 active queue에서 제거했다. +- 실행 순서와 차단 관계: [전역 마일스톤 실행 순서](../../../../priority-queue.md) +- 후속: [Heavy Plan/Review 실행과 검증 MVP](../../../../phase/knowledge-tool-optimization-extension/milestones/knowledge-tool-validation-optimization.md), [Execution Preset 하이브리드 Mode 라우팅](../../../../phase/knowledge-tool-optimization-extension/milestones/openai-compatible-hybrid-request-execution-routing.md) +- 추가 비교 검증: 정식 기능과 `[claude-smoke]` 완료 이후 [[bench-02] IOP 원샷 Agent 모델 비교 벤치마크](../../../../phase/knowledge-tool-optimization-extension/milestones/iop-one-shot-agent-model-comparison.md)에서 수행하며, [[bench-01] Agent 비교 벤치마크 파이프라인 준비](../../../../phase/knowledge-tool-optimization-extension/milestones/agent-comparison-benchmark-pipeline.md)는 이 Milestone과 병렬로 진행할 수 있다. 이 비교는 현재 Milestone의 완료 Task나 필수 smoke를 대체하지 않는다. - 확인 필요: 없음 diff --git a/agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md b/agent-roadmap/archive/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md similarity index 93% rename from agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md rename to agent-roadmap/archive/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md index 82edf3b9..663aef3c 100644 --- a/agent-roadmap/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md +++ b/agent-roadmap/archive/sdd/knowledge-tool-optimization-extension/iop-owned-single-request-agent-execution/SDD.md @@ -2,8 +2,8 @@ ## 위치 -- Milestone: [IOP 단일 요청 Agent 실행](../../../phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) -- Phase: [PHASE.md](../../../phase/knowledge-tool-optimization-extension/PHASE.md) +- Milestone: [IOP 단일 요청 Agent 실행](../../../../archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) +- Phase: [PHASE.md](../../../../phase/knowledge-tool-optimization-extension/PHASE.md) ## 상태 @@ -38,13 +38,13 @@ | 영역 | 기준 | 메모 | |------|------|------| -| Roadmap | [Milestone 문서](../../../phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) | 목표, Task와 완료 상태 원장 | +| Roadmap | [Milestone 문서](../../../../archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) | 목표, Task와 완료 상태 원장 | | Edge Runtime | `apps/edge/internal/openai`, `apps/edge/internal/service` | single ingress, coordinator, stage dispatch, Anthropic outer stream | | Node Runtime | `apps/node/internal/node`, `apps/node/internal/transport`와 전용 workspace executor | 승인된 IOP Node의 request-scoped workspace/tool 실행; provider execution runtime과 분리 | | Config/Wire | `packages/go/config`, `proto/iop`, `configs/edge.yaml` | 새 preset model/workspace reference와 전용 Edge-Node tool request/result 계약의 구현 원본 | | Stream Runtime | `packages/go/streamgate` | internal terminal hold, repetition/no-progress와 final commit | -| API Contract | [Anthropic-Compatible Messages API](../../../../agent-contract/outer/anthropic-compatible-api.md) | 외부 단일 Messages request/stream/error 계약 | -| Runtime Contract | [Edge-Node Runtime Wire](../../../../agent-contract/inner/edge-node-runtime-wire.md) | 현재 provider wire 기준; 전용 workspace tool wire 구현 시 함께 갱신 | +| API Contract | [Anthropic-Compatible Messages API](../../../../../agent-contract/outer/anthropic-compatible-api.md) | 외부 단일 Messages request/stream/error 계약 | +| Runtime Contract | [Edge-Node Runtime Wire](../../../../../agent-contract/inner/edge-node-runtime-wire.md) | 현재 provider wire 기준; 전용 workspace tool wire 구현 시 함께 갱신 | | User Decision | D01-D10 | 2026-08-05 최종 합의와 2026-08-08 플랫폼 중립화 결정, 기존 provider/runtime 계약에 따른 책임 분리 | ## State Machine @@ -74,7 +74,7 @@ State invariant: ## Interface Contract -- 계약 원문: [Anthropic-Compatible Messages API](../../../../agent-contract/outer/anthropic-compatible-api.md), [Edge Config And Runtime Refresh](../../../../agent-contract/inner/edge-config-runtime-refresh.md), [Edge-Node Runtime Wire](../../../../agent-contract/inner/edge-node-runtime-wire.md) +- 계약 원문: [Anthropic-Compatible Messages API](../../../../../agent-contract/outer/anthropic-compatible-api.md), [Edge Config And Runtime Refresh](../../../../../agent-contract/inner/edge-config-runtime-refresh.md), [Edge-Node Runtime Wire](../../../../../agent-contract/inner/edge-node-runtime-wire.md) - 외부 입력: - `POST /v1/messages`: Claude가 보내는 유일한 작업 ingress다. - `model`: fixed `light` single-request execution preset에 매핑되는 public model id다. @@ -158,4 +158,4 @@ State invariant: - 표준선: 기존 Anthropic bridge, provider-pool authorization/lease, Stream Evidence Gate와 Edge-Node transport를 재사용하되 caller tool continuation을 one-shot 내부 tool runtime으로 대체한다. - 구현 순서: preset/workspace config → Edge-Node tool wire와 Node executor → single-request coordinator → plan/work/review stage → stream/error/cleanup → actual Claude smoke. -- 후속 SDD: [Heavy Plan/Review 실행과 검증 MVP](../../../phase/knowledge-tool-optimization-extension/milestones/knowledge-tool-validation-optimization.md) +- 후속 SDD: [Heavy Plan/Review 실행과 검증 MVP](../../../../phase/knowledge-tool-optimization-extension/milestones/knowledge-tool-validation-optimization.md) diff --git a/agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md b/agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md index 95c9c022..701f645c 100644 --- a/agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md +++ b/agent-roadmap/phase/knowledge-tool-optimization-extension/PHASE.md @@ -49,9 +49,9 @@ Phase를 가로지르는 실제 다음 작업 선택은 [전역 마일스톤 실 - 경로: [[output-02] OpenAI-compatible Incomplete Tool Call Syntax Gate](milestones/openai-compatible-incomplete-tool-call-syntax-gate.md) - 요약: terminal provider 응답에서 완성된 tool call 수와 raw/reasoning/content tool-call marker scanner 결과가 불일치하는 케이스를 runtime에서 deterministic하게 판정해 incomplete tool-call syntax로 분류한다. -- [검토중] [route-02] IOP 단일 요청 Agent 실행 - - 경로: [[route-02] IOP 단일 요청 Agent 실행](milestones/iop-owned-single-request-agent-execution.md) - - 요약: Claude→IOP `/v1/messages` POST를 정확히 1회로 고정하고, 승인된 IOP Node의 request-scoped workspace/tool executor로 Gemini 3.6 Flash high plan → ornith-fast work → Gemini 3.6 Flash high review/repair를 내부에서 끝낸 뒤 하나의 outer stream과 terminal을 반환한다. +- [완료] [route-02] IOP 단일 요청 Agent 실행 + - 경로: [[route-02] IOP 단일 요청 Agent 실행](../../archive/phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) + - 요약: Claude→IOP `/v1/messages` POST를 정확히 1회로 고정하고 승인된 IOP Node의 request-scoped workspace/tool executor로 Gemini plan → ornith-fast work → Gemini review/repair를 내부에서 완료했다. 실제 Claude S12 smoke에서 ingress 1회, 단일 terminal, 정확한 workspace 결과와 cleanup을 검증했다. - [계획] [bench-01] Agent 비교 벤치마크 파이프라인 준비 - 경로: [[bench-01] Agent 비교 벤치마크 파이프라인 준비](milestones/agent-comparison-benchmark-pipeline.md) diff --git a/agent-roadmap/priority-queue.md b/agent-roadmap/priority-queue.md index 3b50ef38..322b307c 100644 --- a/agent-roadmap/priority-queue.md +++ b/agent-roadmap/priority-queue.md @@ -6,9 +6,6 @@ ### route -2. [[route-02] IOP 단일 요청 Agent 실행](phase/knowledge-tool-optimization-extension/milestones/iop-owned-single-request-agent-execution.md) - Claude의 Anthropic Messages 요청 정확히 1회 안에서 승인된 IOP Node가 request-scoped workspace와 도구 실행을 소유하고 Gemini 3.6 Flash high plan → ornith-fast work → Gemini 3.6 Flash high review/repair를 하나의 응답으로 완료한다. - 3. [[route-03] Heavy Plan/Review 실행과 검증 MVP](phase/knowledge-tool-optimization-extension/milestones/knowledge-tool-validation-optimization.md) Hot Path의 lightweight Plan/Review를 장기 작업용 `heavy` mode로 확장해 `heavy-only` preset에서 재계획·검증·review/repair·resume 경계를 먼저 검증한다. @@ -26,13 +23,11 @@ 2. [[bench-02] IOP 원샷 Agent 모델 비교 벤치마크](phase/knowledge-tool-optimization-extension/milestones/iop-one-shot-agent-model-comparison.md) `[route-02]` 정식 smoke 뒤 동일 정적 웹 fixture로 Sonnet/Gemini/GPT 단독과 Gemini/GPT 하이브리드의 9개 IOP 경유 조합을 각각 한 번 비교한다. - - 선행 차단: `[route-02]` ### output 1. [[output-01] OpenAI-compatible 출력 검증 필터](phase/knowledge-tool-optimization-extension/milestones/openai-compatible-output-validation-filters.md) 실제 의미 필터 전에 deterministic diagnostic mock으로 실제 Stream Evidence Gate의 pass·observe-only·blocking recovery를 관측하는 smoke를 통과시키고, OpenAI-compatible single-stream 반복과 incoming request history에 누적된 assistant 반복, JSON contract 검증/repair 경로를 안정화한다. - - 동시 차단: `[route-02]` 2. [[output-02] OpenAI-compatible Incomplete Tool Call Syntax Gate](phase/knowledge-tool-optimization-extension/milestones/openai-compatible-incomplete-tool-call-syntax-gate.md) terminal provider 응답의 incomplete tool-call syntax를 deterministic하게 판정한다. diff --git a/agent-spec/input/openai-compatible-surface.md b/agent-spec/input/openai-compatible-surface.md index ab5dfe51..71768736 100644 --- a/agent-spec/input/openai-compatible-surface.md +++ b/agent-spec/input/openai-compatible-surface.md @@ -144,6 +144,12 @@ source_evidence: - type: test path: apps/edge/internal/openai/single_request_quality_gate_test.go notes: S11 timeout, budget, repetition, malformed, context, length, cancel, and tool terminal evidence + - type: test + path: scripts/e2e-single-request-claude.sh + notes: Supervised external Claude qualification harness with retry and session-title generation disabled + - type: complete-log + path: agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/25+23,24_claude_smoke_qualification/complete.log + notes: S12 one-ingress Claude qualification with expected stage order, terminal, workspace output, timing, cleanup, and redacted evidence --- # 스펙: OpenAI-Compatible 입력 표면 @@ -165,7 +171,7 @@ Edge가 OpenAI-compatible HTTP 요청을 받아 내부 `adapter + target` 실행 | marked preset single-request admission | An authorized fixed single-request preset compiles one service-owned admission value at request start: requested public model, canonical plan/work/review bindings resolved through managed authorization, opaque workspace capability, and absolute resource caps. Later refresh cannot mutate the admitted shape. No private binding is echoed to the caller. Compiled only after every canonical reference is verified through its catalog binding for the authenticated principal; missing, duplicate, unauthorized, dynamically selected, or option-inconsistent inputs are rejected without fallback. | | marked single-request ingress | One validated and authorized Messages POST enters the separate service coordinator capability before legacy provider/caller continuation and increments `iop_anthropic_single_request_ingress_total` once. Non-streaming returns one buffered final-only message. Streaming keeps one envelope across the coordinator lifetime, exposes only fixed plan/work/review/repair text blocks plus `event: ping`, and commits one final text/error terminal. Internal reasoning/tool wire never becomes caller `tool_use`; success is acknowledged only after the complete terminal write succeeds. | | marked single-request S11 terminal policy | The service freezes one closed `end_turn`, `length`, `error`, or `cancelled` disposition. `error` classes are provider, validation, timeout, budget, repetition, malformed, context, internal-tool, and workspace-cleanup. Buffered and SSE share one projection: `end_turn`; `max_tokens` with no private partial output; `400 invalid_request_error` for validation/context; `502 api_error` for other failures; and silent cancellation after caller disconnect. No terminal classification retries, falls back, opens a second request, or later writes success. | -| marked single-request observation evidence | A single real POST links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation without public tool protocol. `iop_anthropic_single_request_ingress_total` is unlabeled (no request_id, stage_id, provider identity, or content). Internal tool names, raw arguments, private results, and workspace references are absent from the public terminal and from log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here; actual Claude timing evidence on an approved IOP Node is explicitly deferred to `claude-smoke` (SDD S12). | +| marked single-request observation evidence | A single real POST links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation without public tool protocol. `iop_anthropic_single_request_ingress_total` is unlabeled (no request_id, stage_id, provider identity, or content). Internal tool names, raw arguments, private results, and workspace references are absent from the public terminal and from log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here. SDD S12 qualified an external Claude task on an approved IOP Node with ingress `0→1`, Gemini → ornith-fast → Gemini, one `end_turn`, the exact expected workspace result, timing, cleanup, and redacted evidence. | | marked internal workspace tool loop | The service accepts only closed read/list/write/delete/command calls from the saved internal stage, opens the admitted Node workspace once, executes calls sequentially on the frozen connection generation, correlates one result to one unique request/stage/tool identity, and resumes only through the emitting executor's optional continuation. Strict decoding, capability checks, cumulative per-stage iteration/output/deadline limits, request wall-clock budget, and typed cancellation fail closed without fallback or another Messages request. | | managed provider credential | After candidate selection, Edge obtains a short-lived Node-targeted lease on the authenticated CP connection, fences it immediately before send, and never accepts caller provider credentials or same-model slot fallback. | | legacy provider auth forwarding | Only when managed mode is disabled, `openai.provider_auth` can read a raw provider token from the configured caller header and forward it to the selected provider. | @@ -255,7 +261,7 @@ sequenceDiagram - provider-pool model group은 capacity + priority + availability 기준으로 provider candidate를 먼저 선택하고, 선택된 provider가 OpenAI-compatible 호출 방식을 지원하면 raw tunnel passthrough로 dispatch한다. Ollama/native provider가 선택되면 normalized `RunRequest` path로 dispatch한다. - Anthropic Messages and count-tokens do not use legacy direct-route or single-target fallback. Native responses preserve provider status, allowed headers, and body/SSE bytes; bridge responses are converted between Anthropic Messages and Chat Completions shapes. - A marked single-request Messages dispatch requires the narrow service coordinator capability and never falls back to the generic provider pool. The handler copies the immutable binding and request input and counts the accepted HTTP admission once with no labels. The service projects exactly one frozen terminal candidate through both response modes: buffered/SSE `end_turn`; buffered/SSE `max_tokens` without private partial content; `invalid_request_error` for validation/context; `api_error` for provider, timeout, budget, repetition, malformed, internal-tool, and workspace-cleanup failures; or silent cancellation after caller disconnect. The streaming path maps only fixed plan/work/review/repair summaries, serializes pings and monotonic text-block indices with one terminal owner, stops and joins liveness before terminal/return, and acknowledges completion only after `message_stop`. Arbitrary progress, reasoning, tool/provider/credential/workspace data, raw failures, and internal stage terminals stay private. No classified terminal triggers retry, fallback, partial success, a second request, or a later success terminal. Count-tokens does not enter or increment this path. -- Marked single-request observation evidence links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation for one real POST. `iop_anthropic_single_request_ingress_total` is strictly unlabeled: no request_id, stage_id, provider identity, content, or workspace reference appears as a metric label. Internal tool names (`workspace_read`, `workspace_write`, etc.), raw arguments, private results, and workspace references are absent from the public terminal JSON and from log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here. Actual Claude timing evidence on an approved IOP Node is explicitly deferred to `claude-smoke` (SDD S12); deterministic coordinator/tool-loop tests do not imply external qualification. +- Marked single-request observation evidence links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation for one real POST. `iop_anthropic_single_request_ingress_total` is strictly unlabeled: no request_id, stage_id, provider identity, content, or workspace reference appears as a metric label. Internal tool names (`workspace_read`, `workspace_write`, etc.), raw arguments, private results, and workspace references are absent from the public terminal JSON and from log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here. SDD S12 qualifies the external Claude path on an approved IOP Node with one accepted ingress, the expected stage sequence, one terminal, exact output, timing, cleanup, and redacted evidence. - Internal workspace calls use a service-owned schema independent of caller-facing tool codecs. The five closed operation names decode into typed Node requests only after request/stage/tool identity, canonical relative path, approved operation/command/environment capability, and immutable budget checks. The loop opens once, preserves the admitted connection generation, executes one pending call at a time, accepts only correlated typed results, and returns a deep-copied raw-free result to the same executor continuation. Repeated IDs, stale responses, malformed or denied input, timeout, output/iteration exhaustion, and cancellation never become public Anthropic tool protocol or trigger a second ingress. - Claude Code Messages requests may use adaptive thinking, `output_config.effort`, structured output, cache-control annotations, and supported beta headers. The Chat bridge consumes those headers, maps supported fields, and requires callers to replay opaque `tool_use.id` values unchanged so Gemini thought signatures can be restored on tool-result turns. - provider capacity와 long-context slot은 model alias별이 아니라 `node_id + provider_id`별로 공유한다. queue pending 상한과 timeout은 Edge root `provider_pool` policy이며, lease 반환·refresh·disconnect/reconnect가 모든 model group waiter를 global enqueue 순서로 재평가한다. @@ -312,11 +318,12 @@ sequenceDiagram - Grafana guide는 metric 조회와 operator-managed price baseline 예시이며 live cloud pricing, billing, chargeback, long-term ledger, 사용자별 제한 enforcement의 source of truth가 아니다. - Seulgivibe Claude/OpenAI proxy는 별도 OpenAI-compatible provider family label로 보존될 수 있지만, HTTP body shape는 provider tunnel passthrough 경계를 따른다. - Anthropic metrics are not inferred from native responses or tunnel frames; adding them requires a separate runtime change. -- The marked single-request SSE projector and private workspace continuation do not add event kinds, filters, release rules, or recovery behavior to the generic Stream Evidence Gate. The active Plan -> Work -> Review composite and request-artifact cleanup use the closed S11 `error-cancel`/length policy with deterministic local evidence. S11 is implemented; only actual external Claude qualification remains explicitly deferred to S12 (`claude-smoke`). +- The marked single-request SSE projector and private workspace continuation do not add event kinds, filters, release rules, or recovery behavior to the generic Stream Evidence Gate. The active Plan -> Work -> Review composite and request-artifact cleanup use the closed S11 `error-cancel`/length policy with deterministic local evidence. S11 is implemented, and S12 (`claude-smoke`) qualifies the external Claude path on an approved IOP Node. - Managed API-key profiles qualify end to end: the Control Plane canonicalizes the resolved auth header (for example lowercase `x-api-key` to `X-Api-Key`) before signing the lease scope, so lease issuance and consumption succeed and the Node injects only that exact header upstream. A lease failure fails closed with a sanitized provider-dispatch error and no Node/upstream call, never a fallback to a bearer slot or caller auth. This outbound provider-auth canonicalization is separate from inbound IOP `X-Api-Key`/Bearer caller-auth equivalence. ## 변경 기록 +- 2026-08-09: Qualified S12 `claude-smoke` with one supervised external Claude task on an approved IOP Node: ingress `0→1`, Gemini → ornith-fast → Gemini, one `end_turn`, exact 42-byte workspace output, timing, cleanup, and redacted evidence. - 2026-08-07: Implemented and documented S11 `error-cancel`: one closed service terminal disposition, request-local typed failure/no-progress classification, shared buffered/SSE `end_turn`/`max_tokens`/`invalid_request_error`/`api_error` mapping, silent disconnect, private-partial suppression, and deterministic one-ingress/one-terminal/no-second-request evidence. S12 external qualification remains pending. - 2026-07-07: 현재 코드와 OpenAI-compatible 계약 기준으로 bootstrap spec 작성. - 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리. diff --git a/agent-spec/runtime/edge-node-execution.md b/agent-spec/runtime/edge-node-execution.md index 4b91ce15..05e50e7f 100644 --- a/agent-spec/runtime/edge-node-execution.md +++ b/agent-spec/runtime/edge-node-execution.md @@ -189,6 +189,9 @@ source_evidence: - type: test path: apps/edge/internal/openai/liveness_recovery_observability_test.go notes: Deterministic S06 OpenAI recovery observation regression with closed label values + - type: complete-log + path: agent-task/archive/2026/08/m-iop-owned-single-request-agent-execution/25+23,24_claude_smoke_qualification/complete.log + notes: S12 external Claude qualification with one ingress, Gemini to ornith-fast to Gemini stage order, exact workspace result, one end_turn, timing, cleanup, and redacted evidence --- # Edge-Node Provider Execution @@ -207,7 +210,7 @@ The shared `packages/go/execution` package contains provider lifecycle, registry | normalized execution | `adapter + target`으로 provider 실행을 선택하고 ordered `RunEvent` stream을 반환한다. | | single-request coordinator | Immutable admission과 closed stage envelope을 service-owned state graph (`accepted`, `planning`, `working`, `reviewing`, `repairing`, `internal_tool`, `finalizing`, `completed`, `failed`, `cancelled`)로 처리한다. An internal tool result can resume only its saved stage. After a successful workspace open, every terminal path waits for one cleanup before the finalizing candidate can reach surface acknowledgement. | | single-request S11 terminal policy | One validated, copy-safe terminal disposition is frozen across envelope/result/progress with kinds `end_turn`, `length`, `error`, and `cancelled`. Error classes are `provider`, `validation`, `timeout`, `budget`, `repetition`, `malformed`, `context`, `internal_tool`, and `workspace_cleanup`. Cleanup can replace a pending success/length before publication; no acknowledgement race can publish a second terminal. | -| single-request observation evidence | Stage-pure timing, tool/cleanup/total counts, cardinality-bounded labels, Node logs, and raw-free correlation are documented for the single-request path. `iop_anthropic_single_request_ingress_total` is strictly unlabeled. A separate `edge_single_request_terminal_rejection` event projects only the fixed terminal kind/error class and HTTP status, so `malformed` and `validation` remain distinguishable without raw model output. The private stage decoder accepts and discards only bounded standard Chat Completions `usage` bookkeeping and optional string `message.reasoning_content`; neither enters stage results or artifacts, while non-string reasoning and unknown envelope members fail closed. Gemini Plan/Review also admit only exact non-empty `extra_content.google.thought_signature`; terminal signatures are discarded and a Review tool-call signature is replayed only in the matching request-local Gemini continuation. Work, artifacts, results, and observations never retain it. The Claude qualification child disables SDK retry and session-title generation so only the actual task can consume ingress. Internal tool names, raw arguments, and private results are absent from public output and log projections. Actual Claude timing evidence on an approved IOP Node is explicitly deferred to `claude-smoke` (SDD S12); deterministic coordinator/tool-loop tests do not imply external qualification. | +| single-request observation evidence | Stage-pure timing, tool/cleanup/total counts, cardinality-bounded labels, Node logs, and raw-free correlation are documented for the single-request path. `iop_anthropic_single_request_ingress_total` is strictly unlabeled. A separate `edge_single_request_terminal_rejection` event projects only the fixed terminal kind/error class and HTTP status, so `malformed` and `validation` remain distinguishable without raw model output. The private stage decoder accepts and discards only bounded standard Chat Completions `usage` bookkeeping and optional string `message.reasoning_content`; neither enters stage results or artifacts, while non-string reasoning and unknown envelope members fail closed. Gemini Plan/Review also admit only exact non-empty `extra_content.google.thought_signature`; terminal signatures are discarded and a Review tool-call signature is replayed only in the matching request-local Gemini continuation. Work, artifacts, results, and observations never retain it. The Claude qualification child disables SDK retry and session-title generation so only the actual task can consume ingress. Internal tool names, raw arguments, and private results are absent from public output and log projections. SDD S12 qualified the external Claude path on an approved IOP Node with ingress `0→1`, Gemini → ornith-fast → Gemini stage order, one `end_turn`, the exact expected workspace result, timing, cleanup, and redacted evidence. | | workspace admission | An opaque `workspace_ref` resolves only through the configured Node catalog. Edge freezes the exact configured owner, dispatch-ready connection generation, closed operation/command/environment-name capabilities, and effective limits before executor startup; unavailable, foreign, pending, malformed, and stale candidates fail closed without fallback or reselection. | | workspace runtime wire | The dedicated `WorkspaceOpen`/`Tool`/`Artifact`/`Cancel`/`Cleanup` request-response families carry immutable coordinator identities and closed status/error codes. `WorkspaceArtifact` admits only enum-selected `PLAN`/`REVIEW` and `READ`/`WRITE`; it carries no relative path. Edge overwrites open capabilities with frozen request authority; Node copies only catalog-subset operations/command ids and equal-or-lower effective limits. | | workspace tool executor | A validated `darwin|linux` Node catalog owns opened root and directory handles only when every entry platform matches the host exactly. Windows, unknown hosts, and cross-platform catalogs fail before root open; empty catalogs remain compatible. Go 1.24-compatible no-follow file primitives provide bounded read, bounded list, structured write, and non-recursive delete. Exact operator-owned command templates run through an inherited-root `fchdir`/`exec` shim with minimal allowlisted environment, shared stdout/stderr bounds, process-group timeout/cancel, and stable typed results. OS is runtime evidence rather than a caller-visible selector. | @@ -236,11 +239,11 @@ The shared `packages/go/execution` package contains provider lifecycle, registry - provider usage, capacity, queue pressure, lifecycle, reconnect, tool calling은 Edge-Node 실행 경로에서 계속 지원한다. - single-request coordinator owns the service-level workspace admission described above as well as executor envelope privacy and the service-owned state graph. It exposes no workspace root, command executable/template/arguments, or environment values to the coordinator-facing binding. - The request-local single-request quality gate classifies provider/tool timeouts, exhausted stage/request budgets, first proven repeated action/result no-progress, malformed calls/results, context/output limits, cancellation, internal-tool failures, and workspace cleanup into the closed terminal vocabulary. It retains only fixed hashes for repetition evidence and never retries, reselects, falls back, exposes a partial success, or starts a second request after classification. -- The service freezes the first public terminal candidate. Legacy successful results normalize to `end_turn`; output limits produce `length`; caller disconnect produces silent `cancelled`; validation/context become `invalid_request_error`; other errors become `api_error`. Buffered and SSE projectors share that policy, emit at most one terminal, and never expose private partial stage content for `length`. This completes deterministic S11 `error-cancel` evidence without changing the Edge-Node protobuf wire. S12 external Claude qualification on an approved IOP Node remains pending. +- The service freezes the first public terminal candidate. Legacy successful results normalize to `end_turn`; output limits produce `length`; caller disconnect produces silent `cancelled`; validation/context become `invalid_request_error`; other errors become `api_error`. Buffered and SSE projectors share that policy, emit at most one terminal, and never expose private partial stage content for `length`. This completes deterministic S11 `error-cancel` evidence without changing the Edge-Node protobuf wire. S12 external Claude qualification on an approved IOP Node is complete. - The request-local internal tool loop is implemented between the coordinator and the dedicated workspace wire. Strict decode and capability checks happen before wire effects; Node results are accepted only for the one pending call and return only bounded typed fields to the same optional executor continuation. Repeated or stale identities, malformed/denied calls, exhausted immutable budgets, and cancellation terminate internally without selecting another Node or involving the HTTP caller. - Request-owned plan and review artifact access is implemented between the controller and the same dedicated workspace wire. Only `SingleRequestArtifactPlan` and `SingleRequestArtifactReview` are accepted. Artifact and model-tool callers share one serialized open attempt and the same opened cleanup gate; terminal and cancellation paths wait for in-flight artifact work before issuing exactly one cleanup. Edge bounds writes before dispatch and reads before acceptance, validates the echoed kind/operation and canonical terminal, and never reselects after a generation mismatch. Node maps the closed selectors to `plan.md` and `review.md`, validates the inventoried parent/file identity with descriptor-relative no-follow reads, and never grants the public workspace tool surface access to `.iop`. - The private Plan stage is installed in the composite single-request executor at Edge input startup (`apps/edge/internal/input/manager.go`). Its provider codec accepts only frozen Plan options and selected dispatch facts, uses the admitted stage deadline and exact output limit, accepts only `RESPONSE_START`, zero or more `BODY`, then `END`, and projects all provider failures to a generic internal failure. The fixed Plan prompt and Edge-owned OpenAI `json_schema` response format request exactly `plan` and `verification`; caller/config options cannot override the format, and the strict parser retains the semantic nonempty/exact-field boundary before the closed PLAN artifact is written. -- The private Work stage is installed in the composite single-request executor at Edge input startup (`apps/edge/internal/input/manager.go`). It reads only `SingleRequestArtifactPlan`, retains only request/stage/tool identifiers while waiting for the coordinator-owned continuation, and sends no `reasoning_effort` field in an initial or resumed provider request. Its provider messages contain the immutable task, PLAN, admitted tool schemas, and bounded typed tool results; Review/repair and composite installation are active, while external Claude qualification remains explicitly deferred to S12 (`claude-smoke`). +- The private Work stage is installed in the composite single-request executor at Edge input startup (`apps/edge/internal/input/manager.go`). It reads only `SingleRequestArtifactPlan`, retains only request/stage/tool identifiers while waiting for the coordinator-owned continuation, and sends no `reasoning_effort` field in an initial or resumed provider request. Its provider messages contain the immutable task, PLAN, admitted tool schemas, and bounded typed tool results; Review/repair and composite installation are active, and S12 (`claude-smoke`) qualifies the external Claude path. - The Node-private workspace request/result wire is implemented, including catalog delivery, parser registration, optional handler behavior, stable typed failures, generation-fenced dispatch, context-cancel propagation, and request cleanup. Before ready, a non-empty catalog requires a supported `darwin|linux` host and exact entry/host matching before any root open; unsupported and cross-platform catalogs fail closed while empty catalogs remain compatible. The Node installs the workspace handler before ready and cleans active requests before closing workspace authority ahead of session/store teardown. Request authority is immutable and request-local. File operations reserve `.iop`, reject symlink/mount/replaced-parent/special-file paths before effects, process bounded list batches with deterministic truncation, and use a same-parent structured write. Command execution resolves only admitted ids to fixed templates, enters the already-opened root descriptor through `fchdir`, provides only allowlisted environment entries, shares one output cap across drained stdout/stderr, and owns the complete process group through exit, timeout, context cancel, exact request/tool cancel, or request cleanup. - managed mode는 등록과 dispatch 전에 CA로 검증된 Edge/Node workload identity를 요구한다. - revoked, disabled, expired, stale, replayed, wrong-recipient, mismatched lease는 provider나 credential fallback 없이 fail closed한다. @@ -249,7 +252,7 @@ IOP no longer provides persistent shell sessions, terminal emulation, process re The current spec maps reviewed Node and Edge observability producers to S06 behavior and deterministic tests. Node exposes bounded stall counters/histograms and dedicated structured logs with closed label values and raw-payload exclusion. Edge service queue exposes bounded overlay evidence/transition counters and dedicated structured logs with closed label values and identity exclusion. Edge OpenAI server exposes bounded eligibility/results counters and dedicated structured logs with closed label values and identifier exclusion. All projections are local observations and do not widen the wire protocol. -Single-request lifecycle observation evidence links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation for one real POST. `iop_anthropic_single_request_ingress_total` is strictly unlabeled (no request_id, stage_id, provider identity, content, or workspace reference). Internal tool names, raw arguments, and private results are absent from public output and log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here. Actual Claude timing evidence on an approved IOP Node is explicitly deferred to `claude-smoke` (SDD S12); deterministic coordinator/tool-loop tests do not imply external qualification. +Single-request lifecycle observation evidence links ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, and raw-free correlation for one real POST. `iop_anthropic_single_request_ingress_total` is strictly unlabeled (no request_id, stage_id, provider identity, content, or workspace reference). Internal tool names, raw arguments, and private results are absent from public output and log projections. Stage-pure timing, cardinality-bounded labels, and privacy semantics are documented here. SDD S12 external Claude qualification on an approved IOP Node confirms the same boundary with ingress `0→1`, Gemini → ornith-fast → Gemini, one `end_turn`, the exact expected workspace result, timing, cleanup, and redacted evidence. ## 주요 흐름 @@ -341,11 +344,12 @@ Heartbeat interval/wait는 protobuf field가 아닌 양쪽 transport 구현의 l - Node retry and `recovery_eligible` remain prohibited. Hard deadline and connection disconnect continue to take precedence over a simultaneous stall timer. - Operational projections never widen the wire protocol; they carry no new frame, field, ordering rule, or retry semantic. - Workspace admission and the private wire both fence the exact ready connection generation. The wire never exposes workspace fields through provider `RunRequest`, `NodeCommand`, or public API output. The executor exposes no caller access to `.iop`; only request-owned internal runtime code can derive and inventory `.iop/job/`. Structured write input is required for WRITE, while legacy content-only input remains rejected. COMMAND is non-interactive and has no shell, PTY, arbitrary argv, ambient environment, path-based cwd lookup, or persistent process ownership. Cleanup never rolls back or deletes user-requested workspace results. -- The composite single-request executor is installed at Edge input startup (`apps/edge/internal/input/manager.go`), wiring the active Plan -> Work -> Review stage pipeline for single-request execution. Private stage outcomes use the implemented closed S11 terminal policy and stop without retry/fallback or a second request. Deterministic local activation and terminal evidence are proven, while actual external Claude qualification remains explicitly deferred to S12 (`claude-smoke`). -- Single-request observation evidence (ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, raw-free correlation) is documented and tested. `iop_anthropic_single_request_ingress_total` is strictly unlabeled. Internal tool names, raw arguments, and private results are absent from public output and log projections. Actual Claude timing evidence on an approved IOP Node is explicitly deferred to `claude-smoke` (SDD S12); deterministic coordinator/tool-loop tests do not imply external qualification. +- The composite single-request executor is installed at Edge input startup (`apps/edge/internal/input/manager.go`), wiring the active Plan -> Work -> Review stage pipeline for single-request execution. Private stage outcomes use the implemented closed S11 terminal policy and stop without retry/fallback or a second request. Deterministic local activation and terminal evidence are proven, and S12 (`claude-smoke`) qualifies the external Claude path on an approved IOP Node. +- Single-request observation evidence (ingress=1, request-total=1, terminal=1, stage/tool/cleanup counts, raw-free correlation) is documented and tested. `iop_anthropic_single_request_ingress_total` is strictly unlabeled. Internal tool names, raw arguments, and private results are absent from public output and log projections. SDD S12 confirms the external path with one ingress, the expected stage sequence, one terminal, exact output, timing, cleanup, and redacted evidence. ## 변경 기록 +- 2026-08-09: Qualified S12 `claude-smoke` on an approved IOP Node. One supervised Claude task with SDK retry and session-title generation disabled produced ingress `0→1`, Gemini → ornith-fast → Gemini, one `end_turn`, the exact 42-byte workspace result, stage/total timing, cleanup, and redacted evidence. - 2026-08-08: Expanded workspace runtime admission to the closed `darwin|linux` implementation set with exact catalog/host matching before root open, preserved empty-catalog compatibility, and kept Windows/unknown hosts fail-closed. - 2026-08-07: Implemented the S11 `error-cancel` boundary: one frozen service terminal disposition, request-local typed stage classification, fixed-hash repetition/no-progress detection, shared buffered/SSE Anthropic mapping, silent disconnect cancellation, private-partial suppression for `max_tokens`, and deterministic one-ingress/one-terminal/no-second-request evidence. The Edge-Node protobuf wire is unchanged and S12 remains pending. - 2026-08-07: Installed the composite single-request executor at Edge input startup (`apps/edge/internal/input/manager.go`), activating the Plan -> Work -> Review stage pipeline. Production construction evidence is test-covered (`apps/edge/internal/input/manager_test.go`), while actual Claude/Mac external qualification remains explicitly deferred to S12 (`claude-smoke`).