---
spec_doc_type: spec
spec_id: runtime/provider-pool-config-refresh
status: 부분
source_evidence:
  - type: contract
    path: agent-contract/inner/edge-config-runtime-refresh.md
    notes: Edge config, provider pool, config refresh, Node payload 연결 계약
  - type: code
    path: packages/go/config/provider_types.go
    notes: provider/model catalog 설정 타입, response_stall_timeout_ms validation과 effective helper
  - type: code
    path: packages/go/execution/liveness.go
    notes: Stall timeout default, validation, and effective helper used by config
  - type: code
    path: packages/go/config/edge_types.go
    notes: Edge root provider_pool canonical queue policy 타입과 기본값
  - type: code
    path: packages/go/config/load.go
    notes: Edge config load, default 적용, relative-only single-request 템플릿 파일 해석
  - type: code
    path: packages/go/singlerequesttemplate/template.go
    notes: 단일 요청 Plan/Review 템플릿 문법과 기본 템플릿, digest helper
  - type: code
    path: apps/edge/internal/openai/single_request_preset_binding.go
    notes: Admission 시 effective Plan/Review 템플릿 freeze와 built-in default fallback
  - type: code
    path: packages/go/config/validate.go
    notes: provider/model references, managed credential exclusivity, TLS/key requirements, and numeric bounds
  - type: code
    path: configs/edge.yaml
    notes: provider-pool 권장 설정 예시와 live refresh 설정 예시
  - type: code
    path: apps/edge/internal/service/provider_resolution.go
    notes: provider 후보 해석과 capacity/priority policy 구성
  - type: code
    path: apps/edge/internal/service/model_queue_admission.go
    notes: provider 전역 lease, 공통 pending 상한, global enqueue 순서와 long-context admission
  - type: code
    path: apps/edge/internal/service/model_queue_release.go
    notes: Lease release, disconnect/reconnect candidate rebuild, global queue pump, and generation/sequence-fenced runtime health transitions
  - type: code
    path: apps/edge/internal/service/model_queue_snapshot.go
    notes: Config-preserving effective runtime health and capacity projection
  - type: code
    path: apps/edge/internal/service/provider_health_observability.go
    notes: Post-decision bounded metrics and safe structured-log projection
  - type: code
    path: apps/edge/internal/service/status_provider.go
    notes: lease state와 candidate pressure 기반 online/offline provider snapshot
  - type: code
    path: packages/go/config/protocol_profile.go
    notes: ConcreteProtocolProfile, overlay validation, capability admission, model/operation/effort normalization
  - type: test
    path: packages/go/config/protocol_profile_test.go
    notes: Effort exact/nearest-lower mapping, tools 조합, 상향 매핑 거부 검증
  - type: code
    path: apps/edge/internal/configrefresh/classify.go
    notes: dry-run/apply classification과 changed path report 생성
  - type: code
    path: apps/edge/internal/bootstrap/runtime.go
    notes: mutable config apply, runtime snapshot 교체, Node refresh push
  - type: code
    path: apps/edge/internal/node/mapper.go
    notes: provider-first NodeConfigPayload compile과 OpenAI-compatible provider label 보존
  - type: test
    path: packages/go/config/provider_catalog_config_test.go
    notes: provider/model catalog load와 queue 설정 검증
  - type: test
    path: packages/go/config/provider_catalog_validation_config_test.go
    notes: provider/model 참조와 validation 검증
  - type: test
    path: packages/go/config/usage_attribution_config_test.go
    notes: attribution policy 기본값·enum과 direct provider binding 검증
  - type: test
    path: packages/go/config/stream_evidence_gate_config_test.go
    notes: Stream Evidence Gate 기본값, recovery cap과 ingress 상한 검증
  - type: test
    path: apps/edge/internal/node/mapper_test.go
    notes: provider-first adapter payload, Seulgivibe alias/provider label 검증
  - type: test
    path: apps/edge/internal/configrefresh/node_runtime_classify_test.go
    notes: node runtime과 model catalog refresh classification 검증
  - type: test
    path: apps/edge/internal/configrefresh/provider_classify_test.go
    notes: provider field refresh classification 검증
  - type: test
    path: apps/edge/internal/service/model_queue_admission_test.go
    notes: cross-model capacity, terminal unavailable, disconnect/event-drop 회귀 검증
  - type: test
    path: apps/edge/internal/service/status_provider_test.go
    notes: cross-model candidate pressure와 offline/reconnect snapshot 검증
  - type: test
    path: apps/edge/internal/service/provider_health_overlay_test.go
    notes: Runtime-unavailable admission/snapshot gating, config immutability, and exact CAPABILITIES recovery
  - type: test
    path: apps/edge/internal/service/provider_health_observability_test.go
    notes: Normalized/tunnel decision projection, stale/recovery counters, private registry isolation, and lock-safe observation
  - type: test
    path: apps/edge/internal/bootstrap/reconnect_readiness_integration_test.go
    notes: dispatch-ready reconnect가 기존 queued waiter를 실제 Node terminal까지 수렴시키는 검증
  - type: test
    path: scripts/e2e-provider-capacity-smoke.sh
    notes: loopback OpenAI-compatible provider에서 two-alias capacity-1 queue와 final counter 회복 검증
