<!-- task=m-agent-task-runtime-target-selector/09+08_admission_quota_batch plan=1 tag=REFACTOR -->

# Plan - Admission batch quota snapshot 재사용

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

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

## 배경

같은 scheduling admission batch의 최초 selector 결정들이 unique probe key별 관측을 한 번만 수행하고 동일 snapshot identity와 checked_at을 재사용하도록 batch 경계를 만든다.

## 분석 결과

### 읽은 파일

- `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/test_dispatch.py`
- `apps/node/internal/adapters/cli/status/quota.go`
- `apps/node/cmd/node/quota_probe.go`
- `agent-test/local/rules.md`

### SDD 기준

- 대상 Acceptance Scenario는 S11이다. 이 child는 scheduling batch lifetime, unique probe key와 shared snapshot identity 범위만 소유한다.

### 테스트 환경 규칙

- `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 유지보수는 필요하지 않다.
- quota probe와 runner는 fake로 주입해 호출 횟수, batch identity, 실제 동시 시작을 결정적으로 측정한다.
- 계획 시점 전체 Python suite 기준선은 169 tests PASS다.

### 테스트 커버리지 공백

- 같은 admission batch의 task별 selector 평가가 quota를 반복 관측해 snapshot id와 시각이 달라질 수 있다.
- mixed target에서 unique probe key별 single observation과 local/resume probe 0회가 검증되지 않는다.

### 심볼 참조

- `dispatch.py:4228` ready admission pass에 immutable aggregate quota envelope을 만들고 최초 decision에 주입한다. Node quota JSON producer는 변경하지 않는다.

### 분할 판단

- 원본 `07+06_throughput_policy`를 admission batch, target cap 제거, unknown 격리의 세 behavior slice로 분할했다.
- batch envelope이 뒤 두 child의 producer이므로 첫 번째 index를 갖는다.

### 범위 결정 근거

- quota batch envelope은 admission pass 수명으로만 재사용하고 영속/global cache를 만들지 않는다.
- target semaphore 제거와 probe failure 격리는 후속 child 범위다.

### 최종 라우팅

- 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=`boundary_contract`, `concurrent_consistency`; 이 기록은 lane/G를 바꾸지 않는다.
- build scores(scope_coupling/state_concurrency/blast_irreversibility/evidence_diagnosis/verification_complexity)=`1/2/1/1/2`, final=`local-G07`, filename=`PLAN-local-G07.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)=`1/2/1/1/2`, final=`cloud-G07`, filename=`CODE_REVIEW-cloud-G07.md`.

## 구현 체크리스트

- [ ] 한 scheduling admission batch가 unique adapter+target+profile별 probe를 한 번씩 실행해 aggregate envelope을 만들고 모든 최초 selector 결정이 동일 batch snapshot id와 checked_at을 재사용한다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 구현 항목

### [REFACTOR-2] Admission batch quota snapshot

- 문제: `dispatch.py:4228`의 ready admission pass에서 task별 selector 평가가 single-target Go probe를 개별 호출하면 같은 pass 안에서도 quota 상태/시각이 달라지고, 한 snapshot을 mixed target에 재사용하면 unmatched target이 `unknown`이 된다.
- 해결 방법:

```python
# Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:4228
for task, stage in select_dispatch_candidates(ready)[0]:
    resolve_decision(task)
```

```python
# After
batch_snapshot = quota_provider.aggregate(
    snapshot_id=batch_id,
    checked_at=admission_time,
    keys=unique_probe_keys,
)
for task in ready_tasks:
    resolve_decision(task, quota_snapshot=batch_snapshot)
```

  probe key는 `(adapter, canonical target, command/profile, required_caps)`다. 한 pass에서 key별 Go probe를 최대 한 번 실행하고 결과 targets를 immutable aggregate envelope으로 합치며 top-level batch id/time을 모든 최초 decision에 전달한다. child Go snapshot id/reason은 target entry evidence로 보존한다. 동일 target N개는 probe 1회, mixed target은 unique key 수만큼 호출한다. local decision은 `not_applicable`이고 persisted decision이 있는 resume/selfcheck는 admission probe 대상이 아니다.
- 수정 파일 및 체크리스트:
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py`: batch context와 snapshot lifetime.
  - [ ] `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py`: probe count와 identity assertions.
- 테스트 작성: `ThroughputQuotaBatchTest`에서 동일 target N개=probe 1회, mixed target=unique key별 1회, 전체 decision의 같은 batch id/time, target별 status 매칭, local/resume probe 0회를 검증한다.
- 중간 검증: `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest -v` — probe count/envelope identity matrix가 PASS해야 한다.

## 의존 관계 및 구현 순서

1. `08+07_selfcheck_policy`의 `complete.log`가 PASS여야 한다.
2. 이 task의 batch envelope을 `10+09_target_cap_removal`과 그 후속 `11+10_unknown_isolation`이 소비한다.

## 수정 파일 요약

| 파일 | 항목 |
|---|---|
| `agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py` | REFACTOR-2 |
| `agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` | REFACTOR-2 |

## 최종 검증

- `python3 agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py ThroughputQuotaBatchTest -v` — probe count/envelope identity matrix PASS.
- `python3 -m unittest discover -s agent-ops/skills/project/orchestrate-agent-task-loop/tests -p 'test_*.py'` — fresh process 전체 suite PASS.
- `python3 -m py_compile agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py agent-ops/skills/project/orchestrate-agent-task-loop/tests/test_dispatch.py` — exit 0.
- `git diff --check` — 출력 없음.

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

