# SDD: CLI Agent Group Grade Routing

## 위치

- Milestone: [cli-agent-group-grade-routing](../../../phase/automation-runtime-bridge/milestones/cli-agent-group-grade-routing.md)
- Phase: [PHASE.md](../../../phase/automation-runtime-bridge/PHASE.md)

## 상태

[검토중]

## SDD 잠금

- 상태: 잠금
- 사용자 리뷰: [USER_REVIEW.md](USER_REVIEW.md)
- 잠금 항목:
  - [ ] [D01] 선택된 agent 실행 실패, provider quota 소진, 실행 중단, 중복 실행 위험이 발생했을 때 자동 재라우팅/재시도/중단 중 어떤 후속 정책을 적용할지 결정한다.

## 문제 / 비목표

- 문제: plan/code-review/doc 같은 파일 기반 agent-task는 이미 `PLAN-{lane}-GNN.md`, `CODE_REVIEW-{lane}-GNN.md` naming contract로 lane과 grade를 표현하지만, runtime이 이 정보를 CLI provider agent, 목적별 agent group, local/cloud capability, resource/quota 상태에 연결하는 계약이 없다. 이 SDD는 예약어 설정, agent group assignment, OpenAI-compatible metadata 입력, route log, validation 경계를 고정한다.
- 비목표:
  - 선택된 agent 실패 뒤 자동 retry/fallback 정책을 이번 문서에서 확정하지 않는다. D01 사용자 리뷰가 해결되기 전에는 fail-fast routing error 또는 실행 실패 reporting까지만 표준선으로 둔다.
  - benchmark runner 자체를 구현하지 않는다. 자동 설정은 benchmark profile, prompt, LLM 산출 schema, validation 경계까지만 다룬다.
  - 원격 터미널/CLI 터널링, oto scheduler/CI-CD, RAG/tool policy routing은 다루지 않는다.
  - 파일 내용을 OpenAI-compatible prompt 본문에 inline으로 넣는 입력 방식은 채택하지 않는다.

## Source of Truth

| 영역 | 기준 | 메모 |
|------|------|------|
| Roadmap | [cli-agent-group-grade-routing](../../../phase/automation-runtime-bridge/milestones/cli-agent-group-grade-routing.md) | 목표, 기능 Task, 잠금 항목, 범위 제외 기준 |
| Contract | [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md) | OpenAI-compatible `metadata.agent_group.task_file`와 `metadata.agent_group.params` 계약을 추가할 원문 |
| Code | `packages/go/config`, `apps/edge/internal/openai`, `apps/edge/internal/service`, `apps/node/internal/adapters/cli`, `configs` | config schema, OpenAI metadata parsing, routing decision, CLI adapter execution 연결 기준 |
| External Provider | CLI provider agent catalog | opencode/codex/claude/gemini 등 provider-specific 실행 대상은 cli provider agent id로 참조한다 |
| User Decision | D01 | 실행 실패/중단/quota 소진 시 자동 retry/fallback/중단 정책은 사용자 결정 전까지 잠금 |

## State Machine

| 상태 | 진입 조건 | 다음 상태 | 근거 |
|------|-----------|-----------|------|
| `prefix-configured` | 예약어 config가 `prefix`, `default_agent`, `prompt_template`, `file_payload_policy=path`를 가진다 | `request-received` | Edge config 또는 config refresh 결과 |
| `group-configured` | agent group이 `purpose`, `assignment_mode`, agent id set, lane별 coverage 또는 auto assignment profile을 가진다 | `request-received` | Edge config 또는 config refresh 결과 |
| `auto-assignment-needed` | 신규 auto group이 저장되었거나 기존 group이 수정되고 agent id contain set이 이전 저장값과 다르며 `assignment_mode=auto`다 | `auto-assignment-validated` 또는 `routing-config-error` | group edit/save event의 이전/현재 agent id set 비교 |
| `auto-assignment-validated` | auto assignment evaluator가 benchmark profile, benchmark sorting prompt, agent catalog를 입력으로 만든 assignment 결과가 output schema, lane coverage, local/cloud capability validator를 통과한다 | `group-configured` | auto assignment output schema |
| `request-received` | OpenAI-compatible 또는 native run request가 `metadata.agent_group.task_file` 또는 동등한 native field를 가진다 | `filename-parsed` 또는 `routing-input-error` | request metadata |
| `filename-parsed` | task file basename에서 등록 prefix, `local|cloud`, `G01`~`G10`을 파싱했다. prefix는 마지막 `-{lane}-GNN.md` suffix 왼쪽 전체다 | `direct-agent-selected` 또는 `group-routing` | filename contract |
| `direct-agent-selected` | 예약어 `default_agent`가 cli provider agent id이고 lane/grade capability가 맞다 | `execution-dispatched` | route prefix config |
| `group-routing` | 예약어 `default_agent=auto`이고 참조 agent group이 존재한다 | `candidate-selected` 또는 `routing-config-error` | route prefix config와 group assignment |
| `candidate-selected` | lane/grade 후보 중 route score가 가장 높은 agent를 골랐다 | `execution-dispatched` | resource/quota snapshot, coverage table |
| `execution-dispatched` | prompt template과 task file path를 CLI adapter/provider에 전달했다 | `execution-complete` 또는 `execution-failed-locked` | RunRequest/execution id |
| `execution-failed-locked` | 선택된 agent 실행 실패, quota 소진, 중단, 중복 실행 위험이 발생했다 | `blocked-on-D01` | D01 결정 전 자동 retry/fallback 금지 |
| `routing-input-error` | task file, filename, prefix, lane/grade가 유효하지 않다 | terminal error | OpenAI-compatible error 또는 native error |
| `routing-config-error` | group coverage gap, capability mismatch, missing default agent/group이 있다 | terminal error | config validation 또는 route validation |

