iop/agent-task/m-agent-task-runtime-target-selector/05+04_dispatch_integration/PLAN-local-G08.md

20 KiB

Plan - Dispatcher selector 단일 실행 기준 통합

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

선행 04+03_failover_budgetcomplete.log가 확인된 뒤 구현한다. 구현과 검증을 끝낸 뒤 CODE_REVIEW-cloud-G09.md의 구현 에이전트 소유 섹션을 실제 내용과 원문 출력으로 채운다. active 파일은 그대로 두고 review 준비 완료만 보고한다. 종결은 code-review skill 전용이다. 차단되면 정확한 원인, 시도한 명령/출력, 재개 조건만 구현 에이전트 소유 evidence에 기록한다. 사용자에게 질문하거나 user-input 도구를 호출하지 않고, control-plane stop file 생성, 다음 상태 분류, archive/log 이동, complete.log 또는 roadmap 수정은 하지 않는다.

배경

dispatcher는 selector가 존재해도 worker route, 실행, dry-run에서 정적 route_agent()를 다시 사용한다. 이 작업은 앞선 persisted decision과 failover 계약을 모든 live invocation의 단일 기준으로 연결해 고정, 재개, 전환을 실제 실행 경계에서 완성한다.

Roadmap Targets

  • Milestone: agent-roadmap/phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md
  • Milestone link: Milestone 문서
  • Task ids:
    • route-pin: 작업 세대별 최초 route 고정과 재시작 복원
    • context-failover: 단방향 adapter failover와 논리적 context 이전
    • failure-budget: target 전환 전후 동일 stage 실패 예산 공유
    • dispatch-integration: persisted selector decision 기반 최초 실행·resume·failover
  • Completion mode: check-on-pass

분석 결과

읽은 파일

  • agent-roadmap/phase/automation-runtime-bridge/milestones/agent-task-runtime-target-selector.md
  • agent-roadmap/sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md
  • 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/execution_target_policy.py
  • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py
  • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py
  • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_execution_target_policy.py
  • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py
  • agent-test/local/rules.md
  • agent-ops/rules/common/rules-agent-spec.md
  • agent-spec/index.md
  • agent-contract/index.md

SDD 기준

  • SDD: agent-roadmap/sdd/automation-runtime-bridge/agent-task-runtime-target-selector/SDD.md, 상태 [승인됨], 잠금 해제.
  • 대상 Acceptance Scenario: S05 → route-pin, S06 → context-failover, S07 → failure-budget, S09 → dispatch-integration.
  • Evidence Map S05~S07/S09 행의 persisted resume/restart, logical context transition, stage counter, worker/review initial·resume·failover invocation audit를 구현 체크리스트와 SelectorDispatcherIntegrationTest에 반영했다.

