- Archive provider-resource-admission-ownership milestone/SDD - Align contract: CP-edge wire, runtime refresh, node runtime, OpenAI surface - Update roadmap: phase state, priority queue - Update specs: control-plane ops, OpenAI surface, edge execution, provider pool refresh - Add node runtime supervisor bootstrapping and unit tests - Fix control-plane edge registry handler and http_views - Fix edge model queue admission and long context queue tests
29 KiB
SDD: Provider Resource Admission Ownership 정합화
위치
- Milestone: Milestone 문서
- Phase: PHASE.md
상태
[승인됨]
SDD 잠금
- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목: 없음
문제 / 비목표
- 문제: provider config는 capacity와 long-context slot을 provider/resource 속성으로 선언하지만 Edge admission은 model group별 counter를 사용한다. 같은 provider를 여러 외부 model key가 공유하면 각 model group이 독립 capacity를 보고 중복 예약할 수 있으며, snapshot은 이를 사후 합산할 뿐 admission을 막지 못한다. provider가 선택되기 전 model group queue에는 첫 후보 provider의 queue policy가 암묵 적용되고, 한 model group의 lease 반환이 다른 group의 대기 요청을 깨우지 못한다. Node는 최초 Edge 연결이 성공한 뒤에만 reconnect supervisor를 시작하므로 부팅 시 네트워크가 늦거나 Edge가 unavailable이면 initial dial 실패로 process가 종료될 수 있고, 유한 retry가 소진된 장기 단절에서도 외부 process supervisor 없이는 영구 이탈할 수 있다. Node disconnect 때 live registry에서 항목이 사라지는 것만으로는 provider resource의 offline 전이와 queue terminal 조건이 하나의 상태로 보장되지 않아, 구성된 provider가 관측 표면에서 사라지거나 후보가 없어진 요청이 queue timeout까지 남을 수 있다. accepted reconnect가 provider resource를 새 generation 후보로 복구하더라도 관련 queue를 직접 깨우지 않으면 기존 waiter는 새 요청, config refresh 또는 다른 lease 반환 같은 무관한 trigger까지 고립될 수 있다. 현재 disconnect listener는 registry ownership 확인보다 먼저 node event를 발행하므로 거절된 duplicate connection의 close도 live Node의 queue release를 유발할 수 있고, resource cleanup이 drop 가능한 event bus subscriber에 의존해 correctness 전이가 누락될 수 있다.
- 비목표:
- 특정 model alias나 RTX5090 환경 설정을 이 SDD에서 추가하지 않는다.
- Prometheus/Grafana 관측 기능과 provider runtime/model acquisition lifecycle을 구현하지 않는다.
- Node 연결이 유지된 상태에서 provider endpoint를 능동 probe해 정적 provider health를 대체하는 runtime health state machine은 구현하지 않는다.
- provider
request_timeout_ms와 surfacetimeout_sec의 deadline 결합 규칙을 재설계하지 않는다. total_context_tokens를 요청별 input/output KV token reservation ledger로 확장하지 않는다.- cross-Edge distributed semaphore나 cloud-wide quota를 설계하지 않는다.
- 외부 OpenAI-compatible API의
model필드 의미를 변경하지 않는다.
Source of Truth
| 영역 | 기준 | 메모 |
|---|---|---|
| Roadmap | Milestone 문서 | 목표, 범위, 기능 Task와 완료 상태의 장기 원장 |
| Admission Code | apps/edge/internal/service/model_queue_*, provider_resolution.go, queue_reservation.go |
provider lease, model queue 대기와 release/wake-up 구현 기준 |
| Connectivity Code | apps/edge/internal/transport/connection_handlers.go, apps/edge/internal/service/status_provider.go, apps/node/internal/bootstrap/runtime_supervisor.go, apps/node/internal/transport/client.go |
Node initial connect/reconnect supervision, disconnect 감지, provider offline snapshot과 heartbeat fallback 기준 |
| Node Execution Code | apps/node/internal/node/run_handler.go, apps/node/internal/node/tunnel_handler.go |
normalized/tunnel adapter capacity 안전장치 구현 기준 |
| Config Contract | Edge Config And Runtime Refresh Contract | provider/model config 의미와 live refresh 기준 |
| Wire Contract | Edge-Node Runtime Wire Contract | provider tunnel, normalized run과 snapshot 의미 기준 |
| Control Plane Contract | Control Plane-Edge Wire Contract | EdgeNodeSnapshot.connected와 provider snapshot 운영 상태 기준 |
| OpenAI Surface Contract | OpenAI-Compatible API Contract | static model catalog 유지와 unavailable dispatch error 변환 기준 |
| External Provider | 없음 | Lemonade/vLLM/Ollama endpoint는 resource 소비자이며 admission source of truth가 아니다 |
| User Decision | D01 — Edge provider-pool 소유 | 아직 provider가 선택되지 않은 대기 요청의 max_queue와 queue_timeout_ms는 Edge 공통 admission policy가 소유한다. provider는 provider별 실행 속성만 소유한다. |
State Machine
lease lifecycle과 provider resource 점유 상태는 서로 직교한다. lease는 queued → reserved → tracked → released로 이동하고, resource의 available/full/over-capacity 및 long-slot 상태는 현재 counter와 config에서 파생한다. 한 요청은 reserved 전까지 특정 provider slot을 소유하지 않는다.
각 Node 연결은 registry가 발급한 내부 connection_generation과 현재 client ownership을 가진다. provider lease와 dispatch handoff는 해당 generation에 묶이고, 현재 owner가 아닌 rejected/stale client의 disconnect callback은 resource state나 관측용 disconnect event를 변경하지 않는다.
Node daemon의 연결 lifecycle은 connecting → connected → retrying을 반복하고 local shutdown 또는 명시적인 terminal policy에서만 stopped로 이동한다. retryable한 initial dial/register 실패와 연결 수립 뒤 disconnect는 모두 retrying으로 들어가며, 명시적으로 reconnect.max_attempts=0을 설정한 unlimited profile은 원격 네트워크/Edge unavailable만으로 stopped가 되지 않는다. max_attempts를 생략한 기존 설정은 기본값 10을 유지한다.
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|---|---|---|---|
node:connecting |
daemon 시작 또는 retry cadence 도달 | node:connected, node:retrying, node:stopped |
accepted registration, retryable connect 실패, non-retryable bootstrap 오류 또는 local shutdown |
node:connected |
Edge registration이 accepted되고 session handler가 준비됐다 | node:retrying, node:stopped |
remote disconnect 또는 local shutdown |
node:retrying |
retryable initial connect 실패 또는 established session disconnect가 발생했다 | node:connected, node:retrying, node:stopped |
다음 retry, accepted registration, 유한 retry exhaustion 또는 local shutdown |
node:stopped |
local shutdown, non-retryable bootstrap 오류 또는 명시적인 유한 retry exhaustion이 발생했다 | 없음 | process terminal 결과 |
lease:queued |
모든 현재 후보가 full/long-full이지만 queue policy가 대기를 허용한다 | lease:reserved, lease:released |
전역 scheduler admission, timeout 또는 caller cancel |
lease:reserved |
원자적 admission이 resource_key counter를 올리고 고유 lease identity를 발급했다 |
lease:tracked, lease:released |
dispatch 전 run/tunnel 추적 또는 request build/send 실패 |
lease:tracked |
dispatch lifecycle이 lease 반환 책임을 handoff 받았다 | lease:released |
complete/error/cancel, tunnel terminal/close, node disconnect |
lease:released |
같은 lease identity의 counter 반환이 한 번 반영됐다 | 없음 | 중복 terminal/release는 idempotent no-op |
resource:excluded |
provider disabled, capacity 0, Node transport disconnect 또는 기존 health filter 제외 중 하나가 적용된다 | resource:available |
config, connectivity와 health의 모든 exclusion 원인이 해소된다 |
resource:available |
eligible이고 in_flight < capacity다 |
resource:full, resource:over-capacity, resource:excluded |
lease 획득 또는 config/connectivity 변경 |
resource:full |
in_flight == capacity다 |
resource:available, resource:over-capacity, resource:excluded |
lease 반환 또는 config/connectivity 변경 |
resource:over-capacity |
live refresh로 in_flight > capacity가 됐다 |
resource:full, resource:available, resource:excluded |
기존 lease 반환 또는 config/connectivity 변경 |
resource:long-available |
long_context_capacity == 0이거나 long_in_flight < long_context_capacity다 |
resource:long-full, resource:excluded |
long lease 획득 또는 config/connectivity 변경 |
resource:long-full |
long_context_capacity > 0이고 long_in_flight >= long_context_capacity다 |
resource:long-available, resource:excluded |
long lease 반환, long capacity refresh 또는 connectivity 변경 |
Interface Contract
- 계약 원문: Edge Config And Runtime Refresh Contract, Edge-Node Runtime Wire Contract, Control Plane-Edge Wire Contract, OpenAI-Compatible API Contract
- 입력:
model_group_key: 외부 OpenAI-compatible model key이며 후보 provider 집합과 model queue를 선택한다.resource_key:node_id + provider_id로 식별하는 provider 실행 resource다.capacity,long_context_capacity: provider resource가 선언하는 일반·long 동시 실행 slot이다.total_context_tokens:context_window_tokens * long_context_capacity의 정적 load/refresh 검증 기준이다. runtime lease counter로 사용하지 않는다.context_class: long-context slot이 필요한 요청인지 판정하는 입력이다.priority,enabled: provider가 소유하는 tie-break와 admin intent다. Node transport connectivity는 provider resource의 연결 기반 availability 입력으로 사용한다. 기존 provider health는 소비하되 Node 연결이 살아 있는 endpoint를 능동 probe하는 새 health state machine은 만들지 않는다.connection_generation: registry가 accepted Node connection에 부여하는 내부 generation이다. wire/config field가 아니며 candidate, lease와 dispatch handoff의 stale 여부를 판정한다.- Node reconnect policy: initial connect와 established-session reconnect에 공통인
reconnect.interval_sec와reconnect.max_attempts를 사용한다. 명시적인max_attempts=0은 local shutdown까지 retryable transport/register failure를 재시도하는 unlimited mode, 생략은 기존 기본값10, 양수는 기존 호환 유한 attempt limit, 음수는 config validation error다.interval_sec생략도 기존 기본값10을 유지하고 unlimited mode에서는 양수만 허용한다. 유한 mode의 exhaustion은 명시적인 non-zero terminal 결과로 노출한다. - queue policy: Edge가 소유하는
provider_pool.max_queue와provider_pool.queue_timeout_ms를 모든 provider-pool 대기 요청에 공통 적용한다.max_queue는 model group 경계를 합친 Edge provider-pool 전체 pending request 상한이며,queue_timeout_ms는 각 pending request의 공통 최대 대기 시간이다. provider별max_queue와queue_timeout_ms는 canonical policy로 사용하지 않는다.
- 출력:
- provider lease: 고유
lease_id, 선택된resource_key,connection_generation, model group, 일반/long reservation과 lifecycle owner를 포함한다. - queue result: dispatched, queued, full, unavailable, timeout, cancelled와 provider/context blocking reason을 구분한다. live candidate가 없어진 요청은 원래 queue timeout을 기다리지 않고 unavailable로 수렴한다. unavailable은 내부 typed reason으로 두고 OpenAI-compatible 표면은 기존 dispatch error envelope/status를 유지하며, 새 외부 field나 status code가 필요하면 outer contract 변경으로 별도 검토한다.
- provider snapshot: admission과 동일한 resource state의 connectivity availability, capacity, in-flight와 long-context 상태를 보고한다. 구성에 존재하지만 연결이 끊긴 Node는
EdgeNodeSnapshot.connected=false, 그 provider는status=unavailable,health=offline, effectivecapacity/in_flight/queued/long_context_capacity/long_in_flight/long_queued=0으로 유지한다. reconnect하면 configured capacity를 다시 노출한다. online provider의queued는 Edge provider-pool queue에서 해당 provider를 현재 후보로 포함하는 고유 대기 요청 수이며 여러 provider snapshot에 동시에 나타날 수 있고,long_queued는 그중 long 요청 수다. 이 값은 provider가 소유한 실제 queue depth가 아니라 Edge queue에서 파생한 candidate pressure다.
- provider lease: 고유
- scheduler와 release:
- Node connectivity supervisor는 최초 dial/register 전에 시작한다. retryable한 network unreachable, connection refused, timeout과 일시적인 Edge unavailable은 initial connect와 established-session reconnect에서 같은 bounded cadence로 한 번에 하나씩 재시도하며, 명시적인
max_attempts=0에서는 원격 unavailable 기간만으로 process를 종료하지 않는다. local config/credential validation처럼 재시도로 회복되지 않는 오류, 양수max_attemptsexhaustion과 local shutdown은 각각 terminal error 또는 정상 종료로 구분한다. - OS 시작 배치나 service는 Node process를 실행하는 host lifecycle만 소유한다. Task Scheduler 또는 외부 restart loop의 존재를 Node 연결 복구 correctness 전제로 두지 않는다.
- queue item은 최초 enqueue 때 단조 증가하는
enqueue_seq를 한 번 부여받는다. lease 반환, capacity 증가, provider disable/re-enable 또는 node disconnect 뒤 모든 model group에서 가장 이른 현재 dispatch 가능한 item부터 다시 평가한다. - queue item에 저장된 과거 candidate snapshot을 refresh 또는 disconnect 뒤 그대로 신뢰하지 않는다. dispatch 직전에 live config/registry에서 eligibility, capacity, priority와 served target을 다시 확인해 disabled/disconnected provider를 제거하고 남은 후보나 명시적인 unavailable 결과로 수렴시킨다.
- Node transport의 정상 close/disconnect callback은 먼저 현재 client ownership/generation을 compare-and-fence한다. 성공한 현재 owner만 같은 admission state에서 resource를 excluded로 전이하고 lease 반환·queue 재평가를 수행한 뒤 관측용
node.disconnectedevent를 발행한다. rejected duplicate 또는 stale callback은 이 전이와 event를 만들지 않는다. - resource fencing, lease 반환과 queue wake-up은 drop 가능한 event bus delivery에 의존하지 않는 직접 lifecycle 경로다. event bus는 console, Control Plane relay, replay 같은 관측 fanout으로만 사용한다.
- disconnect 처리와 admission이 경쟁할 때 candidate generation을 reserve와 dispatch handoff에서 다시 확인해 fenced generation에는 새 lease 또는 handoff를 허용하지 않는다. 이미 tracked된 요청은 즉시 disconnect terminal 결과로 종료할 수 있지만 다른 Node로 투명 이관하지 않으며, lease 반환은 동일한 idempotent lifecycle을 따른다.
- Node reconnect는 같은 configured resource identity에 새
connection_generation을 부여해 available 후보로 복구하고 관련 queue의 candidate를 live config/registry에서 다시 구성해 즉시 pump한다. model catalog는 설정된 외부 key로 유지하며 availability는 dispatch eligibility와 terminal 결과를 결정한다. - orderly close를 받을 수 없는 전원 단절·network blackhole에서는 heartbeat timeout이 fallback detection bound다.
- long slot 때문에 막힌 오래된 item은 실행 가능한 뒤 normal item을 막지 않는다. 같은 dispatch 가능성 안에서는
enqueue_seq를 보존한다. - Edge provider lease가 cross-model capacity의 authoritative state다. Node adapter-local gate는 stable adapter instance별로 normalized/tunnel 실행을 함께 제한하되 Edge state를 복제하거나 분산 lease로 취급하지 않는 defense-in-depth 안전장치다.
- capacity를 in-flight보다 낮추면 기존 lease는 유지하고
over-capacity동안 새 lease를 발급하지 않는다. capacity 증가와 provider 재활성화는 관련 queue를 즉시 pump한다. - provider-pool queue policy의 canonical config는 Edge의
provider_pool블록이다. migration 동안 Edge 공통 값이 없고 legacy provider queue 값이 모두 동일할 때만 그 값을 공통 policy로 승격할 수 있다. 값이 다르면 first-candidate 값을 선택하지 않고 validation error로 거부한다. canonical Edge 값이 있으면 legacy provider queue 값은 policy 결정에 사용하지 않으며, provider/adapter의 legacy queue 필드는 제거 대상으로 둔다.
- Node connectivity supervisor는 최초 dial/register 전에 시작한다. retryable한 network unreachable, connection refused, timeout과 일시적인 Edge unavailable은 initial connect와 established-session reconnect에서 같은 bounded cadence로 한 번에 하나씩 재시도하며, 명시적인
- 금지:
- 같은
resource_key의 capacity나 long-context counter를 model group별 source of truth로 복제하지 않는다. - snapshot 사후 집계를 admission 판단 대신 사용하지 않는다.
- queue item을 아직 선택되지 않은 provider의 in-flight 또는 lease로 계산하지 않는다.
- provider가 가득 찬 상태를 피하려고 같은 물리 provider를 model alias별 provider ID로 중복 등록하지 않는다.
- Node provider tunnel이 adapter capacity 안전장치를 우회해 두 번째 upstream 요청을 열지 않는다.
- config refresh에서 기존 lease를 삭제하거나 진행 중 요청을 새 capacity에 맞추기 위해 강제 취소하지 않는다.
- resource cleanup이나 queue wake-up의 유일한 trigger를 best-effort event subscriber로 두지 않는다.
- current connection owner 확인 전에
node.disconnectedevent를 발행하지 않는다. - retryable initial connect 실패를 Fx startup terminal error로 바로 반환해 connectivity supervisor를 시작하지 못하게 하지 않는다.
- unlimited retry profile의 Node 생존성을 Windows Task Scheduler나 외부 process restart에만 의존하지 않는다.
- provider-pool queue의 공통
max_queue또는queue_timeout_ms를 provider나 model group 속성으로 복제하지 않는다.
- 같은
Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|---|---|---|---|---|
| S01 | provider-state |
capacity 1인 같은 provider를 두 model group이 참조한다 | 두 model key 요청을 동시에 admission한다 | 하나만 provider lease를 얻고 다른 요청은 다른 후보를 선택하거나 대기하며 합산 in-flight는 1이다 |
| S02 | lease-lifecycle |
일반 또는 long lease가 reserved/tracked 상태이고 event subscriber가 정상 또는 포화 상태다 | send 실패, complete/error/cancel, tunnel terminal/close 또는 node disconnect가 단독·경쟁으로 발생한다 | event delivery 성공 여부와 무관하게 같은 lease는 정확히 한 번 반환되고 일반·long counter가 음수가 되거나 남지 않는다 |
| S03 | cross-group-wakeup |
여러 model group item이 같은 provider를 기다리고 앞선 long item 하나만 long-full로 막혀 있다 | 다른 group의 lease가 반환되거나 capacity가 늘어난다 | 전역 enqueue_seq상 가장 이른 현재 dispatch 가능한 item부터 예약되고 막힌 long item이 normal item을 막거나 특정 group이 굶지 않는다 |
| S04 | long-state |
두 model group이 같은 provider의 long-context slot을 공유한다 | 두 long 요청을 동시에 admission하고 잘못된 total-context 설정을 load/refresh한다 | 전역 long slot을 넘는 요청은 dispatch되지 않고 total_context_tokens < context_window_tokens * long_context_capacity 설정은 거부된다 |
| S05 | tunnel-guard |
capacity 1 adapter에서 한 normalized 또는 tunnel 실행이 진행 중이다 | Edge queue를 우회한 두 번째 tunnel 요청이 Node에 도착한다 | 두 번째 upstream provider 요청이 열리지 않고 명시적인 concurrency 결과가 반환된다 |
| S06 | queue-ownership |
Edge에 공통 provider_pool queue policy가 있고 서로 다른 legacy queue 값을 가진 여러 provider와 공유 provider model alias가 있다 |
여러 model group의 provider-pool 요청이 대기한다 | Edge 공통 max_queue가 전체 pending request 상한으로 적용되고 공통 queue_timeout_ms가 각 요청에 적용되며, first-candidate나 model group이 policy owner가 되지 않고 legacy 충돌은 명시적으로 거부된다 |
| S07 | snapshot-source |
여러 model group이 같은 provider를 사용하거나 multi-candidate 상태로 기다린다 | 실행·대기·반환 중 snapshot을 조회한다 | 일반·long in-flight는 lease state와 같고 provider queued 값은 Edge queue에서 파생한 candidate pressure로 계산되어 실제 Edge queue 총합과 구분된다 |
| S08 | refresh-consistency |
provider에 진행 중 lease와 대기 item이 있다 | capacity를 in-flight 아래로 낮췄다가 높이거나 provider를 disable/re-enable한다 | 기존 요청은 유지되고 과점유 중 새 admission은 차단되며 모든 관련 대기 item이 새 후보·capacity로 재평가된다 |
| S09 | cross-model-tests |
ornith:35b와 ornith-fast 형태의 공유 provider fixture가 있다 |
capacity, long, fairness, cancel, send 실패, delayed initial connect, long-outage reconnect, graceful disconnect, heartbeat-timeout fallback, duplicate/stale disconnect, event fanout 포화, reconnect된 provider의 기존 waiter 즉시 wake-up과 refresh 회귀·race suite를 실행한다 | 모든 경로에서 provider peak concurrency와 최종 counter가 기대값을 만족한다 |
| S10 | contract-spec-sync |
구현과 검증이 완료되었다 | 계약과 living spec을 동기화한다 | config, Edge-Node, Control Plane-Edge, OpenAI-compatible 계약과 agent-spec이 queue/snapshot/error 의미 및 Edge-Node 책임 경계를 같은 의미로 설명한다 |
| S11 | capacity-smoke |
local 또는 dev-corp에 capacity 1 공유 provider와 두 model key가 구성되어 있다 | 두 model key를 동시에 호출하고 종료까지 관측한다 | backend peak concurrency가 1이고 대기/fallback 뒤 모든 counter가 0으로 회복된다 |
| S12 | disconnect-exclusion |
accepted connection과 같은 Node ID의 rejected duplicate 또는 이전 generation callback이 존재하고 provider가 admission 가능하다 | stale client가 닫히거나 현재 Node가 정상 close/heartbeat timeout으로 disconnect되고 admission이 경합한다 | stale close는 live resource와 event를 바꾸지 않고, current owner close만 즉시 fencing되어 fenced generation의 새 lease/dispatch handoff가 없으며 기존 lease는 한 번 반환된다 |
| S13 | disconnect-queue-resolution |
실행 중 요청과 같은 Node/provider를 후보로 가진 대기 요청이 있고 event subscriber가 정상 또는 포화 상태다 | authoritative Node disconnect가 적용된다 | event delivery와 무관하게 모든 관련 queue가 live 후보로 즉시 재평가되어 fallback하고 후보가 없으면 queue timeout을 기다리지 않고 unavailable로 종료되며 reservation이 남지 않는다 |
| S14 | offline-snapshot |
구성에 등록된 Node/provider가 연결되어 있다 | disconnect 후 status를 조회하고 같은 identity로 reconnect한다 | offline snapshot은 connected=false, provider status=unavailable, health=offline, effective capacity/counter 0을 보고하고 admission에서도 제외하며, reconnect 뒤 이전 generation의 orphan/excluded 상태에 막히지 않는 새 generation의 configured capacity와 available eligibility를 복구하되 model catalog entry는 유지한다 |
| S15 | reconnect-candidate-recovery |
대상 Node/provider가 offline이고 같은 model group의 다른 live 후보가 full이라 요청이 대기 중이다 | 같은 resource identity의 새 Node connection이 accepted된다 | 관련 queue가 live config/registry에서 후보군을 다시 구성해 새 요청, config refresh 또는 다른 lease 반환 없이 기존 waiter를 즉시 pump하고, stale/rejected connection 전이는 후보 복구나 wake-up을 일으키지 않는다 |
| S16 | node-connectivity-supervision |
명시적인 reconnect.max_attempts=0과 양수 interval_sec를 가진 Node가 네트워크 또는 Edge보다 먼저 시작되고 provider config는 유효하다 |
retryable initial connect 실패가 기존 bounded retry 구간보다 오래 지속된 뒤 Edge가 reachable해진다 | 같은 Node process가 한 번에 하나의 연결만 bounded cadence로 재시도하고 registration이 accepted되면 session과 provider를 정확히 한 번 활성화하며 OS Task Scheduler나 외부 restart loop를 요구하지 않는다 |
| S17 | node-connectivity-supervision |
Node가 initial 또는 established-session retry 중이거나 reconnect config를 load한다 | local shutdown, non-retryable local config/credential 오류, 양수 max_attempts exhaustion, 음수 max_attempts, unlimited profile의 0 이하 interval_sec 또는 max_attempts 생략이 발생한다 |
local shutdown은 대기 중 retry를 즉시 취소해 정상 종료하고 fatal 오류와 유한 exhaustion은 hot loop 없이 관측 가능한 non-zero terminal 결과로 종료하며 잘못된 config는 validation error로 거부되고 생략한 max_attempts는 기존 기본값 10을 유지한다 |
Evidence Map
| Scenario | Required Evidence | agent-task 연결 |
완료 Evidence 기대 |
|---|---|---|---|
| S01, S02, S03, S04, S06, S07, S08, S09, S12, S13, S14, S15 | Edge provider resource/connectivity/queue deterministic tests, connection ownership·event-drop 회귀와 race 검증 | agent-task/m-provider-resource-admission-ownership-alignment/... |
complete.log의 Roadmap Completion에 provider-state, lease-lifecycle, long-state, cross-group-wakeup, queue-ownership, snapshot-source, refresh-consistency, disconnect-exclusion, disconnect-queue-resolution, offline-snapshot, reconnect-candidate-recovery, cross-model-tests와 최종 test/race 명령을 기록 |
| S16, S17 | Node connectivity supervisor deterministic tests와 delayed-network/Edge startup smoke | agent-task/m-provider-resource-admission-ownership-alignment/... |
node-connectivity-supervision Roadmap Completion, initial/established retry·shutdown·fatal/exhaustion test와 OS Task Scheduler 비의존 startup evidence를 기록 |
| S05 | Node normalized/tunnel adapter-local capacity 테스트 | agent-task/m-provider-resource-admission-ownership-alignment/... |
tunnel-guard Task와 Node package test 결과를 기록 |
| S10 | agent-contract와 agent-spec diff 및 link 검증 | agent-task/m-provider-resource-admission-ownership-alignment/... |
contract-spec-sync Task와 갱신 문서 목록을 기록 |
| S11 | local 또는 dev-corp capacity 1 cross-model smoke log | agent-task/m-provider-resource-admission-ownership-alignment/... |
capacity-smoke Task, backend peak concurrency 1과 종료 후 counter 0 회복 evidence를 기록 |
Cross-repo Dependencies
- 없음
Drift Check
- Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- Evidence Map이 code-review/complete.log에서 검증 가능하다.
- agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- 사용자 리뷰가 필요한 항목은
USER_REVIEW.md에만 남겼다.
사용자 리뷰 이력
- 2026-07-18: D01 provider-pool queue policy 소유권 사용자 검토 요청.
- 2026-07-19: provider별 속성은 provider가 소유하고 공통 속성은 Edge가 소유한다는 원칙에 따라 Edge 공통
provider_poolqueue policy로 확정하고 리뷰를 해결했다. user_review_0.log
작업 컨텍스트
- 표준선: provider capacity, long-context slot, priority처럼 provider별로 달라지는 실행 속성의 source of truth는 provider resource state다. 여러 후보와 model group에 공통인 provider-pool queue depth와 timeout은 Edge가 소유한다. model group은 외부 model key, 후보 집합과 요청의 논리적 분류만 소유한다.
total_context_tokens는 현재 계약과 validation 구현에 따라 정적 long-context 설정 검증값으로 유지한다. 현재 리팩토링이 완료된 파일 구조를 후속 구현 기준으로 사용한다. - 후속 SDD: Node 연결이 살아 있는 동안 provider endpoint/Lemonade 프로세스 장애를 탐지하는 runtime health probe source of truth와 provider/surface request timeout 결합 규칙은 별도 Milestone이 생성될 때 설계한다.