<!-- task=node_router_registry plan=0 tag=REFACTOR -->

# node.Node 라우터·레지스트리 이중 참조 제거

## 이 파일을 읽는 구현 에이전트에게

각 체크리스트 항목을 완료하고 중간 검증 명령을 실행한 뒤 출력을 `CODE_REVIEW-local-G03.md`의 검증 결과 섹션에 붙여 넣으세요.
`CODE_REVIEW-local-G03.md`의 "이 파일을 읽는 리뷰 에이전트에게" 섹션에 있는 아카이브·complete.log 생성 지시는 **리뷰 에이전트 전용**이므로 구현 에이전트는 실행하지 마세요.

## 배경

`node.Node`는 현재 `runtime.Router`와 `*adapters.Registry` 둘 다를 필드로 가진다.
`OnRunRequest`에서 `router.Resolve()`로 adapter 이름을 결정한 뒤 다시 `registry.Get()`으로 어댑터 인스턴스를 꺼낸다.
라우터가 이름만 반환하기 때문에 불가피하게 이중 참조가 발생하며, `node.Node`가 `adapters.Registry` 구현 세부에 의존하게 된다.
`runtime.Router`가 이름 대신 `runtime.Adapter`를 직접 반환하도록 인터페이스를 확장하면 `Node`에서 registry 의존성을 제거할 수 있다.

---

### [REFACTOR-1] `runtime.Router` 인터페이스 확장 — `ResolveAdapter` 추가

**문제**

`apps/node/internal/runtime/types.go:128`
```go
// Router resolves a RunRequest into a concrete ExecutionSpec.
type Router interface {
    Resolve(ctx context.Context, req RunRequest) (ExecutionSpec, error)
}
```
`Resolve`는 어댑터 이름만 담긴 `ExecutionSpec`을 돌려준다. `Node`가 이름을 가지고 다시 `registry.Get()`을 호출해야 한다.

**해결 방법**

`Router` 인터페이스에 `ResolveAdapter` 메서드를 추가한다.

```go
// Router resolves a RunRequest into a concrete ExecutionSpec and the Adapter to execute it.
type Router interface {
    Resolve(ctx context.Context, req RunRequest) (ExecutionSpec, error)
    ResolveAdapter(ctx context.Context, req RunRequest) (ExecutionSpec, Adapter, error)
}
```

기존 `Resolve`는 하위 호환성 유지를 위해 남겨 두고, `Node`는 `ResolveAdapter`만 사용한다.

**수정 파일 및 체크리스트**

- [x] `apps/node/internal/runtime/types.go` — `Router` 인터페이스에 `ResolveAdapter` 추가

**테스트 작성**: 스킵 — 인터페이스 추가만이므로 구현 측 테스트로 검증 가능.

**중간 검증**
```
go build ./apps/node/internal/runtime/...
```
기대 결과: 빌드 성공.

---

### [REFACTOR-2] `router.defaultRouter` 에 `ResolveAdapter` 구현

**문제**

`apps/node/internal/router/router.go`의 `defaultRouter`가 `runtime.Router`를 구현하는데, 새 메서드가 없으면 인터페이스 불일치 컴파일 오류가 발생한다.

**해결 방법**

`Resolve` 로직을 내부 헬퍼로 추출하고, `ResolveAdapter`는 같은 헬퍼를 거쳐 registry에서 어댑터를 꺼내 반환한다.

```go
func (r *defaultRouter) ResolveAdapter(ctx context.Context, req runtime.RunRequest) (runtime.ExecutionSpec, runtime.Adapter, error) {
    spec, err := r.Resolve(ctx, req)
    if err != nil {
        return runtime.ExecutionSpec{}, nil, err
    }
    adapter, ok := r.registry.Get(spec.Adapter)
    if !ok {
        return runtime.ExecutionSpec{}, nil, fmt.Errorf("router: adapter %q not found", spec.Adapter)
    }
    return spec, adapter, nil
}
```

**수정 파일 및 체크리스트**

- [x] `apps/node/internal/router/router.go` — `ResolveAdapter` 메서드 추가

**테스트 작성**: `apps/node/internal/router/router_test.go`에 `TestResolveAdapter_Found` / `TestResolveAdapter_NotFound` 추가.
- `TestResolveAdapter_Found`: 등록된 어댑터 이름으로 요청 시 올바른 spec과 adapter 인스턴스가 반환되는지 확인.
- `TestResolveAdapter_NotFound`: 등록되지 않은 어댑터 이름으로 요청 시 에러 반환 확인.

**중간 검증**
```
go test ./apps/node/internal/router/...
```
기대 결과: 신규 테스트 포함 모두 PASS.

---

### [REFACTOR-3] `node.Node`에서 `registry` 필드 제거, `ResolveAdapter` 사용

**문제**

`apps/node/internal/node/node.go:29–33`
```go
type Node struct {
    nodeID   string
    router   runtime.Router
    registry *adapters.Registry   // ← 제거 대상
    store    *store.Store
    ...
}
```
`OnRunRequest:84`에서 `registry.Get(spec.Adapter)`를 직접 호출한다.

**해결 방법**

`registry` 필드를 제거하고, `OnRunRequest` 내 어댑터 조회를 `router.ResolveAdapter` 한 번의 호출로 대체한다.

Before (`node.go:75–88` 근방):
```go
spec, err := n.router.Resolve(ctx, rr)
if err != nil {
    return fmt.Errorf("node: resolve: %w", err)
}
adapter, ok := n.registry.Get(spec.Adapter)
if !ok {
    return fmt.Errorf("node: adapter %q not found after routing", spec.Adapter)
}
```

After:
```go
spec, adapter, err := n.router.ResolveAdapter(ctx, rr)
if err != nil {
    return fmt.Errorf("node: resolve: %w", err)
}
```

`New()` 시그니처에서 `registry *adapters.Registry` 파라미터를 제거한다.

**수정 파일 및 체크리스트**

- [x] `apps/node/internal/node/node.go` — `registry` 필드 제거, `New()` 파라미터 제거, `OnRunRequest` 수정
- [x] `apps/node/internal/bootstrap/module.go` — `node.New(...)` 호출에서 `reg` 인수 제거
- [x] `apps/node/internal/node/node_test.go` — `makeNode` 시그니처 및 호출 수정

**테스트 작성**: 기존 테스트에서 `makeNode`가 `fixedRouter`를 사용하므로 `fixedRouter`에도 `ResolveAdapter` 구현 추가가 필요하다.

**중간 검증**
```
go test ./apps/node/...
```
기대 결과: 모두 PASS, registry 참조 없음.

---

## 수정 파일 요약

| 파일 | 항목 |
|------|------|
| `apps/node/internal/runtime/types.go` | REFACTOR-1 |
| `apps/node/internal/router/router.go` | REFACTOR-2 |
| `apps/node/internal/router/router_test.go` | REFACTOR-2 |
| `apps/node/internal/node/node.go` | REFACTOR-3 |
| `apps/node/internal/bootstrap/module.go` | REFACTOR-3 |
| `apps/node/internal/node/node_test.go` | REFACTOR-3 |

## 의존 관계 및 구현 순서

REFACTOR-1 → REFACTOR-2 → REFACTOR-3 순서로 구현해야 한다. 컴파일 의존성이 있다.

## 최종 검증

```
go build ./apps/node/...
go test ./apps/node/...
```
기대 결과: 빌드 성공, 모든 테스트 PASS, `node.Node` 구조체에 `registry` 필드 없음.