---

# 스펙: Provider Pool과 Config Refresh

## 목적

Edge 설정에서 provider-pool이 어떻게 모델 실행 후보를 고르고, 어떤 설정 변경이 재시작 없이 반영되는지 설명한다.

## 기능 목록

| 기능 | 설명 |
|------|------|
| model catalog | `models[].id`는 외부 OpenAI-compatible `model` key이자 provider-pool `ModelGroupKey`다. |
| usage attribution policy | `models[].usage_attribution`은 `provider|model_group`만 허용하고 생략 시 provider 귀속으로 해석한다. model-group 귀속은 운영자의 명시적 opt-in이다. |
| provider mapping | `models[].providers`는 provider id를 실제 served model name으로 매핑한다. |
| node provider catalog | `nodes[].providers[]`는 Node 아래 resource/provider catalog이며 provider id는 Edge config에서 전역 유일해야 한다. |
| response-stall timeout | `response_stall_timeout_ms`는 provider별 response-stall timeout이다. zero/omitted는 `60000ms`, invalid negative/overflow 값은 validation error이며 selected candidate의 effective 값은 normalized/tunnel request에 보존된다. |
| config validation | config load가 provider id 참조, served model membership, numeric bounds, long-context budget을 검증한다. |
| provider 후보 필터링 | dispatch는 dispatch-ready connection을 가진 Node의 provider 후보 중 catalog match, enabled, healthy/available, capacity 조건을 만족하는 후보만 사용한다. protocol profile capability(`messages`, `chat`, `responses`, `streaming`, `tool_calling`, `count_tokens`, `models`)는 operation별 admission에 사용된다. |
| provider 전역 capacity/priority dispatch | `node_id + provider_id` lease가 여러 model group의 일반·long in-flight를 합산한다. available 후보 중 낮은 in-flight를 고르고 동률이면 낮은 `priority`와 round-robin을 적용한다. |
| provider-pool 공통 queue policy | Edge root `provider_pool.max_queue`가 모든 model group의 전체 pending 상한을, `queue_timeout_ms`가 각 pending request timeout을 소유한다. |
| global queue 재평가 | lease 반환, capacity/priority/enabled refresh, disconnect/reconnect 뒤 global enqueue 순서에서 현재 dispatch 가능한 가장 이른 waiter부터 candidate를 다시 구성한다. |
| provider snapshot | 일반·long in-flight는 provider lease state, queued 값은 Edge queue에서 해당 provider를 후보로 포함하는 고유 pending request pressure에서 계산한다. offline provider는 catalog identity를 유지하고 effective 수치를 0으로 보고한다. |
| runtime provider health overlay | A confirmed current bound unavailable stall lowers a separate `(node_id, connection_generation, provider_id)` overlay. The provider is excluded from effective admission and its snapshot projects unavailable with zero effective capacity/counters, while configured health remains unchanged. Only a later exact higher-sequence available CAPABILITIES probe recovers it; inconclusive evidence is a no-op. Post-decision metrics/logs expose only closed source, health, decision, and state-change values; they contain no resource identity or raw request/response data. |
| mixed provider execution path | 같은 model group의 OpenAI-compatible provider와 Ollama/native provider를 같은 후보군으로 두며, 선택된 provider capability로 passthrough 또는 normalized 실행 경로를 결정한다. OpenAI-compatible provider는 `openai_chat`, `anthropic_messages`, 또는 `openai_responses` driver로 해석된다. |
| provider operation normalization | `protocol_profiles[].normalization.effort` records operation-scoped provider wire, supported normalized grades, tools compatibility, and explicit token-budget compatibility. Request admission uses these facts rather than caller identity; exact grade misses use only the nearest lower declared grade and never upgrade. |
| long-context admission | estimated input token이 threshold 이상이면 `context_class=long`으로 분류하고, provider long slot이 있으면 일반 capacity slot과 함께 점유한다. |
| config refresh dry-run/apply | loopback admin HTTP `POST /refresh`가 candidate config를 dry-run 또는 apply한다. |
| refresh classification | listener, Edge identity, bootstrap path, adapter structural 변경 등은 restart-required로 분류한다. |
| Stream Evidence Gate config | `openai.stream_evidence_gate` provides runtime activation, request-total/strategy fault recovery caps, ingress snapshot bounds, and per-filter capability/enforcement/Unicode hold policy; it is currently restart-required. |
| mutable apply | 적용 가능한 변경은 Edge `Cfg`, `NodeStore`, service/input model catalog, OpenAI long-context threshold를 copy-on-write로 교체한다. |
| single-request snapshot isolation | An admitted single-request binding is independent of subsequent model catalog, execution preset, or provider pool changes. Refresh replaces the live catalog and preset snapshots used by future admissions; already-admitted bindings retain their original values. |
| fixed single-request policy | `execution_presets[].single_request` declares an operator-owned immutable plan→work→review light path with absolute wall-clock (`≤1800000ms`), stage-timeout (`≤600000ms`), tool-iteration (`≤64`), and output-byte (`≤16MiB`) caps. Selector and plan/review stages require `reasoning_effort=high`; work stage forbids it. `workspace_ref` is opaque (never raw path/credential/Node/endpoint). single_request preset rejects `workspace_tools`. Catalog and mapping changes are live-apply and affect only new request snapshots; admitted bindings retain their frozen values across refresh. |
| single-request effective templates | Optional `templates` (`plan_file`/`review_file`) load bounded Markdown relative to the directory containing `edge.yaml` only. The PLAN grammar yields deterministic `P1..Pn`; the REVIEW grammar permits only worker item status, changes, verification, and deviations, rejecting old reviewer-final templates. Absolute and empty paths are rejected before any filesystem access; non-regular files, sizes over 8192 bytes, non-UTF-8 content, and invalid grammar fail closed at load. Each file falls back to its built-in default independently, and refresh diff evidence reports SHA-256 digests only, never template paths or contents. |
| effective-template admission freeze | Admission copies the resolved effective Plan/Review pair into the immutable binding, and that pair survives binding clone and workspace revalidation. A later refresh swaps the preset snapshot used by future admissions only: already-admitted work keeps its frozen pair, while a request admitted after the refresh observes the refreshed pair. Templates select internal stage input and internal artifact shape only; caller request and response schemas are unchanged. |
| operator-owned workspace catalog | `nodes[].workspaces[]` is the operator-owned bounded capability catalog for each node. Each entry is keyed by a globally unique, trimmed `ref`, declares `platform` in the closed `darwin|linux` implementation set, and retains the existing absolute clean root, closed operations, approved commands, environment allowlist, and bounded byte/time limits. Refs remain globally unique and any catalog change is `restart_required`. Empty catalogs are backward-compatible on any host. A non-empty catalog requires a supported Node host and every entry must match that host before any root is opened; Windows, unknown hosts, and cross-platform catalogs fail closed. The catalog is delivered by the Node-private typed config payload and retained as opened immutable runtime authority. Raw roots and command details never enter presets, public responses, provider requests, or metadata; operating system is runtime evidence rather than a caller selector. |
| Node config refresh push | 변경이 있으면 Edge가 dispatch-ready Node에 node-specific `NodeConfigRefreshRequest`를 push한다. accepted지만 pending인 Node는 register response config를 적용한 뒤 ready가 될 때까지 push 대상이 아니다. |
| Node registry swap | Node는 refresh payload로 새 adapter registry를 만들고 router registry를 swap한다. old registry stop은 active run이 있으면 drain 이후로 지연한다. |
| principal token mapping config | `openai.principal_tokens[]`는 raw token 없이 `token_ref`, `token_hash_sha256`, `principal_ref`, optional alias를 관리하고 OpenAI usage metering의 principal/token label 후보를 제공한다. 같은 principal에 여러 token entry를 둘 수 있다. |
| credential mode switch | `credential_plane.enabled` selects managed projection/lease ownership and requires every credential-bearing hop to use TLS plus externally mounted issuer/recipient/keyring material. |
| managed/legacy exclusivity | Managed mode rejects static principal mappings, legacy bearer/provider-auth forwarding, configured provider credential headers/env/args, and endpoint user-info. Legacy mode retains request-time `openai.provider_auth` compatibility. |
| managed route binding | An authenticated projection route binds slot, profile, upstream model, selector, credential revision, route revision, and generation before provider-pool admission and lease acquisition. |
| provider auth forwarding config | In legacy mode only, `openai.provider_auth` forwards a caller-supplied raw provider token to the selected compatible provider tunnel header. |
| Seulgivibe provider aliases | `seulgivibe_claude`, `seulgivibe_openai` provider type은 runtime adapter type을 `openai_compat`로 정규화하고, 명시 provider label이 없으면 canonical Seulgivibe alias를 Node payload provider label로 보존한다. |

