iop/agent-roadmap/sdd/knowledge-tool-optimization-extension/stream-evidence-gate-core/SDD.md
toki c90bb755a9 feat: streamline plan/code-review/finalize router, add stream gate SDDs, sync dev-test inventory, update roadmap milestones
- Refactor plan, code-review, finalize-task-routing, refine-local-plans, router skills
- Add agent-workflow-loop-orchestration skill and plan agent configs
- Update roadmap: knowledge-tool-optimization milestones, stream-evidence-gate-core SDD
- Add stream-evidence-gate-core task, archive, and Go streamgate package
- Update dev-test inventory (edge/node smoke), agent-contract, edge-local-dev-guide
- Deprecate USER_REVIEW for output-validation-filters SDD
2026-07-24 15:11:00 +09:00

46 KiB

SDD: Stream Evidence Gate Core

위치

상태

[승인됨]

SDD 잠금

  • 상태: 해제
  • 사용자 리뷰: 없음
  • 잠금 항목: 없음

문제 / 비목표

  • 문제: 여러 stream consumer가 raw protocol parser, bounded tail, response-start staging, release/terminal commit과 recovery를 자체 구현하면 repeat loop, tool-call syntax, missing tool-call, schema, workflow terminal replacement마다 byte 경계·eager HTTP commit·중복 [DONE]·post-commit 복구 안전성이 달라진다. 또한 filter fan-out 동안 provider event를 계속 무제한 적재하거나 각 consumer가 자체 로그만 남기면 메모리 경계와 같은 request timeline의 decision을 보장할 수 없다. Core는 normalized event 이후의 hold/release, single-flight evaluation, strategy별 recovery와 sanitized observation mechanics를 한 번만 제공해야 한다.
  • 비목표:
    • raw OpenAI SSE, CLI JSON, agent-family protocol을 직접 parse한다.
    • 반복/문법/tool 필요성 같은 semantic detector 또는 LLM judge를 결정한다.
    • filter가 개별 retry loop/counter, raw request mutation, provider selection 또는 dispatch를 소유한다.
    • provider-pool의 후보 선택 알고리즘이나 filter별 corrective prompt의 의미를 Core가 결정한다.
    • 기본 rolling 경로에서 전체 응답을 buffer하거나 terminal gate를 hard bound 없이 운영하거나 시간 경과만으로 미검증 event를 release한다.
    • Edge provider-pool 또는 Node runtime의 구체 타입을 공통 Core package가 import한다.
    • cross-request 오류를 사건으로 저장·중복 집계하거나, 연결된 소스 저장소를 LLM으로 분석해 수정 제안·프로젝트 작업 문서·사용자 승인·변경 요청·병합·배포를 조율한다.

Source of Truth

영역 기준 메모
Roadmap Milestone 문서 범위, consumer, 완료 evidence 기준
Code packages/go/streamgate, packages/go/config, apps/edge/internal/openai, apps/edge/internal/service, apps/node/internal/runtime, apps/node/internal/adapters/openai_compat, apps/node/internal/adapters/cli transport-agnostic Core와 host codec/rebuilder/dispatcher/release sink의 책임 경계
Contract openai-compatible-api.md OpenAI caller surface는 기존 계약을 유지하고 Core는 내부 normalized event만 소비한다
Config Contract edge-config-runtime-refresh.md limit 정책의 실제 config field/default/validation/refresh 분류를 구현할 때 함께 갱신한다. 현재 active 계약에 미구현 field를 선반영하지 않는다.
User Decision 현재 사용자 요청 기본 500-rune evidence hold, no time-based release, 공통 Core 분리, model/provider별 filter on/off, 동일 immutable evidence batch의 활성 filter 병렬 평가와 all-complete barrier, filter intent를 하나의 공통 RecoveryPlan으로 재조립·재admission하는 Coordinator를 같은 Milestone에 두는 방향이 확정됐다. consumer별 외부 보조 준비는 filter barrier 안이 아니라 plan 선택/current attempt 종료 뒤 optional RecoveryPlanPreparer로 직렬화한다. terminal failure는 bounded sanitized 원인 사슬을 내부에 보존하고 endpoint host가 외부 OpenAI-compatible 오류로 한 번만 평탄화한다. cross-request 오류 사건의 중복 집계와 LLM 기반 소스 분석·수정 제안·승인·변경 요청·병합·배포는 Core 밖의 별도 범용 플랫폼으로 프로젝트화한다.

State Machine

