<a id="providers"></a>
# Provider, extension과 coordination

상위 경로: [Norma 시스템 지도](../README.md#system-map) → [기능 지도](../features/README.md#feature-map) → Provider와 확장

`llm.Provider`는 `Stream`과 `Complete`를 공통 contract로 둔다. built-in adapter는 Anthropic Messages, OpenAI Chat Completions, OpenAI Responses format을 내부 content blocks와 stream events로 정규화한다. model name, endpoint, API key, reasoning/thinking와 output cap field는 host config가 정한다. [provider 근거](evidence:llm-provider)

| format | 기본 endpoint source | output/reasoning 차이 |
|---|---|---|
| `anthropic` | config → `ANTHROPIC_BASE_URL` → public default | Anthropic system cache boundary, thinking signature |
| `openai` | config → `OPENAI_BASE_URL` → public default | Chat Completions, 선택 `max_completion_tokens` |
| `openai-responses` | 같은 OpenAI source | Responses request/stream translation |
| custom | host `Provider` 구현 | contract 충족과 오류/usage fidelity가 host 책임 |

adapter가 normalized event를 만든다는 사실은 endpoint 호환성이나 thinking/tool block의 무손실 보장을 뜻하지 않는다. `MessagesForAPI`는 마지막 compaction boundary 뒤 messages를 취하고 orphan tool pairs를 정리한다. [message boundary 근거](evidence:llm-boundary)

<a id="provider-retry"></a>
## Retry와 rate limit

model request establishment의 network error, 408/429/500/502/503/504는 기본 최대 3회 retry되며 0.5s→1s→2s, 최대 8s backoff를 사용한다. fixed interval과 retry count를 설정할 수 있다. stream body를 읽기 시작한 뒤 끊기면 자동 재개하지 않는다. OpenAI empty response의 전체-request retry는 별도 설정이다. [retry 근거](evidence:llm-retry)

provider rate limiter는 같은 provider instance를 공유하는 callers 전체에 per-second/per-minute token bucket을 적용한다. logical request 한 번을 세고 내부 retry는 추가 count하지 않는다. cancel된 reservation은 반환하지 않아 더 보수적으로 남는다. 이것은 model API rate이며 target traffic rate limiting과 관계없다.

<a id="mcp"></a>
## MCP

`mcp.InProcessServer`와 stdio `Client.Tools`는 MCP tool을 `mcp__<server>__<tool>` 이름의 `CoreTool`로 감싼다. stdio client는 initialize와 tools/list를 수행하고 calls를 JSON-RPC로 전달한다. [MCP 근거](evidence:mcp-tools)

- child process는 host environment와 cwd를 사용한다.
- wrapping tool의 permission metadata는 호출 때 적용되지만 server 내부 effect scope는 별도다.
- returned text 중심으로 변환하며 image/resource/structured content fidelity가 제한된다.
- initial list 뒤 dynamic tool refresh는 확인되지 않았다.
- client process close는 Session.Close와 별도 lifecycle이다.

<a id="skills"></a>
## Skill

`skill.LoadDir`은 child directory의 `SKILL.md` YAML frontmatter와 Markdown body를 읽어 Registry를 만든다. `Skill` tool은 선택한 instructions를 `Result.Extra`의 별도 user message로 넣고 declared MCP names를 optional callback으로 unlock한다. [skill 근거](evidence:skills)

skill instruction은 system message나 signed policy가 아니다. unreadable/malformed entry는 load 과정에서 skip/축소될 수 있고 source trust, content digest, sandbox를 검증하지 않는다. compaction은 skill message를 boundary 뒤에 재주입할 수 있으므로 untrusted skill은 장기 prompt injection 경로다.

<a id="plan-mode"></a>
## Plan mode

`plan.Controller`는 live permission mode를 보유하고 `EnterPlanMode`와 `ExitPlanMode`를 제공한다. plan에서는 read-only tool만 허용된다. Exit tool은 permission상 read-only지만 approver를 호출하고 승인되면 default `acceptEdits` 또는 target mode로 전환한다. approver가 nil이면 자동 승인한다. [plan 근거](evidence:plan)

```mermaid
stateDiagram-v2
  [*] --> Normal
  Normal --> Plan: EnterPlanMode
  Plan --> Plan: Exit rejected
  Plan --> TargetMode: Exit approved
```

mode는 tool call마다 다시 읽힌다. 같은 assistant response에서 Exit가 먼저 완료되고 뒤의 mutating call이 대기 중이면 새 mode로 실행될 수 있다. model response 전체가 승인 transaction은 아니다. 예제 `security-audit`은 parent가 `ModePlan`인데 `Agent` tool은 read-only가 아니어서 현재 구성에서는 delegation이 permission에서 거부된다.

<a id="subagent-boundary"></a>
## Subagent 격리의 범위

`subagent.NewTool`은 agent type/prompt/workdir를 받아 새 child `harness.Query`를 실행한다. child conversation과 optional transcript sidechain은 분리되고 recursion depth 기본값은 3이다. [subagent 근거](evidence:subagent)

```mermaid
flowchart TD
  P[Parent conversation] -->|Agent tool| C[Child Query]
  C --> CC[Child conversation]
  C -->|shared reference| X[Provider / tool instances / hooks / filesystem]
  C --> S[(sidechain transcript)]
```

| 상속·공유 | 별도 또는 누락 |
|---|---|
| provider, selected/shared CoreTool instances, hooks, WorkingDir | child conversation, optional tool subset, system prompt |
| parent registry pointer when no subset | parent allow/deny, BashEnv, Tasks, compactor, output caps, MaxDuration |
| shared dispatcher closure가 가리키는 UnlockSet 가능 | parent schema filter와 plan mode 값의 자동 상속 |
| host filesystem/network | child usage와 terminal reason의 parent aggregate |

따라서 “isolated”는 대화 context 수준이다. 보안 격리는 independent process/container, credentials, filesystem과 brokered network가 담당해야 한다.

<a id="coordination"></a>
## Coordinator의 두 경로

`coordinator.Config`은 다음 두 실행 방식을 제공한다. [coordinator 근거](evidence:coordinator)

| 방식 | 실제 실행 | 병렬성 | 반환 한계 |
|---|---|---|---|
| LLM-driven `AgentTool` | coordinator model이 child task 생성 | Agent tool이 exclusive라 한 response에서는 직렬 | worker output을 model이 다시 소비 |
| programmatic `RunParallel` | task마다 goroutine + semaphore | `MaxParallel` 기본 4 | 입력 순서 Result, terminal reason은 error로 축약 |

worker default permission은 `acceptEdits`이고 `MaxTurns == 0`은 tool-turn 제한 없음이다. config에는 child hard duration, transcript, compactor, per-child egress와 total cost accounting이 없다. persistent frontier, lease, semantic dedup와 finding validation은 coordinator가 아니라 host control plane 책임이다.