## 범위

- 포함: Edge config load/default/validation, Stream Evidence Gate 설정, provider-pool dispatch, queue admission, long-context admission, config refresh classification/apply, Node config refresh payload.
- 제외: 개별 adapter의 provider API 호출 세부, OpenAI HTTP request/response shape, Control Plane credential lifecycle API details, private credential contents.

## 주요 흐름

```mermaid
sequenceDiagram
  participant Caller
  participant Service as Edge service
  participant Queue as Provider queue
  participant Node

  Caller->>Service: SubmitRun(model)
  Service->>Queue: dispatch-ready provider 후보 선택(capacity + priority)
  Queue-->>Service: selected provider + served target
  alt selected provider supports OpenAI-compatible call
    Service->>Node: ProviderTunnelRequest(adapter, served target, response-stall timeout)
  else selected provider is Ollama/native
    Service->>Node: RunRequest(adapter, served target, response-stall timeout)
  end

  participant Operator
  participant Refresh as Config refresh
  Operator->>Refresh: POST /refresh(apply)
  Refresh->>Refresh: load, validate, classify
  Refresh->>Node: NodeConfigRefreshRequest
```

## 계약

- `iop.edge-config-runtime-refresh`: `agent-contract/inner/edge-config-runtime-refresh.md`
- `iop.edge-node-runtime-wire`: `agent-contract/inner/edge-node-runtime-wire.md`
- proto 원문: `proto/iop/runtime.proto`
- `execution_presets[].single_request` is the operator-owned fixed single-request policy. Absolute caps: `wall_clock_ms ∈ [1, 1800000]`, `timeout_ms ∈ [1, 600000]`, `timeout_ms ≤ wall_clock_ms`, `max_tool_iterations ∈ [1, 64]`, `max_output_bytes ∈ [1, 16777216]`. Stages enforce exactly plan→work→review with `reasoning_effort=high` on selector and plan/review, forbidden on work. `workspace_ref` is opaque (never raw path/credential/Node/endpoint). single_request preset rejects `workspace_tools`. Catalog/mapping changes are live-apply; admitted bindings are snapshot-isolated across refresh.
- `execution_presets[].single_request.templates` is optional. `plan_file` and `review_file` must be non-empty paths relative to the directory containing `edge.yaml`; absolute and empty values are rejected before the loader opens anything. Each file is loaded as a regular file of at most 8192 valid UTF-8 bytes and must satisfy the documented template grammar; an omitted file falls back to its built-in default independently of the other. Refresh diff evidence reports only SHA-256 digests of the effective templates. Admission freezes the effective pair into the request binding, so a refreshed template reaches only requests admitted after the refresh.