상태 진입 조건 다음 상태 근거
request_bound host가 configured byte limit 안의 lossless ingress snapshot, registry/config generation, required filter capability와 recovery budget snapshot을 고정했다 attempt_dispatching, terminal_error 같은 request의 recovery cycle이 같은 policy, strategy budget과 request 전체 hard cap을 사용하고 oversize는 dispatch 전 종료
attempt_dispatching 최초 request 또는 rebuilt request가 준비됐다 filter_resolving, terminal_error host AttemptDispatcher가 admission을 한 번 수행해 actual model/provider/execution path/event source/controller를 반환
filter_resolving attempt의 실제 model/provider/capability가 확정됐다 normalized, terminal_error 고정된 registry snapshot으로 active filter set과 hold/failure policy를 attempt-local resolve. replace-attempt는 pending/look-behind를 초기화하고 continuation은 committed look-behind/release cursor를 보존
normalized codec이 raw provider/agent event를 typed normalized event로 변환했다 response_staging, evidence_holding, terminal_evaluating, terminal_error response-start/text/reasoning/tool/terminal/error event
response_staging provider response-start 또는 protocol opening event가 도착했지만 safe output은 없다 evidence_holding, terminal_evaluating, terminal_error status/header/opening role을 ReleaseSink에 쓰지 않고 attempt-local staging
evidence_holding hold mode가 요구한 rolling rune, terminal 또는 fragment completion evidence가 아직 부족하다 filter_evaluating, terminal_evaluating, terminal_error channel별 rolling/terminal/fragment mode와 hard bound. 시간은 release 조건이 아님
filter_evaluating subscribed event/hold trigger가 준비되거나 terminal batch가 확정됐다 arbitrating, terminal_error event-applicable/trigger-ready filter는 병렬 실행하고, subscribed event가 없으면 not_applicable_for_epoch, release-blocking trigger 미충족이면 deferred_by_requirement로 표시한다. complete outcome set을 모으는 동안 다음 ingress는 bounded backpressure
arbitrating base event disposition과 모든 active filter outcome이 준비됐다 releasing, evidence_holding, recovery_planning, terminal_error deterministic single action. blocking deferred는 hold, eligible recovery는 candidate를 대체하고 unmatched provider error는 base terminal 유지
recovery_planning Arbiter가 하나의 RecoveryIntent를 선택했다 attempt_aborting, terminal_error strategy budget과 request 전체 recovery hard cap, cancel, commit/terminal/tool side effect, strategy eligibility, same-plan re-entry guard를 확인하고 preparer/continuation에 필요한 bounded request-local snapshot과 cursor를 immutable하게 고정한다
attempt_aborting recovery eligibility가 통과했다 plan_preparing, rebuilding, terminal_error 현재 attempt를 idempotent cancel/close하고 실패/timeout이면 보조 plan 준비나 다음 attempt를 시작하지 않음
plan_preparing 이전 attempt ownership이 종료됐고 선택된 plan이 consumer별 보조 directive 준비를 요구한다 rebuilding, terminal_error optional RecoveryPlanPreparer를 plan/idempotency key당 최대 1회, bounded deadline으로 호출한다. filter barrier 밖에서 실행하고 실패/timeout이면 fallback 없이 terminal
rebuilding 이전 attempt ownership이 종료됐고 보조 plan 준비가 완료됐거나 필요 없다 attempt_dispatching, terminal_error lossless ingress/continuation snapshot과 endpoint/family RequestRebuilder; cycle당 rebuilt request 하나
releasing Arbiter가 safe prefix 또는 protocol-safe replacement를 허용했다 stream_open, evidence_holding, terminal_evaluating 첫 release라면 staged response-start/opening event를 먼저 한 번 commit하고 safe event를 순서대로 전송
stream_open downstream transport/opening event 또는 body가 commit됐다 evidence_holding, terminal_evaluating, recovery_planning, done, terminal_error 이미 전송한 event rollback/exact/schema replay 금지. safe-prefix continuation 또는 pre-terminal replacement만 capability와 side-effect guard 아래 허용
terminal_evaluating terminal event가 evidence 충족 전 또는 후 도착했다 filter_evaluating, terminal_error terminal flag가 포함된 final EvidenceBatch, all-filter evaluation
terminal_error provider idle/error, hard buffer overflow, blocking filter failure, recovery ineligible 또는 post-terminal error가 발생했다 done uncommitted이면 staged response를 버리고 host error status/body, stream-open이면 protocol terminal error만 한 번 전송
done terminal sequence 또는 terminal error가 downstream에 확정됐다 없음 terminal event 1회