## Interface Contract

- 계약 원문: [openai-compatible-api.md](../../../../agent-contract/outer/openai-compatible-api.md)
- 입력:
  - `metadata.agent_group.task_file`: agent group routing에 사용할 task file 경로다. 절대 경로와 상대 경로를 모두 허용한다. OpenAI-compatible CLI route에서 상대 경로는 `metadata.workspace` 기준으로 해석하고, 상대 경로인데 `metadata.workspace`가 없으면 실패한다.
  - `metadata.agent_group.params`: 예약어 `user_params_schema`로 검증한 사용자 parameter 객체다. 예약어 prompt renderer가 CLI별 prompt 또는 argument로 변환할 수 있다.
  - `route_prefixes[].prefix`: 사용자 추가 가능한 예약어다. task file basename에서 마지막 `-{lane}-GNN.md` suffix를 제거한 왼쪽 전체 값과 일치해야 한다.
  - `route_prefixes[].default_agent`: `auto` 또는 cli provider agent id다. `auto`면 `agent_group`을 사용하고, 특정 id면 agent group routing 없이 그 agent를 직접 선택한다.
  - `route_prefixes[].agent_group`: `default_agent=auto`일 때만 노출/필수인 목적별 agent group id다. `default_agent`가 특정 cli provider agent id이면 필수가 아니며 direct mode routing에 사용하지 않는다.
  - `route_prefixes[].prompt_template`: agent에게 전달할 최초 prompt template이다. 구현자/리뷰어/문서 작성자 같은 역할 지시를 여기에 둔다.
  - `route_prefixes[].user_params_schema`: `metadata.agent_group.params` 검증 schema다.
  - `route_prefixes[].file_payload_policy`: 이번 Milestone에서는 `path`만 표준선이다. 파일 내용 inline 전달은 금지한다.
  - `cli_provider_agents[].id`: 예약어 direct default agent와 agent group `agents[]`가 참조하는 실행 agent id다.
  - `cli_provider_agents[].adapter` / `target`: 내부 실행 기준인 `adapter + target`을 가리킨다.
  - `cli_provider_agents[].native_lane`: `local` 또는 `cloud`다.
  - `cli_provider_agents[].serves_lanes`: agent가 처리 가능한 lane 목록이다. cloud-capable agent는 `local`과 `cloud`를 모두 가질 수 있지만 local-only agent는 `cloud`를 가질 수 없다.
  - `agent_groups[].purpose`: `coding`, `docs` 같은 목적이다. 기본 agent group은 `coding`이며, `docs`는 문서/테스트용 추가 기본 후보로 둔다. 이 값은 자동 assignment의 benchmark profile 선택 기준이다.
  - `agent_groups[].assignment_mode`: `manual` 또는 `auto`다.
  - `agent_groups[].agents`: agent id 목록이다. 신규 auto group은 최초 자동 설정 대상이며, 기존 group의 변경 감지는 contain set 기준이다. 순서 변경만으로는 자동 assignment 재계산을 트리거하지 않는다. group 저장 시점의 이전/현재 agent id set 비교로 충분하며 hash/cache 기반 변경 감지는 요구하지 않는다.
  - `agent_groups[].benchmark_profile`: 자동 assignment에 사용할 benchmark category와 weight를 가리킨다.
  - `agent_groups[].auto_assignment_evaluator`: benchmark sorting prompt를 실행해 agent 순위와 lane별 grade range 초안을 반환할 LLM/evaluator target이다. group에 값이 없으면 system default evaluator를 사용할 수 있지만, 실행 전 어떤 evaluator를 썼는지 route/config log에 남겨야 한다.
  - `agent_groups[].benchmark_sorting_prompt`: 자동 assignment 때 agent 순위와 grade range 산출을 요청할 사용자 설정 prompt template이다.
  - `agent_groups[].auto_assignment_output_schema`: 자동 assignment 결과가 따라야 할 schema다. 최소한 lane별 range, agent id, `G01`~`G10` coverage, overlap 반영 여부, capability validation 근거를 표현해야 한다.
  - `agent_groups[].grade_overlap`: grade range overlap 폭이다. 예를 들어 `2`이면 각 grade의 상위/하위 2개 grade까지 인접 후보가 겹쳐 처리 가능하도록 assignment를 산출하거나 검증한다.
  - `agent_groups[].manual_ranges` / `auto_ranges`: lane별 `G01`~`G10` coverage를 표현한다. local lane과 cloud lane assignment table은 분리하며, cloud-capable agent는 local lane coverage에 포함될 수 있지만 local-only agent는 cloud lane coverage에 포함될 수 없다.