## 설정/데이터/이벤트

- `long_context_threshold_tokens` 기본 예시는 `100000`이고 0 이하 값은 config load에서 거부된다.
- `credential_plane.enabled=true` requires Edge-Node server TLS, an enabled TLS Control Plane connector, and OpenAI ingress TLS when enabled. The Edge cannot combine managed mode with `openai.bearer_token`, `openai.principal_tokens[]`, `openai.provider_auth`, or static provider credential sources.
- Node managed mode requires Edge transport TLS, `recipient_key_id`/recipient private-key path, issuer key id/public-key path, and a bounded replay cache. All cert/key/keyring values are external file references and credential-plane changes are restart-required.
- `protocol_profiles` is the top-level catalog of custom overlays. A `ProtocolProfileConf` supplies `base`, `driver`, `base_url`, operation paths, `auth`, `capabilities`, `model_mapping`, `normalization`, and `extensions`; `base` inheritance is separate from legacy provider-type normalization.
- `normalization.effort[operation]` must reference a declared operation and a recognized wire. Grade keys are the closed `none|low|medium|high|xhigh|max` order. Runtime mapping takes the exact key or nearest lower key; canonical mapped values cannot exceed the source grade. `with_tools` and `token_budget` describe whether that operation preserves the corresponding semantic combination.
- `nodes[].providers[].profile` selects a catalog entry. Config normalization resolves that selection (or a legacy type alias) into the runtime-only `RuntimeProfile` snapshot; the source YAML remains a selector plus catalog, not a per-model overlay.
- `nodes[].providers[].response_stall_timeout_ms` is validated at config load: zero/omitted resolves to `60000ms`; safe positive values are retained; negative and duration-overflow values are rejected. Its effective value is immutable for the selected provider attempt and survives queue re-resolution for both execution paths.
- Profile catalog and provider-selector changes are restart-required. Snapshot immutability describes loaded runtime state and does not make those changes live-applicable.
- `ConcreteProtocolProfile.MapModel(model)`은 provider의 model alias 정규화를 수행한다. provider가 model mapping을 정의하면 IOP external `model` key를 provider served target으로 변환한다.
- `ConcreteProtocolProfile.ResolveOperationURL(op)` returns the complete resolved upstream URL. Absolute operation URLs are returned unchanged, while relative operation paths are joined once to the normalized base URL; the listed `/v1/...` values are operation-path inputs, not return values.
- Built-in GLM profiles: `glm` (General API, `https://api.z.ai/api/paas/v4`) and `glm_coding` (Coding Plan, `https://api.z.ai/api/coding/paas/v4`) are independent identities sharing the `openai_chat` driver, Bearer auth, and `models`/`chat`/`streaming`/`tool_calling` capabilities. Neither declares the `responses` operation or capability. Endpoint selection is driven by external model IDs mapped to distinct provider IDs, never by an extra request field. No automatic fallback exists between the two profiles; selecting one profile routes to one endpoint exclusively. Both are comment-only in the example config and disabled by default. Coding Plan usage is subject to current Z.AI subscription terms.
- `validOperationsByDriver`는 driver별 허용 operation의 closed set이다. `openai_chat`은 `models`, `chat_completions`, `responses`, `count_tokens`를 허용한다. `anthropic_messages`는 `models`, `messages`, `count_tokens`를 허용한다. `openai_responses`는 `models`, `responses`, `count_tokens`를 허용한다.
- `openai.stream_evidence_gate.enabled` 기본값은 `false`다. request fault recovery는 0..3, strategy cap은 request-total 이하, ingress snapshot은 1..16777216 bytes이며 설정 변경은 restart-required다.
- `filters[].hold_evidence_runes` is bounded `1..65536` and defaults to 500. For `repeat_guard` it controls the Unicode pending/look-behind evidence window, not a time-based release or a cross-request retention period.
- Blocking repeat capability admission is re-resolved for the actual provider/path while the request-start filter policy, history snapshot, recovery ordinals, and temperature candidate order remain generation-stable across provider switches.
- `provider_pool.max_queue`는 0/생략 시 기본값 `16`, `queue_timeout_ms`는 생략 시 `30000`이고 명시적 0은 timeout 없음이다. canonical root key가 없을 때만 서로 같은 legacy provider queue pair를 승격하며 값이 다르면 load를 거부한다.
- `nodes[].providers[].capacity`와 `long_context_capacity`는 provider resource 속성이고 같은 provider를 공유하는 model alias가 합산 점유한다. 같은 key의 legacy adapter capacity는 transport 상한이므로 provider admission capacity는 그 이하일 수 있고 상한 초과만 거부한다. `total_context_tokens`는 runtime ledger가 아니라 `context_window_tokens * long_context_capacity` 정적 validation 값이다.
- `models[].usage_attribution`은 생략 시 `provider`, 명시값은 `provider|model_group`만 허용한다. 변경은 model catalog policy 변경으로 live apply되며 `models["<id>"].usage_attribution` 경로로 보고한다.
- provider `enabled=false`는 dispatch pool에서 제외하지만 adapter process lifecycle 변경을 의미하지 않는다.
- Runtime health is not a config-refresh field. The overlay never rewrites `nodes[].providers[].health`, is discarded across connection generations, and participates only in effective candidate eligibility and snapshot projection.
- accepted registration은 provider candidate를 바로 복구하지 않는다. Node가 config 적용과 handler 설치 뒤 ready ack를 받아야 해당 generation이 candidate, connected snapshot, refresh push 대상이 되며 이 transition이 stranded provider-pool waiter를 재평가한다.
- provider capacity, long-context capacity, priority, enabled toggle, root queue policy와 model generation policy는 live apply 대상으로 분류된다. apply는 기존 lease를 보존하고 이후 admission 및 모든 관련 waiter의 live candidate/deadline을 새 값으로 재평가한다.
- `response_stall_timeout_ms` 변경은 restart-required다. request hard timeout, queue timeout, heartbeat/disconnect, client response-idle timeout과 watchdog timer lifecycle은 별도 소유권이다.
- Edge listener, control plane, openai/a2a listener, bootstrap artifact path, node 추가/삭제, node token/alias, adapter 설정 변경은 restart-required 대상이다.
- `openai.principal_tokens[]`는 `token_ref`와 `token_hash_sha256` 중복을 거부하고, raw token 원문은 tracked config에 저장하지 않는다.
- 여러 `openai.principal_tokens[]` entry가 같은 `principal_ref`를 공유할 수 있으며, 이때 `token_ref`가 앱/통합/용도별 사용량 분해 기준이다.
- `openai.principal_tokens[]` 변경은 credential/hash 변경으로 보고 restart-required로 분류된다.
- In legacy mode, `openai.provider_auth.enabled=true` resolves omitted fields to `from_header=X-IOP-Provider-Authorization`, `target_header=Authorization`, `scheme=Bearer`, and `required=true`. Managed mode rejects the setting and caller-supplied legacy provider credentials.
- Seulgivibe provider catalog는 top-level `models[]`의 정적 provider mapping을 source of truth로 사용한다. provider `/models` endpoint는 IOP catalog source가 아니다.
- `models[]`는 mixed provider group과 Ollama-only group을 모두 표현할 수 있다. Ollama는 후보에서 제외하지 않고 `capacity`/`priority`로 운영자가 낮은 동시성과 낮은 선호도를 표현한다.
- refresh result는 changed nodes/providers/models와 restart-required paths를 stable non-nil slice로 보고한다.

