<!-- task=m-iop-agent-cli-runtime/04+01,02,03_task_manager plan=0 tag=API -->

# Common AgentTaskManager 구현 계획

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

`CODE_REVIEW-cloud-G10.md`의 구현 소유 섹션과 실제 검증 출력을 채우는 것이 구현의 필수 마지막 단계다. active pair를 그대로 두고 리뷰 준비 완료를 보고한다. blocker 발생 시 정확한 원인/명령/output/재개 조건만 evidence에 쓰고 사용자 질문·user-input·stop 파일·상태 분류·archive/`complete.log` 처리는 하지 않는다.

## 배경

현재 Python dispatcher가 task artifact scanning, provider process, review loop와 recovery를 한 process에서 수행하며 모델 감시 흐름에 결합돼 있다. 공통 Go `AgentTaskManager`가 등록 project의 수동 start intent만 받아 explicit dependency, provider capacity, isolated dispatch와 review/serial integration을 끝까지 조율하고 시작 기록이 있는 중단 작업만 설정에 따라 재개해야 한다.

## Roadmap Targets

- Milestone: `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md`
- Milestone link: [Milestone 문서](agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md)
- Task ids:
  - `common-runtime`: AgentTaskManager를 포함한 공통 runtime 단일 구현 완성
  - `task-manager`: 수동 start/auto-resume/dependency-ready isolated dispatch/review/serial integration
- Completion mode: check-on-pass

## 분석 결과

### 읽은 파일

- 규칙/설계: `agent-ops/rules/project/rules.md`, `agent-ops/rules/common/rules-roadmap.md`, `agent-ops/rules/common/rules-agent-spec.md`, `agent-ops/rules/project/domain/platform-common/rules.md`, `agent-ops/rules/project/domain/node/rules.md`, `agent-ops/rules/project/domain/testing/rules.md`, `agent-roadmap/current.md`, `agent-roadmap/priority-queue.md`, `agent-roadmap/ROADMAP.md`, `agent-roadmap/phase/automation-runtime-bridge/PHASE.md`, `agent-roadmap/phase/automation-runtime-bridge/milestones/iop-agent-cli-runtime.md`, `agent-roadmap/sdd/automation-runtime-bridge/iop-agent-cli-runtime/SDD.md`.
- 계약/스펙: `agent-contract/index.md`, `agent-contract/inner/edge-node-runtime-wire.md`, `agent-contract/inner/edge-config-runtime-refresh.md`, `agent-spec/index.md`, `agent-spec/runtime/edge-node-execution.md`, `agent-spec/runtime/provider-pool-config-refresh.md`.
- Python parity: `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`.
- Python tests: `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`.
- Go runtime/config: `apps/node/internal/runtime/types.go`, `apps/node/internal/adapters/registry.go`, `apps/node/internal/adapters/config_set.go`, `apps/node/internal/bootstrap/module.go`, `apps/node/internal/node/run_handler.go`, `apps/node/internal/router/router.go`, `packages/go/config/load.go`, `packages/go/config/provider_types.go`, `packages/go/config/validate.go`.
- 테스트 규칙: `agent-test/local/rules.md`, `agent-test/local/platform-common-smoke.md`, `agent-test/local/node-smoke.md`, `agent-test/local/testing-smoke.md`.

### SDD 기준

- 승인/잠금 해제된 SDD의 S03, S16 및 Evidence Map S03/S16이 기준이다.
- S03: 미선택 ready Milestone은 daemon start로 자동 시작하지 않고, manual start 기록이 있는 작업만 끝까지 수행하며 interrupted 작업은 default auto-resume/local override를 따른다.
- S16: explicit predecessor만 gate다. 번호나 overlapping/unknown write-set은 dependency를 만들지 않으며 provider limit 안에서 task별 isolation mode로 병렬 dispatch한다.
- Evidence는 manual-start/no-unselected-start/default-auto-resume multi-project trace와 dependency-only/parallel dispatch matrix로 고정한다.
- S01의 남은 `AgentTaskManager` 단일 공통 구현과 duplicate search도 이 plan에서 완성해 `common-runtime`을 닫는다.

### 테스트 환경 규칙

- `test_env=local`; local rules와 platform-common/node/testing profiles를 읽었다. Go tests는 `-count=1`, scheduler/process/state races는 `-race -count=1`; cache는 허용하지 않는다.
- actual root `/config/workspace/iop-s0`, branch `dev`, HEAD `0565d2be66cc`, 기존 roadmap/SDD dirty 변경이 있다. 사용자 변경을 보존한다. local rules의 root 표기 불일치는 actual root로 대체하며 rule maintenance는 범위 밖이다.
- Go `/config/.local/bin/go`, `go1.26.2 linux/arm64`. unit/integration tests는 fake clock, fake provider, temp workspaces와 injected ports로 외부 provider/Edge/port 없이 실행한다.
- 실제 isolation/change-set backend는 후속 S18/S19 owner다. 이 계획은 production manager의 strict ports, admission permit 요구와 scheduler semantics를 구현하고 fixture backend로 orchestration을 증명한다. backend 미설정이면 typed blocker이며 unsafe direct workspace fallback은 없다.

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