- 출력:
  - route decision: request/execution id, task file, parsed prefix/lane/grade, route prefix config id, group id 또는 direct default agent id, 후보 agent, 탈락 사유, 선택 agent, route score 입력 metric snapshot.
  - routing error: invalid task file, invalid filename, unknown prefix, missing group/default agent, lane/grade coverage gap, capability mismatch, malformed auto assignment result.
  - execution dispatch: 선택된 cli provider agent id와 해당 adapter/target에 전달한 path-only task file prompt.
- 금지:
  - `metadata.agent_route_prefix` 같은 중복 field를 만들지 않는다. prefix/lane/grade는 task file basename에서만 얻는다.
  - `metadata.cli` wrapper를 만들지 않는다.
  - task file 경로나 workspace를 prompt 본문에만 섞어 routing source로 사용하지 않는다.
  - agent group routing이 걸린 요청에서 filename 형식 오류를 best-effort로 추정하지 않는다.
  - agent id 목록의 순서만 바뀐 경우 자동 assignment를 재계산하지 않는다.
  - D01이 해결되기 전에는 실행 실패 뒤 자동 retry/fallback 정책을 구현하지 않는다.

## Acceptance Scenarios

| ID | Milestone Task | Given | When | Then |
|----|----------------|-------|------|------|
| S01 | `provider-agent` | local-only agent와 cloud-capable agent가 cli provider agent catalog에 있다 | config validation을 실행한다 | local-only agent는 cloud lane 후보가 될 수 없고, cloud-capable agent는 local lane 후보가 될 수 있다 |
| S02 | `group-schema` | manual coding group이 lane별 grade range를 가진다 | 한 lane의 `G01`~`G10` coverage 중 일부가 비어 있다 | config validation이 coverage gap을 routing-config-error로 보고한다 |
| S03 | `group-schema` | 신규 auto group이 저장되거나 기존 auto group의 `agents[]`가 같은 id set을 다른 순서로 저장된다 | group 저장을 처리한다 | 신규 auto group은 최초 assignment 대상으로 처리되고, 기존 group은 contain set이 같으면 auto assignment 재계산을 트리거하지 않는다 |
| S04 | `prefix-schema` | `PLAN` 예약어가 `default_agent=auto`로 설정되어 있다 | `agent_group` 없이 저장하거나 존재하지 않는 group을 참조한다 | config validation이 실패한다 |
| S05 | `metadata-contract` | OpenAI-compatible request가 absolute `metadata.agent_group.task_file=/repo/agent-task/x/PLAN-local-G08.md` 또는 `metadata.workspace=/repo`와 relative `metadata.agent_group.task_file=agent-task/x/PLAN-local-G08.md`를 가진다 | request metadata를 파싱한다 | 두 입력 모두 `/repo/agent-task/x/PLAN-local-G08.md`로 해석되고, prefix/lane/grade는 basename에서만 파싱된다 |
| S06 | `filename-parse` | agent group routing 요청이 `PLAN-local-G08.md`, `CODE_REVIEW-cloud-G07.md`, `DOC-local-G04.md`를 가리킨다 | filename parser가 실행된다 | 마지막 `-{lane}-GNN.md` suffix 왼쪽 전체가 prefix로 해석되고, 등록 prefix, lane, grade가 정확히 추출된다 |
| S07 | `filename-parse` | 요청 task file basename이 `PLAN-G08.md`, `PLAN-cloud-8.md`, `PLAN-local-G11.md`, `UNKNOWN-local-G02.md` 중 하나다 | filename parser가 실행된다 | 추정 없이 routing-input-error를 반환한다 |
| S08 | `default-agent` | `DOC` 예약어가 특정 cli provider agent id를 `default_agent`로 가진다 | `DOC-cloud-G08.md` 요청이 local-only direct agent로 들어온다 | agent group routing으로 우회하지 않고 capability mismatch error를 반환한다 |
| S09 | `manual-routing` | 같은 local grade를 처리할 수 있는 agent가 둘 이상이고 resource/quota metric이 다르다 | route score를 계산한다 | local resource 여유 또는 cloud subscription/quota 잔여량이 더 좋은 후보가 선택된다 |
| S10 | `auto-routing` | coding group과 docs group이 각각 `assignment_mode=auto`이며 auto assignment evaluator, benchmark sorting prompt, output schema를 가진다 | 자동 assignment prompt를 실행하고 schema를 검증한다 | coding group은 coding benchmark profile, docs group은 documentation benchmark profile을 사용하며 evaluator 응답이 output schema, lane coverage, capability 규칙을 통과한 경우에만 저장된다 |
| S11 | `route-log` | routing 성공 또는 routing error가 발생한다 | request/execution이 종료된다 | route log에서 같은 request id로 입력, 후보, 선택/실패 사유, resource/quota snapshot을 추적할 수 있다 |
| S12 | `contract-docs` | 구현이 `metadata.agent_group.task_file`을 지원한다 | 계약 문서와 README/config examples를 확인한다 | task file, params, filename contract, path-only payload policy가 문서화되어 있다 |
| S13 | `config-examples` | 기본 `coding` group, 문서/테스트용 `docs` group, `PLAN`, `CODE_REVIEW`, `DOC` 예약어를 설정하려는 운영자가 있다 | config sample을 확인한다 | manual assignment, auto assignment, direct default agent, `default_agent=auto` group routing 예시가 재현 가능하게 제공된다 |
| S14 | `routing-tests` | config validation, filename parsing, routing scorer, auto assignment result validation이 구현되어 있다 | targeted test suite를 실행한다 | S01~S13, S15~S17의 핵심 routing 계약이 자동 테스트로 검증된다 |
| S15 | `group-schema` | agent group의 `grade_overlap`이 `2`로 설정되어 있다 | manual/auto assignment 결과를 검증한다 | 각 grade의 상위/하위 2개 grade까지 인접 후보 overlap이 range에 반영되며 lane별 coverage가 유지된다 |
| S16 | `metadata-contract` | OpenAI-compatible request가 relative `metadata.agent_group.task_file`을 갖지만 `metadata.workspace`가 없다 | request metadata를 파싱한다 | 상대 task file을 해석하지 않고 routing-input-error를 반환한다 |
| S17 | `prefix-schema` | 예약어가 `default_agent=<agent-id>` direct mode로 설정되어 있다 | `<agent-id>`가 cli provider agent catalog에 없는 상태로 config validation을 실행한다 | `agent_group`으로 우회하지 않고 missing direct default agent error를 반환한다 |

