17 KiB
17 KiB
SDD: Agent Task 동적 실행 Target Selector
위치
- Milestone: Milestone 문서
- Phase: PHASE.md
상태
[승인됨]
SDD 잠금
- 상태: 해제
- 사용자 리뷰: 없음
- 잠금 항목:
- 없음
문제 / 비목표
- 문제: 정적 lane/G 파일명만으로는 시간대, cloud quota, 이전 실행 실패, 재개 여부를 반영할 수 없고, dispatcher가 호출 시마다 route를 다시 계산하면 시간 경계에서 같은 work unit의 target과 selfcheck 정책이 바뀔 수 있다. 실행 대상 선택과 failover를 결정적 상태 계약으로 분리하면서도 한 branch의 실패나 user review가 독립 branch까지 중단시키지 않아야 한다.
- 비목표:
finalize-task-routing의 lane/G 의미 판정 변경- cloud quota 구매·credential 회전·billing 시스템 구축
- adapter 간 native session protocol 통합
- 알림 UI와 운영 dashboard 구현
Source of Truth
| 영역 | 기준 | 메모 |
|---|---|---|
| Roadmap | Milestone 문서 | 정책 범위, 기능 Task와 완료 상태의 장기 원장 |
| Selector Code | agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py |
구현 후 selector 입출력, policy matrix와 reason code의 원본 |
| Dispatcher State | agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py와 .git/agent-task-dispatcher/state.json |
task/plan/tag 세대별 route pin, stage별 failure budget, failover와 resume의 실행 원본 |
| Quota Status | apps/node/internal/adapters/cli/status를 호출하는 좁은 JSON probe |
Go parser를 중복 구현하지 않고 cloud available, exhausted, unknown snapshot을 제공함 |
| External Provider | Gemini/Claude/Codex CLI adapter가 보고한 normalized status와 runtime result | generic stderr만으로 provider/quota 상태를 추정하지 않음 |
| User Decision | 없음 | 시간, route, failover, selfcheck, 실패 예산, 동시성 정책이 확정됨 |
State Machine
| 상태 | 진입 조건 | 다음 상태 | 근거 |
|---|---|---|---|
unselected |
PLAN task/plan/tag 세대에 persisted decision이 없음 |
selected 또는 blocked_task |
task file, worker/review stage, KST 시간 구간, quota snapshot, route rule |
selected |
selector가 eligible adapter + target을 반환하고 decision을 work unit에 저장함 |
executing |
selector JSON과 persisted decision |
executing |
선택 target invocation이 시작됨 | completed, selfcheck, recovering, blocked_task, review_blocked |
invocation result, normalized error, review 상태 |
recovering |
동일 stage 실패 예산이 10 미만이고 same-target recovery 또는 아직 사용하지 않은 eligible failover가 있음 | executing 또는 failover_selected |
stage별 누적 failure count, reason code, quota snapshot |
failover_selected |
primary target의 첫 실행 실패 뒤 정책이 단방향 다음 target을 선택하고 logical context package를 준비함 | executing |
이전 decision, PLAN/locator, normalized output·raw log, workspace; 이전 target으로 bounce하지 않음 |
selfcheck |
실제 worker 완료 target이 local model임 | completed, recovering, blocked_task |
completing target의 pinned local target과 selfcheck stage 결과; 새 route를 평가하지 않음 |
review_blocked |
해당 work unit 진행에 필요한 user review가 미해결임 | selected 또는 blocked_task |
review evidence; 해결 후에도 같은 pinned decision을 사용하고 해당 task의 의존 branch만 대기 |
blocked_task |
실패 예산 10회 도달, 야간 Laguna 실패 뒤 Gemini quota exhausted 또는 eligible target 부재 | terminal | persisted blocker와 실패 evidence; 독립 branch는 계속 실행 |
completed |
현재 stage가 성공했고 worker인 경우 필요한 local selfcheck까지 성공함 | terminal | stage 성공 시 해당 stage failure budget 초기화, 결과 artifact와 완료 evidence |
blocked_task는 현재 자동 실행의 terminal state다. 명시적retry_blocked는 해당 task group의 blocker와 stage 실패 카운터만 초기화하고 route history는 보존한다. quota exhaustion으로 대기하던 단방향 failover는 새 snapshot으로 eligibility를 다시 평가할 수 있지만, 시간 변화만으로 새 initial route를 만들거나 이미 사용한 이전 target으로 bounce하지 않는다.
Interface Contract
- 계약 원문: 없음
- 입력:
- CLI 필수 입력:
select_execution_target.py <task-file> stage:worker | review. 생략하면 task file의PLAN-*/CODE_REVIEW-*prefix에서 판별함task_file: 정적 routing source인*-{local|cloud}-GNN.md경로work_unit_id: PLAN 첫머리task/plan/tag세대로 selector가 파생하는 안정적인 identity. PLAN 본문만 바뀌면 유지하고 plan 또는 tag 세대가 바뀌면 새 identity가 됨prior_decision: dispatcher state에서 불러오는 선택 target, 사용한 candidate와 transition history; 최초 선택이면 없음quota_snapshot: 선택 입력에서 생략할 수 있는 cloud usage normalized JSON. runtime 기본은 selector가 좁은 JSON probe를 호출하고, dispatcher의 같은 admission batch 재사용 또는 테스트에서는 snapshot id, adapter, target/profile, status, 관측 시각을 가진 값을 주입할 수 있음evaluated_at: 기본은 runtime clock의 평가 시각. 시간 정책은Asia/Seoul로 변환하며 테스트에서는 고정 시각을 주입할 수 있음transition:initial | resume | failover.resume은 prior decision을 그대로 쓰고failover만 다음 candidate를 선택함
- CLI 필수 입력:
- 출력:
schema_version,work_unit_id,stage,lane,gradeselected: canonicaladapter,target,execution_class=local_model|cloud_model,selfcheck_requiredcandidates:candidate_rank가 낮을수록 먼저 시도하는 ordered 후보와 각 후보의 eligibility/rejection reason. task DAG scheduling priority와는 별개임decision:rule_id, 낮을수록 먼저 적용되는policy_priority,reason_codes,evaluated_at,timezone,time_window,pinnedquota:snapshot_id,mode=bounded|unbounded,status=available|exhausted|unknown|not_applicable,source,checked_attransition: 이전 target, 다음 target, trigger,context_transfer=logical|native_resume|none
- 금지:
- active/resume work unit의 persisted decision을 시간 변화만으로 교체하지 않는다.
- generic stderr나 model list만으로 quota exhausted 또는 provider 장애를 단정하지 않는다.
- cross-adapter 전환을 native session transfer로 기록하지 않는다.
- local
quota_mode=unbounded를 실패 예산 무제한 또는 동시 실행 강제 상한으로 해석하지 않는다. - user review/blocker가 없는 독립 task를 다른 branch의 실패 때문에 중단하지 않는다.
- selfcheck와 resume을 새 최초 선택으로 처리하거나 공식 CODE_REVIEW를 worker route matrix로 재라우팅하지 않는다.
- quota 정규화:
- JSON probe는 target/profile별 required cap set을 선언한다. Gemini처럼 overall과 matching model cap을 모두 제공하는 profile은 둘을 required로 사용할 수 있고, 제공하지 않는 cap을 임의로 만들어내지 않는다.
- required cap 중 하나라도 confirmed
0% remaining이면exhausted다. - required cap이 모두 관측되고 모두 positive remaining이면
available이다. - checker 오류, parse 실패, required cap 누락 또는 상충하는 evidence는
unknown이다. unknowncloud candidate는 각 work unit에서 1회만 시도하되 unknown 자체로 target 전역 동시 실행 cap을 만들지 않는다. runtime quota 오류가 확인되면 같은 snapshot을exhausted로 갱신해 다음 candidate 또는 task-local blocker로 전환한다.
- decision 고정:
- 최초
initialdecision은task/plan/tag세대에 저장한다. KST 구간 변화, PLAN 본문 수정과 dispatcher 재시작은 재선택 사유가 아니다. failover는 ordered candidate에서 아직 사용하지 않은 다음 target으로만 이동한다. 시간대 또는 quota 회복만으로 이전 target에 bounce하지 않는다.selfcheck는 worker를 완료한 pinned local target을 그대로 사용하며 selector의 새initial호출 대상이 아니다.- selector는 실패 횟수를 증가시키거나 초기화하지 않는다. dispatcher가
recovery_failures[stage]를 소유하고 target 전환에서는 보존하며 해당 stage 성공에서만 초기화한다.
- 최초
- 정책 matrix:
local-G01~G06:pi + iop/ornith-fast, local selfchecklocal-G07~G08, KST[07:00, 23:00):agy + Gemini 3.6 Flash Medium; 실패 시pi + iop/laguna-s:2.1로 logical context transfer 후 계속 실행local-G07~G08, KST[23:00, 07:00):pi + iop/laguna-s:2.1; 실패 시 Gemini quota가available또는unknown이면agy + Gemini 3.6 Flash Medium, confirmedexhausted이면 해당 task 차단local-G09~G10:claude + claude-opus-4-8, cloud completion이므로 selfcheck 없음cloud-G01~G06:claude + sonnettarget. adapter가 호출 시 canonical concrete target을 기록함cloud-G07~G08:claude + claude-opus-4-8cloud-G09~G10:codex + gpt-5.6-solxhigh- 모든 공식
CODE_REVIEW-*: Codexgpt-5.6-solxhigh 고정. worker candidate matrix와 quota 기반 재선택을 적용하지 않음 - dynamic
local-G07~G08failover는 primary에서 alternate로 한 번만 이동한다. alternate 실패는 같은 target에서 남은 동일-stage 예산으로 복구하며 primary로 돌아가지 않음 - 그 밖의 cloud worker는 기존 promotable failure chain을 유지하되 같은 stage의 10회 예산을 공유함
- cloud completion은 selfcheck 없음. local completion만 selfcheck를 실행함
- target별 정책 semaphore는 두지 않으며 dependency-ready task를 모두 dispatch함
Acceptance Scenarios
| ID | Milestone Task | Given | When | Then |
|---|---|---|---|---|
| S01 | selector-contract |
유효한 task file, task/plan/tag 세대, 고정 시각·quota·prior decision |
selector를 반복 호출함 | candidate rank와 decision evidence를 포함한 JSON이 완전하며 동일 입력은 동일 결정을 반환하고 invalid 입력은 명시 오류가 됨 |
| S02 | time-route |
local-G07 또는 local-G08과 네 KST 경계 시각 |
최초 route를 평가함 | [07:00,23:00)은 Gemini Medium, [23:00,07:00)은 Laguna가 선택됨 |
| S03 | grade-route |
worker local/cloud G01~G10과 CODE_REVIEW matrix |
모든 최초 route를 평가함 | 빈 grade 없이 확정된 adapter+target에 매핑되고 cloud G01~G06에 Gemini가 남지 않으며 CODE_REVIEW는 Codex로 고정됨 |
| S04 | quota-input |
target/profile required cap set, overall/model-specific remaining, parse 실패 및 local 후보 | JSON probe와 admission을 평가함 | required cap 중 0은 exhausted, 모두 양수는 available, 누락·충돌은 unknown이고 unknown은 work unit별 1회만 시도되되 target 전역 cap은 생기지 않으며 local은 unbounded/not_applicable로 반환됨 |
| S05 | route-pin |
주간에 선택된 task/plan/tag 세대가 PLAN 본문 변경, 야간 경계와 dispatcher 재시작을 지남 |
resume함 | 시간·본문 변경만으로 target이 바뀌지 않고 plan/tag 세대가 바뀔 때만 새 initial decision이 생성됨 |
| S06 | context-failover |
주간 Gemini 실패 또는 야간 Laguna 실패 | failover를 평가함 | 주간은 Laguna로, 야간은 quota가 있는 Gemini로 한 번만 이어지고 quota가 없으면 해당 task만 차단되며 이전 target으로 bounce하지 않음 |
| S07 | failure-budget |
같은 work unit의 동일 stage에서 target 전환을 포함한 연속 실패 | 자동 복구를 반복함 | stage budget이 target 간 공유되고 다른 stage와는 분리되며 10회 전에는 단일 Pi/provider/SSE 오류로 전체 루프가 차단되지 않고 stage 성공 시 초기화됨 |
| S08 | dependency-drain |
병렬 DAG의 한 branch가 user review 또는 terminal blocker에 걸림 | scheduler가 drain함 | 해당 branch와 의존 task만 대기/차단되고 다른 dependency-ready branch는 완료됨 |
| S09 | dispatch-integration |
selector decision이 저장된 worker/review initial, resume와 failover 작업 | dispatcher가 invocation함 | 실행 target, task/plan/tag persisted decision 및 transition history가 일치함 |
| S10 | selfcheck-policy |
Gemini→Laguna 완료, Laguna→Gemini 완료, cloud lane 완료 | completion stage를 처리함 | 첫 경우만 pinned Laguna로 selfcheck가 실행되고 나머지는 selfcheck 없이 완료됨 |
| S11 | throughput-policy |
동일 target을 쓰는 독립 dependency-ready task 여러 개와 같은 admission batch quota 조회 | dispatch함 | target별 정적 cap 없이 admission되며 같은 snapshot id가 batch에서 재사용되고 조회 실패는 unknown으로 격리됨 |
| S12 | audit-tests |
성공, failover, quota block, budget block, review block 실행 | evidence를 수집함 | work unit, rule, candidate, reason, transition이 테스트·로그·운영 설명에서 추적됨 |
Evidence Map
| Scenario | Required Evidence | agent-task 연결 |
완료 Evidence 기대 |
|---|---|---|---|
| S01 | selector schema/invalid input 단위 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: selector-contract, 테스트 결과 |
| S02 | KST 네 경계값 단위 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: time-route, 경계 matrix 결과 |
| S03 | worker local/cloud G01~G10 및 CODE_REVIEW route matrix 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: grade-route, matrix snapshot |
| S04 | Go usage checker JSON bridge, required-cap tri-state, unknown 1회와 local unbounded 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: quota-input, quota snapshot evidence |
| S05 | persisted route resume/restart 통합 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: route-pin, decision state evidence |
| S06 | 주간·야간 failover와 logical context 통합 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: context-failover, transition evidence |
| S07 | 동일 stage target 전환, stage 분리와 성공 초기화를 포함한 10회 failure budget 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: failure-budget, stage counter evidence |
| S08 | user review/blocker 병렬 DAG drain 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: dependency-drain, 독립 branch 완료 evidence |
| S09 | dispatcher worker/review initial, resume와 failover 통합 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: dispatch-integration, invocation audit |
| S10 | completing target 기반 selfcheck 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: selfcheck-policy, stage evidence |
| S11 | 동시 dispatch와 batch quota snapshot 테스트 | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: throughput-policy, admission evidence |
| S12 | 회귀 테스트, reason/transition 로그, 운영 문서 diff | agent-task/m-agent-task-runtime-target-selector/... |
Roadmap Completion: audit-tests, 최종 test summary |
Cross-repo Dependencies
- 없음
Drift Check
- Milestone 기능 Task와 Acceptance Scenario가 일치한다.
- Evidence Map이 code-review/complete.log에서 검증 가능하다.
- agent-contract를 쓰는 경우 SDD에 계약 원문을 복제하지 않았다.
- 사용자 리뷰가 필요한 항목은
USER_REVIEW.md에만 남겼다.
사용자 리뷰 이력
- 2026-07-25: KST 주간/야간 구간, target matrix, Medium reasoning, failover, quota, local selfcheck, 10회 실패 예산, work-unit pin, task-local blocker와 무제한 target별 처리량 정책을 대화에서 확정했다.
작업 컨텍스트
- 표준선: selector는
adapter + target을 결정적으로 반환하고, skill은 호출 시점과 persisted decision 소비 절차를 정의한다. static lane/G 분류는 기존finalize-task-routing책임으로, 공식 review는 기존 Codex 고정 lifecycle로 남긴다. - 후속 SDD: 없음