테스트 환경 규칙

  • test_env=local; agent-test/local/rules.md가 존재하며 끝까지 읽었다.
  • agent-ops Python dispatcher에 매칭되는 agent-test/local/*.md profile은 없다. 현재 checkout의 Python test manifest를 fallback 근거로 삼아 fresh-process unittest, py_compile, git diff --check를 사용하며 test-rule 유지보수는 필요하지 않다.
  • provider subprocess는 fake runner와 임시 workspace/state/locator로 대체하고 실제 cloud/local 호출은 하지 않는다.
  • 계획 시점 전체 Python suite 기준선은 169 tests PASS다.

테스트 커버리지 공백

  • dispatch.py:977 정적 route, run_worker() 부근의 실행 spec, dry-run 출력은 selector decision을 소비하지 않는다.
  • dispatcher와 standalone selector 어디에도 iop-node quota-probe runtime 호출부와 target/profile required-cap argv 매핑이 없다.
  • initial/resume/failover 각각의 invocation target과 persisted history가 일치하는 end-to-end 테스트가 없다.
  • review 고정 decision의 resume/finalization recovery와 worker 동적 decision이 분리되는지 검증하지 않는다.
  • dry-run 무상태, retry-blocked eligibility 재평가, runtime locator/status의 selector audit field 회귀가 없다.

심볼 참조

  • route_agent() 정의 dispatch.py:977과 호출부 run_worker()(dispatch.py:3423), run_selfcheck()(dispatch.py:3468), dry-run(dispatch.py:4201), 테스트 참조 test_dispatch.py:275,2499를 selector bridge로 옮긴 뒤 제거한다.
  • 기존 promoted_spec()(dispatch.py:1631)과 run_escalating() promotion call-site(dispatch.py:2960-2987)는 기타 cloud worker chain으로 유지하되 transition history/budget 소비자로 갱신한다.

분할 판단

  • split 정책을 먼저 평가했다. shared task group은 m-agent-task-runtime-target-selector이고 topology는 03 → 04 → 05 → 06 → 07 → 08이다.
  • 현재 05+04_dispatch_integration의 predecessor 04+03_failover_budget는 active plan만 있고 complete.log가 없어 미충족이다.
  • 03 state와 04 failover/budget 계약을 함께 소비한다. selfcheck와 throughput이 같은 dispatcher/test를 수정하므로 0607로 직렬 분리하고 운영 audit는 08이 소유한다.

범위 결정 근거

  • selector target matrix와 Go quota producer는 변경하지 않는다. selector/dispatcher에 producer의 runtime argv/profile 매핑 소비를 추가한다.
  • selfcheck target 재사용과 semaphore 제거는 후속 plan 범위다.
  • 사용자 리뷰/blocker dependency drain은 이미 완료된 정책을 보존하며 재설계하지 않는다.
  • agent-spec/index.mdagent-contract/index.md에 매칭되는 활성 selector/dispatcher spec/contract가 없으므로 승인 SDD, 코드와 테스트를 기준으로 삼는다.

최종 라우팅

  • evaluation_mode: isolated-reassessment
  • finalizer=finalize-task-policy.sh, finalizer_mode=pair; build/review status=routed.
  • build closures: scope_closed=true(승인 SDD와 이 subtask 경계가 고정됨), context_closed=true(명시된 source/test 범위를 한 local 작업에서 유지 가능), verification_closed=true, evidence_trusted=true, ownership_closed=true, decision_closed=true.
  • review closures: scope_closed=true, context_closed=true(동일 source/test 및 구현 evidence로 판정 가능), verification_closed=true, evidence_trusted=true, ownership_closed=true, decision_closed=true.
  • build route_basis=local-fit; capability_gap=none.
  • build loop-risk audit: matched signatures=temporal_state, boundary_contract, variant_product; 이 기록은 lane/G를 바꾸지 않는다.
  • build scores(scope_coupling/state_concurrency/blast_irreversibility/evidence_diagnosis/verification_complexity)=2/2/1/1/2, final=local-G08, filename=PLAN-local-G08.md.
  • review route_basis=official-review; official execution=codex/gpt-5.6-sol xhigh; capability_gap=none.
  • review scores(scope_coupling/state_concurrency/blast_irreversibility/evidence_diagnosis/verification_complexity)=2/2/1/2/2, final=cloud-G09, filename=CODE_REVIEW-cloud-G09.md.

구현 체크리스트

  • worker/review initial invocation과 dry-run이 persisted selector decision을 단일 route source로 사용한다.
  • bounded candidate의 runtime quota probe가 target/profile required-cap argv로 실행되고 probe 오류는 unknown snapshot으로 decision에 남는다.
  • resume은 시간·본문 변경에도 pinned target을 사용하고, 확정된 provider target 불가 또는 명시적 retry eligibility만 transition history와 invocation target을 변경한다.
  • cross-adapter invocation이 persisted normalized-output context package를 소비하고 dynamic failover와 기존 cloud promotion 모두 같은 stage 실패 예산을 이어받는다.
  • dry-run은 read-only preview이고 retry-blocked는 blocker/budget만 초기화하며 review resume/finalization recovery는 pinned Codex와 독립 budget을 유지한다.
  • locator/state/status가 work unit, rule/candidate/reason, quota, transition과 stage budget을 추적하고 통합 테스트가 restart·blocker·독립 drain을 함께 검증한다.
  • CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

구현 항목

[REFACTOR-1] Live route source 교체

  • 문제: dispatch.py:977route_agent()와 worker/dry-run call-site가 파일명 lane/G를 정적 CLI spec으로 다시 해석한다.
  • 해결 방법:
# Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:3423
agent = route_agent(task)
# After
decision = state.resolve_execution_decision(task, stage=stage, transition=transition)
agent = agent_spec_from_decision(decision)

selector decision 생성과 저장을 원자적으로 연결하고 이미 저장된 stage decision은 반드시 resume으로 검증한다. route_agent()와 위에 열거한 source/test call-site를 제거한다. 공식 review는 selector의 review 고정 결과를 쓰고 worker decision을 재사용하지 않는다. dry-run은 persisted decision이 있으면 read-only resume preview, 없으면 non-persisted initial preview만 만들며 state/history/admission 기록을 쓰지 않는다.

  • 수정 파일 및 체크리스트:
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py: live worker/review/dry-run route 소비자와 static helper 제거.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py: 제거된 helper 참조 갱신과 initial/review/dry-run assertions.
  • 테스트 작성: test_dispatch.pySelectorDispatcherIntegrationTest에서 정적 matrix와 selector 결과가 다른 temp task, fake runner, reopened state를 사용해 worker/review initial과 dry-run 무상태를 검증한다.
  • 중간 검증: python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py SelectorDispatcherIntegrationTest -v — initial/review/dry-run case가 PASS해야 한다.

[REFACTOR-2] Runtime quota probe와 resume/failover state machine

  • 문제: select_execution_target.py:485-491은 probe command를 source 문자열로만 기록하고 실제 호출하지 않으며, dispatch.py:2690-2987 recovery는 selector transition history와 runtime quota를 하나의 state machine으로 갱신하지 않는다.
  • 해결 방법:
# Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py:485
"source": quota_probe_command,
# After
snapshot = probe_candidate_quota(
    target=candidate.target,
    adapter=candidate.adapter,
    required_caps=quota_probe_spec(candidate).required_caps,
    checked_at=evaluated_at,
)
  • bounded candidate를 실제 평가할 때 shell 없이 iop-node quota-probe --target <canonical> --command <adapter> --required-cap ... --checked-at <RFC3339>를 실행한다. policy-owned mapping은 Gemini 3.6 Flash Low/Medium/High에 overall과 해당 model:<canonical> cap을, Claude/Codex에 overall을 선언한다. local model candidate는 probe하지 않고 오류/parse failure는 같은 target의 unknown snapshot으로 정규화한다.
  • persisted prior decision을 resume에 전달한다. local-G07~G08은 Gemini 3.6 Flash (Medium) 단일 target이므로 Laguna failover를 평가하지 않고 confirmed exhausted 또는 provider target 불가를 task-local blocker로 기록한다. 그 밖의 process/generic 오류와 dispatcher state/work-log/control/user-review/cancel 오류는 같은 target recovery로 처리한다.
  • 기타 cloud worker의 기존 agy → Claude → Codex promotion은 보존하면서 normalized transition/history와 공통 stage budget에 기록한다.
  • selector 반환, budget increment, used-candidate/history append, 다음 invocation 준비를 동일 task-state update 안에서 처리한다. unknown candidate의 confirmed runtime provider-quota는 해당 work unit의 derived evidence만 exhausted로 바꾸고, generic stderr는 unknown을 유지한다. invalid/no-eligible와 confirmed exhausted는 task-local blocker다.
  • 수정 파일 및 체크리스트:
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/execution_target_policy.py: canonical target별 command/profile/required-cap probe spec.
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py: no-shell subprocess/normalization과 injectable snapshot 경계.
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py: resume/failover/promotion orchestration과 task-local terminal state.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_execution_target_policy.py: target별 probe spec matrix.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py: probe argv, parse/error→unknown과 bounded/local matrix.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py: restart/time/failure-class/failover/promotion integration.
  • 테스트 작성: SelectorDispatcherIntegrationTest에서 day→night pin, body edit, new generation, qualified provider target 불가 failover, generic/Pi same-target recovery, excluded control error, available/unknown 1회 Gemini, confirmed exhausted blocker, 기존 cloud promotion을 검증한다. selector test에는 fake probe argv와 snapshot을 검증한다.
  • 중간 검증: python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py SelectorDispatcherIntegrationTest -vpython3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py -v가 PASS해야 한다.

[REFACTOR-3] Context, budget과 retry-blocked 소비

  • 문제: dispatch.py:2658-2687 continuation과 dispatch.py:2708-2828 budget lifecycle은 선행 task의 normalized context package, selector history, clear_blocked() 보존 규칙을 실제 invocation에 연결하지 않는다.
  • 해결 방법:
# Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:2790
prompt = base_prompt(task, role, spec) if previous_locator is None else continuation_prompt(...)
# After
prompt = continuation_prompt_from_package(
    context_package,
    target=decision["selected"],
    native_resume=same_pi_target,
)

qualified primary failure는 count 1, alternate 추가 9회 실패가 총 10 blocker가 되며 성공 target을 state/locator에 기록하고 동일 stage 성공에서만 reset한다. 일반 primary 실패는 same-target count만 늘리고 전환하지 않는다. --retry-blocked는 blocker와 stage counter를 초기화하되 decision/history/used candidate를 보존하고, quota-exhausted alternate만 새 snapshot으로 재평가해 available/unknown이면 진행하며 시간 변화·실패 횟수로 새 initial/bounce/cloud 승격하지 않는다.

  • 수정 파일 및 체크리스트:
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py: invocation payload, locator/result state, budget lifecycle.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py: fake runner input과 누적 failure evidence.
  • 테스트 작성: SelectorDispatcherIntegrationTest에서 primary 1회→alternate 9회=10 blocker, restart, 성공 reset, retry-blocked exhausted→available, history 보존, 독립 branch drain을 temp state/locator로 검증한다.
  • 중간 검증: python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py SelectorDispatcherIntegrationTest -v — context/budget/retry/drain case가 PASS해야 한다.

[REFACTOR-4] Review recovery와 runtime audit evidence

  • 문제: dispatch.py:794-865 finalization recovery는 active review .md가 없을 수 있고, locator/status/dry-run(dispatch.py:2158-2186,3665-3672,4193-4222)에는 selector work unit/rule/candidate/reason/quota/transition/budget이 없다.
  • 해결 방법:
# Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:4198
spec = AgentSpec("codex", "gpt-5.6-sol", "codex/gpt-5.6-sol xhigh") if stage == "review" else route_agent(task)
# After
decision = read_or_preview_stage_decision(task, state, stage=stage, dry_run=args.dry_run)
lines = status_lines(task, stage, "ready") + selector_evidence_lines(decision)

active review initial에서 Codex decision을 stage별로 보존하고 archive recovery는 동일 header identity의 persisted decision을 resume한다. legacy state에 decision이 없으면 archived header를 검증해 official-review fixed Codex decision만 합성하며 quota probe/failover를 금지한다. locator와 persisted transition history에 work_unit_id, rule_id, priority, candidate ranks/selected target, reason codes, quota snapshot id/status, transition과 stage budget을 기록하고 status/dry-run은 secret/raw provider output 없이 같은 evidence를 표시한다.

  • 수정 파일 및 체크리스트:
    • agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py: review recovery와 locator/status/dry-run audit fields.
    • agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py: active review resume, post-archive restart와 evidence consistency.
  • 테스트 작성: SelectorDispatcherIntegrationTest에서 review pinned Codex resume/독립 budget/no quota/no worker decision reuse, legacy fixed recovery, state-locator-status-dry-run evidence 일치를 검증한다.
  • 중간 검증: python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py SelectorDispatcherIntegrationTest -v — review recovery와 audit assertions가 PASS해야 한다.

의존 관계 및 구현 순서

  1. 04+03_failover_budget의 archived complete.log가 PASS여야 한다. 계획 작성 시점에는 아직 미충족이다.
  2. REFACTOR-1 → REFACTOR-2 → REFACTOR-3 순서로 연결한다.
  3. 이 task 완료 후 06+05_selfcheck_policy, 이어서 07+06_throughput_policy를 진행한다.

수정 파일 요약

파일 항목
agent-ops/skills/project/orchestrate-agent-task-loop/scripts/execution_target_policy.py REFACTOR-2
agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py REFACTOR-2
agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py REFACTOR-1, REFACTOR-2, REFACTOR-3, REFACTOR-4
agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_execution_target_policy.py REFACTOR-2
agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py REFACTOR-2
agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py REFACTOR-1, REFACTOR-2, REFACTOR-3, REFACTOR-4

최종 검증

  • python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py SelectorDispatcherIntegrationTest -v — initial/resume/failover/retry/review/audit 회귀 PASS.
  • python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py -v — runtime probe와 selector 전체 회귀 PASS.
  • python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py BlockerDrainTest -v — 독립 branch drain 회귀 PASS.
  • python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py' — fresh process 전체 suite PASS; cache output은 허용하지 않는다.
  • python3 -m py_compile agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py agent-ops/skills/project/orchestrate-agent-task-loop/scripts/execution_target_policy.py agent-ops/skills/project/orchestrate-agent-task-loop/scripts/select_execution_target.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_execution_target_policy.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_select_execution_target.py — exit 0.
  • git diff --check — 출력 없음.

모든 코드 변경 완료 후 반드시 CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 채운다.