Interface Contract

  • 계약 원문: openai-compatible-api.md
  • 입력:
    • NormalizedEvent: codec이 생성한 response_start, text_delta, reasoning_delta, tool_call_fragment, terminal, provider_error 중 하나다. response_start는 status와 host가 allowlist로 정규화한 header metadata를 포함한다. hop-by-hop header와 body 변환 시 무효가 되는 Content-Length는 staging/release 대상에서 제외한다. Core는 raw wire bytes나 caller 제품명을 받지 않는다.
    • BaseEventDisposition: Core가 event lifecycle에서 만드는 hold, release_candidate, terminal_success_candidate, terminal_error_candidate다. response-start는 hold, user-facing safe delta는 release candidate, normal terminal은 success terminal candidate, provider error는 error terminal candidate다. filter가 매칭되지 않아도 이 기본 의미는 사라지지 않는다.
    • StreamTraceContext: runtime이 이미 가진 내부 request_id, run_id, provider id, model id correlation이다. Core는 새 public field를 만들지 않고 이 값을 observation에 전파한다.
    • FilterContext: immutable request-local config generation, attempt/epoch, environment, endpoint/family, model group/actual model/provider/execution path, stream, commit/terminal/tool-side-effect state와 trace correlation이다.
    • FilterRegistrySnapshot: request 시작 시 고정한 stable filter id별 구현과 config generation이다. admission 전 RequiredFilterCapabilities로 필수 후보를 제한하고 actual target이 정해진 각 attempt에서 environment/model/provider/capability별 active set, priority, hold mode와 blocking|observe_only enforcement를 resolve한다. config reload는 다음 request부터 반영하고 현재 request의 retry에는 섞지 않는다.
    • EvidenceBatch: 같은 evaluation epoch의 normalized event snapshot, channel별 pending, bounded committed look-behind, staged response-start metadata, terminal 여부와 commit state를 read-only accessor로 제공한다. 모든 trigger-ready filter가 같은 logical snapshot을 읽고 mutable map/slice 참조를 공유하지 않는다. replace-attempt는 buffer를 초기화하고 continuation은 committed look-behind/release cursor만 보존하며 observation/log로 원문을 전달하지 않는다.
    • stream_hold.evidence_runes: 기본 500. environment/model/provider policy가 제공하는 Unicode rune evidence window다.
    • FilterHoldRequirement: 대상 channel, none|rolling_window|terminal_gate|fragment_gate, subscribed normalized event kinds, evidence_runes, terminal/fragment trigger와 max_buffer_runes hard bound다. none event-only filter는 channel release를 막지 않는다. Core는 channel별 strongest blocking requirement를 합성하고 filter가 별도 raw buffer나 unbounded hold를 만들지 못하게 한다.
    • Filter: Go interface로 stable ID, attempt-level Applies, HoldRequirement/event subscription, synchronous Evaluate(context.Context, FilterContext, EvidenceBatch) (FilterDecision, error)를 제공한다. 공통 context는 caller cancel과 bounded deadline을 전달하며 구현은 취소를 준수해야 한다. Gate Coordinator가 fan-out/수집을 관리하고 구현은 concurrency, request mutation, submit을 소유하지 않는다.
    • FilterDecision: pass, observe, violation, fatal, replacement 중 하나다. consumer_id, stable filter_id, rule_id, sanitized evidence와 선택적 RecoveryIntent를 포함한다. evidence 수집 종료점은 static hold requirement가 결정하므로 filter가 평가 뒤 무기한 need_more_evidence로 hold를 연장하지 않는다.
    • FilterOutcome: active filter마다 evaluated(FilterDecision), evaluation_error, not_applicable_for_epoch, deferred_by_requirement 중 하나다. not-applicable은 subscribed event가 없어 release를 막지 않는 outcome이고, deferred는 static blocking trigger 미충족이라 pass로 간주하지 않는다. terminal/hard bound에서 계속 deferred면 invalid/fail policy로 수렴한다.
    • FilterFailurePolicy: Registry가 filter 외부에서 정한다. blocking filter error/deadline은 fatal로, observe-only filter error/deadline은 observe_error outcome으로 정규화되며 둘 다 all-complete 결과와 observation에 포함된다. caller cancel/host shutdown은 barrier를 취소하고 no-release로 종료한다.
    • FailureCause: terminal failure의 내부 원인 한 단계다. stage, 안정 code, consumer/filter/rule id만 가지며 raw stack trace, provider body/endpoint, user prompt, output/reasoning 원문, tool args/result, 인증 정보는 가질 수 없다. FailureCauseChain은 request-local 최대 4단계이고 consumer와 Coordinator가 append-only로 조립한다.
    • RecoveryIntent: exact_replay, continuation_repair, schema_repair strategy id와 typed repair directive, sanitized reason, filter priority를 가진다. raw body/auth, attempt counter, provider 선택 또는 실행 함수는 포함하지 않는다.
    • RecoveryBudget: immutable request-local max_recovery_attempts_total, strategy별 limit과 사용량을 가진다. 전체 cap 기본값이자 MVP 절대 상한은 최초 provider 실행을 제외한 3회이며 request 시작 시 policy snapshot으로 고정한다. policy는 0..3에서 낮출 수만 있고 3 초과는 config load/refresh validation error다. exact/continuation/schema recovery dispatch를 모두 합산하고 각 strategy cap보다 우선한다. 어느 cap이든 소진되면 새 plan을 만들지 않고 terminal로 수렴하며 filter/strategy를 바꿔 우회할 수 없다.
    • IngressSnapshot: endpoint/family rebuilder가 unknown caller field를 손실 없이 보존할 수 있는 request-local snapshot이다. max_ingress_snapshot_bytes 기본값이자 MVP 절대 상한은 16 MiB이며 policy는 이보다 낮출 수만 있고 request 시작 시 고정한다. host는 io.ReadAll 전에 http.MaxBytesReader 또는 동등한 limit+1 판정을 raw body에 적용한다. OpenAI JSON endpoint는 raw body를 canonical lossless representation으로 사용하고, 다른 codec의 lossless tree는 byte-preserving round-trip fixture가 있을 때만 허용한다. retained_bytes는 runtime heap 추정치가 아니라 request가 소유한 backing byte/string buffer의 보수적 logical byte 합이며 shared backing은 한 번만 센다. SnapshotBuilder는 bounded typed semantic view를 더한 직후, RequestRebuilder는 임시 output 할당 전후의 current peak를 다시 검사하고 canonical full copy를 둘 이상 유지하지 않는다. 따라서 raw body가 pre-read gate와 같아도 typed view를 더한 total retained limit을 넘으면 수용하지 않는다. ingress/rebuild 초과는 snapshot/filter/log/cross-request cache에 원문을 남기거나 provider/recovery budget을 소비하지 않고 terminal로 끝내며 raw auth header도 보존하지 않는다. request 종료/cancel/overflow에서는 retained object를 release한다.
    • AttemptDispatcher: host가 구현하며 rebuilt request와 required capability를 기존 admission에 전달해 actual model/provider/execution path, normalized event source와 AttemptController를 가진 AttemptBinding 하나를 반환한다. concrete provider selection/auth/model rewrite 알고리즘은 Core 밖에 있다.
    • AttemptController: 현재 attempt의 provider transport cancel/close와 ownership release를 idempotent하게 수행한다. Core가 plan 선택 시 고정한 request-local preparation/continuation snapshot과 cursor는 지우지 않는다. recovery는 성공적인 abort 또는 명시적 already-closed 확인 전에 preparer나 다음 dispatch를 호출하지 않는다.
    • RecoveryPlanPreparer: 선택된 plan이 consumer별 bounded directive 준비를 요구할 때 host/consumer가 구현하는 optional interface다. Core가 all-complete arbitration과 eligibility 판정, current attempt ownership 종료 뒤 plan/idempotency key당 최대 1회 bounded deadline으로 호출한다. 입력은 plan 선택 시 고정한 request-local bounded preparation snapshot이며 구체 범위는 consumer 계약이 정하고 observation/log에는 복제하지 않는다. preparer는 provider admission/dispatch, retry/fallback, budget mutation을 소유하지 않고 성공 시 typed directive만 보강한다. 실패는 bounded cause를 가진 terminal로 수렴하며 outbound recovery dispatch가 없으므로 request recovery budget을 소비하지 않는다. snapshot은 성공·실패·cancel 뒤 release한다.
    • RequestRebuilder: host/endpoint/family가 bounded lossless ingress snapshot, selected plan과 continuation snapshot을 새 request로 조립한다. exact replay는 admission rewrite 전 caller raw body를 보존하고 continuation/schema만 typed patch를 적용한다. rebuild 전 예상 peak와 할당 후 실제 peak retained_bytes를 모두 확인하고 한도를 넘으면 기존 snapshot과 임시 output을 즉시 release한 뒤 dispatch하지 않는다.
    • ReleaseSink: host transport가 staged response-start, safe event, replacement, terminal/error를 순서대로 commit하고 현재 commit state를 반환한다. 첫 commit 뒤 status/header를 다시 쓰거나 opening role/event를 중복 전송하지 않는다.
  • 출력:
    • ReleaseEvent: filter 통과 safe prefix 또는 허용된 replacement다.
    • CommitState: transport_uncommitted, stream_open, terminal_committed다. HTTP status/header flush, SSE/agent opening event 또는 첫 body 중 가장 이른 user-visible write가 stream_open이다. exact/schema replay는 uncommitted에 한정하고 continuation/replacement는 별도 capability matrix를 따른다.
    • TerminalResult: terminal event 또는 terminal error를 한 번만 전송한다. terminal error는 선택적 bounded FailureCauseChain과 외부 오류의 type/code/안전한 message template/param descriptor를 가진다. Core는 이를 재해석하지 않고 ReleaseSink에 전달하며 endpoint codec이 외부 오류 계약으로 직렬화한다.
    • ArbitrationResult: base disposition과 모든 filter outcome을 합성한 release, hold, terminal, recover, replacement 단일 action이다. hold는 blocking deferred가 hard bound 전일 때만, recover는 eligible intent가 base candidate를 대체할 때만 선택한다. unmatched error는 terminal을 유지하고 같은 epoch에서 action/dispatch를 둘 이상 만들지 않는다.
    • RecoveryPlan: 선택된 strategy, replace_attempt|continue_stream resume mode, contributing filter/rule ids, plan/idempotency key, strategy attempt와 request 전체 recovery attempt, 각 limit, typed directive, optional bounded preparation snapshot reference와 preparer id/status, commit/side-effect eligibility와 continuation cursor를 포함한다. RecoveryPlan Coordinator만 생성하며 preparer 성공 전에는 rebuild/dispatch 가능한 final plan으로 표시하지 않는다. snapshot 원문은 observation에 직렬화하지 않는다.
    • RebuiltRequest: RequestRebuilder가 immutable ingress snapshot과 plan directive로 만든 endpoint별 새 attempt다. 기존 admission 경로가 provider를 다시 선택하고 auth를 재계산한다.
    • FilterObservation: filter evaluation 또는 lifecycle/coordinator decision마다 emit하는 내부 timeline event다. 기존 request/run/provider/model correlation과 config generation, attempt/epoch, consumer/filter/rule id, hold mode, event kind, effective/pending rune, decision/failure policy, commit state, terminal reason, plan id/strategy/resume mode/shared attempt, preparer id/status/deadline outcome, 최대 4개의 sanitized cause code와 sanitized evidence를 담는다. 기존 sink가 저장·보존을 소유하며 preparer input/output, raw output/prompt/tool args/result/auth와 delta 원문은 담지 않는다.
  • 금지:
    • stream_hold.evidence_runes가 부족한 event를 시간 경과만으로 release하지 않는다.
    • provider idle/timeout 시 pending tail을 flush하지 않는다.
    • Core가 raw protocol parser, caller-specific policy, cross-request cache, LLM judge를 소유하지 않는다.
    • 이미 전송한 event를 rollback하거나 exact/schema replay하지 않는다. continuation/replacement도 기존 bytes를 suppress하지 않고 terminal/tool side effect 뒤에는 실행하지 않는다.
    • 활성 filter outcome이 모두 수집되기 전에 release/retry/replace하지 않는다. filter error/deadline을 silent pass로 간주하지 않는다.
    • 하나의 evaluation epoch를 겹쳐 실행하거나 evaluation 중 provider event를 unbounded queue에 적재하지 않는다.
    • provider response status/header 또는 opening role/event를 safe release 전에 eager commit하지 않는다.
    • terminal gate hard bound 초과 시 partial output을 release하지 않는다.
    • 현재 attempt cancel/close ownership이 끝나기 전에 다음 recovery attempt를 dispatch하지 않는다.
    • filter가 request를 직접 mutate하거나 retry loop/counter, provider admission, request submit을 실행하지 않는다.
    • 선택된 recovery plan의 후처리인 RecoveryPlanPreparer/translator를 filter evaluation 또는 all-complete barrier 안에서 실행하지 않는다. consumer가 별도 Filter로 선언한 bounded LLM judge까지 금지하는 규칙은 아니지만, 해당 Filter는 자신의 invocation cap/deadline/failure policy를 지켜야 한다. preparer 실패를 재호출/fallback으로 우회하거나 recovery dispatch 없이 budget을 소비하지 않는다.
    • 같은 evaluation epoch/recovery cycle에서 둘 이상의 RecoveryPlan 또는 outbound retry를 실행하지 않는다. 단, request-local strategy budget과 request 전체 hard cap이 모두 남아 있고 새 attempt가 다시 violation을 반환하면 다음 cycle을 시작할 수 있다.
    • max_recovery_attempts_total을 3보다 높이거나 strategy별 limit 합으로 request 전체 cap을 우회하지 않는다.
    • request body를 unbounded io.ReadAll한 뒤 길이만 검사하지 않는다. max_ingress_snapshot_bytes를 넘는 요청이나 rebuilt request를 snapshot으로 보존하거나 provider에 dispatch하지 않는다.
    • OpenAI JSON raw body 외 표현을 byte-preserving 증거 없이 exact replay canonical source로 사용하거나 canonical lossless representation의 full copy를 둘 이상 유지하지 않는다.
    • FilterObservation에 raw output, raw request/prompt, tool args/result, 인증 정보 또는 caller 제품명을 넣지 않는다.
    • raw stack trace나 provider endpoint/body를 FailureCauseChain, TerminalResult, public error message, FilterObservation에 넣지 않는다.