## 검증

- `go test ./packages/go/config`
- `go test ./apps/edge/internal/configrefresh`
- `go test ./apps/edge/internal/service`
- `go test ./apps/edge/internal/node`
- `go test ./apps/node/internal/adapters ./apps/node/internal/node`
- `go test ./apps/edge/internal/bootstrap -run '^TestActualNodeReconnectReadyPumpsQueuedWaiterExactlyOnce$'`
- `./scripts/e2e-provider-capacity-smoke.sh`

## 한계와 주의사항

- Active health coverage is intentionally limited to confirmed response-stall evidence and explicit exact-target CAPABILITIES recovery. It is not a general background provider health polling system.
- refresh admin API는 operator-local 표면이다. 접근 제어 없이 public interface에 노출하지 않는다.
- Stream Evidence Gate의 request-local lifecycle과 지원 OpenAI 경로는 `agent-spec/runtime/stream-evidence-gate.md`에서 관리한다.
- adapter structural 변경은 contract상 restart-required로 분류된다. Node handler가 registry swap을 지원하더라도 Edge refresh classifier가 허용한 변경만 apply해야 한다.
- no-change apply는 runtime snapshot을 교체하지만 Node push는 생략한다.
- `NodeRuntimeConfig.concurrency`는 legacy metadata이며 provider-pool admission의 node-wide capacity로 쓰지 않는다.
- provider가 full인 상태는 일시적인 queue block이지만, disconnect/disable로 live candidate가 모두 사라지면 waiter는 기존 queue timeout까지 남지 않고 unavailable로 종료된다.
- credential, private endpoint, bearer token 원문은 tracked config/docs/spec에 남기지 않는다.
- TLS private keys, the at-rest keyring, issuer private key, Node recipient private key, lease envelopes, and slot plaintext must remain external/runtime-only and must not appear in refresh reports, logs, metrics, or task artifacts.
- principal token mapping은 완성된 사용자/테넌트 source of truth가 아니라 외부 `principal_ref` 또는 내부 alias에 대한 얇은 운영 매핑이다.
- Seulgivibe provider endpoint와 raw user token은 환경별 private config 또는 request-time header로 주입해야 하며 tracked 예시에 실제 값을 남기지 않는다.