- Python `dispatch.py:380-890`은 task/state store, `988-1227`은 artifact/dependency scan, `3019-4278`은 provider execution, `4666-5109`은 review outcome을 다루지만 Go manager tests는 없다.
- 기존 Node run admission은 provider request concurrency만 다루며 project/Milestone selection, explicit task dependency, isolated siblings, review/integration queue와 interrupted auto-resume를 다루지 않는다.
- S03 multi-project, S16 matrix, no-supervisor/no-unselected-start와 terminal-deferred queue advance를 새 deterministic/race integration tests로 추가해야 한다.

### 심볼 참조

- Python `Task`, `StateStore`, `scan_tasks`, `dependency_state`, `select_dispatch_candidates`, `run_review`, `review_outcome`은 parity 대상이지 Go public symbol로 이식할 이름 계약이 아니다.
- predecessor의 `agentruntime.Provider`, catalog resolver와 guardrail Permit을 소비한다. rename/remove가 필요하면 `packages/go`와 Node call sites를 전부 `rg --sort path`로 갱신한다.
- `AgentTaskManager`는 `packages/go/agenttask` 단일 concrete implementation으로 두고 host에는 lifecycle adapter만 허용한다.

### 분할 판단

- stable contract는 registered ProjectWorkflow snapshot + persisted manual StartIntent → dependency-ready WorkUnit → admitted isolated execution → official review → ordinal integration/follow-up/terminal state다.
- predecessor 01, 02, 03 모두 active/archive `complete.log`가 없어 `missing`이다. 구현 전에 세 completion이 필요하다.
- 실제 overlay materialization/atomic integration storage는 later tasks의 port implementation이지만 manager가 unsafe fallback 없이 그 lifecycle과 ordering을 소유하는 것은 이 plan 범위다.

### 범위 결정 근거

- repo-global/local merge watcher, target selection/quota/failover, full state reconciliation, CLI surface, socket/client manager는 다른 feature tasks이므로 구현하지 않고 typed interfaces로 주입한다.
- Python code 삭제/cutover는 `parity-cutover` task가 소유하므로 이 plan은 read-only behavior fixture로만 사용한다.
- Node를 두 번째 supervisor로 만들지 않는다. 공통 manager package는 Node에서 복제하지 않으며 `iop-agent serve` host wiring은 `cli-surface`에서 수행한다.

### 최종 라우팅

- `evaluation_mode=first_pass`, finalizer=`finalize-task-routing` 1회.
- build `cloud/G10/routed`, review `cloud/G10/routed`; `large_indivisible_context=true`.
- positive loop risks 5개: durable state machine, multi-project concurrency, explicit dependency semantics, review/follow-up lifecycle, serial integration/recovery ordering.
- recovery `review_rework_count=0`, `evidence_integrity_failure=false`; capability gap evidence 없음.
- canonical files: `PLAN-cloud-G10.md`, `CODE_REVIEW-cloud-G10.md`.

## 구현 체크리스트

- [ ] API-1 AgentTaskManager state machine/public ports와 durable identities를 agent runtime 계약에 확정한다.
- [ ] API-2 project workflow scan, manual start intent와 default/override interrupted resume를 구현한다.
- [ ] API-3 explicit-dependency-only/provider-capacity scheduler와 admitted isolated parallel dispatch를 구현한다.
- [ ] API-4 worker→submission→official review→follow-up→ordinal integration orchestration을 구현한다.
- [ ] API-5 S03/S16 multi-project·restart·concurrency integration/race tests와 common-runtime duplicate search를 통과시킨다.
- [ ] CODE_REVIEW-*-G??.md의 구현 에이전트 소유 섹션을 실제 구현 내용과 검증 출력으로 채운다.

## 의존 관계 및 구현 순서

1. `01_common_runtime_node_bridge/complete.log`, `02+01_provider_catalog/complete.log`, `03+01,02_guardrail_admission/complete.log`가 각각 active sibling 또는 matching archive에 정확히 하나 존재해야 한다. 현재 모두 missing이다.
2. API-1 → API-2 → API-3 → API-4 → API-5 순서로 진행한다.

### [API-1] Manager 계약/state machine/ports