Acceptance Scenarios

ID Milestone Task Given When Then
S01 evidence-tail rolling text delta가 pending에 누적되고 이전 safe prefix가 release됐다 기본 500 rune 또는 policy override 200 rune 증거가 충족돼 다음 batch를 만든다 filter는 같은 크기의 bounded committed look-behind와 pending을 함께 보아 경계 반복을 판정하고 전체 응답을 buffer하지 않는다. replace-attempt는 초기화하고 continuation은 look-behind/cursor를 보존한다
S02 evidence-tail 한국어 UTF-8 multi-byte 문자열이 chunk 경계에서 나뉜다 evidence rune을 계산한다 byte가 아닌 Unicode rune으로 정확히 계산하고 깨진 문자 없이 release한다
S03 commit-boundary evidence 미충족 pending tail과 staged response-start가 있고 terminal event가 온다 Core가 최종 all-filter evaluation을 한다 safe response-start/output 또는 terminal error가 한 번만 결정되고 eager status/role은 없다
S04 commit-boundary evidence 미충족 pending tail 상태에서 provider idle timeout이 난다 terminal error를 처리한다 pending tail은 release하지 않고 terminal error만 전송한다
S05 commit-boundary Core가 safe prefix를 전송해 stream이 열렸다 이후 provider error 또는 filter violation이 난다 already released event는 rollback/exact/schema replay하지 않고 continuation-safe가 아니면 terminal policy로 끝낸다
S06 policy-hook output validation, syntax gate, missing-tool gate, integrity filter, workflow terminal hook이 같은 Core를 소비한다 각 filter가 자기 detector decision과 선택적 RecoveryIntent를 반환한다 semantic policy/intent는 filter별로 분리되고 request mutation/retry loop/rebuild/dispatch는 공통 Coordinator만 수행한다
S07 policy-hook rolling, schema terminal, tool fragment와 provider-error event-only filter가 같은 request에서 활성이다 Core가 channel requirement와 event applicability outcome을 합성한다 rolling은 조기 violation 가능, schema는 terminal 전 blocking deferred, tool은 fragment gate, 평상시 provider-error는 not-applicable로 content를 막지 않는다. duplicate buffer/commit은 없다
S08 filter-observation repeat guard와 syntax gate가 stable filter id 및 sanitized fingerprint/count/offset evidence를 제공한다 Core가 filter와 coordinator lifecycle event를 emit한다 같은 request/run/provider/model timeline에서 config generation, attempt/epoch, staging, 각 decision/failure policy, plan/strategy/resume mode를 추적할 수 있고 raw output/prompt/tool args는 없다
S09 event-contract OpenAI와 agent-family codec이 response-start를 포함한 서로 다른 raw event를 같은 normalized table로 변환한다 transport-agnostic Core와 host ReleaseSink가 event를 처리한다 Core가 raw parser나 Edge/Node internal type을 import하지 않고 typed event/host interface만으로 같은 lifecycle을 통과한다
S10 adoption-doc output validation, syntax, missing-tool, integrity, workflow consumer 문서가 Core를 참조한다 consumer boundary와 observation/recovery mapping을 검토한다 모든 consumer가 Core link, own semantic policy/intent, stable filter id와 sanitized observation을 명시한다. recovery consumer는 strategy/request-total cap과 common RecoveryPlan을, ingress snapshot consumer는 bounded snapshot을 사용하고 raw buffer·snapshot/rebuild·retry loop를 중복 구현하지 않는다
S11 filter-registry request 중 config reload가 일어나고 recovery admission이 provider를 바꾸며 required filter capability가 있다 registry가 request snapshot과 각 attempt actual target으로 active set을 resolve한다 같은 config generation을 유지하면서 provider별 set은 다시 계산되고 unsupported candidate는 dispatch 전 제외되며 disabled/duplicate filter는 평가되지 않는다
S12 parallel-evaluation ready filter, terminal-trigger filter, error-event-only filter가 있고 평가 중 provider event가 더 도착한다 Gate Coordinator가 evaluated/deferred/not-applicable outcome과 bounded backpressure를 만든다 complete outcome set 전 action하지 않고 blocking deferred는 pass가 아니며 event-only not-applicable은 release를 막지 않는다. race/overlap/unbounded queue가 없고 failure/cancel policy가 적용된다
S13 decision-arbiter release/error/terminal base disposition과 pass/not-applicable/deferred/violation/fatal/replacement outcome이 섞여 있다 Arbiter가 결과를 합성한다 unmatched provider error는 terminal, matched eligible error는 recovery, blocking deferred는 hold가 되고 priority/stable id로 single action만 선택한다
S14 recovery-coordinator 두 filter가 같은 epoch에 retryable violation을 반환하고 strategy budget과 기본/절대 상한 3회의 request 전체 recovery cap이 남아 있다 Coordinator가 intent를 dedupe하고 commit/side-effect별 eligibility와 두 budget을 판정한다 cycle마다 plan/idempotency key/dispatch 하나만 만들고 exact/schema는 uncommitted에, continuation은 safe stream-open에만 허용한다. 0/1/3회 policy는 적용되고 4 이상 config는 거부되며 strategy 또는 전체 cap 소진, cancel/side-effect/re-entry 위반은 다른 strategy 우회 없이 terminal로 끝낸다
S15 request-rebuilder configured byte limit 안에서 unknown caller field가 있는 Chat/Responses raw snapshot과 exact/continuation/schema directive가 있다 endpoint rebuilder와 host dispatcher가 새 attempt를 만든다 OpenAI JSON raw body를 canonical source로 하나만 보존하고 bounded typed view/rebuild allocation을 계상한다. exact는 admission rewrite 전 caller body와 unknown field를 보존하고 typed patch만 적용한다. concrete model/auth는 admission에서 다시 계산되고 normalized↔tunnel path 전환도 새 AttemptBinding으로 이어진다
S16 vertical-slice model/provider on/off 가능한 NoopFilter와 test-only InjectedViolationFilter가 등록돼 있다 staged response-start부터 filters, Arbiter, abort, Rebuilder, Dispatcher, ReleaseSink까지 한 사이클 돈다 disabled/enabled pass, eager header/role 없음, injected violation one retry, simultaneous violation single retry, provider/path switch, failure policy, backpressure, exhausted terminal이 end-to-end로 검증된다
S17 commit-boundary provider가 response-start를 보낸 뒤 user body release 전에 filter-matched 500 body/error를 보낸다 provider-error filter가 exact replay intent를 반환한다 original status/header/role은 commit되지 않고 retry 성공 attempt의 response-start와 output만 한 번 노출된다
S18 recovery-coordinator 정상 text prefix가 이미 release된 뒤 repeat filter가 continuation intent를 반환한다 Coordinator가 tool/terminal side effect가 없는 stream-open eligibility를 확인한다 released prefix와 committed look-behind/cursor를 보존하고 새 attempt의 opening/prefix 중복 없이 같은 stream에 continuation만 이어 쓰며 exact/schema intent는 거부된다
S19 evidence-tail terminal-gate filter가 활성이고 provider output이 configured hard bound를 넘는다 Core가 buffer overflow를 감지한다 partial output이나 staged response-start를 release하지 않고 attempt를 취소한 뒤 terminal error로 끝낸다
S20 recovery-coordinator recovery가 선택됐지만 현재 attempt abort가 timeout 또는 실패한다 Coordinator가 다음 dispatch를 준비한다 next attempt를 시작하지 않고 terminal로 끝내 두 live provider attempt와 중복 side effect를 방지한다
S21 event-contract consumer가 repetition_loop_detected 뒤 auxiliary recovery step 실패를 뜻하는 recovery_notice_translation_failed 원인을 terminal로 올린다 Core가 terminal result와 host ReleaseSink를 처리한다 원인 사슬은 최대 4개의 sanitized code로 보존되고, host는 endpoint별 외부 오류 하나만 전송한다. raw stack/provider body는 observation과 public output에 없다
S22 request-rebuilder ingress raw body가 pre-read limit을 넘거나 raw body가 gate와 같아도 typed view/rebuild 임시 output을 포함한 logical current peak retained size가 기본값/절대 상한 16 MiB 또는 작은 policy override를 넘는다 host가 limit+1 overflow를 snapshot 생성 전에 판정하고 SnapshotBuilder/Rebuilder가 owned backing은 합산하되 shared backing은 한 번만 세어 typed view 추가 직후와 rebuild 할당 전후에 retained bytes를 다시 검사한다 pre-read limit-1/limit은 body gate만 통과하고 limit+1은 즉시 거부된다. 이후 initial retained overflow는 endpoint host의 request-too-large 오류, rebuild overflow는 commit state에 맞는 recovery terminal error 하나로 끝난다. canonical raw body와 임시 object를 release하고 request-local cache·filter·로그에 남기지 않으며 provider dispatch/recovery budget 소비가 없고 16 MiB 초과 config는 거부된다
S23 recovery-coordinator all-complete Arbiter가 보조 directive 준비가 필요한 continuation plan 하나를 선택했다 Coordinator가 bounded preparation/continuation snapshot과 cursor를 고정하고 current provider transport ownership을 종료한 뒤 registered RecoveryPlanPreparer를 실행한다 abort가 snapshot을 지우지 않고 preparer는 filter barrier 밖에서 plan/idempotency key당 최대 1회와 bounded deadline을 지킨다. 성공 뒤에만 rebuild/dispatch하고 timeout/invalid/error는 재호출/fallback 없이 single terminal로 끝내며 outbound dispatch가 없으므로 recovery budget을 소비하지 않는다. snapshot은 모든 종료 경로에서 release된다

