- Update roadmap milestones and phase docs across multiple phases - Update plan, code-review, create-roadmap, update-roadmap, finalize-task-routing skills - Update dev-corp-runtime-deploy, dev-runtime-deploy, orchestrate-agent-task-loop skills - Refactor agent-task-loop dispatch script - Add streamgate Go package (commit_boundary, evidence_tail, filter_registry, stream_release) - Add test inventory files (dev, dev-corp, unified) - Update test smoke tests and rules for dev/dev-corp - Update docs/edge-local-dev-guide and e2e scripts - Update inventory-query Go package - Remove deprecated templates and inventory.yaml files - Add orchestrate-agent-task-loop tests
24 KiB
Milestone: Stream Evidence Gate Core
위치
- Roadmap: ROADMAP.md
- Phase: PHASE.md
목표
OpenAI-compatible output validation, incomplete tool-call syntax, missing tool-call gate, runtime output integrity, agent workflow terminal hook이 같은 stream hold/release 구현을 각각 만들지 않도록 공통 stream mechanics를 제공한다. 이 Core는 provider/agent codec이 정규화한 response-start/content/reasoning/tool/terminal/error event를 받아 evidence-based pending tail, Unicode rune 경계, filter-before-release, transport/terminal commit 경계를 책임진다. 같은 Milestone 안의 Gate Coordinator는 Filter Registry snapshot, 병렬 evaluation barrier, deterministic Arbiter와 RecoveryPlan Coordinator를 조합해 모든 활성 filter 결과를 모은 뒤 하나의 release/terminal/recovery action만 결정한다. 반복·문법·LLM judge 같은 의미 판정은 소비 Milestone에 남기되, filter가 직접 재요청하거나 request를 재조립하지 않는다.
상태
[진행중]
구현 잠금
- 상태: 해제
- SDD: 필요
- SDD 문서: SDD.md
- SDD 사유: 여러 endpoint/codec 소비자가 공유하는 stream event lifecycle, release commit, config policy, terminal/error 경계가 바뀌는 공통 runtime 모듈이다.
- 잠금 해제 조건: 없음
- 결정 필요: 없음
범위
- codec이 생성한 response-start/text/reasoning/tool-call/terminal/error normalized event의 공통 stream gate interface
- 기본
stream_hold.evidence_runes=500과 environment/model/provider별 override, 활성 filter requirement를none|rolling_window|terminal_gate|fragment_gate로 합성하는 Unicode rune 기준 pending/look-behind 및 hard bound. provider-error처럼 hold 없는 filter는 event-kind subscription만 선언 - response status/header와 opening role/event를 first safe release까지 staging하는 filter-before-release,
transport_uncommitted|stream_open|terminal_committedcommit state, single terminal sequence - evidence 미충족 provider idle/timeout의 no-release terminal error 처리
- request 시작 시 고정한 config/registry generation 안에서 environment/model group/model/provider별 enable/disable, required capability와 fail policy를 해석하는 공통 Filter Registry와 Go
Filterinterface/shared helper - 동일 immutable
EvidenceBatch를 활성 filter에 병렬 전달하고 모든 결과를 모으는 single-flight evaluation barrier, bounded ingress backpressure 및 deterministic decision arbiter - filter의
RecoveryIntent를 하나의RecoveryPlan으로 합성하고 strategy별 budget보다 우선하는 request 전체 recovery hard cap을 적용한 뒤, current attempt ownership 종료, optional one-shot plan 준비, request-local bounded lossless ingress snapshot의 endpoint별 rebuild와 host dispatch를 순서대로 cycle당 한 번만 수행하는 공통 coordinator packages/go/streamgate의 transport-agnostic Core와 Edge/Node host/consumer가 구현하는AttemptDispatcher,AttemptController, optionalRecoveryPlanPreparer,RequestRebuilder,ReleaseSink경계- stable consumer/filter/rule id, plan preparer lifecycle과 sanitized evidence를 기존 request/run/provider/model correlation으로 연결해 observability sink로 emit하는 raw-free
FilterObservationtimeline - terminal failure의 최대 4단계 sanitized 원인 사슬을
ReleaseSink까지 전달하고, endpoint host가 외부 OpenAI-compatible 오류 하나로 직렬화하는 공통 경계 - OpenAI-compatible output validation의 반복 guard/provider-error retry, tool-call syntax gate, missing tool-call gate, runtime integrity filter, agent workflow terminal hook이 공통 mechanics를 소비하는 문서 포인터와 adoption contract
기능
Epic: [stream-core] Evidence-based Stream Gate
의미 판정과 stream event mechanics를 분리해 재사용 가능한 release barrier를 제공한다.
- [event-contract] codec이 전달하는 normalized response-start/text/reasoning/tool-call/terminal/error event, event kind별
hold|release_candidate|terminal_success_candidate|terminal_error_candidatebase disposition, immutableEvidenceBatch, filter decision과 optional intent를 정의한다. terminal failure는 raw 값을 포함하지 않는 최대 4단계FailureCauseChain과 endpoint host가 한 번만 직렬화할TerminalResult로 수렴한다. unmatched provider error는 terminal-error candidate이며 filter pass로 content처럼 release되지 않는다. Core는 raw parser나 caller 제품명을 해석하지 않는다. 검증: OpenAI/agent-family codec이 staged response-start, success terminal, matched/unmatched error의 같은 event/base-disposition table과 보조 recovery 실패의 bounded cause/single external error fixture를 통과한다. - [evidence-tail]
rolling_window는 pending tail에 기본 500 Unicode rune의 evidence가 쌓이거나 terminal event가 올 때 filter를 평가하고 통과한 safe prefix만 release한다. 같은 effective rune 수의 committed look-behind만 남겨 경계 반복을 판정하며 evidence 충족 외의 시간 기반 대기 조건을 더하지 않는다.terminal_gate는 schema처럼 terminal 전체가 필요한 channel만 명시적max_buffer_runes까지 보류하고 초과 시 fail-closed하며,fragment_gate는 완성 전 tool fragment만 bounded hold한다. look-behind/pending은 filter 입력에만 쓰고 observation/log에 복제하지 않는다. replace-attempt recovery에서는 초기화하고 continuation recovery에서는 committed look-behind와 release cursor를 보존한다. 검증: 200/500-rune rolling, terminal-gate hard-limit, fragment bound, UTF-8 한국어 경계 반복, replace/continue reset 차이, terminal-before-evidence fixture가 통과한다. - [commit-boundary] provider response status/header와 normalized opening role/event를 staging하고 첫 safe release 때 한 번만 commit한다.
transport_uncommitted에서는 exact/schema replay가 가능하고,stream_open뒤에는 이미 보낸 event를 rollback/exact replay하지 않는다. 다만continuation_repair와 protocol-safe terminal replacement는 released safe prefix를 보존하고 terminal/tool side effect 전 같은 downstream stream을 이어갈 수 있다. evidence 미충족 idle timeout은 pending을 보내지 않고 pre-commit HTTP error 또는 post-commit terminal SSE/error로 끝낸다. 검증: response-start 뒤 body 전 matched 500 replay, eager role/header 금지, post-open exact replay 금지, post-open continuation, idle no-release, single terminal fixture가 통과한다. - [filter-registry] Go 상속 대신
Filterinterface와 shared helper struct를 두고 stable filter id, applicability, hold mode/bound, context-aware evaluation, sanitized evidence를 공통 계약으로 제공한다. request 시작 시 config/registry generation을 immutable snapshot으로 고정하고, admission 전 필수 filter capability로 후보를 제한한 뒤 실제 model/provider가 정해진 각 attempt에서 active set을 다시 resolve한다. enforcement는blocking|observe_only로 두고 blocking error/deadline은 fail-closed, observe-only error/deadline은 명시적observe_error로 정규화한다. duplicate stable id와 filter가 runtime에 enablement/failure mode를 바꾸는 동작을 거부한다. 검증: model/provider on/off, policy precedence, config reload 격리, required capability no-candidate, provider 전환 re-resolution, duplicate id/failure mode fixture가 통과한다. - [parallel-evaluation] Gate Coordinator가 immutable
EvidenceBatch를 event-applicable/trigger-ready filter에 동시에 전달한다. subscribed event가 없는 filter는not_applicable_for_epoch, release를 막는 static trigger 미충족 filter는deferred_by_requirement로 Core가 정규화한다. 모든 active filter가 evaluated/error/not-applicable/deferred outcome 중 하나를 가진 뒤 Arbiter를 호출한다. epoch는 single-flight이고 다음 ingress는 bounded backpressure를 받으며 caller cancel/host shutdown만 no-release로 중단한다. 검증: event-only/not-applicable와 blocking deferred 구분, barrier, 역순 완료, race, ingress/backpressure, cancel/deadline/failure fixture가 통과한다. - [decision-arbiter] normalized event의 base disposition과 모든 active filter outcome을 deterministic하게 합성한다. fatal은 terminal, eligible recovery는 terminal-error/release candidate를 대체할 수 있고 conflict는 priority/stable id로 하나만 선택한다. blocking deferred는 bounded hold, not-applicable은 release를 막지 않는다. unmatched provider error와 invalid terminal은 filter pass여도 base terminal을 유지한다. 검증: release+pass, hold+not-applicable, pass+blocking-deferred, unmatched error+pass, matched error+recovery, deferred+violation, fatal+retry와 replacement conflict가 single action을 반환한다.
- [policy-hook] repeat, malformed syntax, missing tool-call, schema, terminal integrity, workflow replacement는
rolling_window|terminal_gate|fragment_gate와 대상 channel/hard bound를, provider-error 같은 hold 없는 filter는none과 subscribed event kinds를 등록한다. 모두 enforcement, semantic detector와 optional intent를 독립 policy로 제공하고 Core는 channel별 requirement를 합성한다. 검증: rolling/schema/tool fragment/provider-error event-only mixed composition과 decision/intent 표가 통과한다. - [recovery-coordinator] Arbiter가 선택한 하나의
RecoveryIntent를 strategy별 budget, fault recovery의 request 전체max_recovery_attempts_totalhard cap, caller cancel, terminal/tool side effect, strategy별 commit eligibility, same-plan re-entry guard로 검증해exact_replay|continuation_repair|schema_repair|managed_continuation중 하나의RecoveryPlan으로 만든다. fault recovery 전체 cap은 최초 provider 실행을 제외한 exact/schema/provider-error/일반 repair dispatch를 합산하며 기본값이자 MVP 절대 상한은 3회다. provider output-cap terminal의 managed continuation은 오류 recovery가 아니라 progress trajectory로 분류하고, request 시작 시 고정한 attempt cap·context window·reserve·consumer가 고정한 optional caller logical output cap과 Rebuilder가 조립 뒤 측정한rebuilt_prompt_tokens의 다음 allowance가 남을 때만 별도ManagedTrajectoryBudget으로 dispatch한다. assistant prefix는 rebuilt prompt에 포함되므로 누적 output과 이중 계상하지 않는다. fault cap은 strategy cap보다 우선하고 어느 cap이든 소진되면 다른 fault filter/strategy로 우회하지 않고 terminal로 끝낸다. exact/schema는transport_uncommitted에서만 replace-attempt로 실행하고, continuation은stream_open에서도 released safe prefix/cursor를 보존한 continue-stream plan으로 실행할 수 있다. plan 선택 시 Core는 preparer/continuation에 필요한 bounded request-local snapshot과 cursor를 immutable하게 고정한다. 새 dispatch 전에 현재AttemptController의 provider transport ownership만 idempotent cancel/close하고 실패하면 두 provider를 병행하지 않고 terminal로 끝낸다. 선택된 plan이 consumer별 보조 준비를 요구하면 Core는 all-complete/Arbiter와 attempt ownership 종료 뒤 optionalRecoveryPlanPreparer를 plan/idempotency key당 최대 1회, bounded deadline으로 호출한다. preparer는 Filter 병렬 평가에 참여하거나 provider 선택/retry/fallback을 실행하지 않으며 실패는 dispatch 없이 terminal로 끝난다. recovery budget은 preparer 호출이 아니라 실제 outbound recovery dispatch 직전에만 소비하고 bounded preparation snapshot은 성공·실패·cancel 뒤 release한다. cycle당 plan/dispatch는 하나지만 두 cap에 여유가 있으면 다음 attempt 결과를 새 cycle로 평가한다. 검증: 동시 violation, pre/post-commit strategy matrix, 0/1/3회 policy와 4회 이상 config rejection, strategy cap과 request 전체 cap의 교차 소진, bounded multi-cycle, snapshot-freeze/transport-only abort, abort-before-prepare/dispatch, preparer 단일 호출/deadline/failure-no-budget/snapshot-release, cancel/side-effect/re-entry fixture가 통과한다. - [request-rebuilder] transport-agnostic Coordinator가 기본값이자 MVP 절대 상한 16 MiB이며 request 시작 시 immutable하게 고정되는
max_ingress_snapshot_bytes안의 request-local lossless ingress snapshot과 selected directive를 endpoint/family별RequestRebuilder에 전달하고, hostAttemptDispatcher가 새 admission과 concrete provider/model/auth 계산을 수행한다. host는io.ReadAll전에http.MaxBytesReader또는 동등한 limit+1 판정으로 raw body를 제한하며 policy는 16 MiB 이하로만 낮출 수 있다. OpenAI JSON endpoint는 raw body를 canonical lossless representation으로 보존하고, 다른 lossless tree는 byte-preserving round-trip fixture가 있는 codec만 허용한다.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을 넘으면 dispatch 전에 거부한다. ingress limit 초과는 snapshot 생성/provider dispatch 전에 fail-closed하며 filter/로그/cross-request cache에 원문을 노출하지 않고 auth도 저장하지 않는다. rebuilt request도 같은 limit을 다시 확인하고 request 종료/cancel/overflow에서 retained object를 release한다. dispatcher는 actual model/provider/execution path와 normalized event source를 가진AttemptBinding을 반환하며 normalized↔tunnel path 전환도 같은 Core cycle에 연결한다. 검증: caller raw body/unknown field 보존, continuation/schema typed patch, Chat/Responses serializer, auth 비보존, provider/path 전환, pre-read raw body limit-1/limit/limit+1, exact-limit body+typed-view overflow, 16 MiB 초과 config rejection, shared-backing logical peak accounting과 rebuild 전후 overflow/no-dispatch/no-raw-log/release fixture가 통과한다. - [vertical-slice] production semantic filter보다 먼저 model/provider별 on/off 가능한
NoopFilter와 test-onlyInjectedViolationFilter로request snapshot → host dispatch → staged response-start → Core evidence hold → parallel filters → all-complete Arbiter → RecoveryPlan → abort → optional prepare → rebuild → single re-admission → release한 사이클을 얇게 완성한다. 검증: disabled passthrough, enabled pass, eager header/role 없음, injected violation one retry success, optional preparer success/failure, two violation single retry, provider/path switch, filter failure mode, backpressure, retry exhausted terminal fixture가 통과한다. - [filter-observation] Core가 filter/coordinator decision마다 기존 request/run/provider/model correlation과 config generation, attempt/epoch,
consumer_id,filter_id,rule_id, hold mode, event kind, effective/pending rune, decision/error policy, commit state, terminal reason, plan id/strategy/shared attempt, preparer id/status/deadline outcome, 최대 4개의 sanitized cause code와 consumer-provided sanitized evidence를 하나의FilterObservationtimeline event로 emit한다. 저장·보존은 기존 observability sink가 소유한다. preparer input/output와 raw output/prompt/tool args/result/auth 값은 허용하지 않으며 filter가 평가되지 않은 delta마다 observation을 만들지 않는다. 검증: 병렬 filter 시작/완료, response staging, arbitration, release/terminal/recovery/abort/prepare/rebuild/dispatch가 하나의 request timeline에서 stable id, bounded cause와 sanitized fingerprint/count/offset만으로 추적된다. - [adoption-doc] OpenAI-compatible 출력 검증 필터, OpenAI-compatible Incomplete Tool Call Syntax Gate, OpenAI-compatible Runtime Output Integrity Filter, LLM 판별 기반 Missing Tool Call 재시도 Gate, 에이전트 작업 루프 오케스트레이션 MVP에 이 Core의 소비 경계, 각자 남는 semantic policy/intent, stable filter id와
FilterObservationmapping, 공통 RecoveryPlan 및 boundedFailureCauseChain/endpoint별 단일 외부 오류 직렬화 사용을 기록한다. recovery consumer는 strategy/request-total cap을, ingress snapshot consumer는 bounded snapshot을 사용한다. 검증: 각 Milestone이 자신에게 적용되는 Core interface와 limit을 명시하고 raw stream buffer·request snapshot/rebuild·retry loop·공개 오류 사슬 직렬화를 재구현하지 않는다.
완료 리뷰
- 상태: 없음
- 요청일: 없음
- 완료 근거: 기능 Task가 아직 충족되지 않았다.
- 검토 항목:
- 모든 consumer가 동일한 normalized event/release contract를 사용하고 raw parser를 Core에 중복하지 않는다.
- rolling/terminal/fragment hold, staged response-start, all-filter completion barrier, single-flight backpressure, deterministic single action, idle no-release, strategy별 post-commit eligibility, common RecoveryPlan/rebuild/dispatcher, single terminal sequence와 raw-free
FilterObservationtimeline 검증이 Evidence Map과 일치한다.
- agent-ui 상태 반영: 해당 없음
- 리뷰 코멘트: 없음
범위 제외
- 반복루프, missing tool-call, malformed syntax, schema violation의 semantic detector 자체 구현
- LLM judge 호출, filter별 corrective prompt 의미 생성, provider/model selection 알고리즘 자체
- OpenAI Chat Completions SSE나 agent-family CLI JSON raw protocol parsing을 Core가 직접 소유하는 방식
- 기본 streaming 경로의 전체 응답 buffer, terminal-gate hard bound 없는 buffer, 시간 경과에 따른 미검증 출력 release, 이미 downstream으로 보낸 event의 rollback
- 새 public OpenAI API field, caller 제품명 분기, cross-request output cache
- cross-request 오류 지문·중복 횟수·사건 상태 저장, 연결 저장소 소스 분석, LLM 수정 제안, 프로젝트 작업 문서 생성, 사용자 승인, 변경 요청·병합·배포를 조율하는 범용 오류 수정 플랫폼. Core는 이에 필요한 raw-free terminal/observation event를 방출하는 경계까지만 소유한다.
작업 컨텍스트
- 관련 경로:
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 - 표준선(선택):
packages/go/streamgate의 Gate Coordinator는host dispatch → codec normalized event → staged response-start/evidence gate → attempt-local active filter registry → single-flight parallel evaluation/all-complete barrier → decision arbiter → release 또는 RecoveryPlan Coordinator → current attempt abort → optional one-shot prepare → host rebuild/single re-admission한 사이클을 소유한다. codec은 raw bytes를 event로 바꾸고 filter는 의미 판정과 intent만 제공하며 Core는 Edge/Node internal package를 import하지 않는다. - 표준선(선택): 현재 Edge의
writeProviderTunnelResponse는 provider response-start에서 status/header를 flush하고streamChatCompletion은 role chunk를 즉시 쓴다. Core 채택 시 두 eager write를 hostReleaseSinkstaging 뒤로 이동해야 하며,buffered_sse.go/completeChatCompletion의 tool-validation retry loop는 공통 RecoveryPlan Coordinator로 흡수해 중복 실행하지 않는다. - 표준선(선택): 현재 Chat/Responses handler는 request body를 unbounded
io.ReadAll로 읽는다. Core 채택 시 host request reader가 snapshot 생성 전 기본/절대 상한 16 MiB를 적용하고 limit+1 overflow를 구분해야 하며, 읽은 뒤 길이만 검사하는 방식은 허용하지 않는다. - 표준선(선택): cross-request 오류 수집·중복 집계·소스 분석·수정 제안·승인·변경 요청·병합·배포는 별도 범용 플랫폼으로 프로젝트화한다. Core는 요청 응답을 그 비동기 분석 때문에 지연하지 않고, bounded
FailureCauseChain,TerminalResult, raw-freeFilterObservation을 공통 관측 경계에 전달하는 역할만 맡는다. - 표준선(선택): 기본 evidence window는 500 Unicode rune이다. terminal event는 부족한 evidence에서도 최종 filter evaluation을 트리거할 수 있으나 idle timeout은 release가 아니라 terminal error다.
- 표준선(선택): Coordinator는 별도 Milestone으로 분리하지 않고 이 Milestone 안의 컴포넌트로 둔다. Gate Coordinator가 single-flight evaluation과 state ownership을 맡고 RecoveryPlan Coordinator가 strategy eligibility, strategy별 budget과 request 전체 hard cap을 맡는다. filter evaluation은 동일 immutable batch에서 병렬화하되 request mutation, abort, optional plan prepare, retry budget, rebuild, dispatch는 직렬화한다.
- 표준선(선택): managed provider length continuation은 filter가 provider output-cap terminal을 의미 판정해
managed_continuationintent를 낼 때만 Core가 같은 downstream stream의 safe prefix, look-behind, release cursor와 terminal gate ownership을 보존해 진행한다. 이는 exact/schema/오류 repair의 최대 3회 fault cap과 별도의 context-window trajectory이며, 다음 dispatch allowance는 작은 attempt cap과remaining_caller_logical_cap(if set)과context_window - rebuilt_prompt_tokens - reserve의 최소값으로 계산한다. provider attempt cap은 내부 운영 단위이며 caller cap은 논리 요청 전체에 한 번만 적용한다. rebuilt prompt는 original request와 보존한 assistant prefix를 포함하므로 누적 output을 별도 합산하지 않는다. context 여유 소진, caller cancel, complete tool call/side effect, 미완성 fragment 복구 실패에서는 단일 final terminal로 수렴한다. context 여유 또는 caller logical cap 소진에서만 host가 endpoint-native logicallengthterminal과 단일 종료 marker를 한 번 직렬화하며, attempt 중간 terminal은 commit하지 않는다. - 표준선(선택):
FilterObservation은 Core가 emit하는 공통 내부 관측 envelope이며, 저장·보존은 기존 observability sink가 소유한다. Core는 기존 request/run/provider/model correlation과 stream lifecycle 필드를 연결하고, consumer는 stableconsumer_id/filter_id/rule_id와 fingerprint·count·offset 같은 sanitized evidence만 제공한다. 이는 외부 API field나 raw output 보존 정책을 바꾸지 않는다. - 선행 작업: 없음
- 후속 작업: OpenAI-compatible 출력 검증 필터, OpenAI-compatible Incomplete Tool Call Syntax Gate, OpenAI-compatible Runtime Output Integrity Filter, LLM 판별 기반 Missing Tool Call 재시도 Gate, 에이전트 작업 루프 오케스트레이션 MVP
- 확인 필요: 없음