- 문제: `dispatch.py:380-890`에 Python-specific Task/StateStore가 결합돼 있고 공통 Go manager 계약이 없다.
- 해결 방법: `agent-runtime` inner contract에 manager commands/events/states와 identity revisions를 고정하고 `packages/go/agenttask`에 단일 `Manager`를 구현한다. Clock, WorkflowAdapter, StateStore, Selector, IsolationBackend, ProviderInvoker, Reviewer, Integrator는 narrow ports로 주입하되 state transition은 manager만 소유한다.

```go
// Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:380-890 — Python dispatcher owns scan/state/exec/review.
// After:
// type AgentTaskManager interface { StartProject(context.Context, StartRequest) error; Reconcile(context.Context) error; StopProject(context.Context, ProjectID) error }
```

- 수정 파일 및 체크리스트:
  - [ ] `agent-contract/inner/agent-runtime.md`에 commands/state/events/ports 및 no-unsafe-fallback 추가.
  - [ ] `packages/go/agenttask/types.go`, `manager.go`, `state_machine.go`, `ports.go` 구현.
  - [ ] project/work unit/attempt/dispatch ordinal/config/grant identity를 immutable typed value로 유지.
- 테스트 작성: `state_machine_test.go`에 legal/illegal transition, duplicate command idempotency, corrupt identity blocker tests를 작성한다.
- 중간 검증: `/config/.local/bin/go test -count=1 ./packages/go/agenttask/...`가 PASS해야 한다.

### [API-2] Manual start와 interrupted resume

- 문제: daemon이 filesystem의 ready Milestone을 발견하는 것과 사용자가 선택/시작한 intent를 구분하지 않으면 자동 최초 시작이 발생한다.
- 해결 방법: WorkflowAdapter scan 결과와 durable StartIntent를 별도로 저장한다. `Serve/Reconcile`은 미선택 ready를 관측만 하고, manual start 또는 previously-started interrupted record만 claim한다. `auto_resume_interrupted`는 omitted=true, explicit false면 stopped로 유지한다.

```go
// Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:1122-1227 — discovered ready task may become a candidate.
// After: candidate = ready && (manualStartIntent || (interrupted && autoResume))
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/agenttask/workflow.go`, `intent.go`, `reconcile.go` 구현.
  - [ ] project별 오류/blocker가 manager 전체 loop를 종료하지 않도록 격리.
  - [ ] state store interface가 atomic revision compare-and-swap을 요구하도록 정의.
- 테스트 작성: unselected ready 0 invocation, manual start full progression, interrupted default resume, override false stopped, duplicate daemon/lease blocker fixtures 추가.
- 중간 검증: `/config/.local/bin/go test -race -count=1 ./packages/go/agenttask/... -run 'Test(ManualStart|InterruptedResume|NoUnselectedStart|ProjectIsolation)'`가 PASS해야 한다.

### [API-3] Explicit dependency와 isolated parallel scheduler

- 문제: `dispatch.py:1160-1227`은 directory predecessor completion을 판정하지만 manager가 provider capacity와 task isolation을 결합한 공통 concurrency admission을 제공하지 않는다.
- 해결 방법: normalized explicit predecessor completion만 readiness gate로 사용한다. task 번호/write-set overlap/unknown은 dependency가 아니며, capacity와 guardrail Permit이 있는 sibling을 isolation backend에서 각각 준비해 병렬 invoke한다. isolation backend 없음/실패는 blocker이고 canonical direct write fallback은 없다.

```go
// Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:5110-5120 — process-local candidate scan.
// After: Scheduler.Admit(ReadySet, ProviderLimits, IsolationDescriptors) []DispatchLease
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/agenttask/dependency.go`, `scheduler.go`, `dispatch.go` 구현.
  - [ ] provider/profile concurrency counter와 project/workspace lease cancellation-safe release 구현.
  - [ ] same/different workspace, disjoint/overlap/unknown write-set을 isolation descriptor와 함께 event에 기록.
- 테스트 작성: S16 full matrix, provider limit, explicit predecessor missing/ambiguous/complete, cancel ticket release와 `go test -race` ordering tests 추가.
- 중간 검증: `/config/.local/bin/go test -race -count=1 ./packages/go/agenttask/... -run 'Test(Scheduler|ExplicitDependency|ProviderCapacity|IsolatedDispatch)'`가 PASS해야 한다.

### [API-4] Review, follow-up와 serial integration orchestration

- 문제: `dispatch.py:4666-5109`의 review loop와 filesystem outcome 판정이 Python process에 결합돼 있고 dispatch completion order가 canonical integration order를 결정할 위험이 있다.
- 해결 방법: worker submission gate 통과 후에만 official review를 요청한다. PASS change set은 최초 dispatch ordinal queue에 넣고 Integrator port를 한 번에 하나 호출한다. WARN/FAIL follow-up은 새 attempt로 돌리고 USER_REVIEW/terminal-deferred blocker는 보존하되 뒤 independent queue는 진행한다.