Evidence Map

Scenario Required Evidence agent-task 연결 완료 Evidence 기대
S01 200/500-rune policy, pending+look-behind bound, cross-boundary repeat, replace reset/continuation preserve tests agent-task/m-stream-evidence-gate-core/... Roadmap Completionevidence-tail, rolling threshold/bounded look-behind/recovery-mode assertion
S02 Korean multi-byte split chunk rune fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completionevidence-tail, Unicode boundary assertion
S03 terminal-before-evidence with staged response-start fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completioncommit-boundary, no eager commit/single terminal assertion
S04 provider idle timeout before evidence fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completioncommit-boundary, pending-tail no-release assertion
S05 uncommitted/stream-open/terminal-committed error and strategy matrix agent-task/m-stream-evidence-gate-core/... Roadmap Completioncommit-boundary, no rollback/exact-schema post-open prohibition assertion
S06 OpenAI and agent-family fake codec consumer decision/intent matrix, direct-submit prohibition check agent-task/m-stream-evidence-gate-core/... Roadmap Completionpolicy-hook, consumer semantic/intent ownership and common recovery ownership assertion
S07 rolling/terminal/fragment mixed-mode composition and channel-bound tests agent-task/m-stream-evidence-gate-core/... Roadmap Completionpolicy-hook, strongest bounded requirement/single buffer assertion
S08 multi-consumer observation timeline with config/attempt/epoch/staging/failure/resume mode and raw absence agent-task/m-stream-evidence-gate-core/... Roadmap Completionfilter-observation, one raw-free request timeline assertion
S09 OpenAI/agent-family fake codec response-start/event table and host-interface package dependency test agent-task/m-stream-evidence-gate-core/... Roadmap Completionevent-contract, transport-agnostic typed contract/no internal import assertion
S10 five dependent Milestone Core-link/filter-id/sanitized-observation/limits/common-RecoveryPlan mapping review agent-task/m-stream-evidence-gate-core/... Roadmap Completionadoption-doc, consumer documentation/no duplicate raw buffer/snapshot/rebuild/retry loop assertion
S11 config-generation isolation, policy precedence, required capability admission, provider-switch and duplicate-id tests agent-task/m-stream-evidence-gate-core/... Roadmap Completionfilter-registry, snapshot/attempt re-resolution/preflight assertion
S12 evaluated/deferred/not-applicable mixed outcomes, barrier, race, backpressure, failure/cancel fixtures agent-task/m-stream-evidence-gate-core/... Roadmap Completionparallel-evaluation, complete outcome set/deferred-vs-event-only/single-flight assertion
S13 base disposition + all outcome arbitration matrix, matched/unmatched error and deterministic repeats agent-task/m-stream-evidence-gate-core/... Roadmap Completiondecision-arbiter, default terminal preservation/single-action assertion
S14 simultaneous intent, commit-strategy matrix, 0/1/3회 및 4 이상 config validation, bounded cycles, strategy/request-total cap 교차 소진, cancel/side-effect/re-entry fixtures agent-task/m-stream-evidence-gate-core/... Roadmap Completionrecovery-coordinator, one plan/dispatch per cycle, absolute total-cap 우선과 no strategy bypass assertion
S15 Chat/Responses bounded raw canonical exact/continuation/schema rebuild, byte-preserving alternative codec, unknown field, auth safety and path-switch tests agent-task/m-stream-evidence-gate-core/... Roadmap Completionrequest-rebuilder, caller-body equality/retained-byte accounting/host admission/AttemptBinding assertion
S16 staged-start NoopFilter/InjectedViolationFilter vertical integration with path switch and backpressure agent-task/m-stream-evidence-gate-core/... Roadmap Completionvertical-slice, request-to-recovery-to-release one-cycle assertion
S17 response-start + matched 500 before body, successful replay response exposure fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completioncommit-boundary, original start hidden/final start once assertion
S18 stream-open repeat continuation, preserved cursor/look-behind and duplicate opening/prefix suppression fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completionrecovery-coordinator, safe post-open continuation/exact-schema rejection assertion
S19 terminal-gate hard buffer overflow and attempt cancel fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completionevidence-tail, fail-closed/no partial release assertion
S20 attempt abort timeout/failure before next dispatch fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completionrecovery-coordinator, no overlapping live attempt assertion
S21 bounded failure-cause chain, one terminal result, Chat/Responses host error codec fake fixture agent-task/m-stream-evidence-gate-core/... Roadmap Completionevent-contract, raw-free cause chain/endpoint-specific single-error assertion
S22 host pre-read raw-body limit-1/limit/limit+1, exact-limit body+typed-view overflow, configured smaller limit, rebuild pre/post-allocation peak overflow, initial-vs-rebuild terminal and release/raw-absence fixtures agent-task/m-stream-evidence-gate-core/... Roadmap Completionrequest-rebuilder, body-gate-vs-total-retained 구분/no unbounded read/no dispatch/no budget/no raw retention and 16 MiB absolute-cap assertion
S23 all-complete snapshot freeze, transport-only abort-before-prepare, plan/idempotency-key single call, success/timeout/invalid/error, no-fallback/no-budget/snapshot-release fixtures agent-task/m-stream-evidence-gate-core/... Roadmap Completionrecovery-coordinator, preparer의 filter-barrier 외부 실행/one-shot deadline/success-only rebuild/request-local snapshot lifetime assertion

