# Milestone: Agent Task 동적 실행 Target Selector

## 위치

- Roadmap: [ROADMAP.md](../../../../ROADMAP.md)
- Phase: [PHASE.md](../../../../phase/automation-runtime-bridge/PHASE.md)

## 목표

정적 `PLAN-{lane}-GNN.md` / `CODE_REVIEW-{lane}-GNN.md` 분류 결과를 실행 시점의 시간대, quota 상태, 이전 실패와 작업 상태에 결합해 결정적인 `adapter + target` 선택으로 변환한다.
선택 결과는 작업 단위로 고정·보존하고, 대상 전환이 필요할 때도 논리적 컨텍스트와 공통 실패 예산을 이어받아 독립 작업을 끝까지 drain하는 실행 경계를 확립한다.

## 상태

[완료]

## 승격 조건

- 없음

## 구현 잠금

- 상태: 해제
- SDD: 필요
- SDD 문서: [SDD.md](../../../sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md)
- SDD 사유: 시간·quota 기반 선택, 작업 단위 route 고정, adapter 간 failover, 재시도 예산과 task-local blocker 상태 전이를 변경한다.
- 잠금 해제 조건: 아래 체크리스트
  - [x] SDD 잠금이 해제되어 있다
  - [x] SDD 사용자 리뷰가 없거나 승인/해결되었다
  - [x] Acceptance Scenario가 Milestone 기능 Task와 연결되어 있다
  - [x] Evidence Map이 완료 시 `Roadmap Completion`과 최종 검증 evidence로 검증 가능하게 연결되어 있다
- 결정 필요: 없음

## 범위

- task file 경로를 필수 입력으로 받고 worker/review stage를 판별해 정규화된 JSON으로 `adapter + target`, 후보 rank, 선택 근거, quota snapshot, selfcheck 여부, 평가 시각과 적용 rule을 반환하는 결정적 selector
- `local-G01~G06`은 `iop/ornith:35b`, `local-G07~G08`은 KST 주간 Gemini 3.6 Flash (Medium)·야간 Laguna로 선택하는 실행 정책
- 기존 정적 lane/G route를 유지하면서 `cloud-G01~G06`을 Gemini 3.6 Flash Low/Medium/High로, `cloud-G07~G08`을 Opus로, `cloud-G09~G10`을 Codex로 선택하는 실행 정책
- 모든 공식 `CODE_REVIEW-*`를 Codex `gpt-5.6-sol` xhigh로 고정하고 worker target 선택과 분리하는 실행 정책
- 기존 Go CLI usage checker를 중복 파싱하지 않고 좁은 JSON probe로 연결한 cloud quota의 `available | exhausted | unknown` 정규화와 local target의 `unbounded / not_applicable` 처리
- `task/plan/tag` 세대 단위 route 고정, dispatcher 재시작 후 재개, 확정된 provider target 불가 때만 adapter 전환하는 논리적 컨텍스트 이전, 동일 stage에서 target 전환 전후에 공유하는 자동 실패 예산 10회
- 사용자 리뷰와 반복 실패 또는 eligible target 부재로 중단된 task 및 그 의존 task만 차단하고 독립적인 dependency-ready task는 끝까지 실행하는 drain 정책
- 실제 완료 target이 local model일 때만 selfcheck를 실행하고 cloud model 완료에는 selfcheck를 추가하지 않는 정책
- target별 인위적인 동시 실행 상한을 두지 않고 dependency-ready task를 처리량 우선으로 dispatch하는 정책

## 기능

### Epic: [selector] 결정적 실행 대상 선택

정적 task route와 실행 시점 상태를 재현 가능한 `adapter + target` 결정으로 변환하는 capability를 묶는다.