## Evidence Map

| Scenario | Required Evidence | `agent-task` 연결 | 완료 Evidence 기대 |
|----------|-------------------|------------------|---------------------------|
| S01 | config validation unit test | `agent-task/m-cli-agent-group-grade-routing/...` | `provider-agent` Roadmap Completion과 local/cloud capability test output |
| S02 | lane coverage validation test | `agent-task/m-cli-agent-group-grade-routing/...` | `group-schema` Roadmap Completion과 coverage gap test output |
| S03 | contain-set change detection test | `agent-task/m-cli-agent-group-grade-routing/...` | `group-schema` Roadmap Completion과 reorder-no-recompute test output |
| S04 | route prefix validation test | `agent-task/m-cli-agent-group-grade-routing/...` | `prefix-schema` Roadmap Completion과 missing group/default agent test output |
| S05 | OpenAI metadata parser test for absolute and relative task file paths | `agent-task/m-cli-agent-group-grade-routing/...` | `metadata-contract` Roadmap Completion과 parser test output |
| S06 | filename parser positive table test | `agent-task/m-cli-agent-group-grade-routing/...` | `filename-parse` Roadmap Completion과 positive filename test output |
| S07 | filename parser negative table test | `agent-task/m-cli-agent-group-grade-routing/...` | `filename-parse` Roadmap Completion과 routing-input-error test output |
| S08 | direct default agent capability mismatch test | `agent-task/m-cli-agent-group-grade-routing/...` | `default-agent` Roadmap Completion과 direct-agent error test output |
| S09 | manual routing scorer test with resource/quota metric fixtures | `agent-task/m-cli-agent-group-grade-routing/...` | `manual-routing` Roadmap Completion과 selected candidate evidence |
| S10 | auto assignment prompt/schema validation test or golden fixture | `agent-task/m-cli-agent-group-grade-routing/...` | `auto-routing` Roadmap Completion과 benchmark profile selection evidence |
| S11 | route log unit/integration test | `agent-task/m-cli-agent-group-grade-routing/...` | `route-log` Roadmap Completion과 request id trace evidence |
| S12 | docs/contract diff plus doc link validation | `agent-task/m-cli-agent-group-grade-routing/...` | `contract-docs` Roadmap Completion과 OpenAI-compatible/README documentation evidence |
| S13 | config sample diff plus validation output | `agent-task/m-cli-agent-group-grade-routing/...` | `config-examples` Roadmap Completion과 example validation evidence |
| S14 | targeted routing/config test suite output | `agent-task/m-cli-agent-group-grade-routing/...` | `routing-tests` Roadmap Completion과 final verification output |
| S15 | grade overlap validation test | `agent-task/m-cli-agent-group-grade-routing/...` | `group-schema` Roadmap Completion과 overlap width test output |
| S16 | OpenAI metadata parser negative test for relative path without workspace | `agent-task/m-cli-agent-group-grade-routing/...` | `metadata-contract` Roadmap Completion과 missing workspace error evidence |
| S17 | direct default agent missing validation test | `agent-task/m-cli-agent-group-grade-routing/...` | `prefix-schema` Roadmap Completion과 missing direct agent evidence |