Cross-repo Dependencies

  • 없음

Drift Check

  • Milestone 기능 Task와 Acceptance Scenario가 일치한다.
  • Evidence Map이 code-review/complete.log에서 검증 가능하다.
  • agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
  • 사용자 리뷰가 필요한 항목은 없음으로 확인했다.

사용자 리뷰 이력

  • 2026-07-23: 사용자 요청으로 evidence-based pending tail을 공통 stream mechanics로 분리했다. 기본 500-rune hold와 time-based release 금지는 확정됐다.
  • 2026-07-23: 사용자 요청으로 여러 consumer filter의 decision을 같은 request timeline에서 추적할 수 있는 raw-free FilterObservation 공통 계약을 추가했다.
  • 2026-07-23: Coordinator를 별도 Milestone으로 분리하지 않고 이 Core Milestone 안의 독립 컴포넌트로 두기로 했다. 활성 filter는 동일 immutable evidence batch를 병렬 평가하고 all-complete barrier 뒤 결과를 합성하며, filter는 model/provider별 on/off 가능한 공통 interface 구현체로 detector와 RecoveryIntent만 제공한다. 공통 Coordinator가 하나의 plan, strategy/request-total budget, endpoint별 request rebuild와 single re-admission을 소유한다.
  • 2026-07-23: 재검토에서 HTTP/SSE response-start staging, rolling/terminal/fragment hold 분리, single-flight backpressure, request-local config snapshot, strategy별 post-commit eligibility와 host adapter interface를 보강했다. 특히 exact/schema replay는 uncommitted에 한정하고, 반복 continuation은 이미 보낸 safe prefix를 보존한 stream-open 복구로 명시했다.
  • 2026-07-23: 안정성 재검토에서 strategy별 budget을 합산하는 최초 실행 제외 기본값/절대 상한 3회의 request 전체 recovery hard cap과 기본값/절대 상한 16 MiB lossless ingress snapshot retained-byte hard limit을 추가했다. OpenAI JSON raw body를 canonical source로 하나만 보존하고 host pre-read limit을 적용하도록 확정했다.
  • 2026-07-23: consumer별 번역/보조 호출이 병렬 filter barrier 안에서 반복 실행되지 않도록 optional RecoveryPlanPreparer를 all-complete/Arbiter와 current attempt ownership 종료 뒤의 one-shot 단계로 추가했다. preparer 실패는 fallback·recovery dispatch·budget 소비 없이 terminal로 수렴한다.
  • 2026-07-23: cross-request 오류 수집·안전한 지문 기반 중복 집계·연결 소스 분석·수정 제안·프로젝트 작업 문서·사용자 승인·변경 요청·병합·배포·재발 확인은 별도 범용 플랫폼으로 프로젝트화하기로 확정했다. Core는 동기 요청 경로에서 이 루프를 실행하지 않고 raw-free terminal/observation event를 전달하는 경계만 소유한다.