- [x] [selector-contract] task file을 필수 입력으로 받는 별도 selector가 worker/review stage, lane/grade, 선택 target, candidate rank, reason code, quota snapshot, selfcheck와 `evaluated_at`을 안정적인 JSON 계약으로 반환한다. 검증: 동일한 task 세대·시간 구간·quota snapshot·이전 결정은 동일한 route 결과를 내고 invalid filename/stage는 명시적 입력 오류로 종료한다.
- [x] [time-route] `local-G07~G08`은 KST `[07:00,23:00)`에 Gemini 3.6 Flash (Medium), `[23:00,07:00)`에 Laguna를 최초 target으로 선택한다. 검증: 네 KST 경계 matrix가 canonical adapter+target을 반환한다.
- [x] [grade-route] `local-G01~G06`은 Pi `iop/ornith:35b`, `local-G07~G08`은 시간대별 Gemini 3.6 Flash (Medium) 또는 Laguna, cloud `G01~G06`은 Gemini 3.6 Flash Low/Medium/High, `G07~G08`은 Opus, `G09~G10`은 Codex로 선택하며 모든 공식 CODE_REVIEW는 Codex `gpt-5.6-sol` xhigh로 고정한다. 검증: worker local/cloud `G01~G10`과 CODE_REVIEW route matrix가 빈 구간 없이 canonical adapter/target으로 해석된다.
- [x] [quota-input] 좁은 JSON quota probe가 기존 Go usage checker의 overall/model-specific remaining 신호를 재사용해 cloud quota를 `available | exhausted | unknown`으로 정규화하고, local target은 `quota_mode=unbounded`, `quota_status=not_applicable`로 표현한다. 검증: target/profile의 required cap 중 하나라도 0이면 exhausted, required cap이 모두 확인되고 양수이면 available, 누락·충돌·parse 실패는 unknown이며 unknown 후보는 각 work unit에서 runtime quota 결과가 확정될 때까지 1회만 admission하되 target 전역 cap을 만들지 않고 generic stderr만으로 quota/provider 장애를 추정하지 않는다.

### Epic: [continuation] 고정·Failover·중단 전파

시간 경계나 dispatcher 재시작에도 작업 identity를 유지하고, 실패가 전체 작업 그래프를 불필요하게 멈추지 않게 한다.

- [x] [route-pin] 최초 선택 결과를 PLAN의 `task/plan/tag` 세대에 고정·보존하고 시간 경계, PLAN 본문 갱신 또는 dispatcher 재시작만으로 활성/재개 작업의 target을 바꾸지 않는다. 검증: 주야간 경계를 넘긴 재개와 재시작이 persisted route를 유지하며 명시적 failover event만 target을 변경하고 새 plan/tag 세대만 새 최초 선택을 만든다.
- [x] [context-failover] 확정된 provider quota/rate-limit, context/output limit, model unavailable 또는 transport disconnect로 현재 target을 계속 쓸 수 없을 때만 주간 Gemini는 Laguna로, 야간 Laguna는 quota가 남은 Gemini Medium으로 한 번 논리적 컨텍스트를 이전한다. 그 밖의 실패는 같은 target에서 복구하고, 야간 Gemini quota가 없으면 해당 task를 중단한다. 검증: PLAN/locator, 이전 normalized output·raw log, 현재 workspace가 다음 adapter 입력에 연결되고 same-Pi session만 native resume을 사용하며 시간·quota 회복이나 실패 횟수만으로 이전 target에 bounce하거나 cloud로 승격하지 않는다.
- [x] [failure-budget] 자동 복구 실패 예산 10회를 같은 work unit의 동일 stage에서 target 전환 전후에 공유하되, 실패 횟수는 target 전환 또는 cloud 승격 조건으로 쓰지 않는다. 검증: target 전환 후에도 해당 stage의 누적 실패 횟수가 보존되고 stage 성공 시에만 초기화되며 10회 도달 또는 eligible target 부재 시 해당 task가 terminal blocker가 된다.
- [x] [dependency-drain] user review가 해당 task의 진행을 실제로 막는 경우와 terminal blocker가 된 경우에만 그 task 및 의존 task를 중단하고, 독립적인 dependency-ready task는 완료될 때까지 dispatch한다. 검증: 병렬 DAG에서 한 branch의 review/blocker가 다른 독립 branch의 완료를 막지 않는다.

### Epic: [integration] Dispatcher와 검증 통합

selector 결정을 오케스트레이션 루프의 단일 실행 기준으로 사용하고 정책 drift를 자동 검증한다.

- [x] [dispatch-integration] `orchestrate-agent-task-loop` dispatcher가 정적 `finalize-task-routing` 결과를 다시 분류하지 않고 `task/plan/tag` 세대에 저장된 selector decision으로 worker/review 최초 실행, resume과 failover를 수행한다. 검증: 시간 경계와 재시작을 포함한 dispatcher 통합 테스트에서 invocation, persisted target과 transition history가 일치한다.
- [x] [selfcheck-policy] selfcheck 여부를 task lane이 아니라 실제 worker 완료 target의 local/cloud 성격으로 결정하고, local selfcheck는 새 route를 평가하지 않고 그 완료 target을 그대로 사용한다. 검증: Gemini→Laguna 완료는 Laguna selfcheck를 실행하고 Laguna→Gemini 완료와 모든 cloud target 완료는 selfcheck를 실행하지 않는다.
- [x] [throughput-policy] selector와 dispatcher는 target별 정적 semaphore를 정책으로 강제하지 않고 모든 dependency-ready task를 dispatch하며, quota 조회는 scheduling admission batch 안에서 snapshot id와 관측 시각이 같은 결과로 재사용한다. 검증: 서로 독립적인 동일 target task가 인위적 target cap 없이 admission되고 quota 조회 실패는 해당 batch의 `unknown`으로 격리된다.
- [x] [audit-tests] 선택, 고정, failover, quota, 실패 예산, blocker 전파를 reason code와 work unit identity로 추적할 수 있고 단위·통합 테스트 및 운영 설명이 정책 matrix와 일치한다.