## Cross-repo Dependencies

- 없음

## Drift Check

- [x] Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- [x] Evidence Map이 code-review/complete.log에서 검증 가능하다.
- [x] agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- [x] 사용자 리뷰가 필요한 항목은 [USER_REVIEW.md](USER_REVIEW.md)에만 남겼다.

## 사용자 리뷰 이력

- 없음

## 작업 컨텍스트

- 표준선: 내부 실행은 `adapter + target` 기준을 유지한다. OpenAI-compatible agent group routing 문맥은 `metadata.agent_group` 아래에 둔다. task file 경로는 path-only payload로 전달하고 prefix/lane/grade는 basename에서만 파싱한다. agent group의 agent list 변경 감지는 순서가 아니라 contain set 기준이다.
- agent-ops 결합 기준: `plan`/`code-review` 스킬은 `PLAN-*`/`CODE_REVIEW-*` 파일과 각 루프의 lifecycle을 소유하고, CLI provider agent 선택은 Edge/runtime routing 책임으로 둔다. 스킬이 provider/agent를 직접 고르거나 다른 스킬 그룹 절차를 자동 호출하지 않는다.
- `metadata.agent_group`은 OpenAI-compatible 요청의 라우팅 metadata 컨테이너다. 실제 목적별 agent group assignment는 예약어 `default_agent=auto`일 때만 사용하며, direct `default_agent=<agent-id>` 요청은 같은 task file path와 filename validation을 사용하되 group 후보 산출로 넘어가지 않는다.
- `DOC-*` 같은 추가 prefix는 이 Milestone에서는 route prefix 계약으로만 다룬다. 별도 문서 작성 skill lifecycle이 필요하면 후속 Milestone/SDD에서 추가하고, 이번 라우팅 계약은 prefix config와 runtime dispatch 경계만 고정한다.
- 후속 SDD: D01이 자동 retry/fallback/중복 실행 방지의 별도 상태 전이를 크게 확장하면 CLI Agent 사용량 알림과 자동 이어받기 MVP SDD 또는 별도 follow-up SDD에서 다룬다.