```go
// Before: agent-ops/skills/project/orchestrate-agent-task-loop/scripts/dispatch.py:4666-5109 — review/archive outcome handled inside dispatcher loop.
// After: Manager records ReviewResult and IntegrationRecord, then advances by dispatch ordinal.
```

- 수정 파일 및 체크리스트:
  - [ ] `packages/go/agenttask/review.go`, `followup.go`, `integration_queue.go` 구현.
  - [ ] exact verdict/artifact identity와 at-most-once external call idempotency key 검증.
  - [ ] integration failure가 partial completion을 기록하지 않고 retained change-set/blocker를 반환하도록 port contract 요구.
- 테스트 작성: PASS/WARN/FAIL/USER_REVIEW, follow-up, out-of-order worker completion, terminal-deferred queue advance, restart replay/no-duplicate matrix 추가.
- 중간 검증: `/config/.local/bin/go test -race -count=1 ./packages/go/agenttask/... -run 'Test(Review|Followup|Integration|RestartReplay)'`가 PASS해야 한다.

### [API-5] S03/S16 통합과 단일 구현 evidence

- 문제: unit tests만으로 multi-project full progression, no model supervisor, default resume와 common implementation uniqueness를 증명하지 못한다.
- 해결 방법: fake filesystem workflow, state store, isolation, provider, reviewer, integrator를 조합한 deterministic end-to-end fixture에서 두 project와 sibling tasks를 terminal 상태까지 구동하고 event/ledger snapshot을 golden으로 검증한다.
- 수정 파일 및 체크리스트:
  - [ ] `packages/go/agenttask/manager_integration_test.go`에 S03/S16 scenario 추가.
  - [ ] host-specific manager implementation 및 Python runtime import/fallback이 새 Go runtime에 없는지 search evidence 확보.
- 테스트 작성: 이 항목의 integration/race tests를 새로 작성한다.
- 중간 검증: `/config/.local/bin/go test -race -count=1 ./packages/go/agenttask/...`가 PASS해야 한다.

## 수정 파일 요약

| 파일/영역 | 항목 |
|---|---|
| `agent-contract/inner/agent-runtime.md` | API-1 |
| `packages/go/agenttask/types.go`, `manager.go`, `state_machine.go`, `ports.go` | API-1 |
| `packages/go/agenttask/workflow.go`, `intent.go`, `reconcile.go` | API-2 |
| `packages/go/agenttask/dependency.go`, `scheduler.go`, `dispatch.go` | API-3 |
| `packages/go/agenttask/review.go`, `followup.go`, `integration_queue.go` | API-4 |
| `packages/go/agenttask/*_test.go` | API-1~API-5 |

## 최종 검증

```bash
shopt -s nullglob
predecessor01=(agent-task/m-iop-agent-cli-runtime/01_common_runtime_node_bridge/complete.log agent-task/archive/[0-9][0-9][0-9][0-9]/[0-1][0-9]/m-iop-agent-cli-runtime/01_common_runtime_node_bridge/complete.log)
predecessor02=(agent-task/m-iop-agent-cli-runtime/02+01_provider_catalog/complete.log agent-task/archive/[0-9][0-9][0-9][0-9]/[0-1][0-9]/m-iop-agent-cli-runtime/02+01_provider_catalog/complete.log)
predecessor03=(agent-task/m-iop-agent-cli-runtime/03+01,02_guardrail_admission/complete.log agent-task/archive/[0-9][0-9][0-9][0-9]/[0-1][0-9]/m-iop-agent-cli-runtime/03+01,02_guardrail_admission/complete.log)
test "${#predecessor01[@]}" -eq 1
test "${#predecessor02[@]}" -eq 1
test "${#predecessor03[@]}" -eq 1
/config/.local/bin/go test -count=1 ./packages/go/agenttask/...
/config/.local/bin/go test -race -count=1 ./packages/go/agenttask/...
/config/.local/bin/go test -count=1 ./packages/go/agentruntime/... ./packages/go/agentprovider/... ./packages/go/agentguard/...
rg --sort path -n 'type AgentTaskManager|type Manager struct' packages/go apps || true
rg --sort path -n 'orchestrate-agent-task-loop|dispatch\.py|python(3)?' packages/go apps || true
git diff --check
```

기대 결과: 모든 predecessor gate와 fresh/race suites가 PASS한다. S03 trace는 unselected invocation 0, manual project terminal completion, default resume와 override stop을 보인다. S16 trace는 explicit predecessor만 gate하고 isolated siblings가 provider capacity 안에서 병렬 실행되며 integration은 ordinal 순서다. 첫 search는 공통 concrete manager 한 구현만, 두 번째는 새 Go runtime의 Python production/fallback dependency가 없음을 보여야 하고 `git diff --check`는 무출력이어야 한다.

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