작업 컨텍스트

  • 표준선: packages/go/streamgate는 codec과 semantic filter 사이의 transport-agnostic boundary다. Gate Coordinator가 state/single-flight fan-out/all-complete barrier를, Arbiter가 single action을, RecoveryPlan Coordinator가 strategy eligibility, strategy별 budget, request 전체 hard cap과 abort 뒤 optional one-shot RecoveryPlanPreparer 호출 순서를 담당한다. raw parser, concrete admission/auth/model rewrite, preparer 의미와 response writer는 host/consumer interface 뒤에 둔다.
  • 구현 접점: Edge writeProviderTunnelResponse의 provider response-start header flush와 streamChatCompletion의 opening role write는 현재 eager commit 지점이므로 ReleaseSink 뒤로 이동한다. streamBufferedChatCompletion/completeChatCompletion의 tool-validation retry loop는 새 Coordinator의 첫 migration 대상이며 기존 loop와 공존시키지 않는다.
  • 표준선: default rolling hold는 text/reasoning release barrier다. terminal 전체가 필요한 schema/judge는 explicit bounded terminal_gate, incomplete tool fragment는 fragment_gate를 선언한다. rolling look-behind는 사용자 전송을 지연시키지 않고 cross-boundary 판정에만 쓰며 terminal gate를 제외한 기본 경로는 content 전체를 보관하지 않는다.
  • 표준선: response status/header와 opening role/event도 user-visible commit이다. 첫 safe release 전에는 staging하고, stream-open 이후 exact/schema replay는 금지한다. continuation recovery는 기존 safe prefix와 look-behind/release cursor를 보존하고 terminal/tool side effect 전 같은 downstream stream에 이어 쓰는 별도 resume mode다.
  • 표준선: request 시작 시 registry/config generation을 고정한다. recovery가 provider/path를 바꾸면 같은 snapshot으로 attempt-local active set을 다시 resolve하고, required filter capability는 admission 후보 조건으로 전달한다.
  • 표준선: Core는 관측 envelope와 request/run/provider/model correlation을 emit하지만 filter 의미나 observation 저장·보존을 소유하지 않는다. filter는 자기 stable filter/rule id와 sanitized evidence만 채우며, 기존 observability sink가 모든 filter/coordinator decision을 하나의 FilterObservation timeline으로 남긴다.
  • 표준선: 별도 오류 수정 플랫폼은 오류 발생 → 수집·민감정보 제거 → 안정 지문 기반 사건 생성/중복 count → 실행 버전·커밋·저장소 연결 → 비동기 LLM 분석과 재현 근거 → 중립 수정 제안 → 프로젝트별 작업 문서 → 사용자 승인 → 격리 수정·테스트·독립 검토 → 변경 요청 → 병합·배포 → 재발 관찰 생명주기를 소유한다. 같은 오류는 새 작업을 만들지 않고 canonical 사건의 count와 first/last seen·영향 버전만 갱신한다.
  • 표준선: filter interface는 context-aware synchronous pure evaluation 형태로 유지하고 Gate Coordinator가 goroutine fan-out/수집, single-flight backpressure와 cancel/deadline fail policy를 관리한다. 이로써 filter 구현이 concurrency, request mutation, attempt counter, provider admission을 각각 재구현하지 않는다.
  • 표준선: 최초 vertical slice는 production semantic detector 대신 model/provider별 on/off 가능한 NoopFilter와 test-only InjectedViolationFilter로 공통 파이프라인 한 사이클을 먼저 검증한다.
  • 표준선: host는 ingress body를 읽기 전에 기본값/절대 상한 16 MiB의 max_ingress_snapshot_bytes를 적용한다. OpenAI JSON은 raw body를 canonical source로 하나만 보존하고 Core/Rebuilder가 typed view와 rebuild 임시 output까지 retained byte를 계상한다. limit 초과는 provider dispatch와 recovery budget 소비 전에 fail-closed한다.
  • 표준선: limit policy를 실제 config에 구현할 때 Edge Config And Runtime Refresh Contract에 field/default/range와 refresh 분류를 함께 반영한다. 현재 active contract에는 미구현 config field를 선반영하지 않는다.
  • 후속 SDD: OpenAI-compatible 출력 검증 필터 SDD