## 변경 기록

- 2026-08-14: Updated the frozen single-request template grammar for deterministic PLAN IDs and a worker-to-reviewer REVIEW handoff; legacy reviewer-final custom templates now fail closed at loading/admission.
- 2026-08-08: Synchronized the implemented workspace catalog/runtime boundary with closed `darwin|linux` admission, exact catalog/host matching before root open, empty-catalog compatibility, and Windows/unknown fail-closed scope.
- 2026-07-07: 현재 코드, 계약, config 예시 기준으로 bootstrap spec 작성.
- 2026-07-07: 기능 목록 중심으로 축소하고 주요 흐름을 Mermaid sequence diagram으로 정리.
- 2026-07-10: OpenAI usage metering용 principal token hash mapping config와 restart-required 기준을 반영.
- 2026-07-11: Seulgivibe OpenAI-compatible provider aliases, provider auth forwarding config, static catalog 기준을 현재 코드/계약/테스트 기준으로 반영.
- 2026-07-12: Model Group Mixed Provider Dispatch 종료 검토 기준으로 mixed provider group과 Ollama-only group, capacity/priority 가중 예시를 반영.
- 2026-07-18: 저장소 구조 분해 뒤 config, provider resolution/admission, split test의 `source_evidence`를 현재 경로로 동기화.
- 2026-07-22: pending accepted connection과 dispatch-ready connection을 구분하고, ready ack 뒤에만 provider candidate 복구·refresh push·queued waiter pump가 일어나는 현재 동작을 반영.
- 2026-07-22: root provider-pool queue policy, cross-model provider lease, global queue 재평가, refresh lease 보존과 connectivity 기반 snapshot 의미를 현재 구현·계약·테스트 기준으로 동기화.
- 2026-07-28: Stream Evidence Gate 설정 기본값·상한·restart-required 분류와 runtime spec 포인터를 반영.
- 2026-07-31: model별 provider-default/model-group opt-in attribution policy와 live-apply refresh 분류를 반영했다.
- 2026-08-01: protocol profile catalog/selector ownership, runtime-only profile resolution, and restart-required refresh semantics were synchronized with config source.
- 2026-08-02: Synchronized the managed credential mode switch, TLS/key prerequisites, legacy-auth exclusion, projected route binding, and restart-required credential-plane classification with current validation/runtime source.
- 2026-08-02: Added the `glm_coding` built-in profile alongside `glm` (General API), both exposing only `models` + `chat_completions` with Bearer auth and no Responses. Endpoint selection is driven by external model IDs mapped to distinct provider IDs. No automatic fallback between General API and Coding Plan. Both are comment-only in the example config and disabled by default. Coding Plan usage is subject to current Z.AI subscription terms.
- 2026-08-04: Added provider response-stall timeout validation/default, restart-required refresh classification, selected-candidate propagation, and Node retention. Timer/watchdog lifecycle remains out of scope.
- 2026-08-05: Added the separate generation-scoped runtime provider health overlay, effective admission/snapshot exclusion, config-health immutability, and exact higher-sequence CAPABILITIES recovery.
- 2026-08-05: Added post-decision provider-health operational evidence with bounded counters and structured logs, isolated from overlay state and provider identity.
- 2026-08-09: Synchronized the single-request effective-template boundary: relative-only `plan_file`/`review_file` resolution against the `edge.yaml` directory with pre-access absolute rejection, independent per-file default fallback, load-time regular-file/size/UTF-8/grammar rejection, digest-only refresh diff evidence, and admission-time freezing so a refresh reaches only newly admitted requests (`packages/go/config/load.go`, `apps/edge/internal/configrefresh/classify.go`, `apps/edge/internal/openai/single_request_preset_binding.go`).
- 2026-08-13: Added operation-scoped provider effort normalization facts and nearest-lower, never-upgrade mapping semantics to the protocol profile catalog.
- 2026-08-06: Synchronized the fixed single-request policy (`execution_presets[].single_request`) absolute caps, plan→work→review stage shape, opaque `workspace_ref`, live-apply classification, and snapshot-isolation semantics with current code, contract, and classifier implementation.
- 2026-08-06: Required effective positive workspace-operation bounds and clarified that the later Node-private typed config/admission transport is deferred; public/preset/provider surfaces retain no raw workspace roots or command templates.