## 완료 리뷰

- 상태: 통과
- 요청일: 2026-07-27
- 완료 근거: 01~11번 PASS archive가 selector 계약, route pin/failover, failure budget, dependency drain, dispatcher 연동, selfcheck와 throughput 정책을 구현·검증했다. `07+06_context_review_recovery/complete.log`가 `audit-tests`를 직접 PASS로 기록했으며, 중복된 12번 audit pair는 임시 Python 구현 폐기 예정에 따른 2026-07-27 사용자 결정으로 미실행 종료했다.
- 검토 항목:
  - [x] 모든 기능 Task와 Task 안의 검증이 기존 PASS evidence 또는 사용자 승인 검증 면제로 닫혔다
  - [x] selector JSON, dispatcher persisted state와 usage checker 경계가 01~11번 완료 근거와 일치한다
  - [x] `Roadmap Completion`과 기존 회귀 evidence가 SDD Evidence Map의 시나리오를 가리킨다
- agent-ui 상태 반영: 해당 없음
- Spec sync: 해당 없음 — `agent-spec/index.md`에 이 임시 Agent-Ops Python selector/dispatcher와 매칭되는 제품 구현 spec이 없고, 곧 공통 Go runtime으로 대체할 참조 구현이므로 living spec을 추가하지 않는다.
- 리뷰 코멘트: 12번 전용 `DynamicSelectorAuditTest`와 추가 전체 회귀는 실행하지 않았으며 PASS로 가장하지 않는다. 마일스톤 종료는 기존 `audit-tests` PASS 근거와 사용자의 명시적 검증 면제 결정에 따른다.

## 범위 제외

- `finalize-task-routing`이 소유하는 PLAN/CODE_REVIEW lane 및 G 등급 판정 변경
- 공식 CODE_REVIEW의 Codex 고정 route와 독립 review lifecycle 변경
- cloud provider별 quota 구매, credential/account 회전, billing API 신규 연동
- local model의 가상 quota 또는 비용 제한
- adapter 간 native session 호환을 가장하는 전송
- 모델별 인위적인 처리량 상한과 시간 변화만으로 활성 작업을 재라우팅하는 동작
- 알림 UI, Control Plane dashboard, 외부 webhook 완성

## 작업 컨텍스트

- 관련 경로: `agent-ops/skills/project/orchestrate-agent-task-loop/SKILL.md`, `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`, `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py`, `agent-ops/skills/project/orchestrate-agent-task-loop/tests`, `apps/node/internal/adapters/cli/status`, `apps/edge/internal/service/node_command.go`
- 표준선(선택): 실행 정책은 모델명이 아니라 `adapter + target`으로 표현하고, selector는 결정적 상태 판별을, skill은 호출 시점과 결과 소비 절차를 소유한다.
- 표준선(선택): route는 시간 단위가 아니라 work unit 단위로 고정한다. 시간은 최초 선택 입력일 뿐이며 활성 작업의 변경 사유가 아니다.
- 표준선(선택): work unit의 selector identity는 PLAN의 `task/plan/tag` 세대이며 PLAN 본문 변경은 새 선택을 만들지 않는다. worker, selfcheck, review는 실패 예산을 공유하지 않는 별도 stage다.
- 표준선(선택): cross-adapter continuation은 PLAN/locator, 이전 출력과 로그, 현재 workspace를 이용한 logical context transfer이며 native session transfer로 표현하지 않는다.
- 표준선(선택): local quota가 무제한이라는 뜻은 quota admission을 하지 않는다는 뜻이며, 자동 실패 예산 10회와 terminal blocker 규칙은 그대로 적용한다. 실패 횟수는 target 전환 또는 cloud 승격 근거가 아니다.
- 큐 배치: 현재 진행 작업으로 전역 실행 순서 첫 항목
- 선행 작업: `finalize-task-routing`이 생성하는 정적 PLAN/CODE_REVIEW lane/G 파일명 계약
- 후속 작업: [Provider 사용량 알림과 운영 표면](../../../../phase/automation-runtime-bridge/milestones/provider-usage-notification-operations-surface.md)의 runtime event 소비 알림 표면
- 확인 필요: 2026-07-26 사용자 결정으로 `local-G07~G08` canonical 정책은 주간 Gemini ↔ 야간 Laguna이며, 단일 Gemini/no-Laguna 정책을 사용하지 않는다.
